journal/Journal.Core/Models/JournalEntry.cs
Jacob Schmidt 0d77300c22 feat: Project Journal backend monorepo
Monorepo with centralized build props, npm workspaces, LlamaSharp AI,
SQLite/SQLCipher storage, Svelte frontend, and unified smoke tests.

Co-Authored-By: Oz <oz-agent@warp.dev>
2026-03-02 20:56:26 -06:00

99 lines
3.1 KiB
C#

namespace Journal.Core.Models;
public class JournalEntry
{
public string Date { get; set; }
public List<Fragment> Fragments { get; set; }
public string RawContent { get; set; }
public Dictionary<string, ParsedSection> Sections { get; set; }
public JournalEntry(
string date,
IEnumerable<Fragment>? fragments = null,
string rawContent = "",
IDictionary<string, ParsedSection>? sections = null)
{
if (string.IsNullOrWhiteSpace(date))
throw new ArgumentException("Date is required", nameof(date));
Date = date.Trim();
Fragments = fragments is null ? [] : [.. fragments];
RawContent = rawContent ?? "";
Sections = sections is null ? [] : new Dictionary<string, ParsedSection>(sections);
}
public string GetSection(string sectionTitle)
{
if (string.IsNullOrWhiteSpace(sectionTitle))
return "";
if (!Sections.TryGetValue(sectionTitle, out var section))
return "";
return string.Join("\n", section.Content);
}
public bool? GetCheckboxState(string sectionTitle, string checkboxText)
{
if (string.IsNullOrWhiteSpace(sectionTitle) || string.IsNullOrWhiteSpace(checkboxText))
return null;
if (!Sections.TryGetValue(sectionTitle, out var section))
return null;
return section.Checkboxes.TryGetValue(checkboxText, out var checkedState) ? checkedState : null;
}
public void MergeWith(JournalEntry otherEntry)
{
ArgumentNullException.ThrowIfNull(otherEntry);
foreach (var (title, newSection) in otherEntry.Sections)
{
if (newSection.Content.Any(line => !string.IsNullOrWhiteSpace(line)))
Sections[title] = newSection;
}
var existingFragmentDescriptions = Fragments
.Select(fragment => fragment.Description)
.ToHashSet(StringComparer.Ordinal);
foreach (var newFragment in otherEntry.Fragments)
{
if (!existingFragmentDescriptions.Contains(newFragment.Description))
Fragments.Add(newFragment);
}
}
public string ToMarkdown()
{
var lines = new List<string>
{
"---",
"type: journal",
"---",
$"**Date:** {Date}\n"
};
foreach (var title in SectionTitles.Canonical)
{
if (!Sections.TryGetValue(title, out var section))
continue;
lines.Add($"## {section.Title}\n");
lines.AddRange(section.Content);
lines.Add("");
}
if (Fragments.Count > 0)
{
lines.Add("# Fragments\n");
foreach (var fragment in Fragments)
{
var timeStr = fragment.Time != default ? $"@{fragment.Time:O}" : "";
var tagsStr = string.Join(" ", fragment.Tags.Select(tag => $"#{tag}"));
var header = $"{fragment.Type} {timeStr} {tagsStr}".Trim();
lines.Add($"{header}\n{fragment.Description}\n");
}
}
return string.Join("\n", lines);
}
}