Files
tiku-backend.net/Tiku.Infrastructure/Learning/PracticeReports/PracticeReportService.cs

106 lines
4.2 KiB
C#

using System.Text.Json;
using Microsoft.EntityFrameworkCore;
using Npgsql;
using Tiku.Application.Learning;
using Tiku.Domain.Common;
using Tiku.Domain.Content;
using Tiku.Domain.Learning;
namespace Tiku.Infrastructure.Learning;
internal sealed class PracticeReportService(LearningServiceDependencies dependencies)
: LearningActivityServiceBase(dependencies), IPracticeReportService
{
public async Task<PracticeSessionReportItem> GetPracticeSessionReportAsync(
LearningActor actor,
PracticeSessionFilter filter,
CancellationToken cancellationToken = default)
{
if (!filter.PracticeSessionId.HasValue)
throw new LearningValidationException("practice_session_id_required", "Practice session id is required.");
var report = await learningPersistence.PracticeSessionReports
.AsNoTracking()
.SingleOrDefaultAsync(
item =>
item.TenantId == actor.TenantId &&
item.UserId == actor.UserId &&
item.PracticeSessionId == filter.PracticeSessionId.Value,
cancellationToken);
if (report is null)
throw new LearningResourceNotFoundException("practice_report_not_found",
"Practice session report was not found.");
return ToItem(report);
}
public async Task<LearningList<PracticeSessionReportItem>> GetPracticeReportsAsync(
LearningActor actor,
PracticeSessionFilter filter,
CancellationToken cancellationToken = default)
{
var query = learningPersistence.PracticeSessionReports
.AsNoTracking()
.Where(report =>
report.TenantId == actor.TenantId &&
report.UserId == actor.UserId);
if (!string.IsNullOrWhiteSpace(filter.Mode)) query = query.Where(report => report.Mode == filter.Mode.Trim());
var items = await query
.OrderByDescending(report => report.SubmittedAt)
.Take(ResolveLimit(filter.Limit))
.ToArrayAsync(cancellationToken);
return new LearningList<PracticeSessionReportItem>(items.Select(ToItem).ToArray());
}
public async Task<LearningList<PracticeHistoryItem>> GetPracticeHistoryAsync(
LearningActor actor,
PracticeSessionFilter filter,
CancellationToken cancellationToken = default)
{
var query = learningPersistence.PracticeSessions
.AsNoTracking()
.Where(session =>
session.TenantId == actor.TenantId &&
session.UserId == actor.UserId);
if (!string.IsNullOrWhiteSpace(filter.Mode)) query = query.Where(session => session.Mode == filter.Mode.Trim());
var rows = await query
.GroupJoin(
learningPersistence.PracticeSessionReports.AsNoTracking(),
session => new { session.TenantId, PracticeSessionId = session.Id },
report => new { report.TenantId, report.PracticeSessionId },
(session, reports) => new { session, report = reports.FirstOrDefault() })
.OrderByDescending(row => row.session.FinishedAt ?? row.session.StartedAt)
.Take(ResolveLimit(filter.Limit))
.ToArrayAsync(cancellationToken);
var now = DateTimeOffset.UtcNow;
var items = rows
.Select(row => new PracticeHistoryItem(
row.session.Id,
row.session.Mode,
row.session.ResourceType,
row.session.ResourceId,
row.session.QuestionCount,
row.report?.AnsweredCount ?? 0,
row.report?.CorrectCount ?? 0,
row.report?.WrongCount ?? 0,
row.session.StartedAt,
row.session.FinishedAt,
row.session.ExpiresAt,
ResolvePracticeSessionHistoryStatus(row.session, now),
row.report?.Id,
row.report?.Score,
row.report?.TotalScore,
row.report?.Accuracy))
.Where(item => string.IsNullOrWhiteSpace(filter.Status) || item.Status == filter.Status.Trim())
.ToArray();
return new LearningList<PracticeHistoryItem>(items);
}
}