diff --git a/backend/core/__pycache__/celery_app.cpython-312.pyc b/backend/core/__pycache__/celery_app.cpython-312.pyc index 39f3ce1..1a911f9 100644 Binary files a/backend/core/__pycache__/celery_app.cpython-312.pyc and b/backend/core/__pycache__/celery_app.cpython-312.pyc differ diff --git a/backend/core/__pycache__/config.cpython-312.pyc b/backend/core/__pycache__/config.cpython-312.pyc index 10a0ab4..7ed7cd5 100644 Binary files a/backend/core/__pycache__/config.cpython-312.pyc and b/backend/core/__pycache__/config.cpython-312.pyc differ diff --git a/backend/core/__pycache__/database.cpython-312.pyc b/backend/core/__pycache__/database.cpython-312.pyc index 29f2ec8..3fd3b46 100644 Binary files a/backend/core/__pycache__/database.cpython-312.pyc and b/backend/core/__pycache__/database.cpython-312.pyc differ diff --git a/backend/core/celery_app.py b/backend/core/celery_app.py index 1bb2eb4..9eced90 100644 --- a/backend/core/celery_app.py +++ b/backend/core/celery_app.py @@ -1,7 +1,6 @@ # core/celery_app.py from celery import Celery -from core.config import settings # Import your settings - +from core.config import settings celery_app = Celery( "worker", broker=settings.REDIS_URL, @@ -9,9 +8,5 @@ celery_app = Celery( include=[ "modules.auth.tasks", "modules.admin.tasks", - ], # Add paths to modules containing tasks - # Add other modules with tasks here, e.g., "modules.some_other_module.tasks" -) - -# Optional: Update Celery configuration directly if needed -# celery_app.conf.update(task_track_started=True) + ], +) \ No newline at end of file diff --git a/backend/core/config.py b/backend/core/config.py index 30b9b76..eee5bff 100644 --- a/backend/core/config.py +++ b/backend/core/config.py @@ -20,7 +20,7 @@ class Settings(BaseSettings): JWT_SECRET_KEY: str # Other settings - GOOGLE_API_KEY: str = "" # Example with a default + GOOGLE_API_KEY: str class Config: # Tell pydantic-settings to load variables from a .env file diff --git a/backend/core/database.py b/backend/core/database.py index cfec7b6..396734e 100644 --- a/backend/core/database.py +++ b/backend/core/database.py @@ -1,4 +1,3 @@ -# core/database.py from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker, Session, declarative_base from typing import Generator @@ -13,7 +12,7 @@ _SessionLocal = None def get_engine(): global _engine - if _engine is None: + if (_engine is None): if not settings.DB_URL: raise ValueError("DB_URL is not set in Settings.") print(f"Connecting to database at {settings.DB_URL}") diff --git a/backend/main.py b/backend/main.py index ccec921..23523af 100644 --- a/backend/main.py +++ b/backend/main.py @@ -1,4 +1,3 @@ -# main.py from contextlib import _AsyncGeneratorContextManager, asynccontextmanager from typing import Any, Callable from fastapi import FastAPI @@ -7,14 +6,9 @@ from core.database import get_engine, Base from modules import router import logging - -# import all models to ensure they are registered before create_all - - logging.getLogger("passlib").setLevel(logging.ERROR) # fix bc package logging is broken -# Create DB tables (remove in production; use migrations instead) def lifespan_factory() -> Callable[[FastAPI], _AsyncGeneratorContextManager[Any]]: @asynccontextmanager @@ -29,16 +23,11 @@ def lifespan_factory() -> Callable[[FastAPI], _AsyncGeneratorContextManager[Any] lifespan = lifespan_factory() app = FastAPI(lifespan=lifespan) -# Include module router app.include_router(router) -# CORS app.add_middleware( CORSMiddleware, allow_origins=[ - "http://localhost:8081", # Keep for web testing if needed - "http://192.168.1.9:8081", # Add your mobile device/emulator origin (adjust port if needed) - "http://192.168.255.221:8081", "https://maia.depaoli.id.au", ], allow_credentials=True, @@ -47,7 +36,6 @@ app.add_middleware( ) -# Health endpoint @app.get("/api/health") def health(): return {"status": "ok"} diff --git a/backend/modules/admin/__pycache__/api.cpython-312.pyc b/backend/modules/admin/__pycache__/api.cpython-312.pyc index 7beb637..6587770 100644 Binary files a/backend/modules/admin/__pycache__/api.cpython-312.pyc and b/backend/modules/admin/__pycache__/api.cpython-312.pyc differ diff --git a/backend/modules/admin/__pycache__/tasks.cpython-312.pyc b/backend/modules/admin/__pycache__/tasks.cpython-312.pyc index ccdd8d4..e44cad9 100644 Binary files a/backend/modules/admin/__pycache__/tasks.cpython-312.pyc and b/backend/modules/admin/__pycache__/tasks.cpython-312.pyc differ diff --git a/backend/modules/admin/api.py b/backend/modules/admin/api.py index 9b3c260..876c80b 100644 --- a/backend/modules/admin/api.py +++ b/backend/modules/admin/api.py @@ -1,7 +1,7 @@ # modules/admin/api.py from typing import Annotated -from fastapi import APIRouter, Depends # Import Body -from pydantic import BaseModel # Import BaseModel +from fastapi import APIRouter, Depends +from pydantic import BaseModel from sqlalchemy.orm import Session from core.database import get_db from modules.auth.dependencies import admin_only @@ -10,7 +10,6 @@ from .tasks import cleardb router = APIRouter(prefix="/admin", tags=["admin"], dependencies=[Depends(admin_only)]) -# Define a Pydantic model for the request body class ClearDbRequest(BaseModel): hard: bool @@ -20,7 +19,6 @@ def read_admin(): return {"message": "Admin route"} -# Change to POST and use the request body model @router.post("/cleardb") def clear_db(payload: ClearDbRequest, db: Annotated[Session, Depends(get_db)]): """ @@ -28,6 +26,6 @@ def clear_db(payload: ClearDbRequest, db: Annotated[Session, Depends(get_db)]): 'hard'=True: Drop and recreate all tables. 'hard'=False: Delete data from tables except users. """ - hard = payload.hard # Get 'hard' from the payload + hard = payload.hard cleardb.delay(hard) return {"message": "Clearing database in the background", "hard": hard} diff --git a/backend/modules/admin/services.py b/backend/modules/admin/services.py index 00b45ac..57df60d 100644 --- a/backend/modules/admin/services.py +++ b/backend/modules/admin/services.py @@ -1,4 +1 @@ -# modules/admin/services.py - - ## temp diff --git a/backend/modules/admin/tasks.py b/backend/modules/admin/tasks.py index 1ba029f..d0e8943 100644 --- a/backend/modules/admin/tasks.py +++ b/backend/modules/admin/tasks.py @@ -18,16 +18,13 @@ def cleardb(hard: bool): db = SessionLocal() if hard: - # Drop and recreate all tables Base.metadata.drop_all(bind=engine) Base.metadata.create_all(bind=engine) db.commit() return {"message": "Database reset (HARD)"} else: - # Delete data from tables except users tables = Base.metadata.tables.keys() for table_name in tables: - # delete all tables that isn't the users table if table_name != "users": table = Base.metadata.tables[table_name] print(f"Deleting table: {table_name}") diff --git a/backend/modules/auth/__pycache__/api.cpython-312.pyc b/backend/modules/auth/__pycache__/api.cpython-312.pyc index fec21f8..3026180 100644 Binary files a/backend/modules/auth/__pycache__/api.cpython-312.pyc and b/backend/modules/auth/__pycache__/api.cpython-312.pyc differ diff --git a/backend/modules/auth/__pycache__/dependencies.cpython-312.pyc b/backend/modules/auth/__pycache__/dependencies.cpython-312.pyc index 3e8403e..29e52de 100644 Binary files a/backend/modules/auth/__pycache__/dependencies.cpython-312.pyc and b/backend/modules/auth/__pycache__/dependencies.cpython-312.pyc differ diff --git a/backend/modules/auth/__pycache__/schemas.cpython-312.pyc b/backend/modules/auth/__pycache__/schemas.cpython-312.pyc index 4ba8ddf..12c8dfd 100644 Binary files a/backend/modules/auth/__pycache__/schemas.cpython-312.pyc and b/backend/modules/auth/__pycache__/schemas.cpython-312.pyc differ diff --git a/backend/modules/auth/__pycache__/security.cpython-312.pyc b/backend/modules/auth/__pycache__/security.cpython-312.pyc index 0d639b2..bf301a3 100644 Binary files a/backend/modules/auth/__pycache__/security.cpython-312.pyc and b/backend/modules/auth/__pycache__/security.cpython-312.pyc differ diff --git a/backend/modules/auth/__pycache__/services.cpython-312.pyc b/backend/modules/auth/__pycache__/services.cpython-312.pyc index 7cec1a2..cb3b621 100644 Binary files a/backend/modules/auth/__pycache__/services.cpython-312.pyc and b/backend/modules/auth/__pycache__/services.cpython-312.pyc differ diff --git a/backend/modules/auth/api.py b/backend/modules/auth/api.py index 29ded0b..31515bd 100644 --- a/backend/modules/auth/api.py +++ b/backend/modules/auth/api.py @@ -1,4 +1,3 @@ -# modules/auth/api.py from fastapi import APIRouter, Depends, HTTPException, status from fastapi.security import OAuth2PasswordRequestForm from jose import JWTError @@ -25,7 +24,7 @@ from sqlalchemy.orm import Session from typing import Annotated from core.database import get_db from datetime import timedelta -from core.config import settings # Assuming settings is defined in core.config +from core.config import settings from core.exceptions import unauthorized_exception router = APIRouter(prefix="/auth", tags=["auth"]) diff --git a/backend/modules/auth/dependencies.py b/backend/modules/auth/dependencies.py index ec84bea..b793462 100644 --- a/backend/modules/auth/dependencies.py +++ b/backend/modules/auth/dependencies.py @@ -1,4 +1,3 @@ -# modules/auth/dependencies.py from fastapi import Depends from modules.auth.security import get_current_user from modules.auth.schemas import UserRole diff --git a/backend/modules/auth/schemas.py b/backend/modules/auth/schemas.py index e04cd50..0e86298 100644 --- a/backend/modules/auth/schemas.py +++ b/backend/modules/auth/schemas.py @@ -1,4 +1,3 @@ -# modules/auth/schemas.py from enum import Enum as PyEnum from pydantic import BaseModel diff --git a/backend/modules/auth/security.py b/backend/modules/auth/security.py index 1dd2f0d..9ad0d8c 100644 --- a/backend/modules/auth/security.py +++ b/backend/modules/auth/security.py @@ -29,7 +29,7 @@ password_hasher = PasswordHasher() def hash_password(password: str) -> str: """Hash a password with Argon2 (and optional pepper).""" - peppered_password = password + settings.PEPPER # Prepend/append pepper + peppered_password = password + settings.PEPPER return password_hasher.hash(peppered_password) @@ -47,10 +47,8 @@ def authenticate_user(username: str, password: str, db: Session) -> User | None: Authenticate a user by checking username/password against the database. Returns User object if valid, None otherwise. """ - # Get user from database user = db.query(User).filter(User.username == username).first() - # If user not found or password doesn't match if not user or not verify_password(password, user.hashed_password): return None @@ -65,7 +63,6 @@ def create_access_token(data: dict, expires_delta: timedelta | None = None): expire = datetime.now(timezone.utc) + timedelta( minutes=settings.ACCESS_TOKEN_EXPIRE_MINUTES ) - # expire = datetime.now(timezone.utc) + timedelta(seconds=5) to_encode.update({"exp": expire, "token_type": TokenType.ACCESS}) return jwt.encode( to_encode, settings.JWT_SECRET_KEY, algorithm=settings.JWT_ALGORITHM @@ -89,22 +86,6 @@ def create_refresh_token(data: dict, expires_delta: timedelta | None = None): def verify_token( token: str, expected_token_type: TokenType, db: Session ) -> TokenData | None: - """Verify a JWT token and return TokenData if valid. - - Parameters - ---------- - token: str - The JWT token to be verified. - expected_token_type: TokenType - The expected type of token (access or refresh) - db: Session - Database session to fetch user data. - - Returns - ------- - TokenData | None - TokenData instance if the token is valid, None otherwise. - """ is_blacklisted = ( db.query(TokenBlacklist).filter(TokenBlacklist.token == token).first() is not None @@ -137,7 +118,6 @@ def get_current_user( headers={"WWW-Authenticate": "Bearer"}, ) - # Check if the token is blacklisted is_blacklisted = ( db.query(TokenBlacklist).filter(TokenBlacklist.token == token).first() is not None @@ -178,7 +158,6 @@ def blacklist_tokens(access_token: str, refresh_token: str, db: Session) -> None ) expires_at = datetime.fromtimestamp(payload.get("exp")) - # Add the token to the blacklist blacklisted_token = TokenBlacklist(token=token, expires_at=expires_at) db.add(blacklisted_token) @@ -191,7 +170,6 @@ def blacklist_token(token: str, db: Session) -> None: ) expires_at = datetime.fromtimestamp(payload.get("exp")) - # Add the token to the blacklist blacklisted_token = TokenBlacklist(token=token, expires_at=expires_at) db.add(blacklisted_token) db.commit() diff --git a/backend/modules/auth/services.py b/backend/modules/auth/services.py index 3f50a37..0a15257 100644 --- a/backend/modules/auth/services.py +++ b/backend/modules/auth/services.py @@ -1,4 +1,3 @@ -# modules/auth/services.py from sqlalchemy.orm import Session from modules.auth.models import User from modules.auth.schemas import UserResponse diff --git a/backend/modules/calendar/__pycache__/api.cpython-312.pyc b/backend/modules/calendar/__pycache__/api.cpython-312.pyc index 0416d50..cd1869b 100644 Binary files a/backend/modules/calendar/__pycache__/api.cpython-312.pyc and b/backend/modules/calendar/__pycache__/api.cpython-312.pyc differ diff --git a/backend/modules/calendar/__pycache__/schemas.cpython-312.pyc b/backend/modules/calendar/__pycache__/schemas.cpython-312.pyc index 7612fb2..35c2e07 100644 Binary files a/backend/modules/calendar/__pycache__/schemas.cpython-312.pyc and b/backend/modules/calendar/__pycache__/schemas.cpython-312.pyc differ diff --git a/backend/modules/calendar/api.py b/backend/modules/calendar/api.py index cdb1cf6..5564f61 100644 --- a/backend/modules/calendar/api.py +++ b/backend/modules/calendar/api.py @@ -1,4 +1,3 @@ -# modules/calendar/api.py from fastapi import APIRouter, Depends, status from sqlalchemy.orm import Session from datetime import datetime diff --git a/backend/modules/calendar/schemas.py b/backend/modules/calendar/schemas.py index 3cec1c7..3b8de61 100644 --- a/backend/modules/calendar/schemas.py +++ b/backend/modules/calendar/schemas.py @@ -1,7 +1,6 @@ -# modules/calendar/schemas.py from datetime import datetime -from pydantic import BaseModel, field_validator # Add field_validator -from typing import List, Optional # Add List and Optional +from pydantic import BaseModel, field_validator +from typing import List, Optional # Base schema for common fields, including tags diff --git a/backend/modules/nlp/__pycache__/api.cpython-312.pyc b/backend/modules/nlp/__pycache__/api.cpython-312.pyc index 91a1205..2ab4e87 100644 Binary files a/backend/modules/nlp/__pycache__/api.cpython-312.pyc and b/backend/modules/nlp/__pycache__/api.cpython-312.pyc differ diff --git a/backend/modules/nlp/__pycache__/models.cpython-312.pyc b/backend/modules/nlp/__pycache__/models.cpython-312.pyc index f113b09..515297e 100644 Binary files a/backend/modules/nlp/__pycache__/models.cpython-312.pyc and b/backend/modules/nlp/__pycache__/models.cpython-312.pyc differ diff --git a/backend/modules/nlp/__pycache__/schemas.cpython-312.pyc b/backend/modules/nlp/__pycache__/schemas.cpython-312.pyc index bcce691..d0f66b6 100644 Binary files a/backend/modules/nlp/__pycache__/schemas.cpython-312.pyc and b/backend/modules/nlp/__pycache__/schemas.cpython-312.pyc differ diff --git a/backend/modules/nlp/__pycache__/service.cpython-312.pyc b/backend/modules/nlp/__pycache__/service.cpython-312.pyc index c02a10a..acd4ce1 100644 Binary files a/backend/modules/nlp/__pycache__/service.cpython-312.pyc and b/backend/modules/nlp/__pycache__/service.cpython-312.pyc differ diff --git a/backend/modules/nlp/api.py b/backend/modules/nlp/api.py index 0ce4878..70fda60 100644 --- a/backend/modules/nlp/api.py +++ b/backend/modules/nlp/api.py @@ -1,4 +1,3 @@ -# modules/nlp/api.py from fastapi import APIRouter, Depends, HTTPException from sqlalchemy.orm import Session from typing import List @@ -8,7 +7,6 @@ from core.database import get_db from modules.auth.dependencies import get_current_user from modules.auth.models import User -# Import the new service functions and Enum from modules.nlp.service import ( process_request, ask_ai, @@ -17,7 +15,6 @@ from modules.nlp.service import ( MessageSender, ) -# Import the response schema and the new ChatMessage model for response type hinting from modules.nlp.schemas import ProcessCommandRequest, ProcessCommandResponse from modules.calendar.service import ( create_calendar_event, @@ -28,7 +25,6 @@ from modules.calendar.service import ( from modules.calendar.models import CalendarEvent from modules.calendar.schemas import CalendarEventCreate, CalendarEventUpdate -# Import TODO services, schemas, and model from modules.todo import service as todo_service from modules.todo.models import Todo from modules.todo.schemas import TodoCreate, TodoUpdate @@ -38,24 +34,22 @@ from datetime import datetime class ChatMessageResponse(BaseModel): id: int - sender: MessageSender # Use the enum directly + sender: MessageSender text: str timestamp: datetime class Config: - from_attributes = True # Allow Pydantic to work with ORM models + from_attributes = True router = APIRouter(prefix="/nlp", tags=["nlp"]) -# Helper to format calendar events (expects list of CalendarEvent models) def format_calendar_events(events: List[CalendarEvent]) -> List[str]: if not events: return ["You have no events matching that criteria."] formatted = ["Here are the events:"] for event in events: - # Access attributes directly from the model instance start_str = ( event.start.strftime("%Y-%m-%d %H:%M") if event.start else "No start time" ) @@ -65,7 +59,6 @@ def format_calendar_events(events: List[CalendarEvent]) -> List[str]: return formatted -# Helper to format TODO items (expects list of Todo models) def format_todos(todos: List[Todo]) -> List[str]: if not todos: return ["Your TODO list is empty."] @@ -80,7 +73,6 @@ def format_todos(todos: List[Todo]) -> List[str]: return formatted -# Update the response model for the endpoint @router.post("/process-command", response_model=ProcessCommandResponse) def process_command( request_data: ProcessCommandRequest, @@ -92,34 +84,25 @@ def process_command( """ user_input = request_data.user_input - # --- Save User Message --- save_chat_message( db, user_id=current_user.id, sender=MessageSender.USER, text=user_input ) - # ------------------------ command_data = process_request(user_input) intent = command_data["intent"] params = command_data["params"] response_text = command_data["response_text"] - responses = [response_text] # Start with the initial response + responses = [response_text] - # --- Save Initial AI Response --- - # Save the first response generated by process_request save_chat_message( db, user_id=current_user.id, sender=MessageSender.AI, text=response_text ) - # ----------------------------- if intent == "error": - # Don't raise HTTPException here if we want to save the error message - # Instead, return the error response directly - # save_chat_message(db, user_id=current_user.id, sender=MessageSender.AI, text=response_text) # Already saved above return ProcessCommandResponse(responses=responses) if intent == "clarification_needed" or intent == "unknown": - # save_chat_message(db, user_id=current_user.id, sender=MessageSender.AI, text=response_text) # Already saved above return ProcessCommandResponse(responses=responses) try: @@ -127,11 +110,9 @@ def process_command( case "ask_ai": ai_answer = ask_ai(**params) responses.append(ai_answer) - # --- Save Additional AI Response --- save_chat_message( db, user_id=current_user.id, sender=MessageSender.AI, text=ai_answer ) - # --------------------------------- return ProcessCommandResponse(responses=responses) case "get_calendar_events": @@ -140,12 +121,10 @@ def process_command( ) formatted_responses = format_calendar_events(events) responses.extend(formatted_responses) - # --- Save Additional AI Responses --- for resp in formatted_responses: save_chat_message( db, user_id=current_user.id, sender=MessageSender.AI, text=resp ) - # ---------------------------------- return ProcessCommandResponse(responses=responses) case "add_calendar_event": @@ -159,20 +138,17 @@ def process_command( title = created_event.title or "Untitled Event" add_response = f"Added: {title} starting at {start_str}." responses.append(add_response) - # --- Save Additional AI Response --- save_chat_message( db, user_id=current_user.id, sender=MessageSender.AI, text=add_response, ) - # --------------------------------- return ProcessCommandResponse(responses=responses) case "update_calendar_event": event_id = params.pop("event_id", None) if event_id is None: - # Save the error message before raising error_msg = "Event ID is required for update." save_chat_message( db, @@ -188,20 +164,17 @@ def process_command( title = updated_event.title or "Untitled Event" update_response = f"Updated event ID {updated_event.id}: {title}." responses.append(update_response) - # --- Save Additional AI Response --- save_chat_message( db, user_id=current_user.id, sender=MessageSender.AI, text=update_response, ) - # --------------------------------- return ProcessCommandResponse(responses=responses) case "delete_calendar_event": event_id = params.get("event_id") if event_id is None: - # Save the error message before raising error_msg = "Event ID is required for delete." save_chat_message( db, @@ -213,29 +186,24 @@ def process_command( delete_calendar_event(db, current_user.id, event_id) delete_response = f"Deleted event ID {event_id}." responses.append(delete_response) - # --- Save Additional AI Response --- save_chat_message( db, user_id=current_user.id, sender=MessageSender.AI, text=delete_response, ) - # --------------------------------- return ProcessCommandResponse(responses=responses) - # --- Add TODO Cases --- case "get_todos": todos: List[Todo] = todo_service.get_todos( db, user=current_user, **params ) formatted_responses = format_todos(todos) responses.extend(formatted_responses) - # --- Save Additional AI Responses --- for resp in formatted_responses: save_chat_message( db, user_id=current_user.id, sender=MessageSender.AI, text=resp ) - # ---------------------------------- return ProcessCommandResponse(responses=responses) case "add_todo": @@ -247,14 +215,12 @@ def process_command( f"Added TODO: '{created_todo.task}' (ID: {created_todo.id})." ) responses.append(add_response) - # --- Save Additional AI Response --- save_chat_message( db, user_id=current_user.id, sender=MessageSender.AI, text=add_response, ) - # --------------------------------- return ProcessCommandResponse(responses=responses) case "update_todo": @@ -279,14 +245,12 @@ def process_command( status = "complete" if params["complete"] else "incomplete" update_response += f" Marked as {status}." responses.append(update_response) - # --- Save Additional AI Response --- save_chat_message( db, user_id=current_user.id, sender=MessageSender.AI, text=update_response, ) - # --------------------------------- return ProcessCommandResponse(responses=responses) case "delete_todo": @@ -307,26 +271,21 @@ def process_command( f"Deleted TODO ID {deleted_todo.id}: '{deleted_todo.task}'." ) responses.append(delete_response) - # --- Save Additional AI Response --- save_chat_message( db, user_id=current_user.id, sender=MessageSender.AI, text=delete_response, ) - # --------------------------------- return ProcessCommandResponse(responses=responses) - # --- End TODO Cases --- case _: print( f"Warning: Unhandled intent '{intent}' reached api.py match statement." ) - # The initial response_text was already saved return ProcessCommandResponse(responses=responses) except HTTPException as http_exc: - # Don't save again if already saved before raising if http_exc.status_code != 400 or ("event_id" not in http_exc.detail.lower()): save_chat_message( db, @@ -340,11 +299,9 @@ def process_command( error_response = ( "Sorry, I encountered an error while trying to perform that action." ) - # --- Save Final Error AI Response --- save_chat_message( db, user_id=current_user.id, sender=MessageSender.AI, text=error_response ) - # ---------------------------------- return ProcessCommandResponse(responses=[error_response]) @@ -355,6 +312,3 @@ def read_chat_history( """Retrieves the last 50 chat messages for the current user.""" history = get_chat_history(db, user_id=current_user.id, limit=50) return history - - -# ------------------------------------- diff --git a/backend/modules/nlp/models.py b/backend/modules/nlp/models.py index 120fcee..87cd143 100644 --- a/backend/modules/nlp/models.py +++ b/backend/modules/nlp/models.py @@ -1,4 +1,3 @@ -# /home/cdp/code/MAIA/backend/modules/nlp/models.py from sqlalchemy import Column, Integer, Text, DateTime, ForeignKey, Enum as SQLEnum from sqlalchemy.orm import relationship from sqlalchemy.sql import func diff --git a/backend/modules/nlp/schemas.py b/backend/modules/nlp/schemas.py index d2a2807..bff1a12 100644 --- a/backend/modules/nlp/schemas.py +++ b/backend/modules/nlp/schemas.py @@ -1,4 +1,3 @@ -# modules/nlp/schemas.py from pydantic import BaseModel from typing import List @@ -9,5 +8,4 @@ class ProcessCommandRequest(BaseModel): class ProcessCommandResponse(BaseModel): responses: List[str] - # Optional: Keep details if needed for specific frontend logic beyond display # details: dict | None = None diff --git a/backend/modules/nlp/service.py b/backend/modules/nlp/service.py index ef4bca4..18ba6dd 100644 --- a/backend/modules/nlp/service.py +++ b/backend/modules/nlp/service.py @@ -1,5 +1,3 @@ -# modules/nlp/service.py - from sqlalchemy.orm import Session from sqlalchemy import desc # Import desc for ordering from google import genai diff --git a/backend/modules/todo/__init__.py b/backend/modules/todo/__init__.py index d6fc651..1a6df05 100644 --- a/backend/modules/todo/__init__.py +++ b/backend/modules/todo/__init__.py @@ -1,2 +1 @@ -# backend/modules/todo/__init__.py # This file makes the 'todo' directory a Python package. diff --git a/backend/modules/todo/__pycache__/__init__.cpython-312.pyc b/backend/modules/todo/__pycache__/__init__.cpython-312.pyc index 1a4f3d3..d61a235 100644 Binary files a/backend/modules/todo/__pycache__/__init__.cpython-312.pyc and b/backend/modules/todo/__pycache__/__init__.cpython-312.pyc differ diff --git a/backend/modules/todo/__pycache__/api.cpython-312.pyc b/backend/modules/todo/__pycache__/api.cpython-312.pyc index 3444835..f3c8318 100644 Binary files a/backend/modules/todo/__pycache__/api.cpython-312.pyc and b/backend/modules/todo/__pycache__/api.cpython-312.pyc differ diff --git a/backend/modules/todo/__pycache__/models.cpython-312.pyc b/backend/modules/todo/__pycache__/models.cpython-312.pyc index 4cb46ba..537ee04 100644 Binary files a/backend/modules/todo/__pycache__/models.cpython-312.pyc and b/backend/modules/todo/__pycache__/models.cpython-312.pyc differ diff --git a/backend/modules/todo/api.py b/backend/modules/todo/api.py index 29cb261..ed6d53e 100644 --- a/backend/modules/todo/api.py +++ b/backend/modules/todo/api.py @@ -1,17 +1,16 @@ -# backend/modules/todo/api.py from fastapi import APIRouter, Depends, HTTPException, status from sqlalchemy.orm import Session from typing import List from . import service, schemas from core.database import get_db -from modules.auth.dependencies import get_current_user # Corrected import -from modules.auth.models import User # Assuming User model is in auth.models +from modules.auth.dependencies import get_current_user +from modules.auth.models import User router = APIRouter( prefix="/todos", tags=["todos"], - dependencies=[Depends(get_current_user)], # Corrected dependency + dependencies=[Depends(get_current_user)], responses={404: {"description": "Not found"}}, ) @@ -20,7 +19,7 @@ router = APIRouter( def create_todo_endpoint( todo: schemas.TodoCreate, db: Session = Depends(get_db), - current_user: User = Depends(get_current_user), # Corrected dependency + current_user: User = Depends(get_current_user), ): return service.create_todo(db=db, todo=todo, user=current_user) @@ -30,7 +29,7 @@ def read_todos_endpoint( skip: int = 0, limit: int = 100, db: Session = Depends(get_db), - current_user: User = Depends(get_current_user), # Corrected dependency + current_user: User = Depends(get_current_user), ): todos = service.get_todos(db=db, user=current_user, skip=skip, limit=limit) return todos @@ -40,7 +39,7 @@ def read_todos_endpoint( def read_todo_endpoint( todo_id: int, db: Session = Depends(get_db), - current_user: User = Depends(get_current_user), # Corrected dependency + current_user: User = Depends(get_current_user), ): db_todo = service.get_todo(db=db, todo_id=todo_id, user=current_user) if db_todo is None: @@ -53,7 +52,7 @@ def update_todo_endpoint( todo_id: int, todo_update: schemas.TodoUpdate, db: Session = Depends(get_db), - current_user: User = Depends(get_current_user), # Corrected dependency + current_user: User = Depends(get_current_user), ): return service.update_todo( db=db, todo_id=todo_id, todo_update=todo_update, user=current_user @@ -64,6 +63,6 @@ def update_todo_endpoint( def delete_todo_endpoint( todo_id: int, db: Session = Depends(get_db), - current_user: User = Depends(get_current_user), # Corrected dependency + current_user: User = Depends(get_current_user), ): return service.delete_todo(db=db, todo_id=todo_id, user=current_user) diff --git a/backend/modules/todo/models.py b/backend/modules/todo/models.py index f3b713a..f416e83 100644 --- a/backend/modules/todo/models.py +++ b/backend/modules/todo/models.py @@ -16,4 +16,4 @@ class Todo(Base): owner = relationship( "User" - ) # Add relationship if needed, assuming User model exists in auth.models + ) diff --git a/backend/modules/user/__pycache__/api.cpython-312.pyc b/backend/modules/user/__pycache__/api.cpython-312.pyc index 4044254..ca97248 100644 Binary files a/backend/modules/user/__pycache__/api.cpython-312.pyc and b/backend/modules/user/__pycache__/api.cpython-312.pyc differ diff --git a/backend/modules/user/api.py b/backend/modules/user/api.py index 34317f7..6f54cfa 100644 --- a/backend/modules/user/api.py +++ b/backend/modules/user/api.py @@ -1,4 +1,3 @@ -# modules/user/api.py from typing import Annotated from fastapi import APIRouter, Depends from sqlalchemy.orm import Session diff --git a/interfaces/nativeapp/app.json b/interfaces/nativeapp/app.json index baa11bc..6c5c812 100644 --- a/interfaces/nativeapp/app.json +++ b/interfaces/nativeapp/app.json @@ -20,7 +20,8 @@ "foregroundImage": "./assets/adaptive-icon.png", "backgroundColor": "#ffffff" }, - "softwareKeyboardLayoutMode": "resize" + "softwareKeyboardLayoutMode": "resize", + "package": "com.seedeep.maia" }, "web": { "favicon": "./assets/favicon.png" @@ -28,6 +29,12 @@ "plugins": [ "expo-secure-store", "expo-font" - ] + ], + "extra": { + "eas": { + "projectId": "4d7d70ce-a4d8-4307-8827-8ef713b95b78" + } + }, + "owner": "cdp202" } } diff --git a/interfaces/nativeapp/eas.json b/interfaces/nativeapp/eas.json new file mode 100644 index 0000000..4b759f9 --- /dev/null +++ b/interfaces/nativeapp/eas.json @@ -0,0 +1,21 @@ +{ + "cli": { + "version": ">= 16.3.2", + "appVersionSource": "remote" + }, + "build": { + "development": { + "developmentClient": true, + "distribution": "internal" + }, + "preview": { + "distribution": "internal" + }, + "production": { + "autoIncrement": true + } + }, + "submit": { + "production": {} + } +} diff --git a/interfaces/nativeapp/package-lock.json b/interfaces/nativeapp/package-lock.json index 238de80..f262723 100644 --- a/interfaces/nativeapp/package-lock.json +++ b/interfaces/nativeapp/package-lock.json @@ -18,6 +18,7 @@ "axios": "^1.8.4", "date-fns": "^4.1.0", "expo": "^52.0.46", + "expo-dev-client": "~5.0.20", "expo-font": "~13.0.4", "expo-secure-store": "~14.0.1", "expo-splash-screen": "~0.29.24", @@ -3746,6 +3747,21 @@ "node": ">=8" } }, + "node_modules/ajv": { + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", + "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, "node_modules/anser": { "version": "1.4.10", "resolved": "https://registry.npmjs.org/anser/-/anser-1.4.10.tgz", @@ -5307,6 +5323,54 @@ "react-native": "*" } }, + "node_modules/expo-dev-client": { + "version": "5.0.20", + "resolved": "https://registry.npmjs.org/expo-dev-client/-/expo-dev-client-5.0.20.tgz", + "integrity": "sha512-bLNkHdU7V3I4UefgJbJnIDUBUL0LxIal/xYEx9BbgDd3B7wgQKY//+BpPIxBOKCQ22lkyiHY8y9tLhO903sAgg==", + "dependencies": { + "expo-dev-launcher": "5.0.35", + "expo-dev-menu": "6.0.25", + "expo-dev-menu-interface": "1.9.3", + "expo-manifests": "~0.15.8", + "expo-updates-interface": "~1.0.0" + }, + "peerDependencies": { + "expo": "*" + } + }, + "node_modules/expo-dev-launcher": { + "version": "5.0.35", + "resolved": "https://registry.npmjs.org/expo-dev-launcher/-/expo-dev-launcher-5.0.35.tgz", + "integrity": "sha512-hEQr0ZREnUMxZ6wtQgfK1lzYnbb0zar3HqYZhmANzXmE6UEPbQ4GByLzhpfz/d+xxdBVQZsrHdtiV28KPG2sog==", + "dependencies": { + "ajv": "8.11.0", + "expo-dev-menu": "6.0.25", + "expo-manifests": "~0.15.8", + "resolve-from": "^5.0.0" + }, + "peerDependencies": { + "expo": "*" + } + }, + "node_modules/expo-dev-menu": { + "version": "6.0.25", + "resolved": "https://registry.npmjs.org/expo-dev-menu/-/expo-dev-menu-6.0.25.tgz", + "integrity": "sha512-K2m4z/I+CPWbMtHlDzU68lHaQs52De0v5gbsjAmA5ig8FrYh4MKZvPxSVANaiKENzgmtglu8qaFh7ua9Gt2TfA==", + "dependencies": { + "expo-dev-menu-interface": "1.9.3" + }, + "peerDependencies": { + "expo": "*" + } + }, + "node_modules/expo-dev-menu-interface": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/expo-dev-menu-interface/-/expo-dev-menu-interface-1.9.3.tgz", + "integrity": "sha512-KY/dWTBE1l47i9V366JN5rC6YIdOc9hz8yAmZzkl5DrPia5l3M2WIjtnpHC9zUkNjiSiG2urYoOAq4H/uLdmyg==", + "peerDependencies": { + "expo": "*" + } + }, "node_modules/expo-file-system": { "version": "18.0.12", "resolved": "https://registry.npmjs.org/expo-file-system/-/expo-file-system-18.0.12.tgz", @@ -5331,6 +5395,11 @@ "react": "*" } }, + "node_modules/expo-json-utils": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/expo-json-utils/-/expo-json-utils-0.14.0.tgz", + "integrity": "sha512-xjGfK9dL0B1wLnOqNkX0jM9p48Y0I5xEPzHude28LY67UmamUyAACkqhZGaPClyPNfdzczk7Ej6WaRMT3HfXvw==" + }, "node_modules/expo-keep-awake": { "version": "14.0.3", "resolved": "https://registry.npmjs.org/expo-keep-awake/-/expo-keep-awake-14.0.3.tgz", @@ -5340,6 +5409,18 @@ "react": "*" } }, + "node_modules/expo-manifests": { + "version": "0.15.8", + "resolved": "https://registry.npmjs.org/expo-manifests/-/expo-manifests-0.15.8.tgz", + "integrity": "sha512-VuIyaMfRfLZeETNsRohqhy1l7iZ7I+HKMPfZXVL2Yn17TT0WkOhZoq1DzYwPbOHPgp1Uk6phNa86EyaHrD2DLw==", + "dependencies": { + "@expo/config": "~10.0.11", + "expo-json-utils": "~0.14.0" + }, + "peerDependencies": { + "expo": "*" + } + }, "node_modules/expo-modules-autolinking": { "version": "2.0.8", "resolved": "https://registry.npmjs.org/expo-modules-autolinking/-/expo-modules-autolinking-2.0.8.tgz", @@ -5427,6 +5508,14 @@ "react-native": "*" } }, + "node_modules/expo-updates-interface": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/expo-updates-interface/-/expo-updates-interface-1.0.0.tgz", + "integrity": "sha512-93oWtvULJOj+Pp+N/lpTcFfuREX1wNeHtp7Lwn8EbzYYmdn37MvZU3TPW2tYYCZuhzmKEXnUblYcruYoDu7IrQ==", + "peerDependencies": { + "expo": "*" + } + }, "node_modules/exponential-backoff": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/exponential-backoff/-/exponential-backoff-3.1.2.tgz", @@ -6596,6 +6685,11 @@ "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==" }, + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + }, "node_modules/json5": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", @@ -10308,6 +10402,14 @@ "browserslist": ">= 4.21.0" } }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dependencies": { + "punycode": "^2.1.0" + } + }, "node_modules/use-latest-callback": { "version": "0.2.3", "resolved": "https://registry.npmjs.org/use-latest-callback/-/use-latest-callback-0.2.3.tgz", diff --git a/interfaces/nativeapp/package.json b/interfaces/nativeapp/package.json index f8a2494..4c66651 100644 --- a/interfaces/nativeapp/package.json +++ b/interfaces/nativeapp/package.json @@ -33,7 +33,8 @@ "react-native-safe-area-context": "4.12.0", "react-native-screens": "~4.4.0", "react-native-vector-icons": "^10.2.0", - "react-native-web": "~0.19.13" + "react-native-web": "~0.19.13", + "expo-dev-client": "~5.0.20" }, "devDependencies": { "@babel/core": "^7.25.2", diff --git a/interfaces/nativeapp/src/api/client.ts b/interfaces/nativeapp/src/api/client.ts index 0bd4e05..1b0ce6b 100644 --- a/interfaces/nativeapp/src/api/client.ts +++ b/interfaces/nativeapp/src/api/client.ts @@ -1,15 +1,13 @@ // src/api/client.ts -import axios, { AxiosError } from 'axios'; // Import AxiosError +import axios, { AxiosError } from 'axios'; import { Platform } from 'react-native'; import * as SecureStore from 'expo-secure-store'; import AsyncStorage from '@react-native-async-storage/async-storage'; -// const API_BASE_URL = process.env.EXPO_PUBLIC_API_URL || 'http://192.168.255.221:8000/api'; // Use your machine's IP -// const API_BASE_URL = process.env.EXPO_PUBLIC_API_URL || 'http://192.168.1.9:8000/api'; // Use your machine's IP -const API_BASE_URL = process.env.EXPO_PUBLIC_API_URL || 'https://maia.depaoli.id.au/api'; // Use your machine's IP -const ACCESS_TOKEN_KEY = 'maia_access_token'; // Renamed for clarity -const REFRESH_TOKEN_KEY = 'maia_refresh_token'; // Key for refresh token +const API_BASE_URL = process.env.EXPO_PUBLIC_API_URL || 'https://maia.depaoli.id.au/api'; +const ACCESS_TOKEN_KEY = 'maia_access_token'; +const REFRESH_TOKEN_KEY = 'maia_refresh_token'; console.log("Using API Base URL:", API_BASE_URL); @@ -34,7 +32,7 @@ const deleteToken = async (key: string): Promise => { if (Platform.OS === 'web') { await AsyncStorage.removeItem(key); } else { - await SecureStore.deleteItemAsync(key).catch(() => {}); // Ignore delete error + await SecureStore.deleteItemAsync(key).catch(() => {}); } }; diff --git a/interfaces/nativeapp/src/navigation/AuthNavigator.tsx b/interfaces/nativeapp/src/navigation/AuthNavigator.tsx index a4672c5..77d2358 100644 --- a/interfaces/nativeapp/src/navigation/AuthNavigator.tsx +++ b/interfaces/nativeapp/src/navigation/AuthNavigator.tsx @@ -13,7 +13,7 @@ const AuthNavigator = () => { return ( - {/* Add Register screen */} + ); }; diff --git a/interfaces/nativeapp/src/screens/RegisterScreen.tsx b/interfaces/nativeapp/src/screens/RegisterScreen.tsx index 6be8cb9..ddc25e1 100644 --- a/interfaces/nativeapp/src/screens/RegisterScreen.tsx +++ b/interfaces/nativeapp/src/screens/RegisterScreen.tsx @@ -35,11 +35,7 @@ const RegisterScreen: React.FC = ({ navigation }) => { await register(username, password, name); console.log("[RegisterScreen] handleRegister: Registration successful (from context perspective)."); // Show success message and navigate back to Login - Alert.alert( - 'Registration Successful', - 'Your account has been created. Please log in.', - [{ text: 'OK', onPress: () => navigation.navigate('Login') }] - ); + navigation.navigate('Login'); } catch (err: any) { console.log("[RegisterScreen] handleRegister: Caught error from context register."); const errorMessage = err.response?.data?.detail ||