|
|
""" |
|
|
Migration 002: Create Assignments Table |
|
|
Creates the assignments table for managing order-driver assignments with route data |
|
|
""" |
|
|
|
|
|
import sys |
|
|
import os |
|
|
|
|
|
|
|
|
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))) |
|
|
|
|
|
from database.connection import get_db_connection |
|
|
|
|
|
MIGRATION_SQL = """ |
|
|
-- Create assignments table with enhanced route tracking |
|
|
CREATE TABLE IF NOT EXISTS assignments ( |
|
|
assignment_id VARCHAR(50) PRIMARY KEY, |
|
|
order_id VARCHAR(50) NOT NULL, |
|
|
driver_id VARCHAR(50) NOT NULL, |
|
|
|
|
|
-- Assignment metadata |
|
|
assigned_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, |
|
|
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, |
|
|
sequence_number INTEGER, |
|
|
|
|
|
-- Route data from Google Routes API |
|
|
route_distance_meters INTEGER, |
|
|
route_duration_seconds INTEGER, |
|
|
route_duration_in_traffic_seconds INTEGER, |
|
|
route_summary TEXT, |
|
|
route_polyline TEXT, |
|
|
|
|
|
-- Origin/destination tracking |
|
|
driver_start_location_lat DECIMAL(10, 8), |
|
|
driver_start_location_lng DECIMAL(11, 8), |
|
|
delivery_location_lat DECIMAL(10, 8), |
|
|
delivery_location_lng DECIMAL(11, 8), |
|
|
delivery_address TEXT, |
|
|
|
|
|
-- Timing data |
|
|
estimated_arrival TIMESTAMP, |
|
|
actual_arrival TIMESTAMP, |
|
|
|
|
|
-- Actual vs estimated tracking |
|
|
actual_distance_meters INTEGER, |
|
|
|
|
|
-- Vehicle context |
|
|
vehicle_type VARCHAR(50), |
|
|
|
|
|
-- Status management |
|
|
status VARCHAR(20) CHECK(status IN ('active', 'in_progress', 'completed', 'cancelled', 'failed')) DEFAULT 'active', |
|
|
|
|
|
-- Additional metadata |
|
|
traffic_delay_seconds INTEGER, |
|
|
weather_conditions JSONB, |
|
|
route_confidence VARCHAR(100), |
|
|
notes TEXT, |
|
|
|
|
|
-- Foreign keys with proper cascade behavior |
|
|
FOREIGN KEY (order_id) REFERENCES orders(order_id) ON DELETE CASCADE, |
|
|
FOREIGN KEY (driver_id) REFERENCES drivers(driver_id) ON DELETE RESTRICT |
|
|
); |
|
|
|
|
|
-- Create indexes for performance |
|
|
CREATE INDEX IF NOT EXISTS idx_assignments_driver ON assignments(driver_id); |
|
|
CREATE INDEX IF NOT EXISTS idx_assignments_order ON assignments(order_id); |
|
|
CREATE INDEX IF NOT EXISTS idx_assignments_status ON assignments(status); |
|
|
CREATE INDEX IF NOT EXISTS idx_assignments_assigned_at ON assignments(assigned_at); |
|
|
|
|
|
-- Create trigger for auto-updating updated_at |
|
|
CREATE OR REPLACE FUNCTION update_updated_at_column() |
|
|
RETURNS TRIGGER AS $$ |
|
|
BEGIN |
|
|
NEW.updated_at = CURRENT_TIMESTAMP; |
|
|
RETURN NEW; |
|
|
END; |
|
|
$$ LANGUAGE plpgsql; |
|
|
|
|
|
CREATE TRIGGER update_assignments_timestamp |
|
|
BEFORE UPDATE ON assignments |
|
|
FOR EACH ROW |
|
|
EXECUTE FUNCTION update_updated_at_column(); |
|
|
|
|
|
-- Add unique constraint to prevent multiple active assignments per order |
|
|
CREATE UNIQUE INDEX IF NOT EXISTS idx_assignments_unique_active_order |
|
|
ON assignments(order_id) |
|
|
WHERE status IN ('active', 'in_progress'); |
|
|
""" |
|
|
|
|
|
ROLLBACK_SQL = """ |
|
|
DROP INDEX IF EXISTS idx_assignments_unique_active_order; |
|
|
DROP TRIGGER IF EXISTS update_assignments_timestamp ON assignments; |
|
|
DROP INDEX IF EXISTS idx_assignments_assigned_at; |
|
|
DROP INDEX IF EXISTS idx_assignments_status; |
|
|
DROP INDEX IF EXISTS idx_assignments_order; |
|
|
DROP INDEX IF EXISTS idx_assignments_driver; |
|
|
DROP TABLE IF EXISTS assignments CASCADE; |
|
|
""" |
|
|
|
|
|
|
|
|
def up(): |
|
|
"""Apply migration - create assignments table""" |
|
|
print("Running migration 002: Create assignments table...") |
|
|
|
|
|
try: |
|
|
conn = get_db_connection() |
|
|
cursor = conn.cursor() |
|
|
|
|
|
|
|
|
cursor.execute(MIGRATION_SQL) |
|
|
|
|
|
conn.commit() |
|
|
cursor.close() |
|
|
conn.close() |
|
|
|
|
|
print("SUCCESS: Migration 002 applied successfully") |
|
|
print(" - Created assignments table") |
|
|
print(" - Created indexes (driver, order, status, assigned_at)") |
|
|
print(" - Created update trigger") |
|
|
print(" - Created unique constraint for active assignments per order") |
|
|
return True |
|
|
|
|
|
except Exception as e: |
|
|
print(f"ERROR: Migration 002 failed: {e}") |
|
|
return False |
|
|
|
|
|
|
|
|
def down(): |
|
|
"""Rollback migration - drop assignments table""" |
|
|
print("Rolling back migration 002: Drop assignments table...") |
|
|
|
|
|
try: |
|
|
conn = get_db_connection() |
|
|
cursor = conn.cursor() |
|
|
|
|
|
|
|
|
cursor.execute(ROLLBACK_SQL) |
|
|
|
|
|
conn.commit() |
|
|
cursor.close() |
|
|
conn.close() |
|
|
|
|
|
print("SUCCESS: Migration 002 rolled back successfully") |
|
|
return True |
|
|
|
|
|
except Exception as e: |
|
|
print(f"ERROR: Migration 002 rollback failed: {e}") |
|
|
return False |
|
|
|
|
|
|
|
|
if __name__ == "__main__": |
|
|
import sys |
|
|
|
|
|
if len(sys.argv) > 1 and sys.argv[1] == "down": |
|
|
down() |
|
|
else: |
|
|
up() |
|
|
|