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