openstef 3.4.56__py3-none-any.whl → 4.0.0a3__py3-none-any.whl
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.
- openstef-4.0.0a3.dist-info/METADATA +177 -0
- openstef-4.0.0a3.dist-info/RECORD +4 -0
- {openstef-3.4.56.dist-info → openstef-4.0.0a3.dist-info}/WHEEL +1 -2
- openstef/__init__.py +0 -14
- openstef/__main__.py +0 -3
- openstef/app_settings.py +0 -19
- openstef/data/NL_terrestrial_radiation.csv +0 -25585
- openstef/data/NL_terrestrial_radiation.csv.license +0 -3
- openstef/data/dazls_model_3.4.24/dazls_stored_3.4.24_baseline_model.z +0 -0
- openstef/data/dazls_model_3.4.24/dazls_stored_3.4.24_baseline_model.z.license +0 -3
- openstef/data/dazls_model_3.4.24/dazls_stored_3.4.24_model_card.md +0 -18
- openstef/data/dazls_model_3.4.24/dazls_stored_3.4.24_model_card.md.license +0 -3
- openstef/data/dutch_holidays.csv +0 -1759
- openstef/data/dutch_holidays.csv.license +0 -3
- openstef/data/pv_single_coefs.csv +0 -601
- openstef/data/pv_single_coefs.csv.license +0 -3
- openstef/data_classes/__init__.py +0 -3
- openstef/data_classes/data_prep.py +0 -99
- openstef/data_classes/model_specifications.py +0 -30
- openstef/data_classes/prediction_job.py +0 -135
- openstef/data_classes/split_function.py +0 -97
- openstef/enums.py +0 -140
- openstef/exceptions.py +0 -74
- openstef/feature_engineering/__init__.py +0 -3
- openstef/feature_engineering/apply_features.py +0 -138
- openstef/feature_engineering/bidding_zone_to_country_mapping.py +0 -106
- openstef/feature_engineering/cyclic_features.py +0 -161
- openstef/feature_engineering/data_preparation.py +0 -152
- openstef/feature_engineering/feature_adder.py +0 -206
- openstef/feature_engineering/feature_applicator.py +0 -202
- openstef/feature_engineering/general.py +0 -141
- openstef/feature_engineering/holiday_features.py +0 -231
- openstef/feature_engineering/lag_features.py +0 -165
- openstef/feature_engineering/missing_values_transformer.py +0 -141
- openstef/feature_engineering/rolling_features.py +0 -58
- openstef/feature_engineering/weather_features.py +0 -492
- openstef/metrics/__init__.py +0 -3
- openstef/metrics/figure.py +0 -303
- openstef/metrics/metrics.py +0 -486
- openstef/metrics/reporter.py +0 -222
- openstef/model/__init__.py +0 -3
- openstef/model/basecase.py +0 -82
- openstef/model/confidence_interval_applicator.py +0 -242
- openstef/model/fallback.py +0 -77
- openstef/model/metamodels/__init__.py +0 -3
- openstef/model/metamodels/feature_clipper.py +0 -90
- openstef/model/metamodels/grouped_regressor.py +0 -222
- openstef/model/metamodels/missing_values_handler.py +0 -138
- openstef/model/model_creator.py +0 -214
- openstef/model/objective.py +0 -426
- openstef/model/objective_creator.py +0 -65
- openstef/model/regressors/__init__.py +0 -3
- openstef/model/regressors/arima.py +0 -197
- openstef/model/regressors/custom_regressor.py +0 -64
- openstef/model/regressors/dazls.py +0 -116
- openstef/model/regressors/flatliner.py +0 -95
- openstef/model/regressors/gblinear_quantile.py +0 -334
- openstef/model/regressors/lgbm.py +0 -29
- openstef/model/regressors/linear.py +0 -90
- openstef/model/regressors/linear_quantile.py +0 -305
- openstef/model/regressors/regressor.py +0 -114
- openstef/model/regressors/xgb.py +0 -52
- openstef/model/regressors/xgb_multioutput_quantile.py +0 -261
- openstef/model/regressors/xgb_quantile.py +0 -228
- openstef/model/serializer.py +0 -431
- openstef/model/standard_deviation_generator.py +0 -81
- openstef/model_selection/__init__.py +0 -3
- openstef/model_selection/model_selection.py +0 -311
- openstef/monitoring/__init__.py +0 -3
- openstef/monitoring/performance_meter.py +0 -92
- openstef/monitoring/teams.py +0 -203
- openstef/pipeline/__init__.py +0 -3
- openstef/pipeline/create_basecase_forecast.py +0 -133
- openstef/pipeline/create_component_forecast.py +0 -168
- openstef/pipeline/create_forecast.py +0 -171
- openstef/pipeline/optimize_hyperparameters.py +0 -317
- openstef/pipeline/train_create_forecast_backtest.py +0 -163
- openstef/pipeline/train_model.py +0 -561
- openstef/pipeline/utils.py +0 -52
- openstef/postprocessing/__init__.py +0 -3
- openstef/postprocessing/postprocessing.py +0 -275
- openstef/preprocessing/__init__.py +0 -3
- openstef/preprocessing/preprocessing.py +0 -42
- openstef/settings.py +0 -15
- openstef/tasks/__init__.py +0 -3
- openstef/tasks/calculate_kpi.py +0 -324
- openstef/tasks/create_basecase_forecast.py +0 -118
- openstef/tasks/create_components_forecast.py +0 -162
- openstef/tasks/create_forecast.py +0 -145
- openstef/tasks/create_solar_forecast.py +0 -420
- openstef/tasks/create_wind_forecast.py +0 -80
- openstef/tasks/optimize_hyperparameters.py +0 -135
- openstef/tasks/split_forecast.py +0 -273
- openstef/tasks/train_model.py +0 -224
- openstef/tasks/utils/__init__.py +0 -3
- openstef/tasks/utils/dependencies.py +0 -107
- openstef/tasks/utils/predictionjobloop.py +0 -243
- openstef/tasks/utils/taskcontext.py +0 -160
- openstef/validation/__init__.py +0 -3
- openstef/validation/validation.py +0 -322
- openstef-3.4.56.dist-info/METADATA +0 -154
- openstef-3.4.56.dist-info/RECORD +0 -102
- openstef-3.4.56.dist-info/top_level.txt +0 -1
- /openstef-3.4.56.dist-info/LICENSE → /openstef-4.0.0a3.dist-info/licenses/LICENSE.md +0 -0
|
@@ -1,243 +0,0 @@
|
|
|
1
|
-
# SPDX-FileCopyrightText: 2017-2023 Contributors to the OpenSTEF project <korte.termijn.prognoses@alliander.com> # noqa E501>
|
|
2
|
-
#
|
|
3
|
-
# SPDX-License-Identifier: MPL-2.0
|
|
4
|
-
|
|
5
|
-
import random
|
|
6
|
-
import sys
|
|
7
|
-
from collections import defaultdict
|
|
8
|
-
from itertools import chain
|
|
9
|
-
from typing import Callable
|
|
10
|
-
|
|
11
|
-
from openstef.data_classes.prediction_job import PredictionJobDataClass
|
|
12
|
-
from openstef.exceptions import PredictionJobException
|
|
13
|
-
from openstef.tasks.utils.dependencies import find_groups, has_dependencies
|
|
14
|
-
from openstef.tasks.utils.taskcontext import TaskContext
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
class PredictionJobLoop:
|
|
18
|
-
"""Convenience objects that maps a function over prediction jobs.
|
|
19
|
-
|
|
20
|
-
Default behaviour is to automatically get prediction jobs from the
|
|
21
|
-
database. Any keyword argument passed will be directed to the getting
|
|
22
|
-
function. If another set of prediction jobs is desired, manually pass
|
|
23
|
-
them using the prediction_jobs argument.
|
|
24
|
-
|
|
25
|
-
Tip: For debugging a specific PID, use debug_pid=specific_pid
|
|
26
|
-
|
|
27
|
-
Args:
|
|
28
|
-
context: The context to run this loop in.
|
|
29
|
-
stop_on_exception: Whether to break out of the
|
|
30
|
-
loop when an exception is raised. Defaults to False.
|
|
31
|
-
random_order: Whether to randomize the order of the
|
|
32
|
-
prediction jobs. Defaults to True. Does not apply to manually
|
|
33
|
-
passed prediction jobs.
|
|
34
|
-
on_exception_callback: Callback, will be called
|
|
35
|
-
everytime an exception is raised. Callable gets the pj and
|
|
36
|
-
exception raised as arguments
|
|
37
|
-
on_successful_callback: Callback, will be called
|
|
38
|
-
everytime an iteration is successful (no exception is raised).
|
|
39
|
-
Callable gets the pj as argument.
|
|
40
|
-
on_end_callback: Callback, will be called everytime an
|
|
41
|
-
iteration is completed. Callable gets the pj and and bool
|
|
42
|
-
indicating success as argument.
|
|
43
|
-
prediction_jobs: Manually pass a list of
|
|
44
|
-
prediction jobs that will be looped over. If set to None, will fetch
|
|
45
|
-
prediction jobs from database based on pj_kwargs
|
|
46
|
-
debug_pid: enter a specific pid for debugging.
|
|
47
|
-
If not None, the prediction job loop will only look at this pid
|
|
48
|
-
**pj_kwargs: Any other kwargs willed will be directed to the
|
|
49
|
-
prediction job getting function.
|
|
50
|
-
|
|
51
|
-
"""
|
|
52
|
-
|
|
53
|
-
def __init__(
|
|
54
|
-
self,
|
|
55
|
-
context: TaskContext,
|
|
56
|
-
stop_on_exception: bool = False,
|
|
57
|
-
random_order: bool = True,
|
|
58
|
-
on_exception_callback: Callable = None,
|
|
59
|
-
on_successful_callback: Callable = None,
|
|
60
|
-
on_end_callback: Callable = None,
|
|
61
|
-
prediction_jobs: list[PredictionJobDataClass] = None,
|
|
62
|
-
debug_pid: int = None,
|
|
63
|
-
**pj_kwargs
|
|
64
|
-
):
|
|
65
|
-
"""Initialzie prediction job loop."""
|
|
66
|
-
self.context = context
|
|
67
|
-
self.stop_on_exception = stop_on_exception
|
|
68
|
-
self.random_order = random_order
|
|
69
|
-
self.on_exception_callback = on_exception_callback
|
|
70
|
-
self.on_successful_callback = on_successful_callback
|
|
71
|
-
self.on_end_callback = on_end_callback
|
|
72
|
-
self.pj_kwargs = pj_kwargs
|
|
73
|
-
self.debug_pid = debug_pid
|
|
74
|
-
|
|
75
|
-
if prediction_jobs is None:
|
|
76
|
-
self.prediction_jobs = self._get_prediction_jobs()
|
|
77
|
-
else:
|
|
78
|
-
self.prediction_jobs = prediction_jobs
|
|
79
|
-
|
|
80
|
-
if has_dependencies(self.prediction_jobs):
|
|
81
|
-
# Groups so that prediction jobs in each group
|
|
82
|
-
# depend only on the pj in the previous groups
|
|
83
|
-
_, pj_groups = find_groups(self.prediction_jobs, self.random_order)
|
|
84
|
-
# Concatenate groups
|
|
85
|
-
self.prediction_jobs = list(chain(*pj_groups))
|
|
86
|
-
else:
|
|
87
|
-
if self.random_order:
|
|
88
|
-
random.shuffle(self.prediction_jobs)
|
|
89
|
-
|
|
90
|
-
def _get_prediction_jobs(self):
|
|
91
|
-
"""Fetches prediction jobs from database."""
|
|
92
|
-
self.context.logger.info(
|
|
93
|
-
"Querying prediction jobs from database", **self.pj_kwargs
|
|
94
|
-
)
|
|
95
|
-
if self.debug_pid is None:
|
|
96
|
-
prediction_jobs = self.context.database.get_prediction_jobs(
|
|
97
|
-
**self.pj_kwargs
|
|
98
|
-
)
|
|
99
|
-
else:
|
|
100
|
-
# retrieve prediction_job for single pid - useful for debugging
|
|
101
|
-
prediction_jobs = [
|
|
102
|
-
self.context.database.get_prediction_job(
|
|
103
|
-
pid=self.debug_pid, **self.pj_kwargs
|
|
104
|
-
)
|
|
105
|
-
]
|
|
106
|
-
|
|
107
|
-
return prediction_jobs
|
|
108
|
-
|
|
109
|
-
def map(self, function: Callable, *args, **kwargs):
|
|
110
|
-
"""Maps the passed function over all prediction jobs.
|
|
111
|
-
|
|
112
|
-
Args:
|
|
113
|
-
function: The function that will be applied to each prediction
|
|
114
|
-
job separately.
|
|
115
|
-
*args: Any other arguments or passed to the function.
|
|
116
|
-
**kwargs: All keyword arguments are passed to the function. This method is
|
|
117
|
-
highly prefered over using args, since kwargs will be automatically
|
|
118
|
-
logged.
|
|
119
|
-
|
|
120
|
-
Raises:
|
|
121
|
-
PredictionJobException: This exception will be raised if one or more
|
|
122
|
-
prediction jobs raised an exception during the loop.
|
|
123
|
-
|
|
124
|
-
"""
|
|
125
|
-
pids_successful = []
|
|
126
|
-
pids_unsuccessful = []
|
|
127
|
-
pids_unsuccessful_dict = defaultdict(list)
|
|
128
|
-
last_job_exception = None
|
|
129
|
-
|
|
130
|
-
num_jobs = len(self.prediction_jobs)
|
|
131
|
-
self.context.perf_meter.checkpoint("pre-loop")
|
|
132
|
-
|
|
133
|
-
# loop over prediction jobs
|
|
134
|
-
for i, prediction_job in enumerate(self.prediction_jobs):
|
|
135
|
-
successful = False
|
|
136
|
-
|
|
137
|
-
self.context.logger = self.context.logger.bind(
|
|
138
|
-
prediction_id=prediction_job["id"],
|
|
139
|
-
prediction_name=prediction_job["name"],
|
|
140
|
-
)
|
|
141
|
-
|
|
142
|
-
self.context.perf_meter.start_level(
|
|
143
|
-
"iteration", i, num_jobs=num_jobs, pid=prediction_job["id"], **kwargs
|
|
144
|
-
)
|
|
145
|
-
|
|
146
|
-
try:
|
|
147
|
-
function(prediction_job, *args, **kwargs)
|
|
148
|
-
|
|
149
|
-
pids_successful.append(prediction_job["id"])
|
|
150
|
-
successful = True
|
|
151
|
-
|
|
152
|
-
self._handle_successful_iteration(prediction_job)
|
|
153
|
-
|
|
154
|
-
except Exception as exception:
|
|
155
|
-
pids_unsuccessful.append(prediction_job["id"])
|
|
156
|
-
pids_unsuccessful_dict[str(exception)].append(prediction_job["id"])
|
|
157
|
-
last_job_exception = exception
|
|
158
|
-
|
|
159
|
-
self._handle_exception_during_iteration(prediction_job, exception)
|
|
160
|
-
|
|
161
|
-
if self.stop_on_exception:
|
|
162
|
-
break
|
|
163
|
-
finally:
|
|
164
|
-
self._handle_finished_last_iteration(prediction_job, successful)
|
|
165
|
-
|
|
166
|
-
self.context.logger = self.context.logger.unbind("prediction_id")
|
|
167
|
-
|
|
168
|
-
jobs_successful = len(pids_successful)
|
|
169
|
-
jobs_unsuccessful = len(pids_unsuccessful)
|
|
170
|
-
jobs_started = jobs_successful + jobs_unsuccessful
|
|
171
|
-
|
|
172
|
-
# This log is for human readable logging
|
|
173
|
-
self.context.perf_meter.checkpoint(
|
|
174
|
-
"loop",
|
|
175
|
-
num_jobs=num_jobs,
|
|
176
|
-
jobs_started=jobs_started,
|
|
177
|
-
jobs_successful=jobs_successful,
|
|
178
|
-
jobs_unsuccessful=jobs_unsuccessful,
|
|
179
|
-
successful=int(jobs_unsuccessful > 0),
|
|
180
|
-
)
|
|
181
|
-
|
|
182
|
-
if jobs_unsuccessful > 0:
|
|
183
|
-
metrics = {
|
|
184
|
-
"num_jobs": num_jobs,
|
|
185
|
-
"pids_successful": pids_successful,
|
|
186
|
-
"pids_unsuccessful": pids_unsuccessful,
|
|
187
|
-
"exceptions": pids_unsuccessful_dict,
|
|
188
|
-
"jobs_successful": jobs_successful,
|
|
189
|
-
"jobs_unsuccessful": jobs_unsuccessful,
|
|
190
|
-
"jobs_started": jobs_started,
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
raise PredictionJobException(metrics) from last_job_exception
|
|
194
|
-
|
|
195
|
-
def _handle_successful_iteration(self, prediction_job):
|
|
196
|
-
if self.on_successful_callback is not None:
|
|
197
|
-
try:
|
|
198
|
-
self.on_successful_callback(prediction_job)
|
|
199
|
-
except Exception:
|
|
200
|
-
_, exc_info, stack_info = sys.exc_info()
|
|
201
|
-
self.context.logger.error(
|
|
202
|
-
"An exception occured when executing the on_successful_callback "
|
|
203
|
-
" callback function for this iteration",
|
|
204
|
-
exc_info=exc_info,
|
|
205
|
-
stack_info=stack_info,
|
|
206
|
-
)
|
|
207
|
-
|
|
208
|
-
def _handle_exception_during_iteration(self, prediction_job, e):
|
|
209
|
-
_, exc_info, stack_info = sys.exc_info()
|
|
210
|
-
self.context.logger.error(
|
|
211
|
-
"An exception occured during this iteration",
|
|
212
|
-
exc_info=exc_info,
|
|
213
|
-
stack_info=stack_info,
|
|
214
|
-
)
|
|
215
|
-
|
|
216
|
-
if self.on_exception_callback is None:
|
|
217
|
-
return
|
|
218
|
-
|
|
219
|
-
try:
|
|
220
|
-
self.on_exception_callback(prediction_job, e)
|
|
221
|
-
except Exception:
|
|
222
|
-
_, exc_info, stack_info = sys.exc_info()
|
|
223
|
-
self.context.logger.error(
|
|
224
|
-
"An exception occured when executing the on_exception_callback "
|
|
225
|
-
"callback function for this iteration",
|
|
226
|
-
exc_info=exc_info,
|
|
227
|
-
stack_info=stack_info,
|
|
228
|
-
)
|
|
229
|
-
|
|
230
|
-
def _handle_finished_last_iteration(self, prediction_job, successful):
|
|
231
|
-
self.context.perf_meter.complete_level(successful)
|
|
232
|
-
|
|
233
|
-
if self.on_end_callback is not None:
|
|
234
|
-
try:
|
|
235
|
-
self.on_end_callback(prediction_job, successful)
|
|
236
|
-
except Exception:
|
|
237
|
-
_, exc_info, stack_info = sys.exc_info()
|
|
238
|
-
self.context.logger.error(
|
|
239
|
-
"An exception occured when executing the on_end_callback callback "
|
|
240
|
-
" function for this iteration",
|
|
241
|
-
exc_info=exc_info,
|
|
242
|
-
stack_info=stack_info,
|
|
243
|
-
)
|
|
@@ -1,160 +0,0 @@
|
|
|
1
|
-
# SPDX-FileCopyrightText: 2017-2023 Contributors to the OpenSTEF project <korte.termijn.prognoses@alliander.com> # noqa E501>
|
|
2
|
-
#
|
|
3
|
-
# SPDX-License-Identifier: MPL-2.0
|
|
4
|
-
import logging
|
|
5
|
-
import traceback
|
|
6
|
-
from typing import Callable
|
|
7
|
-
|
|
8
|
-
import structlog
|
|
9
|
-
|
|
10
|
-
from openstef.exceptions import PredictionJobException
|
|
11
|
-
from openstef.monitoring.performance_meter import PerformanceMeter
|
|
12
|
-
from openstef.monitoring.teams import post_teams
|
|
13
|
-
from openstef.settings import Settings
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
class TaskContext:
|
|
17
|
-
"""A context manager that can be used to run tasks with.
|
|
18
|
-
|
|
19
|
-
Should be used as:
|
|
20
|
-
|
|
21
|
-
.. code-block:: py
|
|
22
|
-
|
|
23
|
-
with TaskContext("my_task_name") as context:
|
|
24
|
-
pass
|
|
25
|
-
|
|
26
|
-
Args:
|
|
27
|
-
name: Name of the task
|
|
28
|
-
config: Configuration object, can be found in openSTEF-dbc
|
|
29
|
-
database: Database object, can be found in openSTEF-dbc
|
|
30
|
-
suppress_exceptions: If set to False the context
|
|
31
|
-
manager will pass any raised exception on. Defaults to False.
|
|
32
|
-
post_teams_on_exception: If set to True the context
|
|
33
|
-
manager will automatically post a message to teams when an
|
|
34
|
-
exception is encountered. Defaults to True.
|
|
35
|
-
on_exception : Callback, will be called
|
|
36
|
-
when an exception is raised. Callable gets exc_type, exc_info,
|
|
37
|
-
stack_info as arguments.
|
|
38
|
-
on_successful: Callback, will be called
|
|
39
|
-
everytime if the task is successful (no exception is raised).
|
|
40
|
-
on_end: Callback, will be called if the task is
|
|
41
|
-
completed. Callable gets a bool indicating success as argument.
|
|
42
|
-
|
|
43
|
-
"""
|
|
44
|
-
|
|
45
|
-
def __init__(
|
|
46
|
-
self,
|
|
47
|
-
name: str,
|
|
48
|
-
config: object,
|
|
49
|
-
database: object,
|
|
50
|
-
suppress_exceptions: bool = False,
|
|
51
|
-
post_teams_on_exception: bool = True,
|
|
52
|
-
on_exception: Callable = None,
|
|
53
|
-
on_successful: Callable = None,
|
|
54
|
-
on_end: Callable = None,
|
|
55
|
-
):
|
|
56
|
-
"""Initialize context manager."""
|
|
57
|
-
self.name = name
|
|
58
|
-
self.suppress_exceptions = suppress_exceptions
|
|
59
|
-
self.post_teams_on_exception = post_teams_on_exception
|
|
60
|
-
self.on_exception = on_exception
|
|
61
|
-
self.on_successful = on_successful
|
|
62
|
-
self.on_end = on_end
|
|
63
|
-
self.config = config
|
|
64
|
-
self.database = database
|
|
65
|
-
|
|
66
|
-
def __enter__(self):
|
|
67
|
-
structlog.configure(
|
|
68
|
-
wrapper_class=structlog.make_filtering_bound_logger(
|
|
69
|
-
logging.getLevelName(Settings.log_level)
|
|
70
|
-
)
|
|
71
|
-
)
|
|
72
|
-
self.logger = structlog.get_logger(__name__).bind(task=self.name)
|
|
73
|
-
|
|
74
|
-
self.perf_meter = PerformanceMeter(self.logger)
|
|
75
|
-
|
|
76
|
-
self.perf_meter.start_level("task", self.name)
|
|
77
|
-
|
|
78
|
-
return self
|
|
79
|
-
|
|
80
|
-
def __exit__(self, exc_type: type, exc_info: str, stack_info: str) -> bool:
|
|
81
|
-
self.successful = exc_type is None
|
|
82
|
-
|
|
83
|
-
if self.successful:
|
|
84
|
-
if self.on_successful is not None:
|
|
85
|
-
self.on_successful()
|
|
86
|
-
else:
|
|
87
|
-
self.logger.error(
|
|
88
|
-
"An error has occured",
|
|
89
|
-
exc_info=exc_info,
|
|
90
|
-
stack_info=stack_info,
|
|
91
|
-
)
|
|
92
|
-
if self.on_exception is not None:
|
|
93
|
-
self.on_exception(exc_type, exc_info, stack_info)
|
|
94
|
-
|
|
95
|
-
if self.post_teams_on_exception:
|
|
96
|
-
self._send_teams_message(exc_type, exc_info, stack_info)
|
|
97
|
-
|
|
98
|
-
if self.on_end is not None:
|
|
99
|
-
self.on_end(self.successful)
|
|
100
|
-
|
|
101
|
-
self.perf_meter.complete_level(self.successful)
|
|
102
|
-
|
|
103
|
-
# Returning true stops the propagation of the exception
|
|
104
|
-
return self.suppress_exceptions
|
|
105
|
-
|
|
106
|
-
def _send_teams_message(
|
|
107
|
-
self, exc_type: type, exc_info: str, stack_info: str
|
|
108
|
-
) -> None:
|
|
109
|
-
stack_text = "".join(traceback.format_exception(exc_type, exc_info, stack_info))
|
|
110
|
-
# The teams webhook behaves weird. Using the correct order \r\n does NOT work
|
|
111
|
-
# The 4 spaces forces teams to format a monospaced message for some reason
|
|
112
|
-
stack_text = stack_text.replace("\n", "\n\r ")
|
|
113
|
-
msg = {
|
|
114
|
-
"title": f"Task '{self.name}' raised an exception",
|
|
115
|
-
"color": "#a54f4f",
|
|
116
|
-
"sections": [],
|
|
117
|
-
}
|
|
118
|
-
if exc_type is PredictionJobException:
|
|
119
|
-
metrics = exc_info.metrics
|
|
120
|
-
msg["sections"].append(
|
|
121
|
-
{
|
|
122
|
-
"facts": [
|
|
123
|
-
("Number of pjs in this task", metrics["num_jobs"]),
|
|
124
|
-
("Number of pjs started", metrics["jobs_started"]),
|
|
125
|
-
("Number of pjs successful", metrics["jobs_successful"]),
|
|
126
|
-
("Number of pjs unsuccessful", metrics["jobs_unsuccessful"]),
|
|
127
|
-
],
|
|
128
|
-
}
|
|
129
|
-
)
|
|
130
|
-
string_pids_unsuccessful = ", ".join(
|
|
131
|
-
[f"{pid:d}" for pid in metrics["pids_unsuccessful"]]
|
|
132
|
-
)
|
|
133
|
-
msg["sections"].append(
|
|
134
|
-
{
|
|
135
|
-
"facts": [
|
|
136
|
-
("Unsuccessful pid(s)", string_pids_unsuccessful),
|
|
137
|
-
]
|
|
138
|
-
}
|
|
139
|
-
)
|
|
140
|
-
# Add details of exceptions and pids
|
|
141
|
-
string_pids_exceptions = "\n".join(
|
|
142
|
-
[f"{key}:{value}\n" for key, value in metrics["exceptions"].items()]
|
|
143
|
-
)
|
|
144
|
-
msg["sections"].append(
|
|
145
|
-
{
|
|
146
|
-
"facts": [
|
|
147
|
-
("Exceptions: pid(s)", string_pids_exceptions),
|
|
148
|
-
]
|
|
149
|
-
}
|
|
150
|
-
)
|
|
151
|
-
|
|
152
|
-
msg["sections"].append(
|
|
153
|
-
{
|
|
154
|
-
"text": f" {stack_text}",
|
|
155
|
-
}
|
|
156
|
-
)
|
|
157
|
-
|
|
158
|
-
post_teams(
|
|
159
|
-
msg, url=self.config.teams_monitoring_url, proxies=self.config.proxies
|
|
160
|
-
)
|
openstef/validation/__init__.py
DELETED