/// <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); } } } }
/// <summary> /// Constructor. /// </summary> public StudentRepoPushEvents( ClassroomMembership student, IList<GitHubPushEvent> pushEvents) { Student = student; PushEvents = pushEvents; }
/// <summary> /// Constructor. /// </summary> public StudentDownloadRequest( ClassroomMembership student, bool submitted) { Student = student; Submitted = submitted; }
/// <summary> /// Constructor. /// </summary> public StudentSubmission( ClassroomMembership student, IArchive contents) { Student = student; Contents = contents; }
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> /// 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); }
/// <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.OverwriteRepositoryAsync ( repository, c_starterCommitMessage, templateContents, entry => project.GetFileType(entry) != FileType.Private, entry => project.GetFileType(entry) == FileType.Immutable ); await _repoClient.EnsurePushWebhookAsync(repository, webhookUrl); 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> /// Returns the name of the student project repository. /// </summary> public string GetStudentRepoName(ClassroomMembership student) { return $"{Name}_{student.GitHubTeam}"; }
/// <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 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> /// 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> /// 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 the repository name for the given student/project. /// </summary> public string GetRepoName(Project project, ClassroomMembership student) { return $"{project.Name}_{student.GitHubTeam}"; }