162 lines
9.5 KiB
C#
162 lines
9.5 KiB
C#
using System.Data;
|
|
using Dapper;
|
|
using Microsoft.Data.SqlClient;
|
|
using PlotLine.Models;
|
|
|
|
namespace PlotLine.Data;
|
|
|
|
public interface INameIntelligenceRepository
|
|
{
|
|
Task<NameReferenceImportStatus?> GetLatestImportAsync(string packageId, string version, CancellationToken cancellationToken = default);
|
|
Task<int> StartImportAsync(string packageId, string version, string checksum, IDbConnection connection, IDbTransaction transaction, CancellationToken cancellationToken = default);
|
|
Task CompleteImportAsync(int importId, bool succeeded, int localeRows, int givenNameRows, int relationshipRows, int usageRows, string? errorSummary, IDbConnection connection, IDbTransaction transaction, CancellationToken cancellationToken = default);
|
|
Task UpsertLocalesAsync(DataTable rows, IDbConnection connection, IDbTransaction transaction, CancellationToken cancellationToken = default);
|
|
Task UpsertGivenNamesAsync(DataTable rows, IDbConnection connection, IDbTransaction transaction, CancellationToken cancellationToken = default);
|
|
Task UpsertRelationshipsAsync(DataTable rows, IDbConnection connection, IDbTransaction transaction, CancellationToken cancellationToken = default);
|
|
Task UpsertUsagesAsync(DataTable rows, IDbConnection connection, IDbTransaction transaction, CancellationToken cancellationToken = default);
|
|
Task<IReadOnlyList<NameLocale>> ListLocalesAsync(CancellationToken cancellationToken = default);
|
|
Task<NameRecognitionResult> RecogniseAsync(string normalisedName, string? localeCode, int? birthYear, CancellationToken cancellationToken = default);
|
|
Task<IReadOnlyList<NameRelationshipReference>> ListRelationshipsAsync(int givenNameId, CancellationToken cancellationToken = default);
|
|
Task<IReadOnlyList<NameSearchResult>> SearchAsync(int userId, NameSearchFilter filter, CancellationToken cancellationToken = default);
|
|
Task<IReadOnlyList<NameUsageDecoration>> ListUsedDecorationsAsync(int userId, int projectId, int? bookId, string normalisedName, string usedScope, CancellationToken cancellationToken = default);
|
|
}
|
|
|
|
public sealed class NameIntelligenceRepository(ISqlConnectionFactory connectionFactory) : INameIntelligenceRepository
|
|
{
|
|
public async Task<NameReferenceImportStatus?> GetLatestImportAsync(string packageId, string version, CancellationToken cancellationToken = default)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
return await connection.QuerySingleOrDefaultAsync<NameReferenceImportStatus>(Command(
|
|
"dbo.NameReferenceImport_GetLatest",
|
|
new { PackageID = packageId, SemanticVersion = version },
|
|
cancellationToken: cancellationToken));
|
|
}
|
|
|
|
public async Task<int> StartImportAsync(string packageId, string version, string checksum, IDbConnection connection, IDbTransaction transaction, CancellationToken cancellationToken = default)
|
|
=> await connection.QuerySingleAsync<int>(Command(
|
|
"dbo.NameReferenceImport_Start",
|
|
new { PackageID = packageId, SemanticVersion = version, PackageChecksum = checksum },
|
|
transaction,
|
|
cancellationToken));
|
|
|
|
public Task CompleteImportAsync(int importId, bool succeeded, int localeRows, int givenNameRows, int relationshipRows, int usageRows, string? errorSummary, IDbConnection connection, IDbTransaction transaction, CancellationToken cancellationToken = default)
|
|
=> connection.ExecuteAsync(Command(
|
|
"dbo.NameReferenceImport_Complete",
|
|
new
|
|
{
|
|
NameReferenceImportID = importId,
|
|
Succeeded = succeeded,
|
|
LocaleRows = localeRows,
|
|
GivenNameRows = givenNameRows,
|
|
RelationshipRows = relationshipRows,
|
|
UsageRows = usageRows,
|
|
ErrorSummary = errorSummary
|
|
},
|
|
transaction,
|
|
cancellationToken));
|
|
|
|
public Task UpsertLocalesAsync(DataTable rows, IDbConnection connection, IDbTransaction transaction, CancellationToken cancellationToken = default)
|
|
=> ExecuteTableAsync("dbo.NameLocale_BulkUpsert", "Rows", "dbo.NameLocaleImportTable", rows, connection, transaction, cancellationToken);
|
|
|
|
public Task UpsertGivenNamesAsync(DataTable rows, IDbConnection connection, IDbTransaction transaction, CancellationToken cancellationToken = default)
|
|
=> ExecuteTableAsync("dbo.GivenName_BulkUpsert", "Rows", "dbo.GivenNameImportTable", rows, connection, transaction, cancellationToken);
|
|
|
|
public Task UpsertRelationshipsAsync(DataTable rows, IDbConnection connection, IDbTransaction transaction, CancellationToken cancellationToken = default)
|
|
=> ExecuteTableAsync("dbo.NameRelationship_BulkUpsert", "Rows", "dbo.NameRelationshipImportTable", rows, connection, transaction, cancellationToken);
|
|
|
|
public Task UpsertUsagesAsync(DataTable rows, IDbConnection connection, IDbTransaction transaction, CancellationToken cancellationToken = default)
|
|
=> ExecuteTableAsync("dbo.NameUsage_BulkUpsert", "Rows", "dbo.NameUsageImportTable", rows, connection, transaction, cancellationToken);
|
|
|
|
public async Task<IReadOnlyList<NameLocale>> ListLocalesAsync(CancellationToken cancellationToken = default)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
var rows = await connection.QueryAsync<NameLocale>(Command("dbo.NameLocale_List", cancellationToken: cancellationToken));
|
|
return rows.ToList();
|
|
}
|
|
|
|
public async Task<NameRecognitionResult> RecogniseAsync(string normalisedName, string? localeCode, int? birthYear, CancellationToken cancellationToken = default)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
using var grid = await connection.QueryMultipleAsync(Command(
|
|
"dbo.GivenName_Recognise",
|
|
new { NormalisedName = normalisedName, LocaleCode = localeCode, BirthYear = birthYear },
|
|
cancellationToken: cancellationToken));
|
|
var name = await grid.ReadSingleOrDefaultAsync<GivenNameReference>();
|
|
if (name is null)
|
|
{
|
|
return new NameRecognitionResult();
|
|
}
|
|
|
|
var usage = await grid.ReadSingleOrDefaultAsync<NameUsageProfile>();
|
|
var related = (await grid.ReadAsync<NameRelationshipReference>()).ToList();
|
|
return new NameRecognitionResult
|
|
{
|
|
Recognised = true,
|
|
Name = name,
|
|
Usage = usage,
|
|
RelatedForms = related,
|
|
Message = "Recognised given-name form."
|
|
};
|
|
}
|
|
|
|
public async Task<IReadOnlyList<NameRelationshipReference>> ListRelationshipsAsync(int givenNameId, CancellationToken cancellationToken = default)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
var rows = await connection.QueryAsync<NameRelationshipReference>(Command(
|
|
"dbo.GivenName_Relationships",
|
|
new { GivenNameID = givenNameId },
|
|
cancellationToken: cancellationToken));
|
|
return rows.ToList();
|
|
}
|
|
|
|
public async Task<IReadOnlyList<NameSearchResult>> SearchAsync(int userId, NameSearchFilter filter, CancellationToken cancellationToken = default)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
var rows = await connection.QueryAsync<NameSearchResult>(Command(
|
|
"dbo.GivenName_Search",
|
|
new
|
|
{
|
|
UserID = userId,
|
|
filter.ProjectID,
|
|
filter.BookID,
|
|
Search = NullIfWhiteSpace(filter.Search),
|
|
LocaleCode = NullIfWhiteSpace(filter.LocaleCode),
|
|
filter.BirthYear,
|
|
GenderPreference = NullIfWhiteSpace(filter.GenderPreference),
|
|
filter.GenderThreshold,
|
|
NameType = NullIfWhiteSpace(filter.NameType),
|
|
filter.MinimumUsage,
|
|
filter.UsedScope,
|
|
filter.UsedOnly,
|
|
filter.Page,
|
|
filter.PageSize
|
|
},
|
|
cancellationToken: cancellationToken));
|
|
return rows.ToList();
|
|
}
|
|
|
|
public async Task<IReadOnlyList<NameUsageDecoration>> ListUsedDecorationsAsync(int userId, int projectId, int? bookId, string normalisedName, string usedScope, CancellationToken cancellationToken = default)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
var rows = await connection.QueryAsync<NameUsageDecoration>(Command(
|
|
"dbo.GivenName_UsedDecorations",
|
|
new { UserID = userId, ProjectID = projectId, BookID = bookId, NormalisedName = normalisedName, UsedScope = usedScope },
|
|
cancellationToken: cancellationToken));
|
|
return rows.ToList();
|
|
}
|
|
|
|
public IDbConnection CreateConnection() => connectionFactory.CreateConnection();
|
|
|
|
private static async Task ExecuteTableAsync(string procedure, string parameterName, string typeName, DataTable rows, IDbConnection connection, IDbTransaction transaction, CancellationToken cancellationToken)
|
|
{
|
|
var parameters = new DynamicParameters();
|
|
parameters.Add(parameterName, rows.AsTableValuedParameter(typeName));
|
|
await connection.ExecuteAsync(Command(procedure, parameters, transaction, cancellationToken));
|
|
}
|
|
|
|
private static CommandDefinition Command(string commandText, object? parameters = null, IDbTransaction? transaction = null, CancellationToken cancellationToken = default)
|
|
=> new(commandText, parameters, transaction, commandType: CommandType.StoredProcedure, cancellationToken: cancellationToken);
|
|
|
|
private static string? NullIfWhiteSpace(string? value) => string.IsNullOrWhiteSpace(value) ? null : value.Trim();
|
|
}
|