Coverage for tests/test_stationmeta.py: 100%
323 statements
« prev ^ index » next coverage.py v7.11.0, created at 2025-11-03 20:32 +0000
« prev ^ index » next coverage.py v7.11.0, created at 2025-11-03 20:32 +0000
1# SPDX-FileCopyrightText: 2021 Forschungszentrum Jülich GmbH
2# SPDX-License-Identifier: MIT
4import pytest
5import json
6from fastapi import Request
7from sqlalchemy import insert
8from toardb.stationmeta.models import (
9 StationmetaCore,
10 StationmetaGlobal,
11 StationmetaGlobalService,
12 StationmetaRole,
13 StationmetaChangelog,
14 stationmeta_core_stationmeta_roles_table
15)
16from toardb.toardb import app
17from toardb.stationmeta import crud
18from toardb.stationmeta.schemas import (
19 get_geom_from_coordinates,
20 Coordinates,
21 StationmetaCreate
22)
23from toardb.auth_user.models import AuthUser
24from toardb.contacts.models import Person, Organisation, Contact
25from toardb.test_base import (
26 client,
27 get_test_db,
28 override_dependency,
29 create_test_database,
30 url,
31 get_test_engine,
32 test_db_session as db,
33)
34from unittest.mock import patch
37class TestApps:
38 def setup(self):
39 self.application_url = "/stationmeta/"
41 """Set up all the data before each test
42 If you want the setup only once (per test module),
43 the scope argument is not working in the expected way, as discussed here:
44 https://stackoverflow.com/questions/45817153/py-test-fixture-use-function-fixture-in-scope-fixture
45 """
46 @pytest.fixture(autouse=True)
47 def setup_db_data(self, db):
48 # id_seq will not be reset automatically between tests!
49 _db_conn = get_test_engine()
50 fake_conn = _db_conn.raw_connection()
51 fake_cur = fake_conn.cursor()
52 fake_cur.execute("ALTER SEQUENCE auth_user_id_seq RESTART WITH 1")
53 fake_conn.commit()
54 fake_cur.execute("ALTER SEQUENCE stationmeta_core_id_seq RESTART WITH 1")
55 fake_conn.commit()
56 fake_cur.execute("ALTER SEQUENCE stationmeta_global_id_seq RESTART WITH 1")
57 fake_conn.commit()
58 fake_cur.execute("ALTER SEQUENCE stationmeta_annotations_id_seq RESTART WITH 1")
59 fake_conn.commit()
60 fake_cur.execute("ALTER SEQUENCE stationmeta_roles_id_seq RESTART WITH 3")
61 fake_conn.commit()
62 fake_cur.execute("ALTER SEQUENCE stationmeta_aux_doc_id_seq RESTART WITH 1")
63 fake_conn.commit()
64 fake_cur.execute("ALTER SEQUENCE stationmeta_aux_image_id_seq RESTART WITH 1")
65 fake_conn.commit()
66 fake_cur.execute("ALTER SEQUENCE stationmeta_aux_url_id_seq RESTART WITH 1")
67 fake_conn.commit()
68 fake_cur.execute("ALTER SEQUENCE persons_id_seq RESTART WITH 1")
69 fake_conn.commit()
70 fake_cur.execute("ALTER SEQUENCE organisations_id_seq RESTART WITH 1")
71 fake_conn.commit()
72 fake_cur.execute("ALTER SEQUENCE contacts_id_seq RESTART WITH 1")
73 fake_conn.commit()
74 infilename = "tests/fixtures/auth_user/auth.json"
75 with open(infilename) as f:
76 metajson=json.load(f)
77 for entry in metajson:
78 new_auth_user= AuthUser(**entry)
79 db.add(new_auth_user)
80 db.commit()
81 db.refresh(new_auth_user)
82 infilename = "tests/fixtures/contacts/persons.json"
83 with open(infilename) as f:
84 metajson=json.load(f)
85 for entry in metajson:
86 new_person = Person(**entry)
87 db.add(new_person)
88 db.commit()
89 db.refresh(new_person)
90 infilename = "tests/fixtures/contacts/organisations.json"
91 with open(infilename) as f:
92 metajson=json.load(f)
93 for entry in metajson:
94 new_organisation = Organisation(**entry)
95 db.add(new_organisation)
96 db.commit()
97 db.refresh(new_organisation)
98 infilename = "tests/fixtures/contacts/contacts.json"
99 with open(infilename) as f:
100 metajson=json.load(f)
101 for entry in metajson:
102 new_contact = Contact(**entry)
103 db.add(new_contact)
104 db.commit()
105 db.refresh(new_contact)
106 # I also need to upload tests with nested data!!!
107 infilename = "tests/fixtures/stationmeta/stationmeta_core.json"
108 with open(infilename) as f:
109 metajson=json.load(f)
110 for entry in metajson:
111 new_stationmeta_core = StationmetaCore(**entry)
112 # there's a mismatch with coordinates --> how to automatically switch back and forth?!
113 tmp_coordinates = new_stationmeta_core.coordinates
114 new_stationmeta_core.coordinates = get_geom_from_coordinates(Coordinates(**new_stationmeta_core.coordinates))
115 # there's also a mismatch with additional_metadata --> BUT: this should not be switched back!
116 # in upload command, we have now: "additional_metadata": "{}"
117 # but return from this method gives: "additional_metadata": {}
118 # ==> there is a mismatch between model(JSONB) and schema(JSON)
119 new_stationmeta_core.additional_metadata = str(new_stationmeta_core.additional_metadata)
120 db.add(new_stationmeta_core)
121 db.commit()
122 db.refresh(new_stationmeta_core)
123 infilename = "tests/fixtures/stationmeta/stationmeta_changelog.json"
124 with open(infilename) as f:
125 metajson=json.load(f)
126 for entry in metajson:
127 new_stationmeta_changelog = StationmetaChangelog(**entry)
128 db.add(new_stationmeta_changelog)
129 db.commit()
130 db.refresh(new_stationmeta_changelog)
131 infilename = "tests/fixtures/stationmeta/stationmeta_global.json"
132 with open(infilename) as f:
133 metajson=json.load(f)
134 for entry in metajson:
135 new_stationmeta_global = StationmetaGlobal(**entry)
136 db.add(new_stationmeta_global)
137 db.commit()
138 db.refresh(new_stationmeta_global)
139 infilename = "tests/fixtures/stationmeta/stationmeta_global_services.json"
140 with open(infilename) as f:
141 metajson=json.load(f)
142 for entry in metajson:
143 new_stationmeta_global_service = StationmetaGlobalService(**entry)
144 db.add(new_stationmeta_global_service)
145 db.commit()
146 db.refresh(new_stationmeta_global_service)
147 infilename = "tests/fixtures/stationmeta/stationmeta_roles.json"
148 with open(infilename) as f:
149 metajson=json.load(f)
150 for entry in metajson:
151 new_stationmeta_role = StationmetaRole(**entry)
152 db.add(new_stationmeta_role)
153 db.commit()
154 db.refresh(new_stationmeta_role)
155 infilename = "tests/fixtures/stationmeta/stationmeta_core_stationmeta_roles.json"
156 with open(infilename) as f:
157 metajson=json.load(f)
158 for entry in metajson:
159 db.execute(insert(stationmeta_core_stationmeta_roles_table).values(station_id=entry["station_id"], role_id=entry["role_id"]))
160 db.execute("COMMIT")
163 # 1. tests retrieving station metadata
165 def test_get_all(self, client, db):
166 response = client.get("/stationmeta/")
167 expected_status_code = 200
168 assert response.status_code == expected_status_code
169 expected_resp = [{'id': 1,
170 'codes': ['China11'],
171 'name': 'Mount Tai',
172 'coordinates': {'lat': 36.256, 'lng': 117.106, 'alt': 1534.0},
173 'coordinate_validation_status': 'not checked',
174 'country': 'Germany',
175 'state': 'Shandong Sheng',
176 'type': 'unknown',
177 'type_of_area': 'unknown',
178 'timezone': 'Asia/Shanghai',
179 'additional_metadata': {'dummy_info': 'Here is some more information about the station'},
180 'roles': [{'id': 2,
181 'role': 'resource provider',
182 'status': 'active',
183 'contact': {'id': 1,
184 'name': 'UBA',
185 'longname': 'Umweltbundesamt',
186 'kind': 'government',
187 'city': 'Dessau-Roßlau',
188 'postcode': '06844',
189 'street_address': 'Wörlitzer Platz 1',
190 'country': 'Germany',
191 'homepage': 'https://www.umweltbundesamt.de',
192 'contact_url': 'mailto:immission@uba.de'}}],
193 'aux_images': [],
194 'aux_docs': [],
195 'aux_urls': [],
196 'globalmeta': {'mean_topography_srtm_alt_90m_year1994': -999.0,
197 'mean_topography_srtm_alt_1km_year1994': -999.0,
198 'max_topography_srtm_relative_alt_5km_year1994': -999.0,
199 'min_topography_srtm_relative_alt_5km_year1994': -999.0,
200 'stddev_topography_srtm_relative_alt_5km_year1994': -999.0,
201 'climatic_zone_year2016': '6 (warm temperate dry)',
202 'htap_region_tier1_year2010': '11 (MDE Middle East: S. '
203 'Arabia, Oman, etc, Iran, Iraq)',
204 'dominant_landcover_year2012': '10 (Cropland, rainfed)',
205 'landcover_description_25km_year2012': '11 (Cropland, '
206 'rainfed, herbaceous '
207 'cover): 30.7 %, 12 '
208 '(Cropland, rainfed, '
209 'tree or shrub cover): '
210 '25.0 %, 210 (Water '
211 'bodies): 16.9 %, 130 '
212 '(Grassland): 8.6 %, '
213 '190 (Urban areas): '
214 '5.8 %, 100 (Mosaic '
215 'tree and shrub (>50%) '
216 '/ herbaceous cover '
217 '(<50%)): 3.5 %, 40 '
218 '(Mosaic natural '
219 'vegetation (tree, '
220 'shrub, herbaceous '
221 'cover) (>50%) / '
222 'cropland (<50%)): 2.6 '
223 '%, 10 (Cropland, '
224 'rainfed): 2.0 %, 30 '
225 '(Mosaic cropland '
226 '(>50%) / natural '
227 'vegetation (tree, '
228 'shrub, herbaceous '
229 'cover) (<50%)): 1.9 %',
230 'dominant_ecoregion_year2017': '-1 (undefined)',
231 'ecoregion_description_25km_year2017': '',
232 'distance_to_major_road_year2020': -999.0,
233 'mean_stable_nightlights_1km_year2013': -999.0,
234 'mean_stable_nightlights_5km_year2013': -999.0,
235 'max_stable_nightlights_25km_year2013': -999.0,
236 'max_stable_nightlights_25km_year1992': -999.0,
237 'mean_population_density_250m_year2015': -1.0,
238 'mean_population_density_5km_year2015': -1.0,
239 'max_population_density_25km_year2015': -1.0,
240 'mean_population_density_250m_year1990': -1.0,
241 'mean_population_density_5km_year1990': -1.0,
242 'max_population_density_25km_year1990': -1.0,
243 'mean_nox_emissions_10km_year2015': -999.0,
244 'mean_nox_emissions_10km_year2000': -999.0,
245 'toar1_category': 'unclassified',
246 'toar2_category': 'urban'},
247 'changelog': [{'datetime': '2023-07-05T08:23:04.551645+00:00',
248 'description': 'station created',
249 'old_value': '',
250 'new_value': '',
251 'station_id': 1,
252 'author_id': 1,
253 'type_of_change': 'created'}]},
254 {'id': 2,
255 'codes': ['SDZ54421'],
256 'name': 'Shangdianzi',
257 'coordinates': {'lat': 40.65, 'lng': 117.106, 'alt': 293.9},
258 'coordinate_validation_status': 'not checked',
259 'country': 'China',
260 'state': 'Beijing Shi',
261 'type': 'unknown',
262 'type_of_area': 'unknown',
263 'timezone': 'Asia/Shanghai',
264 'additional_metadata': {'add_type': 'nature reservation'},
265 'roles': [{'id': 1,
266 'role': 'resource provider',
267 'status': 'active',
268 'contact': {'id': 2,
269 'name': 'FZJ',
270 'longname': 'Forschungszentrum Jülich',
271 'kind': 'research',
272 'city': 'Jülich',
273 'postcode': '52425',
274 'street_address': 'Wilhelm-Johnen-Straße',
275 'country': 'Germany',
276 'homepage': 'https://www.fz-juelich.de',
277 'contact_url': 'mailto:toar-data@fz-juelich.de'}}],
278 'aux_images': [],
279 'aux_docs': [],
280 'aux_urls': [],
281 'globalmeta': {'mean_topography_srtm_alt_90m_year1994': -999.0,
282 'mean_topography_srtm_alt_1km_year1994': -999.0,
283 'max_topography_srtm_relative_alt_5km_year1994': -999.0,
284 'min_topography_srtm_relative_alt_5km_year1994': -999.0,
285 'stddev_topography_srtm_relative_alt_5km_year1994': -999.0,
286 'climatic_zone_year2016': '6 (warm temperate dry)',
287 'htap_region_tier1_year2010': '11 (MDE Middle East: S. '
288 'Arabia, Oman, etc, Iran, Iraq)',
289 'dominant_landcover_year2012': '11 (Cropland, rainfed, herbaceous cover)',
290 'landcover_description_25km_year2012': '',
291 'dominant_ecoregion_year2017': '-1 (undefined)',
292 'ecoregion_description_25km_year2017': '',
293 'distance_to_major_road_year2020': -999.0,
294 'mean_stable_nightlights_1km_year2013': -999.0,
295 'mean_stable_nightlights_5km_year2013': -999.0,
296 'max_stable_nightlights_25km_year2013': -999.0,
297 'max_stable_nightlights_25km_year1992': -999.0,
298 'mean_population_density_250m_year2015': -1.0,
299 'mean_population_density_5km_year2015': -1.0,
300 'max_population_density_25km_year2015': -1.0,
301 'mean_population_density_250m_year1990': -1.0,
302 'mean_population_density_5km_year1990': -1.0,
303 'max_population_density_25km_year1990': -1.0,
304 'mean_nox_emissions_10km_year2015': -999.0,
305 'mean_nox_emissions_10km_year2000': -999.0,
306 'toar1_category': 'unclassified',
307 'toar2_category': 'suburban'},
308 'changelog': [{'datetime': '2023-07-15T19:27:09.463245+00:00',
309 'description': 'station created',
310 'old_value': '',
311 'new_value': '',
312 'station_id': 2,
313 'author_id': 1,
314 'type_of_change': 'created'}]},
315 {'id': 3,
316 'codes': ['China_test8'],
317 'name': 'Test_China',
318 'coordinates': {'lat': 36.256, 'lng': 117.106, 'alt': 1534.0},
319 'coordinate_validation_status': 'not checked',
320 'country': 'China',
321 'state': 'Shandong Sheng',
322 'type': 'unknown',
323 'type_of_area': 'unknown',
324 'timezone': 'Asia/Shanghai',
325 'additional_metadata': {},
326 'aux_images': [],
327 'aux_docs': [],
328 'aux_urls': [],
329 'globalmeta': {'mean_topography_srtm_alt_90m_year1994': -999.0,
330 'mean_topography_srtm_alt_1km_year1994': -999.0,
331 'max_topography_srtm_relative_alt_5km_year1994': -999.0,
332 'min_topography_srtm_relative_alt_5km_year1994': -999.0,
333 'stddev_topography_srtm_relative_alt_5km_year1994': -999.0,
334 'climatic_zone_year2016': '6 (warm temperate dry)',
335 'htap_region_tier1_year2010': '10 (SAF Sub Saharan/sub Sahel '
336 'Africa)',
337 'dominant_landcover_year2012': '10 (Cropland, rainfed)',
338 'landcover_description_25km_year2012': '',
339 'dominant_ecoregion_year2017': '-1 (undefined)',
340 'ecoregion_description_25km_year2017': '',
341 'distance_to_major_road_year2020': -999.0,
342 'mean_stable_nightlights_1km_year2013': -999.0,
343 'mean_stable_nightlights_5km_year2013': -999.0,
344 'max_stable_nightlights_25km_year2013': -999.0,
345 'max_stable_nightlights_25km_year1992': -999.0,
346 'mean_population_density_250m_year2015': -1.0,
347 'mean_population_density_5km_year2015': -1.0,
348 'max_population_density_25km_year2015': -1.0,
349 'mean_population_density_250m_year1990': -1.0,
350 'mean_population_density_5km_year1990': -1.0,
351 'max_population_density_25km_year1990': -1.0,
352 'mean_nox_emissions_10km_year2015': -999.0,
353 'mean_nox_emissions_10km_year2000': -999.0,
354 'toar1_category': 'unclassified',
355 'toar2_category': 'suburban'},
356 'changelog': [{'datetime': '2023-08-15T21:16:20.596545+00:00',
357 'description': 'station created',
358 'old_value': '',
359 'new_value': '',
360 'station_id': 3,
361 'author_id': 1,
362 'type_of_change': 'created'}]}]
363 assert response.json() == expected_resp
366 def test_get_special_nested(self, client, db):
367 response = client.get("/stationmeta/China_test8")
368 expected_status_code = 200
369 assert response.status_code == expected_status_code
370 expected_resp = {'id': 3, 'codes': ['China_test8'], 'name': 'Test_China',
371 'coordinates': {'lat': 36.256, 'lng': 117.106, 'alt': 1534.0},
372 'coordinate_validation_status': 'not checked',
373 'country': 'China', 'state': 'Shandong Sheng',
374 'type': 'unknown',
375 'type_of_area': 'unknown', 'timezone': 'Asia/Shanghai',
376 'additional_metadata': {},
377 'aux_images': [], 'aux_docs': [],
378 'aux_urls': [],
379 'globalmeta': {'climatic_zone_year2016': '6 (warm temperate dry)',
380 'distance_to_major_road_year2020': -999.0,
381 'dominant_ecoregion_year2017': '-1 (undefined)',
382 'dominant_landcover_year2012': '10 (Cropland, rainfed)',
383 'ecoregion_description_25km_year2017': '',
384 'landcover_description_25km_year2012': '',
385 'htap_region_tier1_year2010': '10 (SAF Sub Saharan/sub Sahel Africa)',
386 'max_stable_nightlights_25km_year1992': -999.0,
387 'max_stable_nightlights_25km_year2013': -999.0,
388 'max_population_density_25km_year1990': -1.0,
389 'max_population_density_25km_year2015': -1.0,
390 'max_topography_srtm_relative_alt_5km_year1994': -999.0,
391 'mean_stable_nightlights_1km_year2013': -999.0,
392 'mean_stable_nightlights_5km_year2013': -999.0,
393 'mean_nox_emissions_10km_year2000': -999.0,
394 'mean_nox_emissions_10km_year2015': -999.0,
395 'mean_population_density_250m_year1990': -1.0,
396 'mean_population_density_250m_year2015': -1.0,
397 'mean_population_density_5km_year1990': -1.0,
398 'mean_population_density_5km_year2015': -1.0,
399 'mean_topography_srtm_alt_1km_year1994': -999.0,
400 'mean_topography_srtm_alt_90m_year1994': -999.0,
401 'min_topography_srtm_relative_alt_5km_year1994': -999.0,
402 'stddev_topography_srtm_relative_alt_5km_year1994': -999.0,
403 'toar1_category': 'unclassified',
404 'toar2_category': 'suburban'},
405 'changelog': [{'datetime': '2023-08-15T21:16:20.596545+00:00',
406 'description': 'station created',
407 'old_value': '',
408 'new_value': '',
409 'station_id': 3,
410 'author_id': 1,
411 'type_of_change': 'created'
412 }]}
413 assert response.json() == expected_resp
416 def test_get_special_with_fields(self, client, db):
417 response = client.get("/stationmeta/China11?fields=codes,globalmeta")
418 expected_status_code = 200
419 assert response.status_code == expected_status_code
420 expected_resp = {'codes': ['China11'],
421 'globalmeta': {'mean_topography_srtm_alt_90m_year1994': -999.0,
422 'mean_topography_srtm_alt_1km_year1994': -999.0,
423 'max_topography_srtm_relative_alt_5km_year1994': -999.0,
424 'min_topography_srtm_relative_alt_5km_year1994': -999.0,
425 'stddev_topography_srtm_relative_alt_5km_year1994': -999.0,
426 'climatic_zone_year2016': '6 (warm temperate dry)',
427 'htap_region_tier1_year2010': '11 (MDE Middle East: S. Arabia, Oman, etc, Iran, Iraq)',
428 'dominant_landcover_year2012': '10 (Cropland, rainfed)',
429 'landcover_description_25km_year2012': '11 (Cropland, rainfed, herbaceous cover): 30.7 %, 12 (Cropland, rainfed, tree or shrub cover): 25.0 %, 210 (Water bodies): 16.9 %, 130 (Grassland): 8.6 %, 190 (Urban areas): 5.8 %, 100 (Mosaic tree and shrub (>50%) / herbaceous cover (<50%)): 3.5 %, 40 (Mosaic natural vegetation (tree, shrub, herbaceous cover) (>50%) / cropland (<50%)): 2.6 %, 10 (Cropland, rainfed): 2.0 %, 30 (Mosaic cropland (>50%) / natural vegetation (tree, shrub, herbaceous cover) (<50%)): 1.9 %',
430 'dominant_ecoregion_year2017': '-1 (undefined)',
431 'ecoregion_description_25km_year2017': '',
432 'distance_to_major_road_year2020': -999.0,
433 'mean_stable_nightlights_1km_year2013': -999.0,
434 'mean_stable_nightlights_5km_year2013': -999.0,
435 'max_stable_nightlights_25km_year2013': -999.0,
436 'max_stable_nightlights_25km_year1992': -999.0,
437 'mean_population_density_250m_year2015': -1.0,
438 'mean_population_density_5km_year2015': -1.0,
439 'max_population_density_25km_year2015': -1.0,
440 'mean_population_density_250m_year1990': -1.0,
441 'mean_population_density_5km_year1990': -1.0,
442 'max_population_density_25km_year1990': -1.0,
443 'mean_nox_emissions_10km_year2015': -999.0,
444 'mean_nox_emissions_10km_year2000': -999.0,
445 'toar1_category': 'unclassified',
446 'toar2_category': 'urban'
447 }
448 }
449 assert response.json() == expected_resp
452 def test_get_special_with_ambiguous_field(self, client, db):
453 response = client.get("/stationmeta/China11?fields=id")
454 expected_status_code = 200
455 assert response.status_code == expected_status_code
456 expected_resp = {'id': 1}
457 assert response.json() == expected_resp
460 def test_get_special_with_fields_station_not_found(self, client, db):
461 response = client.get("/stationmeta/DENW078?fields=codes,globalmeta")
462 expected_status_code = 404
463 assert response.status_code == expected_status_code
464 expected_resp = {"detail":"Metadata for station 'DENW078' not found."}
465 assert response.json() == expected_resp
467 def test_get_all_with_fields(self, client, db):
468 response = client.get("/stationmeta/?fields=codes,globalmeta")
469 expected_status_code = 200
470 assert response.status_code == expected_status_code
471 expected_resp = [{'codes': ['China11'],
472 'globalmeta': {'mean_topography_srtm_alt_90m_year1994': -999.0,
473 'mean_topography_srtm_alt_1km_year1994': -999.0,
474 'max_topography_srtm_relative_alt_5km_year1994': -999.0,
475 'min_topography_srtm_relative_alt_5km_year1994': -999.0,
476 'stddev_topography_srtm_relative_alt_5km_year1994': -999.0,
477 'climatic_zone_year2016': '6 (warm temperate dry)',
478 'htap_region_tier1_year2010': '11 (MDE Middle East: S. Arabia, Oman, etc, Iran, Iraq)',
479 'dominant_landcover_year2012': '10 (Cropland, rainfed)',
480 'landcover_description_25km_year2012': '11 (Cropland, rainfed, herbaceous cover): 30.7 %, 12 (Cropland, rainfed, tree or shrub cover): 25.0 %, 210 (Water bodies): 16.9 %, 130 (Grassland): 8.6 %, 190 (Urban areas): 5.8 %, 100 (Mosaic tree and shrub (>50%) / herbaceous cover (<50%)): 3.5 %, 40 (Mosaic natural vegetation (tree, shrub, herbaceous cover) (>50%) / cropland (<50%)): 2.6 %, 10 (Cropland, rainfed): 2.0 %, 30 (Mosaic cropland (>50%) / natural vegetation (tree, shrub, herbaceous cover) (<50%)): 1.9 %',
481 'dominant_ecoregion_year2017': '-1 (undefined)',
482 'ecoregion_description_25km_year2017': '',
483 'distance_to_major_road_year2020': -999.0,
484 'mean_stable_nightlights_1km_year2013': -999.0,
485 'mean_stable_nightlights_5km_year2013': -999.0,
486 'max_stable_nightlights_25km_year2013': -999.0,
487 'max_stable_nightlights_25km_year1992': -999.0,
488 'mean_population_density_250m_year2015': -1.0,
489 'mean_population_density_5km_year2015': -1.0,
490 'max_population_density_25km_year2015': -1.0,
491 'mean_population_density_250m_year1990': -1.0,
492 'mean_population_density_5km_year1990': -1.0,
493 'max_population_density_25km_year1990': -1.0,
494 'mean_nox_emissions_10km_year2015': -999.0,
495 'mean_nox_emissions_10km_year2000': -999.0,
496 'toar1_category': 'unclassified',
497 'toar2_category': 'urban'
498 }
499 },
500 {'codes': ['SDZ54421'],
501 'globalmeta': {'mean_topography_srtm_alt_90m_year1994': -999.0,
502 'mean_topography_srtm_alt_1km_year1994': -999.0,
503 'max_topography_srtm_relative_alt_5km_year1994': -999.0,
504 'min_topography_srtm_relative_alt_5km_year1994': -999.0,
505 'stddev_topography_srtm_relative_alt_5km_year1994': -999.0,
506 'climatic_zone_year2016': '6 (warm temperate dry)',
507 'htap_region_tier1_year2010': '11 (MDE Middle East: S. Arabia, Oman, etc, Iran, Iraq)',
508 'dominant_landcover_year2012': '11 (Cropland, rainfed, herbaceous cover)',
509 'landcover_description_25km_year2012': '',
510 'dominant_ecoregion_year2017': '-1 (undefined)',
511 'ecoregion_description_25km_year2017': '',
512 'distance_to_major_road_year2020': -999.0,
513 'mean_stable_nightlights_1km_year2013': -999.0,
514 'mean_stable_nightlights_5km_year2013': -999.0,
515 'max_stable_nightlights_25km_year2013': -999.0,
516 'max_stable_nightlights_25km_year1992': -999.0,
517 'mean_population_density_250m_year2015': -1.0,
518 'mean_population_density_5km_year2015': -1.0,
519 'max_population_density_25km_year2015': -1.0,
520 'mean_population_density_250m_year1990': -1.0,
521 'mean_population_density_5km_year1990': -1.0,
522 'max_population_density_25km_year1990': -1.0,
523 'mean_nox_emissions_10km_year2015': -999.0,
524 'mean_nox_emissions_10km_year2000': -999.0,
525 'toar1_category': 'unclassified',
526 'toar2_category': 'suburban'
527 }
528 },
529 {'codes': ['China_test8'],
530 'globalmeta': {'mean_topography_srtm_alt_90m_year1994': -999.0,
531 'mean_topography_srtm_alt_1km_year1994': -999.0,
532 'max_topography_srtm_relative_alt_5km_year1994': -999.0,
533 'min_topography_srtm_relative_alt_5km_year1994': -999.0,
534 'stddev_topography_srtm_relative_alt_5km_year1994': -999.0,
535 'climatic_zone_year2016': '6 (warm temperate dry)',
536 'htap_region_tier1_year2010': '10 (SAF Sub Saharan/sub Sahel Africa)',
537 'dominant_landcover_year2012': '10 (Cropland, rainfed)',
538 'landcover_description_25km_year2012': '',
539 'dominant_ecoregion_year2017': '-1 (undefined)',
540 'ecoregion_description_25km_year2017': '',
541 'distance_to_major_road_year2020': -999.0,
542 'mean_stable_nightlights_1km_year2013': -999.0,
543 'mean_stable_nightlights_5km_year2013': -999.0,
544 'max_stable_nightlights_25km_year2013': -999.0,
545 'max_stable_nightlights_25km_year1992': -999.0,
546 'mean_population_density_250m_year2015': -1.0,
547 'mean_population_density_5km_year2015': -1.0,
548 'max_population_density_25km_year2015': -1.0,
549 'mean_population_density_250m_year1990': -1.0,
550 'mean_population_density_5km_year1990': -1.0,
551 'max_population_density_25km_year1990': -1.0,
552 'mean_nox_emissions_10km_year2015': -999.0,
553 'mean_nox_emissions_10km_year2000': -999.0,
554 'toar1_category': 'unclassified',
555 'toar2_category': 'suburban'
556 }
557 }]
558 assert response.json() == expected_resp
561 def test_get_all_wrong_params(self, client, db):
562 response = client.get("/stationmeta/?altitude=2000&limit=None")
563 expected_status_code = 400
564 assert response.status_code == expected_status_code
565 expected_resp = "'An unknown argument was received: altitude.'"
566 assert response.json() == expected_resp
569 def test_get_stationmeta_not_found(self, client, db):
570 response = client.get("/stationmeta/-1")
571 expected_status_code = 404
572 assert response.status_code == expected_status_code
573 expected_resp = {"detail": "Metadata for station '-1' not found."}
574 assert response.json() == expected_resp
577 def test_get_stationmeta_by_id_not_found(self, client, db):
578 response = client.get("/stationmeta/id/-1")
579 expected_status_code = 404
580 assert response.status_code == expected_status_code
581 expected_resp = {"detail": "Metadata for station with ID '-1' not found."}
582 assert response.json() == expected_resp
585 def test_get_stationmeta_by_id(self, client, db):
586 response = client.get("/stationmeta/id/1")
587 expected_status_code = 200
588 assert response.status_code == expected_status_code
589 expected_resp = {'id': 1, 'codes': ['China11'], 'name': 'Mount Tai',
590 'coordinates': {'lat': 36.256, 'lng': 117.106, 'alt': 1534.0},
591 'coordinate_validation_status': 'not checked',
592 'country': 'Germany', 'state': 'Shandong Sheng',
593 'type': 'unknown', 'type_of_area': 'unknown',
594 'timezone': 'Asia/Shanghai', 'additional_metadata': {},
595 'roles': [{ 'contact': {
596 'city': 'Dessau-Roßlau',
597 'contact_url': 'mailto:immission@uba.de',
598 'country': 'Germany',
599 'homepage': 'https://www.umweltbundesamt.de',
600 'id': 1,
601 'kind': 'government',
602 'longname': 'Umweltbundesamt',
603 'name': 'UBA',
604 'postcode': '06844',
605 'street_address': 'Wörlitzer Platz 1',
606 },
607 'id': 2,
608 'role': 'resource provider',
609 'status': 'active' }],
610 'additional_metadata': {'dummy_info': 'Here is some more information about the station'},
611 'aux_images': [], 'aux_docs': [], 'aux_urls': [],
612 'globalmeta': {'climatic_zone_year2016': '6 (warm temperate dry)',
613 'distance_to_major_road_year2020': -999.0,
614 'dominant_ecoregion_year2017': '-1 (undefined)',
615 'dominant_landcover_year2012': '10 (Cropland, rainfed)',
616 'ecoregion_description_25km_year2017': '',
617 'landcover_description_25km_year2012': '11 (Cropland, rainfed, '
618 +'herbaceous cover): '
619 +'30.7 %, 12 (Cropland, '
620 +'rainfed, tree or shrub '
621 +'cover): 25.0 %, 210 '
622 +'(Water bodies): 16.9 '
623 +'%, 130 (Grassland): '
624 +'8.6 %, 190 (Urban '
625 +'areas): 5.8 %, 100 '
626 +'(Mosaic tree and shrub '
627 +'(>50%) / herbaceous '
628 +'cover (<50%)): 3.5 %, '
629 +'40 (Mosaic natural '
630 +'vegetation (tree, '
631 +'shrub, herbaceous '
632 +'cover) (>50%) / '
633 +'cropland (<50%)): 2.6 '
634 +'%, 10 (Cropland, '
635 +'rainfed): 2.0 %, 30 '
636 +'(Mosaic cropland '
637 +'(>50%) / natural '
638 +'vegetation (tree, '
639 +'shrub, herbaceous '
640 +'cover) (<50%)): 1.9 %',
641 'htap_region_tier1_year2010': '11 (MDE Middle East: S. Arabia, Oman, etc, Iran, Iraq)',
642 'max_stable_nightlights_25km_year1992': -999.0,
643 'max_stable_nightlights_25km_year2013': -999.0,
644 'max_population_density_25km_year1990': -1.0,
645 'max_population_density_25km_year2015': -1.0,
646 'max_topography_srtm_relative_alt_5km_year1994': -999.0,
647 'mean_stable_nightlights_1km_year2013': -999.0,
648 'mean_stable_nightlights_5km_year2013': -999.0,
649 'mean_nox_emissions_10km_year2000': -999.0,
650 'mean_nox_emissions_10km_year2015': -999.0,
651 'mean_population_density_250m_year1990': -1.0,
652 'mean_population_density_250m_year2015': -1.0,
653 'mean_population_density_5km_year1990': -1.0,
654 'mean_population_density_5km_year2015': -1.0,
655 'mean_topography_srtm_alt_1km_year1994': -999.0,
656 'mean_topography_srtm_alt_90m_year1994': -999.0,
657 'min_topography_srtm_relative_alt_5km_year1994': -999.0,
658 'stddev_topography_srtm_relative_alt_5km_year1994': -999.0,
659 'toar1_category': 'unclassified',
660 'toar2_category': 'urban'},
661 'changelog': [{'datetime': '2023-07-05T08:23:04.551645+00:00',
662 'description': 'station created',
663 'old_value': '',
664 'new_value': '',
665 'station_id': 1,
666 'author_id': 1,
667 'type_of_change': 'created'
668 }]}
669 assert response.json() == expected_resp
672 def test_get_stationmeta_changelog(self, client, db):
673 response = client.get("/stationmeta_changelog/1")
674 expected_status_code = 200
675 assert response.status_code == expected_status_code
676 expected_resp = [{ 'datetime': '2023-07-05T08:23:04.551645+00:00',
677 'description': 'station created',
678 'old_value': '',
679 'new_value': '',
680 'station_id': 1,
681 'author_id': 1,
682 'type_of_change': 'created'
683 }]
684 assert response.json() == expected_resp
686 # 2. tests creating station metadata
689 def test_insert_new_wrong_credentials(self, client, db):
690 response = client.post("/stationmeta/",
691 json={"stationmeta":
692 {"codes":["ttt3","ttt4"],
693 "name":"Test_China","coordinates":{"lat":37.256,"lng":117.106,"alt":1534.0},
694 "coordinate_validation_status": "NotChecked",
695 "country":"CN","state":"Shandong Sheng",
696 "type":"Unknown","type_of_area":"Unknown","timezone":"Asia/Shanghai",
697 "additional_metadata": "{}" }
698 },
699 headers={"email": "j.doe@fz-juelich.de"}
700 )
701 expected_status_code=401
702 assert response.status_code == expected_status_code
703 expected_resp = {'detail': 'Unauthorized.'}
704 assert response.json() == expected_resp
707 def test_insert_new_without_credentials(self, client, db):
708 response = client.post("/stationmeta/",
709 json={"stationmeta":
710 {"codes":["ttt3","ttt4"],
711 "name":"Test_China","coordinates":{"lat":37.256,"lng":117.106,"alt":1534.0},
712 "coordinate_validation_status": "NotChecked",
713 "country":"CN","state":"Shandong Sheng",
714 "type":"Unknown","type_of_area":"Unknown","timezone":"Asia/Shanghai",
715 "additional_metadata": "{}" }
716 })
717 expected_status_code=401
718 assert response.status_code == expected_status_code
719 expected_resp = {'detail': 'Unauthorized.'}
720 assert response.json() == expected_resp
723 def test_insert_new(self, client, db):
724 with patch("toardb.stationmeta.crud.determine_stationmeta_global", return_value={}):
725 response = client.post("/stationmeta/",
726 json={"stationmeta":
727 {"codes":["ttt3","ttt4"],
728 "name":"Test_China","coordinates":{"lat":37.256,"lng":117.106,"alt":1534.0},
729 "coordinate_validation_status": "NotChecked",
730 "country":"CN","state":"Shandong Sheng",
731 "type":"Unknown","type_of_area":"Unknown","timezone":"Asia/Shanghai",
732 "additional_metadata": "{}" }
733 },
734 headers={"email": "s.schroeder@fz-juelich.de"}
735 )
736 expected_status_code = 200
737 assert response.status_code == expected_status_code
738 expected_resp = {'detail': {'message': "new station: ['ttt3', 'ttt4'],Test_China,{'lat': 37.256, 'lng': 117.106, 'alt': 1534.0}",
739 'station_id': 4}}
740 assert response.json() == expected_resp
743 def test_insert_new_with_roles(self, client, db):
744 with patch("toardb.stationmeta.crud.determine_stationmeta_global", return_value={}):
745 response = client.post("/stationmeta/",
746 json={"stationmeta":
747 {"codes":["ttt3","ttt4"],
748 "name":"Test_China","coordinates":{"lat":37.256,"lng":117.106,"alt":1534.0},
749 "coordinate_validation_status": "NotChecked",
750 "country":"CN","state":"Shandong Sheng",
751 "type":"Unknown","type_of_area":"Unknown","timezone":"Asia/Shanghai",
752 "roles": [{"role": "PointOfContact", "contact_id": 3, "status": "Active"},
753 {"role": "Originator", "contact_id": 1, "status": "Active"}],
754 "additional_metadata": "{}" }
755 },
756 headers={"email": "s.schroeder@fz-juelich.de"}
757 )
758 expected_status_code = 200
759 assert response.status_code == expected_status_code
760 expected_resp = {'detail': {'message': "new station: ['ttt3', 'ttt4'],Test_China,{'lat': 37.256, 'lng': 117.106, 'alt': 1534.0}",
761 'station_id': 4}}
762 assert response.json() == expected_resp
765 def test_insert_new_with_annotations(self, client, db):
766 with patch("toardb.stationmeta.crud.determine_stationmeta_global", return_value={}):
767 response = client.post("/stationmeta/",
768 json={"stationmeta":
769 {"codes":["ttt3","ttt4"],
770 "name":"Test_China","coordinates":{"lat":37.256,"lng":117.106,"alt":1534.0},
771 "coordinate_validation_status": "NotChecked",
772 "country":"CN","state":"Shandong Sheng",
773 "type":"Unknown","type_of_area":"Unknown","timezone":"Asia/Shanghai",
774 "annotations": [{"kind": "User",
775 "text": "some foo",
776 "date_added":"2021-07-27 00:00",
777 "approved": True,
778 "contributor_id":1}],
779 "additional_metadata": "{}" }
780 },
781 headers={"email": "s.schroeder@fz-juelich.de"}
782 )
783 expected_status_code = 200
784 assert response.status_code == expected_status_code
785 expected_resp = {'detail': {'message': "new station: ['ttt3', 'ttt4'],Test_China,{'lat': 37.256, 'lng': 117.106, 'alt': 1534.0}",
786 'station_id': 4}}
787 assert response.json() == expected_resp
790 def test_insert_new_same_coordinates(self, client, db):
791 response = client.post("/stationmeta/",
792 json={"stationmeta":
793 {"codes":["ttt3","ttt4"],
794 "name":"Test_China","coordinates":{"lat":36.256,"lng":117.106,"alt":1534.0},
795 "coordinate_validation_status": "NotChecked",
796 "country":"CN","state":"Shandong Sheng",
797 "type":"Unknown","type_of_area":"Unknown","timezone":"Asia/Shanghai",
798 "additional_metadata":"{}"},
799 },
800 headers={"email": "s.schroeder@fz-juelich.de"}
801 )
802 expected_status_code = 444
803 assert response.status_code == expected_status_code
804 expected_resp = {'detail': {'message': 'more than one station falls within the given radius!\nchoose which station record to patch (add station code)',
805 'station_ids': [1, 3],
806 'station_records': [[['China11'], 'lat=36.256 lng=117.106 alt=1534.0', 'Shandong Sheng', 84],
807 [['China_test8'], 'lat=36.256 lng=117.106 alt=1534.0', 'Shandong Sheng', 48]]}}
808 assert response.json() == expected_resp
811 def test_insert_duplicate(self, client, db):
812 response = client.post("/stationmeta/",
813 json={"stationmeta":
814 {"codes":["China11"],
815 "name":"Test_China","coordinates":{"lat":36.256,"lng":117.106,"alt":1534.0},
816 "coordinate_validation_status": "NotChecked",
817 "country":"CN","state":"Shandong Sheng",
818 "type":"Unknown","type_of_area":"Unknown","timezone":"Asia/Shanghai",
819 "additional_metadata":"{}"}
820 },
821 headers={"email": "s.schroeder@fz-juelich.de"}
822 )
823 expected_status_code = 443
824 assert response.status_code == expected_status_code
825 expected_resp = {'detail' : {'message': 'Station already registered!',
826 'station_id': 1}}
827 assert response.json() == expected_resp
830 # 3. tests updating station metadata
832 def test_patch_stationmeta_no_description(self, client, db):
833 response = client.patch("/stationmeta/-1",
834 json={"stationmeta":
835 {"name":"TTTT95TTTT"}
836 },
837 headers={"email": "s.schroeder@fz-juelich.de"}
838 )
839 expected_status_code = 404
840 assert response.status_code == expected_status_code
841 expected_resp = {"detail": "description text ist missing."}
842 assert response.json() == expected_resp
845 def test_patch_stationmeta_not_found1(self, client, db):
846 response = client.patch("/stationmeta/-1?description=changing station name",
847 json={"stationmeta":
848 {"name":"TTTT95TTTT"}
849 },
850 headers={"email": "s.schroeder@fz-juelich.de"}
851 )
852 expected_status_code = 404
853 assert response.status_code == expected_status_code
854 expected_resp = {"detail": "Station for patching not found."}
855 assert response.json() == expected_resp
858 def test_patch_stationmeta_name(self, client, db):
859 response = client.patch("/stationmeta/SDZ54421?description=changing station name",
860 json={"stationmeta":
861 {"name":"TTTT95TTTT"}
862 },
863 headers={"email": "s.schroeder@fz-juelich.de"}
864 )
865 expected_status_code = 200
866 assert response.status_code == expected_status_code
867 expected_resp = {'message': 'patched stationmeta record for station_id 2', 'station_id': 2}
868 response_json = response.json()
869 assert response_json == expected_resp
870 response = client.get(f"/stationmeta/id/{response_json['station_id']}")
871 response_json = response.json()
872 # just check special changes
873 assert response_json['name'] == 'TTTT95TTTT'
874 assert response_json['changelog'][1]['old_value'] == "{'name': 'Shangdianzi'}"
875 assert response_json['changelog'][1]['new_value'] == "{'name': 'TTTT95TTTT'}"
876 assert response_json['changelog'][1]['author_id'] == 1
877 assert response_json['changelog'][1]['type_of_change'] == 'single value correction in metadata'
880 def test_patch_single_stationmeta_global(self, client, db):
881 response = client.patch("/stationmeta/SDZ54421?description=changing global metadata",
882 json={"stationmeta":
883 {"globalmeta": {"climatic_zone_year2016": "WarmTemperateMoist"}}
884 },
885 headers={"email": "s.schroeder@fz-juelich.de"}
886 )
887 expected_status_code = 200
888 assert response.status_code == expected_status_code
889 expected_resp = {'message': 'patched stationmeta record for station_id 2', 'station_id': 2}
890 response_json = response.json()
891 assert response_json == expected_resp
892 response = client.get(f"/stationmeta/id/{response_json['station_id']}")
893 response_json = response.json()
894 # just check special changes
895 assert response_json['name'] == 'Shangdianzi'
896 assert response_json['changelog'][1]['old_value'] == "{'climatic_zone_year2016': 'WarmTemperateDry'}"
897 assert response_json['changelog'][1]['new_value'] == "{'climatic_zone_year2016': 'WarmTemperateMoist'}"
898 assert response_json['changelog'][1]['author_id'] == 1
899 assert response_json['changelog'][1]['type_of_change'] == 'single value correction in metadata'
902 def test_patch_multiple_stationmeta_global(self, client, db):
903 response = client.patch("/stationmeta/SDZ54421?description=changing global metadata",
904 json={"stationmeta":
905 {"globalmeta": {"climatic_zone_year2016": "WarmTemperateMoist",
906 "toar1_category": "RuralLowElevation",
907 "toar2_category": "Urban",
908 "htap_region_tier1_year2010": "HTAPTier1PAN",
909 "dominant_landcover_year2012": "TreeNeedleleavedEvergreenClosedToOpen",
910 "landcover_description_25km_year2012": "TreeNeedleleavedEvergreenClosedToOpen: 100 %",
911 "dominant_ecoregion_year2017": "Guianansavanna",
912 "ecoregion_description_25km_year2017": "Guianansavanna: 90 %, Miskitopineforests: 10 %"}}
913 },
914 headers={"email": "s.schroeder@fz-juelich.de"}
915 )
916 expected_status_code = 200
917 assert response.status_code == expected_status_code
918 expected_resp = {'message': 'patched stationmeta record for station_id 2', 'station_id': 2}
919 response_json = response.json()
920 assert response_json == expected_resp
921 response = client.get(f"/stationmeta/id/{response_json['station_id']}")
922 response_json = response.json()
923 # just check special changes
924 assert response_json['name'] == 'Shangdianzi'
925 assert response_json['changelog'][1]['old_value'] == (
926 "{'climatic_zone_year2016': 'WarmTemperateDry', "
927 "'htap_region_tier1_year2010': 'HTAPTier1MDE', "
928 "'dominant_landcover_year2012': 'CroplandRainfedHerbaceousCover', "
929 "'landcover_description_25km_year2012': '', "
930 "'dominant_ecoregion_year2017': 'Undefined', "
931 "'ecoregion_description_25km_year2017': '', "
932 "'toar1_category': 'Unclassified', "
933 "'toar2_category': 'Suburban'}" )
934 assert response_json['changelog'][1]['new_value'] == (
935 "{'climatic_zone_year2016': 'WarmTemperateMoist', "
936 "'htap_region_tier1_year2010': 'HTAPTier1PAN', "
937 "'dominant_landcover_year2012': 'TreeNeedleleavedEvergreenClosedToOpen', "
938 "'landcover_description_25km_year2012': 'TreeNeedleleavedEvergreenClosedToOpen: 100 %', "
939 "'dominant_ecoregion_year2017': 'Guianansavanna', "
940 "'ecoregion_description_25km_year2017': 'Guianansavanna: 90 %, Miskitopineforests: 10 %'"
941 ", 'toar1_category': 'RuralLowElevation'"
942 ", 'toar2_category': 'Urban'}" )
943 assert response_json['changelog'][1]['author_id'] == 1
944 assert response_json['changelog'][1]['type_of_change'] == 'single value correction in metadata'
947 def test_patch_stationmeta_roles_and_annotations(self, client, db):
948 response = client.patch("/stationmeta/SDZ54421?description=adding annotation text",
949 json={"stationmeta":
950 {"roles": [{"role": "PointOfContact", "contact_id": 3, "status": "Active"},
951 {"role": "Originator", "contact_id": 1, "status": "Active"}],
952 "annotations": [{"kind": "User",
953 "text": "some annotation text",
954 "date_added": "2025-02-10 17:00",
955 "approved": True,
956 "contributor_id":1}]
957 }
958 },
959 headers={"email": "s.schroeder@fz-juelich.de"}
960 )
961 expected_status_code = 200
962 assert response.status_code == expected_status_code
963 expected_resp = {'message': 'patched stationmeta record for station_id 2', 'station_id': 2}
964 response_json = response.json()
965 assert response_json == expected_resp
966 response = client.get(f"/stationmeta/id/{response_json['station_id']}")
967 response_json = response.json()
968 assert response_json['annotations'] == [{'id': 1, 'kind': 'user comment', 'text': 'some annotation text', 'date_added': '2025-02-10T17:00:00+00:00', 'approved': True, 'contributor_id': 1}]
969 assert response_json['changelog'][1]['old_value'] == "{'roles': {{'role': 'ResourceProvider', 'status': 'Active', 'contact_id': 5},}"
970 assert response_json['changelog'][1]['new_value'] == (
971 "{'roles': [{'role': 'PointOfContact', 'contact_id': 3, 'status': 'Active'}, "
972 "{'role': 'Originator', 'contact_id': 1, 'status': 'Active'}], "
973 "'annotations': [{'kind': 'User', 'text': 'some annotation text', 'date_added': '2025-02-10 17:00', 'approved': True, 'contributor_id': 1}]}" )
974 assert response_json['changelog'][1]['author_id'] == 1
975 assert response_json['changelog'][1]['type_of_change'] == 'comprehensive metadata revision'
978 def test_delete_roles_from_stationmeta(self, client, db):
979 response = client.patch("/stationmeta/delete_field/China11?field=roles",
980 headers={"email": "s.schroeder@fz-juelich.de"})
981 expected_status_code = 200
982 assert response.status_code == expected_status_code
983 # Database defaults cannot be patched within pytest
984 patched_response = response.json()
985 patched_response["changelog"][-1]["datetime"] = ""
986 expected_resp = {'id': 1,
987 'codes': ['China11'],
988 'name': 'Mount Tai',
989 'coordinates': {'lat': 36.256, 'lng': 117.106, 'alt': 1534.0},
990 'coordinate_validation_status': 'not checked',
991 'country': 'Germany',
992 'state': 'Shandong Sheng',
993 'type': 'unknown',
994 'type_of_area': 'unknown',
995 'timezone': 'Asia/Shanghai',
996 'additional_metadata': {'dummy_info': 'Here is some more information about the station'},
997 'aux_images': [],
998 'aux_docs': [],
999 'aux_urls': [],
1000 'changelog': [
1001 {
1002 'author_id': 1,
1003 'datetime': '2023-07-05T08:23:04.551645+00:00',
1004 'description': 'station created',
1005 'new_value': '',
1006 'old_value': '',
1007 'station_id': 1,
1008 'type_of_change': 'created',
1009 },
1010 {
1011 'author_id': 1,
1012 'datetime': '',
1013 'description': 'delete field roles',
1014 'new_value': "'roles': []",
1015 'old_value': "'roles': '[]'",
1016 'station_id': 1,
1017 'type_of_change': 'single value correction in metadata',
1018 },
1019 ],
1020 }
1021 assert patched_response == expected_resp
1024 def test_delete_field_station_not_found(self, client, db):
1025 response = client.patch("/stationmeta/delete_field/China22?field=timezone",
1026 headers={"email": "s.schroeder@fz-juelich.de"})
1027 expected_status_code = 404
1028 assert response.status_code == expected_status_code
1029 expected_resp = {'detail': 'Station for deleting field not found.'}
1030 assert response.json() == expected_resp
1033 def test_test(self, client, db):
1034 response = client.patch("/stationmeta/SDY54421?description=changing global metadata",
1035 json={"stationmeta":
1036 {"globalmeta": { "toar2_category": "Rural"}}
1037 },
1038 headers={"email": "s.schroeder@fz-juelich.de"}
1039 )
1040 expected_status_code = 404
1041 assert response.status_code == expected_status_code
1042 expected_resp = {'detail': 'Station for patching not found.'}
1043 assert response.json() == expected_resp