Skip to content

fix(ontology): persist concept_type and property metadata in RDF#540

Merged
larsgeorge-db merged 2 commits into
mainfrom
fix-concept-rdf-types
Jun 22, 2026
Merged

fix(ontology): persist concept_type and property metadata in RDF#540
larsgeorge-db merged 2 commits into
mainfrom
fix-concept-rdf-types

Conversation

@larsgeorge-db

Copy link
Copy Markdown
Collaborator

Summary

Custom Properties created via the concept editor were always rendered as plain Concepts in the UI. The backend was hardcoding skos:Concept on write and dropping the property-specific fields (property_type, domain, range), so there was no way to recover the user's intent on read.

This PR wires those fields end-to-end so every option in the editor dropdown round-trips correctly:

  • concept (default) → skos:Concept
  • classowl:Class + rdfs:Class
  • property + objectowl:ObjectProperty + rdf:Property (+ optional rdfs:domain/rdfs:range)
  • property + datatypeowl:DatatypeProperty + rdf:Property
  • property + annotationowl:AnnotationProperty + rdf:Property
  • individualowl:NamedIndividual
  • termskos:Concept + custom ontos:conceptType "term" annotation so the user-facing type can be reconstructed

Changes

  • models/ontology.py — expose property_type / domain / range on OntologyConcept, ConceptCreate, ConceptUpdate, plus concept_type on update for draft retypes.
  • controller/semantic_models_manager.py
    • New _resolve_concept_rdf_types and _detect_concept_type helpers centralise the mapping in both directions.
    • create_concept now emits the resolved rdf:type set and writes rdfs:domain / rdfs:range for properties.
    • update_concept supports draft retyping and partial domain/range updates without clobbering unspecified fields.
    • Read paths (_extract_concept_metadata, get_concept, get_concept_details, _compute_all_concepts) surface property_type / domain / range.
    • promote_concept / migrate_concept forward the new fields.
  • routes/semantic_models_routes.py — pass the new fields through POST /knowledge/concepts and PATCH /knowledge/concepts/{iri}.
  • tests/integration/test_knowledge_routes.py — new TestKnowledgeConceptTypes class with 10 cases covering create + read for every type combination and partial PATCH of property fields.

Test plan

  • Backend integration tests for every concept type (TestKnowledgeConceptTypes, 10 cases) pass.
  • Live end-to-end Playwright walk-through of all 7 dialog options against a running stack:
    • Concept → badge Concept
    • Class → badge Class
    • Property + Object Property → badge Property, DOMAIN: Person, RANGE: Organization, property_type=object
    • Property + Datatype Property → badge Property, RANGE: string (from xsd:string), property_type=datatype
    • Property + Annotation Property → badge Property, no domain/range, property_type=annotation
    • Individual → concept_type=individual
    • Term → concept_type=term (recovered from ontos:conceptType annotation)
  • Cleanup: all seven verification concepts were deleted; API now returns 404 for each.

Custom Properties created via the concept editor were always stored as
skos:Concept and rendered as "Concept" in the UI because the backend
ignored concept_type, property_type, domain, and range on write and had
no way to recover them on read. The fix wires those fields end-to-end so
every dialog option (concept, class, property/{object,datatype,annotation},
individual, term) round-trips correctly.

- models/ontology.py: expose property_type/domain/range on
  OntologyConcept, ConceptCreate, and ConceptUpdate (incl. concept_type
  on update for draft retypes).
- controller/semantic_models_manager.py:
  * _resolve_concept_rdf_types / _detect_concept_type centralise the
    mapping between UI types and rdf:type triples (owl:Class,
    owl:ObjectProperty, owl:DatatypeProperty, owl:AnnotationProperty,
    owl:NamedIndividual, skos:Concept + ontos:conceptType for "term").
  * create_concept writes the resolved rdf:type set plus
    rdfs:domain/rdfs:range when supplied.
  * update_concept allows draft retyping and partial domain/range
    updates without clobbering unspecified fields.
  * Read paths (_extract_concept_metadata, get_concept,
    get_concept_details, _compute_all_concepts) surface
    property_type/domain/range from the graph.
  * promote_concept / migrate_concept forward the new fields.
- routes/semantic_models_routes.py: pass property_type/domain/range
  through POST /knowledge/concepts and PATCH
  /knowledge/concepts/{iri}.
- tests/integration/test_knowledge_routes.py: add
  TestKnowledgeConceptTypes covering create+read for all type
  combinations and partial PATCH updates of property fields.

Verified end-to-end via Playwright for all 7 dialog options
(concept, class, property{object,datatype,annotation}, individual, term).
# Conflicts:
#	src/backend/src/routes/semantic_models_routes.py
@larsgeorge-db larsgeorge-db enabled auto-merge June 21, 2026 09:37

@mvkonchits-db mvkonchits-db left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM.

Round-tripping concept_type / property_type / domain / range end-to-end fixes a real round-trip gap — Custom Properties were silently collapsing to skos:Concept on write. The _resolve_concept_rdf_types / _detect_concept_type symmetry is the right shape; centralising the mapping in one place keeps the create/update/promote/migrate paths honest. The ontos:conceptType "term" annotation as a sidecar for the SKOS-collision case is a pragmatic call.

  • All 9 CI checks green (Backend, Frontend, TS, Build, Alembic single-head, Coverage, Title lint).
  • 10-case TestKnowledgeConceptTypes covers create + read for every type combo plus partial PATCH of property fields.
  • Live Playwright walkthrough of all 7 dialog options documented in the test plan, with cleanup verified.

One forward-looking note (non-blocking): #541 is stacked on this branch and will auto-retarget to main after merge — happy to take a look once CI re-runs on the retargeted base.

@larsgeorge-db larsgeorge-db added this pull request to the merge queue Jun 22, 2026
Merged via the queue into main with commit 2eac71b Jun 22, 2026
9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants