git fixec null symbol

This commit is contained in:
2026-09-02 13:45:27 +03:00
parent dfc71c0130
commit 8bf360d583
2 changed files with 48 additions and 20 deletions
+17 -1
View File
@@ -8,11 +8,22 @@ from pathlib import Path
from typing import Optional from typing import Optional
def sanitize_text(value: str | None) -> str:
"""Remove NUL bytes — PostgreSQL text fields reject them; SchDoc may contain them."""
if not value:
return ""
return value.replace("\x00", "")
@dataclass @dataclass
class ComponentProperty: class ComponentProperty:
name: str name: str
text: str text: str
def __post_init__(self) -> None:
self.name = sanitize_text(self.name)
self.text = sanitize_text(self.text)
@dataclass @dataclass
class DielProperty: class DielProperty:
@@ -22,6 +33,10 @@ class DielProperty:
diel_type: int diel_type: int
layer_number: int layer_number: int
def __post_init__(self) -> None:
self.name = sanitize_text(self.name)
self.value = sanitize_text(self.value)
@dataclass @dataclass
class ProjectData: class ProjectData:
@@ -342,6 +357,7 @@ class AltiumParser:
def make_complex_string(text: str, props: list[ComponentProperty]) -> str: def make_complex_string(text: str, props: list[ComponentProperty]) -> str:
text = sanitize_text(text)
if not text.startswith("="): if not text.startswith("="):
return text return text
expr = text[1:] expr = text[1:]
@@ -351,7 +367,7 @@ def make_complex_string(text: str, props: list[ComponentProperty]) -> str:
key = m.group(1) key = m.group(1)
return mapping.get(key, "") return mapping.get(key, "")
return re.sub(r"['\"]([^'\"]+)['\"]", repl, expr) return sanitize_text(re.sub(r"['\"]([^'\"]+)['\"]", repl, expr))
def find_prjpcb(extract_dir: Path) -> Optional[Path]: def find_prjpcb(extract_dir: Path) -> Optional[Path]:
+31 -19
View File
@@ -20,7 +20,7 @@ from app.models import (
Variant, Variant,
VariantProperty, VariantProperty,
) )
from app.services.altium_parser import AltiumParser, ProjectData, find_prjpcb, make_complex_string from app.services.altium_parser import AltiumParser, ProjectData, find_prjpcb, make_complex_string, sanitize_text
from app.services.designators import DEFAULT_MAPPINGS from app.services.designators import DEFAULT_MAPPINGS
@@ -79,7 +79,7 @@ def ensure_default_mappings(db: Session, project: Project) -> None:
def save_project_data(db: Session, project: Project, data: ProjectData) -> None: def save_project_data(db: Session, project: Project, data: ProjectData) -> None:
variant_objs: list[Variant] = [] variant_objs: list[Variant] = []
for name in data.variant_names: for name in data.variant_names:
v = Variant(project_id=project.id, name=name) v = Variant(project_id=project.id, name=sanitize_text(name))
db.add(v) db.add(v)
variant_objs.append(v) variant_objs.append(v)
db.flush() db.flush()
@@ -95,7 +95,7 @@ def save_project_data(db: Session, project: Project, data: ProjectData) -> None:
designator = "" designator = ""
for p in comp_props: for p in comp_props:
if p.name.lower() == "designator": if p.name.lower() == "designator":
designator = p.text designator = sanitize_text(p.text)
break break
if not designator: if not designator:
continue continue
@@ -105,7 +105,13 @@ def save_project_data(db: Session, project: Project, data: ProjectData) -> None:
db.add(comp) db.add(comp)
db.flush() db.flush()
for p in comp_props: for p in comp_props:
db.add(ComponentProperty(component_id=comp.id, key=p.name, value=p.text)) db.add(
ComponentProperty(
component_id=comp.id,
key=sanitize_text(p.name),
value=sanitize_text(p.text),
)
)
designator_to_component[designator] = comp designator_to_component[designator] = comp
# link to all variants as fitted by default # link to all variants as fitted by default
for v in variant_objs: for v in variant_objs:
@@ -126,7 +132,7 @@ def save_project_data(db: Session, project: Project, data: ProjectData) -> None:
designator = "" designator = ""
for p in comp_props: for p in comp_props:
if p.name == "Designator": if p.name == "Designator":
designator = p.text designator = sanitize_text(p.text)
break break
if not designator: if not designator:
continue continue
@@ -149,8 +155,8 @@ def save_project_data(db: Session, project: Project, data: ProjectData) -> None:
VariantProperty( VariantProperty(
component_id=comp.id, component_id=comp.id,
variant_id=variant.id, variant_id=variant.id,
key=p.name, key=sanitize_text(p.name),
value=p.text, value=sanitize_text(p.text),
) )
) )
@@ -159,16 +165,18 @@ def save_project_data(db: Session, project: Project, data: ProjectData) -> None:
for item in params: for item in params:
if len(item) < 2: if len(item) < 2:
continue continue
name, value = item[0], item[1] name, value = sanitize_text(item[0]), sanitize_text(item[1])
value = make_complex_string(value, flat_props) value = sanitize_text(make_complex_string(value, flat_props))
db.add( db.add(
ProjectParam( ProjectParam(
project_id=project.id, project_id=project.id,
name=name, name=name,
value=value, value=value,
variant_name=data.variant_names[variant_idx] variant_name=sanitize_text(
if variant_idx < len(data.variant_names) data.variant_names[variant_idx]
else "", if variant_idx < len(data.variant_names)
else ""
),
) )
) )
@@ -181,15 +189,16 @@ def save_project_data(db: Session, project: Project, data: ProjectData) -> None:
db.add( db.add(
DielMaterial( DielMaterial(
pcb_data_id=pcb.id, pcb_data_id=pcb.id,
name=m.name, name=sanitize_text(m.name),
value=m.value, value=sanitize_text(m.value),
height=m.height, height=m.height,
diel_type=m.diel_type, diel_type=m.diel_type,
layer_number=m.layer_number, layer_number=m.layer_number,
) )
) )
project.pcb_doc_name = Path(data.pcb_doc_file_name).name if data.pcb_doc_file_name else None pcb_name = sanitize_text(Path(data.pcb_doc_file_name).name if data.pcb_doc_file_name else "")
project.pcb_doc_name = pcb_name or None
if "No Variations" in data.variant_names: if "No Variations" in data.variant_names:
project.current_variant = "No Variations" project.current_variant = "No Variations"
elif data.variant_names: elif data.variant_names:
@@ -247,10 +256,13 @@ def ingest_zip(
db.refresh(project) db.refresh(project)
return project return project
except Exception as e: except Exception as e:
project.status = "error" db.rollback()
project.error_message = str(e) proj = db.get(Project, project.id)
db.commit() if proj:
raise proj.status = "error"
proj.error_message = str(e)[:2000]
db.commit()
raise ValueError(str(e)) from e
def get_project_full(db: Session, project_id: int) -> Project | None: def get_project_full(db: Session, project_id: int) -> Project | None: