diff --git a/.github/workflows/ci_cd.yml b/.github/workflows/ci_cd.yml index 399a419b2..37218ec55 100644 --- a/.github/workflows/ci_cd.yml +++ b/.github/workflows/ci_cd.yml @@ -30,7 +30,7 @@ jobs: runs-on: ubuntu-latest steps: - name: PyAnsys code style checks - uses: ansys/actions/code-style@v8 + uses: ansys/actions/code-style@v9.0 with: python-version: ${{ env.MAIN_PYTHON_VERSION }} show-diff-on-failure: false @@ -55,7 +55,7 @@ jobs: python-version: [ '3.10', '3.11', '3.12', '3.13' ] steps: - name: Build wheelhouse - uses: ansys/actions/build-wheelhouse@v8 + uses: ansys/actions/build-wheelhouse@v9.0 with: library-name: ${{ env.PACKAGE_NAME }} operating-system: ${{ matrix.os }} @@ -75,7 +75,6 @@ jobs: matrix: os: [ ubuntu-latest ] python-version: [ '3.10', '3.11', '3.12', '3.13' ] - steps: - uses: actions/checkout@v4 @@ -118,7 +117,7 @@ jobs: # needs: [docs-style] steps: - name: Run Ansys documentation building action - uses: ansys/actions/doc-build@v8 + uses: ansys/actions/doc-build@v9.0 with: python-version: ${{ env.MAIN_PYTHON_VERSION }} check-links: false @@ -130,7 +129,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Build library source and wheel artifacts - uses: ansys/actions/build-library@v8 + uses: ansys/actions/build-library@v9.0 with: library-name: ${{ env.PACKAGE_NAME }} python-version: ${{ env.MAIN_PYTHON_VERSION }} @@ -142,14 +141,14 @@ jobs: runs-on: ubuntu-latest steps: - name: Release to the public PyPI repository - uses: ansys/actions/release-pypi-public@v8 + uses: ansys/actions/release-pypi-public@v9.0 with: library-name: ${{ env.PACKAGE_NAME }} twine-username: "__token__" twine-token: ${{ secrets.PYPI_TOKEN }} - name: Release to GitHub - uses: ansys/actions/release-github@v8 + uses: ansys/actions/release-github@v9.0 if: ${{ !env.ACT }} with: library-name: ${{ env.PACKAGE_NAME }} @@ -161,7 +160,7 @@ jobs: needs: [ docs, package ] steps: - name: Deploy the latest documentation - uses: ansys/actions/doc-deploy-dev@v8 + uses: ansys/actions/doc-deploy-dev@v9.0 if: ${{ !env.ACT }} with: cname: ${{ env.DOCUMENTATION_CNAME }} @@ -176,7 +175,7 @@ jobs: needs: [ docs, release ] steps: - name: Deploy the stable documentation - uses: ansys/actions/doc-deploy-stable@v8 + uses: ansys/actions/doc-deploy-stable@v9.0 if: ${{ !env.ACT }} with: cname: ${{ env.DOCUMENTATION_CNAME }} @@ -194,7 +193,7 @@ jobs: - name: Microsoft Teams Notification uses: jdcargile/ms-teams-notification@v1.4 with: - github-token: ${{ github.token }} # this will use the runner's token. + github-token: ${{ github.token }} ms-teams-webhook-uri: ${{ secrets.MS_TEAMS_WEBHOOK_URI_CI }} notification-summary: CI build failure notification-color: dc3545 diff --git a/src/ansys/dynamicreporting/core/exceptions.py b/src/ansys/dynamicreporting/core/exceptions.py index 9261557fb..aaf9cd181 100644 --- a/src/ansys/dynamicreporting/core/exceptions.py +++ b/src/ansys/dynamicreporting/core/exceptions.py @@ -135,3 +135,9 @@ class IntegrityError(ADRException): """Exception raised if there is a constraint violation while saving an object in the database.""" detail = "A database integrity check failed." + + +class InvalidFieldError(ADRException): + """Exception raised if a field is not valid.""" + + detail = "Field is invalid." diff --git a/src/ansys/dynamicreporting/core/serverless/adr.py b/src/ansys/dynamicreporting/core/serverless/adr.py index de2410fec..2c4657cc1 100644 --- a/src/ansys/dynamicreporting/core/serverless/adr.py +++ b/src/ansys/dynamicreporting/core/serverless/adr.py @@ -818,10 +818,16 @@ def copy_objects( "'target_media_dir' argument must be specified because one of the objects" " contains media to copy.'" ) - # save or load sessions, datasets - since it is possible they are shared + # try to load sessions, datasets - since it is possible they are shared # and were saved already. - session, _ = Session.get_or_create(**item.session.as_dict(), using=target_database) - dataset, _ = Dataset.get_or_create(**item.dataset.as_dict(), using=target_database) + try: + session = Session.get(guid=item.session.guid, using=target_database) + except Session.DoesNotExist: + session = Session.create(**item.session.as_dict(), using=target_database) + try: + dataset = Dataset.get(guid=item.dataset.guid, using=target_database) + except Dataset.DoesNotExist: + dataset = Dataset.create(**item.dataset.as_dict(), using=target_database) item.session = session item.dataset = dataset copy_list.append(item) diff --git a/src/ansys/dynamicreporting/core/serverless/base.py b/src/ansys/dynamicreporting/core/serverless/base.py index c8a34f53a..d56298639 100644 --- a/src/ansys/dynamicreporting/core/serverless/base.py +++ b/src/ansys/dynamicreporting/core/serverless/base.py @@ -23,8 +23,8 @@ from django.db.utils import IntegrityError as DBIntegrityError from ..exceptions import ( - ADRException, IntegrityError, + InvalidFieldError, MultipleObjectsReturnedError, ObjectDoesNotExistError, ObjectNotSavedError, @@ -44,7 +44,9 @@ def wrapper(*args, **kwargs): try: return func(*args, **kwargs) except (FieldError, FieldDoesNotExist, ValidationError, DataError) as e: - raise ADRException(extra_detail=f"One or more fields set or accessed are invalid: {e}") + raise InvalidFieldError( + extra_detail=f"One or more fields set or accessed are invalid: {e}" + ) return wrapper @@ -75,13 +77,11 @@ def __new__( if parents: # dynamically make the properties listed into class attrs if "_properties" in namespace: - dynamic_props_field = namespace["_properties"] - if hasattr(dynamic_props_field, "default"): - props = dynamic_props_field.default - new_namespace = {**namespace} - for prop in props: - new_namespace[prop] = None - new_cls = super_new(mcs, cls_name, bases, new_namespace, **kwargs) + props = namespace["_properties"] + new_namespace = {**namespace} + for prop in props: + new_namespace[prop] = None + new_cls = super_new(mcs, cls_name, bases, new_namespace, **kwargs) # save every class extending BaseModel mcs._cls_registry[cls_name] = new_cls # add exceptions @@ -266,7 +266,7 @@ def _orm_db(self) -> str: def db(self): return self._orm_db - def as_dict(self): + def as_dict(self, recursive=False) -> dict[str, Any]: out_dict = {} # use a combination of vars and fields cls_fields = set(self._get_field_names() + self._get_var_field_names()) @@ -276,6 +276,9 @@ def as_dict(self): value = getattr(self, field_, None) if value is None: # skip and use defaults continue + if isinstance(value, list) and recursive: + # convert to guids + value = [obj.guid for obj in value] out_dict[field_] = value return out_dict @@ -293,7 +296,14 @@ def _prepare_for_save(self, **kwargs): continue if isinstance(value, list): objs = [obj._orm_instance for obj in value] - getattr(self._orm_instance, field_).add(*objs) + try: + getattr(self._orm_instance, field_).add(*objs) + except (ObjectDoesNotExist, ValueError) as e: + if value: + obj_cls = value[0].__class__ + raise obj_cls.NotSaved(extra_detail=str(e)) + else: + raise ValueError(str(e)) else: if isinstance(value, BaseModel): # relations try: @@ -340,7 +350,7 @@ def delete(self, **kwargs): def from_db(cls, orm_instance, parent=None): cls_fields = dict(cls._get_field_names(with_types=True, include_private=True)) model_fields = cls._get_orm_field_names(orm_instance) - obj = cls() + obj = cls.__new__(cls) # Bypass __init__ to skip validation for field_ in model_fields: if field_ in cls_fields: attr = field_ @@ -416,6 +426,11 @@ def create(cls, **kwargs): @classmethod @handle_field_errors def get(cls, **kwargs): + """Get an object from the database using the ORM model.""" + # convert basemodel instances to orm instances + for key, value in kwargs.items(): + if isinstance(value, BaseModel): + kwargs[key] = value._orm_instance try: orm_instance = cls._orm_model_cls.objects.using(kwargs.pop("using", "default")).get( **kwargs @@ -429,39 +444,26 @@ def get(cls, **kwargs): @classmethod @handle_field_errors - def get_or_create(cls, **kwargs): - try: - return cls.get(**kwargs), False - except cls.DoesNotExist: - # Try to create an object using passed params. - try: - return cls.create(**kwargs), True - except cls.IntegrityError: - try: - return cls.get(**kwargs), False - except cls.DoesNotExist: - pass - raise - - @classmethod - @handle_field_errors - def filter(cls, **kwargs): + def filter(cls, **kwargs) -> "ObjectSet": + for key, value in kwargs.items(): + if isinstance(value, BaseModel): + kwargs[key] = value._orm_instance qs = cls._orm_model_cls.objects.using(kwargs.pop("using", "default")).filter(**kwargs) return ObjectSet(_model=cls, _orm_model=cls._orm_model_cls, _orm_queryset=qs) @classmethod @handle_field_errors - def find(cls, query="", **kwargs): + def find(cls, query="", **kwargs) -> "ObjectSet": qs = cls._orm_model_cls.find(query=query, **kwargs) return ObjectSet(_model=cls, _orm_model=cls._orm_model_cls, _orm_queryset=qs) - def get_tags(self): + def get_tags(self) -> str: return self.tags - def set_tags(self, tag_str): + def set_tags(self, tag_str: str) -> None: self.tags = tag_str - def add_tag(self, tag, value=None): + def add_tag(self, tag: str, value: str | None = None) -> None: self.rem_tag(tag) tags = shlex.split(self.get_tags()) if value: @@ -470,14 +472,14 @@ def add_tag(self, tag, value=None): tags.append(tag) self._rebuild_tags(tags) - def rem_tag(self, tag): + def rem_tag(self, tag: str) -> None: tags = shlex.split(self.get_tags()) for t in tags: if t == tag or t.split("=")[0] == tag: tags.remove(t) self._rebuild_tags(tags) - def remove_tag(self, tag): + def remove_tag(self, tag: str) -> None: self.rem_tag(tag) @@ -532,8 +534,8 @@ def delete(self): def values_list(self, *fields, flat=False): if flat and len(fields) > 1: - raise TypeError( - "'flat' is not valid when values_list is called with more than one " "field." + raise ValueError( + "'flat' is not valid when values_list is called with more than one field." ) ret = [] for obj in self._obj_set: @@ -555,11 +557,9 @@ def __get__(self, obj, obj_type=None): return getattr(obj, self._name, self._default) def __set__(self, obj, value): - cleaned_value = None - if value is not None: - cleaned_value = self.process(value, obj) + cleaned_value = self.process(value, obj) setattr(obj, self._name, cleaned_value) @abstractmethod def process(self, value, obj): - pass + pass # pragma: no cover diff --git a/src/ansys/dynamicreporting/core/serverless/item.py b/src/ansys/dynamicreporting/core/serverless/item.py index 6d3958ebc..a248430ae 100644 --- a/src/ansys/dynamicreporting/core/serverless/item.py +++ b/src/ansys/dynamicreporting/core/serverless/item.py @@ -62,7 +62,7 @@ def reset(self): class ItemContent(Validator): def process(self, value, obj): if value is None: - raise ValueError(extra_detail="Content cannot be None") + raise ValueError("Content cannot be None") return value @@ -206,16 +206,24 @@ class FilePayloadMixin: _file_ext: str = field(init=False, compare=False, default="") @property - def file_ext(self): - return self._file_ext + def file_path(self): + try: + return self._orm_instance.payloadfile.path + except (AttributeError, ValueError): + # If the file path is not set, return None + return None @property def has_file(self): - return self._file is not None + return self.file_path is not None and Path(self.file_path).is_file() @property - def file_path(self): - return self._orm_instance.payloadfile.path + def file_ext(self): + try: + return Path(self._orm_instance.payloadfile.path).suffix.lower().lstrip(".") + except (AttributeError, ValueError): + # If the file path is not set, return None + return None @classmethod def from_db(cls, orm_instance, **kwargs): @@ -236,7 +244,6 @@ def _save_file(target_path, content): out_file.write(chunk) def save(self, **kwargs): - # todo: check backward compatibility: _movie is now _anim. self._orm_instance.payloadfile = f"{self.guid}_{self.type}.{self._file_ext}" # Save file to the target path self._save_file(self.file_path, self._file) @@ -269,9 +276,8 @@ def __init_subclass__(cls, **kwargs): Item._type_registry[cls.type] = cls def __post_init__(self): - # todo: can be bypassed by setting type at instantiation - if self.type == "none": - raise TypeError("Cannot instantiate Item directly. Use Item.create()") + if self.__class__ is Item: + raise ADRException("Cannot instantiate Item directly. Use the Item.create() method.") super().__post_init__() def save(self, **kwargs): @@ -312,27 +318,26 @@ def create(cls, **kwargs): return super().create(**new_kwargs) @classmethod - def get(cls, **kwargs): - new_kwargs = {"type": cls.type, **kwargs} if cls.type != "none" else kwargs - return super().get(**new_kwargs) + def _validate_kwargs(cls, **kwargs): + if "content" in kwargs: + raise ValueError("'content' kwarg is not supported for get and filter methods") + return {"type": cls.type, **kwargs} if cls.type != "none" else kwargs @classmethod - def get_or_create(cls, **kwargs): - new_kwargs = {"type": cls.type, **kwargs} if cls.type != "none" else kwargs - return super().get_or_create(**new_kwargs) + def get(cls, **kwargs): + return super().get(**cls._validate_kwargs(**kwargs)) @classmethod def filter(cls, **kwargs): - new_kwargs = {"type": cls.type, **kwargs} if cls.type != "none" else kwargs - return super().filter(**new_kwargs) + return super().filter(**cls._validate_kwargs(**kwargs)) @classmethod def find(cls, query="", **kwargs): - if cls.type == "none": + if cls is Item: return super().find(query=query, **kwargs) if "i_type|cont" in query: raise ADRException( - extra_detail="The 'i_type' filter is not required if using a subclass of Item" + extra_detail="The 'i_type' filter is not allowed if using a subclass of Item" ) return super().find(query=f"A|i_type|cont|{cls.type};{query}", **kwargs) # noqa: E702 @@ -426,11 +431,13 @@ def save(self, **kwargs): target_ext = "png" if not self._enhanced else self._file_ext self._orm_instance.payloadfile = f"{self.guid}_image.{target_ext}" # Save the image - if target_ext == "png" and self._file_ext != target_ext: + if self._file_ext != target_ext and target_ext == "png": + # Convert to PNG format + self._file_ext = target_ext try: - image.save(self.file_path, format="PNG") + image.save(self.file_path, format=self._file_ext.upper()) except OSError as e: - print(f"Error converting image to PNG: {e}") + raise ADRException(f"Error converting image to {self._file_ext}: {e}") from e else: # save image as is (if enhanced or already PNG) self._save_file(self.file_path, img_bytes) image.close() diff --git a/src/ansys/dynamicreporting/core/serverless/template.py b/src/ansys/dynamicreporting/core/serverless/template.py index a1a08be05..6ac53f536 100644 --- a/src/ansys/dynamicreporting/core/serverless/template.py +++ b/src/ansys/dynamicreporting/core/serverless/template.py @@ -16,11 +16,11 @@ class Template(BaseModel): item_filter: str = field(compare=False, kw_only=True, default="") parent: "Template" = field(compare=False, kw_only=True, default=None) children: list["Template"] = field(compare=False, kw_only=True, default_factory=list) + report_type: str = "" _children_order: str = field( compare=False, init=False, default="" ) # computed from self.children _master: bool = field(compare=False, init=False, default=True) - report_type: str = "" _properties: tuple = tuple() # todo: add properties of each type ref: report_objects _orm_model: str = "reports.models.Template" # Class-level registry of subclasses keyed by type @@ -32,7 +32,7 @@ def __init_subclass__(cls, **kwargs): Template._type_registry[cls.report_type] = cls def __post_init__(self): - if self.report_type == "": + if self.__class__ is Template: raise TypeError("Cannot instantiate Template directly. Use Template.create()") super().__post_init__() @@ -58,8 +58,8 @@ def master(self): return self._master def save(self, **kwargs): - if self.parent is not None and not self.parent._saved: - raise Template.NotSaved( + if self.parent is not None and not self.parent.saved: + raise self.parent.__class__.NotSaved( extra_detail="Failed to save template because its parent is not saved" ) children_order = [] @@ -68,8 +68,8 @@ def save(self, **kwargs): raise TypeError( f"Failed to save template because child '{child}' is not a Template object" ) - if not child._saved: - raise Template.NotSaved( + if not child.saved: + raise child.__class__.NotSaved( extra_detail="Failed to save template because its children are not saved" ) children_order.append(child.guid) @@ -122,27 +122,26 @@ def create(cls, **kwargs): return super().create(**new_kwargs) @classmethod - def get(cls, **kwargs): - new_kwargs = {"report_type": cls.report_type, **kwargs} if cls.report_type else kwargs - return super().get(**new_kwargs) + def _validate_kwargs(cls, **kwargs): + if "children" in kwargs: + raise ValueError("'children' kwarg is not supported for get and filter methods") + return {"report_type": cls.report_type, **kwargs} if cls.report_type else kwargs @classmethod - def get_or_create(cls, **kwargs): - new_kwargs = {"report_type": cls.report_type, **kwargs} if cls.report_type else kwargs - return super().get_or_create(**new_kwargs) + def get(cls, **kwargs): + return super().get(**cls._validate_kwargs(**kwargs)) @classmethod def filter(cls, **kwargs): - new_kwargs = {"report_type": cls.report_type, **kwargs} if cls.report_type else kwargs - return super().filter(**new_kwargs) + return super().filter(**cls._validate_kwargs(**kwargs)) @classmethod def find(cls, query="", **kwargs): - if not cls.report_type: + if cls is Template: return super().find(query=query, **kwargs) if "t_types|cont" in query: raise ADRException( - extra_detail="The 't_types' filter is not required if using a subclass of Template" + extra_detail="The 't_types' filter is not allowed if using a subclass of Template" ) query_string = f"A|t_types|cont|{cls.report_type};{query}" # noqa: E702 return super().find(query=query_string, **kwargs) diff --git a/tests/serverless/conftest.py b/tests/serverless/conftest.py index a1e5de38c..1ad693245 100644 --- a/tests/serverless/conftest.py +++ b/tests/serverless/conftest.py @@ -4,7 +4,7 @@ import pytest from ansys.dynamicreporting.core.constants import DOCKER_DEV_REPO_URL -from ansys.dynamicreporting.core.serverless import ADR +from ansys.dynamicreporting.core.serverless import ADR, Item, Template @pytest.fixture(scope="session") diff --git a/tests/serverless/test_item.py b/tests/serverless/test_item.py index 71121ad07..e2540ae6b 100644 --- a/tests/serverless/test_item.py +++ b/tests/serverless/test_item.py @@ -1,5 +1,35 @@ +from pathlib import Path +import tempfile +from unittest import mock +from uuid import uuid4 + +from PIL import Image as PILImage import pytest +from ansys.dynamicreporting.core.exceptions import ADRException + + +@pytest.mark.ado_test +def test_field_error(adr_serverless): + from ansys.dynamicreporting.core.exceptions import InvalidFieldError + from ansys.dynamicreporting.core.serverless import HTML + + with pytest.raises(InvalidFieldError): + assert HTML.get(lol=1) + + +@pytest.mark.ado_test +def test_field_type_error(adr_serverless): + from ansys.dynamicreporting.core.serverless import HTML + + with pytest.raises(TypeError): + HTML.create( + name=1, # error + content="

Heading 1

", + session=adr_serverless.session, + dataset=adr_serverless.dataset, + ) + @pytest.mark.ado_test def test_create_html_cls(adr_serverless): @@ -15,25 +45,230 @@ def test_create_html_cls(adr_serverless): @pytest.mark.ado_test -def test_add_rem_tag(adr_serverless): +def test_create_item_cls(adr_serverless): + from ansys.dynamicreporting.core.serverless import Item + + intro_html = Item.create( + name="test_create_item_cls", + type="html", + content="

Heading 1

", + session=adr_serverless.session, + dataset=adr_serverless.dataset, + ) + assert Item.get(guid=intro_html.guid).guid == intro_html.guid + + +@pytest.mark.ado_test +def test_create_item_cls_error(adr_serverless): + from ansys.dynamicreporting.core.serverless import Item + + with pytest.raises(ADRException): + Item.create( + name="test_create_item_cls_error", + content="

Heading 1

", + session=adr_serverless.session, + dataset=adr_serverless.dataset, + ) + + +@pytest.mark.ado_test +def test_item_cls_init(adr_serverless): + from ansys.dynamicreporting.core.serverless import Item + + with pytest.raises(ADRException): + Item( + name="test_item_cls_init", + type="tree", + content="

Heading 1

", + session=adr_serverless.session, + dataset=adr_serverless.dataset, + ) + + +@pytest.mark.ado_test +def test_item_get_w_content_error(adr_serverless): + from ansys.dynamicreporting.core.serverless import Item + + intro_html = Item.create( + name="test_create_item_cls", + type="html", + content="

Heading 1

", + session=adr_serverless.session, + dataset=adr_serverless.dataset, + ) + with pytest.raises(ValueError): + Item.get(content=intro_html.content) + + +@pytest.mark.ado_test +def test_item_filter(adr_serverless): from ansys.dynamicreporting.core.serverless import HTML intro_html = HTML.create( - name="test_create_html_cls", + name="test_item_filter", + content="

Heading 1

", + session=adr_serverless.session, + dataset=adr_serverless.dataset, + ) + assert ( + HTML.filter( + name="test_item_filter", session=adr_serverless.session, dataset=adr_serverless.dataset + )[0].guid + == intro_html.guid + ) + + +@pytest.mark.ado_test +def test_item_cls_filter(adr_serverless): + from ansys.dynamicreporting.core.serverless import HTML, Item + + intro_html = HTML.create( + name="test_item_cls_filter", + content="

Heading 1

", + session=adr_serverless.session, + dataset=adr_serverless.dataset, + ) + assert ( + Item.filter( + name="test_item_cls_filter", + session=adr_serverless.session, + dataset=adr_serverless.dataset, + )[0].guid + == intro_html.guid + ) + + +@pytest.mark.ado_test +def test_item_find(adr_serverless): + from ansys.dynamicreporting.core.serverless import HTML + + intro_html = HTML.create( + name="test_item_find", + content="

Heading 1

", + session=adr_serverless.session, + dataset=adr_serverless.dataset, + ) + assert HTML.find(query="A|i_name|cont|test_item_find")[0].guid == intro_html.guid + + +@pytest.mark.ado_test +def test_item_find_raises_exception(adr_serverless): + from ansys.dynamicreporting.core.exceptions import ADRException + from ansys.dynamicreporting.core.serverless import HTML + + with pytest.raises(ADRException): + HTML.find(query="A|i_type|cont|html") + + +@pytest.mark.ado_test +def test_item_cls_find(adr_serverless): + from ansys.dynamicreporting.core.serverless import HTML, Item + + intro_html = HTML.create( + name="test_item_cls_find", + content="

Heading 1

", + session=adr_serverless.session, + dataset=adr_serverless.dataset, + ) + assert Item.find(query="A|i_name|cont|test_item_cls_find")[0].guid == intro_html.guid + + +@pytest.mark.ado_test +def test_str(adr_serverless): + from ansys.dynamicreporting.core.serverless import HTML + + intro_html = HTML.create( + name="test_str", content="

Heading 1

", session=adr_serverless.session, dataset=adr_serverless.dataset, ) - intro_html.add_tag("pptx_slide_title", "headers and breaks") + assert str(intro_html) == f"" + + +@pytest.mark.ado_test +def test_repr(adr_serverless): + from ansys.dynamicreporting.core.serverless import HTML + + intro_html = HTML.create( + name="test_repr", + content="

Heading 1

", + session=adr_serverless.session, + dataset=adr_serverless.dataset, + ) + assert repr(intro_html) == f"" + + +@pytest.mark.ado_test +def test_add_tag(adr_serverless): + from ansys.dynamicreporting.core.serverless import HTML + + intro_html = HTML.create( + name="test_add_tag", + content="

Heading 1

", + session=adr_serverless.session, + dataset=adr_serverless.dataset, + ) + intro_html.add_tag("pptx_slide_title", value="headers and breaks") intro_html.save() assert "pptx_slide_title" in HTML.get(guid=intro_html.guid).get_tags() - intro_html.rem_tag("pptx_slide_title") - intro_html.remove_tag("pptx_slide_title") + +@pytest.mark.ado_test +def test_add_tag_key(adr_serverless): + from ansys.dynamicreporting.core.serverless import String + + intro_text = String( + name="intro_text", + content="intro text", + tags="dp=dp227 section=intro", + source="sls-test", + session=adr_serverless.session, + dataset=adr_serverless.dataset, + ) + intro_text.add_tag("sls-test") + intro_text.save() + + assert "sls-test" in String.get(guid=intro_text.guid).tags + + +@pytest.mark.ado_test +def test_rem_tag(adr_serverless): + from ansys.dynamicreporting.core.serverless import HTML + + intro_html = HTML.create( + name="test_rem_tag", + content="

Heading 1

", + tags="tag1=1 tag2", + session=adr_serverless.session, + dataset=adr_serverless.dataset, + ) + intro_html.rem_tag("tag1") + intro_html.remove_tag("tag2") intro_html.save() - assert "pptx_slide_title" not in HTML.get(guid=intro_html.guid).get_tags() + tags = HTML.get(guid=intro_html.guid).get_tags() + assert "tag1" not in tags and "tag2" not in tags + + +@pytest.mark.ado_test +def test_rem_empty_tag(adr_serverless): + from ansys.dynamicreporting.core.serverless import HTML + + intro_html = HTML.create( + name="test_rem_empty_tag", + content="

Heading 1

", + tags="", + session=adr_serverless.session, + dataset=adr_serverless.dataset, + ) + intro_html.rem_tag("tag1") + intro_html.remove_tag("tag2") + intro_html.save() + + tags = HTML.get(guid=intro_html.guid).get_tags() + assert "tag1" not in tags and "tag2" not in tags @pytest.mark.ado_test @@ -57,10 +292,824 @@ def test_get_tags(adr_serverless): from ansys.dynamicreporting.core.serverless import HTML intro_html = HTML.create( - name="test_set_tags", + name="test_get_tags", tags="dp=dp227", content="

Heading 1

", session=adr_serverless.session, dataset=adr_serverless.dataset, ) assert "dp=dp227" in HTML.get(guid=intro_html.guid).get_tags() + + +@pytest.mark.ado_test +def test_db(adr_serverless): + from ansys.dynamicreporting.core.serverless import HTML + + intro_html = HTML.create( + name="test_db", + content="

Heading 1

", + session=adr_serverless.session, + dataset=adr_serverless.dataset, + ) + from django.conf import settings + + assert intro_html.db in settings.DATABASES + + +@pytest.mark.ado_test +def test_reinit(adr_serverless): + from ansys.dynamicreporting.core.serverless import HTML + + intro_html = HTML.create( + name="test_reinit", + content="

Heading 1

", + session=adr_serverless.session, + dataset=adr_serverless.dataset, + ) + intro_html.reinit() + assert intro_html.saved is False + + +@pytest.mark.ado_test +def test_integrity_error(adr_serverless): + from ansys.dynamicreporting.core.serverless import HTML + + intro_html = HTML.create( + name="test_integrity_error", + content="

Heading 1

", + session=adr_serverless.session, + dataset=adr_serverless.dataset, + ) + with pytest.raises(HTML.IntegrityError): + HTML.create( + guid=intro_html.guid, + name="test_integrity_error", + content="

Heading 1

", + session=adr_serverless.session, + dataset=adr_serverless.dataset, + ) + + +@pytest.mark.ado_test +def test_delete_not_saved(adr_serverless): + from ansys.dynamicreporting.core.serverless import HTML + + intro_html = HTML( + name="test_delete_not_saved", + content="

Heading 1

", + session=adr_serverless.session, + dataset=adr_serverless.dataset, + ) + with pytest.raises(HTML.NotSaved): + intro_html.delete() + + +@pytest.mark.ado_test +def test_save_item(adr_serverless): + from ansys.dynamicreporting.core.serverless import HTML + + intro_html = HTML( + name="test_save_item", + content="

Heading 1

", + session=adr_serverless.session, + dataset=adr_serverless.dataset, + ) + intro_html.save() + assert HTML.get(guid=intro_html.guid).guid == intro_html.guid + + +@pytest.mark.ado_test +def test_save_item_no_session(adr_serverless): + from ansys.dynamicreporting.core.serverless import HTML + + intro_html = HTML( + name="test_save_item_no_session", + content="

Heading 1

", + dataset=adr_serverless.dataset, + ) + with pytest.raises(ADRException): + intro_html.save() + + +@pytest.mark.ado_test +def test_save_item_no_dataset(adr_serverless): + from ansys.dynamicreporting.core.serverless import HTML + + intro_html = HTML( + name="test_save_item_no_dataset", + content="

Heading 1

", + session=adr_serverless.session, + ) + with pytest.raises(ADRException): + intro_html.save() + + +@pytest.mark.ado_test +def test_save_item_session_unsaved(adr_serverless): + from ansys.dynamicreporting.core.serverless import HTML, Session + + session = Session() + intro_html = HTML( + name="test_save_item_session_unsaved", + content="

Heading 1

", + session=session, + dataset=adr_serverless.dataset, + ) + with pytest.raises(Session.NotSaved): + intro_html.save() + + +@pytest.mark.ado_test +def test_save_item_dataset_unsaved(adr_serverless): + from ansys.dynamicreporting.core.serverless import HTML, Dataset + + dataset = Dataset() + intro_html = HTML( + name="test_save_item_dataset_unsaved", + content="

Heading 1

", + session=adr_serverless.session, + dataset=dataset, + ) + with pytest.raises(Dataset.NotSaved): + intro_html.save() + + +@pytest.mark.ado_test +def test_create_item(adr_serverless): + from ansys.dynamicreporting.core.serverless import Tree + + tree_content = [ + {"key": "root", "name": "Solver", "value": "My Solver"}, + {"key": "root", "name": "Number cells", "value": 10e6}, + {"key": "root", "name": "Mesh Size", "value": "1.0 mm^3"}, + {"key": "root", "name": "Mesh Type", "value": "Hex8"}, + ] + + tree_kwargs = { + "name": "test_create_item", + "source": "sls-test", + "content": tree_content, + "tags": "dp=dp227", + "session": adr_serverless.session, + "dataset": adr_serverless.dataset, + } + tree = Tree.create(**tree_kwargs) + assert tree.saved is True + + +@pytest.mark.ado_test +def test_get_item(adr_serverless): + from ansys.dynamicreporting.core.serverless import HTML + + intro_html = HTML.create( + name="test_get_item", + content="

Heading 1

", + session=adr_serverless.session, + dataset=adr_serverless.dataset, + ) + item = HTML.get(guid=intro_html.guid) + assert item.guid == intro_html.guid + + +@pytest.mark.ado_test +def test_item_delete(adr_serverless): + from ansys.dynamicreporting.core.serverless import HTML + + intro_html = HTML.create( + name="test_item_delete", + content="

Heading 1

", + session=adr_serverless.session, + dataset=adr_serverless.dataset, + ) + intro_html.delete() + with pytest.raises(HTML.DoesNotExist): + HTML.get(guid=intro_html.guid) + + +@pytest.mark.ado_test +def test_get_item_does_not_exist(adr_serverless): + from ansys.dynamicreporting.core.serverless import HTML + + with pytest.raises(HTML.DoesNotExist): + HTML.get(guid=str(uuid4())) + + +@pytest.mark.ado_test +def test_get_item_multiple(adr_serverless): + from ansys.dynamicreporting.core.serverless import HTML + + HTML.create( + name="test_get_item_multiple", + content="

Heading 1

", + session=adr_serverless.session, + dataset=adr_serverless.dataset, + ) + HTML( + name="test_get_item_multiple", + content="

Heading 2

", + session=adr_serverless.session, + dataset=adr_serverless.dataset, + ).save() + with pytest.raises(HTML.MultipleObjectsReturned): + HTML.get(name="test_get_item_multiple") + + +@pytest.mark.ado_test +def test_item_objectset_repr(adr_serverless): + from ansys.dynamicreporting.core.serverless import HTML + + intro_html = HTML.create( + name="test_item_objectset_repr", + content="

Heading 1

", + source="sls-test", + tags="dp=dp227", + session=adr_serverless.session, + dataset=adr_serverless.dataset, + ) + objs = adr_serverless.query(query_type=HTML, query="A|i_name|cont|test_item_objectset_repr;") + assert repr(objs) == f"]>" + + +@pytest.mark.ado_test +def test_item_objectset_str(adr_serverless): + from ansys.dynamicreporting.core.serverless import HTML + + intro_html = HTML.create( + name="test_item_objectset_str", + content="

Heading 1

", + source="sls-test", + tags="dp=dp227", + session=adr_serverless.session, + dataset=adr_serverless.dataset, + ) + objs = adr_serverless.query(query_type=HTML, query="A|i_name|cont|test_item_objectset_str;") + assert str(objs) == f"[]" + + +@pytest.mark.ado_test +def test_item_objectset_delete(adr_serverless): + from ansys.dynamicreporting.core.serverless import HTML + + intro_html = HTML.create( + name="test_item_objectset_delete", + content="

Heading 1

", + source="sls-test", + tags="dp=dp227", + session=adr_serverless.session, + dataset=adr_serverless.dataset, + ) + objs = adr_serverless.query(query_type=HTML, query="A|i_name|cont|test_item_objectset_delete;") + objs.delete() + with pytest.raises(HTML.DoesNotExist): + HTML.get(guid=intro_html.guid) + + +@pytest.mark.ado_test +def test_item_objectset_getitem(adr_serverless): + from ansys.dynamicreporting.core.serverless import HTML + + intro_html = HTML.create( + name="test_item_objectset_getitem", + content="

Heading 1

", + source="sls-test", + tags="dp=dp227", + session=adr_serverless.session, + dataset=adr_serverless.dataset, + ) + objs = adr_serverless.query(query_type=HTML, query="A|i_name|cont|test_item_objectset_getitem;") + assert objs[0].guid == intro_html.guid + + +@pytest.mark.ado_test +def test_item_objectset_saved(adr_serverless): + from ansys.dynamicreporting.core.serverless import HTML + + HTML.create( + name="test_item_objectset_saved", + content="

Heading 1

", + source="sls-test", + tags="dp=dp227", + session=adr_serverless.session, + dataset=adr_serverless.dataset, + ) + objs = adr_serverless.query(query_type=HTML, query="A|i_name|cont|test_item_objectset_saved;") + assert objs.saved is True + + +@pytest.mark.ado_test +def test_item_objectset_values_list(adr_serverless): + from ansys.dynamicreporting.core.serverless import HTML + + HTML.create( + name="test_item_objectset_values_list", + content="

Heading 1

", + source="sls-test", + tags="dp=dp227", + session=adr_serverless.session, + dataset=adr_serverless.dataset, + ) + objs = adr_serverless.query( + query_type=HTML, query="A|i_name|cont|test_item_objectset_values_list;" + ) + assert objs.values_list("name", flat=True) == ["test_item_objectset_values_list"] + + +@pytest.mark.ado_test +def test_item_objectset_values_list_error(adr_serverless): + from ansys.dynamicreporting.core.serverless import HTML + + HTML.create( + name="test_item_objectset_values_list_error", + content="

Heading 1

", + source="sls-test", + tags="dp=dp227", + session=adr_serverless.session, + dataset=adr_serverless.dataset, + ) + objs = adr_serverless.query( + query_type=HTML, query="A|i_name|cont|test_item_objectset_values_list_error;" + ) + with pytest.raises(ValueError): + objs.values_list("name", "guid", flat=True) + + +@pytest.mark.ado_test +def test_item_objectset_values_list_empty(adr_serverless): + from ansys.dynamicreporting.core.serverless import HTML + + objs = adr_serverless.query( + query_type=HTML, query="A|i_name|cont|test_item_objectset_values_list_empty;" + ) + assert objs.values_list("name", flat=True) == [] + + +@pytest.mark.ado_test +def test_string_content_none(adr_serverless): + from ansys.dynamicreporting.core.serverless import String + + with pytest.raises(ValueError): + String( + name="test_string_content_none", + content=None, + tags="dp=dp227", + source="sls-test", + session=adr_serverless.session, + dataset=adr_serverless.dataset, + ) + + +@pytest.mark.ado_test +def test_string_content_wrong_type(adr_serverless): + from ansys.dynamicreporting.core.serverless import String + + with pytest.raises(TypeError): + String( + name="test_string_content_wrong_type", + content=1, + tags="dp=dp227", + source="sls-test", + session=adr_serverless.session, + dataset=adr_serverless.dataset, + ) + + +@pytest.mark.ado_test +def test_string_content_empty(adr_serverless): + from ansys.dynamicreporting.core.serverless import String + + string = String( + name="test_string_content_empty", + content="", + tags="dp=dp227", + source="sls-test", + session=adr_serverless.session, + dataset=adr_serverless.dataset, + ) + string.save() + assert String.get(guid=string.guid).content == "" + + +@pytest.mark.ado_test +def test_table_content_not_numpy(adr_serverless): + from ansys.dynamicreporting.core.serverless import Table + + with pytest.raises(TypeError): + Table( + name="test_table_content_not_numpy", + content="", + tags="dp=dp227", + source="sls-test", + session=adr_serverless.session, + dataset=adr_serverless.dataset, + ) + + +@pytest.mark.ado_test +def test_table_content_invalid_dtype(adr_serverless): + import numpy as np + + from ansys.dynamicreporting.core.serverless import Table + + with pytest.raises(TypeError): + Table( + name="test_table_content_invalid_dtype", + content=np.array([[1, 2], [3, 4]], dtype=int), # Invalid dtype + session=adr_serverless.session, + tags="dp=dp227", + source="sls-test", + dataset=adr_serverless.dataset, + ) + + +@pytest.mark.ado_test +def test_table_content_invalid_shape(adr_serverless): + import numpy as np + + from ansys.dynamicreporting.core.serverless import Table + + with pytest.raises(ValueError): + Table( + name="test_table_content_invalid_shape", + content=np.array([1, 2, 3], dtype="|S20"), # Invalid shape (1D array instead of 2D) + session=adr_serverless.session, + tags="dp=dp227", + source="sls-test", + dataset=adr_serverless.dataset, + ) + + +@pytest.mark.ado_test +def test_create_tree_success(adr_serverless): + from ansys.dynamicreporting.core.serverless import Tree + + tree_content = [ + {"key": "root", "name": "Solver", "value": "My Solver"}, + {"key": "root", "name": "Number cells", "value": 10e6}, + ] + tree = Tree.create( + name="test_create_tree_success", + content=tree_content, + tags="dp=dp227 section=data", + session=adr_serverless.session, + dataset=adr_serverless.dataset, + source="sls-test", + ) + assert Tree.get(guid=tree.guid).guid == tree.guid + + +@pytest.mark.ado_test +@pytest.mark.parametrize( + "bad_content", + [ + [{"name": "Missing key", "value": "Oops"}], # Missing 'key' + [{"key": "root", "value": "Oops"}], # Missing 'name' + [{"key": "root", "name": "Missing value"}], # Missing 'value' + ], +) +def test_create_tree_missing_keys(adr_serverless, bad_content): + from ansys.dynamicreporting.core.serverless import Tree + + with pytest.raises(ValueError): + Tree.create( + name="test_create_tree_missing_keys", + content=bad_content, + tags="dp=dp227 section=data", + session=adr_serverless.session, + dataset=adr_serverless.dataset, + source="sls-test", + ) + + +@pytest.mark.ado_test +def test_create_tree_invalid_value_type(adr_serverless): + from ansys.dynamicreporting.core.serverless import Tree + + bad_content = [{"key": "root", "name": "Invalid value", "value": {"not": "allowed"}}] + with pytest.raises(ValueError): + Tree.create( + name="test_create_tree_invalid_value_type", + content=bad_content, + tags="dp=dp227 section=data", + session=adr_serverless.session, + dataset=adr_serverless.dataset, + source="sls-test", + ) + + +@pytest.mark.ado_test +def test_create_tree_non_dict_element(adr_serverless): + from ansys.dynamicreporting.core.serverless import Tree + + bad_content = [ + {"key": "root", "name": "Good", "value": "OK"}, + "I am not a dictionary", + ] + with pytest.raises(ValueError): + Tree.create( + name="test_create_tree_non_dict_element", + content=bad_content, + tags="dp=dp227 section=data", + session=adr_serverless.session, + dataset=adr_serverless.dataset, + source="sls-test", + ) + + +@pytest.mark.ado_test +def test_create_tree_invalid_nested_value(adr_serverless): + from ansys.dynamicreporting.core.serverless import Tree + + bad_content = [ + { + "key": "root", + "name": "Nested Tree", + "value": "Root", + "children": [{"key": "child", "name": "Bad Child", "value": {"bad": "value"}}], + } + ] + with pytest.raises(ValueError): + Tree.create( + name="test_create_tree_invalid_nested_value", + content=bad_content, + tags="dp=dp227 section=data", + session=adr_serverless.session, + dataset=adr_serverless.dataset, + source="sls-test", + ) + + +@pytest.mark.ado_test +def test_tree_content_value_list_valid(adr_serverless): + from ansys.dynamicreporting.core.serverless import Tree + + tree_content = [ + {"key": "root", "name": "Solver List", "value": ["Ansys", "Fluent", "CFX"]}, + ] + tree = Tree.create( + name="test_tree_content_value_list_valid", + content=tree_content, + tags="dp=dp227", + session=adr_serverless.session, + dataset=adr_serverless.dataset, + source="sls-test", + ) + assert Tree.get(guid=tree.guid).guid == tree.guid + + +@pytest.mark.ado_test +def test_tree_content_value_list_invalid(adr_serverless): + from ansys.dynamicreporting.core.serverless import Tree + + tree_content = [ + {"key": "root", "name": "Solver List", "value": ["Ansys", object()]}, + ] + with pytest.raises(ValueError): + Tree.create( + name="test_tree_content_value_list_invalid", + content=tree_content, + tags="dp=dp227", + session=adr_serverless.session, + dataset=adr_serverless.dataset, + source="sls-test", + ) + + +@pytest.mark.ado_test +def test_tree_content_invalid(adr_serverless): + from ansys.dynamicreporting.core.serverless import Tree + + tree_content = "This is not a valid content" + with pytest.raises(ValueError): + Tree.create( + name="test_tree_content_invalid", + content=tree_content, + tags="dp=dp227", + session=adr_serverless.session, + dataset=adr_serverless.dataset, + source="sls-test", + ) + + +@pytest.mark.ado_test +def test_item_file_ext_no_save(adr_serverless): + from ansys.dynamicreporting.core.serverless import Image + + # image + intro_image = Image( + name="test_item_file_ext_no_save", + content=str(Path(__file__).parent / "test_data" / "nexus_logo.png"), + tags="dp=dp227 section=data", + source="sls-test", + session=adr_serverless.session, + dataset=adr_serverless.dataset, + ) + assert intro_image.file_ext is None + + +@pytest.mark.ado_test +def test_item_file_ext(adr_serverless): + from ansys.dynamicreporting.core.serverless import Image + + # image + intro_image = Image( + name="test_item_file_ext", + content=str(Path(__file__).parent / "test_data" / "nexus_logo.png"), + tags="dp=dp227 section=data", + source="sls-test", + session=adr_serverless.session, + dataset=adr_serverless.dataset, + ) + intro_image.save() + + assert intro_image.file_ext == "png" + + +@pytest.mark.ado_test +def test_item_has_no_file(adr_serverless): + from ansys.dynamicreporting.core.serverless import Image + + # image + intro_image = Image( + name="test_item_has_no_file", + content=str(Path(__file__).parent / "test_data" / "nexus_logo.png"), + tags="dp=dp227 section=data", + source="sls-test", + session=adr_serverless.session, + dataset=adr_serverless.dataset, + ) + + assert intro_image.has_file is False + + +@pytest.mark.ado_test +def test_item_has_file(adr_serverless): + from ansys.dynamicreporting.core.serverless import Image + + # image + intro_image = Image( + name="test_item_has_file", + content=str(Path(__file__).parent / "test_data" / "nexus_logo.png"), + tags="dp=dp227 section=data", + source="sls-test", + session=adr_serverless.session, + dataset=adr_serverless.dataset, + ) + intro_image.save() + + assert intro_image.has_file is True + + +@pytest.mark.ado_test +def test_item_is_enhanced(adr_serverless): + from ansys.dynamicreporting.core.serverless import Image + + # image + intro_image = Image( + name="test_item_is_enhanced", + content=str(Path(__file__).parent / "test_data" / "nexus_logo.png"), + tags="dp=dp227 section=data", + source="sls-test", + ) + + assert intro_image.enhanced is False + + +@pytest.mark.ado_test +def test_file_size_zero_fails_validation(adr_serverless): + from ansys.dynamicreporting.core.serverless import File + + with tempfile.NamedTemporaryFile(delete=False, suffix=".txt") as tmp: + tmp_path = Path(tmp.name) + + try: + with pytest.raises(ValueError, match="The file specified is empty"): + File.create( + name="test_file_size_zero_fails_validation", + content=str(tmp_path), + tags="dp=dp227", + session=adr_serverless.session, + dataset=adr_serverless.dataset, + source="sls-test", + ) + finally: + tmp_path.unlink() + + +def test_image_on_disk(adr_serverless): + from ansys.dynamicreporting.core.serverless import Image + + with tempfile.NamedTemporaryFile(suffix=".png", delete=False) as tmp: + tmp_path = Path(tmp.name) + img = PILImage.new("RGB", (10, 10), color="blue") + img.save(tmp_path, "PNG") + + try: + image_obj = Image.create( + name="test_image_on_disk", + content=str(tmp_path), + tags="dp=dp227", + session=adr_serverless.session, + dataset=adr_serverless.dataset, + source="sls-test", + ) + assert Path(image_obj.file_path).is_file() is True and image_obj.file_ext == "png" + finally: + tmp_path.unlink(missing_ok=True) + + +@pytest.mark.ado_test +def test_image_conversion_to_png(adr_serverless): + from ansys.dynamicreporting.core.serverless import Image + + with tempfile.NamedTemporaryFile(suffix=".jpg", delete=False) as tmp: + tmp_path = Path(tmp.name) + img = PILImage.new("RGB", (10, 10), color="red") + img.save(tmp_path, "JPEG") # Save as JPEG + + try: + image_obj = Image.create( + name="test_image_conversion_to_png", + content=str(tmp_path), + tags="dp=dp227", + session=adr_serverless.session, + dataset=adr_serverless.dataset, + source="sls-test", + ) + file_path = Path(image_obj.file_path) + assert ( + Path(image_obj._file.name).suffix == ".jpg" + and file_path.is_file() + and file_path.suffix == ".png" + ) + finally: + tmp_path.unlink(missing_ok=True) + + +@pytest.mark.ado_test +def test_invalid_file_extension_fails(adr_serverless): + from ansys.dynamicreporting.core.serverless import Image + + with tempfile.NamedTemporaryFile(suffix=".txt", delete=False) as tmp: + tmp_path = Path(tmp.name) + tmp.write(b"Dummy content") + tmp.flush() + + try: + with pytest.raises(ValueError, match="File type txt is not supported"): + Image.create( + name="test_invalid_file_extension_fails", + content=str(tmp_path), + tags="dp=dp227", + session=adr_serverless.session, + dataset=adr_serverless.dataset, + source="sls-test", + ) + finally: + tmp_path.unlink(missing_ok=True) + + +@pytest.mark.ado_test +def test_image_save_raises_adr_exception(adr_serverless): + from ansys.dynamicreporting.core.serverless import Image + + with tempfile.NamedTemporaryFile(suffix=".jpg", delete=False) as tmp: + tmp_path = Path(tmp.name) + img = PILImage.new("RGB", (10, 10), color="red") + img.save(tmp_path, "JPEG") + + try: + with mock.patch("PIL.Image.Image.save", side_effect=OSError("Simulated save error")): + with pytest.raises(ADRException, match="Error converting image"): + Image.create( + name="test_image_save_raises_adr_exception", + content=str(tmp_path), + tags="dp=dp227", + session=adr_serverless.session, + dataset=adr_serverless.dataset, + source="sls-test", + ) + finally: + tmp_path.unlink(missing_ok=True) + + +@pytest.mark.ado_test +def test_is_enhanced_fails_on_non_enhanced_tiff(adr_serverless): + from ansys.dynamicreporting.core.serverless import Image + + with tempfile.NamedTemporaryFile(suffix=".tiff", delete=False) as tmp: + tmp_path = Path(tmp.name) + img = PILImage.new("RGB", (10, 10), color="blue") + img.save(tmp_path, format="TIFF") + + try: + with pytest.raises(ADRException, match="The enhanced image is empty"): + Image.create( + name="test_is_enhanced_fails_on_non_enhanced_tiff", + content=str(tmp_path), + tags="dp=dp227", + session=adr_serverless.session, + dataset=adr_serverless.dataset, + source="sls-test", + ) + finally: + tmp_path.unlink(missing_ok=True) diff --git a/tests/serverless/test_template.py b/tests/serverless/test_template.py index e69de29bb..0a905d201 100644 --- a/tests/serverless/test_template.py +++ b/tests/serverless/test_template.py @@ -0,0 +1,181 @@ +import pytest + + +@pytest.mark.ado_test +def test_create_template_cls(adr_serverless): + from ansys.dynamicreporting.core.serverless import PanelLayout + + results_panel = PanelLayout.create(name="test_create_template_cls", tags="dp=dp227") + + assert PanelLayout.get(name="test_create_template_cls").guid == results_panel.guid + + +@pytest.mark.ado_test +def test_init_template_cls(adr_serverless): + from ansys.dynamicreporting.core.serverless import PanelLayout + + results_panel = PanelLayout(name="test_init_template_cls", tags="dp=dp227") + results_panel.save() + + assert PanelLayout.get(name="test_init_template_cls").guid == results_panel.guid + + +@pytest.mark.ado_test +def test_edit_template(adr_serverless): + # Templates/reports + from ansys.dynamicreporting.core.serverless import PanelLayout + + results_panel = PanelLayout.create(name="test_edit_template", tags="dp=dp227") + results_panel.params = ( + '{"HTML": "

Results

\\nYour simulation results.", "properties": {"TOCItem": "1"}}' + ) + results_panel.save() + + assert "Your simulation results" in PanelLayout.get(name="test_edit_template").params + + +@pytest.mark.ado_test +def test_raise_child_type_init(adr_serverless): + # Templates/reports + from ansys.dynamicreporting.core.serverless import BasicLayout + + with pytest.raises(TypeError): + BasicLayout( + name="test_raise_child_type_init", parent=None, tags="dp=dp227", children=["T1"] + ) + + +@pytest.mark.ado_test +def test_raise_child_type_create(adr_serverless): + from ansys.dynamicreporting.core.serverless import BasicLayout + + with pytest.raises(TypeError): + BasicLayout.create( + name="test_raise_child_type_create", parent=None, tags="dp=dp227", children=["T1"] + ) + + +@pytest.mark.ado_test +def test_raise_child_type_save(adr_serverless): + from ansys.dynamicreporting.core.serverless import BasicLayout + + with pytest.raises(TypeError): + top_parent = BasicLayout(name="test_raise_child_type_save", parent=None, tags="dp=dp227") + top_parent.children.append("T1") + top_parent.save() + + +@pytest.mark.ado_test +def test_as_dict(adr_serverless): + from ansys.dynamicreporting.core.serverless import BasicLayout, TOCLayout + + top_parent = adr_serverless.create_template( + BasicLayout, + name="test_as_dict", + parent=None, + tags="dp=dp227", + params='{"HTML": "

Serverless Simulation Report

"}', + ) + + toc_layout = adr_serverless.create_template( + TOCLayout, name="TOC", parent=top_parent, tags="dp=dp227" + ) + toc_layout.params = '{"TOCitems": 1, "HTML": "

Table of Content

"}' + toc_layout.set_filter("A|i_name|eq|__NonexistentName__;") + toc_layout.save() + + top_dict = top_parent.as_dict(recursive=True) + + assert ( + top_dict["name"] == "test_as_dict" + and top_dict["tags"] == "dp=dp227" + and top_dict["params"] == '{"HTML": "

Serverless Simulation Report

"}' + and top_dict["children"][0] == toc_layout.guid + ) + + +@pytest.mark.ado_test +def test_parent_not_saved(adr_serverless): + from ansys.dynamicreporting.core.serverless import BasicLayout, TOCLayout + + top_parent = BasicLayout( + name="test_parent_not_saved", + parent=None, + tags="dp=dp227", + params='{"HTML": "

Serverless Simulation Report

"}', + ) + toc_layout = TOCLayout(name="TOC", parent=top_parent, tags="dp=dp227") + with pytest.raises(BasicLayout.NotSaved): + toc_layout.save() + + +@pytest.mark.ado_test +def test_child_bad_type(adr_serverless): + from ansys.dynamicreporting.core.serverless import HTML, BasicLayout + + top_parent = adr_serverless.create_template( + BasicLayout, + name="test_child_bad_type", + parent=None, + tags="dp=dp227", + params='{"HTML": "

Serverless Simulation Report

"}', + ) + # Wrong type of child + toc_layout = HTML.create( + name="test_child_bad_type_item", + content="

Heading 1

", + session=adr_serverless.session, + dataset=adr_serverless.dataset, + ) + top_parent.children.append(toc_layout) + with pytest.raises(TypeError): + top_parent.save() + + +@pytest.mark.ado_test +def test_child_not_saved(adr_serverless): + from ansys.dynamicreporting.core.serverless import BasicLayout, TOCLayout + + top_parent = adr_serverless.create_template( + BasicLayout, + name="test_child_not_saved", + parent=None, + tags="dp=dp227", + params='{"HTML": "

Serverless Simulation Report

"}', + ) + toc_layout = TOCLayout(name="TOC", parent=top_parent, tags="dp=dp227") + top_parent.children.append(toc_layout) + with pytest.raises(TOCLayout.NotSaved): + top_parent.save() + + +@pytest.mark.ado_test +def test_child_not_exist(adr_serverless): + from ansys.dynamicreporting.core.serverless import BasicLayout, TOCLayout + + top_parent = adr_serverless.create_template( + BasicLayout, + name="test_child_not_exist", + parent=None, + tags="dp=dp227", + params='{"HTML": "

Serverless Simulation Report

"}', + ) + toc_layout = TOCLayout(name="TOC", parent=top_parent, tags="dp=dp227") + toc_layout._saved = True # Simulate that the child is saved, but does not exist in the database + top_parent.children.append(toc_layout) + with pytest.raises(TOCLayout.NotSaved): + top_parent.save() + + +@pytest.mark.ado_test +def test_create_template(adr_serverless): + from ansys.dynamicreporting.core.serverless import BasicLayout + + template_kwargs = { + "name": "test_create_template", + "parent": None, + "tags": "dp=dp227", + "params": '{"HTML": "

Serverless Simulation Report

"}', + } + template = BasicLayout.create(**template_kwargs) + assert template.saved is True