Skip to content

Commit ae977c6

Browse files
committed
Added moodle functionality
1 parent aaf323b commit ae977c6

30 files changed

+694
-133
lines changed

grade-management-new/GradeManagement.Bll/GradeManagement.Bll.csproj

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,18 @@
1414
<ItemGroup>
1515
<PackageReference Include="AutoMapper" Version="13.0.1" />
1616
<PackageReference Include="AutSoft.Linq" Version="0.10.0" />
17+
<PackageReference Include="BouncyCastle.Cryptography" Version="2.5.1" />
1718
<PackageReference Include="CsvHelper" Version="32.0.3" />
19+
<PackageReference Include="jose-jwt" Version="5.1.1" />
20+
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
21+
</ItemGroup>
22+
23+
<ItemGroup>
24+
<Reference Include="Microsoft.AspNetCore.Http.Features">
25+
<HintPath>..\..\..\..\.dotnet\shared\Microsoft.AspNetCore.App\8.0.11\Microsoft.AspNetCore.Http.Features.dll</HintPath>
26+
</Reference>
27+
<Reference Include="Microsoft.Extensions.Configuration.Binder">
28+
<HintPath>..\..\..\..\.dotnet\shared\Microsoft.AspNetCore.App\8.0.11\Microsoft.Extensions.Configuration.Binder.dll</HintPath>
29+
</Reference>
1830
</ItemGroup>
1931
</Project>

grade-management-new/GradeManagement.Bll/ServiceCollectionExtensions.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using GradeManagement.Bll.Services;
2+
using GradeManagement.Bll.Services.Moodle;
23

34
using Microsoft.Extensions.DependencyInjection;
45

@@ -24,6 +25,8 @@ public static IServiceCollection AddBllServices(this IServiceCollection services
2425
services.AddTransient<ExerciseService>();
2526
services.AddTransient<UserService>();
2627
services.AddTransient<SubjectTeacherService>();
28+
services.AddTransient<MoodleIntegrationService>();
29+
services.AddTransient<TokenGeneratorService>();
2730

2831
return services;
2932
}

grade-management-new/GradeManagement.Bll/Services/AssignmentEventProcessorService.cs

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -93,12 +93,10 @@ public async Task ConsumePullRequestOpenedEventAsync(PullRequestOpened pullReque
9393
BranchName = pullRequestOpened.BranchName,
9494
AssignmentId = assignment.Id
9595
};
96-
pullRequestToCreate = await pullRequestService.CreateWithoutQfAsync(pullRequestToCreate, assignment.SubjectId);
96+
await pullRequestService.CreateWithoutQfAsync(pullRequestToCreate, assignment.SubjectId);
9797
pullRequest = await pullRequestService.GetModelByUrlWithoutQfAsync(pullRequestOpened.PullRequestUrl);
9898
}
9999

100-
101-
102100
var assignmentLog = new AssignmentLog()
103101
{
104102
EventType = EventType.PullRequestOpened,
@@ -123,15 +121,16 @@ public async Task ConsumeCiEvaluationCompletedEventAsync(CiEvaluationCompleted c
123121
await using var transaction = await gradeManagementDbContext.Database.BeginTransactionAsync();
124122
try
125123
{
126-
var pullRequest = await pullRequestService.GetModelByUrlWithoutQfAsync(ciEvaluationCompleted.PullRequestUrl);
124+
var pullRequest =
125+
await pullRequestService.GetModelByUrlWithoutQfAsync(ciEvaluationCompleted.PullRequestUrl);
127126
var repositoryName = GetRepositoryNameFromUrl(ciEvaluationCompleted.GitHubRepositoryUrl);
128127
var exercise = await exerciseService.GetExerciseModelByGitHubRepoNameWithoutQfAsync(repositoryName);
129128
var studentGitHubId = repositoryName.Remove(0, (exercise.GithubPrefix + "-").Length);
130129
var student = await studentService.GetStudentModelByGitHubIdAsync(studentGitHubId);
131130
var assignment = await assignmentService.GetAssignmentModelByGitHubRepoNameWithoutQfAsync(repositoryName);
132131
var subject = await subjectService.GetModelByIdWithoutQfAsync(exercise.SubjectId);
133132

134-
if(ciEvaluationCompleted.CiApiKey != subject.CiApiKey)
133+
if (ciEvaluationCompleted.CiApiKey != subject.CiApiKey)
135134
{
136135
throw new SecurityTokenException("Invalid API key");
137136
}
@@ -149,7 +148,8 @@ public async Task ConsumeCiEvaluationCompletedEventAsync(CiEvaluationCompleted c
149148

150149
foreach (var scoreEvent in ciEvaluationCompleted.Scores)
151150
{
152-
await scoreService.CreateScoreBasedOnOrderAsync(scoreEvent.Key, scoreEvent.Value, pullRequest.Id, pullRequest.SubjectId, exercise.Id);
151+
await scoreService.CreateScoreBasedOnOrderAsync(scoreEvent.Key, scoreEvent.Value, pullRequest.Id,
152+
pullRequest.SubjectId, exercise.Id);
153153
}
154154

155155
var assignmentLog = new AssignmentLog()
@@ -196,7 +196,7 @@ public async Task ConsumeTeacherAssignedEventAsync(TeacherAssigned teacherAssign
196196
pullRequest.TeacherId = null; // Unassign previous teacher
197197
await gradeManagementDbContext.SaveChangesAsync();
198198

199-
if(teacherAssigned.TeacherGitHubIds == null || teacherAssigned.TeacherGitHubIds.Count == 0)
199+
if (teacherAssigned.TeacherGitHubIds == null || teacherAssigned.TeacherGitHubIds.Count == 0)
200200
{
201201
await transaction.CommitAsync();
202202
return;
@@ -229,7 +229,6 @@ public async Task ConsumeTeacherAssignedEventAsync(TeacherAssigned teacherAssign
229229

230230
await transaction.CommitAsync();
231231
}
232-
233232
}
234233
catch
235234
{
@@ -247,24 +246,24 @@ public async Task ConsumeAssignmentGradedByTeacherEventAsync(AssignmentGradedByT
247246
var assignment = await assignmentService.GetAssignmentModelByGitHubRepoNameWithoutQfAsync(repositoryName);
248247
var exercise = await exerciseService.GetExerciseModelByGitHubRepoNameWithoutQfAsync(repositoryName);
249248
var teacher = await userService.GetModelByGitHubIdAsync(assignmentGradedByTeacher.TeacherGitHubId);
250-
var pullRequest = await pullRequestService.GetModelByUrlWithoutQfAsync(assignmentGradedByTeacher.PullRequestUrl);
249+
var pullRequest =
250+
await pullRequestService.GetModelByUrlWithoutQfAsync(assignmentGradedByTeacher.PullRequestUrl);
251251

252252
if (assignmentGradedByTeacher.Scores.IsNullOrEmpty())
253253
{
254-
var latestScores = await pullRequestService.GetLatestUnapprovedScoreModelsWithoutQfByIdAsync(pullRequest.Id);
254+
var latestScores =
255+
await pullRequestService.GetLatestUnapprovedScoreModelsWithoutQfByIdAsync(pullRequest.Id);
255256
foreach (var scoreEntity in latestScores)
256257
{
257-
scoreEntity.IsApproved = true;
258-
scoreEntity.TeacherId = teacher.Id;
258+
await scoreService.ApproveScoreAsync(scoreEntity.Id, teacher.Id);
259259
}
260-
261-
await gradeManagementDbContext.SaveChangesAsync();
262260
}
263261
else
264262
{
265263
foreach (var eventScore in assignmentGradedByTeacher.Scores)
266264
{
267-
await scoreService.CreateOrApprovePointsFromTeacherInputWithoutQfAsync(eventScore.Key, eventScore.Value, pullRequest.Id, teacher.Id, pullRequest.SubjectId, exercise.Id);
265+
await scoreService.CreateOrApprovePointsFromTeacherInputWithoutQfAsync(eventScore.Key,
266+
eventScore.Value, pullRequest.Id, teacher.Id, pullRequest.SubjectId, exercise.Id);
268267
}
269268
}
270269

@@ -292,7 +291,8 @@ public async Task ConsumePullRequestStatusChangedEventAsync(PullRequestStatusCha
292291
await using var transaction = await gradeManagementDbContext.Database.BeginTransactionAsync();
293292
try
294293
{
295-
var pullRequest = await pullRequestService.GetModelByUrlWithoutQfAsync(pullRequestStatusChanged.PullRequestUrl);
294+
var pullRequest =
295+
await pullRequestService.GetModelByUrlWithoutQfAsync(pullRequestStatusChanged.PullRequestUrl);
296296
pullRequest.Status = pullRequestStatusChanged.pullRequestStatus;
297297
await gradeManagementDbContext.SaveChangesAsync();
298298

grade-management-new/GradeManagement.Bll/Services/ExerciseService.cs

Lines changed: 44 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -31,30 +31,35 @@ public class ExerciseService(
3131
{
3232
public async Task<IEnumerable<ExerciseResponse>> GetAllAsync()
3333
{
34-
return await gradeManagementDbContext.Exercise
34+
var exercises = await gradeManagementDbContext.Exercise
3535
.Include(e => e.ScoreTypeExercises).ThenInclude(ste => ste.ScoreType)
3636
.ProjectTo<ExerciseResponse>(mapper.ConfigurationProvider)
3737
.ToListAsync();
38+
return exercises;
3839
}
3940

4041
public async Task<ExerciseResponse> GetByIdAsync(long id)
4142
{
42-
return await gradeManagementDbContext.Exercise
43-
.Include(e => e.ScoreTypeExercises).ThenInclude(ste => ste.ScoreType)
44-
.ProjectTo<ExerciseResponse>(mapper.ConfigurationProvider)
43+
var exerciseEntity = await gradeManagementDbContext.Exercise
44+
.Include(e => e.ScoreTypeExercises)
45+
.ThenInclude(ste => ste.ScoreType)
4546
.SingleEntityAsync(e => e.Id == id, id);
47+
48+
return mapper.Map<ExerciseResponse>(exerciseEntity);
4649
}
4750

4851
public async Task<ExerciseResponse> CreateAsync(ExerciseRequest requestDto)
4952
{
53+
Exercise exerciseEntity;
5054
await using var transaction = await gradeManagementDbContext.Database.BeginTransactionAsync();
5155
try
5256
{
53-
var exerciseEntity = new Exercise()
57+
exerciseEntity = new Exercise()
5458
{
5559
Name = requestDto.Name,
5660
GithubPrefix = requestDto.GithubPrefix,
57-
dueDate = requestDto.dueDate,
61+
ClassroomUrl = requestDto.ClassroomUrl,
62+
DueDate = requestDto.DueDate,
5863
CourseId = requestDto.CourseId,
5964
SubjectId = gradeManagementDbContext.SubjectIdValue
6065
};
@@ -63,16 +68,17 @@ public async Task<ExerciseResponse> CreateAsync(ExerciseRequest requestDto)
6368
exerciseEntity = await gradeManagementDbContext.Exercise
6469
.SingleEntityAsync(e => e.Id == exerciseEntity.Id, exerciseEntity.Id);
6570
exerciseEntity.ScoreTypeExercises =
66-
await GetScoreTypeExercisesByTypeAndOrderAsync(requestDto.ScoreTypes, exerciseEntity.Id);
71+
await GetScoreTypeExercisesByTypeAndOrdernAsync(requestDto.ScoreTypes, exerciseEntity.Id);
6772
await gradeManagementDbContext.SaveChangesAsync();
6873
await transaction.CommitAsync();
69-
return await GetByIdAsync(exerciseEntity.Id);
7074
}
7175
catch
7276
{
7377
await transaction.RollbackAsync();
7478
throw;
7579
}
80+
81+
return await GetByIdAsync(exerciseEntity.Id);
7682
}
7783

7884
public async Task<ExerciseResponse> UpdateAsync(long id, ExerciseRequest requestDto)
@@ -88,9 +94,10 @@ public async Task<ExerciseResponse> UpdateAsync(long id, ExerciseRequest request
8894

8995
exerciseEntity.Name = requestDto.Name;
9096
exerciseEntity.GithubPrefix = requestDto.GithubPrefix;
91-
exerciseEntity.dueDate = requestDto.dueDate;
97+
exerciseEntity.DueDate = requestDto.DueDate;
9298
exerciseEntity.CourseId = requestDto.CourseId;
93-
exerciseEntity.ScoreTypeExercises = await GetScoreTypeExercisesByTypeAndOrderAsync(requestDto.ScoreTypes, id);
99+
exerciseEntity.ScoreTypeExercises =
100+
await GetScoreTypeExercisesByTypeAndOrderInTransactionAsync(requestDto.ScoreTypes, id);
94101

95102
await gradeManagementDbContext.SaveChangesAsync();
96103
return await GetByIdAsync(exerciseEntity.Id);
@@ -121,22 +128,13 @@ public async Task<Exercise> GetExerciseModelByGitHubRepoNameWithoutQfAsync(strin
121128
.SingleEntityAsync(e => githubRepoName.StartsWith(e.GithubPrefix), 0);
122129
}
123130

124-
private async Task<List<ScoreTypeExercise>> GetScoreTypeExercisesByTypeAndOrderAsync(
131+
private async Task<List<ScoreTypeExercise>> GetScoreTypeExercisesByTypeAndOrderInTransactionAsync(
125132
Dictionary<int, string> scoreTypes, long exerciseId)
126133
{
127134
await using var transaction = await gradeManagementDbContext.Database.BeginTransactionAsync();
128135
try
129136
{
130-
foreach (var (order, type) in scoreTypes)
131-
{
132-
var scoreType = await scoreTypeService.GetOrCreateScoreTypeByTypeStringAsync(type);
133-
gradeManagementDbContext.ScoreTypeExercise.Add(new ScoreTypeExercise
134-
{
135-
ScoreTypeId = scoreType.Id, ExerciseId = exerciseId, Order = order
136-
});
137-
}
138-
139-
await gradeManagementDbContext.SaveChangesAsync();
137+
await GetScoreTypeExercisesByTypeAndOrdernAsync(scoreTypes, exerciseId);
140138

141139
await transaction.CommitAsync();
142140
}
@@ -146,74 +144,32 @@ private async Task<List<ScoreTypeExercise>> GetScoreTypeExercisesByTypeAndOrderA
146144
throw;
147145
}
148146

149-
return gradeManagementDbContext.ScoreTypeExercise.Where(s => s.ExerciseId == exerciseId).ToList();
150-
}
151-
152-
public async Task<ScoreType> GetScoreTypeByOrderAndExerciseIdAsync(int order, long exerciseId)
153-
{
154-
var scoreTypeExercise = await gradeManagementDbContext.ScoreTypeExercise
155-
.Include(ste => ste.ScoreType)
156-
.SingleEntityAsync(ste => ste.ExerciseId == exerciseId && ste.Order == order, 0);
157-
return scoreTypeExercise.ScoreType;
147+
return await gradeManagementDbContext.ScoreTypeExercise.Where(s => s.ExerciseId == exerciseId).ToListAsync();
158148
}
159149

160-
public async Task<string> GetCsvByExerciseId(long exerciseId)
150+
private async Task<List<ScoreTypeExercise>> GetScoreTypeExercisesByTypeAndOrdernAsync(
151+
Dictionary<int, string> scoreTypes, long exerciseId)
161152
{
162-
var assignments = await gradeManagementDbContext.Assignment
163-
.Where(a => a.ExerciseId == exerciseId)
164-
.Include(assignment => assignment.Student)
165-
.ToListAsync();
166-
167-
var records = new List<Dictionary<string, object>>();
168-
169-
foreach (var assignment in assignments)
153+
foreach (var (order, type) in scoreTypes)
170154
{
171-
var pullRequest = await assignmentService.GetMergedPullRequestModelByIdAsync(assignment.Id);
172-
if (pullRequest == null)
173-
{
174-
continue;
175-
}
176-
177-
var scores = await pullRequestService.GetApprovedScoreModelsByIdAsync(pullRequest.Id);
178-
var record = new Dictionary<string, object>
155+
var scoreType = await scoreTypeService.GetOrCreateScoreTypeByTypeStringAsync(type);
156+
gradeManagementDbContext.ScoreTypeExercise.Add(new ScoreTypeExercise
179157
{
180-
{ "NeptunCode", assignment.Student.NeptunCode }, { "SumOfScores", scores.Sum(s => s.Value) }
181-
};
182-
foreach (var score in scores)
183-
{
184-
record[score.ScoreType.Type] = score.Value;
185-
}
186-
187-
188-
records.Add(record);
189-
}
190-
191-
await using var writer = new StringWriter();
192-
await using var csv = new CsvWriter(writer, CultureInfo.InvariantCulture);
193-
194-
// Write the header
195-
var keys = records[0].Keys;
196-
foreach (var key in keys)
197-
{
198-
csv.WriteField(key);
158+
ScoreTypeId = scoreType.Id, ExerciseId = exerciseId, Order = order
159+
});
199160
}
200161

201-
await csv.NextRecordAsync();
202-
203-
// Write the records
204-
foreach (var record in records)
205-
{
206-
foreach (var value in record.Values)
207-
{
208-
csv.WriteField(value);
209-
}
210-
211-
await csv.NextRecordAsync();
212-
}
162+
await gradeManagementDbContext.SaveChangesAsync();
213163

214-
await csv.FlushAsync();
164+
return await gradeManagementDbContext.ScoreTypeExercise.Where(s => s.ExerciseId == exerciseId).ToListAsync();
165+
}
215166

216-
return writer.ToString();
167+
public async Task<ScoreType> GetScoreTypeByOrderAndExerciseIdAsync(int order, long exerciseId)
168+
{
169+
var scoreTypeExercise = await gradeManagementDbContext.ScoreTypeExercise
170+
.Include(ste => ste.ScoreType)
171+
.SingleEntityAsync(ste => ste.ExerciseId == exerciseId && ste.Order == order, 0);
172+
return scoreTypeExercise.ScoreType;
217173
}
218174

219175
public async Task<IEnumerable<Shared.Dtos.ScoreTypeExercise>> GetScoreTypeExercisesByIdAsync(long id)
@@ -225,4 +181,12 @@ public async Task<string> GetCsvByExerciseId(long exerciseId)
225181
.ProjectTo<Shared.Dtos.ScoreTypeExercise>(mapper.ConfigurationProvider)
226182
.ToListAsync();
227183
}
184+
185+
public async Task SetMoodleScoreUrlByClassroomUrlWithoutQueryFilterAsync(string clasroomUrl, string moodleScoreUrl)
186+
{
187+
var exercise = await gradeManagementDbContext.Exercise.IgnoreQueryFiltersButNotIsDeleted().SingleEntityAsync(e => e.ClassroomUrl == clasroomUrl, 0);
188+
if (exercise.MoodleScoreUrl == moodleScoreUrl) return;
189+
exercise.MoodleScoreUrl = moodleScoreUrl;
190+
await gradeManagementDbContext.SaveChangesAsync();
191+
}
228192
}

0 commit comments

Comments
 (0)