github.com/quickfeed/quickfeed@v0.0.0-20240507093252-ed8ca812a09c/assignments/assignments_parser.go (about) 1 package assignments 2 3 import ( 4 "fmt" 5 "time" 6 7 "github.com/quickfeed/quickfeed/qf" 8 "google.golang.org/protobuf/types/known/timestamppb" 9 "gopkg.in/yaml.v2" 10 ) 11 12 const defaultAutoApproveScoreLimit = 80 13 14 // assignmentData holds information about a single assignment. 15 // This is only used for parsing the 'assignment.yml' file. 16 // Note that the struct can be private, but the fields must be 17 // public to allow parsing. 18 type assignmentData struct { 19 Order uint32 `yaml:"order"` 20 Deadline string `yaml:"deadline"` 21 IsGroupLab bool `yaml:"isgrouplab"` 22 AutoApprove bool `yaml:"autoapprove"` 23 ScoreLimit uint32 `yaml:"scorelimit"` 24 Reviewers uint32 `yaml:"reviewers"` 25 ContainerTimeout uint32 `yaml:"containertimeout"` 26 } 27 28 func newAssignmentFromFile(contents []byte, assignmentName string, courseID uint64) (*qf.Assignment, error) { 29 var newAssignment assignmentData 30 err := yaml.Unmarshal(contents, &newAssignment) 31 if err != nil { 32 return nil, fmt.Errorf("error unmarshalling assignment: %w", err) 33 } 34 if newAssignment.Order < 1 { 35 return nil, fmt.Errorf("assignment order must be greater than 0") 36 } 37 // if no auto approve score limit is defined; use the default 38 if newAssignment.ScoreLimit < 1 { 39 newAssignment.ScoreLimit = defaultAutoApproveScoreLimit 40 } 41 deadline, err := FixDeadline(newAssignment.Deadline) 42 if err != nil { 43 return nil, fmt.Errorf("error parsing deadline: %w", err) 44 } 45 // AssignmentID field from the parsed yaml is used to set Order, not assignment ID, 46 // or it will cause a database constraint violation (IDs must be unique) 47 // The Name field below is the folder name of the assignment. 48 assignment := &qf.Assignment{ 49 CourseID: courseID, 50 Deadline: deadline, 51 Name: assignmentName, 52 Order: newAssignment.Order, 53 IsGroupLab: newAssignment.IsGroupLab, 54 AutoApprove: newAssignment.AutoApprove, 55 ScoreLimit: newAssignment.ScoreLimit, 56 Reviewers: newAssignment.Reviewers, 57 ContainerTimeout: newAssignment.ContainerTimeout, 58 } 59 return assignment, nil 60 } 61 62 func FixDeadline(in string) (*timestamppb.Timestamp, error) { 63 acceptedLayouts := []string{ 64 "2006-1-2T15:04:05", 65 "2006-1-2 15:04:05", 66 "2006-1-2T15:04", 67 "2006-1-2 15:04", 68 "2006-1-2T1504", 69 "2006-1-2 1504", 70 "2006-1-2T15", 71 "2006-1-2 15", 72 "2006-1-2 3pm", 73 "2006-1-2 3:04pm", 74 "2006-1-2 3:04:05pm", 75 "2-1-2006T15:04:05", 76 "2-1-2006 15:04:05", 77 "2-1-2006T15:04", 78 "2-1-2006 15:04", 79 "2-1-2006T1504", 80 "2-1-2006 1504", 81 "2-1-2006T15", 82 "2-1-2006 15", 83 "2-1-2006 3pm", 84 "2-1-2006 3:04pm", 85 "2-1-2006 3:04:05pm", 86 } 87 for _, layout := range acceptedLayouts { 88 t, err := time.Parse(layout, in) 89 if err != nil { 90 continue 91 } 92 return timestamppb.New(t), nil 93 } 94 return nil, fmt.Errorf("invalid date format: %s", in) 95 }