Browse Source

bump dependencies

clovis 2 months ago
parent
commit
ff12e4672c
11 changed files with 581 additions and 573 deletions
  1. 2 0
      Readme.md
  2. 0 1
      app/api/deps.py
  3. 19 4
      app/api/endpoints/sms.py
  4. 1 2
      app/api/endpoints/users.py
  5. 4 2
      app/core/config.py
  6. 19 8
      app/tests/test_sms.py
  7. 428 443
      poetry.lock
  8. 8 9
      pyproject.toml
  9. 44 55
      requirements-dev.txt
  10. 29 49
      requirements.txt
  11. 27 0
      update_password.py

+ 2 - 0
Readme.md

@@ -33,9 +33,11 @@ Run specific tests
 
 ## Update requirements
 
+```
 > poetry lock
 > poetry export -f requirements.txt --output requirements.txt --without-hashes
 > poetry export -f requirements.txt --output requirements-dev.txt --without-hashes --with dev
+```
 
 ## Credit
 

+ 0 - 1
app/api/deps.py

@@ -1,5 +1,4 @@
 import time
-from collections.abc import AsyncGenerator
 from typing import Generator
 
 import jwt

+ 19 - 4
app/api/endpoints/sms.py

@@ -1,7 +1,8 @@
+from typing import Annotated
 import datetime
 from uuid import UUID
 
-from fastapi import APIRouter, Depends, HTTPException
+from fastapi import APIRouter, Depends, HTTPException, Query
 from sqlalchemy import delete, select
 from sqlalchemy.orm import Session
 
@@ -76,7 +77,9 @@ async def delete_sms(
     session: Session = Depends(deps.get_session),
 ):
     """Delete a sms from the project"""
-    session.execute(delete(Sms).where((Sms.id == sms_id) & (Sms.project_id == str(project_id))))
+    session.execute(
+        delete(Sms).where((Sms.id == sms_id) & (Sms.project_id == str(project_id)))
+    )
     session.commit()
 
 
@@ -84,10 +87,20 @@ async def delete_sms(
 async def list_sms_to_send(
     current_user: User = Depends(deps.get_current_user),
     session: Session = Depends(deps.get_session),
+    max_delay: Annotated[
+        int | None, Query(description="the maximum delay a sms should be send with")
+    ] = 10,
 ):
     """List sms that should be send by now"""
+
+    now = datetime.datetime.now()
+    min_sending_time = now - datetime.timedelta(minutes=max_delay)
     results = session.execute(
-        select(Sms).where((Sms.sending_time < datetime.datetime.now()) & (Sms.send_time == None))
+        select(Sms).where(
+            (Sms.sending_time > min_sending_time)
+            & (Sms.sending_time < now)
+            & (Sms.send_time == None)
+        )
     )
     return results.scalars().all()
 
@@ -126,5 +139,7 @@ async def list_future_sms(
     session: Session = Depends(deps.get_session),
 ):
     """List sms that should be sent in the future"""
-    results = session.execute(select(Sms).where(Sms.sending_time > datetime.datetime.now()))
+    results = session.execute(
+        select(Sms).where(Sms.sending_time > datetime.datetime.now())
+    )
     return results.scalars().all()

+ 1 - 2
app/api/endpoints/users.py

@@ -11,7 +11,6 @@ from app.schemas.responses import UserResponse
 router = APIRouter()
 
 
-
 @router.get("", response_model=list[UserResponse])
 async def list_users(
     current_user: User = Depends(deps.get_current_user),
@@ -39,7 +38,7 @@ async def delete_current_user(
     session.commit()
 
 
-@router.post("/reset-password", response_model=UserResponse)
+@router.post("/update-password", response_model=UserResponse)
 async def reset_current_user_password(
     user_update_password: UserUpdatePasswordRequest,
     session: Session = Depends(deps.get_session),

+ 4 - 2
app/core/config.py

@@ -20,7 +20,7 @@ import tomllib
 from pathlib import Path
 from typing import Literal
 
-from pydantic import AnyHttpUrl, EmailStr, PostgresDsn, field_validator, validator
+from pydantic import AnyHttpUrl, EmailStr
 from pydantic_settings import BaseSettings, SettingsConfigDict
 
 PROJECT_DIR = Path(__file__).parent.parent.parent
@@ -93,7 +93,9 @@ class Settings(BaseSettings):
     FIRST_SUPERUSER_EMAIL: EmailStr
     FIRST_SUPERUSER_PASSWORD: str
 
-    model_config = SettingsConfigDict(env_file=f"{PROJECT_DIR}/.env", case_sensitive=True)
+    model_config = SettingsConfigDict(
+        env_file=f"{PROJECT_DIR}/.env", case_sensitive=True
+    )
 
 
 settings: Settings = Settings()  # type: ignore

+ 19 - 8
app/tests/test_sms.py

@@ -5,7 +5,7 @@ from sqlalchemy import select
 from sqlalchemy.orm import Session
 
 from app.main import app
-from app.models import Project, Sms, Sms, Volunteer
+from app.models import Project, Sms, Volunteer
 from app.tests.conftest import default_project_id, default_volunteer_id, default_sms_id
 
 
@@ -44,9 +44,10 @@ async def test_create_sms(
     session: Session,
 ):
     # Test without autentication
-    response = await client.post(app.url_path_for("create_sms", project_id=default_project_id))
+    response = await client.post(
+        app.url_path_for("create_sms", project_id=default_project_id)
+    )
     assert response.status_code == 401
-    starting_time = datetime(1900, 1, 1)
     payload = {"phone_number": "06 75 75 75 75 ", "content": "sms_content"}
     # test invalid project_id
     response = await client.post(
@@ -66,7 +67,9 @@ async def test_create_sms(
     assert response.status_code == 200
     assert response.json()["content"] == "sms_content"
     result = session.execute(
-        select(Sms).where((Sms.project_id == default_project_id) & (Sms.id != default_sms_id))
+        select(Sms).where(
+            (Sms.project_id == default_project_id) & (Sms.id != default_sms_id)
+        )
     )
     sms = result.scalars().first()
 
@@ -210,14 +213,18 @@ async def test_delete_sms(
 
     # can delete random uuid
     response = await client.delete(
-        app.url_path_for("delete_sms", project_id=default_project_id, sms_id=uuid.uuid4()),
+        app.url_path_for(
+            "delete_sms", project_id=default_project_id, sms_id=uuid.uuid4()
+        ),
         headers=default_user_headers,
     )
     assert response.status_code == 200
 
     # Cannot delete non uuid string
     response = await client.delete(
-        app.url_path_for("delete_sms", project_id=default_project_id, sms_id="not uidstr"),
+        app.url_path_for(
+            "delete_sms", project_id=default_project_id, sms_id="not uidstr"
+        ),
         headers=default_user_headers,
     )
     assert response.status_code == 422
@@ -229,7 +236,9 @@ async def test_delete_sms_with_volunteer(
     session: Session,
     default_public_project: Project,
 ):
-    result = session.execute(select(Volunteer).where(Volunteer.id == default_volunteer_id))
+    result = session.execute(
+        select(Volunteer).where(Volunteer.id == default_volunteer_id)
+    )
     volunteer = result.scalars().first()
     assert volunteer is not None
     assert len(volunteer.sms) == 0
@@ -258,7 +267,9 @@ async def test_delete_sms_with_volunteer(
     session.refresh(volunteer)
     assert len(volunteer.sms) == 0, "Sms should be deleted"
 
-    result = session.execute(select(Volunteer).where(Volunteer.id == default_volunteer_id))
+    result = session.execute(
+        select(Volunteer).where(Volunteer.id == default_volunteer_id)
+    )
     volunteer_result = result.scalars().first()
     assert volunteer_result is not None, "Volunteer should not be deleted"
     assert len(volunteer_result.sms) == 0, "No more sms associated to volunteer"

File diff suppressed because it is too large
+ 428 - 443
poetry.lock


+ 8 - 9
pyproject.toml

@@ -2,33 +2,32 @@
 authors = ["clovis jaquin <clovis@jaquin.fr>"]
 description = "FastAPI project that can parse gsheet planning for brass dans la garonne event and manage creating automatic SMS notification for volunteer"
 name = "bdlg-2023"
-version = "0.2.2"
+version = "0.2.3"
 
 [tool.poetry.dependencies]
-fastapi = "^0.111.0"
+fastapi = "0.116.*"
 pyjwt = {extras = ["crypto"], version = "^2.6.0"}
 python = "^3.11"
 python-multipart = "^0.0.9"
-sqlalchemy = "^2.0.1"
+sqlalchemy = "2.0.*"
 alembic = "^1.9.2"
 numpy = '<2.0.0'
 passlib = {extras = ["bcrypt"], version = "^1.7.4"}
-pydantic = {extras = ["dotenv", "email"], version = "^2.7.1"}
+pydantic = {extras = ["dotenv", "email"], version = "2.11.*"}
 pandas = "2.1.0"
 pandera = "0.20.3"
-requests = "2.31.0"
+requests = "2.32.*"
 pydantic-settings = "^2.2.1"
 psycopg2 = "^2.9.9"
 
 [tool.poetry.group.dev.dependencies]
-autoflake = "^2.0.1"
 coverage = "^7.1.0"
-httpx = "^0.23.3"
+httpx = "*"
 pytest = "^7.2.1"
 pytest-asyncio = "^0.20.3"
-uvicorn = {extras = ["standard"], version = "^0.20.0"}
+uvicorn = {extras = ["standard"], version = "*"}
 pre-commit = "^3.0.4"
-ruff = "^0.4.7"
+ruff = "*"
 
 [build-system]
 build-backend = "poetry.core.masonry.api"

+ 44 - 55
requirements-dev.txt

@@ -1,81 +1,70 @@
-alembic==1.15.1 ; python_version >= "3.11" and python_version < "4.0"
+alembic==1.16.5 ; python_version >= "3.11" and python_version < "4.0"
 annotated-types==0.7.0 ; python_version >= "3.11" and python_version < "4.0"
-anyio==4.8.0 ; python_version >= "3.11" and python_version < "4.0"
-autoflake==2.3.1 ; python_version >= "3.11" and python_version < "4.0"
+anyio==4.10.0 ; python_version >= "3.11" and python_version < "4.0"
 bcrypt==4.3.0 ; python_version >= "3.11" and python_version < "4.0"
-certifi==2025.1.31 ; python_version >= "3.11" and python_version < "4.0"
-cffi==1.17.1 ; python_version >= "3.11" and python_version < "4.0" and platform_python_implementation != "PyPy"
+certifi==2025.8.3 ; python_version >= "3.11" and python_version < "4.0"
+cffi==2.0.0 ; python_version >= "3.11" and platform_python_implementation != "PyPy" and python_version < "4.0"
 cfgv==3.4.0 ; python_version >= "3.11" and python_version < "4.0"
-charset-normalizer==3.4.1 ; python_version >= "3.11" and python_version < "4.0"
-click==8.1.8 ; python_version >= "3.11" and python_version < "4.0"
+charset-normalizer==3.4.3 ; python_version >= "3.11" and python_version < "4.0"
+click==8.2.1 ; python_version >= "3.11" and python_version < "4.0"
 colorama==0.4.6 ; python_version >= "3.11" and python_version < "4.0" and (sys_platform == "win32" or platform_system == "Windows")
-coverage==7.6.12 ; python_version >= "3.11" and python_version < "4.0"
-cryptography==44.0.2 ; python_version >= "3.11" and python_version < "4.0"
-distlib==0.3.9 ; python_version >= "3.11" and python_version < "4.0"
-dnspython==2.7.0 ; python_version >= "3.11" and python_version < "4.0"
-email-validator==2.2.0 ; python_version >= "3.11" and python_version < "4.0"
-fastapi-cli==0.0.7 ; python_version >= "3.11" and python_version < "4.0"
-fastapi==0.111.1 ; python_version >= "3.11" and python_version < "4.0"
-filelock==3.17.0 ; python_version >= "3.11" and python_version < "4.0"
-greenlet==3.1.1 ; python_version < "3.14" and (platform_machine == "win32" or platform_machine == "WIN32" or platform_machine == "AMD64" or platform_machine == "amd64" or platform_machine == "x86_64" or platform_machine == "ppc64le" or platform_machine == "aarch64") and python_version >= "3.11"
-h11==0.14.0 ; python_version >= "3.11" and python_version < "4.0"
-httpcore==0.16.3 ; python_version >= "3.11" and python_version < "4.0"
+coverage==7.10.6 ; python_version >= "3.11" and python_version < "4.0"
+cryptography==46.0.1 ; python_version >= "3.11" and python_version < "4.0"
+distlib==0.4.0 ; python_version >= "3.11" and python_version < "4.0"
+dnspython==2.8.0 ; python_version >= "3.11" and python_version < "4.0"
+email-validator==2.3.0 ; python_version >= "3.11" and python_version < "4.0"
+fastapi==0.116.2 ; python_version >= "3.11" and python_version < "4.0"
+filelock==3.19.1 ; python_version >= "3.11" and python_version < "4.0"
+greenlet==3.2.4 ; python_version < "3.14" and (platform_machine == "aarch64" or platform_machine == "ppc64le" or platform_machine == "x86_64" or platform_machine == "amd64" or platform_machine == "AMD64" or platform_machine == "win32" or platform_machine == "WIN32") and python_version >= "3.11"
+h11==0.16.0 ; python_version >= "3.11" and python_version < "4.0"
+httpcore==1.0.9 ; python_version >= "3.11" and python_version < "4.0"
 httptools==0.6.4 ; python_version >= "3.11" and python_version < "4.0"
-httpx==0.23.3 ; python_version >= "3.11" and python_version < "4.0"
-identify==2.6.8 ; python_version >= "3.11" and python_version < "4.0"
+httpx==0.28.1 ; python_version >= "3.11" and python_version < "4.0"
+identify==2.6.14 ; python_version >= "3.11" and python_version < "4.0"
 idna==3.10 ; python_version >= "3.11" and python_version < "4.0"
-iniconfig==2.0.0 ; python_version >= "3.11" and python_version < "4.0"
-jinja2==3.1.6 ; python_version >= "3.11" and python_version < "4.0"
-mako==1.3.9 ; python_version >= "3.11" and python_version < "4.0"
-markdown-it-py==3.0.0 ; python_version >= "3.11" and python_version < "4.0"
+iniconfig==2.1.0 ; python_version >= "3.11" and python_version < "4.0"
+mako==1.3.10 ; python_version >= "3.11" and python_version < "4.0"
 markupsafe==3.0.2 ; python_version >= "3.11" and python_version < "4.0"
-mdurl==0.1.2 ; python_version >= "3.11" and python_version < "4.0"
 multimethod==1.10 ; python_version >= "3.11" and python_version < "4.0"
-mypy-extensions==1.0.0 ; python_version >= "3.11" and python_version < "4.0"
+mypy-extensions==1.1.0 ; python_version >= "3.11" and python_version < "4.0"
 nodeenv==1.9.1 ; python_version >= "3.11" and python_version < "4.0"
 numpy==1.26.4 ; python_version >= "3.11" and python_version < "4.0"
-packaging==24.2 ; python_version >= "3.11" and python_version < "4.0"
+packaging==25.0 ; python_version >= "3.11" and python_version < "4.0"
 pandas==2.1.0 ; python_version >= "3.11" and python_version < "4.0"
 pandera==0.20.3 ; python_version >= "3.11" and python_version < "4.0"
 passlib[bcrypt]==1.7.4 ; python_version >= "3.11" and python_version < "4.0"
-platformdirs==4.3.6 ; python_version >= "3.11" and python_version < "4.0"
-pluggy==1.5.0 ; python_version >= "3.11" and python_version < "4.0"
+platformdirs==4.4.0 ; python_version >= "3.11" and python_version < "4.0"
+pluggy==1.6.0 ; python_version >= "3.11" and python_version < "4.0"
 pre-commit==3.8.0 ; python_version >= "3.11" and python_version < "4.0"
 psycopg2==2.9.10 ; python_version >= "3.11" and python_version < "4.0"
-pycparser==2.22 ; python_version >= "3.11" and python_version < "4.0" and platform_python_implementation != "PyPy"
-pydantic-core==2.27.2 ; python_version >= "3.11" and python_version < "4.0"
-pydantic-settings==2.8.1 ; python_version >= "3.11" and python_version < "4.0"
-pydantic==2.10.6 ; python_version >= "3.11" and python_version < "4.0"
-pydantic[dotenv,email]==2.10.6 ; python_version >= "3.11" and python_version < "4.0"
-pyflakes==3.2.0 ; python_version >= "3.11" and python_version < "4.0"
-pygments==2.19.1 ; python_version >= "3.11" and python_version < "4.0"
+pycparser==2.23 ; python_version >= "3.11" and platform_python_implementation != "PyPy" and python_version < "4.0" and implementation_name != "PyPy"
+pydantic-core==2.33.2 ; python_version >= "3.11" and python_version < "4.0"
+pydantic-settings==2.10.1 ; python_version >= "3.11" and python_version < "4.0"
+pydantic==2.11.9 ; python_version >= "3.11" and python_version < "4.0"
+pydantic[dotenv,email]==2.11.9 ; python_version >= "3.11" and python_version < "4.0"
 pyjwt[crypto]==2.10.1 ; python_version >= "3.11" and python_version < "4.0"
 pytest-asyncio==0.20.3 ; python_version >= "3.11" and python_version < "4.0"
 pytest==7.4.4 ; python_version >= "3.11" and python_version < "4.0"
 python-dateutil==2.9.0.post0 ; python_version >= "3.11" and python_version < "4.0"
-python-dotenv==1.0.1 ; python_version >= "3.11" and python_version < "4.0"
+python-dotenv==1.1.1 ; python_version >= "3.11" and python_version < "4.0"
 python-multipart==0.0.9 ; python_version >= "3.11" and python_version < "4.0"
-pytz==2025.1 ; python_version >= "3.11" and python_version < "4.0"
+pytz==2025.2 ; python_version >= "3.11" and python_version < "4.0"
 pyyaml==6.0.2 ; python_version >= "3.11" and python_version < "4.0"
-requests==2.31.0 ; python_version >= "3.11" and python_version < "4.0"
-rfc3986[idna2008]==1.5.0 ; python_version >= "3.11" and python_version < "4.0"
-rich-toolkit==0.13.2 ; python_version >= "3.11" and python_version < "4.0"
-rich==13.9.4 ; python_version >= "3.11" and python_version < "4.0"
-ruff==0.4.10 ; python_version >= "3.11" and python_version < "4.0"
-shellingham==1.5.4 ; python_version >= "3.11" and python_version < "4.0"
+requests==2.32.5 ; python_version >= "3.11" and python_version < "4.0"
+ruff==0.13.0 ; python_version >= "3.11" and python_version < "4.0"
 six==1.17.0 ; python_version >= "3.11" and python_version < "4.0"
 sniffio==1.3.1 ; python_version >= "3.11" and python_version < "4.0"
-sqlalchemy==2.0.38 ; python_version >= "3.11" and python_version < "4.0"
-starlette==0.37.2 ; python_version >= "3.11" and python_version < "4.0"
-typeguard==4.4.2 ; python_version >= "3.11" and python_version < "4.0"
-typer==0.15.2 ; python_version >= "3.11" and python_version < "4.0"
-typing-extensions==4.12.2 ; python_version >= "3.11" and python_version < "4.0"
+sqlalchemy==2.0.43 ; python_version >= "3.11" and python_version < "4.0"
+starlette==0.48.0 ; python_version >= "3.11" and python_version < "4.0"
+typeguard==4.4.4 ; python_version >= "3.11" and python_version < "4.0"
+typing-extensions==4.15.0 ; python_version >= "3.11" and python_version < "4.0"
 typing-inspect==0.9.0 ; python_version >= "3.11" and python_version < "4.0"
-tzdata==2025.1 ; python_version >= "3.11" and python_version < "4.0"
-urllib3==2.3.0 ; python_version >= "3.11" and python_version < "4.0"
-uvicorn[standard]==0.20.0 ; python_version >= "3.11" and python_version < "4.0"
+typing-inspection==0.4.1 ; python_version >= "3.11" and python_version < "4.0"
+tzdata==2025.2 ; python_version >= "3.11" and python_version < "4.0"
+urllib3==2.5.0 ; python_version >= "3.11" and python_version < "4.0"
+uvicorn[standard]==0.35.0 ; python_version >= "3.11" and python_version < "4.0"
 uvloop==0.21.0 ; (sys_platform != "win32" and sys_platform != "cygwin") and platform_python_implementation != "PyPy" and python_version >= "3.11" and python_version < "4.0"
-virtualenv==20.29.3 ; python_version >= "3.11" and python_version < "4.0"
-watchfiles==1.0.4 ; python_version >= "3.11" and python_version < "4.0"
+virtualenv==20.34.0 ; python_version >= "3.11" and python_version < "4.0"
+watchfiles==1.1.0 ; python_version >= "3.11" and python_version < "4.0"
 websockets==15.0.1 ; python_version >= "3.11" and python_version < "4.0"
-wrapt==1.17.2 ; python_version >= "3.11" and python_version < "4.0"
+wrapt==1.17.3 ; python_version >= "3.11" and python_version < "4.0"

+ 29 - 49
requirements.txt

@@ -1,65 +1,45 @@
-alembic==1.15.1 ; python_version >= "3.11" and python_version < "4.0"
+alembic==1.16.5 ; python_version >= "3.11" and python_version < "4.0"
 annotated-types==0.7.0 ; python_version >= "3.11" and python_version < "4.0"
-anyio==4.8.0 ; python_version >= "3.11" and python_version < "4.0"
+anyio==4.10.0 ; python_version >= "3.11" and python_version < "4.0"
 bcrypt==4.3.0 ; python_version >= "3.11" and python_version < "4.0"
-certifi==2025.1.31 ; python_version >= "3.11" and python_version < "4.0"
-cffi==1.17.1 ; python_version >= "3.11" and python_version < "4.0" and platform_python_implementation != "PyPy"
-charset-normalizer==3.4.1 ; python_version >= "3.11" and python_version < "4.0"
-click==8.1.8 ; python_version >= "3.11" and python_version < "4.0"
-colorama==0.4.6 ; python_version >= "3.11" and python_version < "4.0" and platform_system == "Windows"
-cryptography==44.0.2 ; python_version >= "3.11" and python_version < "4.0"
-dnspython==2.7.0 ; python_version >= "3.11" and python_version < "4.0"
-email-validator==2.2.0 ; python_version >= "3.11" and python_version < "4.0"
-fastapi-cli==0.0.7 ; python_version >= "3.11" and python_version < "4.0"
-fastapi==0.111.1 ; python_version >= "3.11" and python_version < "4.0"
-greenlet==3.1.1 ; python_version < "3.14" and (platform_machine == "win32" or platform_machine == "WIN32" or platform_machine == "AMD64" or platform_machine == "amd64" or platform_machine == "x86_64" or platform_machine == "ppc64le" or platform_machine == "aarch64") and python_version >= "3.11"
-h11==0.14.0 ; python_version >= "3.11" and python_version < "4.0"
-httpcore==0.16.3 ; python_version >= "3.11" and python_version < "4.0"
-httptools==0.6.4 ; python_version >= "3.11" and python_version < "4.0"
-httpx==0.23.3 ; python_version >= "3.11" and python_version < "4.0"
+certifi==2025.8.3 ; python_version >= "3.11" and python_version < "4.0"
+cffi==2.0.0 ; python_version >= "3.11" and platform_python_implementation != "PyPy" and python_version < "4.0"
+charset-normalizer==3.4.3 ; python_version >= "3.11" and python_version < "4.0"
+cryptography==46.0.1 ; python_version >= "3.11" and python_version < "4.0"
+dnspython==2.8.0 ; python_version >= "3.11" and python_version < "4.0"
+email-validator==2.3.0 ; python_version >= "3.11" and python_version < "4.0"
+fastapi==0.116.2 ; python_version >= "3.11" and python_version < "4.0"
+greenlet==3.2.4 ; python_version < "3.14" and (platform_machine == "aarch64" or platform_machine == "ppc64le" or platform_machine == "x86_64" or platform_machine == "amd64" or platform_machine == "AMD64" or platform_machine == "win32" or platform_machine == "WIN32") and python_version >= "3.11"
 idna==3.10 ; python_version >= "3.11" and python_version < "4.0"
-jinja2==3.1.6 ; python_version >= "3.11" and python_version < "4.0"
-mako==1.3.9 ; python_version >= "3.11" and python_version < "4.0"
-markdown-it-py==3.0.0 ; python_version >= "3.11" and python_version < "4.0"
+mako==1.3.10 ; python_version >= "3.11" and python_version < "4.0"
 markupsafe==3.0.2 ; python_version >= "3.11" and python_version < "4.0"
-mdurl==0.1.2 ; python_version >= "3.11" and python_version < "4.0"
 multimethod==1.10 ; python_version >= "3.11" and python_version < "4.0"
-mypy-extensions==1.0.0 ; python_version >= "3.11" and python_version < "4.0"
+mypy-extensions==1.1.0 ; python_version >= "3.11" and python_version < "4.0"
 numpy==1.26.4 ; python_version >= "3.11" and python_version < "4.0"
-packaging==24.2 ; python_version >= "3.11" and python_version < "4.0"
+packaging==25.0 ; python_version >= "3.11" and python_version < "4.0"
 pandas==2.1.0 ; python_version >= "3.11" and python_version < "4.0"
 pandera==0.20.3 ; python_version >= "3.11" and python_version < "4.0"
 passlib[bcrypt]==1.7.4 ; python_version >= "3.11" and python_version < "4.0"
 psycopg2==2.9.10 ; python_version >= "3.11" and python_version < "4.0"
-pycparser==2.22 ; python_version >= "3.11" and python_version < "4.0" and platform_python_implementation != "PyPy"
-pydantic-core==2.27.2 ; python_version >= "3.11" and python_version < "4.0"
-pydantic-settings==2.8.1 ; python_version >= "3.11" and python_version < "4.0"
-pydantic==2.10.6 ; python_version >= "3.11" and python_version < "4.0"
-pydantic[dotenv,email]==2.10.6 ; python_version >= "3.11" and python_version < "4.0"
-pygments==2.19.1 ; python_version >= "3.11" and python_version < "4.0"
+pycparser==2.23 ; python_version >= "3.11" and platform_python_implementation != "PyPy" and python_version < "4.0" and implementation_name != "PyPy"
+pydantic-core==2.33.2 ; python_version >= "3.11" and python_version < "4.0"
+pydantic-settings==2.10.1 ; python_version >= "3.11" and python_version < "4.0"
+pydantic==2.11.9 ; python_version >= "3.11" and python_version < "4.0"
+pydantic[dotenv,email]==2.11.9 ; python_version >= "3.11" and python_version < "4.0"
 pyjwt[crypto]==2.10.1 ; python_version >= "3.11" and python_version < "4.0"
 python-dateutil==2.9.0.post0 ; python_version >= "3.11" and python_version < "4.0"
-python-dotenv==1.0.1 ; python_version >= "3.11" and python_version < "4.0"
+python-dotenv==1.1.1 ; python_version >= "3.11" and python_version < "4.0"
 python-multipart==0.0.9 ; python_version >= "3.11" and python_version < "4.0"
-pytz==2025.1 ; python_version >= "3.11" and python_version < "4.0"
-pyyaml==6.0.2 ; python_version >= "3.11" and python_version < "4.0"
-requests==2.31.0 ; python_version >= "3.11" and python_version < "4.0"
-rfc3986[idna2008]==1.5.0 ; python_version >= "3.11" and python_version < "4.0"
-rich-toolkit==0.13.2 ; python_version >= "3.11" and python_version < "4.0"
-rich==13.9.4 ; python_version >= "3.11" and python_version < "4.0"
-shellingham==1.5.4 ; python_version >= "3.11" and python_version < "4.0"
+pytz==2025.2 ; python_version >= "3.11" and python_version < "4.0"
+requests==2.32.5 ; python_version >= "3.11" and python_version < "4.0"
 six==1.17.0 ; python_version >= "3.11" and python_version < "4.0"
 sniffio==1.3.1 ; python_version >= "3.11" and python_version < "4.0"
-sqlalchemy==2.0.38 ; python_version >= "3.11" and python_version < "4.0"
-starlette==0.37.2 ; python_version >= "3.11" and python_version < "4.0"
-typeguard==4.4.2 ; python_version >= "3.11" and python_version < "4.0"
-typer==0.15.2 ; python_version >= "3.11" and python_version < "4.0"
-typing-extensions==4.12.2 ; python_version >= "3.11" and python_version < "4.0"
+sqlalchemy==2.0.43 ; python_version >= "3.11" and python_version < "4.0"
+starlette==0.48.0 ; python_version >= "3.11" and python_version < "4.0"
+typeguard==4.4.4 ; python_version >= "3.11" and python_version < "4.0"
+typing-extensions==4.15.0 ; python_version >= "3.11" and python_version < "4.0"
 typing-inspect==0.9.0 ; python_version >= "3.11" and python_version < "4.0"
-tzdata==2025.1 ; python_version >= "3.11" and python_version < "4.0"
-urllib3==2.3.0 ; python_version >= "3.11" and python_version < "4.0"
-uvicorn[standard]==0.20.0 ; python_version >= "3.11" and python_version < "4.0"
-uvloop==0.21.0 ; (sys_platform != "win32" and sys_platform != "cygwin") and platform_python_implementation != "PyPy" and python_version >= "3.11" and python_version < "4.0"
-watchfiles==1.0.4 ; python_version >= "3.11" and python_version < "4.0"
-websockets==15.0.1 ; python_version >= "3.11" and python_version < "4.0"
-wrapt==1.17.2 ; python_version >= "3.11" and python_version < "4.0"
+typing-inspection==0.4.1 ; python_version >= "3.11" and python_version < "4.0"
+tzdata==2025.2 ; python_version >= "3.11" and python_version < "4.0"
+urllib3==2.5.0 ; python_version >= "3.11" and python_version < "4.0"
+wrapt==1.17.3 ; python_version >= "3.11" and python_version < "4.0"

+ 27 - 0
update_password.py

@@ -0,0 +1,27 @@
+import argparse
+
+from sqlalchemy import select
+from app.core.security import get_password_hash
+from app.core.session import session
+from app.models import User
+
+
+def update_user_pwd(email: str, new_password: str):
+    with session() as db:
+        user = db.execute(select(User).where(User.email == email)).scalars().first()
+        if user is not None:
+            user.hashed_password = get_password_hash(new_password)
+            db.commit()
+        else:
+            raise ValueError(f"User {email} not known !")
+
+
+if __name__ == "__main__":
+    parser = argparse.ArgumentParser(
+        prog="BDLGPlanner password manager",
+        description="This program help admin to update password from users that forgot it",
+    )
+    parser.add_argument("email")
+    parser.add_argument("password")
+    args = parser.parse_args()
+    update_user_pwd(args.email, args.password)

Some files were not shown because too many files changed in this diff