// clearPreviousResults clears the previous anti-plagiarim results, // because the specific urls can change. It takes as input // org, a database record for the class, and isGroup, whether or not // the request if for individual or group assignments. func clearPreviousResults(org *git.Organization, isGroup bool) { fmt.Printf("Clearing old anti-plagiarism results.\n") if isGroup { // Clear old group results // For each group for groupName := range org.Groups { // Get the Group ID groupID, err := strconv.Atoi(groupName[len(git.GroupRepoPrefix):]) if err != nil { fmt.Printf("clearPreviousResults: Could not get group number from %s. %s\n", groupName, err) continue } // Get the database record group, _ := git.NewGroup(org.Name, groupID, false) // For each lab for labIndex := 1; labIndex <= org.GroupAssignments; labIndex++ { // Clear the specific lab results results := git.AntiPlagiarismResults{MossPct: 0.0, MossURL: "", DuplPct: 0.0, DuplURL: "", JplagPct: 0.0, JplagURL: ""} group.AddAntiPlagiarismResults(org.Name, labIndex, &results) } // Save the database record group.Save() } } else { // Clear old individual results // For each student for username := range org.Members { // Get the database record student, _ := git.NewMemberFromUsername(username, false) // For each lab for labIndex := 1; labIndex <= org.IndividualAssignments; labIndex++ { // Clear the specific lab results results := git.AntiPlagiarismResults{MossPct: 0, MossURL: "", DuplPct: 0, DuplURL: "", JplagPct: 0, JplagURL: ""} student.AddAntiPlagiarismResults(org.Name, labIndex, &results) } // Save the database record student.Save() } } fmt.Printf("Finished clearing old anti-plagiarism results.\n") }
// 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) } }
// 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) } }
// 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) } }
// 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) } }
// 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) } }
// 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) }
// 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) }
// 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) }
// 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) }
// 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() }
// 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) } }
// StartTestBuildProcess will use the payload from github to start the ci build. func StartTestBuildProcess(load github.PushPayload) (err error) { userlogin := *load.Pusher.Name reponame := *load.Repo.Name orgname := *load.Organization.Login if !git.HasMember(userlogin) { log.Println("Not a valid user: "******"Not a valid org: ", orgname) return errors.New("Not a valid org: " + orgname) } org, err := git.NewOrganization(orgname, true) user, err := git.NewMemberFromUsername(userlogin, true) isgroup := !strings.Contains(reponame, "-"+git.StandardRepoName) var labfolder string var destfolder string var labnum int var username string var gnum = -1 if isgroup { gnum, err = strconv.Atoi(reponame[len("group"):]) if err != nil { log.Println(err) return err } group, err := git.NewGroup(org.Name, gnum, true) if err != nil { log.Println(err) return err } labnum = group.CurrentLabNum if labnum > org.GroupAssignments { labnum = org.GroupAssignments } labfolder = org.GroupLabFolders[labnum] username = reponame destfolder = git.GroupsRepoName } else { labnum = user.Courses[org.Name].CurrentLabNum if labnum > org.IndividualAssignments { labnum = org.IndividualAssignments } labfolder = org.IndividualLabFolders[labnum] username = strings.TrimRight(reponame, "-"+git.StandardRepoName) destfolder = git.StandardRepoName } opt := ci.DaemonOptions{ Org: org.Name, User: username, Group: gnum, Repo: reponame, BaseFolder: org.CI.Basepath, LabFolder: labfolder, LabNumber: labnum, AdminToken: org.AdminToken, DestFolder: destfolder, IsPush: true, Secret: org.CI.Secret, } go ci.StartTesterDaemon(opt) return }
// 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 } }
// StartTesterDaemon will start a new test build in the background. func StartTesterDaemon(opt DaemonOptions) { // safeguard defer func() { if r := recover(); r != nil { log.Println("Recovered from panic: ", r) } }() var logarray []string logarray = append(logarray, "CI starting up on repo "+opt.Org+"/"+opt.Repo) // Test execution log.Println("CI starting up on repo", opt.Org, "/", opt.Repo) env, err := NewVirtual() if err != nil { panic(err) } err = env.NewContainer("autograder") if err != nil { panic(err) } // cleanup defer env.RemoveContainer() // mkdir /testground/github.com/ // git clone user-labs // git clone test-labs // cp test-labs user-labs // /bin/sh dependecies.sh // /bin/sh test.sh cmds := []struct { Cmd string Breakable bool }{ {"mkdir -p " + opt.BaseFolder, true}, {"git clone https://" + opt.AdminToken + ":[email protected]/" + opt.Org + "/" + opt.Repo + ".git" + " " + opt.BaseFolder + opt.DestFolder + "/", true}, {"git clone https://" + opt.AdminToken + ":[email protected]/" + opt.Org + "/" + git.TestRepoName + ".git" + " " + opt.BaseFolder + git.TestRepoName + "/", true}, {"/bin/bash -c \"cp -rf \"" + opt.BaseFolder + git.TestRepoName + "/*\" \"" + opt.BaseFolder + opt.DestFolder + "/\" \"", true}, {"chmod 777 " + opt.BaseFolder + opt.DestFolder + "/dependencies.sh", true}, {"/bin/sh -c \"(cd \"" + opt.BaseFolder + opt.DestFolder + "/\" && ./dependencies.sh)\"", true}, {"chmod 777 " + opt.BaseFolder + opt.DestFolder + "/" + opt.LabFolder + "/test.sh", true}, {"/bin/sh -c \"(cd \"" + opt.BaseFolder + opt.DestFolder + "/" + opt.LabFolder + "/\" && ./test.sh)\"", false}, } r, err := NewBuildResult() if err != nil { log.Println(err) return } r.Log = logarray r.Course = opt.Org r.Timestamp = time.Now() r.PushTime = time.Now() r.User = opt.User r.Status = "Active lab assignment" r.Labnum = opt.LabNumber starttime := time.Now() // executes build commands for _, cmd := range cmds { err = execute(&env, cmd.Cmd, r, opt) if err != nil { logOutput(err.Error(), r, opt) log.Println(err) if cmd.Breakable { logOutput("Unexpected end of integration.", r, opt) break } } } r.BuildTime = time.Since(starttime) // parsing the results SimpleParsing(r) if len(r.TestScores) > 0 { r.TotalScore = CalculateTestScore(r.TestScores) } else { if r.NumPasses+r.NumFails != 0 { r.TotalScore = int((float64(r.NumPasses) / float64(r.NumPasses+r.NumFails)) * 100.0) } } if r.NumBuildFailure > 0 { r.TotalScore = 0 } defer func() { // saves the build results if err := r.Save(); err != nil { log.Println("Error saving build results:", err) return } }() // Build for group assignment. Stores build ID in group. if opt.Group > 0 { group, err := git.NewGroup(opt.Org, opt.Group, false) if err != nil { log.Println(err) return } oldbuildID := group.GetLastBuildID(opt.LabNumber) if oldbuildID > 0 { oldr, err := GetBuildResult(oldbuildID) if err != nil { log.Println(err) return } r.Status = oldr.Status if !opt.IsPush { r.PushTime = oldr.PushTime } } group.AddBuildResult(opt.LabNumber, r.ID) if err := group.Save(); err != nil { group.Unlock() log.Println(err) } // build for single user. Stores build ID to user. } else { user, err := git.NewMemberFromUsername(opt.User, false) if err != nil { log.Println(err) return } oldbuildID := user.GetLastBuildID(opt.Org, opt.LabNumber) if oldbuildID > 0 { oldr, err := GetBuildResult(oldbuildID) if err != nil { log.Println(err) return } r.Status = oldr.Status if !opt.IsPush { r.PushTime = oldr.PushTime } } user.AddBuildResult(opt.Org, opt.LabNumber, r.ID) if err := user.Save(); err != nil { user.Unlock() log.Println(err) } } }
// 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 } } }
// getFileResults will read a results file and store the data in the database. // It returns a bool value indicating if the function was successful or not. // It takes as input resultsFile, the name of the results file, labIndex, // the index of the lab, tool, which antiplagiarism tool made the file, // org, a database record for the class, and isGroup, // whether or not the request is for individual or group assignments. func getFileResults(resultsFile string, labIndex int, tool string, org *git.Organization, isGroup bool) bool { buf, err := ioutil.ReadFile(resultsFile) if err != nil { return false } var fileResults apCommon.ResultEntries err = json.Unmarshal(buf, &fileResults) if err != nil { fmt.Printf("Error unmarshalling results from JSON format. File: %s. %s\n", resultsFile, err) return false } if fileResults == nil { return false } for _, fileResult := range fileResults { if isGroup { // Make sure that this is a group if !strings.HasPrefix(fileResult.Repo, "group") { continue } // Get the Group ID groupID, err := strconv.Atoi(fileResult.Repo[len(git.GroupRepoPrefix):]) if err != nil { fmt.Printf("getFileResults: Could not get group number from %s. %s\n", fileResult.Repo, err) continue } // Get the database record group, _ := git.NewGroup(org.Name, groupID, false) // Update the results results := group.GetAntiPlagiarismResults(org.Name, labIndex) if results != nil { switch tool { case "dupl": results.DuplPct = fileResult.Percent results.DuplURL = fileResult.URL case "jplag": results.JplagPct = fileResult.Percent results.JplagURL = fileResult.URL case "moss": results.MossPct = fileResult.Percent results.MossURL = fileResult.URL } group.AddAntiPlagiarismResults(org.Name, labIndex, results) // Save the database record group.Save() } } else { // Make sure that this is a group if strings.HasPrefix(fileResult.Repo, "group") { continue } length := len(fileResult.Repo) username := fileResult.Repo[:length-5] // Get the database record student, _ := git.NewMemberFromUsername(username, false) // Update the results results := student.GetAntiPlagiarismResults(org.Name, labIndex) if results != nil { switch tool { case "dupl": results.DuplPct = fileResult.Percent results.DuplURL = fileResult.URL case "jplag": results.JplagPct = fileResult.Percent results.JplagURL = fileResult.URL case "moss": results.MossPct = fileResult.Percent results.MossURL = fileResult.URL } student.AddAntiPlagiarismResults(org.Name, labIndex, results) // Save the database record student.Save() } } } return true }