263 lines
8.8 KiB
Python
263 lines
8.8 KiB
Python
|
|
from __future__ import annotations
|
||
|
|
|
||
|
|
from typing import Any
|
||
|
|
|
||
|
|
from .registry import TABLE_SPECS
|
||
|
|
|
||
|
|
_JSON_SCHEMA = "https://json-schema.org/draft/2020-12/schema"
|
||
|
|
|
||
|
|
|
||
|
|
def _record_schema(title: str, required: tuple[str, ...]) -> dict[str, Any]:
|
||
|
|
return {
|
||
|
|
"$schema": _JSON_SCHEMA,
|
||
|
|
"title": title,
|
||
|
|
"type": "object",
|
||
|
|
"required": list(required),
|
||
|
|
"properties": {
|
||
|
|
field: {
|
||
|
|
"type": ["string", "number", "integer", "boolean", "array", "object", "null"]
|
||
|
|
}
|
||
|
|
for field in required
|
||
|
|
},
|
||
|
|
"additionalProperties": True,
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
def _domain_schema(title: str, table_names: tuple[str, ...]) -> dict[str, Any]:
|
||
|
|
return {
|
||
|
|
"$schema": _JSON_SCHEMA,
|
||
|
|
"title": title,
|
||
|
|
"type": "object",
|
||
|
|
"required": list(table_names),
|
||
|
|
"properties": {
|
||
|
|
name: {
|
||
|
|
"type": "array",
|
||
|
|
"items": {"$ref": f"tables/{name}.schema.json"},
|
||
|
|
}
|
||
|
|
for name in table_names
|
||
|
|
},
|
||
|
|
"additionalProperties": False,
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
def _manifest_schema() -> dict[str, Any]:
|
||
|
|
return {
|
||
|
|
"$schema": _JSON_SCHEMA,
|
||
|
|
"title": "Beihai synthetic shipyard APS dataset manifest",
|
||
|
|
"type": "object",
|
||
|
|
"required": [
|
||
|
|
"datasetType",
|
||
|
|
"organizationScenario",
|
||
|
|
"generatedAt",
|
||
|
|
"generatorVersion",
|
||
|
|
"schemaVersion",
|
||
|
|
"randomSeed",
|
||
|
|
"businessDigest",
|
||
|
|
"fileCount",
|
||
|
|
"files",
|
||
|
|
],
|
||
|
|
"properties": {
|
||
|
|
"datasetType": {"const": "SYNTHETIC"},
|
||
|
|
"organizationScenario": {"const": "BEIHAI_SHIPYARD_APS"},
|
||
|
|
"generatedAt": {"type": "string", "format": "date-time"},
|
||
|
|
"generatorVersion": {"type": "string"},
|
||
|
|
"schemaVersion": {"type": "string"},
|
||
|
|
"randomSeed": {"type": "integer"},
|
||
|
|
"businessDigest": {"type": "string", "minLength": 64, "maxLength": 64},
|
||
|
|
"fileCount": {"type": "integer", "minimum": 1},
|
||
|
|
"files": {
|
||
|
|
"type": "array",
|
||
|
|
"items": {
|
||
|
|
"type": "object",
|
||
|
|
"required": ["path", "bytes", "sha256"],
|
||
|
|
"properties": {
|
||
|
|
"path": {"type": "string", "minLength": 1},
|
||
|
|
"kind": {"type": "string"},
|
||
|
|
"rowCount": {"type": ["integer", "null"], "minimum": 0},
|
||
|
|
"bytes": {"type": "integer", "minimum": 0},
|
||
|
|
"sha256": {
|
||
|
|
"type": "string",
|
||
|
|
"minLength": 64,
|
||
|
|
"maxLength": 64,
|
||
|
|
},
|
||
|
|
},
|
||
|
|
"additionalProperties": False,
|
||
|
|
},
|
||
|
|
},
|
||
|
|
},
|
||
|
|
"additionalProperties": True,
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
def _skill_schema() -> dict[str, Any]:
|
||
|
|
required = (
|
||
|
|
"skillId",
|
||
|
|
"name",
|
||
|
|
"description",
|
||
|
|
"inputSchema",
|
||
|
|
"outputSchema",
|
||
|
|
"requiredData",
|
||
|
|
"algorithmCandidates",
|
||
|
|
"hardConstraints",
|
||
|
|
"softConstraints",
|
||
|
|
"fallbackAlgorithm",
|
||
|
|
"timeoutSeconds",
|
||
|
|
"validationRules",
|
||
|
|
"evidenceFields",
|
||
|
|
"version",
|
||
|
|
)
|
||
|
|
schema = _record_schema("Shipyard APS skill", required)
|
||
|
|
schema["properties"].update(
|
||
|
|
{
|
||
|
|
"skillId": {"type": "string", "pattern": "^ship-[a-z0-9-]+$"},
|
||
|
|
"timeoutSeconds": {"type": "integer", "minimum": 1},
|
||
|
|
"requiredData": {"type": "array", "items": {"type": "string"}},
|
||
|
|
"algorithmCandidates": {"type": "array", "items": {"type": "string"}},
|
||
|
|
"hardConstraints": {"type": "array", "items": {"type": "string"}},
|
||
|
|
"softConstraints": {"type": "array", "items": {"type": "string"}},
|
||
|
|
"validationRules": {"type": "array", "items": {"type": "string"}},
|
||
|
|
"evidenceFields": {"type": "array", "items": {"type": "string"}},
|
||
|
|
}
|
||
|
|
)
|
||
|
|
return schema
|
||
|
|
|
||
|
|
|
||
|
|
def _knowledge_schema() -> dict[str, Any]:
|
||
|
|
required = (
|
||
|
|
"knowledgeId",
|
||
|
|
"title",
|
||
|
|
"category",
|
||
|
|
"content",
|
||
|
|
"applicableShipTypes",
|
||
|
|
"applicableWorkshops",
|
||
|
|
"tags",
|
||
|
|
"sourceType",
|
||
|
|
"version",
|
||
|
|
"effectiveDate",
|
||
|
|
"confidence",
|
||
|
|
"relatedResourceIds",
|
||
|
|
"relatedMaterialGroups",
|
||
|
|
"relatedOperationCodes",
|
||
|
|
)
|
||
|
|
schema = _record_schema("Synthetic shipbuilding knowledge asset", required)
|
||
|
|
schema["properties"].update(
|
||
|
|
{
|
||
|
|
"sourceType": {"const": "SYNTHETIC_KNOWLEDGE"},
|
||
|
|
"confidence": {"type": "number", "minimum": 0, "maximum": 1},
|
||
|
|
"effectiveDate": {"type": "string", "format": "date"},
|
||
|
|
"applicableShipTypes": {"type": "array", "items": {"type": "string"}},
|
||
|
|
"applicableWorkshops": {"type": "array", "items": {"type": "string"}},
|
||
|
|
"tags": {"type": "array", "items": {"type": "string"}},
|
||
|
|
"relatedResourceIds": {"type": "array", "items": {"type": "string"}},
|
||
|
|
"relatedMaterialGroups": {"type": "array", "items": {"type": "string"}},
|
||
|
|
"relatedOperationCodes": {"type": "array", "items": {"type": "string"}},
|
||
|
|
}
|
||
|
|
)
|
||
|
|
return schema
|
||
|
|
|
||
|
|
|
||
|
|
def build_schema_documents() -> dict[str, dict[str, Any]]:
|
||
|
|
"""Return deterministic JSON Schema documents keyed below the schemas directory."""
|
||
|
|
groups: tuple[tuple[str, str, tuple[str, ...]], ...] = (
|
||
|
|
(
|
||
|
|
"project.schema.json",
|
||
|
|
"Project and contract domain",
|
||
|
|
("contracts", "ship-projects", "milestones"),
|
||
|
|
),
|
||
|
|
("wbs.schema.json", "Shipyard WBS domain", ("wbs", "blocks", "zones", "work-packages")),
|
||
|
|
(
|
||
|
|
"engineering.schema.json",
|
||
|
|
"Engineering and routing domain",
|
||
|
|
(
|
||
|
|
"drawings",
|
||
|
|
"ebom",
|
||
|
|
"pbom",
|
||
|
|
"mbom",
|
||
|
|
"routings",
|
||
|
|
"routing-operations",
|
||
|
|
"engineering-releases",
|
||
|
|
),
|
||
|
|
),
|
||
|
|
(
|
||
|
|
"material.schema.json",
|
||
|
|
"Material and inventory domain",
|
||
|
|
(
|
||
|
|
"materials",
|
||
|
|
"inventory",
|
||
|
|
"inventory-allocations",
|
||
|
|
"substitutes",
|
||
|
|
"planned-receipts",
|
||
|
|
"material-requirements",
|
||
|
|
"kit-readiness",
|
||
|
|
),
|
||
|
|
),
|
||
|
|
(
|
||
|
|
"planning.schema.json",
|
||
|
|
"Planning domain",
|
||
|
|
(
|
||
|
|
"production-orders",
|
||
|
|
"work-orders",
|
||
|
|
"operations",
|
||
|
|
"purchase-suggestions",
|
||
|
|
"outsource-suggestions",
|
||
|
|
"capacity-demands",
|
||
|
|
"schedule-versions",
|
||
|
|
"schedule-slots",
|
||
|
|
"resource-loads",
|
||
|
|
"conflicts",
|
||
|
|
"kpis",
|
||
|
|
),
|
||
|
|
),
|
||
|
|
(
|
||
|
|
"execution.schema.json",
|
||
|
|
"Execution domain",
|
||
|
|
("mes-orders", "operation-reports", "material-issues"),
|
||
|
|
),
|
||
|
|
(
|
||
|
|
"quality.schema.json",
|
||
|
|
"Quality domain",
|
||
|
|
("quality-inspections", "nonconformities", "rework-orders"),
|
||
|
|
),
|
||
|
|
)
|
||
|
|
documents: dict[str, dict[str, Any]] = {
|
||
|
|
"manifest.schema.json": _manifest_schema(),
|
||
|
|
"world.schema.json": {
|
||
|
|
"$schema": _JSON_SCHEMA,
|
||
|
|
"title": "Synthetic shipyard APS world",
|
||
|
|
"type": "object",
|
||
|
|
"required": ["datasetMetadata", "shipyardTables", "planningSourceHash"],
|
||
|
|
"properties": {
|
||
|
|
"datasetMetadata": {"type": "object"},
|
||
|
|
"shipyardTables": {"type": "object"},
|
||
|
|
"planningSourceHash": {"type": "string"},
|
||
|
|
},
|
||
|
|
"additionalProperties": True,
|
||
|
|
},
|
||
|
|
"scenario.schema.json": _record_schema(
|
||
|
|
"Shipyard planning scenario",
|
||
|
|
("scenarioId", "status", "datasetType"),
|
||
|
|
),
|
||
|
|
"skill.schema.json": _skill_schema(),
|
||
|
|
"knowledge-asset.schema.json": _knowledge_schema(),
|
||
|
|
"validation-report.schema.json": {
|
||
|
|
"$schema": _JSON_SCHEMA,
|
||
|
|
"title": "Synthetic dataset validation report",
|
||
|
|
"type": "object",
|
||
|
|
"required": ["valid", "blockingIssues"],
|
||
|
|
"properties": {
|
||
|
|
"valid": {"type": "boolean"},
|
||
|
|
"blockingIssues": {"type": "array", "items": {"type": "string"}},
|
||
|
|
"metrics": {"type": "object"},
|
||
|
|
},
|
||
|
|
"additionalProperties": True,
|
||
|
|
},
|
||
|
|
}
|
||
|
|
for path, title, tables in groups:
|
||
|
|
documents[path] = _domain_schema(title, tables)
|
||
|
|
for name, spec in TABLE_SPECS.items():
|
||
|
|
documents[f"tables/{name}.schema.json"] = _record_schema(
|
||
|
|
f"{name} row",
|
||
|
|
spec.required,
|
||
|
|
)
|
||
|
|
return {key: documents[key] for key in sorted(documents)}
|