weatherdb 1.1.0__py3-none-any.whl
Sign up to get free protection for your applications and to get access to all the features.
- docker/Dockerfile +30 -0
- docker/docker-compose.yaml +58 -0
- docker/docker-compose_test.yaml +24 -0
- docker/start-docker-test.sh +6 -0
- docs/requirements.txt +10 -0
- docs/source/Changelog.md +2 -0
- docs/source/License.rst +7 -0
- docs/source/Methode.md +161 -0
- docs/source/_static/custom.css +8 -0
- docs/source/_static/favicon.ico +0 -0
- docs/source/_static/logo.png +0 -0
- docs/source/api/api.rst +15 -0
- docs/source/api/cli.rst +8 -0
- docs/source/api/weatherDB.broker.rst +10 -0
- docs/source/api/weatherDB.config.rst +7 -0
- docs/source/api/weatherDB.db.rst +23 -0
- docs/source/api/weatherDB.rst +22 -0
- docs/source/api/weatherDB.station.rst +56 -0
- docs/source/api/weatherDB.stations.rst +46 -0
- docs/source/api/weatherDB.utils.rst +22 -0
- docs/source/conf.py +137 -0
- docs/source/index.rst +33 -0
- docs/source/setup/Configuration.md +127 -0
- docs/source/setup/Hosting.md +9 -0
- docs/source/setup/Install.md +49 -0
- docs/source/setup/Quickstart.md +183 -0
- docs/source/setup/setup.rst +12 -0
- weatherdb/__init__.py +24 -0
- weatherdb/_version.py +1 -0
- weatherdb/alembic/README.md +8 -0
- weatherdb/alembic/alembic.ini +80 -0
- weatherdb/alembic/config.py +9 -0
- weatherdb/alembic/env.py +100 -0
- weatherdb/alembic/script.py.mako +26 -0
- weatherdb/alembic/versions/V1.0.0_initial_database_creation.py +898 -0
- weatherdb/alembic/versions/V1.0.2_more_charachters_for_settings+term_station_ma_raster.py +88 -0
- weatherdb/alembic/versions/V1.0.5_fix-ma-raster-values.py +152 -0
- weatherdb/alembic/versions/V1.0.6_update-views.py +22 -0
- weatherdb/broker.py +667 -0
- weatherdb/cli.py +214 -0
- weatherdb/config/ConfigParser.py +663 -0
- weatherdb/config/__init__.py +5 -0
- weatherdb/config/config_default.ini +162 -0
- weatherdb/db/__init__.py +3 -0
- weatherdb/db/connections.py +374 -0
- weatherdb/db/fixtures/RichterParameters.json +34 -0
- weatherdb/db/models.py +402 -0
- weatherdb/db/queries/get_quotient.py +155 -0
- weatherdb/db/views.py +165 -0
- weatherdb/station/GroupStation.py +710 -0
- weatherdb/station/StationBases.py +3108 -0
- weatherdb/station/StationET.py +111 -0
- weatherdb/station/StationP.py +807 -0
- weatherdb/station/StationPD.py +98 -0
- weatherdb/station/StationT.py +164 -0
- weatherdb/station/__init__.py +13 -0
- weatherdb/station/constants.py +21 -0
- weatherdb/stations/GroupStations.py +519 -0
- weatherdb/stations/StationsBase.py +1021 -0
- weatherdb/stations/StationsBaseTET.py +30 -0
- weatherdb/stations/StationsET.py +17 -0
- weatherdb/stations/StationsP.py +128 -0
- weatherdb/stations/StationsPD.py +24 -0
- weatherdb/stations/StationsT.py +21 -0
- weatherdb/stations/__init__.py +11 -0
- weatherdb/utils/TimestampPeriod.py +369 -0
- weatherdb/utils/__init__.py +3 -0
- weatherdb/utils/dwd.py +350 -0
- weatherdb/utils/geometry.py +69 -0
- weatherdb/utils/get_data.py +285 -0
- weatherdb/utils/logging.py +126 -0
- weatherdb-1.1.0.dist-info/LICENSE +674 -0
- weatherdb-1.1.0.dist-info/METADATA +765 -0
- weatherdb-1.1.0.dist-info/RECORD +77 -0
- weatherdb-1.1.0.dist-info/WHEEL +5 -0
- weatherdb-1.1.0.dist-info/entry_points.txt +2 -0
- weatherdb-1.1.0.dist-info/top_level.txt +3 -0
@@ -0,0 +1,30 @@
|
|
1
|
+
# libraries
|
2
|
+
import logging
|
3
|
+
|
4
|
+
from ..db.connections import db_engine
|
5
|
+
from .StationsBase import StationsBase
|
6
|
+
from .StationsP import StationsP
|
7
|
+
|
8
|
+
# set settings
|
9
|
+
# ############
|
10
|
+
__all__ = ["StationsBaseTET"]
|
11
|
+
log = logging.getLogger(__name__)
|
12
|
+
|
13
|
+
# class definition
|
14
|
+
##################
|
15
|
+
class StationsBaseTET(StationsBase):
|
16
|
+
@db_engine.deco_update_privilege
|
17
|
+
def fillup(self, only_real=False, stids="all", **kwargs):
|
18
|
+
# create virtual stations if necessary
|
19
|
+
if not only_real:
|
20
|
+
meta = self.get_meta(
|
21
|
+
infos=["Station_id"], only_real=False)
|
22
|
+
meta_p = StationsP().get_meta(
|
23
|
+
infos=["Station_id"], only_real=False)
|
24
|
+
stids_missing = set(meta_p.index.values) - set(meta.index.values)
|
25
|
+
if stids != "all":
|
26
|
+
stids_missing = set(stids).intersection(stids_missing)
|
27
|
+
for stid in stids_missing:
|
28
|
+
self._StationClass(stid) # this creates the virtual station
|
29
|
+
|
30
|
+
super().fillup(only_real=only_real, stids=stids, **kwargs)
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# libraries
|
2
|
+
import logging
|
3
|
+
|
4
|
+
from ..station import StationET
|
5
|
+
from .StationsBaseTET import StationsBaseTET
|
6
|
+
|
7
|
+
# set settings
|
8
|
+
# ############
|
9
|
+
__all__ = ["StationsET"]
|
10
|
+
log = logging.getLogger(__name__)
|
11
|
+
|
12
|
+
# class definition
|
13
|
+
##################
|
14
|
+
class StationsET(StationsBaseTET):
|
15
|
+
"""A class to work with and download potential Evapotranspiration (VPGB) data for several stations."""
|
16
|
+
_StationClass = StationET
|
17
|
+
_timeout_raw_imp = 120
|
@@ -0,0 +1,128 @@
|
|
1
|
+
# libraries
|
2
|
+
import logging
|
3
|
+
|
4
|
+
from ..db.connections import db_engine
|
5
|
+
from ..station import StationP
|
6
|
+
from .StationsBase import StationsBase
|
7
|
+
|
8
|
+
# set settings
|
9
|
+
# ############
|
10
|
+
__all__ = ["StationsP"]
|
11
|
+
log = logging.getLogger(__name__)
|
12
|
+
|
13
|
+
# class definition
|
14
|
+
##################
|
15
|
+
class StationsP(StationsBase):
|
16
|
+
"""A class to work with and download 10 minutes precipitation data for several stations."""
|
17
|
+
_StationClass = StationP
|
18
|
+
_timeout_raw_imp = 360
|
19
|
+
|
20
|
+
@db_engine.deco_update_privilege
|
21
|
+
def update_richter_class(self, stids="all", do_mp=True, **kwargs):
|
22
|
+
"""Update the Richter exposition class.
|
23
|
+
|
24
|
+
Get the value from the raster, compare with the richter categories and save to the database.
|
25
|
+
|
26
|
+
Parameters
|
27
|
+
----------
|
28
|
+
stids: string or list of int, optional
|
29
|
+
The Stations for which to compute.
|
30
|
+
Can either be "all", for all possible stations
|
31
|
+
or a list with the Station IDs.
|
32
|
+
The default is "all".
|
33
|
+
**kwargs : dict, optional
|
34
|
+
The keyword arguments to be handed to the station.StationP.update_richter_class and get_stations method.
|
35
|
+
|
36
|
+
Raises
|
37
|
+
------
|
38
|
+
ValueError
|
39
|
+
If the given stids (Station_IDs) are not all valid.
|
40
|
+
"""
|
41
|
+
self._run_method(
|
42
|
+
stations=self.get_stations(only_real=True, stids=stids, **kwargs),
|
43
|
+
method="update_richter_class",
|
44
|
+
name="update richter class for {para}".format(para=self._para.upper()),
|
45
|
+
kwds=kwargs,
|
46
|
+
do_mp=do_mp)
|
47
|
+
|
48
|
+
@db_engine.deco_update_privilege
|
49
|
+
def richter_correct(self, stids="all", **kwargs):
|
50
|
+
"""Richter correct the filled data.
|
51
|
+
|
52
|
+
Parameters
|
53
|
+
----------
|
54
|
+
stids: string or list of int, optional
|
55
|
+
The Stations for which to compute.
|
56
|
+
Can either be "all", for all possible stations
|
57
|
+
or a list with the Station IDs.
|
58
|
+
The default is "all".
|
59
|
+
**kwargs : dict, optional
|
60
|
+
The additional keyword arguments for the _run_method and get_stations method
|
61
|
+
|
62
|
+
Raises
|
63
|
+
------
|
64
|
+
ValueError
|
65
|
+
If the given stids (Station_IDs) are not all valid.
|
66
|
+
"""
|
67
|
+
self._run_method(
|
68
|
+
stations=self.get_stations(only_real=False, stids=stids, **kwargs),
|
69
|
+
method="richter_correct",
|
70
|
+
name="richter correction on {para}".format(para=self._para.upper()),
|
71
|
+
do_mp=False, **kwargs)
|
72
|
+
|
73
|
+
@db_engine.deco_update_privilege
|
74
|
+
def last_imp_corr(self, stids="all", do_mp=False, **kwargs):
|
75
|
+
"""Richter correct the filled data for the last imported period.
|
76
|
+
|
77
|
+
Parameters
|
78
|
+
----------
|
79
|
+
stids: string or list of int, optional
|
80
|
+
The Stations for which to compute.
|
81
|
+
Can either be "all", for all possible stations
|
82
|
+
or a list with the Station IDs.
|
83
|
+
The default is "all".
|
84
|
+
do_mp : bool, optional
|
85
|
+
Should the method be done in multiprocessing mode?
|
86
|
+
If False the methods will be called in threading mode.
|
87
|
+
Multiprocessing needs more memory and a bit more initiating time. Therefor it is only usefull for methods with a lot of computation effort in the python code.
|
88
|
+
If the most computation of a method is done in the postgresql database, then threading is enough to speed the process up.
|
89
|
+
The default is False.
|
90
|
+
**kwargs : dict, optional
|
91
|
+
The additional keyword arguments for the _run_method and get_stations method
|
92
|
+
|
93
|
+
Raises
|
94
|
+
------
|
95
|
+
ValueError
|
96
|
+
If the given stids (Station_IDs) are not all valid.
|
97
|
+
"""
|
98
|
+
stations = self.get_stations(only_real=True, stids=stids, **kwargs)
|
99
|
+
period = stations[0].get_last_imp_period(all=True)
|
100
|
+
log.info("The {para_long} Stations fillup of the last import is started for the period {min_tstp} - {max_tstp}".format(
|
101
|
+
para_long=self._para_long,
|
102
|
+
**period.get_sql_format_dict(format="%Y%m%d %H:%M")))
|
103
|
+
self._run_method(
|
104
|
+
stations=stations,
|
105
|
+
method="last_imp_corr",
|
106
|
+
kwds={"_last_imp_period": period},
|
107
|
+
name="richter correction on {para}".format(para=self._para.upper()),
|
108
|
+
do_mp=do_mp, **kwargs)
|
109
|
+
|
110
|
+
@db_engine.deco_update_privilege
|
111
|
+
def update(self, only_new=True, **kwargs):
|
112
|
+
"""Make a complete update of the stations.
|
113
|
+
|
114
|
+
Does the update_raw, quality check, fillup and richter correction of the stations.
|
115
|
+
|
116
|
+
Parameters
|
117
|
+
----------
|
118
|
+
only_new : bool, optional
|
119
|
+
Should a only new values be computed?
|
120
|
+
If False: The stations are updated for the whole possible period.
|
121
|
+
If True, the stations are only updated for new values.
|
122
|
+
The default is True.
|
123
|
+
"""
|
124
|
+
super().update(only_new=only_new, **kwargs)
|
125
|
+
if only_new:
|
126
|
+
self.last_imp_richter_correct(**kwargs)
|
127
|
+
else:
|
128
|
+
self.richter_correct(**kwargs)
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# libraries
|
2
|
+
import logging
|
3
|
+
|
4
|
+
from ..station import StationPD
|
5
|
+
from .StationsP import StationsP
|
6
|
+
from .StationsBase import StationsBase
|
7
|
+
|
8
|
+
# set settings
|
9
|
+
# ############
|
10
|
+
__all__ = ["StationsPD"]
|
11
|
+
log = logging.getLogger(__name__)
|
12
|
+
|
13
|
+
# class definition
|
14
|
+
##################
|
15
|
+
|
16
|
+
class StationsPD(StationsBase):
|
17
|
+
"""A class to work with and download daily precipitation data for several stations.
|
18
|
+
|
19
|
+
Those stations data are only downloaded to do some quality checks on the 10 minutes data.
|
20
|
+
Therefor there is no special quality check and richter correction done on this data.
|
21
|
+
If you want daily precipitation data, better use the 10 minutes station class (StationP) and aggregate to daily values.
|
22
|
+
"""
|
23
|
+
_StationClass = StationPD
|
24
|
+
_timeout_raw_imp = 120
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# libraries
|
2
|
+
import logging
|
3
|
+
|
4
|
+
from ..station import StationT
|
5
|
+
from .StationsBaseTET import StationsBaseTET
|
6
|
+
|
7
|
+
# set settings
|
8
|
+
# ############
|
9
|
+
__all__ = ["StationsT"]
|
10
|
+
log = logging.getLogger(__name__)
|
11
|
+
|
12
|
+
# class definition
|
13
|
+
##################
|
14
|
+
|
15
|
+
class StationsT(StationsBaseTET):
|
16
|
+
"""A class to work with and download temperature data for several stations."""
|
17
|
+
_StationClass = StationT
|
18
|
+
_timeout_raw_imp = 120
|
19
|
+
|
20
|
+
def get_quotient(self, **kwargs):
|
21
|
+
raise NotImplementedError("The quotient is not yet implemented for temperature.")
|
@@ -0,0 +1,11 @@
|
|
1
|
+
"""
|
2
|
+
This module has grouping classes for all the stations of one parameter. E.G. StationsP (or StationsP) groups all the Precipitation Stations available.
|
3
|
+
Those classes can get used to do actions on all the stations.
|
4
|
+
"""
|
5
|
+
from .StationsP import StationsP
|
6
|
+
from .StationsPD import StationsPD
|
7
|
+
from .StationsT import StationsT
|
8
|
+
from .StationsET import StationsET
|
9
|
+
from .GroupStations import GroupStations
|
10
|
+
|
11
|
+
__all__ = ["StationsP", "StationsPD", "StationsT", "StationsET", "GroupStations"]
|
@@ -0,0 +1,369 @@
|
|
1
|
+
import re
|
2
|
+
from pandas import Timestamp, NaT, Timedelta
|
3
|
+
import datetime
|
4
|
+
|
5
|
+
class TimestampPeriod(object):
|
6
|
+
"""A class to save a Timespan with a minimal and maximal Timestamp.
|
7
|
+
"""
|
8
|
+
_COMPARE = {
|
9
|
+
"inner": {
|
10
|
+
0: max,
|
11
|
+
1: min},
|
12
|
+
"outer": {
|
13
|
+
0: min,
|
14
|
+
1: max}}
|
15
|
+
_REGEX_HAS_TIME = re.compile(
|
16
|
+
r"((^\d{6}[ \-\.]+)"+ # 991231
|
17
|
+
r"|(^\d{8}[ \-\.]*)|" + # 19991231
|
18
|
+
r"(^\d{1,4}[ \-\.]\d{1,2}[ \-\.]\d{1,4}[ \-\.]+))" + # 1999-12-31
|
19
|
+
r"+(\d+)") # has additional numbers -> time
|
20
|
+
|
21
|
+
def __init__(self, start, end, tzinfo="UTC"):
|
22
|
+
"""Initiate a TimestampPeriod.
|
23
|
+
|
24
|
+
Parameters
|
25
|
+
----------
|
26
|
+
start : pd.Timestamp or similar
|
27
|
+
The start of the Period.
|
28
|
+
end : pd.Timestamp or similar
|
29
|
+
The end of the Period.
|
30
|
+
tzinfo : str or datetime.timezone object or None, optional
|
31
|
+
The timezone to set to the timestamps.
|
32
|
+
If the timestamps already have a timezone they will get converted.
|
33
|
+
If None, then the timezone is not changed or set.
|
34
|
+
The default is "UTC".
|
35
|
+
"""
|
36
|
+
# check if input is a date or a timestamp
|
37
|
+
if ((isinstance(start, datetime.date) and isinstance(end, datetime.date)) or
|
38
|
+
(isinstance(start, str) and not self._REGEX_HAS_TIME.match(start) and
|
39
|
+
isinstance(end, str) and not self._REGEX_HAS_TIME.match(end))):
|
40
|
+
self.is_date = True
|
41
|
+
else:
|
42
|
+
self.is_date = False
|
43
|
+
|
44
|
+
# convert to correct timestamp format
|
45
|
+
period = list([start, end])
|
46
|
+
for i, tstp in enumerate(period):
|
47
|
+
if not isinstance(tstp, Timestamp):
|
48
|
+
period[i] = Timestamp(tstp)
|
49
|
+
|
50
|
+
# check timezone
|
51
|
+
self.tzinfo = tzinfo
|
52
|
+
if tzinfo is not None:
|
53
|
+
if period[i].tzinfo is None:
|
54
|
+
period[i] = period[i].tz_localize(tzinfo)
|
55
|
+
else:
|
56
|
+
period[i] = period[i].tz_convert(tzinfo)
|
57
|
+
|
58
|
+
self.start = period[0]
|
59
|
+
self.end = period[1]
|
60
|
+
|
61
|
+
@staticmethod
|
62
|
+
def _check_period(period):
|
63
|
+
if not isinstance(period, TimestampPeriod):
|
64
|
+
period = TimestampPeriod(*period)
|
65
|
+
return period
|
66
|
+
|
67
|
+
def union(self, other, how="inner"):
|
68
|
+
"""Unite 2 TimestampPeriods to one.
|
69
|
+
|
70
|
+
Compares the Periods and computes a new one.
|
71
|
+
|
72
|
+
Parameters
|
73
|
+
----------
|
74
|
+
other : TimestampPeriod
|
75
|
+
The other TimestampPeriod with whome to compare.
|
76
|
+
how : str, optional
|
77
|
+
How to compare the 2 TimestampPeriods.
|
78
|
+
Can be "inner" or "outer".
|
79
|
+
"inner": the maximal Timespan for both is computed.
|
80
|
+
"outer": The minimal Timespan for both is computed.
|
81
|
+
The default is "inner".
|
82
|
+
|
83
|
+
Returns
|
84
|
+
-------
|
85
|
+
TimestampPeriod
|
86
|
+
A new TimespanPeriod object uniting both TimestampPeriods.
|
87
|
+
"""
|
88
|
+
other = self._check_period(other)
|
89
|
+
|
90
|
+
# check for daily period in elements
|
91
|
+
tdsother = [Timedelta(0), Timedelta(0)]
|
92
|
+
tdsself = [Timedelta(0), Timedelta(0)]
|
93
|
+
if self.is_date and not other.is_date and not other.is_empty():
|
94
|
+
tdsself[1] = Timedelta(
|
95
|
+
hours=other.end.hour,
|
96
|
+
minutes=other.end.minute,
|
97
|
+
seconds=other.end.second)
|
98
|
+
elif not self.is_date and other.is_date and not self.is_empty():
|
99
|
+
tdsother[1] = Timedelta(
|
100
|
+
hours=self.end.hour,
|
101
|
+
minutes=self.end.minute,
|
102
|
+
seconds=self.end.second)
|
103
|
+
|
104
|
+
# check if empty and inner
|
105
|
+
if how=="inner" and (self.is_empty() or other.is_empty()):
|
106
|
+
return TimestampPeriod(None, None)
|
107
|
+
|
108
|
+
# get the united period
|
109
|
+
period = [None, None]
|
110
|
+
for i in range(2):
|
111
|
+
comp_list = [val + td
|
112
|
+
for val, td in zip([self[i], other[i]],
|
113
|
+
[tdsself[i], tdsother[i]])
|
114
|
+
if isinstance(val, Timestamp)]
|
115
|
+
if len(comp_list) > 0:
|
116
|
+
period[i] = self._COMPARE[how][i](comp_list)
|
117
|
+
|
118
|
+
# check if end < start
|
119
|
+
if period[0]>=period[1]:
|
120
|
+
period = (None, None)
|
121
|
+
|
122
|
+
# return the period
|
123
|
+
if self.is_date and other.is_date and all(period):
|
124
|
+
# if both were data periods, then the result should also be a date period
|
125
|
+
return TimestampPeriod(
|
126
|
+
*[val.date() for val in period
|
127
|
+
if isinstance(val, Timestamp)])
|
128
|
+
else:
|
129
|
+
return TimestampPeriod(*period)
|
130
|
+
|
131
|
+
def get_period(self):
|
132
|
+
return (self.start, self.end)
|
133
|
+
|
134
|
+
def __getitem__(self, key):
|
135
|
+
if key == 0 or key == "start":
|
136
|
+
return self.start
|
137
|
+
elif key == 1 or key == "end":
|
138
|
+
return self.end
|
139
|
+
|
140
|
+
def __setitem__(self, key, value):
|
141
|
+
if value != Timestamp:
|
142
|
+
value = Timestamp(value)
|
143
|
+
|
144
|
+
if key == 0 or key == "start":
|
145
|
+
self.start = value
|
146
|
+
elif key == 1 or key == "end":
|
147
|
+
self.end = value
|
148
|
+
|
149
|
+
def __iter__(self):
|
150
|
+
return self.get_period().__iter__()
|
151
|
+
|
152
|
+
def __str__(self):
|
153
|
+
msg = "TimestampPeriod: {0} - {1}"
|
154
|
+
if self.is_date:
|
155
|
+
return msg.format(*self.strftime(format="%Y-%m-%d"))
|
156
|
+
else:
|
157
|
+
return msg.format(*self.strftime(format="%Y-%m-%d %H:%M:%S"))
|
158
|
+
|
159
|
+
def __repr__(self):
|
160
|
+
return self.__str__()
|
161
|
+
|
162
|
+
def __eq__(self, other):
|
163
|
+
other = self._check_period(other)
|
164
|
+
|
165
|
+
if self.start == other.start and self.end == other.end:
|
166
|
+
return True
|
167
|
+
else:
|
168
|
+
return False
|
169
|
+
|
170
|
+
def __ne__(self, other):
|
171
|
+
return not self.__eq__(other)
|
172
|
+
|
173
|
+
def __lt__(self, other):
|
174
|
+
return self.inside(other)
|
175
|
+
|
176
|
+
def __le__(self, other):
|
177
|
+
return self.inside(other)
|
178
|
+
|
179
|
+
def __gt__(self, other):
|
180
|
+
return self.contains(other)
|
181
|
+
|
182
|
+
def __ge__(self, other):
|
183
|
+
return self.contains(other)
|
184
|
+
|
185
|
+
def has_NaT(self):
|
186
|
+
"""Has the TimestampPeriod at least one NaT.
|
187
|
+
|
188
|
+
This means that the start or end is not given.
|
189
|
+
Normally this should never happen, because it makes no sense.
|
190
|
+
|
191
|
+
Returns
|
192
|
+
-------
|
193
|
+
bool
|
194
|
+
True if the TimestampPeriod has at least on NaT.
|
195
|
+
False if the TimestampPeriod has at least a start or a end.
|
196
|
+
"""
|
197
|
+
return any([tstp is NaT for tstp in self])
|
198
|
+
|
199
|
+
def has_only_NaT(self):
|
200
|
+
"""Has the TimestampPeriod only NaT, meaning is empty.
|
201
|
+
|
202
|
+
This means that the start and end is not given.
|
203
|
+
|
204
|
+
Returns
|
205
|
+
-------
|
206
|
+
bool
|
207
|
+
True if the TimestampPeriod is empty.
|
208
|
+
False if the TimestampPeriod has a start and an end.
|
209
|
+
"""
|
210
|
+
return all([tstp is NaT for tstp in self])
|
211
|
+
|
212
|
+
def is_empty(self):
|
213
|
+
"""Is the TimestampPeriod empty.
|
214
|
+
|
215
|
+
This means that the start and end is not given.
|
216
|
+
|
217
|
+
Returns
|
218
|
+
-------
|
219
|
+
bool
|
220
|
+
True if the TimestampPeriod is empty.
|
221
|
+
False if the TimestampPeriod has a start and an end.
|
222
|
+
"""
|
223
|
+
return self.has_only_NaT()
|
224
|
+
|
225
|
+
def strftime(self, format="%Y-%m-%d %H:%M:%S"):
|
226
|
+
"""Convert the TimestampPeriod to a list of strings.
|
227
|
+
|
228
|
+
Formates the Timestamp as a string.
|
229
|
+
|
230
|
+
Parameters
|
231
|
+
----------
|
232
|
+
format : str, optional
|
233
|
+
The Timestamp-format to use.
|
234
|
+
The Default is "%Y-%m-%d %H:%M:%S"
|
235
|
+
|
236
|
+
Returns
|
237
|
+
-------
|
238
|
+
list of 2 strings
|
239
|
+
A list of the start and end of the TimestampPeriod as formated string.
|
240
|
+
"""
|
241
|
+
out = [tstp.strftime(format) if tstp is not NaT else None
|
242
|
+
for tstp in self.get_period()]
|
243
|
+
return out
|
244
|
+
|
245
|
+
def inside(self, other):
|
246
|
+
"""Is the TimestampPeriod inside another TimestampPeriod?
|
247
|
+
|
248
|
+
Parameters
|
249
|
+
----------
|
250
|
+
other : Timestampperiod or tuple of 2 Timestamp or Timestamp strings
|
251
|
+
The other Timestamp to test against.
|
252
|
+
Test if this TimestampPeriod is inside the other.
|
253
|
+
|
254
|
+
Returns
|
255
|
+
-------
|
256
|
+
bool
|
257
|
+
True if this TimestampPeriod is inside the other.
|
258
|
+
Meaning that the start is higher or equal than the others starts
|
259
|
+
and the end is smaller than the others end.
|
260
|
+
"""
|
261
|
+
other = self._check_period(other)
|
262
|
+
if self.start >= other.start and self.end <= other.end:
|
263
|
+
return True
|
264
|
+
else:
|
265
|
+
return False
|
266
|
+
|
267
|
+
def contains(self, other):
|
268
|
+
"""Does this TimestampPeriod contain another TimestampPeriod?
|
269
|
+
|
270
|
+
Parameters
|
271
|
+
----------
|
272
|
+
other : Timestampperiod or tuple of 2 Timestamp or Timestamp strings
|
273
|
+
The other Timestamp to test against.
|
274
|
+
Test if this TimestampPeriod contains the other.
|
275
|
+
|
276
|
+
Returns
|
277
|
+
-------
|
278
|
+
bool
|
279
|
+
True if this TimestampPeriod contains the other.
|
280
|
+
Meaning that the start is smaller or equal than the others starts
|
281
|
+
and the end is higher than the others end.
|
282
|
+
"""
|
283
|
+
other = self._check_period(other)
|
284
|
+
return other.inside(self)
|
285
|
+
|
286
|
+
def get_sql_format_dict(self, format="'%Y%m%d %H:%M'"):
|
287
|
+
"""Get the dictionary to use in sql queries.
|
288
|
+
|
289
|
+
Parameters
|
290
|
+
----------
|
291
|
+
format : str, optional
|
292
|
+
The Timestamp-format to use.
|
293
|
+
The Default is "'%Y%m%d %H:%M'"
|
294
|
+
|
295
|
+
Returns
|
296
|
+
-------
|
297
|
+
dict
|
298
|
+
a dictionary with 2 keys (min_tstp, max_tstp) and the corresponding Timestamp as formated string.
|
299
|
+
"""
|
300
|
+
period_str = self.strftime(format=format)
|
301
|
+
period_str = [str(el).replace("None", "NULL") for el in period_str]
|
302
|
+
return dict(min_tstp=period_str[0], max_tstp=period_str[1])
|
303
|
+
|
304
|
+
def get_interval(self):
|
305
|
+
"""Get the interval of the TimestampPeriod.
|
306
|
+
|
307
|
+
Returns
|
308
|
+
-------
|
309
|
+
pd.Timedelta
|
310
|
+
The interval of this TimestampPeriod.
|
311
|
+
E.G. Timedelta(2 days 12:30:12)
|
312
|
+
"""
|
313
|
+
return self.end - self.start
|
314
|
+
|
315
|
+
def get_middle(self):
|
316
|
+
"""Get the middle Timestamp of the TimestampPeriod.
|
317
|
+
|
318
|
+
Returns
|
319
|
+
-------
|
320
|
+
Timestamp
|
321
|
+
The middle Timestamp of this TimestampPeriod.
|
322
|
+
"""
|
323
|
+
middle = self.start + self.get_interval() / 2
|
324
|
+
if self.is_date:
|
325
|
+
middle = Timestamp(middle.date())
|
326
|
+
if self.tzinfo is not None:
|
327
|
+
if middle.tzinfo is None:
|
328
|
+
middle = middle.tz_localize(self.tzinfo)
|
329
|
+
return middle
|
330
|
+
|
331
|
+
def copy(self):
|
332
|
+
"""Copy this TimestampPeriod.
|
333
|
+
|
334
|
+
Returns
|
335
|
+
-------
|
336
|
+
TimestampPeriod
|
337
|
+
a new TimestampPeriod object that is equal to this one.
|
338
|
+
"""
|
339
|
+
new = TimestampPeriod(self.start, self.end)
|
340
|
+
new.is_date = self.is_date
|
341
|
+
return new
|
342
|
+
|
343
|
+
def expand_to_timestamp(self):
|
344
|
+
if self.is_date:
|
345
|
+
return TimestampPeriod(
|
346
|
+
start=self.start,
|
347
|
+
end=self.end + Timedelta(
|
348
|
+
hours=23, minutes=59, seconds=59, milliseconds=999))
|
349
|
+
else:
|
350
|
+
return self
|
351
|
+
|
352
|
+
def set_tz(self, tzinfo):
|
353
|
+
"""Set the TimestampPeriod to a new timezone.
|
354
|
+
|
355
|
+
Parameters
|
356
|
+
----------
|
357
|
+
tzinfo : str or datetime.timezone object
|
358
|
+
The timezone to set the TimestampPeriod to.
|
359
|
+
|
360
|
+
Returns
|
361
|
+
-------
|
362
|
+
TimestampPeriod
|
363
|
+
This TimestampPeriod.
|
364
|
+
"""
|
365
|
+
if not self.is_date:
|
366
|
+
self.start.replace(tzinfo=tzinfo)
|
367
|
+
self.end.replace(tzinfo=tzinfo)
|
368
|
+
return self
|
369
|
+
|