goodmap 1.1.13__tar.gz → 1.2.0__tar.gz
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-1.1.13 → goodmap-1.2.0}/PKG-INFO +4 -3
- {goodmap-1.1.13 → goodmap-1.2.0}/README.md +1 -0
- goodmap-1.2.0/goodmap/api_models.py +105 -0
- {goodmap-1.1.13 → goodmap-1.2.0}/goodmap/core.py +39 -0
- goodmap-1.2.0/goodmap/core_api.py +611 -0
- goodmap-1.2.0/goodmap/data_models/location.py +195 -0
- {goodmap-1.1.13 → goodmap-1.2.0}/goodmap/formatter.py +20 -0
- goodmap-1.2.0/goodmap/goodmap.py +184 -0
- goodmap-1.2.0/goodmap/json_security.py +102 -0
- {goodmap-1.1.13 → goodmap-1.2.0}/pyproject.toml +11 -14
- goodmap-1.1.13/goodmap/core_api.py +0 -500
- goodmap-1.1.13/goodmap/data_models/location.py +0 -68
- goodmap-1.1.13/goodmap/goodmap.py +0 -100
- {goodmap-1.1.13 → goodmap-1.2.0}/LICENSE.md +0 -0
- {goodmap-1.1.13 → goodmap-1.2.0}/goodmap/__init__.py +0 -0
- {goodmap-1.1.13 → goodmap-1.2.0}/goodmap/clustering.py +0 -0
- {goodmap-1.1.13 → goodmap-1.2.0}/goodmap/config.py +0 -0
- {goodmap-1.1.13 → goodmap-1.2.0}/goodmap/data_validator.py +0 -0
- {goodmap-1.1.13 → goodmap-1.2.0}/goodmap/db.py +0 -0
- {goodmap-1.1.13 → goodmap-1.2.0}/goodmap/exceptions.py +0 -0
- {goodmap-1.1.13 → goodmap-1.2.0}/goodmap/templates/goodmap-admin.html +0 -0
- {goodmap-1.1.13 → goodmap-1.2.0}/goodmap/templates/map.html +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: goodmap
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.2.0
|
|
4
4
|
Summary: Map engine to serve all the people :)
|
|
5
5
|
Author: Krzysztof Kolodzinski
|
|
6
6
|
Author-email: krzysztof.kolodzinski@problematy.pl
|
|
@@ -18,10 +18,9 @@ Requires-Dist: Flask-WTF (>=1.2.1,<2.0.0)
|
|
|
18
18
|
Requires-Dist: PyYAML (>=6.0,<7.0)
|
|
19
19
|
Requires-Dist: aiohttp (>=3.8.4,<4.0.0)
|
|
20
20
|
Requires-Dist: deprecation (>=2.1.0,<3.0.0)
|
|
21
|
-
Requires-Dist: flask-restx (>=1.3.0,<2.0.0)
|
|
22
21
|
Requires-Dist: google-cloud-storage (>=2.7.0,<3.0.0)
|
|
23
22
|
Requires-Dist: gql (>=3.4.0,<4.0.0)
|
|
24
|
-
Requires-Dist: gunicorn (>=20.1
|
|
23
|
+
Requires-Dist: gunicorn (>=20.1,<24.0)
|
|
25
24
|
Requires-Dist: humanize (>=4.6.0,<5.0.0)
|
|
26
25
|
Requires-Dist: myst-parser (>=4.0.0,<5.0.0) ; extra == "docs"
|
|
27
26
|
Requires-Dist: numpy (>=2.2.0,<3.0.0)
|
|
@@ -29,6 +28,7 @@ Requires-Dist: platzky (>=1.0.0,<2.0.0)
|
|
|
29
28
|
Requires-Dist: pydantic (>=2.7.1,<3.0.0)
|
|
30
29
|
Requires-Dist: pysupercluster-problematy (>=0.7.8,<0.8.0)
|
|
31
30
|
Requires-Dist: scipy (>=1.15.1,<2.0.0)
|
|
31
|
+
Requires-Dist: spectree (>=2.0.1,<3.0.0)
|
|
32
32
|
Requires-Dist: sphinx (>=8.0.0,<9.0.0) ; extra == "docs"
|
|
33
33
|
Requires-Dist: sphinx-rtd-theme (>=3.0.0,<4.0.0) ; extra == "docs"
|
|
34
34
|
Requires-Dist: tomli (>=2.0.0,<3.0.0) ; extra == "docs"
|
|
@@ -137,4 +137,5 @@ You can find examples of working configuration and database in `examples/` direc
|
|
|
137
137
|
- `mongo_e2e_test_config.yml` - MongoDB configuration example
|
|
138
138
|
|
|
139
139
|
|
|
140
|
+
# final test
|
|
140
141
|
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
"""Pydantic models for API request/response validation.
|
|
2
|
+
|
|
3
|
+
This module defines request and response models for the Goodmap REST API.
|
|
4
|
+
These models are used by Spectree for automatic OpenAPI schema generation
|
|
5
|
+
and request/response validation.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from typing import Literal
|
|
9
|
+
|
|
10
|
+
from pydantic import BaseModel, Field
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class LocationReportRequest(BaseModel):
|
|
14
|
+
"""Request model for reporting a location issue."""
|
|
15
|
+
|
|
16
|
+
id: str = Field(..., description="Location UUID to report")
|
|
17
|
+
description: str = Field(..., min_length=1, description="Description of the problem")
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class LocationReportResponse(BaseModel):
|
|
21
|
+
"""Response model for location report submission."""
|
|
22
|
+
|
|
23
|
+
message: str = Field(..., description="Success message")
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class SuggestionStatusRequest(BaseModel):
|
|
27
|
+
"""Request model for updating suggestion status."""
|
|
28
|
+
|
|
29
|
+
status: Literal["accepted", "rejected"] = Field(
|
|
30
|
+
..., description="Status to set for the suggestion"
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class ReportUpdateRequest(BaseModel):
|
|
35
|
+
"""Request model for updating a report's status and priority."""
|
|
36
|
+
|
|
37
|
+
status: Literal["resolved", "rejected"] | None = Field(
|
|
38
|
+
None, description="New status for the report"
|
|
39
|
+
)
|
|
40
|
+
priority: Literal["critical", "high", "medium", "low"] | None = Field(
|
|
41
|
+
None, description="New priority for the report"
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class VersionResponse(BaseModel):
|
|
46
|
+
"""Response model for version endpoint."""
|
|
47
|
+
|
|
48
|
+
backend: str = Field(..., description="Backend version")
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class CSRFTokenResponse(BaseModel):
|
|
52
|
+
"""Response model for CSRF token endpoint (deprecated)."""
|
|
53
|
+
|
|
54
|
+
csrf_token: str = Field(..., description="CSRF token")
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
class PaginationParams(BaseModel):
|
|
58
|
+
"""Common pagination and filtering parameters."""
|
|
59
|
+
|
|
60
|
+
page: int | None = Field(None, ge=1, description="Page number (1-indexed)")
|
|
61
|
+
per_page: int | None = Field(None, ge=1, le=100, description="Items per page")
|
|
62
|
+
sort_by: str | None = Field(None, description="Field to sort by")
|
|
63
|
+
sort_order: Literal["asc", "desc"] | None = Field(None, description="Sort direction")
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
class ClusteringParams(BaseModel):
|
|
67
|
+
"""Parameters for clustering request."""
|
|
68
|
+
|
|
69
|
+
zoom: int = Field(7, ge=0, le=16, description="Map zoom level for clustering")
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
class ErrorResponse(BaseModel):
|
|
73
|
+
"""Standard error response."""
|
|
74
|
+
|
|
75
|
+
message: str = Field(..., description="Error message")
|
|
76
|
+
error: str | None = Field(None, description="Detailed error information")
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
class SuccessResponse(BaseModel):
|
|
80
|
+
"""Standard success response."""
|
|
81
|
+
|
|
82
|
+
message: str = Field(..., description="Success message")
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
class BasicLocationInfo(BaseModel):
|
|
86
|
+
"""Basic location information (uuid + position)."""
|
|
87
|
+
|
|
88
|
+
uuid: str = Field(..., description="Location UUID")
|
|
89
|
+
position: tuple[float, float] = Field(
|
|
90
|
+
..., description="Location coordinates as (latitude, longitude)"
|
|
91
|
+
)
|
|
92
|
+
remark: bool = Field(False, description="Whether location has a remark")
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
class ClusterInfo(BaseModel):
|
|
96
|
+
"""Cluster information for map display."""
|
|
97
|
+
|
|
98
|
+
uuid: str | None = Field(None, description="Location UUID (None for multi-point clusters)")
|
|
99
|
+
position: tuple[float, float] = Field(..., description="Cluster center coordinates")
|
|
100
|
+
count: int = Field(..., description="Number of locations in cluster")
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
# Note: Full location model is dynamically created from LocationBase
|
|
104
|
+
# and cannot be statically defined here. API endpoints will use the
|
|
105
|
+
# dynamically created location_model passed to core_pages() function.
|
|
@@ -1,9 +1,20 @@
|
|
|
1
|
+
"""Core data filtering and sorting utilities for location queries."""
|
|
2
|
+
|
|
1
3
|
from typing import Any, Dict, List
|
|
2
4
|
|
|
3
5
|
# TODO move filtering to db site
|
|
4
6
|
|
|
5
7
|
|
|
6
8
|
def does_fulfill_requirement(entry, requirements):
|
|
9
|
+
"""Check if an entry fulfills all category requirements.
|
|
10
|
+
|
|
11
|
+
Args:
|
|
12
|
+
entry: Location data entry to check
|
|
13
|
+
requirements: List of (category, values) tuples to match
|
|
14
|
+
|
|
15
|
+
Returns:
|
|
16
|
+
bool: True if entry matches all non-empty requirements
|
|
17
|
+
"""
|
|
7
18
|
matches = []
|
|
8
19
|
for category, values in requirements:
|
|
9
20
|
if not values:
|
|
@@ -13,6 +24,15 @@ def does_fulfill_requirement(entry, requirements):
|
|
|
13
24
|
|
|
14
25
|
|
|
15
26
|
def sort_by_distance(data: List[Dict[str, Any]], query_params: Dict[str, List[str]]):
|
|
27
|
+
"""Sort locations by distance from query coordinates.
|
|
28
|
+
|
|
29
|
+
Args:
|
|
30
|
+
data: List of location dictionaries
|
|
31
|
+
query_params: Query parameters containing 'lat' and 'lon'
|
|
32
|
+
|
|
33
|
+
Returns:
|
|
34
|
+
List[Dict[str, Any]]: Sorted data (or original if no coordinates provided)
|
|
35
|
+
"""
|
|
16
36
|
try:
|
|
17
37
|
if "lat" in query_params and "lon" in query_params:
|
|
18
38
|
lat = float(query_params["lat"][0])
|
|
@@ -25,6 +45,15 @@ def sort_by_distance(data: List[Dict[str, Any]], query_params: Dict[str, List[st
|
|
|
25
45
|
|
|
26
46
|
|
|
27
47
|
def limit(data, query_params):
|
|
48
|
+
"""Limit number of results based on query parameter.
|
|
49
|
+
|
|
50
|
+
Args:
|
|
51
|
+
data: List of data to limit
|
|
52
|
+
query_params: Query parameters containing optional 'limit'
|
|
53
|
+
|
|
54
|
+
Returns:
|
|
55
|
+
Limited data (or original if no limit specified)
|
|
56
|
+
"""
|
|
28
57
|
try:
|
|
29
58
|
if "limit" in query_params:
|
|
30
59
|
limit = int(query_params["limit"][0])
|
|
@@ -36,6 +65,16 @@ def limit(data, query_params):
|
|
|
36
65
|
|
|
37
66
|
|
|
38
67
|
def get_queried_data(all_data, categories, query_params):
|
|
68
|
+
"""Filter, sort, and limit location data based on query parameters.
|
|
69
|
+
|
|
70
|
+
Args:
|
|
71
|
+
all_data: Complete list of location data
|
|
72
|
+
categories: Available categories for filtering
|
|
73
|
+
query_params: Query parameters for filtering, sorting, and limiting
|
|
74
|
+
|
|
75
|
+
Returns:
|
|
76
|
+
Filtered, sorted, and limited location data
|
|
77
|
+
"""
|
|
39
78
|
requirements = []
|
|
40
79
|
for key in categories.keys():
|
|
41
80
|
requirements.append((key, query_params.get(key)))
|