# app/models/__init__.py - Models Package from .base import BaseModel from .user import User from .category import Category # Import all models for relationship setup __all__ = ['BaseModel', 'User', 'Category'] # app/models/base.py - Base Model Class from datetime import datetime from ..extensions import db class BaseModel(db.Model): """Base model with common fields for all RateRight models""" __abstract__ = True id = db.Column(db.Integer, primary_key=True) created_at = db.Column(db.DateTime, default=datetime.utcnow, nullable=False) updated_at = db.Column(db.DateTime, default=datetime.utcnow, onupdate=datetime.utcnow, nullable=False) def to_dict(self): """Convert model to dictionary for JSON serialization""" return {c.name: getattr(self, c.name) for c in self.__table__.columns} # app/models/user.py - User Model (Workers & Contractors) from datetime import datetime, date from werkzeug.security import generate_password_hash, check_password_hash from ..extensions import db from .base import BaseModel class User(BaseModel): """User model for Australian construction marketplace workers and contractors""" __tablename__ = 'users' # Basic Information email = db.Column(db.String(120), unique=True, nullable=False, index=True) password_hash = db.Column(db.String(255), nullable=False) first_name = db.Column(db.String(80), nullable=False) last_name = db.Column(db.String(80), nullable=False) role = db.Column(db.String(20), nullable=False) # 'worker' or 'contractor' # Contact Information phone_number = db.Column(db.String(20), nullable=False) location = db.Column(db.String(200), nullable=False) # Australian Business Information (Legal Compliance) business_name = db.Column(db.String(200)) primary_trade = db.Column(db.String(100)) abn_number = db.Column(db.String(11), unique=True, nullable=False, index=True) gst_registered = db.Column(db.Boolean, default=False, nullable=False) # Work Statistics & Performance jobs_completed = db.Column(db.Integer, default=0, nullable=False) average_rating = db.Column(db.Numeric(3, 2), default=0.0) total_reviews = db.Column(db.Integer, default=0, nullable=False) response_rate = db.Column(db.Numeric(5, 2), default=0.0) # Australian Insurance & Compliance Requirements public_liability_insurance = db.Column(db.Boolean, default=False, nullable=False) public_liability_amount = db.Column(db.Numeric(12, 2)) workers_comp_insurance = db.Column(db.Boolean, default=False, nullable=False) insurance_expiry_date = db.Column(db.Date) # WHS (Workplace Health & Safety) Compliance worker_control_level = db.Column(db.String(50)) white_card_number = db.Column(db.String(50)) white_card_expiry = db.Column(db.Date) # Account Management is_active = db.Column(db.Boolean, default=True, nullable=False) account_status = db.Column(db.String(20), default='active', nullable=False) privacy_consent = db.Column(db.Boolean, default=False, nullable=False) terms_accepted = db.Column(db.Boolean, default=False, nullable=False) terms_accepted_date = db.Column(db.DateTime) date_created = db.Column(db.DateTime, default=datetime.utcnow, nullable=False) last_login = db.Column(db.DateTime) # Gamification System (80/20 engagement) total_points = db.Column(db.Integer, default=0, nullable=False) current_level = db.Column(db.Integer, default=1, nullable=False) seasonal_league = db.Column(db.String(20), default='bronze') def set_password(self, password): """Set password hash for secure authentication""" self.password_hash = generate_password_hash(password) def check_password(self, password): """Check password against stored hash""" return check_password_hash(self.password_hash, password) def is_compliance_valid(self): """Check if user meets Australian construction industry compliance""" issues = [] # ABN validation if not self.abn_number or len(self.abn_number) != 11: issues.append("Valid 11-digit ABN required") # Insurance requirements if not self.public_liability_insurance: issues.append("Public liability insurance required") if self.public_liability_amount and self.public_liability_amount < 1000000: issues.append("Minimum $1M public liability insurance required") # Insurance expiry check if self.insurance_expiry_date and self.insurance_expiry_date <= date.today(): issues.append("Insurance has expired") # Terms and privacy if not self.privacy_consent: issues.append("Privacy consent required") if not self.terms_accepted: issues.append("Terms acceptance required") return len(issues) == 0, issues def get_gamification_level(self): """Calculate user level based on points (500 points per level)""" return (self.total_points // 500) + 1 def points_to_next_level(self): """Calculate points needed for next level""" current_level_points = (self.current_level - 1) * 500 next_level_points = self.current_level * 500 return next_level_points - self.total_points def __repr__(self): return f'' # app/models/category.py - Construction Trade Categories from ..extensions import db from .base import BaseModel class Category(BaseModel): """Australian construction trade categories with WHS risk levels""" __tablename__ = 'categories' name = db.Column(db.String(100), unique=True, nullable=False) description = db.Column(db.Text) # WHS Act 2011 Risk Classification whs_risk_level = db.Column(db.String(20), default='medium', nullable=False) # Risk levels: 'low', 'medium', 'high', 'extreme' # Australian Construction Industry Requirements insurance_requirements = db.Column(db.Text) license_required = db.Column(db.Boolean, default=False, nullable=False) white_card_required = db.Column(db.Boolean, default=True, nullable=False) # Category Management is_active = db.Column(db.Boolean, default=True, nullable=False) sort_order = db.Column(db.Integer, default=0, nullable=False) def get_insurance_minimum(self): """Get minimum insurance amount based on risk level""" minimums = { 'low': 1000000, # $1M 'medium': 2000000, # $2M 'high': 5000000, # $5M 'extreme': 10000000 # $10M } return minimums.get(self.whs_risk_level, 2000000) def __repr__(self): return f'' # app/utils/__init__.py - Utilities Package """Utility functions for RateRight construction marketplace""" # Import utilities (will be added in future chunks) __all__ = [] # app/utils/validators.py - Australian Business Validation def validate_abn(abn): """Basic ABN validation (Australian Business Number)""" if not abn or len(abn) != 11: return False return abn.isdigit() def validate_australian_business_number(abn): """ Validate ABN using the official 88/99 weighting method As per Australian Taxation Office specifications """ if not abn or len(abn) != 11 or not abn.isdigit(): return False # ATO weighting factors weights = [10, 1, 3, 5, 7, 9, 11, 13, 15, 17, 19] total = 0 for i, digit in enumerate(abn): digit = int(digit) if i == 0: digit -= 1 # Subtract 1 from first digit total += digit * weights[i] return total % 89 == 0 def validate_phone_number(phone): """Validate Australian phone number format""" if not phone: return False # Remove spaces and common formatting clean_phone = phone.replace(' ', '').replace('-', '').replace('(', '').replace(')', '') # Australian mobile: 04xx xxx xxx or landline: 0x xxxx xxxx if clean_phone.startswith('04') and len(clean_phone) == 10: return True if clean_phone.startswith('0') and len(clean_phone) == 10: return True return False # test_chunk2.py - Test Core Models """ Test file for Chunk 2: Core Models Run this to verify User and Category models work correctly """ def test_chunk2(): """Test core models implementation""" print("Testing Chunk 2: Core Models") try: # Test imports from app import create_app from app.models import User, Category, BaseModel from app.utils.validators import validate_abn, validate_australian_business_number print("✅ All imports successful") # Create app context for database operations app = create_app() with app.app_context(): from app.extensions import db # Create tables db.create_all() print("✅ Database tables created") # Test User model user = User( email="test.contractor@example.com", first_name="Test", last_name="User", role="contractor", phone_number="0412345678", location="Sydney, NSW", abn_number="12345678901", business_name="Test Construction Pty Ltd" ) user.set_password("testpass123") db.session.add(user) db.session.commit() print("✅ User model works") # Test password checking assert user.check_password("testpass123") == True assert user.check_password("wrongpass") == False print("✅ Password hashing works") # Test Category model category = Category( name="General Construction", description="General building and construction work", whs_risk_level="medium", insurance_requirements="Public liability $2M minimum" ) db.session.add(category) db.session.commit() print("✅ Category model works") # Test ABN validation assert validate_abn("12345678901") == True assert validate_abn("123") == False print("✅ ABN validation works") # Test compliance checking is_compliant, issues = user.is_compliance_valid() print(f"✅ Compliance check works (compliant: {is_compliant})") print("\n🎉 CHUNK 2 COMPLETE - Core models working!") print("📊 Database tables: users, categories") print("🇦🇺 Australian compliance: ABN, insurance, WHS") print("🎮 Gamification: points, levels ready") return True except Exception as e: print(f"❌ Error: {e}") import traceback traceback.print_exc() return False if __name__ == "__main__": test_chunk2()