syncmodels 0.1.314__py2.py3-none-any.whl → 0.1.315__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 +1 -1
- syncmodels/crawler.py +8 -1
- syncmodels/crud.py +2 -2
- syncmodels/definitions.py +1 -0
- syncmodels/helpers/crawler.py +1 -1
- syncmodels/logic/analyzer.py +8 -0
- syncmodels/logic/browser.py +4 -4
- syncmodels/storage.py +24 -18
- syncmodels/syncmodels.py +2 -2
- {syncmodels-0.1.314.dist-info → syncmodels-0.1.315.dist-info}/METADATA +2 -2
- {syncmodels-0.1.314.dist-info → syncmodels-0.1.315.dist-info}/RECORD +16 -16
- {syncmodels-0.1.314.dist-info → syncmodels-0.1.315.dist-info}/AUTHORS.rst +0 -0
- {syncmodels-0.1.314.dist-info → syncmodels-0.1.315.dist-info}/LICENSE +0 -0
- {syncmodels-0.1.314.dist-info → syncmodels-0.1.315.dist-info}/WHEEL +0 -0
- {syncmodels-0.1.314.dist-info → syncmodels-0.1.315.dist-info}/entry_points.txt +0 -0
- {syncmodels-0.1.314.dist-info → syncmodels-0.1.315.dist-info}/top_level.txt +0 -0
syncmodels/__init__.py
CHANGED
syncmodels/crawler.py
CHANGED
@@ -1502,6 +1502,7 @@ class NormalizePlugin(iPlugin):
|
|
1502
1502
|
SCORE = 950 # before SetURIPlugin
|
1503
1503
|
|
1504
1504
|
async def handle(self, stream: List[Dict], context: Dict):
|
1505
|
+
stream[:] = stream[: iPlugin.MAX_RECORDS] # debug code
|
1505
1506
|
|
1506
1507
|
for idx, data in enumerate(stream):
|
1507
1508
|
|
@@ -1701,6 +1702,8 @@ class HashStreamPlugin(iPlugin):
|
|
1701
1702
|
SCORE = 50 # before SortPlugin
|
1702
1703
|
|
1703
1704
|
async def handle(self, stream: List[Dict], context: Dict):
|
1705
|
+
stream[:] = stream[: iPlugin.MAX_RECORDS] # debug code
|
1706
|
+
|
1704
1707
|
blueprint = hashlib.sha1(b"")
|
1705
1708
|
for data in stream:
|
1706
1709
|
new = hashlib.sha1(pickle.dumps(data))
|
@@ -2648,13 +2651,16 @@ class iAsyncCrawler(iCrawler):
|
|
2648
2651
|
else:
|
2649
2652
|
item, model = data, None
|
2650
2653
|
|
2654
|
+
result = False
|
2651
2655
|
if item:
|
2652
2656
|
# assert isinstance(item, BaseModel)
|
2653
2657
|
# result = await self.syncmodel.put(item)
|
2654
2658
|
context.update(data)
|
2655
2659
|
context[MODEL_KEY] = model
|
2656
2660
|
context["item"] = item
|
2657
|
-
result = all(
|
2661
|
+
result = all(
|
2662
|
+
[await sync.put(context=context, **context) for sync in self.syncmodel]
|
2663
|
+
)
|
2658
2664
|
# save original item if a raw storage has been specified
|
2659
2665
|
if self.raw_storage:
|
2660
2666
|
fqid = item.id
|
@@ -2667,6 +2673,7 @@ class iAsyncCrawler(iCrawler):
|
|
2667
2673
|
await self.save(nice=True)
|
2668
2674
|
else:
|
2669
2675
|
foo = 1
|
2676
|
+
return result
|
2670
2677
|
|
2671
2678
|
async def update_meta(self, tube, meta: Dict) -> bool:
|
2672
2679
|
meta = json_compatible(meta)
|
syncmodels/crud.py
CHANGED
@@ -175,7 +175,7 @@ class iCRUD:
|
|
175
175
|
"Get an object from URI"
|
176
176
|
raise NotImplementedError()
|
177
177
|
|
178
|
-
async def put(self, uri: URI, data: JSON = None, **kw) -> bool:
|
178
|
+
async def put(self, uri: URI, data: JSON = None, context={}, **kw) -> bool:
|
179
179
|
"Put an object from URI"
|
180
180
|
raise NotImplementedError()
|
181
181
|
|
@@ -351,7 +351,7 @@ class iStorage(iCRUD):
|
|
351
351
|
log.error(why)
|
352
352
|
log.error("".join(traceback.format_exception(*sys.exc_info())))
|
353
353
|
|
354
|
-
async def put(self, uri: URI, data: JSON = None, **kw) -> bool:
|
354
|
+
async def put(self, uri: URI, data: JSON = None, context={}, **kw) -> bool:
|
355
355
|
if data is None:
|
356
356
|
data = kw
|
357
357
|
# else:
|
syncmodels/definitions.py
CHANGED
syncmodels/helpers/crawler.py
CHANGED
@@ -137,7 +137,7 @@ class GeojsonManager:
|
|
137
137
|
"returns the uri of a related object"
|
138
138
|
for candidate in [ORG_KEY, ID_KEY]:
|
139
139
|
if _sid := data.get(candidate):
|
140
|
-
_sid = parse_duri(_sid)
|
140
|
+
_sid = parse_duri(str(_sid))
|
141
141
|
if _sid["fscheme"] != "test":
|
142
142
|
|
143
143
|
_sid["path"] = "/{thing}/geo".format_map(_sid)
|
syncmodels/logic/analyzer.py
CHANGED
@@ -250,6 +250,10 @@ class XPathAnalyzer:
|
|
250
250
|
return found > 0
|
251
251
|
|
252
252
|
def _analyze_meta(self, line, ctx) -> bool:
|
253
|
+
# TODO: tampodary skip meta info
|
254
|
+
return False
|
255
|
+
|
256
|
+
def _analyze_meta_old(self, line, ctx) -> bool:
|
253
257
|
keyword = ctx.get("keyword") or {"name", "itemprop"}
|
254
258
|
item = ctx["item"]
|
255
259
|
info = ctx["info"]
|
@@ -292,6 +296,10 @@ class XPathAnalyzer:
|
|
292
296
|
return self._analyze_text(line, ctx)
|
293
297
|
|
294
298
|
def _analyze_ldjson(self, line, ctx) -> bool:
|
299
|
+
# TODO: this funcion has been temporary disabled
|
300
|
+
return False
|
301
|
+
|
302
|
+
def _analyze_ldjson_old(self, line, ctx) -> bool:
|
295
303
|
# TODO: helper for loading json
|
296
304
|
line = line.strip()
|
297
305
|
line = html2text(line)
|
syncmodels/logic/browser.py
CHANGED
@@ -1824,9 +1824,10 @@ class BrowserLogicSession(iBrowserSession):
|
|
1824
1824
|
|
1825
1825
|
async def _locate(self, page, selector, ctx):
|
1826
1826
|
elements = []
|
1827
|
-
timeout = ctx["info"].get("timeout", 5)
|
1828
|
-
num_selectors = len(ctx["info"]["selector"])
|
1829
|
-
timeout = 1 + timeout // num_selectors
|
1827
|
+
# timeout = ctx["info"].get("timeout", 5)
|
1828
|
+
# num_selectors = len(ctx["info"]["selector"])
|
1829
|
+
# timeout = 1 + timeout // num_selectors
|
1830
|
+
timeout = 1.2
|
1830
1831
|
t1 = time.time() + timeout
|
1831
1832
|
while time.time() < t1:
|
1832
1833
|
try:
|
@@ -1841,7 +1842,6 @@ class BrowserLogicSession(iBrowserSession):
|
|
1841
1842
|
await asyncio.sleep(1)
|
1842
1843
|
except Exception as why:
|
1843
1844
|
log.error("selector: %s -> %s", selector, why)
|
1844
|
-
|
1845
1845
|
foo = 1
|
1846
1846
|
|
1847
1847
|
async def _mark_element(self, element, style):
|
syncmodels/storage.py
CHANGED
@@ -86,6 +86,7 @@ from .definitions import (
|
|
86
86
|
LIMIT_KEY,
|
87
87
|
WAVE_LAST_KEY,
|
88
88
|
COMPARISON_PATTERNS,
|
89
|
+
PUSHED,
|
89
90
|
)
|
90
91
|
from .crud import (
|
91
92
|
DEFAULT_DATABASE,
|
@@ -785,7 +786,7 @@ class WaveStorage(iWaves, iStorage):
|
|
785
786
|
async def update(self, query: URI | QUERY, data) -> List[JSON]:
|
786
787
|
return await self.storage.update(query, data)
|
787
788
|
|
788
|
-
async def put(self, uri: URI, data: JSON = None, **kw) -> bool:
|
789
|
+
async def put(self, uri: URI, data: JSON = None, context={}, **kw) -> bool:
|
789
790
|
"""
|
790
791
|
Try to insert a new wave object into storage:
|
791
792
|
|
@@ -797,7 +798,7 @@ class WaveStorage(iWaves, iStorage):
|
|
797
798
|
|
798
799
|
2. if the object has been inserted in `tube` then:
|
799
800
|
- create / update Snapshot
|
800
|
-
-
|
801
|
+
- cteate / update Wave info with original the original query data
|
801
802
|
|
802
803
|
|
803
804
|
e: exists
|
@@ -905,6 +906,7 @@ class WaveStorage(iWaves, iStorage):
|
|
905
906
|
since_value = pytz.utc.localize(since_value)
|
906
907
|
since_value = since_value.astimezone(UTC_TZ)
|
907
908
|
since_value = since_value.strftime("%Y-%m-%dT%H:%M:%SZ")
|
909
|
+
|
908
910
|
break
|
909
911
|
else:
|
910
912
|
monotonic_key = MONOTONIC_KEY # ??
|
@@ -916,16 +918,16 @@ class WaveStorage(iWaves, iStorage):
|
|
916
918
|
|
917
919
|
data_sort_blueprint = build_dict(data, sort_keys)
|
918
920
|
# data_sort_blueprint = build_comparisson_dict(data, reverse_sort_keys)
|
919
|
-
|
921
|
+
data_sort_bp = {
|
920
922
|
MONOTONIC_SINCE_KEY: monotonic_key,
|
921
923
|
MONOTONIC_SINCE_VALUE: since_value,
|
922
924
|
MONOTONIC_SINCE_OPERATOR: ">=",
|
923
925
|
ORDER_KEY: monotonic_key,
|
924
926
|
DIRECTION_KEY: DIRECTION_DESC,
|
925
|
-
LIMIT_KEY: kw.get(
|
926
|
-
|
927
|
-
), # TODO: this is temporal, ideally None
|
928
|
-
ORG_KEY: uid,
|
927
|
+
# LIMIT_KEY: kw.get(
|
928
|
+
# LIMIT_KEY, 50 # TODO: agp: set in definition?
|
929
|
+
# ), # TODO: this is temporal, ideally None
|
930
|
+
# ORG_KEY: uid,
|
929
931
|
# **data_sort_blueprint, # implies sv = True
|
930
932
|
}
|
931
933
|
# TODO: LIMIT 1 ?
|
@@ -958,29 +960,32 @@ class WaveStorage(iWaves, iStorage):
|
|
958
960
|
t0 = time.time()
|
959
961
|
# search the same data
|
960
962
|
# TODO: update blueprint
|
961
|
-
|
962
|
-
# MONOTONIC_SINCE_KEY: monotonic_key,
|
963
|
-
# MONOTONIC_SINCE_VALUE: since_value,
|
964
|
-
# MONOTONIC_SINCE_OPERATOR: ">=",
|
965
|
-
# ORDER_KEY: monotonic_key,
|
966
|
-
# DIRECTION_KEY: DIRECTION_DESC,
|
963
|
+
identical_bp = {
|
967
964
|
LIMIT_KEY: kw.get(
|
968
|
-
LIMIT_KEY,
|
965
|
+
LIMIT_KEY, 10 # TODO: agp: set in definition?
|
969
966
|
), # TODO: this is temporal, ideally None
|
970
967
|
ORG_KEY: uid,
|
971
968
|
**data_sort_blueprint, # implies sv = True
|
972
969
|
}
|
973
970
|
identical = await self.storage.query(
|
974
971
|
query,
|
975
|
-
**
|
972
|
+
**identical_bp,
|
976
973
|
# **data_sort_bp,
|
977
974
|
)
|
975
|
+
|
978
976
|
# TODO: try to create only a single query
|
979
977
|
# TODO: review different structures case
|
978
|
+
similar_bp = {
|
979
|
+
LIMIT_KEY: kw.get(
|
980
|
+
LIMIT_KEY, 50 # TODO: agp: set in definition?
|
981
|
+
), # TODO: this is temporal, ideally None
|
982
|
+
ORG_KEY: uid,
|
983
|
+
**data_sort_blueprint, # implies sv = True
|
984
|
+
}
|
980
985
|
similar = await self.storage.query(
|
981
986
|
query,
|
982
|
-
**
|
983
|
-
|
987
|
+
**similar_bp,
|
988
|
+
**data_sort_bp,
|
984
989
|
)
|
985
990
|
t1 = time.time()
|
986
991
|
_elapsed = t1 - t0
|
@@ -988,7 +993,7 @@ class WaveStorage(iWaves, iStorage):
|
|
988
993
|
N = len(existing)
|
989
994
|
log.debug(
|
990
995
|
"[%s] found [%s] similar records in %s secs",
|
991
|
-
|
996
|
+
identical_bp,
|
992
997
|
N,
|
993
998
|
_elapsed,
|
994
999
|
)
|
@@ -1165,6 +1170,7 @@ class WaveStorage(iWaves, iStorage):
|
|
1165
1170
|
data[ID_KEY] = "{thing}:{id}".format_map(_uri)
|
1166
1171
|
|
1167
1172
|
# must push the data?
|
1173
|
+
context[PUSHED] = push
|
1168
1174
|
if push:
|
1169
1175
|
if isinstance(self.storage, SurrealistStorage):
|
1170
1176
|
log_records.debug(
|
syncmodels/syncmodels.py
CHANGED
@@ -255,7 +255,7 @@ class SyncModel(iCRUD):
|
|
255
255
|
# )
|
256
256
|
# return []
|
257
257
|
|
258
|
-
async def put(self, item: BaseModel, **kw) -> bool:
|
258
|
+
async def put(self, item: BaseModel, context={}, **kw) -> bool:
|
259
259
|
"""Try to create / update an item of `type_` class from raw data
|
260
260
|
|
261
261
|
- get the pydantic item
|
@@ -291,7 +291,7 @@ class SyncModel(iCRUD):
|
|
291
291
|
kw["uri"] = fqid
|
292
292
|
kw["data"] = data
|
293
293
|
for storage in self.storage:
|
294
|
-
result = await storage.put(**kw)
|
294
|
+
result = await storage.put(context=context, **kw)
|
295
295
|
results.append(result)
|
296
296
|
|
297
297
|
return all(results)
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.2
|
2
2
|
Name: syncmodels
|
3
|
-
Version: 0.1.
|
3
|
+
Version: 0.1.315
|
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.
|
21
|
+
Requires-Dist: agptools>=0.1.315
|
22
22
|
Requires-Dist: aiocache
|
23
23
|
Requires-Dist: aiohttp
|
24
24
|
Requires-Dist: Click
|
@@ -1,8 +1,8 @@
|
|
1
|
-
syncmodels/__init__.py,sha256=
|
1
|
+
syncmodels/__init__.py,sha256=PBl-Xi0JTeRosOEvZ6CFBlA-pigf3ngrZFcaMEhU-HY,142
|
2
2
|
syncmodels/context.py,sha256=k1Gs_ip9BfyRFpyRnzqYvRDKo0sYBqJsh6z9sWln9oE,451
|
3
|
-
syncmodels/crawler.py,sha256=
|
4
|
-
syncmodels/crud.py,sha256=
|
5
|
-
syncmodels/definitions.py,sha256=
|
3
|
+
syncmodels/crawler.py,sha256=18pAO0AkckennZcBCJvgCrlJ9QLu_8Hv8bIVi_VYM_c,94651
|
4
|
+
syncmodels/crud.py,sha256=ozumS7XgmXSFcFN2SZBH0jB0j_1vK2xE-FeFcTG7ikw,15327
|
5
|
+
syncmodels/definitions.py,sha256=vQ6-Zsftzy5y02z6Dd3_p5cd37Zqk0lcVrv-06gnDZk,5475
|
6
6
|
syncmodels/exceptions.py,sha256=ZLAwu19cs2UN2Sv3jaLnixT_jRI7T42TfyutCkUsuIk,685
|
7
7
|
syncmodels/geofactory.py,sha256=1FkrdEn0QA0O4_lSUAwjqXH2dmlQWi32AkntnG4AEQY,10372
|
8
8
|
syncmodels/http.py,sha256=FFVT3QJJgur2dv1Q_7l9ZsWN8z6_gUjOT9hJff1ZAqk,3335
|
@@ -11,8 +11,8 @@ syncmodels/registry.py,sha256=YaQtgbSwa0je1MpCcVHALI3_b85vrddyOlhsnrUcKZs,8224
|
|
11
11
|
syncmodels/requests.py,sha256=wWoC5hPDm1iBM_zrlyKRauzhXgdKR3pT5RqyC-5UZhQ,538
|
12
12
|
syncmodels/runner.py,sha256=IHDKuQ3yJ1DN9wktMiIrerPepYX61tc3AzbFfuUqEFw,5454
|
13
13
|
syncmodels/schema.py,sha256=uinUt8Asq_x7xa6MKWVXNyoWO6gKocjGPppjimaXzEU,2492
|
14
|
-
syncmodels/storage.py,sha256
|
15
|
-
syncmodels/syncmodels.py,sha256=
|
14
|
+
syncmodels/storage.py,sha256=invDyQMfDHavQH5Fnbi7afSeIgH6NoVtD_LlNPOqSTI,71646
|
15
|
+
syncmodels/syncmodels.py,sha256=jcUxVbv1hrx5hI81VCO1onIM6WyORTqJVPwIqlPocOc,10596
|
16
16
|
syncmodels/timequeue.py,sha256=YRd3ULRaIhoszaBsYhfr0epMqAbL6-NwVEtScjUYttM,595
|
17
17
|
syncmodels/wave.py,sha256=Gra22BLiA9z2nF-6diXpjAc4GZv9nebmyvHxdAfXec4,7764
|
18
18
|
syncmodels/auth/__init__.py,sha256=xs9Y_bTR99TKt7NyEtMI3lLPxQy9FuP6CYakgAAthCo,1139
|
@@ -24,7 +24,7 @@ syncmodels/cli/surreal.py,sha256=eL7pDicLo0_68JhpCZacde6DOVcfHE_UEzczvgDuvAc,106
|
|
24
24
|
syncmodels/cli/wingdbstub.py,sha256=q4z-RqHN1ttzNtiLYTzqQG2ZYZ6W3NOnEd2E5NGhfao,17165
|
25
25
|
syncmodels/cli/workspace.py,sha256=wajZnxf567nYoQysTEgxrDAp8ZBU8zSuoP4KyZtqvdc,2461
|
26
26
|
syncmodels/helpers/__init__.py,sha256=qZet64gMJNAAqzUdEqCV5WDk5D2Dbw1Kxlt9Jo6x3m4,23
|
27
|
-
syncmodels/helpers/crawler.py,sha256=
|
27
|
+
syncmodels/helpers/crawler.py,sha256=2WiDxnjxZKbGa8x71bgeSc2wmabKa_wD-ofQMbgVXLo,4805
|
28
28
|
syncmodels/helpers/explorer.py,sha256=-Dol3z1pALCMI9OPSvVbROaTzLjbUpS0suJ82Z6Rmb4,7447
|
29
29
|
syncmodels/helpers/faker.py,sha256=tPtibNh28KoHb9kcwE9WaPdPrSrN6xMbCv5HhNFEVG0,1222
|
30
30
|
syncmodels/helpers/general.py,sha256=UAcSfrvsaT15iJuxsR3WMk51UjpLLGDf14xmpBojndg,6160
|
@@ -37,8 +37,8 @@ syncmodels/helpers/surreal.py,sha256=zoWtGm5oAxwvgJNq_NTpKOHN3h9FNObhFDLuiBOl1YY
|
|
37
37
|
syncmodels/helpers/units.py,sha256=g50m5DQrAyP_qpDRa4LCEA5Rz2UZUmlIixfWG_ddw9I,3571
|
38
38
|
syncmodels/logic/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
39
39
|
syncmodels/logic/activity_logger.py,sha256=8wjvgRwaNbibYWGgl-trovSS70yNkoCTlb-AIx3aZEE,14053
|
40
|
-
syncmodels/logic/analyzer.py,sha256=
|
41
|
-
syncmodels/logic/browser.py,sha256=
|
40
|
+
syncmodels/logic/analyzer.py,sha256=OiRZBJoqjc_qb3w1jBjGftWgd20Cig0s7_GLTCbp3fw,12539
|
41
|
+
syncmodels/logic/browser.py,sha256=vMxGaYIR1ov0tOMbWNpd4MMFQBunRuvxE8F-Gfo9cTo,84795
|
42
42
|
syncmodels/logic/swarm.py,sha256=eRBVlNAOzzWKFGCb7LGLx2aj7yQlTY1OwLoeSEllvXY,17207
|
43
43
|
syncmodels/mapper/__init__.py,sha256=jS82LFr9zzyqXBz82tSw04vDowhTpKxhg_W2XvhUlt0,129
|
44
44
|
syncmodels/mapper/fiware.py,sha256=auszPmhCS46z_68MXjksrQAFUfctjbVrVdBvOpOkMj8,523
|
@@ -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.
|
306
|
-
syncmodels-0.1.
|
307
|
-
syncmodels-0.1.
|
308
|
-
syncmodels-0.1.
|
309
|
-
syncmodels-0.1.
|
310
|
-
syncmodels-0.1.
|
311
|
-
syncmodels-0.1.
|
305
|
+
syncmodels-0.1.315.dist-info/AUTHORS.rst,sha256=3ZPoqg8Aav8DSYKd0fwcwn4_5HwSiMLart0E5Un00-U,168
|
306
|
+
syncmodels-0.1.315.dist-info/LICENSE,sha256=uzMOYtIiUsnsD0xHJR7aJWJ4v_bvan0kTnvufy5eNoA,1075
|
307
|
+
syncmodels-0.1.315.dist-info/METADATA,sha256=bojiX01oxFCT4ssaQM1RCj1aSocBx1zkjXCRv_vgspY,2700
|
308
|
+
syncmodels-0.1.315.dist-info/WHEEL,sha256=SrDKpSbFN1G94qcmBqS9nyHcDMp9cUS9OC06hC0G3G0,109
|
309
|
+
syncmodels-0.1.315.dist-info/entry_points.txt,sha256=dMnigjZsHMxTwXiiZyBZdBbMYE0-hY3L5cG15EcDAzw,51
|
310
|
+
syncmodels-0.1.315.dist-info/top_level.txt,sha256=2DfQ9NuAhKMjY3BvQGVBA7GfqTm7EoHNbaehSUiqiHQ,11
|
311
|
+
syncmodels-0.1.315.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|