Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

1306 add reference property for storedfiles #1309

Merged
merged 11 commits into from
Apr 17, 2023
4 changes: 3 additions & 1 deletion CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ Change Log

UNRELEASED
_______________
* :star: Added the possibility to created, edit and delete `StoredFiles` (#1298)
* :star: Added the possibility to create, edit and delete `StoredFiles` (#1298)
* :star: Added the concept of a `StoredFilesReferenceProperty` (#1306)
* :star: Added `fileDisplay` representation for `StoredFilesReferenceProperty` (#1306)

v4.6.0 (16FEB23)
----------------
Expand Down
9 changes: 9 additions & 0 deletions pykechain/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,12 +80,14 @@
from .models.form import Form
from .models.input_checks import (
check_base,
check_date,
check_datetime,
check_enum,
check_list_of_base,
check_list_of_dicts,
check_list_of_text,
check_text,
check_time,
check_type,
check_url,
check_user,
Expand Down Expand Up @@ -2083,6 +2085,13 @@ def create_property(
)
options.update(scope_options)

elif property_type == PropertyType.DATETIME_VALUE:
default_value = check_datetime(dt=default_value, key="default_value")
elif property_type == PropertyType.DATE_VALUE:
default_value = check_date(dt=default_value, key="default_value")
elif property_type == PropertyType.TIME_VALUE:
default_value = check_time(dt=default_value, key="default_value")
KaczuH marked this conversation as resolved.
Show resolved Hide resolved

data = dict(
name=check_text(name, "name"),
part_id=model.id,
Expand Down
1 change: 0 additions & 1 deletion pykechain/defaults.py
Original file line number Diff line number Diff line change
Expand Up @@ -715,7 +715,6 @@
"created_at",
"updated_at",
"tags",

]
)
},
Expand Down
18 changes: 16 additions & 2 deletions pykechain/enums.py
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,7 @@ class PropertyType(Enum):
STATUS_REFERENCES_VALUE = "STATUS_REFERENCES_VALUE"
TEAM_REFERENCES_VALUE = "TEAM_REFERENCES_VALUE"
USER_REFERENCES_VALUE = "USER_REFERENCES_VALUE"
STOREDFILE_REFERENCES_VALUE = "STOREDFILE_REFERENCES_VALUE"
JSON_VALUE = "JSON_VALUE"
GEOJSON_VALUE = "GEOJSON_VALUE"
WEATHER_VALUE = "WEATHER_VALUE"
Expand Down Expand Up @@ -756,6 +757,19 @@ class PropertyRepresentation(Enum):
USE_PROPERTY_NAME = "usePropertyName"
CAMERA_SCANNER_INPUT = "cameraScannerInput"
SIGNATURE = "signature"
FILE_DISPLAY = "fileDisplay"


class FileDisplayRepresentationValues(Enum):
"""
Values that can be put in the FileDisplayRepresentationValues for representing stored files.

:cvar CARDS: thumbnails of attachments inside stored files, when applicable.
:cvar TEXT: name of the attachments inside stored files.
"""

CARDS = "CARDS"
TEXT = "TEXT"


class SignatureRepresentationValues(Enum):
Expand All @@ -766,8 +780,8 @@ class SignatureRepresentationValues(Enum):
:cvar NAME_AND_DATE: A name and Date background to the signature field when filling in.
"""

CLEAN = 'clean'
NAME_AND_DATE = 'nameAndDate'
CLEAN = "clean"
NAME_AND_DATE = "nameAndDate"


class GeoCoordinateConfig(Enum):
Expand Down
4 changes: 4 additions & 0 deletions pykechain/models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
ContextReferencesProperty,
ScopeReferencesProperty,
StatusReferencesProperty,
StoredFilesReferencesProperty,
UserReferencesProperty,
FormReferencesProperty,
)
Expand Down Expand Up @@ -55,6 +56,7 @@
"FormReferencesProperty",
"ContextReferencesProperty",
"StatusReferencesProperty",
'StoredFilesReferencesProperty',
]

# This map is used to identify the correct class for the (KE-chain provided) property type.
Expand All @@ -70,6 +72,7 @@
PropertyType.FORM_REFERENCES_VALUE: FormReferencesProperty,
PropertyType.CONTEXT_REFERENCES_VALUE: ContextReferencesProperty,
PropertyType.STATUS_REFERENCES_VALUE: StatusReferencesProperty,
PropertyType.STOREDFILE_REFERENCES_VALUE: StoredFilesReferencesProperty,
}

__all__ = (
Expand Down Expand Up @@ -102,6 +105,7 @@
"UserReferencesProperty",
"FormReferencesProperty",
"StatusReferencesProperty",
"StoredFilesReferencesProperty",
"AnyProperty",
"PropertyValueFilter",
"Notification",
Expand Down
73 changes: 55 additions & 18 deletions pykechain/models/input_checks.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,22 @@
from __future__ import annotations

import warnings
from datetime import datetime
from datetime import date, datetime, time
from enum import Enum
from typing import Any, Callable, Dict, Iterable, List, Optional, Union

import jsonschema

from pykechain.exceptions import IllegalArgumentError
from pykechain.utils import Empty, empty, is_url, is_uuid, parse_datetime
from pykechain.utils import (
Empty,
empty,
is_url,
is_uuid,
parse_date,
parse_datetime,
parse_time,
)

iter_types = (list, tuple, set)

Expand Down Expand Up @@ -154,23 +162,52 @@ def check_enum(value: Optional[Any], enum: type(Enum), key: str) -> Optional[Any

def check_datetime(dt: Optional[Union[datetime, str]], key: str) -> Optional[str]:
"""Validate a datetime value to be a datetime and be timezone aware."""
if dt is not None and dt is not empty:
if isinstance(dt, str):
dt = parse_datetime(dt)

if isinstance(dt, datetime):
if not dt.tzinfo:
warnings.warn(
f"`{key}` `{dt.isoformat(sep=' ')}` is naive and not timezone aware, "
f"use pytz.timezone info. Date will be interpreted as UTC time."
)
dt = dt.isoformat(sep="T")
else:
raise IllegalArgumentError(
f"`{key}` should be a correctly formatted string or a datetime.datetime() object, "
f'"{dt}" ({type(dt)}) is not.'
if dt is None or dt is empty:
return dt
if isinstance(dt, str):
dt = parse_datetime(dt)
if isinstance(dt, datetime):
if not dt.tzinfo:
warnings.warn(
f"`{key}` `{dt.isoformat(sep=' ')}` is naive and not timezone aware, "
f"use pytz.timezone info. Date will be interpreted as UTC time."
)
return dt
return dt.isoformat(sep="T")
else:
raise IllegalArgumentError(
f"`{key}` should be a correctly formatted string or a datetime.datetime() object, "
f'"{dt}" ({type(dt)}) is not.'
)


def check_date(dt: Optional[Union[datetime, str]], key: str) -> Optional[str]:
"""Validate a date value to be a date."""
if dt is None or dt is empty:
return dt
if isinstance(dt, str):
dt = parse_date(dt)
if isinstance(dt, date):
return dt.strftime("%Y-%m-%d")
else:
raise IllegalArgumentError(
f"`{key}` should be a correctly formatted string or a datetime.date() object, "
f'"{dt}" ({type(dt)}) is not.'
)


def check_time(dt: Optional[Union[datetime, str]], key: str) -> Optional[str]:
"""Validate a time value to be a time."""
if dt is None or dt is empty:
return dt
if isinstance(dt, str):
dt = parse_time(dt)
if isinstance(dt, time):
return dt.strftime("%H:%M:%S")
else:
raise IllegalArgumentError(
f"`{key}` should be a correctly formatted string or a datetime.time() object, "
f'"{dt}" ({type(dt)}) is not.'
)


def check_base(
Expand Down
43 changes: 33 additions & 10 deletions pykechain/models/property_reference.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,13 @@
from pykechain.defaults import PARTS_BATCH_LIMIT
from pykechain.exceptions import IllegalArgumentError
from pykechain.models import Activity, Scope, user
from pykechain.models.base_reference import _ReferenceProperty, _ReferencePropertyInScope
from pykechain.models.base_reference import (
_ReferenceProperty,
_ReferencePropertyInScope,
)
from pykechain.models.context import Context
from pykechain.models.form import Form
from pykechain.models.stored_file import StoredFile
from pykechain.models.value_filter import ScopeFilter
from pykechain.models.workflow import Status
from pykechain.utils import get_in_chunks
Expand Down Expand Up @@ -171,7 +175,7 @@ class FormReferencesProperty(_ReferencePropertyInScope):
.. versionadded:: 3.7
"""

# REFERENCED_CLASS = Form
REFERENCED_CLASS = Form

def _retrieve_objects(self, **kwargs) -> List[Form]:
"""
Expand All @@ -194,7 +198,7 @@ class ContextReferencesProperty(_ReferencePropertyInScope):
.. versionadded:: 3.7
"""

# REFERENCED_CLASS = Context
REFERENCED_CLASS = Context

def _retrieve_objects(self, **kwargs) -> List[Context]:
"""
Expand All @@ -212,23 +216,42 @@ def _retrieve_objects(self, **kwargs) -> List[Context]:


class StatusReferencesProperty(_ReferenceProperty):
"""A virtual object representing a KE-chain Context References property.
"""A virtual object representing a KE-chain Status References property.

.. versionadded:: 3.19
"""

# REFERENCED_CLASS = Status
REFERENCED_CLASS = Status

def _retrieve_objects(self, **kwargs) -> List[Context]:
def _retrieve_objects(self, **kwargs) -> List[Status]:
"""
Retrieve a list of Contexts.
Retrieve a list of Statuses.

:param kwargs: optional inputs
:return: list of Context objects
:return: list of Status objects
"""
statuses = []
for statuse_json in self._value:
status = Status(client=self._client, json=statuse_json)
for status_json in self._value:
status = Status(client=self._client, json=status_json)
status.refresh() # To populate the object with all expected data
statuses.append(status)
return statuses


class StoredFilesReferencesProperty(_ReferenceProperty):
"""A virtual object representing a KE-chain StoredFile References property.

.. versionadded:: 4.7
"""

REFERENCED_CLASS = StoredFile

def _retrieve_objects(self, **kwargs) -> List[StoredFile]:
"""
Retrieve a list of StoredFile.

:param kwargs: optional inputs
:return: list of StoredFile objects
"""
return [StoredFile(client=self._client, json=stored_files_json) for stored_files_json in
self._value]
3 changes: 3 additions & 0 deletions pykechain/models/representations/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
LinkTarget,
SignatureRepresentation,
SignificantDigits,
StoredFilesDisplayRepresentation,
ThousandsSeparator,
UsePropertyNameRepresentation,
)
Expand All @@ -40,6 +41,8 @@
CameraScannerInputRepresentation,
# Attachment properties
SignatureRepresentation,
# Stored files reference properties
StoredFilesDisplayRepresentation,
)

__all__ = (
Expand Down
2 changes: 2 additions & 0 deletions pykechain/models/representations/component.py
Original file line number Diff line number Diff line change
Expand Up @@ -158,5 +158,7 @@ def _valid_object_type(representation: BaseRepresentation, obj: "Base") -> bool:
)
elif rtype == _AllRepresentations.SIGNATURE:
return obj.type == PropertyType.ATTACHMENT_VALUE
elif rtype == _AllRepresentations.FILE_DISPLAY:
return obj.type == PropertyType.STOREDFILE_REFERENCES_VALUE
else:
return False
22 changes: 21 additions & 1 deletion pykechain/models/representations/representations.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from typing import Any

from pykechain.enums import (
FontAwesomeMode,
FileDisplayRepresentationValues, FontAwesomeMode,
GeoCoordinateConfig,
LinkTargets,
PropertyRepresentation,
Expand Down Expand Up @@ -281,3 +281,23 @@ def validate_representation(self, value: Any) -> None:
check_enum(
value, SignatureRepresentationValues, "signature representation values"
)


class StoredFilesDisplayRepresentation(SimpleConfigValueKeyRepresentation):
"""Representation for the stored files display inside a `StoredFilesReferencesProperty`."""

rtype = PropertyRepresentation.FILE_DISPLAY
_config_value_key = "mode"

def validate_representation(self, value: Any) -> None:
"""
Validate whether the representation value can be set.

:param value: representation value to set.
:type value: Any
:raises IllegalArgumentError
:return: None
"""
check_enum(
value, FileDisplayRepresentationValues, "file display representation values"
)
Loading