udata 9.2.5.dev32190__py2.py3-none-any.whl → 9.2.5.dev32222__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.
Potentially problematic release.
This version of udata might be problematic. Click here for more details.
- udata/api/__init__.py +1 -0
- udata/core/dataservices/api.py +1 -1
- udata/core/dataservices/apiv2.py +29 -0
- udata/core/dataservices/search.py +118 -0
- udata/core/dataset/search.py +2 -3
- udata/core/organization/search.py +2 -3
- udata/core/reuse/search.py +2 -3
- udata/search/query.py +4 -2
- udata/static/chunks/{10.dac55d18d0b4ef3cdacf.js → 10.a99bb538cfbadb38dbcb.js} +3 -3
- udata/static/chunks/{10.dac55d18d0b4ef3cdacf.js.map → 10.a99bb538cfbadb38dbcb.js.map} +1 -1
- udata/static/chunks/{11.4a20a75f827c5a1125c3.js → 11.bb1c1fb39f740fbbeec0.js} +3 -3
- udata/static/chunks/{11.4a20a75f827c5a1125c3.js.map → 11.bb1c1fb39f740fbbeec0.js.map} +1 -1
- udata/static/chunks/{13.645dd0b7c0b9210f1b56.js → 13.bef5fdb3e147e94fea99.js} +2 -2
- udata/static/chunks/{13.645dd0b7c0b9210f1b56.js.map → 13.bef5fdb3e147e94fea99.js.map} +1 -1
- udata/static/chunks/{17.8e19985c4d12a3b7b0c0.js → 17.b91d28f550dc44bc4979.js} +2 -2
- udata/static/chunks/{17.8e19985c4d12a3b7b0c0.js.map → 17.b91d28f550dc44bc4979.js.map} +1 -1
- udata/static/chunks/{19.825a43c330157e351fca.js → 19.2c615ffee1e807000770.js} +3 -3
- udata/static/chunks/{19.825a43c330157e351fca.js.map → 19.2c615ffee1e807000770.js.map} +1 -1
- udata/static/chunks/{8.5ee0cf635c848abbfc05.js → 8.291bde987ed97294e4de.js} +2 -2
- udata/static/chunks/{8.5ee0cf635c848abbfc05.js.map → 8.291bde987ed97294e4de.js.map} +1 -1
- udata/static/chunks/{9.df3c36f8d0d210621fbb.js → 9.985935421e62c97a9f86.js} +3 -3
- udata/static/chunks/{9.df3c36f8d0d210621fbb.js.map → 9.985935421e62c97a9f86.js.map} +1 -1
- udata/static/common.js +1 -1
- udata/static/common.js.map +1 -1
- {udata-9.2.5.dev32190.dist-info → udata-9.2.5.dev32222.dist-info}/METADATA +3 -1
- {udata-9.2.5.dev32190.dist-info → udata-9.2.5.dev32222.dist-info}/RECORD +30 -28
- {udata-9.2.5.dev32190.dist-info → udata-9.2.5.dev32222.dist-info}/LICENSE +0 -0
- {udata-9.2.5.dev32190.dist-info → udata-9.2.5.dev32222.dist-info}/WHEEL +0 -0
- {udata-9.2.5.dev32190.dist-info → udata-9.2.5.dev32222.dist-info}/entry_points.txt +0 -0
- {udata-9.2.5.dev32190.dist-info → udata-9.2.5.dev32222.dist-info}/top_level.txt +0 -0
udata/api/__init__.py
CHANGED
|
@@ -303,6 +303,7 @@ def init_app(app):
|
|
|
303
303
|
import udata.core.dataset.api # noqa
|
|
304
304
|
import udata.core.dataset.apiv2 # noqa
|
|
305
305
|
import udata.core.dataservices.api # noqa
|
|
306
|
+
import udata.core.dataservices.apiv2 # noqa
|
|
306
307
|
import udata.core.discussions.api # noqa
|
|
307
308
|
import udata.core.reuse.api # noqa
|
|
308
309
|
import udata.core.reuse.apiv2 # noqa
|
udata/core/dataservices/api.py
CHANGED
|
@@ -7,8 +7,8 @@ from flask_login import current_user
|
|
|
7
7
|
|
|
8
8
|
from udata.api import API, api, fields
|
|
9
9
|
from udata.api_fields import patch
|
|
10
|
+
from udata.core.dataservices.permissions import OwnablePermission
|
|
10
11
|
from udata.core.dataset.models import Dataset
|
|
11
|
-
from udata.core.dataset.permissions import OwnablePermission
|
|
12
12
|
from udata.core.followers.api import FollowAPI
|
|
13
13
|
from udata.rdf import RDF_EXTENSIONS, graph_response, negociate_content
|
|
14
14
|
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
from flask import request
|
|
2
|
+
|
|
3
|
+
from udata import search
|
|
4
|
+
from udata.api import API, apiv2
|
|
5
|
+
from udata.core.dataservices.models import Dataservice, HarvestMetadata
|
|
6
|
+
from udata.utils import multi_to_dict
|
|
7
|
+
|
|
8
|
+
from .search import DataserviceSearch
|
|
9
|
+
|
|
10
|
+
apiv2.inherit("DataservicePage", Dataservice.__page_fields__)
|
|
11
|
+
apiv2.inherit("Dataservice (read)", Dataservice.__read_fields__)
|
|
12
|
+
apiv2.inherit("HarvestMetadata (read)", HarvestMetadata.__read_fields__)
|
|
13
|
+
|
|
14
|
+
ns = apiv2.namespace("dataservices", "Dataservice related operations")
|
|
15
|
+
|
|
16
|
+
search_parser = DataserviceSearch.as_request_parser()
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@ns.route("/search/", endpoint="dataservice_search")
|
|
20
|
+
class DataserviceSearchAPI(API):
|
|
21
|
+
"""Dataservices collection search endpoint"""
|
|
22
|
+
|
|
23
|
+
@apiv2.doc("search_dataservices")
|
|
24
|
+
@apiv2.expect(search_parser)
|
|
25
|
+
@apiv2.marshal_with(Dataservice.__page_fields__)
|
|
26
|
+
def get(self):
|
|
27
|
+
"""Search all dataservices"""
|
|
28
|
+
search_parser.parse_args()
|
|
29
|
+
return search.query(DataserviceSearch, **multi_to_dict(request.args))
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import datetime
|
|
2
|
+
|
|
3
|
+
from bson.objectid import ObjectId
|
|
4
|
+
from flask_restx.inputs import boolean
|
|
5
|
+
|
|
6
|
+
from udata.api import api
|
|
7
|
+
from udata.api.parsers import ModelApiParser
|
|
8
|
+
from udata.models import Dataservice, Organization, User
|
|
9
|
+
from udata.search import (
|
|
10
|
+
BoolFilter,
|
|
11
|
+
Filter,
|
|
12
|
+
ModelSearchAdapter,
|
|
13
|
+
ModelTermsFilter,
|
|
14
|
+
register,
|
|
15
|
+
)
|
|
16
|
+
from udata.utils import to_iso_datetime
|
|
17
|
+
|
|
18
|
+
__all__ = ("DataserviceSearch",)
|
|
19
|
+
|
|
20
|
+
DEFAULT_SORTING = "-created_at"
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class DataserviceApiParser(ModelApiParser):
|
|
24
|
+
sorts = {
|
|
25
|
+
"created": "created_at",
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
def __init__(self):
|
|
29
|
+
super().__init__()
|
|
30
|
+
self.parser.add_argument("tag", type=str, location="args")
|
|
31
|
+
self.parser.add_argument("organization", type=str, location="args")
|
|
32
|
+
self.parser.add_argument("is_restricted", type=bool, location="args")
|
|
33
|
+
|
|
34
|
+
@staticmethod
|
|
35
|
+
def parse_filters(dataservices, args):
|
|
36
|
+
if args.get("q"):
|
|
37
|
+
# Following code splits the 'q' argument by spaces to surround
|
|
38
|
+
# every word in it with quotes before rebuild it.
|
|
39
|
+
# This allows the search_text method to tokenise with an AND
|
|
40
|
+
# between tokens whereas an OR is used without it.
|
|
41
|
+
phrase_query = " ".join([f'"{elem}"' for elem in args["q"].split(" ")])
|
|
42
|
+
dataservices = dataservices.search_text(phrase_query)
|
|
43
|
+
if args.get("tag"):
|
|
44
|
+
dataservices = dataservices.filter(tags=args["tag"])
|
|
45
|
+
if args.get("organization"):
|
|
46
|
+
if not ObjectId.is_valid(args["organization"]):
|
|
47
|
+
api.abort(400, "Organization arg must be an identifier")
|
|
48
|
+
dataservices = dataservices.filter(organization=args["organization"])
|
|
49
|
+
if "is_restricted" in args:
|
|
50
|
+
dataservices = dataservices.filter(is_restricted=boolean(args["is_restricted"]))
|
|
51
|
+
return dataservices
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
@register
|
|
55
|
+
class DataserviceSearch(ModelSearchAdapter):
|
|
56
|
+
model = Dataservice
|
|
57
|
+
search_url = "dataservices/"
|
|
58
|
+
|
|
59
|
+
sorts = {
|
|
60
|
+
"created": "created_at",
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
filters = {
|
|
64
|
+
"tag": Filter(),
|
|
65
|
+
"organization": ModelTermsFilter(model=Organization),
|
|
66
|
+
"archived": BoolFilter(),
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
@classmethod
|
|
70
|
+
def is_indexable(cls, dataservice: Dataservice) -> bool:
|
|
71
|
+
return dataservice.deleted_at is None and not dataservice.private
|
|
72
|
+
|
|
73
|
+
@classmethod
|
|
74
|
+
def mongo_search(cls, args):
|
|
75
|
+
dataservices = Dataservice.objects.visible()
|
|
76
|
+
dataservices = DataserviceApiParser.parse_filters(dataservices, args)
|
|
77
|
+
|
|
78
|
+
sort = (
|
|
79
|
+
cls.parse_sort(args["sort"])
|
|
80
|
+
or ("$text_score" if args["q"] else None)
|
|
81
|
+
or DEFAULT_SORTING
|
|
82
|
+
)
|
|
83
|
+
return dataservices.order_by(sort).paginate(args["page"], args["page_size"])
|
|
84
|
+
|
|
85
|
+
@classmethod
|
|
86
|
+
def serialize(cls, dataservice: Dataservice) -> dict:
|
|
87
|
+
organization = None
|
|
88
|
+
owner = None
|
|
89
|
+
if dataservice.organization:
|
|
90
|
+
org = Organization.objects(id=dataservice.organization.id).first()
|
|
91
|
+
organization = {
|
|
92
|
+
"id": str(org.id),
|
|
93
|
+
"name": org.name,
|
|
94
|
+
"public_service": 1 if org.public_service else 0,
|
|
95
|
+
"followers": org.metrics.get("followers", 0),
|
|
96
|
+
}
|
|
97
|
+
elif dataservice.owner:
|
|
98
|
+
owner = User.objects(id=dataservice.owner.id).first()
|
|
99
|
+
extras = {}
|
|
100
|
+
for key, value in dataservice.extras.items():
|
|
101
|
+
extras[key] = to_iso_datetime(value) if isinstance(value, datetime.datetime) else value
|
|
102
|
+
|
|
103
|
+
return {
|
|
104
|
+
"id": str(dataservice.id),
|
|
105
|
+
"title": dataservice.title,
|
|
106
|
+
"description": dataservice.description,
|
|
107
|
+
"base_api_url": dataservice.base_api_url,
|
|
108
|
+
"created_at": to_iso_datetime(dataservice.created_at),
|
|
109
|
+
"archived": to_iso_datetime(dataservice.archived_at)
|
|
110
|
+
if dataservice.archived_at
|
|
111
|
+
else None,
|
|
112
|
+
"organization": organization,
|
|
113
|
+
"owner": str(owner.id) if owner else None,
|
|
114
|
+
"tags": dataservice.tags,
|
|
115
|
+
"extras": extras,
|
|
116
|
+
"followers": dataservice.metrics.get("followers", 0),
|
|
117
|
+
"is_restricted": dataservice.is_restricted or False,
|
|
118
|
+
}
|
udata/core/dataset/search.py
CHANGED
|
@@ -52,7 +52,7 @@ class DatasetSearch(ModelSearchAdapter):
|
|
|
52
52
|
|
|
53
53
|
@classmethod
|
|
54
54
|
def mongo_search(cls, args):
|
|
55
|
-
datasets = Dataset.objects(
|
|
55
|
+
datasets = Dataset.objects.visible()
|
|
56
56
|
datasets = DatasetApiParser.parse_filters(datasets, args)
|
|
57
57
|
|
|
58
58
|
sort = (
|
|
@@ -60,8 +60,7 @@ class DatasetSearch(ModelSearchAdapter):
|
|
|
60
60
|
or ("$text_score" if args["q"] else None)
|
|
61
61
|
or DEFAULT_SORTING
|
|
62
62
|
)
|
|
63
|
-
|
|
64
|
-
return datasets.order_by(sort).skip(offset).limit(args["page_size"]), datasets.count()
|
|
63
|
+
return datasets.order_by(sort).paginate(args["page"], args["page_size"])
|
|
65
64
|
|
|
66
65
|
@classmethod
|
|
67
66
|
def serialize(cls, dataset):
|
|
@@ -34,7 +34,7 @@ class OrganizationSearch(search.ModelSearchAdapter):
|
|
|
34
34
|
|
|
35
35
|
@classmethod
|
|
36
36
|
def mongo_search(cls, args):
|
|
37
|
-
orgs = Organization.objects(
|
|
37
|
+
orgs = Organization.objects.visible()
|
|
38
38
|
orgs = OrgApiParser.parse_filters(orgs, args)
|
|
39
39
|
|
|
40
40
|
sort = (
|
|
@@ -42,8 +42,7 @@ class OrganizationSearch(search.ModelSearchAdapter):
|
|
|
42
42
|
or ("$text_score" if args["q"] else None)
|
|
43
43
|
or DEFAULT_SORTING
|
|
44
44
|
)
|
|
45
|
-
|
|
46
|
-
return orgs.order_by(sort).skip(offset).limit(args["page_size"]), orgs.count()
|
|
45
|
+
return orgs.order_by(sort).paginate(args["page"], args["page_size"])
|
|
47
46
|
|
|
48
47
|
@classmethod
|
|
49
48
|
def serialize(cls, organization):
|
udata/core/reuse/search.py
CHANGED
|
@@ -44,7 +44,7 @@ class ReuseSearch(ModelSearchAdapter):
|
|
|
44
44
|
|
|
45
45
|
@classmethod
|
|
46
46
|
def mongo_search(cls, args):
|
|
47
|
-
reuses = Reuse.objects(
|
|
47
|
+
reuses = Reuse.objects.visible()
|
|
48
48
|
reuses = ReuseApiParser.parse_filters(reuses, args)
|
|
49
49
|
|
|
50
50
|
sort = (
|
|
@@ -52,8 +52,7 @@ class ReuseSearch(ModelSearchAdapter):
|
|
|
52
52
|
or ("$text_score" if args["q"] else None)
|
|
53
53
|
or DEFAULT_SORTING
|
|
54
54
|
)
|
|
55
|
-
|
|
56
|
-
return reuses.order_by(sort).skip(offset).limit(args["page_size"]), reuses.count()
|
|
55
|
+
return reuses.order_by(sort).paginate(args["page"], args["page_size"])
|
|
57
56
|
|
|
58
57
|
@classmethod
|
|
59
58
|
def serialize(cls, reuse: Reuse) -> dict:
|
udata/search/query.py
CHANGED
|
@@ -54,8 +54,10 @@ class SearchQuery:
|
|
|
54
54
|
"sort": self.sort,
|
|
55
55
|
}
|
|
56
56
|
query_args.update(self._filters)
|
|
57
|
-
result
|
|
58
|
-
return SearchResult(
|
|
57
|
+
result = self.adapter.mongo_search(query_args)
|
|
58
|
+
return SearchResult(
|
|
59
|
+
query=self, mongo_objects=list(result), total=result.total, **query_args
|
|
60
|
+
)
|
|
59
61
|
|
|
60
62
|
def to_url(self, url=None, replace=False, **kwargs):
|
|
61
63
|
"""Serialize the query into an URL"""
|