feat: init install + cleanup code

This commit is contained in:
swve 2023-11-20 22:38:49 +01:00
parent 2485285a06
commit 38288e8a57
28 changed files with 310 additions and 583 deletions

View file

@ -1,23 +1,8 @@
import logging import logging
from fastapi import FastAPI from fastapi import FastAPI
import motor.motor_asyncio import motor.motor_asyncio
from sqlmodel import Field, SQLModel, Session, create_engine from sqlmodel import SQLModel, Session, create_engine
from src.db import (
user_organizations,
users,
roles,
organization_settings,
organizations,
courses,
course_authors,
chapters,
activities,
course_chapters,
chapter_activities,
collections,
blocks,
)
engine = create_engine( engine = create_engine(
"postgresql://learnhouse:learnhouse@db:5432/learnhouse", echo=True "postgresql://learnhouse:learnhouse@db:5432/learnhouse", echo=True

View file

@ -1,7 +1,6 @@
from typing import Literal, Optional from typing import Optional
from click import Option
from sqlalchemy import JSON, Column from sqlalchemy import JSON, Column
from sqlmodel import Field, Session, SQLModel, create_engine, select from sqlmodel import Field, SQLModel
from enum import Enum from enum import Enum

View file

@ -1,6 +1,5 @@
from typing import Optional from typing import Optional
from sqlmodel import Field, SQLModel from sqlmodel import Field, SQLModel
from enum import Enum
class ChapterActivity(SQLModel, table=True): class ChapterActivity(SQLModel, table=True):
id: Optional[int] = Field(default=None, primary_key=True) id: Optional[int] = Field(default=None, primary_key=True)

View file

@ -1,7 +1,7 @@
from typing import List, Optional from typing import List, Optional
from pydantic import BaseModel from pydantic import BaseModel
from sqlmodel import Field, SQLModel from sqlmodel import Field, SQLModel
from src.db.activities import Activity, ActivityRead, ActivityUpdate from src.db.activities import ActivityRead
class ChapterBase(SQLModel): class ChapterBase(SQLModel):

View file

@ -1,6 +1,5 @@
from typing import Optional from typing import Optional
from sqlmodel import Field, SQLModel from sqlmodel import Field, SQLModel
from enum import Enum
class CourseChapter(SQLModel, table=True): class CourseChapter(SQLModel, table=True):
id: Optional[int] = Field(default=None, primary_key=True) id: Optional[int] = Field(default=None, primary_key=True)

View file

@ -0,0 +1,31 @@
from typing import Optional
from sqlalchemy import JSON, Column
from sqlmodel import Field, SQLModel
class InstallBase(SQLModel):
step: int = Field(default=0)
data: dict = Field(default={}, sa_column=Column(JSON))
class Install(InstallBase, table=True):
id: Optional[int] = Field(default=None, primary_key=True)
install_uuid: str = Field(default=None)
creation_date: str = ""
update_date: str = ""
class InstallCreate(InstallBase):
pass
class InstallUpdate(InstallBase):
pass
class InstallRead(InstallBase):
id: Optional[int] = Field(default=None, primary_key=True)
install_uuid: str = Field(default=None)
creation_date: str
update_date: str
pass

View file

@ -1,19 +1,46 @@
from enum import Enum from enum import Enum
from typing import Optional from typing import Optional, Union
from pydantic import BaseModel
from sqlalchemy import JSON, Column from sqlalchemy import JSON, Column
from sqlmodel import Field, SQLModel from sqlmodel import Field, SQLModel
# Rights
class Permission(BaseModel):
action_create: bool
action_read: bool
action_update: bool
action_delete: bool
def __getitem__(self, item):
return getattr(self, item)
class Rights(BaseModel):
courses: Permission
users: Permission
collections: Permission
organizations: Permission
coursechapters: Permission
activities: Permission
def __getitem__(self, item):
return getattr(self, item)
# Database Models
class RoleTypeEnum(str, Enum): class RoleTypeEnum(str, Enum):
TYPE_ORGANIZATION = "TYPE_ORGANIZATION" TYPE_ORGANIZATION = "TYPE_ORGANIZATION" # Organization roles are associated with an organization, they are used to define the rights of a user in an organization
TYPE_ORGANIZATION_API_TOKEN = "TYPE_ORGANIZATION_API_TOKEN" TYPE_ORGANIZATION_API_TOKEN = "TYPE_ORGANIZATION_API_TOKEN" # Organization API Token roles are associated with an organization, they are used to define the rights of an API Token in an organization
TYPE_GLOBAL = "TYPE_GLOBAL" TYPE_GLOBAL = "TYPE_GLOBAL" # Global roles are not associated with an organization, they are used to define the default rights of a user
class RoleBase(SQLModel): class RoleBase(SQLModel):
name: str name: str
description: Optional[str] description: Optional[str]
rights: dict = Field(default={}, sa_column=Column(JSON)) rights: Optional[Union[Rights,dict]] = Field(default={}, sa_column=Column(JSON))
class Role(RoleBase, table=True): class Role(RoleBase, table=True):
@ -26,11 +53,12 @@ class Role(RoleBase, table=True):
class RoleCreate(RoleBase): class RoleCreate(RoleBase):
org_id: int = Field(default=None, foreign_key="organization.id") org_id: Optional[int] = Field(default=None, foreign_key="organization.id")
class RoleUpdate(SQLModel): class RoleUpdate(SQLModel):
role_id: int = Field(default=None, foreign_key="role.id") role_id: int = Field(default=None, foreign_key="role.id")
name: Optional[str] name: Optional[str]
description: Optional[str] description: Optional[str]
rights: Optional[dict] = Field(default={}, sa_column=Column(JSON)) rights: Optional[Union[Rights,dict]] = Field(default={}, sa_column=Column(JSON))

View file

@ -1,10 +1,8 @@
from typing import Optional from typing import Optional
from pydantic import BaseModel from pydantic import BaseModel
from sqlmodel import Field, SQLModel from sqlmodel import Field, SQLModel
from enum import Enum from src.db.trail_runs import TrailRunRead
from src.db.trail_runs import TrailRun, TrailRunRead
from src.db.trail_steps import TrailStep
class TrailBase(SQLModel): class TrailBase(SQLModel):

View file

@ -5,7 +5,6 @@ from src.db.users import UserRead
from src.core.events.database import get_db_session from src.core.events.database import get_db_session
from config.config import get_learnhouse_config from config.config import get_learnhouse_config
from src.security.auth import AuthJWT, authenticate_user from src.security.auth import AuthJWT, authenticate_user
from src.services.users.users import PublicUser
router = APIRouter() router = APIRouter()

View file

@ -3,7 +3,6 @@ from src.db.activities import ActivityCreate, ActivityUpdate
from src.db.users import PublicUser from src.db.users import PublicUser
from src.core.events.database import get_db_session from src.core.events.database import get_db_session
from src.services.courses.activities.activities import ( from src.services.courses.activities.activities import (
Activity,
create_activity, create_activity,
get_activity, get_activity,
get_activities, get_activities,

View file

@ -4,7 +4,6 @@ from src.db.collections import CollectionCreate, CollectionUpdate
from src.security.auth import get_current_user from src.security.auth import get_current_user
from src.services.users.users import PublicUser from src.services.users.users import PublicUser
from src.services.courses.collections import ( from src.services.courses.collections import (
Collection,
create_collection, create_collection,
get_collection, get_collection,
get_collections, get_collections,

View file

@ -2,7 +2,7 @@ from fastapi import APIRouter, Depends, UploadFile, Form, Request
from sqlmodel import Session from sqlmodel import Session
from src.core.events.database import get_db_session from src.core.events.database import get_db_session
from src.db.users import PublicUser from src.db.users import PublicUser
from src.db.courses import Course, CourseCreate, CourseUpdate from src.db.courses import CourseCreate, CourseUpdate
from src.security.auth import get_current_user from src.security.auth import get_current_user
from src.services.courses.courses import ( from src.services.courses.courses import (

View file

@ -1,6 +1,5 @@
from fastapi import APIRouter, Request from fastapi import APIRouter
from config.config import get_learnhouse_config from config.config import get_learnhouse_config
from src.services.dev.mocks.initial import create_initial_data
router = APIRouter() router = APIRouter()
@ -10,9 +9,3 @@ router = APIRouter()
async def config(): async def config():
config = get_learnhouse_config() config = get_learnhouse_config()
return config.dict() return config.dict()
@router.get("/mock/initial")
async def initial_data(request: Request):
await create_initial_data(request)
return {"Message": "Initial data created 🤖"}

View file

@ -1,70 +1,71 @@
from fastapi import APIRouter, Request from fastapi import APIRouter, Depends, Request
from src.core.events.database import get_db_session
from src.db.organizations import OrganizationCreate
from src.db.users import UserCreate
from src.services.install.install import ( from src.services.install.install import (
create_install_instance, create_install_instance,
create_sample_data,
get_latest_install_instance, get_latest_install_instance,
install_create_organization, install_create_organization,
install_create_organization_user, install_create_organization_user,
install_default_elements, install_default_elements,
update_install_instance, update_install_instance,
) )
from src.services.orgs.schemas.orgs import Organization
from src.services.users.schemas.users import UserWithPassword
router = APIRouter() router = APIRouter()
@router.post("/start") @router.post("/start")
async def api_create_install_instance(request: Request, data: dict): async def api_create_install_instance(
request: Request, data: dict, db_session=Depends(get_db_session),
):
# create install # create install
install = await create_install_instance(request, data) install = await create_install_instance(request, data, db_session)
return install return install
@router.get("/latest") @router.get("/latest")
async def api_get_latest_install_instance(request: Request): async def api_get_latest_install_instance(request: Request, db_session=Depends(get_db_session),):
# get latest created install # get latest created install
install = await get_latest_install_instance(request) install = await get_latest_install_instance(request, db_session=db_session)
return install return install
@router.post("/default_elements") @router.post("/default_elements")
async def api_install_def_elements(request: Request): async def api_install_def_elements(request: Request, db_session=Depends(get_db_session),):
elements = await install_default_elements(request, {}) elements = await install_default_elements(request, {}, db_session)
return elements return elements
@router.post("/org") @router.post("/org")
async def api_install_org(request: Request, org: Organization): async def api_install_org(
organization = await install_create_organization(request, org) request: Request, org: OrganizationCreate, db_session=Depends(get_db_session),
):
organization = await install_create_organization(request, org, db_session)
return organization return organization
@router.post("/user") @router.post("/user")
async def api_install_user(request: Request, data: UserWithPassword, org_slug: str): async def api_install_user(
user = await install_create_organization_user(request, data, org_slug) request: Request, data: UserCreate, org_slug: str, db_session=Depends(get_db_session),
):
user = await install_create_organization_user(request, data, org_slug, db_session)
return user return user
@router.post("/sample")
async def api_install_user_sample(request: Request, username: str, org_slug: str):
sample = await create_sample_data(org_slug, username, request)
return sample
@router.post("/update") @router.post("/update")
async def api_update_install_instance(request: Request, data: dict, step: int): async def api_update_install_instance(
request: Request, data: dict, step: int, db_session=Depends(get_db_session),
):
request.app.db["installs"] request.app.db["installs"]
# get latest created install # get latest created install
install = await update_install_instance(request, data, step) install = await update_install_instance(request, data, step, db_session)
return install return install

View file

@ -3,7 +3,6 @@ from sqlmodel import Session
from src.core.events.database import get_db_session from src.core.events.database import get_db_session
from src.db.roles import RoleCreate, RoleUpdate from src.db.roles import RoleCreate, RoleUpdate
from src.security.auth import get_current_user from src.security.auth import get_current_user
from src.services.roles.schemas.roles import Role
from src.services.roles.roles import create_role, delete_role, read_role, update_role from src.services.roles.roles import create_role, delete_role, read_role, update_role
from src.services.users.schemas.users import PublicUser from src.services.users.schemas.users import PublicUser

View file

@ -1,19 +1,9 @@
import stat
from typing import Literal
from pydantic import BaseModel
from sqlmodel import Session, select from sqlmodel import Session, select
from src.db.chapters import Chapter
from src.db.organizations import Organization from src.db.organizations import Organization
from src import db
from src.db.activities import ActivityCreate, Activity, ActivityRead, ActivityUpdate from src.db.activities import ActivityCreate, Activity, ActivityRead, ActivityUpdate
from src.db.chapter_activities import ChapterActivity from src.db.chapter_activities import ChapterActivity
from src.security.rbac.rbac import ( from src.db.users import PublicUser
authorization_verify_based_on_roles, from fastapi import HTTPException, Request
authorization_verify_if_element_is_public,
authorization_verify_if_user_is_anon,
)
from src.db.users import AnonymousUser, PublicUser
from fastapi import HTTPException, status, Request
from uuid import uuid4 from uuid import uuid4
from datetime import datetime from datetime import datetime

View file

@ -9,7 +9,6 @@ from src.db.activities import (
from src.db.chapter_activities import ChapterActivity from src.db.chapter_activities import ChapterActivity
from src.db.course_chapters import CourseChapter from src.db.course_chapters import CourseChapter
from src.db.users import PublicUser from src.db.users import PublicUser
from src.security.rbac.rbac import authorization_verify_based_on_roles
from src.services.courses.activities.uploads.pdfs import upload_pdf from src.services.courses.activities.uploads.pdfs import upload_pdf
from fastapi import HTTPException, status, UploadFile, Request from fastapi import HTTPException, status, UploadFile, Request
from uuid import uuid4 from uuid import uuid4

View file

@ -7,9 +7,6 @@ from src.db.activities import Activity, ActivityRead, ActivitySubTypeEnum, Activ
from src.db.chapter_activities import ChapterActivity from src.db.chapter_activities import ChapterActivity
from src.db.course_chapters import CourseChapter from src.db.course_chapters import CourseChapter
from src.db.users import PublicUser from src.db.users import PublicUser
from src.security.rbac.rbac import (
authorization_verify_based_on_roles,
)
from src.services.courses.activities.uploads.videos import upload_video from src.services.courses.activities.uploads.videos import upload_video
from fastapi import HTTPException, status, UploadFile, Request from fastapi import HTTPException, status, UploadFile, Request
from uuid import uuid4 from uuid import uuid4

View file

@ -2,7 +2,6 @@ from datetime import datetime
from typing import List from typing import List
from uuid import uuid4 from uuid import uuid4
from sqlmodel import Session, select from sqlmodel import Session, select
from src import db
from src.db.course_chapters import CourseChapter from src.db.course_chapters import CourseChapter
from src.db.activities import Activity, ActivityRead from src.db.activities import Activity, ActivityRead
from src.db.chapter_activities import ChapterActivity from src.db.chapter_activities import ChapterActivity
@ -14,13 +13,6 @@ from src.db.chapters import (
ChapterUpdateOrder, ChapterUpdateOrder,
DepreceatedChaptersRead, DepreceatedChaptersRead,
) )
from src.security.auth import non_public_endpoint
from src.security.rbac.rbac import (
authorization_verify_based_on_roles,
authorization_verify_based_on_roles_and_authorship,
authorization_verify_if_element_is_public,
authorization_verify_if_user_is_anon,
)
from src.services.courses.courses import Course from src.services.courses.courses import Course
from src.services.users.users import PublicUser from src.services.users.users import PublicUser
from fastapi import HTTPException, status, Request from fastapi import HTTPException, status, Request

View file

@ -1,8 +1,6 @@
from datetime import datetime from datetime import datetime
from gc import collect from typing import List
from typing import List, Literal
from uuid import uuid4 from uuid import uuid4
from pydantic import BaseModel
from sqlmodel import Session, select from sqlmodel import Session, select
from src.db.collections import ( from src.db.collections import (
Collection, Collection,
@ -12,10 +10,6 @@ from src.db.collections import (
) )
from src.db.collections_courses import CollectionCourse from src.db.collections_courses import CollectionCourse
from src.db.courses import Course from src.db.courses import Course
from src.security.rbac.rbac import (
authorization_verify_based_on_roles_and_authorship,
authorization_verify_if_user_is_anon,
)
from src.services.users.users import PublicUser from src.services.users.users import PublicUser
from fastapi import HTTPException, status, Request from fastapi import HTTPException, status, Request
from typing import List from typing import List

View file

@ -1,13 +1,11 @@
import json import json
from typing import List, Literal, Optional from typing import Literal
from uuid import uuid4 from uuid import uuid4
from pydantic import BaseModel
from sqlmodel import Session, select from sqlmodel import Session, select
from src.db.course_authors import CourseAuthor, CourseAuthorshipEnum from src.db.course_authors import CourseAuthor, CourseAuthorshipEnum
from src.db.users import PublicUser, AnonymousUser from src.db.users import PublicUser, AnonymousUser
from src.db.courses import Course, CourseCreate, CourseRead, CourseUpdate from src.db.courses import Course, CourseCreate, CourseRead, CourseUpdate
from src.security.rbac.rbac import ( from src.security.rbac.rbac import (
authorization_verify_based_on_roles,
authorization_verify_based_on_roles_and_authorship, authorization_verify_based_on_roles_and_authorship,
authorization_verify_if_element_is_public, authorization_verify_if_element_is_public,
authorization_verify_if_user_is_anon, authorization_verify_if_user_is_anon,

View file

@ -1,213 +0,0 @@
import os
import requests
from datetime import datetime
from uuid import uuid4
from fastapi import Request
from src.services.users.schemas.users import UserInDB
from src.security.security import security_hash_password
from src.services.courses.activities.activities import Activity, create_activity
from src.services.users.users import PublicUser
from src.services.orgs.orgs import Organization, create_org
from src.services.roles.schemas.roles import Permission, Elements, RoleInDB
from faker import Faker
async def create_initial_data(request: Request):
fake = Faker(['en_US'])
fake_multilang = Faker(
['en_US', 'de_DE', 'ja_JP', 'es_ES', 'it_IT', 'pt_BR', 'ar_PS'])
# Create users
########################################
database_users = request.app.db["users"]
await database_users.delete_many({})
users = []
admin_user = UserInDB(
user_id="user_admin",
creation_date=str(datetime.now()),
update_date=str(datetime.now()),
roles= [],
orgs=[],
username="admin",
email="admin@admin.admin",
password=str(await security_hash_password("admin")),
)
await database_users.insert_one(admin_user.dict())
# find admin user
users = request.app.db["users"]
admin_user = await users.find_one({"username": "admin"})
if admin_user:
admin_user = UserInDB(**admin_user)
current_user = PublicUser(**admin_user.dict())
else:
raise Exception("Admin user not found")
# Create roles
########################################
database_roles = request.app.db["roles"]
await database_roles.delete_many({})
roles = []
admin_role = RoleInDB(
name="Admin",
description="Admin",
elements=Elements(
courses=Permission(
action_create=True,
action_read=True,
action_update=True,
action_delete=True,
),
users=Permission(
action_create=True,
action_read=True,
action_update=True,
action_delete=True,
),
houses=Permission(
action_create=True,
action_read=True,
action_update=True,
action_delete=True,
),
collections=Permission(
action_create=True,
action_read=True,
action_update=True,
action_delete=True,
),
organizations=Permission(
action_create=True,
action_read=True,
action_update=True,
action_delete=True,
),
coursechapters=Permission(
action_create=True,
action_read=True,
action_update=True,
action_delete=True,
),
activities=Permission(
action_create=True,
action_read=True,
action_update=True,
action_delete=True,
),
),
org_id="org_test",
role_id="role_admin",
created_at=str(datetime.now()),
updated_at=str(datetime.now()),
)
roles.append(admin_role)
for role in roles:
database_roles.insert_one(role.dict())
# Create organizations
########################################
database_orgs = request.app.db["organizations"]
await database_orgs.delete_many({})
organizations = []
for i in range(0, 2):
company = fake.company()
# remove whitespace and special characters and make lowercase
slug = ''.join(e for e in company if e.isalnum()).lower()
# org = Organization(
# name=company,
# description=fake.unique.text(),
# email=fake.unique.email(),
# slug=slug,
# logo="",
# default=False
# )
# organizations.append(org)
# await create_org(request, org, current_user)
# Generate Courses and CourseChapters
########################################
database_courses = request.app.db["courses"]
await database_courses.delete_many({})
courses = []
orgs = request.app.db["organizations"]
if await orgs.count_documents({}) > 0:
for org in await orgs.find().to_list(length=100):
for i in range(0, 5):
# get image in BinaryIO format from unsplash and save it to disk
image = requests.get(
"https://source.unsplash.com/random/800x600")
with open("thumbnail.jpg", "wb") as f:
f.write(image.content)
course_id = f"course_{uuid4()}"
# course = CourseInDB(
# name=fake_multilang.unique.sentence(),
# description=fake_multilang.unique.text(),
# mini_description=fake_multilang.unique.text(),
# thumbnail="thumbnail",
# org_id=org['org_id'],
# learnings=[fake_multilang.unique.sentence()
# for i in range(0, 5)],
# public=True,
# chapters=[],
# course_id=course_id,
# creationDate=str(datetime.now()),
# updateDate=str(datetime.now()),
# authors=[current_user.user_id],
# chapters_content=[],
# )
courses = request.app.db["courses"]
name_in_disk = f"test_mock{course_id}.jpeg"
image = requests.get(
"https://source.unsplash.com/random/800x600/?img=1")
# check if folder exists and create it if not
if not os.path.exists("content/uploads/img"):
os.makedirs("content/uploads/img")
with open(f"content/uploads/img/{name_in_disk}", "wb") as f:
f.write(image.content)
# course.thumbnail = name_in_disk
# course = CourseInDB(**course.dict())
# await courses.insert_one(course.dict())
# create chapters
# for i in range(0, 5):
# coursechapter = CourseChapter(
# name=fake_multilang.unique.sentence(),
# description=fake_multilang.unique.text(),
# activities=[],
# )
# coursechapter = await create_coursechapter(request,coursechapter, course_id, current_user)
# if coursechapter:
# # create activities
# for i in range(0, 5):
# activity = Activity(
# name=fake_multilang.unique.sentence(),
# type="dynamic",
# content={},
# )
# activity = await create_activity(request,activity, "org_test", coursechapter['coursechapter_id'], current_user)

View file

@ -1,33 +1,15 @@
from datetime import datetime from datetime import datetime
from uuid import uuid4 from uuid import uuid4
from fastapi import HTTPException, Request, status from fastapi import HTTPException, Request
from pydantic import BaseModel from sqlalchemy import desc
import requests from sqlmodel import Session, select
from src.db.install import Install
from src.db.organizations import Organization, OrganizationCreate
from src.db.roles import Permission, Rights, Role, RoleTypeEnum
from src.db.user_organizations import UserOrganization
from src.db.users import User, UserCreate, UserRead
from config.config import get_learnhouse_config from config.config import get_learnhouse_config
from src.security.security import security_hash_password from src.security.security import security_hash_password
from src.services.courses.activities.activities import Activity, create_activity
from src.services.orgs.schemas.orgs import Organization, OrganizationInDB
from faker import Faker
from src.services.roles.schemas.roles import Elements, Permission, RoleInDB
from src.services.users.schemas.users import (
PublicUser,
User,
UserInDB,
UserOrganization,
UserRolesInOrganization,
UserWithPassword,
)
class InstallInstance(BaseModel):
install_id: str
created_date: str
updated_date: str
step: int
data: dict
async def isInstallModeEnabled(): async def isInstallModeEnabled():
@ -42,37 +24,29 @@ async def isInstallModeEnabled():
) )
async def create_install_instance(request: Request, data: dict): async def create_install_instance(request: Request, data: dict, db_session: Session):
installs = request.app.db["installs"] install = Install.from_orm(data)
# get install_id # complete install instance
install_id = str(f"install_{uuid4()}") install.install_uuid = str(f"install_{uuid4()}")
created_date = datetime.now().strftime("%Y-%m-%d %H:%M:%S") install.update_date = str(datetime.now())
updated_date = datetime.now().strftime("%Y-%m-%d %H:%M:%S") install.creation_date = str(datetime.now())
step = 1
# create install # insert install instance
install = InstallInstance( db_session.add(install)
install_id=install_id,
created_date=created_date,
updated_date=updated_date,
step=step,
data=data,
)
# insert install # commit changes
installs.insert_one(install.dict()) db_session.commit()
# refresh install instance
db_session.refresh(install)
return install return install
async def get_latest_install_instance(request: Request): async def get_latest_install_instance(request: Request, db_session: Session):
installs = request.app.db["installs"] statement = select(Install).order_by(desc(Install.creation_date)).limit(1)
install = db_session.exec(statement).first()
# get latest created install instance using find_one
install = await installs.find_one(
sort=[("created_date", -1)], limit=1, projection={"_id": 0}
)
if install is None: if install is None:
raise HTTPException( raise HTTPException(
@ -80,37 +54,31 @@ async def get_latest_install_instance(request: Request):
detail="No install instance found", detail="No install instance found",
) )
else: return install
install = InstallInstance(**install)
return install
async def update_install_instance(request: Request, data: dict, step: int): async def update_install_instance(
installs = request.app.db["installs"] request: Request, data: dict, step: int, db_session: Session
):
# get latest created install statement = select(Install).order_by(desc(Install.creation_date)).limit(1)
install = await installs.find_one( install = db_session.exec(statement).first()
sort=[("created_date", -1)], limit=1, projection={"_id": 0}
)
if install is None: if install is None:
return None raise HTTPException(
status_code=404,
else: detail="No install instance found",
# update install
install["data"] = data
install["step"] = step
install["updated_date"] = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
# update install
await installs.update_one(
{"install_id": install["install_id"]}, {"$set": install}
) )
install = InstallInstance(**install) install.step = step
install.data = data
return install # commit changes
db_session.commit()
# refresh install instance
db_session.refresh(install)
return install
############################################################################################################ ############################################################################################################
@ -119,24 +87,34 @@ async def update_install_instance(request: Request, data: dict, step: int):
# Install Default roles # Install Default roles
async def install_default_elements(request: Request, data: dict): async def install_default_elements(request: Request, data: dict, db_session: Session):
roles = request.app.db["roles"] # remove all default roles
statement = select(Role).where(Role.role_type == RoleTypeEnum.TYPE_GLOBAL)
roles = db_session.exec(statement).all()
# check if default roles ADMIN_ROLE and USER_ROLE already exist for role in roles:
admin_role = await roles.find_one({"role_id": "role_admin"}) db_session.delete(role)
user_role = await roles.find_one({"role_id": "role_member"})
if admin_role is not None or user_role is not None: db_session.commit()
# Check if default roles already exist
statement = select(Role).where(Role.role_type == RoleTypeEnum.TYPE_GLOBAL)
roles = db_session.exec(statement).all()
if roles and len(roles) == 3:
raise HTTPException( raise HTTPException(
status_code=400, status_code=409,
detail="Default roles already exist", detail="Default roles already exist",
) )
# get default roles # Create default roles
ADMIN_ROLE = RoleInDB( role_global_admin = Role(
name="Admin Role", name="Admin",
description="This role grants all permissions to the user", description="Standard Admin Role",
elements=Elements( id=1,
role_type=RoleTypeEnum.TYPE_GLOBAL,
role_uuid="role_global_admin",
rights=Rights(
courses=Permission( courses=Permission(
action_create=True, action_create=True,
action_read=True, action_read=True,
@ -149,12 +127,6 @@ async def install_default_elements(request: Request, data: dict):
action_update=True, action_update=True,
action_delete=True, action_delete=True,
), ),
houses=Permission(
action_create=True,
action_read=True,
action_update=True,
action_delete=True,
),
collections=Permission( collections=Permission(
action_create=True, action_create=True,
action_read=True, action_read=True,
@ -180,16 +152,65 @@ async def install_default_elements(request: Request, data: dict):
action_delete=True, action_delete=True,
), ),
), ),
org_id="*", creation_date=str(datetime.now()),
role_id="role_admin", update_date=str(datetime.now()),
created_at=str(datetime.now()),
updated_at=str(datetime.now()),
) )
USER_ROLE = RoleInDB( role_global_maintainer = Role(
name="Member Role", name="Maintainer",
description="This role grants read-only permissions to the user", description="Standard Maintainer Role",
elements=Elements( id=2,
role_type=RoleTypeEnum.TYPE_GLOBAL,
role_uuid="role_global_maintainer",
rights=Rights(
courses=Permission(
action_create=True,
action_read=True,
action_update=True,
action_delete=True,
),
users=Permission(
action_create=True,
action_read=True,
action_update=True,
action_delete=True,
),
collections=Permission(
action_create=True,
action_read=True,
action_update=True,
action_delete=True,
),
organizations=Permission(
action_create=True,
action_read=True,
action_update=True,
action_delete=True,
),
coursechapters=Permission(
action_create=True,
action_read=True,
action_update=True,
action_delete=True,
),
activities=Permission(
action_create=True,
action_read=True,
action_update=True,
action_delete=True,
),
),
creation_date=str(datetime.now()),
update_date=str(datetime.now()),
)
role_global_user = Role(
name="User",
description="Standard User Role",
role_type=RoleTypeEnum.TYPE_GLOBAL,
role_uuid="role_global_user",
id=3,
rights=Rights(
courses=Permission( courses=Permission(
action_create=False, action_create=False,
action_read=True, action_read=True,
@ -197,13 +218,7 @@ async def install_default_elements(request: Request, data: dict):
action_delete=False, action_delete=False,
), ),
users=Permission( users=Permission(
action_create=False, action_create=True,
action_read=True,
action_update=False,
action_delete=False,
),
houses=Permission(
action_create=False,
action_read=True, action_read=True,
action_update=False, action_update=False,
action_delete=False, action_delete=False,
@ -233,185 +248,122 @@ async def install_default_elements(request: Request, data: dict):
action_delete=False, action_delete=False,
), ),
), ),
org_id="*", creation_date=str(datetime.now()),
role_id="role_member", update_date=str(datetime.now()),
created_at=str(datetime.now()),
updated_at=str(datetime.now()),
) )
try: # Serialize rights to JSON
# insert default roles role_global_admin.rights = role_global_admin.rights.dict() # type: ignore
await roles.insert_many([USER_ROLE.dict(), ADMIN_ROLE.dict()]) role_global_maintainer.rights = role_global_maintainer.rights.dict() # type: ignore
return True role_global_user.rights = role_global_user.rights.dict() # type: ignore
except Exception: # Insert roles in DB
raise HTTPException( db_session.add(role_global_admin)
status_code=400, db_session.add(role_global_maintainer)
detail="Error while inserting default roles", db_session.add(role_global_user)
)
# commit changes
db_session.commit()
# refresh roles
db_session.refresh(role_global_admin)
return True
# Organization creation # Organization creation
async def install_create_organization( async def install_create_organization(
request: Request, request: Request, org_object: OrganizationCreate, db_session: Session
org_object: Organization,
): ):
orgs = request.app.db["organizations"] org = Organization.from_orm(org_object)
request.app.db["users"]
# find if org already exists using name # Complete the org object
org.org_uuid = f"org_{uuid4()}"
org.creation_date = str(datetime.now())
org.update_date = str(datetime.now())
isOrgAvailable = await orgs.find_one({"slug": org_object.slug.lower()}) db_session.add(org)
db_session.commit()
db_session.refresh(org)
if isOrgAvailable: return org
raise HTTPException(
status_code=status.HTTP_409_CONFLICT,
detail="Organization slug already exists",
)
# generate org_id with uuid4
org_id = str(f"org_{uuid4()}")
org = OrganizationInDB(org_id=org_id, **org_object.dict())
org_in_db = await orgs.insert_one(org.dict())
if not org_in_db:
raise HTTPException(
status_code=status.HTTP_503_SERVICE_UNAVAILABLE,
detail="Unavailable database",
)
return org.dict()
async def install_create_organization_user( async def install_create_organization_user(
request: Request, user_object: UserWithPassword, org_slug: str request: Request, user_object: UserCreate, org_slug: str, db_session: Session
): ):
users = request.app.db["users"] user = User.from_orm(user_object)
isUsernameAvailable = await users.find_one({"username": user_object.username}) # Complete the user object
isEmailAvailable = await users.find_one({"email": user_object.email}) user.user_uuid = f"user_{uuid4()}"
user.password = await security_hash_password(user_object.password)
user.email_verified = False
user.creation_date = str(datetime.now())
user.update_date = str(datetime.now())
if isUsernameAvailable: # Verifications
# Check if Organization exists
statement = select(Organization).where(Organization.slug == org_slug)
org = db_session.exec(statement)
if not org.first():
raise HTTPException( raise HTTPException(
status_code=status.HTTP_409_CONFLICT, detail="Username already exists" status_code=400,
detail="Organization does not exist",
) )
if isEmailAvailable: # Username
statement = select(User).where(User.username == user.username)
result = db_session.exec(statement)
if result.first():
raise HTTPException( raise HTTPException(
status_code=status.HTTP_409_CONFLICT, detail="Email already exists" status_code=400,
detail="Username already exists",
) )
# Generate user_id with uuid4 # Email
user_id = str(f"user_{uuid4()}") statement = select(User).where(User.email == user.email)
result = db_session.exec(statement)
# Set the username & hash the password if result.first():
user_object.username = user_object.username.lower()
user_object.password = await security_hash_password(user_object.password)
# Get org_id from org_slug
orgs = request.app.db["organizations"]
# Check if the org exists
isOrgExists = await orgs.find_one({"slug": org_slug})
# If the org does not exist, raise an error
if not isOrgExists:
raise HTTPException( raise HTTPException(
status_code=status.HTTP_409_CONFLICT, status_code=400,
detail="You are trying to create a user in an organization that does not exist", detail="Email already exists",
) )
org_id = isOrgExists["org_id"] # Exclude unset values
user_data = user.dict(exclude_unset=True)
for key, value in user_data.items():
setattr(user, key, value)
# Create initial orgs list with the org_id passed in # Add user to database
orgs = [UserOrganization(org_id=org_id, org_role="owner")] db_session.add(user)
db_session.commit()
db_session.refresh(user)
# Give role
roles = [UserRolesInOrganization(role_id="role_admin", org_id=org_id)]
# Create the user
user = UserInDB( # get org id
user_id=user_id, statement = select(Organization).where(Organization.slug == org_slug)
org = db_session.exec(statement)
org = org.first()
org_id = org.id if org else 0
# Link user and organization
user_organization = UserOrganization(
user_id=user.id if user.id else 0,
org_id=org_id or 0,
role_id=1,
creation_date=str(datetime.now()), creation_date=str(datetime.now()),
update_date=str(datetime.now()), update_date=str(datetime.now()),
orgs=orgs,
roles=roles,
**user_object.dict(),
) )
# Insert the user into the database db_session.add(user_organization)
await users.insert_one(user.dict()) db_session.commit()
db_session.refresh(user_organization)
return User(**user.dict()) user = UserRead.from_orm(user)
return user
async def create_sample_data(org_slug: str, username: str, request: Request):
Faker(["en_US"])
fake_multilang = Faker(
["en_US", "de_DE", "ja_JP", "es_ES", "it_IT", "pt_BR", "ar_PS"]
)
users = request.app.db["users"]
orgs = request.app.db["organizations"]
user = await users.find_one({"username": username})
org = await orgs.find_one({"slug": org_slug.lower()})
user_id = user["user_id"]
org_id = org["org_id"]
current_user = PublicUser(**user)
for i in range(0, 5):
# get image in BinaryIO format from unsplash and save it to disk
image = requests.get("https://source.unsplash.com/random/800x600")
with open("thumbnail.jpg", "wb") as f:
f.write(image.content)
# course_id = f"course_{uuid4()}"
# course = CourseInDB(
# name=fake_multilang.unique.sentence(),
# description=fake_multilang.unique.text(),
# mini_description=fake_multilang.unique.text(),
# thumbnail="thumbnail",
# org_id=org_id,
# learnings=[fake_multilang.unique.sentence() for i in range(0, 5)],
# public=True,
# chapters=[],
# course_id=course_id,
# creationDate=str(datetime.now()),
# updateDate=str(datetime.now()),
# authors=[user_id],
# chapters_content=[],
# )
# courses = request.app.db["courses"]
# course = CourseInDB(**course.dict())
# await courses.insert_one(course.dict())
# # create chapters
# for i in range(0, 5):
# coursechapter = CourseChapter(
# name=fake_multilang.unique.sentence(),
# description=fake_multilang.unique.text(),
# activities=[],
# )
# coursechapter = await create_coursechapter(
# request, coursechapter, course_id, current_user
# )
# if coursechapter:
# # create activities
# for i in range(0, 5):
# activity = Activity(
# name=fake_multilang.unique.sentence(),
# type="dynamic",
# content={},
# )
# activity = await create_activity(
# request,
# activity,
# org_id,
# coursechapter["coursechapter_id"],
# current_user,
# )

View file

@ -1,10 +1,7 @@
from datetime import datetime from datetime import datetime
import json
from operator import or_
from typing import Literal
from uuid import uuid4 from uuid import uuid4
from sqlmodel import Session, select from sqlmodel import Session, select
from src.db.users import UserRead, PublicUser from src.db.users import PublicUser
from src.db.user_organizations import UserOrganization from src.db.user_organizations import UserOrganization
from src.db.organizations import ( from src.db.organizations import (
Organization, Organization,
@ -12,10 +9,6 @@ from src.db.organizations import (
OrganizationRead, OrganizationRead,
OrganizationUpdate, OrganizationUpdate,
) )
from src.security.rbac.rbac import (
authorization_verify_based_on_roles,
authorization_verify_if_user_is_anon,
)
from src.services.orgs.logos import upload_org_logo from src.services.orgs.logos import upload_org_logo
from fastapi import HTTPException, UploadFile, status, Request from fastapi import HTTPException, UploadFile, status, Request

View file

@ -1,9 +1,8 @@
from uuid import uuid4 from uuid import uuid4
from sqlmodel import Session, select from sqlmodel import Session, select
from src.db.roles import Role, RoleCreate, RoleUpdate from src.db.roles import Role, RoleCreate, RoleUpdate
from src.security.rbac.rbac import authorization_verify_if_user_is_anon
from src.services.users.schemas.users import PublicUser from src.services.users.schemas.users import PublicUser
from fastapi import HTTPException, status, Request from fastapi import HTTPException, Request
from datetime import datetime from datetime import datetime

View file

@ -1,12 +1,9 @@
from datetime import datetime from datetime import datetime
import stat
from typing import List, Literal, Optional
from uuid import uuid4 from uuid import uuid4
from fastapi import HTTPException, Request, status from fastapi import HTTPException, Request, status
from pydantic import BaseModel
from sqlmodel import Session, select from sqlmodel import Session, select
from src.db.courses import Course from src.db.courses import Course
from src.db.trail_runs import TrailRun, TrailRunCreate, TrailRunRead from src.db.trail_runs import TrailRun, TrailRunRead
from src.db.trail_steps import TrailStep from src.db.trail_steps import TrailStep
from src.db.trails import Trail, TrailCreate, TrailRead from src.db.trails import Trail, TrailCreate, TrailRead
from src.db.users import PublicUser from src.db.users import PublicUser

View file

@ -77,7 +77,7 @@ async def create_user(
user_organization = UserOrganization( user_organization = UserOrganization(
user_id=user.id if user.id else 0, user_id=user.id if user.id else 0,
org_id=int(org_id), org_id=int(org_id),
role_id=1, role_id=3,
creation_date=str(datetime.now()), creation_date=str(datetime.now()),
update_date=str(datetime.now()), update_date=str(datetime.now()),
) )