project-agent 0.2.1__tar.gz → 0.2.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {project_agent-0.2.1 → project_agent-0.2.2}/PKG-INFO +1 -1
- {project_agent-0.2.1 → project_agent-0.2.2}/pyproject.toml +1 -1
- project_agent-0.2.2/src/project_agent/application/context/project_summary.py +144 -0
- {project_agent-0.2.1 → project_agent-0.2.2}/src/project_agent/domain/project_context/models.py +26 -0
- {project_agent-0.2.1 → project_agent-0.2.2}/src/project_agent/infrastructure/db/repositories.py +4 -0
- {project_agent-0.2.1 → project_agent-0.2.2}/src/project_agent/interface/cli/commands/context.py +71 -0
- {project_agent-0.2.1 → project_agent-0.2.2}/src/project_agent.egg-info/PKG-INFO +1 -1
- {project_agent-0.2.1 → project_agent-0.2.2}/src/project_agent.egg-info/SOURCES.txt +2 -0
- project_agent-0.2.2/tests/test_context_project_summary.py +322 -0
- {project_agent-0.2.1 → project_agent-0.2.2}/README.md +0 -0
- {project_agent-0.2.1 → project_agent-0.2.2}/setup.cfg +0 -0
- {project_agent-0.2.1 → project_agent-0.2.2}/src/project_agent/__init__.py +0 -0
- {project_agent-0.2.1 → project_agent-0.2.2}/src/project_agent/application/__init__.py +0 -0
- {project_agent-0.2.1 → project_agent-0.2.2}/src/project_agent/application/bootstrap/__init__.py +0 -0
- {project_agent-0.2.1 → project_agent-0.2.2}/src/project_agent/application/bootstrap/checkpoint.py +0 -0
- {project_agent-0.2.1 → project_agent-0.2.2}/src/project_agent/application/bootstrap/init.py +0 -0
- {project_agent-0.2.1 → project_agent-0.2.2}/src/project_agent/application/bootstrap/loop_state.py +0 -0
- {project_agent-0.2.1 → project_agent-0.2.2}/src/project_agent/application/context/__init__.py +0 -0
- {project_agent-0.2.1 → project_agent-0.2.2}/src/project_agent/application/context/binding_normalization.py +0 -0
- {project_agent-0.2.1 → project_agent-0.2.2}/src/project_agent/application/context/get_project_context.py +0 -0
- {project_agent-0.2.1 → project_agent-0.2.2}/src/project_agent/application/context/get_project_owner.py +0 -0
- {project_agent-0.2.1 → project_agent-0.2.2}/src/project_agent/application/context/get_task.py +0 -0
- {project_agent-0.2.1 → project_agent-0.2.2}/src/project_agent/application/context/get_user_context.py +0 -0
- {project_agent-0.2.1 → project_agent-0.2.2}/src/project_agent/application/context/list_projects.py +0 -0
- {project_agent-0.2.1 → project_agent-0.2.2}/src/project_agent/application/context/list_tasks.py +0 -0
- {project_agent-0.2.1 → project_agent-0.2.2}/src/project_agent/application/context/resolve_binding.py +0 -0
- {project_agent-0.2.1 → project_agent-0.2.2}/src/project_agent/application/context/resolve_group.py +0 -0
- {project_agent-0.2.1 → project_agent-0.2.2}/src/project_agent/application/context/resolve_user.py +0 -0
- {project_agent-0.2.1 → project_agent-0.2.2}/src/project_agent/application/context/search_user.py +0 -0
- {project_agent-0.2.1 → project_agent-0.2.2}/src/project_agent/application/context/upsert_user_identity.py +0 -0
- {project_agent-0.2.1 → project_agent-0.2.2}/src/project_agent/application/doc/__init__.py +0 -0
- {project_agent-0.2.1 → project_agent-0.2.2}/src/project_agent/application/doc/render.py +0 -0
- {project_agent-0.2.1 → project_agent-0.2.2}/src/project_agent/application/doc/sync.py +0 -0
- {project_agent-0.2.1 → project_agent-0.2.2}/src/project_agent/application/doc/writeback.py +0 -0
- {project_agent-0.2.1 → project_agent-0.2.2}/src/project_agent/application/doc_hub/__init__.py +0 -0
- {project_agent-0.2.1 → project_agent-0.2.2}/src/project_agent/application/doc_hub/manage.py +0 -0
- {project_agent-0.2.1 → project_agent-0.2.2}/src/project_agent/application/doc_hub/read.py +0 -0
- {project_agent-0.2.1 → project_agent-0.2.2}/src/project_agent/application/loop/__init__.py +0 -0
- {project_agent-0.2.1 → project_agent-0.2.2}/src/project_agent/application/loop/enqueue.py +0 -0
- {project_agent-0.2.1 → project_agent-0.2.2}/src/project_agent/application/loop/run.py +0 -0
- {project_agent-0.2.1 → project_agent-0.2.2}/src/project_agent/application/mind/__init__.py +0 -0
- {project_agent-0.2.1 → project_agent-0.2.2}/src/project_agent/application/mind/create_version.py +0 -0
- {project_agent-0.2.1 → project_agent-0.2.2}/src/project_agent/application/mind/delete_task.py +0 -0
- {project_agent-0.2.1 → project_agent-0.2.2}/src/project_agent/application/mind/finalize_version_closure.py +0 -0
- {project_agent-0.2.1 → project_agent-0.2.2}/src/project_agent/application/mind/milestones.py +0 -0
- {project_agent-0.2.1 → project_agent-0.2.2}/src/project_agent/application/mind/planned_versions.py +0 -0
- {project_agent-0.2.1 → project_agent-0.2.2}/src/project_agent/application/mind/propose_change.py +0 -0
- {project_agent-0.2.1 → project_agent-0.2.2}/src/project_agent/application/mind/read.py +0 -0
- {project_agent-0.2.1 → project_agent-0.2.2}/src/project_agent/application/mind/set_tracking.py +0 -0
- {project_agent-0.2.1 → project_agent-0.2.2}/src/project_agent/application/mind/signoff_proposal.py +0 -0
- {project_agent-0.2.1 → project_agent-0.2.2}/src/project_agent/application/mind/snooze_task_reminder.py +0 -0
- {project_agent-0.2.1 → project_agent-0.2.2}/src/project_agent/application/mind/switch_version.py +0 -0
- {project_agent-0.2.1 → project_agent-0.2.2}/src/project_agent/application/mind/update_version.py +0 -0
- {project_agent-0.2.1 → project_agent-0.2.2}/src/project_agent/application/mind/write_task_update.py +0 -0
- {project_agent-0.2.1 → project_agent-0.2.2}/src/project_agent/application/monitoring/__init__.py +0 -0
- {project_agent-0.2.1 → project_agent-0.2.2}/src/project_agent/application/monitoring/evaluate_followups.py +0 -0
- {project_agent-0.2.1 → project_agent-0.2.2}/src/project_agent/application/monitoring/plan.py +0 -0
- {project_agent-0.2.1 → project_agent-0.2.2}/src/project_agent/application/monitoring/run.py +0 -0
- {project_agent-0.2.1 → project_agent-0.2.2}/src/project_agent/application/report/__init__.py +0 -0
- {project_agent-0.2.1 → project_agent-0.2.2}/src/project_agent/application/report/build.py +0 -0
- {project_agent-0.2.1 → project_agent-0.2.2}/src/project_agent/application/scheduled/__init__.py +0 -0
- {project_agent-0.2.1 → project_agent-0.2.2}/src/project_agent/application/scheduled/run.py +0 -0
- {project_agent-0.2.1 → project_agent-0.2.2}/src/project_agent/application/version_closure/__init__.py +0 -0
- {project_agent-0.2.1 → project_agent-0.2.2}/src/project_agent/application/version_closure/evaluate.py +0 -0
- {project_agent-0.2.1 → project_agent-0.2.2}/src/project_agent/domain/__init__.py +0 -0
- {project_agent-0.2.1 → project_agent-0.2.2}/src/project_agent/domain/bootstrap/__init__.py +0 -0
- {project_agent-0.2.1 → project_agent-0.2.2}/src/project_agent/domain/bootstrap/models.py +0 -0
- {project_agent-0.2.1 → project_agent-0.2.2}/src/project_agent/domain/doc/__init__.py +0 -0
- {project_agent-0.2.1 → project_agent-0.2.2}/src/project_agent/domain/doc/models.py +0 -0
- {project_agent-0.2.1 → project_agent-0.2.2}/src/project_agent/domain/doc_hub/__init__.py +0 -0
- {project_agent-0.2.1 → project_agent-0.2.2}/src/project_agent/domain/doc_hub/models.py +0 -0
- {project_agent-0.2.1 → project_agent-0.2.2}/src/project_agent/domain/loop/__init__.py +0 -0
- {project_agent-0.2.1 → project_agent-0.2.2}/src/project_agent/domain/loop/models.py +0 -0
- {project_agent-0.2.1 → project_agent-0.2.2}/src/project_agent/domain/mind/__init__.py +0 -0
- {project_agent-0.2.1 → project_agent-0.2.2}/src/project_agent/domain/mind/models.py +0 -0
- {project_agent-0.2.1 → project_agent-0.2.2}/src/project_agent/domain/monitoring/__init__.py +0 -0
- {project_agent-0.2.1 → project_agent-0.2.2}/src/project_agent/domain/monitoring/evaluator.py +0 -0
- {project_agent-0.2.1 → project_agent-0.2.2}/src/project_agent/domain/monitoring/models.py +0 -0
- {project_agent-0.2.1 → project_agent-0.2.2}/src/project_agent/domain/project_context/__init__.py +0 -0
- {project_agent-0.2.1 → project_agent-0.2.2}/src/project_agent/domain/report/__init__.py +0 -0
- {project_agent-0.2.1 → project_agent-0.2.2}/src/project_agent/domain/report/models.py +0 -0
- {project_agent-0.2.1 → project_agent-0.2.2}/src/project_agent/domain/scheduled/__init__.py +0 -0
- {project_agent-0.2.1 → project_agent-0.2.2}/src/project_agent/domain/scheduled/models.py +0 -0
- {project_agent-0.2.1 → project_agent-0.2.2}/src/project_agent/domain/shared/__init__.py +0 -0
- {project_agent-0.2.1 → project_agent-0.2.2}/src/project_agent/domain/shared/documents.py +0 -0
- {project_agent-0.2.1 → project_agent-0.2.2}/src/project_agent/domain/shared/models.py +0 -0
- {project_agent-0.2.1 → project_agent-0.2.2}/src/project_agent/domain/shared/project_mind_derived.py +0 -0
- {project_agent-0.2.1 → project_agent-0.2.2}/src/project_agent/domain/shared/project_mind_readiness.py +0 -0
- {project_agent-0.2.1 → project_agent-0.2.2}/src/project_agent/domain/shared/statuses.py +0 -0
- {project_agent-0.2.1 → project_agent-0.2.2}/src/project_agent/domain/user/__init__.py +0 -0
- {project_agent-0.2.1 → project_agent-0.2.2}/src/project_agent/domain/user/models.py +0 -0
- {project_agent-0.2.1 → project_agent-0.2.2}/src/project_agent/domain/version_closure/__init__.py +0 -0
- {project_agent-0.2.1 → project_agent-0.2.2}/src/project_agent/domain/version_closure/evaluator.py +0 -0
- {project_agent-0.2.1 → project_agent-0.2.2}/src/project_agent/domain/version_closure/models.py +0 -0
- {project_agent-0.2.1 → project_agent-0.2.2}/src/project_agent/infrastructure/__init__.py +0 -0
- {project_agent-0.2.1 → project_agent-0.2.2}/src/project_agent/infrastructure/clock/__init__.py +0 -0
- {project_agent-0.2.1 → project_agent-0.2.2}/src/project_agent/infrastructure/clock/workday_clock.py +0 -0
- {project_agent-0.2.1 → project_agent-0.2.2}/src/project_agent/infrastructure/db/__init__.py +0 -0
- {project_agent-0.2.1 → project_agent-0.2.2}/src/project_agent/infrastructure/db/migration.py +0 -0
- {project_agent-0.2.1 → project_agent-0.2.2}/src/project_agent/infrastructure/db/schema.py +0 -0
- {project_agent-0.2.1 → project_agent-0.2.2}/src/project_agent/infrastructure/db/session.py +0 -0
- {project_agent-0.2.1 → project_agent-0.2.2}/src/project_agent/infrastructure/db/tables.py +0 -0
- {project_agent-0.2.1 → project_agent-0.2.2}/src/project_agent/interface/__init__.py +0 -0
- {project_agent-0.2.1 → project_agent-0.2.2}/src/project_agent/interface/cli/__init__.py +0 -0
- {project_agent-0.2.1 → project_agent-0.2.2}/src/project_agent/interface/cli/app.py +0 -0
- {project_agent-0.2.1 → project_agent-0.2.2}/src/project_agent/interface/cli/commands/__init__.py +0 -0
- {project_agent-0.2.1 → project_agent-0.2.2}/src/project_agent/interface/cli/commands/bootstrap.py +0 -0
- {project_agent-0.2.1 → project_agent-0.2.2}/src/project_agent/interface/cli/commands/db.py +0 -0
- {project_agent-0.2.1 → project_agent-0.2.2}/src/project_agent/interface/cli/commands/doc.py +0 -0
- {project_agent-0.2.1 → project_agent-0.2.2}/src/project_agent/interface/cli/commands/doc_hub.py +0 -0
- {project_agent-0.2.1 → project_agent-0.2.2}/src/project_agent/interface/cli/commands/loop.py +0 -0
- {project_agent-0.2.1 → project_agent-0.2.2}/src/project_agent/interface/cli/commands/mind.py +0 -0
- {project_agent-0.2.1 → project_agent-0.2.2}/src/project_agent/interface/cli/commands/monitoring.py +0 -0
- {project_agent-0.2.1 → project_agent-0.2.2}/src/project_agent/interface/cli/commands/project_boundary.py +0 -0
- {project_agent-0.2.1 → project_agent-0.2.2}/src/project_agent/interface/cli/commands/report.py +0 -0
- {project_agent-0.2.1 → project_agent-0.2.2}/src/project_agent/interface/cli/commands/scheduled.py +0 -0
- {project_agent-0.2.1 → project_agent-0.2.2}/src/project_agent/interface/cli/commands/task_event_notify.py +0 -0
- {project_agent-0.2.1 → project_agent-0.2.2}/src/project_agent/interface/cli/commands/upgrade.py +0 -0
- {project_agent-0.2.1 → project_agent-0.2.2}/src/project_agent/interface/cli/commands/version_closure.py +0 -0
- {project_agent-0.2.1 → project_agent-0.2.2}/src/project_agent/interface/cli/helptext.py +0 -0
- {project_agent-0.2.1 → project_agent-0.2.2}/src/project_agent.egg-info/dependency_links.txt +0 -0
- {project_agent-0.2.1 → project_agent-0.2.2}/src/project_agent.egg-info/entry_points.txt +0 -0
- {project_agent-0.2.1 → project_agent-0.2.2}/src/project_agent.egg-info/requires.txt +0 -0
- {project_agent-0.2.1 → project_agent-0.2.2}/src/project_agent.egg-info/top_level.txt +0 -0
- {project_agent-0.2.1 → project_agent-0.2.2}/tests/test_bootstrap_checkpoint.py +0 -0
- {project_agent-0.2.1 → project_agent-0.2.2}/tests/test_cli_help.py +0 -0
- {project_agent-0.2.1 → project_agent-0.2.2}/tests/test_cli_version.py +0 -0
- {project_agent-0.2.1 → project_agent-0.2.2}/tests/test_context_get_project_owner.py +0 -0
- {project_agent-0.2.1 → project_agent-0.2.2}/tests/test_context_get_project_pending_states.py +0 -0
- {project_agent-0.2.1 → project_agent-0.2.2}/tests/test_context_list_projects.py +0 -0
- {project_agent-0.2.1 → project_agent-0.2.2}/tests/test_context_list_tasks.py +0 -0
- {project_agent-0.2.1 → project_agent-0.2.2}/tests/test_context_resolve_binding.py +0 -0
- {project_agent-0.2.1 → project_agent-0.2.2}/tests/test_context_search_user.py +0 -0
- {project_agent-0.2.1 → project_agent-0.2.2}/tests/test_context_upsert_user_identity.py +0 -0
- {project_agent-0.2.1 → project_agent-0.2.2}/tests/test_db_migration.py +0 -0
- {project_agent-0.2.1 → project_agent-0.2.2}/tests/test_db_schema.py +0 -0
- {project_agent-0.2.1 → project_agent-0.2.2}/tests/test_db_session.py +0 -0
- {project_agent-0.2.1 → project_agent-0.2.2}/tests/test_doc_hub.py +0 -0
- {project_agent-0.2.1 → project_agent-0.2.2}/tests/test_doc_render_planned_versions.py +0 -0
- {project_agent-0.2.1 → project_agent-0.2.2}/tests/test_finalize_version_closure.py +0 -0
- {project_agent-0.2.1 → project_agent-0.2.2}/tests/test_loop_jobs.py +0 -0
- {project_agent-0.2.1 → project_agent-0.2.2}/tests/test_mind_governance.py +0 -0
- {project_agent-0.2.1 → project_agent-0.2.2}/tests/test_mind_proposal_extensions.py +0 -0
- {project_agent-0.2.1 → project_agent-0.2.2}/tests/test_mind_proposal_signoff.py +0 -0
- {project_agent-0.2.1 → project_agent-0.2.2}/tests/test_mind_read.py +0 -0
- {project_agent-0.2.1 → project_agent-0.2.2}/tests/test_monitoring_plan.py +0 -0
- {project_agent-0.2.1 → project_agent-0.2.2}/tests/test_monitoring_run.py +0 -0
- {project_agent-0.2.1 → project_agent-0.2.2}/tests/test_overview_sync_hints.py +0 -0
- {project_agent-0.2.1 → project_agent-0.2.2}/tests/test_pending_state_idempotency.py +0 -0
- {project_agent-0.2.1 → project_agent-0.2.2}/tests/test_project_group_boundary_guard.py +0 -0
- {project_agent-0.2.1 → project_agent-0.2.2}/tests/test_project_group_routing_contracts.py +0 -0
- {project_agent-0.2.1 → project_agent-0.2.2}/tests/test_release_versioning.py +0 -0
- {project_agent-0.2.1 → project_agent-0.2.2}/tests/test_report_build.py +0 -0
- {project_agent-0.2.1 → project_agent-0.2.2}/tests/test_report_persistence.py +0 -0
- {project_agent-0.2.1 → project_agent-0.2.2}/tests/test_runtime_bundles.py +0 -0
- {project_agent-0.2.1 → project_agent-0.2.2}/tests/test_runtime_output_hygiene_contracts.py +0 -0
- {project_agent-0.2.1 → project_agent-0.2.2}/tests/test_scheduled_run.py +0 -0
- {project_agent-0.2.1 → project_agent-0.2.2}/tests/test_version_closure_trigger.py +0 -0
- {project_agent-0.2.1 → project_agent-0.2.2}/tests/test_version_status_auto_sync.py +0 -0
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from datetime import UTC, date, datetime
|
|
4
|
+
from typing import Any, Protocol
|
|
5
|
+
from zoneinfo import ZoneInfo, ZoneInfoNotFoundError
|
|
6
|
+
|
|
7
|
+
from project_agent.domain.project_context.models import (
|
|
8
|
+
PersonalTaskSummary,
|
|
9
|
+
ProjectSummaryResult,
|
|
10
|
+
TeamTaskSummary,
|
|
11
|
+
)
|
|
12
|
+
from project_agent.domain.shared.statuses import (
|
|
13
|
+
is_done_status,
|
|
14
|
+
is_terminal_task_status,
|
|
15
|
+
normalize_task_status,
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class ProjectStore(Protocol):
|
|
20
|
+
def find_by_id(self, project_id: str) -> Any: ...
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class TaskStore(Protocol):
|
|
24
|
+
def list_by_project_id(self, project_id: str, task_id: str | None = None) -> list[Any]: ...
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def build_project_summary(
|
|
28
|
+
*,
|
|
29
|
+
project_id: str,
|
|
30
|
+
user_id: str,
|
|
31
|
+
timezone: str | None,
|
|
32
|
+
project_store: ProjectStore,
|
|
33
|
+
task_store: TaskStore,
|
|
34
|
+
) -> ProjectSummaryResult | None:
|
|
35
|
+
project = project_store.find_by_id(project_id)
|
|
36
|
+
if not project:
|
|
37
|
+
return None
|
|
38
|
+
|
|
39
|
+
tz = resolve_summary_timezone(timezone or project.get("timezone") or "UTC")
|
|
40
|
+
today = datetime.now(tz).date()
|
|
41
|
+
tasks = task_store.list_by_project_id(project_id)
|
|
42
|
+
|
|
43
|
+
return ProjectSummaryResult(
|
|
44
|
+
project_id=project["project_id"],
|
|
45
|
+
project_name=project["project_name"],
|
|
46
|
+
team_task=_build_team_task_summary(tasks, today=today, tz=tz),
|
|
47
|
+
personal=_build_personal_task_summary(tasks, user_id=user_id, today=today, tz=tz),
|
|
48
|
+
updated_at=project.get("last_updated_at"),
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def resolve_summary_timezone(value: str) -> ZoneInfo:
|
|
53
|
+
try:
|
|
54
|
+
return ZoneInfo(value)
|
|
55
|
+
except ZoneInfoNotFoundError as exc:
|
|
56
|
+
raise ValueError(f"unknown timezone: {value}") from exc
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def _build_team_task_summary(
|
|
60
|
+
tasks: list[Any],
|
|
61
|
+
*,
|
|
62
|
+
today: date,
|
|
63
|
+
tz: ZoneInfo,
|
|
64
|
+
) -> TeamTaskSummary:
|
|
65
|
+
return TeamTaskSummary(
|
|
66
|
+
status="HAS_TASK" if tasks else "NO_TASK",
|
|
67
|
+
new_assigned_today=sum(1 for task in tasks if _is_today(_task_value(task, "created_at"), today, tz)),
|
|
68
|
+
completed_today=sum(
|
|
69
|
+
1
|
|
70
|
+
for task in tasks
|
|
71
|
+
if is_done_status(_task_value(task, "task_status"))
|
|
72
|
+
and _is_today(_task_value(task, "updated_at") or _task_value(task, "last_updated_at"), today, tz)
|
|
73
|
+
),
|
|
74
|
+
in_progress=sum(
|
|
75
|
+
1
|
|
76
|
+
for task in tasks
|
|
77
|
+
if normalize_task_status(_task_value(task, "task_status")) == "in_progress"
|
|
78
|
+
),
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def _build_personal_task_summary(
|
|
83
|
+
tasks: list[Any],
|
|
84
|
+
*,
|
|
85
|
+
user_id: str,
|
|
86
|
+
today: date,
|
|
87
|
+
tz: ZoneInfo,
|
|
88
|
+
) -> PersonalTaskSummary:
|
|
89
|
+
personal_tasks = [task for task in tasks if _task_value(task, "owner_user_id") == user_id]
|
|
90
|
+
return PersonalTaskSummary(
|
|
91
|
+
new_tasks_today=sum(
|
|
92
|
+
1 for task in personal_tasks if _is_today(_task_value(task, "created_at"), today, tz)
|
|
93
|
+
),
|
|
94
|
+
due_tasks_today=sum(
|
|
95
|
+
1
|
|
96
|
+
for task in personal_tasks
|
|
97
|
+
if _task_due_date(_task_value(task, "end_date"), tz=tz) == today
|
|
98
|
+
and not is_terminal_task_status(_task_value(task, "task_status"))
|
|
99
|
+
),
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def _task_value(task: Any, key: str) -> Any:
|
|
104
|
+
if isinstance(task, dict):
|
|
105
|
+
return task.get(key)
|
|
106
|
+
return getattr(task, key, None)
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def _is_today(value: Any, today: date, tz: ZoneInfo) -> bool:
|
|
110
|
+
parsed = _parse_datetime(value, tz=tz)
|
|
111
|
+
return parsed is not None and parsed.date() == today
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def _task_due_date(value: Any, *, tz: ZoneInfo) -> date | None:
|
|
115
|
+
if value is None:
|
|
116
|
+
return None
|
|
117
|
+
if isinstance(value, date) and not isinstance(value, datetime):
|
|
118
|
+
return value
|
|
119
|
+
token = str(value).strip()
|
|
120
|
+
if not token:
|
|
121
|
+
return None
|
|
122
|
+
try:
|
|
123
|
+
return date.fromisoformat(token[:10])
|
|
124
|
+
except ValueError:
|
|
125
|
+
parsed = _parse_datetime(token, tz=tz)
|
|
126
|
+
return parsed.date() if parsed is not None else None
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def _parse_datetime(value: Any, *, tz: ZoneInfo) -> datetime | None:
|
|
130
|
+
if value is None:
|
|
131
|
+
return None
|
|
132
|
+
if isinstance(value, datetime):
|
|
133
|
+
parsed = value
|
|
134
|
+
else:
|
|
135
|
+
token = str(value).strip()
|
|
136
|
+
if not token:
|
|
137
|
+
return None
|
|
138
|
+
try:
|
|
139
|
+
parsed = datetime.fromisoformat(token.replace("Z", "+00:00"))
|
|
140
|
+
except ValueError:
|
|
141
|
+
return None
|
|
142
|
+
if parsed.tzinfo is None:
|
|
143
|
+
parsed = parsed.replace(tzinfo=UTC)
|
|
144
|
+
return parsed.astimezone(tz)
|
{project_agent-0.2.1 → project_agent-0.2.2}/src/project_agent/domain/project_context/models.py
RENAMED
|
@@ -137,6 +137,32 @@ class GetTaskResult(ProjectAgentModel):
|
|
|
137
137
|
task: TaskSummary = Field(description="命中的单条 Task 详情。")
|
|
138
138
|
|
|
139
139
|
|
|
140
|
+
class TeamTaskSummary(ProjectAgentModel):
|
|
141
|
+
"""项目团队任务汇总。"""
|
|
142
|
+
|
|
143
|
+
status: str = Field(description="团队任务状态,例如 HAS_TASK 或 NO_TASK。")
|
|
144
|
+
new_assigned_today: int = Field(description="今天新分配到项目的任务数量。")
|
|
145
|
+
completed_today: int = Field(description="今天完成的项目任务数量。")
|
|
146
|
+
in_progress: int = Field(description="进行中的项目任务数量。")
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
class PersonalTaskSummary(ProjectAgentModel):
|
|
150
|
+
"""当前用户在项目内的任务汇总。"""
|
|
151
|
+
|
|
152
|
+
new_tasks_today: int = Field(description="今天分配给当前用户的新任务数量。")
|
|
153
|
+
due_tasks_today: int = Field(description="今天到期且未终态的当前用户任务数量。")
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
class ProjectSummaryResult(ProjectAgentModel):
|
|
157
|
+
"""`context project-summary` 的结构化输出。"""
|
|
158
|
+
|
|
159
|
+
project_id: str = Field(description="项目内部唯一标识。")
|
|
160
|
+
project_name: str = Field(description="项目名称。")
|
|
161
|
+
team_task: TeamTaskSummary = Field(description="团队任务汇总。")
|
|
162
|
+
personal: PersonalTaskSummary = Field(description="当前用户任务汇总。")
|
|
163
|
+
updated_at: str | None = Field(default=None, description="项目 UTC 更新时间。")
|
|
164
|
+
|
|
165
|
+
|
|
140
166
|
class PendingStateSummary(ProjectAgentModel):
|
|
141
167
|
"""项目上下文里暴露给 router 或 gate 的 pending state 摘要。"""
|
|
142
168
|
|
{project_agent-0.2.1 → project_agent-0.2.2}/src/project_agent/infrastructure/db/repositories.py
RENAMED
|
@@ -1914,6 +1914,8 @@ def _to_participant_row(record: ProjectParticipantRecord) -> dict:
|
|
|
1914
1914
|
"user_id": record.user_id,
|
|
1915
1915
|
"role": record.role,
|
|
1916
1916
|
"status": record.status,
|
|
1917
|
+
"created_at": _dump_datetime(record.created_at),
|
|
1918
|
+
"updated_at": _dump_datetime(record.updated_at),
|
|
1917
1919
|
}
|
|
1918
1920
|
|
|
1919
1921
|
|
|
@@ -1978,6 +1980,8 @@ def _to_task_row(record: ProjectTaskRecord) -> dict:
|
|
|
1978
1980
|
"start_date": record.start_date,
|
|
1979
1981
|
"end_date": record.end_date,
|
|
1980
1982
|
"last_updated_at": _dump_datetime(record.last_updated_at),
|
|
1983
|
+
"created_at": _dump_datetime(record.created_at),
|
|
1984
|
+
"updated_at": _dump_datetime(record.updated_at),
|
|
1981
1985
|
"latest_update": payload.get("latest_update"),
|
|
1982
1986
|
"has_effective_update": payload.get("has_effective_update"),
|
|
1983
1987
|
"effective_update_fields": payload.get("effective_update_fields", []),
|
{project_agent-0.2.1 → project_agent-0.2.2}/src/project_agent/interface/cli/commands/context.py
RENAMED
|
@@ -10,6 +10,7 @@ from project_agent.application.context.get_task import get_task as get_task_serv
|
|
|
10
10
|
from project_agent.application.context.get_user_context import get_user_context as get_user_context_service
|
|
11
11
|
from project_agent.application.context.list_tasks import list_tasks as list_tasks_service
|
|
12
12
|
from project_agent.application.context.list_projects import list_projects as list_projects_service
|
|
13
|
+
from project_agent.application.context.project_summary import build_project_summary as build_project_summary_service
|
|
13
14
|
from project_agent.application.context.resolve_group import resolve_group as resolve_group_service
|
|
14
15
|
from project_agent.application.context.resolve_user import resolve_user as resolve_user_service
|
|
15
16
|
from project_agent.application.context.search_user import search_user as search_user_service
|
|
@@ -101,6 +102,14 @@ class ListTasksCommandInput(BaseModel):
|
|
|
101
102
|
external_subject_id: str | None = None
|
|
102
103
|
|
|
103
104
|
|
|
105
|
+
class ProjectSummaryCommandInput(BaseModel):
|
|
106
|
+
project_id: str
|
|
107
|
+
user_id: str | None = None
|
|
108
|
+
channel: str | None = None
|
|
109
|
+
external_subject_id: str | None = None
|
|
110
|
+
timezone: str | None = None
|
|
111
|
+
|
|
112
|
+
|
|
104
113
|
class ResolveUserCommandInput(BaseModel):
|
|
105
114
|
channel: str
|
|
106
115
|
external_subject_id: str
|
|
@@ -378,6 +387,68 @@ def list_projects(
|
|
|
378
387
|
typer.echo(json.dumps(result, indent=2, ensure_ascii=True))
|
|
379
388
|
|
|
380
389
|
|
|
390
|
+
@app.command("project-summary", help="读取项目团队与当前用户的任务汇总。")
|
|
391
|
+
def project_summary(
|
|
392
|
+
project_id: str = project_id_option(),
|
|
393
|
+
user_id: str | None = typer.Option(None, "--user-id", help="内部 canonical userId。"),
|
|
394
|
+
channel: str | None = channel_option(),
|
|
395
|
+
external_subject_id: str | None = typer.Option(None, "--external-subject-id", help="渠道侧主体 ID。与 --channel 配合后可直接解析内部 userId。"),
|
|
396
|
+
timezone: str | None = typer.Option(None, "--timezone", help="用于计算今日统计的 IANA 时区,例如 Asia/Shanghai。"),
|
|
397
|
+
db_path: str | None = db_path_option(),
|
|
398
|
+
) -> None:
|
|
399
|
+
payload = ProjectSummaryCommandInput(
|
|
400
|
+
project_id=project_id,
|
|
401
|
+
user_id=user_id,
|
|
402
|
+
channel=channel,
|
|
403
|
+
external_subject_id=external_subject_id,
|
|
404
|
+
timezone=timezone,
|
|
405
|
+
)
|
|
406
|
+
_validate_external_identity_args(
|
|
407
|
+
channel=payload.channel,
|
|
408
|
+
external_subject_id=payload.external_subject_id,
|
|
409
|
+
)
|
|
410
|
+
if payload.user_id is not None and payload.external_subject_id is not None:
|
|
411
|
+
_echo_invalid_input("user_id cannot be used together with channel/external_subject_id")
|
|
412
|
+
raise typer.Exit(code=1)
|
|
413
|
+
if payload.user_id is None and payload.external_subject_id is None:
|
|
414
|
+
_echo_invalid_input("project-summary requires --user-id or --channel with --external-subject-id")
|
|
415
|
+
raise typer.Exit(code=1)
|
|
416
|
+
|
|
417
|
+
with open_session(db_path) as session:
|
|
418
|
+
resolved_user_id = payload.user_id
|
|
419
|
+
if payload.external_subject_id is not None:
|
|
420
|
+
resolved_user_id = _resolve_user_id_or_exit(
|
|
421
|
+
session=session,
|
|
422
|
+
channel=payload.channel,
|
|
423
|
+
external_subject_id=payload.external_subject_id,
|
|
424
|
+
)
|
|
425
|
+
if resolved_user_id is None:
|
|
426
|
+
_echo_invalid_input("project-summary requires a resolved user_id")
|
|
427
|
+
raise typer.Exit(code=1)
|
|
428
|
+
try:
|
|
429
|
+
result = build_project_summary_service(
|
|
430
|
+
project_id=payload.project_id,
|
|
431
|
+
user_id=resolved_user_id,
|
|
432
|
+
timezone=payload.timezone,
|
|
433
|
+
project_store=SqlAlchemyProjectStore(session),
|
|
434
|
+
task_store=SqlAlchemyTaskStore(session),
|
|
435
|
+
)
|
|
436
|
+
except ValueError as exc:
|
|
437
|
+
_echo_invalid_input(str(exc))
|
|
438
|
+
raise typer.Exit(code=1) from exc
|
|
439
|
+
|
|
440
|
+
if result is None:
|
|
441
|
+
typer.echo(
|
|
442
|
+
'{\n'
|
|
443
|
+
' "status": "not_found",\n'
|
|
444
|
+
f' "projectId": "{payload.project_id}"\n'
|
|
445
|
+
'}'
|
|
446
|
+
)
|
|
447
|
+
raise typer.Exit(code=1)
|
|
448
|
+
|
|
449
|
+
typer.echo(result.model_dump_json(indent=2, by_alias=True))
|
|
450
|
+
|
|
451
|
+
|
|
381
452
|
@app.command("get-project-owner", help="按项目 ID 查询项目 Owner 用户详情及渠道身份映射。")
|
|
382
453
|
def get_project_owner(
|
|
383
454
|
project_id: str = project_id_option(),
|
|
@@ -20,6 +20,7 @@ src/project_agent/application/context/get_task.py
|
|
|
20
20
|
src/project_agent/application/context/get_user_context.py
|
|
21
21
|
src/project_agent/application/context/list_projects.py
|
|
22
22
|
src/project_agent/application/context/list_tasks.py
|
|
23
|
+
src/project_agent/application/context/project_summary.py
|
|
23
24
|
src/project_agent/application/context/resolve_binding.py
|
|
24
25
|
src/project_agent/application/context/resolve_group.py
|
|
25
26
|
src/project_agent/application/context/resolve_user.py
|
|
@@ -125,6 +126,7 @@ tests/test_context_get_project_owner.py
|
|
|
125
126
|
tests/test_context_get_project_pending_states.py
|
|
126
127
|
tests/test_context_list_projects.py
|
|
127
128
|
tests/test_context_list_tasks.py
|
|
129
|
+
tests/test_context_project_summary.py
|
|
128
130
|
tests/test_context_resolve_binding.py
|
|
129
131
|
tests/test_context_search_user.py
|
|
130
132
|
tests/test_context_upsert_user_identity.py
|
|
@@ -0,0 +1,322 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import sqlite3
|
|
3
|
+
import sys
|
|
4
|
+
from datetime import UTC, datetime, timedelta
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
sys.path.insert(0, str(Path(__file__).resolve().parents[4]))
|
|
8
|
+
|
|
9
|
+
from fastapi import FastAPI
|
|
10
|
+
from fastapi.testclient import TestClient
|
|
11
|
+
from typer.testing import CliRunner
|
|
12
|
+
|
|
13
|
+
from project_agent.infrastructure.db.schema import create_schema
|
|
14
|
+
from project_agent.interface.cli.app import app
|
|
15
|
+
from runtime.mushroomAgent.deploy.project_agent_api import create_router
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
runner = CliRunner()
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def test_project_summary_cli_resolves_external_subject_and_returns_counts(tmp_path) -> None:
|
|
22
|
+
db_path = tmp_path / "project-agent.db"
|
|
23
|
+
create_schema(str(db_path))
|
|
24
|
+
now = datetime.now(UTC).replace(tzinfo=None)
|
|
25
|
+
_insert_user(db_path, user_id="usr_member_001", display_name="Member One")
|
|
26
|
+
_insert_identity(
|
|
27
|
+
db_path,
|
|
28
|
+
identity_id="uci_member_001",
|
|
29
|
+
user_id="usr_member_001",
|
|
30
|
+
channel="openim",
|
|
31
|
+
external_subject_id="open_user_001",
|
|
32
|
+
)
|
|
33
|
+
_insert_project(db_path, project_id="proj_summary_001", updated_at=now)
|
|
34
|
+
_insert_task(
|
|
35
|
+
db_path,
|
|
36
|
+
task_id="task_owned_due",
|
|
37
|
+
project_id="proj_summary_001",
|
|
38
|
+
owner_user_id="usr_member_001",
|
|
39
|
+
task_status="in_progress",
|
|
40
|
+
end_date=now.date().isoformat(),
|
|
41
|
+
created_at=now,
|
|
42
|
+
updated_at=now,
|
|
43
|
+
)
|
|
44
|
+
_insert_task(
|
|
45
|
+
db_path,
|
|
46
|
+
task_id="task_done_today",
|
|
47
|
+
project_id="proj_summary_001",
|
|
48
|
+
owner_user_id="usr_other_001",
|
|
49
|
+
task_status="done",
|
|
50
|
+
end_date=now.date().isoformat(),
|
|
51
|
+
created_at=now - timedelta(days=1),
|
|
52
|
+
updated_at=now,
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
result = runner.invoke(
|
|
56
|
+
app,
|
|
57
|
+
[
|
|
58
|
+
"context",
|
|
59
|
+
"project-summary",
|
|
60
|
+
"--project-id",
|
|
61
|
+
"proj_summary_001",
|
|
62
|
+
"--channel",
|
|
63
|
+
"openim",
|
|
64
|
+
"--external-subject-id",
|
|
65
|
+
"open_user_001",
|
|
66
|
+
"--timezone",
|
|
67
|
+
"UTC",
|
|
68
|
+
"--db-path",
|
|
69
|
+
str(db_path),
|
|
70
|
+
],
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
assert result.exit_code == 0
|
|
74
|
+
payload = json.loads(result.stdout)
|
|
75
|
+
assert payload["projectId"] == "proj_summary_001"
|
|
76
|
+
assert payload["projectName"] == "Summary Project"
|
|
77
|
+
assert payload["teamTask"] == {
|
|
78
|
+
"status": "HAS_TASK",
|
|
79
|
+
"newAssignedToday": 1,
|
|
80
|
+
"completedToday": 1,
|
|
81
|
+
"inProgress": 1,
|
|
82
|
+
}
|
|
83
|
+
assert payload["personal"] == {
|
|
84
|
+
"newTasksToday": 1,
|
|
85
|
+
"dueTasksToday": 1,
|
|
86
|
+
}
|
|
87
|
+
assert payload["updatedAt"].endswith("Z")
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def test_project_summary_api_uses_backend_summary_service(tmp_path) -> None:
|
|
91
|
+
db_path = tmp_path / "project-agent.db"
|
|
92
|
+
create_schema(str(db_path))
|
|
93
|
+
now = datetime.now(UTC).replace(tzinfo=None)
|
|
94
|
+
_insert_user(db_path, user_id="usr_member_001", display_name="Member One")
|
|
95
|
+
_insert_identity(
|
|
96
|
+
db_path,
|
|
97
|
+
identity_id="uci_member_001",
|
|
98
|
+
user_id="usr_member_001",
|
|
99
|
+
channel="openim",
|
|
100
|
+
external_subject_id="open_user_001",
|
|
101
|
+
)
|
|
102
|
+
_insert_project(db_path, project_id="proj_summary_001", updated_at=now)
|
|
103
|
+
_insert_binding(
|
|
104
|
+
db_path,
|
|
105
|
+
binding_id="bind_summary_001",
|
|
106
|
+
project_id="proj_summary_001",
|
|
107
|
+
channel="openim",
|
|
108
|
+
external_channel_id="group_summary_001",
|
|
109
|
+
)
|
|
110
|
+
_insert_task(
|
|
111
|
+
db_path,
|
|
112
|
+
task_id="task_owned_due",
|
|
113
|
+
project_id="proj_summary_001",
|
|
114
|
+
owner_user_id="usr_member_001",
|
|
115
|
+
task_status="in_progress",
|
|
116
|
+
end_date=now.date().isoformat(),
|
|
117
|
+
created_at=now,
|
|
118
|
+
updated_at=now,
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
fastapi_app = FastAPI()
|
|
122
|
+
fastapi_app.include_router(create_router(None, {"db_path": str(db_path)}))
|
|
123
|
+
response = TestClient(fastapi_app).get(
|
|
124
|
+
"/api/projects/summary",
|
|
125
|
+
params={
|
|
126
|
+
"groupId": "group_summary_001",
|
|
127
|
+
"userId": "open_user_001",
|
|
128
|
+
"channel": "openim",
|
|
129
|
+
"timezone": "UTC",
|
|
130
|
+
},
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
assert response.status_code == 200
|
|
134
|
+
assert response.json()["personal"] == {
|
|
135
|
+
"newTasksToday": 1,
|
|
136
|
+
"dueTasksToday": 1,
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def test_project_summary_cli_rejects_missing_user_identity(tmp_path) -> None:
|
|
141
|
+
db_path = tmp_path / "project-agent.db"
|
|
142
|
+
create_schema(str(db_path))
|
|
143
|
+
_insert_project(db_path, project_id="proj_summary_001", updated_at=datetime.now(UTC).replace(tzinfo=None))
|
|
144
|
+
|
|
145
|
+
result = runner.invoke(
|
|
146
|
+
app,
|
|
147
|
+
[
|
|
148
|
+
"context",
|
|
149
|
+
"project-summary",
|
|
150
|
+
"--project-id",
|
|
151
|
+
"proj_summary_001",
|
|
152
|
+
"--timezone",
|
|
153
|
+
"UTC",
|
|
154
|
+
"--db-path",
|
|
155
|
+
str(db_path),
|
|
156
|
+
],
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
assert result.exit_code == 1
|
|
160
|
+
assert json.loads(result.stdout)["message"] == (
|
|
161
|
+
"project-summary requires --user-id or --channel with --external-subject-id"
|
|
162
|
+
)
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
def _insert_project(db_path, *, project_id: str, updated_at: datetime) -> None:
|
|
166
|
+
with sqlite3.connect(db_path) as connection:
|
|
167
|
+
connection.execute(
|
|
168
|
+
"""
|
|
169
|
+
INSERT INTO projects (
|
|
170
|
+
project_id,
|
|
171
|
+
project_name,
|
|
172
|
+
owner_user_id,
|
|
173
|
+
primary_goal,
|
|
174
|
+
mind_stage,
|
|
175
|
+
lifecycle_status,
|
|
176
|
+
current_revision_id,
|
|
177
|
+
current_version_id,
|
|
178
|
+
timezone,
|
|
179
|
+
created_at,
|
|
180
|
+
updated_at
|
|
181
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
182
|
+
""",
|
|
183
|
+
(
|
|
184
|
+
project_id,
|
|
185
|
+
"Summary Project",
|
|
186
|
+
"usr_owner",
|
|
187
|
+
"Summarize project tasks",
|
|
188
|
+
"v1",
|
|
189
|
+
"active",
|
|
190
|
+
"rev_summary_001",
|
|
191
|
+
"ver_summary_001",
|
|
192
|
+
"UTC",
|
|
193
|
+
_db_datetime(updated_at),
|
|
194
|
+
_db_datetime(updated_at),
|
|
195
|
+
),
|
|
196
|
+
)
|
|
197
|
+
connection.commit()
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
def _insert_user(db_path, *, user_id: str, display_name: str) -> None:
|
|
201
|
+
with sqlite3.connect(db_path) as connection:
|
|
202
|
+
connection.execute(
|
|
203
|
+
"""
|
|
204
|
+
INSERT INTO users (
|
|
205
|
+
user_id,
|
|
206
|
+
display_name,
|
|
207
|
+
status,
|
|
208
|
+
created_at,
|
|
209
|
+
updated_at
|
|
210
|
+
) VALUES (?, ?, ?, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)
|
|
211
|
+
""",
|
|
212
|
+
(user_id, display_name, "active"),
|
|
213
|
+
)
|
|
214
|
+
connection.commit()
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
def _insert_identity(
|
|
218
|
+
db_path,
|
|
219
|
+
*,
|
|
220
|
+
identity_id: str,
|
|
221
|
+
user_id: str,
|
|
222
|
+
channel: str,
|
|
223
|
+
external_subject_id: str,
|
|
224
|
+
) -> None:
|
|
225
|
+
with sqlite3.connect(db_path) as connection:
|
|
226
|
+
connection.execute(
|
|
227
|
+
"""
|
|
228
|
+
INSERT INTO user_channel_identities (
|
|
229
|
+
identity_id,
|
|
230
|
+
user_id,
|
|
231
|
+
channel,
|
|
232
|
+
external_subject_id,
|
|
233
|
+
status,
|
|
234
|
+
profile_json,
|
|
235
|
+
created_at,
|
|
236
|
+
updated_at
|
|
237
|
+
) VALUES (?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)
|
|
238
|
+
""",
|
|
239
|
+
(identity_id, user_id, channel, external_subject_id, "active", "{}"),
|
|
240
|
+
)
|
|
241
|
+
connection.commit()
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
def _insert_binding(
|
|
245
|
+
db_path,
|
|
246
|
+
*,
|
|
247
|
+
binding_id: str,
|
|
248
|
+
project_id: str,
|
|
249
|
+
channel: str,
|
|
250
|
+
external_channel_id: str,
|
|
251
|
+
) -> None:
|
|
252
|
+
with sqlite3.connect(db_path) as connection:
|
|
253
|
+
connection.execute(
|
|
254
|
+
"""
|
|
255
|
+
INSERT INTO project_bindings (
|
|
256
|
+
binding_id,
|
|
257
|
+
project_id,
|
|
258
|
+
channel,
|
|
259
|
+
external_channel_id,
|
|
260
|
+
binding_kind,
|
|
261
|
+
status,
|
|
262
|
+
payload_json,
|
|
263
|
+
created_at,
|
|
264
|
+
updated_at
|
|
265
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)
|
|
266
|
+
""",
|
|
267
|
+
(binding_id, project_id, channel, external_channel_id, "project_group", "active", "{}"),
|
|
268
|
+
)
|
|
269
|
+
connection.commit()
|
|
270
|
+
|
|
271
|
+
|
|
272
|
+
def _insert_task(
|
|
273
|
+
db_path,
|
|
274
|
+
*,
|
|
275
|
+
task_id: str,
|
|
276
|
+
project_id: str,
|
|
277
|
+
owner_user_id: str,
|
|
278
|
+
task_status: str,
|
|
279
|
+
end_date: str,
|
|
280
|
+
created_at: datetime,
|
|
281
|
+
updated_at: datetime,
|
|
282
|
+
) -> None:
|
|
283
|
+
with sqlite3.connect(db_path) as connection:
|
|
284
|
+
connection.execute(
|
|
285
|
+
"""
|
|
286
|
+
INSERT INTO project_tasks (
|
|
287
|
+
task_id,
|
|
288
|
+
project_id,
|
|
289
|
+
version_id,
|
|
290
|
+
task_name,
|
|
291
|
+
task_description,
|
|
292
|
+
owner_user_id,
|
|
293
|
+
task_status,
|
|
294
|
+
start_date,
|
|
295
|
+
end_date,
|
|
296
|
+
last_updated_at,
|
|
297
|
+
payload_json,
|
|
298
|
+
created_at,
|
|
299
|
+
updated_at
|
|
300
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
301
|
+
""",
|
|
302
|
+
(
|
|
303
|
+
task_id,
|
|
304
|
+
project_id,
|
|
305
|
+
"ver_summary_001",
|
|
306
|
+
task_id,
|
|
307
|
+
f"{task_id} description",
|
|
308
|
+
owner_user_id,
|
|
309
|
+
task_status,
|
|
310
|
+
created_at.date().isoformat(),
|
|
311
|
+
end_date,
|
|
312
|
+
_db_datetime(updated_at),
|
|
313
|
+
"{}",
|
|
314
|
+
_db_datetime(created_at),
|
|
315
|
+
_db_datetime(updated_at),
|
|
316
|
+
),
|
|
317
|
+
)
|
|
318
|
+
connection.commit()
|
|
319
|
+
|
|
320
|
+
|
|
321
|
+
def _db_datetime(value: datetime) -> str:
|
|
322
|
+
return value.isoformat(sep=" ")
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{project_agent-0.2.1 → project_agent-0.2.2}/src/project_agent/application/bootstrap/__init__.py
RENAMED
|
File without changes
|
{project_agent-0.2.1 → project_agent-0.2.2}/src/project_agent/application/bootstrap/checkpoint.py
RENAMED
|
File without changes
|
|
File without changes
|
{project_agent-0.2.1 → project_agent-0.2.2}/src/project_agent/application/bootstrap/loop_state.py
RENAMED
|
File without changes
|
{project_agent-0.2.1 → project_agent-0.2.2}/src/project_agent/application/context/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{project_agent-0.2.1 → project_agent-0.2.2}/src/project_agent/application/context/get_task.py
RENAMED
|
File without changes
|
|
File without changes
|