Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 11 additions & 9 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,22 +19,23 @@ jobs:
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.14.3"
python-version: "3.14"
cache: "pip"
cache-dependency-path: backend/requirements.txt

- name: Install dependencies
run: |
python -m pip install --upgrade pip
if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
pip install -r requirements.txt

- name: Run tests (if present)
run: |
if [ -f api/tests.py ] || [ -d api/tests ]; then
python manage.py test
else
echo "No backend tests found in api/; skipping test step."
fi
- name: Lint Python
run: ruff check .

- name: Security audit
run: safety check

- name: Run tests
run: pytest -v

frontend:
runs-on: ubuntu-latest
Expand All @@ -57,6 +58,7 @@ jobs:

- name: Build
run: npm run build

docker-build:
runs-on: ubuntu-latest
steps:
Expand Down
29 changes: 28 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ build/
*.whl
venv/
.venv/
env/
.env
db.sqlite3
*.log
Expand All @@ -35,3 +34,31 @@ frontend/dist/
# OS
.DS_Store
Thumbs.db

# Agent/Session files
AGENTS.md
session-*.md

# Testing/Coverage
.pytest_cache/

# Expanded environment file ignores (protects accidental secrets)
.env.*
frontend/.env
backend/.env

# Ignore all SQLite DBs, not just the default
*.sqlite3

# Local coverage/test artifacts (frontend & backend)
frontend/coverage/
backend/coverage/

# LaTeX/TeX build artifacts (if any contributor compiles PDFs locally)
*.pdf
*.aux
*.log
*.out

# Backup and temp files
#*#
155 changes: 144 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,32 +1,129 @@
# cheat-sheet
# Cheat Sheet Generator

[![CI](https://github.com/ChicoState/cheat-sheet/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/ChicoState/cheat-sheet/actions/workflows/ci.yml)

A full-stack web application built with **React** (frontend) and **Django** (backend).
A full-stack web application for generating LaTeX-based cheat sheets. Users select math classes and formula categories, then view the generated LaTeX code alongside a live PDF preview.

## Features

- **Class Selection**: Choose from PRE-ALGEBRA, ALGEBRA I, ALGEBRA II, and GEOMETRY
- **Category Selection**: Select categories with checkboxes (no Ctrl/Cmd needed)
- **Live Preview**: Split-view interface with LaTeX code and PDF preview
- **Auto-compile**: PDF generates automatically when you generate a cheat sheet
- **PDF Export**: Compile to PDF using Tectonic LaTeX engine on the backend
- **Download Options**: Download as .tex source or .pdf


## To-Do / Known Issues

- Fix bugs:
- Dark mode (complete variable coverage, remove hardcoded colors)
- Compile button not working after changes
- Implement or migrate database (currently MariaDB; consider migration if needed)
- Increase formatting options (columns, margins, text sizing, etc.)


## Planned Features

> These features are not yet implemented

- **Formatting Options**:
- Column layout (single, two, three columns)
- Text sizes (font scaling)
- Margin adjustments
- **Image Insertion**:
- Allow users to insert images, store them in the database
- Embed images in PDF and keep code reference in exported .tex
- **User Accounts**: Register and log in with username and password
- **Database Storage**: Save and manage cheat sheets in database
- **Autosave & Version History**: Every compile is saved automatically; revert to any previous version
- **Custom LaTeX Syntax Shortcuts**: Allow users to use shortcuts for custom LaTeX commands

## Tech Stack

| Layer | Technology |
|-------|------------|
| Frontend | React 18 + Vite + npm |
| Backend | Django 6 + Django REST Framework |
| LaTeX Engine | Tectonic |
| Database | SQLite (dev) / MariaDB (prod) |
| Container | Docker Compose |

### Backend Dependencies
- django>=6.0
- djangorestframework>=3.15
- django-cors-headers>=4.4
- python-dotenv>=1.0
- dj-database-url>=2.1
- pymysql>=1.1
- pytest>=8.0
- pytest-django>=4.8
- ruff>=0.4.0 (linting)
- safety>=2.0 (security)

Comment on lines +52 to +63
Copy link

Copilot AI Mar 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

README lists pymysql>=1.1 as a backend dependency, but the actual backend dependency in backend/requirements.txt is mysqlclient (and pymysql is not listed). Update the README dependency list to match the real requirements to avoid confusing setup/install instructions.

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot open a new pull request to apply changes based on this feedback

### Frontend Dependencies
- react, react-dom
- vite

## Project Structure

```
├── backend/ # Django REST API
│ ├── cheat_sheet/ # Django project settings
│ ├── api/ # Main API app
│ ├── manage.py
│ └── requirements.txt
├── frontend/ # React + Vite
├── backend/ # Django REST API
│ ├── cheat_sheet/ # Django project settings
│ ├── api/ # API app
│ │ ├── views.py # API endpoints
│ │ ├── models.py # Database models
│ │ ├── formula_data/ # Hardcoded formula data
│ │ │ ├── pre_algebra.py
│ │ │ ├── algebra_i.py
│ │ │ ├── algebra_ii.py
│ │ │ └── geometry.py
│ ├── requirements.txt # Python dependencies
│ ├── Dockerfile # Backend container
│ └── manage.py
├── frontend/ # React + Vite
│ ├── src/
│ │ ├── App.jsx # Main app component
│ │ ├── components/
│ │ │ └── CreateCheatSheet.jsx # Main UI
│ │ └── App.css
│ ├── package.json
│ └── vite.config.js
│ ├── vite.config.js
│ └── Dockerfile
├── docker-compose.yml # Container orchestration
└── README.md
```

## API Endpoints

| Method | Endpoint | Description |
|--------|----------|-------------|
| GET | `/api/health/` | Health check |
| GET | `/api/classes/` | List available classes with categories and formulas |
| POST | `/api/generate-sheet/` | Generate LaTeX for selected formulas |
| POST | `/api/compile/` | Compile LaTeX to PDF |

### Available Formula Classes

- **PRE-ALGEBRA** - Order of Operations, Fractions, Ratios, Properties, Area/Perimeter, Solving Equations
- **ALGEBRA I** - Linear Equations, Inequalities, Integer Rules, Decimals/Percents, Mean/Median/Mode, Quadratics, Polynomials, Exponents, Radicals, Functions, Absolute Value, Rational Expressions
- **ALGEBRA II** - Complex Numbers, Logarithms, Exponential Functions, Polynomial Theorems, Conic Sections, Sequences/Series, Matrices, Binomial Theorem
- **GEOMETRY** - Angle Relationships, Parallel Lines, Triangles, Pythagorean Theorem, Similar/Congruent Triangles, Quadrilaterals, Polygons, Circles, Circle Theorems, Coordinate Geometry, Surface Area/Volume, Transformations

## Getting Started

### Prerequisites

- Python 3.13+
- Node.js 20+
- Tectonic (for PDF compilation)

### Backend

```bash
cd backend
python -m venv venv
source venv/bin/activate
source venv/bin/activate # On Windows: venv\Scripts\activate
pip install -r requirements.txt
python manage.py migrate
python manage.py runserver
Expand All @@ -42,12 +139,48 @@ npm install
npm run dev
```

The frontend will be available at `http://localhost:5173/`.

### Docker

```bash
docker compose up --build
```

This will build and start both the Django backend and the React frontend services using Docker.
This builds and starts the Django backend, React frontend, and MariaDB database.

The app will be available at `http://localhost:5173/`. API requests are proxied to the Django backend.

## Running Tests

### Backend (pytest)

```bash
cd backend
pytest -v # Run with verbose output
pytest -k "test_name" # Run tests matching pattern
```

### Frontend (ESLint)

```bash
cd frontend
npx eslint src/ # Lint source files
npx eslint --fix src/ # Auto-fix lint issues
```

### CI Pipeline

The project includes GitHub Actions CI that runs:
- Backend: Ruff linting, Safety security scan, pytest
- Frontend: ESLint, build verification
- Docker: Image build verification
Comment on lines +164 to +177
Copy link

Copilot AI Mar 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

README states the CI pipeline and local instructions use ESLint (npx eslint …) and that CI runs ESLint, but the workflow currently only runs npm run build and eslint packages were removed from frontend/package.json. Either re-add/configure ESLint (and a CI lint step) or update the README section to reflect the current tooling.

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot open a new pull request to apply changes based on this feedback


## User Flow

1. **Enter Title**: Give your cheat sheet a name
2. **Select Class**: Click on a class (PRE-ALGEBRA, ALGEBRA I, ALGEBRA II, GEOMETRY)
3. **Select Categories**: Check the categories you want (no Ctrl/Cmd needed)
4. **Generate**: Click "Generate Cheat Sheet" - LaTeX generates and PDF compiles automatically
5. **Preview**: View the PDF in the preview pane, or click the circular button to recompile
6. **Download**: Download as .tex or .pdf
8 changes: 5 additions & 3 deletions backend/.env.docker
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# Example Docker environment configuration for Django.
# Set a strong, unique value for DJANGO_SECRET_KEY via environment variables in each environment.
# Docker environment configuration for Django.
DJANGO_SECRET_KEY=
DJANGO_DEBUG=True
DJANGO_ALLOWED_HOSTS=localhost,127.0.0.1,0.0.0.0
DJANGO_ALLOWED_HOSTS=localhost,127.0.0.1,0.0.0.0,backend
CORS_ALLOWED_ORIGINS=http://localhost:3000,http://localhost:5173

# MariaDB connection (matches the db service in docker-compose.yml)
DATABASE_URL=mysql://cheatsheet_user:cheatsheet_pass@db:3306/cheatsheet_db
6 changes: 5 additions & 1 deletion backend/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
wget \
tar \
fontconfig \
default-libmysqlclient-dev \
pkg-config \
gcc \
default-mysql-client \
&& rm -rf /var/lib/apt/lists/*

# Download and extract the appropriate Tectonic binary
Expand All @@ -31,4 +35,4 @@ COPY . .

EXPOSE 8000

CMD ["python", "manage.py", "runserver", "0.0.0.0:8000"]
CMD ["sh", "-c", "until mysqladmin ping -hdb -ucheatsheet_user -pcheatsheet_pass --silent; do sleep 2; done && python manage.py runserver 0.0.0.0:8000"]
Copy link

Copilot AI Mar 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Docker CMD hard-codes DB credentials in the image (mysqladmin ping ... -ucheatsheet_user -pcheatsheet_pass). This leaks credentials into the image history and process list. Prefer reading credentials from environment variables (or relying on DATABASE_URL) when waiting for DB readiness.

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot open a new pull request to apply changes based on this feedback

31 changes: 31 additions & 0 deletions backend/api/admin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@



#from django.contrib import admin
#from .models import Template, CheatSheet, PracticeProblem
#
#
#@admin.register(Template)
#class TemplateAdmin(admin.ModelAdmin):
# list_display = ("name", "subject", "default_columns", "default_margins", "updated_at")
# list_filter = ("subject",)
# search_fields = ("name", "description")
Comment on lines +1 to +12
Copy link

Copilot AI Mar 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This module currently contains only commented-out admin registrations (and leading blank lines). Either enable and format the admin setup (so models are manageable in Django admin) or delete the file / remove the commented code to keep the codebase clean.

Copilot uses AI. Check for mistakes.
#
#
#class PracticeProblemInline(admin.TabularInline):
# model = PracticeProblem
# extra = 1
#
#
#@admin.register(CheatSheet)
#class CheatSheetAdmin(admin.ModelAdmin):
# list_display = ("title", "template", "columns", "margins", "font_size", "updated_at")
# list_filter = ("template",)
# search_fields = ("title",)
# inlines = [PracticeProblemInline]
#
#
#@admin.register(PracticeProblem)
#class PracticeProblemAdmin(admin.ModelAdmin):
# list_display = ("cheat_sheet", "order", "question_latex")
# list_filter = ("cheat_sheet",)
6 changes: 6 additions & 0 deletions backend/api/apps.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from django.apps import AppConfig


class ApiConfig(AppConfig):
default_auto_field = "django.db.models.BigAutoField"
name = "api"
42 changes: 42 additions & 0 deletions backend/api/formula_data/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
"""
Formula data organized by class and category.
Each module exports FORMULAS dict and CLASS_NAME.
"""

from .pre_algebra import FORMULAS as PRE_ALGEBRA, CLASS_NAME as PRE_ALGEBRA_NAME
from .algebra_i import FORMULAS as ALGEBRA_I, CLASS_NAME as ALGEBRA_I_NAME
from .algebra_ii import FORMULAS as ALGEBRA_II, CLASS_NAME as ALGEBRA_II_NAME
from .geometry import FORMULAS as GEOMETRY, CLASS_NAME as GEOMETRY_NAME

AVAILABLE_CLASSES = [PRE_ALGEBRA_NAME, ALGEBRA_I_NAME, ALGEBRA_II_NAME, GEOMETRY_NAME]

FORMULA_DATA = {
PRE_ALGEBRA_NAME: PRE_ALGEBRA,
ALGEBRA_I_NAME: ALGEBRA_I,
ALGEBRA_II_NAME: ALGEBRA_II,
GEOMETRY_NAME: GEOMETRY,
}


def get_formula_data():
"""Return the full formula data structure."""
return FORMULA_DATA


def get_available_classes():
"""Return list of available class names."""
return AVAILABLE_CLASSES


def get_classes_with_details():
"""Return full structure with classes, categories, and formulas."""
return [
{
"name": class_name,
"categories": [
{"name": cat_name, "formulas": formulas}
for cat_name, formulas in categories.items()
]
}
for class_name, categories in FORMULA_DATA.items()
]
Loading