fix(ontology): persist concept_type and property metadata in RDF#540
Merged
Conversation
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).
6 tasks
# Conflicts: # src/backend/src/routes/semantic_models_routes.py
mvkonchits-db
approved these changes
Jun 22, 2026
mvkonchits-db
left a comment
Contributor
There was a problem hiding this comment.
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
TestKnowledgeConceptTypescovers 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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Custom Properties created via the concept editor were always rendered as plain Concepts in the UI. The backend was hardcoding
skos:Concepton 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:Conceptclass→owl:Class+rdfs:Classproperty+object→owl:ObjectProperty+rdf:Property(+ optionalrdfs:domain/rdfs:range)property+datatype→owl:DatatypeProperty+rdf:Propertyproperty+annotation→owl:AnnotationProperty+rdf:Propertyindividual→owl:NamedIndividualterm→skos:Concept+ customontos:conceptType "term"annotation so the user-facing type can be reconstructedChanges
models/ontology.py— exposeproperty_type/domain/rangeonOntologyConcept,ConceptCreate,ConceptUpdate, plusconcept_typeon update for draft retypes.controller/semantic_models_manager.py_resolve_concept_rdf_typesand_detect_concept_typehelpers centralise the mapping in both directions.create_conceptnow emits the resolvedrdf:typeset and writesrdfs:domain/rdfs:rangefor properties.update_conceptsupports draft retyping and partialdomain/rangeupdates without clobbering unspecified fields._extract_concept_metadata,get_concept,get_concept_details,_compute_all_concepts) surfaceproperty_type/domain/range.promote_concept/migrate_conceptforward the new fields.routes/semantic_models_routes.py— pass the new fields through POST/knowledge/conceptsand PATCH/knowledge/concepts/{iri}.tests/integration/test_knowledge_routes.py— newTestKnowledgeConceptTypesclass with 10 cases covering create + read for every type combination and partial PATCH of property fields.Test plan
TestKnowledgeConceptTypes, 10 cases) pass.ConceptClassProperty,DOMAIN: Person,RANGE: Organization,property_type=objectProperty,RANGE: string(fromxsd:string),property_type=datatypeProperty, no domain/range,property_type=annotationconcept_type=individualconcept_type=term(recovered fromontos:conceptTypeannotation)