Refactor backend to SQLModel; reset schema; add Company OS endpoints
This commit is contained in:
BIN
backend/app/api/__pycache__/activities.cpython-312.pyc
Normal file
BIN
backend/app/api/__pycache__/activities.cpython-312.pyc
Normal file
Binary file not shown.
BIN
backend/app/api/__pycache__/hr.cpython-312.pyc
Normal file
BIN
backend/app/api/__pycache__/hr.cpython-312.pyc
Normal file
Binary file not shown.
BIN
backend/app/api/__pycache__/org.cpython-312.pyc
Normal file
BIN
backend/app/api/__pycache__/org.cpython-312.pyc
Normal file
Binary file not shown.
BIN
backend/app/api/__pycache__/projects.cpython-312.pyc
Normal file
BIN
backend/app/api/__pycache__/projects.cpython-312.pyc
Normal file
Binary file not shown.
Binary file not shown.
BIN
backend/app/api/__pycache__/utils.cpython-312.pyc
Normal file
BIN
backend/app/api/__pycache__/utils.cpython-312.pyc
Normal file
Binary file not shown.
BIN
backend/app/api/__pycache__/work.cpython-312.pyc
Normal file
BIN
backend/app/api/__pycache__/work.cpython-312.pyc
Normal file
Binary file not shown.
30
backend/app/api/activities.py
Normal file
30
backend/app/api/activities.py
Normal file
@@ -0,0 +1,30 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
|
||||
from fastapi import APIRouter, Depends
|
||||
from sqlmodel import Session, select
|
||||
|
||||
from app.db.session import get_session
|
||||
from app.models.activity import Activity
|
||||
|
||||
router = APIRouter(prefix="/activities", tags=["activities"])
|
||||
|
||||
|
||||
@router.get("")
|
||||
def list_activities(limit: int = 50, session: Session = Depends(get_session)):
|
||||
items = session.exec(select(Activity).order_by(Activity.id.desc()).limit(max(1, min(limit, 200)))).all()
|
||||
out = []
|
||||
for a in items:
|
||||
out.append(
|
||||
{
|
||||
"id": a.id,
|
||||
"actor_employee_id": a.actor_employee_id,
|
||||
"entity_type": a.entity_type,
|
||||
"entity_id": a.entity_id,
|
||||
"verb": a.verb,
|
||||
"payload": json.loads(a.payload_json) if a.payload_json else None,
|
||||
"created_at": a.created_at,
|
||||
}
|
||||
)
|
||||
return out
|
||||
61
backend/app/api/hr.py
Normal file
61
backend/app/api/hr.py
Normal file
@@ -0,0 +1,61 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException
|
||||
from sqlmodel import Session, select
|
||||
|
||||
from app.api.utils import log_activity
|
||||
from app.db.session import get_session
|
||||
from app.models.hr import EmploymentAction, HeadcountRequest
|
||||
from app.schemas.hr import EmploymentActionCreate, HeadcountRequestCreate, HeadcountRequestUpdate
|
||||
|
||||
router = APIRouter(prefix="/hr", tags=["hr"])
|
||||
|
||||
|
||||
@router.get("/headcount", response_model=list[HeadcountRequest])
|
||||
def list_headcount_requests(session: Session = Depends(get_session)):
|
||||
return session.exec(select(HeadcountRequest).order_by(HeadcountRequest.id.desc())).all()
|
||||
|
||||
|
||||
@router.post("/headcount", response_model=HeadcountRequest)
|
||||
def create_headcount_request(payload: HeadcountRequestCreate, session: Session = Depends(get_session)):
|
||||
req = HeadcountRequest(**payload.model_dump())
|
||||
session.add(req)
|
||||
session.commit()
|
||||
session.refresh(req)
|
||||
log_activity(session, actor_employee_id=req.requested_by_manager_id, entity_type="headcount_request", entity_id=req.id, verb="submitted")
|
||||
session.commit()
|
||||
return req
|
||||
|
||||
|
||||
@router.patch("/headcount/{request_id}", response_model=HeadcountRequest)
|
||||
def update_headcount_request(request_id: int, payload: HeadcountRequestUpdate, session: Session = Depends(get_session)):
|
||||
req = session.get(HeadcountRequest, request_id)
|
||||
if not req:
|
||||
raise HTTPException(status_code=404, detail="Request not found")
|
||||
|
||||
data = payload.model_dump(exclude_unset=True)
|
||||
for k, v in data.items():
|
||||
setattr(req, k, v)
|
||||
|
||||
session.add(req)
|
||||
session.commit()
|
||||
session.refresh(req)
|
||||
log_activity(session, actor_employee_id=req.requested_by_manager_id, entity_type="headcount_request", entity_id=req.id, verb="updated", payload=data)
|
||||
session.commit()
|
||||
return req
|
||||
|
||||
|
||||
@router.get("/actions", response_model=list[EmploymentAction])
|
||||
def list_employment_actions(session: Session = Depends(get_session)):
|
||||
return session.exec(select(EmploymentAction).order_by(EmploymentAction.id.desc())).all()
|
||||
|
||||
|
||||
@router.post("/actions", response_model=EmploymentAction)
|
||||
def create_employment_action(payload: EmploymentActionCreate, session: Session = Depends(get_session)):
|
||||
action = EmploymentAction(**payload.model_dump())
|
||||
session.add(action)
|
||||
session.commit()
|
||||
session.refresh(action)
|
||||
log_activity(session, actor_employee_id=action.issued_by_employee_id, entity_type="employment_action", entity_id=action.id, verb=action.action_type, payload={"employee_id": action.employee_id})
|
||||
session.commit()
|
||||
return action
|
||||
79
backend/app/api/org.py
Normal file
79
backend/app/api/org.py
Normal file
@@ -0,0 +1,79 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException
|
||||
from sqlmodel import Session, select
|
||||
|
||||
from app.api.utils import log_activity
|
||||
from app.db.session import get_session
|
||||
from app.models.org import Department, Employee
|
||||
from app.schemas.org import DepartmentCreate, DepartmentUpdate, EmployeeCreate, EmployeeUpdate
|
||||
|
||||
router = APIRouter(tags=["org"])
|
||||
|
||||
|
||||
@router.get("/departments", response_model=list[Department])
|
||||
def list_departments(session: Session = Depends(get_session)):
|
||||
return session.exec(select(Department).order_by(Department.name.asc())).all()
|
||||
|
||||
|
||||
@router.post("/departments", response_model=Department)
|
||||
def create_department(payload: DepartmentCreate, session: Session = Depends(get_session)):
|
||||
dept = Department(name=payload.name, head_employee_id=payload.head_employee_id)
|
||||
session.add(dept)
|
||||
session.commit()
|
||||
session.refresh(dept)
|
||||
log_activity(session, actor_employee_id=None, entity_type="department", entity_id=dept.id, verb="created", payload={"name": dept.name})
|
||||
session.commit()
|
||||
return dept
|
||||
|
||||
|
||||
@router.patch("/departments/{department_id}", response_model=Department)
|
||||
def update_department(department_id: int, payload: DepartmentUpdate, session: Session = Depends(get_session)):
|
||||
dept = session.get(Department, department_id)
|
||||
if not dept:
|
||||
raise HTTPException(status_code=404, detail="Department not found")
|
||||
|
||||
data = payload.model_dump(exclude_unset=True)
|
||||
for k, v in data.items():
|
||||
setattr(dept, k, v)
|
||||
|
||||
session.add(dept)
|
||||
session.commit()
|
||||
session.refresh(dept)
|
||||
log_activity(session, actor_employee_id=None, entity_type="department", entity_id=dept.id, verb="updated", payload=data)
|
||||
session.commit()
|
||||
return dept
|
||||
|
||||
|
||||
@router.get("/employees", response_model=list[Employee])
|
||||
def list_employees(session: Session = Depends(get_session)):
|
||||
return session.exec(select(Employee).order_by(Employee.id.asc())).all()
|
||||
|
||||
|
||||
@router.post("/employees", response_model=Employee)
|
||||
def create_employee(payload: EmployeeCreate, session: Session = Depends(get_session)):
|
||||
emp = Employee(**payload.model_dump())
|
||||
session.add(emp)
|
||||
session.commit()
|
||||
session.refresh(emp)
|
||||
log_activity(session, actor_employee_id=None, entity_type="employee", entity_id=emp.id, verb="created", payload={"name": emp.name, "type": emp.employee_type})
|
||||
session.commit()
|
||||
return emp
|
||||
|
||||
|
||||
@router.patch("/employees/{employee_id}", response_model=Employee)
|
||||
def update_employee(employee_id: int, payload: EmployeeUpdate, session: Session = Depends(get_session)):
|
||||
emp = session.get(Employee, employee_id)
|
||||
if not emp:
|
||||
raise HTTPException(status_code=404, detail="Employee not found")
|
||||
|
||||
data = payload.model_dump(exclude_unset=True)
|
||||
for k, v in data.items():
|
||||
setattr(emp, k, v)
|
||||
|
||||
session.add(emp)
|
||||
session.commit()
|
||||
session.refresh(emp)
|
||||
log_activity(session, actor_employee_id=None, entity_type="employee", entity_id=emp.id, verb="updated", payload=data)
|
||||
session.commit()
|
||||
return emp
|
||||
45
backend/app/api/projects.py
Normal file
45
backend/app/api/projects.py
Normal file
@@ -0,0 +1,45 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException
|
||||
from sqlmodel import Session, select
|
||||
|
||||
from app.api.utils import log_activity
|
||||
from app.db.session import get_session
|
||||
from app.models.projects import Project
|
||||
from app.schemas.projects import ProjectCreate, ProjectUpdate
|
||||
|
||||
router = APIRouter(prefix="/projects", tags=["projects"])
|
||||
|
||||
|
||||
@router.get("", response_model=list[Project])
|
||||
def list_projects(session: Session = Depends(get_session)):
|
||||
return session.exec(select(Project).order_by(Project.name.asc())).all()
|
||||
|
||||
|
||||
@router.post("", response_model=Project)
|
||||
def create_project(payload: ProjectCreate, session: Session = Depends(get_session)):
|
||||
proj = Project(**payload.model_dump())
|
||||
session.add(proj)
|
||||
session.commit()
|
||||
session.refresh(proj)
|
||||
log_activity(session, actor_employee_id=None, entity_type="project", entity_id=proj.id, verb="created", payload={"name": proj.name})
|
||||
session.commit()
|
||||
return proj
|
||||
|
||||
|
||||
@router.patch("/{project_id}", response_model=Project)
|
||||
def update_project(project_id: int, payload: ProjectUpdate, session: Session = Depends(get_session)):
|
||||
proj = session.get(Project, project_id)
|
||||
if not proj:
|
||||
raise HTTPException(status_code=404, detail="Project not found")
|
||||
|
||||
data = payload.model_dump(exclude_unset=True)
|
||||
for k, v in data.items():
|
||||
setattr(proj, k, v)
|
||||
|
||||
session.add(proj)
|
||||
session.commit()
|
||||
session.refresh(proj)
|
||||
log_activity(session, actor_employee_id=None, entity_type="project", entity_id=proj.id, verb="updated", payload=data)
|
||||
session.commit()
|
||||
return proj
|
||||
@@ -1,64 +0,0 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from app.db.session import SessionLocal
|
||||
from app.models.task import Task
|
||||
from app.schemas.task import TaskCreate, TaskOut, TaskUpdate
|
||||
|
||||
router = APIRouter(prefix="/tasks", tags=["tasks"])
|
||||
|
||||
|
||||
def get_db():
|
||||
db = SessionLocal()
|
||||
try:
|
||||
yield db
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
|
||||
@router.get("", response_model=list[TaskOut])
|
||||
def list_tasks(db: Session = Depends(get_db)):
|
||||
return db.query(Task).order_by(Task.id.desc()).all()
|
||||
|
||||
|
||||
@router.post("", response_model=TaskOut)
|
||||
def create_task(payload: TaskCreate, db: Session = Depends(get_db)):
|
||||
task = Task(
|
||||
title=payload.title,
|
||||
description=payload.description,
|
||||
status=payload.status,
|
||||
assignee=payload.assignee,
|
||||
)
|
||||
db.add(task)
|
||||
db.commit()
|
||||
db.refresh(task)
|
||||
return task
|
||||
|
||||
|
||||
@router.patch("/{task_id}", response_model=TaskOut)
|
||||
def update_task(task_id: int, payload: TaskUpdate, db: Session = Depends(get_db)):
|
||||
task = db.get(Task, task_id)
|
||||
if not task:
|
||||
raise HTTPException(status_code=404, detail="Task not found")
|
||||
|
||||
data = payload.model_dump(exclude_unset=True)
|
||||
for k, v in data.items():
|
||||
setattr(task, k, v)
|
||||
|
||||
db.add(task)
|
||||
db.commit()
|
||||
db.refresh(task)
|
||||
return task
|
||||
|
||||
|
||||
@router.delete("/{task_id}")
|
||||
def delete_task(task_id: int, db: Session = Depends(get_db)):
|
||||
task = db.get(Task, task_id)
|
||||
if not task:
|
||||
raise HTTPException(status_code=404, detail="Task not found")
|
||||
|
||||
db.delete(task)
|
||||
db.commit()
|
||||
return {"ok": True}
|
||||
28
backend/app/api/utils.py
Normal file
28
backend/app/api/utils.py
Normal file
@@ -0,0 +1,28 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
from typing import Any
|
||||
|
||||
from sqlmodel import Session
|
||||
|
||||
from app.models.activity import Activity
|
||||
|
||||
|
||||
def log_activity(
|
||||
session: Session,
|
||||
*,
|
||||
actor_employee_id: int | None,
|
||||
entity_type: str,
|
||||
entity_id: int | None,
|
||||
verb: str,
|
||||
payload: dict[str, Any] | None = None,
|
||||
) -> None:
|
||||
session.add(
|
||||
Activity(
|
||||
actor_employee_id=actor_employee_id,
|
||||
entity_type=entity_type,
|
||||
entity_id=entity_id,
|
||||
verb=verb,
|
||||
payload_json=json.dumps(payload) if payload is not None else None,
|
||||
)
|
||||
)
|
||||
80
backend/app/api/work.py
Normal file
80
backend/app/api/work.py
Normal file
@@ -0,0 +1,80 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import datetime
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException
|
||||
from sqlmodel import Session, select
|
||||
|
||||
from app.api.utils import log_activity
|
||||
from app.db.session import get_session
|
||||
from app.models.work import Task, TaskComment
|
||||
from app.schemas.work import TaskCommentCreate, TaskCreate, TaskUpdate
|
||||
|
||||
router = APIRouter(tags=["work"])
|
||||
|
||||
|
||||
@router.get("/tasks", response_model=list[Task])
|
||||
def list_tasks(project_id: int | None = None, session: Session = Depends(get_session)):
|
||||
stmt = select(Task).order_by(Task.id.asc())
|
||||
if project_id is not None:
|
||||
stmt = stmt.where(Task.project_id == project_id)
|
||||
return session.exec(stmt).all()
|
||||
|
||||
|
||||
@router.post("/tasks", response_model=Task)
|
||||
def create_task(payload: TaskCreate, session: Session = Depends(get_session)):
|
||||
task = Task(**payload.model_dump())
|
||||
task.updated_at = datetime.utcnow()
|
||||
session.add(task)
|
||||
session.commit()
|
||||
session.refresh(task)
|
||||
log_activity(session, actor_employee_id=task.created_by_employee_id, entity_type="task", entity_id=task.id, verb="created", payload={"project_id": task.project_id, "title": task.title})
|
||||
session.commit()
|
||||
return task
|
||||
|
||||
|
||||
@router.patch("/tasks/{task_id}", response_model=Task)
|
||||
def update_task(task_id: int, payload: TaskUpdate, session: Session = Depends(get_session)):
|
||||
task = session.get(Task, task_id)
|
||||
if not task:
|
||||
raise HTTPException(status_code=404, detail="Task not found")
|
||||
|
||||
data = payload.model_dump(exclude_unset=True)
|
||||
for k, v in data.items():
|
||||
setattr(task, k, v)
|
||||
task.updated_at = datetime.utcnow()
|
||||
|
||||
session.add(task)
|
||||
session.commit()
|
||||
session.refresh(task)
|
||||
log_activity(session, actor_employee_id=None, entity_type="task", entity_id=task.id, verb="updated", payload=data)
|
||||
session.commit()
|
||||
return task
|
||||
|
||||
|
||||
@router.delete("/tasks/{task_id}")
|
||||
def delete_task(task_id: int, session: Session = Depends(get_session)):
|
||||
task = session.get(Task, task_id)
|
||||
if not task:
|
||||
raise HTTPException(status_code=404, detail="Task not found")
|
||||
session.delete(task)
|
||||
session.commit()
|
||||
log_activity(session, actor_employee_id=None, entity_type="task", entity_id=task_id, verb="deleted")
|
||||
session.commit()
|
||||
return {"ok": True}
|
||||
|
||||
|
||||
@router.get("/task-comments", response_model=list[TaskComment])
|
||||
def list_task_comments(task_id: int, session: Session = Depends(get_session)):
|
||||
return session.exec(select(TaskComment).where(TaskComment.task_id == task_id).order_by(TaskComment.id.asc())).all()
|
||||
|
||||
|
||||
@router.post("/task-comments", response_model=TaskComment)
|
||||
def create_task_comment(payload: TaskCommentCreate, session: Session = Depends(get_session)):
|
||||
c = TaskComment(**payload.model_dump())
|
||||
session.add(c)
|
||||
session.commit()
|
||||
session.refresh(c)
|
||||
log_activity(session, actor_employee_id=c.author_employee_id, entity_type="task", entity_id=c.task_id, verb="commented")
|
||||
session.commit()
|
||||
return c
|
||||
Reference in New Issue
Block a user