2016-10-11 22:18:36 +00:00
package main
import (
"bufio"
"encoding/csv"
2017-10-27 21:40:33 +00:00
"encoding/json"
2016-10-11 22:18:36 +00:00
"flag"
"fmt"
"io/ioutil"
"log"
"net/http"
"os"
"path"
"path/filepath"
2017-10-27 21:40:33 +00:00
"strings"
2016-10-11 22:18:36 +00:00
"time"
)
var students [ ] [ ] string
var rendusDir string
var title string
func Serve ( w http . ResponseWriter , r * http . Request ) {
log . Printf ( "Handling %s request from %s: %s [%s]\n" , r . Method , r . RemoteAddr , r . URL . Path , r . UserAgent ( ) )
w . Header ( ) . Set ( "Content-Type" , "text/html" )
w . Write ( [ ] byte ( ` < ! DOCTYPE html >
< html >
< head >
< title > ` + title + ` < / title >
< meta http - equiv = "Content-Type" content = "text/html; charset=UTF-8" >
2022-08-15 08:12:36 +00:00
< meta name = "robots" content = "noindex, nofollow" >
2019-11-02 15:31:13 +00:00
< link rel = "stylesheet" href = "https://stackpath.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css" integrity = "sha384-HSMxcRTRxnN+Bdg0JdbxYKrThecOKuH5zCYotlSAcp1+c8xmyTe9GYg1l9a69psu" crossorigin = "anonymous" >
2017-10-27 21:40:33 +00:00
< style > . table > tbody > tr > th , . table > tbody > tr > td { vertical - align : middle ; }
td . danger { text - align : center ; }
2021-09-24 07:54:55 +00:00
. hash { max - width : 150 px ; overflow : hidden ; white - space : nowrap ; text - overflow : ellipsis ; display : inline - block ; }
2017-10-27 21:40:33 +00:00
< / style >
< script type = "text/javascript" >
function disp ( rendus ) {
Object . keys ( rendus ) . map ( function ( login ) {
student = rendus [ login ] ;
var row = document . createElement ( "tr" ) ;
var col = document . createElement ( "th" ) ;
2021-09-24 07:55:14 +00:00
col . id = login ;
2017-10-27 21:40:33 +00:00
col . innerHTML = login ;
row . appendChild ( col ) ;
Object . keys ( student ) . map ( function ( wn ) {
work = student [ wn ] ;
if ( work ) {
var col = document . createElement ( "td" ) ;
col . className = "success" ;
2021-09-24 07:54:55 +00:00
col . innerHTML = ( new Intl . DateTimeFormat ( ' default ' , { weekday : ' short ' , month : ' short ' , day : ' numeric ' , hour : ' numeric ' , minute : ' numeric ' , second : ' numeric ' } ) . format ( new Date ( work [ "date" ] ) ) ) + ' < br > < span class = "hash" title = "' + work[" hash "] + '" > ' + work [ "hash" ] + ' < / span > ' ;
2017-10-27 21:40:33 +00:00
row . appendChild ( col ) ;
} else {
var col = document . createElement ( "td" ) ;
col . className = "danger" ;
col . innerHTML = "<span class=\"glyphicon glyphicon-remove\" aria-hidden=\"true\"></span>" ;
row . appendChild ( col ) ;
}
} ) ;
document . getElementById ( "students" ) . appendChild ( row ) ;
} ) ;
Object . keys ( student ) . map ( function ( wn ) {
var col = document . createElement ( "th" ) ;
col . innerHTML = wn ;
document . getElementById ( "head" ) . appendChild ( col ) ;
} ) ;
}
2019-11-02 23:35:27 +00:00
function disp_std ( rendus , login ) {
Object . keys ( rendus ) . map ( function ( label ) {
var work = rendus [ label ] ;
var row = document . createElement ( "tr" ) ;
var col = document . createElement ( "th" ) ;
col . innerHTML = label ;
row . appendChild ( col ) ;
if ( work ) {
var col = document . createElement ( "td" ) ;
col . className = "success" ;
2021-09-24 07:54:55 +00:00
col . innerHTML = ( new Intl . DateTimeFormat ( ' default ' , { weekday : ' long ' , month : ' long ' , day : ' numeric ' , year : ' numeric ' , hour : ' numeric ' , minute : ' numeric ' , second : ' numeric ' } ) . format ( new Date ( work [ "date" ] ) ) ) + ' < br > ' + work [ "hash" ] ;
2019-11-02 23:35:27 +00:00
row . appendChild ( col ) ;
} else {
var col = document . createElement ( "td" ) ;
col . className = "danger" ;
col . innerHTML = "<span class=\"glyphicon glyphicon-remove\" aria-hidden=\"true\"></span>" ;
row . appendChild ( col ) ;
}
document . getElementById ( "students" ) . appendChild ( row ) ;
} ) ;
var col = document . createElement ( "th" ) ;
col . innerHTML = login ;
document . getElementById ( "head" ) . appendChild ( col ) ;
}
2017-10-27 21:40:33 +00:00
< / script >
2016-10-11 22:18:36 +00:00
< / head >
< body class = "container" >
< h1 > ` + title + ` < / h1 >
2017-10-27 21:40:33 +00:00
< table class = "table table-striped table-hover table-condensed" >
< thead >
< tr id = "head" > < th > < / th > < / tr >
< / thead >
< tbody id = "students" >
< / tbody >
< / table >
2018-10-17 14:27:37 +00:00
< script type = "text/javascript" >
2019-11-02 23:35:27 +00:00
if ( window . location . pathname [ window . location . pathname . length - 1 ] != "/" )
fetch ( window . location . pathname . replace ( ".html" , "" ) + ".json" )
. then ( function ( response ) {
return response . json ( ) ;
} )
. then ( function ( submissions ) {
var spl = window . location . pathname . split ( "/" )
disp_std ( submissions , spl [ spl . length - 1 ] ) ;
} ) ;
else
fetch ( ' rendus . json ' ) // You can also fetch login_x.json
. then ( function ( response ) {
return response . json ( ) ;
} )
. then ( function ( submissions ) {
disp ( submissions ) ;
} ) ;
2018-10-17 14:27:37 +00:00
< / script >
2017-10-27 21:40:33 +00:00
< / body >
< / html >
` ) )
}
func genStudents ( ) map [ string ] map [ string ] * Submission {
ret := map [ string ] map [ string ] * Submission { }
for _ , student := range students {
login := student [ 2 ]
ret [ login ] = genStudent ( login )
}
return ret
}
func ServeJSON ( w http . ResponseWriter , r * http . Request ) {
2018-10-17 14:30:34 +00:00
if addr := r . Header . Get ( "X-Forwarded-For" ) ; addr != "" {
r . RemoteAddr = addr
}
2017-10-27 21:40:33 +00:00
log . Printf ( "Handling %s request from %s: %s [%s]\n" , r . Method , r . RemoteAddr , r . URL . Path , r . UserAgent ( ) )
2018-10-17 14:27:37 +00:00
w . Header ( ) . Set ( "Content-Type" , "application/json" )
2017-10-27 21:40:33 +00:00
if j , err := json . Marshal ( genStudents ( ) ) ; err != nil {
http . Error ( w , fmt . Sprintf ( "{errmsg:%q}" , err ) , http . StatusInternalServerError )
} else {
w . WriteHeader ( http . StatusOK )
w . Write ( j )
}
}
type Submission struct {
Date time . Time ` json:"date" `
Hash string ` json:"hash" `
}
func genStudent ( login string ) map [ string ] * Submission {
ret := map [ string ] * Submission { }
2016-10-11 22:18:36 +00:00
if dirs , err := ioutil . ReadDir ( rendusDir ) ; err == nil {
for _ , dir := range dirs {
if dir . IsDir ( ) {
2017-10-27 21:40:33 +00:00
p := path . Join ( rendusDir , dir . Name ( ) , login )
if fi , err := os . Stat ( p ) ; err == nil {
ret [ dir . Name ( ) ] = new ( Submission )
ret [ dir . Name ( ) ] . Date = fi . ModTime ( )
if lnk , err := os . Readlink ( p ) ; err == nil {
2019-11-02 15:30:41 +00:00
ret [ dir . Name ( ) ] . Hash = strings . TrimPrefix ( lnk , login + "." )
2016-10-11 22:18:36 +00:00
}
2017-10-27 21:40:33 +00:00
} else {
ret [ dir . Name ( ) ] = nil
2016-10-11 22:18:36 +00:00
}
}
}
}
2017-10-27 21:40:33 +00:00
return ret
}
func ServeJSONStudent ( w http . ResponseWriter , r * http . Request ) {
2018-10-17 14:30:34 +00:00
if addr := r . Header . Get ( "X-Forwarded-For" ) ; addr != "" {
r . RemoteAddr = addr
}
2017-10-27 21:40:33 +00:00
log . Printf ( "Handling %s request from %s: %s [%s]\n" , r . Method , r . RemoteAddr , r . URL . Path , r . UserAgent ( ) )
w . Header ( ) . Set ( "Content-Type" , "application/json" )
2022-08-15 07:41:44 +00:00
w . Header ( ) . Set ( "Access-Control-Allow-Origin" , "*" )
w . Header ( ) . Set ( "Access-Control-Allow-Methods" , "GET, OPTIONS" )
2017-10-27 21:40:33 +00:00
login := strings . TrimSuffix ( strings . TrimPrefix ( r . URL . Path , "/" ) , ".json" )
2022-07-08 11:46:27 +00:00
var ret [ ] byte
status := http . StatusOK
if len ( r . URL . Query ( ) . Get ( "rendu" ) ) > 0 {
limit := r . URL . Query ( ) . Get ( "rendu" )
for k , v := range genStudent ( login ) {
if limit == k {
r , err := json . Marshal ( v )
if err != nil {
http . Error ( w , fmt . Sprintf ( "{errmsg:%q}" , err ) , http . StatusInternalServerError )
return
}
if v == nil {
status = http . StatusNotFound
}
ret = append ( ret , r ... )
}
}
2017-10-27 21:40:33 +00:00
} else {
2022-07-08 11:46:27 +00:00
var err error
ret , err = json . Marshal ( genStudent ( login ) )
if err != nil {
http . Error ( w , fmt . Sprintf ( "{errmsg:%q}" , err ) , http . StatusInternalServerError )
return
}
2017-10-27 21:40:33 +00:00
}
2022-07-08 11:46:27 +00:00
w . WriteHeader ( status )
w . Write ( ret )
2016-10-11 22:18:36 +00:00
}
2021-09-24 07:54:25 +00:00
type arrayFlags [ ] string
func ( i * arrayFlags ) String ( ) string {
return fmt . Sprintf ( "%v" , * i )
}
func ( i * arrayFlags ) Set ( value string ) error {
* i = append ( * i , value )
return nil
}
2016-10-11 22:18:36 +00:00
func main ( ) {
2021-09-24 07:54:25 +00:00
var studentsFiles arrayFlags
2016-10-11 22:18:36 +00:00
var bind = flag . String ( "bind" , "0.0.0.0:8081" , "Bind port/socket" )
2021-09-24 07:54:25 +00:00
flag . Var ( & studentsFiles , "students" , "Path to a CSV file containing in the third column the name of the directory to look for (can be repeated multiple times)" )
2016-10-11 22:18:36 +00:00
flag . StringVar ( & rendusDir , "path" , "./rendu/" , "Path to the submissions directory (each subdirectory is a project)" )
flag . StringVar ( & title , "title" , "Rendus VIRLI" , "Title of the page" )
flag . Parse ( )
var err error
if rendusDir , err = filepath . Abs ( rendusDir ) ; err != nil {
log . Fatal ( err )
} else if dirs , err := ioutil . ReadDir ( rendusDir ) ; err != nil {
log . Fatal ( err )
} else {
var projects [ ] string
nbProj := 0
for _ , dir := range dirs {
if dir . IsDir ( ) {
projects = append ( projects , dir . Name ( ) )
nbProj ++
}
}
log . Println ( nbProj , "projects found:" , projects )
}
// Read and parse students list
log . Println ( "Reading students files..." )
2021-09-24 07:54:25 +00:00
for _ , studentsFile := range studentsFiles {
log . Println ( "Reading" , studentsFile , "..." )
if studentsFile , err = filepath . Abs ( studentsFile ) ; err != nil {
log . Fatal ( err )
} else if fi , err := os . Open ( studentsFile ) ; err != nil {
2016-10-11 22:18:36 +00:00
log . Fatal ( err )
2021-09-24 07:54:25 +00:00
} else {
var nstudents [ ] [ ] string
r := csv . NewReader ( bufio . NewReader ( fi ) )
if nstudents , err = r . ReadAll ( ) ; err != nil {
log . Fatal ( err )
}
students = append ( students , nstudents ... )
2016-10-11 22:18:36 +00:00
}
}
log . Println ( len ( students ) , "students loaded." )
log . Println ( "Registering handlers..." )
http . HandleFunc ( "/" , Serve )
2017-10-27 21:40:33 +00:00
http . HandleFunc ( "/rendus.json" , ServeJSON )
for _ , student := range students {
2019-11-02 15:30:41 +00:00
http . HandleFunc ( "/" + student [ 2 ] + ".json" , ServeJSONStudent )
2017-10-27 21:40:33 +00:00
}
2016-10-11 22:18:36 +00:00
log . Println ( fmt . Sprintf ( "Ready, listening on %s" , * bind ) )
if err := http . ListenAndServe ( * bind , nil ) ; err != nil {
log . Fatal ( "Unable to listen and serve: " , err )
}
}