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"
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 + "," ) {
w . Group = ""
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 + "," ) {
w . Group = ""
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
}
2022-09-04 16:03:51 +00:00
work , err := NewWork ( new . Title , new . Promo , new . Group , new . Shown , new . DescriptionRaw , new . Tag , new . SubmissionURL , 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 )
} )
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 )
} )
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 )
} )
2022-07-08 08:26:06 +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 ) {
u := c . MustGet ( "LoggedUser" ) . ( * User )
w := c . MustGet ( "work" ) . ( * Work )
if u . IsAdmin {
c . JSON ( http . StatusOK , w )
} else if w . Shown && w . StartAvailability . Before ( time . Now ( ) ) && ( w . Group == "" || strings . Contains ( u . Groups , "," + w . Group + "," ) ) {
c . JSON ( http . StatusOK , w )
2022-07-08 08:26:06 +00:00
} else {
2022-07-09 17:42:00 +00:00
c . AbortWithStatusJSON ( http . StatusForbidden , gin . H { "errmsg" : "Permission denied" } )
2022-07-08 08:26:06 +00:00
}
2022-07-09 17:42:00 +00:00
} )
// Grades related to works
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
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
} else if work , err := getWork ( wid ) ; err != nil {
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-09-07 19:33:54 +00:00
func workUserAccessHandler ( c * gin . Context ) {
u := c . MustGet ( "LoggedUser" ) . ( * User )
w := c . MustGet ( "work" ) . ( * Work )
if u . IsAdmin {
c . Next ( )
} else if w . Shown && ( w . Group == "" || strings . Contains ( u . Groups , "," + w . Group + "," ) ) {
c . Next ( )
} else {
c . AbortWithStatusJSON ( http . StatusNotFound , gin . H { "errmsg" : "Work not found." } )
return
}
}
2022-07-08 08:26:06 +00:00
type OneWork struct {
Kind string ` json:"kind" `
Id int64 ` json:"id" `
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-09-04 16:03:51 +00:00
if rows , errr := DBQuery ( "SELECT kind, id, 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-09-04 16:03:51 +00:00
if err = rows . Scan ( & w . Kind , & w . Id , & 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" `
Title string ` json:"title" `
Promo uint ` json:"promo" `
Group string ` json:"group" `
Shown bool ` json:"shown" `
2022-09-04 15:59:36 +00:00
Description string ` json:"description" `
DescriptionRaw string ` json:"descr_raw,omitempty" `
2022-09-04 16:03:51 +00:00
Tag string ` json:"tag" `
2022-07-08 08:26:06 +00:00
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 getWorks ( cnd string , param ... interface { } ) ( items [ ] * Work , err error ) {
2022-09-04 16:03:51 +00:00
if rows , errr := DBQuery ( "SELECT id_work, title, promo, grp, shown, description, tag, submission_url, 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
2022-09-04 16:03:51 +00:00
if err = rows . Scan ( & w . Id , & w . Title , & w . Promo , & w . Group , & w . Shown , & w . DescriptionRaw , & w . Tag , & 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
}
}
2022-07-09 17:42:00 +00:00
func getWork ( id int ) ( w * Work , err error ) {
w = new ( Work )
2022-09-04 16:03:51 +00:00
err = DBQueryRow ( "SELECT id_work, title, promo, grp, shown, description, tag, submission_url, corrected, start_availability, end_availability FROM works WHERE id_work=?" , id ) . Scan ( & w . Id , & w . Title , & w . Promo , & w . Group , & w . Shown , & w . DescriptionRaw , & w . Tag , & w . SubmissionURL , & 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
}
2022-09-04 16:03:51 +00:00
func NewWork ( title string , promo uint , group string , shown bool , description string , tag string , submissionurl * string , startAvailability time . Time , endAvailability time . Time ) ( * Work , error ) {
if res , err := DBExec ( "INSERT INTO works (title, promo, grp, shown, description, tag, submission_url, start_availability, end_availability) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)" , title , promo , group , shown , description , tag , submissionurl , 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 {
2022-09-04 16:03:51 +00:00
return & Work { wid , title , promo , group , shown , description , description , tag , submissionurl , false , startAvailability , endAvailability } , nil
2022-07-08 08:26:06 +00:00
}
}
func ( w * Work ) Update ( ) ( * Work , error ) {
2022-09-04 16:03:51 +00:00
if _ , err := DBExec ( "UPDATE works SET title = ?, promo = ?, grp = ?, shown = ?, description = ?, tag = ?, submission_url = ?, corrected = ?, start_availability = ?, end_availability = ? WHERE id_work = ?" , w . Title , w . Promo , w . Group , w . Shown , w . DescriptionRaw , w . Tag , w . SubmissionURL , w . Corrected , w . StartAvailability , w . EndAvailability , w . Id ) ; err != nil {
2022-07-08 08:26:06 +00:00
return nil , err
} else {
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
}
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
}
}
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
}
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
}
}