Public Access
226 lines
6.9 KiB
C#
226 lines
6.9 KiB
C#
using System.Text;
|
|
|
|
namespace MeetingAssistant.MeetingNotes;
|
|
|
|
public sealed class MeetingArtifactFrontmatter
|
|
{
|
|
public string Title { get; set; } = "";
|
|
|
|
public string? OneLiner { get; set; }
|
|
|
|
public DateTimeOffset? StartTime { get; set; }
|
|
|
|
public DateTimeOffset? EndTime { get; set; }
|
|
|
|
public List<string>? Attendees { get; set; }
|
|
|
|
public List<string>? Projects { get; set; }
|
|
|
|
public string Meeting { get; set; } = "";
|
|
|
|
public string Transcript { get; set; } = "";
|
|
|
|
public string AssistantContext { get; set; } = "";
|
|
|
|
public string Summary { get; set; } = "";
|
|
|
|
public string? State { get; set; }
|
|
|
|
public string? Agenda { get; set; }
|
|
|
|
public DateTimeOffset? ScheduledEnd { get; set; }
|
|
}
|
|
|
|
public static class MeetingArtifactFrontmatterRenderer
|
|
{
|
|
public static string Render(
|
|
MeetingArtifactFrontmatter frontmatter,
|
|
string body)
|
|
{
|
|
var builder = new StringBuilder();
|
|
builder.AppendLine("---");
|
|
AppendScalar(builder, "title", frontmatter.Title);
|
|
AppendScalarIfNotEmpty(builder, "oneliner", frontmatter.OneLiner);
|
|
AppendDateTime(builder, "start_time", frontmatter.StartTime);
|
|
AppendDateTime(builder, "end_time", frontmatter.EndTime);
|
|
AppendListIfNotNull(builder, "attendees", frontmatter.Attendees);
|
|
AppendListIfNotNull(builder, "projects", frontmatter.Projects);
|
|
AppendQuotedIfNotEmpty(builder, "meeting", frontmatter.Meeting);
|
|
AppendQuotedIfNotEmpty(builder, "transcript", frontmatter.Transcript);
|
|
AppendQuotedIfNotEmpty(builder, "assistant_context", frontmatter.AssistantContext);
|
|
AppendQuotedIfNotEmpty(builder, "summary", frontmatter.Summary);
|
|
if (!string.IsNullOrWhiteSpace(frontmatter.State))
|
|
{
|
|
AppendScalar(builder, "state", frontmatter.State);
|
|
}
|
|
|
|
if (frontmatter.Agenda is not null)
|
|
{
|
|
AppendBlockScalar(builder, "agenda", frontmatter.Agenda);
|
|
}
|
|
|
|
if (frontmatter.ScheduledEnd is not null)
|
|
{
|
|
AppendDateTime(builder, "scheduled_end", frontmatter.ScheduledEnd);
|
|
}
|
|
|
|
builder.AppendLine("---");
|
|
builder.AppendLine();
|
|
builder.Append(body.TrimStart('\r', '\n'));
|
|
return builder.ToString();
|
|
}
|
|
|
|
public static (string Frontmatter, string Body) Split(string content)
|
|
{
|
|
var document = MarkdownDocumentParser.SplitOptional(content);
|
|
return (document.Frontmatter, document.Body);
|
|
}
|
|
|
|
public static MeetingArtifactFrontmatter Create(
|
|
MeetingSessionArtifacts artifacts,
|
|
MeetingNote meetingNote,
|
|
string title,
|
|
string sourceNotePath,
|
|
string? state = null)
|
|
{
|
|
return Create(
|
|
artifacts,
|
|
title,
|
|
meetingNote.Frontmatter.StartTime,
|
|
meetingNote.Frontmatter.EndTime,
|
|
sourceNotePath,
|
|
state);
|
|
}
|
|
|
|
public static MeetingArtifactFrontmatter Create(
|
|
MeetingSessionArtifacts artifacts,
|
|
string title,
|
|
DateTimeOffset? startTime,
|
|
DateTimeOffset? endTime,
|
|
string sourceNotePath,
|
|
string? state = null)
|
|
{
|
|
return new MeetingArtifactFrontmatter
|
|
{
|
|
Title = title,
|
|
StartTime = startTime,
|
|
EndTime = endTime,
|
|
Meeting = LinkUnlessSelf(artifacts.MeetingNotePath, sourceNotePath, "Meeting Note"),
|
|
Transcript = LinkUnlessSelf(artifacts.TranscriptPath, sourceNotePath, "Transcript"),
|
|
AssistantContext = LinkUnlessSelf(artifacts.AssistantContextPath, sourceNotePath, "Assistant Context"),
|
|
Summary = LinkUnlessSelf(artifacts.SummaryPath, sourceNotePath, "Summary"),
|
|
State = state
|
|
};
|
|
}
|
|
|
|
public static string DefaultTitle(MeetingNote meetingNote, string fallback)
|
|
{
|
|
return string.IsNullOrWhiteSpace(meetingNote.Frontmatter.Title)
|
|
? fallback
|
|
: meetingNote.Frontmatter.Title;
|
|
}
|
|
|
|
private static void AppendScalar(StringBuilder builder, string key, string? value)
|
|
{
|
|
builder.Append(key);
|
|
builder.Append(": ");
|
|
builder.AppendLine(string.IsNullOrWhiteSpace(value) ? "\"\"" : EscapeListItem(value));
|
|
}
|
|
|
|
private static void AppendScalarIfNotEmpty(StringBuilder builder, string key, string? value)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(value))
|
|
{
|
|
return;
|
|
}
|
|
|
|
AppendScalar(builder, key, value);
|
|
}
|
|
|
|
private static void AppendQuoted(StringBuilder builder, string key, string value)
|
|
{
|
|
builder.Append(key);
|
|
builder.Append(": ");
|
|
builder.AppendLine(EscapeQuoted(value));
|
|
}
|
|
|
|
private static void AppendQuotedIfNotEmpty(StringBuilder builder, string key, string? value)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(value))
|
|
{
|
|
return;
|
|
}
|
|
|
|
AppendQuoted(builder, key, value);
|
|
}
|
|
|
|
private static void AppendDateTime(StringBuilder builder, string key, DateTimeOffset? value)
|
|
{
|
|
builder.Append(key);
|
|
builder.Append(": ");
|
|
builder.AppendLine(value is null ? "\"\"" : EscapeQuoted(value.Value.ToString("O")));
|
|
}
|
|
|
|
private static void AppendListIfNotNull(StringBuilder builder, string key, IReadOnlyList<string>? values)
|
|
{
|
|
if (values is null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
builder.Append(key);
|
|
builder.AppendLine(":");
|
|
foreach (var value in values)
|
|
{
|
|
builder.Append("- ");
|
|
builder.AppendLine(EscapeListItem(value));
|
|
}
|
|
}
|
|
|
|
private static void AppendBlockScalar(StringBuilder builder, string key, string value)
|
|
{
|
|
if (string.IsNullOrEmpty(value))
|
|
{
|
|
builder.Append(key);
|
|
builder.AppendLine(": \"\"");
|
|
return;
|
|
}
|
|
|
|
builder.Append(key);
|
|
builder.AppendLine(": |-");
|
|
foreach (var line in value.Replace("\r\n", "\n", StringComparison.Ordinal).Replace('\r', '\n').Split('\n'))
|
|
{
|
|
builder.Append(" ");
|
|
builder.AppendLine(line);
|
|
}
|
|
}
|
|
|
|
private static string EscapeQuoted(string value)
|
|
{
|
|
return $"\"{value.Replace("\"", "\\\"", StringComparison.Ordinal)}\"";
|
|
}
|
|
|
|
private static string EscapeListItem(string value)
|
|
{
|
|
return value.Contains(':', StringComparison.Ordinal) || value.StartsWith("[", StringComparison.Ordinal)
|
|
? EscapeQuoted(value)
|
|
: value;
|
|
}
|
|
|
|
private static string LinkUnlessSelf(string targetPath, string sourceNotePath, string label)
|
|
{
|
|
return IsSamePath(targetPath, sourceNotePath)
|
|
? ""
|
|
: ObsidianLink.ToWikiLink(targetPath, sourceNotePath, label);
|
|
}
|
|
|
|
private static bool IsSamePath(string first, string second)
|
|
{
|
|
return string.Equals(
|
|
Path.GetFullPath(first),
|
|
Path.GetFullPath(second),
|
|
StringComparison.OrdinalIgnoreCase);
|
|
}
|
|
|
|
}
|