threedi-modelchecker 2.7.1.dev2__tar.gz → 2.7.3__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.
Files changed (44) hide show
  1. {threedi_modelchecker-2.7.1.dev2 → threedi_modelchecker-2.7.3}/CHANGES.rst +17 -1
  2. {threedi_modelchecker-2.7.1.dev2/threedi_modelchecker.egg-info → threedi_modelchecker-2.7.3}/PKG-INFO +2 -2
  3. {threedi_modelchecker-2.7.1.dev2 → threedi_modelchecker-2.7.3}/pyproject.toml +1 -1
  4. {threedi_modelchecker-2.7.1.dev2 → threedi_modelchecker-2.7.3}/threedi_modelchecker/__init__.py +1 -1
  5. {threedi_modelchecker-2.7.1.dev2 → threedi_modelchecker-2.7.3}/threedi_modelchecker/checks/cross_section_definitions.py +3 -0
  6. {threedi_modelchecker-2.7.1.dev2 → threedi_modelchecker-2.7.3}/threedi_modelchecker/checks/geo_query.py +1 -1
  7. {threedi_modelchecker-2.7.1.dev2 → threedi_modelchecker-2.7.3}/threedi_modelchecker/checks/other.py +34 -65
  8. {threedi_modelchecker-2.7.1.dev2 → threedi_modelchecker-2.7.3}/threedi_modelchecker/checks/raster.py +11 -15
  9. {threedi_modelchecker-2.7.1.dev2 → threedi_modelchecker-2.7.3}/threedi_modelchecker/config.py +490 -279
  10. {threedi_modelchecker-2.7.1.dev2 → threedi_modelchecker-2.7.3}/threedi_modelchecker/model_checks.py +0 -1
  11. threedi_modelchecker-2.7.3/threedi_modelchecker/tests/data/empty.gpkg +0 -0
  12. {threedi_modelchecker-2.7.1.dev2 → threedi_modelchecker-2.7.3}/threedi_modelchecker/tests/factories.py +34 -31
  13. {threedi_modelchecker-2.7.1.dev2 → threedi_modelchecker-2.7.3}/threedi_modelchecker/tests/test_checks_base.py +124 -43
  14. {threedi_modelchecker-2.7.1.dev2 → threedi_modelchecker-2.7.3}/threedi_modelchecker/tests/test_checks_factories.py +1 -1
  15. {threedi_modelchecker-2.7.1.dev2 → threedi_modelchecker-2.7.3}/threedi_modelchecker/tests/test_checks_other.py +14 -76
  16. {threedi_modelchecker-2.7.1.dev2 → threedi_modelchecker-2.7.3}/threedi_modelchecker/tests/test_checks_raster.py +54 -56
  17. {threedi_modelchecker-2.7.1.dev2 → threedi_modelchecker-2.7.3/threedi_modelchecker.egg-info}/PKG-INFO +2 -2
  18. {threedi_modelchecker-2.7.1.dev2 → threedi_modelchecker-2.7.3}/threedi_modelchecker.egg-info/requires.txt +1 -1
  19. threedi_modelchecker-2.7.1.dev2/threedi_modelchecker/tests/data/empty.gpkg +0 -0
  20. {threedi_modelchecker-2.7.1.dev2 → threedi_modelchecker-2.7.3}/LICENSE +0 -0
  21. {threedi_modelchecker-2.7.1.dev2 → threedi_modelchecker-2.7.3}/MANIFEST.in +0 -0
  22. {threedi_modelchecker-2.7.1.dev2 → threedi_modelchecker-2.7.3}/README.rst +0 -0
  23. {threedi_modelchecker-2.7.1.dev2 → threedi_modelchecker-2.7.3}/setup.cfg +0 -0
  24. {threedi_modelchecker-2.7.1.dev2 → threedi_modelchecker-2.7.3}/threedi_modelchecker/checks/__init__.py +0 -0
  25. {threedi_modelchecker-2.7.1.dev2 → threedi_modelchecker-2.7.3}/threedi_modelchecker/checks/base.py +0 -0
  26. {threedi_modelchecker-2.7.1.dev2 → threedi_modelchecker-2.7.3}/threedi_modelchecker/checks/factories.py +0 -0
  27. {threedi_modelchecker-2.7.1.dev2 → threedi_modelchecker-2.7.3}/threedi_modelchecker/checks/timeseries.py +0 -0
  28. {threedi_modelchecker-2.7.1.dev2 → threedi_modelchecker-2.7.3}/threedi_modelchecker/exporters.py +0 -0
  29. {threedi_modelchecker-2.7.1.dev2 → threedi_modelchecker-2.7.3}/threedi_modelchecker/interfaces/__init__.py +0 -0
  30. {threedi_modelchecker-2.7.1.dev2 → threedi_modelchecker-2.7.3}/threedi_modelchecker/interfaces/raster_interface.py +0 -0
  31. {threedi_modelchecker-2.7.1.dev2 → threedi_modelchecker-2.7.3}/threedi_modelchecker/interfaces/raster_interface_gdal.py +0 -0
  32. {threedi_modelchecker-2.7.1.dev2 → threedi_modelchecker-2.7.3}/threedi_modelchecker/interfaces/raster_interface_rasterio.py +0 -0
  33. {threedi_modelchecker-2.7.1.dev2 → threedi_modelchecker-2.7.3}/threedi_modelchecker/scripts.py +0 -0
  34. {threedi_modelchecker-2.7.1.dev2 → threedi_modelchecker-2.7.3}/threedi_modelchecker/tests/__init__.py +0 -0
  35. {threedi_modelchecker-2.7.1.dev2 → threedi_modelchecker-2.7.3}/threedi_modelchecker/tests/conftest.py +0 -0
  36. {threedi_modelchecker-2.7.1.dev2 → threedi_modelchecker-2.7.3}/threedi_modelchecker/tests/data/__init__.py +0 -0
  37. {threedi_modelchecker-2.7.1.dev2 → threedi_modelchecker-2.7.3}/threedi_modelchecker/tests/test_checks_cross_section_definitions.py +0 -0
  38. {threedi_modelchecker-2.7.1.dev2 → threedi_modelchecker-2.7.3}/threedi_modelchecker/tests/test_checks_timeseries.py +0 -0
  39. {threedi_modelchecker-2.7.1.dev2 → threedi_modelchecker-2.7.3}/threedi_modelchecker/tests/test_exporters.py +0 -0
  40. {threedi_modelchecker-2.7.1.dev2 → threedi_modelchecker-2.7.3}/threedi_modelchecker/tests/test_model_checks.py +0 -0
  41. {threedi_modelchecker-2.7.1.dev2 → threedi_modelchecker-2.7.3}/threedi_modelchecker.egg-info/SOURCES.txt +0 -0
  42. {threedi_modelchecker-2.7.1.dev2 → threedi_modelchecker-2.7.3}/threedi_modelchecker.egg-info/dependency_links.txt +0 -0
  43. {threedi_modelchecker-2.7.1.dev2 → threedi_modelchecker-2.7.3}/threedi_modelchecker.egg-info/entry_points.txt +0 -0
  44. {threedi_modelchecker-2.7.1.dev2 → threedi_modelchecker-2.7.3}/threedi_modelchecker.egg-info/top_level.txt +0 -0
@@ -2,15 +2,31 @@ Changelog of threedi-modelchecker
2
2
  =================================
3
3
 
4
4
 
5
- 2.7.1 (unreleased)
5
+ 2.7.3 (2024-05-22)
6
6
  ------------------
7
+
8
+ - Expand description of check 188.
9
+ - Add missing spaces in error message for check 185.
10
+
11
+
12
+ 2.7.2 (2024-04-23)
13
+ ------------------
14
+
15
+ - Fix bug with check 183
16
+
17
+
18
+ 2.7.1 (2024-04-22)
19
+ ------------------
20
+
7
21
  - Add info check 1406 to inform the user if a raster is not compressed.
8
22
  - Add check 799 to warn if raster friction pixels are < 1 while Chezy friction is selected
9
23
  - Change error message for check 1500
24
+ - Fix check 183 which failed in the QGIS plugin
10
25
 
11
26
 
12
27
  2.7.0 (2024-03-12)
13
28
  ------------------
29
+
14
30
  - Support geopackage
15
31
  - Support changes in threedi-schema (0.220) needed for geopackage support
16
32
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: threedi-modelchecker
3
- Version: 2.7.1.dev2
3
+ Version: 2.7.3
4
4
  Summary: Checks validity of a 3Di schematisation
5
5
  Author-email: Nelen & Schuurmans <info@nelen-schuurmans.nl>
6
6
  License: MIT
@@ -15,7 +15,7 @@ License-File: LICENSE
15
15
  Requires-Dist: Click
16
16
  Requires-Dist: GeoAlchemy2!=0.11.*,>=0.9
17
17
  Requires-Dist: SQLAlchemy>=1.4
18
- Requires-Dist: threedi-schema==0.222.dev2
18
+ Requires-Dist: threedi-schema==0.220.*
19
19
  Provides-Extra: rasterio
20
20
  Requires-Dist: rasterio>=1.3; extra == "rasterio"
21
21
  Provides-Extra: test
@@ -19,7 +19,7 @@ dependencies = [
19
19
  "Click",
20
20
  "GeoAlchemy2>=0.9,!=0.11.*",
21
21
  "SQLAlchemy>=1.4",
22
- "threedi-schema==0.222.dev2"
22
+ "threedi-schema==0.220.*"
23
23
  ]
24
24
 
25
25
  [project.optional-dependencies]
@@ -1,5 +1,5 @@
1
1
  from .model_checks import * # NOQA
2
2
 
3
3
  # fmt: off
4
- __version__ = '2.7.1.dev2'
4
+ __version__ = '2.7.3'
5
5
  # fmt: on
@@ -626,6 +626,9 @@ class CrossSectionVariableFrictionRangeCheck(CrossSectionVariableRangeCheck):
626
626
  invalids.append(record)
627
627
  return invalids
628
628
 
629
+ def description(self):
630
+ return f"some values in {self.column_name} are {self.range_str}, which is not allowed for friction type(s) {self.friction_types}"
631
+
629
632
 
630
633
  class OpenIncreasingCrossSectionVariableCheck(CrossSectionBaseCheck):
631
634
  """
@@ -8,7 +8,7 @@ DEFAULT_EPSG = 28992
8
8
 
9
9
 
10
10
  def epsg_code_query():
11
- epsg_code = Query(models.ModelSettings.epsg_code).limit(1).scalar_subquery()
11
+ epsg_code = Query(models.GlobalSetting.epsg_code).limit(1).scalar_subquery()
12
12
  return func.coalesce(epsg_code, literal(DEFAULT_EPSG)).label("epsg_code")
13
13
 
14
14
 
@@ -10,6 +10,15 @@ from .base import BaseCheck, CheckLevel
10
10
  from .cross_section_definitions import cross_section_configuration
11
11
  from .geo_query import distance, length, transform
12
12
 
13
+ # Use these to make checks only work on the first global settings entry:
14
+ first_setting = (
15
+ Query(models.GlobalSetting.id)
16
+ .order_by(models.GlobalSetting.id)
17
+ .limit(1)
18
+ .scalar_subquery()
19
+ )
20
+ first_setting_filter = models.GlobalSetting.id == first_setting
21
+
13
22
 
14
23
  class CorrectAggregationSettingsExist(BaseCheck):
15
24
  """Check if aggregation settings are correctly filled with aggregation_method and flow_variable as required"""
@@ -21,24 +30,30 @@ class CorrectAggregationSettingsExist(BaseCheck):
21
30
  *args,
22
31
  **kwargs,
23
32
  ):
24
- super().__init__(column=models.ModelSettings.id, *args, **kwargs)
33
+ super().__init__(column=models.GlobalSetting.id, *args, **kwargs)
25
34
  self.aggregation_method = aggregation_method.value
26
35
  self.flow_variable = flow_variable.value
27
36
 
28
37
  def get_invalid(self, session: Session) -> List[NamedTuple]:
29
- global_settings = self.to_check(session)
38
+ global_settings = self.to_check(session).filter(first_setting_filter)
30
39
  correctly_defined = session.execute(
31
- select(models.AggregationSettings).filter(
40
+ select(models.AggregationSettings)
41
+ .filter(
32
42
  models.AggregationSettings.aggregation_method
33
43
  == self.aggregation_method,
34
44
  models.AggregationSettings.flow_variable == self.flow_variable,
35
45
  )
46
+ .filter(
47
+ models.AggregationSettings.global_settings_id
48
+ == global_settings.subquery().c.id
49
+ )
36
50
  ).all()
51
+
37
52
  return global_settings.all() if len(correctly_defined) == 0 else []
38
53
 
39
54
  def description(self) -> str:
40
55
  return (
41
- "To use the water balance tool, aggregation_settings should have a row where "
56
+ "To use the water balance tool, v2_aggregation_settings should have a row where "
42
57
  f"aggregation_method is {self.aggregation_method} and flow_variable is {self.flow_variable}."
43
58
  )
44
59
 
@@ -229,14 +244,12 @@ class Use0DFlowCheck(BaseCheck):
229
244
  """
230
245
 
231
246
  def __init__(self, *args, **kwargs):
232
- super().__init__(
233
- column=models.SimulationTemplateSettings.use_0d_inflow, *args, **kwargs
234
- )
247
+ super().__init__(column=models.GlobalSetting.use_0d_inflow, *args, **kwargs)
235
248
 
236
249
  def to_check(self, session):
237
250
  """Return a Query object on which this check is applied"""
238
- return session.query(models.SimulationTemplateSettings).filter(
239
- models.SimulationTemplateSettings.use_0d_inflow != 0
251
+ return session.query(models.GlobalSetting).filter(
252
+ models.GlobalSetting.use_0d_inflow != 0
240
253
  )
241
254
 
242
255
  def get_invalid(self, session):
@@ -288,7 +301,7 @@ class ConnectionNodes(BaseCheck):
288
301
  class ConnectionNodesLength(BaseCheck):
289
302
  """Check that the distance between `start_node` and `end_node` is at least
290
303
  `min_distance`. The coords will be transformed into (the first entry) of
291
- ModelSettings.epsg_code.
304
+ GlobalSettings.epsg_code.
292
305
  """
293
306
 
294
307
  def __init__(
@@ -456,7 +469,7 @@ class ChannelManholeLevelCheck(BaseCheck):
456
469
 
457
470
  class OpenChannelsWithNestedNewton(BaseCheck):
458
471
  """Checks whether the model has any closed cross-section in use when the
459
- NumericalSettings.use_nested_newton is turned off.
472
+ NumericalSettings.use_of_nested_newton is turned off.
460
473
 
461
474
  See https://github.com/nens/threeditoolbox/issues/522
462
475
  """
@@ -466,7 +479,7 @@ class OpenChannelsWithNestedNewton(BaseCheck):
466
479
  column=models.CrossSectionDefinition.id,
467
480
  level=level,
468
481
  filters=Query(models.NumericalSettings)
469
- .filter(models.NumericalSettings.use_nested_newton == 0)
482
+ .filter(models.NumericalSettings.use_of_nested_newton == 0)
470
483
  .exists(),
471
484
  *args,
472
485
  **kwargs,
@@ -519,8 +532,8 @@ class OpenChannelsWithNestedNewton(BaseCheck):
519
532
  def description(self) -> str:
520
533
  return (
521
534
  f"{self.column_name} has a closed cross section definition while "
522
- f"NumericalSettings.use_nested_newton is switched off. "
523
- f"This gives convergence issues. We recommend setting use_nested_newton = 1."
535
+ f"NumericalSettings.use_of_nested_newton is switched off. "
536
+ f"This gives convergence issues. We recommend setting use_of_nested_newton = 1."
524
537
  )
525
538
 
526
539
 
@@ -718,8 +731,7 @@ class PumpStorageTimestepCheck(BaseCheck):
718
731
  .filter(
719
732
  (models.ConnectionNode.storage_area != None)
720
733
  & (
721
- (
722
- # calculate how many seconds the pumpstation takes to empty its storage: (storage * height)/pump capacity
734
+ ( # calculate how many seconds the pumpstation takes to empty its storage: (storage * height)/pump capacity
723
735
  (
724
736
  # Arithmetic operations on None return None, so without this
725
737
  # conditional type cast, no invalid results would be returned
@@ -732,7 +744,9 @@ class PumpStorageTimestepCheck(BaseCheck):
732
744
  )
733
745
  )
734
746
  / (models.Pumpstation.capacity / 1000)
735
- < Query(models.TimeStepSettings.time_step).scalar_subquery()
747
+ < Query(models.GlobalSetting.sim_time_step)
748
+ .filter(first_setting_filter)
749
+ .scalar_subquery()
736
750
  )
737
751
  )
738
752
  .all()
@@ -805,7 +819,7 @@ class InflowNoFeaturesCheck(BaseCheck):
805
819
  """Check that the surface table in the global use_0d_inflow setting contains at least 1 feature."""
806
820
 
807
821
  def __init__(self, *args, surface_table, condition=True, **kwargs):
808
- super().__init__(*args, column=models.ModelSettings.id, **kwargs)
822
+ super().__init__(*args, column=models.GlobalSetting.id, **kwargs)
809
823
  self.surface_table = surface_table
810
824
  self.condition = condition
811
825
 
@@ -814,13 +828,13 @@ class InflowNoFeaturesCheck(BaseCheck):
814
828
  select(func.count(self.surface_table.id))
815
829
  ).scalar()
816
830
  return (
817
- session.query(models.ModelSettings)
831
+ session.query(models.GlobalSetting)
818
832
  .filter(self.condition, surface_table_length == 0)
819
833
  .all()
820
834
  )
821
835
 
822
836
  def description(self) -> str:
823
- return f"model_settings.use_0d_inflow is set to use {self.surface_table.__tablename__}, but {self.surface_table.__tablename__} does not contain any features."
837
+ return f"v2_global_settings.use_0d_inflow is set to use {self.surface_table.__tablename__}, but {self.surface_table.__tablename__} does not contain any features."
824
838
 
825
839
 
826
840
  class NodeSurfaceConnectionsCheck(BaseCheck):
@@ -1068,48 +1082,3 @@ class AllPresentVariableVegetationParameters(AllPresent):
1068
1082
  )
1069
1083
  )
1070
1084
  )
1071
-
1072
-
1073
- class UsedSettingsPresentCheck(BaseCheck):
1074
- def __init__(
1075
- self,
1076
- column,
1077
- settings_table,
1078
- filters=None,
1079
- level=CheckLevel.ERROR,
1080
- error_code=0,
1081
- ):
1082
- super().__init__(column, filters, level, error_code)
1083
- self.settings_table = settings_table
1084
-
1085
- def get_invalid(self, session: Session) -> List[NamedTuple]:
1086
- # more than 1 row should be caught by another check
1087
- all_results = self.to_check(session).filter(self.column == True).all()
1088
- use_cols = len(all_results) > 0
1089
- if use_cols and session.query(self.settings_table).count() == 0:
1090
- return all_results
1091
- return []
1092
-
1093
- def description(self) -> str:
1094
- return f"{self.column_name} in {self.table.name} is set to True but {self.settings_table.__tablename__} is empty"
1095
-
1096
-
1097
- class MaxOneRecordCheck(BaseCheck):
1098
- def __init__(self, column, filters=None, level=CheckLevel.ERROR, error_code=0):
1099
- super().__init__(column, filters, level, error_code)
1100
- self.observed_length = 0
1101
-
1102
- def get_invalid(self, session: Session) -> List[NamedTuple]:
1103
- # return mock list in case the table is empty when it shouldn't be
1104
- all_results = self.to_check(session).all()
1105
- self.observed_length = len(all_results)
1106
- if self.observed_length > 1:
1107
- return all_results if self.observed_length > 0 else ["foo"]
1108
- else:
1109
- return []
1110
-
1111
- def description(self) -> str:
1112
- return (
1113
- f"{self.table.name} has {self.observed_length} rows, "
1114
- f"but should have at most 1 row."
1115
- )
@@ -1,7 +1,7 @@
1
1
  from dataclasses import dataclass
2
2
  from math import isclose
3
3
  from pathlib import Path
4
- from typing import Dict, Optional, Type
4
+ from typing import Optional, Set, Type
5
5
 
6
6
  from threedi_schema import models
7
7
 
@@ -22,7 +22,7 @@ class Context:
22
22
 
23
23
  @dataclass
24
24
  class ServerContext(Context):
25
- available_rasters: Dict[str, str]
25
+ available_rasters: Set[str]
26
26
  raster_interface: Type[RasterInterface] = GDALRasterInterface
27
27
 
28
28
 
@@ -68,9 +68,7 @@ class BaseRasterCheck(BaseCheck):
68
68
  ]
69
69
 
70
70
  def get_path_local(self, record, context: LocalContext) -> Optional[str]:
71
- abs_path = context.base_path.joinpath(
72
- "rasters", getattr(record, self.column.name)
73
- )
71
+ abs_path = context.base_path / getattr(record, self.column.name)
74
72
  if abs_path.exists():
75
73
  return str(abs_path)
76
74
 
@@ -161,7 +159,7 @@ class RasterHasMatchingEPSGCheck(BaseRasterCheck):
161
159
  """Check whether a raster's EPSG code matches the EPSG code in the global settings for the SQLite."""
162
160
 
163
161
  def get_invalid(self, session):
164
- epsg_code_query = session.query(models.ModelSettings.epsg_code).first()
162
+ epsg_code_query = session.query(models.GlobalSetting.epsg_code).first()
165
163
  if epsg_code_query is not None:
166
164
  self.epsg_code = epsg_code_query[0]
167
165
  else:
@@ -177,7 +175,7 @@ class RasterHasMatchingEPSGCheck(BaseRasterCheck):
177
175
  return raster.epsg_code == self.epsg_code
178
176
 
179
177
  def description(self):
180
- return f"The file in {self.column_name} has no EPSG code or the EPSG code does not match does not match model_settings.epsg_code"
178
+ return f"The file in {self.column_name} has no EPSG code or the EPSG code does not match does not match v2_global_settings.epsg_code"
181
179
 
182
180
 
183
181
  class RasterSquareCellsCheck(BaseRasterCheck):
@@ -204,11 +202,9 @@ class RasterGridSizeCheck(BaseRasterCheck):
204
202
  """Check whether the global settings' grid size is an even multiple of a raster's cell size (at least 2x)."""
205
203
 
206
204
  def get_invalid(self, session):
207
- minimum_cell_size_query = session.query(
208
- models.ModelSettings.minimum_cell_size
209
- ).first()
210
- if minimum_cell_size_query is not None:
211
- self.minimum_cell_size = minimum_cell_size_query[0]
205
+ grid_space_query = session.query(models.GlobalSetting.grid_space).first()
206
+ if grid_space_query is not None:
207
+ self.grid_space = grid_space_query[0]
212
208
  else:
213
209
  return []
214
210
  return super().get_invalid(session)
@@ -221,17 +217,17 @@ class RasterGridSizeCheck(BaseRasterCheck):
221
217
  try:
222
218
  return (
223
219
  isclose(
224
- a=((self.minimum_cell_size / raster.pixel_size[0]) % 2),
220
+ a=((self.grid_space / raster.pixel_size[0]) % 2),
225
221
  b=0,
226
222
  rel_tol=1e-09,
227
223
  )
228
- ) and (self.minimum_cell_size >= (2 * raster.pixel_size[0]))
224
+ ) and (self.grid_space >= (2 * raster.pixel_size[0]))
229
225
  # if one of the fields is a NoneType it will be caught elsewhere
230
226
  except TypeError:
231
227
  return True
232
228
 
233
229
  def description(self):
234
- return "model_settings.minimum_cell_size is not a positive even multiple of the raster cell size."
230
+ return "v2_global_settings.grid_space is not a positive even multiple of the raster cell size."
235
231
 
236
232
 
237
233
  class RasterPixelCountCheck(BaseRasterCheck):