• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

nens / ThreeDiToolbox / #2589

19 Sep 2025 08:50AM UTC coverage: 35.01% (-0.1%) from 35.146%
#2589

push

coveralls-python

web-flow
Merge 38792c162 into f6f4be1e7

62 of 260 new or added lines in 40 files covered. (23.85%)

6 existing lines in 5 files now uncovered.

4859 of 13879 relevant lines covered (35.01%)

0.35 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

22.67
/tool_fraction_analysis/fraction_model.py
1
from qgis.PyQt.QtCore import Qt
1✔
2
from qgis.PyQt.QtGui import QColor
1✔
3
from qgis.PyQt.QtGui import QStandardItem
1✔
4
from qgis.PyQt.QtGui import QStandardItemModel
1✔
5
from threedi_results_analysis.threedi_plugin_model import ThreeDiResultItem
1✔
6

7
import logging
1✔
8
import numpy as np
1✔
9
import pyqtgraph as pg
1✔
10

11

12
logger = logging.getLogger(__name__)
1✔
13

14
FRACTION_COLOR_LIST = [
1✔
15
    (135, 86, 146),  # paars
16
    (243, 132, 0),  # oranje
17
    (190, 0, 50),  # rood
18
    (194, 178, 128),  # beige
19
    (0, 136, 86),  # groen
20
    (161, 202, 241),  # lichtblauw
21
    (230, 143, 172),
22
    (0, 103, 165),
23
    (249, 147, 121),
24
    (96, 78, 151),
25
    (246, 166, 0),
26
    (179, 68, 108),
27
    (220, 211, 0),
28
    (136, 45, 23),
29
    (141, 182, 0),
30
    (101, 69, 34),
31
    (226, 88, 34),
32
    (43, 61, 38),
33
]
34

35

36
class FractionModel(QStandardItemModel):
1✔
37

38
    def __init__(self, parent, result_model):
1✔
39
        super().__init__(parent)
×
40
        self.result_model = result_model
×
41
        self.setHorizontalHeaderLabels(["active", "pattern", "substance"])
×
42
        self.result_item = None
×
43

44
    def clear(self):
1✔
45
        self.result_item = None
×
46
        super().clear()
×
47

48
    def set_fraction(self, item: ThreeDiResultItem, substance: str):
1✔
49
        self.clear()
×
50

51
        self.setHorizontalHeaderLabels(["active", "pattern", "substance"])
×
52
        self.result_item = item
×
53

54
        # Retrieve the substances
55
        threedi_result = self.result_item.threedi_result
×
56
        water_quality_vars = threedi_result.available_water_quality_vars
×
57

58
        # We'll alphabetically sort them
NEW
59
        water_quality_vars = sorted(water_quality_vars, key=lambda x: x["name"] or x["parameters"])
×
60
        for wq_var in water_quality_vars:
×
61
            if wq_var["unit"] != substance:
×
62
                continue
×
63
            color_item = QStandardItem()
×
NEW
64
            color_item.setData((Qt.PenStyle.SolidLine, self.get_new_color()))
×
65
            color_item.setEditable(False)
×
66
            # Display the "name" if present, otherwise parameter name
67
            substance_name = wq_var["name"] or wq_var["parameters"]
×
68
            substance_item = QStandardItem(substance_name)
×
69
            substance_item.setEditable(False)
×
70
            check_item = QStandardItem("")
×
71
            check_item.setCheckable(True)
×
72
            check_item.setEditable(False)
×
NEW
73
            check_item.setCheckState(Qt.CheckState.Checked)
×
74
            check_item.setData(wq_var["parameters"])
×
75
            self.appendRow([check_item, color_item, substance_item])
×
76

77
    def get_new_color(self) -> QColor:
1✔
78
        return FRACTION_COLOR_LIST[self.rowCount() % len(FRACTION_COLOR_LIST)]
×
79

80
    def create_plots(self, feature_id, time_units, stacked, volume, unit_conversion):
1✔
81
        plots = []
×
82
        cumulative_ts_table = None
×
83

84
        if volume:
×
85
            volume_series = self.timeseries_table("vol", feature_id, time_units=time_units)
×
86

87
        for row in range(self.rowCount()):
×
88
            style, color = self.item(row, 1).data()
×
89
            pen = pg.mkPen(color=QColor(*color), width=2, style=style)
×
90
            substance = self.item(row, 0).data()
×
NEW
91
            visible = (self.item(row, 0).checkState() == Qt.CheckState.Checked)
×
UNCOV
92
            ts_table = self.timeseries_table(substance, feature_id, time_units=time_units)
×
93

94
            if volume:
×
95
                # Some conversion for convenient volume units
96
                ts_table = ts_table * unit_conversion
×
97
                # multiply with nodes' volume when volume_mode is selected
98
                ts_table[:, 1] = np.multiply(ts_table[:, 1], volume_series[:, 1])
×
99

100
            if stacked:
×
101
                if cumulative_ts_table is not None:
×
102
                    ts_table[:, 1] = np.add(ts_table[:, 1], cumulative_ts_table)
×
103
                cumulative_ts_table = ts_table[:, 1]  # Don't sum the timekeys
×
104

105
            plot = pg.PlotDataItem(ts_table, pen=pen)
×
NEW
106
            plots.append((substance, plot, visible))
×
107

108
        return plots
×
109

110
    def timeseries_table(self, substance, id, time_units):
1✔
111
        timeseries = self.result_item.threedi_result.get_timeseries(
×
112
            substance, node_id=id, fill_value=np.NaN
113
        )
114
        if timeseries.shape[1] == 1:
×
115
            logger.error("1-element timeserie, plotting empty serie")
×
116
            return np.array([], dtype=float)
×
117
        if time_units == "hrs":
×
118
            vector = np.array([3600, 1])
×
119
        elif time_units == "mins":
×
120
            vector = np.array([60, 1])
×
121
        else:
122
            vector = np.array([1, 1])
×
123
        return timeseries / vector
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc