2022-07-08 08:26:06 +00:00
package main
import (
2022-07-08 10:15:49 +00:00
"database/sql"
2022-07-08 08:26:06 +00:00
"encoding/json"
2022-07-08 10:15:49 +00:00
"errors"
2022-07-08 08:26:06 +00:00
"fmt"
2022-07-08 10:15:49 +00:00
"net/http"
2022-07-08 08:26:06 +00:00
"strconv"
"strings"
"time"
"github.com/julienschmidt/httprouter"
)
func init ( ) {
router . GET ( "/api/works" , apiAuthHandler (
func ( u * User , _ httprouter . Params , _ [ ] byte ) HTTPResponse {
if u == nil {
2022-07-11 16:28:19 +00:00
return formatApiResponse ( getWorks ( fmt . Sprintf ( "WHERE shown = TRUE AND NOW() > start_availability AND promo = %d ORDER BY start_availability ASC" , currentPromo ) ) )
2022-07-08 08:26:06 +00:00
} else if u . IsAdmin {
return formatApiResponse ( getWorks ( "ORDER BY promo DESC, start_availability ASC" ) )
} else {
2022-07-11 16:28:19 +00:00
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
if err != nil {
return APIErrorResponse { err : err }
}
var response [ ] Work
for _ , w := range works {
if w . Group == "" || strings . Contains ( u . Groups , "," + w . Group + "," ) {
2022-07-09 18:52:22 +00:00
w . Group = ""
2022-07-08 08:26:06 +00:00
response = append ( response , w )
}
}
return formatApiResponse ( response , nil )
}
} ) )
router . GET ( "/api/all_works" , apiAuthHandler (
func ( u * User , _ httprouter . Params , _ [ ] byte ) HTTPResponse {
if u == nil {
2022-07-11 16:28:19 +00:00
return formatApiResponse ( allWorks ( fmt . Sprintf ( "WHERE (shown = TRUE OR direct IS NOT NULL) AND NOW() > start_availability AND promo = %d ORDER BY start_availability ASC, end_availability ASC" , currentPromo ) ) )
2022-07-08 08:26:06 +00:00
} else if u . IsAdmin {
return formatApiResponse ( allWorks ( "ORDER BY promo DESC, start_availability ASC" ) )
} else {
2022-07-11 16:28:19 +00:00
works , err := allWorks ( fmt . Sprintf ( "WHERE (shown = TRUE OR direct IS NOT NULL) AND promo = %d ORDER BY start_availability ASC, end_availability ASC" , u . Promo ) )
2022-07-08 08:26:06 +00:00
if err != nil {
return APIErrorResponse { err : err }
}
var response [ ] OneWork
for _ , w := range works {
if w . Group == "" || strings . Contains ( u . Groups , "," + w . Group + "," ) {
2022-07-09 18:52:22 +00:00
w . Group = ""
2022-07-08 08:26:06 +00:00
response = append ( response , w )
}
}
return formatApiResponse ( response , nil )
}
} ) )
router . POST ( "/api/works" , apiHandler ( func ( _ httprouter . Params , body [ ] byte ) HTTPResponse {
var new Work
if err := json . Unmarshal ( body , & new ) ; err != nil {
return APIErrorResponse { err : err }
}
if new . Promo == 0 {
new . Promo = currentPromo
}
return formatApiResponse ( NewWork ( new . Title , new . Promo , new . Group , new . Shown , new . SubmissionURL , new . StartAvailability , new . EndAvailability ) )
} , adminRestricted ) )
2022-07-11 16:33:57 +00:00
router . GET ( "/api/works/:wid" , apiAuthHandler ( workAuthHandler (
func ( w Work , u * User , _ [ ] byte ) HTTPResponse {
if u . IsAdmin {
return APIResponse { w }
} else if w . Shown && w . StartAvailability . Before ( time . Now ( ) ) && ( w . Group == "" || strings . Contains ( u . Groups , "," + w . Group + "," ) ) {
return APIResponse { w }
} else {
return APIErrorResponse { status : http . StatusForbidden , err : fmt . Errorf ( "Permission denied" ) }
}
} ) , loggedUser ) )
2022-07-08 08:26:06 +00:00
router . PUT ( "/api/works/:wid" , apiHandler ( workHandler ( func ( current Work , body [ ] byte ) HTTPResponse {
var new Work
if err := json . Unmarshal ( body , & new ) ; err != nil {
return APIErrorResponse { err : err }
}
new . Id = current . Id
return formatApiResponse ( new . Update ( ) )
} ) , adminRestricted ) )
router . DELETE ( "/api/works/:wid" , apiHandler ( workHandler (
func ( w Work , _ [ ] byte ) HTTPResponse {
return formatApiResponse ( w . Delete ( ) )
} ) , adminRestricted ) )
2022-07-08 10:15:49 +00:00
// Grades related to works
router . GET ( "/api/works/:wid/grades" , apiHandler ( workHandler (
func ( w Work , _ [ ] byte ) HTTPResponse {
return formatApiResponse ( w . GetGrades ( "" ) )
} ) , adminRestricted ) )
router . PUT ( "/api/works/:wid/grades" , apiHandler ( workHandler (
func ( w Work , body [ ] byte ) HTTPResponse {
_ , err := w . DeleteGrades ( )
if err != nil {
return APIErrorResponse { err : err }
}
var grades [ ] WorkGrade
if err := json . Unmarshal ( body , & grades ) ; err != nil {
return APIErrorResponse { err : err }
}
err = w . AddGrades ( grades )
if err != nil {
return APIErrorResponse { err : err }
}
return APIResponse { true }
} ) , adminRestricted ) )
router . GET ( "/api/works/:wid/score" , apiAuthHandler ( workAuthHandler (
func ( w Work , u * User , _ [ ] byte ) HTTPResponse {
if g , err := u . GetMyWorkGrade ( & w ) ; err != nil && errors . Is ( err , sql . ErrNoRows ) {
return APIErrorResponse { status : http . StatusNotFound , err : fmt . Errorf ( "Aucune note n'a été attribuée pour ce travail. Avez-vous rendu ce travail ?" ) }
} else if err != nil {
return APIErrorResponse { err : err }
} else {
return APIResponse { g }
}
} ) , loggedUser ) )
2022-07-08 08:26:06 +00:00
}
func workHandler ( f func ( Work , [ ] byte ) HTTPResponse ) func ( httprouter . Params , [ ] byte ) HTTPResponse {
return func ( ps httprouter . Params , body [ ] byte ) HTTPResponse {
if wid , err := strconv . Atoi ( string ( ps . ByName ( "wid" ) ) ) ; err != nil {
return APIErrorResponse { err : err }
} else if work , err := getWork ( wid ) ; err != nil {
return APIErrorResponse { err : err }
} else {
return f ( work , body )
}
}
}
2022-07-08 10:15:49 +00:00
func workAuthHandler ( f func ( Work , * User , [ ] byte ) HTTPResponse ) func ( * User , httprouter . Params , [ ] byte ) HTTPResponse {
return func ( u * User , ps httprouter . Params , body [ ] byte ) HTTPResponse {
if wid , err := strconv . Atoi ( string ( ps . ByName ( "wid" ) ) ) ; err != nil {
return APIErrorResponse { err : err }
} else if work , err := getWork ( wid ) ; err != nil {
return APIErrorResponse { err : err }
} else {
return f ( work , u , body )
}
}
}
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" `
}
func allWorks ( cnd string , param ... interface { } ) ( items [ ] OneWork , err error ) {
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 {
return nil , errr
} else {
defer rows . Close ( )
for rows . Next ( ) {
var w OneWork
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 {
return
}
items = append ( items , w )
}
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" `
SubmissionURL * string ` json:"submission_url" `
Corrected bool ` json:"corrected" `
StartAvailability time . Time ` json:"start_availability" `
EndAvailability time . Time ` json:"end_availability" `
}
func getWorks ( cnd string , param ... interface { } ) ( items [ ] Work , err error ) {
if rows , errr := DBQuery ( "SELECT id_work, title, promo, grp, shown, submission_url, corrected, start_availability, end_availability FROM works " + cnd , param ... ) ; errr != nil {
return nil , errr
} else {
defer rows . Close ( )
for rows . Next ( ) {
var w Work
if err = rows . Scan ( & w . Id , & w . Title , & w . Promo , & w . Group , & w . Shown , & w . SubmissionURL , & w . Corrected , & w . StartAvailability , & w . EndAvailability ) ; err != nil {
return
}
items = append ( items , w )
}
if err = rows . Err ( ) ; err != nil {
return
}
return
}
}
func getWork ( id int ) ( w Work , err error ) {
err = DBQueryRow ( "SELECT id_work, title, promo, grp, shown, 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 . SubmissionURL , & w . Corrected , & w . StartAvailability , & w . EndAvailability )
return
}
func NewWork ( title string , promo uint , group string , shown bool , submissionurl * string , startAvailability time . Time , endAvailability time . Time ) ( * Work , error ) {
if res , err := DBExec ( "INSERT INTO works (title, promo, grp, shown, submission_url, start_availability, end_availability) VALUES (?, ?, ?, ?, ?, ?, ?)" , title , promo , group , shown , submissionurl , startAvailability , endAvailability ) ; err != nil {
return nil , err
} else if wid , err := res . LastInsertId ( ) ; err != nil {
return nil , err
} else {
return & Work { wid , title , promo , group , shown , submissionurl , false , startAvailability , endAvailability } , nil
}
}
func ( w * Work ) Update ( ) ( * Work , error ) {
if _ , err := DBExec ( "UPDATE works SET title = ?, promo = ?, grp = ?, shown = ?, submission_url = ?, corrected = ?, start_availability = ?, end_availability = ? WHERE id_work = ?" , w . Title , w . Promo , w . Group , w . Shown , w . SubmissionURL , w . Corrected , w . StartAvailability , w . EndAvailability , w . Id ) ; err != nil {
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" `
Login string ` json:"login,omit_empty" `
IdUser int64 ` json:"id_user,omit_empty" `
IdWork int64 ` json:"id_work,omit_empty" `
Date time . Time ` json:"date" `
Grade float64 ` json:"score" `
Comment string ` json:"comment,omit_empty" `
}
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
}
}