syncmodels 0.1.312__py2.py3-none-any.whl → 0.1.313__py2.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.
syncmodels/__init__.py CHANGED
@@ -2,4 +2,4 @@
2
2
 
3
3
  __author__ = """Asterio Gonzalez"""
4
4
  __email__ = "asterio.gonzalez@gmail.com"
5
- __version__ = "0.1.312"
5
+ __version__ = "0.1.313"
syncmodels/crawler.py CHANGED
@@ -217,7 +217,7 @@ class iAgent(iRunner):
217
217
  include=None,
218
218
  exclude=None,
219
219
  credentials=None,
220
- prefix=None,
220
+ prefix="",
221
221
  *args,
222
222
  **kw,
223
223
  ):
@@ -235,9 +235,8 @@ class iAgent(iRunner):
235
235
  }
236
236
 
237
237
  # prefix template
238
- if prefix:
239
- self.prefix_template = self._compile_template(prefix)
240
- self.prefix = prefix
238
+ self.prefix_template = self._compile_template(prefix)
239
+ self.prefix = prefix
241
240
 
242
241
  # config file
243
242
  if not config_path:
@@ -268,6 +267,11 @@ class iAgent(iRunner):
268
267
 
269
268
  return template
270
269
 
270
+ async def start(self):
271
+ "start runner"
272
+ await super().start()
273
+ await self._bootstrap()
274
+
271
275
  async def _bootstrap(self):
272
276
  "Add the initial tasks to be executed by agent"
273
277
  log.debug(">> [%s] entering bootstrap()", self.name)
@@ -403,7 +407,7 @@ class iAgent(iRunner):
403
407
  log.debug("+ Task: [%s]: [%s] %s", i, self.name, task)
404
408
 
405
409
  # TODO: agp: generalize and provide a better way to save activity records
406
- if activity_logger := getattr(self, "activity_logger"):
410
+ if activity_logger := getattr(self, "activity_logger", None):
407
411
  if activity := task.get(ACTIVITY_LOG_KEY):
408
412
  activity.status = "pending"
409
413
  await activity_logger.update_activity(activity)
@@ -1635,6 +1639,21 @@ class SortPlugin(iPlugin):
1635
1639
  kind = context.get(KIND_KEY)
1636
1640
  SortKeyFinder.register(kind, sort_key)
1637
1641
 
1642
+ mapper = self.bot.parent.MAPPERS.get(kind)
1643
+ if not mapper:
1644
+ log.error("cant find mapper [%s] in crawler??", kind)
1645
+ return
1646
+
1647
+ # TODO: unify this code (exists sort_key or not exists)
1648
+ model = mapper.PYDANTIC
1649
+ item_fields = model.model_fields
1650
+ reverse = list(set(sort_key).intersection(item_fields))
1651
+ if not reverse:
1652
+ log.error("model [%s] has not datetime alike key??", model)
1653
+ return
1654
+
1655
+ context[REVERSE_SORT_KEY] = reverse
1656
+
1638
1657
  def order(data, accessor):
1639
1658
  result = data
1640
1659
  for key in accessor:
@@ -1642,7 +1661,6 @@ class SortPlugin(iPlugin):
1642
1661
  result = result[key]
1643
1662
  except Exception as why:
1644
1663
  pass
1645
-
1646
1664
  return result
1647
1665
 
1648
1666
  stream.sort(key=partial(order, accessor=sort_key))
@@ -2521,11 +2539,6 @@ class iAsyncCrawler(iCrawler):
2521
2539
  # TODO: REVIEW: always is a single value (len==1), why not return waves[0]
2522
2540
  return waves
2523
2541
 
2524
- async def start(self):
2525
- "start runner"
2526
- await super().start()
2527
- await self._bootstrap()
2528
-
2529
2542
  async def idle(self):
2530
2543
  "default implementation when loop has nothing to do"
2531
2544
  # check running bots
@@ -117,7 +117,7 @@ class OrionInjector:
117
117
  "put",
118
118
  self.target_url
119
119
  # + "/v2/entities/{id}/attrs?options=append,keyValues",
120
- + "/v2/entities/{id}/attrs",
120
+ + "/v2/entities/{id}/attrs?type={type}",
121
121
  self.FULL_EXCLUDE,
122
122
  ],
123
123
  ),
@@ -137,6 +137,7 @@ class OrionInjector:
137
137
  self.target_url
138
138
  # + "/v2/entities/{id}/attrs?options=append,keyValues",
139
139
  + "/v2/entities/{id}",
140
+ # + "/v2/entities/{id}?type={type}", # TODO: avoid posibly 409 if id is used in other fs_pathservice
140
141
  # self.EXCLUDE_ALL,
141
142
  self.FULL_EXCLUDE,
142
143
  ],
@@ -7,4 +7,5 @@ from .model import (
7
7
  IntEnum,
8
8
  Field,
9
9
  field_validator,
10
+ model_validator,
10
11
  )
syncmodels/model/model.py CHANGED
@@ -23,7 +23,7 @@ from typing_extensions import Annotated
23
23
  from pydantic import BaseModel as _BaseModel
24
24
  from pydantic import Field
25
25
  from pydantic import PlainSerializer, BeforeValidator
26
- from pydantic.functional_validators import field_validator
26
+ from pydantic.functional_validators import field_validator, model_validator
27
27
 
28
28
  # from pydantic.dataclasses import dataclass
29
29
 
syncmodels/runner.py CHANGED
@@ -62,7 +62,9 @@ class iRunner:
62
62
  self.t0 = 0
63
63
  self.t1 = 0
64
64
  self.nice = 600
65
+ self.clock = 0
65
66
 
67
+ self.hooks = []
66
68
  self.stop_when_empty = stop_when_empty
67
69
 
68
70
  self._wip = []
@@ -160,6 +162,9 @@ class iRunner:
160
162
  if not func:
161
163
  func = getattr(self, task[FUNC_KEY]) # let fail with AttributeError
162
164
  await func(**task)
165
+ self.clock += 1
166
+ for hook in self.hooks:
167
+ await hook(**task)
163
168
 
164
169
  async def idle(self):
165
170
  "default implementation when loop has nothing to do"
syncmodels/storage.py CHANGED
@@ -9,7 +9,7 @@ import time
9
9
  import sys
10
10
  import traceback
11
11
  from typing import List
12
- from datetime import timedelta
12
+ from datetime import timedelta, datetime
13
13
  import pytz
14
14
  from multiprocessing import Process
15
15
  import random
@@ -129,6 +129,20 @@ def split_fqui(fqid):
129
129
  return fqid, None
130
130
 
131
131
 
132
+ def normalize_payload(data, keys):
133
+ for key in set(keys).intersection(data):
134
+ value = DATE(data[key])
135
+ if isinstance(value, datetime):
136
+ if not value.tzinfo:
137
+ value = pytz.utc.localize(value)
138
+ value = value.astimezone(UTC_TZ)
139
+ value = value.strftime("%Y-%m-%dT%H:%M:%SZ")
140
+
141
+ data[key] = value
142
+
143
+ return data
144
+
145
+
132
146
  # ---------------------------------------------------------
133
147
  # Data Store / Ignore Policies
134
148
  # ---------------------------------------------------------
@@ -853,6 +867,9 @@ class WaveStorage(iWaves, iStorage):
853
867
  # now
854
868
  # ['2005-06-01T00:00:00.000+02:00']
855
869
  # [datetime.datetime(2005, 6, 1, 0, 0, tzinfo=tzoffset(None, 7200))]
870
+
871
+ normalize_payload(data, sort_keys)
872
+
856
873
  monotonic = data.setdefault(MONOTONIC_KEY, monotonic_wave())
857
874
  for monotonic_key in set(sort_keys or []).intersection(data):
858
875
  monotonic_value = DATE(data[monotonic_key])
@@ -878,8 +895,8 @@ class WaveStorage(iWaves, iStorage):
878
895
 
879
896
  query = f"{namespace}://{database}/{thing}"
880
897
 
881
- # data_sort_blueprint = build_dict(data, sort_keys)
882
- data_sort_blueprint = build_dict(data, reverse_sort_keys)
898
+ data_sort_blueprint = build_dict(data, sort_keys)
899
+ # data_sort_blueprint = build_comparisson_dict(data, reverse_sort_keys)
883
900
  blueprint = {
884
901
  MONOTONIC_SINCE_KEY: monotonic_key,
885
902
  MONOTONIC_SINCE_VALUE: since_value,
@@ -890,7 +907,7 @@ class WaveStorage(iWaves, iStorage):
890
907
  LIMIT_KEY, 50 # TODO: agp: set in definition?
891
908
  ), # TODO: this is temporal, ideally None
892
909
  ORG_KEY: uid,
893
- **data_sort_blueprint, # implies sv = True
910
+ # **data_sort_blueprint, # implies sv = True
894
911
  }
895
912
  # TODO: LIMIT 1 ?
896
913
 
@@ -921,15 +938,38 @@ class WaveStorage(iWaves, iStorage):
921
938
 
922
939
  self.behavior_uri[query] = behavior
923
940
 
924
- t0 = time.time()
925
- existing2 = await self.storage.query(
941
+ # t0 = time.time()
942
+
943
+ # search the same data
944
+ # TODO: update blueprint
945
+ blueprint = {
946
+ # MONOTONIC_SINCE_KEY: monotonic_key,
947
+ # MONOTONIC_SINCE_VALUE: since_value,
948
+ # MONOTONIC_SINCE_OPERATOR: ">=",
949
+ # ORDER_KEY: monotonic_key,
950
+ # DIRECTION_KEY: DIRECTION_DESC,
951
+ LIMIT_KEY: kw.get(
952
+ LIMIT_KEY, 50 # TODO: agp: set in definition?
953
+ ), # TODO: this is temporal, ideally None
954
+ ORG_KEY: uid,
955
+ **data_sort_blueprint, # implies sv = True
956
+ }
957
+ identical = await self.storage.query(
926
958
  query,
927
959
  **blueprint,
928
960
  # **data_sort_bp,
929
961
  )
930
- t1 = time.time()
931
- _elapsed = t1 - t0
932
- N2 = len(existing2)
962
+ # TODO: try to create only a single query
963
+ # TODO: review different structures case
964
+ similar = await self.storage.query(
965
+ query,
966
+ **blueprint,
967
+ # **data_sort_bp,
968
+ )
969
+ # t1 = time.time()
970
+ # _elapsed = t1 - t0
971
+ existing = identical + similar
972
+ N2 = len(existing)
933
973
  if data_sort_blueprint and N2 > 1:
934
974
  if behavior & ALLOW_DUPLICATED_ITEMS:
935
975
  log.debug(
@@ -937,7 +977,7 @@ class WaveStorage(iWaves, iStorage):
937
977
  uid,
938
978
  N2,
939
979
  )
940
- existing2.clear()
980
+ existing.clear()
941
981
  else:
942
982
  log.debug(
943
983
  "tube has multiples records: [%s] = %s records, must just 1 and sort_key is defined by: [%s]",
@@ -947,20 +987,9 @@ class WaveStorage(iWaves, iStorage):
947
987
  )
948
988
 
949
989
  push = True
950
- for exists in existing2:
990
+ for exists in existing:
951
991
  existing_sort_blueprint = build_dict(exists, reverse_sort_keys)
952
-
953
- # check if stream provide an old-echo item
954
- # sort_keys must exists in both data
955
- old_echo = [
956
- old_echo.append(data[_] < exists[_]) if _ in exists else False
957
- ]
958
- if any(old_echo):
959
- log.warning(
960
- "provider give old data again, preserving storage value and ignore this old_echo"
961
- )
962
- push = False
963
- break
992
+ # existing_sort_blueprint = build_comparisson_dict(exists, reverse_sort_keys)
964
993
 
965
994
  same_sort_key = existing_sort_blueprint == data_sort_blueprint
966
995
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: syncmodels
3
- Version: 0.1.312
3
+ Version: 0.1.313
4
4
  Summary: Synchronizable Models
5
5
  Home-page: https://github.com/asterio.gonzalez/syncmodels
6
6
  Author: Asterio Gonzalez
@@ -18,7 +18,7 @@ Classifier: Programming Language :: Python :: 3.11
18
18
  Requires-Python: >=3.6
19
19
  License-File: LICENSE
20
20
  License-File: AUTHORS.rst
21
- Requires-Dist: agptools>=0.1.312
21
+ Requires-Dist: agptools>=0.1.313
22
22
  Requires-Dist: aiocache
23
23
  Requires-Dist: aiohttp
24
24
  Requires-Dist: Click
@@ -1,6 +1,6 @@
1
- syncmodels/__init__.py,sha256=ZH2kRAl4tVru0sACzSbL7-N33RPSBU6YP06lTOu_Bzk,142
1
+ syncmodels/__init__.py,sha256=F-gwFoSn9hneX4i5kpR9dYY6CUDVidYhx9nW2WiEJcU,142
2
2
  syncmodels/context.py,sha256=k1Gs_ip9BfyRFpyRnzqYvRDKo0sYBqJsh6z9sWln9oE,451
3
- syncmodels/crawler.py,sha256=cyGX1ZiVWaEWdQAUjDhjH9Qb8VAeV-yB4rFA1weAC9I,93701
3
+ syncmodels/crawler.py,sha256=vfMn39Fev1yKY8ELvTK3rM-RUPiiy8YoxAG2q7b9MkM,94247
4
4
  syncmodels/crud.py,sha256=viHBwzczcjNyFiLxL7VGYSbWJW5VjU8AvKaPufBMP7M,15303
5
5
  syncmodels/definitions.py,sha256=2P-Sfgj18viSHZ-wAK4WfQEzDKdyygs8Z-XLzA0jg_k,5420
6
6
  syncmodels/exceptions.py,sha256=8EOYW8h_2noeoKAYqG4aEQTgB1FEkamxKt3t4pDJ3pM,626
@@ -9,9 +9,9 @@ syncmodels/http.py,sha256=FFVT3QJJgur2dv1Q_7l9ZsWN8z6_gUjOT9hJff1ZAqk,3335
9
9
  syncmodels/parallel.py,sha256=Ll8HmyFF9v9fIofqqSgfhyTlklvb77mTtNdG5Y9lqdQ,7145
10
10
  syncmodels/registry.py,sha256=YaQtgbSwa0je1MpCcVHALI3_b85vrddyOlhsnrUcKZs,8224
11
11
  syncmodels/requests.py,sha256=wWoC5hPDm1iBM_zrlyKRauzhXgdKR3pT5RqyC-5UZhQ,538
12
- syncmodels/runner.py,sha256=T6A_aZPtj4vqoqEf1bhd_efFEGbE6aSVjP8nJlAHFWQ,5320
12
+ syncmodels/runner.py,sha256=IHDKuQ3yJ1DN9wktMiIrerPepYX61tc3AzbFfuUqEFw,5454
13
13
  syncmodels/schema.py,sha256=uinUt8Asq_x7xa6MKWVXNyoWO6gKocjGPppjimaXzEU,2492
14
- syncmodels/storage.py,sha256=vyIW_C6FoSh4VdCs-GcvNOKdpIfqyaIc1Cq5PV081Lo,69485
14
+ syncmodels/storage.py,sha256=N69l-CSZOZomwVJMALKbPI1jPsngxDNlxbxX9SV4vfg,70505
15
15
  syncmodels/syncmodels.py,sha256=EzSC4C75V4wJDmsLLbp8YUVwqA6A16KCNW8nB-MqPcs,10567
16
16
  syncmodels/timequeue.py,sha256=YRd3ULRaIhoszaBsYhfr0epMqAbL6-NwVEtScjUYttM,595
17
17
  syncmodels/wave.py,sha256=Gra22BLiA9z2nF-6diXpjAc4GZv9nebmyvHxdAfXec4,7764
@@ -32,7 +32,7 @@ syncmodels/helpers/geojson.py,sha256=9VZjdEw19fLS_Q9topapUOPGxRg2D4GOqm2tlx11W7M
32
32
  syncmodels/helpers/importers.py,sha256=KImR9pQu4ir6EI6Ipta0q3RWloFT_VTJi67kM0lZsKQ,3919
33
33
  syncmodels/helpers/loaders.py,sha256=aus0aRcbU1vVa_zWo42aX6uV3B0fQ0aQpkTWlR9xGLA,4325
34
34
  syncmodels/helpers/models.py,sha256=c_ATzmiw5mVY1IGnwmyhjIuu5d2idHU-XeRigZSMkOQ,719
35
- syncmodels/helpers/orion.py,sha256=Fx-sVvI35FyKFcSd0vfY2Tjp7hlIwBJpshs2vb3fcsc,30584
35
+ syncmodels/helpers/orion.py,sha256=Lblk4OGRNidYl1-2lZKUPXyvuR1hXip7FHloXLtU-XA,30717
36
36
  syncmodels/helpers/surreal.py,sha256=zoWtGm5oAxwvgJNq_NTpKOHN3h9FNObhFDLuiBOl1YY,10050
37
37
  syncmodels/helpers/units.py,sha256=g50m5DQrAyP_qpDRa4LCEA5Rz2UZUmlIixfWG_ddw9I,3571
38
38
  syncmodels/logic/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -43,11 +43,11 @@ syncmodels/logic/swarm.py,sha256=eRBVlNAOzzWKFGCb7LGLx2aj7yQlTY1OwLoeSEllvXY,172
43
43
  syncmodels/mapper/__init__.py,sha256=jS82LFr9zzyqXBz82tSw04vDowhTpKxhg_W2XvhUlt0,129
44
44
  syncmodels/mapper/fiware.py,sha256=auszPmhCS46z_68MXjksrQAFUfctjbVrVdBvOpOkMj8,523
45
45
  syncmodels/mapper/mapper.py,sha256=SphMhr59bbTWWxnvitonURk3lSPDerGqUTs5-P-Tjlg,17397
46
- syncmodels/model/__init__.py,sha256=tHoKorgbN77ulv2SZ_fGqZqkq7yoWUvqM5SKKxrJWRE,127
46
+ syncmodels/model/__init__.py,sha256=mD6fpRvBOl7i98pqqwpB2NKlviyD-G8iRXqufjU-9z8,148
47
47
  syncmodels/model/activity.py,sha256=tKzc9zc_5Sx549yTdaTYkfnLnbEsEmzpu53BHstqvFQ,4592
48
48
  syncmodels/model/geofilters.py,sha256=YxZZPk60MvzrBslOrpJec_Er1aTWGg1wgYrXPovrpP8,9284
49
49
  syncmodels/model/geojson.py,sha256=BsW3phXHBGeaIOyOZg7U8bRYKKerAEgNjCl8EnBS2Dg,8329
50
- syncmodels/model/model.py,sha256=0DtnsqlK1oEzHe5CdPMRnNaYJT3Hi2S-adqdydBSyYM,11541
50
+ syncmodels/model/model.py,sha256=c8fAvGZ8_oXYQbdNixltRFhWjY3zYE1Y7MTLXIjbpac,11558
51
51
  syncmodels/model/swarm.py,sha256=0QMc1iyiuW5goLLQGFTIky3dJcc5BfuBPHtVbn7iRK4,1266
52
52
  syncmodels/model/schema_org/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
53
53
  syncmodels/model/schema_org/aboutpage.py,sha256=_sqPMc7LxGWNSHwkvTB6OZZAfEovgHltisp6AAz2t9Y,452
@@ -302,10 +302,10 @@ syncmodels/session/postgresql.py,sha256=ZMIu1Rv93pKfvFlovFBmWArzlrT2xaQWNYGZT_LW
302
302
  syncmodels/session/sql.py,sha256=bD7zXRrEKKJmqY2UoibWENuWb5zHrrU72F3_dYbS6LY,6569
303
303
  syncmodels/session/sqlite.py,sha256=nCDjopLiBpX1F10qkKoARM7JrVdIpJ1WdGOduFVxaiA,2080
304
304
  syncmodels/source/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
305
- syncmodels-0.1.312.dist-info/AUTHORS.rst,sha256=3ZPoqg8Aav8DSYKd0fwcwn4_5HwSiMLart0E5Un00-U,168
306
- syncmodels-0.1.312.dist-info/LICENSE,sha256=uzMOYtIiUsnsD0xHJR7aJWJ4v_bvan0kTnvufy5eNoA,1075
307
- syncmodels-0.1.312.dist-info/METADATA,sha256=CvmyXbM7GlQD3qm93hNGd_yq6yWd_MXAC6BatNPu_iA,2700
308
- syncmodels-0.1.312.dist-info/WHEEL,sha256=SrDKpSbFN1G94qcmBqS9nyHcDMp9cUS9OC06hC0G3G0,109
309
- syncmodels-0.1.312.dist-info/entry_points.txt,sha256=dMnigjZsHMxTwXiiZyBZdBbMYE0-hY3L5cG15EcDAzw,51
310
- syncmodels-0.1.312.dist-info/top_level.txt,sha256=2DfQ9NuAhKMjY3BvQGVBA7GfqTm7EoHNbaehSUiqiHQ,11
311
- syncmodels-0.1.312.dist-info/RECORD,,
305
+ syncmodels-0.1.313.dist-info/AUTHORS.rst,sha256=3ZPoqg8Aav8DSYKd0fwcwn4_5HwSiMLart0E5Un00-U,168
306
+ syncmodels-0.1.313.dist-info/LICENSE,sha256=uzMOYtIiUsnsD0xHJR7aJWJ4v_bvan0kTnvufy5eNoA,1075
307
+ syncmodels-0.1.313.dist-info/METADATA,sha256=NRPuII9unMl4-z_TQqvb9W-Tp94LIT6CXU3ctK_v3us,2700
308
+ syncmodels-0.1.313.dist-info/WHEEL,sha256=SrDKpSbFN1G94qcmBqS9nyHcDMp9cUS9OC06hC0G3G0,109
309
+ syncmodels-0.1.313.dist-info/entry_points.txt,sha256=dMnigjZsHMxTwXiiZyBZdBbMYE0-hY3L5cG15EcDAzw,51
310
+ syncmodels-0.1.313.dist-info/top_level.txt,sha256=2DfQ9NuAhKMjY3BvQGVBA7GfqTm7EoHNbaehSUiqiHQ,11
311
+ syncmodels-0.1.313.dist-info/RECORD,,