From b8daba25cfb64be1aa474ebe36c3233b4de12d10 Mon Sep 17 00:00:00 2001 From: Akram Zaabi Date: Mon, 30 Mar 2026 13:13:08 +0100 Subject: [PATCH] docs: improve setup instructions, add .env.sample and basic test --- .env.sample | 106 ++++++++----- .github/copilot-setup-steps.yml | 11 +- README.md | 261 ++++++++++++-------------------- web/tests/test_env_sample.py | 36 +++++ 4 files changed, 208 insertions(+), 206 deletions(-) create mode 100644 web/tests/test_env_sample.py diff --git a/.env.sample b/.env.sample index 275d11499..7ee132e56 100644 --- a/.env.sample +++ b/.env.sample @@ -1,47 +1,71 @@ -DEBUG=False +# Core Django settings +# Use "development" for local work. Production/staging deployments should override this. ENVIRONMENT=development -SECRET_KEY=your-secret-key-here -DATABASE_URL=sqlite:///db.sqlite3 -EMAIL_FROM=admin@example.com -SENDGRID_API_KEY=your-sendgrid-api-key -SENDGRID_PASSWORD=your-sendgrid-password -SLACK_WEBHOOK_URL=your-slack-webhook-url + +# Replace this in shared or deployed environments. +SECRET_KEY=django-insecure-change-me + +# Must be a valid Fernet key. Generate one with: python web/master_key.py MESSAGE_ENCRYPTION_KEY=5ezrkqK2lhifqBRt9f8_dZhFQF_f5ipSQDV8Vzv9Dek= -# Generate a key by running: python web/master_key.py -# Stripe Configuration -STRIPE_PUBLISHABLE_KEY=your-stripe-publishable-key -STRIPE_SECRET_KEY=your-stripe-secret-key -STRIPE_WEBHOOK_SECRET=your-stripe-webhook-secret -DJANGO_DEBUG=True +# Comma-separated hostnames/origins used by Django security checks. +ALLOWED_HOSTS=127.0.0.1,localhost +CSRF_TRUSTED_ORIGINS=http://127.0.0.1:8000,http://localhost:8000 -# Admin Configuration +# The admin is mounted at /en//. ADMIN_URL=a-dmin-url123 -# Google Cloud Storage Configuration -GS_BUCKET_NAME=your-bucket-name -GS_PROJECT_ID=your-project-id -SERVICE_ACCOUNT_FILE=your-service-account-file-path - -#Twitter Configuration -TWITTER_API_KEY=your_api_key_here -TWITTER_API_SECRET_KEY=your_api_secret_here -TWITTER_ACCESS_TOKEN=your_access_token_here -TWITTER_ACCESS_TOKEN_SECRET=your_access_token_secret_here - -#Deployment Configuration -APP_PORT=8000 -DOMAIN_NAME=yourdomain.com -ENABLE_HTTPS_REDIRECT=True -PROJECT_NAME=education-website -PYTHON_VERSION=3.11 -VPS_IP=your-vps-ip -VPS_PASSWORD=your-vps-password -VPS_USER=root -DB_NAME=education_website -DB_USER=root -DB_PASSWORD=your_secure_database_password -GIT_REPO=your_git_repo_to_deploy -GIT_BRANCH=your_repo_branch -ALLOWED_HOSTS=example.com,www.example.com,127.0.0.1,localhost -CSRF_TRUSTED_ORIGINS=http://domain.com +# Local services +# Keep the SQLite value for the simplest local setup. +DATABASE_URL=sqlite:///db.sqlite3 +REDIS_URL=redis://127.0.0.1:6379/0 +MEDIA_ROOT=media +CACHE_MIDDLEWARE_SECONDS=300 +CACHE_MIDDLEWARE_KEY_PREFIX=aol +CSRF_COOKIE_SECURE=False +SESSION_COOKIE_SECURE=False + +# Email and notifications +EMAIL_FROM=noreply@example.com +SLACK_WEBHOOK_URL= +EMAIL_SLACK_WEBHOOK= +MAILGUN_SENDING_KEY= +MAILGUN_DOMAIN= + +# Legacy system status check; only needed if you still use SendGrid. +SENDGRID_PASSWORD= + +# Billing +STRIPE_PUBLISHABLE_KEY= +STRIPE_SECRET_KEY= +STRIPE_WEBHOOK_SECRET= + +# Monitoring +SENTRY_DSN= +SENTRY_LOG_LEVEL=INFO +SENTRY_TRACES_SAMPLE_RATE=0.0 + +# Content and social integrations +MAILCHIMP_API_KEY= +MAILCHIMP_LIST_ID= +INSTAGRAM_ACCESS_TOKEN= +FACEBOOK_ACCESS_TOKEN= +TWITTER_USERNAME=alphaonelabs +TWITTER_API_KEY= +TWITTER_API_SECRET_KEY= +TWITTER_ACCESS_TOKEN= +TWITTER_ACCESS_TOKEN_SECRET= +YOUTUBE_API_KEY= +YOUTUBE_CHANNEL_ID= + +# GitHub integrations +GITHUB_ACCESS_TOKEN= +GITHUB_TOKEN= +GITHUB_REPO=AlphaOneLabs/education-website +GITHUB_WEBHOOK_SECRET= + +# Google Cloud Storage +# Leave these blank unless you are testing cloud media storage. +GS_BUCKET_NAME= +GS_PROJECT_ID= +SERVICE_ACCOUNT_FILE=google_credentials.json diff --git a/.github/copilot-setup-steps.yml b/.github/copilot-setup-steps.yml index 180f0bc91..73a5f2bac 100644 --- a/.github/copilot-setup-steps.yml +++ b/.github/copilot-setup-steps.yml @@ -127,10 +127,11 @@ setup_steps: name: "Run Development Server" description: "Start the Django development server" commands: - - "python manage.py runserver" + - "poetry run uvicorn web.asgi:application --host 127.0.0.1 --port 8000 --reload" notes: - - "Server runs at http://localhost:8000" - - "Admin panel available at http://localhost:8000/admin" + - "Server runs at http://127.0.0.1:8000/en/" + - "Use Uvicorn for local WebSocket support" + - "Admin panel path is controlled by ADMIN_URL in .env" - "Press Ctrl+C to stop the server" docker_setup: @@ -211,7 +212,7 @@ tech_stack: - "Nginx" - "Uvicorn (ASGI)" - "Django Channels (WebSockets)" - - "SendGrid for emails" + - "Mailgun with Slack notification fallback" - "Stripe for payments" important_notes: @@ -225,7 +226,7 @@ important_notes: useful_commands: development: - run_server: "python manage.py runserver" + run_server: "poetry run uvicorn web.asgi:application --host 127.0.0.1 --port 8000 --reload" make_migrations: "python manage.py makemigrations" migrate: "python manage.py migrate" create_superuser: "python manage.py createsuperuser" diff --git a/README.md b/README.md index e50b5aaca..ef3f61b80 100644 --- a/README.md +++ b/README.md @@ -1,226 +1,167 @@ # Alpha One Labs Education Platform -A modern, feature-rich education platform built with Django and Tailwind CSS that enables seamless learning experiences through course creation, peer connections, study groups, and interactive forums. - -## Project Overview - -Alpha One Labs is an education platform designed to facilitate both learning and teaching. The platform provides a comprehensive environment where educators can create and manage courses, while students can learn, collaborate, and engage with peers. With features like study groups, peer connections, and discussion forums, we aim to create a collaborative learning environment that goes beyond traditional online education. - -## Features - -### For Students - -- 📚 Course enrollment and management -- 👥 Peer-to-peer connections and messaging -- 📝 Study group creation and participation -- 💬 Interactive discussion forums -- 📊 Progress tracking and analytics -- 🌟 Submit links and receive grades with feedback -- 🌙 Dark mode support -- 📱 Responsive design for all devices - -### For Teachers - -- 📝 Course creation and management -- 📊 Student progress monitoring -- 📈 Analytics dashboard -- 📣 Marketing tools for course promotion -- 💯 Grade submitted links and provide feedback -- 💰 Payment integration with Stripe -- 📧 Email marketing capabilities -- 🔔 Automated notifications - -### Technical Features - -- 🔒 Secure authentication system -- 🌐 Internationalization support -- 🚀 Performance optimized -- 📦 Modular architecture -- ⚡ Real-time updates -- 🔍 Search functionality -- 🎨 Customizable UI -- 🏆 "Get a Grade" system with academic grading scale +Alpha One Labs is a Django-based learning platform for running courses, managing enrollments, supporting peer collaboration, and powering interactive community features such as forums, study groups, progress tracking, and virtual labs. ## Tech Stack -### Backend - - Python 3.10+ - Django 5.x -- Redis (channels + caching) -- MySQL (production) / SQLite (development optional) - -### Frontend +- Django Channels + Uvicorn for ASGI/WebSockets +- Redis for channels and shared cache +- SQLite for local development, MySQL for production-like environments +- Poetry for dependency management -- Tailwind CSS -- Alpine.js -- Font Awesome icons -- JavaScript (Vanilla) - -### Infrastructure - -- Docker support -- Nginx -- Uvicorn (ASGI) -- Django Channels (WebSockets) -- SendGrid for emails (graceful fallback) -- Stripe for payments - -## Setup Instructions +## Quick Start ### Prerequisites -- Python 3.10 or higher -- pip or poetry for package management +- Python 3.10 or newer - Git +- Poetry 1.8.3 +- Redis (optional for local real-time features) +- MySQL (optional; SQLite works out of the box for local development) -### Local Development Setup +### 1. Clone the repository + +```bash +git clone https://github.com/alphaonelabs/alphaonelabs-education-website.git +cd alphaonelabs-education-website +``` -1. Clone the repository: +### 2. Create and activate a virtual environment - ```bash - git clone https://github.com/alphaonelabs/alphaonelabs-education-website.git - cd alphaonelabs-education-website - ``` +macOS/Linux: -2. Set up virtual environment: +```bash +python -m venv .venv +source .venv/bin/activate +``` - ```bash - python -m venv venv - source venv/bin/activate # On Windows: venv\Scripts\activate - ``` +Windows PowerShell: -3. Install dependencies (Poetry is the single source of truth): +```powershell +python -m venv .venv +.\.venv\Scripts\Activate.ps1 +``` - ```bash - # Ensure Poetry installed (once) - pip install --upgrade pip - pip install poetry==1.8.3 +### 3. Install dependencies - # Install project deps into local venv (recommended) - poetry install +```bash +python -m pip install --upgrade pip +pip install poetry==1.8.3 +poetry install +``` - # Activate virtualenv (Poetry 1.8 auto-detects) - poetry shell # or just use: poetry run - ``` +You can run project commands either from your activated virtual environment or with `poetry run`. -4. Set up environment variables: +### 4. Create your local environment file - ```bash - cp .env.sample .env - # Edit .env with your configuration - ``` +macOS/Linux: -5. Run migrations: +```bash +cp .env.sample .env +``` - ```bash - python manage.py migrate - ``` +Windows PowerShell: -6. Create a superuser: +```powershell +Copy-Item .env.sample .env +``` - ```bash - python manage.py createsuperuser - ``` +Then review `.env` before starting the app. For most local work, the sample values are enough to boot with SQLite. -7. Create test data: +If you want a fresh secure messaging key, generate one with: - ```bash - python manage.py create_test_data - ``` +```bash +python web/master_key.py +``` -8. Run the development server with ASGI support (required for WebSockets): +### 5. Run migrations - ```bash - poetry run uvicorn web.asgi:application --host 127.0.0.1 --port 8000 --reload - ``` +```bash +poetry run python manage.py migrate +``` - **Note:** WebSocket features (Live Avatars, Real-time Chat) require ASGI. Django's `runserver` command uses WSGI and will not support WebSockets. +### 6. Optional bootstrap commands -9. Visit [http://localhost:8000](http://localhost:8000) in your browser. +Create an admin account: -### Docker Setup +```bash +poetry run python manage.py createsuperuser +``` -1. Build the Docker image: +Load sample data: - ```bash - docker build -t education-website . - ``` +```bash +poetry run python manage.py create_test_data +``` -2. Run the Docker container: +### 7. Start the development server - ```bash - docker run -d -p 8000:8000 education-website - ``` +Recommended for full local development, including WebSockets: -3. Visit [http://localhost:8000](http://localhost:8000) in your browser. +```bash +poetry run uvicorn web.asgi:application --host 127.0.0.1 --port 8000 --reload +``` -### Admin Credentials: +If you only need standard Django pages and do not need WebSockets, this also works: -- **Email:** `admin@example.com` -- **Password:** `adminpassword` +```bash +poetry run python manage.py runserver +``` -## Environment Variables Configuration +Open the app at [http://127.0.0.1:8000/en/](http://127.0.0.1:8000/en/). -Copy `.env.sample` to `.env` and configure the variables. +The admin lives behind the `ADMIN_URL` value from your `.env` file. With the default sample config, that is: -## Development Guidelines +`http://127.0.0.1:8000/en/a-dmin-url123/` -### Code Style +## Environment Variables -- Follow PEP 8 guidelines for Python code. -- Use **Black** for code formatting. -- Use **isort** for import sorting. -- Follow Django's coding style guide. -- Use **ESLint** for JavaScript code. +`.env.sample` is the source of truth for local configuration. It includes comments for each variable and groups them by purpose. -### Git Workflow +The most important variables for local development are: -1. Create a new branch for each feature/bugfix. -2. Follow **conventional commits** for commit messages. -3. Submit **pull requests** for review. -4. Ensure all **tests pass** before merging. +- `ENVIRONMENT`: use `development` locally +- `SECRET_KEY`: Django secret key +- `MESSAGE_ENCRYPTION_KEY`: required for secure messaging features +- `DATABASE_URL`: leave on SQLite for the easiest local setup +- `ALLOWED_HOSTS`: comma-separated hostnames the app will serve +- `CSRF_TRUSTED_ORIGINS`: comma-separated origins for local forms/admin access +- `ADMIN_URL`: custom admin path segment +- `REDIS_URL`: Redis endpoint for channels and shared cache -### Testing +Most other values are optional unless you are actively working on integrations such as Stripe, Mailgun, GitHub webhooks, Google Cloud Storage, or social publishing. -- Write unit tests for new features. -- Run tests before committing: +## Testing and Linting - ```bash - python manage.py test - ``` +Run the Django test suite: -### Pre-commit Hooks (Important) +```bash +poetry run python manage.py test +``` -We use pre-commit to enforce formatting (black, isort), linting (flake8, djlint), etc. +Install and run pre-commit hooks: ```bash poetry run pre-commit install -poetry run pre-commit run --hook-stage commit poetry run pre-commit run --all-files ``` -### Documentation +## Common Errors -- Document all new features and API endpoints -- Update README.md when adding major features -- Use docstrings for Python functions and classes -- Comment complex logic +- WebSockets or live updates are not working: run the app with `uvicorn web.asgi:application`, not only `manage.py runserver`. +- You see `DisallowedHost` or CSRF failures on a custom host/port: update `ALLOWED_HOSTS` and `CSRF_TRUSTED_ORIGINS` in `.env`. +- Redis connection warnings appear locally: most pages can still work, but real-time features need a reachable `REDIS_URL`. +- MySQL client build errors occur during setup: keep `DATABASE_URL` on SQLite for local work, or install the MySQL client libraries required by `mysqlclient`. +- Google Cloud Storage errors appear in local development: leave `GS_BUCKET_NAME`, `GS_PROJECT_ID`, and `SERVICE_ACCOUNT_FILE` blank unless you are testing GCS integration. ## Contributing -We welcome contributions! Please see our [Contributing Guidelines](CONTRIBUTING.md) for details on how to submit pull requests, report issues, and contribute to the project. +Please read [CONTRIBUTING.md](CONTRIBUTING.md) before opening a pull request. ## Support -If you encounter any issues or need support, please: - -1. Search existing [Issues](https://github.com/alphaonelabs/education-website/issues) -2. Create a new issue if your problem persists -3. Join us on [Slack](https://join.slack.com/t/alphaonelabs/shared_invite/zt-7dvtocfr-1dYWOL0XZwEEPUeWXxrB1A) -4. Join us on [Discord](https://discord.gg/HJtuzTJN3h) - -## Acknowledgments - -- Thanks to all contributors who have helped shape this project -- Built with ❤️ by the Alpha One Labs team +- Search existing [GitHub issues](https://github.com/alphaonelabs/education-website/issues) +- Join the community on [Slack](https://join.slack.com/t/alphaonelabs/shared_invite/zt-7dvtocfr-1dYWOL0XZwEEPUeWXxrB1A) +- Join the community on [Discord](https://discord.gg/HJtuzTJN3h) diff --git a/web/tests/test_env_sample.py b/web/tests/test_env_sample.py new file mode 100644 index 000000000..21218e46f --- /dev/null +++ b/web/tests/test_env_sample.py @@ -0,0 +1,36 @@ +import re +from pathlib import Path + +from django.test import SimpleTestCase + + +class EnvSampleTests(SimpleTestCase): + def test_env_sample_covers_runtime_environment_variables(self): + repo_root = Path(__file__).resolve().parents[2] + env_sample = (repo_root / ".env.sample").read_text(encoding="utf-8") + settings_source = (repo_root / "web" / "settings.py").read_text(encoding="utf-8") + + sample_variables = { + line.split("=", 1)[0].strip() + for line in env_sample.splitlines() + if line.strip() and not line.lstrip().startswith("#") and "=" in line + } + + referenced_variables = set(re.findall(r'env\.(?:str|bool|list)\("([A-Z0-9_]+)"', settings_source)) + referenced_variables.update(re.findall(r'os\.getenv\("([A-Z0-9_]+)"', settings_source)) + referenced_variables.update(re.findall(r'os\.environ\.get\("([A-Z0-9_]+)"', settings_source)) + referenced_variables.update({"DATABASE_URL", "SENDGRID_PASSWORD"}) + + missing_variables = referenced_variables - sample_variables + + self.assertFalse( + missing_variables, + f".env.sample is missing runtime variables: {sorted(missing_variables)}", + ) + + def test_env_sample_does_not_list_unused_debug_flags(self): + repo_root = Path(__file__).resolve().parents[2] + env_sample = (repo_root / ".env.sample").read_text(encoding="utf-8") + + self.assertNotIn("DEBUG=", env_sample) + self.assertNotIn("DJANGO_DEBUG=", env_sample)