Implement complete multi-property architecture: - Properties (groups of spaces) with public/private visibility - Property managers (many-to-many) with role-based permissions - Organizations with member management - Anonymous/guest booking support via public API (/api/public/*) - Property-scoped spaces, bookings, and settings - Frontend: property selector, organization management, public booking views - Migration script and updated seed data Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
107 lines
4.2 KiB
Python
107 lines
4.2 KiB
Python
"""Migration script to add multi-property support to existing database."""
|
|
import sys
|
|
import os
|
|
|
|
# Add backend to path
|
|
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
|
|
|
from sqlalchemy import inspect, text
|
|
from app.db.session import Base, SessionLocal, engine
|
|
from app.models import (
|
|
Organization, OrganizationMember, Property, PropertyAccess,
|
|
PropertyManager, PropertySettings, User, Space,
|
|
)
|
|
|
|
|
|
def migrate():
|
|
"""Run migration to add multi-property tables and data."""
|
|
db = SessionLocal()
|
|
inspector = inspect(engine)
|
|
existing_tables = inspector.get_table_names()
|
|
|
|
print("Starting multi-property migration...")
|
|
|
|
# Step 1: Create all new tables
|
|
print("1. Creating new tables...")
|
|
Base.metadata.create_all(bind=engine)
|
|
print(" Tables created successfully.")
|
|
|
|
# Step 2: Add property_id column to spaces if not exists
|
|
space_columns = [col["name"] for col in inspector.get_columns("spaces")]
|
|
if "property_id" not in space_columns:
|
|
print("2. Adding property_id column to spaces...")
|
|
with engine.connect() as conn:
|
|
conn.execute(text("ALTER TABLE spaces ADD COLUMN property_id INTEGER REFERENCES properties(id)"))
|
|
conn.commit()
|
|
print(" Column added.")
|
|
else:
|
|
print("2. property_id column already exists in spaces.")
|
|
|
|
# Step 3: Add guest columns to bookings if not exists
|
|
booking_columns = [col["name"] for col in inspector.get_columns("bookings")]
|
|
with engine.connect() as conn:
|
|
if "guest_name" not in booking_columns:
|
|
print("3. Adding guest columns to bookings...")
|
|
conn.execute(text("ALTER TABLE bookings ADD COLUMN guest_name VARCHAR"))
|
|
conn.execute(text("ALTER TABLE bookings ADD COLUMN guest_email VARCHAR"))
|
|
conn.execute(text("ALTER TABLE bookings ADD COLUMN guest_organization VARCHAR"))
|
|
conn.execute(text("ALTER TABLE bookings ADD COLUMN is_anonymous BOOLEAN DEFAULT 0 NOT NULL"))
|
|
conn.commit()
|
|
print(" Guest columns added.")
|
|
else:
|
|
print("3. Guest columns already exist in bookings.")
|
|
|
|
# Step 4: Create "Default Property"
|
|
print("4. Creating Default Property...")
|
|
existing_default = db.query(Property).filter(Property.name == "Default Property").first()
|
|
if not existing_default:
|
|
default_prop = Property(
|
|
name="Default Property",
|
|
description="Default property for migrated spaces",
|
|
is_public=True,
|
|
is_active=True,
|
|
)
|
|
db.add(default_prop)
|
|
db.flush()
|
|
|
|
# Step 5: Migrate existing spaces to Default Property
|
|
print("5. Migrating existing spaces to Default Property...")
|
|
spaces_without_property = db.query(Space).filter(Space.property_id == None).all() # noqa: E711
|
|
for space in spaces_without_property:
|
|
space.property_id = default_prop.id
|
|
db.flush()
|
|
print(f" Migrated {len(spaces_without_property)} spaces.")
|
|
|
|
# Step 6: Rename admin users to superadmin
|
|
print("6. Updating admin roles to superadmin...")
|
|
admin_users = db.query(User).filter(User.role == "admin").all()
|
|
for u in admin_users:
|
|
u.role = "superadmin"
|
|
db.flush()
|
|
print(f" Updated {len(admin_users)} users.")
|
|
|
|
# Step 7: Create PropertyManager entries for superadmins
|
|
print("7. Creating PropertyManager entries for superadmins...")
|
|
superadmins = db.query(User).filter(User.role == "superadmin").all()
|
|
for sa in superadmins:
|
|
existing_pm = db.query(PropertyManager).filter(
|
|
PropertyManager.property_id == default_prop.id,
|
|
PropertyManager.user_id == sa.id,
|
|
).first()
|
|
if not existing_pm:
|
|
db.add(PropertyManager(property_id=default_prop.id, user_id=sa.id))
|
|
db.flush()
|
|
print(f" Created entries for {len(superadmins)} superadmins.")
|
|
|
|
db.commit()
|
|
print("\nMigration completed successfully!")
|
|
else:
|
|
print(" Default Property already exists. Skipping data migration.")
|
|
print("\nMigration already applied.")
|
|
|
|
db.close()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
migrate()
|