Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[NEAT-222] DMS (and all other rules) dump order 🖶 #433

Merged
merged 77 commits into from
May 6, 2024
Merged
Show file tree
Hide file tree
Changes from 71 commits
Commits
Show all changes
77 commits
Select commit Hold shift + click to select a range
f5ae804
tests: Added failing tests
doctrino May 3, 2024
d55a2a7
refactor: added DMS Node Entitiy
doctrino May 3, 2024
0f4670e
refactor: setup shell for new wrapped entities
doctrino May 3, 2024
4beeaa1
feat: Implemented wrapped filter
doctrino May 3, 2024
d3215b3
refactor: Switched to wrapped entity
doctrino May 3, 2024
9f3b2b8
refactor: introduce DMSFilder base class
doctrino May 3, 2024
a528e82
refactor: moved out creation of filter method
doctrino May 3, 2024
6f41734
refactor: moved logic
doctrino May 3, 2024
025a35b
refactor: reorg
doctrino May 3, 2024
bd79ce2
refactor: support setting explicit NodeType and HasData Filter
doctrino May 3, 2024
fba94de
build: changelog
doctrino May 3, 2024
65b732d
refactor: introduce data model type
doctrino May 3, 2024
bbe5c62
Merge remote-tracking branch 'origin/main' into smart-default-filter
doctrino May 3, 2024
70d0141
refactor; implemented smart default
doctrino May 3, 2024
916d77d
tests: updated tests
doctrino May 3, 2024
4667bc9
style: better words
doctrino May 3, 2024
a4a6113
build: bug
doctrino May 3, 2024
55918fb
tests: updated tests
doctrino May 3, 2024
4c30469
refactor; extend column
doctrino May 3, 2024
24745d8
tests: Added olav test
doctrino May 3, 2024
e24c0e0
fix: parent filter when no properties
doctrino May 3, 2024
a16cff4
fix: Dump dataModelType
doctrino May 3, 2024
1eae946
refactor: fix comparison
doctrino May 3, 2024
1082c88
tests: update tests
doctrino May 3, 2024
9185cb6
refactor: moved modelType up
doctrino May 3, 2024
a89a6fd
refactor: robust against duplicated node types
doctrino May 3, 2024
7f420dc
build: changelog
doctrino May 3, 2024
c3a3990
build; changelog
doctrino May 3, 2024
00efc21
build; updated docs
doctrino May 3, 2024
5c9ae45
docs; documented new smart defaults
doctrino May 3, 2024
3eeeb6f
Merge branch 'main' into smart-default-filter
doctrino May 4, 2024
9768e54
refactor; DMSImporter infer data model type
doctrino May 4, 2024
98a46d6
tests: update
doctrino May 4, 2024
5540281
Merge remote-tracking branch 'origin/main' into smart-default-filter
doctrino May 4, 2024
751ac9e
Merge branch 'main' into smart-default-filter
doctrino May 4, 2024
182cb3f
tests: updated test to return direct listable
doctrino May 4, 2024
56d9ed7
refactor: removed warning and create correct container
doctrino May 4, 2024
32d8bae
refactor: reimplement the view property conversion
doctrino May 4, 2024
30f9880
refactor: pre-commits
doctrino May 4, 2024
57a9f74
tests: update tests
doctrino May 4, 2024
4edf938
refactor: update examples
doctrino May 4, 2024
431486c
tests: updated tests
doctrino May 4, 2024
b31d241
refactor: fix
doctrino May 4, 2024
2345035
tests: updated tests
doctrino May 4, 2024
288481b
refactor: update ref rules
doctrino May 4, 2024
0ffd918
docs; docs section
doctrino May 4, 2024
4d4a557
build: bump + changelog
doctrino May 4, 2024
dbe92e0
refactor: added validation of value type for relation
doctrino May 4, 2024
0bd56d8
tests: added DMSImporter test for tutorial examples
doctrino May 4, 2024
c160e33
refactor: ensure deterministic output when dumping DMSRules
doctrino May 4, 2024
e86c4d5
refactor: moved out DMS Rules serialization
doctrino May 4, 2024
3a2a36b
refactor: fixed minor bugs
doctrino May 4, 2024
ea02e07
refactor: complete reimplementation of DMS importer
doctrino May 4, 2024
a996913
refactor: implementation
doctrino May 4, 2024
e6209bb
fix: a few minor bugs in the DMSImporter
doctrino May 4, 2024
163dce7
fix; added safety
doctrino May 4, 2024
aa1bc42
refactor: Added support for exporting/importing ref model
doctrino May 4, 2024
1feed9d
refactor: fix extended
doctrino May 4, 2024
86c62e9
fix: typo in Olav rules
doctrino May 4, 2024
efa2065
fix: bug in importer
doctrino May 4, 2024
7dceade
refactor: Olav passing tests
doctrino May 4, 2024
61f735c
refactor: support solo inwards edges
doctrino May 5, 2024
eed79c7
refactor: Robustify DMRRules read methods part 1
doctrino May 5, 2024
90bda17
refactor; validate format of string as well
doctrino May 5, 2024
4d8d8f8
refactor: Robustify DMRRules read methods part 2
doctrino May 5, 2024
b2363b4
refactor; pass context from dictionary and zip file
doctrino May 5, 2024
bd82da8
fix: introduced bug
doctrino May 5, 2024
809342b
refactor; introduced bug
doctrino May 5, 2024
4522560
fix: Finidhed up the last issues
doctrino May 5, 2024
e38d7af
fix: immutable classes cannot be used for exceptions
doctrino May 5, 2024
26df293
refactor: removed properties for SheetEntity
doctrino May 5, 2024
cf58b85
merge
doctrino May 6, 2024
5882d65
refactor: bad merge
doctrino May 6, 2024
40949df
Merge branch 'suppor-listable-direct-relations' into upgrade-dms-impo…
doctrino May 6, 2024
9063f3f
merge
doctrino May 6, 2024
ecd9f9f
Merge branch 'upgrade-dms-importer' into dms-dump-order
doctrino May 6, 2024
8add6d8
Merge remote-tracking branch 'origin/main' into dms-dump-order
doctrino May 6, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
.PHONY: run-explorer run-tests run-linters build-ui build-python build-docker run-docker compose-up

version="0.75.9"
version="0.77.0"
run-explorer:
@echo "Running explorer API server..."
# open "http://localhost:8000/static/index.html" || true
Expand Down
2 changes: 1 addition & 1 deletion cognite/neat/_version.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = "0.75.9"
__version__ = "0.77.0"
6 changes: 3 additions & 3 deletions cognite/neat/rules/exporters/_rules2dms.py
Original file line number Diff line number Diff line change
Expand Up @@ -115,11 +115,11 @@ def export(self, rules: Rules) -> DMSSchema:
)
is_new_model = dms_rules.reference is None
if is_new_model or is_solution_model:
return dms_rules.as_schema(self.export_pipeline, self.instance_space)
return dms_rules.as_schema(False, self.export_pipeline, self.instance_space)

# This is an extension of an existing model.
reference_rules = cast(DMSRules, dms_rules.reference).model_copy(deep=True)
reference_schema = reference_rules.as_schema(self.export_pipeline)
reference_schema = reference_rules.as_schema(include_ref=False, include_pipeline=self.export_pipeline)

# Todo Move this to an appropriate location
# Merging Reference with User Rules
Expand All @@ -142,7 +142,7 @@ def export(self, rules: Rules) -> DMSSchema:
property_.reference = None
combined_rules.properties.append(property_)

schema = combined_rules.as_schema(self.export_pipeline, self.instance_space)
schema = combined_rules.as_schema(True, self.export_pipeline, self.instance_space)

if dms_rules.metadata.extension in (ExtensionCategory.addition, ExtensionCategory.reshape):
# We do not freeze views as they might be changed, even for addition,
Expand Down
9 changes: 0 additions & 9 deletions cognite/neat/rules/exporters/_rules2excel.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,13 +120,6 @@ def _write_sheets(self, workbook: Workbook, dumped_rules: dict[str, Any], rules:
else:
sheet = workbook.create_sheet(sheet_name)

# Reorder such that the first column is class + the first field of the subclass
# of sheet entity. This is to make the properties/classes/views/containers sheet more readable.
# For example, for the properties these that means class, property, name, description
# instead of class, name, description, property
move = len(SheetEntity.model_fields) - 1 # -1 is for the class field
headers = headers[:1] + headers[move : move + 1] + headers[1:move] + headers[move + 1 :]

main_header = self._main_header_by_sheet_name[sheet_name]
sheet.append([main_header] + [""] * (len(headers) - 1))
sheet.merge_cells(start_row=1, start_column=1, end_row=1, end_column=len(headers))
Expand All @@ -150,8 +143,6 @@ def _write_sheets(self, workbook: Workbook, dumped_rules: dict[str, Any], rules:
cell.border = Border(left=side, right=side, top=side, bottom=side)
fill_color = next(fill_colors)

# Need to do the same reordering as for the headers above
row = row[:1] + row[move : move + 1] + row[1:move] + row[move + 1 :]
sheet.append(row)
if self._styling_level > 2 and is_properties:
for cell in sheet[sheet.max_row]:
Expand Down
6 changes: 6 additions & 0 deletions cognite/neat/rules/importers/_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,12 @@ def _to_output(
else:
return output, issues

@classmethod
def _return_or_raise(cls, issue_list: IssueList, errors: Literal["raise", "continue"]) -> tuple[None, IssueList]:
if errors == "raise":
raise issue_list.as_errors()
return None, issue_list

def _default_metadata(self):
return {
"prefix": "neat",
Expand Down
445 changes: 320 additions & 125 deletions cognite/neat/rules/importers/_dms2rules.py

Large diffs are not rendered by default.

6 changes: 0 additions & 6 deletions cognite/neat/rules/importers/_spreadsheet2rules.py
Original file line number Diff line number Diff line change
Expand Up @@ -229,12 +229,6 @@ def to_rules(
role=role,
)

@classmethod
def _return_or_raise(cls, issue_list: IssueList, errors: Literal["raise", "continue"]) -> tuple[None, IssueList]:
if errors == "raise":
raise issue_list.as_errors()
return None, issue_list


class GoogleSheetImporter(BaseImporter):
def __init__(self, sheet_id: str, skiprows: int = 1):
Expand Down
3 changes: 3 additions & 0 deletions cognite/neat/rules/issues/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,9 @@ def from_pydantic_errors(cls, errors: list[ErrorDetails], **kwargs) -> "list[Nea
all_errors.append(DefaultPydanticError.from_pydantic_error(error))
return all_errors

def as_exception(self) -> Exception:
return ValueError(self.message())


@dataclass(frozen=True)
class DefaultPydanticError(NeatValidationError):
Expand Down
94 changes: 44 additions & 50 deletions cognite/neat/rules/issues/dms.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,11 @@
"DuplicatedViewInDataModelError",
"DirectRelationMissingSourceWarning",
"ContainerPropertyUsedMultipleTimesError",
"DirectRelationListWarning",
"ReverseOfDirectRelationListWarning",
"EmptyContainerWarning",
"UnsupportedRelationWarning",
"MultipleReferenceWarning",
"HasDataFilterOnNoPropertiesViewWarning",
"ReverseRelationMissingOtherSideWarning",
"NodeTypeFilterOnParentViewWarning",
"ChangingContainerError",
"ChangingViewError",
Expand Down Expand Up @@ -297,54 +296,6 @@ def dump(self) -> dict[str, Any]:
return output


@dataclass(frozen=True)
class DirectRelationListWarning(DMSSchemaWarning):
description = "The container property is set to a direct relation list, which is not supported by the CDF API"
fix = "Make the property into a multiedge connection instead"
error_name: ClassVar[str] = "DirectRelationListWarning"
view_id: dm.ViewId
container_id: dm.ContainerId
property: str

def message(self) -> str:
return (
f"The property in {self.container_id}.{self.property} is a list of direct relations. "
f"This is not supported by the API, so it will be converted to an MultiEdgeConnection on"
f"the view {self.view_id}.{self.property} instead"
)

def dump(self) -> dict[str, Any]:
output = super().dump()
output["view_id"] = self.view_id.dump()
output["container_id"] = self.container_id.dump()
output["property"] = self.property
return output


@dataclass(frozen=True)
class ReverseOfDirectRelationListWarning(DMSSchemaWarning):
description = (
"The view property is set to a reverse of a direct relation list, which is not supported by the CDF API"
)
fix = "Make the property into a multiedge connection instead"
error_name: ClassVar[str] = "ReverseOfDirectRelationListWarning"
view_id: dm.ViewId
property: str

def message(self) -> str:
return (
f"The property pointed to be {self.view_id}.{self.property} is a list of direct relations. "
f"This is not supported by the API, so the {self.view_id}.{self.property} "
"will be converted from a reverse direct relation to an MultiEdgeConnection instead"
)

def dump(self) -> dict[str, Any]:
output = super().dump()
output["view_id"] = self.view_id.dump()
output["property"] = self.property
return output


@dataclass(frozen=True)
class EmptyContainerWarning(DMSSchemaWarning):
description = "The container is empty"
Expand Down Expand Up @@ -387,6 +338,24 @@ def dump(self) -> dict[str, Any]:
return output


@dataclass(frozen=True)
class ReverseRelationMissingOtherSideWarning(DMSSchemaWarning):
description = "The relation is missing the other side"
fix = "Add the other side of the relation"
error_name: ClassVar[str] = "ReverseRelationMissingOtherSideWarning"
view_id: dm.ViewId
property: str

def message(self) -> str:
return f"The reverse relation specified in {self.view_id}.{self.property} is missing the other side."

def dump(self) -> dict[str, Any]:
output = super().dump()
output["view_id"] = self.view_id.dump()
output["property"] = self.property
return output


@dataclass(frozen=True)
class MultipleReferenceWarning(DMSSchemaWarning):
description = "The view is implements multiple views from other spaces"
Expand Down Expand Up @@ -444,3 +413,28 @@ def dump(self) -> dict[str, Any]:
output = super().dump()
output["view_id"] = self.view_id.dump()
return output


@dataclass(frozen=True)
class HasDataFilterOnViewWithReferencesWarning(DMSSchemaWarning):
description = (
"Setting a hasData filter on a solution view which reference other containers is not recommended."
"This will lead to no nodes being returned when querying the solution view."
)
fix = "Use a node type filter instead"
error_name: ClassVar[str] = "HasDataFilterOnReferencedViewWarning"

view_id: dm.ViewId
references: list[dm.ViewId]

def message(self) -> str:
return (
f"Setting a hasData filter on view {self.view_id} which references other views {self.references}. "
"This is not recommended as it will lead to no nodes being returned when querying the solution view."
)

def dump(self) -> dict[str, Any]:
output = super().dump()
output["view_id"] = self.view_id.dump()
output["references"] = [view.dump() for view in sorted(self.references, key=lambda x: x.as_tuple())]
return output
41 changes: 41 additions & 0 deletions cognite/neat/rules/issues/fileread.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,13 @@
"InvalidFileFormatWarning",
"UnsupportedSpecWarning",
"UnknownItemWarning",
"FailedLoadWarning",
"BugInImporterWarning",
"FileReadError",
"FileNotFoundError",
"FileNotAFileError",
"InvalidFileFormatError",
"FailedStringLoadError",
]


Expand Down Expand Up @@ -96,6 +99,26 @@ def dump(self) -> dict[str, str | None]:
return output


@dataclass(frozen=True)
class FailedLoadWarning(FileReadWarning):
description = "The file content is invalid"
fix = "Check if the file content is valid"

expected_format: str
error_message: str

def message(self) -> str:
return (
f"Failed to load {self.filepath.name}. Expected format: {self.expected_format}. Error: {self.error_message}"
)

def dump(self) -> dict[str, str | None]:
output = super().dump()
output["expected_format"] = self.expected_format
output["error_message"] = self.error_message
return output


@dataclass(frozen=True)
class BugInImporterWarning(FileReadWarning):
description = "A bug was raised during reading."
Expand Down Expand Up @@ -143,6 +166,24 @@ def message(self) -> str:
return f"{self.filepath} is not in the expected format. Expected format: {self.expected_format}."


@dataclass(frozen=True)
class FailedStringLoadError(NeatValidationError):
description = "The file content is invalid"
fix = "Check if the file content is valid"

expected_format: str
error_message: str

def message(self) -> str:
return f"Failed to load string. Expected format: {self.expected_format}. Error: {self.error_message}"

def dump(self) -> dict[str, str | None]:
output = super().dump()
output["expected_format"] = self.expected_format
output["error_message"] = self.error_message
return output


@dataclass(frozen=True)
class NoFilesFoundError(FileReadError):
description = "No files were found in the directory"
Expand Down
Loading
Loading