101 lines
3.6 KiB
Python
101 lines
3.6 KiB
Python
from dataclasses import dataclass, field
|
|
|
|
# Define canonical section titles for parsing and reconstruction
|
|
SECTION_TITLES = [
|
|
"Summary",
|
|
"Cognitive State",
|
|
"Mental / Emotional Snapshot",
|
|
"Memory / Mind Failures",
|
|
"Events / Triggers",
|
|
"Communication / Expression Log",
|
|
"Coping / Tools Used",
|
|
"Reflection",
|
|
"Core Events or Memories",
|
|
"Autism/ADHD-Related Elements",
|
|
"Emotional & Bodily Reactions",
|
|
"Truth to Anchor Myself To",
|
|
]
|
|
|
|
|
|
@dataclass
|
|
class Fragment:
|
|
type: str
|
|
description: str
|
|
time: str | None = None
|
|
tags: list[str] = field(default_factory=list)
|
|
|
|
|
|
@dataclass
|
|
class ParsedSection:
|
|
title: str
|
|
content: list[str] = field(default_factory=list) # Each item is a line or a block
|
|
checkboxes: dict[str, bool] = field(
|
|
default_factory=dict
|
|
) # { "checkbox_text": is_checked }
|
|
|
|
|
|
@dataclass
|
|
class JournalEntry:
|
|
date: str
|
|
fragments: list[Fragment] = field(default_factory=list)
|
|
raw_content: str = ""
|
|
# New: Structured representation of sections
|
|
sections: dict[str, ParsedSection] = field(default_factory=dict)
|
|
|
|
def merge_with(self, other_entry: "JournalEntry"):
|
|
"""Merges another entry's data into this one."""
|
|
# Merge sections: Overwrite if the new section has meaningful content
|
|
for title, new_section in other_entry.sections.items():
|
|
# Heuristic: content is meaningful if it's not empty or just whitespace
|
|
if any(line.strip() for line in new_section.content):
|
|
self.sections[title] = new_section
|
|
|
|
# Merge fragments: Add new fragments, avoid duplicates by description
|
|
existing_fragment_descs = {f.description for f in self.fragments}
|
|
for new_fragment in other_entry.fragments:
|
|
if new_fragment.description not in existing_fragment_descs:
|
|
self.fragments.append(new_fragment)
|
|
|
|
def to_markdown(self) -> str:
|
|
"""Reconstructs the journal entry as a markdown string."""
|
|
lines: list[str] = []
|
|
# Frontmatter (simplified for now)
|
|
lines.append("---")
|
|
lines.append("type: journal")
|
|
lines.append("---")
|
|
lines.append(f"**Date:** {self.date}\n")
|
|
|
|
# Write sections in canonical order
|
|
for title in SECTION_TITLES:
|
|
if title in self.sections:
|
|
section = self.sections[title]
|
|
lines.append(f"## {section.title}\n")
|
|
# The content list no longer contains the header
|
|
lines.extend(section.content)
|
|
lines.append("") # newline after section
|
|
|
|
# Append all fragments at the end
|
|
if self.fragments:
|
|
lines.append("# Fragments\n")
|
|
for frag in self.fragments:
|
|
time_str = f"@{frag.time}" if frag.time else ""
|
|
tags_str = " ".join([f"#{tag}" for tag in frag.tags])
|
|
header = f"{frag.type} {time_str} {tags_str}".strip()
|
|
lines.append(f"{header}\n{frag.description}\n")
|
|
|
|
return "\n".join(lines)
|
|
|
|
def get_section(self, section_title: str) -> str:
|
|
# This method will now retrieve content from the parsed sections
|
|
if section_title in self.sections:
|
|
return "\n".join(self.sections[section_title].content)
|
|
return ""
|
|
|
|
def get_checkbox_state(self, section_title: str, checkbox_text: str) -> bool | None:
|
|
if (
|
|
section_title in self.sections
|
|
and checkbox_text in self.sections[section_title].checkboxes
|
|
):
|
|
return self.sections[section_title].checkboxes[checkbox_text]
|
|
return None
|