/// <summary> /// Builds a submission archive containing the submissions of /// all students. /// </summary> public async Task <Stream> BuildSubmissionArchiveAsync( Project project, IArchive templateContents, IList <StudentSubmission> submissions, ProjectSubmissionDownloadFormat format) { var stream = _fileSystem.CreateNewTempFile(); using (ZipArchive archive = new ZipArchive(stream, ZipArchiveMode.Create, leaveOpen: true)) { foreach (var result in submissions) { await WriteSubmissionToArchiveAsync ( archive, project, result.Student, templateContents, result.Contents, format ); result.Contents.Dispose(); } } stream.Position = 0; return(stream); }
/// <summary> /// Downloads the submissions for a given checkpoint, according to the /// options specified by the user, in the form of a zip archive. /// </summary> public async Task <Stream> DownloadSubmissionsAsync( string classroomName, string projectName, string checkpointName, IList <int> selectedUserIds, ProjectSubmissionDownloadFormat format) { var checkpoint = await LoadCheckpointAsync ( classroomName, projectName, checkpointName ); var allCheckpointSubmissions = GetCheckpointSubmissionsQuery ( checkpoint, section: null // Include all sections ); var selectedUserIdsHash = new HashSet <int>(selectedUserIds); var userIdsWithSubmissions = new HashSet <int> ( await allCheckpointSubmissions .Where(submission => selectedUserIdsHash.Contains(submission.Commit.UserId)) .GroupBy(submission => submission.Commit.User) .Select(group => group.Key.Id) .ToListAsync() ); var sectionMemberships = await LoadSectionMembershipsAsync(classroomName); var studentDownloadRequests = sectionMemberships .Where ( sm => selectedUserIdsHash.Contains(sm.ClassroomMembership.UserId) ) .Select ( sm => new StudentDownloadRequest ( sm.ClassroomMembership, userIdsWithSubmissions.Contains(sm.ClassroomMembership.User.Id) ) ) .ToList(); var templateContents = await _submissionDownloader.DownloadTemplateContentsAsync ( checkpoint.Project ); var submissionContents = await _submissionDownloader.DownloadSubmissionsAsync ( checkpoint, studentDownloadRequests ); return(await _submissionArchiveBuilder.BuildSubmissionArchiveAsync ( checkpoint.Project, templateContents, submissionContents, format )); }
/// <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); } } } }