unicat.utils
from unicat.utils import *
def gid() -> str # a uuid4
def maybe_gid(possible_gid: Any) -> bool
def test_true(data: Any) -> bool
def test_false(data: Any) -> bool
def make_bool(any: Any) -> bool
def make_bool_str(any: Any) -> str
def make_str(any: Any) -> str
def make_json_str(any: Any) -> str
def make_json(any: Any) -> Any
def make_json_list(any: Any) -> list[Any]
def make_int(any: Any) -> int
def make_float(any: Any) -> float
def noop(any: Any) -> Any
def make_str_list(any: Any) -> list[str]
def convert_value_to_fielddata(fieldtype: str, value: Any) -> bool | str | Any | int | float | list[str] | list[Any]
"""Return a value suitable for writing to Unicat, for list field types this
means converting newline-separated entries to lists.
For class or classlist fields, we produce a JSON data structure.
This can raise exceptions when the value doesn't match the type, e.g. converting a
string to a float.
"""
def convert_fielddata_to_value(fieldtype: str, fielddata: Any) -> str | int | float
"""Return a value suitable for writing in a cell, such as a text or a number.
Lists will be flattened by stringifying each item and concatenating with \n.
For class or classlist fields, we will have pretty-printed JSON.
"""
def merge_dicts(a: dict, b: dict) -> dict
"""Merge b into a, returning a.
a is updated; if you don't want to mutate a, call it as `merge_dicts(dict(a), b)`.
"""
def diff_record_fields_data(unicat: Unicat, record: UnicatRecord, localizedfielddata: dict) -> dict # dict format: d[language][fieldname] = value
"""Return a version of localizedfielddata that only has data that is different from
the raw record data.
"""
def hash_text(text: str) -> str # unicode ok
def hash_data(data: Any) -> str # data must be json-serializable
class DuckObject # quickly construct an object-like duck from a dict or kwargs, see below
class FieldColumns # flatten a list of (class-)fields, see below
DuckObjects are used to quickly construct an object-like duck.
Uses keyword arguments to construct any duck-like object with those attributes.
ducklike = DuckObject(walk="waddle", talk="quack")
assert ducklike.walk == "waddle"
assert ducklike.talk == "quack"
assert not hasattr(ducklike, "bill")
duckquery = DuckObject(
q="",
filter=["value", "is", [base_artnr_field.gid, article.base_artnr]]
)
print(duckquery.q, duckquery.filter)
duckrecord = DuckObject(gid="<anything gid-like>")
print(duckrecord.gid)
FieldColumns are needed for class fields, that have nested subfields.
With FieldColumns, we can flatten a list of fields to a list of columns, associated with those fields.
Each column has a fieldstack, that is a list of the field and nested subfields. For a regular field, the fieldstack is just that field. A classfield with three subfields will yield three columns, and each column has a fieldstack with the classfield first, and then the subfield for that column. If that subfield is another classfield, we'll get more columns and deeper stackfields.
For example, you have [image, dimensions]
as fields, and dimensions
is of type class, and has subfields width
and length
; FieldColumns will give you [image, dimensions.width, dimensions.length]
, useable for writing to tab-separated files or spreadsheets.
Client code can look something like this, for reading from Unicat:
columns = FieldColumns(fields, prefix_column_count=3)
for column in columns:
column_name = column.name() # e.g. 'image' or 'dimensions.width__mm'
column_label = column.label() # e.g. 'Image' or 'Dimensions\nWidth [mm]'
column_value = None
error = None
try:
record_field = column.extract_record_field(record.fields[language])
column_value = record_field.value if record_field else None
except KeyError:
error = (
"Field is not part of this definition. Do not enter data here."
)
FieldColumns can also be used to write to Unicat, when we need a record's fields data structure:
recordfields_data = {}
for column in columns:
fieldvalue = row[column.index].value
column.update_fields_data(recordfields_data, fieldvalue)
unicat.mutate.update_record(record, {language: recordfields_data})