Passed
Push — main ( 58f0af...6f2e14 )
by Guy
01:14
created

ims_envista.meteo_data.meteo_data_from_json()   C

Complexity

Conditions 7

Size

Total Lines 66
Code Lines 58

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 7
eloc 58
nop 2
dl 0
loc 66
rs 6.9745
c 0
b 0
f 0

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
"""Data Class for IMS Meteorological Readings."""
2
3
from __future__ import annotations
4
5
import datetime
6
import textwrap
7
import time
8
from dataclasses import dataclass, field
9
10
import pytz
11
12
from .const import (
13
    API_BP,
14
    API_CHANNELS,
15
    API_DATA,
16
    API_DATETIME,
17
    API_DIFF,
18
    API_GRAD,
19
    API_NAME,
20
    API_NIP,
21
    API_RAIN,
22
    API_RAIN_1_MIN,
23
    API_RH,
24
    API_STATION_ID,
25
    API_STATUS,
26
    API_STD_WD,
27
    API_TD,
28
    API_TD_MAX,
29
    API_TD_MIN,
30
    API_TG,
31
    API_TIME,
32
    API_TW,
33
    API_VALID,
34
    API_VALUE,
35
    API_WD,
36
    API_WD_MAX,
37
    API_WS,
38
    API_WS_1MM,
39
    API_WS_10MM,
40
    API_WS_MAX,
41
    VARIABLES,
42
)
43
44
45
@dataclass
46
class MeteorologicalData:
47
    """Meteorological Data."""
48
49
    station_id: int
50
    """Station ID"""
51
    datetime: datetime.datetime
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable datetime does not seem to be defined.
Loading history...
52
    """Date and time of the data"""
53
    rain: float
54
    """Rainfall in mm"""
55
    ws: float
56
    """Wind speed in m/s"""
57
    ws_max: float
58
    """Gust wind speed in m/s"""
59
    wd: float
60
    """Wind direction in deg"""
61
    wd_max: float
62
    """Gust wind direction in deg"""
63
    std_wd: float
64
    """Standard deviation wind direction in deg"""
65
    td: float
66
    """Temperature in °C"""
67
    td_max: float
68
    """Maximum Temperature in °C"""
69
    td_min: float
70
    """Minimum Temperature in °C"""
71
    tg: float
72
    """Ground Temperature in °C"""
73
    tw: float
74
    """TW Temperature (?) in °C"""
75
    rh: float
76
    """Relative humidity in %"""
77
    ws_1mm: float
78
    """Maximum 1 minute wind speed in m/s"""
79
    ws_10mm: float
80
    """Maximum 10 minute wind speed in m/s"""
81
    time: datetime.time
82
    """Time"""
83
    bp: float
84
    """Maximum barometric pressure in mb"""
85
    diff_r: float
86
    """Distributed radiation in w/m^2"""
87
    grad: float
88
    """Global radiation in w/m^2"""
89
    nip: float
90
    """Direct radiation in w/m^2"""
91
    rain_1_min: float
92
    """Rainfall per minute in mm"""
93
94
    def _pretty_print(self) -> str:
95
        return textwrap.dedent(
96
            """Station: {}, Date: {}, Readings: [(TD: {}{}), (TDmax: {}{}), (TDmin: {}{}), (TG: {}{}), (RH: {}{}), (Rain: {}{}), (WS: {}{}), (WSmax: {}{}), (WD: {}{}), (WDmax: {}{}),  (STDwd: {}{}), (WS1mm: {}{}), (WS10mm: {}{}), (Time: {}{})]
97
            """
98
        ).format(
99
            self.station_id,
100
            self.datetime,
101
            self.td,
102
            VARIABLES[API_TD].unit,
103
            self.td_max,
104
            VARIABLES[API_TD_MAX].unit,
105
            self.td_min,
106
            VARIABLES[API_TD_MIN].unit,
107
            self.tg,
108
            VARIABLES[API_TG].unit,
109
            self.rh,
110
            VARIABLES[API_RH].unit,
111
            self.rain,
112
            VARIABLES[API_RAIN].unit,
113
            self.ws,
114
            VARIABLES[API_WS].unit,
115
            self.ws_max,
116
            VARIABLES[API_WS_MAX].unit,
117
            self.wd,
118
            VARIABLES[API_WD].unit,
119
            self.wd_max,
120
            VARIABLES[API_WD_MAX].unit,
121
            self.std_wd,
122
            VARIABLES[API_STD_WD].unit,
123
            self.ws_1mm,
124
            VARIABLES[API_WS_1MM].unit,
125
            self.ws_10mm,
126
            VARIABLES[API_WS_10MM].unit,
127
            self.time,
128
            VARIABLES[API_TIME].unit,
129
        )
130
131
    def __str__(self) -> str:
132
        return self._pretty_print()
133
134
    def __repr__(self) -> str:
135
        return self._pretty_print().replace("\n", " ")
136
137
138
@dataclass
139
class StationMeteorologicalReadings:
140
    """Station Meteorological Readings."""
141
142
    station_id: int
143
    """ Station Id"""
144
    data: list[MeteorologicalData] = field(default_factory=list)
145
    """ List of Meteorological Data """
146
147
    def __repr__(self) -> str:
148
        return textwrap.dedent("""Station ({}), Data: {}""").format(
149
            self.station_id, self.data
150
        )
151
152
tz = pytz.timezone("Asia/Jerusalem")
153
154
def meteo_data_from_json(station_id: int, data: dict) -> MeteorologicalData:
155
    """Create a MeteorologicalData object from a JSON object."""
156
    is_dst = bool(time.localtime(time.time()).tm_isdst)
157
158
    dt = datetime.datetime.fromisoformat(data[API_DATETIME])
159
    dt.replace(tzinfo=tz)
160
161
    channel_value_dict = {}
162
    for channel_value in data[API_CHANNELS]:
163
        if channel_value[API_VALID] is True and channel_value[API_STATUS] == 1:
164
            channel_value_dict[channel_value[API_NAME]] = float(
165
                channel_value[API_VALUE]
166
            )
167
168
    rain = channel_value_dict.get(API_RAIN)
169
    ws_max = channel_value_dict.get(API_WS_MAX)
170
    wd_max = channel_value_dict.get(API_WD_MAX)
171
    ws = channel_value_dict.get(API_WS)
172
    wd = channel_value_dict.get(API_WD)
173
    std_wd = channel_value_dict.get(API_STD_WD)
174
    td = channel_value_dict.get(API_TD)
175
    rh = channel_value_dict.get(API_RH)
176
    td_max = channel_value_dict.get(API_TD_MAX)
177
    td_min = channel_value_dict.get(API_TD_MIN)
178
    ws_1mm = channel_value_dict.get(API_WS_1MM)
179
    ws_10mm = channel_value_dict.get(API_WS_10MM)
180
    tg = channel_value_dict.get(API_TG)
181
    tw = channel_value_dict.get(API_TW)
182
    time_val = channel_value_dict.get(API_TIME)
183
    if time_val:
184
        t = time.strptime(str(int(time_val)), "%H%M")
185
        time_val = datetime.time(t.tm_hour, t.tm_min, tzinfo=tz)
186
    bp = channel_value_dict.get(API_BP)
187
    diff_r = channel_value_dict.get(API_DIFF)
188
    grad = channel_value_dict.get(API_GRAD)
189
    nip = channel_value_dict.get(API_NIP)
190
    rain_1_min = channel_value_dict.get(API_RAIN_1_MIN)
191
192
    if is_dst and time_val:
193
        # Strange IMS logic :o
194
        dt = dt + datetime.timedelta(hours=1)
195
        time_val = time_val.replace(hour=(time_val.hour+1)%24)
196
197
    return MeteorologicalData(
198
        station_id=station_id,
199
        datetime=dt,
200
        rain=rain,
201
        ws=ws,
202
        ws_max=ws_max,
203
        wd=wd,
204
        wd_max=wd_max,
205
        std_wd=std_wd,
206
        td=td,
207
        td_max=td_max,
208
        td_min=td_min,
209
        tg=tg,
210
        tw=tw,
211
        rh=rh,
212
        ws_1mm=ws_1mm,
213
        ws_10mm=ws_10mm,
214
        time=time_val,
215
        bp=bp,
216
        diff_r=diff_r,
217
        grad=grad,
218
        nip=nip,
219
        rain_1_min=rain_1_min
220
    )
221
222
223
def station_meteo_data_from_json(json: dict) -> StationMeteorologicalReadings | None:
224
    station_id = int(json[API_STATION_ID])
225
    data = json.get(API_DATA)
226
    if not data:
227
        return None
228
    meteo_data = [meteo_data_from_json(station_id, single_meteo_data) for single_meteo_data in data]
229
    return StationMeteorologicalReadings(station_id, meteo_data)
230