From d855e5b3752656ab7749732022a4b754ca20fa5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=ABshira=20Randobrava?= <121398589+d3shira@users.noreply.github.com> Date: Thu, 2 Jul 2026 16:18:39 +0200 Subject: [PATCH 1/4] GET employee records endpoint and simple html to show table on website --- backend/app/main.py | 26 +++++++++++++++++ backend/app/schemas/employee.py | 11 ++++++++ backend/app/templates/index.html | 48 ++++++++++++++++++++++++++++++++ 3 files changed, 85 insertions(+) create mode 100644 backend/app/schemas/employee.py create mode 100644 backend/app/templates/index.html diff --git a/backend/app/main.py b/backend/app/main.py index e69de29..ac7ed48 100644 --- a/backend/app/main.py +++ b/backend/app/main.py @@ -0,0 +1,26 @@ +from fastapi import FastAPI, Request, Depends +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 EmployeeRecord +from app.schemas.employee import EmployeeRecordSchema + +app = FastAPI() +templates = Jinja2Templates(directory="app/templates") + +def get_db(): + db = SessionLocal() + try: + yield db + finally: + db.close() + +@app.get("/", response_class=HTMLResponse) +def show_records(request: Request, db: Session = Depends(get_db)): + db_records = db.query(EmployeeRecord).all() + serialized_records = [EmployeeRecordSchema.model_validate(rec) for rec in db_records] + + return templates.TemplateResponse(request = request, name = "index.html", context = {"records": serialized_records}) + \ No newline at end of file diff --git a/backend/app/schemas/employee.py b/backend/app/schemas/employee.py new file mode 100644 index 0000000..95b1156 --- /dev/null +++ b/backend/app/schemas/employee.py @@ -0,0 +1,11 @@ +from pydantic import BaseModel, ConfigDict + +class EmployeeRecordSchema(BaseModel): + model_config = ConfigDict(from_attributes=True) + + id: int + employee_id: int + exam_score: int + years_exp: int + salary: float + diff --git a/backend/app/templates/index.html b/backend/app/templates/index.html new file mode 100644 index 0000000..be9993f --- /dev/null +++ b/backend/app/templates/index.html @@ -0,0 +1,48 @@ + + + + + ML Project Records + + + + +

Database Records View

+

Reading directly from your PostgreSQL employee_records table:

+ + + + + + + + + + + + + {% for record in records %} + + + + + + + + {% else %} + + + + {% endfor %} + +
Record IDEmployee IDExam ScoreYears of ExperienceSalary
{{ record.id }}{{ record.employee_id }}{{ record.exam_score }}{{ record.years_exp }}${{ "%.2f"|format(record.salary) }}
+ No records found in the database yet. +
+ + + \ No newline at end of file From 1977f36b0041b9419bc6b4f2a495bf70f76bfcb9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=ABshira=20Randobrava?= <121398589+d3shira@users.noreply.github.com> Date: Thu, 2 Jul 2026 18:20:33 +0200 Subject: [PATCH 2/4] GET/CREATE schemas for each database model(table) --- backend/app/schemas/employee.py | 34 +++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/backend/app/schemas/employee.py b/backend/app/schemas/employee.py index 95b1156..f5ca780 100644 --- a/backend/app/schemas/employee.py +++ b/backend/app/schemas/employee.py @@ -1,5 +1,24 @@ +from typing import Optional + from pydantic import BaseModel, ConfigDict +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) @@ -9,3 +28,18 @@ class EmployeeRecordSchema(BaseModel): years_exp: int salary: float +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 + From fd7bafc002e7132209b54224827338fe4b9f1ca2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=ABshira=20Randobrava?= <121398589+d3shira@users.noreply.github.com> Date: Thu, 2 Jul 2026 19:02:45 +0200 Subject: [PATCH 3/4] separate html pages for each table --- backend/app/main.py | 112 +++++++++++++++++++++-- backend/app/templates/departments.html | 71 +++++++++++++++ backend/app/templates/employees.html | 86 ++++++++++++++++++ backend/app/templates/index.html | 51 ++++------- backend/app/templates/records.html | 118 +++++++++++++++++++++++++ 5 files changed, 400 insertions(+), 38 deletions(-) create mode 100644 backend/app/templates/departments.html create mode 100644 backend/app/templates/employees.html create mode 100644 backend/app/templates/records.html diff --git a/backend/app/main.py b/backend/app/main.py index ac7ed48..7254a2f 100644 --- a/backend/app/main.py +++ b/backend/app/main.py @@ -1,11 +1,18 @@ -from fastapi import FastAPI, Request, Depends +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 EmployeeRecord -from app.schemas.employee import EmployeeRecordSchema +from app.database.models import Department, Employee, EmployeeRecord +from app.schemas.employee import ( + DepartmentCreate, + DepartmentSchema, + EmployeeCreate, + EmployeeSchema, + EmployeeRecordSchema, + EmployeeRecordCreate, +) app = FastAPI() templates = Jinja2Templates(directory="app/templates") @@ -17,10 +24,103 @@ def get_db(): finally: db.close() + @app.get("/", response_class=HTMLResponse) -def show_records(request: Request, db: Session = Depends(get_db)): +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() - serialized_records = [EmployeeRecordSchema.model_validate(rec) for rec in db_records] + 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") - return templates.TemplateResponse(request = request, name = "index.html", context = {"records": serialized_records}) + 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 \ No newline at end of file diff --git a/backend/app/templates/departments.html b/backend/app/templates/departments.html new file mode 100644 index 0000000..010acdd --- /dev/null +++ b/backend/app/templates/departments.html @@ -0,0 +1,71 @@ + + + + + Departments + + + +
+ +
+
+

Departments

+
+

+ + +
+ +

Existing departments

+ +
+ + + + diff --git a/backend/app/templates/employees.html b/backend/app/templates/employees.html new file mode 100644 index 0000000..a090d36 --- /dev/null +++ b/backend/app/templates/employees.html @@ -0,0 +1,86 @@ + + + + + Employees + + + +
+ +
+
+

Employees

+
+

+

+

+

+ + +
+ +

Existing employees

+ +
+ + + + diff --git a/backend/app/templates/index.html b/backend/app/templates/index.html index be9993f..ee07a2e 100644 --- a/backend/app/templates/index.html +++ b/backend/app/templates/index.html @@ -11,38 +11,25 @@ + + +
+ +
-

Database Records View

-

Reading directly from your PostgreSQL employee_records table:

- - - - - - - - - - - - - {% for record in records %} - - - - - - - - {% else %} - - - - {% endfor %} - -
Record IDEmployee IDExam ScoreYears of ExperienceSalary
{{ record.id }}{{ record.employee_id }}{{ record.exam_score }}{{ record.years_exp }}${{ "%.2f"|format(record.salary) }}
- No records found in the database yet. -
- +
+

ML App Home

+

Use the pages below to manage your data.

+ +
\ No newline at end of file diff --git a/backend/app/templates/records.html b/backend/app/templates/records.html new file mode 100644 index 0000000..cea096f --- /dev/null +++ b/backend/app/templates/records.html @@ -0,0 +1,118 @@ + + + + + Records + + + +
+ +
+
+

Records

+
+

+

+

+

+ + +
+ +

Employee records

+ + + + + + + + + + + + {% for record in records %} + + + + + + + + {% else %} + + + + {% endfor %} + +
Record IDEmployee IDExam ScoreYears of ExperienceSalary
{{ record.id }}{{ record.employee_id }}{{ record.exam_score }}{{ record.years_exp }}${{ "%.2f"|format(record.salary) }}
No records found in the database yet.
+
+ + + + From b7cee03df6ac4a143cec6b18c1dfa4d73f6a291d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=ABshira=20Randobrava?= <121398589+d3shira@users.noreply.github.com> Date: Fri, 3 Jul 2026 00:36:01 +0200 Subject: [PATCH 4/4] add comments for clarity --- backend/app/main.py | 2 ++ backend/app/schemas/employee.py | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/backend/app/main.py b/backend/app/main.py index 7254a2f..0507979 100644 --- a/backend/app/main.py +++ b/backend/app/main.py @@ -14,6 +14,8 @@ EmployeeRecordCreate, ) +# ENDPOINTS IN MAIN.PY BUT USUALLY WHEN THE PROJECT IS BIGGER WE SEPARATE THEM IN ROUTER + app = FastAPI() templates = Jinja2Templates(directory="app/templates") diff --git a/backend/app/schemas/employee.py b/backend/app/schemas/employee.py index f5ca780..14bf9fc 100644 --- a/backend/app/schemas/employee.py +++ b/backend/app/schemas/employee.py @@ -2,6 +2,8 @@ from pydantic import BaseModel, ConfigDict +# GET SCHEMAS + class DepartmentSchema(BaseModel): model_config = ConfigDict(from_attributes=True) @@ -28,6 +30,8 @@ class EmployeeRecordSchema(BaseModel): 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