Skip to content

Commit c7990e8

Browse files
committed
csv loader: table testing f/w
1 parent f667b2b commit c7990e8

File tree

6 files changed

+234
-7
lines changed

6 files changed

+234
-7
lines changed
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
columnA columnB columnB columnC
2+
table:row1 first value second value something else
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
columnA objectB columnC
2+
table:row1 {\"name\": \"foo\", \"value\": \"bar\"} something else
Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
# Auto generated from table.yaml by pythongen.py version: 0.9.0
2+
# Generation date: 2022-09-29T09:11:32
3+
# Schema: table
4+
#
5+
# id: https://w3id.org/linkml/examples/table
6+
# description: Represent a table in linkml
7+
# license: https://creativecommons.org/publicdomain/zero/1.0/
8+
9+
import dataclasses
10+
import sys
11+
import re
12+
from jsonasobj2 import JsonObj, as_dict
13+
from typing import Optional, List, Union, Dict, ClassVar, Any
14+
from dataclasses import dataclass
15+
from linkml_runtime.linkml_model.meta import EnumDefinition, PermissibleValue, PvFormulaOptions
16+
17+
from linkml_runtime.utils.slot import Slot
18+
from linkml_runtime.utils.metamodelcore import empty_list, empty_dict, bnode
19+
from linkml_runtime.utils.yamlutils import YAMLRoot, extended_str, extended_float, extended_int
20+
from linkml_runtime.utils.dataclass_extensions_376 import dataclasses_init_fn_with_kwargs
21+
from linkml_runtime.utils.formatutils import camelcase, underscore, sfx
22+
from linkml_runtime.utils.enumerations import EnumDefinitionImpl
23+
from rdflib import Namespace, URIRef
24+
from linkml_runtime.utils.curienamespace import CurieNamespace
25+
from linkml_runtime.linkml_model.types import String, Uriorcurie
26+
from linkml_runtime.utils.metamodelcore import URIorCURIE
27+
28+
metamodel_version = "1.7.0"
29+
version = None
30+
31+
# Overwrite dataclasses _init_fn to add **kwargs in __init__
32+
dataclasses._init_fn = dataclasses_init_fn_with_kwargs
33+
34+
# Namespaces
35+
LINKML = CurieNamespace('linkml', 'https://w3id.org/linkml/')
36+
TABLE = CurieNamespace('table', 'https://w3id.org/linkml/examples/table/')
37+
DEFAULT_ = TABLE
38+
39+
40+
# Types
41+
42+
# Class references
43+
class RowColumnA(URIorCURIE):
44+
pass
45+
46+
47+
@dataclass
48+
class Object(YAMLRoot):
49+
"""
50+
An object (bnode) which needs embedding in a single row
51+
"""
52+
_inherited_slots: ClassVar[List[str]] = []
53+
54+
class_class_uri: ClassVar[URIRef] = TABLE.Object
55+
class_class_curie: ClassVar[str] = "table:Object"
56+
class_name: ClassVar[str] = "Object"
57+
class_model_uri: ClassVar[URIRef] = TABLE.Object
58+
59+
name: Optional[str] = None
60+
value: Optional[str] = None
61+
62+
def __post_init__(self, *_: List[str], **kwargs: Dict[str, Any]):
63+
if self.name is not None and not isinstance(self.name, str):
64+
self.name = str(self.name)
65+
66+
if self.value is not None and not isinstance(self.value, str):
67+
self.value = str(self.value)
68+
69+
super().__post_init__(**kwargs)
70+
71+
72+
@dataclass
73+
class Row(YAMLRoot):
74+
"""
75+
A single data point made up of columns.
76+
"""
77+
_inherited_slots: ClassVar[List[str]] = []
78+
79+
class_class_uri: ClassVar[URIRef] = TABLE.Row
80+
class_class_curie: ClassVar[str] = "table:Row"
81+
class_name: ClassVar[str] = "Row"
82+
class_model_uri: ClassVar[URIRef] = TABLE.Row
83+
84+
columnA: Union[str, RowColumnA] = None
85+
objectB: Optional[Union[dict, Object]] = None
86+
columnC: Optional[str] = None
87+
88+
def __post_init__(self, *_: List[str], **kwargs: Dict[str, Any]):
89+
if self._is_empty(self.columnA):
90+
self.MissingRequiredField("columnA")
91+
if not isinstance(self.columnA, RowColumnA):
92+
self.columnA = RowColumnA(self.columnA)
93+
94+
if self.objectB is not None and not isinstance(self.objectB, Object):
95+
self.objectB = Object(**as_dict(self.objectB))
96+
97+
if self.columnC is not None and not isinstance(self.columnC, str):
98+
self.columnC = str(self.columnC)
99+
100+
super().__post_init__(**kwargs)
101+
102+
103+
@dataclass
104+
class Table(YAMLRoot):
105+
"""
106+
Container of rows.
107+
"""
108+
_inherited_slots: ClassVar[List[str]] = []
109+
110+
class_class_uri: ClassVar[URIRef] = TABLE.Table
111+
class_class_curie: ClassVar[str] = "table:Table"
112+
class_name: ClassVar[str] = "Table"
113+
class_model_uri: ClassVar[URIRef] = TABLE.Table
114+
115+
rows: Optional[Union[Dict[Union[str, RowColumnA], Union[dict, Row]], List[Union[dict, Row]]]] = empty_dict()
116+
117+
def __post_init__(self, *_: List[str], **kwargs: Dict[str, Any]):
118+
self._normalize_inlined_as_list(slot_name="rows", slot_type=Row, key_name="columnA", keyed=True)
119+
120+
super().__post_init__(**kwargs)
121+
122+
123+
# Enumerations
124+
125+
126+
# Slots
127+
class slots:
128+
pass
129+
130+
slots.object__name = Slot(uri=TABLE.name, name="object__name", curie=TABLE.curie('name'),
131+
model_uri=TABLE.object__name, domain=None, range=Optional[str])
132+
133+
slots.object__value = Slot(uri=TABLE.value, name="object__value", curie=TABLE.curie('value'),
134+
model_uri=TABLE.object__value, domain=None, range=Optional[str])
135+
136+
slots.row__columnA = Slot(uri=TABLE.columnA, name="row__columnA", curie=TABLE.curie('columnA'),
137+
model_uri=TABLE.row__columnA, domain=None, range=URIRef)
138+
139+
slots.row__objectB = Slot(uri=TABLE.objectB, name="row__objectB", curie=TABLE.curie('objectB'),
140+
model_uri=TABLE.row__objectB, domain=None, range=Optional[Union[dict, Object]])
141+
142+
slots.row__columnC = Slot(uri=TABLE.columnC, name="row__columnC", curie=TABLE.curie('columnC'),
143+
model_uri=TABLE.row__columnC, domain=None, range=Optional[str])
144+
145+
slots.table__rows = Slot(uri=TABLE.rows, name="table__rows", curie=TABLE.curie('rows'),
146+
model_uri=TABLE.table__rows, domain=None, range=Optional[Union[Dict[Union[str, RowColumnA], Union[dict, Row]], List[Union[dict, Row]]]])
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
id: https://w3id.org/linkml/examples/table
2+
name: table
3+
description: |-
4+
Represent a table in linkml
5+
license: https://creativecommons.org/publicdomain/zero/1.0/
6+
imports:
7+
- linkml:types
8+
prefixes:
9+
table: https://w3id.org/linkml/examples/table/
10+
linkml: https://w3id.org/linkml/
11+
default_prefix: table
12+
default_range: string
13+
14+
classes:
15+
16+
Object:
17+
description: |-
18+
An object (bnode) which needs embedding in a single row
19+
attributes:
20+
name:
21+
range: string
22+
value:
23+
range: string
24+
25+
Row:
26+
description: |-
27+
A single data point made up of columns.
28+
attributes:
29+
columnA:
30+
range: uriorcurie
31+
identifier: true
32+
objectB:
33+
range: Object
34+
inlined: true
35+
columnC:
36+
range: string
37+
multivalued: false
38+
39+
Table:
40+
description: |-
41+
Container of rows.
42+
tree_root: true
43+
attributes:
44+
rows:
45+
range: Row
46+
inlined: true
47+
inlined_as_list: true
48+
multivalued: true

tests/test_loaders_dumpers/test_csv_loader_dumper.py

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
from linkml_runtime.loaders import csv_loader
1414
from linkml_runtime.utils.yamlutils import as_json_object
1515
from tests.test_loaders_dumpers.models.books_normalized import Shop, Book, GenreEnum, BookSeries
16+
from tests.test_loaders_dumpers.models.table import Table, Row
1617

1718

1819
ROOT = os.path.abspath(os.path.dirname(__file__))
@@ -26,6 +27,11 @@
2627
OUTPUT = os.path.join(OUTPUT_DIR, 'books_flattened.tsv')
2728
OUTPUT2 = os.path.join(OUTPUT_DIR, 'books_flattened_02.tsv')
2829

30+
TABLE_SCHEMA = os.path.join(MODEL_DIR, 'table.yaml')
31+
TABLE_DATA_JSON = os.path.join(INPUT_DIR, 'table-json.tsv')
32+
TABLE_DATA_INLINED = os.path.join(INPUT_DIR, 'table-inlined.tsv')
33+
34+
2935
def _json(obj) -> str:
3036
return json.dumps(obj, indent=' ', sort_keys=True)
3137

@@ -84,13 +90,11 @@ def test_csvgen_unroundtrippable(self):
8490
logging.debug(json_dumper.dumps(roundtrip))
8591
assert roundtrip == data
8692

87-
88-
89-
90-
91-
92-
93-
93+
def test_table_model(self):
94+
schemaview = SchemaView(SCHEMA)
95+
table_json= csv_loader.load(TABLE_DATA_JSON, target_class=Table, index_slot='rows', schemaview=schemaview)
96+
for row in table_json.rows:
97+
assert len(row["columnB"]) == 2
9498

9599
if __name__ == '__main__':
96100
unittest.main()

tests/test_utils/test_csv_utils.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import pytest
2+
import unittest
3+
4+
from linkml_runtime.utils.csvutils import _get_key_config, get_configmap
5+
from linkml_runtime.utils.schemaview import SchemaView
6+
from tests.support.test_environment import TestEnvironmentTestCase
7+
from tests.test_utils.environment import env
8+
9+
10+
class CsvUtilTestCase(TestEnvironmentTestCase):
11+
env = env
12+
13+
def test_null_configmap(self):
14+
get_configmap(None, "unknown")
15+
# TODO: with pytest, use captlog to verify the output
16+
# assert 'Index slot or schema not specified' in caplog.text
17+
18+
def test_get_configmap(self):
19+
fname = env.input_path('kitchen_sink.yaml')
20+
schema = SchemaView(fname)
21+
get_configmap(schema, "unknown")
22+
23+
24+
if __name__ == '__main__':
25+
unittest.main()

0 commit comments

Comments
 (0)