/// <summary> /// Constructor. /// </summary> public StudentDownloadRequest( ClassroomMembership student, bool submitted) { Student = student; Submitted = submitted; }
/// <summary> /// Returns whether or not the given user is in the classroom's GitHub organization. /// </summary> private async Task <bool> EnsureGitHubMembershipUpdated( User user, ClassroomMembership membership) { if (membership.InGitHubOrganization) { return(true); } await _dbContext.Entry(membership) .Reference(cm => cm.Classroom) .LoadAsync(); var memberInOrganization = await _gitHubOrgClient.CheckMemberAsync ( membership.Classroom.GitHubOrganization, user.GitHubLogin ); if (memberInOrganization) { membership.InGitHubOrganization = true; _dbContext.ClassroomMemberships.Update(membership); return(true); } else { return(false); } }
/// <summary> /// Constructor. /// </summary> public StudentRepoPushEvents( ClassroomMembership student, IList <GitHubPushEvent> pushEvents) { Student = student; PushEvents = pushEvents; }
/// <summary> /// Constructor. /// </summary> public StudentSubmission( ClassroomMembership student, IArchive contents) { Student = student; Contents = contents; }
/// <summary> /// Ensures a classroom membership exists for the given user and role. /// The caller is responsible for saving changes. /// </summary> private async Task <ClassroomMembership> EnsureClassroomMembershipAsync( User user, Classroom classroom, ClassroomRole role) { var classroomMembership = user?.ClassroomMemberships ?.SingleOrDefault(m => m.ClassroomId == classroom.Id); if (user.ClassroomMemberships == null) { user.ClassroomMemberships = new List <ClassroomMembership>(); } if (classroomMembership == null) { classroomMembership = new ClassroomMembership() { ClassroomId = classroom.Id, Classroom = classroom, InGitHubOrganization = false, GitHubTeam = await GetNewGitHubTeamNameAsync(classroom, user), Role = role }; user.ClassroomMemberships.Add(classroomMembership); } else if (role > classroomMembership.Role) { classroomMembership.Role = role; } return(classroomMembership); }
/// <summary> /// Returns all commits for the given user/project. /// </summary> private async Task <ICollection <GitHubCommit> > GetAllCommitsAsync( Project project, ClassroomMembership student) { var orgName = student.Classroom.GitHubOrganization; var repoName = project.GetStudentRepoName(student); return(await _repoClient.GetAllCommitsAsync(orgName, repoName)); }
/// <summary> /// Returns the file's contents, with any applicable /// transformations applied. /// </summary> public byte[] GetFileContents( Project project, ClassroomMembership student, IArchiveFile entry) { if (entry.FullPath.EndsWith(".project")) { var newContents = entry.GetEncodedData().Replace ( $"<name>{project.Name}</name>", $"<name>{project.Name}_{student.GitHubTeam}</name>" ); using (var memoryStream = new MemoryStream()) using (var streamWriter = new StreamWriter(memoryStream)) { streamWriter.Write(newContents); streamWriter.Flush(); return(memoryStream.ToArray()); } } else if (entry.FullPath.EndsWith(".classpath")) { var jUnitPath = "org.eclipse.jdt.junit.JUNIT_CONTAINER/4"; using (var stream = new MemoryStream(entry.GetRawData())) { var projectNode = XElement.Load(stream); var hasJUnit = projectNode .Elements(XName.Get("classpathentry")) .Any ( elt => elt.Attribute(XName.Get("path")) != null && elt.Attribute(XName.Get("path")).Value == jUnitPath ); if (!hasJUnit) { projectNode.Add(XElement.Parse($"<classpathentry kind=\"con\" path=\"{jUnitPath}\"/>")); } using (var newStream = new MemoryStream()) { projectNode.Save(newStream); newStream.Flush(); return(newStream.ToArray()); } } } else { return(entry.GetRawData()); } }
/// <summary> /// Returns a list of push events for the given project. /// </summary> private async Task <StudentRepoPushEvents> GetAllPushEventsAsync( ClassroomMembership student, GitHubRepository repository) { var pushEvents = await _repoClient.GetPushEventsAsync ( repository.Owner, repository.Name ); return(new StudentRepoPushEvents(student, pushEvents)); }
public void GetRepoName_ReturnsCorrectName() { var repoMetadataRetriever = new RepositoryMetadataRetriever(repoClient: null); var project = new Project() { Name = "Project1" }; var student = new ClassroomMembership() { GitHubTeam = "LastNameFirstName" }; var result = repoMetadataRetriever.GetRepoName(project, student); Assert.Equal("Project1_LastNameFirstName", result); }
/// <summary> /// Invites the user to a new GitHub team. /// </summary> private async Task EnsureUserInGithubOrgAsync(User user, ClassroomMembership membership) { if (membership.InGitHubOrganization) { return; } var team = await _gitHubTeamClient.CreateTeamAsync ( membership.Classroom.GitHubOrganization, membership.GitHubTeam ); await _gitHubTeamClient.InviteUserToTeamAsync ( membership.Classroom.GitHubOrganization, team, user.GitHubLogin ); }
/// <summary> /// Returns a new student submission. /// </summary> private StudentSubmission GetSubmission( ClassroomMembership student, IArchive contents) { return(new StudentSubmission(student, contents)); }
/// <summary> /// Creates a repository for the given student, and pushes the non-test files /// from the source project to the new repository. /// </summary> private async Task <CreateAndPushResult> CreateAndPushAsync( Project project, ClassroomMembership student, string webhookUrl, bool overwriteIfSafe, ICollection <GitHubTeam> teams, ICollection <GitHubRepository> repositories, IArchive templateContents) { string orgName = project.Classroom.GitHubOrganization; string repoName = $"{project.Name}_{student.GitHubTeam}"; try { var repository = repositories.SingleOrDefault(repo => repo.Name == repoName); var team = teams.First(teamCandidate => teamCandidate.Name == student.GitHubTeam); bool repositoryAlreadyExisted = (repository != null); if (repositoryAlreadyExisted) { if (!overwriteIfSafe) { return(CreateAndPushResult.Exists); } var commits = await _repoClient.GetAllCommitsAsync(orgName, repoName); if (commits.Count > c_numInitialCommits) { return(CreateAndPushResult.Exists); } } else { repository = await _repoClient.CreateRepositoryAsync ( orgName, repoName, team, overwrite : false ); var staffTeam = GetStaffTeam(project.Classroom, teams); if (staffTeam != null) { await _teamClient.AddRepositoryAsync(orgName, repoName, staffTeam); } } await _repoClient.EnsurePushWebhookAsync(repository, webhookUrl); await _repoClient.OverwriteRepositoryAsync ( repository, c_starterCommitMessage, templateContents, entry => project.GetFileType(entry) != FileType.Private, entry => project.GetFileType(entry) == FileType.Immutable ); return(repositoryAlreadyExisted ? CreateAndPushResult.Overwritten : CreateAndPushResult.Created); } catch (Exception ex) { _logger.LogError ( (EventId)0, ex, "Failed to create repository {RepoName} in organization {Org}.", repoName, orgName ); return(CreateAndPushResult.Failed); } }
/// <summary> /// Writes the contents of a submission to an archive. /// </summary> private async Task WriteSubmissionToArchiveAsync( ZipArchive archive, Project project, ClassroomMembership student, IArchive templateContents, IArchive submissionContents, ProjectSubmissionDownloadFormat format) { bool includeEclipseProjects = ( format == ProjectSubmissionDownloadFormat.Eclipse || format == ProjectSubmissionDownloadFormat.All ); bool includeFlatFiles = ( format == ProjectSubmissionDownloadFormat.Flat || format == ProjectSubmissionDownloadFormat.All ); // Submissions are grouped by section. If a student is in multiple // sections, just grab the first one string sectionName = student.SectionMemberships.First().Section.Name; var studentFolder = $"EclipseProjects\\{sectionName}\\{student.GitHubTeam}"; // The project will contain all non-immutable submission files, // plus all immutable and private files from the template project. var projectContents = submissionContents.Files .Where ( entry => project.GetFileType(entry) == FileType.Public ) .Concat ( templateContents.Files.Where ( entry => project.GetFileType(entry) != FileType.Public ) ) .ToList(); foreach (var entry in projectContents) { if (ExcludeEntry(project, entry)) { continue; } var contents = _transformer.GetFileContents(project, student, entry); var archiveFilePath = entry.FullPath; var fileName = archiveFilePath.Substring(archiveFilePath.LastIndexOf("/") + 1); if (includeEclipseProjects) { var archiveFileFolder = archiveFilePath.Contains("/") ? archiveFilePath.Substring(0, archiveFilePath.LastIndexOf("/")) : archiveFilePath; var localFileFolder = $"{studentFolder}\\{archiveFileFolder}"; var localFilePath = $"{localFileFolder}\\{fileName}"; // Add the file to the student project folder. var projectFolderEntry = archive.CreateEntry(localFilePath); using (Stream stream = projectFolderEntry.Open()) { await stream.WriteAsync(contents, offset : 0, count : contents.Length); } } // Add the file to the folder containing all files, if applicable. if (includeFlatFiles && fileName.EndsWith(".java") && project.GetFileType(entry) == FileType.Public) { // Files are grouped by base name, and then by section var baseFileName = fileName.Substring(0, fileName.LastIndexOf(".")); var allFilesEntry = archive.CreateEntry($"AllFiles\\{baseFileName}\\{sectionName}\\{student.GitHubTeam}-{fileName}"); using (Stream stream = allFilesEntry.Open()) { await stream.WriteAsync(contents, offset : 0, count : contents.Length); } } } }
/// <summary> /// Returns the repository name for the given student/project. /// </summary> public string GetRepoName(Project project, ClassroomMembership student) { return($"{project.Name}_{student.GitHubTeam}"); }
/// <summary> /// Returns the name of the student project repository. /// </summary> public string GetStudentRepoName(ClassroomMembership student) { return($"{Name}_{student.GitHubTeam}"); }
/// <summary> /// Writes the contents of a submission to an archive. /// </summary> private async Task WriteSubmissionToArchiveAsync( ZipArchive archive, Project project, ClassroomMembership student, IArchive templateContents, IArchive submissionContents) { var studentFolder = $"EclipseProjects\\{student.GitHubTeam}"; // The project will contain all non-immutable submission files, // plus all immutable and private files from the template project. var projectContents = submissionContents.Files .Where ( entry => project.GetFileType(entry) == FileType.Public ) .Concat ( templateContents.Files.Where ( entry => project.GetFileType(entry) != FileType.Public ) ) .ToList(); foreach (var entry in projectContents) { if (ExcludeEntry(project, entry)) { continue; } var contents = _transformer.GetFileContents(project, student, entry); var archiveFilePath = entry.FullPath; var archiveFileFolder = archiveFilePath.Contains("/") ? archiveFilePath.Substring(0, archiveFilePath.LastIndexOf("/")) : archiveFilePath; var localFileFolder = $"{studentFolder}\\{archiveFileFolder}"; var fileName = archiveFilePath.Substring(archiveFilePath.LastIndexOf("/") + 1); var localFilePath = $"{localFileFolder}\\{fileName}"; // Add the file to the student project folder. var projectFolderEntry = archive.CreateEntry(localFilePath); using (Stream stream = projectFolderEntry.Open()) { await stream.WriteAsync(contents, offset : 0, count : contents.Length); } // Add the file to the folder containing all files, if applicable. if (fileName.EndsWith(".java") && project.GetFileType(entry) == FileType.Public) { var allFilesEntry = archive.CreateEntry($"AllFiles\\{student.GitHubTeam}-{fileName}"); using (Stream stream = allFilesEntry.Open()) { await stream.WriteAsync(contents, offset : 0, count : contents.Length); } } } }