semantic update

This commit is contained in:
2026-02-23 13:15:48 +03:00
parent 008b6d72c9
commit 26880d2e09
29 changed files with 5134 additions and 958 deletions

View File

@@ -9,7 +9,7 @@
# @INVARIANT: List responses are deterministic and include applied filter echo metadata.
# [SECTION: IMPORTS]
from datetime import datetime
from datetime import datetime, timezone
from typing import List, Optional
from ...core.task_manager import TaskManager
@@ -23,9 +23,14 @@ from .normalizer import normalize_task_report
# @TIER: CRITICAL
# @PRE: TaskManager dependency is initialized.
# @POST: Provides deterministic list/detail report responses.
# @INVARIANT: Service methods are read-only over task history source.
class ReportsService:
# [DEF:__init__:Function]
# @TIER: CRITICAL
# @PURPOSE: Initialize service with TaskManager dependency.
# @PRE: task_manager is a live TaskManager instance.
# @POST: self.task_manager is assigned and ready for read operations.
# @INVARIANT: Constructor performs no task mutations.
# @PARAM: task_manager (TaskManager) - Task manager providing source task history.
def __init__(self, task_manager: TaskManager):
self.task_manager = task_manager
@@ -33,6 +38,9 @@ class ReportsService:
# [DEF:_load_normalized_reports:Function]
# @PURPOSE: Build normalized reports from all available tasks.
# @PRE: Task manager returns iterable task history records.
# @POST: Returns normalized report list preserving source cardinality.
# @INVARIANT: Every returned item is a TaskReport.
# @RETURN: List[TaskReport] - Reports sorted later by list logic.
def _load_normalized_reports(self) -> List[TaskReport]:
tasks = self.task_manager.get_all_tasks()
@@ -40,8 +48,40 @@ class ReportsService:
return reports
# [/DEF:_load_normalized_reports:Function]
# [DEF:_to_utc_datetime:Function]
# @PURPOSE: Normalize naive/aware datetime values to UTC-aware datetime for safe comparisons.
# @PRE: value is either datetime or None.
# @POST: Returns UTC-aware datetime or None.
# @INVARIANT: Naive datetimes are interpreted as UTC to preserve deterministic ordering/filtering.
# @PARAM: value (Optional[datetime]) - Source datetime value.
# @RETURN: Optional[datetime] - UTC-aware datetime or None.
def _to_utc_datetime(self, value: Optional[datetime]) -> Optional[datetime]:
if value is None:
return None
if value.tzinfo is None:
return value.replace(tzinfo=timezone.utc)
return value.astimezone(timezone.utc)
# [/DEF:_to_utc_datetime:Function]
# [DEF:_datetime_sort_key:Function]
# @PURPOSE: Produce stable numeric sort key for report timestamps.
# @PRE: report contains updated_at datetime.
# @POST: Returns float timestamp suitable for deterministic sorting.
# @INVARIANT: Mixed naive/aware datetimes never raise TypeError.
# @PARAM: report (TaskReport) - Report item.
# @RETURN: float - UTC timestamp key.
def _datetime_sort_key(self, report: TaskReport) -> float:
updated = self._to_utc_datetime(report.updated_at)
if updated is None:
return 0.0
return updated.timestamp()
# [/DEF:_datetime_sort_key:Function]
# [DEF:_matches_query:Function]
# @PURPOSE: Apply query filtering to a report.
# @PRE: report and query are normalized schema instances.
# @POST: Returns True iff report satisfies all active query filters.
# @INVARIANT: Filter evaluation is side-effect free.
# @PARAM: report (TaskReport) - Candidate report.
# @PARAM: query (ReportQuery) - Applied query.
# @RETURN: bool - True if report matches all filters.
@@ -50,9 +90,13 @@ class ReportsService:
return False
if query.statuses and report.status not in query.statuses:
return False
if query.time_from and report.updated_at < query.time_from:
report_updated_at = self._to_utc_datetime(report.updated_at)
query_time_from = self._to_utc_datetime(query.time_from)
query_time_to = self._to_utc_datetime(query.time_to)
if query_time_from and report_updated_at and report_updated_at < query_time_from:
return False
if query.time_to and report.updated_at > query.time_to:
if query_time_to and report_updated_at and report_updated_at > query_time_to:
return False
if query.search:
needle = query.search.lower()
@@ -64,6 +108,9 @@ class ReportsService:
# [DEF:_sort_reports:Function]
# @PURPOSE: Sort reports deterministically according to query settings.
# @PRE: reports contains only TaskReport items.
# @POST: Returns reports ordered by selected sort field and order.
# @INVARIANT: Sorting criteria are deterministic for equal input.
# @PARAM: reports (List[TaskReport]) - Filtered reports.
# @PARAM: query (ReportQuery) - Sort config.
# @RETURN: List[TaskReport] - Sorted reports.
@@ -75,7 +122,7 @@ class ReportsService:
elif query.sort_by == "task_type":
reports.sort(key=lambda item: item.task_type.value, reverse=reverse)
else:
reports.sort(key=lambda item: item.updated_at, reverse=reverse)
reports.sort(key=self._datetime_sort_key, reverse=reverse)
return reports
# [/DEF:_sort_reports:Function]