Skip to content

Commit 04c17d1

Browse files
committed
Suppress more errors
* Catch NullGeometries in validation * Validate timeseries and inhom properties group by group * Validate increasing time for timeseries entries * Validate inhom group for number of layers; add more name info to errors * Do not error if domain button is clicked with empty dataset * Add ConditionallyRequired for ttim Well caisson radius
1 parent 6b2ccbf commit 04c17d1

15 files changed

+100
-40
lines changed

plugin/qgistim/core/elements/building_pit.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,11 @@
99
AllGreaterEqual,
1010
AllRequired,
1111
AtleastOneTrue,
12+
Equals,
1213
Membership,
1314
OffsetAllRequired,
1415
OptionalFirstOnly,
1516
Positive,
16-
Range,
1717
Required,
1818
SemiConfined,
1919
StrictlyDecreasing,
@@ -33,7 +33,7 @@ class BuildingPitSchema(RowWiseSchema):
3333
class AssociatedBuildingPitSchema(TableSchema):
3434
timml_schemata = {
3535
"inhomogeneity_id": AllRequired(),
36-
"layer": AllRequired(Range()),
36+
"layer": AllRequired(Equals("aquifer layers")),
3737
"aquifer_top": AllRequired(StrictlyDecreasing()),
3838
"aquifer_bottom": AllRequired(StrictlyDecreasing()),
3939
"aquitard_c": OffsetAllRequired(StrictlyPositive()),

plugin/qgistim/core/elements/circular_area_sink.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
Optional,
1515
Positive,
1616
Required,
17+
StrictlyIncreasing,
1718
)
1819

1920

@@ -34,7 +35,7 @@ class CircularAreaSinkSchema(RowWiseSchema):
3435
)
3536
timeseries_schemata = {
3637
"timeseries_id": AllRequired(),
37-
"time_start": AllRequired(Positive()),
38+
"time_start": AllRequired(Positive(), StrictlyIncreasing()),
3839
"rate": AllRequired(),
3940
}
4041

plugin/qgistim/core/elements/element.py

Lines changed: 33 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -449,18 +449,8 @@ def to_ttim(self, other):
449449
other = other.copy() # avoid side-effects
450450
timeseries = self.table_to_dict(self.ttim_layer)
451451
if timeseries:
452-
errors = self.schema.validate_timeseries(
453-
name=self.ttim_layer.name(), data=timeseries
454-
)
455-
if errors:
456-
return ElementExtraction(errors=errors)
457-
458-
grouped = self.groupby(timeseries, "timeseries_id")
459-
# Store timeseries in other dict for validation.
460452
other["ttim timeseries IDs"] = set(timeseries["timeseries_id"])
461-
462453
else:
463-
grouped = {}
464454
other["ttim timeseries IDs"] = {None}
465455

466456
data = self.table_to_records(self.timml_layer)
@@ -470,6 +460,20 @@ def to_ttim(self, other):
470460
if errors:
471461
return ElementExtraction(errors=errors)
472462

463+
if timeseries:
464+
grouped = self.groupby(timeseries, "timeseries_id")
465+
errors = {}
466+
for timeseries_id, group in grouped.items():
467+
_errors = self.schema.validate_timeseries(
468+
name=f"Timeseries, timeseries id {timeseries_id}", data=group
469+
)
470+
errors.update(_errors)
471+
472+
if errors:
473+
return ElementExtraction(errors=errors)
474+
else:
475+
grouped = {}
476+
473477
elements = []
474478
times = set()
475479
for row in data:
@@ -526,15 +530,14 @@ def to_timml(self, other) -> ElementExtraction:
526530
return ElementExtraction(errors=missing)
527531

528532
properties = self.table_to_dict(self.assoc_layer)
529-
errors = self.assoc_schema.validate_timml(
530-
name=self.assoc_layer.name(),
531-
data=properties,
532-
)
533-
if errors:
534-
return ElementExtraction(errors=errors)
535-
536533
other = other.copy() # Avoid side-effects
537-
other["properties inhomogeneity_id"] = list(set(properties["inhomogeneity_id"]))
534+
if properties:
535+
other["properties inhomogeneity_id"] = list(
536+
set(properties["inhomogeneity_id"])
537+
)
538+
else:
539+
other["properties inhomogeneity_id"] = [None]
540+
538541
data = self.table_to_records(self.timml_layer)
539542
errors = self.schema.validate_timml(
540543
name=self.timml_layer.name(),
@@ -545,6 +548,18 @@ def to_timml(self, other) -> ElementExtraction:
545548
return ElementExtraction(errors=errors)
546549

547550
grouped = self.groupby(properties, "inhomogeneity_id")
551+
errors = {}
552+
for inhom_id, group in grouped.items():
553+
_errors = self.assoc_schema.validate_timml(
554+
name=f"Properties, inhomogeneity_id {inhom_id}",
555+
data=group,
556+
other=other,
557+
)
558+
errors.update(_errors)
559+
560+
if errors:
561+
return ElementExtraction(errors=errors)
562+
548563
elements = [self.process_timml_row(row=row, grouped=grouped) for row in data]
549564
return ElementExtraction(data=elements)
550565

plugin/qgistim/core/elements/head_line_sink.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
Optional,
1212
Positive,
1313
Required,
14+
StrictlyIncreasing,
1415
StrictlyPositive,
1516
)
1617

@@ -35,7 +36,7 @@ class HeadLineSinkSchema(RowWiseSchema):
3536
}
3637
timeseries_schemata = {
3738
"timeseries_id": AllRequired(),
38-
"time_start": AllRequired(Positive()),
39+
"time_start": AllRequired(Positive(), StrictlyIncreasing()),
3940
"head": AllRequired(),
4041
}
4142

plugin/qgistim/core/elements/headwell.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
Optional,
2121
Positive,
2222
Required,
23+
StrictlyIncreasing,
2324
StrictlyPositive,
2425
)
2526

@@ -43,7 +44,7 @@ class HeadWellSchema(RowWiseSchema):
4344
)
4445
timeseries_schemata = {
4546
"timeseries_id": AllRequired(),
46-
"time_start": AllRequired(Positive()),
47+
"time_start": AllRequired(Positive(), StrictlyIncreasing()),
4748
"head": AllRequired(),
4849
}
4950

plugin/qgistim/core/elements/leaky_building_pit.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,11 @@
99
AllGreaterEqual,
1010
AllRequired,
1111
AtleastOneTrue,
12+
Equals,
1213
Membership,
1314
OffsetAllRequired,
1415
OptionalFirstOnly,
1516
Positive,
16-
Range,
1717
Required,
1818
RequiredFirstOnly,
1919
SemiConfined,
@@ -34,7 +34,7 @@ class LeakyBuildingPitSchema(RowWiseSchema):
3434
class AssociatedLeakyBuildingPitchema(TableSchema):
3535
timml_schemata = {
3636
"inhomogeneity_id": AllRequired(),
37-
"layer": AllRequired(Range()),
37+
"layer": AllRequired(Equals("aquifer layers")),
3838
"aquifer_top": AllRequired(StrictlyDecreasing()),
3939
"aquifer_bottom": AllRequired(StrictlyDecreasing()),
4040
"aquitard_c": OffsetAllRequired(StrictlyPositive()),

plugin/qgistim/core/elements/line_sink_ditch.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
Optional,
1212
Positive,
1313
Required,
14+
StrictlyIncreasing,
1415
StrictlyPositive,
1516
)
1617

@@ -35,7 +36,7 @@ class LineSinkDitchSchema(RowWiseSchema):
3536
}
3637
timeseries_schemata = {
3738
"timeseries_id": AllRequired(),
38-
"time_start": AllRequired(Positive()),
39+
"time_start": AllRequired(Positive(), StrictlyIncreasing()),
3940
"discharge": AllRequired(),
4041
}
4142

plugin/qgistim/core/elements/observation.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,23 @@
33
from qgistim.core.elements.colors import LIGHT_BLUE
44
from qgistim.core.elements.element import TransientElement
55
from qgistim.core.elements.schemata import RowWiseSchema
6-
from qgistim.core.schemata import AllRequired, Positive, Required
6+
from qgistim.core.schemata import (
7+
AllRequired,
8+
Membership,
9+
Positive,
10+
Required,
11+
StrictlyIncreasing,
12+
)
713

814

915
class HeadObservationSchema(RowWiseSchema):
1016
timml_schemata = {
1117
"geometry": Required(),
1218
}
19+
ttim_schemata = {"timeseries_id": Required(Membership("ttim timeseries IDs"))}
1320
timeseries_schemata = {
1421
"timeseries_id": AllRequired(),
15-
"time": AllRequired(Positive()),
22+
"time": AllRequired(Positive(), StrictlyIncreasing()),
1623
}
1724

1825

plugin/qgistim/core/elements/polygon_inhomogeneity.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,15 @@
88
from qgistim.core.schemata import (
99
AllGreaterEqual,
1010
AllRequired,
11+
Equals,
1112
Membership,
1213
OffsetAllRequired,
1314
OptionalFirstOnly,
1415
Positive,
15-
Range,
1616
Required,
1717
SemiConfined,
1818
StrictlyDecreasing,
19+
StrictlyPositive,
1920
)
2021

2122

@@ -31,11 +32,11 @@ class PolygonInhomogeneitySchema(RowWiseSchema):
3132
class AssociatedPolygonInhomogeneitySchema(TableSchema):
3233
timml_schemata = {
3334
"inhomogeneity_id": AllRequired(),
34-
"layer": AllRequired(Range()),
35+
"layer": AllRequired(Equals("aquifer layers")),
3536
"aquifer_top": AllRequired(StrictlyDecreasing()),
3637
"aquifer_bottom": AllRequired(StrictlyDecreasing()),
37-
"aquitard_c": OffsetAllRequired(Positive()),
38-
"aquifer_k": AllRequired(Positive()),
38+
"aquitard_c": OffsetAllRequired(StrictlyPositive()),
39+
"aquifer_k": AllRequired(StrictlyPositive()),
3940
"semiconf_top": OptionalFirstOnly(),
4041
"semiconf_head": OptionalFirstOnly(),
4142
"rate": OptionalFirstOnly(),

plugin/qgistim/core/elements/schemata.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,15 +31,15 @@ def _validate_table(vd: ValidationData) -> Dict[str, List]:
3131
for variable, schema in vd.schemata.items():
3232
_errors = schema.validate(vd.data[variable], vd.other)
3333
if _errors:
34-
errors[variable].extend(_errors)
34+
errors[f"{vd.name} {variable}"].extend(_errors)
3535

3636
# The consistency schema rely on the row input being valid.
3737
# Hence, they are tested second.
3838
if not errors:
3939
for schema in vd.consistency_schemata:
4040
_error = schema.validate(vd.data, vd.other)
4141
if _error:
42-
errors["Table:"].append(_error)
42+
errors[vd.name].append(_error)
4343

4444
return errors
4545

plugin/qgistim/core/elements/well.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
from qgistim.core.schemata import (
99
AllOrNone,
1010
AllRequired,
11+
ConditionallyRequired,
1112
Membership,
1213
NotBoth,
1314
Optional,
@@ -25,8 +26,9 @@ class WellSchema(RowWiseSchema):
2526
"resistance": Required(Positive()),
2627
"layer": Required(Membership("aquifer layers")),
2728
}
29+
timml_consistency_schemata = (ConditionallyRequired("slug", "caisson_radius"),)
2830
ttim_schemata = {
29-
"caisson_radius": Required(StrictlyPositive),
31+
"caisson_radius": Optional(StrictlyPositive()),
3032
"slug": Required(),
3133
"time_start": Optional(Positive()),
3234
"time_end": Optional(Positive()),
@@ -67,7 +69,6 @@ class Well(TransientElement):
6769
timml_defaults = {
6870
"radius": QgsDefaultValue("0.1"),
6971
"resistance": QgsDefaultValue("0.0"),
70-
"caisson_radius": QgsDefaultValue("0.1"),
7172
"slug": QgsDefaultValue("False"),
7273
}
7374
transient_columns = (

plugin/qgistim/core/extractor.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ def extract_coordinates(geometry):
2626
coordinates.append((vertex.x(), vertex.y()))
2727
centroid = geometry.centroid().asPoint()
2828
return (centroid.x(), centroid.y()), coordinates
29-
29+
3030
@classmethod
3131
def table_to_records(cls, layer: QgsVectorLayer) -> List[Dict[str, Any]]:
3232
geomtype = layer.geometryType()

plugin/qgistim/core/schemata.py

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import abc
22
import operator
3-
from typing import List, Sequence, Union
3+
from typing import Any, Dict, List, Sequence, Union
44

55
OPERATORS = {
66
"<": operator.lt,
@@ -255,6 +255,20 @@ def validate(self, data, _=None) -> MaybeError:
255255
return None
256256

257257

258+
class Equals(IterableSchema):
259+
def __init__(self, other: str):
260+
self.other = other
261+
262+
def validate(self, data, other: Dict[str, Any]) -> MaybeError:
263+
other_data = other[self.other]
264+
if data != other_data:
265+
return (
266+
f"Values are not equal to values of {self.other}: "
267+
f"{data} versus {other_data}"
268+
)
269+
return None
270+
271+
258272
class Increasing(IterableSchema):
259273
def validate(self, data, _=None) -> MaybeError:
260274
monotonic = all(a <= b for a, b in zip(data, data[1:]))
@@ -311,6 +325,18 @@ def validate(self, data, _=None) -> MaybeError:
311325
# Consistency schemata
312326

313327

328+
class ConditionallyRequired(ConsistencySchema):
329+
def __init__(self, a: str, b: str):
330+
self.a = a
331+
self.b = b
332+
333+
def validate(self, data, _=None) -> MaybeError:
334+
print(data)
335+
if data[self.a] and data[self.b] is None:
336+
return f"If {self.a} is True, {self.b} is required."
337+
return None
338+
339+
314340
class SemiConfined(ConsistencySchema):
315341
def validate(self, data, _=None) -> MaybeError:
316342
semiconf_data = {

plugin/qgistim/widgets/compute_widget.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -382,6 +382,9 @@ def domain(self) -> None:
382382
Write the current viewing extent as rectangle to the GeoPackage.
383383
"""
384384
item = self.parent.domain_item()
385+
# Return in case no dataset has been set yet.
386+
if item is None:
387+
return
385388
ymax, ymin = item.element.update_extent(self.parent.iface)
386389
self.set_spacing_from_domain(ymax, ymin)
387390
self.parent.iface.mapCanvas().refreshAllLayers()

plugin/qgistim/widgets/dataset_widget.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -253,7 +253,7 @@ def extract_data(self, transient: bool) -> Tuple[Dict[str, Any], Dict[str, Any]]
253253
raise e
254254

255255
if transient:
256-
if times:
256+
if times and (times != {0}):
257257
data["timml Aquifer:Aquifer"]["tmax"] = max(times)
258258
else:
259259
errors["Model"] = {"TTim input:": ["No transient forcing defined."]}
@@ -521,8 +521,11 @@ def active_elements(self):
521521
return active_elements
522522

523523
def domain_item(self):
524+
items = self.dataset_tree.items()
525+
if len(items) == 0:
526+
return
524527
# Find domain entry
525-
for item in self.dataset_tree.items():
528+
for item in items:
526529
if isinstance(item.element, Domain):
527530
return item
528531
else:

0 commit comments

Comments
 (0)