196 lines
5.5 KiB
Python
196 lines
5.5 KiB
Python
"""
|
|
Pydantic models for ifccommit.yaml operation declarations and API payloads.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
from typing import Literal, Optional
|
|
from pydantic import BaseModel, model_validator
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# ifccommit.yaml — operation declarations
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
class Operation(BaseModel):
|
|
name: str
|
|
command: Literal[
|
|
"list",
|
|
"info",
|
|
"extract",
|
|
"insert",
|
|
"replace",
|
|
"split",
|
|
"space",
|
|
"move",
|
|
"copy",
|
|
"diff",
|
|
"history",
|
|
"modify",
|
|
"merge",
|
|
"remove",
|
|
]
|
|
|
|
# Shared fields
|
|
input: Optional[str] = None
|
|
output: Optional[str] = None
|
|
|
|
# extract (bulk) / split
|
|
types: Optional[list[str]] = None
|
|
|
|
# extract (raw ifcopenshell selector query, used when types/id are insufficient)
|
|
query: Optional[str] = None
|
|
|
|
# extract (specific): type + id
|
|
type: Optional[str] = None # singular IFC type (e.g. IfcSpace)
|
|
id: Optional[str] = None # space name (e.g. A102)
|
|
|
|
# insert / replace / merge
|
|
base: Optional[str] = None
|
|
part: Optional[str] = None
|
|
|
|
# replace / space / merge
|
|
space: Optional[str] = None
|
|
|
|
# split
|
|
outdir: Optional[str] = None
|
|
|
|
# info
|
|
ifc_type: Optional[str] = None
|
|
|
|
# space
|
|
by: Literal["name", "longname"] = "name"
|
|
|
|
# move / copy / modify
|
|
element: Optional[str] = None # element name substring
|
|
entity_id: Optional[int] = None
|
|
entity_ids: Optional[list[int]] = None
|
|
tags: Optional[list[str]] = None
|
|
x: float = 0.0
|
|
y: float = 0.0
|
|
z: float = 0.0
|
|
|
|
# modify / merge
|
|
tag: Optional[str] = None
|
|
|
|
# diff
|
|
target: Optional[str] = None
|
|
verbose: bool = False
|
|
|
|
# history
|
|
yaml_src: Optional[str] = None # path to the yaml file (e.g. yaml/duplex.yaml)
|
|
write_psets: bool = False # write mode: stamp Pset_GitCommit on output elements
|
|
|
|
@model_validator(mode="after")
|
|
def check_required_fields(self) -> "Operation":
|
|
cmd = self.command
|
|
missing: list[str] = []
|
|
|
|
if cmd in (
|
|
"list",
|
|
"extract",
|
|
"split",
|
|
"space",
|
|
"move",
|
|
"copy",
|
|
"diff",
|
|
"modify",
|
|
):
|
|
if not self.input:
|
|
missing.append("input")
|
|
if cmd in (
|
|
"extract",
|
|
"insert",
|
|
"replace",
|
|
"space",
|
|
"move",
|
|
"copy",
|
|
"modify",
|
|
"merge",
|
|
):
|
|
if not self.output:
|
|
missing.append("output")
|
|
if cmd == "extract":
|
|
if not self.types and not self.query and not (self.type and self.id):
|
|
missing.append("types (bulk), query, or type+id (specific)")
|
|
if cmd in ("insert",):
|
|
if not self.base:
|
|
missing.append("base")
|
|
if not self.part:
|
|
missing.append("part")
|
|
if cmd in ("replace", "merge"):
|
|
if not self.base:
|
|
missing.append("base")
|
|
if not self.space:
|
|
missing.append("space")
|
|
if not self.part:
|
|
missing.append("part")
|
|
if cmd == "split":
|
|
if not self.outdir:
|
|
missing.append("outdir")
|
|
if cmd == "space":
|
|
if not self.space:
|
|
missing.append("space")
|
|
if cmd == "remove":
|
|
if not self.input:
|
|
missing.append("input")
|
|
if not self.type:
|
|
missing.append("type")
|
|
if cmd == "info":
|
|
if not self.input:
|
|
missing.append("input")
|
|
if not self.ifc_type:
|
|
missing.append("ifc_type")
|
|
if cmd == "history":
|
|
if self.input and not self.output:
|
|
missing.append("output (required when input is set)")
|
|
if cmd == "copy":
|
|
if not self.input:
|
|
missing.append("input")
|
|
if not self.output:
|
|
missing.append("output")
|
|
if not self.entity_ids and not self.tags:
|
|
missing.append("entity_ids or tags")
|
|
|
|
if missing:
|
|
raise ValueError(
|
|
f"Operation '{self.name}' (command={cmd}) is missing required fields: {missing}"
|
|
)
|
|
return self
|
|
|
|
|
|
class IfcCommitYaml(BaseModel):
|
|
src: Optional[str] = None
|
|
operations: list[Operation] = []
|
|
dest: Optional[str] = None
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# API payloads
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
class RepoRequest(BaseModel):
|
|
repo: str # e.g. "orgname/reponame"
|
|
branch: str = "main"
|
|
token: str
|
|
yaml_file: str = "yaml/duplex.yaml" # repo-relative path to the pipeline yaml
|
|
overrides: dict[str, str] = {} # op name → ifc_type (for info ops)
|
|
id_overrides: dict[str, str] = {} # op name → id (for extract ops)
|
|
element_overrides: dict[str, str] = {} # op name → element (for modify ops)
|
|
|
|
|
|
class EntityRequest(BaseModel):
|
|
repo: str
|
|
branch: str = "main"
|
|
token: str
|
|
file: str # repo-relative path to the IFC file
|
|
ifc_type: str # IFC type to list (e.g. IfcSpace)
|
|
location: Optional[str] = None # if set, filter IfcElement by this space name
|
|
|
|
|
|
class HistoryRequest(BaseModel):
|
|
repo: str
|
|
branch: str = "main"
|
|
token: str
|
|
file: str = "ifc/history.json" # repo-relative path to the history JSON
|