@@ -65,8 +65,10 @@ import {
65
65
NBGRADER_MAX_OUTPUT ,
66
66
NBGRADER_MAX_OUTPUT_PER_CELL ,
67
67
NBGRADER_TIMEOUT_MS ,
68
- PEER_GRADING_GUIDE_FN ,
68
+ PEER_GRADING_GUIDE_FILENAME ,
69
69
STUDENT_SUBDIR ,
70
+ PEER_GRADING_GUIDELINES_GRADE_MARKER ,
71
+ PEER_GRADING_GUIDELINES_COMMENT_MARKER ,
70
72
} from "./consts" ;
71
73
72
74
export class AssignmentsActions {
@@ -891,10 +893,10 @@ ${details}
891
893
} ) ;
892
894
this . course_actions . student_projects . action_all_student_projects ( "start" ) ;
893
895
// We request to start all projects simultaneously, and the system
894
- // will start doing that. I think it's no so much important that
896
+ // will start doing that. I think it's not so much important that
895
897
// the projects are actually running, but that they were started
896
898
// before the copy operations started.
897
- await delay ( 15 * 1000 ) ;
899
+ await delay ( 5 * 1000 ) ;
898
900
this . course_actions . clear_activity ( id ) ;
899
901
}
900
902
@@ -948,8 +950,120 @@ ${details}
948
950
desc ,
949
951
short_desc ,
950
952
) ;
953
+ await this . peerParseStudentGrading ( assignment_id ) ;
951
954
}
952
955
956
+ private peerParseStudentGrading = async ( assignment_id : string ) => {
957
+ // For each student do the following:
958
+ // If they already have a recorded grade, do nothing further.
959
+ // If they do not have a recorded grade, load all of the
960
+ // PEER_GRADING_GUIDE_FILENAME files that were collected
961
+ // from the students, then create a grade from that (if possible), along
962
+ // with a comment that explains how that grade was obtained, without
963
+ // saying which student did what.
964
+ const { store, assignment } = this . course_actions . resolve ( {
965
+ assignment_id,
966
+ } ) ;
967
+ if ( assignment == null ) {
968
+ throw Error ( "no such assignment" ) ;
969
+ }
970
+ const id = this . course_actions . set_activity ( {
971
+ desc : "Parsing peer grading" ,
972
+ } ) ;
973
+ const allGrades = assignment . get ( "grades" , Map ( ) ) . toJS ( ) as {
974
+ [ student_id : string ] : string ;
975
+ } ;
976
+ const allComments = assignment . get ( "comments" , Map ( ) ) . toJS ( ) as {
977
+ [ student_id : string ] : string ;
978
+ } ;
979
+ // compute missing grades
980
+ for ( const student_id of store . get_student_ids ( ) ) {
981
+ if ( allGrades [ student_id ] ) {
982
+ // a grade is already set
983
+ continue ;
984
+ }
985
+ // attempt to compute a grade
986
+ const peer_student_ids : string [ ] = store . get_peers_that_graded_student (
987
+ assignment_id ,
988
+ student_id ,
989
+ ) ;
990
+ const course_project_id = store . get ( "course_project_id" ) ;
991
+ const grades : number [ ] = [ ] ;
992
+ let comments : string [ ] = [ ] ;
993
+ const student_name = store . get_student_name ( student_id ) ;
994
+ this . course_actions . set_activity ( {
995
+ id,
996
+ desc : `Parsing peer grading of ${ student_name } ` ,
997
+ } ) ;
998
+ for ( const peer_student_id of peer_student_ids ) {
999
+ const path = join (
1000
+ `${ assignment . get ( "collect_path" ) } -peer-grade` ,
1001
+ student_id ,
1002
+ peer_student_id ,
1003
+ PEER_GRADING_GUIDE_FILENAME ,
1004
+ ) ;
1005
+ try {
1006
+ const contents = await webapp_client . project_client . read_text_file ( {
1007
+ project_id : course_project_id ,
1008
+ path,
1009
+ } ) ;
1010
+ const i = contents . lastIndexOf ( PEER_GRADING_GUIDELINES_GRADE_MARKER ) ;
1011
+ if ( i == - 1 ) {
1012
+ continue ;
1013
+ }
1014
+ let j = contents . lastIndexOf ( PEER_GRADING_GUIDELINES_COMMENT_MARKER ) ;
1015
+ if ( j == - 1 ) {
1016
+ j = contents . length ;
1017
+ }
1018
+ const grade = parseFloat (
1019
+ contents
1020
+ . slice ( i + PEER_GRADING_GUIDELINES_GRADE_MARKER . length , j )
1021
+ . trim ( ) ,
1022
+ ) ;
1023
+ if ( ! isFinite ( grade ) && isNaN ( grade ) ) {
1024
+ continue ;
1025
+ }
1026
+ const comment = contents . slice (
1027
+ j + PEER_GRADING_GUIDELINES_COMMENT_MARKER . length ,
1028
+ ) ;
1029
+ grades . push ( grade ) ;
1030
+ comments . push ( comment ) ;
1031
+ } catch ( err ) {
1032
+ // grade not available for some reason
1033
+ console . warn ( "issue reading peer grading file" , {
1034
+ path,
1035
+ err,
1036
+ student_name,
1037
+ } ) ;
1038
+ }
1039
+ }
1040
+ if ( grades . length > 0 ) {
1041
+ const grade = grades . reduce ( ( a , b ) => a + b ) / grades . length ;
1042
+ allGrades [ student_id ] = `${ grade } ` ;
1043
+ if ( ! allComments [ student_id ] ) {
1044
+ const studentComments = comments
1045
+ . filter ( ( x ) => x . trim ( ) )
1046
+ . map ( ( x ) => `- ${ x } ` )
1047
+ . join ( "\n\n" ) ;
1048
+ allComments [ student_id ] = `Grades: ${ grades . join ( ", " ) } \n\n${
1049
+ studentComments ? "Student Comments:\n" + studentComments : ""
1050
+ } `;
1051
+ }
1052
+ }
1053
+ }
1054
+ // set them in the course data
1055
+ this . course_actions . set (
1056
+ {
1057
+ table : "assignments" ,
1058
+ assignment_id,
1059
+ grades : allGrades ,
1060
+ comments : allComments ,
1061
+ } ,
1062
+ true ,
1063
+ ) ;
1064
+ this . course_actions . clear_activity ( id ) ;
1065
+ } ;
1066
+
953
1067
private async assignment_action_all_students (
954
1068
assignment_id : string ,
955
1069
new_only : boolean ,
@@ -1094,7 +1208,7 @@ ${details}
1094
1208
// write instructions file for the student, where they enter the grade,
1095
1209
// and also it tells them what to do.
1096
1210
await this . write_text_file_to_course_project ( {
1097
- path : join ( src_path , PEER_GRADING_GUIDE_FN ) ,
1211
+ path : join ( src_path , PEER_GRADING_GUIDE_FILENAME ) ,
1098
1212
content : guidelines ,
1099
1213
} ) ;
1100
1214
const target_path = join ( target_base_path , peer_student_id ) ;
@@ -1125,10 +1239,10 @@ ${details}
1125
1239
1126
1240
// Collect all the peer graading of the given student (not the work the student did, but
1127
1241
// the grading about the student!).
1128
- private async peer_collect_from_student (
1242
+ private peer_collect_from_student = async (
1129
1243
assignment_id : string ,
1130
1244
student_id : string ,
1131
- ) : Promise < void > {
1245
+ ) : Promise < void > => {
1132
1246
if ( this . start_copy ( assignment_id , student_id , "last_peer_collect" ) ) {
1133
1247
return ;
1134
1248
}
@@ -1215,7 +1329,7 @@ ${details}
1215
1329
} catch ( err ) {
1216
1330
finish ( err ) ;
1217
1331
}
1218
- }
1332
+ } ;
1219
1333
1220
1334
// This doesn't really stop it yet, since that's not supported by the backend.
1221
1335
// It does stop the spinner and let the user try to restart the copy.
0 commit comments