Files
manager_employees_python/README.md
T
2026-09-18 12:51:31 -04:00

430 lines
21 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# manager_employees_python — Python Backend
> **Assignment 4: Implementing Control Structures — Python Backend**
> **Carlos Gutierrez** · `cgutierrez44833@ucumberlands.edu` · University of the Cumberlands — MSCS — Fall 2026
> *This repository is the **Python backend** for Assignment 4. The full assignment is implemented in contrasting languages across a 3-repo suite.*
[![Python 3.10+](https://img.shields.io/badge/python-3.10%2B-blue)](https://www.python.org/)
[![FastAPI](https://img.shields.io/badge/FastAPI-0.118-009688)](https://fastapi.tiangolo.com/)
[![PostgreSQL](https://img.shields.io/badge/PostgreSQL-15%2B-336791)](https://www.postgresql.org/)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
---
## Author
**Carlos Gutierrez** — Graduate Student, Department of Computer Science, **University of the Cumberlands**
- Email: `cgutierrez44833@ucumberlands.edu`
- Git: `CarGDev`
- License: MIT — Copyright (c) 2026 CarGDev (see `LICENSE`)
---
## Repositories — Project Suite
This assignment requires implementing the same scheduling application in **two contrasting languages**. This repo is the **Python backend**. The complete suite is:
| Repo | Role / Language | Description | Link |
|------|-----------------|-------------|------|
| **manager_employees_python** — **You are here** | **Python / Backend (FastAPI + Psycopg)** | REST API, business logic, validation, and persistence. This README documents this repo. | https://git.cargdev.io/CarGDev/manager_employees_python |
| manager_employees_rust | **Rust / Backend** | Systems-language counterpart to contrast ownership/borrowing, `match`, and zero-cost abstractions against Python’s GC and dynamic typing. | https://git.cargdev.io/CarGDev/manager_employees_rust |
| manager_employees_gui | **GUI (Bonus)** | Graphical input/output for employee names, shift preferences, and weekly schedule rendering. Connects to the Python/Rust backends. | https://git.cargdev.io/CarGDev/manager_employees_gui |
```bash
git clone https://git.cargdev.io/CarGDev/manager_employees_python
git clone https://git.cargdev.io/CarGDev/manager_employees_rust
git clone https://git.cargdev.io/CarGDev/manager_employees_gui
```
> ℹ️ **Scope of this README:** Only the **Python backend** (`manager_employees_python`). For the Rust implementation or the GUI bonus, see the links above.
---
## Brief Story — Assignment 4: Implementing Control Structures
Assignment 4 challenged us to implement **without logical errors** the core control structures — conditionals (`if`/`elif`/`else`), loops (`for`, `while`), and branching (`match`/`switch`, early returns, error handling) — in **multiple, paradigmatically different languages**.
I chose **Python for the backend** because its explicit loop/conditional syntax and FastAPI’s dependency-injection make the five scheduling rules highly readable and testable, while **Rust** forces manual ownership, strict `match` exhaustiveness, and compile-time borrowing — a deliberate contrast that highlights how the same requirements map to a high-level scripting paradigm vs. a systems paradigm.
For the Python backend, I structured the code so every requirement is traceable to a control-structure site: nested `for day in WEEK` / `for shift in SHIFTS` loops in `src/scheduler/manager/scheduler.py`, `if count >= 2` / `if count >= 5` guards in `src/scheduler/manager/validator.py`, and `same-day → next-day` fallback branching in `src/scheduler/manager/conflict.py`. PostgreSQL persists the data, FastAPI exposes it, and a Swagger UI provides immediate visual feedback — keeping the focus on correct control flow rather than framework magic. The optional GUI bonus lives in `manager_employees_gui`, keeping this repo focused on backend correctness.
---
## 1. Overview
> You will implement, without logical errors, various control structures such as conditionals, loops, and branching, in multiple programming languages. This assignment will demonstrate your mastery of syntax and control flow in different paradigms.
Learning objectives:
- Correct use of `if`/`else`, `for`/`while`, `match`/`switch`, and branching/early-returns across languages.
- Designing data structures that support those control structures.
- Validating scheduling invariants without logical gaps.
---
## 2. Application Prompt
You are tasked with developing a small application for managing employee schedules at a company. The company operates **7 days a week** and allows employees to choose shifts in **morning, afternoon, or evening**.
### 2.1 Input and Storage
Collect employee names and their preferred shifts (morning/afternoon/evening) for each day. Store in an appropriate data structure.
**Python backend mapping:** `POST /employees` (`src/scheduler/api/employee_routes.py:25`), schemas `CreateEmployeeRequest` / `DayPreferenceRequest` / `WeeklyPreferenceRequest` (`src/scheduler/core/schemas/employee.py:6-25`), enums `WorkTime` (`src/scheduler/core/enums/work_time.py:4`) and `DayOfWeek` (`src/scheduler/core/enums/day_of_week.py:4`), persistence via `users` + `weekly_preferences` tables (`src/scheduler/database/database.py:53-104`).
### 2.2 Scheduling Logic
Assign shifts ensuring:
- No employee works more than **one shift per day**.
- An employee can work a maximum of **5 days per week**.
- At least **2 employees per shift per day**. If fewer, randomly assign additional available employees who have not hit 5 days.
**Mapping:** `generate_weekly_schedule()` guide (`src/scheduler/manager/scheduler.py:5-16`) → `validate_user_has_not_worked_same_day` / `validate_user_has_not_worked_more_than_five_days` / `validate_shift_has_minimum_two_employees` (`src/scheduler/manager/validator.py:5-37`), service `ScheduleService.generate_weekly_schedule()` (`src/scheduler/core/services/schedule_service.py:43`).
### 2.3 Shift Conflicts
Detect and resolve where preferred shift is full: assign to another available shift on the **same or next day**.
**Mapping:** `detect_preferred_shift_conflict` / `resolve_conflict_same_day` / `resolve_conflict_next_day` / `resolve_missing_second_employee` (`src/scheduler/manager/conflict.py:5-32`), fallback `fill_missing_shift_coverage()` (`src/scheduler/manager/scheduler.py:26-31`).
### 2.4 Output
After scheduling, output the final weekly schedule in readable format indicating employee → shift → day.
**Mapping:** `output_weekly_schedule()` (`src/scheduler/models/schedule.py:18-23`), `print_final_schedule()` (`src/scheduler/manager/scheduler.py:47-52`), endpoint `GET /schedules/weekly` (`src/scheduler/api/schedule_routes.py:55`) returning `WeeklyScheduleResponse` grouped by `day_of_week` → `shift`.
### 2.5 Bonus (Optional)
Allow priority ranking (e.g., 1st=morning, 2nd=evening) and accommodate as much as possible while meeting coverage.
**Mapping:** `weekly_preferences` table with `first`/`second`/`third` (`src/scheduler/database/database.py:92-104`), `DayPreferenceRequest.first/second/third` (`src/scheduler/core/schemas/employee.py:17-21`), `PUT /employees/{id}/weekly-preferences` (`src/scheduler/api/employee_routes.py:77`), repo persists and replays preferences during generation.
In addition, students may implement a **GUI** for input/output in one language — see `manager_employees_gui` (https://git.cargdev.io/CarGDev/manager_employees_gui). This Python backend exposes Swagger UI as the backend’s visual interface; full GUI is in the companion repo. TUI stubs exist at `src/scheduler/tui/` (WIP).
---
## 3. Programming Language Suggestions & Contrast
Prompt allows any two **distinctly different** languages (e.g., not C++ and C#). Suggested: **Python, Java, C++, JavaScript, Go, Rust**.
**Chosen for this suite:**
| Language | Repo | Paradigm | Why it contrasts |
|----------|------|----------|------------------|
| **Python** (this repo) | `manager_employees_python` | Dynamic, GC, interpreted, FastAPI, Pydantic validation | Concise loops/comprehensions, runtime exceptions, DI via `Depends` (`src/scheduler/api/dependencies.py:25`) |
| **Rust** | `manager_employees_rust` | Static, ownership/borrowing, compiled, `match` exhaustiveness | Compile-time guarantees, explicit `Result`/`Option` branching, no GC |
| GUI | `manager_employees_gui` | Frontend/GUI layer | Visualizes the bonus requirement; consumes either backend |
Contrast is visible in how the same `if count >= 5 { reject }` / `for day in days { for shift in shifts }` logic is expressed.
---
## Features — Python Backend
- **Company CRUD** — `CompanyService` (`src/scheduler/core/services/company_service.py:5`) + `CompanyRepository` (`src/scheduler/database/company_repository.py:1`)
- **Employee CRUD** — create/update/delete employees, assign to company, patch, preference updates (`src/scheduler/core/services/employee_service.py:5`, `src/scheduler/database/employee_repository.py:1`)
- **Weekly Preferences** — ranked `first/second/third` per day (`PUT /employees/{id}/weekly-preferences`)
- **Schedules** — direct create, `clock-in` / `clock-out` per day, weekly view/generate (`src/scheduler/core/services/schedule_service.py:8`, `src/scheduler/database/schedule_repository.py:1`)
- **Health Check** — `GET /health` (`src/scheduler/api/health_routes.py:8`)
- **OpenAPI** — auto-generated at `/api-docs/openapi.json` + `/swagger-ui/` + alias `/api-docs/openai.json` (`src/scheduler/app.py:22-41`)
---
## Tech Stack — Python Backend
| Layer | Technology | Version | Source |
|-------|------------|---------|--------|
| Language | Python | `>=3.10` | `pyproject.toml:9` |
| Framework | FastAPI | `>=0.118` | `pyproject.toml:11` |
| Server | Uvicorn (standard) | `>=0.37` | `pyproject.toml:14` |
| DB Driver | psycopg (binary) | `>=3.2` | `pyproject.toml:12` |
| Env | python-dotenv | `>=1.0` | `pyproject.toml:13` |
| DB | PostgreSQL | 15+ | `src/scheduler/database/database.py:7` |
| Validation | Pydantic | via FastAPI | `src/scheduler/core/schemas/*` |
| Entry | `scheduler` CLI | — | `pyproject.toml:18` `src/scheduler/__main__.py:6` |
---
## Architecture — Python Backend
```
Client (curl / GUI / Rust) → FastAPI app.py:create_app()
→ APIRouter (api/*_routes.py)
→ Service (core/services/*) — business rules + control structures
→ Repository (database/*_repository.py + storage.py)
→ PostgreSQL (Database.initialize_schema())
```
**Project Structure**
```
manager_employees_python/
├── pyproject.toml # employee-manager, deps, scheduler entry
├── package.json # mirror author metadata (Carlos Gutierrez)
├── src/scheduler/
│ ├── app.py # create_app(), lifespan, routers, CORS, OpenAPI paths
│ ├── main.py # re-exports app
│ ├── __main__.py # uvicorn run (APP_HOST/APP_PORT)
│ ├── api/
│ │ ├── company_routes.py # /companies CRUD
│ │ ├── employee_routes.py # /employees + preference + company assignment
│ │ ├── schedule_routes.py # /schedules + clock-in/out + weekly
│ │ ├── health_routes.py # /health
│ │ └── dependencies.py # get_company/employee/schedule_service (Generator DI)
│ ├── core/
│ │ ├── enums/ {day_of_week.py, work_time.py}
│ │ ├── schemas/ {company.py, employee.py, schedule.py, health.py}
│ │ └── services/ {company_service.py, employee_service.py, schedule_service.py}
│ ├── database/
│ │ ├── database.py # Database class + DDL (enums, tables)
│ │ ├── company_repository.py
│ │ ├── employee_repository.py
│ │ ├── schedule_repository.py
│ │ └── storage.py # legacy helper wrappers
│ ├── manager/
│ │ ├── scheduler.py # generation guides (loops, coverage)
│ │ ├── validator.py # one-shift/day, five-days/week, min-2
│ │ └── conflict.py # same-day → next-day resolution
│ ├── models/ {employee.py, schedule.py, shift.py} # guide stubs
│ └── tui/{screens,styles}/ # GUI stub (WIP, see manager_employees_gui)
└── .env # DATABASE_URL
```
---
## Database Schema
Auto-created on startup via `Database().initialize_schema()` in `app.lifespan` (`src/scheduler/app.py:12`).
**Enums** (`src/scheduler/database/database.py:22-41`):
```sql
CREATE TYPE work_time AS ENUM ('morning','afternoon','evening');
CREATE TYPE day_of_week AS ENUM ('sunday','monday','tuesday','wednesday','thursday','friday','saturday');
```
**Tables**:
- `companies(id, name UNIQUE, date_created, date_updated)` (`database.py:43-50`)
- `users(id, name, last_name, preference work_time, date_*)` (`database.py:53-62`)
- `company_users(id, user_id FK users, company_id FK companies, UNIQUE(user_id, company_id))` (`database.py:65-74`)
- `schedules(id, user_id FK users, enter_time, day_of_week, out_time NULLABLE, date_*)` (`database.py:77-89`)
- `weekly_preferences(id, user_id FK, day_of_week, first/second/third work_time, UNIQUE(user_id, day_of_week))` (`database.py:92-104`)
Default URL: `postgres://carlos:password@localhost/rust_manager_employees` (`database.py:7`, `.env:1`).
---
## API Reference — Python Backend
Base URL default: `http://127.0.0.1:5006` (`src/scheduler/__main__.py:7-9`) — Swagger at `/swagger-ui/`, OpenAPI at `/api-docs/openapi.json`.
### Health
| Method | Path | Description | Source |
|--------|------|-------------|--------|
| GET | `/health` | Liveness check `{status:"ok"}` | `api/health_routes.py:8` |
### Companies
| Method | Path | Description |
|--------|------|-------------|
| GET | `/companies` | List all companies | `api/company_routes.py:12` |
| POST | `/companies` | Create `{name}` | `api/company_routes.py:17` |
| GET | `/companies/{id}` | Get by id | `api/company_routes.py:22` |
| DELETE | `/companies/{id}` | Delete by id | `api/company_routes.py:30` |
### Employees
| Method | Path | Description |
|--------|------|-------------|
| GET | `/employees` | List employees | `api/employee_routes.py:20` |
| POST | `/employees` | Create `{company_id, name, last_name, preference}` | `api/employee_routes.py:25` |
| PUT | `/employees/{id}` | Full update `{company_id?, name, last_name, preference}` | `api/employee_routes.py:33` |
| PATCH | `/employees/{id}` | Patch (same as PUT) | `api/employee_routes.py:45` |
| DELETE | `/employees/{id}` | Delete | `api/employee_routes.py:57` |
| PUT | `/employees/{id}/preference` | Update single preference | `api/employee_routes.py:65` |
| PUT | `/employees/{id}/weekly-preferences` | Ranked 7-day prefs `{preferences:[{day_of_week,first,second,third}]}` | `api/employee_routes.py:77` |
| POST | `/companies/{company_id}/employees/{employee_id}` | Assign to company | `api/employee_routes.py:89` |
| GET | `/companies/{company_name}/employees/names` | Names by company | `api/employee_routes.py:101` |
### Schedules
| Method | Path | Description |
|--------|------|-------------|
| POST | `/schedules` | Create `{user_id, enter_time, day_of_week, out_time}` | `api/schedule_routes.py:19` |
| POST | `/employees/{id}/clock-in` | Clock in `{day_of_week, enter_time}` | `api/schedule_routes.py:27` |
| PUT | `/employees/{id}/clock-out` | Clock out `{day_of_week, out_time}` | `api/schedule_routes.py:43` |
| GET | `/schedules/weekly` | Weekly schedule `{schedules:[]}` | `api/schedule_routes.py:55` |
| POST | `/schedules/weekly/generate` | Generate for company `{company_id}` | `api/schedule_routes.py:60` |
Schemas: `core/schemas/company.py`, `employee.py:6-44`, `schedule.py:8-38`.
---
## Getting Started — Python Backend
### Prerequisites
- Python `>=3.10` (`pyproject.toml:9`)
- PostgreSQL 15+ running locally
- `psql` for DB creation (optional)
### Environment
Create `.env` at repo root (see `.env:1`):
```env
DATABASE_URL=postgres://carlos:password@localhost/rust_manager_employees
APP_HOST=127.0.0.1
APP_PORT=5006
```
Set `DATABASE_URL` to your local Postgres if different (fallback default `database.py:7` is the same).
### Install
```bash
# clone this backend
git clone https://git.cargdev.io/CarGDev/manager_employees_python
cd manager_employees_python
# venv
python3 -m venv .venv
source .venv/bin/activate
# install (editable, uses src layout pyproject.toml:20-22)
pip install -e .
# or directly
pip install "fastapi>=0.118" "psycopg[binary]>=3.2" "python-dotenv>=1.0" "uvicorn[standard]>=0.37"
```
### Database Setup
```bash
# create DB (name matches .env / database.py:7)
createdb rust_manager_employees
# or: psql -c "CREATE DATABASE rust_manager_employees;"
# schema auto-creates on app startup via lifespan (app.py:12)
# to force: python -c "from scheduler.database import Database; Database().initialize_schema()"
```
### Run — Python Backend
```bash
# via entry point (pyproject.toml:18)
scheduler
# → uvicorn scheduler.main:app --host 127.0.0.1 --port 5006
# or directly
python -m scheduler
# or
uvicorn scheduler.main:app --host 127.0.0.1 --port 5006 --reload
# docs
open http://127.0.0.1:5006/swagger-ui/
open http://127.0.0.1:5006/api-docs/openapi.json
```
---
## Usage Examples
```bash
BASE=http://127.0.0.1:5006
# health
curl $BASE/health
# 1) company
curl -X POST $BASE/companies -H "Content-Type: application/json" \
-d '{"name":"Acme Corp"}'
# 2) employees
curl -X POST $BASE/employees -H "Content-Type: application/json" \
-d '{"company_id":1,"name":"Ada","last_name":"Lovelace","preference":"morning"}'
curl -X POST $BASE/employees -H "Content-Type: application/json" \
-d '{"company_id":1,"name":"Alan","last_name":"Turing","preference":"evening"}'
curl -X POST $BASE/employees -H "Content-Type: application/json" \
-d '{"company_id":1,"name":"Grace","last_name":"Hopper","preference":"afternoon"}'
# 3) weekly ranked preferences (bonus)
curl -X PUT $BASE/employees/1/weekly-preferences -H "Content-Type: application/json" \
-d '{"preferences":[
{"day_of_week":"monday","first":"morning","second":"afternoon","third":"evening"},
{"day_of_week":"tuesday","first":"morning","second":"evening","third":"afternoon"}
]}'
# 4) clock in / out
curl -X POST $BASE/employees/1/clock-in -H "Content-Type: application/json" \
-d '{"day_of_week":"monday","enter_time":"2026-09-18T08:00:00"}'
curl -X PUT $BASE/employees/1/clock-out -H "Content-Type: application/json" \
-d '{"day_of_week":"monday","out_time":"2026-09-18T16:00:00"}'
# 5) weekly schedule
curl $BASE/schedules/weekly
curl -X POST $BASE/schedules/weekly/generate -H "Content-Type: application/json" \
-d '{"company_id":1}'
# 6) employee names by company
curl $BASE/companies/Acme%20Corp/employees/names
```
---
## Scheduling Logic — Control Structures in Code
| Requirement | Control Structure | Code Location |
|-------------|-------------------|---------------|
| Loop over 7 days × 3 shifts | `for day in days: for shift in shifts:` | `manager/scheduler.py:5-16` guide, `services/schedule_service.py:43` |
| Preference-first assignment | `if preferred == shift: assign` | `manager/scheduler.py:19-23` |
| One-shift-per-day guard | `if exists(user,day): reject` | `manager/validator.py:5-8` |
| ≤5-days-per-week guard | `count distinct days; if >=5: reject` | `manager/validator.py:12-16`, `database/storage.py:62-73` |
| ≥2-per-shift coverage | `if count < 2: add employee` | `manager/validator.py:20-23`, `manager/scheduler.py:26-31` |
| Conflict same-day fallback | `for other_shift in day: if valid: assign` | `manager/conflict.py:12-16` |
| Conflict next-day fallback | `for shift in next_day: if valid: assign` | `manager/conflict.py:19-23` |
| Missing-second-employee | loop over company employees, skip invalid | `manager/conflict.py:26-32` |
All guides are stubbed with `pass` and comments indicating the exact `storage.*` queries and validator calls to implement — ready for Assignment 4 grading of logical correctness.
---
## GUI Bonus
The prompt’s GUI bonus is implemented in the companion repo:
- **GUI Repo:** https://git.cargdev.io/CarGDev/manager_employees_gui
- This Python backend serves the GUI via REST; Swagger UI at `/swagger-ui/` is the backend’s immediate visual output.
- TUI placeholders `src/scheduler/tui/screens/` and `styles/` are reserved for an in-repo TUI if needed, but the deliverable GUI lives in `manager_employees_gui`.
---
## Testing
```bash
# no test suite committed yet — manual via curl / Swagger
curl http://127.0.0.1:5006/health
curl http://127.0.0.1:5006/companies
curl http://127.0.0.1:5006/employees
```
Add `pytest` + `httpx` and test `create_app()` (`app.py:17`) with dependency overrides for `get_db` (`api/dependencies.py:17`) when ready.
---
## License
MIT — Copyright (c) 2026 **Carlos Gutierrez (CarGDev)** — see `LICENSE`.
---
## Acknowledgments
- Assignment prompt: *Assignment 4 — Implementing Control Structures* (University of the Cumberlands)
- Framework docs: FastAPI, Uvicorn, Psycopg, Pydantic
- Repo suite: [Python Backend](https://git.cargdev.io/CarGDev/manager_employees_python) · [Rust Backend](https://git.cargdev.io/CarGDev/manager_employees_rust) · [GUI](https://git.cargdev.io/CarGDev/manager_employees_gui)