# models.py - CHUNK 4: ADD these models to your existing models.py file # ADD AFTER the Invoice class: class WHSAssessment(db.Model): __tablename__ = 'whs_assessments' # WHS Act 2011 compliance - MANDATORY FOR CONSTRUCTION id = db.Column(db.Integer, primary_key=True) job_id = db.Column(db.Integer, db.ForeignKey('jobs.id'), nullable=False) contract_id = db.Column(db.Integer, db.ForeignKey('contracts.id')) risk_assessment = db.Column(db.Text) safety_plan_accepted = db.Column(db.Boolean, default=False) incident_report = db.Column(db.Text) insurance_verified = db.Column(db.Boolean, default=False) completed_by = db.Column(db.Integer, db.ForeignKey('users.id')) created_at = db.Column(db.DateTime, default=datetime.utcnow) updated_at = db.Column(db.DateTime, default=datetime.utcnow) class Dispute(db.Model): __tablename__ = 'disputes' id = db.Column(db.Integer, primary_key=True) job_id = db.Column(db.Integer, db.ForeignKey('jobs.id'), nullable=False) complainant_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False) respondent_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False) dispute_type = db.Column(db.String(50)) # payment, quality, whs_breach, scope_change description = db.Column(db.Text) evidence_urls = db.Column(db.Text) status = db.Column(db.String(20), default='open') resolution = db.Column(db.Text) dispute_resolution_method = db.Column(db.String(50), default='mediation') resolved_by_admin = db.Column(db.Boolean, default=False) created_at = db.Column(db.DateTime, default=datetime.utcnow) resolved_at = db.Column(db.DateTime) class JobProgress(db.Model): __tablename__ = 'job_progress' id = db.Column(db.Integer, primary_key=True) job_id = db.Column(db.Integer, db.ForeignKey('jobs.id'), nullable=False) contract_id = db.Column(db.Integer, db.ForeignKey('contracts.id')) status = db.Column(db.String(20)) # assigned, in_progress, completed, cancelled start_date = db.Column(db.Date) actual_completion_date = db.Column(db.Date) progress_percentage = db.Column(db.Integer, default=0) worker_notes = db.Column(db.Text) contractor_notes = db.Column(db.Text) updated_by = db.Column(db.Integer, db.ForeignKey('users.id')) updated_at = db.Column(db.DateTime, default=datetime.utcnow) class Review(db.Model): __tablename__ = 'reviews' id = db.Column(db.Integer, primary_key=True) reviewer_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False) reviewee_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False) job_id = db.Column(db.Integer, db.ForeignKey('jobs.id')) rating = db.Column(db.Integer, nullable=False) # 1-5 stars comment = db.Column(db.Text) date_created = db.Column(db.DateTime, default=datetime.utcnow) class AuditLog(db.Model): __tablename__ = 'audit_logs' # Legal compliance - track all changes id = db.Column(db.Integer, primary_key=True) table_name = db.Column(db.String(50), nullable=False) record_id = db.Column(db.Integer, nullable=False) action = db.Column(db.String(20), nullable=False) # create, update, delete old_values = db.Column(db.Text) # JSON new_values = db.Column(db.Text) # JSON changed_by = db.Column(db.Integer, db.ForeignKey('users.id')) timestamp = db.Column(db.DateTime, default=datetime.utcnow) # database_setup.py - CHUNK 4: REPLACE your existing database_setup.py with this: import os from flask import Flask from models import db def create_database(): app = Flask(__name__) # SQLite configuration (stable, no SSL issues) basedir = os.path.abspath(os.path.dirname(__file__)) app.config['SQLALCHEMY_DATABASE_URI'] = f'sqlite:///{os.path.join(basedir, "rateright.db")}' app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False app.config['SECRET_KEY'] = 'rateright-legal-compliant-2025' # Initialize database db.init_app(app) with app.app_context(): # Create all tables db.create_all() print("✅ CHUNK 4: Safety compliance & trust tables created successfully!") # Verify table creation tables = db.engine.table_names() print(f"✅ Created {len(tables)} tables: {', '.join(tables)}") print("✅ Core Tables: users, categories, jobs, applications") print("✅ Legal Tables: contracts, payments, invoices") print("✅ Safety Tables: whs_assessments, disputes, job_progress, reviews, audit_logs") return app if __name__ == '__main__': create_database() # test_chunk4.py - CHUNK 4: NEW FILE - Create this new test file from database_setup import create_database from models import db, User, Category, Job, Application, Contract, Payment, Invoice, WHSAssessment, Dispute, JobProgress, Review, AuditLog from datetime import datetime, timedelta, date import json def test_chunk4(): app = create_database() with app.app_context(): print("🧪 Testing CHUNK 4: Safety Compliance & Trust Models...") # Clear existing data db.drop_all() db.create_all() # Create test users contractor = User( email='contractor@rateright.com.au', first_name='Emma', last_name='Taylor', role='contractor', phone_number='0412345678', location='Perth, WA', business_name='Taylor Construction', abn_number='12345678901', gst_registered=True ) contractor.set_password('password123') db.session.add(contractor) worker = User( email='worker@rateright.com.au', first_name='Jake', last_name='Wilson', role='worker', phone_number='0423456789', location='Perth, WA', primary_trade='Steelfixer', abn_number='98765432109', gst_registered=True, public_liability_insurance='QBE-PL-2025-001', public_liability_amount=20000000.00, jobs_completed=28, average_rating=4.9, total_reviews=22 ) worker.set_password('password123') db.session.add(worker) # Create category and job category = Category( name='Steelfixer', description='Steel reinforcement and rebar work', whs_risk_level='high', insurance_requirements='$20M public liability required' ) db.session.add(category) db.session.commit() job = Job( title='Steel Reinforcement - Basement', description='Steel fixing for basement construction. Complex rebar layout.', contractor_id=contractor.id, category_id=category.id, location='Perth, WA', budget_min=2000, budget_max=3000, whs_requirements='Manual handling training, cut-resistant gloves, site induction', insurance_required=True, white_card_required=True, status='in_progress' ) db.session.add(job) db.session.commit() # Create application and contract application = Application( job_id=job.id, worker_id=worker.id, status='accepted', proposed_rate=2800, cover_letter='Qualified steelfixer, can read complex plans.', abn_verified=True, insurance_verified=True ) db.session.add(application) db.session.commit() contract = Contract( job_id=job.id, application_id=application.id, contractor_id=contractor.id, worker_id=worker.id, agreed_rate=2800, start_date=date.today() - timedelta(days=5), expected_completion_date=date.today() + timedelta(days=10), terms_and_conditions='Standard ICA template with Fair Work compliance.', contractor_signed=True, worker_signed=True, status='signed' ) db.session.add(contract) db.session.commit() # Create WHS Assessment whs_assessment = WHSAssessment( job_id=job.id, contract_id=contract.id, risk_assessment='High risk: Steel fixing. Controls: PPE, lifting aids, manual handling training.', safety_plan_accepted=True, incident_report='No incidents reported.', insurance_verified=True, completed_by=contractor.id ) db.session.add(whs_assessment) # Create Job Progress job_progress = JobProgress( job_id=job.id, contract_id=contract.id, status='in_progress', start_date=date.today() - timedelta(days=5), progress_percentage=60, worker_notes='60% complete. Basement level 1 finished.', contractor_notes='Good progress, high quality work.', updated_by=worker.id ) db.session.add(job_progress) # Create Review review = Review( reviewer_id=contractor.id, reviewee_id=worker.id, job_id=job.id, rating=5, comment='Outstanding steelfixing work. Highly recommend Jake for complex projects.' ) db.session.add(review) # Create Test Dispute dispute = Dispute( job_id=job.id, complainant_id=worker.id, respondent_id=contractor.id, dispute_type='scope_change', description='Additional rebar work required due to structural changes.', evidence_urls='["/evidence/drawings_v2.pdf", "/evidence/photos_extra_work.jpg"]', status='investigating', dispute_resolution_method='mediation' ) db.session.add(dispute) # Create Audit Log audit_log = AuditLog( table_name='contracts', record_id=contract.id, action='create', old_values='{}', new_values=json.dumps({"status": "signed", "agreed_rate": "2800"}), changed_by=contractor.id ) db.session.add(audit_log) db.session.commit() # Verify counts user_count = User.query.count() job_count = Job.query.count() contract_count = Contract.query.count() whs_count = WHSAssessment.query.count() progress_count = JobProgress.query.count() review_count = Review.query.count() dispute_count = Dispute.query.count() audit_count = AuditLog.query.count() print(f"✅ Users created: {user_count}") print(f"✅ Jobs created: {job_count}") print(f"✅ Contracts created: {contract_count}") print(f"✅ WHS assessments: {whs_count}") print(f"✅ Job progress records: {progress_count}") print(f"✅ Reviews: {review_count}") print(f"✅ Disputes: {dispute_count}") print(f"✅ Audit logs: {audit_count}") # Test safety compliance workflow test_whs = WHSAssessment.query.first() test_progress = JobProgress.query.first() test_review = Review.query.first() test_dispute = Dispute.query.first() print(f"✅ WHS safety plan accepted: {test_whs.safety_plan_accepted}") print(f"✅ Insurance verified: {test_whs.insurance_verified}") print(f"✅ Job progress: {test_progress.progress_percentage}%") print(f"✅ Worker rating: {test_review.rating}/5 stars") print(f"✅ Dispute type: {test_dispute.dispute_type}") print(f"✅ Dispute status: {test_dispute.status}") # Test worker profile updates worker_profile = User.query.filter_by(role='worker').first() print(f"✅ Worker completed jobs: {worker_profile.jobs_completed}") print(f"✅ Worker average rating: {worker_profile.average_rating}") print(f"✅ Worker total reviews: {worker_profile.total_reviews}") print("✅ CHUNK 4 completed successfully!") print("✅ Safety workflow: WHS Assessment → Progress Tracking → Reviews") print("✅ WHS Act 2011 compliance ready!") print("✅ Trust system working (reviews + disputes)") print("✅ Audit trail for legal compliance!") print("📦 Ready for CHUNK 5...") if __name__ == '__main__': test_chunk4()