// HomeHandler is a http handler for the home page for logged in users. func HomeHandler(w http.ResponseWriter, r *http.Request) { member, err := checkMemberApproval(w, r, true) if err != nil { return } view := HomeView{ StdTemplate: StdTemplate{ Member: member, }, Teaching: make(map[string]*git.Organization), Assisting: make(map[string]*git.Organization), Courses: make(map[string]*git.Organization), } for key := range member.Teaching { view.Teaching[key], _ = git.NewOrganization(key, true) } for key := range member.AssistantCourses { view.Assisting[key], _ = git.NewOrganization(key, true) } for key := range member.Courses { view.Courses[key], _ = git.NewOrganization(key, true) } if !member.IsComplete() { http.Redirect(w, r, pages.REGISTER_REDIRECT, 307) return } execTemplate("home.html", w, view) }
// RequestRandomGroupHandler is a http handler used by a student to request a random group assignment. func RequestRandomGroupHandler(w http.ResponseWriter, r *http.Request) { // Checks if the user is signed in and a teacher. member, err := checkMemberApproval(w, r, false) if err != nil { http.Error(w, err.Error(), 404) log.Println(err) return } orgname := r.FormValue("course") if !git.HasOrganization(orgname) { http.Error(w, "Does not have organization.", 404) } org, err := git.NewOrganization(orgname, false) if err != nil { http.Error(w, "Does not have organization.", 404) } defer func() { err := org.Save() if err != nil { org.Unlock() log.Println(err) } }() org.PendingRandomGroup[member.Username] = nil }
// SlipdaysHandler is used to get used slipdays for a user in a course. func SlipdaysHandler(w http.ResponseWriter, r *http.Request) { // Checks if the user is signed in and a teacher. member, err := checkMemberApproval(w, r, false) if err != nil { http.Error(w, err.Error(), 404) log.Println(err) return } orgname := r.FormValue("Course") org, err := git.NewOrganization(orgname, true) if err != nil { log.Println(err) http.Error(w, err.Error(), 500) return } view := SlipdaysView{ Course: orgname, MaxSlipdays: org.SlipdaysMax, } if org.IsTeacher(member) { username := r.FormValue("Username") user, err := git.NewMemberFromUsername(username, true) if err != nil { log.Println(err) http.Error(w, err.Error(), 404) return } if !org.IsMember(member) { http.Error(w, "Unknown member of course.", 404) return } courseopt := user.Courses[org.Name] view.UsedSlipdays = courseopt.UsedSlipDays view.Username = user.Username } else if org.IsMember(member) { courseopt := member.Courses[org.Name] view.UsedSlipdays = courseopt.UsedSlipDays view.Username = member.Username } else { http.Error(w, "Unknown member of course.", 404) return } enc := json.NewEncoder(w) err = enc.Encode(view) if err != nil { log.Println(err) http.Error(w, err.Error(), 404) return } }
// RemoveUserHandler is http handler used to remove users from the list of students on a course. func RemoveUserHandler(w http.ResponseWriter, r *http.Request) { // Checks if the user is signed in and a teacher. member, err := checkTeacherApproval(w, r, true) if err != nil { http.Redirect(w, r, "/", 307) log.Println(err) return } username := r.FormValue("user") course := r.FormValue("course") if !git.HasOrganization(course) { http.Error(w, "Unknown course.", 404) return } org, err := git.NewOrganization(course, false) if err != nil { http.Error(w, "Not valid organization.", 404) return } defer func() { err := org.Save() if err != nil { org.Unlock() log.Println(err) } }() if !org.IsTeacher(member) { http.Error(w, "Is not a teacher or assistant for this course.", 404) return } user, err := git.NewMemberFromUsername(username, false) if err != nil { http.Error(w, err.Error(), 500) return } defer func() { err := user.Save() if err != nil { user.Unlock() log.Println(err) } }() if org.IsMember(user) { org.RemoveMembership(user) user.RemoveOrganization(org) } else { http.Error(w, "Couldn't find this user in this course. ", 404) return } }
// AddAssistantHandler is a http handler used to add users as assistants on a course. func AddAssistantHandler(w http.ResponseWriter, r *http.Request) { // Checks if the user is signed in and a teacher. member, err := checkTeacherApproval(w, r, false) if err != nil { log.Println(err) return } username := r.FormValue("assistant") course := r.FormValue("course") if !git.HasOrganization(course) { http.Error(w, "Unknown course.", 404) return } if username == member.Username { return } assistant, err := git.NewMemberFromUsername(username, false) if err != nil { http.Error(w, err.Error(), 500) return } defer func() { if err := assistant.Save(); err != nil { assistant.Unlock() log.Println(err) } }() org, err := git.NewOrganization(course, false) if err != nil { http.Error(w, err.Error(), 500) return } defer func() { if err := org.Save(); err != nil { org.Unlock() log.Println(err) } }() if !org.IsTeacher(member) { http.Error(w, "User is not the teacher for this course.", 404) return } assistant.AddAssistingOrganization(org) org.AddTeacher(assistant) if _, ok := org.PendingUser[username]; ok { delete(org.PendingUser, username) } }
// RegisterCourseMemberHandler is a http handler which register new students // signing up for a course. After registering the student, this handler // gives back a informal page about how to accept the invitation to the // organization on github. func RegisterCourseMemberHandler(w http.ResponseWriter, r *http.Request) { // Checks if the user is signed in and a teacher. member, err := checkMemberApproval(w, r, true) if err != nil { log.Println(err) return } // Gets the org and check if valid orgname := "" if path := strings.Split(r.URL.Path, "/"); len(path) == 4 { if !git.HasOrganization(path[3]) { http.Redirect(w, r, "/course/register", 307) return } orgname = path[3] } else { http.Redirect(w, r, "/course/register", 307) return } org, err := git.NewOrganization(orgname, false) if err != nil { http.Redirect(w, r, "/course/register", 307) return } defer func() { err = org.Save() if err != nil { org.Unlock() log.Println(err) } }() if _, ok := org.Members[member.Username]; ok { http.Redirect(w, r, "/course/"+orgname, 307) return } err = org.AddMembership(member) if err != nil { log.Println("Error adding the student to course. Error msg:", err) } view := NewMemberView{ StdTemplate: StdTemplate{ Member: member, }, Org: orgname, } execTemplate("course-registeredmemberinfo.html", w, view) }
// ListReviewsHandler will write back a list of all the code reviews // in a course, as json data. // // Expected input keys: course func ListReviewsHandler(w http.ResponseWriter, r *http.Request) { view := ListReviewsView{ Error: true, } enc := json.NewEncoder(w) // Checks if the user is signed in. member, err := checkMemberApproval(w, r, true) if err != nil { log.Println(err) return } if r.FormValue("course") == "" { view.Errormsg = "Missing required course field." enc.Encode(view) return } if !git.HasOrganization(r.FormValue("course")) { view.Errormsg = "Unknown course." enc.Encode(view) return } org, err := git.NewOrganization(r.FormValue("course"), true) if err != nil { view.Errormsg = "Unknown course." enc.Encode(view) return } if !org.IsMember(member) { view.Errormsg = "Not a member of this course." enc.Encode(view) return } crlist := []*git.CodeReview{} for _, crid := range org.CodeReviewlist { if cr, err := git.GetCodeReview(crid); err == nil { crlist = append(crlist, cr) } } view.Error = false view.Reviews = crlist enc.Encode(view) }
// RemovePendingUserHandler is http handler used to remove users from the list of pending students on a course. func RemovePendingUserHandler(w http.ResponseWriter, r *http.Request) { // Checks if the user is signed in and a teacher. member, err := checkTeacherApproval(w, r, true) if err != nil { http.Redirect(w, r, "/", 307) log.Println(err) return } username := r.FormValue("user") course := r.FormValue("course") if !git.HasOrganization(course) { http.Error(w, "Unknown course.", 404) return } org, err := git.NewOrganization(course, false) if err != nil { http.Error(w, "Not valid organization.", 404) return } defer func() { err := org.Save() if err != nil { org.Unlock() log.Println(err) } }() org.Lock() defer org.Unlock() if !org.IsTeacher(member) { http.Error(w, "Is not a teacher or assistant for this course.", 404) return } if _, ok := org.PendingUser[username]; ok { delete(org.PendingUser, username) } }
// ScoreboardHandler is a http handler to give the user a page // showing the scoreboard for a course func ScoreboardHandler(w http.ResponseWriter, r *http.Request) { member, err := checkMemberApproval(w, r, true) if err != nil { return } // Gets the org and check if valid orgname := "" if path := strings.Split(r.URL.Path, "/"); len(path) == 3 { if !git.HasOrganization(path[2]) { http.Redirect(w, r, HomeURL, 307) return } orgname = path[2] } else { http.Redirect(w, r, HomeURL, 307) return } org, err := git.NewOrganization(orgname, true) if err != nil { http.Error(w, err.Error(), 500) return } if !org.IsMember(member) { http.Redirect(w, r, HomeURL, 307) return } view := ScoreboardView{ StdTemplate: StdTemplate{ Member: member, }, Org: org, } execTemplate("scoreboard.html", w, view) }
// CIResultSummaryHandler is a http handler used to get a build summary // of the build for a user or group. This handler writes back the summary // as JSON data. func CIResultSummaryHandler(w http.ResponseWriter, r *http.Request) { // Checks if the user is signed in and a teacher. teacher, err := checkTeacherApproval(w, r, false) if err != nil { http.Error(w, err.Error(), 404) log.Println(err) return } orgname := r.FormValue("Course") username := r.FormValue("Username") if orgname == "" || username == "" { http.Error(w, "Empty request.", 404) return } org, err := git.NewOrganization(orgname, true) if err != nil { http.Error(w, err.Error(), 500) return } if !org.IsTeacher(teacher) { http.Error(w, "Not a teacher for this course.", 404) return } res := make(map[string]*ci.BuildResult) notes := make(map[string]string) //credit := make(map[string]score.Score) //if group ... if strings.HasPrefix(username, git.GroupRepoPrefix) { groupid, err := strconv.Atoi(username[len(git.GroupRepoPrefix):]) if err != nil { log.Println(err) http.Error(w, err.Error(), 500) return } group, err := git.NewGroup(orgname, groupid, true) if err != nil { log.Println(err) http.Error(w, err.Error(), 500) return } for labnum, lab := range group.Assignments { labname := org.GroupLabFolders[labnum] buildid := group.GetLastBuildID(labnum) if buildid < 0 { continue } build, err := ci.GetBuildResult(buildid) if err != nil { log.Println(err) continue } res[labname] = build notes[labname] = lab.Notes //credit[labname] = lab.ExtraCredit } } else { user, err := git.NewMemberFromUsername(username, true) if err != nil { log.Println(err) http.Error(w, err.Error(), 500) return } courseopt, ok := user.Courses[orgname] if ok { for labnum, lab := range courseopt.Assignments { labname := org.IndividualLabFolders[labnum] buildid := user.GetLastBuildID(orgname, labnum) if buildid < 0 { continue } build, err := ci.GetBuildResult(buildid) if err != nil { log.Println(err) continue } res[labname] = build notes[labname] = lab.Notes //credit[labname] = lab.ExtraCredit } } } view := SummaryView{ Course: orgname, User: username, Summary: res, Notes: notes, //ExtraCredit: credit, } enc := json.NewEncoder(w) err = enc.Encode(view) if err != nil { http.Error(w, err.Error(), 404) } }
// ManualCITriggerHandler is a http handler for manually triggering test builds. func ManualCITriggerHandler(w http.ResponseWriter, r *http.Request) { // Checks if the user is signed in and a teacher. member, err := checkMemberApproval(w, r, false) if err != nil { http.Error(w, err.Error(), 404) log.Println(err) return } course := r.FormValue("course") user := r.FormValue("user") lab := r.FormValue("lab") if !git.HasOrganization(course) { http.Error(w, "Unknown organization", 404) return } org, err := git.NewOrganization(course, true) if err != nil { http.Error(w, "Organization Error", 404) return } // Defaults back to username or group name for the user if not a teacher. if !org.IsTeacher(member) { if org.IsMember(member) { if strings.Contains(user, "group") { if member.Courses[org.Name].IsGroupMember { user = "******" + strconv.Itoa(member.Courses[org.Name].GroupNum) } else { http.Error(w, "Not a group member", 404) return } } else { user = member.Username } } else { http.Error(w, "Not a member of the course", 404) return } } groupid := -1 labnum := -1 if strings.Contains(user, "group") { groupid, err = strconv.Atoi(user[len("group"):]) if err != nil { http.Error(w, err.Error(), 500) return } for i, name := range org.GroupLabFolders { if name == lab { labnum = i break } } } else { for i, name := range org.IndividualLabFolders { if name == lab { labnum = i break } } } var repo string var destfolder string if _, ok := org.Members[user]; ok { repo = user + "-" + git.StandardRepoName destfolder = git.StandardRepoName } else if _, ok := org.Groups[user]; ok { repo = user destfolder = git.GroupsRepoName } else { http.Error(w, "Unknown user", 404) return } opt := ci.DaemonOptions{ Org: org.Name, User: user, Group: groupid, Repo: repo, BaseFolder: org.CI.Basepath, LabFolder: lab, LabNumber: labnum, AdminToken: org.AdminToken, DestFolder: destfolder, Secret: org.CI.Secret, IsPush: false, } log.Println(opt) ci.StartTesterDaemon(opt) }
// CIResultHandler is a http handeler for getting results from // a build. This handler writes back the results as JSON data. func CIResultHandler(w http.ResponseWriter, r *http.Request) { // Checks if the user is signed in and a teacher. member, err := checkMemberApproval(w, r, false) if err != nil { http.Error(w, err.Error(), 404) log.Println(err) return } // TODO: add more security orgname := r.FormValue("Course") username := r.FormValue("Username") labname := r.FormValue("Labname") org, err := git.NewOrganization(orgname, true) if err != nil { http.Error(w, err.Error(), 500) return } if !org.IsMember(member) { http.Error(w, "Not a member for this course.", 404) return } var res *ci.BuildResult if strings.HasPrefix(username, git.GroupRepoPrefix) { labnum := -1 for i, name := range org.GroupLabFolders { if name == labname { labnum = i break } } if labnum < 0 { http.Error(w, "No lab with that name found.", 404) return } groupid, err := strconv.Atoi(username[len(git.GroupRepoPrefix):]) if err != nil { http.Error(w, "Could not convert the group ID.", 404) return } group, err := git.NewGroup(orgname, groupid, true) if err != nil { log.Println(err) http.Error(w, err.Error(), 404) return } buildid := group.GetLastBuildID(labnum) if buildid < 0 { http.Error(w, "Could not find the build.", 404) return } res, err = ci.GetBuildResult(buildid) if err != nil { log.Println(err) http.Error(w, err.Error(), 404) return } } else { labnum := -1 for i, name := range org.IndividualLabFolders { if name == labname { labnum = i break } } if labnum < 0 { http.Error(w, "No lab with that name found.", 404) return } user, err := git.NewMemberFromUsername(username, true) if err != nil { log.Println(err) http.Error(w, err.Error(), 404) return } buildid := user.GetLastBuildID(orgname, labnum) if buildid < 0 { http.Error(w, "Could not find the build.", 404) return } res, err = ci.GetBuildResult(buildid) if err != nil { log.Println(err) http.Error(w, err.Error(), 404) return } } enc := json.NewEncoder(w) err = enc.Encode(res) if err != nil { http.Error(w, err.Error(), 404) } }
// AddGroupMemberHandler is a http handler adding an additional member to a active group. func AddGroupMemberHandler(w http.ResponseWriter, r *http.Request) { view := AddGroupMemberView{} view.Error = true enc := json.NewEncoder(w) // Checks if the user is signed in and a teacher. member, err := checkTeacherApproval(w, r, true) if err != nil { err = enc.Encode(ErrSignIn) return } orgname := r.FormValue("course") if orgname == "" || !git.HasOrganization(orgname) { err = enc.Encode(ErrUnknownCourse) return } groupid, err := strconv.Atoi(r.FormValue("groupid")) if err != nil { view.ErrorMsg = err.Error() err = enc.Encode(view) return } if !git.HasGroup(groupid) { err = enc.Encode(ErrUnknownGroup) return } org, err := git.NewOrganization(orgname, false) if err != nil { view.ErrorMsg = err.Error() err = enc.Encode(view) return } defer func() { err := org.Save() if err != nil { org.Unlock() log.Println(err) } }() if !org.IsTeacher(member) { err = enc.Encode(ErrNotTeacher) return } group, err := git.NewGroup(orgname, groupid, false) if err != nil { view.ErrorMsg = err.Error() err = enc.Encode(view) return } defer func() { err := group.Save() if err != nil { group.Unlock() log.Println(err) } }() if group.TeamID == 0 { teams, err := org.ListTeams() if err != nil { view.ErrorMsg = err.Error() err = enc.Encode(view) return } if team, ok := teams[git.GroupRepoPrefix+strconv.Itoa(groupid)]; ok { group.TeamID = team.ID } else { view.ErrorMsg = "Error finding team on GitHub." err = enc.Encode(view) return } } r.ParseForm() members := r.PostForm["member"] for _, username := range members { if username == "" || !git.HasMember(username) { continue } group.AddMember(username) org.AddMemberToTeam(group.TeamID, username) delete(org.PendingRandomGroup, username) } group.Activate() view.Added = true view.Error = false enc.Encode(view) }
// TeachersPanelHandler is a http handler serving the Teacher panel. // This page shows a summary of all the students and groups. func TeachersPanelHandler(w http.ResponseWriter, r *http.Request) { // Checks if the user is signed in and a teacher. member, err := checkTeacherApproval(w, r, true) if err != nil { log.Println(err) http.Redirect(w, r, pages.HOMEPAGE, 307) return } // Gets the org and check if valid orgname := "" if path := strings.Split(r.URL.Path, "/"); len(path) == 4 { if !git.HasOrganization(path[3]) { http.Redirect(w, r, pages.HOMEPAGE, 307) return } orgname = path[3] } else { http.Redirect(w, r, pages.HOMEPAGE, 307) return } org, err := git.NewOrganization(orgname, true) if err != nil { http.Error(w, err.Error(), 500) return } if !org.IsTeacher(member) { log.Println("User is not a teacher for this course.") http.Redirect(w, r, pages.HOMEPAGE, 307) return } // gets pending users users := org.PendingUser var status string for username := range users { // check status up against Github users[username], err = git.NewMemberFromUsername(username, true) if err != nil { continue } status, err = org.GetMembership(users[username].(*git.Member)) if err != nil { log.Println(err) continue } if status == "active" { continue // TODO: what about group assignments? } else if status == "pending" { delete(users, username) } else { delete(users, username) log.Println("Got a unexpected status back from Github regarding Membership") } } // gets teachers for username := range org.Teachers { org.Teachers[username], _ = git.NewMemberFromUsername(username, true) } // gets users for username := range org.Members { org.Members[username], _ = git.NewMemberFromUsername(username, true) } // get pending groups pendinggroups := make(map[int]*git.Group) for groupID := range org.PendingGroup { group, err := git.NewGroup(org.Name, groupID, true) if err != nil { log.Println(err) } if group.Course != org.Name { org.Lock() delete(org.PendingGroup, groupID) err := org.Save() if err != nil { log.Println(err) org.Unlock() } continue } for key := range group.Members { groupmember, _ := git.NewMemberFromUsername(key, true) group.Members[key] = groupmember } pendinggroups[groupID] = group } // get groups for groupname := range org.Groups { groupID, _ := strconv.Atoi(groupname[5:]) group, _ := git.NewGroup(org.Name, groupID, true) for key := range group.Members { groupmember, _ := git.NewMemberFromUsername(key, true) group.Members[key] = groupmember } org.Groups[groupname] = group } _, _, labtype := org.FindCurrentLab() view := TeachersPanelView{ StdTemplate: StdTemplate{ Member: member, }, PendingUser: users, Org: org, PendingGroup: pendinggroups, CurrentLabType: labtype, } execTemplate("teacherspanel.html", w, view) }
// ApUserResultsHandler is a http handeler for getting all results for a user // from the latest anti-plagiarism test. This handler writes back the results as JSON data. func ApUserResultsHandler(w http.ResponseWriter, r *http.Request) { // Checks if the user is signed in and a teacher. member, err := checkMemberApproval(w, r, false) if err != nil { http.Error(w, err.Error(), 404) log.Println(err) return } // TODO: add more security orgname := r.FormValue("Course") username := r.FormValue("Username") org, err := git.NewOrganization(orgname, true) if err != nil { http.Error(w, err.Error(), 500) return } if !org.IsMember(member) { http.Error(w, "Not a member for this course.", 404) return } results := make(map[string]git.AntiPlagiarismResults) if strings.HasPrefix(username, git.GroupRepoPrefix) { // Get the group ID from the group name groupid, err := strconv.Atoi(username[len(git.GroupRepoPrefix):]) if err != nil { http.Error(w, "Could not convert the group ID.", 404) return } // Get the group from the database group, err := git.NewGroup(orgname, groupid, true) if err != nil { log.Println(err) http.Error(w, err.Error(), 404) return } // For each lab for i, name := range org.GroupLabFolders { // Get the results for the lab temp := group.GetAntiPlagiarismResults(org.Name, i) if temp != nil { results[name] = *temp } } } else { // Get user from the database user, err := git.NewMemberFromUsername(username, true) if err != nil { log.Println(err) http.Error(w, err.Error(), 404) return } // For each lab for i, name := range org.IndividualLabFolders { // Get the results for the lab temp := user.GetAntiPlagiarismResults(org.Name, i) if temp != nil { results[name] = *temp } } } enc := json.NewEncoder(w) // Encode the results in JSON err = enc.Encode(results) if err != nil { http.Error(w, err.Error(), 404) } }
// NotesHandler will add a note to a lab for a given user. // Page requested with method GET will return latest note and POST will store a // new note to the user or group. // required input: // - Course // - Username //or // - Group // - labnum // - Notes func NotesHandler(w http.ResponseWriter, r *http.Request) { // Checks if the user is signed in and a teacher. teacher, err := checkTeacherApproval(w, r, false) if err != nil { http.Error(w, err.Error(), 404) log.Println(err) return } course := r.FormValue("Course") username := r.FormValue("Username") notes := r.FormValue("Notes") groupid, _ := strconv.Atoi(r.FormValue("Group")) labnum, err := strconv.Atoi(r.FormValue("Labnum")) if err != nil { log.Println(err) http.Error(w, err.Error(), 404) return } org, err := git.NewOrganization(course, true) if err != nil { log.Println(err) http.Error(w, err.Error(), 500) return } if !org.IsTeacher(teacher) { log.Println(err) http.Error(w, "Not a teacher of this course", 404) return } if groupid > 0 { group, err := git.NewGroup(org.Name, groupid, false) if err != nil { log.Println(err) http.Error(w, err.Error(), 500) return } if group.Course != org.Name { log.Println(err) http.Error(w, "Not a group in this course", 404) return } if r.Method == "POST" { group.AddNotes(labnum, notes) } else { view := &NotesView{ Course: course, Group: groupid, Labnum: labnum, Notes: group.GetNotes(labnum), } enc := json.NewEncoder(w) if err = enc.Encode(view); err != nil { log.Println(err) http.Error(w, err.Error(), 500) return } } if err = group.Save(); err != nil { group.Unlock() log.Println(err) http.Error(w, err.Error(), 500) return } } else { user, err := git.NewMemberFromUsername(username, false) if err != nil { log.Println(err) http.Error(w, err.Error(), 500) return } if !org.IsMember(user) { log.Println(err) http.Error(w, "Not a member of this course", 404) return } if r.Method == "POST" { user.AddNotes(org.Name, labnum, notes) } else { view := &NotesView{ Course: course, Username: username, Labnum: labnum, Notes: user.GetNotes(course, labnum), } enc := json.NewEncoder(w) if err = enc.Encode(view); err != nil { log.Println(err) http.Error(w, err.Error(), 500) return } } if err = user.Save(); err != nil { user.Unlock() log.Println(err) http.Error(w, err.Error(), 500) return } } }
// ApLabResultsHandler is a http handeler for getting results for one lab of a user // from the latest anti-plagiarism test. This handler writes back the results as JSON data. func ApLabResultsHandler(w http.ResponseWriter, r *http.Request) { // Checks if the user is signed in and a teacher. member, err := checkMemberApproval(w, r, false) if err != nil { http.Error(w, err.Error(), 404) log.Println(err) return } // TODO: add more security orgname := r.FormValue("Course") username := r.FormValue("Username") labname := r.FormValue("Labname") org, err := git.NewOrganization(orgname, true) if err != nil { http.Error(w, err.Error(), 500) return } if !org.IsMember(member) { http.Error(w, "Not a member for this course.", 404) return } var results *git.AntiPlagiarismResults if strings.HasPrefix(username, git.GroupRepoPrefix) { labIndex := -1 // Find the correct lab index for i, name := range org.GroupLabFolders { if name == labname { labIndex = i break } } if labIndex < 0 { http.Error(w, "No lab with that name found.", 404) return } // Get the group ID from the group name groupid, err := strconv.Atoi(username[len(git.GroupRepoPrefix):]) if err != nil { http.Error(w, "Could not convert the group ID.", 404) return } // Get the group from the database group, err := git.NewGroup(orgname, groupid, true) if err != nil { log.Println(err) http.Error(w, err.Error(), 404) return } // Get the results for the lab results = group.GetAntiPlagiarismResults(org.Name, labIndex) } else { labIndex := -1 // Find the correct lab index for i, name := range org.IndividualLabFolders { if name == labname { labIndex = i break } } if labIndex < 0 { http.Error(w, "No lab with that name found.", 404) return } // Get the user from the database user, err := git.NewMemberFromUsername(username, true) if err != nil { log.Println(err) http.Error(w, err.Error(), 404) return } // Get the results for the lab results = user.GetAntiPlagiarismResults(org.Name, labIndex) } enc := json.NewEncoder(w) err = enc.Encode(results) if err != nil { http.Error(w, err.Error(), 404) } }
// CreateOrgHandler is a http handler which will link a new course // to a github organization. This function will make a new course // in autograder and then create all needed repositories on github. // // Expected input: org, desc, groups, indv // Optional input: private, template func CreateOrgHandler(w http.ResponseWriter, r *http.Request) { // Checks if the user is signed in and a teacher. member, err := checkTeacherApproval(w, r, true) if err != nil { log.Println(err) return } member.Lock() org, err := git.NewOrganization(r.FormValue("org"), false) if err != nil { log.Println(err) return } defer func() { // Saved the new organization info err = org.Save() if err != nil { org.Unlock() member.Unlock() log.Println(err) return } err = member.Save() if err != nil { member.Unlock() log.Println(err) return } }() log.Println("Creating Course") org.AdminToken = member.GetToken() org.Private = r.FormValue("private") == "on" org.ScreenName = org.Name org.Description = r.FormValue("desc") groups, err := strconv.Atoi(r.FormValue("groups")) if err != nil { log.Println("Cannot convert number of groups assignments from string to int: ", err) groups = 0 } org.GroupAssignments = groups indv, err := strconv.Atoi(r.FormValue("indv")) if err != nil { log.Println("Cannot convert number of individual assignments from string to int: ", err) http.Redirect(w, r, pages.HOMEPAGE, 307) return } org.IndividualAssignments = indv currepos, err := org.ListRepos() if err != nil { log.Println("Problem listing repos in the new course organization: ", err) http.Redirect(w, r, pages.HOMEPAGE, 307) return } templaterepos := make(map[string]git.Repo) if r.FormValue("template") != "" { templateorg, _ := git.NewOrganization(r.FormValue("template"), true) templaterepos, err = templateorg.ListRepos() if err != nil { log.Println("Problem listing repos in the template organization: ", err) http.Redirect(w, r, pages.HOMEPAGE, 307) return } } // creates the course info repo if _, ok := currepos[git.CourseInfoName]; !ok { if _, ok = templaterepos[git.CourseInfoName]; ok { err = org.Fork(r.FormValue("template"), git.CourseInfoName) if err != nil { log.Println("Couldn't fork the course info repo: ", err) http.Redirect(w, r, pages.HOMEPAGE, 307) return } } else { repo := git.RepositoryOptions{ Name: git.CourseInfoName, Private: false, AutoInit: true, } err = org.CreateRepo(repo) if err != nil { log.Println(err) return } } } log.Println("Created ", org.Name, "/", git.CourseInfoName) // creates the lab assignment repo labsl := make(chan int, 1) if _, ok := currepos[git.StandardRepoName]; !ok { go func(l chan int) { defer func() { log.Println("Created ", org.Name, "/", git.StandardRepoName) l <- 1 }() if _, ok = templaterepos[git.StandardRepoName]; ok { err = org.Fork(r.FormValue("template"), git.StandardRepoName) if err != nil { log.Println("Couldn't fork the individual assignment repo: ", err) http.Redirect(w, r, pages.HOMEPAGE, 307) return } } else { repo := git.RepositoryOptions{ Name: git.StandardRepoName, Private: org.Private, AutoInit: true, Issues: true, } err = org.CreateRepo(repo) if err != nil { log.Println(err) return } _, err = org.CreateFile(git.StandardRepoName, ".gitignore", git.IgnoreFileContent, "Standard .gitignore file") if err != nil { log.Println(err) } for i := 0; i < org.IndividualAssignments; i++ { path := "lab" + strconv.Itoa(i+1) + "/README.md" commitmessage := "Adding readme file for lab assignment " + strconv.Itoa(i+1) content := "# Lab assignment " + strconv.Itoa(i+1) _, err = org.CreateFile(git.StandardRepoName, path, content, commitmessage) if err != nil { log.Println(err) } } } }(labsl) } else { labsl <- 1 } // creates test repo testl := make(chan int, 1) if _, ok := currepos[git.TestRepoName]; !ok { go func(l chan int) { defer func() { log.Println("Created ", org.Name, "/", git.TestRepoName) l <- 1 }() if _, ok = templaterepos[git.TestRepoName]; ok { err = org.Fork(r.FormValue("template"), git.TestRepoName) if err != nil { log.Println("Couldn't fork the test repo: ", err) http.Redirect(w, r, pages.HOMEPAGE, 307) return } } else { repo := git.RepositoryOptions{ Name: git.TestRepoName, Private: org.Private, AutoInit: true, Issues: true, //Hook: "push", // TODO: uncomment when CI rebuilds all on new test. } err = org.CreateRepo(repo) if err != nil { log.Println(err) return } for i := 0; i < org.IndividualAssignments; i++ { path := "lab" + strconv.Itoa(i+1) + "/README.md" commitmessage := "Adding readme file for lab assignment " + strconv.Itoa(i+1) content := "# Lab assignment " + strconv.Itoa(i+1) _, err = org.CreateFile(git.TestRepoName, path, content, commitmessage) content = "# Lab assignment " + strconv.Itoa(i+1) + " test" if err != nil { log.Println(err) } } for i := 0; i < org.GroupAssignments; i++ { path := "grouplab" + strconv.Itoa(i+1) + "/README.md" commitmessage := "Adding readme file for lab assignment " + strconv.Itoa(i+1) content := "# Lab assignment " + strconv.Itoa(i+1) _, err = org.CreateFile(git.TestRepoName, path, content, commitmessage) content = "# Lab assignment " + strconv.Itoa(i+1) + " test" if err != nil { log.Println(err) } } } }(testl) } else { testl <- 1 } // creates the group assignment repo, if number of assignments are larger than 0. glabsl := make(chan int) if org.GroupAssignments > 0 { if _, ok := currepos[git.GroupsRepoName]; !ok { go func(l chan int) { defer func() { log.Println("Created ", org.Name, "/", git.GroupsRepoName) l <- 1 }() if _, ok = templaterepos[git.GroupsRepoName]; ok { err = org.Fork(r.FormValue("template"), git.GroupsRepoName) if err != nil { log.Println("Couldn't fork the group assignment repo: ", err) http.Redirect(w, r, pages.HOMEPAGE, 307) return } } else { repo := git.RepositoryOptions{ Name: git.GroupsRepoName, Private: org.Private, AutoInit: true, Issues: true, } err = org.CreateRepo(repo) if err != nil { log.Println(err) return } _, err = org.CreateFile(git.GroupsRepoName, ".gitignore", git.IgnoreFileContent, "Standard .gitignore file") if err != nil { log.Println(err) } for i := 0; i < org.GroupAssignments; i++ { path := "grouplab" + strconv.Itoa(i+1) + "/README.md" commitmessage := "Adding readme file for lab assignment " + strconv.Itoa(i+1) content := "# Lab assignment " + strconv.Itoa(i+1) _, err = org.CreateFile(git.GroupsRepoName, path, content, commitmessage) content = "# Lab assignment " + strconv.Itoa(i+1) + " test" if err != nil { log.Println(err) } } } }(glabsl) } else { glabsl <- 1 } } else { glabsl <- 1 } // wait on github completion of repos // TODO: fix correct channel use further up. <-labsl <-testl <-glabsl // Creates the student team // TODO: put this in a seperate go rutine and check if the team exsists already. var repos []string repos = append(repos, git.StandardRepoName, git.CourseInfoName) if org.GroupAssignments > 0 { repos = append(repos, git.GroupsRepoName) } team := git.TeamOptions{ Name: "students", Permission: git.PullPermission, RepoNames: repos, } org.StudentTeamID, err = org.CreateTeam(team) if err != nil { log.Println(err) } log.Println("Created team ", org.Name, "/", "students") org.AddTeacher(member) member.AddTeachingOrganization(org) http.Redirect(w, r, pages.FRONTPAGE, 307) }
// LeaderboardDataHandler is a http handler which return the leaderboard for a course in JSON format. func LeaderboardDataHandler(w http.ResponseWriter, r *http.Request) { view := LeaderboardDataView{} view.Error = true enc := json.NewEncoder(w) member, err := checkMemberApproval(w, r, true) if err != nil { enc.Encode(ErrAccessToken) return } orgname := r.FormValue("course") period, err := strconv.Atoi(r.FormValue("period")) if err != nil { enc.Encode(ErrMissingField) return } if !git.HasOrganization(orgname) { enc.Encode(ErrUnknownCourse) return } org, err := git.NewOrganization(orgname, true) if err != nil { view.ErrorMsg = err.Error() enc.Encode(view) return } if !org.IsMember(member) { enc.Encode(ErrNotMember) return } var t time.Time if period == TotalScore { view.Error = false view.Leaderboard = org.GetTotalLeaderboard() view.Scores = org.TotalScore } else if period == MonthlyScore { t, err = time.Parse("1", r.FormValue("month")) if err != nil { t = time.Now() } month := t.Month() view.Error = false view.Leaderboard = org.GetMonthlyLeaderboard(month) view.Scores = org.MonthlyScore[month] } else if period == WeeklyScore { week, err := strconv.Atoi(r.FormValue("week")) if err != nil { _, week = time.Now().ISOWeek() } if week < 1 || week > 53 { view.ErrorMsg = "Week need to be between 1 and 53." enc.Encode(view) return } view.Error = false view.Leaderboard = org.GetWeeklyLeaderboard(week) view.Scores = org.WeeklyScore[week] } enc.Encode(view) }
// PublishReviewHandler is a http handler which will publish a new // code review to github. The function output json as the answer. // // Expected input keys: course, title, fileext, desc and code. func PublishReviewHandler(w http.ResponseWriter, r *http.Request) { view := PublishReviewView{ Error: true, } enc := json.NewEncoder(w) // Checks if the user is signed in. member, err := checkMemberApproval(w, r, true) if err != nil { view.Errormsg = "Not logged in." enc.Encode(view) return } if r.FormValue("course") == "" || r.FormValue("title") == "" || r.FormValue("fileext") == "" || r.FormValue("desc") == "" || r.FormValue("code") == "" { view.Errormsg = "Missing some required input data." enc.Encode(view) return } if !git.HasOrganization(r.FormValue("course")) { view.Errormsg = "Unknown course." enc.Encode(view) return } org, err := git.NewOrganization(r.FormValue("course"), false) if err != nil { view.Errormsg = "Error while getting orgaization data from storage." enc.Encode(view) return } defer func() { if err := org.Save(); err != nil { org.Unlock() log.Println(err) } }() if !org.IsMember(member) { view.Errormsg = "Not a member of this course." enc.Encode(view) return } alfanumreg, err := regexp.Compile("[^A-Za-z0-9]+") if err != nil { view.Errormsg = "Internal sanitazion error." enc.Encode(view) return } reg, err := regexp.Compile("[^A-Za-z0-9 -.]+") if err != nil { view.Errormsg = "Internal sanitazion error." enc.Encode(view) return } // removes illigal chars ext := r.FormValue("fileext") ext = alfanumreg.ReplaceAllString(ext, "") ext = strings.TrimSpace(ext) title := r.FormValue("title") title = reg.ReplaceAllString(title, "") title = strings.TrimSpace(title) cr, err := git.NewCodeReview() if err != nil { view.Errormsg = "Couldn't create code review: " + err.Error() enc.Encode(view) return } cr.Title = title cr.Ext = ext cr.Desc = r.FormValue("desc") cr.Code = r.FormValue("code") cr.User = member.Username err = org.AddCodeReview(cr) if err != nil { view.Errormsg = err.Error() enc.Encode(view) return } if err := cr.Save(); err != nil { view.Errormsg = err.Error() enc.Encode(view) return } view.Error = false view.CommitURL = cr.URL enc.Encode(view) }
// CIResultListHandler returns a list of a given number of build results for a // group or user. // Required input: // - Course string // - Username string //or // - Group int // if Group value is higher than 0 it defaults to group selection. // - Labnum int // - Length int // number of results to find, defoult 10 // - Offset int // default 0 func CIResultListHandler(w http.ResponseWriter, r *http.Request) { // Checks if the user is signed in and a teacher. teacher, err := checkTeacherApproval(w, r, false) if err != nil { http.Error(w, err.Error(), 404) log.Println(err) return } course := r.FormValue("Course") username := r.FormValue("Username") groupid, _ := strconv.Atoi(r.FormValue("Group")) labnum, err := strconv.Atoi(r.FormValue("Labnum")) if err != nil { log.Println(err) http.Error(w, err.Error(), 404) return } length, err := strconv.Atoi(r.FormValue("Length")) if err != nil { length = 10 } offset, _ := strconv.Atoi(r.FormValue("Offset")) org, err := git.NewOrganization(course, true) if err != nil { log.Println(err) http.Error(w, err.Error(), 500) return } if !org.IsTeacher(teacher) { log.Println(err) http.Error(w, "Not a teacher of this course", 404) return } view := CIResultListview{ Course: course, Username: username, Group: groupid, Labnum: labnum, Length: length, Offset: offset, Builds: make([]*ci.BuildResult, 0), } if groupid > 0 { group, err := git.NewGroup(org.Name, groupid, true) if err != nil { log.Println(err) http.Error(w, err.Error(), 500) return } if group.Course != org.Name { log.Println(err) http.Error(w, "Not a group in this course", 404) return } if lab, ok := group.Assignments[labnum]; ok { buildlength := len(lab.Builds) - offset for i := buildlength; i > buildlength && i >= 0; i-- { build, err := ci.GetBuildResult(lab.Builds[i]) if err != nil { log.Println(err) continue } view.Builds = append(view.Builds, build) } } } else { user, err := git.NewMemberFromUsername(username, true) if err != nil { log.Println(err) http.Error(w, err.Error(), 500) return } if !org.IsMember(user) { log.Println(err) http.Error(w, "Not a member of this course", 404) return } if courseopt, ok := user.Courses[org.Name]; ok { if lab, ok := courseopt.Assignments[labnum]; ok { buildlength := len(lab.Builds) - offset for i := buildlength; i > buildlength && i >= 0; i-- { build, err := ci.GetBuildResult(lab.Builds[i]) if err != nil { log.Println(err) continue } view.Builds = append(view.Builds, build) } } } } enc := json.NewEncoder(w) err = enc.Encode(view) if err != nil { http.Error(w, err.Error(), 404) } }
// NewGroupHandler is a http handler used when submitting a new group for approval. func NewGroupHandler(w http.ResponseWriter, r *http.Request) { // Checks if the user is signed in. member, err := checkMemberApproval(w, r, false) if err != nil { http.Error(w, err.Error(), 404) log.Println(err) return } newgrouplock.Lock() defer newgrouplock.Unlock() course := r.FormValue("course") if _, ok := member.Courses[course]; !ok { http.Redirect(w, r, pages.FRONTPAGE, 307) log.Println("Unknown course.") return } org, err := git.NewOrganization(course, false) if err != nil { http.Error(w, err.Error(), 500) log.Println(err) return } defer func() { err := org.Save() if err != nil { org.Unlock() log.Println(err) } }() //org.GroupCount = org.GroupCount + 1 gid := git.GetNextGroupID() if gid < 0 { http.Redirect(w, r, pages.FRONTPAGE, 307) log.Println("Error while getting next group ID.") return } group, err := git.NewGroup(course, gid, false) if err != nil { http.Redirect(w, r, pages.FRONTPAGE, 307) log.Println("Couldn't make new group object.", err) return } defer func() { err := group.Save() if err != nil { group.Unlock() log.Println(err) } }() r.ParseForm() members := r.PostForm["member"] if !org.IsTeacher(member) { var found bool for _, u := range members { if u == member.Username { found = true } } if !found { members = append(members, member.Username) } } var opt git.CourseOptions for _, username := range members { user, err := git.NewMemberFromUsername(username, true) if err != nil { continue } opt = user.Courses[course] if !opt.IsGroupMember { user.Lock() opt.IsGroupMember = true opt.GroupNum = group.ID user.Courses[course] = opt err := user.Save() if err != nil { user.Unlock() log.Println(err) } group.AddMember(username) } delete(org.PendingRandomGroup, username) } org.PendingGroup[group.ID] = nil if member.IsTeacher { http.Redirect(w, r, "/course/teacher/"+org.Name+"#groups", 307) } else { http.Redirect(w, r, "/course/"+org.Name+"#groups", 307) } }
// RemovePendingGroupHandler is used to remove a group. func RemovePendingGroupHandler(w http.ResponseWriter, r *http.Request) { // Checks if the user is signed in and a teacher. member, err := checkTeacherApproval(w, r, true) if err != nil { http.Redirect(w, r, "/", 307) log.Println(err) return } groupid, err := strconv.Atoi(r.FormValue("groupid")) if err != nil { http.Error(w, "Group ID is not a number: "+err.Error(), 404) return } course := r.FormValue("course") if !git.HasOrganization(course) { http.Error(w, "Unknown course.", 404) return } org, err := git.NewOrganization(course, false) if err != nil { http.Error(w, "Unknown course.", 404) return } defer func() { err := org.Save() if err != nil { org.Unlock() log.Println(err) } }() if !org.IsTeacher(member) { http.Error(w, "Is not a teacher or assistant for this course.", 404) return } if !git.HasGroup(groupid) { groupname := git.GroupRepoPrefix + strconv.Itoa(groupid) if _, ok := org.Groups[groupname]; ok { delete(org.Groups, groupname) return } http.Error(w, "Unknown group.", 404) return } if _, ok := org.PendingGroup[groupid]; ok { delete(org.PendingGroup, groupid) } groupname := git.GroupRepoPrefix + strconv.Itoa(groupid) if _, ok := org.Groups[groupname]; ok { delete(org.Groups, groupname) } group, err := git.NewGroup(org.Name, groupid, false) if err != nil { http.Error(w, "Could not get the group: "+err.Error(), 404) return } group.Delete() }
// UpdateCourseHandler is a http handler used to update course information. func UpdateCourseHandler(w http.ResponseWriter, r *http.Request) { // Checks if the user is signed in and a teacher. member, err := checkTeacherApproval(w, r, true) if err != nil { http.Redirect(w, r, pages.FRONTPAGE, 307) log.Println(err) return } r.ParseForm() orgname := r.FormValue("org") org, err := git.NewOrganization(orgname, false) if err != nil { http.Error(w, err.Error(), 500) return } defer func() { err := org.Save() if err != nil { org.Unlock() log.Println(err) } }() if !org.IsTeacher(member) { http.Error(w, "Not valid organization.", 404) return } indv, err := strconv.Atoi(r.FormValue("indv")) if err != nil { http.Error(w, "Cant use the individual assignment format.", 415) return } org.IndividualAssignments = indv groups, err := strconv.Atoi(r.FormValue("groups")) if err != nil { http.Error(w, "Cant use the group assignment format.", 415) return } org.GroupAssignments = groups if r.FormValue("screenname") == "" { org.ScreenName = org.Name } else { org.ScreenName = r.FormValue("screenname") } org.Description = r.FormValue("desc") org.Private = r.FormValue("private") == "on" org.CodeReview = r.FormValue("codereview") == "on" org.Slipdays = r.FormValue("slipdays") == "on" maxslipdays, err := strconv.Atoi(r.FormValue("maxslipdays")) if err != nil { http.Error(w, "Cant use the max slip days format.", 415) return } org.SlipdaysMax = maxslipdays basepath := r.FormValue("basepath") if basepath != "" { org.CI.Basepath = basepath } indvfolders := r.PostForm["lab"] for i := 1; i <= indv; i = i + 1 { if len(indvfolders) <= i-1 { org.IndividualLabFolders[i] = "lab" + strconv.Itoa(i) continue } if fname := indvfolders[i-1]; fname != "" { org.IndividualLabFolders[i] = fname } else { org.IndividualLabFolders[i] = "lab" + strconv.Itoa(i) } } groupfolders := r.PostForm["group"] for i := 1; i <= groups; i = i + 1 { if len(groupfolders) <= i-1 { org.GroupLabFolders[i] = "grouplab" + strconv.Itoa(i) continue } if fname := groupfolders[i-1]; fname != "" { org.GroupLabFolders[i] = fname } else { org.GroupLabFolders[i] = "grouplab" + strconv.Itoa(i) } } timelayout := "02/01/2006 15:04" indvdeadlines := r.PostForm["indvdeadline"] for i := 1; i <= indv; i = i + 1 { if len(indvdeadlines) <= i-1 { org.SetIndividualDeadline(i, time.Now()) continue } if timestring := indvdeadlines[i-1]; timestring != "" { t, err := time.Parse(timelayout, timestring) if err != nil { org.SetIndividualDeadline(i, time.Now()) } else { org.SetIndividualDeadline(i, t) } } } groupdeadlines := r.PostForm["groupdeadline"] for i := 1; i <= groups; i = i + 1 { if len(groupdeadlines) <= i-1 { org.SetGroupDeadline(i, time.Now()) continue } if timestring := groupdeadlines[i-1]; timestring != "" { t, err := time.Parse(timelayout, timestring) if err != nil { org.SetGroupDeadline(i, time.Now()) } else { org.SetGroupDeadline(i, t) } } } http.Redirect(w, r, "/course/teacher/"+org.Name, 307) }
// ApManualTestHandler is a http handler for manually triggering // anti-plagiarism tests. func ApManualTestHandler(w http.ResponseWriter, r *http.Request) { if !git.HasOrganization(r.FormValue("course")) { http.Error(w, "Unknown organization", 404) log.Println("Unknown organization") return } org, err := git.NewOrganization(r.FormValue("course"), true) if err != nil { http.Error(w, "Organization Error", 404) log.Println(err) return } var labs []*apProto.ApRequestLab var repos []string isGroup := false if strings.Contains(r.FormValue("labs"), "group") { // Get the information for groups isGroup = true length := len(org.GroupLabFolders) for i := 1; i <= length; i++ { if org.GroupLabFolders[i] != "" { labs = append(labs, &apProto.ApRequestLab{Name: org.GroupLabFolders[i], Language: org.GroupLanguages[i]}) } } // The order of the repos does not matter. for groupName := range org.Groups { repos = append(repos, groupName) } } else { // Get the information for individuals isGroup = false length := len(org.IndividualLabFolders) for i := 1; i <= length; i++ { if org.IndividualLabFolders[i] != "" { labs = append(labs, &apProto.ApRequestLab{Name: org.IndividualLabFolders[i], Language: org.IndividualLanguages[i]}) } } // The order of the repos does not matter. for indvName := range org.Members { repos = append(repos, indvName+"-labs") } } // Create request request := apProto.ApRequest{GithubOrg: org.Name, GithubToken: org.AdminToken, StudentRepos: repos, Labs: labs} go callAntiplagiarism(request, org, isGroup) fmt.Printf("%v\n", request) }
// ShowResultHandler is a http handler for showing a page detailing // lab resutls for a single user or group. func ShowResultHandler(w http.ResponseWriter, r *http.Request) { // Checks if the user is signed in and a teacher. member, err := checkTeacherApproval(w, r, true) if err != nil { return } // Gets the org and check if valid orgname := "" if path := strings.Split(r.URL.Path, "/"); len(path) == 4 { if !git.HasOrganization(path[3]) { http.Redirect(w, r, pages.HOMEPAGE, 307) return } orgname = path[3] } else { http.Redirect(w, r, pages.HOMEPAGE, 307) return } username := r.FormValue("user") if username == "" { http.Redirect(w, r, pages.HOMEPAGE, 307) return } if !git.HasOrganization(orgname) { http.Redirect(w, r, pages.HOMEPAGE, 307) return } org, err := git.NewOrganization(orgname, true) if err != nil { http.Error(w, err.Error(), 500) } isgroup := false groupid := -1 labnum := 0 if !git.HasMember(username) { groupnum, err := strconv.Atoi(username[len("group"):]) if err != nil { http.Redirect(w, r, pages.HOMEPAGE, 307) return } if git.HasGroup(groupnum) { isgroup = true group, err := git.NewGroup(org.Name, groupnum, true) if err != nil { http.Redirect(w, r, pages.HOMEPAGE, 307) return } groupid = group.ID if group.CurrentLabNum >= org.GroupAssignments { labnum = org.GroupAssignments } else { labnum = group.CurrentLabNum } } else { http.Redirect(w, r, pages.HOMEPAGE, 307) return } } else { user, err := git.NewMemberFromUsername(username, true) if err != nil { http.Error(w, err.Error(), 500) } nr := user.Courses[org.Name].CurrentLabNum if nr >= org.IndividualAssignments { labnum = org.IndividualAssignments } else { labnum = nr } } view := ShowResultView{ StdTemplate: StdTemplate{ Member: member, }, Org: org, Username: username, Labnum: labnum, IsGroup: isgroup, GroupID: groupid, } execTemplate("teacherresultpage.html", w, view) }
// ApproveLabHandler is a http handler used by teachers to approve a lab. func ApproveLabHandler(w http.ResponseWriter, r *http.Request) { // Checks if the user is signed in and a teacher. member, err := checkTeacherApproval(w, r, true) if err != nil { log.Println(err) return } course := r.FormValue("Course") username := r.FormValue("User") approve := r.FormValue("Approve") labnum, err := strconv.Atoi(r.FormValue("Labnum")) if err != nil { log.Println(err) http.Error(w, err.Error(), 404) return } if approve != "true" { log.Println("Missing approval") http.Error(w, "Not approved", 404) return } if !git.HasOrganization(course) || username == "" { log.Println("Missing username or uncorrect course") http.Error(w, "Unknown Organization", 404) return } org, err := git.NewOrganization(course, true) if err != nil { log.Println(err) http.Error(w, err.Error(), 404) return } if !org.IsTeacher(member) { log.Println(member.Name + " is not a teacher of " + org.Name) http.Error(w, "Not a teacher of this course.", 404) return } var isgroup bool if git.HasMember(username) { isgroup = false } else { isgroup = strings.Contains(username, "group") if !isgroup { log.Println("No user found") http.Error(w, "Unknown User", 404) return } } var latestbuild int var res *ci.BuildResult if isgroup { gnum, err := strconv.Atoi(username[len("group"):]) if err != nil { log.Println(err) http.Error(w, err.Error(), 404) return } group, err := git.NewGroup(course, gnum, false) if err != nil { log.Println(err) http.Error(w, err.Error(), 404) return } defer func() { if err := group.Save(); err != nil { group.Unlock() log.Println(err) } }() latestbuild = group.GetLastBuildID(labnum) if latestbuild < 0 { http.Error(w, "No build registered on lab.", 500) return } res, err = ci.GetBuildResult(latestbuild) if err != nil { log.Println(err) http.Error(w, err.Error(), 500) return } group.SetApprovedBuild(res.Labnum, res.ID, res.PushTime) if org.Slipdays { for username := range group.Members { user, err := git.NewMemberFromUsername(username, false) if err != nil { log.Println(err) continue } copt := user.Courses[org.Name] err = copt.RecalculateSlipDays() if err != nil { log.Println(err) } user.Courses[org.Name] = copt } } } else { user, err := git.NewMemberFromUsername(username, false) if err != nil { log.Println(err.Error()) http.Error(w, err.Error(), 500) return } defer func() { if err := user.Save(); err != nil { user.Unlock() log.Println(err) } }() latestbuild = user.GetLastBuildID(course, labnum) if latestbuild < 0 { http.Error(w, "No build registered on lab.", 500) return } res, err = ci.GetBuildResult(latestbuild) if err != nil { log.Println(err) http.Error(w, err.Error(), 500) return } user.SetApprovedBuild(org.Name, res.Labnum, res.ID, res.PushTime) if org.Slipdays { copt := user.Courses[org.Name] err = copt.RecalculateSlipDays() if err != nil { log.Println(err) } user.Courses[org.Name] = copt } } res.Status = "Approved" if err := res.Save(); err != nil { log.Println(err) http.Error(w, err.Error(), 500) return } }
// ApproveCourseMembershipHandler is a http handler used when a teacher wants // to accept a student for a course in autograder. This handler will link the // student to the course organization on github and also create all the needed // repositories on github. func ApproveCourseMembershipHandler(w http.ResponseWriter, r *http.Request) { enc := json.NewEncoder(w) view := ApproveMembershipView{} view.Error = true // default is an error; if its not we anyway set it to false before encoding // Checks if the user is signed in and a teacher. /*member*/ _, err := checkTeacherApproval(w, r, false) if err != nil { log.Println(err) view.ErrorMsg = "You are not singed in or not a teacher." enc.Encode(view) return } // Gets the org and check if valid orgname := "" if path := strings.Split(r.URL.Path, "/"); len(path) == 4 { if !git.HasOrganization(path[3]) { http.Redirect(w, r, pages.HOMEPAGE, 307) return } orgname = path[3] } else { http.Redirect(w, r, pages.HOMEPAGE, 307) return } username := r.FormValue("user") if username == "" { view.ErrorMsg = "Username was not set in the request." enc.Encode(view) return } org, err := git.NewOrganization(orgname, false) if err != nil { view.ErrorMsg = "Could not retrieve the stored organization." enc.Encode(view) return } defer func() { err := org.Save() if err != nil { org.Unlock() log.Println(err) } }() teams, err := org.ListTeams() if err != nil { log.Println(err) view.ErrorMsg = "Error communicating with Github. Can't get list teams." enc.Encode(view) return } if org.IndividualAssignments > 0 { repo := git.RepositoryOptions{ Name: username + "-" + git.StandardRepoName, Private: org.Private, AutoInit: true, Issues: true, Hook: "*", } err = org.CreateRepo(repo) if err != nil { log.Println(err) view.ErrorMsg = "Error communicating with Github. Couldn't create repository." enc.Encode(view) return } if t, ok := teams[username]; !ok { newteam := git.TeamOptions{ Name: username, Permission: git.PushPermission, RepoNames: []string{username + "-" + git.StandardRepoName}, } teamID, err := org.CreateTeam(newteam) if err != nil { log.Println(err) view.ErrorMsg = "Error communicating with Github. Can't create team." enc.Encode(view) return } err = org.AddMemberToTeam(teamID, username) if err != nil { log.Println(err) view.ErrorMsg = "Error communicating with Github. Can't add member to team." enc.Encode(view) return } } else { err = org.LinkRepoToTeam(t.ID, username+"-"+git.StandardRepoName) if err != nil { log.Println(err) view.ErrorMsg = "Error communicating with Github. Can't link repo to team." enc.Encode(view) return } err = org.AddMemberToTeam(t.ID, username) if err != nil { log.Println(err) view.ErrorMsg = "Error communicating with Github. Can't add member to team." enc.Encode(view) return } } } delete(org.PendingUser, username) org.Members[username] = nil member, err := git.NewMemberFromUsername(username, false) if err != nil { view.ErrorMsg = "Could not retrieve the stored user." enc.Encode(view) return } defer func() { err = member.Save() if err != nil { member.Unlock() log.Println(err) } }() member.AddOrganization(org) view.Error = false // it wasn't an error after all view.Approved = true view.User = username enc.Encode(view) }
// UserCoursePageHandler is a http handler giving back the main user // page for a course. This page gived information about all the labs // and results for a user. A user can also submit code reviews from // this page. func UserCoursePageHandler(w http.ResponseWriter, r *http.Request) { // Checks if the user is signed in. member, err := checkMemberApproval(w, r, true) if err != nil { log.Println(err) return } // Gets the org and check if valid orgname := "" if path := strings.Split(r.URL.Path, "/"); len(path) == 3 { if !git.HasOrganization(path[2]) { http.Redirect(w, r, pages.HOMEPAGE, 307) return } orgname = path[2] } else { http.Redirect(w, r, pages.HOMEPAGE, 307) return } org, err := git.NewOrganization(orgname, true) if err != nil { http.Redirect(w, r, pages.HOMEPAGE, 307) return } view := MainCourseView{ StdTemplate: StdTemplate{ Member: member, }, Org: org, } nr := member.Courses[org.Name].CurrentLabNum if nr >= org.IndividualAssignments { view.Labnum = org.IndividualAssignments - 1 } else { view.Labnum = nr - 1 } if member.Courses[orgname].IsGroupMember { group, err := git.NewGroup(orgname, member.Courses[orgname].GroupNum, true) if err != nil { log.Println(err) return } if group.Course != orgname { member.Lock() opt := member.Courses[orgname] opt.IsGroupMember = false opt.GroupNum = 0 member.Courses[orgname] = opt err = member.Save() if err != nil { log.Println(err) member.Unlock() } } view.Group = group if group.CurrentLabNum >= org.GroupAssignments { view.GroupLabnum = org.GroupAssignments - 1 } else { view.GroupLabnum = group.CurrentLabNum - 1 } } execTemplate("maincoursepage.html", w, view) }
// ApproveGroupHandler is a http handler used by teachers to approve a group and activate it. func ApproveGroupHandler(w http.ResponseWriter, r *http.Request) { enc := json.NewEncoder(w) view := ApproveGroupView{} view.Error = true // Checks if the user is signed in and a teacher. member, err := checkTeacherApproval(w, r, false) if err != nil { http.Error(w, err.Error(), 404) log.Println(err) return } groupID, err := strconv.Atoi(r.FormValue("groupid")) if err != nil { view.ErrorMsg = err.Error() err = enc.Encode(view) if err != nil { log.Println(err) } return } orgname := r.FormValue("course") group, err := git.NewGroup(orgname, groupID, false) if err != nil { view.ErrorMsg = err.Error() err = enc.Encode(view) if err != nil { log.Println(err) } return } defer func() { err := group.Save() if err != nil { group.Unlock() log.Println(err) } }() if group.Active { view.ErrorMsg = "This group is already active." err = enc.Encode(view) if err != nil { log.Println(err) } return } if len(group.Members) < 1 { view.ErrorMsg = "No members in this group." err = enc.Encode(view) if err != nil { log.Println(err) } return } org, err := git.NewOrganization(orgname, false) if err != nil { view.ErrorMsg = "Could not retrieve stored organization." err = enc.Encode(view) if err != nil { log.Println(err) } return } defer func() { err := org.Save() if err != nil { org.Unlock() log.Println(err) } }() if !org.IsTeacher(member) { err = enc.Encode(ErrNotTeacher) if err != nil { log.Println(err) } return } orgrepos, err := org.ListRepos() if err != nil { log.Println(err) view.ErrorMsg = err.Error() enc.Encode(view) } orgteams, err := org.ListTeams() if err != nil { log.Println(err) view.ErrorMsg = err.Error() enc.Encode(view) } if org.GroupAssignments > 0 { repo := git.RepositoryOptions{ Name: git.GroupRepoPrefix + r.FormValue("groupid"), Private: org.Private, AutoInit: true, Issues: true, Hook: "*", } if _, ok := orgrepos[repo.Name]; !ok { err = org.CreateRepo(repo) if err != nil { log.Println(err) view.ErrorMsg = "Error communicating with Github. Couldn't create repository." enc.Encode(view) return } } newteam := git.TeamOptions{ Name: git.GroupRepoPrefix + r.FormValue("groupid"), Permission: git.PushPermission, RepoNames: []string{git.GroupRepoPrefix + r.FormValue("groupid")}, } var teamID int if team, ok := orgteams[newteam.Name]; ok { teamID = team.ID } else { teamID, err = org.CreateTeam(newteam) if err != nil { log.Println(err) view.ErrorMsg = "Error communicating with Github. Can't create team." enc.Encode(view) return } } group.TeamID = teamID for username := range group.Members { err = org.AddMemberToTeam(teamID, username) if err != nil { log.Println(err) view.ErrorMsg = "Error communicating with Github. Can't add member to team." enc.Encode(view) continue } } } org.AddGroup(group) group.Activate() view.Error = false view.Approved = true view.ID = groupID err = enc.Encode(view) if err != nil { log.Println(err) } }