A Python Flask backend application for the WikiContest platform, converted from Node.js/Express to Python/Flask with SQLAlchemy ORM and MySQL database support.
- Overview
- Features
- Project Structure
- Prerequisites
- Installation
- Configuration
- Running the Application
- API Documentation
- Database Management
- Authentication & Authorization
- Development
- Production Deployment
- Troubleshooting
- Contributing
This backend provides a comprehensive API for managing Wikipedia article contests, including user authentication, contest creation, article submission, and jury review workflows. Built with Flask and SQLAlchemy, it offers a clean RESTful API with robust security features.
- User Management - Registration, login, logout, and profile management
- Contest Management - Create, view, update, and delete contests
- Submission System - Submit Wikipedia articles to contests and manage reviews
- Role-Based Access Control - Admin, creator, jury, and participant roles with granular permissions
- JWT Authentication - Secure token-based authentication with HTTP-only cookies
- MySQL Database - Production-ready database with SQLAlchemy ORM
- Database Migrations - Alembic for version-controlled schema management
- RESTful API - Clean, documented endpoints for frontend integration
- OAuth Support - Wikimedia OAuth 1.0a integration for Toolforge deployment
backend/
├── app/ # Main application package
│ ├── __init__.py # Application factory
│ ├── config.py # Configuration management
│ ├── database.py # SQLAlchemy database instance
│ ├── models/ # SQLAlchemy ORM models
│ │ ├── __init__.py
│ │ ├── base_model.py # Base model with common methods
│ │ ├── user.py # User model
│ │ ├── contest.py # Contest model
│ │ └── submission.py # Submission model
│ ├── routes/ # API route blueprints
│ │ ├── user_routes.py # User management endpoints
│ │ ├── contest_routes.py # Contest management endpoints
│ │ └── submission_routes.py # Submission management endpoints
│ ├── middleware/ # Middleware functions
│ │ └── auth.py # JWT and permission handling
│ └── utils/ # Utility functions
│ └── __init__.py
├── alembic/ # Database migration environment
│ ├── env.py # Alembic environment configuration
│ ├── versions/ # Migration version files
│ ├── script.py.mako # Migration template
│ └── README.md
├── scripts/ # Utility scripts
│ ├── init_db.py # Database initialization
│ ├── backfill_article_info.py # Backfill article metadata
│ └── get_article_metadata.py # Fetch article metadata
├── toolforge/ # Toolforge deployment files
│ ├── toolforge_app.py
│ ├── toolforge_config.toml
│ ├── toolforge_index.html
│ ├── toolforge_login.html
│ └── toolforge_requirements.txt
├── tests/ # Test files (pytest)
├── logs/ # Application logs
├── docs/ # Documentation
│ ├── ALEMBIC_USAGE_GUIDE.md
│ ├── ALEMBIC_MODEL_COMPATIBILITY.md
│ ├── ALEMBIC_SETUP_VERIFICATION.md
│ └── SETUP_NEW_DATABASE.md
├── main.py # Application entry point
├── alembic.ini # Alembic configuration
├── Makefile # Common commands
├── requirements.txt # Python dependencies
├── setup.py # Setup script
├── deploy_to_toolforge.sh # Deployment script
└── README.md # This file
- Application Factory Pattern - Enables multiple app instances with different configurations
- Blueprint Organization - Routes organized by domain (users, contests, submissions)
- Layered Configuration - Separate configs for development, testing, and production
- Centralized Database - Single SQLAlchemy instance with base model inheritance
Ensure you have the following installed:
- Python 3.8 or higher
- MySQL 5.7 or higher (or SQLite for development)
- pip Python package manager
cd backendpython -m venv .venvActivate the virtual environment:
# Windows
.venv\Scripts\activate
# macOS/Linux
source .venv/bin/activatepip install -r requirements.txtRun the following SQL commands to create the database and user:
CREATE DATABASE wikicontest;
CREATE USER 'wikicontest_user'@'localhost' IDENTIFIED BY 'your_password';
GRANT ALL PRIVILEGES ON wikicontest.* TO 'wikicontest_user'@'localhost';
FLUSH PRIVILEGES;Create a .env file in the backend directory with the following variables:
# Database Configuration
DATABASE_URL=mysql+pymysql://wikicontest_user:your_password@localhost/wikicontest
# Security Keys
SECRET_KEY=your_secret_key_here
JWT_SECRET_KEY=your_jwt_secret_key_here
# Environment
FLASK_ENV=developmentThis project uses Alembic exclusively for database schema management. All tables are created automatically through migrations.
Apply migrations to create the database schema:
# Apply all migrations
python -m alembic upgrade head
# Verify migrations were applied
python -m alembic currentImportant: Do not use init_db.py or any other scripts to create tables manually. Alembic handles all schema changes.
For detailed setup instructions, see docs/SETUP_NEW_DATABASE.md.
The Makefile provides convenient commands for common tasks:
# Run the development server
make run
# or
make dev
# View all available commands
make helpStart the Flask development server:
python main.pyOr use Flask directly:
flask runThe API will be available at:
- Base URL:
http://localhost:5000 - API endpoints:
http://localhost:5000/api/
For production environments, use a WSGI server like Gunicorn:
pip install gunicorn
gunicorn -w 4 -b 0.0.0.0:5000 "app:app"| Method | Endpoint | Description | Auth Required |
|---|---|---|---|
| POST | /api/user/register |
Register a new user | No |
| POST | /api/user/login |
Login user | No |
| POST | /api/user/logout |
Logout user | Yes |
| GET | /api/user/dashboard |
Get user dashboard | Yes |
| GET | /api/user/all |
Get all users | Admin only |
| GET | /api/user/profile |
Get user profile | Yes |
| PUT | /api/user/profile |
Update user profile | Yes |
| GET | /api/user/oauth/initiate |
Initiate OAuth login | No |
| GET | /api/user/oauth/callback |
OAuth callback handler | No |
| Method | Endpoint | Description | Auth Required |
|---|---|---|---|
| GET | /api/contest |
Get all contests | No |
| POST | /api/contest |
Create a new contest | Yes |
| GET | /api/contest/<id> |
Get contest by ID | No |
| PUT | /api/contest/<id> |
Update contest | Creator/Admin |
| DELETE | /api/contest/<id> |
Delete contest | Creator/Admin |
| GET | /api/contest/<id>/leaderboard |
Get contest leaderboard | No |
| POST | /api/contest/<id>/submit |
Submit to contest | Yes |
| GET | /api/contest/<id>/submissions |
Get contest submissions | Creator/Jury/Admin |
| Method | Endpoint | Description | Auth Required |
|---|---|---|---|
| GET | /api/submission |
Get all submissions | Admin only |
| GET | /api/submission/<id> |
Get submission by ID | Owner/Jury/Admin |
| PUT | /api/submission/<id> |
Update submission status | Jury/Admin |
| GET | /api/submission/user/<user_id> |
Get user submissions | Owner/Admin |
| GET | /api/submission/contest/<contest_id> |
Get contest submissions | Creator/Jury/Admin |
| GET | /api/submission/pending |
Get pending submissions | Jury/Admin |
| GET | /api/submission/stats |
Get submission statistics | Admin only |
| POST | /api/submission/contest/<contest_id>/refresh-metadata |
Refresh submission metadata | Creator/Admin |
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/cookie |
Check authentication status |
| GET | /api/health |
Health check |
| GET | /api/oauth/config |
OAuth configuration check |
| GET | /api/mediawiki/article-info |
Fetch article info from MediaWiki API |
| GET | /api/mediawiki/preview |
Get article preview from MediaWiki API |
id- Primary keyusername- Unique usernameemail- Unique email addressrole- User role (admin, user)password- Hashed passwordscore- Total accumulated scorecreated_at- Creation timestamp
id- Primary keyname- Contest nameproject_name- Associated project namecreated_by- Creator username (foreign key)description- Contest descriptionstart_date- Contest start dateend_date- Contest end daterules- JSON rules objectmarks_setting_accepted- Points for accepted submissionsmarks_setting_rejected- Points for rejected submissionsjury_members- Comma-separated jury usernamesallowed_submission_type- Type of submissions allowedcreated_at- Creation timestamp
id- Primary keyuser_id- User ID (foreign key)contest_id- Contest ID (foreign key)article_title- Article titlearticle_link- Article URLstatus- Submission status (pending, accepted, rejected)score- Awarded scoresubmitted_at- Submission timestamparticle_author- Author from latest revisionarticle_created_at- Article creation datearticle_word_count- Article size in bytesarticle_page_id- MediaWiki page IDarticle_size_at_start- Article size at contest startarticle_expansion_bytes- Bytes added since contest start
The application uses Alembic for database migrations, providing robust schema versioning and management.
# Create a new migration (after modifying models)
alembic revision --autogenerate -m "Description of changes"
# Apply migrations
alembic upgrade head
# Rollback to previous version
alembic downgrade -1
# View current migration status
alembic current
# View migration history
alembic history- Migration Files - Python files in
alembic/versions/define schema changes - Version Tracking -
alembic_versiontable stores current database version - Migration Chain - Each migration links to the previous one (linked list structure)
- Upgrade/Downgrade - Each migration has functions to apply and reverse changes
- Modify your models in
app/models/ - Generate migration:
make migrate-create MSG="Add new field" - Review the generated file in
alembic/versions/ - Apply migration:
make db-upgrade - Test your application
- Commit migration file to version control
For detailed documentation:
docs/ALEMBIC_USAGE_GUIDE.md- Complete usage guidedocs/ALEMBIC_MODEL_COMPATIBILITY.md- Model compatibilitydocs/ALEMBIC_SETUP_VERIFICATION.md- Setup verification
Located in the scripts/ directory:
Initialize or reset the database:
# Create tables
python scripts/init_db.py
# Reset database
python scripts/init_db.py resetBackfill article metadata for existing submissions:
python scripts/backfill_article_info.pyFetch article metadata from MediaWiki API:
python scripts/get_article_metadata.py "https://en.wikipedia.org/wiki/Article"The application implements JWT-based authentication with comprehensive security features.
- JWT Tokens - Stored in HTTP-only cookies for enhanced security
- CSRF Protection - Enabled for cookie-based authentication
- Role-Based Access - Admin, creator, jury, and participant roles
- Permission System - Contextual permissions based on contest relationships
- Middleware - Automatic authentication and authorization checks
- OAuth 1.0a - Wikimedia OAuth support for Toolforge deployment
Located in app/middleware/auth.py:
require_auth- Require valid JWT tokenrequire_role- Require specific user rolerequire_submission_permission- Require permission for submission accessvalidate_json_data- Validate JSON request datahandle_errors- Error handling decorator
Using Makefile:
# Run tests
make test
# Run tests with coverage report
make test-coverageDirect pytest commands:
# Install test dependencies
pip install pytest pytest-flask
# Run tests
pytestThe codebase follows Python PEP 8 standards with comprehensive comments and documentation. Linting is configured via .pylintrc.
When contributing:
- Follow PEP 8 style guidelines
- Add comprehensive comments and docstrings
- Write tests for new features
- Update documentation for API changes
- Use the modular structure (models, routes, middleware, utils)
- Keep files focused and under 200 lines when possible
Set production environment variables in .env:
# Environment
FLASK_ENV=production
FLASK_DEBUG=False
# Database
DATABASE_URL=mysql+pymysql://user:pass@host:port/db
# Security Keys (use strong, unique values)
SECRET_KEY=your_production_secret_key
JWT_SECRET_KEY=your_production_jwt_secret
# JWT Cookie Settings
JWT_COOKIE_SECURE=True
JWT_COOKIE_SAMESITE=NoneDeploy with Gunicorn for production:
# Install Gunicorn
pip install gunicorn
# Run with 4 worker processes
gunicorn -w 4 -b 0.0.0.0:5000 "app:app"Example Nginx configuration:
server {
listen 80;
server_name your-domain.com;
location / {
proxy_pass http://127.0.0.1:5000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}For Wikimedia Toolforge deployment:
- See the
toolforge/directory for deployment files - Use the
deploy_to_toolforge.shscript for automated deployment
The application uses Flask's application factory pattern (app/__init__.py), which allows:
- Multiple app instances with different configurations
- Easier testing with isolated app contexts
- Better code organization
Routes are organized into blueprints:
user_bp: User management endpointscontest_bp: Contest management endpointssubmission_bp: Submission management endpoints
Configuration is managed in app/config.py with separate classes for:
DevelopmentConfig: Development settingsTestingConfig: Test settingsProductionConfig: Production settings
- Models: SQLAlchemy ORM models in
app/models/ - Base Model: Common functionality in
app/models/base_model.py - Database Instance: Centralized in
app/database.py
Symptoms: Unable to connect to MySQL database
Solutions:
- Verify MySQL service is running
- Check database credentials in
.env - Ensure database exists
- Verify SQLAlchemy connection string format
Symptoms: ModuleNotFoundError or import failures
Solutions:
- Activate virtual environment
- Install all dependencies:
pip install -r requirements.txt - Ensure you're running from the backend directory
- Verify
app/package structure is correct - Check that all imports use
app.prefix (e.g.,from app.models import User)
Symptoms: Authentication failures, token validation errors
Solutions:
- Check
JWT_SECRET_KEYin environment variables - Ensure cookies are enabled in frontend
- Verify CORS settings allow credentials
- Check cookie domain and path settings
Symptoms: 403 Forbidden responses
Solutions:
- Verify user roles in database
- Check contest relationships for contextual permissions
- Review middleware decorators in routes
Symptoms: Python cannot find application modules
Solutions:
- Ensure all imports use
app.prefix - Check that
app/__init__.pyexists and is properly configured - Verify Python path includes the backend directory
Application logs are written to console by default.
For production:
- Configure logging in
app/__init__.py - Set up log rotation
- Use appropriate log levels (INFO, WARNING, ERROR)
- Log files are stored in the
logs/directory (created automatically)
We welcome contributions to the WikiContest platform!
- Code Style - Follow PEP 8 style guidelines
- Documentation - Add comprehensive comments and docstrings
- Testing - Write tests for new features
- API Changes - Update documentation when modifying endpoints
- Architecture - Use the modular structure (models, routes, middleware, utils)
- File Size - Keep files focused and under 200 lines when possible
This project is part of the WikiContest platform.