labfreed 1.0.0a6__py3-none-any.whl → 1.0.0a7__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
labfreed/__init__.py CHANGED
@@ -2,7 +2,7 @@
2
2
  Python implementation of LabFREED building blocks
3
3
  '''
4
4
 
5
- __version__ = "1.0.0a6"
5
+ __version__ = "1.0.0a7"
6
6
 
7
7
  from labfreed.pac_id import * # noqa: F403
8
8
  from labfreed.pac_cat import * # noqa: F403
@@ -32,7 +32,7 @@ class Labfreed_App_Infrastructure():
32
32
  self._http_client= http_client
33
33
  callback = http_attribute_request_default_callback_factory(http_client)
34
34
 
35
- self._attribute_client = AttributeClient(http_post_callback=callback, cache_store=MemoryAttributeCache())
35
+ self._attribute_client = AttributeClient(http_post_callback=callback, cache_store=MemoryAttributeCache(), always_use_cached_value_for_minutes=1)
36
36
 
37
37
 
38
38
  def add_cit(self, cit:str):
@@ -71,7 +71,7 @@ class Labfreed_App_Infrastructure():
71
71
  ags = {ag.key: pyAttributeGroup.from_attribute_group(ag) for ag in self._attribute_client.get_attributes(url, pac_id=pac.to_url(include_extensions=False), language_preferences=self._language_preferences)}
72
72
  if ags:
73
73
  attribute_groups.update(ags)
74
- pac_info.attributes = attribute_groups
74
+ pac_info.attributes_groups = attribute_groups
75
75
 
76
76
  return pac_info
77
77
 
@@ -20,7 +20,7 @@ class PacInfo(BaseModel):
20
20
  """A convenient collection of information about a PAC-ID"""
21
21
  pac_id:PAC_ID
22
22
  user_handovers: list[ServiceGroup] = Field(default_factory=list)
23
- attributes:dict[str, pyAttributeGroup] = Field(default_factory=dict)
23
+ attributes_groups:dict[str, pyAttributeGroup] = Field(default_factory=dict)
24
24
 
25
25
  @property
26
26
  def pac_url(self):
@@ -44,7 +44,7 @@ class PacInfo(BaseModel):
44
44
 
45
45
  @property
46
46
  def image_url(self) -> str:
47
- if meta := self.attributes.get(MetaAttributeKeys.GROUPKEY.value):
47
+ if meta := self.attributes_groups.get(MetaAttributeKeys.GROUPKEY.value):
48
48
  image_attr = meta.attributes.get(MetaAttributeKeys.IMAGE.value)
49
49
  return image_attr.value
50
50
 
@@ -73,7 +73,7 @@ class PacInfo(BaseModel):
73
73
  @cached_property
74
74
  def _all_attributes(self) -> dict[str, pyAttribute]:
75
75
  out = {}
76
- for ag in self.attributes.values():
76
+ for ag in self.attributes_groups.values():
77
77
  out.update(ag.attributes)
78
78
  return out
79
79
 
@@ -105,12 +105,12 @@ class PacInfo(BaseModel):
105
105
 
106
106
 
107
107
  printout.title1("Attributes")
108
- for ag in self.attributes.values():
108
+ for ag in self.attributes_groups.values():
109
109
  printout.title2(f'{ag.label} (from {ag.origin})')
110
110
  for v in ag.attributes.values():
111
111
  v:pyAttribute
112
112
  #print(f'{k}: ({v.label}) :: {v.value} ')
113
- printout.key_value(v.label, v.value)
113
+ printout.key_value(v.label, ', '.join([str(e) for e in v.value_list]))
114
114
 
115
115
  out = printout.getvalue()
116
116
 
@@ -3,6 +3,8 @@ from abc import ABC
3
3
  from datetime import datetime
4
4
  import re
5
5
  from typing import Annotated, Any, Literal, Union, get_args
6
+ from urllib.parse import urlparse
7
+
6
8
  from labfreed.utilities.ensure_utc_time import ensure_utc
7
9
  from labfreed.labfreed_infrastructure import LabFREED_BaseModel, ValidationMsgLevel, _quote_texts
8
10
  from pydantic import Field, field_validator, model_validator
@@ -12,22 +14,13 @@ class AttributeBase(LabFREED_BaseModel, ABC):
12
14
  key: str
13
15
  value: Any
14
16
  label: str = ""
15
-
16
- observed_at: datetime | None = None
17
-
17
+
18
18
  def __init__(self, **data):
19
19
  # Automatically inject the Literal value for `type`
20
20
  discriminator_value = self._get_discriminator_value()
21
21
  data["type"] = discriminator_value
22
22
  super().__init__(**data)
23
23
 
24
- @field_validator('observed_at', mode='before')
25
- def set_utc_observed_at_if_naive(cls, value):
26
- if isinstance(value, datetime):
27
- return ensure_utc(value)
28
- else:
29
- return value
30
-
31
24
  @classmethod
32
25
  def _get_discriminator_value(cls) -> str:
33
26
  """Extract the Literal value from the 'type' annotation."""
@@ -40,16 +33,10 @@ class AttributeBase(LabFREED_BaseModel, ABC):
40
33
  f"{cls.__name__} must define `type: Literal[<value>]` annotation"
41
34
  ) from e
42
35
 
43
-
44
-
45
-
46
- class ReferenceAttribute(AttributeBase):
47
- type: Literal["reference"]
48
- value: str
49
36
 
50
37
  class DateTimeAttribute(AttributeBase):
51
38
  type: Literal["datetime"]
52
- value: datetime
39
+ value: datetime | list[datetime]
53
40
 
54
41
  @field_validator('value', mode='before')
55
42
  def set_utc__if_naive(cls, value):
@@ -60,15 +47,59 @@ class DateTimeAttribute(AttributeBase):
60
47
 
61
48
  class BoolAttribute(AttributeBase):
62
49
  type: Literal["bool"]
63
- value: bool
50
+ value: bool | list[bool]
64
51
 
65
52
  class TextAttribute(AttributeBase):
66
53
  type: Literal["text"]
67
- value: str
54
+ value: str | list[str]
55
+
56
+ @model_validator(mode='after')
57
+ def _validate_value(self):
58
+ l = [self.value] if isinstance(self.value, str) else self.value
59
+ for v in l:
60
+ if len(v) > 5000:
61
+ self._add_validation_message(
62
+ source="Text Attribute",
63
+ level=ValidationMsgLevel.WARNING, # noqa: F821
64
+ msg=f"Text attribute {v} exceeds 5000 characters. It is recommended to stay below",
65
+ highlight_pattern = f'{v}'
66
+ )
67
+ return self
68
+
69
+
70
+ class ReferenceAttribute(AttributeBase):
71
+ type: Literal["reference"]
72
+ value: str | list[str]
68
73
 
74
+
75
+ class ResourceAttribute(AttributeBase):
76
+ type: Literal["resource"]
77
+ value: str | list[str]
69
78
 
79
+ @model_validator(mode='after')
80
+ def _validate_value(self):
81
+ value_list = self.value if isinstance(self.value, list) else [self.value]
82
+ for v in value_list:
83
+ r = urlparse(v)
84
+ if not all([r.scheme, r.netloc]):
85
+ self._add_validation_message(
86
+ source="Resource Attribute",
87
+ level=ValidationMsgLevel.ERROR, # noqa: F821
88
+ msg=f"Must be a valid url",
89
+ highlight_pattern = f'{v}'
90
+ )
91
+ pattern = re.compile(r"\.\w{1,3}$", re.IGNORECASE)
92
+ if not bool(pattern.search(v)):
93
+ self._add_validation_message(
94
+ source="Resource Attribute",
95
+ level=ValidationMsgLevel.WARNING, # noqa: F821
96
+ msg=f"It is RECOMMENDED resource links end with a file extension",
97
+ highlight_pattern = f'{v}'
98
+ )
99
+ return self
70
100
 
71
101
 
102
+
72
103
  class NumericValue(LabFREED_BaseModel):
73
104
  numerical_value: str
74
105
  unit: str
@@ -120,11 +151,11 @@ class NumericValue(LabFREED_BaseModel):
120
151
 
121
152
  class NumericAttribute(AttributeBase):
122
153
  type: Literal["numeric"]
123
- value: NumericValue
154
+ value: NumericValue | list[NumericValue]
124
155
 
125
156
  class ObjectAttribute(AttributeBase):
126
157
  type: Literal["object"]
127
- value: dict[str, Any]
158
+ value: dict[str, Any] |list[dict[str, Any]]
128
159
 
129
160
 
130
161
 
@@ -136,27 +167,18 @@ Attribute = Annotated[
136
167
  BoolAttribute,
137
168
  TextAttribute,
138
169
  NumericAttribute,
170
+ ResourceAttribute,
139
171
  ObjectAttribute
140
172
  ],
141
173
  Field(discriminator="type")
142
174
  ]
143
175
 
144
- VALID_FOREVER = "forever"
145
176
 
146
177
  class AttributeGroup(LabFREED_BaseModel):
147
178
  key: str
148
179
  label: str = ""
149
180
  attributes: list[Attribute]
150
-
151
- state_of: datetime
152
- valid_until: datetime | Literal["forever"] | None = None
153
-
154
- @field_validator('valid_until', mode='before')
155
- def set_utc_valid_until_if_naive(cls, value):
156
- if isinstance(value, datetime):
157
- return ensure_utc(value)
158
- else:
159
- return value
181
+
160
182
 
161
183
 
162
184
  class AttributesOfPACID(LabFREED_BaseModel):
@@ -1,6 +1,6 @@
1
1
 
2
2
 
3
- from datetime import datetime
3
+ from datetime import datetime, timedelta
4
4
  from typing import Literal, Protocol
5
5
 
6
6
 
@@ -12,28 +12,15 @@ from labfreed.pac_id.pac_id import PAC_ID
12
12
  class CacheableAttributeGroup(AttributeGroup):
13
13
  origin:str
14
14
  language:str
15
- valid_until: Literal['forever'] | datetime | None = None
15
+ value_from: datetime | None = None
16
16
 
17
- # @model_validator(mode='after')
18
- # def set_valid_until(self) -> 'CacheableAttributeGroup':
19
- # vals = [a.valid_until for a in self.attributes]
20
- # if all(e == 'forever' for e in vals):
21
- # self.valid_until = 'forever'
22
- # elif any(e is None for e in vals):
23
- # self.valid_until = None
24
- # else:
25
- # self.valid_until = min(v for v in vals if isinstance(v, datetime))
26
- # return self
27
-
28
-
29
- @property
30
- def still_valid(self):
31
- if self.valid_until is None:
17
+ def still_valid(self, accept_cache_for_minutes):
18
+ if self.value_from is None:
32
19
  return False
33
- if self.valid_until == 'forever':
34
- return True
35
-
36
- return self.valid_until > datetime.now()
20
+ else:
21
+ return ( datetime.now() - timedelta(min=accept_cache_for_minutes)) > self.value_from
22
+
23
+
37
24
 
38
25
 
39
26
 
@@ -1,6 +1,7 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  from dataclasses import dataclass
4
+ from datetime import datetime
4
5
  from typing import Protocol, runtime_checkable
5
6
 
6
7
  import requests
@@ -85,6 +86,7 @@ class AttributeClient():
85
86
 
86
87
  http_post_callback:AttributeRequestCallback
87
88
  cache_store:AttributeCache
89
+ always_use_cached_value_for_minutes:int
88
90
 
89
91
  def get_attributes(self,
90
92
  server_url:str,
@@ -120,7 +122,7 @@ class AttributeClient():
120
122
  else:
121
123
  attribute_groups = self.cache_store.get_all(server_url, pac_id)
122
124
 
123
- if attribute_groups and all([ag.still_valid for ag in attribute_groups]):
125
+ if attribute_groups and all([ag.still_valid(accept_cache_for_minutes=self.always_use_cached_value_for_minutes) for ag in attribute_groups]):
124
126
  return attribute_groups
125
127
 
126
128
  # no valid data found in cache > request to server
@@ -154,7 +156,7 @@ class AttributeClient():
154
156
  origin=server_url,
155
157
  language=r.language,
156
158
  label=ag.label,
157
- state_of=ag.state_of)
159
+ value_from=datetime.now(tz=datetime.UTC))
158
160
  for ag in ag_for_pac.attribute_groups
159
161
  ]
160
162
  self.cache_store.update(server_url, pac_from_response, ags)
@@ -113,8 +113,7 @@ class _BaseExcelAttributeDataSource(AttributeGroupDataSource):
113
113
  attributes = [pyAttribute(key=k, value=v) for k, v in d.items()]
114
114
  return AttributeGroup(
115
115
  key=self._attribute_group_key,
116
- attributes=pyAttributes(attributes).to_payload_attributes(),
117
- state_of=last_changed,
116
+ attributes=pyAttributes(attributes).to_payload_attributes()
118
117
  )
119
118
 
120
119
 
@@ -3,34 +3,58 @@ from datetime import date, datetime, time
3
3
  import json
4
4
  from typing import Literal
5
5
  import warnings
6
- from pydantic import RootModel
6
+ from pydantic import RootModel, field_validator
7
7
 
8
8
  from labfreed.labfreed_infrastructure import LabFREED_BaseModel
9
- from labfreed.pac_attributes.api_data_models.response import AttributeBase, AttributeGroup, BoolAttribute, DateTimeAttribute, NumericAttribute, NumericValue, ObjectAttribute, ReferenceAttribute, TextAttribute
9
+ from labfreed.pac_attributes.api_data_models.response import AttributeBase, AttributeGroup, BoolAttribute, DateTimeAttribute, NumericAttribute, NumericValue, ObjectAttribute, ReferenceAttribute, ResourceAttribute, TextAttribute
10
10
  from labfreed.pac_attributes.client.attribute_cache import CacheableAttributeGroup
11
11
  from labfreed.pac_id.pac_id import PAC_ID
12
12
  from labfreed.trex.pythonic.quantity import Quantity
13
13
 
14
14
 
15
15
  class pyReference(RootModel[str]):
16
- pass
17
16
 
18
17
  def __str__(self):
19
18
  return str(self.root)
19
+
20
+ class pyResource(RootModel[str]):
21
+
22
+ def __str__(self):
23
+ return str(self.root)
24
+
20
25
 
26
+ # the allowed scalar types
27
+ AllowedValue = str | bool | datetime | pyReference | pyResource | Quantity | int | float | dict | object
28
+ # homogeneous list of those
29
+ AllowedList = list[AllowedValue]
21
30
 
22
31
  class pyAttribute(LabFREED_BaseModel):
23
32
  key:str
24
33
  label:str = ""
25
- value: str|bool|datetime|pyReference|Quantity|int|float|dict|object
26
- valid_until: datetime | Literal["forever"] | None = None
27
- observed_at: datetime | None = None
34
+ value: AllowedValue | AllowedList
28
35
 
29
-
36
+ @property
37
+ def value_list(self):
38
+ '''helper function to more conveniently iterate over value elements, even if it's scalar'''
39
+ return self.value if isinstance(self.value, list) else [self.value]
40
+
41
+
42
+ @field_validator('value', mode='before')
43
+ def handle_one_element_list(v):
44
+ if isinstance(v, list) and len(v)==1:
45
+ return v[0]
46
+ else:
47
+ return v
30
48
 
31
49
  class pyAttributes(RootModel[list[pyAttribute]]):
32
50
  def to_payload_attributes(self) -> list[AttributeBase]:
33
- return [self._attribute_to_attribute_payload_type(e) for e in self.root]
51
+ out = []
52
+ for e in self.root:
53
+ apt = self._attribute_to_attribute_payload_type(e)
54
+ if isinstance(apt.value, list) and len(apt.value) ==1:
55
+ apt.value = apt.value[0]
56
+ out.append(apt)
57
+ return out
34
58
 
35
59
 
36
60
  @staticmethod
@@ -38,85 +62,93 @@ class pyAttributes(RootModel[list[pyAttribute]]):
38
62
  common_args = {
39
63
  "key": attribute.key,
40
64
  "label": attribute.label,
41
- "observed_at": attribute.observed_at
42
65
  }
43
- value = attribute.value
44
-
45
- if isinstance(value, bool):
46
- return BoolAttribute(value=value, **common_args)
66
+ value_list = attribute.value_list
67
+ first_value = value_list[0]
68
+ if isinstance(first_value, bool):
69
+ return BoolAttribute(value=value_list, **common_args)
47
70
 
48
- elif isinstance(value, datetime | date | time):
49
- if not value.tzinfo:
50
- warnings.warn(f'No timezone given for {value}. Assuming it is in UTC.')
51
- return DateTimeAttribute(value =value, **common_args)
71
+ elif isinstance(first_value, datetime | date | time):
72
+ for v in value_list:
73
+ if not v.tzinfo:
74
+ warnings.warn(f'No timezone given for {v}. Assuming it is in UTC.')
75
+ return DateTimeAttribute(value=value_list, **common_args)
52
76
  # return DateTimeAttribute(value =_date_value_from_python_type(value).value, **common_args)
53
77
 
54
-
55
- elif isinstance(attribute.value, Quantity|int|float):
56
- if not isinstance(attribute.value, Quantity):
57
- value = Quantity(value=attribute.value, unit='dimensionless')
58
- num_attribute = NumericAttribute(value = NumericValue(numerical_value=value.value_as_str(),
59
- unit = value.unit),
60
- **common_args)
78
+
79
+ elif isinstance(first_value, Quantity|int|float):
80
+ values = []
81
+ for v in value_list:
82
+ if not isinstance(v, Quantity):
83
+ v = Quantity(value=v, unit='dimensionless')
84
+ values.append(NumericValue(numerical_value=v.value_as_str(),
85
+ unit = v.unit))
86
+ num_attribute = NumericAttribute(value = values, **common_args)
61
87
  num_attribute.print_validation_messages()
62
88
  return num_attribute
63
89
 
64
- elif isinstance(value, str):
90
+ elif isinstance(first_value, str):
65
91
  # capture quantities in the form of "100.0e5 g/L"
66
- if q := Quantity.from_str_with_unit(value):
67
- return NumericAttribute(value = NumericValue(numerical_value=q.value_as_str(),
68
- unit = q.unit),
69
- **common_args)
92
+ if Quantity.from_str_with_unit(first_value):
93
+ values = []
94
+ for v in value_list:
95
+ q = Quantity.from_str_with_unit(v)
96
+ values.append( NumericValue(numerical_value=q.value_as_str(), unit = q.unit) )
97
+ return NumericAttribute(value = values,
98
+ **common_args)
99
+
70
100
  else:
71
- return TextAttribute(value = value, **common_args)
101
+ return TextAttribute(value = value_list, **common_args)
72
102
 
73
- elif isinstance(value, pyReference):
74
- return ReferenceAttribute(value = value.root, **common_args)
75
-
76
- elif isinstance(value, PAC_ID):
77
- return ReferenceAttribute(value = value.to_url(include_extensions=False), **common_args)
78
-
103
+ elif isinstance(first_value, pyReference):
104
+ return ReferenceAttribute(value = [v.root for v in value_list], **common_args)
79
105
 
106
+ elif isinstance(first_value, pyResource):
107
+ return ResourceAttribute(value = [v.root for v in value_list], **common_args)
108
+
109
+ elif isinstance(first_value, PAC_ID):
110
+ return ReferenceAttribute(value = [v.to_url(include_extensions=False) for v in value_list], **common_args)
80
111
 
81
112
  else: #this covers the last resort case of arbitrary objects. Must be json serializable.
82
113
  try :
83
- value = json.loads(json.dumps(value))
84
- return ObjectAttribute(value=value, **common_args)
114
+ values = [json.loads(json.dumps(v)) for v in value_list]
115
+ return ObjectAttribute(value=values, **common_args)
85
116
  except TypeError as e: # noqa: F841
86
- raise ValueError(f'Invalid Type: {type(value)} cannot be converted to attribute. You may want to use ObjectAttribute, but would have to implement the conversion from your python type yourself.')
117
+ raise ValueError(f'Invalid Type: {type(first_value)} cannot be converted to attribute. You may want to use ObjectAttribute, but would have to implement the conversion from your python type yourself.')
118
+
87
119
 
88
120
 
89
121
  @staticmethod
90
122
  def from_payload_attributes(attributes:list[AttributeBase]) -> 'pyAttributes':
91
123
  out = list()
92
124
  for a in attributes:
125
+ value_list = a.value if isinstance(a.value, list) else [a.value]
93
126
  match a:
94
-
95
127
  case ReferenceAttribute():
96
- value = pyReference(a.value)
128
+ values = [pyReference(v) for v in value_list]
129
+
130
+ case ResourceAttribute():
131
+ values = [pyResource(v) for v in value_list]
97
132
 
98
133
  case NumericAttribute():
99
- value = Quantity.from_str_value(value=a.value.numerical_value, unit=a.value.unit)
134
+ values = [ Quantity.from_str_value(value=v.numerical_value, unit=v.unit) for v in value_list]
100
135
 
101
136
  case BoolAttribute():
102
- value = a.value
137
+ values = value_list
103
138
 
104
139
  case TextAttribute():
105
- value = a.value
140
+ values = value_list
106
141
 
107
142
  case DateTimeAttribute():
108
- value = a.value
143
+ values = value_list
109
144
 
110
145
  case ObjectAttribute():
111
- value = a.value
146
+ values = value_list
112
147
 
113
148
 
114
149
  attr = pyAttribute(key=a.key,
115
150
  label=a.label,
116
- value=value,
117
- observed_at=a.observed_at
118
- # valid_until=datetime(**_parse_date_time_str(a.valid_until)),
119
- # observed_at=datetime(**_parse_date_time_str(a.value))
151
+ value=values
120
152
  )
121
153
  out.append(attr )
122
154
  return out
@@ -1,6 +1,6 @@
1
1
  from abc import ABC, abstractmethod, abstractproperty
2
2
  from datetime import datetime, timezone
3
- from labfreed.pac_attributes.api_data_models.response import VALID_FOREVER, AttributeBase, AttributeGroup
3
+ from labfreed.pac_attributes.api_data_models.response import AttributeBase, AttributeGroup
4
4
  from labfreed.pac_cat.pac_cat import PAC_CAT
5
5
  from labfreed.pac_id.pac_id import PAC_ID
6
6
 
@@ -36,7 +36,6 @@ class Dict_DataSource(AttributeGroupDataSource):
36
36
  raise ValueError('Invalid data')
37
37
 
38
38
  self._data = data
39
- self._state_of = datetime.now(tz=timezone.utc)
40
39
  self.uses_pac_cat_short_form = uses_pac_cat_short_form
41
40
 
42
41
  super().__init__(*args, **kwargs)
@@ -58,14 +57,8 @@ class Dict_DataSource(AttributeGroupDataSource):
58
57
  if not attributes:
59
58
  return None
60
59
 
61
-
62
- valid_until = VALID_FOREVER if self._is_static else None
63
-
64
-
65
60
  return AttributeGroup(key=self._attribute_group_key,
66
- attributes=attributes,
67
- state_of=self._state_of,
68
- valid_until=valid_until)
61
+ attributes=attributes)
69
62
 
70
63
 
71
64
 
@@ -1,3 +1,5 @@
1
+ import re
2
+ import string
1
3
  import traceback
2
4
  import warnings
3
5
 
@@ -132,7 +134,12 @@ class AttributeServerRequestHandler():
132
134
  if dn := self._get_display_name_for_key(ag.key, language):
133
135
  ag.label = dn
134
136
  else:
135
- ag.label = ag.key.split('/')[-1]
137
+ ag.label = string.capwords(
138
+ re.sub(r'([a-z])([A-Z])', r'\1 \2',
139
+ re.sub('-_', '',
140
+ ag.key.split('/')[-1])
141
+ )
142
+ )
136
143
  rich.print(f"[yellow]WARNING:[/yellow] No translation for '{ag.key}' in '{language}'. Falling back to '{ag.label}'")
137
144
  for a in ag.attributes:
138
145
  if dn := self._get_display_name_for_key(a.key, language):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: labfreed
3
- Version: 1.0.0a6
3
+ Version: 1.0.0a7
4
4
  Summary: Python implementation of LabFREED building blocks
5
5
  Author-email: Reto Thürer <thuerer.r@buchi.com>
6
6
  Requires-Python: >=3.11
@@ -1,8 +1,8 @@
1
- labfreed/__init__.py,sha256=QDD9hO42SH5U1i2nyJ30gD2f7LQKcGLPuewQwzxIxXQ,338
1
+ labfreed/__init__.py,sha256=2M8GLQGnuUWBRUGzOt9MJDSVqxWBbFRcTDuQhOuTu6M,338
2
2
  labfreed/labfreed_infrastructure.py,sha256=YZmU-kgopyB1tvpTR_k_uIt1Q2ezexMrWvu-HaP65IE,10104
3
- labfreed/labfreed_extended/app/app_infrastructure.py,sha256=qn6KesHiOrhTPgXwMZNqFArKlWluxFbk5bgp5u8KDb8,3990
3
+ labfreed/labfreed_extended/app/app_infrastructure.py,sha256=0dO1dXu1DZykRH3gnta34vz5MzEEXUfK1D-tsjNqCaE,4036
4
4
  labfreed/labfreed_extended/app/formatted_print.py,sha256=DcwWP0ix1e_wYNIdceIp6cETkJdG2DqpU8Gs3aZAL40,1930
5
- labfreed/labfreed_extended/app/pac_info/pac_info.py,sha256=hAMKalOs-v5_5DawtTn63-OZILDJpmFpgw-H6uow5qA,6097
5
+ labfreed/labfreed_extended/app/pac_info/pac_info.py,sha256=rb5mraJNsPsm7ujhk6WJmm9tXByhv0E8QTOCcgSaO_U,6159
6
6
  labfreed/labfreed_extended/app/pac_info/html_renderer/external-link.svg,sha256=H5z9s4VvHq09UnHdqfrYNsx-Whljc0gE4qKJ6-3kfgQ,1158
7
7
  labfreed/labfreed_extended/app/pac_info/html_renderer/macros.jinja.html,sha256=1S-dxibPwJshtdelsmyA4LpgOm84L6RTXPNO93gmPfg,5964
8
8
  labfreed/labfreed_extended/app/pac_info/html_renderer/pac-info-style.css,sha256=C5pyD956fd6pJgUBjGxvxgL0Wbgq0v7ZLY4Vr-sJZ7A,4169
@@ -11,18 +11,18 @@ labfreed/labfreed_extended/app/pac_info/html_renderer/pac_info_card.jinja.html,s
11
11
  labfreed/pac_attributes/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
12
12
  labfreed/pac_attributes/well_knonw_attribute_keys.py,sha256=axE81MeJ3G_Wy1PbmNAXH6SfPtl96NXvQJMyrvK10t4,324
13
13
  labfreed/pac_attributes/api_data_models/request.py,sha256=-CI3rU_Bzw2DZGSS06Jl4zajrxMkfPGhKHWmIfnmWlk,1868
14
- labfreed/pac_attributes/api_data_models/response.py,sha256=4VliJuKM_r-J-xaLEqfcdW3JTe2BGG_hQSCaHPG3-xM,5726
14
+ labfreed/pac_attributes/api_data_models/response.py,sha256=eGh474ILEcBC1ijhs1ZZfdhNWRxiPeccGS8aw0zzt0U,6934
15
15
  labfreed/pac_attributes/api_data_models/server_capabilities_response.py,sha256=ypDm4f8xZZl036fp8PuIe6lJHNW5Zg1fItgUlnV75V0,178
16
16
  labfreed/pac_attributes/client/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
17
- labfreed/pac_attributes/client/attribute_cache.py,sha256=eWFy7h-T6gd25ENj9pLvSadNFRrzzIineqBoov2cGyw,2688
18
- labfreed/pac_attributes/client/client.py,sha256=J606oYqcJ98NI0w1bSoahys5t7Wxy4CXjJVGUXyj2Zs,7347
17
+ labfreed/pac_attributes/client/attribute_cache.py,sha256=DbVk6MTOmzNhaf1IDVhzmnlBXiSMPX9Gur43tlBOSdM,2207
18
+ labfreed/pac_attributes/client/client.py,sha256=psOvlYsnK4zPp2jFHHgTPpwjRo8-uJOGLEESXyi-e_I,7510
19
19
  labfreed/pac_attributes/pythonic/attribute_server_factory.py,sha256=_wasafjBlwvzOaM6-uPgqPethsDQHEpaXoiRW7w9aV0,5759
20
- labfreed/pac_attributes/pythonic/excel_attribute_data_source.py,sha256=Mvn-uIMWZjQJfSOMVY244yCKpnYeR4btc9Pe4BL8_4M,7313
21
- labfreed/pac_attributes/pythonic/py_attributes.py,sha256=LoPSH5DWdPTKq-3d2CT-7tTfkqYN9s53sNEeSq_6fHg,5615
20
+ labfreed/pac_attributes/pythonic/excel_attribute_data_source.py,sha256=oP4OHj0DTlH4dD7OlL1qxtX4y9KcuDTCd9Bi_FruP6A,7276
21
+ labfreed/pac_attributes/pythonic/py_attributes.py,sha256=FXSp9_P0o-GuZSDvXtD2fU4g82lglMu9f_-8KPMkEP0,6821
22
22
  labfreed/pac_attributes/pythonic/py_dict_data_source.py,sha256=nAz6GA7Xx_0IORPPpt_Wl3sFJa1Q5Fnq5vdf1uQiJF8,531
23
23
  labfreed/pac_attributes/server/__init__.py,sha256=JvQ2kpQx62OUwP18bGhOWYU9an_nQW59Y8Lh7HyfVxY,301
24
- labfreed/pac_attributes/server/attribute_data_sources.py,sha256=gfaERhFrn3SIoSNRiVzchuxpt2ttoe3gw0-fMmv12hU,2448
25
- labfreed/pac_attributes/server/server.py,sha256=_Rzi_vzX02o0g03lbm-fdg5AJHJnESDWD7cJEKRFs8w,8841
24
+ labfreed/pac_attributes/server/attribute_data_sources.py,sha256=CAPoEc6OQycfc9jU66aL7K4K90ZDrEGG8fXfbVfDKao,2175
25
+ labfreed/pac_attributes/server/server.py,sha256=HKfy5XbDM4AOpfeGrkxqGcfMtaMCoWP_qkJ-etBRROg,9077
26
26
  labfreed/pac_attributes/server/translation_data_sources.py,sha256=axALOqfP840sOSdVCRYtrens97mm-hpfONMUyuVlCrY,2145
27
27
  labfreed/pac_cat/__init__.py,sha256=KNPtQzBD1XVohvG_ucOs7RJj-oi6biUTGB1k-T2o6pk,568
28
28
  labfreed/pac_cat/category_base.py,sha256=D7BzsdF0-JIgag5L2XZJRF4T2LOH5RLh1MMszflkmV8,2526
@@ -64,7 +64,7 @@ labfreed/well_known_keys/labfreed/well_known_keys.py,sha256=p-hXwEEIs7p2SKn9DQeL
64
64
  labfreed/well_known_keys/unece/UneceUnits.json,sha256=kwfQSp_nTuWbADfBBgqTWrvPl6XtM5SedEVLbMJrM7M,898953
65
65
  labfreed/well_known_keys/unece/__init__.py,sha256=MSP9lmjg9_D9iqG9Yq2_ajYfQSNS9wIT7FXA1c--59M,122
66
66
  labfreed/well_known_keys/unece/unece_units.py,sha256=J20d64H69qKDE3XlGdJoXIIh0G-d0jKoiIDsg9an5pk,1655
67
- labfreed-1.0.0a6.dist-info/licenses/LICENSE,sha256=gHFOv9FRKHxO8cInP3YXyPoJnuNeqrvcHjaE_wPSsQ8,1100
68
- labfreed-1.0.0a6.dist-info/WHEEL,sha256=G2gURzTEtmeR8nrdXUJfNiB3VYVxigPQ-bEQujpNiNs,82
69
- labfreed-1.0.0a6.dist-info/METADATA,sha256=llcvfZgrszwp3i_ayvI5HSqO72pp7vRhJZ07-SKUp70,19740
70
- labfreed-1.0.0a6.dist-info/RECORD,,
67
+ labfreed-1.0.0a7.dist-info/licenses/LICENSE,sha256=gHFOv9FRKHxO8cInP3YXyPoJnuNeqrvcHjaE_wPSsQ8,1100
68
+ labfreed-1.0.0a7.dist-info/WHEEL,sha256=G2gURzTEtmeR8nrdXUJfNiB3VYVxigPQ-bEQujpNiNs,82
69
+ labfreed-1.0.0a7.dist-info/METADATA,sha256=D-1dE3dyCXPqcuD860PyNHInj0MAu0b3eezsHvy2pV0,19740
70
+ labfreed-1.0.0a7.dist-info/RECORD,,