__init__.py 6.58 KB
Newer Older
1
from typing import Iterable
2
3
4
import subprocess
import re
import os
5

Fabien Amarger's avatar
Fabien Amarger committed
6
from argparse import ArgumentParser
7
from rdflib import Graph, RDF, RDFS, OWL, XSD, URIRef  # type: ignore
Noé Gaumont's avatar
Noé Gaumont committed
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
from urllib.parse import urlparse
from yams.schema import Schema  # type: ignore
from yams.serialize import serialize_to_python  # type: ignore
from yams.buildobjs import (  # type: ignore
    EntityType,
    RelationDefinition,
    RelationType,
    register_base_types,
)


ETYPE_URI = {}
LITERAL_TYPES_TO_YAMS_TYPES = {
    RDFS.Literal: "String",
    XSD.string: "String",
    XSD.int: "Int",
    XSD.integer: "Int",
    XSD.float: "Float",
}


def fragment_from_uri(uri: str) -> str:
30
31
    if uri == OWL.Thing:
        return "*"
Noé Gaumont's avatar
Noé Gaumont committed
32
33
34
35
36
37
38
39
    url = urlparse(uri)
    # if url.fragment is not None, the fragment is done with `#`
    if url.fragment:
        return url.fragment
    # otherwise split with `/`
    return url.path.split("/")[-1]


40
41
42
43
44
45
46
def generate_yams_domain_from_urirefs(uris: Iterable[URIRef]) -> str:
    fragments = [fragment_from_uri(uri) for uri in uris]
    if not len(fragments):
        return "*"
    return ", ".join(fragments)


Fabien Amarger's avatar
Fabien Amarger committed
47
def owl_model_to_yams(owl_model: Graph, instance_name: str) -> Schema:
Noé Gaumont's avatar
Noé Gaumont committed
48
    # 0. Create a new YAMS Schema
Fabien Amarger's avatar
Fabien Amarger committed
49
    schema = Schema(instance_name)
Noé Gaumont's avatar
Noé Gaumont committed
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
    register_base_types(schema)

    # p = s._entities["Person"] # normalement p = s.entity_schema_for("Person")
    # p.check({'name':1})

    # 1. fetch all classes
    for class_uri, _, _ in owl_model.triples((None, RDF.type, OWL.Class)):
        class_fragment = fragment_from_uri(class_uri)
        schema.add_entity_type(EntityType(class_fragment))
        entity_schema = schema._entities[class_fragment]
        ETYPE_URI[class_fragment] = class_uri
        superior_classes = []
        for _, _, superior_class_uri in owl_model.triples(
            (class_uri, RDFS.subClassOf, None)
        ):
            superior_classes.append(fragment_from_uri(superior_class_uri))
        if superior_classes:
            entity_schema._specialized_type = ", ".join(superior_classes)

    # 2. fetch all datatype properties

    for (
        datatype_property_uri,
        _,
        _,
    ) in owl_model.triples((None, RDF.type, OWL.DatatypeProperty)):
        datatype_property_uri_fragment = fragment_from_uri(datatype_property_uri)
        schema.add_relation_type(RelationType(datatype_property_uri_fragment))
        # take first range, if no range use RDFS.Literal
        _, _, literal_type = next(
            owl_model.triples((datatype_property_uri, RDFS.range, None)),
            (_, _, RDFS.Literal),
        )
        yams_type = LITERAL_TYPES_TO_YAMS_TYPES[literal_type]
84
85
86
87
        yams_domains = generate_yams_domain_from_urirefs(
            domain_uri
            for _, _, domain_uri in owl_model.triples(
                (datatype_property_uri, RDFS.domain, None)
Noé Gaumont's avatar
Noé Gaumont committed
88
            )
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
        )
        schema.add_relation_def(
            RelationDefinition(yams_domains, datatype_property_uri_fragment, yams_type)
        )

    # 3. fetch all object properties

    for (
        object_property_uri,
        _,
        _,
    ) in owl_model.triples((None, RDF.type, OWL.ObjectProperty)):
        object_property_uri_fragment = fragment_from_uri(object_property_uri)
        schema.add_relation_type(RelationType(object_property_uri_fragment))
        domain_fragments = generate_yams_domain_from_urirefs(
            domain_uri
            for _, _, domain_uri in owl_model.triples(
                (object_property_uri, RDFS.domain, None)
            )
        )
        range_fragments = generate_yams_domain_from_urirefs(
            range_uri
            for _, _, range_uri in owl_model.triples(
                (object_property_uri, RDFS.range, None)
            )
        )
        schema.add_relation_def(
            RelationDefinition(
                domain_fragments,
                object_property_uri_fragment,
                range_fragments,
            )
        )
Noé Gaumont's avatar
Noé Gaumont committed
122

123
124
    return schema

125
126
127
128
129
130
131
132
def run_and_print_if_error(command):
    res = subprocess.run(command, shell=True, capture_output=True)
    if res.returncode != 0:
        print(res.stdout.decode())
        print(res.stderr.decode())
        exit(1)
    return res

133
134

def main():
Fabien Amarger's avatar
Fabien Amarger committed
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
    parser = ArgumentParser()
    parser.add_argument(
        "--owl-model",
        "-m",
        default="owl2yams/model.owl",
    )
    parser.add_argument(
        "--instance-name",
        "-n",
        default="owl_instance",
    )
    parser.add_argument(
        "--parse-format",
        "-f",
        choices=["turtle", "xml", "n3", "nquads", "nt", "trix"],
        default="turtle",
    )

    args = parser.parse_args()

155
    owl_model = Graph()
Fabien Amarger's avatar
Fabien Amarger committed
156
    owl_model.parse(args.owl_model, format=args.parse_format)
157

Fabien Amarger's avatar
Fabien Amarger committed
158
    schema = owl_model_to_yams(owl_model, args.instance_name)
159

160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
    cube_name = args.instance_name.replace("_", "-")
    cube_master_folder = f"cubicweb-{cube_name}"

    print(
        f"creating the cube inside the folder {cube_master_folder}/ "
        "(the schema is there)"
    )
    create_cube = (f'cubicweb-ctl newcube {args.instance_name}'
                   ' -s "cube representating {args.owl_model}"')
    run_and_print_if_error(create_cube)

    cube_subfolder = cube_master_folder.replace("-", "_")
    with open(f"{cube_master_folder}/{cube_subfolder}/schema.py", "a") as f:
        f.write(serialize_to_python(schema))

    # should pip install the cube
    pip_install_cube = f"pip install  -e cubicweb-{cube_name}"
    run_and_print_if_error(pip_install_cube)

    print(
        f"creating the instance {args.instance_name}. "
        f"The parameters are in ~/etc/cubicweb.d/{args.instance_name}/"
    )
    create_instance = ("CW_DB_DRIVER=sqlite cubicweb-ctl create "
                       f"{args.instance_name} {args.instance_name} -a --no-db-create")
    run_and_print_if_error(create_instance)

    db_init = ("CW_DB_DRIVER=sqlite cubicweb-ctl db-create "
               f"{args.instance_name} -a --drop=y")
    run_and_print_if_error(db_init)

    source_path = os.path.expanduser(f'~/etc/cubicweb.d/{args.instance_name}/sources')
    # The source file has to be modified to include sqlite
    driver_regexp = re.compile('db-driver.*')
194
    admin_pass = re.compile('password=(.*)')
195
196
197
    with open(source_path, 'r') as f:
        content = f.read()
        replaced_content = driver_regexp.sub('db-driver=sqlite', content)
198
        admin_pwd = next(admin_pass.finditer(content)).group(1)
199
200
201
202
203
204
205
206

    with open(source_path, 'w') as f:
        f.write(replaced_content)

    print(
        "Congratulation ! You can run your instance with : "
        f"`cubicweb-ctl pyramid -D -l info {args.instance_name}`"
    )
207
    print(f"(admin password: {admin_pwd} (from {source_path})")