2022-07-08 08:26:06 +00:00
package main
import (
2022-07-08 10:15:49 +00:00
"database/sql"
"errors"
2022-07-08 08:26:06 +00:00
"fmt"
2022-07-09 17:42:00 +00:00
"log"
2022-07-08 10:15:49 +00:00
"net/http"
2022-07-08 08:26:06 +00:00
"strconv"
"strings"
"time"
2023-03-06 12:31:24 +00:00
"github.com/drone/drone-go/drone"
2022-07-09 17:42:00 +00:00
"github.com/gin-gonic/gin"
2022-09-04 15:59:36 +00:00
"github.com/russross/blackfriday/v2"
2022-07-08 08:26:06 +00:00
)
2022-07-09 17:42:00 +00:00
func declareAPIWorksRoutes ( router * gin . RouterGroup ) {
router . GET ( "/works" , func ( c * gin . Context ) {
var u * User
if user , ok := c . Get ( "LoggedUser" ) ; ok {
u = user . ( * User )
}
2022-07-08 08:26:06 +00:00
2022-07-09 17:42:00 +00:00
var works [ ] * Work
var err error
if u == nil {
works , err = getWorks ( fmt . Sprintf ( "WHERE shown = TRUE AND NOW() > start_availability AND promo = %d ORDER BY start_availability ASC" , currentPromo ) )
} else if u . IsAdmin {
works , err = getWorks ( "ORDER BY promo DESC, start_availability ASC" )
} else {
works , err = getWorks ( fmt . Sprintf ( "WHERE shown = TRUE AND promo = %d ORDER BY start_availability ASC" , u . Promo ) )
}
2022-07-08 08:26:06 +00:00
2022-07-09 17:42:00 +00:00
if err != nil {
log . Println ( "Unable to getWorks:" , err )
c . AbortWithStatusJSON ( http . StatusInternalServerError , gin . H { "errmsg" : "Impossible de récupérer la liste des travaux. Veuillez réessayer dans quelques instants." } )
return
}
2022-07-08 08:26:06 +00:00
2022-07-09 17:42:00 +00:00
var response [ ] * Work
if u == nil || u . IsAdmin {
response = works
} else {
for _ , w := range works {
if w . Group == "" || strings . Contains ( u . Groups , "," + w . Group + "," ) {
2022-11-19 10:41:35 +00:00
// Remove informations not needed on front page for students
w . Promo = 0
2022-07-09 17:42:00 +00:00
w . Group = ""
2022-11-19 10:41:35 +00:00
w . DescriptionRaw = ""
2022-07-09 17:42:00 +00:00
response = append ( response , w )
2022-07-08 08:26:06 +00:00
}
2022-07-09 17:42:00 +00:00
}
}
c . JSON ( http . StatusOK , response )
} )
router . GET ( "/all_works" , func ( c * gin . Context ) {
var u * User
if user , ok := c . Get ( "LoggedUser" ) ; ok {
u = user . ( * User )
}
var works [ ] * OneWork
var err error
if u == nil {
2022-09-02 10:00:21 +00:00
works , err = allWorks ( fmt . Sprintf ( "WHERE shown = TRUE AND NOW() > start_availability AND promo = %d ORDER BY start_availability ASC, end_availability ASC" , currentPromo ) )
2022-07-09 17:42:00 +00:00
} else if u . IsAdmin {
works , err = allWorks ( "ORDER BY promo DESC, start_availability ASC" )
} else {
2022-09-02 10:00:21 +00:00
works , err = allWorks ( fmt . Sprintf ( "WHERE shown = TRUE AND promo = %d ORDER BY start_availability ASC, end_availability ASC" , u . Promo ) )
2022-07-09 17:42:00 +00:00
}
2022-07-08 08:26:06 +00:00
2022-07-09 17:42:00 +00:00
if err != nil {
log . Println ( "Unable to getWorks:" , err )
c . AbortWithStatusJSON ( http . StatusInternalServerError , gin . H { "errmsg" : "Impossible de récupérer la liste des travaux. Veuillez réessayer dans quelques instants." } )
return
}
var response [ ] * OneWork
if u == nil || u . IsAdmin {
response = works
} else {
for _ , w := range works {
if w . Group == "" || strings . Contains ( u . Groups , "," + w . Group + "," ) {
2022-11-19 10:41:35 +00:00
// Remove informations not needed on front page for students
w . Promo = 0
2022-07-09 17:42:00 +00:00
w . Group = ""
2022-11-19 10:41:35 +00:00
2022-07-09 17:42:00 +00:00
response = append ( response , w )
}
2022-07-08 08:26:06 +00:00
}
2022-07-09 17:42:00 +00:00
}
c . JSON ( http . StatusOK , response )
} )
}
func declareAPIAdminWorksRoutes ( router * gin . RouterGroup ) {
router . POST ( "/works" , func ( c * gin . Context ) {
2022-07-08 08:26:06 +00:00
var new Work
2022-07-09 17:42:00 +00:00
if err := c . ShouldBindJSON ( & new ) ; err != nil {
c . AbortWithStatusJSON ( http . StatusBadRequest , gin . H { "errmsg" : err . Error ( ) } )
return
2022-07-08 08:26:06 +00:00
}
if new . Promo == 0 {
new . Promo = currentPromo
}
2023-01-01 14:26:58 +00:00
work , err := NewWork ( new . IdCategory , new . Title , new . Promo , new . Group , new . Shown , new . DescriptionRaw , new . Tag , new . SubmissionURL , new . GradationRepo , new . StartAvailability , new . EndAvailability )
2022-07-09 17:42:00 +00:00
if err != nil {
log . Println ( "Unable to NewWork:" , err )
c . AbortWithStatusJSON ( http . StatusInternalServerError , gin . H { "errmsg" : "An error occurs during work creation" } )
return
}
c . JSON ( http . StatusOK , work )
} )
worksRoutes := router . Group ( "/works/:wid" )
worksRoutes . Use ( workHandler )
worksRoutes . PUT ( "" , func ( c * gin . Context ) {
current := c . MustGet ( "work" ) . ( * Work )
2022-07-08 08:26:06 +00:00
var new Work
2022-07-09 17:42:00 +00:00
if err := c . ShouldBindJSON ( & new ) ; err != nil {
c . AbortWithStatusJSON ( http . StatusBadRequest , gin . H { "errmsg" : err . Error ( ) } )
return
2022-07-08 08:26:06 +00:00
}
new . Id = current . Id
2022-07-09 17:42:00 +00:00
work , err := new . Update ( )
if err != nil {
log . Println ( "Unable to Update work:" , err )
c . AbortWithStatusJSON ( http . StatusInternalServerError , gin . H { "errmsg" : "An error occurs during work update." } )
return
}
c . JSON ( http . StatusOK , work )
} )
worksRoutes . DELETE ( "" , func ( c * gin . Context ) {
w := c . MustGet ( "work" ) . ( * Work )
_ , err := w . Delete ( )
if err != nil {
log . Println ( "Unable to Delte work:" , err )
c . AbortWithStatusJSON ( http . StatusInternalServerError , gin . H { "errmsg" : "An error occurs during work deletion." } )
return
}
c . JSON ( http . StatusOK , nil )
} )
2023-03-05 17:16:38 +00:00
worksRoutes . DELETE ( "/tests" , func ( c * gin . Context ) {
w := c . MustGet ( "work" ) . ( * Work )
err := w . stopTests ( )
if err != nil {
log . Println ( "Unable to stop tests:" , err )
c . AbortWithStatusJSON ( http . StatusInternalServerError , gin . H { "errmsg" : "An error occurs during test stop." } )
return
}
c . JSON ( http . StatusOK , true )
} )
2022-07-08 10:15:49 +00:00
// Grades related to works
2022-07-09 17:42:00 +00:00
worksRoutes . GET ( "/grades" , func ( c * gin . Context ) {
w := c . MustGet ( "work" ) . ( * Work )
2022-07-08 10:15:49 +00:00
2022-07-09 17:42:00 +00:00
grades , err := w . GetGrades ( "" )
if err != nil {
log . Printf ( "Unable to GetGrades(wid=%d): %s" , w . Id , err . Error ( ) )
c . AbortWithStatusJSON ( http . StatusInternalServerError , gin . H { "errmsg" : "An error occurs during grades retrieval." } )
return
}
2022-07-08 10:15:49 +00:00
2022-07-09 17:42:00 +00:00
c . JSON ( http . StatusOK , grades )
} )
2023-03-06 11:53:03 +00:00
worksRoutes . PATCH ( "/grades" , func ( c * gin . Context ) {
w := c . MustGet ( "work" ) . ( * Work )
// Fetch existing grades
grades , err := w . GetGrades ( "" )
if err != nil {
log . Printf ( "Unable to GetGrades(wid=%d): %s" , w . Id , err . Error ( ) )
c . AbortWithStatusJSON ( http . StatusInternalServerError , gin . H { "errmsg" : "An error occurs during grades retrieval." } )
return
}
// Create an index
known_users := map [ int64 ] bool { }
for _ , g := range grades {
known_users [ g . IdUser ] = true
}
// Fetch students list registered for this course
users , err := getFilteredUsers ( w . Promo , w . Group )
if err != nil {
log . Printf ( "Unable to getFilteredUsers(%d, %s): %s" , w . Promo , w . Group , err . Error ( ) )
c . AbortWithStatusJSON ( http . StatusInternalServerError , gin . H { "errmsg" : "An error occurs during users retrieval." } )
return
}
var toAdd [ ] WorkGrade
for _ , user := range users {
if _ , ok := known_users [ user . Id ] ; ! ok {
toAdd = append ( toAdd , WorkGrade {
IdUser : user . Id ,
Login : user . Login ,
Grade : 0 ,
Comment : "- Non rendu -" ,
} )
}
}
if len ( toAdd ) > 0 {
w . AddGrades ( toAdd )
}
c . JSON ( http . StatusOK , toAdd )
} )
2022-07-09 17:42:00 +00:00
worksRoutes . PUT ( "/grades" , func ( c * gin . Context ) {
w := c . MustGet ( "work" ) . ( * Work )
2022-07-08 10:15:49 +00:00
2022-07-09 17:42:00 +00:00
var grades [ ] WorkGrade
if err := c . ShouldBindJSON ( & grades ) ; err != nil {
c . AbortWithStatusJSON ( http . StatusBadRequest , gin . H { "errmsg" : err . Error ( ) } )
return
}
_ , err := w . DeleteGrades ( )
if err != nil {
log . Printf ( "Unable to DeleteGrades(wid=%d): %s" , w . Id , err . Error ( ) )
c . AbortWithStatusJSON ( http . StatusInternalServerError , gin . H { "errmsg" : "An error occurs during grades deletion." } )
return
}
err = w . AddGrades ( grades )
if err != nil {
log . Printf ( "Unable to AddGrades(wid=%d): %s" , w . Id , err . Error ( ) )
c . AbortWithStatusJSON ( http . StatusInternalServerError , gin . H { "errmsg" : "An error occurs during grades erasing." } )
return
}
c . JSON ( http . StatusOK , true )
} )
2023-03-05 12:17:25 +00:00
gradesRoutes := worksRoutes . Group ( "/grades/:gid" )
gradesRoutes . Use ( gradeHandler )
2023-03-07 02:19:28 +00:00
gradesRoutes . PUT ( "" , func ( c * gin . Context ) {
current := c . MustGet ( "grade" ) . ( * WorkGrade )
var new WorkGrade
if err := c . ShouldBindJSON ( & new ) ; err != nil {
c . AbortWithStatusJSON ( http . StatusBadRequest , gin . H { "errmsg" : err . Error ( ) } )
return
}
new . Id = current . Id
grade , err := new . Update ( )
if err != nil {
log . Println ( "Unable to Update grade:" , err )
c . AbortWithStatusJSON ( http . StatusInternalServerError , gin . H { "errmsg" : "An error occurs during grade update." } )
return
}
c . JSON ( http . StatusOK , grade )
} )
2023-03-05 12:17:25 +00:00
gradesRoutes . DELETE ( "" , func ( c * gin . Context ) {
g := c . MustGet ( "grade" ) . ( * WorkGrade )
g . Delete ( )
c . JSON ( http . StatusOK , true )
} )
2023-03-06 12:31:24 +00:00
gradesRoutes . GET ( "/status" , func ( c * gin . Context ) {
g := c . MustGet ( "grade" ) . ( * WorkGrade )
var u * User
if user , ok := c . Get ( "user" ) ; ok {
u = user . ( * User )
} else {
u = c . MustGet ( "LoggedUser" ) . ( * User )
}
repo , err := u . getRepositoryByWork ( g . IdWork )
if err != nil {
log . Printf ( "Unable to getRepositoryByWork(uid=%d, wid=%d): %s" , u . Id , g . IdWork , err . Error ( ) )
c . AbortWithStatusJSON ( http . StatusNotFound , gin . H { "errmsg" : "Unable to find a corresponding repository." } )
return
}
slug := strings . Split ( repo . TestsRef , "/" )
if len ( slug ) < 3 {
return
}
buildn , err := strconv . ParseInt ( slug [ 2 ] , 10 , 32 )
if err != nil {
return
}
client := drone . NewClient ( droneEndpoint , droneConfig )
build , err := client . Build ( slug [ 0 ] , slug [ 1 ] , int ( buildn ) )
if err != nil {
log . Println ( "Unable to communicate with Drone:" , err . Error ( ) )
c . AbortWithStatusJSON ( http . StatusInternalServerError , gin . H { "errmsg" : "Unable to communicate with Drone" } )
return
}
c . JSON ( http . StatusOK , build )
} )
2023-03-07 03:45:04 +00:00
gradesRoutes . GET ( "/traces/*path" , func ( c * gin . Context ) {
2023-03-05 13:20:40 +00:00
g := c . MustGet ( "grade" ) . ( * WorkGrade )
var u * User
if user , ok := c . Get ( "user" ) ; ok {
u = user . ( * User )
} else {
u = c . MustGet ( "LoggedUser" ) . ( * User )
}
repo , err := u . getRepositoryByWork ( g . IdWork )
if err != nil {
log . Printf ( "Unable to getRepositoryByWork(uid=%d, wid=%d): %s" , u . Id , g . IdWork , err . Error ( ) )
c . AbortWithStatusJSON ( http . StatusNotFound , gin . H { "errmsg" : "Unable to find a corresponding repository." } )
return
}
2023-03-07 03:45:04 +00:00
c . Redirect ( http . StatusFound , fmt . Sprintf ( "%s/%s" , droneEndpoint , repo . TestsRef ) + c . Param ( "path" ) )
2023-03-05 13:20:40 +00:00
} )
2023-03-06 12:13:04 +00:00
gradesRoutes . POST ( "/traces" , func ( c * gin . Context ) {
w := c . MustGet ( "work" ) . ( * Work )
g := c . MustGet ( "grade" ) . ( * WorkGrade )
var u * User
if user , ok := c . Get ( "user" ) ; ok {
u = user . ( * User )
} else {
u = c . MustGet ( "LoggedUser" ) . ( * User )
}
repo , err := u . getRepositoryByWork ( g . IdWork )
if err != nil {
log . Printf ( "Unable to getRepositoryByWork(uid=%d, wid=%d): %s" , u . Id , g . IdWork , err . Error ( ) )
c . AbortWithStatusJSON ( http . StatusNotFound , gin . H { "errmsg" : "Unable to find a corresponding repository." } )
return
}
TriggerTests ( c , w , repo , u )
} )
2023-03-07 03:45:04 +00:00
gradesRoutes . GET ( "/forge/*path" , func ( c * gin . Context ) {
2023-03-06 12:08:22 +00:00
g := c . MustGet ( "grade" ) . ( * WorkGrade )
var u * User
if user , ok := c . Get ( "user" ) ; ok {
u = user . ( * User )
} else {
u = c . MustGet ( "LoggedUser" ) . ( * User )
}
repo , err := u . getRepositoryByWork ( g . IdWork )
if err != nil {
log . Printf ( "Unable to getRepositoryByWork(uid=%d, wid=%d): %s" , u . Id , g . IdWork , err . Error ( ) )
c . AbortWithStatusJSON ( http . StatusNotFound , gin . H { "errmsg" : "Unable to find a corresponding repository." } )
return
}
2023-03-07 03:45:04 +00:00
c . Redirect ( http . StatusFound , strings . Replace ( strings . Replace ( repo . URI , ":" , "/" , 1 ) , "git@" , "https://" , 1 ) + c . Param ( "path" ) )
2023-03-06 12:08:22 +00:00
} )
2022-07-08 08:26:06 +00:00
}
2023-03-08 02:30:30 +00:00
type UserTraceItem struct {
2023-03-08 13:03:46 +00:00
Title string ` json:"title" `
Status string ` json:"status" `
Message string ` json:"msg,omitempty" `
2023-03-08 02:30:30 +00:00
}
2023-03-06 13:48:59 +00:00
type UserTrace struct {
2023-03-08 02:30:30 +00:00
Title string ` json:"title" `
Status string ` json:"status" `
Logs [ ] * drone . Line ` json:"logs,omitempty" `
Items [ ] UserTraceItem ` json:"items,omitempty" `
2023-03-06 13:48:59 +00:00
}
2022-07-09 17:42:00 +00:00
func declareAPIAuthWorksRoutes ( router * gin . RouterGroup ) {
worksRoutes := router . Group ( "/works/:wid" )
worksRoutes . Use ( workHandler )
2022-09-07 19:33:54 +00:00
worksRoutes . Use ( workUserAccessHandler )
2022-07-09 17:42:00 +00:00
worksRoutes . GET ( "" , func ( c * gin . Context ) {
2022-11-19 10:41:35 +00:00
c . JSON ( http . StatusOK , c . MustGet ( "work" ) . ( * Work ) )
2022-07-09 17:42:00 +00:00
} )
// Grades related to works
2023-03-06 17:46:41 +00:00
worksRoutes . GET ( "/forge/*path" , func ( c * gin . Context ) {
w := c . MustGet ( "work" ) . ( * Work )
var u * User
if user , ok := c . Get ( "user" ) ; ok {
u = user . ( * User )
} else {
u = c . MustGet ( "LoggedUser" ) . ( * User )
}
repo , err := u . getRepositoryByWork ( w . Id )
if err != nil {
log . Printf ( "Unable to getRepositoryByWork(uid=%d, wid=%d): %s" , u . Id , w . Id , err . Error ( ) )
c . AbortWithStatusJSON ( http . StatusNotFound , gin . H { "errmsg" : "Unable to find a corresponding repository." } )
return
}
c . Redirect ( http . StatusFound , strings . TrimSuffix ( strings . Replace ( strings . Replace ( repo . URI , ":" , "/" , 1 ) , "git@" , "https://" , 1 ) , ".git" ) + c . Param ( "path" ) )
} )
2022-07-09 17:42:00 +00:00
worksRoutes . GET ( "/score" , func ( c * gin . Context ) {
u := c . MustGet ( "LoggedUser" ) . ( * User )
w := c . MustGet ( "work" ) . ( * Work )
2022-07-08 08:26:06 +00:00
2022-09-07 19:33:54 +00:00
if ! u . IsAdmin && ! w . Corrected {
c . AbortWithStatusJSON ( http . StatusForbidden , gin . H { "errmsg" : "Permission denied" } )
} else if g , err := u . GetMyWorkGrade ( w ) ; err != nil && errors . Is ( err , sql . ErrNoRows ) {
2022-07-09 17:42:00 +00:00
c . AbortWithStatusJSON ( http . StatusNotFound , gin . H { "errmsg" : "Aucune note n'a été attribuée pour ce travail. Avez-vous rendu ce travail ?" } )
} else if err != nil {
log . Printf ( "Unable to GetMyWorkGrade(uid=%d;wid=%d): %s" , u . Id , w . Id , err . Error ( ) )
c . AbortWithStatusJSON ( http . StatusInternalServerError , gin . H { "errmsg" : "An error occurs during grade calculation." } )
2022-07-08 10:15:49 +00:00
} else {
2022-07-09 17:42:00 +00:00
c . JSON ( http . StatusOK , g )
2022-07-08 10:15:49 +00:00
}
2022-07-09 17:42:00 +00:00
} )
2022-09-04 09:38:10 +00:00
2023-03-06 13:48:59 +00:00
worksRoutes . GET ( "/traces" , func ( c * gin . Context ) {
u := c . MustGet ( "LoggedUser" ) . ( * User )
w := c . MustGet ( "work" ) . ( * Work )
if ! u . IsAdmin && ! w . Corrected {
c . AbortWithStatusJSON ( http . StatusForbidden , gin . H { "errmsg" : "Permission denied" } )
} else if g , err := u . GetMyWorkGrade ( w ) ; err != nil && errors . Is ( err , sql . ErrNoRows ) {
c . AbortWithStatusJSON ( http . StatusNotFound , gin . H { "errmsg" : "Aucune note n'a été attribuée pour ce travail. Avez-vous rendu ce travail ?" } )
} else if err != nil {
log . Printf ( "Unable to GetMyWorkGrade(uid=%d;wid=%d): %s" , u . Id , w . Id , err . Error ( ) )
c . AbortWithStatusJSON ( http . StatusInternalServerError , gin . H { "errmsg" : "An error occurs during gradation." } )
} else {
repo , err := u . getRepositoryByWork ( g . IdWork )
if err != nil {
log . Printf ( "Unable to getRepositoryByWork(uid=%d, wid=%d): %s" , u . Id , g . IdWork , err . Error ( ) )
c . AbortWithStatusJSON ( http . StatusNotFound , gin . H { "errmsg" : "Unable to find a corresponding repository." } )
return
}
slug := strings . Split ( repo . TestsRef , "/" )
if len ( slug ) < 3 {
return
}
buildn , err := strconv . ParseInt ( slug [ 2 ] , 10 , 32 )
if err != nil {
return
}
client := drone . NewClient ( droneEndpoint , droneConfig )
build , err := client . Build ( slug [ 0 ] , slug [ 1 ] , int ( buildn ) )
if err != nil {
log . Println ( "Unable to communicate with Drone:" , err . Error ( ) )
c . AbortWithStatusJSON ( http . StatusInternalServerError , gin . H { "errmsg" : "Unable to communicate with the gradation service" } )
return
}
var traces [ ] UserTrace
for _ , stage := range build . Stages {
for _ , step := range stage . Steps {
2023-03-07 23:05:30 +00:00
if step . Name == "TP checks" || step . Name == "Clean archive" || strings . HasPrefix ( step . Name , "Test " ) || strings . HasPrefix ( step . Name , "Has " ) || strings . HasPrefix ( step . Name , "Build " ) {
2023-03-06 13:48:59 +00:00
result , err := client . Logs ( slug [ 0 ] , slug [ 1 ] , int ( buildn ) , stage . Number , step . Number )
if err != nil {
log . Printf ( "An error occurs when retrieving logs from Drone (%s/%s/%d/%d/%d): %s" , slug [ 0 ] , slug [ 1 ] , buildn , stage . Number , step . Number , err . Error ( ) )
continue
}
keeptLogs := [ ] * drone . Line { }
2023-03-08 10:50:13 +00:00
firstCmdShown := false
for _ , line := range result {
2023-03-06 13:48:59 +00:00
// Infos about image, skip
2023-03-08 10:50:13 +00:00
if ! firstCmdShown {
if strings . HasPrefix ( line . Message , "+ " ) {
firstCmdShown = true
} else {
continue
}
2023-03-06 13:48:59 +00:00
}
if strings . HasPrefix ( line . Message , "+ export GRADE" ) {
continue
}
if strings . HasPrefix ( line . Message , "+ echo grade:" ) {
line . Message = "+ Your grade for this step is:\r\n"
}
keeptLogs = append ( keeptLogs , line )
}
traces = append ( traces , UserTrace {
Title : step . Name ,
Status : step . Status ,
Logs : keeptLogs ,
} )
2023-03-08 02:30:30 +00:00
} else if step . Name == "Check steps" {
result , err := client . Logs ( slug [ 0 ] , slug [ 1 ] , int ( buildn ) , stage . Number , step . Number )
if err != nil {
log . Printf ( "An error occurs when retrieving logs from Drone (%s/%s/%d/%d/%d): %s" , slug [ 0 ] , slug [ 1 ] , buildn , stage . Number , step . Number , err . Error ( ) )
continue
}
items := [ ] UserTraceItem { }
for _ , line := range result {
if strings . HasPrefix ( line . Message , "report:" ) {
2023-03-08 13:03:46 +00:00
tmp := strings . SplitN ( strings . TrimSpace ( line . Message ) , ":" , 4 )
if len ( tmp ) < 3 {
2023-03-08 02:30:30 +00:00
continue
}
2023-03-08 13:03:46 +00:00
uti := UserTraceItem {
2023-03-08 02:30:30 +00:00
Title : tmp [ 1 ] ,
Status : tmp [ 2 ] ,
2023-03-08 13:03:46 +00:00
}
if len ( tmp ) >= 4 {
uti . Message = tmp [ 3 ]
}
items = append ( items , uti )
2023-03-08 02:30:30 +00:00
}
}
traces = append ( traces , UserTrace {
Title : step . Name ,
Status : step . Status ,
Items : items ,
} )
2023-03-06 13:48:59 +00:00
}
}
}
if traces == nil {
c . JSON ( http . StatusOK , [ ] interface { } { } )
} else {
c . JSON ( http . StatusOK , traces )
}
}
} )
2022-09-04 09:38:10 +00:00
declareAPIAuthRepositoriesRoutes ( worksRoutes )
2022-09-05 02:40:49 +00:00
declareAPIWorkSubmissionsRoutes ( worksRoutes )
2022-07-09 17:42:00 +00:00
}
func workHandler ( c * gin . Context ) {
if wid , err := strconv . Atoi ( string ( c . Param ( "wid" ) ) ) ; err != nil {
c . AbortWithStatusJSON ( http . StatusBadRequest , gin . H { "errmsg" : "Bad work identifier." } )
return
2023-03-05 02:57:37 +00:00
} else if work , err := getWork ( int64 ( wid ) ) ; err != nil {
2022-07-09 17:42:00 +00:00
c . AbortWithStatusJSON ( http . StatusNotFound , gin . H { "errmsg" : "Work not found." } )
return
} else {
c . Set ( "work" , work )
c . Next ( )
2022-07-08 10:15:49 +00:00
}
}
2022-11-19 10:41:35 +00:00
func ( w * Work ) checkUserAccessToWork ( u * User ) bool {
return u . IsAdmin || ( u . Promo == w . Promo && w . Shown && ( w . Group == "" || strings . Contains ( u . Groups , "," + w . Group + "," ) ) )
}
2022-09-07 19:33:54 +00:00
func workUserAccessHandler ( c * gin . Context ) {
u := c . MustGet ( "LoggedUser" ) . ( * User )
w := c . MustGet ( "work" ) . ( * Work )
2022-11-19 10:41:35 +00:00
if ! w . checkUserAccessToWork ( u ) {
2022-09-07 19:33:54 +00:00
c . AbortWithStatusJSON ( http . StatusNotFound , gin . H { "errmsg" : "Work not found." } )
return
}
2022-11-19 10:41:35 +00:00
if ! u . IsAdmin && w . StartAvailability . After ( time . Now ( ) ) {
c . AbortWithStatusJSON ( http . StatusForbidden , gin . H { "errmsg" : "Not accessible yet" } )
return
}
c . Next ( )
2022-09-07 19:33:54 +00:00
}
2022-07-08 08:26:06 +00:00
type OneWork struct {
Kind string ` json:"kind" `
Id int64 ` json:"id" `
2022-11-20 14:28:27 +00:00
IdCategory int64 ` json:"id_category" `
2022-07-08 08:26:06 +00:00
Title string ` json:"title" `
Promo uint ` json:"promo" `
Group string ` json:"group" `
Shown bool ` json:"shown" `
Direct * int64 ` json:"direct" `
SubmissionURL * string ` json:"submission_url" `
Corrected bool ` json:"corrected" `
StartAvailability time . Time ` json:"start_availability" `
EndAvailability time . Time ` json:"end_availability" `
}
2022-07-09 17:42:00 +00:00
func allWorks ( cnd string , param ... interface { } ) ( items [ ] * OneWork , err error ) {
2022-11-20 14:28:27 +00:00
if rows , errr := DBQuery ( "SELECT kind, id, id_category, title, promo, grp, shown, direct, submission_url, corrected, start_availability, end_availability FROM all_works " + cnd , param ... ) ; errr != nil {
2022-07-08 08:26:06 +00:00
return nil , errr
} else {
defer rows . Close ( )
for rows . Next ( ) {
var w OneWork
2022-11-20 14:28:27 +00:00
if err = rows . Scan ( & w . Kind , & w . Id , & w . IdCategory , & w . Title , & w . Promo , & w . Group , & w . Shown , & w . Direct , & w . SubmissionURL , & w . Corrected , & w . StartAvailability , & w . EndAvailability ) ; err != nil {
2022-07-08 08:26:06 +00:00
return
}
2022-07-09 17:42:00 +00:00
items = append ( items , & w )
2022-07-08 08:26:06 +00:00
}
if err = rows . Err ( ) ; err != nil {
return
}
return
}
}
type Work struct {
Id int64 ` json:"id" `
2022-11-20 14:28:27 +00:00
IdCategory int64 ` json:"id_category" `
2022-07-08 08:26:06 +00:00
Title string ` json:"title" `
2022-11-19 10:41:35 +00:00
Promo uint ` json:"promo,omitempty" `
Group string ` json:"group,omitempty" `
2022-07-08 08:26:06 +00:00
Shown bool ` json:"shown" `
2022-11-19 10:41:35 +00:00
Description string ` json:"description,omitempty" `
2022-09-04 15:59:36 +00:00
DescriptionRaw string ` json:"descr_raw,omitempty" `
2022-11-19 10:41:35 +00:00
Tag string ` json:"tag,omitempty" `
SubmissionURL * string ` json:"submission_url,omitempty" `
2023-01-01 14:26:58 +00:00
GradationRepo * string ` json:"gradation_repo" `
2022-11-19 10:41:35 +00:00
Corrected bool ` json:"corrected,omitempty" `
2022-07-08 08:26:06 +00:00
StartAvailability time . Time ` json:"start_availability" `
EndAvailability time . Time ` json:"end_availability" `
}
2022-07-09 17:42:00 +00:00
func getWorks ( cnd string , param ... interface { } ) ( items [ ] * Work , err error ) {
2023-01-01 14:26:58 +00:00
if rows , errr := DBQuery ( "SELECT id_work, id_category, title, promo, grp, shown, description, tag, submission_url, gradation_repo, corrected, start_availability, end_availability FROM works " + cnd , param ... ) ; errr != nil {
2022-07-08 08:26:06 +00:00
return nil , errr
} else {
defer rows . Close ( )
for rows . Next ( ) {
var w Work
2023-01-01 14:26:58 +00:00
if err = rows . Scan ( & w . Id , & w . IdCategory , & w . Title , & w . Promo , & w . Group , & w . Shown , & w . DescriptionRaw , & w . Tag , & w . SubmissionURL , & w . GradationRepo , & w . Corrected , & w . StartAvailability , & w . EndAvailability ) ; err != nil {
2022-07-08 08:26:06 +00:00
return
}
2022-07-09 17:42:00 +00:00
items = append ( items , & w )
2022-07-08 08:26:06 +00:00
}
if err = rows . Err ( ) ; err != nil {
return
}
return
}
}
2023-03-05 02:57:37 +00:00
func getWork ( id int64 ) ( w * Work , err error ) {
2022-07-09 17:42:00 +00:00
w = new ( Work )
2023-01-01 14:26:58 +00:00
err = DBQueryRow ( "SELECT id_work, id_category, title, promo, grp, shown, description, tag, submission_url, gradation_repo, corrected, start_availability, end_availability FROM works WHERE id_work=?" , id ) . Scan ( & w . Id , & w . IdCategory , & w . Title , & w . Promo , & w . Group , & w . Shown , & w . DescriptionRaw , & w . Tag , & w . SubmissionURL , & w . GradationRepo , & w . Corrected , & w . StartAvailability , & w . EndAvailability )
2022-09-04 15:59:36 +00:00
w . Description = string ( blackfriday . Run ( [ ] byte ( w . DescriptionRaw ) ) )
2022-07-08 08:26:06 +00:00
return
}
2023-01-01 14:26:58 +00:00
func NewWork ( id_category int64 , title string , promo uint , group string , shown bool , description string , tag string , submissionurl * string , gradation_repo * string , startAvailability time . Time , endAvailability time . Time ) ( * Work , error ) {
if res , err := DBExec ( "INSERT INTO works (id_category, title, promo, grp, shown, description, tag, submission_url, gradation_repo, start_availability, end_availability) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)" , id_category , title , promo , group , shown , description , tag , submissionurl , gradation_repo , startAvailability , endAvailability ) ; err != nil {
2022-07-08 08:26:06 +00:00
return nil , err
} else if wid , err := res . LastInsertId ( ) ; err != nil {
return nil , err
} else {
2023-01-01 14:26:58 +00:00
return & Work { wid , id_category , title , promo , group , shown , description , description , tag , submissionurl , gradation_repo , false , startAvailability , endAvailability } , nil
2022-07-08 08:26:06 +00:00
}
}
func ( w * Work ) Update ( ) ( * Work , error ) {
2023-01-01 14:26:58 +00:00
if _ , err := DBExec ( "UPDATE works SET id_category = ?, title = ?, promo = ?, grp = ?, shown = ?, description = ?, tag = ?, submission_url = ?, gradation_repo = ?, corrected = ?, start_availability = ?, end_availability = ? WHERE id_work = ?" , w . IdCategory , w . Title , w . Promo , w . Group , w . Shown , w . DescriptionRaw , w . Tag , w . SubmissionURL , w . GradationRepo , w . Corrected , w . StartAvailability , w . EndAvailability , w . Id ) ; err != nil {
2022-07-08 08:26:06 +00:00
return nil , err
} else {
2022-09-16 10:14:26 +00:00
w . Description = string ( blackfriday . Run ( [ ] byte ( w . DescriptionRaw ) ) )
2022-07-08 08:26:06 +00:00
return w , err
}
}
func ( w * Work ) Delete ( ) ( int64 , error ) {
if res , err := DBExec ( "DELETE FROM works WHERE id_work = ?" , w . Id ) ; err != nil {
return 0 , err
} else if nb , err := res . RowsAffected ( ) ; err != nil {
return 0 , err
} else {
return nb , err
}
}
func ClearWorks ( ) ( int64 , error ) {
if res , err := DBExec ( "DELETE FROM works" ) ; err != nil {
return 0 , err
} else if nb , err := res . RowsAffected ( ) ; err != nil {
return 0 , err
} else {
return nb , err
}
}
2022-07-08 10:15:49 +00:00
type WorkGrade struct {
Id int64 ` json:"id" `
2022-09-04 15:59:36 +00:00
Login string ` json:"login,omitempty" `
IdUser int64 ` json:"id_user,omitempty" `
IdWork int64 ` json:"id_work,omitempty" `
2022-07-08 10:15:49 +00:00
Date time . Time ` json:"date" `
Grade float64 ` json:"score" `
2022-09-04 15:59:36 +00:00
Comment string ` json:"comment,omitempty" `
2022-07-08 10:15:49 +00:00
}
2023-03-07 02:19:28 +00:00
func ( g * WorkGrade ) Update ( ) ( * WorkGrade , error ) {
if _ , err := DBExec ( "UPDATE user_work_grades SET id_user = ?, id_work = ?, date = ?, grade = ?, comment = ? WHERE id_gradation = ?" , g . IdUser , g . IdWork , g . Date , g . Grade , g . Comment , g . Id ) ; err != nil {
return nil , err
} else {
return g , err
}
}
2023-03-05 12:17:25 +00:00
func ( g WorkGrade ) Delete ( ) ( int64 , error ) {
if res , err := DBExec ( "DELETE FROM user_work_grades WHERE id_gradation = ?" , g . Id ) ; err != nil {
return 0 , err
} else if nb , err := res . RowsAffected ( ) ; err != nil {
return 0 , err
} else {
return nb , err
}
}
2022-07-08 10:15:49 +00:00
func ( w * Work ) GetGrades ( cnd string , param ... interface { } ) ( grades [ ] WorkGrade , err error ) {
param = append ( [ ] interface { } { w . Id } , param ... )
if rows , errr := DBQuery ( "SELECT G.id_gradation, G.id_user, U.login, G.id_work, G.date, G.grade, G.comment FROM user_work_grades G INNER JOIN users U ON U.id_user = G.id_user WHERE id_work = ? " + cnd , param ... ) ; errr != nil {
return nil , errr
} else {
defer rows . Close ( )
for rows . Next ( ) {
var g WorkGrade
if err = rows . Scan ( & g . Id , & g . IdUser , & g . Login , & g . IdWork , & g . Date , & g . Grade , & g . Comment ) ; err != nil {
return
}
grades = append ( grades , g )
}
if err = rows . Err ( ) ; err != nil {
return
}
return
}
}
2023-03-05 12:17:25 +00:00
func ( w * Work ) GetGrade ( id int64 ) ( g * WorkGrade , err error ) {
g = new ( WorkGrade )
err = DBQueryRow ( "SELECT G.id_gradation, G.id_user, U.login, G.id_work, G.date, G.grade, G.comment FROM user_work_grades G INNER JOIN users U ON U.id_user = G.id_user WHERE id_work = ? AND id_gradation = ?" , w . Id , id ) . Scan ( & g . Id , & g . IdUser , & g . Login , & g . IdWork , & g . Date , & g . Grade , & g . Comment )
return
}
2022-07-08 10:15:49 +00:00
func ( u * User ) GetMyWorkGrade ( w * Work ) ( g WorkGrade , err error ) {
err = DBQueryRow ( "SELECT id_gradation, id_user, id_work, date, grade, comment FROM user_work_grades WHERE id_work = ? AND id_user = ? ORDER BY date DESC LIMIT 1" , w . Id , u . Id ) . Scan ( & g . Id , & g . IdUser , & g . IdWork , & g . Date , & g . Grade , & g . Comment )
return
}
2023-03-05 02:57:58 +00:00
func ( w * Work ) AddGrade ( grade WorkGrade ) error {
2023-03-06 11:31:22 +00:00
u := User { Id : grade . IdUser }
// Search a previous record
g , err := u . GetMyWorkGrade ( w )
if err != nil && err != sql . ErrNoRows {
return err
} else if err == nil {
_ , err = g . Delete ( )
if err != nil {
return err
}
}
2023-03-05 02:57:58 +00:00
return w . AddGrades ( [ ] WorkGrade { grade } )
}
2022-07-08 10:15:49 +00:00
func ( w * Work ) AddGrades ( grades [ ] WorkGrade ) error {
var zerotime time . Time
for i , g := range grades {
if g . IdUser == 0 {
if u , err := getUserByLogin ( g . Login ) ; err != nil {
return fmt . Errorf ( "user %q: %w" , g . Login , err )
} else {
grades [ i ] . IdUser = u . Id
}
}
if zerotime == g . Date {
grades [ i ] . Date = time . Now ( )
}
}
for _ , g := range grades {
if _ , err := DBExec ( "INSERT INTO user_work_grades (id_user, id_work, date, grade, comment) VALUES (?, ?, ?, ?, ?)" , g . IdUser , w . Id , g . Date , g . Grade , g . Comment ) ; err != nil {
return err
}
}
return nil
}
func ( w * Work ) DeleteGrades ( ) ( int64 , error ) {
if res , err := DBExec ( "DELETE FROM user_work_grades WHERE id_work = ?" , w . Id ) ; err != nil {
return 0 , err
} else if nb , err := res . RowsAffected ( ) ; err != nil {
return 0 , err
} else {
return nb , err
}
}