From fbdc07af2c5c5f53ec4aa38b3d1a1a8ca2578011 Mon Sep 17 00:00:00 2001 From: Mamadou DICKO <63923024+mamadoudicko@users.noreply.github.com> Date: Thu, 5 Oct 2023 09:47:29 +0200 Subject: [PATCH] feat: add onboarding table (#1327) * feat: add onboarding table * feat: add Onboarding repository * feat: add onboarding controller --- backend/models/databases/repository.py | 8 +++ backend/models/databases/supabase/__init__.py | 4 +- .../models/databases/supabase/onboarding.py | 62 +++++++++++++++++++ backend/models/databases/supabase/supabase.py | 3 + backend/models/onboarding.py | 12 ++++ .../onboarding/get_user_onboarding.py | 18 ++++++ .../onboarding/udpate_user_onboarding.py | 14 +++++ backend/routes/onboarding_routes.py | 42 +++++++++++++ .../20231004150000_add_onboarding_table.sql | 8 +++ scripts/tables.sql | 13 +++- 10 files changed, 180 insertions(+), 4 deletions(-) create mode 100644 backend/models/databases/supabase/onboarding.py create mode 100644 backend/models/onboarding.py create mode 100644 backend/repository/onboarding/get_user_onboarding.py create mode 100644 backend/repository/onboarding/udpate_user_onboarding.py create mode 100644 backend/routes/onboarding_routes.py create mode 100644 scripts/20231004150000_add_onboarding_table.sql diff --git a/backend/models/databases/repository.py b/backend/models/databases/repository.py index d47f68b36..82e98e0b7 100644 --- a/backend/models/databases/repository.py +++ b/backend/models/databases/repository.py @@ -255,3 +255,11 @@ class Repository(ABC): @abstractmethod def get_all_knowledge_in_brain(self, brain_id: UUID): pass + + @abstractmethod + def get_user_onboarding(self, user_id: UUID): + pass + + @abstractmethod + def update_user_onboarding(self, user_id: UUID, onboarding): + pass diff --git a/backend/models/databases/supabase/__init__.py b/backend/models/databases/supabase/__init__.py index 71e74b007..6a155075a 100644 --- a/backend/models/databases/supabase/__init__.py +++ b/backend/models/databases/supabase/__init__.py @@ -1,11 +1,11 @@ from models.databases.supabase.api_key_handler import ApiKeyHandler from models.databases.supabase.brains import Brain -from models.databases.supabase.brains_subscription_invitations import \ - BrainSubscription +from models.databases.supabase.brains_subscription_invitations import BrainSubscription from models.databases.supabase.chats import Chats from models.databases.supabase.files import File from models.databases.supabase.knowledge import Knowledges from models.databases.supabase.notifications import Notifications +from models.databases.supabase.onboarding import Onboarding from models.databases.supabase.prompts import Prompts from models.databases.supabase.user_usage import UserUsage from models.databases.supabase.vectors import Vector diff --git a/backend/models/databases/supabase/onboarding.py b/backend/models/databases/supabase/onboarding.py new file mode 100644 index 000000000..c9158c749 --- /dev/null +++ b/backend/models/databases/supabase/onboarding.py @@ -0,0 +1,62 @@ +from typing import Optional +from uuid import UUID + +from fastapi import HTTPException +from models.databases.repository import ( + Repository, # Assuming you have a repository class +) +from models.onboarding import Onboardings +from pydantic import BaseModel + + +class OnboardingUpdatableProperties(BaseModel): + """Properties that can be received on onboarding update""" + + onboarding_b1: Optional[bool] + onboarding_b2: Optional[bool] + onboarding_b3: Optional[bool] + + +class Onboarding(Repository): + def __init__(self, supabase_client): + self.db = supabase_client + + def get_user_onboarding(self, user_id: UUID) -> Onboardings: + """ + Get user onboarding information by user_id + """ + onboarding_data = ( + self.db.from_("onboarding") + .select("user_id", "onboarding_b1", "onboarding_b2", "onboarding_b3") + .filter("user_id", "eq", user_id) + .limit(1) + .execute() + ).data + + if not onboarding_data: + raise HTTPException(404, "User onboarding not found") + + return Onboardings(**onboarding_data[0]) + + def update_user_onboarding( + self, user_id: UUID, onboarding: OnboardingUpdatableProperties + ) -> Onboardings: + """Update user onboarding information by user_id""" + response = ( + self.db.from_("onboarding") + .upsert( + { + "user_id": user_id, + "onboarding_b1": onboarding.onboarding_b1, + "onboarding_b2": onboarding.onboarding_b2, + "onboarding_b3": onboarding.onboarding_b3, + }, + unique_keys=["user_id"], + ) + .execute() + .data + ) + + if not response: + raise HTTPException(404, "User onboarding not updated") + return Onboardings(**response[0]) diff --git a/backend/models/databases/supabase/supabase.py b/backend/models/databases/supabase/supabase.py index 4941eb735..e3efdf471 100644 --- a/backend/models/databases/supabase/supabase.py +++ b/backend/models/databases/supabase/supabase.py @@ -7,6 +7,7 @@ from models.databases.supabase import ( File, Knowledges, Notifications, + Onboarding, Prompts, UserUsage, Vector, @@ -23,6 +24,7 @@ class SupabaseDB( ApiKeyHandler, Chats, Vector, + Onboarding, Prompts, Notifications, Knowledges, @@ -39,3 +41,4 @@ class SupabaseDB( Prompts.__init__(self, supabase_client) Notifications.__init__(self, supabase_client) Knowledges.__init__(self, supabase_client) + Onboarding.__init__(self, supabase_client) diff --git a/backend/models/onboarding.py b/backend/models/onboarding.py new file mode 100644 index 000000000..55bb59f0e --- /dev/null +++ b/backend/models/onboarding.py @@ -0,0 +1,12 @@ +from dataclasses import dataclass +from uuid import UUID + +from pydantic import BaseModel + + +@dataclass +class Onboardings(BaseModel): + user_id: UUID + onboarding_b1: bool + onboarding_b2: bool + onboarding_b3: bool diff --git a/backend/repository/onboarding/get_user_onboarding.py b/backend/repository/onboarding/get_user_onboarding.py new file mode 100644 index 000000000..5eff92916 --- /dev/null +++ b/backend/repository/onboarding/get_user_onboarding.py @@ -0,0 +1,18 @@ +from uuid import UUID + +from models.onboarding import Onboardings +from models.settings import get_supabase_db + + +def get_user_onboarding(user_id: UUID) -> Onboardings: + """ + Get a user's onboarding status + + Args: + user_id (UUID): The id of the user + + Returns: + Onboardings: The user's onboarding status + """ + supabase_db = get_supabase_db() + return supabase_db.get_user_onboarding(user_id) diff --git a/backend/repository/onboarding/udpate_user_onboarding.py b/backend/repository/onboarding/udpate_user_onboarding.py new file mode 100644 index 000000000..d6821978f --- /dev/null +++ b/backend/repository/onboarding/udpate_user_onboarding.py @@ -0,0 +1,14 @@ +from uuid import UUID + +from models.databases.supabase.onboarding import OnboardingUpdatableProperties +from models.onboarding import Onboardings +from models.settings import get_supabase_db + + +def update_user_onboarding( + user_id: UUID, onboarding: OnboardingUpdatableProperties +) -> Onboardings: + """Update user onboarding information by user_id""" + + supabase_db = get_supabase_db() + return supabase_db.update_user_onboarding(user_id, onboarding) diff --git a/backend/routes/onboarding_routes.py b/backend/routes/onboarding_routes.py new file mode 100644 index 000000000..7c2ca348c --- /dev/null +++ b/backend/routes/onboarding_routes.py @@ -0,0 +1,42 @@ +from auth import ( + AuthBearer, + get_current_user, # Assuming you have a get_current_user function +) +from fastapi import APIRouter, Depends +from models.databases.supabase.onboarding import OnboardingUpdatableProperties +from models.user_identity import UserIdentity +from repository.onboarding.get_user_onboarding import get_user_onboarding +from repository.onboarding.udpate_user_onboarding import update_user_onboarding + +onboarding_router = APIRouter() + + +@onboarding_router.get( + "/onboarding", + dependencies=[Depends(AuthBearer())], + tags=["Onboarding"], +) +async def get_user_onboarding_handler( + current_user: UserIdentity = Depends(get_current_user), +): + """ + Get user onboarding information for the current user + """ + + return get_user_onboarding(current_user.id) + + +@onboarding_router.put( + "/onboarding", + dependencies=[Depends(AuthBearer())], + tags=["Onboarding"], +) +async def update_user_onboarding_handler( + onboarding: OnboardingUpdatableProperties, + current_user: UserIdentity = Depends(get_current_user), +): + """ + Update user onboarding information for the current user + """ + + return update_user_onboarding(current_user.id, onboarding) diff --git a/scripts/20231004150000_add_onboarding_table.sql b/scripts/20231004150000_add_onboarding_table.sql new file mode 100644 index 000000000..c495eba5a --- /dev/null +++ b/scripts/20231004150000_add_onboarding_table.sql @@ -0,0 +1,8 @@ +-- Create the onboarding table +CREATE TABLE IF NOT EXISTS onboardings ( + user_id UUID NOT NULL REFERENCES auth.users (id), + onboarding_b1 BOOLEAN NOT NULL DEFAULT false, + onboarding_b2 BOOLEAN NOT NULL DEFAULT false, + onboarding_b3 BOOLEAN NOT NULL DEFAULT false, + PRIMARY KEY (user_id) +) \ No newline at end of file diff --git a/scripts/tables.sql b/scripts/tables.sql index cde9111eb..8b2cd2f52 100644 --- a/scripts/tables.sql +++ b/scripts/tables.sql @@ -256,6 +256,15 @@ CREATE TABLE IF NOT EXISTS knowledge_vectors ( ); +-- Create the onboarding table +CREATE TABLE IF NOT EXISTS onboardings ( + user_id UUID NOT NULL REFERENCES auth.users (id), + onboarding_b1 BOOLEAN NOT NULL DEFAULT false, + onboarding_b2 BOOLEAN NOT NULL DEFAULT false, + onboarding_b3 BOOLEAN NOT NULL DEFAULT false, + PRIMARY KEY (user_id) +) + insert into storage.buckets (id, name) values @@ -270,9 +279,9 @@ CREATE POLICY "Access Quivr Storage 1jccrwz_2" ON storage.objects FOR UPDATE TO CREATE POLICY "Access Quivr Storage 1jccrwz_3" ON storage.objects FOR DELETE TO anon USING (bucket_id = 'quivr'); INSERT INTO migrations (name) -SELECT '202309307004032_change_user_settings' +SELECT '20231004150000_add_onboarding_table' WHERE NOT EXISTS ( - SELECT 1 FROM migrations WHERE name = '202309307004032_change_user_settings' + SELECT 1 FROM migrations WHERE name = '20231004150000_add_onboarding_table' );