e-data 2.0.0.dev112__py3-none-any.whl → 2.0.0.dev121__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: e-data
3
- Version: 2.0.0.dev112
3
+ Version: 2.0.0.dev121
4
4
  Summary: Python library for managing spanish energy data from various web providers
5
5
  Author-email: VMG <vmayorg@outlook.es>
6
6
  License: GNU GENERAL PUBLIC LICENSE
@@ -823,7 +823,7 @@ async def main():
823
823
  # Mostrar resumen
824
824
  total_cost = sum(b.value_eur for b in bills)
825
825
  print(f"Coste total: {total_cost:.2f} €")
826
- print(f"Consumo total: {sum(e.value_kWh for e in energy):.2f} kWh")
826
+ print(f"Consumo total: {sum(e.value_kwh for e in energy):.2f} kWh")
827
827
 
828
828
  # Ejecutar
829
829
  asyncio.run(main())
@@ -841,7 +841,7 @@ python -m edata.cli show-supplies <username>
841
841
  python -m edata.cli download-all --cups ES0000000000000000XX <username>
842
842
 
843
843
  # Actualizar facturación con tarifa fija
844
- python -m edata.cli update-bill \
844
+ python -m edata.cli update-custom-bill \
845
845
  --cups ES0000000000000000XX \
846
846
  --p1-kw-year-eur 30.67 \
847
847
  --p2-kw-year-eur 1.42 \
@@ -0,0 +1,28 @@
1
+ e_data-2.0.0.dev121.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
2
+ edata/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
+ edata/cli.py,sha256=0pmlaaXPVfbxPVHMovsuK9qf8PGXl38AV3gtIsk95O4,4010
4
+ edata/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
+ edata/core/const.py,sha256=X9J2f7lt9jXJnghqz0JFi-J7USa3xlYvbuiki70eHNE,43
6
+ edata/core/utils.py,sha256=P4vEPVnymvBp8W2rwwc_XMvd8_6Vx6N0GKnVx0WxE0I,1938
7
+ edata/database/__init__.py,sha256=HP_pDbGLzeMY9Ufm_Dps5cxzW_3YTonT8w8dstG558c,46
8
+ edata/database/controller.py,sha256=_KQJALolxUTf5G405USCoSDxYebg2819aYYgiTZF1kU,16358
9
+ edata/database/models.py,sha256=4rlRpPErULqYr6QJxZar1NBpJPWTcHftDN1MB3bwGyA,5096
10
+ edata/database/queries.py,sha256=kGoTWs0VJ4KjMJqBbhQGUeio55sZAysKABYGBUUGBE8,6687
11
+ edata/database/utils.py,sha256=V-FJfyAh8YJBGHB85BbWtG3Ilq09pM9Gl-L86TffB4g,1062
12
+ edata/models/__init__.py,sha256=nMPPPkGjUsF51PBFlChgHyb5C3aQiccTRiVHUdJjNj4,153
13
+ edata/models/bill.py,sha256=-qZ4l9f8BnPFlykFRoNYnmRe6-ELWmuNh6usks_k4X0,2674
14
+ edata/models/data.py,sha256=O1aj0H_G92mnJjm6mYhC8fnl8ZOoatGiokRf8b7ziD8,2388
15
+ edata/models/supply.py,sha256=qM0Q22-7PfBN93-Tu3ljAE58p5gfob0YWw-o5FccjrI,878
16
+ edata/providers/__init__.py,sha256=5xaRKPttYM6EpVE1I_vhUAF25L9pEM_qK_5Ix6xnQe0,104
17
+ edata/providers/datadis.py,sha256=a084ArY83ncvrMQvMSD2BY1rHlhp42dwJJkU8avmleo,20137
18
+ edata/providers/redata.py,sha256=uvxO2TTtSNSfPRWHZifLjbaP7zAr6O7CZ1oRQFNbIhQ,3122
19
+ edata/services/bill_service.py,sha256=bjBIL1jl0Lcb2TG5VLKZgqT55PXdMPtEQW6fIPjWrVk,13411
20
+ edata/services/data_service.py,sha256=65w-fVllJY8GFM9eUZ7uVF_XK-QgKeYl4wfrqBFpXjE,16772
21
+ edata/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
22
+ edata/tests/test_datadis_connector.py,sha256=O3mjI5uIOgFz3PzQsApcG6J2yXd8rlEtj8By-9dsDbg,9270
23
+ edata/tests/test_redata_connector.py,sha256=SBvxsma5t-qvz2drxNisNM2P1STTurqEs1TSDgwaPJ8,508
24
+ edata/tests/test_services.py,sha256=XiwppLWqxm5vjgRtNmBeDFUaV5vVcQxerGgjnBJgmmk,4521
25
+ e_data-2.0.0.dev121.dist-info/METADATA,sha256=hhW170QW1yry_sAqkesWyOXXXKjbZm3ZvdvYgafEatg,48075
26
+ e_data-2.0.0.dev121.dist-info/WHEEL,sha256=qELbo2s1Yzl39ZmrAibXA2jjPLUYfnVhUNTlyF1rq0Y,92
27
+ e_data-2.0.0.dev121.dist-info/top_level.txt,sha256=Ez-fReWtUVTMcFuH0dzCnoKIgMBHgjLaBuba8QiKC7g,6
28
+ e_data-2.0.0.dev121.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (80.9.0)
2
+ Generator: setuptools (80.10.1)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
edata/cli.py CHANGED
@@ -17,7 +17,7 @@ app = typer.Typer()
17
17
 
18
18
 
19
19
  @app.command()
20
- def show_supplies(username: str):
20
+ def show_supplies(username: str) -> None:
21
21
  """Show supplies and contracts for a given datadis user."""
22
22
 
23
23
  password = getpass()
@@ -28,7 +28,7 @@ def show_supplies(username: str):
28
28
  typer.echo(supply.model_dump_json())
29
29
  if supplies:
30
30
  cups = supplies[0].cups
31
- distributor = supplies[0].distributorCode
31
+ distributor = supplies[0].distributor_code
32
32
  contracts = connector.get_contract_detail(cups, distributor)
33
33
  typer.echo("\nContracts:")
34
34
  for contract in contracts:
@@ -41,7 +41,7 @@ async def _download_all(
41
41
  nif: str,
42
42
  cups: str,
43
43
  authorized_nif: str | None = None,
44
- ):
44
+ ) -> None:
45
45
  """Download all data for a given datadis account and CUPS."""
46
46
 
47
47
  password = getpass()
@@ -63,7 +63,7 @@ def download_all(
63
63
  nif: str,
64
64
  cups: Annotated[str, typer.Option(help="The identifier of the Supply")],
65
65
  authorized_nif: str | None = None,
66
- ):
66
+ ) -> None:
67
67
  """Download all data for a given datadis account and CUPS."""
68
68
 
69
69
  asyncio.run(_download_all(nif, cups, authorized_nif))
@@ -77,7 +77,7 @@ async def _update_custom_bill(
77
77
  p2_kwh_eur: float,
78
78
  p3_kwh_eur: float,
79
79
  meter_month_eur: float,
80
- ):
80
+ ) -> None:
81
81
  """Download all data for a given datadis account and CUPS."""
82
82
 
83
83
  bs = BillService(cups, storage_path="./edata_cli")
@@ -105,7 +105,7 @@ def update_custom_bill(
105
105
  p2_kwh_eur: Annotated[float, typer.Option(help="Price per kWh at P2 tariff")],
106
106
  p3_kwh_eur: Annotated[float, typer.Option(help="Price per kWh at P3 tariff")],
107
107
  meter_month_eur: Annotated[float, typer.Option(help="Monthly cost of the meter")],
108
- ):
108
+ ) -> None:
109
109
  """Download all data for a given datadis account and CUPS."""
110
110
 
111
111
  asyncio.run(
@@ -121,7 +121,7 @@ def update_custom_bill(
121
121
  )
122
122
 
123
123
 
124
- async def _update_pvpc_bill(cups: str):
124
+ async def _update_pvpc_bill(cups: str) -> None:
125
125
  """Download all data for a given datadis account and CUPS."""
126
126
 
127
127
  bs = BillService(cups, storage_path="./edata_cli")
@@ -131,7 +131,7 @@ async def _update_pvpc_bill(cups: str):
131
131
  @app.command()
132
132
  def update_pvpc_bill(
133
133
  cups: Annotated[str, typer.Option(help="The identifier of the Supply")],
134
- ):
134
+ ) -> None:
135
135
  """Download all data for a given datadis account and CUPS."""
136
136
 
137
137
  asyncio.run(
edata/core/utils.py CHANGED
@@ -43,7 +43,7 @@ def get_tariff(dt: datetime) -> int:
43
43
  return 0
44
44
 
45
45
 
46
- def get_contract_for_dt(contracts: list[Contract], date: datetime):
46
+ def get_contract_for_dt(contracts: list[Contract], date: datetime) -> Contract | None:
47
47
  """Return the active contract for a provided datetime."""
48
48
 
49
49
  for contract in contracts:
@@ -49,12 +49,12 @@ class EdataDB:
49
49
  return cls._instance
50
50
 
51
51
  @property
52
- def engine(self):
52
+ def engine(self) -> AsyncEngine | None:
53
53
  """Return the async database engine."""
54
54
 
55
55
  return self._engine
56
56
 
57
- async def _ensure_tables(self):
57
+ async def _ensure_tables(self) -> None:
58
58
  """Create tables if not already created (lazy init)."""
59
59
 
60
60
  if self._tables_initialized:
@@ -84,11 +84,11 @@ class EdataDB:
84
84
  self,
85
85
  session: AsyncSession,
86
86
  query: SelectOfScalar,
87
- data,
87
+ data: typing.Any,
88
88
  commit: bool = True,
89
89
  overrides: dict[str, typing.Any] | None = None,
90
90
  ) -> T | None: # type: ignore
91
- """Updates a single record in the database."""
91
+ """Update a single record in the database."""
92
92
 
93
93
  result = await session.exec(query)
94
94
  existing = result.first()
@@ -240,7 +240,7 @@ class EdataDB:
240
240
  session, q.get_energy(cups, energy.datetime), record
241
241
  )
242
242
 
243
- async def add_energy_list(self, cups: str, energy: list[Energy]):
243
+ async def add_energy_list(self, cups: str, energy: list[Energy]) -> None:
244
244
  """Add or update a list of energy records."""
245
245
 
246
246
  await self._ensure_tables()
@@ -264,7 +264,7 @@ class EdataDB:
264
264
  session, q.get_power(cups, power.datetime), record
265
265
  )
266
266
 
267
- async def add_power_list(self, cups: str, power: list[Power]):
267
+ async def add_power_list(self, cups: str, power: list[Power]) -> None:
268
268
  """Add or update a list of power records."""
269
269
 
270
270
  await self._ensure_tables()
@@ -285,7 +285,7 @@ class EdataDB:
285
285
  session, q.get_pvpc(pvpc.datetime), record
286
286
  )
287
287
 
288
- async def add_pvpc_list(self, pvpc: list[EnergyPrice]):
288
+ async def add_pvpc_list(self, pvpc: list[EnergyPrice]) -> None:
289
289
  """Add or update a list of pvpc records."""
290
290
 
291
291
  await self._ensure_tables()
@@ -351,7 +351,7 @@ class EdataDB:
351
351
  confhash: str,
352
352
  complete: bool,
353
353
  bill: list[Bill],
354
- ):
354
+ ) -> None:
355
355
  """Add or update a list of bill records."""
356
356
 
357
357
  await self._ensure_tables()
@@ -374,7 +374,7 @@ class EdataDB:
374
374
  session, queries, items, override=["complete", "confhash"]
375
375
  )
376
376
 
377
- async def list_supplies(self):
377
+ async def list_supplies(self) -> typing.Sequence[SupplyModel]:
378
378
  """List all supply records."""
379
379
 
380
380
  await self._ensure_tables()
@@ -382,7 +382,9 @@ class EdataDB:
382
382
  result = await session.exec(q.list_supply())
383
383
  return result.all()
384
384
 
385
- async def list_contracts(self, cups: str | None = None):
385
+ async def list_contracts(
386
+ self, cups: str | None = None
387
+ ) -> typing.Sequence[ContractModel]:
386
388
  """List all contract records."""
387
389
 
388
390
  await self._ensure_tables()
@@ -395,7 +397,7 @@ class EdataDB:
395
397
  cups: str,
396
398
  date_from: datetime | None = None,
397
399
  date_to: datetime | None = None,
398
- ):
400
+ ) -> typing.Sequence[EnergyModel]:
399
401
  """List energy records."""
400
402
 
401
403
  await self._ensure_tables()
@@ -408,7 +410,7 @@ class EdataDB:
408
410
  cups: str,
409
411
  date_from: datetime | None = None,
410
412
  date_to: datetime | None = None,
411
- ):
413
+ ) -> typing.Sequence[PowerModel]:
412
414
  """List power records."""
413
415
 
414
416
  await self._ensure_tables()
@@ -420,7 +422,7 @@ class EdataDB:
420
422
  self,
421
423
  date_from: datetime | None = None,
422
424
  date_to: datetime | None = None,
423
- ):
425
+ ) -> typing.Sequence[PVPCModel]:
424
426
  """List pvpc records."""
425
427
 
426
428
  await self._ensure_tables()
@@ -435,7 +437,7 @@ class EdataDB:
435
437
  date_from: datetime | None = None,
436
438
  date_to: datetime | None = None,
437
439
  complete: bool | None = None,
438
- ):
440
+ ) -> typing.Sequence[StatisticsModel]:
439
441
  """List statistics records filtered by type ('day' or 'month') and date range."""
440
442
 
441
443
  await self._ensure_tables()
@@ -452,7 +454,7 @@ class EdataDB:
452
454
  date_from: datetime | None = None,
453
455
  date_to: datetime | None = None,
454
456
  complete: bool | None = None,
455
- ):
457
+ ) -> typing.Sequence[BillModel]:
456
458
  """List bill records filtered by type ('hour', 'day' or 'month') and date range."""
457
459
 
458
460
  await self._ensure_tables()
edata/database/queries.py CHANGED
@@ -215,7 +215,9 @@ def list_pvpc(
215
215
  return query
216
216
 
217
217
 
218
- def get_bill(cups, type_, datetime_):
218
+ def get_bill(
219
+ cups: str, type_: typing.Literal["hour", "day", "month"], datetime_: datetime | None
220
+ ) -> SelectOfScalar[BillModel]:
219
221
  """Query that selects a bill."""
220
222
 
221
223
  query = select(BillModel).where(BillModel.cups == cups)
edata/database/utils.py CHANGED
@@ -7,10 +7,7 @@ T = TypeVar("T", bound=BaseModel)
7
7
 
8
8
 
9
9
  class PydanticJSON(TypeDecorator):
10
- """
11
- Tipo de SQLAlchemy para guardar modelos Pydantic como JSON.
12
- Automáticamente serializa al guardar y deserializa al leer.
13
- """
10
+ """SQLAlchemy type to guard Pydantic models as JSON."""
14
11
 
15
12
  impl = JSON
16
13
  cache_ok = True
@@ -20,6 +17,7 @@ class PydanticJSON(TypeDecorator):
20
17
  self.pydantic_model = pydantic_model
21
18
 
22
19
  def process_bind_param(self, value: T | None, dialect: Any) -> Any:
20
+ """Process binding parameter."""
23
21
  # Python -> Base de Datos
24
22
  if value is None:
25
23
  return None
@@ -27,6 +25,7 @@ class PydanticJSON(TypeDecorator):
27
25
  return value.model_dump(mode="json")
28
26
 
29
27
  def process_result_value(self, value: Any, dialect: Any) -> T | None:
28
+ """Process result value."""
30
29
  # Base de Datos -> Python
31
30
  if value is None:
32
31
  return None
edata/models/bill.py CHANGED
@@ -6,15 +6,15 @@ from pydantic import BaseModel, Field
6
6
 
7
7
 
8
8
  class EnergyPrice(BaseModel):
9
- """Data structure to represent pricing data."""
9
+ """Represent pricing data."""
10
10
 
11
11
  datetime: datetime
12
- value_eur_kWh: float
12
+ value_eur_kwh: float
13
13
  delta_h: float
14
14
 
15
15
 
16
16
  class Bill(BaseModel):
17
- """Data structure to represent a bill during a period."""
17
+ """Represent a bill during a period."""
18
18
 
19
19
  datetime: datetime
20
20
  delta_h: float
@@ -26,7 +26,7 @@ class Bill(BaseModel):
26
26
 
27
27
 
28
28
  class BillingRules(BaseModel):
29
- """Data structure to represent a generic billing rule."""
29
+ """Represent a generic billing rule."""
30
30
 
31
31
  p1_kw_year_eur: float
32
32
  p2_kw_year_eur: float
@@ -50,7 +50,7 @@ class BillingRules(BaseModel):
50
50
 
51
51
 
52
52
  class PVPCBillingRules(BillingRules):
53
- """Data structure to represent a PVPC billing rule."""
53
+ """Represent a PVPC billing rule."""
54
54
 
55
55
  p1_kw_year_eur: float = Field(default=30.67266)
56
56
  p2_kw_year_eur: float = Field(default=1.4243591)
edata/models/data.py CHANGED
@@ -6,32 +6,84 @@ from pydantic import BaseModel, Field
6
6
 
7
7
 
8
8
  class Energy(BaseModel):
9
- """Data structure to represent energy consumption and/or surplus measurements."""
9
+ """Represent energy consumption and/or surplus measurements."""
10
10
 
11
11
  datetime: datetime
12
12
  delta_h: float
13
- value_kWh: float
14
- surplus_kWh: float = Field(0)
13
+ consumption_kwh: float
14
+ surplus_kwh: float = Field(0)
15
+ generation_kwh: float = Field(0)
16
+ selfconsumption_kwh: float = Field(0)
15
17
  real: bool
16
18
 
17
19
 
18
20
  class Power(BaseModel):
19
- """Data structure to represent power measurements."""
21
+ """Represent power measurements."""
20
22
 
21
23
  datetime: datetime
22
- value_kW: float
24
+ value_kw: float
23
25
 
24
26
 
25
27
  class Statistics(BaseModel):
26
- """Data structure to represent aggregated energy/surplus data."""
28
+ """Represent aggregated energy/surplus data."""
27
29
 
28
30
  datetime: datetime
29
31
  delta_h: float = Field(0)
30
- value_kWh: float = Field(0)
31
- value_p1_kWh: float = Field(0)
32
- value_p2_kWh: float = Field(0)
33
- value_p3_kWh: float = Field(0)
34
- surplus_kWh: float = Field(0)
35
- surplus_p1_kWh: float = Field(0)
36
- surplus_p2_kWh: float = Field(0)
37
- surplus_p3_kWh: float = Field(0)
32
+ consumption_kwh: float = Field(0)
33
+ consumption_by_tariff: list[float] = Field(default_factory=lambda: [0.0, 0.0, 0.0])
34
+ surplus_kwh: float = Field(0)
35
+ surplus_by_tariff: list[float] = Field(default_factory=lambda: [0.0, 0.0, 0.0])
36
+ generation_kwh: float = Field(0)
37
+ generation_by_tariff: list[float] = Field(default_factory=lambda: [0.0, 0.0, 0.0])
38
+ selfconsumption_kwh: float = Field(0)
39
+ selfconsumption_by_tariff: list[float] = Field(
40
+ default_factory=lambda: [0.0, 0.0, 0.0]
41
+ )
42
+
43
+ @property
44
+ def consumption_p1_kwh(self) -> float:
45
+ return self.consumption_by_tariff[0]
46
+
47
+ @property
48
+ def consumption_p2_kwh(self) -> float:
49
+ return self.consumption_by_tariff[1]
50
+
51
+ @property
52
+ def consumption_p3_kwh(self) -> float:
53
+ return self.consumption_by_tariff[2]
54
+
55
+ @property
56
+ def surplus_p1_kwh(self) -> float:
57
+ return self.surplus_by_tariff[0]
58
+
59
+ @property
60
+ def surplus_p2_kwh(self) -> float:
61
+ return self.surplus_by_tariff[1]
62
+
63
+ @property
64
+ def surplus_p3_kwh(self) -> float:
65
+ return self.surplus_by_tariff[2]
66
+
67
+ @property
68
+ def generation_p1_kwh(self) -> float:
69
+ return self.generation_by_tariff[0]
70
+
71
+ @property
72
+ def generation_p2_kwh(self) -> float:
73
+ return self.generation_by_tariff[1]
74
+
75
+ @property
76
+ def generation_p3_kwh(self) -> float:
77
+ return self.generation_by_tariff[2]
78
+
79
+ @property
80
+ def selfconsumption_p1_kwh(self) -> float:
81
+ return self.selfconsumption_by_tariff[0]
82
+
83
+ @property
84
+ def selfconsumption_p2_kwh(self) -> float:
85
+ return self.selfconsumption_by_tariff[1]
86
+
87
+ @property
88
+ def selfconsumption_p3_kwh(self) -> float:
89
+ return self.selfconsumption_by_tariff[2]
edata/models/supply.py CHANGED
@@ -16,8 +16,8 @@ class Supply(BaseModel):
16
16
  province: str | None
17
17
  municipality: str | None
18
18
  distributor: str | None
19
- pointType: int
20
- distributorCode: str
19
+ point_type: int
20
+ distributor_code: str
21
21
 
22
22
 
23
23
  class Contract(BaseModel):
@@ -26,6 +26,15 @@ class Contract(BaseModel):
26
26
  date_start: datetime
27
27
  date_end: datetime
28
28
  marketer: str
29
- distributorCode: str
30
- power_p1: float | None
31
- power_p2: float | None
29
+ distributor_code: str
30
+ power: list[float]
31
+
32
+ @property
33
+ def power_p1(self) -> float | None:
34
+ """Return power P1."""
35
+ return self.power[0] if len(self.power) > 0 else None
36
+
37
+ @property
38
+ def power_p2(self) -> float | None:
39
+ """Return power P2."""
40
+ return self.power[1] if len(self.power) > 1 else None