def test_as_dict_includes_expected_fields(self):
        schema = Schema(
            "term_name",
            properties={
                'mandatory': Property('mandatory', 'desc', required=True),
                'whatever_man': Property('whatever_man', 'desc', required=False),
            },
            skos_preflabel="preferred",
            exact_synonym=["splynonym"])

        expected_fields = {
            '$id': 'term_name',
            'type': 'object',
            'additionalProperties': True,
            'properties': {
                'mandatory': {
                    'description': 'desc',
                    'type': 'string'
                },
                'whatever_man': {
                    'description': 'desc',
                    'type': 'string'
                },
            },
            'required': ['mandatory']
        }
        schema_dict = schema.as_dict()

        for expected_field, expected_value in expected_fields.items():
            self.assertIn(expected_field, schema_dict)
            self.assertEqual(schema_dict[expected_field], expected_value)
 def test_type_info_wraps_in_array_if_array(self):
     prop = Property("foo", "desc", min_cardinality=0)
     prop.add_type(RefType("reef"))
     self.assertEqual(prop.type_info(), {
         'type': 'array',
         'items': {'$ref': 'reef'},
     })
    def test_required_property_names_includes_only_required_properties(self):
        schema = Schema(
            "term_name",
            properties={
                'mandatory': Property('mandatory', 'desc', required=True),
                'whatever_man': Property('whatever_man', 'desc', required=False),
            }
        )

        self.assertEqual(schema.required_property_names(), ['mandatory'])
 def test_base_type_info_returns_oneof_for_multiple_values(self):
     prop = Property("foo", "desc")
     prop.add_type(RefType("ref1"))
     prop.add_type(RefType("ref2"))
     self.assertEqual(prop.base_type_info(), {
         'oneOf': [
             {'$ref': 'ref1'},
             {'$ref': 'ref2'},
         ]
     })
    def test_optional_fields_includes_only_defined_fields(self):
        prop = Property(
            "foo",
            "desc",
            comment="hey",
            skos_preflabel="jimbo"
        )

        optional_fields = prop.optional_fields()
        self.assertIn("comment", optional_fields)
        self.assertIn("skos:prefLabel", optional_fields)
        self.assertNotIn("title", optional_fields)
        self.assertNotIn("oneOf", optional_fields)
 def test_as_dict_includes_expected_fields(self):
     prop = Property("foo", "desc", max_cardinality=3)
     prop.add_enum_value("steve")
     prop.add_enum_value("stove")
     expected_fields = {
         'description': 'desc',
         'oneOf': ['steve', 'stove'],
         'type': 'array',
         'items': {'type': 'string'},
         'maxItems': 3,
     }
     dictified = prop.as_dict()
     for expected_field, expected_value in expected_fields.items():
         self.assertIn(expected_field, dictified)
         self.assertEqual(dictified[expected_field], expected_value)
 def test_add_type_allows_further_types_if_not_restrictive(self):
     prop = Property("foo", "desc")
     prop.add_type(PrimitiveType("string"), restrictive=False)
     # we just run this to see if it raises an exception, so there's no assertions in the test
     prop.add_type(RefType("argbl"))
 def test_base_type_info_returns_type_for_single_value(self):
     prop = Property("foo", "desc")
     prop.add_type(RefType("reef"))
     self.assertEqual(prop.base_type_info(), {'$ref': 'reef'})
 def test_is_array_type_true_if_either_cardinality(self):
     self.assertTrue(Property("foo", "desc", min_cardinality=1).is_array_type())
     self.assertTrue(Property("foo", "desc", max_cardinality=3).is_array_type())
 def test_base_type_info_defaults_to_string(self):
     prop = Property("foo", "desc")
     self.assertEqual(prop.base_type_info(), {'type': 'string'})
 def test_add_enum_value_mutex_with_type_constraints(self):
     prop = Property("foo", "desc")
     prop.add_enum_value("steve")
     with self.assertRaises(ValueError):
         prop.add_type(PrimitiveType("string"))
 def test_add_enum_value_adds_enum_value(self):
     prop = Property("foo", "desc")
     prop.add_enum_value("steve")
     self.assertEqual(prop.allowed_values, ["steve"])
 def ensure_property_initialized(self, target_property: rdflib.term.Identifier) -> None:
     property_name = self.graph_manager.namespaced_name_for_node(target_property)
     if property_name not in self.schema.properties:
         self.schema.properties[property_name] = Property(property_name, str(target_property))
 def test_type_info_generates_flat_type_info_if_not_array(self):
     prop = Property("foo", "desc")
     prop.add_type(RefType("reef"))
     self.assertEqual(prop.type_info(), {'$ref': 'reef'})
 def test_add_type_blocks_further_types_if_restrictive(self):
     prop = Property("foo", "desc")
     prop.add_type(PrimitiveType("string"), restrictive=True)
     with self.assertRaises(ValueError):
         prop.add_type(RefType("argbl"))
 def test_add_type_adds_type(self):
     prop = Property("foo", "desc")
     self.assertEqual(len(prop.allowed_types), 0)
     prop.add_type(PrimitiveType("string"))
     self.assertEqual(prop.allowed_types, [PrimitiveType("string")])
 def test_as_dict_always_includes_description_even_if_blank(self):
     prop = Property("foo", "", max_cardinality=3)
     self.assertIn('description', prop.as_dict())
 def test_is_array_type_false_if_neither_cardinality(self):
     self.assertFalse(Property("foo", "desc").is_array_type())
 def test_add_type_ignores_duplicates(self):
     prop = Property("foo", "desc")
     prop.add_type(PrimitiveType("string"))
     prop.add_type(PrimitiveType("string"))
     self.assertEqual(prop.allowed_types, [PrimitiveType("string")])
from data_model_exporter.rdf_graph_manager import RdfGraphManager
from data_model_exporter.schema import Schema
from data_model_exporter.property import Property
from data_model_exporter.property_types import RefType
from data_model_exporter.typing import JsonSchema

# we manually define this because the preinstalled PROV namespace in rdflib doesn't include some terms we use,
# e.g. 'definition'
PROV = Namespace("http://www.w3.org/ns/prov#")

OBO_IN_OWL = Namespace("http://www.geneontology.org/formats/oboInOwl#")

# properties that should appear in the json schema even if they aren't present in the ttl file
UNIVERSAL_PROPERTIES = {
    'rdfs:label': Property('rdfs:label', "A human-readable name for the entity."),
    'id': Property('id', "UUID for this entity."),
    'describedBy': Property('describedBy', "The URL reference to the JSON Schema that defines this object."),
}

SKIPPABLE_DOMAIN_PREDICATES = [
    RDF.type,     # this just indicates something is a property, which we already know
    RDFS.domain,  # no point annotating domain, since it's how we get a list of props for a class to begin with
]

SKIPPABLE_RESTRICTION_PREDICATES = [
    RDF.type,        # indicates that it's a restriction property. we check this explicitly before scanning.
    OWL.onProperty,  # we grab this beforehand as well to see what property the restrictions apply to.
]