gazpar2haws 0.2.1__py3-none-any.whl → 0.3.0b15__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.
gazpar2haws/model.py ADDED
@@ -0,0 +1,234 @@
1
+ from datetime import date
2
+ from enum import Enum
3
+ from pathlib import Path
4
+ from typing import Generic, Optional, TypeVar
5
+
6
+ from pydantic import BaseModel, DirectoryPath, EmailStr, SecretStr, model_validator
7
+ from pydantic_extra_types.timezone_name import TimeZoneName
8
+
9
+ from gazpar2haws.date_array import DateArray
10
+
11
+
12
+ # ----------------------------------
13
+ class LoggingLevel(str, Enum):
14
+ DEBUG = "debug"
15
+ INFO = "info"
16
+ WARNING = "warning"
17
+ ERROR = "error"
18
+ CRITICAL = "critical"
19
+
20
+
21
+ # ----------------------------------
22
+ class TimeUnit(str, Enum):
23
+ DAY = "day"
24
+ WEEK = "week"
25
+ MONTH = "month"
26
+ YEAR = "year"
27
+
28
+
29
+ # ----------------------------------
30
+ class PriceUnit(str, Enum):
31
+ EURO = "€"
32
+ CENT = "¢"
33
+
34
+
35
+ # ----------------------------------
36
+ class QuantityUnit(str, Enum):
37
+ MWH = "MWh"
38
+ KWH = "kWh"
39
+ WH = "Wh"
40
+ M3 = "m³"
41
+ LITER = "l"
42
+
43
+
44
+ # ----------------------------------
45
+ class Logging(BaseModel):
46
+ file: str
47
+ console: bool
48
+ level: LoggingLevel
49
+ format: str
50
+
51
+
52
+ # ----------------------------------
53
+ class Device(BaseModel):
54
+ name: str
55
+ data_source: str = "json"
56
+ tmp_dir: DirectoryPath = DirectoryPath("/tmp")
57
+ as_of_date: date = date.today()
58
+ username: Optional[EmailStr] = None
59
+ password: Optional[SecretStr] = None
60
+ pce_identifier: Optional[SecretStr] = None
61
+ timezone: TimeZoneName = TimeZoneName("Europe/Paris")
62
+ last_days: int = 365
63
+ reset: bool = False
64
+
65
+ @model_validator(mode="after")
66
+ def validate_properties(self):
67
+ if self.data_source not in ["json", "excel", "test"]:
68
+ raise ValueError(f"Invalid data_source{self.data_source} (expected values: json, excel, test)")
69
+ if self.data_source != "test" and self.username is None:
70
+ raise ValueError("Missing username")
71
+ if self.data_source != "test" and self.password is None:
72
+ raise ValueError("Missing password")
73
+ if self.data_source != "test" and self.pce_identifier is None:
74
+ raise ValueError("Missing pce_identifier")
75
+ if self.data_source == "excel" and self.tmp_dir is None or not Path(self.tmp_dir).is_dir():
76
+ raise ValueError(f"Invalid tmp_dir {self.tmp_dir}")
77
+ return self
78
+
79
+
80
+ # ----------------------------------
81
+ class Grdf(BaseModel):
82
+ scan_interval: Optional[int] = 480
83
+ devices: list[Device]
84
+
85
+
86
+ # ----------------------------------
87
+ class HomeAssistant(BaseModel):
88
+ host: str
89
+ port: int
90
+ endpoint: str = "/api/websocket"
91
+ token: SecretStr
92
+
93
+
94
+ # ----------------------------------
95
+ class Period(BaseModel):
96
+ start_date: date
97
+ end_date: Optional[date] = None
98
+
99
+
100
+ # ----------------------------------
101
+ class Value(Period):
102
+ value: float
103
+
104
+
105
+ # ----------------------------------
106
+ class ValueArray(Period):
107
+ value_array: Optional[DateArray] = None
108
+
109
+ @model_validator(mode="after")
110
+ def set_value_array(self):
111
+ if self.value_array is None:
112
+ self.value_array = DateArray(
113
+ start_date=self.start_date, end_date=self.end_date
114
+ ) # pylint: disable=attribute-defined-outside-init
115
+ return self
116
+
117
+
118
+ # ----------------------------------
119
+ class Vat(BaseModel):
120
+ id: str
121
+
122
+
123
+ # ----------------------------------
124
+ class VatRate(Vat, Value):
125
+ pass
126
+
127
+
128
+ # ----------------------------------
129
+ class VatRateArray(Vat, ValueArray):
130
+ pass
131
+
132
+
133
+ # ----------------------------------
134
+ # Define type variables
135
+ ValueUnit = TypeVar("ValueUnit")
136
+ BaseUnit = TypeVar("BaseUnit")
137
+
138
+
139
+ # ----------------------------------
140
+ class Unit(BaseModel, Generic[ValueUnit, BaseUnit]):
141
+ value_unit: Optional[ValueUnit] = None
142
+ base_unit: Optional[BaseUnit] = None
143
+
144
+
145
+ # ----------------------------------
146
+ class Price(Unit[ValueUnit, BaseUnit]): # pylint: disable=too-few-public-methods
147
+ vat_id: Optional[str] = None
148
+
149
+
150
+ # ----------------------------------
151
+ class PriceValue(Price[ValueUnit, BaseUnit], Value):
152
+ pass
153
+
154
+
155
+ # ----------------------------------
156
+ class PriceValueArray(Price[ValueUnit, BaseUnit], ValueArray):
157
+ pass
158
+
159
+
160
+ # ----------------------------------
161
+ class ConsumptionPriceArray(PriceValueArray[PriceUnit, QuantityUnit]): # pylint: disable=too-few-public-methods
162
+ pass
163
+
164
+
165
+ # ----------------------------------
166
+ class SubscriptionPriceArray(PriceValueArray[PriceUnit, TimeUnit]): # pylint: disable=too-few-public-methods
167
+ pass
168
+
169
+
170
+ # ----------------------------------
171
+ class TransportPriceArray(PriceValueArray[PriceUnit, TimeUnit]): # pylint: disable=too-few-public-methods
172
+ pass
173
+
174
+
175
+ # ----------------------------------
176
+ class EnergyTaxesPriceArray(PriceValueArray[PriceUnit, QuantityUnit]): # pylint: disable=too-few-public-methods
177
+ pass
178
+
179
+
180
+ # ----------------------------------
181
+ class Pricing(BaseModel):
182
+ vat: Optional[list[VatRate]] = None
183
+ consumption_prices: list[PriceValue[PriceUnit, QuantityUnit]]
184
+ subscription_prices: Optional[list[PriceValue[PriceUnit, TimeUnit]]] = None
185
+ transport_prices: Optional[list[PriceValue[PriceUnit, TimeUnit]]] = None
186
+ energy_taxes: Optional[list[PriceValue[PriceUnit, QuantityUnit]]] = None
187
+
188
+ @model_validator(mode="before")
189
+ @classmethod
190
+ def propagates_properties(cls, values):
191
+ for price_list in [
192
+ "consumption_prices",
193
+ "subscription_prices",
194
+ "transport_prices",
195
+ "energy_taxes",
196
+ ]:
197
+ prices = values.get(price_list, [])
198
+
199
+ if len(prices) == 0:
200
+ continue
201
+
202
+ if "start_date" not in prices[0]:
203
+ raise ValueError(f"Missing start_date in first element of {price_list}")
204
+ if "value_unit" not in prices[0]:
205
+ prices[0]["value_unit"] = "€"
206
+ if "base_unit" not in prices[0]:
207
+ if price_list in ["consumption_prices", "energy_taxes"]:
208
+ prices[0]["base_unit"] = "kWh"
209
+ else:
210
+ raise ValueError(
211
+ "Missing base_unit in first element of ['transport_prices', 'subscription_prices']"
212
+ )
213
+
214
+ for i in range(len(prices) - 1):
215
+ if "end_date" not in prices[i]:
216
+ prices[i]["end_date"] = prices[i + 1]["start_date"]
217
+ if "value_unit" not in prices[i + 1]:
218
+ prices[i + 1]["value_unit"] = prices[i]["value_unit"]
219
+ if "base_unit" not in prices[i + 1]:
220
+ prices[i + 1]["base_unit"] = prices[i]["base_unit"]
221
+ if "vat_id" not in prices[i + 1] and "vat_id" in prices[i]:
222
+ prices[i + 1]["vat_id"] = prices[i]["vat_id"]
223
+
224
+ return values
225
+
226
+
227
+ # ----------------------------------
228
+ class ConsumptionQuantityArray(Unit[QuantityUnit, TimeUnit], ValueArray):
229
+ pass
230
+
231
+
232
+ # ----------------------------------
233
+ class CostArray(Unit[PriceUnit, TimeUnit], ValueArray):
234
+ pass