otlmow-template 0.11rc4__tar.gz → 1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.2
1
+ Metadata-Version: 2.4
2
2
  Name: otlmow_template
3
- Version: 0.11rc4
3
+ Version: 1.0
4
4
  Author-email: David Vlaminck <david.vlaminck@mow.vlaanderen.be>, Jasper Berton <jasperberton1@telenet.be>
5
5
  License: GNU GENERAL PUBLIC LICENSE
6
6
  Version 3, 29 June 2007
@@ -697,9 +697,9 @@ Classifier: Topic :: Software Development :: Quality Assurance
697
697
  Requires-Python: >=3.9
698
698
  Description-Content-Type: text/markdown
699
699
  License-File: LICENSE
700
- Requires-Dist: otlmow-converter>=1.7
700
+ Requires-Dist: otlmow-converter>=1.10
701
701
  Requires-Dist: otlmow-modelbuilder>=0.25
702
- Requires-Dist: universalasync>=0.4.0
702
+ Dynamic: license-file
703
703
 
704
704
  # OTLMOW-Template
705
705
  [![PyPI](https://img.shields.io/pypi/v/otlmow-template?label=latest%20release)](https://pypi.org/project/otlmow-template/)
@@ -0,0 +1,612 @@
1
+ import contextlib
2
+ import csv
3
+ import logging
4
+ import os
5
+ import shutil
6
+ import tempfile
7
+ from asyncio import sleep
8
+ from collections import defaultdict
9
+ from pathlib import Path
10
+
11
+ from openpyxl.reader.excel import load_workbook
12
+ from openpyxl.styles import PatternFill, Alignment
13
+ from openpyxl.utils import get_column_letter
14
+ from openpyxl.workbook import Workbook
15
+ from openpyxl.worksheet.datavalidation import DataValidation
16
+ from openpyxl.worksheet.dimensions import DimensionHolder, ColumnDimension
17
+ from openpyxl.worksheet.worksheet import Worksheet
18
+ from otlmow_converter.DotnotationHelper import DotnotationHelper
19
+ from otlmow_converter.OtlmowConverter import OtlmowConverter
20
+ from otlmow_model.OtlmowModel.BaseClasses.BooleanField import BooleanField
21
+ from otlmow_model.OtlmowModel.BaseClasses.KeuzelijstField import KeuzelijstField
22
+ from otlmow_model.OtlmowModel.BaseClasses.OTLObject import dynamic_create_instance_from_uri, OTLObject, \
23
+ get_attribute_by_name
24
+ from otlmow_model.OtlmowModel.Helpers.generated_lists import get_hardcoded_relation_dict
25
+ from otlmow_modelbuilder.HelperFunctions import get_ns_and_name_from_uri
26
+ from otlmow_modelbuilder.OSLOCollector import OSLOCollector
27
+ from otlmow_modelbuilder.SQLDataClasses.OSLOClass import OSLOClass
28
+
29
+ ROOT_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
30
+
31
+ enumeration_validation_rules = {
32
+ "valid_uri_and_types": {},
33
+ "valid_regexes": [
34
+ "^https://wegenenverkeer.data.vlaanderen.be/ns/.+"]
35
+ }
36
+
37
+ short_to_long_ns = {
38
+ 'ond': 'https://wegenenverkeer.data.vlaanderen.be/ns/onderdeel#',
39
+ 'onderdeel': 'https://wegenenverkeer.data.vlaanderen.be/ns/onderdeel#',
40
+ 'ins': 'https://wegenenverkeer.data.vlaanderen.be/ns/installatie#',
41
+ 'installatie': 'https://wegenenverkeer.data.vlaanderen.be/ns/installatie#',
42
+ 'imp': 'https://wegenenverkeer.data.vlaanderen.be/ns/implementatieelement#',
43
+ 'implementatieelement': 'https://wegenenverkeer.data.vlaanderen.be/ns/implementatieelement#',
44
+ 'proefenmeting': 'https://wegenenverkeer.data.vlaanderen.be/ns/proefenmeting#',
45
+ 'pro': 'https://wegenenverkeer.data.vlaanderen.be/ns/proefenmeting#',
46
+ 'lev': 'https://wegenenverkeer.data.vlaanderen.be/ns/levenscyclus#',
47
+ 'levenscyclus': 'https://wegenenverkeer.data.vlaanderen.be/ns/levenscyclus#',
48
+
49
+ }
50
+
51
+
52
+ class SubsetTemplateCreator:
53
+ @classmethod
54
+ def _load_collector_from_subset_path(cls, subset_path: Path) -> OSLOCollector:
55
+ collector = OSLOCollector(subset_path)
56
+ collector.collect_all(include_abstract=True)
57
+ return collector
58
+
59
+ @classmethod
60
+ async def generate_template_from_subset_async(
61
+ cls,
62
+ subset_path: Path,
63
+ template_file_path: Path,
64
+ ignore_relations: bool = True,
65
+ filter_attributes_by_subset: bool = True,
66
+ class_uris_filter: [str] = None,
67
+ dummy_data_rows: int = 1,
68
+ add_geometry: bool = True,
69
+ add_attribute_info: bool = False,
70
+ add_deprecated: bool = False,
71
+ generate_choice_list: bool = True,
72
+ split_per_type: bool = True,
73
+ model_directory: Path = None):
74
+ """
75
+ Generate a template from a subset file, async version.
76
+ Await this function!
77
+
78
+ :param subset_path: Path to the subset file
79
+ :param template_file_path: Path to where the template file should be created
80
+ :param ignore_relations: Whether to ignore relations when creating the template, defaults to True
81
+ :param filter_attributes_by_subset: Whether to filter by the attributes in the subset, defaults to True
82
+ :param class_uris_filter: List of class URIs to filter by. If not None, only classes with these URIs will be included, defaults to None
83
+ :param dummy_data_rows: Amount of dummy data rows to add to the template, defaults to 1
84
+ :param add_geometry: Whether to include the geometry attribute in the template, defaults to True
85
+ :param add_attribute_info: Whether to add attribute information to the template (colored grey in Excel), defaults to False
86
+ :param add_deprecated: Whether to add a deprecated row to the template (colored red in Excel), defaults to False
87
+ :param generate_choice_list: Whether to generate a choice list in the template (only for Excel), defaults to True
88
+ :param split_per_type: Whether to split the template into a file per type (only for CSV), defaults to True
89
+ :param model_directory: Path to the model directory, defaults to None
90
+
91
+ :return: None
92
+ """
93
+ # generate objects to write to file
94
+ objects = await cls.generate_objects_for_template_async(
95
+ subset_path=subset_path, ignore_relations=ignore_relations, class_uris_filter=class_uris_filter,
96
+ add_geometry=add_geometry, filter_attributes_by_subset=filter_attributes_by_subset,
97
+ dummy_data_rows=dummy_data_rows, model_directory=model_directory)
98
+
99
+ # write the file
100
+ await OtlmowConverter.from_objects_to_file_async(
101
+ file_path=template_file_path, sequence_of_objects=objects, split_per_type=split_per_type)
102
+
103
+ # alter the file if needed
104
+ extension = template_file_path.suffix.lower()
105
+ if extension == '.xlsx':
106
+ await cls.alter_excel_template_async(
107
+ generate_choice_list=generate_choice_list, file_path=template_file_path, dummy_data_rows=dummy_data_rows,
108
+ instances=objects, add_deprecated=add_deprecated, add_attribute_info=add_attribute_info)
109
+
110
+ elif extension == '.csv':
111
+ await cls.alter_csv_template_async(
112
+ split_per_type=split_per_type, file_path=template_file_path, dummy_data_rows=dummy_data_rows,
113
+ instances=objects, add_deprecated=add_deprecated, add_attribute_info=add_attribute_info)
114
+
115
+ @classmethod
116
+ def generate_template_from_subset(
117
+ cls,
118
+ subset_path: Path,
119
+ template_file_path: Path,
120
+ ignore_relations: bool = True,
121
+ filter_attributes_by_subset: bool = True,
122
+ class_uris_filter: [str] = None,
123
+ dummy_data_rows: int = 1,
124
+ add_geometry: bool = True,
125
+ add_attribute_info: bool = False,
126
+ add_deprecated: bool = False,
127
+ generate_choice_list: bool = True,
128
+ split_per_type: bool = True,
129
+ model_directory: Path = None):
130
+ """
131
+ Generate a template from a subset file.
132
+
133
+ :param subset_path: Path to the subset file
134
+ :param template_file_path: Path to where the template file should be created
135
+ :param ignore_relations: Whether to ignore relations when creating the template, defaults to True
136
+ :param filter_attributes_by_subset: Whether to filter by the attributes in the subset, defaults to True
137
+ :param class_uris_filter: List of class URIs to filter by. If not None, only classes with these URIs will be included, defaults to None
138
+ :param dummy_data_rows: Amount of dummy data rows to add to the template, defaults to 1
139
+ :param add_geometry: Whether to include the geometry attribute in the template, defaults to True
140
+ :param add_attribute_info: Whether to add attribute information to the template (colored grey in Excel), defaults to False
141
+ :param add_deprecated: Whether to tag deprecated attributes in the template, defaults to False
142
+ :param generate_choice_list: Whether to generate a choice list in the template (only for Excel), defaults to True
143
+ :param split_per_type: Whether to split the template into a file per type (only for CSV), defaults to True
144
+ :param model_directory: Path to the model directory, defaults to None
145
+
146
+ :return: None
147
+ """
148
+ # generate objects to write to file
149
+ objects = cls.generate_objects_for_template(
150
+ subset_path=subset_path, ignore_relations=ignore_relations, class_uris_filter=class_uris_filter,
151
+ add_geometry=add_geometry, filter_attributes_by_subset=filter_attributes_by_subset,
152
+ dummy_data_rows=dummy_data_rows, model_directory=model_directory)
153
+
154
+ # write the file
155
+ OtlmowConverter.from_objects_to_file(
156
+ file_path=template_file_path, sequence_of_objects=objects, split_per_type=split_per_type,
157
+ model_directory=model_directory)
158
+
159
+ # alter the file if needed
160
+ extension = template_file_path.suffix.lower()
161
+ if extension == '.xlsx':
162
+ cls.alter_excel_template(
163
+ generate_choice_list=generate_choice_list, file_path=template_file_path, dummy_data_rows=dummy_data_rows,
164
+ instances=objects, add_deprecated=add_deprecated, add_attribute_info=add_attribute_info)
165
+ elif extension == '.csv':
166
+ cls.alter_csv_template(
167
+ split_per_type=split_per_type, file_path=template_file_path, dummy_data_rows=dummy_data_rows,
168
+ instances=objects, add_deprecated=add_deprecated, add_attribute_info=add_attribute_info)
169
+
170
+ @classmethod
171
+ def generate_objects_for_template(
172
+ cls, subset_path: Path, class_uris_filter: [str], filter_attributes_by_subset: bool,
173
+ dummy_data_rows: int, add_geometry: bool, ignore_relations: bool, model_directory: Path = None
174
+ ) -> [OTLObject]:
175
+ """
176
+ This method is used to generate objects for the template. It will generate objects based on the subset file
177
+ """
178
+ collector = cls._load_collector_from_subset_path(subset_path=subset_path)
179
+ filtered_class_list = cls.filters_classes_by_subset(collector=collector, class_uris_filter=class_uris_filter)
180
+ relation_dict = get_hardcoded_relation_dict(model_directory=model_directory)
181
+
182
+ amount_objects_to_create = max(1, dummy_data_rows)
183
+ otl_objects = []
184
+
185
+ while True:
186
+ for oslo_class in [cl for cl in filtered_class_list if cl.abstract == 0]:
187
+ if ignore_relations and oslo_class.objectUri in relation_dict:
188
+ continue
189
+
190
+ for _ in range(amount_objects_to_create):
191
+ otl_object = cls.generate_object_from_oslo_class(
192
+ oslo_class=oslo_class, add_geometry=add_geometry, collector=collector,
193
+ filter_attributes_by_subset=filter_attributes_by_subset, model_directory=model_directory)
194
+ if otl_object is not None:
195
+ otl_objects.append(otl_object)
196
+ created = len(otl_objects)
197
+ unique_ids = len({obj.assetId.identificator if hasattr(obj, 'assetId') else obj.agentId.identificator
198
+ for obj in otl_objects})
199
+ if created == unique_ids:
200
+ break
201
+ otl_objects = []
202
+
203
+ return otl_objects
204
+
205
+ @classmethod
206
+ async def generate_objects_for_template_async(
207
+ cls, subset_path: Path, class_uris_filter: [str], filter_attributes_by_subset: bool,
208
+ dummy_data_rows: int, add_geometry: bool, ignore_relations: bool, model_directory: Path = None
209
+ ) -> [OTLObject]:
210
+ """
211
+ This method is used to generate objects for the template. It will generate objects based on the subset file
212
+ """
213
+ await sleep(0)
214
+ collector = cls._load_collector_from_subset_path(subset_path=subset_path)
215
+ await sleep(0)
216
+ filtered_class_list = cls.filters_classes_by_subset(collector=collector, class_uris_filter=class_uris_filter)
217
+ await sleep(0)
218
+ relation_dict = get_hardcoded_relation_dict(model_directory=model_directory)
219
+
220
+ amount_objects_to_create = max(1, dummy_data_rows)
221
+ otl_objects = []
222
+
223
+ for oslo_class in [cl for cl in filtered_class_list if cl.abstract == 0]:
224
+ await sleep(0)
225
+ if ignore_relations and oslo_class.objectUri in relation_dict:
226
+ continue
227
+
228
+ for _ in range(amount_objects_to_create):
229
+ otl_object = cls.generate_object_from_oslo_class(
230
+ oslo_class=oslo_class, add_geometry=add_geometry, collector=collector,
231
+ filter_attributes_by_subset=filter_attributes_by_subset, model_directory=model_directory)
232
+ await sleep(0)
233
+ if otl_object is not None:
234
+ otl_objects.append(otl_object)
235
+
236
+ return otl_objects
237
+
238
+ @classmethod
239
+ def generate_object_from_oslo_class(
240
+ cls, oslo_class: OSLOClass, add_geometry: bool,
241
+ filter_attributes_by_subset: bool, collector: OSLOCollector, model_directory: Path = None) -> [OTLObject]:
242
+ """
243
+ Generate an object from a given OSLO class
244
+ """
245
+ instance = dynamic_create_instance_from_uri(oslo_class.objectUri, model_directory=model_directory)
246
+ if instance is None:
247
+ return
248
+
249
+ if filter_attributes_by_subset:
250
+ for attribute_object in collector.find_attributes_by_class(oslo_class):
251
+ attr = get_attribute_by_name(instance, attribute_object.name)
252
+ if attr is not None:
253
+ attr.fill_with_dummy_data()
254
+ else:
255
+ logging.warning(f'Attribute {attribute_object.name} not found in class {oslo_class.objectUri}')
256
+ else:
257
+ for attr in instance:
258
+ if attr.naam != 'geometry':
259
+ attr.fill_with_dummy_data()
260
+ with contextlib.suppress(AttributeError):
261
+ if add_geometry:
262
+ geo_attr = get_attribute_by_name(instance, 'geometry')
263
+ if geo_attr is not None:
264
+ geo_attr.fill_with_dummy_data()
265
+
266
+ asset_versie = get_attribute_by_name(instance, 'assetVersie')
267
+ if asset_versie is not None:
268
+ asset_versie.set_waarde(None)
269
+
270
+ DotnotationHelper.clear_list_of_list_attributes(instance)
271
+
272
+ return instance
273
+
274
+ @classmethod
275
+ def alter_excel_template(cls, instances: list, file_path: Path, add_attribute_info: bool,
276
+ generate_choice_list: bool, dummy_data_rows: int, add_deprecated: bool):
277
+ wb = load_workbook(file_path)
278
+ wb.create_sheet('Keuzelijsten')
279
+
280
+ choice_list_dict = {}
281
+ for sheet in wb:
282
+ if sheet.title == 'Keuzelijsten':
283
+ break
284
+
285
+ cls.alter_excel_sheet(add_attribute_info=add_attribute_info, choice_list_dict=choice_list_dict,
286
+ generate_choice_list=generate_choice_list, dummy_data_rows=dummy_data_rows,
287
+ instances=instances, sheet=sheet, add_deprecated=add_deprecated, workbook=wb)
288
+
289
+ wb.save(file_path)
290
+ wb.close()
291
+
292
+ @classmethod
293
+ def fill_class_dict(cls, instances: list) -> dict:
294
+ class_dict = defaultdict(list)
295
+ for instance in instances:
296
+ class_dict[instance.typeURI].append(instance)
297
+ return class_dict
298
+
299
+
300
+ @classmethod
301
+ def alter_csv_template(cls, instances: list, file_path: Path, add_attribute_info: bool,
302
+ split_per_type: bool, dummy_data_rows: int, add_deprecated: bool):
303
+ classes_dict = cls.fill_class_dict(instances)
304
+ if split_per_type:
305
+ for type_uri, typed_instances in classes_dict.items():
306
+ ns, name = get_ns_and_name_from_uri(type_uri)
307
+ class_file_path = file_path.parent / f'{file_path.stem}_{ns}_{name}.csv'
308
+ cls.alter_csv_file(add_attribute_info=add_attribute_info, add_deprecated=add_deprecated,
309
+ dummy_data_rows=dummy_data_rows, instances=typed_instances, file_path=class_file_path)
310
+ else:
311
+ cls.alter_csv_file(add_attribute_info=add_attribute_info, add_deprecated=add_deprecated,
312
+ dummy_data_rows=dummy_data_rows, instances=instances, file_path=file_path)
313
+
314
+ @classmethod
315
+ async def alter_csv_template_async(cls, instances: list, file_path: Path, add_deprecated: bool,
316
+ add_attribute_info: bool, split_per_type: bool, dummy_data_rows: int):
317
+ classes_dict = cls.fill_class_dict(instances)
318
+ if split_per_type:
319
+ for type_uri, typed_instances in classes_dict.items():
320
+ await sleep(0)
321
+ ns, name = get_ns_and_name_from_uri(type_uri)
322
+ class_file_path = file_path.parent / f'{file_path.stem}_{ns}_{name}.csv'
323
+ cls.alter_csv_file(add_attribute_info=add_attribute_info,
324
+ dummy_data_rows=dummy_data_rows, instances=typed_instances, add_deprecated=add_deprecated,
325
+ file_path=class_file_path)
326
+ else:
327
+ await sleep(0)
328
+ cls.alter_csv_file(add_attribute_info=add_attribute_info,
329
+ dummy_data_rows=dummy_data_rows, instances=instances, add_deprecated=add_deprecated,
330
+ file_path=file_path)
331
+
332
+ @classmethod
333
+ def alter_csv_file(cls, add_attribute_info: bool, instances: [OTLObject], add_deprecated: bool, file_path: Path,
334
+ dummy_data_rows: int):
335
+ collected_attribute_info_row = []
336
+ deprecated_attributes_row = []
337
+ instance = instances[0]
338
+ quote_char = '"'
339
+
340
+ with open(file_path, encoding='utf-8') as file:
341
+ csv_reader = csv.reader(file, delimiter=';', quotechar=quote_char)
342
+ header_row = next(csv_reader)
343
+ csv_data = list(csv_reader)
344
+
345
+ for index, header in enumerate(header_row):
346
+ if header is None or header == '':
347
+ continue
348
+
349
+ if header == 'typeURI':
350
+ if add_attribute_info:
351
+ collected_attribute_info_row.append(
352
+ 'De URI van het object volgens https://www.w3.org/2001/XMLSchema#anyURI .')
353
+ if add_deprecated:
354
+ deprecated_attributes_row.append('')
355
+ continue
356
+
357
+ attribute = DotnotationHelper.get_attribute_by_dotnotation(instance, header)
358
+
359
+ if add_attribute_info:
360
+ collected_attribute_info_row.append(attribute.definition)
361
+
362
+ if add_deprecated:
363
+ deprecated_attributes_row.append('DEPRECATED' if attribute.deprecated_version else '')
364
+
365
+ with open(file_path, 'w') as file:
366
+ csv_writer = csv.writer(file, delimiter=';', quotechar=quote_char, quoting=csv.QUOTE_MINIMAL)
367
+ if add_attribute_info:
368
+ csv_writer.writerow(collected_attribute_info_row)
369
+ if add_deprecated and any(deprecated_attributes_row):
370
+ csv_writer.writerow(deprecated_attributes_row)
371
+ csv_writer.writerow(header_row)
372
+ if dummy_data_rows != 0:
373
+ for line in csv_data:
374
+ csv_writer.writerow(line)
375
+
376
+ @classmethod
377
+ async def alter_excel_template_async(cls, instances: list, file_path: Path, add_attribute_info: bool,
378
+ generate_choice_list: bool, dummy_data_rows: int, add_deprecated: bool):
379
+ wb = load_workbook(file_path)
380
+ wb.create_sheet('Keuzelijsten')
381
+
382
+ choice_list_dict = {}
383
+ for sheet in wb:
384
+ if sheet.title == 'Keuzelijsten':
385
+ break
386
+
387
+ cls.alter_excel_sheet(add_attribute_info=add_attribute_info, choice_list_dict=choice_list_dict,
388
+ generate_choice_list=generate_choice_list, dummy_data_rows=dummy_data_rows,
389
+ instances=instances, sheet=sheet, add_deprecated=add_deprecated, workbook=wb)
390
+ await sleep(0)
391
+
392
+ wb.save(file_path)
393
+ wb.close()
394
+
395
+ @classmethod
396
+ def alter_excel_sheet(cls, add_attribute_info: bool, choice_list_dict: dict, generate_choice_list: bool,
397
+ instances: [OTLObject], sheet: Worksheet, add_deprecated: bool, workbook: Workbook,
398
+ dummy_data_rows: int):
399
+ type_uri = cls.get_uri_from_sheet_name(sheet.title)
400
+ instance = next(x for x in instances if x.typeURI == type_uri)
401
+
402
+ boolean_validation = DataValidation(type="list", formula1='"TRUE,FALSE,"', allow_blank=True)
403
+ sheet.add_data_validation(boolean_validation)
404
+ collected_attribute_info = []
405
+ deprecated_attributes_row = []
406
+ header_row = next(sheet.iter_rows(min_row=1, max_row=1))
407
+ for index, header_cell in enumerate(header_row):
408
+ header = header_cell.value
409
+ if header is None or header == '':
410
+ continue
411
+
412
+ if header == 'typeURI':
413
+ data_validation = DataValidation(type="list", formula1=f'"{type_uri}"', allow_blank=True)
414
+ sheet.add_data_validation(data_validation)
415
+ data_validation.add(f'{header_cell.column_letter}2:{header_cell.column_letter}1000')
416
+ if add_attribute_info:
417
+ collected_attribute_info.append('De URI van het object volgens https://www.w3.org/2001/XMLSchema#anyURI .')
418
+ if add_deprecated:
419
+ deprecated_attributes_row.append('')
420
+ continue
421
+
422
+ if type_uri == 'http://purl.org/dc/terms/Agent' and header.startswith('assetId.'):
423
+ continue
424
+
425
+ attribute = DotnotationHelper.get_attribute_by_dotnotation(instance, header)
426
+
427
+ if add_attribute_info:
428
+ collected_attribute_info.append(attribute.definition)
429
+
430
+ if add_deprecated:
431
+ deprecated_attributes_row.append('DEPRECATED' if attribute.deprecated_version else '')
432
+
433
+ if generate_choice_list:
434
+ if issubclass(attribute.field, BooleanField):
435
+ boolean_validation.add(f'{header_cell.column_letter}{2}:{header_cell.column_letter}1000')
436
+ continue
437
+
438
+ if issubclass(attribute.field, KeuzelijstField):
439
+ cls.generate_choice_list_in_excel(
440
+ attribute=attribute, choice_list_dict=choice_list_dict, column=header_cell.column,
441
+ row_nr=1, sheet=sheet, workbook=workbook)
442
+
443
+ if dummy_data_rows == 0:
444
+ sheet.delete_rows(idx=2)
445
+
446
+ if add_deprecated and any(deprecated_attributes_row):
447
+ cls.add_deprecated_row_to_sheet(deprecated_attributes_row, sheet)
448
+
449
+ if add_attribute_info:
450
+ cls.add_attribute_info_to_sheet(collected_attribute_info, sheet)
451
+
452
+ cls.set_fixed_column_width(sheet=sheet, width=25)
453
+
454
+ @classmethod
455
+ def add_deprecated_row_to_sheet(cls, deprecated_attributes_row, sheet):
456
+ sheet.insert_rows(idx=1)
457
+ for index, depr_info in enumerate(deprecated_attributes_row, start=1):
458
+ if depr_info != '':
459
+ cell = sheet.cell(row=1, column=index)
460
+ cell.value = depr_info
461
+ cell.fill = PatternFill(start_color="FF7276", end_color="FF7276", fill_type="solid")
462
+
463
+ @classmethod
464
+ def add_attribute_info_to_sheet(cls, collected_attribute_info, sheet):
465
+ sheet.insert_rows(idx=1)
466
+ for index, attr_info in enumerate(collected_attribute_info, start=1):
467
+ cell = sheet.cell(row=1, column=index)
468
+ cell.value = attr_info
469
+ cell.alignment = Alignment(wrapText=True, vertical='top')
470
+ cell.fill = PatternFill(start_color="808080", end_color="808080", fill_type="solid")
471
+
472
+ @classmethod
473
+ def generate_choice_list_in_excel(cls, attribute, choice_list_dict, column, row_nr, sheet: Worksheet,
474
+ workbook: Workbook):
475
+ choice_list_values = [cv for cv in attribute.field.options.values()
476
+ if cv.status != 'verwijderd']
477
+ if attribute.field.naam not in choice_list_dict:
478
+ cls.add_choice_list_to_sheet(workbook=workbook, name=attribute.field.naam,
479
+ options=choice_list_values, choice_list_dict=choice_list_dict)
480
+ column_in_choice_sheet = choice_list_dict[attribute.field.naam]
481
+ start_range = f"${column_in_choice_sheet}$2"
482
+ end_range = f"${column_in_choice_sheet}${len(choice_list_values) + 1}"
483
+ data_val = DataValidation(type="list", formula1=f"Keuzelijsten!{start_range}:{end_range}",
484
+ allowBlank=True)
485
+ sheet.add_data_validation(data_val)
486
+ data_val.add(f'{get_column_letter(column)}{row_nr + 1}:'
487
+ f'{get_column_letter(column)}1000')
488
+
489
+
490
+ @classmethod
491
+ def determine_multiplicity_csv(cls, template_file_path: Path, subset_path: Path,
492
+ instances: list, temporary_path: Path, **kwargs):
493
+ pass
494
+
495
+
496
+ @classmethod
497
+ def filters_classes_by_subset(cls, collector: OSLOCollector,
498
+ class_uris_filter: [str] = None) -> list[OSLOClass]:
499
+ if class_uris_filter is None:
500
+ return collector.classes
501
+ return [x for x in collector.classes if x.objectUri in class_uris_filter]
502
+
503
+ @classmethod
504
+ def add_type_uri_choice_list_in_excel(cls, sheet, instances, add_attribute_info: bool):
505
+ starting_row = '3' if add_attribute_info else '2'
506
+ if sheet.title == 'Keuzelijsten':
507
+ return
508
+ type_uri_found = False
509
+ for row in sheet.iter_rows(min_row=1, max_row=1):
510
+ for cell in row:
511
+ if cell.value == 'typeURI':
512
+ type_uri_found = True
513
+ break
514
+ if type_uri_found:
515
+ break
516
+ if not type_uri_found:
517
+ return
518
+
519
+ sheet_name = sheet.title
520
+ type_uri = ''
521
+ if sheet_name.startswith('http'):
522
+ type_uri = sheet_name
523
+ else:
524
+ split_name = sheet_name.split("#")
525
+ subclass_name = split_name[1]
526
+
527
+ possible_classes = [x for x in instances if x.typeURI.endswith(subclass_name)]
528
+ if len(possible_classes) == 1:
529
+ type_uri = possible_classes[0].typeURI
530
+
531
+ if type_uri == '':
532
+ return
533
+
534
+ data_validation = DataValidation(type="list", formula1=f'"{type_uri}"', allow_blank=True)
535
+ for rows in sheet.iter_rows(min_row=1, max_row=1, min_col=1, max_col=1):
536
+ for cell in rows:
537
+ column = cell.column
538
+ sheet.add_data_validation(data_validation)
539
+ data_validation.add(f'{get_column_letter(column)}{starting_row}:{get_column_letter(column)}1000')
540
+
541
+ @classmethod
542
+ async def add_type_uri_choice_list_in_excel_async(cls, sheet, instances, add_attribute_info: bool):
543
+ starting_row = '3' if add_attribute_info else '2'
544
+ await sleep(0)
545
+ if sheet.title == 'Keuzelijsten':
546
+ return
547
+ type_uri_found = False
548
+ for row in sheet.iter_rows(min_row=1, max_row=1):
549
+ for cell in row:
550
+ if cell.value == 'typeURI':
551
+ type_uri_found = True
552
+ break
553
+ if type_uri_found:
554
+ break
555
+ if not type_uri_found:
556
+ return
557
+
558
+ await sleep(0)
559
+ sheet_name = sheet.title
560
+ type_uri = ''
561
+ if sheet_name.startswith('http'):
562
+ type_uri = sheet_name
563
+ else:
564
+ split_name = sheet_name.split("#")
565
+ subclass_name = split_name[1]
566
+
567
+ possible_classes = [x for x in instances if x.typeURI.endswith(subclass_name)]
568
+ if len(possible_classes) == 1:
569
+ type_uri = possible_classes[0].typeURI
570
+
571
+ if type_uri == '':
572
+ return
573
+
574
+ data_validation = DataValidation(type="list", formula1=f'"{type_uri}"', allow_blank=True)
575
+ await sleep(0)
576
+ for rows in sheet.iter_rows(min_row=1, max_row=1, min_col=1, max_col=1):
577
+ for cell in rows:
578
+ await sleep(0)
579
+ column = cell.column
580
+ sheet.add_data_validation(data_validation)
581
+ data_validation.add(f'{get_column_letter(column)}{starting_row}:{get_column_letter(column)}1000')
582
+
583
+ @classmethod
584
+ def set_fixed_column_width(cls, sheet, width: int):
585
+ dim_holder = DimensionHolder(worksheet=sheet)
586
+ for col in range(sheet.min_column, sheet.max_column + 1):
587
+ dim_holder[get_column_letter(col)] = ColumnDimension(sheet, min=col, max=col, width=width)
588
+ sheet.column_dimensions = dim_holder
589
+
590
+ @classmethod
591
+ def add_choice_list_to_sheet(cls, workbook, name, options, choice_list_dict):
592
+ active_sheet = workbook['Keuzelijsten']
593
+ column_nr = choice_list_dict.keys().__len__() + 1
594
+ row_nr = 1
595
+ new_header = active_sheet.cell(row=row_nr, column=column_nr)
596
+ if new_header.value is not None:
597
+ raise ValueError(f'Header already exists at column {column_nr}: {new_header.value}')
598
+ new_header.value = name
599
+ for index, option in enumerate(options, start=1):
600
+ cell = active_sheet.cell(row=row_nr + index, column=column_nr)
601
+ cell.value = option.invulwaarde
602
+
603
+ choice_list_dict[name] = new_header.column_letter
604
+
605
+ @classmethod
606
+ def get_uri_from_sheet_name(cls, title: str) -> str:
607
+ if title == 'Agent':
608
+ return 'http://purl.org/dc/terms/Agent'
609
+ if '#' not in title:
610
+ raise ValueError('Sheet title does not contain a #')
611
+ class_ns, class_name = title.split('#', maxsplit=1)
612
+ return short_to_long_ns.get(class_ns, class_ns) + class_name
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.2
1
+ Metadata-Version: 2.4
2
2
  Name: otlmow_template
3
- Version: 0.11rc4
3
+ Version: 1.0
4
4
  Author-email: David Vlaminck <david.vlaminck@mow.vlaanderen.be>, Jasper Berton <jasperberton1@telenet.be>
5
5
  License: GNU GENERAL PUBLIC LICENSE
6
6
  Version 3, 29 June 2007
@@ -697,9 +697,9 @@ Classifier: Topic :: Software Development :: Quality Assurance
697
697
  Requires-Python: >=3.9
698
698
  Description-Content-Type: text/markdown
699
699
  License-File: LICENSE
700
- Requires-Dist: otlmow-converter>=1.7
700
+ Requires-Dist: otlmow-converter>=1.10
701
701
  Requires-Dist: otlmow-modelbuilder>=0.25
702
- Requires-Dist: universalasync>=0.4.0
702
+ Dynamic: license-file
703
703
 
704
704
  # OTLMOW-Template
705
705
  [![PyPI](https://img.shields.io/pypi/v/otlmow-template?label=latest%20release)](https://pypi.org/project/otlmow-template/)
@@ -1,10 +1,7 @@
1
1
  LICENSE
2
2
  README.md
3
3
  pyproject.toml
4
- otlmow_template/CsvTemplateCreator.py
5
- otlmow_template/ExcelTemplateCreator.py
6
4
  otlmow_template/SubsetTemplateCreator.py
7
- otlmow_template/__init__.py
8
5
  otlmow_template.egg-info/PKG-INFO
9
6
  otlmow_template.egg-info/SOURCES.txt
10
7
  otlmow_template.egg-info/dependency_links.txt
@@ -0,0 +1,2 @@
1
+ otlmow-converter>=1.10
2
+ otlmow-modelbuilder>=0.25