nefino-geosync 0.1.0__tar.gz → 0.2.1__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.
Potentially problematic release.
This version of nefino-geosync might be problematic. Click here for more details.
- {nefino_geosync-0.1.0 → nefino_geosync-0.2.1}/.github/workflows/pr-label-check.yml +2 -0
- {nefino_geosync-0.1.0 → nefino_geosync-0.2.1}/.github/workflows/publish.yml +2 -2
- {nefino_geosync-0.1.0 → nefino_geosync-0.2.1}/PKG-INFO +3 -3
- {nefino_geosync-0.1.0 → nefino_geosync-0.2.1}/README.md +1 -1
- {nefino_geosync-0.1.0 → nefino_geosync-0.2.1}/nefino_geosync/compose_requests.py +1 -1
- {nefino_geosync-0.1.0 → nefino_geosync-0.2.1}/nefino_geosync/download_analysis.py +75 -19
- {nefino_geosync-0.1.0 → nefino_geosync-0.2.1}/nefino_geosync/schema.json +3 -50
- {nefino_geosync-0.1.0 → nefino_geosync-0.2.1}/nefino_geosync/schema.py +2 -8
- {nefino_geosync-0.1.0 → nefino_geosync-0.2.1}/pyproject.toml +1 -1
- {nefino_geosync-0.1.0 → nefino_geosync-0.2.1}/.devcontainer/devcontainer.json +0 -0
- {nefino_geosync-0.1.0 → nefino_geosync-0.2.1}/.github/release.yml +0 -0
- {nefino_geosync-0.1.0 → nefino_geosync-0.2.1}/.gitignore +0 -0
- {nefino_geosync-0.1.0 → nefino_geosync-0.2.1}/.vscode/launch.json +0 -0
- {nefino_geosync-0.1.0 → nefino_geosync-0.2.1}/.vscode/settings.json +0 -0
- {nefino_geosync-0.1.0 → nefino_geosync-0.2.1}/LICENSE +0 -0
- {nefino_geosync-0.1.0 → nefino_geosync-0.2.1}/nefino_geosync/__init__.py +0 -0
- {nefino_geosync-0.1.0 → nefino_geosync-0.2.1}/nefino_geosync/access_rule_filter.py +0 -0
- {nefino_geosync-0.1.0 → nefino_geosync-0.2.1}/nefino_geosync/api_client.py +0 -0
- {nefino_geosync-0.1.0 → nefino_geosync-0.2.1}/nefino_geosync/config.py +0 -0
- {nefino_geosync-0.1.0 → nefino_geosync-0.2.1}/nefino_geosync/download_completed_analyses.py +0 -0
- {nefino_geosync-0.1.0 → nefino_geosync-0.2.1}/nefino_geosync/get_downloadable_analyses.py +0 -0
- {nefino_geosync-0.1.0 → nefino_geosync-0.2.1}/nefino_geosync/graphql_errors.py +0 -0
- {nefino_geosync-0.1.0 → nefino_geosync-0.2.1}/nefino_geosync/journal.py +0 -0
- {nefino_geosync-0.1.0 → nefino_geosync-0.2.1}/nefino_geosync/parse_args.py +0 -0
- {nefino_geosync-0.1.0 → nefino_geosync-0.2.1}/nefino_geosync/run.py +0 -0
- {nefino_geosync-0.1.0 → nefino_geosync-0.2.1}/nefino_geosync/start_analyses.py +0 -0
- {nefino_geosync-0.1.0 → nefino_geosync-0.2.1}/nefino_geosync/storage.py +0 -0
|
@@ -49,7 +49,7 @@ jobs:
|
|
|
49
49
|
name: python-package-distributions
|
|
50
50
|
path: dist/
|
|
51
51
|
- name: Publish distribution 📦 to PyPI
|
|
52
|
-
uses: pypa/gh-action-pypi-publish@
|
|
52
|
+
uses: pypa/gh-action-pypi-publish@67339c736fd9354cd4f8cb0b744f2b82a74b5c70 # v1.12.3
|
|
53
53
|
|
|
54
54
|
github-release:
|
|
55
55
|
name: >-
|
|
@@ -78,5 +78,5 @@ jobs:
|
|
|
78
78
|
- name: Create GitHub Release
|
|
79
79
|
uses: softprops/action-gh-release@e7a8f85e1c67a31e6ed99a94b41bd0b71bbee6b8 # v2.0.9
|
|
80
80
|
with:
|
|
81
|
-
generate_release_notes:
|
|
81
|
+
generate_release_notes: true
|
|
82
82
|
files: dist/**
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: nefino-geosync
|
|
3
|
-
Version: 0.1
|
|
3
|
+
Version: 0.2.1
|
|
4
4
|
Summary: Python package to access geographical data from Nefino.LI Geo
|
|
5
5
|
Project-URL: Application, https://nefino.li
|
|
6
6
|
Project-URL: API, https://api.nefino.li
|
|
@@ -267,5 +267,5 @@ pip install nefino-geosync
|
|
|
267
267
|
|
|
268
268
|
## Help
|
|
269
269
|
|
|
270
|
-
- Open an [issue](https://github.com/
|
|
270
|
+
- Open an [issue](https://github.com/nefino/geosync-py/issues) for bug reports or feature requests
|
|
271
271
|
- Contact [Nefino](https://www.nefino.de/kontakt) for account-related inquiries
|
|
@@ -41,5 +41,5 @@ pip install nefino-geosync
|
|
|
41
41
|
|
|
42
42
|
## Help
|
|
43
43
|
|
|
44
|
-
- Open an [issue](https://github.com/
|
|
44
|
+
- Open an [issue](https://github.com/nefino/geosync-py/issues) for bug reports or feature requests
|
|
45
45
|
- Contact [Nefino](https://www.nefino.de/kontakt) for account-related inquiries
|
|
@@ -1,22 +1,25 @@
|
|
|
1
1
|
import os
|
|
2
2
|
import re
|
|
3
|
-
from shutil import move, rmtree
|
|
4
|
-
from urllib.request import urlretrieve
|
|
5
3
|
import zipfile
|
|
4
|
+
from .config import Config
|
|
6
5
|
from .get_downloadable_analyses import AnalysisResult
|
|
7
6
|
from .journal import Journal
|
|
8
|
-
from .
|
|
9
|
-
from .storage import get_download_directory
|
|
7
|
+
from .storage import get_download_directory
|
|
10
8
|
from datetime import datetime
|
|
9
|
+
from shutil import move, rmtree
|
|
10
|
+
from urllib.request import urlretrieve
|
|
11
|
+
|
|
11
12
|
|
|
12
13
|
def download_analysis(analysis: AnalysisResult) -> None:
|
|
13
14
|
"""Downloads the analysis to the local machine."""
|
|
14
15
|
journal = Journal.singleton()
|
|
15
16
|
download_dir = get_download_directory(analysis.pk)
|
|
16
17
|
download_file = os.path.join(download_dir, "download.zip")
|
|
17
|
-
if
|
|
18
|
-
|
|
19
|
-
|
|
18
|
+
if os.path.exists(download_file):
|
|
19
|
+
# remove any failed download
|
|
20
|
+
os.remove(download_file)
|
|
21
|
+
urlretrieve(analysis.url.replace(" ", "%20"), download_file)
|
|
22
|
+
with zipfile.ZipFile(download_file, "r") as zip_ref:
|
|
20
23
|
zip_ref.extractall(download_dir)
|
|
21
24
|
zip_root = get_zip_root(download_dir)
|
|
22
25
|
unpack_items(zip_root, analysis.pk, analysis.started_at)
|
|
@@ -29,42 +32,95 @@ def get_zip_root(download_dir: str) -> str:
|
|
|
29
32
|
return download_dir
|
|
30
33
|
|
|
31
34
|
|
|
32
|
-
FILE_NAME_PATTERN = re.compile(
|
|
35
|
+
FILE_NAME_PATTERN = re.compile(
|
|
36
|
+
r"(?P<layer>^.*?)(?P<buffer>__[0-9]+m)?(?P<ext>\..{3,4}$)"
|
|
37
|
+
)
|
|
38
|
+
|
|
33
39
|
|
|
34
40
|
def unpack_items(zip_root: str, pk: str, started_at: datetime) -> None:
|
|
35
|
-
"""
|
|
41
|
+
"""
|
|
42
|
+
Unpacks the layers from the zip file.
|
|
43
|
+
|
|
44
|
+
Args:
|
|
45
|
+
zip_root: Path to the root directory of the extracted zip
|
|
46
|
+
pk: Primary key of the analysis
|
|
47
|
+
started_at: Timestamp when the analysis started
|
|
48
|
+
"""
|
|
36
49
|
journal = Journal.singleton()
|
|
37
50
|
config = Config.singleton()
|
|
51
|
+
|
|
38
52
|
if pk not in journal.analysis_states:
|
|
39
53
|
print(f"Analysis {pk} not found in journal; skipping download")
|
|
40
54
|
return
|
|
55
|
+
|
|
41
56
|
state = journal.get_state_for_analysis(pk)
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
57
|
+
base_path = get_base_path(zip_root)
|
|
58
|
+
|
|
59
|
+
# Iterate through cluster folders inside the analysis subfolder
|
|
60
|
+
for cluster in (
|
|
61
|
+
f
|
|
62
|
+
for f in os.listdir(base_path)
|
|
63
|
+
if f != "analysis_area" and os.path.isdir(os.path.join(base_path, f))
|
|
64
|
+
):
|
|
65
|
+
cluster_dir = os.path.join(base_path, cluster)
|
|
46
66
|
layers = set()
|
|
67
|
+
|
|
47
68
|
for file in os.listdir(cluster_dir):
|
|
48
69
|
if journal.is_newer_than_saved(file, state, started_at):
|
|
49
70
|
output_dir = os.path.join(config.output_path, state)
|
|
50
71
|
if not os.path.exists(output_dir):
|
|
51
72
|
os.makedirs(output_dir)
|
|
73
|
+
|
|
52
74
|
file_path = os.path.join(cluster_dir, file)
|
|
53
75
|
match = re.match(FILE_NAME_PATTERN, file)
|
|
54
76
|
layer, ext = (match.group("layer"), match.group("ext"))
|
|
55
|
-
|
|
77
|
+
|
|
78
|
+
# Remove any existing files for the same layer
|
|
56
79
|
# this is important to avoid confusion if the pre-buffer changes
|
|
57
|
-
for matching_file in (
|
|
58
|
-
|
|
80
|
+
for matching_file in (
|
|
81
|
+
f for f in os.listdir(output_dir) if f.startswith(layer)
|
|
82
|
+
):
|
|
59
83
|
output_match = re.match(FILE_NAME_PATTERN, matching_file)
|
|
60
84
|
# only remove files that match the layer and extension
|
|
61
85
|
# otherwise, only the last extension to be unpacked would survive
|
|
62
86
|
# also, we are double-checking the layer name here in case we have
|
|
63
87
|
# a layer name which starts with a different layer's name
|
|
64
|
-
if
|
|
65
|
-
|
|
88
|
+
if (
|
|
89
|
+
output_match.group("layer") == layer
|
|
90
|
+
and output_match.group("ext") == ext
|
|
91
|
+
):
|
|
66
92
|
os.remove(os.path.join(output_dir, matching_file))
|
|
93
|
+
|
|
67
94
|
move(file_path, output_dir)
|
|
68
95
|
layers.add(layer)
|
|
96
|
+
|
|
69
97
|
journal.record_layers_unpacked(layers, state, started_at)
|
|
70
|
-
|
|
98
|
+
|
|
99
|
+
rmtree(zip_root)
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def get_base_path(zip_root: str) -> str:
|
|
103
|
+
"""
|
|
104
|
+
Returns the base path for the analysis files in the ZIP structure.
|
|
105
|
+
|
|
106
|
+
Handles two different ZIP structures:
|
|
107
|
+
- Old structure: analysis_summary.xlsx and cluster folders directly in ZIP root
|
|
108
|
+
- New structure: analysis_summary.xlsx and cluster folders inside a dedicated subfolder
|
|
109
|
+
|
|
110
|
+
The presence of analysis_summary.xlsx in the root directory is used to determine
|
|
111
|
+
which structure we're dealing with.
|
|
112
|
+
|
|
113
|
+
Args:
|
|
114
|
+
zip_root: Path to the root directory of the extracted ZIP file
|
|
115
|
+
|
|
116
|
+
Returns:
|
|
117
|
+
str: Path to the directory containing the cluster folders and analysis_summary.xlsx
|
|
118
|
+
"""
|
|
119
|
+
if "analysis_summary.xlsx" in os.listdir(zip_root):
|
|
120
|
+
# Old structure - use zip_root
|
|
121
|
+
return zip_root
|
|
122
|
+
# Get the analysis subfolder name (first and only directory in zip_root)
|
|
123
|
+
analysis_subfolder = next(
|
|
124
|
+
f for f in os.listdir(zip_root) if os.path.isdir(os.path.join(zip_root, f))
|
|
125
|
+
)
|
|
126
|
+
return os.path.join(zip_root, analysis_subfolder)
|
|
@@ -823,17 +823,9 @@
|
|
|
823
823
|
"kind": "NON_NULL",
|
|
824
824
|
"name": null,
|
|
825
825
|
"ofType": {
|
|
826
|
-
"kind": "
|
|
827
|
-
"name":
|
|
828
|
-
"ofType":
|
|
829
|
-
"kind": "NON_NULL",
|
|
830
|
-
"name": null,
|
|
831
|
-
"ofType": {
|
|
832
|
-
"kind": "INPUT_OBJECT",
|
|
833
|
-
"name": "GeoAnalysisObjectInput",
|
|
834
|
-
"ofType": null
|
|
835
|
-
}
|
|
836
|
-
}
|
|
826
|
+
"kind": "INPUT_OBJECT",
|
|
827
|
+
"name": "GeoAnalysisObjectInput",
|
|
828
|
+
"ofType": null
|
|
837
829
|
}
|
|
838
830
|
}
|
|
839
831
|
}
|
|
@@ -1206,16 +1198,6 @@
|
|
|
1206
1198
|
"enumValues": null,
|
|
1207
1199
|
"fields": null,
|
|
1208
1200
|
"inputFields": [
|
|
1209
|
-
{
|
|
1210
|
-
"defaultValue": null,
|
|
1211
|
-
"description": "Operation to be done on those layers. One of \"union\", \"union_negative\" or \"both\". Default is \"both\".",
|
|
1212
|
-
"name": "type",
|
|
1213
|
-
"type": {
|
|
1214
|
-
"kind": "ENUM",
|
|
1215
|
-
"name": "OperationType",
|
|
1216
|
-
"ofType": null
|
|
1217
|
-
}
|
|
1218
|
-
},
|
|
1219
1201
|
{
|
|
1220
1202
|
"defaultValue": null,
|
|
1221
1203
|
"description": "Name of the requested operation",
|
|
@@ -1254,35 +1236,6 @@
|
|
|
1254
1236
|
"name": "GeoAnalysisOperationInput",
|
|
1255
1237
|
"possibleTypes": null
|
|
1256
1238
|
},
|
|
1257
|
-
{
|
|
1258
|
-
"description": "An enumeration.",
|
|
1259
|
-
"enumValues": [
|
|
1260
|
-
{
|
|
1261
|
-
"deprecationReason": null,
|
|
1262
|
-
"description": null,
|
|
1263
|
-
"isDeprecated": false,
|
|
1264
|
-
"name": "UNION"
|
|
1265
|
-
},
|
|
1266
|
-
{
|
|
1267
|
-
"deprecationReason": null,
|
|
1268
|
-
"description": null,
|
|
1269
|
-
"isDeprecated": false,
|
|
1270
|
-
"name": "UNION_NEGATIVE"
|
|
1271
|
-
},
|
|
1272
|
-
{
|
|
1273
|
-
"deprecationReason": null,
|
|
1274
|
-
"description": null,
|
|
1275
|
-
"isDeprecated": false,
|
|
1276
|
-
"name": "BOTH"
|
|
1277
|
-
}
|
|
1278
|
-
],
|
|
1279
|
-
"fields": null,
|
|
1280
|
-
"inputFields": null,
|
|
1281
|
-
"interfaces": null,
|
|
1282
|
-
"kind": "ENUM",
|
|
1283
|
-
"name": "OperationType",
|
|
1284
|
-
"possibleTypes": null
|
|
1285
|
-
},
|
|
1286
1239
|
{
|
|
1287
1240
|
"description": "Input for specs: Output format.",
|
|
1288
1241
|
"enumValues": null,
|
|
@@ -24,11 +24,6 @@ ID = sgqlc.types.ID
|
|
|
24
24
|
|
|
25
25
|
Int = sgqlc.types.Int
|
|
26
26
|
|
|
27
|
-
class OperationType(sgqlc.types.Enum):
|
|
28
|
-
__schema__ = schema
|
|
29
|
-
__choices__ = ('BOTH', 'UNION', 'UNION_NEGATIVE')
|
|
30
|
-
|
|
31
|
-
|
|
32
27
|
class OutputObjectType(sgqlc.types.Enum):
|
|
33
28
|
__schema__ = schema
|
|
34
29
|
__choices__ = ('GPKG', 'QGIS_AND_GPKG', 'QGIS_PRJ', 'SHP')
|
|
@@ -75,7 +70,7 @@ class GeoAnalysisInput(sgqlc.types.Input):
|
|
|
75
70
|
__schema__ = schema
|
|
76
71
|
__field_names__ = ('name', 'specs')
|
|
77
72
|
name = sgqlc.types.Field(String, graphql_name='name')
|
|
78
|
-
specs = sgqlc.types.Field(sgqlc.types.non_null(
|
|
73
|
+
specs = sgqlc.types.Field(sgqlc.types.non_null('GeoAnalysisObjectInput'), graphql_name='specs')
|
|
79
74
|
|
|
80
75
|
|
|
81
76
|
class GeoAnalysisLayerInput(sgqlc.types.Input):
|
|
@@ -97,8 +92,7 @@ class GeoAnalysisObjectInput(sgqlc.types.Input):
|
|
|
97
92
|
|
|
98
93
|
class GeoAnalysisOperationInput(sgqlc.types.Input):
|
|
99
94
|
__schema__ = schema
|
|
100
|
-
__field_names__ = ('
|
|
101
|
-
type = sgqlc.types.Field(OperationType, graphql_name='type')
|
|
95
|
+
__field_names__ = ('operation_name', 'input')
|
|
102
96
|
operation_name = sgqlc.types.Field(sgqlc.types.non_null(String), graphql_name='operationName')
|
|
103
97
|
input = sgqlc.types.Field(sgqlc.types.non_null(sgqlc.types.list_of(String)), graphql_name='input')
|
|
104
98
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|