Skip to content
Merged
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
13 changes: 7 additions & 6 deletions api/api_models/projects.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,12 +51,13 @@ class ProjectResponse(ProjectBase):


class UpdateProject(BaseModel):
name: Optional[str]
description: Optional[str]
project_type: Optional[ProjectType]
project_priority: Optional[ProjectPriority]
project_tools: Optional[List[int]] = Field(None)
manager_id: Optional[int]
name: Optional[str] = None
description: Optional[str] = None
project_type: Optional[ProjectType] = None
project_priority: Optional[ProjectPriority] = None
project_tools: Optional[List[int]] = None
stacks: Optional[List[int]] = None
manager_id: Optional[int] = None


class ProjectMember(BaseModel):
Expand Down
4 changes: 2 additions & 2 deletions api/api_models/skills.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ class SkillBase(BaseModel):
model_config = ConfigDict(from_attributes=True)

@field_validator('name')
def convert_to_lower_case(cls, v):
return v.lower()
def normalize_name(cls, v):
return v.strip().title()


class SkillCreate(SkillBase):
Expand Down
42 changes: 38 additions & 4 deletions api/routes/announcements.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from typing import Annotated, Any

from fastapi import APIRouter, Depends, status
from fastapi import APIRouter, Depends, File, HTTPException, UploadFile, status
from fastapi_pagination.ext.sqlalchemy import paginate
from fastapi_pagination.links import Page
from sqlalchemy.orm import Session
Expand All @@ -9,18 +9,52 @@
from db.database import get_db
from db.repository.announcements import AnnouncementRepository
from services.announcement_service import AnnouncementService
from utils.permissions import is_admin
from utils.cloudinary import upload_file
from utils.permissions import is_admin, user_accepted
from utils.utils import is_image_file

announcement_route = APIRouter(tags=["Announcements"], prefix="/announcements")

DBSession = Annotated[Session, Depends(get_db)]
AdminUser = Annotated[Any, Depends(is_admin)]
AcceptedUser = Annotated[Any, Depends(user_accepted)]


def _service(db: Session) -> AnnouncementService:
return AnnouncementService(AnnouncementRepository(db))


_ALLOWED_IMAGE_CONTENT_TYPES = {
"image/jpeg", "image/png", "image/gif", "image/webp", "image/svg+xml", "image/bmp",
}


@announcement_route.post("/image", status_code=status.HTTP_200_OK)
async def upload_announcement_image(
file: UploadFile = File(...),
_admin: Any = Depends(is_admin),
):
if not is_image_file(file.filename):
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="Invalid file format. Please upload an image.",
)
if file.content_type not in _ALLOWED_IMAGE_CONTENT_TYPES:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="Invalid file type. Please upload an image.",
)
resource_type = "announcement"
upload_folder = "announcements"
url = await upload_file(file, resource_type, upload_folder)
Comment thread
TMCreme marked this conversation as resolved.
if not url:
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="Failed to upload image",
)
return {"url": url}


@announcement_route.post("/", status_code=status.HTTP_201_CREATED, response_model=AnnouncementResponse)
def create_announcement(
announcement: AnnouncementCreate,
Expand All @@ -31,13 +65,13 @@ def create_announcement(


@announcement_route.get("/", status_code=status.HTTP_200_OK, response_model=Page[AnnouncementResponse])
def get_announcements(db: DBSession):
def get_announcements(current_user: AcceptedUser, db: DBSession):
return paginate(db, _service(db).get_all_query())


@announcement_route.get("/{announcement_id}", status_code=status.HTTP_200_OK,
response_model=AnnouncementResponse)
def get_announcement_by_id(announcement_id: int, db: DBSession):
def get_announcement_by_id(announcement_id: int, current_user: AcceptedUser, db: DBSession):
return _service(db).get_by_id(announcement_id)


Expand Down
13 changes: 6 additions & 7 deletions api/routes/coding_challenges.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,7 @@
from db.database import get_db
from db.repository.coding_challenges import CodingChallengeRepository
from services.coding_challenge_service import CodingChallengeService
from utils.permissions import is_admin
from utils.oauth2 import get_current_user
from utils.permissions import is_admin, user_accepted

coding_challenge_route = APIRouter(tags=["Coding Challenges"], prefix="/coding-challenges")

Expand All @@ -32,22 +31,22 @@ def create_challenge(


@coding_challenge_route.get("/latest", status_code=status.HTTP_200_OK, response_model=CodingChallengeResponse | None)
def get_latest_challenge(db: Session = Depends(get_db), current_user=Depends(get_current_user)):
"""Get the most recent coding challenge - All users"""
def get_latest_challenge(db: Session = Depends(get_db), current_user=Depends(user_accepted)):
"""Get the most recent coding challenge - Accepted users"""
return _service(db).get_latest()


@coding_challenge_route.get("/", status_code=status.HTTP_200_OK, response_model=Page[CodingChallengeResponse])
def get_all_challenges(db: Session = Depends(get_db), current_user=Depends(get_current_user)):
"""Get all coding challenges - All users"""
def get_all_challenges(db: Session = Depends(get_db), current_user=Depends(user_accepted)):
"""Get all coding challenges - Accepted users"""
return paginate(db, _service(db).get_all_query())


@coding_challenge_route.get("/{challenge_id}", status_code=status.HTTP_200_OK, response_model=CodingChallengeResponse)
def get_challenge_by_id(
challenge_id: int,
db: Session = Depends(get_db),
current_user=Depends(get_current_user)
current_user=Depends(user_accepted)
):
"""Get a specific challenge by ID"""
return _service(db).get_by_id(challenge_id)
Expand Down
5 changes: 3 additions & 2 deletions api/routes/email_templates.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,13 @@ def create_email_template(template: EmailTemplateCreate, db: Session = Depends(g


@email_templates_route.get("/{template_id}", response_model=EmailTemplateResponse)
def read_email_template(template_id: int, db: Session = Depends(get_db)):
def read_email_template(template_id: int, db: Session = Depends(get_db),
current_user=Depends(is_admin)):
return _service(db).get_by_id(template_id)


@email_templates_route.get("/", response_model=Page[EmailTemplateResponse])
def read_all_email_templates(db: Session = Depends(get_db)):
def read_all_email_templates(db: Session = Depends(get_db), current_user=Depends(is_admin)):
return paginate(db, _service(db).get_all_query())


Expand Down
13 changes: 7 additions & 6 deletions api/routes/feeds.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
from db.database import get_db
from db.repository.feeds import FeedRepository
from services.feed_service import FeedService
from utils.oauth2 import get_current_user
from utils.permissions import user_accepted

feed_route = APIRouter(tags=["Feed"], prefix="/feed")

Expand All @@ -18,27 +18,28 @@ def _service(db: Session) -> FeedService:

@feed_route.post("/", status_code=status.HTTP_201_CREATED, response_model=Feeds)
async def create_feed(content: str = Form(...), feed_pic_url: UploadFile = File(None),
current_user=Depends(get_current_user), db: Session = Depends(get_db)):
current_user=Depends(user_accepted), db: Session = Depends(get_db)):
return await _service(db).create_feed(current_user.id, current_user.username, content, feed_pic_url)


@feed_route.put("/{feed_id}", status_code=status.HTTP_200_OK, response_model=Feeds)
def update_feed_by_id(feed_id: int, updated_feed: FeedUpdate,
db: Session = Depends(get_db), current_user=Depends(get_current_user)):
db: Session = Depends(get_db), current_user=Depends(user_accepted)):
return _service(db).update_feed(feed_id, current_user.id, updated_feed.model_dump())


@feed_route.delete("/{feed_id}", status_code=status.HTTP_204_NO_CONTENT)
def delete_feed_by_id(feed_id: int, db: Session = Depends(get_db),
current_user=Depends(get_current_user)):
current_user=Depends(user_accepted)):
_service(db).delete_feed(feed_id, current_user.id)


@feed_route.get("/{feed_id}", status_code=status.HTTP_200_OK, response_model=Feeds)
def get_feed_by_id(feed_id: int, db: Session = Depends(get_db)):
def get_feed_by_id(feed_id: int, db: Session = Depends(get_db),
current_user=Depends(user_accepted)):
return _service(db).get_feed(feed_id)


@feed_route.get("/", response_model=Page[Feeds])
def get_all_feeds(db: Session = Depends(get_db)):
def get_all_feeds(db: Session = Depends(get_db), current_user=Depends(user_accepted)):
return paginate(db, _service(db).get_all_query())
26 changes: 18 additions & 8 deletions api/routes/profile_page.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,7 @@ class BatchStatusUpdateResponse(BaseModel):
from db.repository.technical_tasks import TechnicalTaskSubmissionRepository
from db.repository.users import UserRepository
from services.user_service import UserService
from utils.oauth2 import get_current_user
from utils.permissions import is_admin
from utils.permissions import is_admin, user_accepted

profile_route = APIRouter(tags=["User"], prefix="/users")
logger = logging.getLogger(__name__)
Expand All @@ -43,21 +42,31 @@ def _service(db: Session) -> UserService:

@profile_route.get("/profile/{user_id}", response_model=ApplicantProfileResponse)
async def get_profile(user_id: int, db: Session = Depends(get_db),
current_user: User = Depends(get_current_user)):
current_user: User = Depends(user_accepted)):
return _service(db).get_profile(user_id)


@profile_route.put("/profile", response_model=ProfileResponse)
async def update_profile(userDetails: ProfileUpdate, current_user: User = Depends(get_current_user),
async def update_profile(userDetails: ProfileUpdate, current_user: User = Depends(user_accepted),
db: Session = Depends(get_db)):
return _service(db).update_profile(current_user.id, userDetails.model_dump(exclude_unset=True))


@profile_route.get("/", response_model=Page[ProfileResponse])
def get_all_profile(skill: str = Query(None), stack: str = Query(None),
active: Optional[bool] = None, p: Optional[str] = None,
db: Session = Depends(get_db), current_user: User = Depends(get_current_user)):
query = _service(db).build_search_query(skill, stack, active, p)
status: Optional[UserStatus] = Query(
None,
description="Filter users by status. Accepted values are defined by the UserStatus enum."
),
db: Session = Depends(get_db), current_user: User = Depends(user_accepted)):
query = _service(db).build_search_query(
skill,
stack,
active,
p,
status.value if status is not None else None
)
return paginate(db, query)


Expand All @@ -69,7 +78,8 @@ def update_profile_status(user_id: int, db: Session = Depends(get_db),


@profile_route.get("/user_info", response_model=dict)
def get_user_info(email: str, db: Session = Depends(get_db)):
def get_user_info(email: str, db: Session = Depends(get_db),
current_user: User = Depends(user_accepted)):
return _service(db).get_user_info(email)


Expand All @@ -82,7 +92,7 @@ async def update_user_status(user_id: int, new_status: UserStatus, db: Session =

@profile_route.patch("/profile/avatar", response_model=ProfileResponse,
status_code=status.HTTP_200_OK)
async def update_avi(current_user: User = Depends(get_current_user),
async def update_avi(current_user: User = Depends(user_accepted),
db: Session = Depends(get_db), file: UploadFile = File(...)):
return await _service(db).update_avatar(current_user, file)

Expand Down
8 changes: 4 additions & 4 deletions api/routes/project.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
from db.repository.users import UserRepository
from services.project_service import ProjectService
from utils.enums import ProjectTeam
from utils.permissions import is_admin, is_project_manager
from utils.permissions import is_admin, is_project_manager, user_accepted

project_router = APIRouter(tags=["Project"], prefix="/projects")

Expand Down Expand Up @@ -45,12 +45,12 @@ def delete(project_id: int, db: Session = Depends(get_db), user: User = Depends(


@project_router.get("/{project_id}", status_code=status.HTTP_200_OK, response_model=ProjectResponse)
def get(project_id: int, db: Session = Depends(get_db)):
def get(project_id: int, db: Session = Depends(get_db), current_user=Depends(user_accepted)):
return _service(db).get_project(project_id)


@project_router.get("/", status_code=status.HTTP_200_OK, response_model=Page[ProjectResponse])
def get_all(db: Session = Depends(get_db)):
def get_all(db: Session = Depends(get_db), current_user=Depends(user_accepted)):
service = _service(db)
page = paginate(db, service.get_all_query())
# Enrich members with team data
Expand All @@ -77,5 +77,5 @@ def remove_user_from_project(
@project_router.get("/{project_id}/members", status_code=status.HTTP_200_OK,
response_model=List[MembersResponse])
def get_project_members(project_id: int, team: Optional[ProjectTeam] = None,
db: Session = Depends(get_db)):
db: Session = Depends(get_db), current_user=Depends(user_accepted)):
return _service(db).get_project_members(project_id, team)
22 changes: 10 additions & 12 deletions api/routes/skills.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,21 @@

from fastapi import APIRouter, Depends, Query, status
from fastapi.params import Body
from fastapi_pagination.ext.sqlalchemy import paginate
from fastapi_pagination.links import Page
from pydantic import BaseModel
from sqlalchemy.orm import Session

from api.api_models.user import Skills
from db.database import get_db
from db.repository.skills import SkillRepository
from services.skill_service import SkillService
from utils.oauth2 import get_current_user
from utils.permissions import is_admin
from utils.permissions import is_admin, user_accepted


class SkillCreate(BaseModel):
name: str
image_url: Optional[str] = None


skill_route = APIRouter(tags=["Skills"], prefix="/skills")


Expand All @@ -27,35 +25,35 @@ def _service(db: Session) -> SkillService:


@skill_route.get("/", response_model=List[Skills], status_code=status.HTTP_200_OK)
def get_skills(user=Depends(get_current_user), db: Session = Depends(get_db)):
def get_skills(user=Depends(user_accepted), db: Session = Depends(get_db)):
return _service(db).get_user_skills(user.id)


@skill_route.post("/", response_model=List[Skills], status_code=status.HTTP_201_CREATED)
def add_skills(skill_ids: list[int] = Body(...), db: Session = Depends(get_db),
current_user=Depends(get_current_user)):
current_user=Depends(user_accepted)):
return _service(db).add_skills(current_user, skill_ids)


@skill_route.delete("/{skill_id}", status_code=status.HTTP_204_NO_CONTENT)
def delete_skill_by_id(skill_id: int, user=Depends(get_current_user),
def delete_skill_by_id(skill_id: int, user=Depends(user_accepted),
db: Session = Depends(get_db)):
_service(db).delete_skill(skill_id)


@skill_route.get("/all", status_code=status.HTTP_200_OK, response_model=Page[Skills])
def get_all(db: Session = Depends(get_db)):
return paginate(db, _service(db).get_all_query())
@skill_route.get("/all", status_code=status.HTTP_200_OK, response_model=List[Skills])
def get_all(db: Session = Depends(get_db), current_user=Depends(user_accepted)):
return _service(db).get_all_flat()
Comment thread
TMCreme marked this conversation as resolved.


@skill_route.post("/data")
def populate_skills(db: Session = Depends(get_db)):
def populate_skills(db: Session = Depends(get_db), _admin=Depends(is_admin)):
return _service(db).populate_skills()


@skill_route.get("/search", response_model=List[dict], status_code=status.HTTP_200_OK)
def search_skills(name: str = Query(..., min_length=1, max_length=50),
db: Session = Depends(get_db)):
db: Session = Depends(get_db), current_user=Depends(user_accepted)):
return _service(db).search_skills(name)


Expand Down
Loading
Loading