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
32 changes: 16 additions & 16 deletions Backend/.github/workflows/ci.yml → .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
name: Project Structure Check
name: Backend Structure Check

on:
pull_request:
push:
branches: [ "main", "dev" ]
branches: ["main", "dev"]

jobs:
structure-check:
Expand All @@ -13,14 +13,14 @@ jobs:
- name: Checkout repository
uses: actions/checkout@v4

- name: Check required directories
- name: Check required backend directories
run: |
required_dirs=(
"app"
"app/models"
"app/routers"
"app/schemas"
"app/core"
"Backend/app"
"Backend/app/models"
"Backend/app/routers"
"Backend/app/schemas"
"Backend/app/core"
".github/workflows"
)

Expand All @@ -31,16 +31,16 @@ jobs:
fi
done

echo "✅ All required directories exist"
echo "✅ All required backend directories exist"

- name: Check required files
- name: Check required backend files
run: |
required_files=(
"app/main.py"
"app/database.py"
"Dockerfile"
"docker-compose.yml"
"requirements.txt"
"Backend/app/main.py"
"Backend/app/database.py"
"Backend/Dockerfile"
"Backend/docker-compose.yml"
"Backend/requirements.txt"
)

for file in "${required_files[@]}"; do
Expand All @@ -50,4 +50,4 @@ jobs:
fi
done

echo "✅ All required files exist"
echo "✅ All required backend files exist"
33 changes: 33 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# ======================
# Python
# ======================
__pycache__/
*.py[cod]
*$py.class

# ======================
# Virtual Environments
# ======================
venv/
.venv/
env/
ml_env/

# ======================
# Environment Variables
# ======================
.env
.env.*
!.env.example

# ======================
# Editor / OS
# ======================
.vscode/
.DS_Store
Thumbs.db

# ======================
# Logs
# ======================
*.log
1 change: 1 addition & 0 deletions Backend/.env
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
DATABASE_URL=postgresql://postgres:postgres@db:5432/maintaflow
Binary file added Backend/app/__pycache__/database.cpython-313.pyc
Binary file not shown.
23 changes: 23 additions & 0 deletions Backend/app/core/auth.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
from fastapi import Depends, HTTPException, status
from app.models.user import User, UserRole
from sqlalchemy.orm import Session
from app.database import get_db


# TEMP AUTH (replace with JWT later)
def get_current_user(db: Session = Depends(get_db)) -> User:
user = db.query(User).first()
if not user:
raise HTTPException(status_code=401, detail="Unauthenticated")
return user


def require_role(*roles: UserRole):
def checker(user: User = Depends(get_current_user)):
if user.role not in roles:
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail="Insufficient permissions"
)
return user
return checker
21 changes: 21 additions & 0 deletions Backend/app/core/permission.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
from fastapi import HTTPException, status
from sqlalchemy.orm import Session
from app.models.team import TeamMember
from app.models.user import User


def user_in_team(db: Session, user: User, team_id: int):
exists = (
db.query(TeamMember)
.filter(
TeamMember.team_id == team_id,
TeamMember.user_id == user.id
)
.first()
)

if not exists:
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail="User not part of this maintenance team"
)
26 changes: 26 additions & 0 deletions Backend/app/database.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker, declarative_base
import os

DATABASE_URL = os.getenv(
"DATABASE_URL",
"postgresql://postgres:postgres@db:5432/maintaflow"
)

engine = create_engine(DATABASE_URL, echo=False)

SessionLocal = sessionmaker(
autocommit=False,
autoflush=False,
bind=engine
)

Base = declarative_base()


def get_db():
db = SessionLocal()
try:
yield db
finally:
db.close()
5 changes: 5 additions & 0 deletions Backend/app/models/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
from .user import User
from .team import MaintenanceTeam, TeamMember
from .equipment import Equipment
from .maintenance_request import MaintenanceRequest
from .maintenance_log import MaintenanceLog
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
36 changes: 36 additions & 0 deletions Backend/app/models/equipment.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
from sqlalchemy import (
Column, Integer, String, Boolean, Date, Enum, ForeignKey, DateTime
)
from sqlalchemy.sql import func
from app.database import Base
import enum


class EquipmentCategory(enum.Enum):
Mechanical = "Mechanical"
Electrical = "Electrical"
IT = "IT"
Vehicle = "Vehicle"
Other = "Other"


class Equipment(Base):
__tablename__ = "equipment"

id = Column(Integer, primary_key=True)
name = Column(String, nullable=False)
serial_number = Column(String, unique=True, nullable=False)

category = Column(Enum(EquipmentCategory), nullable=False)

department_id = Column(Integer, ForeignKey("departments.id"))
assigned_employee_id = Column(Integer, ForeignKey("users.id"))
maintenance_team_id = Column(Integer, ForeignKey("maintenance_teams.id"))
default_technician_id = Column(Integer, ForeignKey("users.id"))

purchase_date = Column(Date)
warranty_expiry_date = Column(Date)
location = Column(String)

is_scrapped = Column(Boolean, default=False)
created_at = Column(DateTime(timezone=True), server_default=func.now())
18 changes: 18 additions & 0 deletions Backend/app/models/maintenance_log.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
from sqlalchemy import Column, Integer, String, ForeignKey, DateTime
from sqlalchemy.sql import func
from app.database import Base


class MaintenanceLog(Base):
__tablename__ = "maintenance_logs"

id = Column(Integer, primary_key=True)
request_id = Column(Integer, ForeignKey("maintenance_requests.id", ondelete="CASCADE"))

action = Column(String)
old_status = Column(String)
new_status = Column(String)

performed_by = Column(Integer, ForeignKey("users.id"))
timestamp = Column(DateTime(timezone=True), server_default=func.now())
notes = Column(String)
46 changes: 46 additions & 0 deletions Backend/app/models/maintenance_request.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
from sqlalchemy import (
Column, Integer, String, Enum, ForeignKey,
Date, DateTime, Numeric
)
from sqlalchemy.sql import func
from app.database import Base
import enum


class RequestType(enum.Enum):
Corrective = "Corrective"
Preventive = "Preventive"


class RequestStatus(enum.Enum):
New = "New"
In_Progress = "In Progress"
Repaired = "Repaired"
Scrap = "Scrap"


class MaintenanceRequest(Base):
__tablename__ = "maintenance_requests"

id = Column(Integer, primary_key=True)
request_number = Column(String, unique=True, nullable=False)

type = Column(Enum(RequestType), nullable=False)
subject = Column(String, nullable=False)
description = Column(String)

equipment_id = Column(Integer, ForeignKey("equipment.id"), nullable=False)
team_id = Column(Integer, ForeignKey("maintenance_teams.id"), nullable=False)
assigned_technician_id = Column(Integer, ForeignKey("users.id"))

status = Column(Enum(RequestStatus), default=RequestStatus.New)

scheduled_date = Column(Date)
start_time = Column(DateTime)
end_time = Column(DateTime)
duration_hours = Column(Numeric(5, 2))

created_by = Column(Integer, ForeignKey("users.id"), nullable=False)

created_at = Column(DateTime(timezone=True), server_default=func.now())
updated_at = Column(DateTime(timezone=True), onupdate=func.now())
18 changes: 18 additions & 0 deletions Backend/app/models/team.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
from sqlalchemy import Column, Integer, String, ForeignKey
from app.database import Base


class MaintenanceTeam(Base):
__tablename__ = "maintenance_teams"

id = Column(Integer, primary_key=True)
name = Column(String, unique=True, nullable=False)
description = Column(String)


class TeamMember(Base):
__tablename__ = "team_members"

id = Column(Integer, primary_key=True)
team_id = Column(Integer, ForeignKey("maintenance_teams.id", ondelete="CASCADE"))
user_id = Column(Integer, ForeignKey("users.id", ondelete="CASCADE"))
23 changes: 23 additions & 0 deletions Backend/app/models/user.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
from sqlalchemy import Column, Integer, String, Boolean, Enum, DateTime
from sqlalchemy.sql import func
from app.database import Base
import enum


class UserRole(enum.Enum):
employee = "employee"
technician = "technician"
manager = "manager"
admin = "admin"


class User(Base):
__tablename__ = "users"

id = Column(Integer, primary_key=True, index=True)
name = Column(String, nullable=False)
email = Column(String, unique=True, nullable=False, index=True)
role = Column(Enum(UserRole), nullable=False)
avatar_url = Column(String)
is_active = Column(Boolean, default=True)
created_at = Column(DateTime(timezone=True), server_default=func.now())
Binary file modified Backend/app/routers/__pycache__/routes.cpython-313.pyc
Binary file not shown.
Loading