Skip to content
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
29 changes: 24 additions & 5 deletions src/CodeClash.API/Controllers/Contests/ContestController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using CodeClash.Application.Contests.GetAllContests;
using CodeClash.Application.Contests.GetContest;
using CodeClash.Application.Contests.RegisterInContest;
using CodeClash.Application.Plagiarism.GetContestPlagiarismCases;
using CodeClash.Domain.Premitives.Responses;
using MediatR;
using Microsoft.AspNetCore.Authorization;
Expand Down Expand Up @@ -36,15 +37,15 @@ public async Task<IActionResult> GetAllContests(
var result = await sender.Send(new GetAllContestsQuery(), cancellationToken);

return result.IsSuccess
? Ok(result.Value)
? Ok(result)
: BadRequest(result.Error);
}


[Authorize]
[HttpPost]
public async Task<IActionResult> CreateContest(
CreateContestCommand command,
[FromBody] CreateContestCommand command,
CancellationToken cancellationToken)
{
var result = await sender.Send(command, cancellationToken);
Expand All @@ -69,11 +70,11 @@ public async Task<IActionResult> RegisterInContest(
: BadRequest(result);
}

[HttpPost("{contestId:guid}/problems/{problemId:guid}")]
[Authorize]
[HttpPost("{contestId:guid}/problems/{problemId:guid}")]
public async Task<IActionResult> AddProblem(
Guid contestId,
Guid problemId,
[FromRoute] Guid contestId,
[FromRoute] Guid problemId,
CancellationToken cancellationToken)
{
var result = await sender.Send(
Expand All @@ -97,4 +98,22 @@ public async Task<IActionResult> AddProblem(
_ => BadRequest(result)
};
}

[Authorize]
[HttpGet("{contestId:guid}/plagiarisms")]
[ProducesResponseType(typeof(GetContestPlagiarismCasesResponse), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public async Task<IActionResult> GetPlagiarismCases(
[FromRoute] Guid contestId,
[FromQuery] decimal threshold,
[FromQuery] List<Guid> problemIds,
CancellationToken cancellationToken)
{
var query = new GetContestPlagiarismCasesQuery(contestId, threshold, problemIds);
var result = await sender.Send(query, cancellationToken);

return result.IsSuccess
? Ok(result)
: NotFound(result);
}
}
19 changes: 19 additions & 0 deletions src/CodeClash.API/Controllers/Plagiarisms/PlagiarismController.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
using CodeClash.Application.Abstractions.Plagiarism;
using CodeClash.Domain.Requests;
using Microsoft.AspNetCore.Mvc;

namespace CodeClash.API.Controllers.Plagiarisms;

[ApiController]
[Route("plagiarisms")]
public sealed class PlagiarismController(
IPlagiarismService plagiarismService) : ControllerBase
{
[HttpPost("similarity")]
[ProducesResponseType(typeof(CodeSimilarityRequest), StatusCodes.Status200OK)]
public IActionResult GetSimilarity([FromBody] CodeSimilarityRequest request)
{
var similarity = plagiarismService.GetSimilarity(request.Code1, request.Code2);
return Ok(similarity);
}
}
5 changes: 4 additions & 1 deletion src/CodeClash.API/Extensions/SeedDataExtension.cs
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,10 @@ private static async Task SeedElasticAsync(

if (success)
{
logger.LogInformation("Seeded {Count} problems into Elasticsearch.", documents.Count);
if (logger.IsEnabled(LogLevel.Information))
{
logger.LogInformation("Seeded {Count} problems into Elasticsearch.", documents.Count);
}
}
else
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using CodeClash.Application.DTO;

namespace CodeClash.Application.Abstractions.Plagiarism;

public interface IPlagiarismService
{
Task<IEnumerable<PlagiarismCaseDto>> GetPlagiarismCases(
Guid contestId,
List<Guid> ProblemIds,
decimal threshold);

decimal GetSimilarity(
string code1,
string code2); // for testing purposes
}
1 change: 1 addition & 0 deletions src/CodeClash.Application/Behaviors/LoggingBehavior.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ public async Task<TResponse> Handle(

try
{
#pragma warning disable CA1873
// Log command execution start
logger.LogInformation("Executing request {Request}", name);

Expand Down
9 changes: 9 additions & 0 deletions src/CodeClash.Application/DTO/PlagiarismCaseDto.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
namespace CodeClash.Application.DTO;

public sealed class PlagiarismCaseDto
{
public SubmitDto FirstSubmission { get; set; }
public SubmitDto SecondSubmission { get; set; }
public decimal Similarity { get; set; }
public Guid ProblemId { get; set; }
}
11 changes: 11 additions & 0 deletions src/CodeClash.Application/DTO/SubmissionDto.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
using CodeClash.Domain.Premitives;

namespace CodeClash.Application.DTO;

public sealed class SubmissionDto
{
public string UserId { get; set; }
public string Code { get; set; }
public DateTime SubmissionDate { get; set; }
public Language Language { get; set; }
}
14 changes: 14 additions & 0 deletions src/CodeClash.Application/DTO/SubmitDto.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
using CodeClash.Domain.Premitives;

namespace CodeClash.Application.DTO;

public sealed class SubmitDto
{
public string UserId { get; set; }
public Guid ProblemId { get; set; }
public Guid? ContestId { get; set; }
public string Code { get; set; }
public Language Language { get; set; }
public DateTime SubmissionDate { get; set; }
public SubmissionResult Result { get; set; }
}
18 changes: 18 additions & 0 deletions src/CodeClash.Application/Mapping/PlagiarismMappings.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
using CodeClash.Application.DTO;
using CodeClash.Domain.Models.Submits;

namespace CodeClash.Application.Mapping;

public static class PlagiarismMappings
{
public static SubmitDto ToDto(this Submit submit) => new()
{
UserId = submit.UserId,
ProblemId = submit.ProblemId,
ContestId = submit.ContestId,
Code = submit.Code,
Language = submit.Language,
SubmissionDate = submit.SubmissionDate,
Result = submit.Result
};
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
using CodeClash.Application.Abstractions.Messaging;
using CodeClash.Application.Abstractions.Plagiarism;
using CodeClash.Domain.Premitives;

namespace CodeClash.Application.Plagiarism.GetContestPlagiarismCases;

internal sealed class GetContestPlagiarismCasesHandler(
IPlagiarismService plagiarismService) : IQueryHandler<GetContestPlagiarismCasesQuery, GetContestPlagiarismCasesResponse>
{
public async Task<Result<GetContestPlagiarismCasesResponse>> Handle(
GetContestPlagiarismCasesQuery request,
CancellationToken cancellationToken)
{
var cases = (await plagiarismService.GetPlagiarismCases(
request.ContestId,
request.ProblemIds,
request.Threshold)).ToList();

return Result.Success(new GetContestPlagiarismCasesResponse(
request.ContestId,
request.Threshold,
request.ProblemIds,
cases));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
using CodeClash.Application.Abstractions.Messaging;

namespace CodeClash.Application.Plagiarism.GetContestPlagiarismCases;

public sealed record GetContestPlagiarismCasesQuery(
Guid ContestId,
decimal Threshold,
List<Guid> ProblemIds) : IQuery<GetContestPlagiarismCasesResponse>;
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
using CodeClash.Application.DTO;

namespace CodeClash.Application.Plagiarism.GetContestPlagiarismCases;

public sealed record GetContestPlagiarismCasesResponse(
Guid ContestId,
decimal Threshold,
List<Guid> ProblemIds,
List<PlagiarismCaseDto> Cases);
3 changes: 3 additions & 0 deletions src/CodeClash.Domain/Abstractions/ISubmissionRepository.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using CodeClash.Domain.Premitives;

namespace CodeClash.Domain.Abstractions;

public interface ISubmissionRepository
{
Task<Submit?> GetByIdAsync(Guid id);
Expand All @@ -17,4 +18,6 @@ public interface ISubmissionRepository
Task<Dictionary<Guid, SubmissionResult>> GetUserSubmissionsAsync(string userId);

Task<Submit?> GetSubmissionIfAuthorized(string userId, Guid submissionId);

Task<List<Submit>> GetContestACSubmissionsByProblemIdsAsync(Guid contestId, List<Guid> problemIds);
}
7 changes: 7 additions & 0 deletions src/CodeClash.Domain/Requests/CodeSimilarityRequest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace CodeClash.Domain.Requests;

public sealed class CodeSimilarityRequest
{
public string Code1 { get; set; }
public string Code2 { get; set; }
}
4 changes: 4 additions & 0 deletions src/CodeClash.Infrastructure/DependencyInjection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using CodeClash.Application.Abstractions.Execution;
using CodeClash.Application.Abstractions.File;
using CodeClash.Application.Abstractions.Identity;
using CodeClash.Application.Abstractions.Plagiarism;
using CodeClash.Application.Abstractions.Roles;
using CodeClash.Application.Helpers;
using CodeClash.Domain.Abstractions;
Expand All @@ -26,6 +27,7 @@
using StackExchange.Redis;

namespace CodeClash.Infrastructure;

public static class DependencyInjection
{
public static IServiceCollection AddInfrastructure(
Expand Down Expand Up @@ -64,6 +66,8 @@ public static IServiceCollection AddInfrastructure(

services.AddScoped<IExecutionService, ExecutionService>();

services.AddScoped<IPlagiarismService, PlagiarismService>();

services.AddScoped<IEmailService, EmailService>();

services.AddScoped<IElasticService, ElasticService>();
Expand Down
2 changes: 2 additions & 0 deletions src/CodeClash.Infrastructure/Implementation/ElasticService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using Nest;

namespace CodeClash.Infrastructure.Implementation;

internal sealed class ElasticService : IElasticService
{
private readonly ElasticClient _client;
Expand Down Expand Up @@ -98,6 +99,7 @@ private async Task EnsureIndexAsync<T>(string indexName) where T : class
}
else
{
#pragma warning disable CA1873
_logger.LogInformation("Index '{IndexName}' created successfully.", indexName);
}
}
Expand Down
Loading
Loading