112 lines
4.5 KiB
C#
112 lines
4.5 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 (filter.BlueprintId.HasValue) query = query.Where(report => report.BlueprintId == filter.BlueprintId.Value);
|
|
|
|
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.TargetType,
|
|
row.session.TargetId,
|
|
row.session.BlueprintId,
|
|
row.session.CollectionId,
|
|
row.session.EntryId,
|
|
row.session.ContentNodeId,
|
|
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);
|
|
}
|
|
}
|