forked from jamesp/sasa-membership
632e66e21d
- Add configurable profile questions with conditional visibility, admin-only fields, user answers, and seeded onboarding/volunteer questions
- Add admin UI for managing profile questions and member profile answers
- Add volunteer level/profile data support across backend schemas, models, API, and migration
- Update dashboard/profile UI, super admin menu, membership service types, and related styling
- Add privacy policy, terms of service, cookie notice, and footer links
- Add frontend Vitest coverage for profile question logic
- Add backend pytest coverage for profile answer normalization and validation
- Update restart.sh to build, run frontend/backend unit tests, and restart only after tests pass
- Refresh README, quickstart, project structure, instructions, and Square docs to match current app features
- Protect feature flag reload behind super-admin access
- Restrict admin-triggered password resets so admins can only reset member accounts
- Replace email template HTML preview rendering with escaped text preview
- Update docs for feature flag reload access, password reset scope, and email template preview safety
-- test user questions are also made by AI and not very useful. but i didn't know what to put there so its good enough for a test
171 lines
6.9 KiB
Python
171 lines
6.9 KiB
Python
from sqlalchemy.orm import Session
|
|
import json
|
|
|
|
from ..models.models import MembershipTier, User, UserRole, EmailTemplate, ProfileQuestion
|
|
from .security import get_password_hash
|
|
from datetime import datetime
|
|
|
|
|
|
def init_default_data(db: Session):
|
|
"""Initialize database with default data if empty"""
|
|
|
|
# Check if membership tiers exist
|
|
existing_tiers = db.query(MembershipTier).count()
|
|
if existing_tiers == 0:
|
|
print("Creating default membership tiers...")
|
|
default_tiers = [
|
|
MembershipTier(
|
|
name="Personal",
|
|
description="Basic membership for individual members",
|
|
annual_fee=5.00,
|
|
benefits="Access to member portal, meeting notifications, event participation",
|
|
is_active=True
|
|
),
|
|
MembershipTier(
|
|
name="Aircraft Owners",
|
|
description="Group membership for aircraft owners",
|
|
annual_fee=25.00,
|
|
benefits="All Personal benefits plus priority event registration, aircraft owner resources",
|
|
is_active=True
|
|
),
|
|
MembershipTier(
|
|
name="Corporate",
|
|
description="Corporate membership for businesses",
|
|
annual_fee=100.00,
|
|
benefits="All benefits plus corporate recognition, promotional opportunities, file access",
|
|
is_active=True
|
|
)
|
|
]
|
|
db.add_all(default_tiers)
|
|
db.commit()
|
|
print(f"✓ Created {len(default_tiers)} default membership tiers")
|
|
|
|
# Check if admin user exists
|
|
admin_exists = db.query(User).filter(User.email == "admin@swanseaairport.org").first()
|
|
if not admin_exists:
|
|
print("Creating default admin user...")
|
|
admin_user = User(
|
|
email="admin@swanseaairport.org",
|
|
hashed_password=get_password_hash("admin123"),
|
|
first_name="System",
|
|
last_name="Administrator",
|
|
role=UserRole.SUPER_ADMIN,
|
|
is_active=True
|
|
)
|
|
db.add(admin_user)
|
|
db.commit()
|
|
print("✓ Created default admin user (admin@swanseaairport.org / admin123)")
|
|
print(" ⚠️ Remember to change the admin password!")
|
|
|
|
# Check if email templates exist
|
|
existing_templates = db.query(EmailTemplate).count()
|
|
if existing_templates == 0:
|
|
print("Creating default email templates...")
|
|
from ..services.email_service import get_default_templates
|
|
default_templates_data = get_default_templates()
|
|
|
|
default_templates = []
|
|
for template_data in default_templates_data:
|
|
template = EmailTemplate(**template_data)
|
|
default_templates.append(template)
|
|
|
|
db.add_all(default_templates)
|
|
db.commit()
|
|
print(f"✓ Created {len(default_templates)} default email templates")
|
|
|
|
# Seed default profile questions for onboarding and profile attributes
|
|
existing_questions = db.query(ProfileQuestion).count()
|
|
if existing_questions == 0:
|
|
print("Creating default profile questions...")
|
|
default_questions = [
|
|
ProfileQuestion(
|
|
key="has_professional_license",
|
|
label="Do you hold a professional aviation-related license?",
|
|
help_text="Select your current license status.",
|
|
input_type="select",
|
|
options_json=json.dumps([
|
|
{"label": "No", "value": "none"},
|
|
{"label": "Student", "value": "student"},
|
|
{"label": "Private Pilot", "value": "ppl"},
|
|
{"label": "Commercial Pilot", "value": "cpl"},
|
|
{"label": "ATPL", "value": "atpl"},
|
|
{"label": "Instructor", "value": "instructor"},
|
|
]),
|
|
is_required=False,
|
|
is_active=True,
|
|
admin_only_edit=False,
|
|
display_order=10,
|
|
),
|
|
ProfileQuestion(
|
|
key="license_number",
|
|
label="License number",
|
|
help_text="Optional: your current license number.",
|
|
input_type="text",
|
|
placeholder="e.g. UK.FCL.123456",
|
|
is_required=False,
|
|
is_active=True,
|
|
admin_only_edit=False,
|
|
display_order=20,
|
|
depends_on_value="ppl",
|
|
),
|
|
ProfileQuestion(
|
|
key="can_support_events",
|
|
label="Can you support airport or membership events?",
|
|
help_text="Choose yes if you're open to helping with events.",
|
|
input_type="boolean",
|
|
is_required=False,
|
|
is_active=True,
|
|
admin_only_edit=False,
|
|
display_order=30,
|
|
),
|
|
ProfileQuestion(
|
|
key="event_support_notes",
|
|
label="What support can you offer?",
|
|
help_text="Examples: stewarding, admin desk, setup/packdown, mentoring.",
|
|
input_type="text",
|
|
placeholder="Type details here",
|
|
is_required=False,
|
|
is_active=True,
|
|
admin_only_edit=False,
|
|
display_order=40,
|
|
depends_on_value="true",
|
|
),
|
|
ProfileQuestion(
|
|
key="hours_available_monthly",
|
|
label="Approximate volunteer hours available each month",
|
|
help_text="Optional estimate in hours.",
|
|
input_type="number",
|
|
is_required=False,
|
|
is_active=True,
|
|
admin_only_edit=False,
|
|
display_order=50,
|
|
),
|
|
ProfileQuestion(
|
|
key="medical_expiry_date",
|
|
label="Medical certificate expiry date",
|
|
help_text="Optional date in YYYY-MM-DD format.",
|
|
input_type="date",
|
|
is_required=False,
|
|
is_active=True,
|
|
admin_only_edit=False,
|
|
display_order=60,
|
|
),
|
|
ProfileQuestion(
|
|
key="completed_training_x",
|
|
label="Completed Training X",
|
|
help_text="This is set by admins once verified.",
|
|
input_type="boolean",
|
|
is_required=False,
|
|
is_active=True,
|
|
admin_only_edit=True,
|
|
display_order=70,
|
|
),
|
|
]
|
|
db.add_all(default_questions)
|
|
db.commit()
|
|
question_by_key = {question.key: question for question in db.query(ProfileQuestion).all()}
|
|
question_by_key["license_number"].depends_on_question_id = question_by_key["has_professional_license"].id
|
|
question_by_key["event_support_notes"].depends_on_question_id = question_by_key["can_support_events"].id
|
|
db.commit()
|
|
print(f"✓ Created {len(default_questions)} default profile questions")
|