/// <summary>
 /// Constructor.
 /// </summary>
 public StudentDownloadRequest(
     ClassroomMembership student,
     bool submitted)
 {
     Student   = student;
     Submitted = submitted;
 }
Esempio n. 2
0
        /// <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;
 }
Esempio n. 4
0
 /// <summary>
 /// Constructor.
 /// </summary>
 public StudentSubmission(
     ClassroomMembership student,
     IArchive contents)
 {
     Student  = student;
     Contents = contents;
 }
Esempio n. 5
0
        /// <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);
        }
Esempio n. 6
0
        /// <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));
        }
Esempio n. 7
0
        /// <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());
            }
        }
Esempio n. 8
0
        /// <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));
        }
Esempio n. 9
0
        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);
        }
Esempio n. 10
0
        /// <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
            );
        }
Esempio n. 11
0
 /// <summary>
 /// Returns a new student submission.
 /// </summary>
 private StudentSubmission GetSubmission(
     ClassroomMembership student,
     IArchive contents)
 {
     return(new StudentSubmission(student, contents));
 }
Esempio n. 12
0
        /// <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}");
 }
Esempio n. 15
0
 /// <summary>
 /// Returns the name of the student project repository.
 /// </summary>
 public string GetStudentRepoName(ClassroomMembership student)
 {
     return($"{Name}_{student.GitHubTeam}");
 }
Esempio n. 16
0
        /// <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);
                    }
                }
            }
        }