Skip to content

Repository files navigation

Text Summarizer

Generate a title and summary for any text or document, then annotate the result with sentiment, part-of-speech tagging and named-entity recognition.

Django REST Framework backend, React + Vite frontend. Summarization runs through the Groq API; the linguistic analysis runs locally with spaCy and NLTK.

Licence: GNU AGPL-3.0-or-later — if you run a modified version as a network service, you must offer its source to users.


Features

  • Paste text, or upload a .txt, .docx or .pdf document.
  • AI-generated title and summary (Groq).
  • Sentiment classification — positive / negative / neutral (NLTK VADER).
  • Part-of-speech tagging with colour-coded output (NLTK).
  • Named-entity recognition rendered with spaCy displaCy.
  • A "Connect Us" contact form protected by reCAPTCHA v2.

Quick start

Requires Python 3.12+ and Node 22+. No database server is needed — the default configuration uses SQLite.

Backend

cd backend
python -m venv .venv
source .venv/bin/activate          # Windows: .venv\Scripts\activate
pip install -r requirements.txt
python manage.py download_nlp_data # fetches the NLTK corpora
python manage.py migrate
cp .env.example .env               # set GROQ_API_KEY
DJANGO_DEBUG=true python manage.py runserver

The API is then on http://localhost:8000/textsummery/.

With DJANGO_DEBUG=true the backend starts with no configuration at all; everything except GROQ_API_KEY has a working development default. Without a Groq key the app runs but process_text returns 503.

Frontend

cd frontend
npm install
cp .env.example .env               # optional; defaults point at localhost:8000
npm run dev

Open http://localhost:5173.

Docker

cp .env.example .env               # set DJANGO_SECRET_KEY and GROQ_API_KEY
docker compose up --build

Frontend on http://localhost:8080, backend on http://localhost:8000. Both images are multi-stage, run as a non-root user, and serve through gunicorn and nginx respectively — no development servers.


Configuration

All backend variables are read in backend/textsummerizer/settings.py. See backend/.env.example for the annotated file.

Backend

Variable Default Description
DJANGO_SECRET_KEY Required when DJANGO_DEBUG=false; the app refuses to start without it. A development-only fallback is used when debug is on.
DJANGO_DEBUG false Never true in production.
DJANGO_ALLOWED_HOSTS localhost,127.0.0.1,[::1] when debug Comma-separated. Required when debug is off.
CORS_ALLOWED_ORIGINS http://localhost:5173 Comma-separated browser origins.
CSRF_TRUSTED_ORIGINS same as CORS_ALLOWED_ORIGINS Comma-separated.
GROQ_API_KEY Required when debug is off. Get one at console.groq.com/keys.
GROQ_MODEL openai/gpt-oss-20b Model used for title + summary.
RECAPTCHA_SECRET_KEY empty When empty the contact form rejects all submissions.
RECAPTCHA_VERIFY_URL Google's siteverify URL Override only for testing.
DB_ENGINE django.db.backends.sqlite3 Set to the postgresql/mysql backend to use a server.
DB_NAME backend/db.sqlite3 File path for SQLite; database name otherwise.
DB_USER, DB_PASSWORD, DB_HOST, DB_PORT empty Required when DB_ENGINE is not SQLite.
IP_HASH_SALT DJANGO_SECRET_KEY Salt for hashing client IPs before storage.
ENABLE_REQUEST_LOGGING true Set false to stop persisting a row per request.
MAX_UPLOAD_SIZE 5242880 (5 MiB) Rejected above this size.
API_THROTTLE_RATE 30/minute DRF anonymous throttle rate.
LOG_LEVEL INFO Root logger level.
SECURE_SSL_REDIRECT true Only applied when debug is off.
SECURE_HSTS_SECONDS 31536000 Only applied when debug is off.

Frontend

Vite inlines VITE_* variables into the bundle at build time, so they are public by design — never put a secret in them.

Variable Default Description
VITE_API_URL http://localhost:8000/textsummery Backend base URL, including the /textsummery prefix.
VITE_RECAPTCHA_SITE_KEY empty Public reCAPTCHA v2 site key. When empty the widget is not rendered and the form shows a notice.

API reference

All endpoints are mounted under the /textsummery/ prefix (backend/textsummerizer/urls.py).

Method Endpoint Description
GET /textsummery/health/ Liveness probe. Returns {"status": "ok"}.
POST /textsummery/process_text/ Summarize and analyse text or an uploaded file.
POST /textsummery/submit_form/ Submit the "Connect Us" contact form.

Django's admin is also mounted, at /admin/.

POST /textsummery/process_text/

Send either text (JSON) or file (multipart). Supplying neither is a 400.

curl -X POST http://localhost:8000/textsummery/process_text/ \
  -H 'Content-Type: application/json' \
  -d '{"text": "Your long text here."}'
curl -X POST http://localhost:8000/textsummery/process_text/ \
  -F 'file=@document.pdf'

200 response

{
  "title": "Generated title",
  "summary": "Generated summary.",
  "sentiment": "positive",
  "pos_result": "<p>…colour-coded HTML…</p>",
  "ner_result": "<div>…displaCy HTML…</div>"
}

pos_result and ner_result are HTML fragments intended for direct rendering. All user-derived tokens are HTML-escaped server-side before the markup is built.

Status When
400 No text or file; unsupported extension; file over MAX_UPLOAD_SIZE; unreadable/corrupt document.
429 Anonymous throttle rate exceeded.
500 Groq returned a malformed response, or the spaCy model / NLTK data is missing on the server.
503 Groq unreachable, rate limiting, or rejecting our credentials.

POST /textsummery/submit_form/

curl -X POST http://localhost:8000/textsummery/submit_form/ \
  -H 'Content-Type: application/json' \
  -d '{
        "first_name": "Ada", "last_name": "Lovelace",
        "company": "Analytical Engines", "email": "ada@example.com",
        "project": ["Text Summarizer"], "message": "Hello.",
        "recaptcha_token": "<token from the widget>"
      }'

Returns 201 with {"message": "Form submitted successfully"}, or 400 with per-field errors. project must contain at least one entry. The captcha token is verified against Google before anything is stored, and is never echoed back in a response.


Data collected

Deployers should read this before putting the app in front of real users.

What Where Notes
Submitted text + sentiment + timestamp UserSummerizerLog table One row per summarization request. Disable with ENABLE_REQUEST_LOGGING=false.
Salted SHA-256 hash of the client IP UserSummerizerLog.ip_hash The raw address is not stored. Salt comes from IP_HASH_SALT.
Contact form fields (name, company, email, message) TextSummerizerFormSubmission table Submitted deliberately by the user.

Submitted text is also transmitted to Groq for summarization; their handling is governed by Groq's own terms.

There is no automatic retention limit — deleting old rows is the deployer's responsibility. Earlier versions of this project additionally wrote every submission to CSV files on disk; that duplicate store has been removed.


Project structure

text-summarizer/
├── backend/
│   ├── textApp/                    # Django app
│   │   ├── management/commands/
│   │   │   └── download_nlp_data.py
│   │   ├── migrations/
│   │   ├── utils/
│   │   │   ├── entity.py           # NER + POS HTML rendering
│   │   │   ├── file_parser.py      # .txt / .docx / .pdf extraction
│   │   │   ├── privacy.py          # IP hashing
│   │   │   ├── sentiment_check.py  # VADER sentiment
│   │   │   └── summarizer.py       # Groq client
│   │   ├── models.py
│   │   ├── serializers.py          # validation
│   │   ├── services.py             # business logic
│   │   ├── tests.py
│   │   └── views.py                # transport only
│   ├── textsummerizer/             # project config
│   ├── .env.example
│   ├── Dockerfile
│   ├── requirements.txt
│   └── requirements-dev.txt
├── frontend/
│   ├── src/
│   │   ├── components/
│   │   │   ├── connect/            # contact modal
│   │   │   └── summarize/          # input, summary, analysis panels
│   │   ├── constants/
│   │   ├── hooks/
│   │   ├── pages/
│   │   ├── services/
│   │   └── test/
│   ├── .env.example
│   ├── Dockerfile
│   └── nginx.conf
├── .github/workflows/ci.yml
├── docker-compose.yml
├── CONTRIBUTING.md
├── SECURITY.md
└── LICENSE

A note on spelling

The package textsummerizer and the app textApp are misspelled and non-PEP8. They are kept deliberately: both names are written into the migration history and INSTALLED_APPS, so renaming them would orphan the django_migrations rows of every existing deployment. The lint warning is suppressed in backend/.pylintrc with that reasoning.


Development

See CONTRIBUTING.md for the exact commands CI runs.

# Backend
cd backend
python -m pylint textApp textsummerizer manage.py
python manage.py test

# Frontend
cd frontend
npm run lint
npm test
npm run build

Limitations

  • Not multi-tenant and has no authentication. Every endpoint is public. The only abuse control is a DRF anonymous throttle (30/minute by default), keyed on IP.
  • Summarization requires a Groq account and sends your text to a third party. There is no local/offline summarization fallback.
  • English only. The spaCy model (en_core_web_sm), the POS tagger and the VADER lexicon are all English-specific.
  • Sentiment is lexicon-based, computed as the mean of per-sentence scores. It does not detect sarcasm, negation scope or domain-specific tone.
  • Sentiment, POS and NER describe the generated summary, not the original input text.
  • pos_result / ner_result are raw HTML rendered with dangerouslySetInnerHTML. This is safe because the backend escapes all user-derived tokens, but any fork changing that rendering must preserve the escaping.
  • PDF extraction is text-layer only. Scanned/image PDFs yield nothing; there is no OCR.
  • Uploads are capped at 5 MiB and processed synchronously in the request — there is no job queue, so long documents block a worker.
  • The contact form needs both reCAPTCHA halves configured, otherwise every submission is rejected.
  • SQLite by default. Fine for evaluation and light use; switch to PostgreSQL or MySQL for concurrent write load.

About

Text summarization and document analysis platform designed to help users quickly transform long content into clear, concise summaries.

Topics

Resources

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Contributors

Languages