Initial SFERA platform baseline
This commit is contained in:
@@ -0,0 +1,3 @@
|
||||
# sfera-job-topology
|
||||
|
||||
Scheduled job inventory and links to semantic routines.
|
||||
@@ -0,0 +1,11 @@
|
||||
[project]
|
||||
name = "sfera-job-topology"
|
||||
version = "0.1.0"
|
||||
requires-python = ">=3.12"
|
||||
dependencies = [
|
||||
"pydantic>=2.0",
|
||||
"sfera-sir",
|
||||
]
|
||||
|
||||
[tool.uv]
|
||||
package = true
|
||||
@@ -0,0 +1,70 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
from sir import EdgeKind, NodeKind, SemanticNode, SirSnapshot
|
||||
|
||||
|
||||
class ScheduledJob(BaseModel):
|
||||
job_id: str
|
||||
name: str
|
||||
routine_name: str
|
||||
schedule: str | None = None
|
||||
attributes: dict = Field(default_factory=dict)
|
||||
|
||||
|
||||
class JobBinding(BaseModel):
|
||||
job: ScheduledJob
|
||||
routine: SemanticNode | None = None
|
||||
|
||||
|
||||
def bind_jobs(snapshot: SirSnapshot, jobs: list[ScheduledJob]) -> list[JobBinding]:
|
||||
routines = {
|
||||
node.name.casefold(): node
|
||||
for node in snapshot.nodes
|
||||
if node.kind.value in {"PROCEDURE", "FUNCTION"}
|
||||
}
|
||||
return [
|
||||
JobBinding(job=job, routine=routines.get(job.routine_name.casefold()))
|
||||
for job in sorted(jobs, key=lambda item: item.name)
|
||||
]
|
||||
|
||||
|
||||
def snapshot_scheduled_jobs(snapshot: SirSnapshot) -> list[JobBinding]:
|
||||
nodes = {node.lineage_id: node for node in snapshot.nodes}
|
||||
bindings: list[JobBinding] = []
|
||||
for job_node in sorted(
|
||||
(node for node in snapshot.nodes if node.kind == NodeKind.SCHEDULED_JOB),
|
||||
key=lambda node: node.qualified_name,
|
||||
):
|
||||
run_edge = next(
|
||||
(
|
||||
edge
|
||||
for edge in snapshot.edges
|
||||
if edge.kind == EdgeKind.RUNS and edge.source_lineage == job_node.lineage_id
|
||||
),
|
||||
None,
|
||||
)
|
||||
routine = nodes.get(run_edge.target_lineage) if run_edge is not None else None
|
||||
routine_name = (
|
||||
routine.name
|
||||
if routine is not None
|
||||
else str(job_node.attributes.get("method") or job_node.attributes.get("Method") or "")
|
||||
)
|
||||
bindings.append(
|
||||
JobBinding(
|
||||
job=ScheduledJob(
|
||||
job_id=job_node.lineage_id,
|
||||
name=job_node.name,
|
||||
routine_name=routine_name,
|
||||
schedule=str(job_node.attributes.get("schedule") or job_node.attributes.get("Schedule") or "")
|
||||
or None,
|
||||
attributes=job_node.attributes,
|
||||
),
|
||||
routine=routine,
|
||||
)
|
||||
)
|
||||
return bindings
|
||||
|
||||
|
||||
__all__ = ["JobBinding", "ScheduledJob", "bind_jobs", "snapshot_scheduled_jobs"]
|
||||
@@ -0,0 +1,43 @@
|
||||
from pathlib import Path
|
||||
|
||||
from job_topology import ScheduledJob, bind_jobs, snapshot_scheduled_jobs
|
||||
from sir import EdgeKind
|
||||
from semantic_kernel import index_project
|
||||
|
||||
|
||||
def test_bind_jobs_to_routines(tmp_path: Path):
|
||||
module = tmp_path / "jobs.bsl"
|
||||
module.write_text("Процедура ОбновитьЦены()\nКонецПроцедуры\n", encoding="utf-8")
|
||||
snapshot = index_project(tmp_path, project_id="demo")
|
||||
|
||||
bindings = bind_jobs(
|
||||
snapshot,
|
||||
[ScheduledJob(job_id="job.1", name="Обновление цен", routine_name="ОбновитьЦены")],
|
||||
)
|
||||
|
||||
assert bindings[0].routine is not None
|
||||
assert bindings[0].routine.name == "ОбновитьЦены"
|
||||
|
||||
|
||||
def test_snapshot_scheduled_jobs_bind_xml_job_to_routine(tmp_path: Path):
|
||||
xml = tmp_path / "metadata.xml"
|
||||
xml.write_text(
|
||||
"""
|
||||
<Configuration>
|
||||
<ScheduledJob name="ОбновлениеЦен" qualifiedName="РегламентноеЗадание.ОбновлениеЦен" method="ОбновитьЦены" schedule="КаждыйДень" />
|
||||
</Configuration>
|
||||
""",
|
||||
encoding="utf-8",
|
||||
)
|
||||
module = tmp_path / "CommonModules" / "РегламентныеОперации" / "Module.bsl"
|
||||
module.parent.mkdir(parents=True)
|
||||
module.write_text("Процедура ОбновитьЦены()\nКонецПроцедуры\n", encoding="utf-8")
|
||||
|
||||
snapshot = index_project(tmp_path, project_id="jobs")
|
||||
bindings = snapshot_scheduled_jobs(snapshot)
|
||||
|
||||
assert any(edge.kind == EdgeKind.RUNS for edge in snapshot.edges)
|
||||
assert bindings[0].job.name == "ОбновлениеЦен"
|
||||
assert bindings[0].job.schedule == "КаждыйДень"
|
||||
assert bindings[0].routine is not None
|
||||
assert bindings[0].routine.name == "ОбновитьЦены"
|
||||
Reference in New Issue
Block a user