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
35 changes: 21 additions & 14 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,22 +1,29 @@
# Byte-compiled / optimized / DLL files
# Python
*.pyc
__pycache__/
.pytest_cache/
*.py[cod]
*$py.class
.Python

# Translations
*.mo
*.pot

# virtualenv
# Virtual Environment
venv/
.venv/
env/
ENV/
# pipenv: https://github.com/kennethreitz/pipenv
/Pipfile

# Database
/db.sqlite3
# Django
*.log
db.sqlite3
db.sqlite3-journal
media/
staticfiles/

# IDE
.vscode/
.idea/
*.swp
*.swo

# Editors stuff
.idea
.vscode
# OS
.DS_Store
Thumbs.db
303 changes: 303 additions & 0 deletions IMPLEMENTATION.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,303 @@
# Bus Shift Management System - Implementation Documentation

## Overview

This implementation adds bus shift management functionality to the existing Django application. A bus shift represents a journey with a specific bus, driver, and multiple stops at different places.

## What Was Implemented

### Models

#### `BusShift` Model
Located in `padam_django/fleet/models.py`

**Fields:**
- `bus` - ForeignKey to Bus model
- `driver` - ForeignKey to Driver model
- `created_at` - Timestamp of shift creation
- `updated_at` - Timestamp of last modification

**Calculated Properties:**
- `departure_time` - Returns arrival time at the first stop
- `arrival_time` - Returns arrival time at the last stop
- `duration` - Calculates total journey time (arrival_time - departure_time)
- `stop_count` - Returns the number of stops in the shift

#### `BusStop` Model
Located in `padam_django/fleet/models.py`

**Fields:**
- `bus_shift` - ForeignKey to BusShift (related_name='stops')
- `place` - ForeignKey to Place model
- `arrival_time` - DateTime when bus arrives at this stop
- `order` - Positive integer for stop sequencing

**Constraints:**
- `unique_together` on ('bus_shift', 'order') - prevents duplicate order numbers within a shift
- Ordered by bus_shift and order by default

### Business Logic & Validation

The following business constraints are enforced in the admin interface (`padam_django/fleet/admin.py`):

#### 1. Minimum Stops Requirement
- Every bus shift must have at least 2 stops
- Validated in `BusShiftAdmin.save_related()`

#### 2. Chronological Order
- Stop sequence numbers must match chronological order
- A stop with a lower order number must have an earlier arrival time
- Example: Stop #1 at 09:00, Stop #2 at 10:00 ✅
- Example: Stop #1 at 10:00, Stop #0 at 09:00 ❌

#### 3. No Overlapping Bus Assignments
- The same bus cannot be assigned to multiple shifts with overlapping time periods
- Uses aggregated min/max arrival times to detect overlaps
- Overlap detection: Two time ranges [s1, e1] and [s2, e2] overlap if s2 ≤ e1 AND e2 ≥ s1

#### 4. No Overlapping Driver Assignments
- The same driver cannot be assigned to multiple shifts with overlapping time periods
- Uses the same overlap detection logic as buses

### Admin Interface

#### `BusShiftAdmin`
Located in `padam_django/fleet/admin.py`

**Features:**
- Tabular inline interface for managing bus stops
- Minimum 2 stops required (enforced at UI level with `min_num=2`)
- `extra=0` means no extra empty forms shown by default (cleaner interface)

**List Display:**
- Shift ID
- Bus (license plate)
- Driver (username)
- Departure time (calculated)
- Arrival time (calculated)
- Duration (calculated)
- Stop count (calculated)

**List Filters:**
- Filter by bus
- Filter by driver

**Search:**
- Search by bus license plate
- Search by driver username, first name, or last name

**Validation:**
All validation happens in the `save_related()` method, which runs after the shift and its stops are saved, allowing us to validate the complete shift with all its stops.

## Design Decisions

### 1. Model Placement
**Decision:** Placed `BusShift` and `BusStop` models in the `fleet` app.

**Rationale:** These models are closely related to bus operations and fit naturally with the existing `Bus` and `Driver` models in the fleet app. This maintains good separation of concerns.

### 2. Stop Sequencing with `order` Field
**Decision:** Used a simple `order` integer field rather than auto-incrementing or enforcing strict sequential numbering (1, 2, 3...).

**Rationale:**
- Allows flexibility for future features (e.g., inserting stops between existing ones)
- Permits gaps in numbering (1, 5, 10) which can be useful for reordering
- Simpler to implement and maintain
- The important constraint is that order matches chronological sequence, not that numbers are sequential

### 3. Overlap Detection Approach
**Decision:** Used database aggregation with `Min()` and `Max()` on arrival times, rather than checking every stop individually.

**Rationale:**
- More efficient - single query per validation instead of N queries
- Correctly handles shifts with many stops
- Leverages Django ORM capabilities
- Scales better with growing data

### 4. Validation Location
**Decision:** Implemented validation in `BusShiftAdmin.save_related()` rather than model-level `clean()` methods.

**Rationale:**
- Stops must be saved before we can validate the complete shift
- `save_related()` runs after inlines are saved, giving us access to all stops
- Keeps validation logic centralized in the admin layer
- Appropriate for a 4-hour test scope

## How to Use

### Creating a Bus Shift

1. Navigate to Django admin: http://localhost:8000/admin
2. Click on "Bus Shifts" → "Add Bus Shift"
3. Select a bus from the dropdown
4. Select a driver from the dropdown
5. Add stops in the inline forms:
- Enter an order number (e.g., 0, 1, 2, or 1, 2, 3)
- Select a place
- Enter an arrival time
6. Click "Save"

**Tips:**
- You can add more stop forms by clicking "Add another Bus Stop"
- Make sure arrival times are in chronological order matching your sequence numbers
- The system will prevent you from assigning a bus or driver that's already busy during those times

### Editing a Bus Shift

1. Click on an existing shift in the list
2. Modify the bus, driver, or stops as needed
3. Add or remove stops using the inline forms
4. Click "Save"

**Note:** The system will validate that changes don't create overlapping assignments.

### Common Validation Errors

**"A bus shift must have at least 2 stops with arrival times"**
- Solution: Add at least 2 stops with valid arrival times

**"Stop sequence numbers must match chronological order..."**
- Solution: Ensure lower order numbers have earlier arrival times
- Example Fix: If Stop #2 is at 09:00 and Stop #1 is at 10:00, swap their order numbers

**"This bus is already assigned to an overlapping shift"**
- Solution: Choose a different bus or adjust the times to not overlap with existing shifts

**"This driver is already assigned to an overlapping shift"**
- Solution: Choose a different driver or adjust the times to not overlap

## What I Would Improve With More Time

### 1. Model-Level Validation
**Current:** Validation happens in admin's `save_related()`
**Improvement:** Add `clean()` method to `BusStop` model to validate chronological order at the model level. This would ensure validation runs regardless of how stops are created (admin, API, scripts, etc.).

```python
def clean(self):
# Validate this stop's time relative to previous and next stops
# Check against all stops with lower order numbers
# Check against all stops with higher order numbers
```

### 2. Past Date Prevention
**Current:** Allows creating shifts with dates in the past
**Improvement:** Add validation to prevent scheduling shifts in the past, or add a flag to distinguish between historical records and future planning.

**Business Discussion Needed:** Should the system allow historical data entry? Or only future scheduling?

### 3. Database Optimization
**Current:** No special indexes
**Improvements:**
- Add database index on `BusStop.arrival_time` for faster overlap queries
- Add index on `BusShift.bus` and `BusShift.driver` for faster lookups
- Consider composite index on `(bus_shift, order)` for stop queries

### 4. Enhanced Error Messages
**Current:** Generic error messages
**Improvement:** Show which specific shift is causing the conflict:
- "Bus ABC-123 is already assigned to Shift #42 (09:00-12:00)"
- Include clickable links to conflicting shifts

### 5. Unit Tests
**Current:** Manual testing only
**Improvement:** Add comprehensive test suite covering:
- Valid shift creation
- Validation edge cases (exact time matches, single stop, etc.)
- Overlap detection accuracy
- Chronological order enforcement
- Model properties (departure_time, duration, etc.)

Example test structure:
```python
class BusShiftTestCase(TestCase):
def test_create_valid_shift(self)
def test_minimum_two_stops_required(self)
def test_overlapping_bus_rejected(self)
def test_overlapping_driver_rejected(self)
def test_non_overlapping_shifts_allowed(self)
def test_chronological_order_enforced(self)
```

### 6. API Endpoints
**Current:** Admin interface only
**Improvement:** Add REST API using Django REST Framework for:
- Listing shifts (with filtering by date, bus, driver)
- Creating/updating shifts programmatically
- Retrieving shift details with nested stops
- Mobile app integration

### 7. Soft Delete
**Current:** Uses CASCADE on delete
**Improvement:** Implement soft delete to retain historical data:
- Add `is_deleted` flag and `deleted_at` timestamp
- Filter out deleted records in queries
- Allows data recovery and historical reporting

### 8. Admin UI Enhancements
**Current:** Basic tabular inline
**Improvements:**
- Drag-and-drop reordering of stops
- Auto-increment order numbers
- Map view showing route on a map
- Calendar view for shift scheduling
- Bulk operations (duplicate shift, cancel multiple shifts)
- Export shifts to CSV/PDF

### 9. Real-Time Conflict Detection
**Current:** Validation on save only
**Improvement:** AJAX-based real-time validation as user types:
- Show conflicts before attempting to save
- Suggest available buses/drivers for selected time range
- Visual timeline showing existing shifts

### 10. Additional Business Rules
**Potential improvements based on real-world requirements:**
- Maximum shift duration limits
- Minimum time between stops (travel time)
- Driver working hours regulations
- Bus maintenance scheduling integration
- Maximum stops per shift
- Geospatial validation (reasonable distances between consecutive stops)

## Technical Specifications

**Python Version:** 3.12.4 (compatible with 3.9+)
**Django Version:** 4.2.16
**Database:** SQLite (development), easily portable to PostgreSQL/MySQL for production

## Files Modified

- `padam_django/fleet/models.py` - Added BusShift and BusStop models
- `padam_django/fleet/admin.py` - Added admin configuration for shift management
- Database migrations in `padam_django/fleet/migrations/`

## Testing Checklist

- [x] Create valid shift with 2+ stops
- [x] Reject shift with < 2 stops
- [x] Reject shift with stops in wrong chronological order
- [x] Reject overlapping bus assignments
- [x] Reject overlapping driver assignments
- [x] Allow same bus at non-overlapping times
- [x] Allow same driver at non-overlapping times
- [x] Edit existing shift successfully
- [x] Add/remove stops from existing shift
- [x] Display calculated fields (departure, arrival, duration, stop count)
- [x] Search and filter functionality works
- [x] Clean error messages displayed to users

## Time Spent

**Total: ~3.5 hours**
- Setup & exploration: 30 minutes
- Model implementation: 45 minutes
- Business logic & validation: 60 minutes
- Admin interface: 45 minutes
- Testing & debugging: 30 minutes
- Documentation: 30 minutes

## Conclusion

This implementation provides a solid foundation for bus shift management with robust validation of business rules. The code is clean, well-documented, and follows Django best practices. The admin interface is user-friendly and provides all essential functionality for managing bus shifts.

The architecture is designed to be extensible, with clear separation of concerns and validation logic that can easily be enhanced or moved to the model layer as the application grows.
Loading