Skip to content

Commit

Permalink
fix: Fix-dependency-handling (#89)
Browse files Browse the repository at this point in the history
  • Loading branch information
ewuerger authored Sep 6, 2024
1 parent 9519a51 commit 64b8c0f
Show file tree
Hide file tree
Showing 16 changed files with 143 additions and 125 deletions.
16 changes: 12 additions & 4 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ default_install_hook_types: [commit-msg, pre-commit]
default_stages: [commit, merge-commit]
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.5.0
rev: v4.6.0
hooks:
- id: check-added-large-files
- id: check-ast
Expand All @@ -26,7 +26,7 @@ repos:
- id: fix-byte-order-marker
- id: trailing-whitespace
- repo: https://github.com/psf/black-pre-commit-mirror
rev: 24.2.0
rev: 24.8.0
hooks:
- id: black
- repo: https://github.com/PyCQA/isort
Expand All @@ -47,12 +47,20 @@ repos:
additional_dependencies:
- pydocstyle[toml]
- repo: https://github.com/pre-commit/mirrors-mypy
rev: v1.8.0
rev: v1.11.2
hooks:
- id: mypy
additional_dependencies:
- bidict
- cairosvg
- capellambse==0.5.69
- click
- jinja2
- polarion-rest-api-client==1.1.2
- pydantic
- types-requests
- types-PyYAML
exclude: tests
- repo: https://github.com/Lucas-C/pre-commit-hooks
rev: v1.5.5
hooks:
Expand Down Expand Up @@ -97,7 +105,7 @@ repos:
- --comment-style
- "..| |"
- repo: https://github.com/fsfe/reuse-tool
rev: v3.0.1
rev: v4.0.3
hooks:
- id: reuse
- repo: https://github.com/qoomon/git-conventional-commits
Expand Down
2 changes: 2 additions & 0 deletions capella2polarion/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,8 @@ def setup_logger(self) -> None:
)
logging.getLogger("httpx").setLevel("WARNING")
logging.getLogger("httpcore").setLevel("WARNING")
logging.getLogger("capellambse").setLevel("WARNING")
logging.getLogger("capellambse_context_diagrams").setLevel("WARNING")

def load_synchronize_config(
self,
Expand Down
30 changes: 17 additions & 13 deletions capella2polarion/connectors/polarion_repo.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,13 @@ def __init__(
):
if polarion_work_items is None:
polarion_work_items = []

check_work_items(polarion_work_items)
self._id_mapping = bidict.bidict(
{
work_item.uuid_capella: work_item.id
for work_item in polarion_work_items
},
}, # type: ignore[arg-type]
)
self._id_mapping.on_dup = bidict.OnDup(
key=bidict.DROP_OLD, val=bidict.DROP_OLD
Expand All @@ -50,12 +52,6 @@ def __sizeof__(self) -> int:
"""Return the amount of registered Capella UUIDs."""
return len(self._id_mapping)

def __getitem__(
self, item: str
) -> tuple[str, data_models.CapellaWorkItem]:
"""Return the polarion ID and work_item for a given Capella UUID."""
return self._id_mapping[item], self._work_items[item]

def __iter__(self) -> cabc.Iterator[str]:
"""Iterate all Capella UUIDs."""
return self._id_mapping.__iter__()
Expand Down Expand Up @@ -89,22 +85,20 @@ def get_work_item_by_polarion_id(
self.get_capella_uuid(work_item_id) # type: ignore
)

def update_work_items(
self,
work_items: list[data_models.CapellaWorkItem],
):
def update_work_items(self, work_items: list[data_models.CapellaWorkItem]):
"""Update all mappings for the given Work Items."""
for work_item in work_items:
assert work_item.id is not None
if uuid_capella := self._id_mapping.inverse.get(work_item.id):
del self._id_mapping[uuid_capella]
del self._work_items[uuid_capella]

check_work_items(work_items)
self._id_mapping.update(
{
work_item.uuid_capella: work_item.id
for work_item in work_items
if work_item.id is not None
}
} # type: ignore[arg-type]
)
self._work_items.update(
{work_item.uuid_capella: work_item for work_item in work_items}
Expand All @@ -127,3 +121,13 @@ def remove_work_items_by_capella_uuid(self, uuids: cabc.Iterable[str]):
workitems) as value. The project can be None and the None value means
that the document is in the same project as the model sync work items.
"""


def check_work_items(work_items: cabc.Iterable[data_models.CapellaWorkItem]):
"""Raise a ``ValueError`` if any work item has no ID."""
if work_item_without_id := next(
(wi for wi in work_items if wi.id is None), None
):
raise ValueError(
f"Found Work Item without ID: {work_item_without_id.title!r}"
)
72 changes: 41 additions & 31 deletions capella2polarion/connectors/polarion_worker.py
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,6 @@ def delete_orphaned_work_items(
If the delete flag is set to ``False`` in the context work items
are marked as ``to be deleted`` via the status attribute.
"""

existing_work_items = {
uuid
for uuid, _, work_item in self.polarion_data_repo.items()
Expand Down Expand Up @@ -192,8 +191,9 @@ def compare_and_update_work_item(
new = converter_data.work_item
assert new is not None
uuid = new.uuid_capella
_, old = self.polarion_data_repo[uuid]
old = self.polarion_data_repo.get_work_item_by_capella_uuid(uuid)
assert old is not None
assert old.id is not None

new.calculate_checksum()
if not self.force_update and new == old:
Expand All @@ -204,12 +204,14 @@ def compare_and_update_work_item(
"Update work item %r for model element %s %r...", *log_args
)

if old.get_current_checksum()[0] != "{": # XXX: Remove in next release
old_checksums = {"__C2P__WORK_ITEM": old.get_current_checksum()}
else:
old_checksums = json.loads(old.get_current_checksum())
try:
old_checksums = json.loads(old.get_current_checksum() or "")
except json.JSONDecodeError:
old_checksums = {"__C2P__WORK_ITEM": ""}

new_checksums = json.loads(new.get_current_checksum())
new_checksum = new.get_current_checksum()
assert new_checksum is not None
new_checksums = json.loads(new_checksum)

new_work_item_check_sum = new_checksums.pop("__C2P__WORK_ITEM")
old_work_item_check_sum = old_checksums.pop("__C2P__WORK_ITEM")
Expand All @@ -220,6 +222,8 @@ def compare_and_update_work_item(
old = self.project_client.work_items.get(
old.id, work_item_cls=data_models.CapellaWorkItem
)
assert old is not None
assert old.id is not None
if old.attachments:
old_attachments = (
self.project_client.work_items.attachments.get_all(
Expand All @@ -238,7 +242,7 @@ def compare_and_update_work_item(
work_item_changed |= self.update_attachments(
new, old_checksums, new_checksums, old_attachments
)
except polarion_api.PolarionApiException as error:
except (polarion_api.PolarionApiException, ValueError) as error:
logger.error(
"Updating attachments for WorkItem %r (%s %s) failed. %s",
*log_args,
Expand Down Expand Up @@ -369,15 +373,25 @@ def update_attachments(
Returns True if new attachments were created. After execution
all attachments of the new work item should have IDs.
"""
new_attachment_dict = {
attachment.file_name: attachment for attachment in new.attachments
}
old_attachment_dict = {
attachment.file_name: attachment for attachment in old_attachments
}
new_attachment_dict: dict[str, polarion_api.WorkItemAttachment] = {}
for attachment in new.attachments:
if attachment.file_name is None:
raise ValueError(
f"Found new attachment without filename: {new.id!r} with "
+ attachment.id
)
new_attachment_dict[attachment.file_name] = attachment

created = False
old_attachment_dict: dict[str, polarion_api.WorkItemAttachment] = {}
for attachment in old_attachments:
if attachment.file_name is None:
raise ValueError(
f"Found old attachment without filename: {new!r} with"
+ attachment.id
)
old_attachment_dict[attachment.file_name] = attachment

created = False
for attachment in old_attachments:
if attachment not in old_attachment_dict.values():
logger.error(
Expand All @@ -396,22 +410,21 @@ def update_attachments(
old_attachment_dict[file_name]
)

if new_attachments := list(
map(
new_attachment_dict.get,
new_attachment_file_names - old_attachment_file_names,
)
):
new_attachments: cabc.Iterable[polarion_api.WorkItemAttachment] = map(
new_attachment_dict.get, # type:ignore[arg-type]
new_attachment_file_names - old_attachment_file_names,
)
if new_attachments := list(filter(None, new_attachments)):
self.project_client.work_items.attachments.create(new_attachments)
created = True

attachments_for_update = {}
attachments_for_update: dict[str, polarion_api.WorkItemAttachment] = {}
for common_attachment_file_name in (
old_attachment_file_names & new_attachment_file_names
):
attachment = new_attachment_dict[common_attachment_file_name]
attachment.id = old_attachment_dict[common_attachment_file_name].id
if (
if attachment.file_name is not None and (
new_checksums.get(attachment.file_name)
!= old_checksums.get(attachment.file_name)
or self.force_update
Expand Down Expand Up @@ -449,14 +462,11 @@ def get_missing_link_ids(

@staticmethod
def _get_link_id(link: polarion_api.WorkItemLink) -> str:
return "/".join(
(
link.primary_work_item_id,
link.role,
link.secondary_work_item_project,
link.secondary_work_item_id,
)
)
secondary_id = link.secondary_work_item_id
if link.secondary_work_item_project:
assert link.secondary_work_item_project is not None
secondary_id = f"{link.secondary_work_item_project}/{secondary_id}"
return "/".join((link.primary_work_item_id, link.role, secondary_id))

def compare_and_update_work_items(
self, converter_session: data_session.ConverterSession
Expand Down
1 change: 1 addition & 0 deletions capella2polarion/converters/converter_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -370,6 +370,7 @@ def _filter_context_diagram_config(
def _filter_links(
c_type: str, links: list[LinkConfig], is_global: bool = False
) -> list[LinkConfig]:
c_class: type[common.ModelObject | diagram.Diagram]
if c_type == "diagram":
c_class = diagram.Diagram
else:
Expand Down
13 changes: 7 additions & 6 deletions capella2polarion/converters/document_config.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# Copyright DB InfraGO AG and contributors
# SPDX-License-Identifier: Apache-2.0
"""Module with classes and a loader for document rendering configs."""
import collections.abc as cabc
import logging
import pathlib
import typing as t
Expand Down Expand Up @@ -56,7 +57,7 @@ class BaseDocumentRenderingConfig(pydantic.BaseModel):
work_item_layouts: dict[str, WorkItemLayout] = pydantic.Field(
default_factory=dict
)
instances: list[DocumentRenderingInstance]
instances: cabc.Sequence[DocumentRenderingInstance]


class FullAuthorityDocumentRenderingConfig(BaseDocumentRenderingConfig):
Expand All @@ -69,7 +70,7 @@ class MixedAuthorityDocumentRenderingConfig(BaseDocumentRenderingConfig):
"""Mixed authority document with multiple auto generated sections."""

sections: dict[str, str]
instances: list[SectionBasedDocumentRenderingInstance]
instances: cabc.Sequence[SectionBasedDocumentRenderingInstance]


class DocumentConfigs(pydantic.BaseModel):
Expand Down Expand Up @@ -117,23 +118,23 @@ def generate_work_item_layouts(
results = []
for _type, conf in configs.items():
if conf.show_title and conf.show_description:
layouter = polarion_api.data_models.Layouter.SECTION
layouter = polarion_api.Layouter.SECTION
elif conf.show_description:
layouter = polarion_api.data_models.Layouter.PARAGRAPH
layouter = polarion_api.Layouter.PARAGRAPH
else:
if not conf.show_title:
logger.warning(
"Either the title or the description must be shown."
"For that reason, the title will be shown for %s.",
_type,
)
layouter = polarion_api.data_models.Layouter.TITLE
layouter = polarion_api.Layouter.TITLE
results.append(
polarion_api.RenderingLayout(
type=_type,
layouter=layouter,
label=polarion_html_helper.camel_case_to_words(_type),
properties=polarion_api.data_models.RenderingProperties(
properties=polarion_api.RenderingProperties(
fields_at_start=conf.fields_at_start,
fields_at_end=conf.fields_at_end,
fields_at_end_as_table=conf.show_fields_as_table,
Expand Down
5 changes: 4 additions & 1 deletion capella2polarion/converters/document_renderer.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ def __insert_work_item(

session.inserted_work_items.append(wi)
if self._is_external_document(session):
# pylint: disable-next=line-too-long
return polarion_html_helper.POLARION_WORK_ITEM_DOCUMENT_PROJECT.format(
pid=wi.id,
lid=layout_index,
Expand Down Expand Up @@ -470,7 +471,9 @@ def _render_mixed_authority_documents(

def _render_full_authority_documents(
self,
full_authority_configs,
full_authority_configs: list[
document_config.FullAuthorityDocumentRenderingConfig
],
):
for config in full_authority_configs:
rendering_layouts = document_config.generate_work_item_layouts(
Expand Down
Loading

0 comments on commit 64b8c0f

Please sign in to comment.