espark-core 0.5.3__py3-none-any.whl → 0.5.4__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.

Potentially problematic release.


This version of espark-core might be problematic. Click here for more details.

@@ -1,18 +1,18 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: espark-core
3
- Version: 0.5.3
3
+ Version: 0.5.4
4
4
  Summary: The core module of the Espark ESP32-based IoT device management framework.
5
5
  License: MIT
6
- Requires-Python: >=3.8
6
+ Requires-Python: >=3.10
7
7
  Description-Content-Type: text/markdown
8
8
  License-File: LICENSE
9
9
  Requires-Dist: aiomqtt>=2.4.0
10
10
  Requires-Dist: apscheduler>=3.11.2
11
- Requires-Dist: fastapi>=0.127.0
11
+ Requires-Dist: fastapi>=0.128.0
12
12
  Requires-Dist: packaging>=25.0
13
13
  Requires-Dist: slack-sdk==3.39.0
14
14
  Requires-Dist: sqlalchemy>=2.0.45
15
- Requires-Dist: sqlmodel>=0.0.29
15
+ Requires-Dist: sqlmodel>=0.0.31
16
16
  Requires-Dist: uvicorn[standard]>=0.40.0
17
17
  Provides-Extra: dev
18
18
  Requires-Dist: aiosqlite==0.22.1; extra == "dev"
@@ -1,4 +1,4 @@
1
- espark_core-0.5.3.dist-info/licenses/LICENSE,sha256=wkIXJHYIspOGVvn_ajKyLucpIyw9_pO3QExFcNTCY78,1065
1
+ espark_core-0.5.4.dist-info/licenses/LICENSE,sha256=wkIXJHYIspOGVvn_ajKyLucpIyw9_pO3QExFcNTCY78,1065
2
2
  esparkcore/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
3
  esparkcore/constants.py,sha256=2Bmx62j4kykkPhcQ9ygEJ08uyVlykXMNFhpO26-Czlg,417
4
4
  esparkcore/data/__init__.py,sha256=abMOPq6jC5qOQCl9j7iPDv8iyjzDF-oVd_gW0la5kL4,45
@@ -11,21 +11,21 @@ esparkcore/data/models/telemetry.py,sha256=OfzGqGj2Y1sdK519sSOEXhfuErHoeEdpy5qmJ
11
11
  esparkcore/data/models/trigger.py,sha256=rJw3ZjWcQiT3ET3W5n_km9F0cNe2BU6GpIigZYYlJZE,986
12
12
  esparkcore/data/models/version.py,sha256=h2O_Owx-exx5tiQiiauCSELkUHDx-QZjiIGqFPybT4M,269
13
13
  esparkcore/data/repositories/__init__.py,sha256=zlTYJ_KKBqkY7KpIi1kJlCG5UA6zXlLm5wK9INLpZQY,358
14
- esparkcore/data/repositories/base_repository.py,sha256=L5APTp8SczndbWqgJyEqKe-4NQoL_YYOapUN6i572cM,2097
14
+ esparkcore/data/repositories/base_repository.py,sha256=mFoWhEwAFOHU1NwvRzWX_lpIvLgIEu9CDqq4axEv5sY,2175
15
15
  esparkcore/data/repositories/device_repository.py,sha256=P9KK-Ta9MgMdrnct5hbGEoipPOPrrDQ4wINQnZZ6iZY,1131
16
16
  esparkcore/data/repositories/notification_repository.py,sha256=N-bcprhnG6noTdhIzLMBJtCmGeMtnZw2bPx8JdjamW8,205
17
17
  esparkcore/data/repositories/outbox_repository.py,sha256=PdPyKq2Km4_7732O6HhA4BlV_zBjYF91GkMltNKUovg,1015
18
- esparkcore/data/repositories/telemetry_repository.py,sha256=9wkgE2SU6_fqJMxpnG_IOF_ONxi9qi0coy_gVSJG2q8,2105
18
+ esparkcore/data/repositories/telemetry_repository.py,sha256=zdod2C9_rJoduEiIYbRjscr1JyYGRAZ4JxAUiE9XyN4,2128
19
19
  esparkcore/data/repositories/trigger_repository.py,sha256=xHYky1ZAT2H0cLPGTjVbmnH6ZHPgFXzga2SeOx-xSNo,185
20
20
  esparkcore/data/repositories/version_repository.py,sha256=QZZX2BpFx1i0oeFJ6j0nZvlZH7cViKU8VERBE8AU3ZY,395
21
21
  esparkcore/notifications/__init__.py,sha256=WrdUPsRS0tg2rAhv4aj4eGDekkfAsKjcRPRlT6btzQs,77
22
22
  esparkcore/notifications/notifier.py,sha256=d4a8leU-oTwWe7ZTLw-vxfCrYlFnCIvbcXabiXkT6Lc,446
23
23
  esparkcore/notifications/slack_notifier.py,sha256=-H-3YYm0CRTV8pZgggrNPJKz6815HTumKvE7VsvvSAA,644
24
24
  esparkcore/routers/__init__.py,sha256=sZv812GJl-JEWJQ7IR1gAQ5BdCf5nTkfbJZXv5jZv_4,225
25
- esparkcore/routers/base_router.py,sha256=t-x8tYZVzBk9bD__9yaGYhm66glq_oM_lAHn0eplM2M,3902
26
- esparkcore/routers/device_router.py,sha256=KM6MI6BaxDbZJaqDPEi6iJYPwnOIujtAFG8J1oGRq3I,2506
25
+ esparkcore/routers/base_router.py,sha256=ELSdrwrKavqc1tYfTnNhNmYmKnCbmlWdvXyB1CBjcYg,4029
26
+ esparkcore/routers/device_router.py,sha256=iqKK1MZM9CWMw331bQvwieXE9TpbKut6BU7GvjLbM3I,2549
27
27
  esparkcore/routers/notification_router.py,sha256=vLG99eW-b9UlO9NiV1yZZeqkG4XLVv5Yj5JvHZLP6Dg,411
28
- esparkcore/routers/telemetry_router.py,sha256=3690D4603gVK3-yaJT-sAVKwhIYEYKxQZpIu84a9flg,2584
28
+ esparkcore/routers/telemetry_router.py,sha256=iPAzZf7B7qnDHfSJYtTGchKLuzdddZQodQHlPYpPOAw,2588
29
29
  esparkcore/routers/trigger_router.py,sha256=YSNYrLTQYeGToVBOlSK4PtDKpdFpesk-ZnNn_lXu6Ec,366
30
30
  esparkcore/routers/version_router.py,sha256=7HYD6sbGWow3DDvK62RXPZu5Qz8DCTTs7wkksQKBq9c,383
31
31
  esparkcore/schedules/__init__.py,sha256=v5Wy3Cbt2AnudGMGXv_Cyaa10s6NAmP66rDjcMSvf0o,74
@@ -35,7 +35,7 @@ esparkcore/services/__init__.py,sha256=RIZOHBfkWn3HsApdA5rH9EZbbXGq0NfiGoOWDO_JX
35
35
  esparkcore/services/mqtt.py,sha256=5h_mhXhFt11TyhGwNhaY08Y4tttvSTPkiR3veTqpb3A,8972
36
36
  esparkcore/utils/__init__.py,sha256=rzuCJUAaQl-yPhM1vcWB_1zVXqtA71ChYP2aM_3UfKk,42
37
37
  esparkcore/utils/logging.py,sha256=sRO8uOcPkFH-DwepqLOTS2qGaopXpMdVOjzZL6RpGs4,257
38
- espark_core-0.5.3.dist-info/METADATA,sha256=KDl4boE59BMZAwh9qW4IxL8rXGqJSglWxRRtgjmrG7w,6329
39
- espark_core-0.5.3.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
40
- espark_core-0.5.3.dist-info/top_level.txt,sha256=hXVyhIPB4aGskFm5queWALxDPhcDkrN7_8vcjwA1iE4,11
41
- espark_core-0.5.3.dist-info/RECORD,,
38
+ espark_core-0.5.4.dist-info/METADATA,sha256=XrE-QAk_K75Ij1iy13zQ8xkR3TJUU2Yf6QB-YdWN9Z0,6330
39
+ espark_core-0.5.4.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
40
+ espark_core-0.5.4.dist-info/top_level.txt,sha256=hXVyhIPB4aGskFm5queWALxDPhcDkrN7_8vcjwA1iE4,11
41
+ espark_core-0.5.4.dist-info/RECORD,,
@@ -1,6 +1,6 @@
1
1
  from typing import Generic, Optional, Sequence, Type, TypeVar
2
2
 
3
- from sqlalchemy import func, ColumnElement
3
+ from sqlalchemy import ColumnElement, func, text
4
4
  from sqlalchemy.ext.asyncio import AsyncSession
5
5
  from sqlmodel import select, SQLModel
6
6
 
@@ -41,14 +41,14 @@ class AsyncRepository(Generic[T]):
41
41
 
42
42
  return (await session.execute(query)).scalars().first()
43
43
 
44
- async def list(self, session: AsyncSession, *conditions: ColumnElement[bool], offset: Optional[int] = None, order_by=None, limit: Optional[int] = None) -> Sequence[T]:
44
+ async def list(self, session: AsyncSession, *conditions: ColumnElement[bool], offset: Optional[int] = None, order_by: ColumnElement | str = None, limit: Optional[int] = None) -> Sequence[T]:
45
45
  query = select(self.model)
46
46
 
47
47
  if conditions:
48
48
  query = query.where(*conditions)
49
49
 
50
50
  if order_by is not None:
51
- query = query.order_by(order_by)
51
+ query = query.order_by(text(order_by) if isinstance(order_by, str) else order_by)
52
52
 
53
53
  if offset is not None:
54
54
  query = query.offset(offset)
@@ -17,7 +17,7 @@ class TelemetryRepository(AsyncRepository[Telemetry]):
17
17
  results = await self.list(session, and_(Telemetry.device_id == device_id, Telemetry.data_type == data_type), order_by=Telemetry.timestamp.desc(), limit=1)
18
18
  return results[0] if results else None
19
19
 
20
- async def list(self, session: AsyncSession, *conditions: ColumnElement[bool], offset: Optional[int] = None, order_by=None, limit: Optional[int] = None) -> Sequence[Telemetry]:
20
+ async def list(self, session: AsyncSession, *conditions: ColumnElement[bool], offset: Optional[int] = None, order_by: ColumnElement | str = None, limit: Optional[int] = None) -> Sequence[Telemetry]:
21
21
  if order_by is None:
22
22
  # pylint: disable=no-member
23
23
  order_by = Telemetry.timestamp.desc()
@@ -1,6 +1,6 @@
1
1
  from typing import Generic, List, Sequence, Type, TypeVar
2
2
 
3
- from fastapi import APIRouter, Body, Depends, HTTPException, Query, status, Response
3
+ from fastapi import APIRouter, Body, Depends, HTTPException, Query, status, Response, Path
4
4
  from sqlalchemy.ext.asyncio import AsyncSession
5
5
  from sqlmodel import SQLModel
6
6
 
@@ -57,7 +57,7 @@ class BaseRouter(Generic[T]):
57
57
  return result
58
58
 
59
59
  @self.router.delete('/{id}', status_code=status.HTTP_204_NO_CONTENT)
60
- async def delete(id: str, session: AsyncSession = Depends(BaseRouter._get_session)) -> None:
60
+ async def delete(id: str = Path(..., min_length=1), session: AsyncSession = Depends(BaseRouter._get_session)) -> None:
61
61
  entity = await self.repo.get(session, self.model.id == id)
62
62
  if not entity:
63
63
  raise HTTPException(status_code=status.HTTP_404_NOT_FOUND)
@@ -67,7 +67,7 @@ class BaseRouter(Generic[T]):
67
67
  await self._after_delete(entity, session)
68
68
 
69
69
  @self.router.get('/{id}', response_model=self.model)
70
- async def get(id: str, session: AsyncSession = Depends(BaseRouter._get_session)) -> T:
70
+ async def get(id: str = Path(..., min_length=1), session: AsyncSession = Depends(BaseRouter._get_session)) -> T:
71
71
  entity = await self.repo.get(session, self.model.id == id)
72
72
  if not entity:
73
73
  raise HTTPException(status_code=status.HTTP_404_NOT_FOUND)
@@ -75,13 +75,13 @@ class BaseRouter(Generic[T]):
75
75
  return entity
76
76
 
77
77
  @self.router.get('/', response_model=List[self.model])
78
- async def list(response: Response, session: AsyncSession = Depends(BaseRouter._get_session), offset: int = Query(None, ge=0), limit: int = Query(None, ge=1, le=100)) -> Sequence[T]:
78
+ async def list(response: Response, session: AsyncSession = Depends(BaseRouter._get_session), order_by: str = Query(None), offset: int = Query(0, ge=0), limit: int = Query(10, ge=1, le=100)) -> Sequence[T]:
79
79
  response.headers['X-Total-Count'] = str(await self.repo.count(session))
80
80
 
81
- return await self.repo.list(session, offset=offset, limit=limit)
81
+ return await self.repo.list(session, order_by=order_by, offset=offset, limit=limit)
82
82
 
83
83
  @self.router.put('/{id}', response_model=self.model)
84
- async def update(id: str, data: dict = Body(...), session: AsyncSession = Depends(BaseRouter._get_session)) -> T:
84
+ async def update(id: str = Path(..., min_length=1), data: dict = Body(...), session: AsyncSession = Depends(BaseRouter._get_session)) -> T:
85
85
  entity = await self.repo.get(session, self.model.id == id)
86
86
  if not entity:
87
87
  raise HTTPException(status_code=status.HTTP_404_NOT_FOUND)
@@ -31,10 +31,10 @@ class DeviceRouter(BaseRouter):
31
31
 
32
32
  def _setup_routes(self) -> None:
33
33
  @self.router.get('/all')
34
- async def list_all(response: Response, session: AsyncSession = Depends(BaseRouter._get_session), offset: int = Query(None, ge=0), limit: int = Query(None, ge=1, le=100)):
34
+ async def list_all(response: Response, session: AsyncSession = Depends(BaseRouter._get_session), order_by: str = Query(None), offset: int = Query(0, ge=0), limit: int = Query(10, ge=1, le=100)):
35
35
  response.headers['X-Total-Count'] = str(await self.repo.count(session))
36
36
 
37
- devices = await self.repo.list(session, offset=offset, limit=limit)
37
+ devices = await self.repo.list(session, order_by=order_by, offset=offset, limit=limit)
38
38
  devices_with_telemetry = []
39
39
  telemetry_repo = TelemetryRepository()
40
40
 
@@ -18,7 +18,7 @@ class TelemetryRouter(BaseRouter):
18
18
 
19
19
  def _setup_routes(self) -> None:
20
20
  @self.router.get('/history', response_model=Sequence[Telemetry])
21
- async def list_history(response: Response, session: AsyncSession = Depends(BaseRouter._get_session), device_id: int = Query(..., ge=1), offset: int = Query(..., min=0)) -> Sequence[Telemetry]:
21
+ async def list_history(response: Response, session: AsyncSession = Depends(BaseRouter._get_session), device_id: str = Query(..., min_length=1), offset: int = Query(0, min=0)) -> Sequence[Telemetry]:
22
22
  from_date = datetime.now(timezone.utc) - timedelta(seconds=offset)
23
23
  results = await self.repo.list(session, and_(Telemetry.device_id == device_id, Telemetry.timestamp >= from_date))
24
24
 
@@ -27,7 +27,7 @@ class TelemetryRouter(BaseRouter):
27
27
  return results
28
28
 
29
29
  @self.router.get('/recent', response_model=Sequence[Telemetry])
30
- async def list_recent(response: Response, session: AsyncSession = Depends(BaseRouter._get_session), offset: int = Query(..., min=0)) -> Sequence[Telemetry]:
30
+ async def list_recent(response: Response, session: AsyncSession = Depends(BaseRouter._get_session), offset: int = Query(0, min=0)) -> Sequence[Telemetry]:
31
31
  from_date = datetime.now(timezone.utc) - timedelta(seconds=offset)
32
32
  device_repo = DeviceRepository()
33
33
  # pylint: disable=no-member