wizata-dsapi 2.2.0.dev7__py3-none-any.whl → 2.2.0.dev9__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.
wizata_dsapi/inventory.py CHANGED
@@ -83,8 +83,9 @@ class InventoryBalance:
83
83
  DataPoint ``datapoint`` (a hardware id), true at ``timestamp`` (when it was
84
84
  counted, not when it was recorded: that is ``created_at``).
85
85
 
86
- PostgreSQL ``"InventoryBalance"``, PK (``datapoint``, ``product``, ``timestamp``).
87
- Writing the same key again replaces the quantity.
86
+ PostgreSQL ``"InventoryBalance"``, PK ``id`` (DEV-6085); (``datapoint``, ``product``,
87
+ ``timestamp``) is unique, and writing the same key again replaces the quantity.
88
+ ``count`` is the key shared by every balance written in one call (one count).
88
89
 
89
90
  On write only:
90
91
 
@@ -99,7 +100,9 @@ class InventoryBalance:
99
100
  return "planner/inventory/balances"
100
101
 
101
102
  def __init__(self, datapoint: str = None, product: str = None, timestamp=None,
102
- quantity: float = None, unit=None, twin: str = None):
103
+ quantity: float = None, unit=None, twin: str = None, balance_id=None, count=None):
104
+ self.id: Optional[str] = _uuid_str(balance_id)
105
+ self.count: Optional[str] = _uuid_str(count)
103
106
  self.datapoint: Optional[str] = datapoint
104
107
  self.product: Optional[str] = product
105
108
  self.timestamp: Optional[datetime] = _ts_in(timestamp)
@@ -113,7 +116,8 @@ class InventoryBalance:
113
116
 
114
117
  def to_dict(self) -> dict:
115
118
  obj = {}
116
- for name, value in (("datapoint", self.datapoint), ("product", self.product),
119
+ for name, value in (("id", self.id), ("count", self.count),
120
+ ("datapoint", self.datapoint), ("product", self.product),
117
121
  ("quantity", self.quantity), ("unit", self.unit),
118
122
  ("twin", self.twin), ("createdBy", self.created_by),
119
123
  ("updatedBy", self.updated_by)):
@@ -128,6 +132,8 @@ class InventoryBalance:
128
132
  @classmethod
129
133
  def from_dict(cls, data: dict) -> "InventoryBalance":
130
134
  obj = cls()
135
+ obj.id = _uuid_str(data.get("id"))
136
+ obj.count = _uuid_str(data.get("count"))
131
137
  obj.datapoint = data.get("datapoint")
132
138
  obj.product = data.get("product")
133
139
  obj.timestamp = _ts_in(data.get("timestamp"))
@@ -140,6 +146,9 @@ class InventoryBalance:
140
146
  obj.updated_by = data.get("updatedBy") or data.get("updated_by")
141
147
  return obj
142
148
 
149
+ def api_id(self) -> str:
150
+ return self.id
151
+
143
152
  def key(self) -> tuple:
144
153
  return self.datapoint, self.product, self.timestamp
145
154
 
wizata_dsapi/product.py CHANGED
@@ -7,7 +7,9 @@ ProductKind — what role a product plays: raw material, container, finished pro
7
7
 
8
8
  Product — something the plant consumes or makes, identified by a Wizata key
9
9
  (e.g. ``can_33_coke``). ``category`` is a backend category id (Volume, Count, ...):
10
- it says how a quantity of that product is expressed.
10
+ it says how a quantity of that product is expressed. ``unit`` (optional, DEV-6087) is
11
+ its default unit, a backend unit id of that category, used for stock totals across
12
+ storages; ``unit_symbol`` is filled on read only.
11
13
 
12
14
  ProductComponent — one line of a product's bill of materials: ``product`` is made of
13
15
  ``quantity`` ``unit`` of ``composed_of``. The unit is a backend unit id and must
@@ -120,12 +122,14 @@ class Product:
120
122
  return "planner/products"
121
123
 
122
124
  def __init__(self, key: str = None, name: str = None, category=None,
123
- kind: str = None, description: str = None):
125
+ kind: str = None, description: str = None, unit=None):
124
126
  self.key: Optional[str] = key
125
127
  self.name: Optional[str] = name
126
128
  self.category: Optional[str] = _uuid_str(category)
127
129
  self.kind: Optional[str] = kind
128
130
  self.description: Optional[str] = description
131
+ self.unit: Optional[str] = _uuid_str(unit)
132
+ self.unit_symbol: Optional[str] = None
129
133
  self.created_at: Optional[datetime] = None
130
134
  self.updated_at: Optional[datetime] = None
131
135
 
@@ -141,6 +145,10 @@ class Product:
141
145
  obj["kind"] = self.kind
142
146
  if self.description is not None:
143
147
  obj["description"] = self.description
148
+ if self.unit is not None:
149
+ obj["unit"] = self.unit
150
+ if self.unit_symbol is not None:
151
+ obj["unitSymbol"] = self.unit_symbol
144
152
  if self.created_at is not None:
145
153
  obj["createdAt"] = _ts_out(self.created_at)
146
154
  if self.updated_at is not None:
@@ -155,6 +163,8 @@ class Product:
155
163
  obj.category = _uuid_str(data.get("category"))
156
164
  obj.kind = data.get("kind")
157
165
  obj.description = data.get("description")
166
+ obj.unit = _uuid_str(data.get("unit"))
167
+ obj.unit_symbol = data.get("unitSymbol") or data.get("unit_symbol")
158
168
  obj.created_at = _ts_in(data.get("createdAt") or data.get("created_at"))
159
169
  obj.updated_at = _ts_in(data.get("updatedAt") or data.get("updated_at"))
160
170
  return obj
wizata_dsapi/version.py CHANGED
@@ -1 +1 @@
1
- __version__ = "2.2.0.dev7"
1
+ __version__ = "2.2.0.dev9"
@@ -2886,7 +2886,8 @@ class WizataDSAPIClient(ApiInterface, ApiDtoInterface):
2886
2886
  inventoryMovement DataPoint by ``datapoint``, or by ``twin`` (the DataPoint
2887
2887
  of the product's category on that Twin). ``unit`` converts the quantity to
2888
2888
  the DataPoint's unit.
2889
- :return: the stored balances, quantities in the DataPoint's unit.
2889
+ :return: the stored balances, quantities in the DataPoint's unit, each with its
2890
+ ``id`` and the ``count`` key the call created.
2890
2891
  """
2891
2892
  body = [b.to_dict() if isinstance(b, InventoryBalance) else b for b in balances]
2892
2893
  response = self._planner_request("POST", "planner/inventory/balances/", body=body)
@@ -2896,8 +2897,8 @@ class WizataDSAPIClient(ApiInterface, ApiDtoInterface):
2896
2897
  """
2897
2898
  List recorded balances, newest first by default.
2898
2899
 
2899
- :param search_query: SearchQuery or dict; filters on ``datapoint`` and
2900
- ``product`` (eq / in) and ``timestamp`` (gte / gt / lt / lte).
2900
+ :param search_query: SearchQuery or dict; filters on ``datapoint``, ``product``,
2901
+ ``twin``, ``count``, ``id`` (eq / in) and ``timestamp`` (eq / gte / gt / lt / lte).
2901
2902
  :return: PagedQueryResult with InventoryBalance results.
2902
2903
  """
2903
2904
  if search_query is None:
@@ -2908,19 +2909,40 @@ class WizataDSAPIClient(ApiInterface, ApiDtoInterface):
2908
2909
  body=search_query.to_dict())
2909
2910
  return PagedQueryResult.from_dict(response.json(), dto_class=InventoryBalance)
2910
2911
 
2911
- def delete_inventory_balance(self, datapoint: str, product: str, timestamp):
2912
+ @staticmethod
2913
+ def _balance_id(value) -> str:
2914
+ balance_id = value.id if isinstance(value, InventoryBalance) else value
2915
+ if not balance_id:
2916
+ raise ValueError("please provide a balance or its id")
2917
+ return urllib.parse.quote(str(balance_id), safe="")
2918
+
2919
+ def get_inventory_balance(self, balance_id):
2920
+ """
2921
+ :param balance_id: the balance's id.
2922
+ :return: InventoryBalance, or None when it does not exist.
2923
+ """
2924
+ response = self._planner_request("GET", f"planner/inventory/balances/{self._balance_id(balance_id)}/",
2925
+ expected=(200, 404))
2926
+ return InventoryBalance.from_dict(response.json()) if response.status_code == 200 else None
2927
+
2928
+ def update_inventory_balance(self, balance: InventoryBalance) -> InventoryBalance:
2929
+ """
2930
+ Change one balance's quantity and/or time (its datapoint and product never change).
2931
+ ``unit`` converts the quantity to the datapoint's unit. 409 when another balance
2932
+ already holds the new time.
2933
+ """
2934
+ body = {k: v for k, v in balance.to_dict().items() if k in ("quantity", "timestamp", "unit")}
2935
+ response = self._planner_request("PUT", f"planner/inventory/balances/{self._balance_id(balance)}/",
2936
+ body=body)
2937
+ return InventoryBalance.from_dict(response.json())
2938
+
2939
+ def delete_inventory_balance(self, balance):
2912
2940
  """
2913
2941
  Delete one recorded balance (a count entered by mistake).
2914
2942
 
2915
- :param timestamp: datetime or ISO string, exactly as stored.
2943
+ :param balance: InventoryBalance or its id.
2916
2944
  """
2917
- if isinstance(timestamp, datetime.datetime):
2918
- if timestamp.tzinfo is None:
2919
- timestamp = timestamp.replace(tzinfo=datetime.timezone.utc)
2920
- timestamp = timestamp.isoformat()
2921
- self._planner_request("DELETE", "planner/inventory/balances/",
2922
- params={"datapoint": datapoint, "product": product,
2923
- "timestamp": timestamp},
2945
+ self._planner_request("DELETE", f"planner/inventory/balances/{self._balance_id(balance)}/",
2924
2946
  expected=(204,))
2925
2947
 
2926
2948
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: wizata_dsapi
3
- Version: 2.2.0.dev7
3
+ Version: 2.2.0.dev9
4
4
  Summary: Wizata Data Science Toolkit
5
5
  Author: Wizata S.A.
6
6
  Author-email: info@wizata.com
@@ -25,7 +25,7 @@ wizata_dsapi/graylog_log.py,sha256=KaHzv9HSqzmg8Cjr1vrsqeggd3tvrDkSN1TXaxCwsPs,4
25
25
  wizata_dsapi/group_system.py,sha256=6rUKe0_J3YWACysyBlzuw_TEpKNXgLOMxhpWsNxOzwY,1708
26
26
  wizata_dsapi/ilogger.py,sha256=YKBTRp7pHYtBlnzroGRPT91_bMVJT93Pft3fUGcFSIM,488
27
27
  wizata_dsapi/insight.py,sha256=ABFZ04DqYxxzqAEfU1tzlTZqqrigM-zN-8Lbetko3g0,6468
28
- wizata_dsapi/inventory.py,sha256=F20bwqL5mltd1_RC-PH9I7QwWxPE4l2yl64uoO_G7GU,6060
28
+ wizata_dsapi/inventory.py,sha256=JN_aPJbR5_hf4Xjh8UsZWIqxG72GGCzaoAYIqwA_TN4,6525
29
29
  wizata_dsapi/mlmodel.py,sha256=ezgln6vyNGqEw7mD4P2YQZxHm5rXGqM9YW40eS09A8Q,26595
30
30
  wizata_dsapi/mobile_asset.py,sha256=FBEvrOaXkT8YaLv1coemqNFJaoY-NVUcgNKPFgUzfP4,7631
31
31
  wizata_dsapi/model_toolkit.py,sha256=UNyw5CFSgZeXydQFsiDIRTjoMeqIsdyIIuiwumLW5bA,1574
@@ -34,7 +34,7 @@ wizata_dsapi/paged_query_result.py,sha256=QOpJMAS6rxJfiz3a5oSfc0gOvwQeZTEhOtBoSs
34
34
  wizata_dsapi/pipeline.py,sha256=mMkNzFQsC9s1ZuC0HrfLG1BsZCnYYlrrhPlo6Out1Zs,40872
35
35
  wizata_dsapi/pipeline_image.py,sha256=WtMY0_ncba9WzZ96KQINiYshJegQdArzhspKTGWBgVA,7758
36
36
  wizata_dsapi/plot.py,sha256=KLeYj6xpG_XFSDuR4A8Iqp2fmAVLeDsHaUOVT2sRUYM,4033
37
- wizata_dsapi/product.py,sha256=DEuqi-1KDih3fyLEmVlsKao57tCpPJQ2sTwhEqe-r0A,7577
37
+ wizata_dsapi/product.py,sha256=EiiT9icS2xr8aN3STre4X7CsaJ33tt7jmOnQPD9pA6U,8139
38
38
  wizata_dsapi/request.py,sha256=sjYNjxI6m88hdPIPOkRfwoFLNaV7cupjnLIktalapNI,29298
39
39
  wizata_dsapi/script.py,sha256=1Mjw3BNxN_R5dx0f55T0WuebqqRr_69rsK5yuQZOjmQ,19467
40
40
  wizata_dsapi/search.py,sha256=pHX-xGIB0E6gJkTxXmLjFe_b8-0gei7hfkW0JnxzCl0,4691
@@ -46,10 +46,10 @@ wizata_dsapi/trigger.py,sha256=waOX2AhrLvFzNFRQ535j6iemgg_QJP2QsWGL2ipljFg,8848
46
46
  wizata_dsapi/twin.py,sha256=8AnrxSaL7dMrILXJ3-GGmmUz078j3qqwzDTe6i86xqA,12672
47
47
  wizata_dsapi/twinregistration.py,sha256=iXt55AVGAQNl0jmxa3ZVynPqlSsIunFf4sLfp-MWTw0,13244
48
48
  wizata_dsapi/user.py,sha256=-hchKAMZULu9P00RUHDqu_5AT4NI5BYOJd8XCBrt3eQ,1950
49
- wizata_dsapi/version.py,sha256=yfwCYQz4p6IHiajShzB17V1HzG-JxgGk1JgH0R0YvRU,27
49
+ wizata_dsapi/version.py,sha256=-k1RPdCS3o4yj0_fbjRN7hn5K2x676nDCXQeGBqBB5Y,27
50
50
  wizata_dsapi/wizard_function.py,sha256=RbM7W7Gf-6Rhp_1dU9DBYkHaciknGAGvuAndhAS_vyo,942
51
51
  wizata_dsapi/wizard_request.py,sha256=v6BaqKLKvTWmUSo0_gda9FabAQz5x_-GOH1Av50GzFo,3762
52
- wizata_dsapi/wizata_dsapi_client.py,sha256=O6Q-cFg0MYaLQqMR8VEMweTZ8fxDh5uuUJM5kavoeuw,121630
52
+ wizata_dsapi/wizata_dsapi_client.py,sha256=uDbrcr57ZdyVYdwmAovYxIFMt42p7WtjUF_0ogoF4lw,122750
53
53
  wizata_dsapi/words.py,sha256=tV8CqzCqODZCV7PgBxBF5exBxeF_ya9t5DiUy-cg6Sg,1535
54
54
  wizata_dsapi/models/__init__.py,sha256=O3Tusyhl8hFb-5QaEBY7DJISRB8Gf_Xbxxr-aFDR1_U,380
55
55
  wizata_dsapi/models/common.py,sha256=5BC3Dc6kMvqPGxHgoCuGVOynu_F1MWh7EJSzXIICSSg,27196
@@ -58,8 +58,8 @@ wizata_dsapi/plots/common.py,sha256=7F4WMBaddubnCpdSafgaI9U9Sz0Sngg0VREP_vnIMno,
58
58
  wizata_dsapi/plots/theme.py,sha256=D4sSgEwabXVxF3z1GV8DivXJbbY61KgxXAWfjUbNDAc,3594
59
59
  wizata_dsapi/scripts/__init__.py,sha256=9fRdTMuuXcqMkIsUPf5dwFsTWwBzKteQdAMVkyy2acQ,212
60
60
  wizata_dsapi/scripts/common.py,sha256=T2cP8aGPg8OgIvBWuQBNonVtph_dHlNFubByuYrLb0o,15675
61
- wizata_dsapi-2.2.0.dev7.dist-info/licenses/LICENSE.txt,sha256=QwcOLU5TJoTeUhuIXzhdCEEDDvorGiC6-3YTOl4TecE,11356
62
- wizata_dsapi-2.2.0.dev7.dist-info/METADATA,sha256=uk069VouGwpiD3c4aQktZwzcILevlxIiFb3WElWMEGg,14037
63
- wizata_dsapi-2.2.0.dev7.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
64
- wizata_dsapi-2.2.0.dev7.dist-info/top_level.txt,sha256=-OeTJbEnh5DuWyTOHtvw0Dw3LRg3G27TNS6W4ZtfwPs,13
65
- wizata_dsapi-2.2.0.dev7.dist-info/RECORD,,
61
+ wizata_dsapi-2.2.0.dev9.dist-info/licenses/LICENSE.txt,sha256=QwcOLU5TJoTeUhuIXzhdCEEDDvorGiC6-3YTOl4TecE,11356
62
+ wizata_dsapi-2.2.0.dev9.dist-info/METADATA,sha256=7gc7U8W1SyZscjCiOaTwmNwAnoos-HiwvtF21PjbaVc,14037
63
+ wizata_dsapi-2.2.0.dev9.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
64
+ wizata_dsapi-2.2.0.dev9.dist-info/top_level.txt,sha256=-OeTJbEnh5DuWyTOHtvw0Dw3LRg3G27TNS6W4ZtfwPs,13
65
+ wizata_dsapi-2.2.0.dev9.dist-info/RECORD,,