Skip to content

Hotfixes to V2 #1450

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 11 commits into from
Oct 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -22,5 +22,6 @@ public static class CacheConstants
public const string LatestPublicSubmissions = "PublicSubmissions";

public const string SubmissionTypesByUsage = "SubmissionTypesByUsage";
public const string SubmissionTypeById = "SubmissionTypesById:{0}";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,7 @@
using OJS.Services.Infrastructure;
using OJS.Services.Infrastructure.Constants;
using OJS.Services.Ui.Models.Cache;
using OJS.Services.Common.Models.Users;
using OJS.Services.Ui.Models.Contests;
using OJS.Data.Models.Contests;
using OJS.Data.Models.Participants;
using System.Collections.Generic;
using System.Threading.Tasks;

Expand Down Expand Up @@ -49,6 +46,5 @@ Task<ContestParticipantsCountCacheModel> GetParticipantsCountForContest(
/// <returns>A ContestServiceModel containing detailed information about the contest.</returns>
Task<ContestServiceModel?> GetContestServiceModelForContest(
int contestId,
StartContestParticipationServiceModel model,
int cacheSeconds = CacheConstants.FiveMinutesInSeconds);
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,9 @@
using OJS.Common.Utils;
using OJS.Services.Infrastructure.Constants;
using OJS.Services.Infrastructure.Cache;
using OJS.Services.Infrastructure.Extensions;
using Microsoft.EntityFrameworkCore;
using OJS.Services.Ui.Data;
using OJS.Services.Ui.Models.Cache;
using OJS.Services.Infrastructure.Exceptions;
using OJS.Services.Ui.Business.Validations.Implementations.Contests;
using OJS.Services.Common.Models.Users;
using OJS.Services.Ui.Models.Contests;
using System.Collections.Generic;
using System.Linq;
Expand Down Expand Up @@ -58,11 +54,10 @@ public async Task<ContestParticipantsCountCacheModel> GetParticipantsCountForCon

public async Task<ContestServiceModel?> GetContestServiceModelForContest(
int contestId,
StartContestParticipationServiceModel model,
int cacheSeconds = CacheConstants.FiveMinutesInSeconds)
=> await this.cache.Get(
string.Format(CacheConstants.ContestDetailsForSubmit, contestId),
async () => (await this.GetContestServiceModel(model)),
async () => (await this.GetContestServiceModel(contestId)),
cacheSeconds);

/// <summary>
Expand Down Expand Up @@ -95,12 +90,9 @@ private async Task<IDictionary<int, ContestParticipantsCountCacheModel>> GetCont
/// </summary>
/// <param name="model">The model containing the contest participation start details, including the contest id and whether it is official.</param>
/// <returns>A ContestServiceModel containing detailed information about the contest.</returns>
private async Task<ContestServiceModel?> GetContestServiceModel(
StartContestParticipationServiceModel model)
private async Task<ContestServiceModel?> GetContestServiceModel(int contestId)
{
var contest = await this.contestsData.GetById<ContestServiceModel>(model.ContestId);

var contestServiceModel = contest?.Map<ContestServiceModel?>();
var contestServiceModel = await this.contestsData.GetById<ContestServiceModel>(contestId);

return contestServiceModel;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -284,13 +284,7 @@ public async Task<ContestParticipationServiceModel> GetParticipationDetails(
{
var user = this.userProviderService.GetCurrentUser();

var participant = await this.participantsData
.GetWithContestAndSubmissionDetailsByContestByUserAndIsOfficial(
model.ContestId,
user.Id,
model.IsOfficial);

if (participant == null)
if (!await this.participantsData.ExistsByContestByUserAndIsOfficial(model.ContestId, user.Id, model.IsOfficial))
{
// Participant must be registered in previous steps
return new ContestParticipationServiceModel
Expand All @@ -299,8 +293,14 @@ public async Task<ContestParticipationServiceModel> GetParticipationDetails(
};
}

var participant = await this.participantsData
.GetWithContestAndProblemsForParticipantByContestByUserAndIsOfficial(
model.ContestId,
user.Id,
model.IsOfficial);

var contest =
await this.contestParticipantsCacheService.GetContestServiceModelForContest(participant.ContestId, model);
await this.contestParticipantsCacheService.GetContestServiceModelForContest(participant.ContestId);

var validationResult = this.contestParticipationValidationService.GetValidationResult((
contest?.Map<Contest>(),
Expand Down Expand Up @@ -330,10 +330,10 @@ public async Task<ContestParticipationServiceModel> GetParticipationDetails(
.Max();
participationModel.LastSubmissionTime = lastSubmissionTime;

participationModel.Contest!.AllowedSubmissionTypes = participationModel
.Contest
.AllowedSubmissionTypes
.DistinctBy(st => st.Id);
participationModel.Contest!.AllowedSubmissionTypes = participationModel.Contest.Problems
.SelectMany(p => p.AllowedSubmissionTypes)
.DistinctBy(st => st.Id)
.ToList();

participationModel.ParticipantId = participant.Id;
participationModel.UserSubmissionsTimeLimit = contest.LimitBetweenSubmissions;
Expand All @@ -349,13 +349,15 @@ public async Task<ContestParticipationServiceModel> GetParticipationDetails(

if (!userIsAdminOrLecturerInContest && isOfficialOnlineContest)
{
participationModel.Contest.Problems = participant
var participantProblems = participant
.ProblemsForParticipants
.Select(x => x.Problem)
.MapCollection<ContestProblemServiceModel>()
.Select(x => x.Problem.Id)
.ToList();

participationModel.Contest.Problems = participationModel.Contest.Problems
.Where(x => participantProblems.Contains(x.Id))
.OrderBy(p => p.ProblemGroupOrderBy)
.ThenBy(p => p.OrderBy)
.ThenBy(p => p.Name)
.ToList();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ public class SubmissionsBusinessService : ISubmissionsBusinessService
private readonly IDatesService dates;
private readonly ITransactionsProvider transactionsProvider;
private readonly ICacheService cache;
private readonly IContestsDataService contestsData;
private readonly ISubmissionTypesDataService submissionTypesData;

public SubmissionsBusinessService(
ILogger<SubmissionsBusinessService> logger,
Expand All @@ -82,7 +84,9 @@ public SubmissionsBusinessService(
ISubmissionsHelper submissionsHelper,
IDatesService dates,
ITransactionsProvider transactionsProvider,
ICacheService cache)
ICacheService cache,
IContestsDataService contestsData,
ISubmissionTypesDataService submissionTypesData)
{
this.logger = logger;
this.submissionsData = submissionsData;
Expand All @@ -105,6 +109,8 @@ public SubmissionsBusinessService(
this.dates = dates;
this.transactionsProvider = transactionsProvider;
this.cache = cache;
this.contestsData = contestsData;
this.submissionTypesData = submissionTypesData;
}

public async Task Retest(int id)
Expand Down Expand Up @@ -432,22 +438,26 @@ await this.problemsDataService.GetWithProblemGroupById(problemId)

public async Task Submit(SubmitSubmissionServiceModel model)
{
var problem = await this.problemsDataService.GetWithProblemGroupCheckerAndTestsById(model.ProblemId);
var problem = await this.problemsDataService.GetWithProblemGroupCheckerAndTestsById(model.ProblemId)
?? throw new BusinessServiceException(ValidationMessages.Problem.NotFound);

if (problem == null)
{
throw new BusinessServiceException(ValidationMessages.Problem.NotFound);
}
var submissionType = await this.cache.Get(
string.Format(CacheConstants.SubmissionTypeById, model.SubmissionTypeId),
async () => await this.submissionTypesData.OneById(model.SubmissionTypeId),
CacheConstants.FiveMinutesInSeconds);

var currentUser = this.userProviderService.GetCurrentUser();

var participant = await this.participantsDataService
.GetWithContestAndSubmissionDetailsByContestByUserAndIsOfficial(
.GetWithContestAndProblemsForParticipantByContestByUserAndIsOfficial(
problem.ProblemGroup.ContestId,
currentUser.Id!,
model.Official);

var contest = await this.contestsData.OneById(problem.ProblemGroup.ContestId);

var submitSubmissionValidationServiceResult = this.submitSubmissionValidationService.GetValidationResult(
(problem, participant, model));
(problem, participant, model, contest, submissionType));

if (!submitSubmissionValidationServiceResult.IsValid)
{
Expand All @@ -468,19 +478,15 @@ public async Task Submit(SubmitSubmissionServiceModel model)

newSubmission.ParticipantId = participant!.Id;
newSubmission.IpAddress = "model.UserHostAddress";
newSubmission.IsPublic = ((participant.IsOfficial && participant.Contest.ContestPassword == null) ||
(!participant.IsOfficial && participant.Contest.PracticePassword == null)) &&
(participant.Contest.IsVisible || participant.Contest.VisibleFrom <= this.dates.GetUtcNow()) &&
!participant.Contest.IsDeleted &&
newSubmission.IsPublic = ((participant.IsOfficial && contest!.ContestPassword == null) ||
(!participant.IsOfficial && contest!.PracticePassword == null)) &&
(contest.IsVisible || contest.VisibleFrom <= this.dates.GetUtcNow()) &&
!contest.IsDeleted &&
problem.ShowResults;

var submissionType = problem.SubmissionTypesInProblems
.First(st => st.SubmissionTypeId == model.SubmissionTypeId)
.SubmissionType;

SubmissionServiceModel submissionServiceModel;
var scope = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled);
if (submissionType.ExecutionStrategyType is ExecutionStrategyType.NotFound or ExecutionStrategyType.DoNothing)
if (submissionType!.ExecutionStrategyType is ExecutionStrategyType.NotFound or ExecutionStrategyType.DoNothing)
{
// Submission is just uploaded and should not be processed
await this.AddNewDefaultProcessedSubmission(participant, newSubmission);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
namespace OJS.Services.Ui.Business.Validations.Implementations.Submissions;

using OJS.Data.Models.Contests;
using OJS.Data.Models.Participants;
using OJS.Data.Models.Problems;
using OJS.Services.Common.Models;
using OJS.Services.Common.Models.Users;
using OJS.Data.Models.Submissions;
using OJS.Services.Common.Validation;
using OJS.Services.Infrastructure;
using OJS.Services.Infrastructure.Models;
using OJS.Services.Ui.Models.Submissions;

public interface ISubmitSubmissionValidationService : IValidationService
<(Problem?, Participant?, SubmitSubmissionServiceModel)>
<(Problem?, Participant?, SubmitSubmissionServiceModel, Contest?, SubmissionType?)>
{
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
namespace OJS.Services.Ui.Business.Validations.Implementations.Submissions;

using OJS.Data.Models.Contests;
using System.Linq;
using System.Text;
using OJS.Data.Models.Participants;
using OJS.Data.Models.Problems;
using OJS.Data.Models.Submissions;
using OJS.Services.Common;
using OJS.Services.Common.Models.Contests;
using OJS.Services.Infrastructure.Extensions;
Expand All @@ -28,9 +30,9 @@ public SubmitSubmissionValidationService(
this.submissionsData = submissionsData;
}

public ValidationResult GetValidationResult((Problem?, Participant?, SubmitSubmissionServiceModel) validationInput)
public ValidationResult GetValidationResult((Problem?, Participant?, SubmitSubmissionServiceModel, Contest?, SubmissionType?) item)
{
var (problem, participant, submitSubmissionServiceModel) = validationInput;
var (problem, participant, submitSubmissionServiceModel, contest, submissionType) = item;

if (problem == null)
{
Expand All @@ -42,8 +44,13 @@ public ValidationResult GetValidationResult((Problem?, Participant?, SubmitSubmi
return ValidationResult.Invalid(ValidationMessages.Participant.NotRegisteredForContest);
}

if (contest == null)
{
return ValidationResult.Invalid("Contest not found");
}

var isAdminOrLecturer = this.lecturersInContestsBusiness
.IsCurrentUserAdminOrLecturerInContest(participant.Contest.Id)
.IsCurrentUserAdminOrLecturerInContest(contest.Id)
.GetAwaiter()
.GetResult();

Expand All @@ -70,7 +77,7 @@ public ValidationResult GetValidationResult((Problem?, Participant?, SubmitSubmi
this.submissionsData.HasUserNotProcessedSubmissionForProblem(problem.Id, participant.UserId);

var userHasUnprocessedSubmissionForContest =
this.submissionsData.HasUserNotProcessedSubmissionForContest(participant.ContestId, participant.UserId);
this.submissionsData.HasUserNotProcessedSubmissionForContest(contest.Id, participant.UserId);

if (userHasUnprocessedSubmissionForProblem)
{
Expand All @@ -79,7 +86,7 @@ public ValidationResult GetValidationResult((Problem?, Participant?, SubmitSubmi
problemIdToString);
}

if (!participant.Contest.AllowParallelSubmissionsInTasks && userHasUnprocessedSubmissionForContest)
if (!contest.AllowParallelSubmissionsInTasks && userHasUnprocessedSubmissionForContest)
{
return ValidationResult.Invalid(
ValidationMessages.Submission.UserHasNotProcessedSubmissionForContest,
Expand All @@ -93,34 +100,31 @@ public ValidationResult GetValidationResult((Problem?, Participant?, SubmitSubmi
}

if (submitSubmissionServiceModel.Official &&
participant.Contest.IsOnlineExam &&
contest.IsOnlineExam &&
!isAdminOrLecturer &&
participant.ProblemsForParticipants.All(p => p.ProblemId != problem.Id))
{
return ValidationResult.Invalid(ValidationMessages.Problem.ProblemNotAssignedToUser, problemIdToString);
}

var submissionType = problem.SubmissionTypesInProblems.FirstOrDefault(st =>
st.SubmissionTypeId == submitSubmissionServiceModel.SubmissionTypeId);

if (submissionType == null)
{
return ValidationResult.Invalid(ValidationMessages.Submission.SubmissionTypeNotFound, problemIdToString);
}

var isFileUpload = submitSubmissionServiceModel.StringContent == null || submitSubmissionServiceModel.ByteContent != null;

if (isFileUpload && !submissionType.SubmissionType.AllowedFileExtensions!.Contains(submitSubmissionServiceModel.FileExtension!))
if (isFileUpload && !submissionType.AllowedFileExtensions!.Contains(submitSubmissionServiceModel.FileExtension!))
{
return ValidationResult.Invalid(ValidationMessages.Submission.InvalidExtension, problemIdToString);
}

if (isFileUpload && !submissionType.SubmissionType.AllowBinaryFilesUpload)
if (isFileUpload && !submissionType.AllowBinaryFilesUpload)
{
return ValidationResult.Invalid(ValidationMessages.Submission.BinaryFilesNotAllowed, problemIdToString);
}

if (!isFileUpload && submissionType.SubmissionType.AllowBinaryFilesUpload)
if (!isFileUpload && submissionType.AllowBinaryFilesUpload)
{
return ValidationResult.Invalid(ValidationMessages.Submission.TextUploadNotAllowed, problemIdToString);
}
Expand All @@ -142,7 +146,7 @@ public ValidationResult GetValidationResult((Problem?, Participant?, SubmitSubmi
return ValidationResult.Invalid(ValidationMessages.Submission.SubmissionTooShort, problemIdToString);
}

var userSubmissionTimeLimit = this.submissionsData.GetUserSubmissionTimeLimit(participant.Id, participant.Contest.LimitBetweenSubmissions);
var userSubmissionTimeLimit = this.submissionsData.GetUserSubmissionTimeLimit(participant.Id, contest.LimitBetweenSubmissions);

if (userSubmissionTimeLimit != 0)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ public interface IParticipantsDataService : IDataService<Participant>
{
Task<Participant?> GetByContestByUserAndByIsOfficial(int contestId, string userId, bool isOfficial);

Task<Participant?> GetWithContestAndSubmissionDetailsByContestByUserAndIsOfficial(int contestId, string userId, bool isOfficial);
Task<Participant?> GetWithContestAndProblemsForParticipantByContestByUserAndIsOfficial(int contestId, string userId, bool isOfficial);

IQueryable<Participant> GetWithProblemsForParticipantsByContestAndUser(int contestId, string userI);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ public async Task<IEnumerable<TServiceModel>> GetAllExpired<TServiceModel>()

public async Task<TServiceModel?> GetById<TServiceModel>(int id)
=> await this.GetByIdQuery(id)
.AsSplitQuery()
.MapCollection<TServiceModel>()
.FirstOrDefaultAsync();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@ namespace OJS.Services.Ui.Data.Implementations

public class ParticipantsDataService : DataService<Participant>, IParticipantsDataService
{
private readonly OjsDbContext db;

public ParticipantsDataService(OjsDbContext db)
: base(db)
{
}
: base(db) => this.db = db;

public Task<Participant?> GetByContestByUserAndByIsOfficial(
int contestId,
Expand All @@ -26,16 +26,10 @@ public ParticipantsDataService(OjsDbContext db)
.GetAllByContestByUserAndIsOfficial(contestId, userId, isOfficial)
.FirstOrDefaultAsync();

public Task<Participant?> GetWithContestAndSubmissionDetailsByContestByUserAndIsOfficial(int contestId, string userId, bool isOfficial)
public Task<Participant?> GetWithContestAndProblemsForParticipantByContestByUserAndIsOfficial(int contestId, string userId, bool isOfficial)
=> this.GetAllByContestByUserAndIsOfficial(contestId, userId, isOfficial)
.Include(p => p.User)
.Include(p => p.Contest)
.ThenInclude(c => c.Category)
.Include(p => p.Contest)
.ThenInclude(c => c.ProblemGroups)
.ThenInclude(pg => pg.Problems)
.ThenInclude(p => p.SubmissionTypesInProblems)
.ThenInclude(sp => sp.SubmissionType)
.Include(p => p.ProblemsForParticipants)
.FirstOrDefaultAsync();

Expand Down
Loading
Loading