- Help.tsx: role tabs, TOC scroll-spy, search, lightbox, react-markdown renderer
- 7 markdown guides (global, client, linguist, reviewer, production, PM, admin)
with explicit click/drag/keyboard annotations throughout
- Sidebar: Help button added at bottom of nav (all roles)
- App.tsx: /help route, no RoleGate
- frontend/public/help-screenshots/{role}/: directories ready for screenshots
- tools/capture-help-screenshots.ts: Playwright screenshot script
- Clicks "Local login" toggle before filling credentials
- Uses test-admin local account (not SSO)
- backend/scripts/seed_test_users.py: idempotent MongoDB seed script
creates 6 local-auth users (admin + 5 roles) for capture + local dev
- .env.screenshots.example: template with test-admin credentials
- Removes docs/video_accessibility_user_guide_v3.md (superseded by in-app guides)
- Deps: react-markdown, remark-gfm, rehype-raw added to frontend
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
85 lines
2.9 KiB
Python
85 lines
2.9 KiB
Python
"""Seed test users for screenshot capture and local development.
|
|
|
|
Run from backend/ directory (with MONGODB_URI set in environment or .env):
|
|
|
|
python scripts/seed_test_users.py
|
|
|
|
Or inside a running Docker container:
|
|
|
|
docker compose exec backend python scripts/seed_test_users.py
|
|
|
|
Idempotent — skips users that already exist (case-insensitive email match).
|
|
"""
|
|
|
|
import asyncio
|
|
import os
|
|
import re
|
|
import sys
|
|
from datetime import datetime
|
|
|
|
from bson import ObjectId
|
|
from motor.motor_asyncio import AsyncIOMotorClient
|
|
from passlib.context import CryptContext
|
|
|
|
# Load .env if dotenv is available (optional convenience for local runs)
|
|
try:
|
|
from dotenv import load_dotenv
|
|
load_dotenv()
|
|
except ImportError:
|
|
pass
|
|
|
|
MONGODB_URI = os.environ.get("MONGODB_URI") or os.environ.get("MONGO_URI")
|
|
MONGODB_DB = os.environ.get("MONGODB_DB", "accessible_video")
|
|
|
|
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
|
|
|
|
TEST_USERS = [
|
|
{"email": "test-admin@oliver.agency", "password": "TestAdmin2026!", "full_name": "Test Admin", "role": "admin"},
|
|
{"email": "test-client@oliver.agency", "password": "TestClient2026!", "full_name": "Test Client", "role": "client"},
|
|
{"email": "test-linguist@oliver.agency", "password": "TestLinguist2026!", "full_name": "Test Linguist", "role": "linguist"},
|
|
{"email": "test-reviewer@oliver.agency", "password": "TestReviewer2026!", "full_name": "Test Reviewer", "role": "reviewer"},
|
|
{"email": "test-production@oliver.agency", "password": "TestProduction2026!", "full_name": "Test Production", "role": "production"},
|
|
{"email": "test-pm@oliver.agency", "password": "TestPM2026!", "full_name": "Test Project Manager","role": "project_manager"},
|
|
]
|
|
|
|
|
|
async def main() -> None:
|
|
if not MONGODB_URI:
|
|
print("ERROR: MONGODB_URI env var is not set.", file=sys.stderr)
|
|
sys.exit(1)
|
|
|
|
client = AsyncIOMotorClient(MONGODB_URI)
|
|
db = client[MONGODB_DB]
|
|
|
|
created = 0
|
|
skipped = 0
|
|
|
|
for u in TEST_USERS:
|
|
pattern = re.compile(f"^{re.escape(u['email'])}$", re.IGNORECASE)
|
|
existing = await db.users.find_one({"email": pattern})
|
|
if existing:
|
|
print(f" skip {u['email']} (already exists)")
|
|
skipped += 1
|
|
continue
|
|
|
|
doc = {
|
|
"_id": str(ObjectId()),
|
|
"email": u["email"],
|
|
"hashed_password": pwd_context.hash(u["password"]),
|
|
"full_name": u["full_name"],
|
|
"role": u["role"],
|
|
"auth_provider": "local",
|
|
"is_active": True,
|
|
"created_at": datetime.utcnow(),
|
|
"updated_at": datetime.utcnow(),
|
|
}
|
|
await db.users.insert_one(doc)
|
|
print(f" created {u['email']} ({u['role']})")
|
|
created += 1
|
|
|
|
client.close()
|
|
print(f"\nDone — {created} created, {skipped} skipped.")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
asyncio.run(main())
|