goodmap 0.5.3__py3-none-any.whl → 1.0.2__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.
- goodmap/core_api.py +18 -90
- goodmap/db.py +737 -19
- {goodmap-0.5.3.dist-info → goodmap-1.0.2.dist-info}/METADATA +6 -4
- {goodmap-0.5.3.dist-info → goodmap-1.0.2.dist-info}/RECORD +6 -6
- {goodmap-0.5.3.dist-info → goodmap-1.0.2.dist-info}/WHEEL +1 -1
- {goodmap-0.5.3.dist-info → goodmap-1.0.2.dist-info/licenses}/LICENSE.md +0 -0
goodmap/core_api.py
CHANGED
|
@@ -22,74 +22,6 @@ def get_or_none(data, *keys):
|
|
|
22
22
|
return data
|
|
23
23
|
|
|
24
24
|
|
|
25
|
-
def paginate_results(items, raw_params, sort_by_default=None):
|
|
26
|
-
"""
|
|
27
|
-
Apply pagination and sorting to a list of items.
|
|
28
|
-
|
|
29
|
-
Args:
|
|
30
|
-
items: The list of items to paginate
|
|
31
|
-
raw_params: The query parameters dictionary
|
|
32
|
-
|
|
33
|
-
Returns:
|
|
34
|
-
Tuple of (paginated_items, pagination_metadata)
|
|
35
|
-
"""
|
|
36
|
-
# Extract pagination parameters
|
|
37
|
-
try:
|
|
38
|
-
page = int(raw_params.pop("page", ["1"])[0])
|
|
39
|
-
except ValueError:
|
|
40
|
-
page = 1
|
|
41
|
-
|
|
42
|
-
per_page_raw = raw_params.pop("per_page", [None])[0]
|
|
43
|
-
if per_page_raw is None:
|
|
44
|
-
per_page = 20
|
|
45
|
-
elif per_page_raw == "all":
|
|
46
|
-
per_page = None
|
|
47
|
-
else:
|
|
48
|
-
try:
|
|
49
|
-
per_page = max(1, int(per_page_raw))
|
|
50
|
-
except ValueError:
|
|
51
|
-
per_page = 20
|
|
52
|
-
|
|
53
|
-
sort_by = raw_params.pop("sort_by", [None])[0] or sort_by_default
|
|
54
|
-
sort_order = raw_params.pop("sort_order", ["asc"])[0].lower()
|
|
55
|
-
|
|
56
|
-
def get_sort_key(item):
|
|
57
|
-
if not sort_by:
|
|
58
|
-
return None
|
|
59
|
-
|
|
60
|
-
value = None
|
|
61
|
-
if isinstance(item, dict):
|
|
62
|
-
value = item.get(sort_by)
|
|
63
|
-
else:
|
|
64
|
-
value = getattr(item, sort_by, None)
|
|
65
|
-
|
|
66
|
-
return (value is not None, value)
|
|
67
|
-
|
|
68
|
-
if sort_by:
|
|
69
|
-
reverse = sort_order == "desc"
|
|
70
|
-
items.sort(key=get_sort_key, reverse=reverse)
|
|
71
|
-
|
|
72
|
-
# Apply pagination
|
|
73
|
-
total = len(items)
|
|
74
|
-
if per_page:
|
|
75
|
-
start = (page - 1) * per_page
|
|
76
|
-
end = start + per_page
|
|
77
|
-
page_items = items[start:end]
|
|
78
|
-
total_pages = (total + per_page - 1) // per_page
|
|
79
|
-
else:
|
|
80
|
-
page_items = items
|
|
81
|
-
total_pages = 1
|
|
82
|
-
page = 1
|
|
83
|
-
per_page = total
|
|
84
|
-
|
|
85
|
-
return page_items, {
|
|
86
|
-
"total": total,
|
|
87
|
-
"page": page,
|
|
88
|
-
"per_page": per_page,
|
|
89
|
-
"total_pages": total_pages,
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
|
|
93
25
|
def core_pages(
|
|
94
26
|
database,
|
|
95
27
|
languages: LanguagesMapping,
|
|
@@ -207,13 +139,14 @@ def core_pages(
|
|
|
207
139
|
class Categories(Resource):
|
|
208
140
|
def get(self):
|
|
209
141
|
"""Shows all available categories"""
|
|
210
|
-
|
|
211
|
-
categories = make_tuple_translation(
|
|
142
|
+
raw_categories = database.get_categories()
|
|
143
|
+
categories = make_tuple_translation(raw_categories)
|
|
212
144
|
|
|
213
145
|
if not feature_flags.get("CATEGORIES_HELP", False):
|
|
214
146
|
return jsonify(categories)
|
|
215
147
|
else:
|
|
216
|
-
|
|
148
|
+
category_data = database.get_category_data()
|
|
149
|
+
categories_help = category_data["categories_help"]
|
|
217
150
|
proper_categories_help = []
|
|
218
151
|
if categories_help is not None:
|
|
219
152
|
for option in categories_help:
|
|
@@ -233,11 +166,11 @@ def core_pages(
|
|
|
233
166
|
class CategoryTypes(Resource):
|
|
234
167
|
def get(self, category_type):
|
|
235
168
|
"""Shows all available types in category"""
|
|
236
|
-
|
|
237
|
-
local_data = make_tuple_translation(
|
|
169
|
+
category_data = database.get_category_data(category_type)
|
|
170
|
+
local_data = make_tuple_translation(category_data["categories"][category_type])
|
|
238
171
|
|
|
239
172
|
categories_options_help = get_or_none(
|
|
240
|
-
|
|
173
|
+
category_data, "categories_options_help", category_type
|
|
241
174
|
)
|
|
242
175
|
proper_categories_options_help = []
|
|
243
176
|
if categories_options_help is not None:
|
|
@@ -268,14 +201,11 @@ def core_pages(
|
|
|
268
201
|
Shows full list of locations, with optional server-side pagination, sorting,
|
|
269
202
|
and filtering.
|
|
270
203
|
"""
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
)
|
|
277
|
-
items = [x.model_dump() for x in page_items]
|
|
278
|
-
return jsonify({"items": items, **pagination})
|
|
204
|
+
query_params = request.args.to_dict(flat=False)
|
|
205
|
+
if "sort_by" not in query_params:
|
|
206
|
+
query_params["sort_by"] = ["name"]
|
|
207
|
+
result = database.get_locations_paginated(query_params)
|
|
208
|
+
return jsonify(result)
|
|
279
209
|
|
|
280
210
|
def post(self):
|
|
281
211
|
"""
|
|
@@ -328,10 +258,9 @@ def core_pages(
|
|
|
328
258
|
List location suggestions, with optional server-side pagination, sorting,
|
|
329
259
|
and filtering by status.
|
|
330
260
|
"""
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
return jsonify({"items": page_items, **pagination})
|
|
261
|
+
query_params = request.args.to_dict(flat=False)
|
|
262
|
+
result = database.get_suggestions_paginated(query_params)
|
|
263
|
+
return jsonify(result)
|
|
335
264
|
|
|
336
265
|
@core_api.route("/admin/suggestions/<suggestion_id>")
|
|
337
266
|
class AdminManageSuggestion(Resource):
|
|
@@ -364,10 +293,9 @@ def core_pages(
|
|
|
364
293
|
List location reports, with optional server-side pagination, sorting,
|
|
365
294
|
and filtering by status/priority.
|
|
366
295
|
"""
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
return jsonify({"items": page_items, **pagination})
|
|
296
|
+
query_params = request.args.to_dict(flat=False)
|
|
297
|
+
result = database.get_reports_paginated(query_params)
|
|
298
|
+
return jsonify(result)
|
|
371
299
|
|
|
372
300
|
@core_api.route("/admin/reports/<report_id>")
|
|
373
301
|
class AdminManageReport(Resource):
|
goodmap/db.py
CHANGED
|
@@ -4,11 +4,53 @@ import tempfile
|
|
|
4
4
|
from functools import partial
|
|
5
5
|
|
|
6
6
|
from goodmap.core import get_queried_data
|
|
7
|
+
from goodmap.data_models.location import LocationBase
|
|
7
8
|
|
|
8
9
|
# TODO file is temporary solution to be compatible with old, static code,
|
|
9
10
|
# it should be replaced with dynamic solution
|
|
10
11
|
|
|
11
12
|
|
|
13
|
+
def __parse_pagination_params(query):
|
|
14
|
+
"""Extract and validate pagination parameters from query."""
|
|
15
|
+
try:
|
|
16
|
+
page = max(1, int(query.get("page", ["1"])[0]))
|
|
17
|
+
except (ValueError, IndexError, TypeError):
|
|
18
|
+
page = 1
|
|
19
|
+
|
|
20
|
+
per_page_raw = query.get("per_page", ["20"])[0] if query.get("per_page") else "20"
|
|
21
|
+
if per_page_raw == "all":
|
|
22
|
+
per_page = None
|
|
23
|
+
else:
|
|
24
|
+
try:
|
|
25
|
+
per_page = max(1, min(int(per_page_raw), 1000)) # Cap at 1000
|
|
26
|
+
except (ValueError, TypeError):
|
|
27
|
+
per_page = 20
|
|
28
|
+
|
|
29
|
+
sort_by = query.get("sort_by", [None])[0]
|
|
30
|
+
sort_order = query.get("sort_order", ["asc"])[0] if query.get("sort_order") else "asc"
|
|
31
|
+
|
|
32
|
+
return page, per_page, sort_by, sort_order.lower()
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def __build_pagination_response(items, total, page, per_page):
|
|
36
|
+
"""Build standardized pagination response."""
|
|
37
|
+
if per_page:
|
|
38
|
+
total_pages = (total + per_page - 1) // per_page
|
|
39
|
+
else:
|
|
40
|
+
total_pages = 1
|
|
41
|
+
per_page = total
|
|
42
|
+
|
|
43
|
+
return {
|
|
44
|
+
"items": items,
|
|
45
|
+
"pagination": {
|
|
46
|
+
"total": total,
|
|
47
|
+
"page": page,
|
|
48
|
+
"per_page": per_page,
|
|
49
|
+
"total_pages": total_pages,
|
|
50
|
+
},
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
|
|
12
54
|
def json_file_atomic_dump(data, file_path):
|
|
13
55
|
dir_name = os.path.dirname(file_path)
|
|
14
56
|
with tempfile.NamedTemporaryFile("w", dir=dir_name, delete=False) as temp_file:
|
|
@@ -18,6 +60,264 @@ def json_file_atomic_dump(data, file_path):
|
|
|
18
60
|
os.replace(temp_file.name, file_path)
|
|
19
61
|
|
|
20
62
|
|
|
63
|
+
class PaginationHelper:
|
|
64
|
+
"""Common pagination utility to eliminate duplication across backends."""
|
|
65
|
+
|
|
66
|
+
@staticmethod
|
|
67
|
+
def get_sort_key(item, sort_by):
|
|
68
|
+
"""Extract sort key from item for both dict and object types."""
|
|
69
|
+
if sort_by == "name" and hasattr(item, "name"):
|
|
70
|
+
value = item.name
|
|
71
|
+
elif isinstance(item, dict):
|
|
72
|
+
value = item.get(sort_by)
|
|
73
|
+
else:
|
|
74
|
+
value = getattr(item, sort_by, None)
|
|
75
|
+
return (value is not None, value or "")
|
|
76
|
+
|
|
77
|
+
@staticmethod
|
|
78
|
+
def apply_pagination_and_sorting(items, page, per_page, sort_by, sort_order):
|
|
79
|
+
"""Apply sorting and pagination to a list of items."""
|
|
80
|
+
# Apply sorting
|
|
81
|
+
if sort_by:
|
|
82
|
+
reverse = sort_order == "desc"
|
|
83
|
+
items.sort(
|
|
84
|
+
key=lambda item: PaginationHelper.get_sort_key(item, sort_by), reverse=reverse # type: ignore
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
total_count = len(items)
|
|
88
|
+
|
|
89
|
+
# Apply pagination
|
|
90
|
+
if per_page:
|
|
91
|
+
start_idx = (page - 1) * per_page
|
|
92
|
+
end_idx = start_idx + per_page
|
|
93
|
+
paginated_items = items[start_idx:end_idx]
|
|
94
|
+
else:
|
|
95
|
+
paginated_items = items
|
|
96
|
+
|
|
97
|
+
return paginated_items, total_count
|
|
98
|
+
|
|
99
|
+
@staticmethod
|
|
100
|
+
def apply_filters(items, filters):
|
|
101
|
+
"""Apply filtering based on provided filters dictionary."""
|
|
102
|
+
filtered_items = items
|
|
103
|
+
|
|
104
|
+
# Apply status filtering
|
|
105
|
+
if "status" in filters:
|
|
106
|
+
statuses = filters["status"]
|
|
107
|
+
if statuses:
|
|
108
|
+
filtered_items = [
|
|
109
|
+
item
|
|
110
|
+
for item in filtered_items
|
|
111
|
+
if (
|
|
112
|
+
item.get("status")
|
|
113
|
+
if isinstance(item, dict)
|
|
114
|
+
else getattr(item, "status", None)
|
|
115
|
+
)
|
|
116
|
+
in statuses
|
|
117
|
+
]
|
|
118
|
+
|
|
119
|
+
# Apply priority filtering
|
|
120
|
+
if "priority" in filters:
|
|
121
|
+
priorities = filters["priority"]
|
|
122
|
+
if priorities:
|
|
123
|
+
filtered_items = [
|
|
124
|
+
item
|
|
125
|
+
for item in filtered_items
|
|
126
|
+
if (
|
|
127
|
+
item.get("priority")
|
|
128
|
+
if isinstance(item, dict)
|
|
129
|
+
else getattr(item, "priority", None)
|
|
130
|
+
)
|
|
131
|
+
in priorities
|
|
132
|
+
]
|
|
133
|
+
|
|
134
|
+
return filtered_items
|
|
135
|
+
|
|
136
|
+
@staticmethod
|
|
137
|
+
def serialize_items(items):
|
|
138
|
+
"""Convert items to dict if needed (for location models)."""
|
|
139
|
+
if items and hasattr(items[0], "model_dump"):
|
|
140
|
+
return [x.model_dump() for x in items]
|
|
141
|
+
else:
|
|
142
|
+
return items
|
|
143
|
+
|
|
144
|
+
@staticmethod
|
|
145
|
+
def create_paginated_response(items, query, extract_filters_func=None):
|
|
146
|
+
"""Create a complete paginated response with all common logic."""
|
|
147
|
+
# Parse pagination parameters using the existing function
|
|
148
|
+
try:
|
|
149
|
+
page = max(1, int(query.get("page", ["1"])[0]))
|
|
150
|
+
except (ValueError, IndexError, TypeError):
|
|
151
|
+
page = 1
|
|
152
|
+
|
|
153
|
+
per_page_raw = query.get("per_page", ["20"])[0] if query.get("per_page") else "20"
|
|
154
|
+
if per_page_raw == "all":
|
|
155
|
+
per_page = None
|
|
156
|
+
else:
|
|
157
|
+
try:
|
|
158
|
+
per_page = max(1, min(int(per_page_raw), 1000)) # Cap at 1000
|
|
159
|
+
except (ValueError, TypeError):
|
|
160
|
+
per_page = 20
|
|
161
|
+
|
|
162
|
+
sort_by = query.get("sort_by", [None])[0]
|
|
163
|
+
sort_order = query.get("sort_order", ["asc"])[0] if query.get("sort_order") else "asc"
|
|
164
|
+
sort_order = sort_order.lower()
|
|
165
|
+
|
|
166
|
+
# Apply filters if any
|
|
167
|
+
filters = {}
|
|
168
|
+
if query:
|
|
169
|
+
if "status" in query:
|
|
170
|
+
filters["status"] = query["status"]
|
|
171
|
+
if "priority" in query:
|
|
172
|
+
filters["priority"] = query["priority"]
|
|
173
|
+
|
|
174
|
+
# Allow custom filter extraction
|
|
175
|
+
if extract_filters_func:
|
|
176
|
+
custom_filters = extract_filters_func(query)
|
|
177
|
+
filters.update(custom_filters)
|
|
178
|
+
|
|
179
|
+
if filters:
|
|
180
|
+
items = PaginationHelper.apply_filters(items, filters)
|
|
181
|
+
|
|
182
|
+
# Apply pagination and sorting
|
|
183
|
+
paginated_items, total_count = PaginationHelper.apply_pagination_and_sorting(
|
|
184
|
+
items, page, per_page, sort_by, sort_order
|
|
185
|
+
)
|
|
186
|
+
|
|
187
|
+
# Serialize items if needed
|
|
188
|
+
serialized_items = PaginationHelper.serialize_items(paginated_items)
|
|
189
|
+
|
|
190
|
+
# Build pagination response directly
|
|
191
|
+
if per_page:
|
|
192
|
+
total_pages = (total_count + per_page - 1) // per_page
|
|
193
|
+
else:
|
|
194
|
+
total_pages = 1
|
|
195
|
+
per_page = total_count
|
|
196
|
+
|
|
197
|
+
return {
|
|
198
|
+
"items": serialized_items,
|
|
199
|
+
"pagination": {
|
|
200
|
+
"total": total_count,
|
|
201
|
+
"page": page,
|
|
202
|
+
"per_page": per_page,
|
|
203
|
+
"total_pages": total_pages,
|
|
204
|
+
},
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
class FileIOHelper:
|
|
209
|
+
"""Common file I/O utilities to eliminate duplication."""
|
|
210
|
+
|
|
211
|
+
@staticmethod
|
|
212
|
+
def read_json_file(file_path):
|
|
213
|
+
"""Read and parse JSON file."""
|
|
214
|
+
with open(file_path, "r") as file:
|
|
215
|
+
return json.load(file)
|
|
216
|
+
|
|
217
|
+
@staticmethod
|
|
218
|
+
def write_json_file_atomic(data, file_path):
|
|
219
|
+
"""Write JSON data to file atomically."""
|
|
220
|
+
json_file_atomic_dump(data, file_path)
|
|
221
|
+
|
|
222
|
+
@staticmethod
|
|
223
|
+
def get_data_from_file(file_path, data_key="map"):
|
|
224
|
+
"""Get data from JSON file with specified key structure."""
|
|
225
|
+
json_data = FileIOHelper.read_json_file(file_path)
|
|
226
|
+
return json_data.get(data_key, {})
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
class ErrorHelper:
|
|
230
|
+
"""Common error handling utilities."""
|
|
231
|
+
|
|
232
|
+
@staticmethod
|
|
233
|
+
def raise_already_exists_error(item_type, uuid):
|
|
234
|
+
"""Raise standardized 'already exists' error."""
|
|
235
|
+
raise ValueError(f"{item_type} with uuid {uuid} already exists")
|
|
236
|
+
|
|
237
|
+
@staticmethod
|
|
238
|
+
def raise_not_found_error(item_type, uuid):
|
|
239
|
+
"""Raise standardized 'not found' error."""
|
|
240
|
+
raise ValueError(f"{item_type} with uuid {uuid} not found")
|
|
241
|
+
|
|
242
|
+
@staticmethod
|
|
243
|
+
def check_item_exists(items, uuid, item_type):
|
|
244
|
+
"""Check if item with UUID exists and raise error if it does."""
|
|
245
|
+
existing = next(
|
|
246
|
+
(
|
|
247
|
+
item
|
|
248
|
+
for item in items
|
|
249
|
+
if (item.get("uuid") if isinstance(item, dict) else getattr(item, "uuid", None))
|
|
250
|
+
== uuid
|
|
251
|
+
),
|
|
252
|
+
None,
|
|
253
|
+
)
|
|
254
|
+
if existing:
|
|
255
|
+
ErrorHelper.raise_already_exists_error(item_type, uuid)
|
|
256
|
+
|
|
257
|
+
@staticmethod
|
|
258
|
+
def find_item_by_uuid(items, uuid, item_type):
|
|
259
|
+
"""Find item by UUID and raise error if not found."""
|
|
260
|
+
item = next(
|
|
261
|
+
(
|
|
262
|
+
item
|
|
263
|
+
for item in items
|
|
264
|
+
if (item.get("uuid") if isinstance(item, dict) else getattr(item, "uuid", None))
|
|
265
|
+
== uuid
|
|
266
|
+
),
|
|
267
|
+
None,
|
|
268
|
+
)
|
|
269
|
+
if not item:
|
|
270
|
+
ErrorHelper.raise_not_found_error(item_type, uuid)
|
|
271
|
+
return item
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+
class CRUDHelper:
|
|
275
|
+
"""Common CRUD operation utilities to eliminate duplication."""
|
|
276
|
+
|
|
277
|
+
@staticmethod
|
|
278
|
+
def add_item_to_json_db(db_data, collection_name, item_data, default_status=None):
|
|
279
|
+
"""Add item to JSON in-memory database."""
|
|
280
|
+
collection = db_data.setdefault(collection_name, [])
|
|
281
|
+
ErrorHelper.check_item_exists(
|
|
282
|
+
collection, item_data.get("uuid"), collection_name.rstrip("s").capitalize()
|
|
283
|
+
)
|
|
284
|
+
|
|
285
|
+
record = dict(item_data)
|
|
286
|
+
if default_status:
|
|
287
|
+
record["status"] = default_status
|
|
288
|
+
collection.append(record)
|
|
289
|
+
|
|
290
|
+
@staticmethod
|
|
291
|
+
def add_item_to_json_file_db(file_path, collection_name, item_data, default_status=None):
|
|
292
|
+
"""Add item to JSON file database."""
|
|
293
|
+
json_file = FileIOHelper.read_json_file(file_path)
|
|
294
|
+
collection = json_file["map"].get(collection_name, [])
|
|
295
|
+
|
|
296
|
+
ErrorHelper.check_item_exists(
|
|
297
|
+
collection, item_data.get("uuid"), collection_name.rstrip("s").capitalize()
|
|
298
|
+
)
|
|
299
|
+
|
|
300
|
+
record = dict(item_data)
|
|
301
|
+
if default_status:
|
|
302
|
+
record["status"] = default_status
|
|
303
|
+
collection.append(record)
|
|
304
|
+
json_file["map"][collection_name] = collection
|
|
305
|
+
|
|
306
|
+
FileIOHelper.write_json_file_atomic(json_file, file_path)
|
|
307
|
+
|
|
308
|
+
@staticmethod
|
|
309
|
+
def add_item_to_mongodb(db_collection, item_data, item_type, default_status=None):
|
|
310
|
+
"""Add item to MongoDB database."""
|
|
311
|
+
existing = db_collection.find_one({"uuid": item_data.get("uuid")})
|
|
312
|
+
if existing:
|
|
313
|
+
ErrorHelper.raise_already_exists_error(item_type, item_data.get("uuid"))
|
|
314
|
+
|
|
315
|
+
record = dict(item_data)
|
|
316
|
+
if default_status:
|
|
317
|
+
record["status"] = default_status
|
|
318
|
+
db_collection.insert_one(record)
|
|
319
|
+
|
|
320
|
+
|
|
21
321
|
# ------------------------------------------------
|
|
22
322
|
# get_location_obligatory_fields
|
|
23
323
|
|
|
@@ -35,6 +335,13 @@ def google_json_db_get_location_obligatory_fields(db):
|
|
|
35
335
|
return json.loads(db.blob.download_as_text(client=None))["map"]["location_obligatory_fields"]
|
|
36
336
|
|
|
37
337
|
|
|
338
|
+
def mongodb_db_get_location_obligatory_fields(db):
|
|
339
|
+
config_doc = db.db.config.find_one({"_id": "map_config"})
|
|
340
|
+
if config_doc and "location_obligatory_fields" in config_doc:
|
|
341
|
+
return config_doc["location_obligatory_fields"]
|
|
342
|
+
return []
|
|
343
|
+
|
|
344
|
+
|
|
38
345
|
def get_location_obligatory_fields(db):
|
|
39
346
|
return globals()[f"{db.module_name}_get_location_obligatory_fields"](db)
|
|
40
347
|
|
|
@@ -56,10 +363,140 @@ def json_db_get_data(self):
|
|
|
56
363
|
return self.data
|
|
57
364
|
|
|
58
365
|
|
|
366
|
+
def mongodb_db_get_data(self):
|
|
367
|
+
config_doc = self.db.config.find_one({"_id": "map_config"})
|
|
368
|
+
if config_doc:
|
|
369
|
+
return {
|
|
370
|
+
"data": list(self.db.locations.find({}, {"_id": 0})),
|
|
371
|
+
"categories": config_doc.get("categories", {}),
|
|
372
|
+
"location_obligatory_fields": config_doc.get("location_obligatory_fields", []),
|
|
373
|
+
# Backward-compat keys expected by core_api today
|
|
374
|
+
"visible_data": config_doc.get("visible_data", {}),
|
|
375
|
+
"meta_data": config_doc.get("meta_data", {}),
|
|
376
|
+
}
|
|
377
|
+
return {
|
|
378
|
+
"data": [],
|
|
379
|
+
"categories": {},
|
|
380
|
+
"location_obligatory_fields": [],
|
|
381
|
+
"visible_data": {},
|
|
382
|
+
"meta_data": {},
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
|
|
59
386
|
def get_data(db):
|
|
60
387
|
return globals()[f"{db.module_name}_get_data"]
|
|
61
388
|
|
|
62
389
|
|
|
390
|
+
# ------------------------------------------------
|
|
391
|
+
# get_categories
|
|
392
|
+
|
|
393
|
+
|
|
394
|
+
def json_db_get_categories(self):
|
|
395
|
+
return self.data["categories"].keys()
|
|
396
|
+
|
|
397
|
+
|
|
398
|
+
def json_file_db_get_categories(self):
|
|
399
|
+
with open(self.data_file_path, "r") as file:
|
|
400
|
+
return json.load(file)["map"]["categories"].keys()
|
|
401
|
+
|
|
402
|
+
|
|
403
|
+
def google_json_db_get_categories(self):
|
|
404
|
+
return json.loads(self.blob.download_as_text(client=None))["map"]["categories"].keys()
|
|
405
|
+
|
|
406
|
+
|
|
407
|
+
def mongodb_db_get_categories(self):
|
|
408
|
+
config_doc = self.db.config.find_one({"_id": "map_config"})
|
|
409
|
+
if config_doc and "categories" in config_doc:
|
|
410
|
+
return list(config_doc["categories"].keys())
|
|
411
|
+
return []
|
|
412
|
+
|
|
413
|
+
|
|
414
|
+
def get_categories(db):
|
|
415
|
+
return globals()[f"{db.module_name}_get_categories"]
|
|
416
|
+
|
|
417
|
+
|
|
418
|
+
# ------------------------------------------------
|
|
419
|
+
# get_category_data
|
|
420
|
+
|
|
421
|
+
|
|
422
|
+
def json_db_get_category_data(self, category_type=None):
|
|
423
|
+
if category_type:
|
|
424
|
+
return {
|
|
425
|
+
"categories": {category_type: self.data["categories"].get(category_type, [])},
|
|
426
|
+
"categories_help": self.data.get("categories_help", []),
|
|
427
|
+
"categories_options_help": {
|
|
428
|
+
category_type: self.data.get("categories_options_help", {}).get(category_type, [])
|
|
429
|
+
},
|
|
430
|
+
}
|
|
431
|
+
return {
|
|
432
|
+
"categories": self.data["categories"],
|
|
433
|
+
"categories_help": self.data.get("categories_help", []),
|
|
434
|
+
"categories_options_help": self.data.get("categories_options_help", {}),
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
|
|
438
|
+
def json_file_db_get_category_data(self, category_type=None):
|
|
439
|
+
with open(self.data_file_path, "r") as file:
|
|
440
|
+
data = json.load(file)["map"]
|
|
441
|
+
if category_type:
|
|
442
|
+
return {
|
|
443
|
+
"categories": {category_type: data["categories"].get(category_type, [])},
|
|
444
|
+
"categories_help": data.get("categories_help", []),
|
|
445
|
+
"categories_options_help": {
|
|
446
|
+
category_type: data.get("categories_options_help", {}).get(category_type, [])
|
|
447
|
+
},
|
|
448
|
+
}
|
|
449
|
+
return {
|
|
450
|
+
"categories": data["categories"],
|
|
451
|
+
"categories_help": data.get("categories_help", []),
|
|
452
|
+
"categories_options_help": data.get("categories_options_help", {}),
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
|
|
456
|
+
def google_json_db_get_category_data(self, category_type=None):
|
|
457
|
+
data = json.loads(self.blob.download_as_text(client=None))["map"]
|
|
458
|
+
if category_type:
|
|
459
|
+
return {
|
|
460
|
+
"categories": {category_type: data["categories"].get(category_type, [])},
|
|
461
|
+
"categories_help": data.get("categories_help", []),
|
|
462
|
+
"categories_options_help": {
|
|
463
|
+
category_type: data.get("categories_options_help", {}).get(category_type, [])
|
|
464
|
+
},
|
|
465
|
+
}
|
|
466
|
+
return {
|
|
467
|
+
"categories": data["categories"],
|
|
468
|
+
"categories_help": data.get("categories_help", []),
|
|
469
|
+
"categories_options_help": data.get("categories_options_help", {}),
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
|
|
473
|
+
def mongodb_db_get_category_data(self, category_type=None):
|
|
474
|
+
config_doc = self.db.config.find_one({"_id": "map_config"})
|
|
475
|
+
if config_doc:
|
|
476
|
+
if category_type:
|
|
477
|
+
return {
|
|
478
|
+
"categories": {
|
|
479
|
+
category_type: config_doc.get("categories", {}).get(category_type, [])
|
|
480
|
+
},
|
|
481
|
+
"categories_help": config_doc.get("categories_help", []),
|
|
482
|
+
"categories_options_help": {
|
|
483
|
+
category_type: config_doc.get("categories_options_help", {}).get(
|
|
484
|
+
category_type, []
|
|
485
|
+
)
|
|
486
|
+
},
|
|
487
|
+
}
|
|
488
|
+
return {
|
|
489
|
+
"categories": config_doc.get("categories", {}),
|
|
490
|
+
"categories_help": config_doc.get("categories_help", []),
|
|
491
|
+
"categories_options_help": config_doc.get("categories_options_help", {}),
|
|
492
|
+
}
|
|
493
|
+
return {"categories": {}, "categories_help": [], "categories_options_help": {}}
|
|
494
|
+
|
|
495
|
+
|
|
496
|
+
def get_category_data(db):
|
|
497
|
+
return globals()[f"{db.module_name}_get_category_data"]
|
|
498
|
+
|
|
499
|
+
|
|
63
500
|
# ------------------------------------------------
|
|
64
501
|
# get_location
|
|
65
502
|
|
|
@@ -85,6 +522,11 @@ def json_db_get_location(self, uuid, location_model):
|
|
|
85
522
|
return get_location_from_raw_data(self.data, uuid, location_model)
|
|
86
523
|
|
|
87
524
|
|
|
525
|
+
def mongodb_db_get_location(self, uuid, location_model):
|
|
526
|
+
location_doc = self.db.locations.find_one({"uuid": uuid}, {"_id": 0})
|
|
527
|
+
return location_model.model_validate(location_doc) if location_doc else None
|
|
528
|
+
|
|
529
|
+
|
|
88
530
|
def get_location(db, location_model):
|
|
89
531
|
return partial(globals()[f"{db.module_name}_get_location"], location_model=location_model)
|
|
90
532
|
|
|
@@ -113,10 +555,91 @@ def json_db_get_locations(self, query, location_model):
|
|
|
113
555
|
return get_locations_list_from_raw_data(self.data, query, location_model)
|
|
114
556
|
|
|
115
557
|
|
|
558
|
+
def mongodb_db_get_locations(self, query, location_model):
|
|
559
|
+
mongo_query = {}
|
|
560
|
+
for key, values in query.items():
|
|
561
|
+
if values:
|
|
562
|
+
mongo_query[key] = {"$in": values}
|
|
563
|
+
|
|
564
|
+
projection = {"_id": 0, "uuid": 1, "position": 1, "remark": 1}
|
|
565
|
+
data = self.db.locations.find(mongo_query, projection)
|
|
566
|
+
return (LocationBase.model_validate(loc) for loc in data)
|
|
567
|
+
|
|
568
|
+
|
|
116
569
|
def get_locations(db, location_model):
|
|
117
570
|
return partial(globals()[f"{db.module_name}_get_locations"], location_model=location_model)
|
|
118
571
|
|
|
119
572
|
|
|
573
|
+
def google_json_db_get_locations_paginated(self, query, location_model):
|
|
574
|
+
"""Google JSON locations with improved pagination."""
|
|
575
|
+
# Get all locations from raw data
|
|
576
|
+
data = json.loads(self.blob.download_as_text(client=None))["map"]
|
|
577
|
+
all_locations = list(get_locations_list_from_raw_data(data, query, location_model))
|
|
578
|
+
return PaginationHelper.create_paginated_response(all_locations, query)
|
|
579
|
+
|
|
580
|
+
|
|
581
|
+
def json_db_get_locations_paginated(self, query, location_model):
|
|
582
|
+
"""JSON locations with improved pagination."""
|
|
583
|
+
# Get all locations from raw data
|
|
584
|
+
all_locations = list(get_locations_list_from_raw_data(self.data, query, location_model))
|
|
585
|
+
return PaginationHelper.create_paginated_response(all_locations, query)
|
|
586
|
+
|
|
587
|
+
|
|
588
|
+
def json_file_db_get_locations_paginated(self, query, location_model):
|
|
589
|
+
"""JSON file locations with improved pagination."""
|
|
590
|
+
data = FileIOHelper.get_data_from_file(self.data_file_path)
|
|
591
|
+
# Get all locations from raw data
|
|
592
|
+
all_locations = list(get_locations_list_from_raw_data(data, query, location_model))
|
|
593
|
+
return PaginationHelper.create_paginated_response(all_locations, query)
|
|
594
|
+
|
|
595
|
+
|
|
596
|
+
def mongodb_db_get_locations_paginated(self, query, location_model):
|
|
597
|
+
"""MongoDB locations with improved pagination."""
|
|
598
|
+
page, per_page, sort_by, sort_order = __parse_pagination_params(query)
|
|
599
|
+
|
|
600
|
+
# Build MongoDB query
|
|
601
|
+
mongo_query = {}
|
|
602
|
+
for key, values in query.items():
|
|
603
|
+
if values:
|
|
604
|
+
mongo_query[key] = {"$in": values}
|
|
605
|
+
|
|
606
|
+
# Get total count
|
|
607
|
+
total_count = self.db.locations.count_documents(mongo_query)
|
|
608
|
+
|
|
609
|
+
# Build aggregation pipeline
|
|
610
|
+
pipeline = [{"$match": mongo_query}]
|
|
611
|
+
|
|
612
|
+
# Add sorting
|
|
613
|
+
if sort_by:
|
|
614
|
+
sort_direction = -1 if sort_order == "desc" else 1
|
|
615
|
+
pipeline.append({"$sort": {sort_by: sort_direction}})
|
|
616
|
+
|
|
617
|
+
# Add pagination
|
|
618
|
+
if per_page:
|
|
619
|
+
pipeline.extend([{"$skip": (page - 1) * per_page}, {"$limit": per_page}]) # type: ignore
|
|
620
|
+
|
|
621
|
+
# Remove MongoDB _id field
|
|
622
|
+
pipeline.append({"$project": {"_id": 0}})
|
|
623
|
+
|
|
624
|
+
# Execute query
|
|
625
|
+
cursor = self.db.locations.aggregate(pipeline)
|
|
626
|
+
locations = [location_model.model_validate(loc) for loc in cursor]
|
|
627
|
+
|
|
628
|
+
# Convert items to dict if needed (for location models)
|
|
629
|
+
if locations and hasattr(locations[0], "model_dump"):
|
|
630
|
+
serialized_locations = [x.model_dump() for x in locations]
|
|
631
|
+
else:
|
|
632
|
+
serialized_locations = locations
|
|
633
|
+
|
|
634
|
+
return __build_pagination_response(serialized_locations, total_count, page, per_page)
|
|
635
|
+
|
|
636
|
+
|
|
637
|
+
def get_locations_paginated(db, location_model):
|
|
638
|
+
return partial(
|
|
639
|
+
globals()[f"{db.module_name}_get_locations_paginated"], location_model=location_model
|
|
640
|
+
)
|
|
641
|
+
|
|
642
|
+
|
|
120
643
|
# ------------------------------------------------
|
|
121
644
|
# add_location
|
|
122
645
|
|
|
@@ -154,6 +677,14 @@ def json_db_add_location(self, location_data, location_model):
|
|
|
154
677
|
self.data["data"].append(location.model_dump())
|
|
155
678
|
|
|
156
679
|
|
|
680
|
+
def mongodb_db_add_location(self, location_data, location_model):
|
|
681
|
+
location = location_model.model_validate(location_data)
|
|
682
|
+
existing = self.db.locations.find_one({"uuid": location_data["uuid"]})
|
|
683
|
+
if existing:
|
|
684
|
+
raise ValueError(f"Location with uuid {location_data['uuid']} already exists")
|
|
685
|
+
self.db.locations.insert_one(location.model_dump())
|
|
686
|
+
|
|
687
|
+
|
|
157
688
|
def add_location(db, location_data, location_model):
|
|
158
689
|
return globals()[f"{db.module_name}_add_location"](db, location_data, location_model)
|
|
159
690
|
|
|
@@ -188,6 +719,13 @@ def json_db_update_location(self, uuid, location_data, location_model):
|
|
|
188
719
|
self.data["data"][idx] = location.model_dump()
|
|
189
720
|
|
|
190
721
|
|
|
722
|
+
def mongodb_db_update_location(self, uuid, location_data, location_model):
|
|
723
|
+
location = location_model.model_validate(location_data)
|
|
724
|
+
result = self.db.locations.update_one({"uuid": uuid}, {"$set": location.model_dump()})
|
|
725
|
+
if result.matched_count == 0:
|
|
726
|
+
raise ValueError(f"Location with uuid {uuid} not found")
|
|
727
|
+
|
|
728
|
+
|
|
191
729
|
def update_location(db, uuid, location_data, location_model):
|
|
192
730
|
return globals()[f"{db.module_name}_update_location"](db, uuid, location_data, location_model)
|
|
193
731
|
|
|
@@ -220,6 +758,12 @@ def json_db_delete_location(self, uuid):
|
|
|
220
758
|
del self.data["data"][idx]
|
|
221
759
|
|
|
222
760
|
|
|
761
|
+
def mongodb_db_delete_location(self, uuid):
|
|
762
|
+
result = self.db.locations.delete_one({"uuid": uuid})
|
|
763
|
+
if result.deleted_count == 0:
|
|
764
|
+
raise ValueError(f"Location with uuid {uuid} not found")
|
|
765
|
+
|
|
766
|
+
|
|
223
767
|
def delete_location(db, uuid):
|
|
224
768
|
return globals()[f"{db.module_name}_delete_location"](db, uuid)
|
|
225
769
|
|
|
@@ -229,29 +773,17 @@ def delete_location(db, uuid):
|
|
|
229
773
|
|
|
230
774
|
|
|
231
775
|
def json_db_add_suggestion(self, suggestion_data):
|
|
232
|
-
|
|
233
|
-
if any(s.get("uuid") == suggestion_data.get("uuid") for s in suggestions):
|
|
234
|
-
raise ValueError(f"Suggestion with uuid {suggestion_data['uuid']} already exists")
|
|
235
|
-
|
|
236
|
-
record = dict(suggestion_data)
|
|
237
|
-
record["status"] = "pending"
|
|
238
|
-
suggestions.append(record)
|
|
776
|
+
CRUDHelper.add_item_to_json_db(self.data, "suggestions", suggestion_data, "pending")
|
|
239
777
|
|
|
240
778
|
|
|
241
779
|
def json_file_db_add_suggestion(self, suggestion_data):
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
suggestions = json_file["map"].get("suggestions", [])
|
|
246
|
-
if any(s.get("uuid") == suggestion_data.get("uuid") for s in suggestions):
|
|
247
|
-
raise ValueError(f"Suggestion with uuid {suggestion_data['uuid']} already exists")
|
|
780
|
+
CRUDHelper.add_item_to_json_file_db(
|
|
781
|
+
self.data_file_path, "suggestions", suggestion_data, "pending"
|
|
782
|
+
)
|
|
248
783
|
|
|
249
|
-
record = dict(suggestion_data)
|
|
250
|
-
record["status"] = "pending"
|
|
251
|
-
suggestions.append(record)
|
|
252
|
-
json_file["map"]["suggestions"] = suggestions
|
|
253
784
|
|
|
254
|
-
|
|
785
|
+
def mongodb_db_add_suggestion(self, suggestion_data):
|
|
786
|
+
CRUDHelper.add_item_to_mongodb(self.db.suggestions, suggestion_data, "Suggestion", "pending")
|
|
255
787
|
|
|
256
788
|
|
|
257
789
|
def add_suggestion(db, suggestion_data):
|
|
@@ -272,6 +804,12 @@ def json_db_get_suggestions(self, query_params):
|
|
|
272
804
|
return suggestions
|
|
273
805
|
|
|
274
806
|
|
|
807
|
+
def json_db_get_suggestions_paginated(self, query):
|
|
808
|
+
"""JSON suggestions with improved pagination."""
|
|
809
|
+
suggestions = self.data.get("suggestions", [])
|
|
810
|
+
return PaginationHelper.create_paginated_response(suggestions, query)
|
|
811
|
+
|
|
812
|
+
|
|
275
813
|
def json_file_db_get_suggestions(self, query_params):
|
|
276
814
|
with open(self.data_file_path, "r") as file:
|
|
277
815
|
json_file = json.load(file)
|
|
@@ -285,10 +823,67 @@ def json_file_db_get_suggestions(self, query_params):
|
|
|
285
823
|
return suggestions
|
|
286
824
|
|
|
287
825
|
|
|
826
|
+
def json_file_db_get_suggestions_paginated(self, query):
|
|
827
|
+
"""JSON file suggestions with improved pagination."""
|
|
828
|
+
with open(self.data_file_path, "r") as file:
|
|
829
|
+
json_file = json.load(file)
|
|
830
|
+
|
|
831
|
+
suggestions = json_file["map"].get("suggestions", [])
|
|
832
|
+
return PaginationHelper.create_paginated_response(suggestions, query)
|
|
833
|
+
|
|
834
|
+
|
|
835
|
+
def mongodb_db_get_suggestions(self, query_params):
|
|
836
|
+
query = {}
|
|
837
|
+
statuses = query_params.get("status")
|
|
838
|
+
if statuses:
|
|
839
|
+
query["status"] = {"$in": statuses}
|
|
840
|
+
|
|
841
|
+
return list(self.db.suggestions.find(query, {"_id": 0}))
|
|
842
|
+
|
|
843
|
+
|
|
844
|
+
def mongodb_db_get_suggestions_paginated(self, query):
|
|
845
|
+
"""MongoDB suggestions with improved pagination."""
|
|
846
|
+
page, per_page, sort_by, sort_order = __parse_pagination_params(query)
|
|
847
|
+
|
|
848
|
+
# Build MongoDB query
|
|
849
|
+
mongo_query = {}
|
|
850
|
+
statuses = query.get("status")
|
|
851
|
+
if statuses:
|
|
852
|
+
mongo_query["status"] = {"$in": statuses}
|
|
853
|
+
|
|
854
|
+
# Get total count
|
|
855
|
+
total_count = self.db.suggestions.count_documents(mongo_query)
|
|
856
|
+
|
|
857
|
+
# Build aggregation pipeline
|
|
858
|
+
pipeline = [{"$match": mongo_query}]
|
|
859
|
+
|
|
860
|
+
# Add sorting
|
|
861
|
+
if sort_by:
|
|
862
|
+
sort_direction = -1 if sort_order == "desc" else 1
|
|
863
|
+
pipeline.append({"$sort": {sort_by: sort_direction}})
|
|
864
|
+
|
|
865
|
+
# Add pagination
|
|
866
|
+
if per_page:
|
|
867
|
+
pipeline.extend([{"$skip": (page - 1) * per_page}, {"$limit": per_page}]) # type: ignore
|
|
868
|
+
|
|
869
|
+
# Remove MongoDB _id field
|
|
870
|
+
pipeline.append({"$project": {"_id": 0}})
|
|
871
|
+
|
|
872
|
+
# Execute query
|
|
873
|
+
cursor = self.db.suggestions.aggregate(pipeline)
|
|
874
|
+
items = list(cursor)
|
|
875
|
+
|
|
876
|
+
return __build_pagination_response(items, total_count, page, per_page)
|
|
877
|
+
|
|
878
|
+
|
|
288
879
|
def get_suggestions(db):
|
|
289
880
|
return globals()[f"{db.module_name}_get_suggestions"]
|
|
290
881
|
|
|
291
882
|
|
|
883
|
+
def get_suggestions_paginated(db):
|
|
884
|
+
return globals()[f"{db.module_name}_get_suggestions_paginated"]
|
|
885
|
+
|
|
886
|
+
|
|
292
887
|
# ------------------------------------------------
|
|
293
888
|
# get_suggestion
|
|
294
889
|
|
|
@@ -307,6 +902,10 @@ def json_file_db_get_suggestion(self, suggestion_id):
|
|
|
307
902
|
)
|
|
308
903
|
|
|
309
904
|
|
|
905
|
+
def mongodb_db_get_suggestion(self, suggestion_id):
|
|
906
|
+
return self.db.suggestions.find_one({"uuid": suggestion_id}, {"_id": 0})
|
|
907
|
+
|
|
908
|
+
|
|
310
909
|
def get_suggestion(db):
|
|
311
910
|
return globals()[f"{db.module_name}_get_suggestion"]
|
|
312
911
|
|
|
@@ -341,6 +940,12 @@ def json_file_db_update_suggestion(self, suggestion_id, status):
|
|
|
341
940
|
json_file_atomic_dump(json_file, self.data_file_path)
|
|
342
941
|
|
|
343
942
|
|
|
943
|
+
def mongodb_db_update_suggestion(self, suggestion_id, status):
|
|
944
|
+
result = self.db.suggestions.update_one({"uuid": suggestion_id}, {"$set": {"status": status}})
|
|
945
|
+
if result.matched_count == 0:
|
|
946
|
+
raise ValueError(f"Suggestion with uuid {suggestion_id} not found")
|
|
947
|
+
|
|
948
|
+
|
|
344
949
|
def update_suggestion(db, suggestion_id, status):
|
|
345
950
|
return globals()[f"{db.module_name}_update_suggestion"](db, suggestion_id, status)
|
|
346
951
|
|
|
@@ -373,6 +978,12 @@ def json_file_db_delete_suggestion(self, suggestion_id):
|
|
|
373
978
|
json_file_atomic_dump(json_file, self.data_file_path)
|
|
374
979
|
|
|
375
980
|
|
|
981
|
+
def mongodb_db_delete_suggestion(self, suggestion_id):
|
|
982
|
+
result = self.db.suggestions.delete_one({"uuid": suggestion_id})
|
|
983
|
+
if result.deleted_count == 0:
|
|
984
|
+
raise ValueError(f"Suggestion with uuid {suggestion_id} not found")
|
|
985
|
+
|
|
986
|
+
|
|
376
987
|
def delete_suggestion(db, suggestion_id):
|
|
377
988
|
return globals()[f"{db.module_name}_delete_suggestion"](db, suggestion_id)
|
|
378
989
|
|
|
@@ -403,6 +1014,14 @@ def json_file_db_add_report(self, report_data):
|
|
|
403
1014
|
json_file_atomic_dump(json_file, self.data_file_path)
|
|
404
1015
|
|
|
405
1016
|
|
|
1017
|
+
def mongodb_db_add_report(self, report_data):
|
|
1018
|
+
existing = self.db.reports.find_one({"uuid": report_data.get("uuid")})
|
|
1019
|
+
if existing:
|
|
1020
|
+
raise ValueError(f"Report with uuid {report_data['uuid']} already exists")
|
|
1021
|
+
|
|
1022
|
+
self.db.reports.insert_one(report_data)
|
|
1023
|
+
|
|
1024
|
+
|
|
406
1025
|
def add_report(db, report_data):
|
|
407
1026
|
return globals()[f"{db.module_name}_add_report"](db, report_data)
|
|
408
1027
|
|
|
@@ -425,6 +1044,12 @@ def json_db_get_reports(self, query_params):
|
|
|
425
1044
|
return reports
|
|
426
1045
|
|
|
427
1046
|
|
|
1047
|
+
def json_db_get_reports_paginated(self, query):
|
|
1048
|
+
"""JSON reports with improved pagination."""
|
|
1049
|
+
reports = self.data.get("reports", [])
|
|
1050
|
+
return PaginationHelper.create_paginated_response(reports, query)
|
|
1051
|
+
|
|
1052
|
+
|
|
428
1053
|
def json_file_db_get_reports(self, query_params):
|
|
429
1054
|
with open(self.data_file_path, "r") as file:
|
|
430
1055
|
json_file = json.load(file)
|
|
@@ -442,10 +1067,75 @@ def json_file_db_get_reports(self, query_params):
|
|
|
442
1067
|
return reports
|
|
443
1068
|
|
|
444
1069
|
|
|
1070
|
+
def json_file_db_get_reports_paginated(self, query):
|
|
1071
|
+
"""JSON file reports with improved pagination."""
|
|
1072
|
+
data = FileIOHelper.get_data_from_file(self.data_file_path)
|
|
1073
|
+
reports = data.get("reports", [])
|
|
1074
|
+
return PaginationHelper.create_paginated_response(reports, query)
|
|
1075
|
+
|
|
1076
|
+
|
|
1077
|
+
def mongodb_db_get_reports(self, query_params):
|
|
1078
|
+
query = {}
|
|
1079
|
+
|
|
1080
|
+
statuses = query_params.get("status")
|
|
1081
|
+
if statuses:
|
|
1082
|
+
query["status"] = {"$in": statuses}
|
|
1083
|
+
|
|
1084
|
+
priorities = query_params.get("priority")
|
|
1085
|
+
if priorities:
|
|
1086
|
+
query["priority"] = {"$in": priorities}
|
|
1087
|
+
|
|
1088
|
+
return list(self.db.reports.find(query, {"_id": 0}))
|
|
1089
|
+
|
|
1090
|
+
|
|
1091
|
+
def mongodb_db_get_reports_paginated(self, query):
|
|
1092
|
+
"""MongoDB reports with improved pagination."""
|
|
1093
|
+
page, per_page, sort_by, sort_order = __parse_pagination_params(query)
|
|
1094
|
+
|
|
1095
|
+
# Build MongoDB query
|
|
1096
|
+
mongo_query = {}
|
|
1097
|
+
|
|
1098
|
+
statuses = query.get("status")
|
|
1099
|
+
if statuses:
|
|
1100
|
+
mongo_query["status"] = {"$in": statuses}
|
|
1101
|
+
|
|
1102
|
+
priorities = query.get("priority")
|
|
1103
|
+
if priorities:
|
|
1104
|
+
mongo_query["priority"] = {"$in": priorities}
|
|
1105
|
+
|
|
1106
|
+
# Get total count
|
|
1107
|
+
total_count = self.db.reports.count_documents(mongo_query)
|
|
1108
|
+
|
|
1109
|
+
# Build aggregation pipeline
|
|
1110
|
+
pipeline = [{"$match": mongo_query}]
|
|
1111
|
+
|
|
1112
|
+
# Add sorting
|
|
1113
|
+
if sort_by:
|
|
1114
|
+
sort_direction = -1 if sort_order == "desc" else 1
|
|
1115
|
+
pipeline.append({"$sort": {sort_by: sort_direction}})
|
|
1116
|
+
|
|
1117
|
+
# Add pagination
|
|
1118
|
+
if per_page:
|
|
1119
|
+
pipeline.extend([{"$skip": (page - 1) * per_page}, {"$limit": per_page}]) # type: ignore
|
|
1120
|
+
|
|
1121
|
+
# Remove MongoDB _id field
|
|
1122
|
+
pipeline.append({"$project": {"_id": 0}})
|
|
1123
|
+
|
|
1124
|
+
# Execute query
|
|
1125
|
+
cursor = self.db.reports.aggregate(pipeline)
|
|
1126
|
+
items = list(cursor)
|
|
1127
|
+
|
|
1128
|
+
return __build_pagination_response(items, total_count, page, per_page)
|
|
1129
|
+
|
|
1130
|
+
|
|
445
1131
|
def get_reports(db):
|
|
446
1132
|
return globals()[f"{db.module_name}_get_reports"]
|
|
447
1133
|
|
|
448
1134
|
|
|
1135
|
+
def get_reports_paginated(db):
|
|
1136
|
+
return globals()[f"{db.module_name}_get_reports_paginated"]
|
|
1137
|
+
|
|
1138
|
+
|
|
449
1139
|
# ------------------------------------------------
|
|
450
1140
|
# get_report
|
|
451
1141
|
|
|
@@ -463,6 +1153,10 @@ def json_file_db_get_report(self, report_id):
|
|
|
463
1153
|
)
|
|
464
1154
|
|
|
465
1155
|
|
|
1156
|
+
def mongodb_db_get_report(self, report_id):
|
|
1157
|
+
return self.db.reports.find_one({"uuid": report_id}, {"_id": 0})
|
|
1158
|
+
|
|
1159
|
+
|
|
466
1160
|
def get_report(db):
|
|
467
1161
|
return globals()[f"{db.module_name}_get_report"]
|
|
468
1162
|
|
|
@@ -503,6 +1197,19 @@ def json_file_db_update_report(self, report_id, status=None, priority=None):
|
|
|
503
1197
|
json_file_atomic_dump(json_file, self.data_file_path)
|
|
504
1198
|
|
|
505
1199
|
|
|
1200
|
+
def mongodb_db_update_report(self, report_id, status=None, priority=None):
|
|
1201
|
+
update_doc = {}
|
|
1202
|
+
if status:
|
|
1203
|
+
update_doc["status"] = status
|
|
1204
|
+
if priority:
|
|
1205
|
+
update_doc["priority"] = priority
|
|
1206
|
+
|
|
1207
|
+
if update_doc:
|
|
1208
|
+
result = self.db.reports.update_one({"uuid": report_id}, {"$set": update_doc})
|
|
1209
|
+
if result.matched_count == 0:
|
|
1210
|
+
raise ValueError(f"Report with uuid {report_id} not found")
|
|
1211
|
+
|
|
1212
|
+
|
|
506
1213
|
def update_report(db, report_id, status=None, priority=None):
|
|
507
1214
|
return globals()[f"{db.module_name}_update_report"](db, report_id, status, priority)
|
|
508
1215
|
|
|
@@ -534,6 +1241,12 @@ def json_file_db_delete_report(self, report_id):
|
|
|
534
1241
|
json_file_atomic_dump(json_file, self.data_file_path)
|
|
535
1242
|
|
|
536
1243
|
|
|
1244
|
+
def mongodb_db_delete_report(self, report_id):
|
|
1245
|
+
result = self.db.reports.delete_one({"uuid": report_id})
|
|
1246
|
+
if result.deleted_count == 0:
|
|
1247
|
+
raise ValueError(f"Report with uuid {report_id} not found")
|
|
1248
|
+
|
|
1249
|
+
|
|
537
1250
|
def delete_report(db, report_id):
|
|
538
1251
|
return globals()[f"{db.module_name}_delete_report"](db, report_id)
|
|
539
1252
|
|
|
@@ -546,18 +1259,23 @@ def delete_report(db, report_id):
|
|
|
546
1259
|
def extend_db_with_goodmap_queries(db, location_model):
|
|
547
1260
|
db.extend("get_data", get_data(db))
|
|
548
1261
|
db.extend("get_locations", get_locations(db, location_model))
|
|
1262
|
+
db.extend("get_locations_paginated", get_locations_paginated(db, location_model))
|
|
549
1263
|
db.extend("get_location", get_location(db, location_model))
|
|
550
1264
|
db.extend("add_location", partial(add_location, location_model=location_model))
|
|
551
1265
|
db.extend("update_location", partial(update_location, location_model=location_model))
|
|
552
1266
|
db.extend("delete_location", delete_location)
|
|
553
|
-
|
|
1267
|
+
db.extend("get_categories", get_categories(db))
|
|
1268
|
+
db.extend("get_category_data", get_category_data(db))
|
|
1269
|
+
if db.module_name in ("json_db", "json_file_db", "mongodb_db"):
|
|
554
1270
|
db.extend("add_suggestion", add_suggestion)
|
|
555
1271
|
db.extend("get_suggestions", get_suggestions(db))
|
|
1272
|
+
db.extend("get_suggestions_paginated", get_suggestions_paginated(db))
|
|
556
1273
|
db.extend("get_suggestion", get_suggestion(db))
|
|
557
1274
|
db.extend("update_suggestion", update_suggestion)
|
|
558
1275
|
db.extend("delete_suggestion", delete_suggestion)
|
|
559
1276
|
db.extend("add_report", add_report)
|
|
560
1277
|
db.extend("get_reports", get_reports(db))
|
|
1278
|
+
db.extend("get_reports_paginated", get_reports_paginated(db))
|
|
561
1279
|
db.extend("get_report", get_report(db))
|
|
562
1280
|
db.extend("update_report", update_report)
|
|
563
1281
|
db.extend("delete_report", delete_report)
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: goodmap
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 1.0.2
|
|
4
4
|
Summary: Map engine to serve all the people :)
|
|
5
|
+
License-File: LICENSE.md
|
|
5
6
|
Author: Krzysztof Kolodzinski
|
|
6
7
|
Author-email: krzysztof.kolodzinski@problematy.pl
|
|
7
8
|
Requires-Python: >=3.10,<4.0
|
|
@@ -10,6 +11,7 @@ Classifier: Programming Language :: Python :: 3.10
|
|
|
10
11
|
Classifier: Programming Language :: Python :: 3.11
|
|
11
12
|
Classifier: Programming Language :: Python :: 3.12
|
|
12
13
|
Classifier: Programming Language :: Python :: 3.13
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
13
15
|
Requires-Dist: Babel (>=2.10.3,<3.0.0)
|
|
14
16
|
Requires-Dist: Flask (==3.0.3)
|
|
15
17
|
Requires-Dist: Flask-Babel (>=4.0.0,<5.0.0)
|
|
@@ -22,11 +24,11 @@ Requires-Dist: google-cloud-storage (>=2.7.0,<3.0.0)
|
|
|
22
24
|
Requires-Dist: gql (>=3.4.0,<4.0.0)
|
|
23
25
|
Requires-Dist: gunicorn (>=20.1.0,<21.0.0)
|
|
24
26
|
Requires-Dist: humanize (>=4.6.0,<5.0.0)
|
|
25
|
-
Requires-Dist: platzky (>=0.
|
|
27
|
+
Requires-Dist: platzky (>=0.4.0,<0.5.0)
|
|
26
28
|
Requires-Dist: pydantic (>=2.7.1,<3.0.0)
|
|
27
29
|
Description-Content-Type: text/markdown
|
|
28
30
|
|
|
29
|
-

|
|
30
32
|
[](https://coveralls.io/github/Problematy/goodmap)
|
|
31
33
|
|
|
32
34
|
# Good Map
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
goodmap/__init__.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
|
|
2
2
|
goodmap/core.py,sha256=rzMhOIYnR1jxTX6uHQJKIPLYxdUm4_v2d6LrtHtJpHU,1465
|
|
3
|
-
goodmap/core_api.py,sha256=
|
|
3
|
+
goodmap/core_api.py,sha256=MxJ7SGOFBBnV_cBIiZoImHLaY8_FtEW-dl3od5-mIt8,13529
|
|
4
4
|
goodmap/data_models/location.py,sha256=H3EKozc-WZvrYm6cwajl8_gaw4rQhxdlvxR1mk4mpkA,1104
|
|
5
5
|
goodmap/data_validator.py,sha256=lBmVAPxvSmEOdUGeVYSjUvVVmKfPyq4CWoHfczTtEMM,4090
|
|
6
|
-
goodmap/db.py,sha256=
|
|
6
|
+
goodmap/db.py,sha256=i-fe_f_s9hQxvTqq4alA-RK9j6vQpnaNpXEDPci3s1Y,42049
|
|
7
7
|
goodmap/formatter.py,sha256=VlUHcK1HtM_IEU0VE3S5TOkZLVheMdakvUeW2tCKdq0,783
|
|
8
8
|
goodmap/goodmap.py,sha256=OOcuDEY7GZsHh8vjIJ9brd4SuGxNRq_lC_DMmNuome8,2731
|
|
9
9
|
goodmap/templates/goodmap-admin.html,sha256=zGuau239BXyBerV21mDXcHy34ke8cLmZcMngcX1xhAs,35598
|
|
10
10
|
goodmap/templates/map.html,sha256=-qbJ313t2EdSyIwULnQy3j81pH4MqI5yjtJ4Xoo7N7M,3979
|
|
11
|
-
goodmap-0.
|
|
12
|
-
goodmap-0.
|
|
13
|
-
goodmap-0.
|
|
14
|
-
goodmap-0.
|
|
11
|
+
goodmap-1.0.2.dist-info/METADATA,sha256=nqqszznBX2xjMT-QiQwWNqUlyqhtjlI4Dsz5yUZtjlg,5356
|
|
12
|
+
goodmap-1.0.2.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
|
|
13
|
+
goodmap-1.0.2.dist-info/licenses/LICENSE.md,sha256=nkCQOR7uheLRvHRfXmwx9LhBnMcPeBU9d4ebLojDiQU,1067
|
|
14
|
+
goodmap-1.0.2.dist-info/RECORD,,
|
|
File without changes
|