Public Access
197 lines
5.9 KiB
C#
197 lines
5.9 KiB
C#
using Microsoft.EntityFrameworkCore;
|
|
using System.ComponentModel.DataAnnotations.Schema;
|
|
|
|
namespace MeetingAssistant.Speakers;
|
|
|
|
public sealed class SpeakerIdentity
|
|
{
|
|
public int Id { get; set; }
|
|
|
|
public string? CanonicalName { get; set; }
|
|
|
|
public DateTimeOffset CreatedAt { get; set; }
|
|
|
|
public DateTimeOffset UpdatedAt { get; set; }
|
|
|
|
public int TranscriptionCount { get; set; }
|
|
|
|
public List<SpeakerCandidateName> CandidateNames { get; set; } = [];
|
|
|
|
public List<SpeakerAlias> Aliases { get; set; } = [];
|
|
|
|
public List<SpeakerSnippet> Snippets { get; set; } = [];
|
|
|
|
public List<SpeakerIdentityReference> References { get; set; } = [];
|
|
|
|
[NotMapped]
|
|
public int ReferenceCount => References.Count;
|
|
|
|
public string? GetDisplayName()
|
|
{
|
|
if (!string.IsNullOrWhiteSpace(CanonicalName))
|
|
{
|
|
return CanonicalName;
|
|
}
|
|
|
|
return Aliases
|
|
.Select(alias => alias.Name)
|
|
.Where(alias => !string.IsNullOrWhiteSpace(alias))
|
|
.Order(StringComparer.OrdinalIgnoreCase)
|
|
.FirstOrDefault();
|
|
}
|
|
}
|
|
|
|
public sealed class SpeakerIdentityReference
|
|
{
|
|
public int Id { get; set; }
|
|
|
|
public int SpeakerIdentityId { get; set; }
|
|
|
|
public SpeakerIdentity? SpeakerIdentity { get; set; }
|
|
|
|
public string MeetingNotePath { get; set; } = "";
|
|
|
|
public string TranscriptPath { get; set; } = "";
|
|
|
|
public DateTimeOffset CreatedAt { get; set; }
|
|
}
|
|
|
|
public static class SpeakerIdentityReferences
|
|
{
|
|
public static SpeakerIdentityReference Create(
|
|
string meetingNotePath,
|
|
string transcriptPath,
|
|
DateTimeOffset createdAt)
|
|
{
|
|
return new SpeakerIdentityReference
|
|
{
|
|
MeetingNotePath = meetingNotePath,
|
|
TranscriptPath = transcriptPath,
|
|
CreatedAt = createdAt
|
|
};
|
|
}
|
|
|
|
public static void AddIfMissing(
|
|
SpeakerIdentity identity,
|
|
SpeakerIdentityReference reference,
|
|
DateTimeOffset? createdAt = null)
|
|
{
|
|
if (IsEmpty(reference) || identity.References.Any(existing => IsSame(existing, reference)))
|
|
{
|
|
return;
|
|
}
|
|
|
|
identity.References.Add(Create(
|
|
reference.MeetingNotePath,
|
|
reference.TranscriptPath,
|
|
createdAt ?? reference.CreatedAt));
|
|
}
|
|
|
|
private static bool IsEmpty(SpeakerIdentityReference reference)
|
|
{
|
|
return string.IsNullOrWhiteSpace(reference.MeetingNotePath) &&
|
|
string.IsNullOrWhiteSpace(reference.TranscriptPath);
|
|
}
|
|
|
|
private static bool IsSame(SpeakerIdentityReference first, SpeakerIdentityReference second)
|
|
{
|
|
return string.Equals(first.MeetingNotePath, second.MeetingNotePath, StringComparison.OrdinalIgnoreCase) &&
|
|
string.Equals(first.TranscriptPath, second.TranscriptPath, StringComparison.OrdinalIgnoreCase);
|
|
}
|
|
}
|
|
|
|
public sealed class SpeakerCandidateName
|
|
{
|
|
public int Id { get; set; }
|
|
|
|
public int SpeakerIdentityId { get; set; }
|
|
|
|
public SpeakerIdentity? SpeakerIdentity { get; set; }
|
|
|
|
public string Name { get; set; } = "";
|
|
}
|
|
|
|
public sealed class SpeakerSnippet
|
|
{
|
|
public int Id { get; set; }
|
|
|
|
public int SpeakerIdentityId { get; set; }
|
|
|
|
public SpeakerIdentity? SpeakerIdentity { get; set; }
|
|
|
|
public byte[] WavBytes { get; set; } = [];
|
|
|
|
public DateTimeOffset CreatedAt { get; set; }
|
|
}
|
|
|
|
public sealed class SpeakerAlias
|
|
{
|
|
public int Id { get; set; }
|
|
|
|
public int SpeakerIdentityId { get; set; }
|
|
|
|
public SpeakerIdentity? SpeakerIdentity { get; set; }
|
|
|
|
public string Name { get; set; } = "";
|
|
}
|
|
|
|
public sealed class SpeakerIdentityDbContext : DbContext
|
|
{
|
|
public SpeakerIdentityDbContext(DbContextOptions<SpeakerIdentityDbContext> options)
|
|
: base(options)
|
|
{
|
|
}
|
|
|
|
public DbSet<SpeakerIdentity> SpeakerIdentities => Set<SpeakerIdentity>();
|
|
|
|
public DbSet<SpeakerCandidateName> SpeakerCandidateNames => Set<SpeakerCandidateName>();
|
|
|
|
public DbSet<SpeakerAlias> SpeakerAliases => Set<SpeakerAlias>();
|
|
|
|
public DbSet<SpeakerSnippet> SpeakerSnippets => Set<SpeakerSnippet>();
|
|
|
|
public DbSet<SpeakerIdentityReference> SpeakerIdentityReferences => Set<SpeakerIdentityReference>();
|
|
|
|
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
|
{
|
|
modelBuilder.Entity<SpeakerIdentity>(entity =>
|
|
{
|
|
entity.Property(identity => identity.TranscriptionCount)
|
|
.IsRequired()
|
|
.ValueGeneratedNever();
|
|
|
|
entity.HasMany(identity => identity.CandidateNames)
|
|
.WithOne(candidate => candidate.SpeakerIdentity)
|
|
.HasForeignKey(candidate => candidate.SpeakerIdentityId)
|
|
.OnDelete(DeleteBehavior.Cascade);
|
|
|
|
entity.HasMany(identity => identity.Aliases)
|
|
.WithOne(alias => alias.SpeakerIdentity)
|
|
.HasForeignKey(alias => alias.SpeakerIdentityId)
|
|
.OnDelete(DeleteBehavior.Cascade);
|
|
|
|
entity.HasMany(identity => identity.Snippets)
|
|
.WithOne(snippet => snippet.SpeakerIdentity)
|
|
.HasForeignKey(snippet => snippet.SpeakerIdentityId)
|
|
.OnDelete(DeleteBehavior.Cascade);
|
|
|
|
entity.HasMany(identity => identity.References)
|
|
.WithOne(reference => reference.SpeakerIdentity)
|
|
.HasForeignKey(reference => reference.SpeakerIdentityId)
|
|
.OnDelete(DeleteBehavior.Cascade);
|
|
});
|
|
|
|
modelBuilder.Entity<SpeakerCandidateName>()
|
|
.HasIndex(candidate => new { candidate.SpeakerIdentityId, candidate.Name })
|
|
.IsUnique();
|
|
|
|
modelBuilder.Entity<SpeakerAlias>()
|
|
.HasIndex(alias => new { alias.SpeakerIdentityId, alias.Name })
|
|
.IsUnique();
|
|
|
|
modelBuilder.Entity<SpeakerIdentityReference>()
|
|
.HasIndex(reference => new { reference.SpeakerIdentityId, reference.MeetingNotePath, reference.TranscriptPath })
|
|
.IsUnique();
|
|
}
|
|
}
|