Skip to content

Commit 38528b1

Browse files
authored
Merge pull request #95 from MyElectricalData/develop
Develop
2 parents 2c8c7fd + 1549160 commit 38528b1

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

65 files changed

+1225
-665
lines changed

CHANGELOG.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,22 @@
11
# Changelog
22

3+
## [1.6.0-dev.2](https://github.com/MyElectricalData/myelectricaldata_new/compare/1.6.0-dev.1...1.6.0-dev.2) (2025-12-22)
4+
5+
### Features
6+
7+
* **ui:** display frontend and backend versions in admin menu ([350971d](https://github.com/MyElectricalData/myelectricaldata_new/commit/350971d06ecff945a281ad1c53078ae25357ff6b))
8+
9+
### Bug Fixes
10+
11+
* **api:** add type annotation to fix mypy error in version.py ([c09d769](https://github.com/MyElectricalData/myelectricaldata_new/commit/c09d7699e34d8a391d09e6ae222d5f1dbb15b01e))
12+
13+
## [1.6.0-dev.1](https://github.com/MyElectricalData/myelectricaldata_new/compare/1.5.0...1.6.0-dev.1) (2025-12-21)
14+
15+
### Features
16+
17+
* **roles:** add persistent default roles and CRUD operations ([9c56c38](https://github.com/MyElectricalData/myelectricaldata_new/commit/9c56c386701450451e4af7a353ffc7477511ac9e))
18+
* **web:** unify notification system with custom Zustand toast ([71825f0](https://github.com/MyElectricalData/myelectricaldata_new/commit/71825f044400304767046d41382a73895e8e2ac3))
19+
320
## [1.5.0](https://github.com/MyElectricalData/myelectricaldata_new/compare/1.4.1...1.5.0) (2025-12-21)
421

522
### Features

apps/api/pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "myelectricaldata-api"
3-
version = "1.5.0"
3+
version = "1.6.0-dev.2"
44
description = "MyElectricalData API Gateway for Enedis data"
55
authors = [{name = "m4dm4rtig4n"}]
66
license = {text = "Apache-2.0"}

apps/api/src/config/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
from .settings import settings
2+
from .version import APP_VERSION
23

3-
__all__ = ["settings"]
4+
__all__ = ["settings", "APP_VERSION"]

apps/api/src/config/version.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
"""Application version management.
2+
3+
Reads version from pyproject.toml at runtime.
4+
"""
5+
6+
import tomllib
7+
from pathlib import Path
8+
9+
10+
def get_version() -> str:
11+
"""Read version from pyproject.toml.
12+
13+
Falls back to 'unknown' if file cannot be read.
14+
"""
15+
try:
16+
# Try multiple possible locations for pyproject.toml
17+
possible_paths = [
18+
Path(__file__).parent.parent.parent.parent / "pyproject.toml", # apps/api/pyproject.toml
19+
Path("/app/pyproject.toml"), # Docker container path
20+
]
21+
22+
for pyproject_path in possible_paths:
23+
if pyproject_path.exists():
24+
with open(pyproject_path, "rb") as f:
25+
data = tomllib.load(f)
26+
version: str = data.get("project", {}).get("version", "unknown")
27+
return version
28+
29+
return "unknown"
30+
except Exception:
31+
return "unknown"
32+
33+
34+
# Export version as constant for easy import
35+
APP_VERSION = get_version()

apps/api/src/main.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
from fastapi.staticfiles import StaticFiles
1010

1111
from .adapters import enedis_adapter
12-
from .config import settings
12+
from .config import APP_VERSION, settings
1313
from .logging_config import setup_logging
1414
from .models.database import init_db
1515
from .routers import (
@@ -86,7 +86,7 @@ def get_servers() -> list[dict[str, str]]:
8686
app = FastAPI(
8787
title="MyElectricalData API",
8888
description="API Gateway for Enedis data access",
89-
version="1.5.15",
89+
version=APP_VERSION,
9090
lifespan=lifespan,
9191
docs_url="/docs", # Use default docs
9292
root_path="/api",
@@ -259,7 +259,7 @@ async def root() -> dict:
259259
"""API information"""
260260
return {
261261
"name": "MyElectricalData API",
262-
"version": "1.5.15",
262+
"version": APP_VERSION,
263263
"description": "API Gateway for Enedis Linky data",
264264
"documentation": "/docs",
265265
}

apps/api/src/models/seed.py

Lines changed: 58 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -133,56 +133,73 @@ async def init_default_roles_and_permissions(db: AsyncSession) -> None:
133133
"""
134134
Initialize default roles and permissions in the database.
135135
136-
This function is idempotent - it only creates entries that don't exist yet.
137-
It will not modify existing roles or permissions.
136+
This function is truly idempotent - it creates missing permissions and roles
137+
without modifying existing ones. Each permission and role is checked individually.
138138
"""
139139
try:
140-
# Check if permissions already exist
141-
result = await db.execute(select(Permission).limit(1))
142-
existing_permissions = result.scalar_one_or_none()
140+
logger.info("[SEED] Checking default permissions and roles...")
143141

144-
if existing_permissions:
145-
logger.info("[SEED] Permissions already exist, skipping seed")
146-
return
142+
# Get existing permissions by name
143+
result = await db.execute(select(Permission))
144+
existing_permissions = {p.name: p for p in result.scalars().all()}
147145

148-
logger.info("[SEED] Initializing default permissions and roles...")
146+
# Create missing permissions
147+
permission_objects: dict[str, Permission] = dict(existing_permissions)
148+
permissions_created = 0
149149

150-
# Create permissions
151-
permission_objects = {}
152150
for perm_data in DEFAULT_PERMISSIONS:
153-
permission = Permission(
154-
name=perm_data["name"],
155-
display_name=perm_data["display_name"],
156-
description=perm_data["description"],
157-
resource=perm_data["resource"],
158-
)
159-
db.add(permission)
160-
permission_objects[perm_data["name"]] = permission
161-
logger.info(f"[SEED] Created permission: {perm_data['name']}")
162-
163-
# Flush to get permission IDs
164-
await db.flush()
165-
166-
# Create roles with their permissions
167-
for role_data in DEFAULT_ROLES:
168-
role = Role(
169-
name=role_data["name"],
170-
display_name=role_data["display_name"],
171-
description=role_data["description"],
172-
is_system=role_data["is_system"],
173-
)
151+
if perm_data["name"] not in existing_permissions:
152+
permission = Permission(
153+
name=perm_data["name"],
154+
display_name=perm_data["display_name"],
155+
description=perm_data["description"],
156+
resource=perm_data["resource"],
157+
)
158+
db.add(permission)
159+
permission_objects[perm_data["name"]] = permission
160+
permissions_created += 1
161+
logger.info(f"[SEED] Created permission: {perm_data['name']}")
162+
163+
if permissions_created > 0:
164+
await db.flush()
165+
logger.info(f"[SEED] Created {permissions_created} missing permission(s)")
166+
else:
167+
logger.info("[SEED] All permissions already exist")
174168

175-
# Assign permissions to role
176-
permissions_list = cast(list[str], role_data["permissions"])
177-
for perm_name in permissions_list:
178-
if perm_name in permission_objects:
179-
role.permissions.append(permission_objects[perm_name])
169+
# Get existing roles by name
170+
result = await db.execute(select(Role))
171+
existing_roles = {r.name: r for r in result.scalars().all()}
180172

181-
db.add(role)
182-
logger.info(f"[SEED] Created role: {role_data['name']} with {len(permissions_list)} permissions")
173+
# Create missing roles with their permissions
174+
roles_created = 0
183175

184-
await db.commit()
185-
logger.info("[SEED] Default roles and permissions initialized successfully")
176+
for role_data in DEFAULT_ROLES:
177+
if role_data["name"] not in existing_roles:
178+
role = Role(
179+
name=role_data["name"],
180+
display_name=role_data["display_name"],
181+
description=role_data["description"],
182+
is_system=role_data["is_system"],
183+
)
184+
185+
# Assign permissions to role
186+
permissions_list = cast(list[str], role_data["permissions"])
187+
for perm_name in permissions_list:
188+
if perm_name in permission_objects:
189+
role.permissions.append(permission_objects[perm_name])
190+
191+
db.add(role)
192+
roles_created += 1
193+
logger.info(f"[SEED] Created role: {role_data['name']} with {len(permissions_list)} permissions")
194+
195+
if roles_created > 0:
196+
await db.commit()
197+
logger.info(f"[SEED] Created {roles_created} missing role(s)")
198+
elif permissions_created > 0:
199+
await db.commit()
200+
logger.info("[SEED] All roles already exist, committed new permissions")
201+
else:
202+
logger.info("[SEED] All roles and permissions already exist, nothing to do")
186203

187204
except Exception as e:
188205
logger.error(f"[SEED] Error initializing roles and permissions: {e}")

0 commit comments

Comments
 (0)