Skip to content
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
128 changes: 128 additions & 0 deletions backend/app/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
from fastapi import FastAPI, Request, Depends, HTTPException
from fastapi.responses import HTMLResponse
from fastapi.templating import Jinja2Templates
from sqlalchemy.orm import Session

from app.database.connection import SessionLocal
from app.database.models import Department, Employee, EmployeeRecord
from app.schemas.employee import (
DepartmentCreate,
DepartmentSchema,
EmployeeCreate,
EmployeeSchema,
EmployeeRecordSchema,
EmployeeRecordCreate,
)

# ENDPOINTS IN MAIN.PY BUT USUALLY WHEN THE PROJECT IS BIGGER WE SEPARATE THEM IN ROUTER

app = FastAPI()
templates = Jinja2Templates(directory="app/templates")

def get_db():
db = SessionLocal()
try:
yield db
finally:
db.close()


@app.get("/", response_class=HTMLResponse)
def home(request: Request):
return templates.TemplateResponse(
request=request,
name="index.html",
context={"title": "ML App Home"},
)


@app.get("/departments", response_class=HTMLResponse)
def department_page(request: Request, db: Session = Depends(get_db)):
db_departments = db.query(Department).all()
return templates.TemplateResponse(
request=request,
name="departments.html",
context={"departments": db_departments},
)


@app.get("/employees", response_class=HTMLResponse)
def employee_page(request: Request, db: Session = Depends(get_db)):
db_departments = db.query(Department).all()
db_employees = db.query(Employee).all()
return templates.TemplateResponse(
request=request,
name="employees.html",
context={
"departments": db_departments,
"employees": db_employees,
},
)


@app.get("/records", response_class=HTMLResponse)
def records_page(request: Request, db: Session = Depends(get_db)):
db_employees = db.query(Employee).all()
db_records = db.query(EmployeeRecord).all()
return templates.TemplateResponse(
request=request,
name="records.html",
context={
"employees": db_employees,
"records": db_records,
},
)


@app.post("/departments", response_model=DepartmentSchema)
def create_department(department_in: DepartmentCreate, db: Session = Depends(get_db)):
department = Department(name=department_in.name)
db.add(department)
db.commit()
db.refresh(department)
return department


@app.post("/employees", response_model=EmployeeSchema)
def create_employee(employee_in: EmployeeCreate, db: Session = Depends(get_db)):
department = None
if employee_in.department_id is not None:
department = db.get(Department, employee_in.department_id)
if department is None:
raise HTTPException(status_code=400, detail="department_id does not exist")
else:
department = db.query(Department).filter_by(name="General").first()
if department is None:
department = Department(name="General")
db.add(department)
db.flush()

employee = Employee(
first_name=employee_in.first_name,
last_name=employee_in.last_name,
email=employee_in.email,
department_id=department.id,
)
db.add(employee)
db.commit()
db.refresh(employee)
return employee


@app.post("/records", response_model=EmployeeRecordSchema)
def create_record(record_in: EmployeeRecordCreate, db: Session = Depends(get_db)):
employee = db.get(Employee, record_in.employee_id)
if employee is None:
raise HTTPException(status_code=400, detail="employee_id does not exist")

db_record = EmployeeRecord(
employee_id=record_in.employee_id,
exam_score=record_in.exam_score,
years_exp=record_in.years_exp,
salary=record_in.salary,
)
db.add(db_record)
db.commit()
db.refresh(db_record)
return db_record

49 changes: 49 additions & 0 deletions backend/app/schemas/employee.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
from typing import Optional

from pydantic import BaseModel, ConfigDict

# GET SCHEMAS

class DepartmentSchema(BaseModel):
model_config = ConfigDict(from_attributes=True)

id: int
name: str


class EmployeeSchema(BaseModel):
model_config = ConfigDict(from_attributes=True)

id: int
first_name: str
last_name: str
email: Optional[str] = None
department_id: int


class EmployeeRecordSchema(BaseModel):
model_config = ConfigDict(from_attributes=True)

id: int
employee_id: int
exam_score: int
years_exp: int
salary: float

# POST SCHEMAS - note that we don't have id fields here because they are created automatically

class DepartmentCreate(BaseModel):
name: str

class EmployeeCreate(BaseModel):
first_name: str
last_name: str
email: Optional[str] = None
department_id: Optional[int] = None

class EmployeeRecordCreate(BaseModel):
employee_id: int
exam_score: int
years_exp: int
salary: float

71 changes: 71 additions & 0 deletions backend/app/templates/departments.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Departments</title>
<style>
body { font-family: Arial, sans-serif; margin: 50px; background-color: #f7f9fc; }
section { background: white; padding: 20px; border-radius: 8px; box-shadow: 0 1px 4px rgba(0,0,0,0.08); }
a { color: #2c3e50; text-decoration: none; }
</style>
</head>
<body>
<header style="margin-bottom: 30px; padding: 20px; background: white; border-radius: 8px; box-shadow: 0 1px 4px rgba(0,0,0,0.08);">
<nav style="display: flex; gap: 24px; align-items: center;">
<a href="/" style="font-weight: 700; color: #2c3e50;">Home</a>
<a href="/departments" style="color: #2c3e50;">Departments</a>
<a href="/employees" style="color: #2c3e50;">Employees</a>
<a href="/records" style="color: #2c3e50;">Records</a>
</nav>
</header>
<section>
<h1>Departments</h1>
<form id="department-form">
<label>Name<br><input type="text" name="name" required></label><br><br>
<button type="submit">Create department</button>
<span id="department-message" style="margin-left: 1rem;color:#2c3e50"></span>
</form>

<h2>Existing departments</h2>
<ul id="department-list">
{% for department in departments %}
<li>{{ department.id }} — {{ department.name }}</li>
{% else %}
<li>No departments yet.</li>
{% endfor %}
</ul>
</section>

<script>
const departmentForm = document.getElementById('department-form');
const departmentList = document.getElementById('department-list');
departmentForm.addEventListener('submit', async event => {
event.preventDefault();
const message = document.getElementById('department-message');
message.textContent = '';
const formData = new FormData(departmentForm);
const payload = { name: formData.get('name')?.toString().trim() };

try {
const response = await fetch('/departments', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload),
});
if (!response.ok) {
const text = await response.text();
throw new Error(text || 'Request failed');
}
const department = await response.json();
const item = document.createElement('li');
item.textContent = `${department.id} — ${department.name}`;
departmentList.appendChild(item);
message.textContent = 'Department created.';
departmentForm.reset();
} catch (error) {
message.textContent = 'Error: ' + error.message;
}
});
</script>
</body>
</html>
86 changes: 86 additions & 0 deletions backend/app/templates/employees.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Employees</title>
<style>
body { font-family: Arial, sans-serif; margin: 50px; background-color: #f7f9fc; }
section { background: white; padding: 20px; border-radius: 8px; box-shadow: 0 1px 4px rgba(0,0,0,0.08); }
a { color: #2c3e50; text-decoration: none; }
</style>
</head>
<body>
<header style="margin-bottom: 30px; padding: 20px; background: white; border-radius: 8px; box-shadow: 0 1px 4px rgba(0,0,0,0.08);">
<nav style="display: flex; gap: 24px; align-items: center;">
<a href="/" style="font-weight: 700; color: #2c3e50;">Home</a>
<a href="/departments" style="color: #2c3e50;">Departments</a>
<a href="/employees" style="color: #2c3e50;">Employees</a>
<a href="/records" style="color: #2c3e50;">Records</a>
</nav>
</header>
<section>
<h1>Employees</h1>
<form id="employee-form">
<label>First Name<br><input type="text" name="first_name" required></label><br><br>
<label>Last Name<br><input type="text" name="last_name" required></label><br><br>
<label>Email<br><input type="email" name="email"></label><br><br>
<label>Department<br>
<select name="department_id">
<option value="">General</option>
{% for department in departments %}
<option value="{{ department.id }}">{{ department.name }}</option>
{% endfor %}
</select>
</label><br><br>
<button type="submit">Create employee</button>
<span id="employee-message" style="margin-left: 1rem;color:#2c3e50"></span>
</form>

<h2>Existing employees</h2>
<ul id="employee-list">
{% for employee in employees %}
<li>{{ employee.id }} — {{ employee.first_name }} {{ employee.last_name }} (dept {{ employee.department_id }})</li>
{% else %}
<li>No employees yet.</li>
{% endfor %}
</ul>
</section>

<script>
const employeeForm = document.getElementById('employee-form');
const employeeList = document.getElementById('employee-list');
employeeForm.addEventListener('submit', async event => {
event.preventDefault();
const message = document.getElementById('employee-message');
message.textContent = '';
const formData = new FormData(employeeForm);
const payload = {
first_name: formData.get('first_name')?.toString().trim(),
last_name: formData.get('last_name')?.toString().trim(),
email: formData.get('email')?.toString().trim() || undefined,
department_id: formData.get('department_id') ? Number(formData.get('department_id')) : undefined,
};

try {
const response = await fetch('/employees', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload),
});
if (!response.ok) {
const text = await response.text();
throw new Error(text || 'Request failed');
}
const employee = await response.json();
const item = document.createElement('li');
item.textContent = `${employee.id} — ${employee.first_name} ${employee.last_name} (dept ${employee.department_id})`;
employeeList.appendChild(item);
message.textContent = 'Employee created.';
employeeForm.reset();
} catch (error) {
message.textContent = 'Error: ' + error.message;
}
});
</script>
</body>
</html>
35 changes: 35 additions & 0 deletions backend/app/templates/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>ML Project Records</title>
<style>
body { font-family: Arial, sans-serif; margin: 50px; background-color: #f7f9fc; }
table { border-collapse: collapse; width: 100%; margin-top: 20px; background: white; }
th, td { border: 1px solid #ccc; padding: 12px; text-align: left; }
th { background-color: #34495e; color: white; }
</style>
</head>
<body>
</head>
<body>
<header style="margin-bottom: 30px; padding: 20px; background: white; border-radius: 8px; box-shadow: 0 1px 4px rgba(0,0,0,0.08);">
<nav style="display: flex; gap: 24px; align-items: center;">
<a href="/" style="font-weight: 700; color: #2c3e50;">Home</a>
<a href="/departments" style="color: #2c3e50;">Departments</a>
<a href="/employees" style="color: #2c3e50;">Employees</a>
<a href="/records" style="color: #2c3e50;">Records</a>
</nav>
</header>

<section style="background: white; padding: 20px; border-radius: 8px; box-shadow: 0 1px 4px rgba(0,0,0,0.08);">
<h1>ML App Home</h1>
<p>Use the pages below to manage your data.</p>
<ul style="padding-left: 20px;">
<li><a href="/departments">Departments</a></li>
<li><a href="/employees">Employees</a></li>
<li><a href="/records">Records</a></li>
</ul>
</section>
</body>
</html>
Loading