Skip to content

Commit 378831c

Browse files
authored
Merge branch 'v2-development' into 1664-improve-exceptiontype-in-submission-execution
2 parents 34ac708 + ed8222f commit 378831c

File tree

16 files changed

+150
-90
lines changed

16 files changed

+150
-90
lines changed

Servers/UI/OJS.Servers.Ui/ClientApp/src/components/mentor/Mentor.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ const Mentor = (props: IMentorProps) => {
4747
problemId: -1,
4848
},
4949
]);
50+
const inputRef = useRef<HTMLInputElement>(null);
5051
const messagesEndRef = useRef<HTMLDivElement>(null);
5152

5253
const [ startConversation, { data: conversationData, error, isLoading } ] = useStartConversationMutation();
@@ -141,6 +142,7 @@ const Mentor = (props: IMentorProps) => {
141142
});
142143

143144
setInputMessage('');
145+
inputRef.current?.focus();
144146
};
145147

146148
const handleKeyPress = (e: React.KeyboardEvent) => {
@@ -277,13 +279,13 @@ const Mentor = (props: IMentorProps) => {
277279
fullWidth
278280
multiline
279281
maxRows={4}
282+
inputRef={inputRef}
280283
value={inputMessage}
281284
onChange={(e) => setInputMessage(e.target.value)}
282285
onKeyDown={handleKeyPress}
283286
placeholder="Напишете вашето съобщение..."
284287
variant="standard"
285288
size="small"
286-
disabled={isLoading}
287289
className={styles.typingField}
288290
/>
289291
<div className={styles.sendButtonContainer}>
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
namespace OJS.Services.Ui.Business.Cache;
2+
3+
using OJS.Services.Common.Models.Users;
4+
using OJS.Services.Infrastructure;
5+
using System.Threading.Tasks;
6+
7+
public interface ILecturersInContestsCacheService : IService
8+
{
9+
/// <summary>
10+
/// Check if user is admin or lecturer in contest, or in category for the contest.
11+
/// Uses in-memory cache for all the lecturers in contests and categories, which is cleared on a short interval.
12+
/// </summary>
13+
/// <param name="contestId">The contest to check for</param>
14+
/// <param name="categoryId">The category of the contest</param>
15+
/// <param name="user">The user to check</param>
16+
/// <returns>True if user is admin or lecturer in contest, or in category for the contest.</returns>
17+
Task<bool> IsUserAdminOrLecturerInContest(int? contestId, int? categoryId, UserInfoModel? user);
18+
}
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
namespace OJS.Services.Ui.Business.Cache.Implementations;
2+
3+
using Microsoft.EntityFrameworkCore;
4+
using Microsoft.Extensions.Caching.Memory;
5+
using OJS.Data.Models;
6+
using OJS.Services.Common.Data;
7+
using OJS.Services.Common.Models.Users;
8+
using System;
9+
using System.Linq;
10+
using System.Threading.Tasks;
11+
12+
public class LecturersInContestsCacheService(
13+
IMemoryCache memoryCache,
14+
IDataService<LecturerInContest> lecturerInContests,
15+
IDataService<LecturerInContestCategory> lecturerInContestCategories)
16+
: ILecturersInContestsCacheService
17+
{
18+
private const string LecturersInContestsCacheKey = "LecturersInContests";
19+
private const string LecturersInContestCategoriesCacheKey = "LecturersInContestCategories";
20+
private readonly TimeSpan cacheExpiration = TimeSpan.FromMinutes(5);
21+
22+
public async Task<bool> IsUserAdminOrLecturerInContest(int? contestId, int? categoryId, UserInfoModel? user)
23+
{
24+
if (contestId is null or 0 || categoryId is null or 0 || user is null)
25+
{
26+
return false;
27+
}
28+
29+
if (user is { IsLecturer: false, IsAdmin: false })
30+
{
31+
return false;
32+
}
33+
34+
if (user.IsAdmin)
35+
{
36+
return true;
37+
}
38+
39+
var lecturersInContestCategories = await memoryCache.GetOrCreateAsync(
40+
LecturersInContestCategoriesCacheKey,
41+
async entry =>
42+
{
43+
entry.AbsoluteExpirationRelativeToNow = this.cacheExpiration;
44+
45+
return await lecturerInContestCategories
46+
.GetQuery()
47+
.GroupBy(l => l.LecturerId)
48+
.ToDictionaryAsync(g => g.Key, g => g.Select(l => l.ContestCategoryId));
49+
});
50+
51+
var userIsLecturerInCategory = lecturersInContestCategories != null &&
52+
lecturersInContestCategories.TryGetValue(user.Id, out var categories) &&
53+
categories.Contains(categoryId.Value);
54+
55+
if (userIsLecturerInCategory)
56+
{
57+
return true;
58+
}
59+
60+
var lecturersInContests = await memoryCache.GetOrCreateAsync(
61+
LecturersInContestsCacheKey,
62+
async entry =>
63+
{
64+
entry.AbsoluteExpirationRelativeToNow = this.cacheExpiration;
65+
66+
return await lecturerInContests
67+
.GetQuery()
68+
.GroupBy(l => l.LecturerId)
69+
.ToDictionaryAsync(g => g.Key, g => g.Select(l => l.ContestId));
70+
});
71+
72+
var userIsLecturerInContest = lecturersInContests != null &&
73+
lecturersInContests.TryGetValue(user.Id, out var contests) &&
74+
contests.Contains(contestId.Value);
75+
76+
return userIsLecturerInContest;
77+
}
78+
}

Services/UI/OJS.Services.Ui.Business/ILecturersInContestsBusinessService.cs

Lines changed: 0 additions & 9 deletions
This file was deleted.

Services/UI/OJS.Services.Ui.Business/Implementations/ContestResultsBusinessService.cs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ public class ContestResultsBusinessService : IContestResultsBusinessService
2525
private readonly IContestResultsAggregatorCommonService contestResultsAggregator;
2626
private readonly IContestsDataService contestsData;
2727
private readonly IContestResultsValidationService contestResultsValidation;
28-
private readonly ILecturersInContestsBusinessService lecturersInContestsBusinessService;
28+
private readonly ILecturersInContestsCacheService lecturersInContestsCache;
2929
private readonly IUserProviderService userProvider;
3030
private readonly IParticipantsDataService participantsData;
3131
private readonly IContestsCacheService contestsCache;
@@ -35,7 +35,7 @@ public ContestResultsBusinessService(
3535
IContestResultsAggregatorCommonService contestResultsAggregator,
3636
IContestsDataService contestsData,
3737
IContestResultsValidationService contestResultsValidation,
38-
ILecturersInContestsBusinessService lecturersInContestsBusinessService,
38+
ILecturersInContestsCacheService lecturersInContestsCache,
3939
IUserProviderService userProvider,
4040
IParticipantsDataService participantsData,
4141
IContestsCacheService contestsCache,
@@ -44,7 +44,7 @@ public ContestResultsBusinessService(
4444
this.contestResultsAggregator = contestResultsAggregator;
4545
this.contestsData = contestsData;
4646
this.contestResultsValidation = contestResultsValidation;
47-
this.lecturersInContestsBusinessService = lecturersInContestsBusinessService;
47+
this.lecturersInContestsCache = lecturersInContestsCache;
4848
this.userProvider = userProvider;
4949
this.participantsData = participantsData;
5050
this.contestsCache = contestsCache;
@@ -56,7 +56,8 @@ public async Task<ContestResultsViewModel> GetContestResults(int contestId, bool
5656
var contest = await this.contestsCache.GetContestDetailsServiceModel(contestId)
5757
?? throw new BusinessServiceException("Contest does not exist or is deleted.");
5858

59-
var isUserAdminOrLecturer = await this.lecturersInContestsBusinessService.IsCurrentUserAdminOrLecturerInContest(contestId);
59+
var user = this.userProvider.GetCurrentUser();
60+
var isUserAdminOrLecturer = await this.lecturersInContestsCache.IsUserAdminOrLecturerInContest(contestId, contest.CategoryId, user);
6061
var contestActivity = await this.contestsActivity.GetContestActivity(contest.Map<ContestForActivityServiceModel>());
6162

6263
var validationResult = this.contestResultsValidation.GetValidationResult((contestActivity, isFullResults, official, isUserAdminOrLecturer));
@@ -66,7 +67,6 @@ public async Task<ContestResultsViewModel> GetContestResults(int contestId, bool
6667
throw new BusinessServiceException(validationResult.Message);
6768
}
6869

69-
var user = this.userProvider.GetCurrentUser();
7070
var problems = contest.Problems.MapCollection<Problem>().ToList();
7171

7272
var contestResultsModel = new ContestResultsModel
@@ -75,7 +75,7 @@ public async Task<ContestResultsViewModel> GetContestResults(int contestId, bool
7575
Problems = problems,
7676
CategoryId = contest.CategoryId.GetValueOrDefault(),
7777
Official = official,
78-
IsUserAdminOrLecturer = user.IsAdminOrLecturer,
78+
IsUserAdminOrLecturer = isUserAdminOrLecturer,
7979
IsFullResults = isFullResults,
8080
TotalResultsCount = null,
8181
IsExportResults = false,

Services/UI/OJS.Services.Ui.Business/Implementations/ContestsBusinessService.cs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ public class ContestsBusinessService : IContestsBusinessService
3636
private readonly IContestParticipationValidationService contestParticipationValidationService;
3737
private readonly IContestParticipantsCacheService contestParticipantsCacheService;
3838
private readonly IContestsCacheService contestsCacheService;
39-
private readonly ILecturersInContestsBusinessService lecturersInContestsBusiness;
39+
private readonly ILecturersInContestsCacheService lecturersInContestsCache;
4040
private readonly IContestDetailsValidationService contestDetailsValidationService;
4141

4242
public ContestsBusinessService(
@@ -50,7 +50,7 @@ public ContestsBusinessService(
5050
IContestParticipationValidationService contestParticipationValidationService,
5151
IContestParticipantsCacheService contestParticipantsCacheService,
5252
IContestsCacheService contestsCacheService,
53-
ILecturersInContestsBusinessService lecturersInContestsBusiness,
53+
ILecturersInContestsCacheService lecturersInContestsCache,
5454
IContestDetailsValidationService contestDetailsValidationService)
5555
{
5656
this.contestsData = contestsData;
@@ -63,7 +63,7 @@ public ContestsBusinessService(
6363
this.contestParticipationValidationService = contestParticipationValidationService;
6464
this.contestParticipantsCacheService = contestParticipantsCacheService;
6565
this.contestsCacheService = contestsCacheService;
66-
this.lecturersInContestsBusiness = lecturersInContestsBusiness;
66+
this.lecturersInContestsCache = lecturersInContestsCache;
6767
this.contestDetailsValidationService = contestDetailsValidationService;
6868
}
6969

@@ -72,7 +72,7 @@ public async Task<ContestDetailsServiceModel> GetContestDetails(int id)
7272
var user = this.userProviderService.GetCurrentUser();
7373
var contest = await this.contestsCacheService.GetContestDetailsServiceModel(id);
7474
var category = await this.contestCategoriesCache.GetById(contest?.CategoryId);
75-
var isLecturerInContestOrAdmin = await this.lecturersInContestsBusiness.IsCurrentUserAdminOrLecturerInContest(contest?.Id);
75+
var isLecturerInContestOrAdmin = await this.lecturersInContestsCache.IsUserAdminOrLecturerInContest(contest?.Id, contest?.CategoryId, user);
7676

7777
var validationResult = this.contestDetailsValidationService.GetValidationResult((
7878
contest,
@@ -173,7 +173,7 @@ public async Task<ContestRegistrationDetailsServiceModel> GetContestRegistration
173173
throw new BusinessServiceException(validationResult.Message);
174174
}
175175

176-
var userIsAdminOrLecturerInContest = await this.lecturersInContestsBusiness.IsCurrentUserAdminOrLecturerInContest(contest?.Id);
176+
var userIsAdminOrLecturerInContest = await this.lecturersInContestsCache.IsUserAdminOrLecturerInContest(contest?.Id, contest?.CategoryId, user);
177177

178178
contest!.RequirePassword = ShouldRequirePassword(contest.HasContestPassword, contest.HasPracticePassword, participant!, isOfficial);
179179
contest.ParticipantId = participant?.Id;
@@ -215,7 +215,7 @@ public async Task<bool> RegisterUserForContest(
215215
throw new BusinessServiceException(validationResult.Message);
216216
}
217217

218-
var userIsAdminOrLecturerInContest = await this.lecturersInContestsBusiness.IsCurrentUserAdminOrLecturerInContest(contest?.Id);
218+
var userIsAdminOrLecturerInContest = await this.lecturersInContestsCache.IsUserAdminOrLecturerInContest(contest?.Id, contest?.CategoryId, user);
219219
var shouldRequirePassword = ShouldRequirePassword(contest!.HasContestPassword, contest!.HasPracticePassword, participantForActivity, isOfficial);
220220
var shouldConfirmParticipation =
221221
ShouldConfirmParticipation(participantForActivity, isOfficial, contest!.IsOnlineExam, userIsAdminOrLecturerInContest);
@@ -308,7 +308,7 @@ public async Task<ContestParticipationServiceModel> GetParticipationDetails(
308308
throw new BusinessServiceException(validationResult.Message);
309309
}
310310

311-
var userIsAdminOrLecturerInContest = await this.lecturersInContestsBusiness.IsCurrentUserAdminOrLecturerInContest(contest?.Id);
311+
var userIsAdminOrLecturerInContest = await this.lecturersInContestsCache.IsUserAdminOrLecturerInContest(contest?.Id, contest?.CategoryId, user);
312312

313313
participant.IsRegisteredParticipant = true;
314314
participant.Contest!.UserIsAdminOrLecturerInContest = userIsAdminOrLecturerInContest;

Services/UI/OJS.Services.Ui.Business/Implementations/LecturersInContestsBusinessService.cs

Lines changed: 0 additions & 32 deletions
This file was deleted.

Services/UI/OJS.Services.Ui.Business/Implementations/SubmissionsBusinessService.cs

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ public class SubmissionsBusinessService : ISubmissionsBusinessService
4848
private readonly IParticipantsDataService participantsDataService;
4949
private readonly IProblemsDataService problemsDataService;
5050
private readonly IUserProviderService userProviderService;
51-
private readonly ILecturersInContestsBusinessService lecturersInContestsBusiness;
51+
private readonly ILecturersInContestsCacheService lecturersInContestsCache;
5252
private readonly ISubmissionDetailsValidationService submissionDetailsValidationService;
5353
private readonly ISubmitSubmissionValidationService submitSubmissionValidationService;
5454
private readonly ISubmissionResultsValidationService submissionResultsValidationService;
@@ -75,7 +75,7 @@ public SubmissionsBusinessService(
7575
ISubmissionsCommonBusinessService submissionsCommonBusinessService,
7676
IUserProviderService userProviderService,
7777
IParticipantScoresBusinessService participantScoresBusinessService,
78-
ILecturersInContestsBusinessService lecturersInContestsBusiness,
78+
ILecturersInContestsCacheService lecturersInContestsCache,
7979
ISubmissionDetailsValidationService submissionDetailsValidationService,
8080
ISubmitSubmissionValidationService submitSubmissionValidationService,
8181
ISubmissionResultsValidationService submissionResultsValidationService,
@@ -98,7 +98,7 @@ public SubmissionsBusinessService(
9898
this.submissionsCommonData = submissionsCommonData;
9999
this.usersBusiness = usersBusiness;
100100
this.problemsDataService = problemsDataService;
101-
this.lecturersInContestsBusiness = lecturersInContestsBusiness;
101+
this.lecturersInContestsCache = lecturersInContestsCache;
102102
this.submissionsCommonBusinessService = submissionsCommonBusinessService;
103103
this.participantsDataService = participantsDataService;
104104
this.userProviderService = userProviderService;
@@ -128,8 +128,8 @@ public async Task Retest(int submissionId, bool verbosely = false)
128128

129129
var user = this.userProviderService.GetCurrentUser();
130130

131-
var userIsAdminOrLecturerInContest = await this.lecturersInContestsBusiness
132-
.IsCurrentUserAdminOrLecturerInContest(submission.ContestId);
131+
var userIsAdminOrLecturerInContest = await this.lecturersInContestsCache
132+
.IsUserAdminOrLecturerInContest(submission.ContestId, submission.ContestCategoryId, user);
133133

134134
var validationResult = await this.retestSubmissionValidationService.GetValidationResult((
135135
submission,
@@ -150,11 +150,11 @@ public async Task<SubmissionDetailsServiceModel> GetDetailsById(int submissionId
150150
var submission = await this.submissionsData.GetSubmissionById<SubmissionDetailsServiceModel>(submissionId)
151151
?? throw new BusinessServiceException(ValidationMessages.Submission.NotFound);
152152

153-
var userAdminOrLecturerInContest = await this.lecturersInContestsBusiness
154-
.IsCurrentUserAdminOrLecturerInContest(submission.ContestId);
155-
156153
var currentUser = this.userProviderService.GetCurrentUser();
157154

155+
var userAdminOrLecturerInContest = await this.lecturersInContestsCache
156+
.IsUserAdminOrLecturerInContest(submission.ContestId, submission.ContestCategoryId, currentUser);
157+
158158
var validationResult = this.submissionDetailsValidationService
159159
.GetValidationResult((submission, currentUser, userAdminOrLecturerInContest));
160160

@@ -351,6 +351,7 @@ public async Task Submit(SubmitSubmissionServiceModel model)
351351
IsOfficial = p.IsOfficial,
352352
ContestId = p.ContestId,
353353
ContestType = p.Contest.Type,
354+
ContestCategoryId = p.Contest.CategoryId,
354355
LastSubmissionTime = p.LastSubmissionTime,
355356
ParticipationStartTime = p.ParticipationStartTime,
356357
ParticipationEndTime = p.ParticipationEndTime,
@@ -377,7 +378,7 @@ public async Task Submit(SubmitSubmissionServiceModel model)
377378
.FirstOrDefault(st => st.Id == model.SubmissionTypeId);
378379

379380
var submitSubmissionValidationServiceResult = await this.submitSubmissionValidationService.GetValidationResult(
380-
(problem, participant, model, submissionType));
381+
(problem, participant, model, submissionType, currentUser));
381382

382383
if (!submitSubmissionValidationServiceResult.IsValid)
383384
{

0 commit comments

Comments
 (0)