Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Grupper #90

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions api_schemas/group_schema.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
from typing import Annotated
from pydantic import StringConstraints
from api_schemas.base_schema import BaseSchema
from api_schemas.user_schemas import UserInGroupRead
from helpers.constants import MAX_GROUP_NAME
from helpers.types import GROUP_TYPE, GROUP_USER_TYPE


class GroupCreate(BaseSchema):
name: Annotated[str, StringConstraints(max_length=MAX_GROUP_NAME)]
group_type: GROUP_TYPE


class GroupUserRead(BaseSchema):
user: UserInGroupRead
group_user_type: str


class GroupRead(BaseSchema):
id: int
name: str
group_type: str
group_users: list[GroupUserRead]


class GroupAddUser(BaseSchema):
group_id: int
user_id: int
group_user_type: GROUP_USER_TYPE
6 changes: 6 additions & 0 deletions api_schemas/user_schemas.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,12 @@ class UserRead(fastapi_users_schemas.BaseUser[int], BaseSchema):
want_notifications: bool


class UserInGroupRead(fastapi_users_schemas.BaseUser[int], BaseSchema):
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also not sure about fastapi_users_schemas.BaseUser[int] here, is this something we always want?

first_name: str
last_name: str
program: str | None


# fastapi-users will take all fields on this model and feed into the user constructor User_DB(...) when /auth/register route is called
class UserCreate(fastapi_users_schemas.BaseUserCreate, BaseSchema):
first_name: Annotated[str, StringConstraints(max_length=MAX_FIRST_NAME_LEN)]
Expand Down
1 change: 1 addition & 0 deletions database.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

# A route accesses DB by "Depends()"ing on this:
def get_db():
print("works")
with session_factory() as session:
yield session

Expand Down
29 changes: 29 additions & 0 deletions db_models/group_model.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
from sqlalchemy import Enum, String
from typing import TYPE_CHECKING, Optional

from helpers.constants import MAX_GROUP_NAME, MAX_GROUP_TYPE_NAME
from .base_model import BaseModel_DB
from sqlalchemy.orm import relationship, Mapped, mapped_column
from sqlalchemy.ext.associationproxy import association_proxy, AssociationProxy

if TYPE_CHECKING:
from .user_model import User_DB
from .group_user_model import GroupUser_DB


class Group_DB(BaseModel_DB):
__tablename__ = "group_table"

id: Mapped[int] = mapped_column(primary_key=True, init=False)

name: Mapped[str] = mapped_column(String(MAX_GROUP_NAME))

group_type: Mapped[Optional[str]] = mapped_column(String(MAX_GROUP_TYPE_NAME), default=None)

group_users: Mapped[list["GroupUser_DB"]] = relationship(
back_populates="group", cascade="all, delete-orphan", init=False
)

users: AssociationProxy[list["User_DB"]] = association_proxy(
target_collection="group_users", attr="user", init=False
)
24 changes: 24 additions & 0 deletions db_models/group_user_model.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
from typing import TYPE_CHECKING
from sqlalchemy import ForeignKey, String

from db_models.group_model import Group_DB
from helpers.constants import MAX_GROUP_USER_TYPE_NAME
from helpers.types import GROUP_USER_TYPE
from .base_model import BaseModel_DB
from sqlalchemy.orm import relationship, Mapped, mapped_column


if TYPE_CHECKING:
from user_model import User_DB


class GroupUser_DB(BaseModel_DB):
__tablename__ = "group_user_table"

user: Mapped["User_DB"] = relationship(back_populates="group_users")
user_id: Mapped[int] = mapped_column(ForeignKey("user_table.id"), primary_key=True)

group: Mapped["Group_DB"] = relationship(back_populates="group_users")
group_id: Mapped[int] = mapped_column(ForeignKey("group_table.id"), primary_key=True)

group_user_type: Mapped[GROUP_USER_TYPE] = mapped_column(default="Default")
12 changes: 11 additions & 1 deletion db_models/user_model.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
from typing import TYPE_CHECKING, Callable, Optional
from fastapi_users_pelicanq.db import SQLAlchemyBaseUserTable
from sqlalchemy import String
from sqlalchemy import ForeignKey, String
from sqlalchemy.orm import Mapped, relationship, mapped_column
from db_models.group_model import Group_DB
from db_models.group_user_model import GroupUser_DB
from helpers.constants import MAX_FIRST_NAME_LEN, MAX_LAST_NAME_LEN, MAX_TELEPHONE_LEN
from helpers.types import MEMBER_TYPE
from .base_model import BaseModel_DB
Expand Down Expand Up @@ -78,5 +80,13 @@ class User_DB(BaseModel_DB, SQLAlchemyBaseUserTable[int]):

want_notifications: Mapped[bool] = mapped_column(default=True)

group_users: Mapped[list["GroupUser_DB"]] = relationship(
back_populates="user", cascade="all, delete-orphan", init=False
)

groups: AssociationProxy[list["Group_DB"]] = association_proxy(
target_collection="group_users", attr="group", init=False
)

# notifications: Mapped[list["Notification_DB"]]
# fredmansky: Mapped["Fredmansky_DB"] should not be implemented like this I think //Benjamin
9 changes: 7 additions & 2 deletions helpers/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,17 @@
MAX_BOOK_CONTENT = 300
MAX_BOOK_AUTHOR = 100

# Song
## Song
MAX_SONG_TITLE = 200
MAX_SONG_AUTHOR = 200
MAX_SONG_CATEGORY = 100
MAX_SONG_CONTENT = 3000
MAX_SONG_MELODY = 200

# Imgs
## Imgs
MAX_IMG_NAME = 200

## Groups
MAX_GROUP_NAME = 200
MAX_GROUP_TYPE_NAME = 100
MAX_GROUP_USER_TYPE_NAME = 50
6 changes: 5 additions & 1 deletion helpers/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,12 @@ def force_utc(date: datetime):
# Adding to them is always safe though.

PERMISSION_TYPE = Literal["view", "manage"]
PERMISSION_TARGET = Literal["Event", "User", "Post", "Permission", "News", "Song", "Ads", "Gallery","Car"]
PERMISSION_TARGET = Literal["Event", "User", "Post", "Permission", "News", "Song", "Ads", "Gallery", "Car", "Groups"]

# This is a little ridiculous now, but if we have many actions, this is a neat system.
# This one is to make one action eg. "be_a_god" mean several actions eg. "view", "manage", "know_everything",
# PermissionCompoundActions: dict[PermissionAction, list[PermissionAction]] = {"manage": ["view"]}


GROUP_TYPE = Literal["Mentor", "Mission", "Default"]
GROUP_USER_TYPE = Literal["Mentor", "Mentee", "Default"]
4 changes: 2 additions & 2 deletions routes/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
from .album_router import album_router
from .ad_router import ad_router
from .car_renting_router import car_router
from .group_router import group_router

# here comes the big momma router
main_router = APIRouter()
Expand Down Expand Up @@ -42,5 +43,4 @@

main_router.include_router(car_router, prefix="/car", tags=["cars"])

main_router.include_router(
song_category_router, prefix="/songs-category", tags=["songs category"])
main_router.include_router(group_router, prefix="/groups", tags=["groups"])
32 changes: 32 additions & 0 deletions routes/group_router.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
from fastapi import APIRouter
from api_schemas.group_schema import GroupAddUser, GroupCreate, GroupRead
from database import DB_dependency
from helpers.types import GROUP_TYPE, GROUP_USER_TYPE
from user.permission import Permission
from services.group_service import add_to_group, get_group, post_group, get_all_groups
from typing import TYPE_CHECKING, Annotated, Literal

if TYPE_CHECKING:
from db_models.user_model import User_DB

group_router = APIRouter()


@group_router.post("/", dependencies=[Permission.require("manage", "Groups")])
def upload_mentor_group(db: DB_dependency, data: GroupCreate):
return post_group(db, data)


@group_router.get("/", dependencies=[Permission.require("view", "Groups")], response_model=list[GroupRead])
def get_groups(db: DB_dependency, group_type: GROUP_TYPE | None = None):
return get_all_groups(db, group_type)


@group_router.get("/{id}", dependencies=[Permission.require("view", "Groups")], response_model=GroupRead)
def get_single_group(db: DB_dependency, id: int):
return get_group(db, id)


@group_router.post("{id}", dependencies=[Permission.require("manage", "Groups")])
def add_user_to_mentor_group(db: DB_dependency, data: GroupAddUser):
return add_to_group(db, data)
5 changes: 5 additions & 0 deletions seed.py
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,9 @@ def seed_permissions(db: Session, posts: list[Post_DB]):
perm7 = Permission_DB(action="manage", target="Gallery")
perm8 = Permission_DB(action="manage", target="Ads")
perm9 = Permission_DB(action="manage", target="Car")
perm10 = Permission_DB(action="manage", target="Groups")
perm11 = Permission_DB(action="view", target="Groups")

posts[0].permissions.append(perm1)
posts[0].permissions.append(perm2)
posts[1].permissions.append(perm3)
Expand All @@ -143,6 +146,8 @@ def seed_permissions(db: Session, posts: list[Post_DB]):
posts[0].permissions.append(perm7)
posts[0].permissions.append(perm8)
posts[0].permissions.append(perm9)
posts[0].permissions.append(perm9)
posts[0].permissions.append(perm10)
db.commit()


Expand Down
65 changes: 65 additions & 0 deletions services/group_service.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
from fastapi import HTTPException
from sqlalchemy.orm import Session
from api_schemas.group_schema import GroupAddUser, GroupCreate
from db_models.group_model import Group_DB
from db_models.group_user_model import GroupUser_DB
from db_models.user_model import User_DB
from sqlalchemy.exc import DataError

from helpers.types import GROUP_USER_TYPE


def post_group(db: Session, data: GroupCreate):
group = Group_DB(name=data.name, group_type=data.group_type)

db.add(group)

try:
db.commit()
except DataError:
db.rollback()
raise HTTPException(400, "Group type not allowed")

return {"message": "group created successfully"}


def get_all_groups(db: Session, group_type: str | None):
if not group_type:
groups = db.query(Group_DB).all()
else:
groups = db.query(Group_DB).filter(Group_DB.group_type == group_type).all()

return groups


def get_group(db: Session, id: int):
group = db.query(Group_DB).filter(Group_DB.id == id).one_or_none()
if group == None:
raise HTTPException(404, detail="Group not found")

return group


def add_to_group(db: Session, data: GroupAddUser):
group1 = db.query(Group_DB).filter(Group_DB.id == data.group_id).one_or_none()

if group1 == None:
raise HTTPException(404, detail="Group not found")

user1 = db.query(User_DB).filter(User_DB.id == data.user_id).one_or_none()

if user1 == None:
raise HTTPException(404, detail="User not found")

group_user = GroupUser_DB(
user=user1, user_id=user1.id, group=group1, group_id=group1.id, group_user_type=data.group_user_type
)

db.add(group_user)
try:
db.commit()
except DataError:
db.rollback()
raise HTTPException(404, detail="Invalid group_user type")

return {"message": f"{user1.first_name} {user1.last_name} added successfully to group {group1.name}"}