diff --git a/Common/OJS.Common/Enumerations/ContestType.cs b/Common/OJS.Common/Enumerations/ContestType.cs index c301234fea..9e7d4de2c2 100644 --- a/Common/OJS.Common/Enumerations/ContestType.cs +++ b/Common/OJS.Common/Enumerations/ContestType.cs @@ -6,5 +6,6 @@ public enum ContestType OnsitePracticalExam = 1, OnlinePracticalExam = 2, Lab = 3, + OnsitePracticalExamWithRandomTasks = 4, } } \ No newline at end of file diff --git a/Common/OJS.Common/Resources.cs b/Common/OJS.Common/Resources.cs index e97515958a..b00df94b23 100644 --- a/Common/OJS.Common/Resources.cs +++ b/Common/OJS.Common/Resources.cs @@ -157,8 +157,8 @@ public static class ProblemGroupsControllers { public const string ActiveContestCannotAddProblemGroup = "The contest is аctive and you cannot add problem groups"; public const string ActiveContestCannotDeleteProblemGroup = "The contest is аctive and you cannot delete problem groups"; - public const string CanCreateOnlyInOnlineContest = "You can create problem groups only in a contest which is of type {0}"; - public const string CanEditOrderbyOnlyInOnlineContest = "You can edit problem groups order only in a contest which is of type {0}."; + public const string CanCreateOnlyInContestWithRandomTasks = "You can create problem groups only in a contest with random tasks"; + public const string CanEditOrderByOnlyInContestWithRandomTasks = "You can edit problem groups order only in a contest with random tasks."; public const string ContestDoesNotExist = "The selected contest does not exist"; public const string ContestRequired = "Contest is required"; public const string CopyAllProblemGroupsSuccessMessage = "Successfully copied all problem groups from the contest \"{0}\" into the contest \"{1}\""; diff --git a/Data/OJS.Data.Models/Contests/Contest.cs b/Data/OJS.Data.Models/Contests/Contest.cs index b8909b3cd4..8184fb9954 100644 --- a/Data/OJS.Data.Models/Contests/Contest.cs +++ b/Data/OJS.Data.Models/Contests/Contest.cs @@ -130,13 +130,13 @@ public bool ResultsArePubliclyVisible public bool HasPracticePassword => this.PracticePassword != null; [NotMapped] - public bool IsOnlineExam => this.Type == ContestType.OnlinePracticalExam; + public bool IsOnlineExam => this.Type is ContestType.OnlinePracticalExam; [NotMapped] - public bool IsOnsiteExam => this.Type == ContestType.OnsitePracticalExam; + public bool IsWithRandomTasks => this.Type is ContestType.OnlinePracticalExam or ContestType.OnsitePracticalExamWithRandomTasks; [NotMapped] - public bool IsExam => this.IsOnlineExam || this.IsOnsiteExam; + public bool IsOnsiteExam => this.Type is ContestType.OnsitePracticalExam; public static IEnumerable Validate(ValidationContext validationContext) { diff --git a/Servers/UI/OJS.Servers.Ui/ClientApp/src/common/contest-types.ts b/Servers/UI/OJS.Servers.Ui/ClientApp/src/common/contest-types.ts index d14fab498e..e57c8947a7 100644 --- a/Servers/UI/OJS.Servers.Ui/ClientApp/src/common/contest-types.ts +++ b/Servers/UI/OJS.Servers.Ui/ClientApp/src/common/contest-types.ts @@ -26,6 +26,7 @@ enum ContestVariation { OnsitePracticalExam = 1, OnlinePracticalExam = 2, Lab = 3, + OnsitePracticalExamWithRandomTasks = 4, } export type { diff --git a/Servers/UI/OJS.Servers.Ui/ClientApp/src/common/types.ts b/Servers/UI/OJS.Servers.Ui/ClientApp/src/common/types.ts index 84e595b174..4e6a43b008 100644 --- a/Servers/UI/OJS.Servers.Ui/ClientApp/src/common/types.ts +++ b/Servers/UI/OJS.Servers.Ui/ClientApp/src/common/types.ts @@ -192,6 +192,7 @@ interface IContestDetailsResponseType { problems: IProblemType[]; canViewResults: boolean; isOnlineExam: boolean; + isWithRandomTasks: boolean; isActive: boolean; canBeCompeted: boolean; canBePracticed: boolean; @@ -211,6 +212,7 @@ interface IContestDetailsSliceType { canViewCompeteResults?: boolean; canViewPracticeResults?: boolean; isOnlineExam?: boolean; + isWithRandomTasks?: boolean; canBeCompeted?: boolean; canBePracticed?: boolean; isAdminOrLecturerInContest?: boolean; @@ -237,6 +239,7 @@ interface IIndexContestsType { hasContestPassword: boolean; hasPracticePassword: boolean; isOnlineExam: boolean; + isWithRandomTasks: boolean; category: string; isLoading: boolean; numberOfProblems: number; diff --git a/Servers/UI/OJS.Servers.Ui/ClientApp/src/common/url-types.ts b/Servers/UI/OJS.Servers.Ui/ClientApp/src/common/url-types.ts index 4f5fda9d94..184aefaee0 100644 --- a/Servers/UI/OJS.Servers.Ui/ClientApp/src/common/url-types.ts +++ b/Servers/UI/OJS.Servers.Ui/ClientApp/src/common/url-types.ts @@ -85,7 +85,7 @@ interface ISubmitContestSolutionParams { problemId: number; submissionTypeId: number; contestId: number; - isOnlineExam?: boolean; + isWithRandomTasks?: boolean; } interface IRegisterUserForContestParams { diff --git a/Servers/UI/OJS.Servers.Ui/ClientApp/src/components/administration/contests/contest-edit/ContestEdit.tsx b/Servers/UI/OJS.Servers.Ui/ClientApp/src/components/administration/contests/contest-edit/ContestEdit.tsx index 2818180d28..0bf92b1078 100644 --- a/Servers/UI/OJS.Servers.Ui/ClientApp/src/components/administration/contests/contest-edit/ContestEdit.tsx +++ b/Servers/UI/OJS.Servers.Ui/ClientApp/src/components/administration/contests/contest-edit/ContestEdit.tsx @@ -158,6 +158,13 @@ const ContestEdit = (props:IContestEditProps) => { isLoading: isCreating, } ] = useCreateContestMutation(); + const isVariation = + (value: string, variation: ContestVariation) => value === getEnumMemberName(ContestVariation, variation).toString(); + + const isOnlineExam = isVariation(contest.type, ContestVariation.OnlinePracticalExam); + + const isWithRandomTasks = isOnlineExam || isVariation(contest.type, ContestVariation.OnsitePracticalExamWithRandomTasks); + const getDefaultContestCategory = useCallback(() => { const defaultCategory = contestCategories![0]; const categoryName = defaultCategory.name; @@ -273,7 +280,9 @@ const ContestEdit = (props:IContestEditProps) => { const isValid = !!Object.keys(ContestVariation).filter((key) => isNaN(Number(key))).some((x) => x === value); currentContestValidations.isTypeValid = isValid; - if (value === getEnumMemberName(ContestVariation, ContestVariation.OnlinePracticalExam).toString()) { + const isOnlineExamValue = isVariation(value, ContestVariation.OnlinePracticalExam); + const isWithRandomTasksValue = isVariation(value, ContestVariation.OnsitePracticalExamWithRandomTasks); + if (isOnlineExamValue || isWithRandomTasksValue) { numberOfProblemGroups = 2; } break; @@ -530,7 +539,7 @@ const ContestEdit = (props:IContestEditProps) => { - { contest.type === getEnumMemberName(ContestVariation, ContestVariation.OnlinePracticalExam) && ( + { isWithRandomTasks && ( { /> - {contest.type === getEnumMemberName(ContestVariation, ContestVariation.OnlinePracticalExam) && ( + {isOnlineExam && ( { : undefined} name="duration" onChange={(e) => onChange(e)} - disabled={contest.type !== getEnumMemberName(ContestVariation, ContestVariation.OnlinePracticalExam)} + disabled={!isOnlineExam} InputLabelProps={{ shrink: true }} error={(contestValidations.isDurationTouched && !contestValidations.isDurationValid)} helperText={(contestValidations.isDurationTouched && !contestValidations.isDurationValid) && diff --git a/Servers/UI/OJS.Servers.Ui/ClientApp/src/pages/contests/contest-solution-submit/ContestSolutionSubmitPage.tsx b/Servers/UI/OJS.Servers.Ui/ClientApp/src/pages/contests/contest-solution-submit/ContestSolutionSubmitPage.tsx index 9a88195669..07452cc0cd 100644 --- a/Servers/UI/OJS.Servers.Ui/ClientApp/src/pages/contests/contest-solution-submit/ContestSolutionSubmitPage.tsx +++ b/Servers/UI/OJS.Servers.Ui/ClientApp/src/pages/contests/contest-solution-submit/ContestSolutionSubmitPage.tsx @@ -356,7 +356,7 @@ const ContestSolutionSubmitPage = () => { id: data!.contest!.id, name: data.contest.name, categoryId: data!.contest!.categoryId, - isOnlineExam: data?.contest?.isOnlineExam, + isWithRandomTasks: data?.contest?.isWithRandomTasks, })); } }, [ contestDetails, contestId, data, dispatch ]); @@ -381,7 +381,7 @@ const ContestSolutionSubmitPage = () => { problemId: selectedContestDetailsProblem?.id!, submissionTypeId: selectedSubmissionType?.id!, contestId: Number(contestId!), - isOnlineExam: contestDetails?.isOnlineExam, + isWithRandomTasks: contestDetails?.isWithRandomTasks, }).then((d) => { if (!(d as any).error) { refetch(); @@ -397,7 +397,7 @@ const ContestSolutionSubmitPage = () => { submissionCode, submitSolution, contestId, - contestDetails?.isOnlineExam, + contestDetails?.isWithRandomTasks, ]); const onSolutionSubmitFile = useCallback(async () => { @@ -413,7 +413,7 @@ const ContestSolutionSubmitPage = () => { problemId: selectedContestDetailsProblem?.id!, submissionTypeId: selectedSubmissionType?.id!, contestId: Number(contestId!), - isOnlineExam: contestDetails?.isOnlineExam, + isWithRandomTasks: contestDetails?.isWithRandomTasks, }); refetch(); getSubmissionsData(); @@ -426,7 +426,7 @@ const ContestSolutionSubmitPage = () => { submitSolutionFile, uploadedFile, contestId, - contestDetails?.isOnlineExam, + contestDetails?.isWithRandomTasks, ]); const sumMyPoints = useMemo(() => contest diff --git a/Servers/UI/OJS.Servers.Ui/ClientApp/src/redux/features/contestsSlice.ts b/Servers/UI/OJS.Servers.Ui/ClientApp/src/redux/features/contestsSlice.ts index a9dbedaa6e..34c640f67e 100644 --- a/Servers/UI/OJS.Servers.Ui/ClientApp/src/redux/features/contestsSlice.ts +++ b/Servers/UI/OJS.Servers.Ui/ClientApp/src/redux/features/contestsSlice.ts @@ -63,10 +63,10 @@ export const contestSlice = createSlice({ state.breadcrumbItems = []; }, setContestDetailsIdAndCategoryId: (state, action: PayloadAction<{ - id: number; name: string; categoryId: number; isOnlineExam?: boolean; + id: number; name: string; categoryId: number; isWithRandomTasks?: boolean; }>) => { - const { id, name, categoryId, isOnlineExam } = action.payload; - state.contestDetails = { id, name, categoryId, isOnlineExam }; + const { id, name, categoryId, isWithRandomTasks } = action.payload; + state.contestDetails = { id, name, categoryId, isWithRandomTasks }; }, setSelectedContestDetailsProblem: (state, action: PayloadAction<{ selectedProblem: IProblemType | null }>) => { const { selectedProblem } = action.payload; diff --git a/Servers/UI/OJS.Servers.Ui/ClientApp/src/redux/services/contestsService.ts b/Servers/UI/OJS.Servers.Ui/ClientApp/src/redux/services/contestsService.ts index 127f7286fe..d5602c28de 100644 --- a/Servers/UI/OJS.Servers.Ui/ClientApp/src/redux/services/contestsService.ts +++ b/Servers/UI/OJS.Servers.Ui/ClientApp/src/redux/services/contestsService.ts @@ -103,14 +103,14 @@ export const contestsService = createApi({ keepUnusedDataFor: 0, }), submitContestSolution: builder.mutation({ - query: ({ content, official, problemId, submissionTypeId, contestId, isOnlineExam }) => ({ + query: ({ content, official, problemId, submissionTypeId, contestId, isWithRandomTasks }) => ({ url: '/Compete/Submit', method: 'POST', - body: { content, official, problemId, submissionTypeId, contestId, isOnlineExam }, + body: { content, official, problemId, submissionTypeId, contestId, isWithRandomTasks }, }), }), submitContestSolutionFile: builder.mutation({ - query: ({ content, official, submissionTypeId, problemId, contestId, isOnlineExam }) => { + query: ({ content, official, submissionTypeId, problemId, contestId, isWithRandomTasks }) => { const formData = new FormData(); formData.append('content', content); formData.append('official', official @@ -119,7 +119,7 @@ export const contestsService = createApi({ formData.append('problemId', problemId.toString()); formData.append('submissionTypeId', submissionTypeId.toString()); formData.append('contestId', contestId.toString()); - formData.append('isOnlineExam', isOnlineExam + formData.append('isWithRandomTasks', isWithRandomTasks ? 'true' : 'false'); diff --git a/Servers/UI/OJS.Servers.Ui/Models/Contests/ContestRegistrationDetailsResponseModel.cs b/Servers/UI/OJS.Servers.Ui/Models/Contests/ContestRegistrationDetailsResponseModel.cs deleted file mode 100644 index 719f268e33..0000000000 --- a/Servers/UI/OJS.Servers.Ui/Models/Contests/ContestRegistrationDetailsResponseModel.cs +++ /dev/null @@ -1,28 +0,0 @@ -namespace OJS.Servers.Ui.Models.Contests; - -using OJS.Services.Infrastructure.Models.Mapping; -using OJS.Services.Ui.Models.Contests; -using System; - -public class ContestRegistrationDetailsResponseModel : IMapFrom -{ - public int Id { get; set; } - - public string Name { get; set; } = string.Empty; - - public bool RequirePassword { get; set; } - - public bool ShouldConfirmParticipation { get; set; } - - public bool IsRegisteredSuccessfully { get; set; } - - public TimeSpan? Duration { get; set; } - - public int NumberOfProblems { get; set; } - - public int? ParticipantId { get; set; } - - public bool IsOnlineExam { get; set; } - - public int? CategoryId { get; set; } -} \ No newline at end of file diff --git a/Servers/UI/OJS.Servers.Ui/Models/Submissions/Compete/SubmissionRequestModel.cs b/Servers/UI/OJS.Servers.Ui/Models/Submissions/Compete/SubmissionRequestModel.cs index c423d04c63..cf96173210 100644 --- a/Servers/UI/OJS.Servers.Ui/Models/Submissions/Compete/SubmissionRequestModel.cs +++ b/Servers/UI/OJS.Servers.Ui/Models/Submissions/Compete/SubmissionRequestModel.cs @@ -12,6 +12,8 @@ public class SubmissionRequestModel : IMapExplicitly public bool IsOnlineExam { get; set; } + public bool IsWithRandomTasks { get; set; } + public int SubmissionTypeId { get; set; } public string? Content { get; set; } diff --git a/Servers/UI/OJS.Servers.Ui/Models/Submissions/Compete/SubmitFileSubmissionRequestModel.cs b/Servers/UI/OJS.Servers.Ui/Models/Submissions/Compete/SubmitFileSubmissionRequestModel.cs index 332bb9468d..943530e11a 100644 --- a/Servers/UI/OJS.Servers.Ui/Models/Submissions/Compete/SubmitFileSubmissionRequestModel.cs +++ b/Servers/UI/OJS.Servers.Ui/Models/Submissions/Compete/SubmitFileSubmissionRequestModel.cs @@ -16,6 +16,8 @@ public class SubmitFileSubmissionRequestModel : IMapExplicitly public bool IsOnlineExam { get; set; } + public bool IsWithRandomTasks { get; set; } + public int SubmissionTypeId { get; set; } public IFormFile? Content { get; set; } = null; diff --git a/Services/Administration/OJS.Services.Administration.Business/Contests/ContestsBusinessService.cs b/Services/Administration/OJS.Services.Administration.Business/Contests/ContestsBusinessService.cs index 0ff8148126..ee513b947c 100644 --- a/Services/Administration/OJS.Services.Administration.Business/Contests/ContestsBusinessService.cs +++ b/Services/Administration/OJS.Services.Administration.Business/Contests/ContestsBusinessService.cs @@ -244,7 +244,7 @@ public override async Task Edit(ContestAdministratio contest.MapFrom(model); - if (model.IsOnlineExam && contest.ProblemGroups.Count == 0) + if (model.IsWithRandomTasks && contest.ProblemGroups.Count == 0) { AddProblemGroupsToContest(contest, model.NumberOfProblemGroups); } diff --git a/Services/Administration/OJS.Services.Administration.Business/Contests/Validators/ContestAdministrationModelValidator.cs b/Services/Administration/OJS.Services.Administration.Business/Contests/Validators/ContestAdministrationModelValidator.cs index 3cb20a11dd..985e396f5b 100644 --- a/Services/Administration/OJS.Services.Administration.Business/Contests/Validators/ContestAdministrationModelValidator.cs +++ b/Services/Administration/OJS.Services.Administration.Business/Contests/Validators/ContestAdministrationModelValidator.cs @@ -88,7 +88,7 @@ private static bool BeAValidContestType(string? type) private static bool ValidateOnlineContestProblemGroups(ContestAdministrationModel model) { - if (model.IsOnlineExam) + if (model.IsWithRandomTasks) { if (model.NumberOfProblemGroups <= 0) { diff --git a/Services/Administration/OJS.Services.Administration.Business/ProblemGroups/Validators/ProblemGroupAdministrationModelValidator.cs b/Services/Administration/OJS.Services.Administration.Business/ProblemGroups/Validators/ProblemGroupAdministrationModelValidator.cs index 6107ade88c..02109a5648 100644 --- a/Services/Administration/OJS.Services.Administration.Business/ProblemGroups/Validators/ProblemGroupAdministrationModelValidator.cs +++ b/Services/Administration/OJS.Services.Administration.Business/ProblemGroups/Validators/ProblemGroupAdministrationModelValidator.cs @@ -14,7 +14,6 @@ public class ProblemGroupAdministrationModelValidator : BaseAdministrationModelValidator { - private readonly IContestsActivityService contestsActivityService; private readonly IContestsDataService contestsDataService; private readonly IProblemGroupsDataService problemGroupsDataService; @@ -24,7 +23,6 @@ public ProblemGroupAdministrationModelValidator( IProblemGroupsDataService problemGroupsDataService) : base(problemGroupsDataService) { - this.contestsActivityService = contestsActivityService; this.contestsDataService = contestsDataService; this.problemGroupsDataService = problemGroupsDataService; @@ -36,19 +34,20 @@ public ProblemGroupAdministrationModelValidator( this.RuleFor(model => model) .MustAsync(async (model, _) => await this.NotBeActiveOrOnlineContest(model)) - .WithMessage($"{string.Format(Resources.ProblemGroupsControllers.CanEditOrderbyOnlyInOnlineContest, ContestType.OnlinePracticalExam.ToString())}") + .WithMessage($"{Resources.ProblemGroupsControllers.CanEditOrderByOnlyInContestWithRandomTasks}") .When(x => x.OperationType is CrudOperationType.Update or CrudOperationType.Delete); this.RuleFor(model => model) - .MustAsync(async (model, _) => await this.IsOnline(model.Contest.Id) && !await this.contestsActivityService.IsContestActive(model.Contest.Id)) + .MustAsync(async (model, _) => await this.contestsDataService.IsWithRandomTasksById(model.Contest.Id) && + !await contestsActivityService.IsContestActive(model.Contest.Id)) .WithMessage($"" + - $"{string.Format(Resources.ProblemGroupsControllers.CanCreateOnlyInOnlineContest, ContestType.OnlinePracticalExam.ToString())}" + + $"{Resources.ProblemGroupsControllers.CanCreateOnlyInContestWithRandomTasks}" + $" or " + $"{Resources.ProblemGroupsControllers.ActiveContestCannotAddProblemGroup}") .When(x => x.OperationType == CrudOperationType.Create); this.RuleFor(x => x.Contest.Id) - .MustAsync(async (id, _) => !await this.contestsActivityService.IsContestActive(id)) + .MustAsync(async (id, _) => !await contestsActivityService.IsContestActive(id)) .WithMessage("Cannot delete problem group when the related contest is active") .When(x => x.OperationType is CrudOperationType.Update); } @@ -60,14 +59,7 @@ private async Task NotBeActiveOrOnlineContest(ProblemGroupsAdministrationM var contestIdToCheck = model.OperationType is CrudOperationType.Update ? model.Contest.Id : problemGroup!.ContestId; - if (Math.Abs(problemGroup!.OrderBy - model.OrderBy) > 0 && !await this.IsOnline(contestIdToCheck)) - { - return false; - } - - return true; + return Math.Abs(problemGroup!.OrderBy - model.OrderBy) <= 0 || + await this.contestsDataService.IsWithRandomTasksById(contestIdToCheck); } - - private async Task IsOnline(int id) - => await this.contestsDataService.IsOnlineById(id); } \ No newline at end of file diff --git a/Services/Administration/OJS.Services.Administration.Business/Problems/ProblemsBusinessService.cs b/Services/Administration/OJS.Services.Administration.Business/Problems/ProblemsBusinessService.cs index 3d49a2a0a9..0efa217f43 100644 --- a/Services/Administration/OJS.Services.Administration.Business/Problems/ProblemsBusinessService.cs +++ b/Services/Administration/OJS.Services.Administration.Business/Problems/ProblemsBusinessService.cs @@ -135,7 +135,7 @@ public override async Task Delete(int id) IsolationLevel.RepeatableRead, TransactionScopeAsyncFlowOption.Enabled); - if (!await this.contestsData.IsOnlineById(problem.ContestId)) + if (!await this.contestsData.IsWithRandomTasksById(problem.ContestId)) { await this.problemGroupsBusiness.DeleteById(problem.ProblemGroupId); } @@ -240,7 +240,7 @@ public override async Task Edit(ProblemAdministratio problem.ProblemGroup.Type = (ProblemGroupType)Enum.Parse(typeof(ProblemGroupType), model.ProblemGroupType!); - if (!problem.ProblemGroup.Contest.IsOnlineExam) + if (!problem.ProblemGroup.Contest.IsWithRandomTasks) { problem.ProblemGroup.OrderBy = model.OrderBy; } diff --git a/Services/Administration/OJS.Services.Administration.Business/Problems/Validators/ProblemAdministrationValidator.cs b/Services/Administration/OJS.Services.Administration.Business/Problems/Validators/ProblemAdministrationValidator.cs index 5fa2b6054b..4eba6106de 100644 --- a/Services/Administration/OJS.Services.Administration.Business/Problems/Validators/ProblemAdministrationValidator.cs +++ b/Services/Administration/OJS.Services.Administration.Business/Problems/Validators/ProblemAdministrationValidator.cs @@ -23,7 +23,6 @@ public class ProblemAdministrationValidator : BaseAdministrationModelValidator

model.Name) .Length(1, ConstraintConstants.Problem.NameMaxLength) @@ -82,7 +80,7 @@ public ProblemAdministrationValidator( .MustAsync(async (model, _) => await this.MustHaveValidProblemGroupId(model)) .WithMessage("Invalid value for \"Problem Group Order By\" has been provided.") .WhenAsync(async (x, _) => x.OperationType is CrudOperationType.Create or CrudOperationType.Update && - await this.IsOnline(x.ContestId)); + await contestsDataService.IsWithRandomTasksById(x.ContestId)); } private async Task ContestMustNotBeActive(int problemId) @@ -142,7 +140,4 @@ private async Task MustHaveValidProblemGroupId(ProblemAdministrationModel return new HashSet(problemGroups).Contains(model.ProblemGroupId); } - - private async Task IsOnline(int contestId) - => await this.contestsDataService.IsOnlineById(contestId); } \ No newline at end of file diff --git a/Services/Administration/OJS.Services.Administration.Data/IContestsDataService.cs b/Services/Administration/OJS.Services.Administration.Data/IContestsDataService.cs index 2d99642453..5b464fdeb3 100644 --- a/Services/Administration/OJS.Services.Administration.Data/IContestsDataService.cs +++ b/Services/Administration/OJS.Services.Administration.Data/IContestsDataService.cs @@ -24,7 +24,7 @@ public interface IContestsDataService : IDataService Task IsActiveById(int id); - Task IsOnlineById(int id); + Task IsWithRandomTasksById(int id); Task IsUserLecturerInContestByContestAndUser(int id, string? userId); diff --git a/Services/Administration/OJS.Services.Administration.Data/Implementations/ContestsDataService.cs b/Services/Administration/OJS.Services.Administration.Data/Implementations/ContestsDataService.cs index 3d8cef2e82..bffde285ee 100644 --- a/Services/Administration/OJS.Services.Administration.Data/Implementations/ContestsDataService.cs +++ b/Services/Administration/OJS.Services.Administration.Data/Implementations/ContestsDataService.cs @@ -13,21 +13,11 @@ namespace OJS.Services.Administration.Data.Implementations using OJS.Services.Common.Data; using OJS.Services.Common.Models.Contests; using OJS.Services.Common.Models.Users; - using OJS.Services.Infrastructure; using OJS.Services.Infrastructure.Extensions; - public class ContestsDataService : AdministrationDataService, IContestsDataService + public class ContestsDataService(OjsDbContext db, IContestsActivityService activityService) + : AdministrationDataService(db), IContestsDataService { - private readonly IDatesService dates; - private readonly IContestsActivityService activityService; - - public ContestsDataService(OjsDbContext db, IDatesService dates, IContestsActivityService activityService) - : base(db) - { - this.dates = dates; - this.activityService = activityService; - } - public Task GetByIdWithProblems(int id) => this.GetByIdQuery(id) .Include(c => c.ProblemGroups) @@ -69,18 +59,17 @@ public async Task IsActiveById(int id) { var contest = await this.OneById(id); - if (contest == null) - { - return false; - } - - return await this.activityService.IsContestActive(contest.Map()); + return contest != null && await activityService.IsContestActive(contest.Map()); } - public async Task IsOnlineById(int id) - => await this.GetByIdQuery(id) + public async Task IsWithRandomTasksById(int id) + { + var type = await this.GetByIdQuery(id) .Select(c => c.Type) - .FirstOrDefaultAsync() == ContestType.OnlinePracticalExam; + .FirstOrDefaultAsync(); + + return type is ContestType.OnlinePracticalExam or ContestType.OnsitePracticalExamWithRandomTasks; + } public Task IsUserLecturerInContestByContestAndUser(int id, string? userId) => this.GetByIdQuery(id) @@ -114,8 +103,5 @@ private async Task GetMaxPointsByIdAndProblemGroupsFilter(int id, Expressio return problemsMaxPoints.Sum(); } - - private IQueryable GetAllVisibleQuery() - => this.GetQuery(c => c.IsVisible); } } \ No newline at end of file diff --git a/Services/Administration/OJS.Services.Administration.Models/Contests/ContestAdministrationModel.cs b/Services/Administration/OJS.Services.Administration.Models/Contests/ContestAdministrationModel.cs index 4efe7f9214..cc094ffe86 100644 --- a/Services/Administration/OJS.Services.Administration.Models/Contests/ContestAdministrationModel.cs +++ b/Services/Administration/OJS.Services.Administration.Models/Contests/ContestAdministrationModel.cs @@ -54,6 +54,9 @@ public class ContestAdministrationModel : BaseAdministrationModel, IMapExpl public bool IsOnlineExam => this.Type == ContestType.OnlinePracticalExam.ToString(); + public bool IsWithRandomTasks => this.IsOnlineExam || + this.Type == ContestType.OnsitePracticalExamWithRandomTasks.ToString(); + public int NumberOfProblemGroups { get; set; } // TODO : Add Automatically change test detailed feedback visiblity and Warn on missing author solutions public void RegisterMappings(IProfileExpression configuration) diff --git a/Services/Administration/OJS.Services.Administration.Models/ProblemGroups/ProblemGroupCreateValidationServiceModel.cs b/Services/Administration/OJS.Services.Administration.Models/ProblemGroups/ProblemGroupCreateValidationServiceModel.cs deleted file mode 100644 index 54ebcaee1b..0000000000 --- a/Services/Administration/OJS.Services.Administration.Models/ProblemGroups/ProblemGroupCreateValidationServiceModel.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace OJS.Services.Administration.Models.ProblemGroups; - -public class ProblemGroupCreateValidationServiceModel -{ - public bool ContestIsOnline { get; set; } - - public bool ContestIsActive { get; set; } -} \ No newline at end of file diff --git a/Services/Administration/OJS.Services.Administration.Models/ProblemGroups/ProblemGroupDeleteValidationServiceModel.cs b/Services/Administration/OJS.Services.Administration.Models/ProblemGroups/ProblemGroupDeleteValidationServiceModel.cs deleted file mode 100644 index bd04aca9e0..0000000000 --- a/Services/Administration/OJS.Services.Administration.Models/ProblemGroups/ProblemGroupDeleteValidationServiceModel.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace OJS.Services.Administration.Models.ProblemGroups; - -public class ProblemGroupDeleteValidationServiceModel -{ - public bool ContestIsActive { get; set; } -} \ No newline at end of file diff --git a/Services/Administration/OJS.Services.Administration.Models/ProblemGroups/ProblemGroupEditValidationServiceModel.cs b/Services/Administration/OJS.Services.Administration.Models/ProblemGroups/ProblemGroupEditValidationServiceModel.cs deleted file mode 100644 index 47ac830e97..0000000000 --- a/Services/Administration/OJS.Services.Administration.Models/ProblemGroups/ProblemGroupEditValidationServiceModel.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace OJS.Services.Administration.Models.ProblemGroups; - -public class ProblemGroupEditValidationServiceModel -{ - public bool ContestIsOnline { get; set; } - - public double ExistingOrderBy { get; set; } - - public double NewOrderBy { get; set; } -} \ No newline at end of file diff --git a/Services/Common/OJS.Workers/OJS.Workers.Common/IExecutionContext.cs b/Services/Common/OJS.Workers/OJS.Workers.Common/IExecutionContext.cs index 05719db4fe..ffe183af67 100644 --- a/Services/Common/OJS.Workers/OJS.Workers.Common/IExecutionContext.cs +++ b/Services/Common/OJS.Workers/OJS.Workers.Common/IExecutionContext.cs @@ -12,7 +12,7 @@ public interface IExecutionContext byte[] FileContent { get; set; } - public byte[] AdditionalFiles { get; set; } + byte[] AdditionalFiles { get; set; } string? AllowedFileExtensions { get; } diff --git a/Services/UI/OJS.Services.Ui.Business/Implementations/ContestsBusinessService.cs b/Services/UI/OJS.Services.Ui.Business/Implementations/ContestsBusinessService.cs index 03153c002a..5da745ce07 100644 --- a/Services/UI/OJS.Services.Ui.Business/Implementations/ContestsBusinessService.cs +++ b/Services/UI/OJS.Services.Ui.Business/Implementations/ContestsBusinessService.cs @@ -99,7 +99,7 @@ public async Task GetContestDetails(int id) ? competeParticipant : practiceParticipant; - if (!isLecturerInContestOrAdmin && participantToGetProblemsFrom != null && contestActivityEntity.CanBeCompeted && contest!.IsOnlineExam) + if (!isLecturerInContestOrAdmin && participantToGetProblemsFrom != null && contestActivityEntity.CanBeCompeted && contest!.IsWithRandomTasks) { var problemsForParticipantIds = participantToGetProblemsFrom.ProblemsForParticipants.Select(x => x.ProblemId); contest.Problems = contest.Problems @@ -108,7 +108,7 @@ public async Task GetContestDetails(int id) } var canShowProblemsInCompete = - (!contest!.HasContestPassword && !contest.IsOnlineExam && contestActivityEntity is { CanBeCompeted: true, CompeteUserActivity: not null }) + (!contest!.HasContestPassword && !contest.IsWithRandomTasks && contestActivityEntity is { CanBeCompeted: true, CompeteUserActivity: not null }) || isLecturerInContestOrAdmin || contestActivityEntity.CompeteUserActivity?.IsActive == true; @@ -327,9 +327,7 @@ public async Task GetParticipationDetails( participant.Contest.Problems.Select(x => x.Id), participantsList); - var isOfficialOnlineContest = model.IsOfficial && contest.IsOnlineExam; - - if (!userIsAdminOrLecturerInContest && isOfficialOnlineContest) + if (!userIsAdminOrLecturerInContest && model.IsOfficial && contest.IsWithRandomTasks) { participant.Contest.Problems = [.. participant.Contest.Problems .Where(x => participant.ProblemsForParticipantIds.Contains(x.Id)) diff --git a/Services/UI/OJS.Services.Ui.Business/Implementations/ParticipantsBusinessService.cs b/Services/UI/OJS.Services.Ui.Business/Implementations/ParticipantsBusinessService.cs index 9118405e06..33f664e886 100644 --- a/Services/UI/OJS.Services.Ui.Business/Implementations/ParticipantsBusinessService.cs +++ b/Services/UI/OJS.Services.Ui.Business/Implementations/ParticipantsBusinessService.cs @@ -36,13 +36,16 @@ public async Task CreateNewByContestByUserByIsOfficialAndIsAdminOrL { var participant = new Participant(contest.Id, userId, isOfficial); - var utcNow = DateTime.SpecifyKind(this.datesService.GetUtcNow(), DateTimeKind.Unspecified); - if (isOfficial && contest.IsOnlineExam) + if (isOfficial) { - participant.ParticipationStartTime = utcNow; - participant.ParticipationEndTime = utcNow + contest.Duration; + if (contest.IsOnlineExam) + { + var utcNow = DateTime.SpecifyKind(this.datesService.GetUtcNow(), DateTimeKind.Unspecified); + participant.ParticipationStartTime = utcNow; + participant.ParticipationEndTime = utcNow + contest.Duration; + } - if (!isAdminOrLecturerInContest) + if (!isAdminOrLecturerInContest && contest.IsWithRandomTasks) { var problemGroups = await this.problemGroupsData .GetAllByContest(contest.Id) diff --git a/Services/UI/OJS.Services.Ui.Business/Implementations/SubmissionsBusinessService.cs b/Services/UI/OJS.Services.Ui.Business/Implementations/SubmissionsBusinessService.cs index b60624e5c9..59a6cbbdd5 100644 --- a/Services/UI/OJS.Services.Ui.Business/Implementations/SubmissionsBusinessService.cs +++ b/Services/UI/OJS.Services.Ui.Business/Implementations/SubmissionsBusinessService.cs @@ -348,7 +348,7 @@ public async Task Submit(SubmitSubmissionServiceModel model) ContestPracticeEndTime = p.Contest.PracticeEndTime, ContestLimitBetweenSubmissions = p.Contest.LimitBetweenSubmissions, ContestAllowParallelSubmissionsInTasks = p.Contest.AllowParallelSubmissionsInTasks, - Problems = model.Official && model.IsOnlineExam + Problems = model.Official && model.IsWithRandomTasks ? p.ProblemsForParticipants .Select(pfp => new ProblemForParticipantServiceModel { diff --git a/Services/UI/OJS.Services.Ui.Models/Contests/ContestDetailsServiceModel.cs b/Services/UI/OJS.Services.Ui.Models/Contests/ContestDetailsServiceModel.cs index 20bf1efef4..f23b410cf9 100644 --- a/Services/UI/OJS.Services.Ui.Models/Contests/ContestDetailsServiceModel.cs +++ b/Services/UI/OJS.Services.Ui.Models/Contests/ContestDetailsServiceModel.cs @@ -47,6 +47,8 @@ public class ContestDetailsServiceModel : IMapExplicitly, ICanBeCompetedAndPract public bool IsOnlineExam { get; set; } + public bool IsWithRandomTasks { get; set; } + public bool CanBeCompeted { get; set; } public bool CanBePracticed { get; set; } diff --git a/Services/UI/OJS.Services.Ui.Models/Contests/ContestRegistrationDetailsServiceModel.cs b/Services/UI/OJS.Services.Ui.Models/Contests/ContestRegistrationDetailsServiceModel.cs index 373261604d..64e5faad02 100644 --- a/Services/UI/OJS.Services.Ui.Models/Contests/ContestRegistrationDetailsServiceModel.cs +++ b/Services/UI/OJS.Services.Ui.Models/Contests/ContestRegistrationDetailsServiceModel.cs @@ -28,6 +28,8 @@ public class ContestRegistrationDetailsServiceModel : IMapExplicitly, IContestFo public bool IsOnlineExam { get; set; } + public bool IsWithRandomTasks { get; set; } + public int? CategoryId { get; set; } public bool IsVisible { get; set; } diff --git a/Services/UI/OJS.Services.Ui.Models/Submissions/SubmitSubmissionServiceModel.cs b/Services/UI/OJS.Services.Ui.Models/Submissions/SubmitSubmissionServiceModel.cs index 6d01a55fda..1391756393 100644 --- a/Services/UI/OJS.Services.Ui.Models/Submissions/SubmitSubmissionServiceModel.cs +++ b/Services/UI/OJS.Services.Ui.Models/Submissions/SubmitSubmissionServiceModel.cs @@ -10,7 +10,7 @@ public class SubmitSubmissionServiceModel : IMapExplicitly public int ContestId { get; set; } - public bool IsOnlineExam { get; set; } + public bool IsWithRandomTasks { get; set; } public int SubmissionTypeId { get; set; }