Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
1395d91
Update ci_cd.yml
viseshrp Apr 11, 2025
51b1369
disable ci
viseshrp Apr 11, 2025
c2cb9c5
add add_properties alias for templates
viseshrp Apr 11, 2025
158b4af
start make use of dynamic template props
viseshrp Apr 11, 2025
d197281
Update test_template.py
viseshrp Apr 11, 2025
04a9b74
fix dynamic prop ref
viseshrp Apr 11, 2025
5434fc3
Update test_template.py
viseshrp Apr 11, 2025
8d56c05
use enums for types
viseshrp Apr 11, 2025
7b924af
test_table_item_properties
viseshrp Apr 12, 2025
248aa40
test property methods
viseshrp Apr 12, 2025
1706672
Update test_template.py
viseshrp Apr 12, 2025
e2d37a9
Update test_template.py
viseshrp Apr 12, 2025
0e78276
Update test_template.py
viseshrp Apr 12, 2025
08c3513
Update test_template.py
viseshrp Apr 12, 2025
5460b24
Update test_template.py
viseshrp Apr 12, 2025
d3c7a67
Update test_template.py
viseshrp Apr 12, 2025
4f03bd3
Update test_template.py
viseshrp Apr 12, 2025
83383bb
Update test_template.py
viseshrp Apr 12, 2025
ab66c1a
Update test_template.py
viseshrp Apr 13, 2025
8cbf04c
fix type checks in find
viseshrp Apr 13, 2025
3539953
Update test_template.py
viseshrp Apr 13, 2025
f07a020
Revert "disable ci"
viseshrp Apr 13, 2025
6f6aa93
Reapply "disable ci"
viseshrp Apr 14, 2025
318c142
update docs
viseshrp Apr 14, 2025
f80fd5e
add media dir to fixture
viseshrp Apr 14, 2025
db16577
add ADR init tests
viseshrp Apr 14, 2025
b05d384
undo init path tests
viseshrp Apr 15, 2025
604c65e
allow Path objs in backup_database
viseshrp Apr 15, 2025
130a96c
Update test_adr.py
viseshrp Apr 15, 2025
7ff6d92
allow Path objs in restore_database
viseshrp Apr 15, 2025
c571127
Update test_adr.py
viseshrp Apr 15, 2025
50d8e10
Update .gitignore
viseshrp Apr 15, 2025
a1678e3
add tests for restore
viseshrp Apr 15, 2025
a59e0ae
rename
viseshrp Apr 15, 2025
db14eb1
ignore some exts from codespell
viseshrp Apr 15, 2025
cd94427
backup without foreign keys
viseshrp Apr 15, 2025
3dd5d92
update test files
viseshrp Apr 15, 2025
726e227
add ignore_primary_keys option to backup_database
viseshrp Apr 15, 2025
7a74bf7
update tests
viseshrp Apr 15, 2025
b2f1cb4
fix
viseshrp Apr 15, 2025
f3edb41
Update test_adr.py
viseshrp Apr 15, 2025
bb69c29
rewrite some error handling
viseshrp Apr 15, 2025
b6d11ea
Update test_adr.py
viseshrp Apr 15, 2025
f9b66eb
Update test_adr.py
viseshrp Apr 15, 2025
0a31207
fix tests
viseshrp Apr 15, 2025
454adaf
fix create_objects cross db copy
viseshrp Apr 15, 2025
0076a90
Update test_adr.py
viseshrp Apr 15, 2025
bc004c2
Revert "Reapply "disable ci""
viseshrp Apr 15, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/ci_cd.yml
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,7 @@ jobs:
if: ${{ !env.ACT }}
with:
library-name: ${{ env.PACKAGE_NAME }}
token: ${{ secrets.GITHUB_TOKEN }}

upload_dev_docs:
name: Upload dev documentation
Expand Down
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -75,4 +75,3 @@ doc/_build
*.tiff
_test_enhanced_images.py
local_tests/
*.gz
75 changes: 75 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,81 @@ installation and the test file you are trying to run.

Note that any tests that require Docker will obviously fail.

Creating a Release
------------------

- Before creating a new branch, make sure your local repository is up to date:

.. code-block:: bash

git pull

This ensures you have the latest changes from the default branch (usually ``main`` or ``develop``).

- Create a new branch for the release:

.. code-block:: bash

git checkout -b release/0.10

**Important:**
The release branch must only include the **major** and **minor** version numbers.
Do not include the patch version.
For example, use ``release/0.10``, not ``release/0.10.0``.

- If creating a **patch release**, do not create a new branch.
Instead, reuse the existing ``release/0.10`` branch.

- Update the version number in ``pyproject.toml``:

If the current version is:

.. code-block:: toml

version = "0.10.0.dev0"

bump it to:

.. code-block:: toml

version = "0.10.0"

- **Important:**
Every time you create a development (``dev``) release, you should first release the corresponding stable version on PyPI before bumping the development version.

For example:

- If you are at ``0.10.0.dev0``, first release ``0.10.0`` on PyPI.
- Then, after the release, bump the version to ``0.10.1.dev0``.

Otherwise, it may feel confusing to have a ``dev`` version without a corresponding stable release.

- Create a commit for the version bump:

.. code-block:: bash

git commit -am "MAINT: Bump version to v0.10.0"

- Then push the branch:

.. code-block:: bash

git push --set-upstream origin release/0.10

- Create a tag for the release:

.. code-block:: bash

git tag v0.10.0
git push origin v0.10.0

**Important:**
The release tag must always include the full **major.minor.patch** version number.
Always include the ``v`` prefix.
For example, use ``v0.10.0``, not ``v0.10``.
Creating and pushing the tag automatically triggers the release workflow in GitHub Actions.


Dependencies
------------
To use PyDynamicReporting, you must have a locally installed and licensed copy
Expand Down
4 changes: 2 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ authors = [

maintainers = [
{name = "ANSYS, Inc.", email = "pyansys.core@ansys.com"},
{name = "Ansys ADR Team", email = "nexus@ansys.com"},
{name = "Ansys ADR Team", email = "adrteam@ansys.com"},
]
description = "Python interface to Ansys Dynamic Reporting"
readme = "README.rst"
Expand Down Expand Up @@ -161,7 +161,7 @@ src_paths = ["doc", "src", "tests"]

[tool.codespell]
ignore-words = "doc/styles/Vocab/ANSYS/accept.txt"
skip = '*.pyc,*.xml,*.gif,*.png,*.jpg,*.js,*.html,doc/source/examples/**/*.ipynb'
skip = '*.pyc,*.xml,*.gif,*.png,*.jpg,*.js,*.html,doc/source/examples/**/*.ipynb,*.json,*.gz'
quiet-level = 3

[tool.bandit]
Expand Down
1 change: 0 additions & 1 deletion src/ansys/dynamicreporting/core/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
# Version
# ------------------------------------------------------------------------------
from pathlib import Path

try:
import importlib.metadata as importlib_metadata # type: ignore
Expand Down
80 changes: 42 additions & 38 deletions src/ansys/dynamicreporting/core/serverless/adr.py
Original file line number Diff line number Diff line change
Expand Up @@ -415,9 +415,7 @@ def setup(self, collect_static: bool = False) -> None:
if self._debug is not None:
overrides["DEBUG"] = self._debug

# cannot be None
overrides["MEDIA_ROOT"] = str(self._media_directory)

if self._static_directory is not None:
overrides["STATIC_ROOT"] = str(self._static_directory)

Expand Down Expand Up @@ -536,14 +534,19 @@ def close(self):
# close db connections
try:
connections.close_all()
except DatabaseError:
except DatabaseError: # pragma: no cover
pass
# cleanup temp files
for tmp_dir in self._tmp_dirs:
tmp_dir.cleanup()

def backup_database(
self, output_directory: str = ".", *, database: str = "default", compress=False
self,
output_directory: str | Path = ".",
*,
database: str = "default",
compress: bool = False,
ignore_primary_keys: bool = False,
) -> None:
if self._in_memory:
raise ADRException("Backup is not available in in-memory mode.")
Expand All @@ -555,21 +558,25 @@ def backup_database(
# call django management command to dump the database
timestamp = datetime.now().strftime("%Y%m%d%H%M%S")
file_path = target_dir / f"backup_{timestamp}.json{'.gz' if compress else ''}"
args = [
"dumpdata",
"--all",
"--database",
database,
"--output",
str(file_path),
"--verbosity",
0,
"--natural-foreign",
]
if ignore_primary_keys:
args.append("--natural-primary")
try:
management.call_command(
"dumpdata",
"--all",
"--database",
database,
"--output",
str(file_path),
"--verbosity",
0,
)
management.call_command(*args)
except Exception as e:
raise ADRException(f"Backup failed: {e}")

def restore_database(self, input_file: str, *, database: str = "default") -> None:
def restore_database(self, input_file: str | Path, *, database: str = "default") -> None:
if database != "default" and database not in self._databases:
raise ADRException(f"{database} must be configured first using the 'databases' option.")
backup_file = Path(input_file).resolve(strict=True)
Expand Down Expand Up @@ -647,10 +654,9 @@ def session_guid(self) -> uuid.UUID:

def create_item(self, item_type: type[Item], **kwargs: Any) -> Item:
if not issubclass(item_type, Item):
raise TypeError(f"{item_type} is not valid")
raise TypeError(f"{item_type.__name__} is not a subclass of Item")
if not kwargs:
raise ADRException("At least one keyword argument must be provided to create the item.")
kwargs["_in_memory"] = self._in_memory
return item_type.create(
session=kwargs.pop("session", self._session),
dataset=kwargs.pop("dataset", self._dataset),
Expand All @@ -660,7 +666,7 @@ def create_item(self, item_type: type[Item], **kwargs: Any) -> Item:
@staticmethod
def create_template(template_type: type[Template], **kwargs: Any) -> Template:
if not issubclass(template_type, Template):
raise TypeError(f"{template_type} is not valid")
raise TypeError(f"{template_type.__name__} is not a subclass of Template")
if not kwargs:
raise ADRException(
"At least one keyword argument must be provided to create the template."
Expand All @@ -681,34 +687,30 @@ def get_report(**kwargs) -> Template:
try:
return Template.get(parent=None, **kwargs)
except Exception as e:
raise e
raise ADRException(f"Report not found: {e}")

@staticmethod
def get_reports(*, fields: list | None = None, flat: bool = False) -> ObjectSet | list:
# return list of reports by default.
# if fields are mentioned, return value list
try:
out = Template.filter(parent=None)
if fields:
out = out.values_list(*fields, flat=flat)
except Exception as e:
raise e

out = Template.filter(parent=None)
if fields:
out = out.values_list(*fields, flat=flat)
return out

def get_list_reports(self, *, r_type: str = "name") -> ObjectSet | list:
def get_list_reports(self, r_type: str | None = "name") -> ObjectSet | list:
supported_types = ("name", "report")
if r_type not in supported_types:
if r_type and r_type not in supported_types:
raise ADRException(f"r_type must be one of {supported_types}")
if r_type == "name":
return self.get_reports(
fields=[
r_type,
],
flat=True,
)
else:
if not r_type or r_type == "report":
return self.get_reports()
# if r_type == "name":
return self.get_reports(
fields=[
r_type,
],
flat=True,
)

def render_report(
self, *, context: dict | None = None, item_filter: str = "", **kwargs: Any
Expand All @@ -732,7 +734,9 @@ def query(
**kwargs: Any,
) -> ObjectSet:
if not issubclass(query_type, (Item, Template, Session, Dataset)):
raise TypeError(f"{query_type} is not valid")
raise TypeError(
f"'{query_type.__name__}' is not a type of Item, Template, Session, or Dataset"
)
return query_type.find(query=query, **kwargs)

@staticmethod
Expand All @@ -744,7 +748,7 @@ def create_objects(
raise ADRException("objects must be an iterable")
count = 0
for obj in objects:
if kwargs.get("using", "default") != obj.db:
if obj.db and kwargs.get("using", "default") != obj.db:
# required if copying across databases
obj.reinit()
obj.save(**kwargs)
Expand Down
11 changes: 11 additions & 0 deletions src/ansys/dynamicreporting/core/serverless/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
from collections.abc import Iterable
from dataclasses import dataclass, field
from dataclasses import fields as dataclass_fields
from enum import Enum
import importlib
import inspect
from itertools import chain
Expand Down Expand Up @@ -563,3 +564,13 @@ def __set__(self, obj, value):
@abstractmethod
def process(self, value, obj):
pass # pragma: no cover


class StrEnum(str, Enum):
"""Enum with a str mixin."""

def __str__(self):
return self.value

def __repr__(self):
return self.value
Loading
Loading