Thanks for taking the time to contribute.
By contributing you agree that your work is licensed under the GNU AGPL-3.0, the same licence as this project.
Prerequisites: Python 3.12+ and Node.js 20+.
# Backend
cd backend
python -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install -r requirements.txt -r requirements-dev.txt
cp .env.example .env # set DEBUG=True for local work
python manage.py migrate
# Frontend
cd ../frontend
npm install
cp .env.example .envYou do not need Hugging Face or Mistral keys to run the tests — the suite stubs both providers. You need them only to exercise recognition for real.
CI fails the build if any of these fail. Run them locally before opening a pull request.
pylint HandWrittenApp HandWrittenRecognition manage.py
pytest
python manage.py check --deploy
pip-audit -r requirements.txtpylint must stay at 10.00/10; the pinned configuration lives in
backend/.pylintrc. check --deploy is run with
production-shaped settings (DEBUG=False plus a generated secret key) and must
report no issues.
npm ci
npm run lint
npm run build
npm audit --audit-level=highThe full workflow is .github/workflows/ci.yml —
that file is the source of truth if this section ever drifts.
- Google-style docstrings on modules, classes and public functions.
- Keep the layering: views stay thin, business logic goes in
services.py, provider calls go inutils.py, validation goes in serializers. - Never catch bare
Exceptionto return a plausible default. A failed provider call must not read as "not handwritten". Raise a typed error fromexceptions.pycarrying the right HTTP status. - Read configuration through
django.conf.settings, at call time rather than import time, so deployments can reconfigure without a code change. - Line length 100.
- Components stay presentational; stateful logic belongs in
src/hooks/. - Keep components under roughly 150 lines — split them rather than growing them.
- Shared values go in
src/constants/, not inline in JSX. - Strict equality only (
===).console.logis a lint error;console.warnandconsole.errorare allowed. - Revoke every
URL.createObjectURLyou create.
Do not rename HandWrittenApp or HandWrittenRecognition. Those names are
baked into the migration history, the database table
handwrittenapp_handwrittenconnectus, DJANGO_SETTINGS_MODULE and the public
API path. The corresponding pylint warning is suppressed with that reasoning in
backend/.pylintrc.
Add tests with any behaviour change. The suite lives in
backend/HandWrittenApp/tests/ and uses pytest with pytest-django.
- Stub external providers — no test may make a network call.
HandWrittenRecognition/settings_test.pysupplies a deterministic environment, so tests never depend on your local.env.- Cover the failure path, not just the happy one. Several existing tests exist specifically to prove that an outage is reported as an error rather than silently converted into a negative result.
- Branch off
main. - Make the change, with tests.
- Run every command above.
- Open the PR describing what changed and why, and how you verified it.
Please keep unrelated changes out of the same PR.
Do not open a public pull request or issue for a vulnerability. Follow SECURITY.md instead.
Include: what you expected, what happened, reproduction steps, your Python and
Node versions, and whether you ran locally or under Docker. Please redact API
keys, .env contents and personal data from logs before posting them.