cognite-neat 0.109.3__py3-none-any.whl → 0.110.0__py3-none-any.whl

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.

Potentially problematic release.


This version of cognite-neat might be problematic. Click here for more details.

Files changed (67) hide show
  1. cognite/neat/_alpha.py +2 -0
  2. cognite/neat/_client/_api/schema.py +17 -1
  3. cognite/neat/_client/data_classes/schema.py +3 -3
  4. cognite/neat/_constants.py +11 -0
  5. cognite/neat/_graph/extractors/_classic_cdf/_classic.py +9 -10
  6. cognite/neat/_graph/extractors/_iodd.py +3 -3
  7. cognite/neat/_graph/extractors/_mock_graph_generator.py +9 -7
  8. cognite/neat/_graph/loaders/_rdf2dms.py +285 -346
  9. cognite/neat/_graph/queries/_base.py +28 -92
  10. cognite/neat/_graph/transformers/__init__.py +1 -3
  11. cognite/neat/_graph/transformers/_rdfpath.py +2 -49
  12. cognite/neat/_issues/__init__.py +1 -6
  13. cognite/neat/_issues/_base.py +21 -252
  14. cognite/neat/_issues/_contextmanagers.py +46 -0
  15. cognite/neat/_issues/_factory.py +61 -0
  16. cognite/neat/_issues/errors/__init__.py +18 -4
  17. cognite/neat/_issues/errors/_wrapper.py +81 -3
  18. cognite/neat/_issues/formatters.py +4 -4
  19. cognite/neat/_issues/warnings/__init__.py +3 -2
  20. cognite/neat/_issues/warnings/_properties.py +8 -0
  21. cognite/neat/_rules/_constants.py +9 -0
  22. cognite/neat/_rules/_shared.py +3 -2
  23. cognite/neat/_rules/analysis/__init__.py +2 -3
  24. cognite/neat/_rules/analysis/_base.py +450 -258
  25. cognite/neat/_rules/catalog/info-rules-imf.xlsx +0 -0
  26. cognite/neat/_rules/exporters/_rules2excel.py +2 -8
  27. cognite/neat/_rules/exporters/_rules2instance_template.py +2 -2
  28. cognite/neat/_rules/exporters/_rules2ontology.py +5 -4
  29. cognite/neat/_rules/importers/_base.py +2 -47
  30. cognite/neat/_rules/importers/_dms2rules.py +7 -10
  31. cognite/neat/_rules/importers/_dtdl2rules/dtdl_importer.py +2 -2
  32. cognite/neat/_rules/importers/_rdf/_inference2rules.py +59 -25
  33. cognite/neat/_rules/importers/_rdf/_shared.py +1 -1
  34. cognite/neat/_rules/importers/_spreadsheet2rules.py +12 -9
  35. cognite/neat/_rules/models/dms/_rules.py +3 -1
  36. cognite/neat/_rules/models/dms/_rules_input.py +4 -0
  37. cognite/neat/_rules/models/dms/_validation.py +14 -4
  38. cognite/neat/_rules/models/entities/_loaders.py +1 -1
  39. cognite/neat/_rules/models/entities/_multi_value.py +2 -2
  40. cognite/neat/_rules/models/information/_rules.py +18 -17
  41. cognite/neat/_rules/models/information/_rules_input.py +2 -1
  42. cognite/neat/_rules/models/information/_validation.py +3 -1
  43. cognite/neat/_rules/transformers/__init__.py +8 -2
  44. cognite/neat/_rules/transformers/_converters.py +242 -43
  45. cognite/neat/_rules/transformers/_verification.py +5 -10
  46. cognite/neat/_session/_base.py +4 -4
  47. cognite/neat/_session/_prepare.py +12 -0
  48. cognite/neat/_session/_read.py +21 -17
  49. cognite/neat/_session/_show.py +11 -123
  50. cognite/neat/_session/_state.py +0 -2
  51. cognite/neat/_session/_subset.py +64 -0
  52. cognite/neat/_session/_to.py +63 -12
  53. cognite/neat/_store/_graph_store.py +5 -246
  54. cognite/neat/_utils/rdf_.py +2 -2
  55. cognite/neat/_utils/spreadsheet.py +44 -1
  56. cognite/neat/_utils/text.py +51 -32
  57. cognite/neat/_version.py +1 -1
  58. {cognite_neat-0.109.3.dist-info → cognite_neat-0.110.0.dist-info}/METADATA +1 -1
  59. {cognite_neat-0.109.3.dist-info → cognite_neat-0.110.0.dist-info}/RECORD +62 -64
  60. {cognite_neat-0.109.3.dist-info → cognite_neat-0.110.0.dist-info}/WHEEL +1 -1
  61. cognite/neat/_graph/queries/_construct.py +0 -187
  62. cognite/neat/_graph/queries/_shared.py +0 -173
  63. cognite/neat/_rules/analysis/_dms.py +0 -57
  64. cognite/neat/_rules/analysis/_information.py +0 -249
  65. cognite/neat/_rules/models/_rdfpath.py +0 -372
  66. {cognite_neat-0.109.3.dist-info → cognite_neat-0.110.0.dist-info}/LICENSE +0 -0
  67. {cognite_neat-0.109.3.dist-info → cognite_neat-0.110.0.dist-info}/entry_points.txt +0 -0
@@ -18,9 +18,6 @@ from cognite.neat._graph.queries import Queries
18
18
  from cognite.neat._graph.transformers import Transformers
19
19
  from cognite.neat._issues import IssueList, catch_issues
20
20
  from cognite.neat._issues.errors import OxigraphStorageLockedError
21
- from cognite.neat._rules.analysis import InformationAnalysis
22
- from cognite.neat._rules.models import InformationRules
23
- from cognite.neat._rules.models.entities import ClassEntity
24
21
  from cognite.neat._shared import InstanceType, Triple
25
22
  from cognite.neat._utils.auxiliary import local_import
26
23
  from cognite.neat._utils.rdf_ import add_triples_in_batch, remove_namespace_from_uri
@@ -55,9 +52,6 @@ class NeatGraphStore:
55
52
  dataset: Dataset,
56
53
  default_named_graph: URIRef | None = None,
57
54
  ):
58
- self.rules: dict[URIRef, InformationRules] = {}
59
- self.base_namespace: dict[URIRef, Namespace] = {}
60
-
61
55
  _start = datetime.now(timezone.utc)
62
56
  self.dataset = dataset
63
57
  self.provenance = Provenance[Entity](
@@ -72,8 +66,7 @@ class NeatGraphStore:
72
66
  )
73
67
 
74
68
  self.default_named_graph = default_named_graph or DATASET_DEFAULT_GRAPH_ID
75
-
76
- self.queries = Queries(self.dataset, self.rules, self.default_named_graph)
69
+ self.queries = Queries(self.dataset, self.default_named_graph)
77
70
 
78
71
  def graph(self, named_graph: URIRef | None = None) -> Graph:
79
72
  """Get named graph from the dataset to query over"""
@@ -116,36 +109,6 @@ class NeatGraphStore:
116
109
  else:
117
110
  return self.dataset.serialize(format="ox-trig" if self.type_ == "OxigraphStore" else "trig")
118
111
 
119
- def add_rules(self, rules: InformationRules, named_graph: URIRef | None = None) -> None:
120
- """This method is used to add rules to a named graph stored in the graph store.
121
-
122
- Args:
123
- rules: InformationRules object containing rules to be added to the named graph
124
- named_graph: URIRef of the named graph to store the rules in, by default None
125
- rules will be added to the default graph
126
-
127
- """
128
-
129
- named_graph = named_graph or self.default_named_graph
130
-
131
- if named_graph in self.named_graphs:
132
- # attaching appropriate namespace to the rules
133
- # as well base_namespace
134
- self.rules[named_graph] = rules
135
- self.base_namespace[named_graph] = rules.metadata.namespace
136
- self.queries = Queries(self.dataset, self.rules)
137
- self.provenance.append(
138
- Change.record(
139
- activity=f"{type(self)}.rules",
140
- start=datetime.now(timezone.utc),
141
- end=datetime.now(timezone.utc),
142
- description=f"Added {type(self.rules).__name__} to {named_graph} named graph",
143
- )
144
- )
145
-
146
- if self.rules[named_graph].prefixes:
147
- self._upsert_prefixes(self.rules[named_graph].prefixes, named_graph)
148
-
149
112
  def _upsert_prefixes(self, prefixes: dict[str, Namespace], named_graph: URIRef) -> None:
150
113
  """Adds prefixes to the graph store."""
151
114
  _start = datetime.now(timezone.utc)
@@ -271,226 +234,22 @@ class NeatGraphStore:
271
234
  last_change.target_entity.issues.extend(issue_list)
272
235
  return issue_list
273
236
 
274
- def _read_via_rules_linkage(
237
+ def read(
275
238
  self,
276
- class_neat_id: URIRef,
277
- property_link_pairs: dict[str, URIRef] | None,
239
+ class_uri: URIRef,
278
240
  named_graph: URIRef | None = None,
279
- ) -> Iterable[tuple[str, dict[str | InstanceType, list[str]]]]:
280
- named_graph = named_graph or self.default_named_graph
281
-
282
- if named_graph not in self.named_graphs:
283
- warnings.warn(
284
- f"Named graph {named_graph} not found in graph store, cannot read",
285
- stacklevel=2,
286
- )
287
- return
288
-
289
- if not self.rules or named_graph not in self.rules:
290
- warnings.warn(
291
- f"Rules for named graph {named_graph} not found in graph store!",
292
- stacklevel=2,
293
- )
294
- return
295
-
296
- if self.multi_type_instances:
297
- warnings.warn(
298
- "Multi typed instances detected, issues with loading can occur!",
299
- stacklevel=2,
300
- )
301
-
302
- analysis = InformationAnalysis(self.rules[named_graph])
303
-
304
- if cls := analysis.classes_by_neat_id.get(class_neat_id):
305
- if property_link_pairs:
306
- property_renaming_config = {
307
- prop_uri: prop_name
308
- for prop_name, prop_neat_id in property_link_pairs.items()
309
- if (prop_uri := analysis.neat_id_to_instance_source_property_uri(prop_neat_id))
310
- }
311
- if information_properties := analysis.classes_with_properties(consider_inheritance=True).get(
312
- cls.class_
313
- ):
314
- for prop in information_properties:
315
- if prop.neatId is None:
316
- continue
317
- # Include renaming done in the Information rules that are not present in the
318
- # property_link_pairs. The use case for this renaming to startNode and endNode
319
- # properties that are not part of DMSRules but will typically be present
320
- # in the Information rules.
321
- if (
322
- uri := analysis.neat_id_to_instance_source_property_uri(prop.neatId)
323
- ) and uri not in property_renaming_config:
324
- property_renaming_config[uri] = prop.property_
325
-
326
- yield from self._read_via_class_entity(cls.class_, property_renaming_config)
327
- return
328
- else:
329
- warnings.warn("Rules not linked", stacklevel=2)
330
- return
331
- else:
332
- warnings.warn("Class with neat id {class_neat_id} found in rules", stacklevel=2)
333
- return
334
-
335
- def _read_via_class_entity(
336
- self,
337
- class_entity: ClassEntity,
338
241
  property_renaming_config: dict[URIRef, str] | None = None,
339
- named_graph: URIRef | None = None,
340
242
  ) -> Iterable[tuple[str, dict[str | InstanceType, list[str]]]]:
341
243
  named_graph = named_graph or self.default_named_graph
342
244
 
343
- if named_graph not in self.named_graphs:
344
- warnings.warn(
345
- f"Named graph {named_graph} not found in graph store, cannot read",
346
- stacklevel=2,
347
- )
348
- return
349
-
350
- if not self.rules or named_graph not in self.rules:
351
- warnings.warn(
352
- f"Rules for named graph {named_graph} not found in graph store!",
353
- stacklevel=2,
354
- )
355
- return
356
- if self.multi_type_instances:
357
- warnings.warn(
358
- "Multi typed instances detected, issues with loading can occur!",
359
- stacklevel=2,
360
- )
361
-
362
- if class_entity not in [definition.class_ for definition in self.rules[named_graph].classes]:
363
- warnings.warn("Desired type not found in graph!", stacklevel=2)
364
- return
365
-
366
- if not (class_uri := InformationAnalysis(self.rules[named_graph]).class_uri(class_entity)):
367
- warnings.warn(
368
- f"Class {class_entity.suffix} does not have namespace defined for prefix {class_entity.prefix} Rules!",
369
- stacklevel=2,
370
- )
371
- return
372
-
373
- has_hop_transformations = InformationAnalysis(self.rules[named_graph]).has_hop_transformations()
374
- has_self_reference_transformations = InformationAnalysis(
375
- self.rules[named_graph]
376
- ).has_self_reference_property_transformations()
377
- if has_hop_transformations or has_self_reference_transformations:
378
- msg = (
379
- f"Rules contain [{'Hop' if has_hop_transformations else ''}"
380
- f", {'SelfReferenceProperty' if has_self_reference_transformations else ''}]"
381
- " rdfpath."
382
- f" Run [{'ReduceHopTraversal' if has_hop_transformations else ''}"
383
- f", {'AddSelfReferenceProperty' if has_self_reference_transformations else ''}]"
384
- " transformer(s) first!"
385
- )
386
-
387
- warnings.warn(
388
- msg,
389
- stacklevel=2,
390
- )
391
- return
392
-
393
- # get all the instances for give class_uri
394
- instance_ids = self.queries.list_instances_ids_of_class(class_uri)
395
-
396
- # get potential property renaming config
397
- property_renaming_config = property_renaming_config or InformationAnalysis(
398
- self.rules[named_graph]
399
- ).define_property_renaming_config(class_entity)
245
+ instance_ids = self.queries.list_instances_ids_of_class(class_uri, named_graph=named_graph)
400
246
 
401
247
  for instance_id in instance_ids:
402
248
  if res := self.queries.describe(
403
- instance_id=instance_id,
404
- instance_type=class_entity.suffix,
405
- property_renaming_config=property_renaming_config,
249
+ instance_id=instance_id, instance_type=class_uri, property_renaming_config=property_renaming_config
406
250
  ):
407
251
  yield res
408
252
 
409
- def read(
410
- self, class_: str, named_graph: URIRef | None = None
411
- ) -> Iterable[tuple[str, dict[str | InstanceType, list[str]]]]:
412
- """Read instances for given class from the graph store.
413
-
414
- !!! note "Assumption"
415
- This method assumes that the class_ belongs to the same (name)space as
416
- the rules which are attached to the graph store.
417
-
418
- """
419
- named_graph = named_graph or self.default_named_graph
420
-
421
- if named_graph not in self.named_graphs:
422
- warnings.warn(
423
- f"Named graph {named_graph} not found in graph store, cannot read",
424
- stacklevel=2,
425
- )
426
- return
427
-
428
- if not self.rules or named_graph not in self.rules:
429
- warnings.warn(
430
- f"Rules for named graph {named_graph} not found in graph store!",
431
- stacklevel=2,
432
- )
433
- return
434
- if self.multi_type_instances:
435
- warnings.warn(
436
- "Multi typed instances detected, issues with loading can occur!",
437
- stacklevel=2,
438
- )
439
-
440
- class_entity = ClassEntity(prefix=self.rules[named_graph].metadata.prefix, suffix=class_)
441
-
442
- if class_entity not in [definition.class_ for definition in self.rules[named_graph].classes]:
443
- warnings.warn("Desired type not found in graph!", stacklevel=2)
444
- return
445
-
446
- yield from self._read_via_class_entity(class_entity)
447
-
448
- def count_of_id(self, neat_id: URIRef, named_graph: URIRef | None = None) -> int:
449
- """Count the number of instances of a given type
450
-
451
- Args:
452
- neat_id: Type for which instances are to be counted
453
-
454
- Returns:
455
- Number of instances
456
- """
457
- named_graph = named_graph or self.default_named_graph
458
-
459
- if named_graph not in self.named_graphs:
460
- warnings.warn(
461
- f"Named graph {named_graph} not found in graph store, cannot count",
462
- stacklevel=2,
463
- )
464
- return 0
465
-
466
- if not self.rules or named_graph not in self.rules:
467
- warnings.warn(
468
- f"Rules for named graph {named_graph} not found in graph store!",
469
- stacklevel=2,
470
- )
471
- return 0
472
-
473
- class_entity = next(
474
- (definition.class_ for definition in self.rules[named_graph].classes if definition.neatId == neat_id),
475
- None,
476
- )
477
- if not class_entity:
478
- warnings.warn("Desired type not found in graph!", stacklevel=2)
479
- return 0
480
-
481
- if not (class_uri := InformationAnalysis(self.rules[named_graph]).class_uri(class_entity)):
482
- warnings.warn(
483
- f"Class {class_entity.suffix} does not have namespace defined for prefix {class_entity.prefix} Rules!",
484
- stacklevel=2,
485
- )
486
- return 0
487
-
488
- return self.count_of_type(class_uri)
489
-
490
- def count_of_type(self, class_uri: URIRef) -> int:
491
- query = f"SELECT (COUNT(?instance) AS ?instanceCount) WHERE {{ ?instance a <{class_uri}> }}"
492
- return int(next(iter(self.dataset.query(query)))[0]) # type: ignore[arg-type, index]
493
-
494
253
  def _parse_file(
495
254
  self,
496
255
  named_graph: URIRef,
@@ -154,7 +154,7 @@ def _traverse(hierarchy: dict, graph: dict, names: list[str]) -> dict:
154
154
  return hierarchy
155
155
 
156
156
 
157
- def get_inheritance_path(child: Any, child_parent: dict[Any, list[Any]]) -> list:
157
+ def get_inheritance_path(child: Any, child_parent: dict[Any, set[Any]]) -> list[Any]:
158
158
  """Returns the inheritance path for a given child
159
159
 
160
160
  Args:
@@ -167,7 +167,7 @@ def get_inheritance_path(child: Any, child_parent: dict[Any, list[Any]]) -> list
167
167
  !!! note "No Circular Inheritance"
168
168
  This method assumes that the child_parent dictionary is a tree and does not contain any cycles.
169
169
  """
170
- path = []
170
+ path: list[Any] = []
171
171
  if child in child_parent:
172
172
  path.extend(child_parent[child])
173
173
  for parent in child_parent[child]:
@@ -5,6 +5,8 @@ import pandas as pd
5
5
  from openpyxl import load_workbook
6
6
  from openpyxl.worksheet.worksheet import Worksheet
7
7
 
8
+ from cognite.neat._rules._constants import get_internal_properties
9
+
8
10
 
9
11
  @dataclass
10
12
  class SpreadsheetRead:
@@ -16,6 +18,7 @@ class SpreadsheetRead:
16
18
 
17
19
  header_row: int = 1
18
20
  empty_rows: list[int] = field(default_factory=list)
21
+ skipped_rows: list[int] = field(default_factory=list)
19
22
  is_one_indexed: bool = True
20
23
 
21
24
  def __post_init__(self):
@@ -28,6 +31,13 @@ class SpreadsheetRead:
28
31
  output += 1
29
32
  else:
30
33
  break
34
+
35
+ for skipped_rows in self.skipped_rows:
36
+ if skipped_rows <= output:
37
+ output += 1
38
+ else:
39
+ break
40
+
31
41
  return output + self.header_row + (1 if self.is_one_indexed else 0)
32
42
 
33
43
 
@@ -63,19 +73,52 @@ def read_individual_sheet(
63
73
 
64
74
  raw = pd.read_excel(excel_file, sheet_name, skiprows=skiprows)
65
75
  is_na = raw.isnull().all(axis=1)
76
+ skip_rows = _find_rows_to_skip(raw)
66
77
  empty_rows = is_na[is_na].index.tolist()
67
78
 
79
+ if skip_rows:
80
+ raw = raw.drop(skip_rows)
81
+
68
82
  raw.dropna(axis=0, how="all", inplace=True)
83
+
69
84
  if "Value Type" in raw.columns:
70
85
  # Special handling for Value Type column, #N/A is treated specially by NEAT it means Unknown
71
86
  raw["Value Type"] = raw["Value Type"].replace(float("nan"), "#N/A")
87
+
72
88
  output = raw.replace(float("nan"), None).to_dict(orient="records")
73
89
  if return_read_info:
74
90
  # If no rows are skipped, row 1 is the header row.
75
- return output, SpreadsheetRead(header_row=skiprows + 1, empty_rows=empty_rows, is_one_indexed=True)
91
+ return output, SpreadsheetRead(
92
+ header_row=skiprows + 1,
93
+ empty_rows=empty_rows,
94
+ is_one_indexed=True,
95
+ skipped_rows=skip_rows,
96
+ )
76
97
  return output
77
98
 
78
99
 
100
+ def _find_rows_to_skip(
101
+ df: pd.DataFrame,
102
+ ) -> list:
103
+ """Find rows which are having all values as None except for internal properties."""
104
+ rows_to_skip = []
105
+
106
+ internal_cols = {val.lower() for val in get_internal_properties()}
107
+ for i, row in df.iterrows():
108
+ user_cols_state = []
109
+ internal_cols_state = []
110
+ for col in df.columns:
111
+ if col.lower() not in internal_cols:
112
+ user_cols_state.append(row[col] == "#N/A" or row[col].__str__().lower() in ["none", "nan"])
113
+ else:
114
+ internal_cols_state.append(row[col] is not None)
115
+
116
+ if all(user_cols_state) and any(internal_cols_state):
117
+ rows_to_skip.append(i)
118
+
119
+ return rows_to_skip
120
+
121
+
79
122
  def _get_row_number(sheet: Worksheet, values_to_find: list[str]) -> int | None:
80
123
  for row_number, row in enumerate(sheet.iter_rows(values_only=True), start=1):
81
124
  if any(value in row for value in values_to_find):
@@ -3,7 +3,7 @@ from collections.abc import Collection
3
3
  from typing import Any
4
4
 
5
5
 
6
- def to_camel(string: str) -> str:
6
+ def to_camel_case(string: str) -> str:
7
7
  """Convert snake_case_name to camelCaseName.
8
8
 
9
9
  Args:
@@ -12,22 +12,34 @@ def to_camel(string: str) -> str:
12
12
  camelCase of the input string.
13
13
 
14
14
  Examples:
15
- >>> to_camel("a_b")
15
+ >>> to_camel_case("a_b")
16
16
  'aB'
17
- >>> to_camel("ScenarioInstance_priceForecast")
17
+ >>> to_camel_case("ScenarioInstance_priceForecast")
18
18
  'scenarioInstancePriceForecast'
19
19
  """
20
- string = re.sub(r"[\s_-]", "_", string)
20
+ string = re.sub(r"[^a-zA-Z0-9_]", "_", string)
21
21
  string = re.sub("_+", "_", string)
22
+ is_all_upper = string.upper() == string
23
+ is_first_upper = (
24
+ len(string) >= 2 and string[:2].upper() == string[:2] and "_" not in string[:2] and not is_all_upper
25
+ )
26
+ return _to_camel_case(string, is_all_upper, is_first_upper)
27
+
28
+
29
+ def _to_camel_case(string, is_all_upper: bool, is_first_upper: bool):
22
30
  if "_" in string:
23
- pascal_splits = [to_pascal(part) for part in string.split("_")]
31
+ pascal_splits = [
32
+ _to_pascal_case(part, is_all_upper, is_first_upper and no == 0)
33
+ for no, part in enumerate(string.split("_"), 0)
34
+ ]
24
35
  else:
25
36
  # Ensure pascal
26
- string = string[0].upper() + string[1:]
37
+ if string:
38
+ string = string[0].upper() + string[1:]
27
39
  pascal_splits = [string]
28
40
  cleaned: list[str] = []
29
41
  for part in pascal_splits:
30
- if part.upper() == part:
42
+ if part.upper() == part and is_all_upper:
31
43
  cleaned.append(part.capitalize())
32
44
  else:
33
45
  cleaned.append(part)
@@ -37,13 +49,16 @@ def to_camel(string: str) -> str:
37
49
  string_split.extend(re.findall(r"[A-Z][a-z0-9]*", part))
38
50
  if not string_split:
39
51
  string_split = [string]
40
- try:
41
- return string_split[0].casefold() + "".join(word.capitalize() for word in string_split[1:])
42
- except IndexError:
52
+ if len(string_split) == 0:
43
53
  return ""
54
+ # The first word is a single letter, keep the original case
55
+ if is_first_upper:
56
+ return "".join(word for word in string_split)
57
+ else:
58
+ return string_split[0].casefold() + "".join(word for word in string_split[1:])
44
59
 
45
60
 
46
- def to_pascal(string: str) -> str:
61
+ def to_pascal_case(string: str) -> str:
47
62
  """Convert string to PascalCaseName.
48
63
 
49
64
  Args:
@@ -52,16 +67,20 @@ def to_pascal(string: str) -> str:
52
67
  PascalCase of the input string.
53
68
 
54
69
  Examples:
55
- >>> to_pascal("a_b")
70
+ >>> to_pascal_case("a_b")
56
71
  'AB'
57
- >>> to_pascal('camel_case')
72
+ >>> to_pascal_case('camel_case')
58
73
  'CamelCase'
59
74
  """
60
- camel = to_camel(string)
75
+ return _to_pascal_case(string, string == string.upper(), True)
76
+
77
+
78
+ def _to_pascal_case(string: str, is_all_upper: bool, is_first_upper: bool) -> str:
79
+ camel = _to_camel_case(string, is_all_upper, is_first_upper)
61
80
  return f"{camel[0].upper()}{camel[1:]}" if camel else ""
62
81
 
63
82
 
64
- def to_snake(string: str) -> str:
83
+ def to_snake_case(string: str) -> str:
65
84
  """
66
85
  Convert input string to snake_case
67
86
 
@@ -71,33 +90,33 @@ def to_snake(string: str) -> str:
71
90
  snake_case of the input string.
72
91
 
73
92
  Examples:
74
- >>> to_snake("aB")
93
+ >>> to_snake_case("aB")
75
94
  'a_b'
76
- >>> to_snake('CamelCase')
95
+ >>> to_snake_case('CamelCase')
77
96
  'camel_case'
78
- >>> to_snake('camelCamelCase')
97
+ >>> to_snake_case('camelCamelCase')
79
98
  'camel_camel_case'
80
- >>> to_snake('Camel2Camel2Case')
99
+ >>> to_snake_case('Camel2Camel2Case')
81
100
  'camel_2_camel_2_case'
82
- >>> to_snake('getHTTPResponseCode')
101
+ >>> to_snake_case('getHTTPResponseCode')
83
102
  'get_http_response_code'
84
- >>> to_snake('get200HTTPResponseCode')
103
+ >>> to_snake_case('get200HTTPResponseCode')
85
104
  'get_200_http_response_code'
86
- >>> to_snake('getHTTP200ResponseCode')
105
+ >>> to_snake_case('getHTTP200ResponseCode')
87
106
  'get_http_200_response_code'
88
- >>> to_snake('HTTPResponseCode')
107
+ >>> to_snake_case('HTTPResponseCode')
89
108
  'http_response_code'
90
- >>> to_snake('ResponseHTTP')
109
+ >>> to_snake_case('ResponseHTTP')
91
110
  'response_http'
92
- >>> to_snake('ResponseHTTP2')
111
+ >>> to_snake_case('ResponseHTTP2')
93
112
  'response_http_2'
94
- >>> to_snake('Fun?!awesome')
113
+ >>> to_snake_case('Fun?!awesome')
95
114
  'fun_awesome'
96
- >>> to_snake('Fun?!Awesome')
115
+ >>> to_snake_case('Fun?!Awesome')
97
116
  'fun_awesome'
98
- >>> to_snake('10CoolDudes')
117
+ >>> to_snake_case('10CoolDudes')
99
118
  '10_cool_dudes'
100
- >>> to_snake('20coolDudes')
119
+ >>> to_snake_case('20coolDudes')
101
120
  '20_cool_dudes'
102
121
  """
103
122
  pattern = re.compile(r"[A-Z]?[a-z]+|[A-Z]+(?=[A-Z][a-z]|\d|\W|$)|\d+")
@@ -117,7 +136,7 @@ def sentence_or_string_to_camel(string: str) -> str:
117
136
  except IndexError:
118
137
  return ""
119
138
  else:
120
- return to_camel(string)
139
+ return to_camel_case(string)
121
140
 
122
141
 
123
142
  def replace_non_alphanumeric_with_underscore(text: str) -> str:
@@ -151,7 +170,7 @@ class NamingStandardization:
151
170
  # Underscore ensure that 'Class' it treated as a separate word
152
171
  # in the to_pascale function
153
172
  clean = f"Class_{clean}"
154
- return to_pascal(clean)
173
+ return to_pascal_case(clean)
155
174
 
156
175
  @classmethod
157
176
  def standardize_property_str(cls, raw: str) -> str:
@@ -160,7 +179,7 @@ class NamingStandardization:
160
179
  # Underscore ensure that 'property' it treated as a separate word
161
180
  # in the to_camel function
162
181
  clean = f"property_{clean}"
163
- return to_camel(clean)
182
+ return to_camel_case(clean)
164
183
 
165
184
  @classmethod
166
185
  def _clean_string(cls, raw: str) -> str:
cognite/neat/_version.py CHANGED
@@ -1,2 +1,2 @@
1
- __version__ = "0.109.3"
1
+ __version__ = "0.110.0"
2
2
  __engine__ = "^2.0.3"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: cognite-neat
3
- Version: 0.109.3
3
+ Version: 0.110.0
4
4
  Summary: Knowledge graph transformation
5
5
  License: Apache-2.0
6
6
  Author: Nikola Vasiljevic