imio.smartweb.common 1.2.36__py3-none-any.whl → 1.2.37__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.
- imio/smartweb/common/rest/configure.zcml +10 -0
- imio/smartweb/common/rest/endpoint.py +175 -0
- {imio_smartweb_common-1.2.36.dist-info → imio_smartweb_common-1.2.37.dist-info}/METADATA +8 -1
- {imio_smartweb_common-1.2.36.dist-info → imio_smartweb_common-1.2.37.dist-info}/RECORD +10 -9
- /imio.smartweb.common-1.2.36-py3.12-nspkg.pth → /imio.smartweb.common-1.2.37-py3.12-nspkg.pth +0 -0
- {imio_smartweb_common-1.2.36.dist-info → imio_smartweb_common-1.2.37.dist-info}/WHEEL +0 -0
- {imio_smartweb_common-1.2.36.dist-info → imio_smartweb_common-1.2.37.dist-info}/licenses/LICENSE.GPL +0 -0
- {imio_smartweb_common-1.2.36.dist-info → imio_smartweb_common-1.2.37.dist-info}/licenses/LICENSE.rst +0 -0
- {imio_smartweb_common-1.2.36.dist-info → imio_smartweb_common-1.2.37.dist-info}/namespace_packages.txt +0 -0
- {imio_smartweb_common-1.2.36.dist-info → imio_smartweb_common-1.2.37.dist-info}/top_level.txt +0 -0
|
@@ -10,4 +10,14 @@
|
|
|
10
10
|
permission="zope2.View"
|
|
11
11
|
/>
|
|
12
12
|
|
|
13
|
+
<plone:service
|
|
14
|
+
name="@find"
|
|
15
|
+
method="GET"
|
|
16
|
+
accept="application/json"
|
|
17
|
+
for="zope.interface.Interface"
|
|
18
|
+
factory=".endpoint.FindEndpoint"
|
|
19
|
+
permission="zope2.View"
|
|
20
|
+
layer="imio.smartweb.common.interfaces.IImioSmartwebCommonLayer"
|
|
21
|
+
/>
|
|
22
|
+
|
|
13
23
|
</configure>
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
|
|
3
|
+
from collections import Counter
|
|
4
|
+
from imio.smartweb.common.rest.utils import get_json
|
|
5
|
+
from plone import api
|
|
6
|
+
from plone.restapi.search.handler import SearchHandler
|
|
7
|
+
from plone.restapi.search.utils import unflatten_dotted_dict
|
|
8
|
+
from plone.restapi.services import Service
|
|
9
|
+
from Products.CMFCore.utils import getToolByName
|
|
10
|
+
from zExceptions import Unauthorized
|
|
11
|
+
|
|
12
|
+
import json
|
|
13
|
+
import logging
|
|
14
|
+
|
|
15
|
+
logger = logging.getLogger("imio.smartweb.common")
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class FindEndpointHandler(SearchHandler):
|
|
19
|
+
def search(self, query=None):
|
|
20
|
+
results = {"items": []}
|
|
21
|
+
portal = api.portal.get()
|
|
22
|
+
get_types_url = f"{portal.absolute_url()}/@types"
|
|
23
|
+
auth_header = self.request._orig_env.get("HTTP_AUTHORIZATION", None)
|
|
24
|
+
types = get_json(get_types_url, auth=auth_header, timeout=20)
|
|
25
|
+
if not types:
|
|
26
|
+
raise Unauthorized("No types found, you are not allowed to search")
|
|
27
|
+
self._constrain_query_by_path(query)
|
|
28
|
+
if not query.get("path"):
|
|
29
|
+
query["path"] = {"query": "/Plone"}
|
|
30
|
+
# query = self._parse_query(query)
|
|
31
|
+
if query.get("type_of_request") == "count_contents_types":
|
|
32
|
+
results = self.count_contents_types(query)
|
|
33
|
+
elif query.get("type_of_request") == "find_big_files_or_images":
|
|
34
|
+
results = self.find_big_files_or_images(query)
|
|
35
|
+
elif query.get("type_of_request") == "get_max_depth":
|
|
36
|
+
results = self.get_max_depth()
|
|
37
|
+
elif query.get("type_of_request") == "check_value_of_field":
|
|
38
|
+
results = self.check_value_of_field(
|
|
39
|
+
query.get("portal_type"),
|
|
40
|
+
query.get("field_name"),
|
|
41
|
+
query.get("expected_values"),
|
|
42
|
+
)
|
|
43
|
+
else:
|
|
44
|
+
return super().search(query)
|
|
45
|
+
return results
|
|
46
|
+
|
|
47
|
+
def count_contents_types(self, query):
|
|
48
|
+
results = {"items": []}
|
|
49
|
+
if query.get("operator") == "and":
|
|
50
|
+
query["path"]["depth"] = -1
|
|
51
|
+
lazy_resultset = self.catalog.searchResults(**query)
|
|
52
|
+
results = {
|
|
53
|
+
"items": [
|
|
54
|
+
{
|
|
55
|
+
"portal_type": query.get("portal_type"),
|
|
56
|
+
"nb_items": len(lazy_resultset),
|
|
57
|
+
}
|
|
58
|
+
]
|
|
59
|
+
}
|
|
60
|
+
else:
|
|
61
|
+
portal_types = (
|
|
62
|
+
query.get("portal_type")
|
|
63
|
+
if isinstance(query.get("portal_type"), list)
|
|
64
|
+
else [query.get("portal_type")] if query.get("portal_type") else []
|
|
65
|
+
)
|
|
66
|
+
for portal_type in portal_types:
|
|
67
|
+
new_query = query.copy()
|
|
68
|
+
new_query["portal_type"] = portal_type
|
|
69
|
+
lazy_resultset = self.catalog.searchResults(**new_query)
|
|
70
|
+
results["items"].append(
|
|
71
|
+
{"portal_type": portal_type, "nb_items": len(lazy_resultset)}
|
|
72
|
+
)
|
|
73
|
+
# fullobjects = False
|
|
74
|
+
# results = getMultiAdapter((lazy_resultset, self.request), ISerializeToJson)(
|
|
75
|
+
# fullobjects=fullobjects
|
|
76
|
+
# )
|
|
77
|
+
return results
|
|
78
|
+
|
|
79
|
+
def find_big_files_or_images(self, query):
|
|
80
|
+
SIZE_LIMIT = query.get("size", "1000000")
|
|
81
|
+
results = {"items": []}
|
|
82
|
+
lazy_resultset = self.catalog.searchResults(**query)
|
|
83
|
+
for brain in lazy_resultset:
|
|
84
|
+
obj = brain.getObject()
|
|
85
|
+
blob = getattr(obj, "file", None) or getattr(obj, "image", None)
|
|
86
|
+
if blob and hasattr(blob, "getSize"):
|
|
87
|
+
size = blob.getSize()
|
|
88
|
+
if size > int(SIZE_LIMIT):
|
|
89
|
+
results["items"].append(
|
|
90
|
+
{
|
|
91
|
+
"title": obj.title,
|
|
92
|
+
"portal_type": obj.portal_type,
|
|
93
|
+
"url": obj.absolute_url(),
|
|
94
|
+
"size": round(size / (1024 * 1024), 2),
|
|
95
|
+
}
|
|
96
|
+
)
|
|
97
|
+
return results
|
|
98
|
+
|
|
99
|
+
def get_max_depth(self):
|
|
100
|
+
portal = api.portal.get()
|
|
101
|
+
catalog = getToolByName(portal, "portal_catalog")
|
|
102
|
+
|
|
103
|
+
max_depth = 0
|
|
104
|
+
results = {"items": []}
|
|
105
|
+
|
|
106
|
+
for brain in catalog():
|
|
107
|
+
path = brain.getPath()
|
|
108
|
+
depth = len(path.strip("/").split("/"))
|
|
109
|
+
if depth > max_depth:
|
|
110
|
+
max_depth = depth
|
|
111
|
+
results["items"] = [{"path": path, "depth": depth}]
|
|
112
|
+
elif depth == max_depth:
|
|
113
|
+
results["items"].append({"path": path, "depth": depth})
|
|
114
|
+
results["max_depth"] = max_depth
|
|
115
|
+
return results
|
|
116
|
+
|
|
117
|
+
def check_value_of_field(self, portal_type, field_name, expected_values):
|
|
118
|
+
"""
|
|
119
|
+
Analyse la répartition des valeurs d'un champ dans les objets d'un content_type donné.
|
|
120
|
+
query sample : http://localhost:8085/Plone/@find?portal_type=imio.events.Event&field_name=event_type&expected_values=["activity", "event-driven"]&type_of_request=check_value_of_field
|
|
121
|
+
|
|
122
|
+
:param content_type: str, le portal_type (ex: "Event")
|
|
123
|
+
:param field_name: str, field name (ex: "event_type")
|
|
124
|
+
:param expected_values: list, list of values we want to check (ex: ["activity", "event-driven"])
|
|
125
|
+
:return: dict {valeur: {"count": n, "percent": x.xx}}
|
|
126
|
+
"""
|
|
127
|
+
brains = self.catalog(portal_type=portal_type)
|
|
128
|
+
|
|
129
|
+
total = len(brains)
|
|
130
|
+
if total == 0:
|
|
131
|
+
return {}
|
|
132
|
+
|
|
133
|
+
# Récupération des valeurs réelles dans les objets
|
|
134
|
+
values = []
|
|
135
|
+
for brain in brains:
|
|
136
|
+
obj = brain.getObject()
|
|
137
|
+
value = getattr(obj, field_name, None)
|
|
138
|
+
if isinstance(value, (list, tuple)): # cas champ multi-valué
|
|
139
|
+
values.extend(value)
|
|
140
|
+
else:
|
|
141
|
+
values.append(value)
|
|
142
|
+
counter = Counter(values)
|
|
143
|
+
|
|
144
|
+
# Calcul stats seulement pour expected_values
|
|
145
|
+
result = {}
|
|
146
|
+
expected_values = normalize_query_param(expected_values)
|
|
147
|
+
for val in expected_values:
|
|
148
|
+
count = counter.get(val, 0)
|
|
149
|
+
percent = (count / total) * 100 if total > 0 else 0
|
|
150
|
+
result[val] = {"count": count, "percent": round(percent, 2)}
|
|
151
|
+
return result
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
class FindEndpoint(Service):
|
|
155
|
+
def reply(self):
|
|
156
|
+
query = self.request.form.copy()
|
|
157
|
+
query = unflatten_dotted_dict(query)
|
|
158
|
+
return FindEndpointHandler(self.context, self.request).search(query)
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
def normalize_query_param(value):
|
|
162
|
+
if isinstance(value, list):
|
|
163
|
+
return value
|
|
164
|
+
|
|
165
|
+
if isinstance(value, str):
|
|
166
|
+
val = value.strip()
|
|
167
|
+
# Try to parse json list
|
|
168
|
+
if val.startswith("[") and val.endswith("]"):
|
|
169
|
+
try:
|
|
170
|
+
return json.loads(val)
|
|
171
|
+
except json.JSONDecodeError:
|
|
172
|
+
return [val]
|
|
173
|
+
else:
|
|
174
|
+
return [val]
|
|
175
|
+
return [value]
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: imio.smartweb.common
|
|
3
|
-
Version: 1.2.
|
|
3
|
+
Version: 1.2.37
|
|
4
4
|
Summary: Common utilities, vocabularies, taxonomies for imio.smartweb & co products
|
|
5
5
|
Home-page: https://github.com/imio/imio.smartweb.common
|
|
6
6
|
Author: iMio
|
|
@@ -178,6 +178,13 @@ Changelog
|
|
|
178
178
|
=========
|
|
179
179
|
|
|
180
180
|
|
|
181
|
+
1.2.37 (2025-09-03)
|
|
182
|
+
-------------------
|
|
183
|
+
|
|
184
|
+
- Add new @find endpoint to find content in instance
|
|
185
|
+
[boulch]
|
|
186
|
+
|
|
187
|
+
|
|
181
188
|
1.2.36 (2025-06-25)
|
|
182
189
|
-------------------
|
|
183
190
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
imio.smartweb.common-1.2.
|
|
1
|
+
imio.smartweb.common-1.2.37-py3.12-nspkg.pth,sha256=XZ3YhlzwpUCC8tXtelHRqxVxo3NWomIiMsUfUshrbeE,1011
|
|
2
2
|
imio/smartweb/common/__init__.py,sha256=Na9XBfEQUMrm2c5jbqQgwWeg40ih0aXVG1vT8NeAjMQ,2709
|
|
3
3
|
imio/smartweb/common/adapters.py,sha256=dG4MALuHPQI6lTeNvs5p4vVOxHoBBYaWN6g8J96KxzQ,1507
|
|
4
4
|
imio/smartweb/common/adapters.zcml,sha256=VO3luZDtRL9FIIEghxPrqpx8paZF3m6MGEy-8kF1sOQ,437
|
|
@@ -75,7 +75,8 @@ imio/smartweb/common/profiles/testing/types/Document.xml,sha256=E3hhW_oL-WarUKcY
|
|
|
75
75
|
imio/smartweb/common/profiles/testing/types/Folder.xml,sha256=RPxIPgViAK0GGGTPKbdlw53sywyQXPhVj6B8x0tYSD8,312
|
|
76
76
|
imio/smartweb/common/profiles/uninstall/browserlayer.xml,sha256=glaCGlITwc36-1Fi2z0VCbLcoQ5GddkAhuW-SGYeZy8,130
|
|
77
77
|
imio/smartweb/common/rest/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
78
|
-
imio/smartweb/common/rest/configure.zcml,sha256=
|
|
78
|
+
imio/smartweb/common/rest/configure.zcml,sha256=eT5gr-PcfMRxQzMME7T3nzz2Y0BVI4r9HyKHIccQC2s,575
|
|
79
|
+
imio/smartweb/common/rest/endpoint.py,sha256=hgQsZW8VV9kOiupnSJfSTcXEecv2iAn76KSum5ysjnY,6712
|
|
79
80
|
imio/smartweb/common/rest/odwb.py,sha256=zRZxowUaE76xlW_blc92vRCzToUpnzw3O5mxFXaeScU,2249
|
|
80
81
|
imio/smartweb/common/rest/search_filters.py,sha256=upZAZ6h50qiSXo0q22lUgNlcmX5elFtrIQs_jFBl1-s,3832
|
|
81
82
|
imio/smartweb/common/rest/utils.py,sha256=9zc5ge_DmVuexxN09c3U6QAITAI1s2hc0m6eZuylSxM,1269
|
|
@@ -147,10 +148,10 @@ imio/smartweb/common/viewlets/skip_to_content.py,sha256=wm22NUf8Qh5uzz8p4vkLCdFN
|
|
|
147
148
|
imio/smartweb/common/widgets/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
148
149
|
imio/smartweb/common/widgets/select.py,sha256=5sV0UIgwu4AvwpgVmJeSZwUWz7OPD9VxYMowfmjcfbA,1313
|
|
149
150
|
imio/smartweb/common/widgets/templates/ajaxselect_display.pt,sha256=v8TT2Xc19er6SUGWabgNfIdrayFYIjxyLK9H_XkIG3M,1209
|
|
150
|
-
imio_smartweb_common-1.2.
|
|
151
|
-
imio_smartweb_common-1.2.
|
|
152
|
-
imio_smartweb_common-1.2.
|
|
153
|
-
imio_smartweb_common-1.2.
|
|
154
|
-
imio_smartweb_common-1.2.
|
|
155
|
-
imio_smartweb_common-1.2.
|
|
156
|
-
imio_smartweb_common-1.2.
|
|
151
|
+
imio_smartweb_common-1.2.37.dist-info/licenses/LICENSE.GPL,sha256=gXf5dRMhNSbfLPYYTY_5hsZ1r7UU1OaKQEAQUhuIBkM,18092
|
|
152
|
+
imio_smartweb_common-1.2.37.dist-info/licenses/LICENSE.rst,sha256=5dd78Fdt0e-oM2ICBrMpjHnT8vEP-jhBDF7akXni6B4,655
|
|
153
|
+
imio_smartweb_common-1.2.37.dist-info/METADATA,sha256=kCY4-SImXYTNlaTQ8SA2iHzNOjaM2B8-wSxhJz_Q-ts,18813
|
|
154
|
+
imio_smartweb_common-1.2.37.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
155
|
+
imio_smartweb_common-1.2.37.dist-info/namespace_packages.txt,sha256=Pg8AH8t9viMMW1hJbNZvTy_n2jXG2igIYUpon5RA4Js,19
|
|
156
|
+
imio_smartweb_common-1.2.37.dist-info/top_level.txt,sha256=ZktC0EGzThvMTAin9_q_41rzvvfMT2FYbP8pbhSLMSA,5
|
|
157
|
+
imio_smartweb_common-1.2.37.dist-info/RECORD,,
|
/imio.smartweb.common-1.2.36-py3.12-nspkg.pth → /imio.smartweb.common-1.2.37-py3.12-nspkg.pth
RENAMED
|
File without changes
|
|
File without changes
|
{imio_smartweb_common-1.2.36.dist-info → imio_smartweb_common-1.2.37.dist-info}/licenses/LICENSE.GPL
RENAMED
|
File without changes
|
{imio_smartweb_common-1.2.36.dist-info → imio_smartweb_common-1.2.37.dist-info}/licenses/LICENSE.rst
RENAMED
|
File without changes
|
|
File without changes
|
{imio_smartweb_common-1.2.36.dist-info → imio_smartweb_common-1.2.37.dist-info}/top_level.txt
RENAMED
|
File without changes
|