feat: add initial support for positioning
Signed-off-by: Konstantin Fickel <mail@konstantinfickel.de>
This commit is contained in:
parent
28dc40ebf0
commit
0c61067db0
9 changed files with 186 additions and 3 deletions
|
|
@ -59,7 +59,7 @@ def new() -> None:
|
||||||
parsed_content = parse_markdown_file(prelimary_path, content)
|
parsed_content = parse_markdown_file(prelimary_path, content)
|
||||||
|
|
||||||
final_file_name = f"{timestamp}.md"
|
final_file_name = f"{timestamp}.md"
|
||||||
if parsed_content.shard and len(markers := parsed_content.shard.markers):
|
if len(markers := parsed_content.shard.markers):
|
||||||
final_file_name = f"{timestamp} {' '.join(markers)}.md"
|
final_file_name = f"{timestamp} {' '.join(markers)}.md"
|
||||||
|
|
||||||
final_path = os.path.join(streamer_directory, final_file_name)
|
final_path = os.path.join(streamer_directory, final_file_name)
|
||||||
|
|
|
||||||
11
src/streamer/localize/__init__.py
Normal file
11
src/streamer/localize/__init__.py
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
from .localize import localize_stream_file
|
||||||
|
from .repostory_configuration import RepositoryConfiguration
|
||||||
|
from .localized_shard import LocalizedShard
|
||||||
|
|
||||||
|
__all__ = [
|
||||||
|
"Dimension",
|
||||||
|
"Marker",
|
||||||
|
"RepositoryConfiguration",
|
||||||
|
"localize_stream_file",
|
||||||
|
"LocalizedShard",
|
||||||
|
]
|
||||||
21
src/streamer/localize/extract_datetime.py
Normal file
21
src/streamer/localize/extract_datetime.py
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
from datetime import datetime
|
||||||
|
import re
|
||||||
|
import os
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
|
||||||
|
def extract_date_from_file_name(file_name: str) -> Optional[datetime]:
|
||||||
|
FILE_NAME_REGEX = r"^(?P<date>\d{8})(?:-(?P<time>\d{4,6}))?.+.md$"
|
||||||
|
base_name = os.path.basename(file_name)
|
||||||
|
match = re.match(FILE_NAME_REGEX, base_name)
|
||||||
|
|
||||||
|
if match:
|
||||||
|
date_str = match.group("date")
|
||||||
|
time_str = match.group("time") or ""
|
||||||
|
time_str = time_str.ljust(6, "0")
|
||||||
|
datetime_str = f"{date_str} {time_str[:2]}:{time_str[2:4]}:{time_str[4:]}"
|
||||||
|
return datetime.strptime(datetime_str, "%Y%m%d %H:%M:%S")
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
__all__ = ["extract_date_from_file_name"]
|
||||||
43
src/streamer/localize/localize.py
Normal file
43
src/streamer/localize/localize.py
Normal file
|
|
@ -0,0 +1,43 @@
|
||||||
|
from typing import Optional
|
||||||
|
from streamer.parse.shard import Shard, StreamFile
|
||||||
|
|
||||||
|
from .repostory_configuration import RepositoryConfiguration
|
||||||
|
from .localized_shard import LocalizedShard
|
||||||
|
from .extract_datetime import extract_date_from_file_name
|
||||||
|
|
||||||
|
|
||||||
|
def localize_shard(
|
||||||
|
shard: Shard, config: RepositoryConfiguration, propagated: dict[str, str]
|
||||||
|
) -> LocalizedShard:
|
||||||
|
position = {**propagated}
|
||||||
|
private_position = {}
|
||||||
|
|
||||||
|
for marker in shard.markers:
|
||||||
|
normalized_marker = marker.lower()
|
||||||
|
if marker_definition := config.markers[normalized_marker]:
|
||||||
|
dimension_name = marker_definition.dimension
|
||||||
|
dimension = config.dimensions[marker_definition.dimension]
|
||||||
|
|
||||||
|
if dimension.propagate:
|
||||||
|
position[dimension_name] = normalized_marker
|
||||||
|
else:
|
||||||
|
private_position[dimension_name] = normalized_marker
|
||||||
|
|
||||||
|
children = [localize_shard(child, config, position) for child in shard.children]
|
||||||
|
|
||||||
|
(position.update(private_position),)
|
||||||
|
|
||||||
|
return LocalizedShard(
|
||||||
|
**shard.model_dump(exclude={"children"}), location=position, children=children
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def localize_stream_file(
|
||||||
|
stream_file: StreamFile, config: RepositoryConfiguration
|
||||||
|
) -> Optional[LocalizedShard]:
|
||||||
|
shard_date = extract_date_from_file_name(stream_file.filename)
|
||||||
|
|
||||||
|
return localize_shard(stream_file.shard, config, {"moment": shard_date.isoformat()})
|
||||||
|
|
||||||
|
|
||||||
|
__all__ = ["localize_stream_file"]
|
||||||
10
src/streamer/localize/localized_shard.py
Normal file
10
src/streamer/localize/localized_shard.py
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
from streamer.parse.shard import Shard
|
||||||
|
|
||||||
|
|
||||||
|
class LocalizedShard(Shard):
|
||||||
|
location: dict[str, str]
|
||||||
|
children: list[LocalizedShard]
|
||||||
|
|
||||||
|
|
||||||
|
__all__ = ["LocalizedShard"]
|
||||||
21
src/streamer/localize/repostory_configuration.py
Normal file
21
src/streamer/localize/repostory_configuration.py
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
from typing import Optional
|
||||||
|
from pydantic import BaseModel
|
||||||
|
|
||||||
|
|
||||||
|
class Dimension(BaseModel):
|
||||||
|
display_name: str
|
||||||
|
comment: Optional[str] = None
|
||||||
|
propagate: bool = False
|
||||||
|
|
||||||
|
|
||||||
|
class Marker(BaseModel):
|
||||||
|
display_name: str
|
||||||
|
dimension: str
|
||||||
|
|
||||||
|
|
||||||
|
class RepositoryConfiguration(BaseModel):
|
||||||
|
dimensions: dict[str, Dimension]
|
||||||
|
markers: dict[str, Marker]
|
||||||
|
|
||||||
|
|
||||||
|
__all__ = ["Dimension", "Marker", "RepositoryConfiguration"]
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
from typing import Optional
|
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -13,7 +12,7 @@ class Shard(BaseModel):
|
||||||
|
|
||||||
class StreamFile(BaseModel):
|
class StreamFile(BaseModel):
|
||||||
filename: str
|
filename: str
|
||||||
shard: Optional[Shard] = None
|
shard: Shard = None
|
||||||
|
|
||||||
|
|
||||||
__all__ = ["Shard", "StreamFile"]
|
__all__ = ["Shard", "StreamFile"]
|
||||||
|
|
|
||||||
32
test/localize/test_extract_datetime.py
Normal file
32
test/localize/test_extract_datetime.py
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
from datetime import datetime
|
||||||
|
from src.streamer.localize.extract_datetime import extract_date_from_file_name
|
||||||
|
|
||||||
|
|
||||||
|
class TestExtractDateTime:
|
||||||
|
def test_extract_date_from_file_name_valid(self):
|
||||||
|
file_name = "20230101-123456 Some Text.md"
|
||||||
|
assert datetime(2023, 1, 1, 12, 34, 56) == extract_date_from_file_name(
|
||||||
|
file_name
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_extract_date_from_file_name_invalid(self):
|
||||||
|
file_name = "invalid-file-name.md"
|
||||||
|
assert extract_date_from_file_name(file_name) is None
|
||||||
|
|
||||||
|
def test_extract_date_from_file_name_without_time(self):
|
||||||
|
file_name = "20230101 Some Text.md"
|
||||||
|
assert datetime(2023, 1, 1, 0, 0, 0) == extract_date_from_file_name(file_name)
|
||||||
|
|
||||||
|
def test_extract_date_from_file_name_short_time(self):
|
||||||
|
file_name = "20230101-1234 Some Text.md"
|
||||||
|
assert datetime(2023, 1, 1, 12, 34, 0) == extract_date_from_file_name(file_name)
|
||||||
|
|
||||||
|
def test_extract_date_from_file_name_empty_string(self):
|
||||||
|
file_name = ""
|
||||||
|
assert extract_date_from_file_name(file_name) is None
|
||||||
|
|
||||||
|
def test_extract_date_from_file_name_with_full_path(self):
|
||||||
|
file_name = "/path/to/20230101-123456 Some Text.md"
|
||||||
|
assert datetime(2023, 1, 1, 12, 34, 56) == extract_date_from_file_name(
|
||||||
|
file_name
|
||||||
|
)
|
||||||
46
test/test_localize.py
Normal file
46
test/test_localize.py
Normal file
|
|
@ -0,0 +1,46 @@
|
||||||
|
from streamer.localize.localize import localize_stream_file
|
||||||
|
from streamer.localize.localized_shard import LocalizedShard
|
||||||
|
from streamer.localize.repostory_configuration import (
|
||||||
|
Dimension,
|
||||||
|
Marker,
|
||||||
|
RepositoryConfiguration,
|
||||||
|
)
|
||||||
|
from streamer.parse.shard import Shard, StreamFile
|
||||||
|
|
||||||
|
repository_configuration = RepositoryConfiguration(
|
||||||
|
dimensions={
|
||||||
|
"project": Dimension(
|
||||||
|
display_name="Project",
|
||||||
|
comment="GTD Project that is being worked on",
|
||||||
|
propagate=True,
|
||||||
|
),
|
||||||
|
"moment": Dimension(
|
||||||
|
display_name="Moment",
|
||||||
|
comment="Timestamp this entry was created at",
|
||||||
|
propagate=True,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
markers={
|
||||||
|
"streamer": Marker(display_name="Streamer", dimension="project"),
|
||||||
|
"jobhunting": Marker(display_name="JobHunting", dimension="project"),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class TestExtractDateTime:
|
||||||
|
def test_project_simple_stream_file(self):
|
||||||
|
stream_file = StreamFile(
|
||||||
|
filename="20250622-121000 Test File.md",
|
||||||
|
shard=Shard(start_line=1, end_line=1, markers=["Streamer"]),
|
||||||
|
)
|
||||||
|
|
||||||
|
assert localize_stream_file(
|
||||||
|
stream_file, repository_configuration
|
||||||
|
) == LocalizedShard(
|
||||||
|
markers=["Streamer"],
|
||||||
|
tags=[],
|
||||||
|
start_line=1,
|
||||||
|
end_line=1,
|
||||||
|
children=[],
|
||||||
|
location={"moment": "2025-06-22T12:10:00", "project": "streamer"},
|
||||||
|
)
|
||||||
Loading…
Add table
Add a link
Reference in a new issue