mirror of
https://github.com/basicmachines-co/basic-memory
synced 2026-06-21 13:47:35 +00:00
Refactor created/modified handling in markdown entities.
Replace frontmatter properties with top-level attributes for `created` and `modified` to simplify handling and ensure consistent attribute usage. Added a utility function for schema-to-markdown conversion and updated related tests and code to reflect these changes.
This commit is contained in:
@@ -53,6 +53,13 @@ def parse(content: str) -> EntityContent:
|
||||
relations=relations,
|
||||
)
|
||||
|
||||
def parse_tags(tags: Any) -> list[str]:
|
||||
"""Parse tags into list of strings."""
|
||||
if isinstance(tags, str):
|
||||
return [t.strip() for t in tags.split(",") if t.strip()]
|
||||
if isinstance(tags, (list, tuple)):
|
||||
return [str(t).strip() for t in tags if str(t).strip()]
|
||||
return []
|
||||
|
||||
class EntityParser:
|
||||
"""Parser for markdown files into Entity objects."""
|
||||
@@ -107,17 +114,10 @@ class EntityParser:
|
||||
# Extract file stat info
|
||||
file_stats = absolute_path.stat()
|
||||
|
||||
|
||||
metadata = post.metadata
|
||||
metadata["title"] = post.metadata.get("title", file_path.name)
|
||||
metadata["type"] = metadata.get("type", "note")
|
||||
metadata["created"] = self.parse_date(
|
||||
post.metadata.get("created")
|
||||
) or datetime.fromtimestamp(file_stats.st_ctime)
|
||||
metadata["modified"] = self.parse_date(
|
||||
post.metadata.get("modified")
|
||||
) or datetime.fromtimestamp(file_stats.st_mtime)
|
||||
metadata["tags"] = self.parse_tags(post.metadata.get("tags", []))
|
||||
metadata["tags"] = parse_tags(post.metadata.get("tags", []))
|
||||
|
||||
# frontmatter
|
||||
entity_frontmatter = EntityFrontmatter(
|
||||
@@ -131,12 +131,7 @@ class EntityParser:
|
||||
content=post.content,
|
||||
observations=entity_content.observations,
|
||||
relations=entity_content.relations,
|
||||
created=datetime.fromtimestamp(file_stats.st_ctime),
|
||||
modified=datetime.fromtimestamp(file_stats.st_mtime),
|
||||
)
|
||||
|
||||
def parse_tags(self, tags: Any) -> list[str]:
|
||||
"""Parse tags into list of strings."""
|
||||
if isinstance(tags, str):
|
||||
return [t.strip() for t in tags.split(",") if t.strip()]
|
||||
if isinstance(tags, (list, tuple)):
|
||||
return [str(t).strip() for t in tags if str(t).strip()]
|
||||
return []
|
||||
|
||||
@@ -105,10 +105,10 @@ class MarkdownProcessor:
|
||||
"type": markdown.frontmatter.type,
|
||||
"permalink": markdown.frontmatter.permalink,
|
||||
"created": markdown.frontmatter.created.isoformat()
|
||||
if markdown.frontmatter.created
|
||||
if markdown.created
|
||||
else None,
|
||||
"modified": markdown.frontmatter.modified.isoformat()
|
||||
if markdown.frontmatter.modified
|
||||
if markdown.modified
|
||||
else None,
|
||||
**metadata,
|
||||
}
|
||||
|
||||
@@ -58,13 +58,6 @@ class EntityFrontmatter(BaseModel):
|
||||
def permalink(self) -> str:
|
||||
return self.metadata.get("permalink") if self.metadata else None
|
||||
|
||||
@property
|
||||
def created(self) -> datetime:
|
||||
return self.metadata.get("created") if self.metadata else None
|
||||
|
||||
@property
|
||||
def modified(self) -> datetime:
|
||||
return self.metadata.get("modified") if self.metadata else None
|
||||
|
||||
|
||||
class EntityMarkdown(BaseModel):
|
||||
@@ -74,3 +67,7 @@ class EntityMarkdown(BaseModel):
|
||||
content: Optional[str] = None
|
||||
observations: List[Observation] = []
|
||||
relations: List[Relation] = []
|
||||
|
||||
# created, updated will have values after a read
|
||||
created: Optional[datetime] = None
|
||||
modified: Optional[datetime] = None
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
from pathlib import Path
|
||||
from typing import Optional
|
||||
|
||||
from frontmatter import Post
|
||||
|
||||
from basic_memory.markdown import EntityMarkdown, EntityFrontmatter, Observation, Relation
|
||||
from basic_memory.markdown.entity_parser import parse
|
||||
from basic_memory.models import Entity, ObservationCategory, Observation as ObservationModel
|
||||
@@ -105,8 +107,8 @@ def entity_model_from_markdown(file_path: Path, markdown: EntityMarkdown, entity
|
||||
model.permalink=permalink
|
||||
model.file_path=str(file_path)
|
||||
model.content_type="text/markdown"
|
||||
model.created_at=markdown.frontmatter.created
|
||||
model.updated_at=markdown.frontmatter.modified
|
||||
model.created_at=markdown.created
|
||||
model.updated_at=markdown.modified
|
||||
model.entity_metadata={k:str(v) for k,v in markdown.frontmatter.metadata.items()}
|
||||
model.observations=[
|
||||
ObservationModel(
|
||||
@@ -119,3 +121,21 @@ def entity_model_from_markdown(file_path: Path, markdown: EntityMarkdown, entity
|
||||
]
|
||||
|
||||
return model
|
||||
|
||||
async def schema_to_markdown(schema):
|
||||
"""
|
||||
Convert schema to markdown.
|
||||
:param schema: the schema to convert
|
||||
:return: Post
|
||||
"""
|
||||
# Add metadata to dict
|
||||
frontmatter_dict = schema.entity_metadata or {}
|
||||
|
||||
# set permalink and type
|
||||
frontmatter_dict["permalink"] = schema.permalink
|
||||
frontmatter_dict["type"] = schema.entity_type
|
||||
|
||||
# Create Post object
|
||||
content = schema.content or ""
|
||||
post = Post(content, **frontmatter_dict)
|
||||
return post
|
||||
|
||||
@@ -9,7 +9,7 @@ from loguru import logger
|
||||
from sqlalchemy.exc import IntegrityError
|
||||
|
||||
from basic_memory.markdown import EntityMarkdown
|
||||
from basic_memory.markdown.utils import entity_model_from_markdown
|
||||
from basic_memory.markdown.utils import entity_model_from_markdown, schema_to_markdown
|
||||
from basic_memory.models import Entity as EntityModel, Observation, Relation
|
||||
from basic_memory.repository import ObservationRepository, RelationRepository
|
||||
from basic_memory.repository.entity_repository import EntityRepository
|
||||
@@ -69,14 +69,7 @@ class EntityService(BaseService[EntityModel]):
|
||||
f"file_path {file_path} for entity {schema.permalink} already exists: {file_path}"
|
||||
)
|
||||
|
||||
# Convert frontmatter to dict
|
||||
frontmatter_dict = schema.entity_metadata or {}
|
||||
frontmatter_dict["permalink"] = schema.permalink
|
||||
frontmatter_dict["type"] = schema.entity_type
|
||||
|
||||
# Create Post object for frontmatter
|
||||
content = schema.content or ""
|
||||
post = Post(content, **frontmatter_dict)
|
||||
post = await schema_to_markdown(schema)
|
||||
|
||||
# write file
|
||||
final_content = frontmatter.dumps(post)
|
||||
@@ -84,6 +77,8 @@ class EntityService(BaseService[EntityModel]):
|
||||
|
||||
# parse entity from file
|
||||
entity_markdown = await self.entity_parser.parse_file(file_path)
|
||||
|
||||
# create entity
|
||||
created_entity = await self.create_entity_from_markdown(
|
||||
file_path, entity_markdown
|
||||
)
|
||||
@@ -94,6 +89,7 @@ class EntityService(BaseService[EntityModel]):
|
||||
# Set final checksum to mark complete
|
||||
return await self.repository.update(entity.id, {"checksum": checksum})
|
||||
|
||||
|
||||
async def update_entity(self, schema: EntitySchema) -> EntityModel:
|
||||
"""Update an entity's content and metadata."""
|
||||
logger.debug(f"Updating entity with permalink: {schema.permalink}")
|
||||
@@ -101,14 +97,7 @@ class EntityService(BaseService[EntityModel]):
|
||||
# get file path
|
||||
file_path = Path(schema.file_path)
|
||||
|
||||
# Convert frontmatter to dict
|
||||
frontmatter_dict = schema.entity_metadata or {}
|
||||
frontmatter_dict["permalink"] = schema.permalink
|
||||
frontmatter_dict["type"] = schema.entity_type
|
||||
|
||||
# Create Post object for frontmatter
|
||||
content = schema.content or ""
|
||||
post = Post(content, **frontmatter_dict)
|
||||
post = await schema_to_markdown(schema)
|
||||
|
||||
# write file
|
||||
final_content = frontmatter.dumps(post)
|
||||
|
||||
Reference in New Issue
Block a user