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