Coverage for portality / models / uploads.py: 72%
151 statements
« prev ^ index » next coverage.py v7.13.5, created at 2026-05-04 09:41 +0100
« prev ^ index » next coverage.py v7.13.5, created at 2026-05-04 09:41 +0100
1from copy import deepcopy
3from portality.dao import DomainObject, ESMappingMissingError
4from portality.lib import dates
7class BaseArticlesUpload(DomainObject):
8 """
9 Base class for article uploads. which handle status and error messages.
11 This is abstract class. it has no __type__ attribute.
12 For object creation and query please use FileUpload or BulkArticles instead.
14 """
16 @property
17 def status(self):
18 return self.data.get("status")
20 @property
21 def owner(self):
22 return self.data.get("owner")
24 @property
25 def imported(self):
26 return self.data.get("imported", 0)
28 @property
29 def failed_imports(self):
30 return self.data.get("failed", 0)
32 @property
33 def updates(self):
34 return self.data.get("update", 0)
36 @property
37 def new(self):
38 return self.data.get("new", 0)
40 @property
41 def error(self):
42 return self.data.get("error")
44 @property
45 def error_details(self):
46 return self.data.get("error_details")
48 @property
49 def failure_reasons(self):
50 return self.data.get("failure_reasons", {})
52 @property
53 def created_timestamp(self):
54 if "created_date" not in self.data:
55 return None
56 return dates.parse(self.data["created_date"])
58 def failed(self, message, details=None):
59 self.data["status"] = "failed"
60 self.data["error"] = message
61 if details is not None:
62 self.data["error_details"] = details
64 def validated(self):
65 self.data["status"] = "validated"
67 def processed(self, count, update, new):
68 self.data["status"] = "processed"
69 self.data["imported"] = count
70 self.data["update"] = update
71 self.data["new"] = new
73 def partial(self, success, fail, update, new):
74 self.data["status"] = "partial"
75 self.data["imported"] = success
76 self.data["failed"] = fail
77 self.data["update"] = update
78 self.data["new"] = new
80 def set_failure_reasons(self, shared, unowned, unmatched):
81 self.data["failure_reasons"] = {}
82 if shared is not None and len(shared) > 0:
83 self.data["failure_reasons"]["shared"] = shared
84 if unowned is not None and len(unowned) > 0:
85 self.data["failure_reasons"]["unowned"] = unowned
86 if unmatched is not None and len(unmatched) > 0:
87 self.data["failure_reasons"]["unmatched"] = unmatched
89 def exists(self):
90 self.data["status"] = "exists"
92 @classmethod
93 def list_valid(cls):
94 q = ValidFileQuery()
95 return cls.iterate(q=q.query(), page_size=10000)
97 @classmethod
98 def list_remote(cls):
99 q = ExistsFileQuery()
100 return cls.iterate(q=q.query(), page_size=10000)
102 @classmethod
103 def by_owner(cls, owner, size=10):
104 q = OwnerFileQuery(owner)
105 try:
106 res = cls.query(q=q.query())
107 except ESMappingMissingError:
108 return []
109 rs = [cls(**r.get("_source")) for r in res.get("hits", {}).get("hits", [])]
110 return rs
113class FileUpload(BaseArticlesUpload):
114 __type__ = "upload"
116 @classmethod
117 def by_properties(cls, status=None, schema=None, max=10000):
118 q = PropertiesQuery(status=status, schema=schema, size=max)
119 return cls.object_query(q=q.query())
121 @property
122 def schema(self):
123 return self.data.get("schema")
125 def set_schema(self, s):
126 self.data["schema"] = s
128 @property
129 def filename(self):
130 return self.data.get("filename")
132 @property
133 def local_filename(self):
134 return self.id + ".xml"
136 def downloaded(self):
137 self.data["status"] = "downloaded"
139 def validated(self, schema=None):
140 self.data["status"] = "validated"
141 if schema is not None:
142 self.data["schema"] = schema
144 def upload(self, owner, filename, status="incoming"):
145 self.data["filename"] = filename
146 self.data["owner"] = owner
147 self.data["status"] = status
150class BulkArticles(BaseArticlesUpload):
151 __type__ = "bulk_articles"
153 @property
154 def local_filename(self):
155 return self.id + ".json"
157 def incoming(self, owner):
158 self.data["owner"] = owner
159 self.data["status"] = "incoming"
162class ValidFileQuery(object):
163 base_query = {
164 "track_total_hits": True,
165 "query": {
166 "term": {"status.exact": "validated"}
167 },
168 "sort": [
169 {"created_date": "asc"}
170 ]
171 }
173 def __init__(self):
174 self._query = deepcopy(self.base_query)
176 def query(self):
177 return self._query
180class ExistsFileQuery(object):
181 base_query = {
182 "track_total_hits": True,
183 "query": {
184 "term": {"status.exact": "exists"}
185 },
186 "sort": [
187 {"created_date": "asc"}
188 ]
189 }
191 def __init__(self):
192 self._query = deepcopy(self.base_query)
194 def query(self):
195 return self._query
198class OwnerFileQuery(object):
199 base_query = {
200 "track_total_hits": True,
201 "query": {
202 "bool": {
203 "must": []
204 }
205 },
206 "sort": [
207 {"created_date": "desc"}
208 ],
209 "size": 10
210 }
212 def __init__(self, owner, size=10):
213 self._query = deepcopy(self.base_query)
214 owner_term = {"match": {"owner": owner}}
215 self._query["query"]["bool"]["must"].append(owner_term)
216 self._query["size"] = size
218 def query(self):
219 return self._query
221class PropertiesQuery:
222 def __init__(self, status=None, schema=None, size=10000):
223 self._status = status
224 self._schema = schema
225 self._size = size
227 def query(self):
228 query = {
229 "query": {
230 "bool": {
231 "must": []
232 }
233 }
234 }
236 if self._status:
237 query["query"]["bool"]["must"].append({"term": {"status.exact": self._status}})
239 if self._schema:
240 query["query"]["bool"]["must"].append({"term": {"schema.exact": self._schema}})
242 if self._size:
243 query["size"] = self._size
245 return query