chore: removed everything related to instructions

This commit is contained in:
2025-09-20 22:42:33 +03:00
parent b6778046c2
commit 8ea18f1096
50 changed files with 0 additions and 1953 deletions

View File

@@ -1,22 +0,0 @@
using GamificationService.Models.Database;
using GamificationService.Models.DTO;
namespace GamificationService.Services.InstructionTests;
public interface IInstructionTestsService
{
public Task<InstructionTest> CreateInstructionTest(InstructionTest instructionTest);
public Task<InstructionTestDTO> CreateInstructionTest(InstructionTestCreateDTO instructionTest);
public Task<bool> UpdateInstructionTest(InstructionTest instructionTest);
public Task<bool> UpdateInstructionTest(InstructionTestCreateDTO instructionTest);
public Task<bool> DeleteInstructionTestByIdAsync(long id);
public Task<InstructionTestResultDTO> SubmitInstructionTestAsync(long userId, InstructionTestSubmissionDTO submission);
public InstructionTestDTO GetInstructionTestById(long id);
public List<InstructionTestDTO> GetInstructionTestsByInstructionId(long instructionId);
public List<InstructionTestQuestionDTO> GetInstructionTestQuestionsByInstructionTestId(long instructionTestId);
public List<InstructionTestResultDTO> GetUserInstructionTestResultsByInstructionTestId(long userId, long instructionId);
public List<InstructionTestResultDTO> GetInstructionTestResultsByUserId(long userId);
public List<InstructionTestResultDTO> GetCompletedInstructionTestsByUserId(long userId);
}

View File

@@ -1,294 +0,0 @@
using AutoMapper;
using GamificationService.Database.Repositories;
using GamificationService.Exceptions.Services.Instruction;
using GamificationService.Exceptions.Services.InstructionTest;
using GamificationService.Models.Database;
using GamificationService.Models.DTO;
using GamificationService.Utils.Enums;
namespace GamificationService.Services.InstructionTests;
public class InstructionTestsService : IInstructionTestsService
{
private readonly ILogger<InstructionTestsService> _logger;
private readonly UnitOfWork _unitOfWork;
private readonly IMapper _mapper;
public InstructionTestsService(ILogger<InstructionTestsService> logger, UnitOfWork unitOfWork, IMapper mapper)
{
_logger = logger;
_unitOfWork = unitOfWork;
_mapper = mapper;
}
public async Task<InstructionTestDTO> CreateInstructionTest(InstructionTestCreateDTO instructionTest)
{
return _mapper.Map<InstructionTestDTO>(await CreateInstructionTest(_mapper.Map<InstructionTest>(instructionTest)));
}
public async Task<bool> DeleteInstructionTestByIdAsync(long id)
{
var instructionTest = _unitOfWork.InstructionTestRepository.GetByID(id);
if (instructionTest == null)
{
_logger.LogError("Instruction test with id {Id} not found", id);
throw new InstructionTestNotFoundException();
}
// Find all questions
var questions = _unitOfWork.InstructionTestQuestionRepository.Get(q => q.InstructionTestId == id);
// Start transaction
await _unitOfWork.BeginTransactionAsync();
foreach (var question in questions)
{
_unitOfWork.InstructionTestQuestionRepository.Delete(question);
}
// Delete instruction test
_unitOfWork.InstructionTestRepository.Delete(instructionTest);
if (await _unitOfWork.SaveAsync())
{
await _unitOfWork.CommitAsync();
_logger.LogInformation("Instruction test deleted ({Id})", id);
return true;
}
else
{
_logger.LogError("Failed to delete instruction test ({Id})", id);
throw new InstructionTestDeletionException();
}
}
public List<InstructionTestResultDTO> GetCompletedInstructionTestsByUserId(long userId)
{
var userTestAttempts = _unitOfWork.InstructionTestResultRepository.Get(
q => q.UserId == userId).ToList();
var userInstructionTests = _unitOfWork.InstructionTestRepository.Get(
q => userTestAttempts.Any(a => a.InstructionTestId == q.Id)).ToList();
var conclusiveUserTestResults = new List<InstructionTestResultDTO>();
foreach (var instructionTest in userInstructionTests)
{
var scoreCalcMethod = instructionTest.ScoreCalcMethod;
int maxScore = 0;
if (scoreCalcMethod == InstructionTestScoreCalcMethod.AverageGrade)
{
maxScore = (int)Math.Round(userTestAttempts.Where(q => q.InstructionTestId == instructionTest.Id).Average(q => q.Score));
}
else
{
maxScore = userTestAttempts.Where(q => q.InstructionTestId == instructionTest.Id).Max(q => q.Score);
}
if (maxScore >= instructionTest.MinScore)
{
conclusiveUserTestResults.Add(_mapper.Map<InstructionTestResultDTO>(userTestAttempts.First(q => q.InstructionTestId == instructionTest.Id)));
}
}
return conclusiveUserTestResults;
}
public InstructionTestDTO GetInstructionTestById(long id)
{
var instructionTest = _unitOfWork.InstructionTestRepository.GetByID(id);
if (instructionTest == null)
{
_logger.LogError("Instruction test with id {Id} not found", id);
throw new InstructionTestNotFoundException();
}
return _mapper.Map<InstructionTestDTO>(instructionTest);
}
public List<InstructionTestQuestionDTO> GetInstructionTestQuestionsByInstructionTestId(long instructionTestId)
{
var questions = _unitOfWork.InstructionTestQuestionRepository.Get(q => q.InstructionTestId == instructionTestId);
return _mapper.Map<List<InstructionTestQuestionDTO>>(questions);
}
public List<InstructionTestResultDTO> GetUserInstructionTestResultsByInstructionTestId(long userId, long instructionTestId)
{
var userTestResults = _unitOfWork.InstructionTestResultRepository.Get(
q => q.UserId == userId && q.InstructionTestId == instructionTestId).ToList();
return _mapper.Map<List<InstructionTestResultDTO>>(userTestResults);
}
public List<InstructionTestResultDTO> GetInstructionTestResultsByUserId(long userId)
{
var userTestResults = _unitOfWork.InstructionTestResultRepository.Get(
q => q.UserId == userId).ToList();
return _mapper.Map<List<InstructionTestResultDTO>>(userTestResults);
}
public List<InstructionTestDTO> GetInstructionTestsByInstructionId(long instructionId)
{
var instructionTest = (_unitOfWork.InstructionRepository.GetByID(instructionId)
?? throw new InstructionNotFoundException())
.InstructionTest ?? throw new InstructionTestNotFoundException();
return _mapper.Map<List<InstructionTestDTO>>(new List<InstructionTest>() {instructionTest});
}
public async Task<InstructionTestResultDTO> SubmitInstructionTestAsync(long userId, InstructionTestSubmissionDTO submission)
{
// Retrieve the test and questions
var instructionTest = _unitOfWork.InstructionTestRepository.GetByID(submission.InstructionTestId);
if (instructionTest == null)
{
_logger.LogError("Instruction test with id {Id} not found", submission.InstructionTestId);
throw new InstructionTestNotFoundException();
}
// Check remaining attempts
var userTestAttempts = _unitOfWork.InstructionTestResultRepository.Get(
q => q.UserId == userId && q.InstructionTestId == submission.InstructionTestId).ToList();
if (userTestAttempts.Count >= instructionTest.MaxAttempts)
{
_logger.LogWarning("User {UserId}: denied submission for test {InstructionTestId}: max attempts reached", userId, submission.InstructionTestId);
throw new InstructionTestSubmissionException();
}
var questions = _unitOfWork.InstructionTestQuestionRepository.Get(q => q.InstructionTestId == submission.InstructionTestId).ToList();
// Verify answers amount
if (questions.Count != submission.Answers.Count)
{
_logger.LogWarning("User {UserId}: denied submission for test {InstructionTestId}: wrong number of answers", userId, submission.InstructionTestId);
throw new InstructionTestSubmissionException();
}
// Evaluate answers
double score = 0;
int maxErrorPerQuestion = 1;
for (int i = 0; i < questions.Count; i++)
{
var question = questions[i];
// User answers for the question without duplicate options
var answer = submission.Answers[i].Distinct();
if (question.IsMultipleAnswer)
{
int correctUserAnswersCount = 0;
int incorrectUserAnswersCount = 0;
int correctAnswersCount = question.CorrectAnswers.Count;
foreach (var option in answer)
{
if (question.CorrectAnswers.Contains(option))
{
correctUserAnswersCount++;
}
else
{
incorrectUserAnswersCount++;
}
}
if (incorrectUserAnswersCount > maxErrorPerQuestion || correctUserAnswersCount == 0)
{
// Nothing scored for the question
continue;
}
// One question is worth 1 point max
double questionScore = correctUserAnswersCount / (double)correctAnswersCount;
// Add the question score, or half of it if an error is present
score += incorrectUserAnswersCount > 0 ? questionScore /= 2 : questionScore;
}
else
{
score += question.CorrectAnswers.Contains(answer.First()) ? 1 : 0;
}
}
score = Math.Round(score / questions.Count)*100;
// Add test result
await _unitOfWork.BeginTransactionAsync();
InstructionTestResult newTestResult = new InstructionTestResult()
{
UserId = userId,
InstructionTestId = submission.InstructionTestId,
Score = (int)score
};
_unitOfWork.InstructionTestResultRepository.Insert(newTestResult);
if (!await _unitOfWork.SaveAsync())
{
_logger.LogError("Failed to save test result for user {UserId} and test {InstructionTestId}", userId, submission.InstructionTestId);
throw new InstructionTestSubmissionException();
}
await _unitOfWork.CommitAsync();
return _mapper.Map<InstructionTestResultDTO>(newTestResult);
}
public async Task<bool> UpdateInstructionTest(InstructionTestCreateDTO instructionTest)
{
return await UpdateInstructionTest(_mapper.Map<InstructionTest>(instructionTest));
}
public async Task<InstructionTest> CreateInstructionTest(InstructionTest instructionTest)
{
instructionTest.Id = 0;
await _unitOfWork.BeginTransactionAsync();
await _unitOfWork.InstructionTestRepository.InsertAsync(instructionTest);
if (await _unitOfWork.SaveAsync() == false)
{
_logger.LogError("Failure to create instruction test");
throw new InstructionTestCreationException();
}
await _unitOfWork.CommitAsync();
_logger.LogInformation($"Created instruction test {instructionTest.Id}");
return instructionTest;
}
public async Task<bool> UpdateInstructionTest(InstructionTest instructionTest)
{
var existingInstructionTest = _unitOfWork.InstructionTestRepository.GetByID(instructionTest.Id);
if (existingInstructionTest == null)
{
throw new InstructionTestNotFoundException();
}
await _unitOfWork.BeginTransactionAsync();
_unitOfWork.InstructionTestQuestionRepository.DeleteRange(existingInstructionTest.Questions);
if (await _unitOfWork.SaveAsync() == false)
{
_logger.LogError($"Failure to create existing questions for instruction test {instructionTest.Id} during update");
throw new InstructionTestCreationException();
}
_unitOfWork.InstructionTestRepository.Update(instructionTest);
if (await _unitOfWork.SaveAsync() == false)
{
_logger.LogError($"Failure to update instruction test {instructionTest.Id}");
}
await _unitOfWork.CommitAsync();
return true;
}
}

View File

@@ -1,21 +0,0 @@
using GamificationService.Models.Database;
using GamificationService.Models.DTO;
using GamificationService.Models.Messages.Instructions;
namespace GamificationService.Services.Instructions;
public interface IInstructionService
{
public Task<Instruction> CreateInstruction(Instruction model);
public Task<bool> UpdateInstructionById(Instruction model);
public Task<bool> DeleteInstructionById(long instructionId);
public Task<InstructionDTO> CreateInstruction(CreateInstructionRequest model);
public Task<bool> UpdateInstructionById(UpdateInstructionRequest model);
public List<InstructionDTO> GetAllInstructions(long userId);
public List<InstructionDTO> GetInstructionsByCategoryId(long userId, long categoryId);
public List<InstructionDTO> GetCompletedInstructions(long userId);
public List<InstructionDTO> GetUnfinishedInstructions(long userId);
public List<InstructionDTO> GetInstructionById(long userId, long instructionId);
}

View File

@@ -1,174 +0,0 @@
using AutoMapper;
using GamificationService.Database.Repositories;
using GamificationService.Exceptions.Services.Instruction;
using GamificationService.Models.Database;
using GamificationService.Models.DTO;
using GamificationService.Models.Messages.Instructions;
using GamificationService.Services.InstructionTests;
namespace GamificationService.Services.Instructions;
public class InstructionService : IInstructionService
{
private readonly UnitOfWork _unitOfWork;
private readonly IMapper _mapper;
private readonly ILogger<InstructionService> _logger;
private readonly IInstructionTestsService _instructionTestService;
public InstructionService(UnitOfWork unitOfWork, IMapper mapper, ILogger<InstructionService> logger, IInstructionTestsService instructionTestService)
{
_unitOfWork = unitOfWork;
_mapper = mapper;
_logger = logger;
_instructionTestService = instructionTestService;
}
public async Task<Instruction> CreateInstruction(Instruction model)
{
model.Id = 0;
await _unitOfWork.BeginTransactionAsync();
await _unitOfWork.InstructionRepository.InsertAsync(model);
if(await _unitOfWork.SaveAsync() == false)
{
_logger.LogError($"Instruction {model.Id} could not be created");
throw new InstructionCreationException();
}
await _unitOfWork.CommitAsync();
_logger.LogInformation($"Instruction {model.Id} created");
return model;
}
public async Task<InstructionDTO> CreateInstruction(CreateInstructionRequest model)
{
Instruction newInstruction = new Instruction()
{
Title = model.Title,
Description = model.Description,
CategoryId = model.CategoryId,
Paragraphs = _mapper.Map<List<InstructionParagraph>>(model.Paragraphs),
AssignDate = model.AssignDate,
DeadlineDate = model.DeadlineDate,
IsEnabled = model.IsEnabled
};
return _mapper.Map<InstructionDTO>(await CreateInstruction(newInstruction));
}
public async Task<bool> DeleteInstructionById(long instructionId)
{
Instruction? instruction = _unitOfWork.InstructionRepository.GetByID(instructionId);
if (instruction == null)
{
throw new InstructionNotFoundException();
}
_unitOfWork.InstructionRepository.Delete(instruction);
if (await _unitOfWork.SaveAsync() == false)
{
_logger.LogError($"Instruction {instructionId} could not be deleted");
throw new InstructionDeletionException();
}
_logger.LogInformation($"Instruction {instructionId} deleted");
return true;
}
public List<InstructionDTO> GetAllInstructions(long userId)
{
// TODO: select accessible only
var instructions = _unitOfWork.InstructionRepository.Get().ToList();
return _mapper.Map<List<InstructionDTO>>(instructions);
}
public List<InstructionDTO> GetCompletedInstructions(long userId)
{
var completedTests = _instructionTestService.GetCompletedInstructionTestsByUserId(userId);
var instructions = _unitOfWork.InstructionRepository.Get()
.Where(i => completedTests.Any(t => i.InstructionTestId == t.Id));
return _mapper.Map<List<InstructionDTO>>(instructions);
}
public List<InstructionDTO> GetInstructionById(long userId, long instructionId)
{
// TODO: select accessible only
var instruction = _unitOfWork.InstructionRepository.GetByID(instructionId);
return _mapper.Map<List<InstructionDTO>>(instruction);
}
public List<InstructionDTO> GetInstructionsByCategoryId(long userId, long categoryId)
{
var category = _unitOfWork.InstructionCategoryRepository.GetByID(categoryId);
if (category == null)
{
throw new CategoryNotFoundException();
}
var instructions = _unitOfWork.InstructionRepository.Get().Where(i => i.CategoryId == categoryId);
return _mapper.Map<List<InstructionDTO>>(instructions);
}
public List<InstructionDTO> GetUnfinishedInstructions(long userId)
{
// TODO: only show accessible
var completedTests = _instructionTestService.GetCompletedInstructionTestsByUserId(userId);
var instructions = _unitOfWork.InstructionRepository.Get()
.Where(i => !completedTests.Any(t => i.InstructionTestId == t.Id));
return _mapper.Map<List<InstructionDTO>>(instructions);
}
public async Task<bool> UpdateInstructionById(Instruction model)
{
var existingInstruction = _unitOfWork.InstructionRepository.GetByID(model.Id);
if (existingInstruction == null)
{
throw new InstructionNotFoundException();
}
await _unitOfWork.BeginTransactionAsync();
_unitOfWork.InstructionParagraphRepository.DeleteRange(existingInstruction.Paragraphs);
if (await _unitOfWork.SaveAsync() == false)
{
_logger.LogError($"Failure to delete older paragraphs for instruction {model.Id} during update");
throw new InstructionUpdateException();
}
_unitOfWork.InstructionRepository.Update(model);
if (await _unitOfWork.SaveAsync() == false)
{
_logger.LogError($"Instruction {model.Id} could not be updated");
throw new InstructionUpdateException();
}
_logger.LogInformation($"Instruction {model.Id} updated");
return true;
}
public async Task<bool> UpdateInstructionById(UpdateInstructionRequest model)
{
Instruction? existingInstruction = _unitOfWork.InstructionRepository.GetByID(model.Id);
if (existingInstruction == null)
{
throw new InstructionNotFoundException();
}
existingInstruction.Title = model.Title;
existingInstruction.Description = model.Description;
existingInstruction.CategoryId = model.CategoryId;
existingInstruction.Paragraphs = _mapper.Map<List<InstructionParagraph>>(model.Paragraphs);
existingInstruction.AssignDate = model.AssignDate;
existingInstruction.DeadlineDate = model.DeadlineDate;
existingInstruction.IsEnabled = model.IsEnabled;
return await UpdateInstructionById(_mapper.Map<Instruction>(model));
}
}