-
Notifications
You must be signed in to change notification settings - Fork 2
Course material file upload, content indexing and topic extraction #19
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
Apps247
wants to merge
86
commits into
main
Choose a base branch
from
aprameya/course-material-coverage-analysis
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from 73 commits
Commits
Show all changes
86 commits
Select commit
Hold shift + click to select a range
750984b
Added PDF text extraction
Apps247 5487aa3
UI Fix
Apps247 f9de9b5
Added OCR feature
Apps247 132e356
Removed broken indexing progress bar feature
Apps247 f743fc7
Added text extraction microservice
Apps247 5c58a47
Fixed AWS Textract microservice and laravel interaction
Apps247 ce4f372
Added OCR timer
Apps247 124dedc
Changed region
Apps247 0ce192d
Added docs
Apps247 bfd1fd9
Changed button colour
Apps247 ab675c9
Changed structure to match other microservices
Apps247 b7baf15
Added search indexing and basic UI
Apps247 00378d4
More robust PDF checking, and optimizations
Apps247 874e914
Added program search
Apps247 5c5c630
Clicking search result shows original page
Apps247 6f3126b
Updated docs
Apps247 2dbc2af
Switched to Spatie\PdfToImage
Apps247 2464bc5
Thumbnail feature, refactored, reverted to poppler
Apps247 2c951b9
Added comments explaining quirky code
Apps247 902c4e1
Added comments explaining quirky code
Apps247 e0af3bb
Merge branch 'aprameya/course-material-coverage-analysis' of https://…
Apps247 6027420
Fixed pdf to image preview issue
Apps247 d891b6f
Simplified DB schema
Apps247 edd9aa1
Refactor and improvements
Apps247 e760414
Added download button to program materials view, misc changes
Apps247 a73d7da
Refactored search controller, added thumbnails to program search
Apps247 a463a15
Bug fix
Apps247 10d3bdb
Merge remote-tracking branch 'upstream/dev' into aprameya/course-mate…
Apps247 9721890
Integrated CourseMaterialFile with CourseMaterial
Apps247 8d184e9
Course Material File UI and controller
Apps247 71f628a
Topic extraction service set up
Apps247 831bf76
Improved topic extraction
Apps247 7a2de2f
Added PowerPoint processing case
Apps247 0465a47
Refactored topic extraction into text extraction service, added type-…
Apps247 85e5c78
Merge remote-tracking branch 'upstream/main' into aprameya/course-mat…
Apps247 207d78d
Improved topic extraction for slides
Apps247 221b0ce
Force Ran composer install, must upgrade to Laravel 12.60 soon
Apps247 b463adb
Textract config and S3 bucket deploy script
Apps247 0743272
Text style scanning fix
Apps247 9b52d96
Added temporary refresh button
Apps247 1a01435
BERTopic improvements DRAFT
Apps247 e063d05
Improved BERTopic extraction DRAFT
Apps247 99ce22e
Improved parameters and postprocessing
Apps247 b02e0cd
Refactored step10 to use single template
Apps247 78fa49b
Added adjective-final removals and custom stopword vectorizing
Apps247 0190b43
Added dropdown for material type
Apps247 6929139
Refactored, fixed type-related bug, different splitting for small doc…
Apps247 670267d
Removed yake
Apps247 9f066d8
Imrpovements for slides and set random seed
Apps247 4bfef6b
Union of font and BERTopic topics, preprocessing improvements
Apps247 c6b4e6e
Simplified pages to text and used marginal relevance model
Apps247 f3e8842
Loosened threshold for slides
Apps247 cce8852
File topic editing UI functionality and bug fixes
Apps247 e922ee6
Made course topics master and material UI just add or remove
Apps247 e4827b0
Added topic matching method
Apps247 9239001
Fixed temporary search
Apps247 9105bec
Added review and accept system for suggested topics
Apps247 5eee2ab
Optimizations and cleanup
Apps247 740d3eb
Uppercase-lowercase Bug Fix
Apps247 6623dd1
Preserved case for topics
Apps247 c83a262
Changed port & bug fix
Apps247 87da677
Refactored out textract
Apps247 a1fb51e
Removed 1 minus for topic score
Apps247 cc35f17
Used model and helper to simplify ExtractedPages
Apps247 2e8f11e
Made only keyword and match topics refresh, text extraction and font …
Apps247 c952998
Removed unnecessary course_id column
Apps247 ca9be4a
Python tests and bug fixes
Apps247 a6a0285
Refactored E2E fixtures
Apps247 b383409
Feature test for suggested topics
Apps247 d37a1dc
End-to-end test
Apps247 2611241
Updated docs
Apps247 bdda3e9
File upload potential fix
Apps247 d7fcfbf
pytest OCR test
Apps247 664e7fb
Requested changes
Apps247 b060ad0
Added enum for topic source and docstring
Apps247 43ac4c6
Further requested changes for server and tests
Apps247 0c330b1
PHP-side requested changes
Apps247 5a4b834
Set allow_credentials=False
Apps247 877bb3d
Multi-part file sending
Apps247 fce7f59
Made search result highlighting more robust
Apps247 64d951a
Bug fix
Apps247 009698a
Added retry button if indexing failed
Apps247 1096066
Updated Schema doc
Apps247 1892cb6
Bug fix
Apps247 7740e75
Added S3 deletion on job completion or failure
Apps247 780bb6d
Fixed material type requirement and added new test
Apps247 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,156 @@ | ||
| # FastAPI Text Extraction Service | ||
|
|
||
| ## Overview | ||
|
|
||
| The Text Extraction Service is a FastAPI application that extracts per-page text, and file-wide topics from PDF course materials. | ||
|
|
||
| Text extraction is performed using a PDF parser, or OCR if required (using Tesseract or Textract). Topic extraction uses a combination of font properties and BERTopic scores to identify suggested topics. | ||
|
|
||
| ## Primary Responsibility | ||
|
|
||
| This service is responsible for: | ||
|
|
||
| - accepting text-topic extraction and topic refresh requests | ||
| - extracting text content from PDF files, page by page, and using OCR if requested | ||
| - extracting potential topics from document text by | ||
| - using BERTopic to extract keywords/phrases from the text | ||
| - using Font properties (or estimated font properties with OCR) and the material type to identify potential topics | ||
| - finding existing course topics in course text | ||
|
|
||
| ## API | ||
|
|
||
| ### `GET /health` | ||
|
|
||
| Returns a simple response: | ||
|
|
||
| ```json | ||
| { | ||
| "status": "ok" | ||
| } | ||
| ``` | ||
|
|
||
| ### `POST /extract` | ||
|
|
||
| The main endpoint. Accepts an [`ExtractRequest`](../python/services/text_extraction_service/app/schemas.py) with: | ||
|
|
||
| - `file` (string, required): base64-encoded PDF content | ||
| - `ocr_enabled` (boolean, default `false`): whether to apply OCR on low-text pages | ||
| - `extraction_engine` (string, default `"tesseract"`): `"tesseract"` or `"textract"` | ||
| - `ocr_threshold` (integer, default `0`): page text length below which OCR is triggered | ||
| - `material_type` (string, optional): `"slides"`, `"article"`, or `null` for default handling | ||
| - `existing_topics` (list of strings, default `[]`): course topics to match against extracted text | ||
|
|
||
| Note (TODO): Having both `ocr_enabled` and `extraction_engine` is slightly redundant - we could have a third `extraction_engine` option be `"text-only"` or similar to cover both. However, this set up allows us to easily remove textract if we need to, as that was discussed to help simplify the set up but later retained as the service was modularized further. A potential improvement is having an `int` field with `0` correspond to text-only, and `1, 2, ...` correspond to OCR engines. | ||
|
|
||
| Example request: | ||
|
|
||
| ```json | ||
| { | ||
| "file": "JVBERi0xLjQK...", | ||
| "ocr_enabled": true, | ||
| "extraction_engine": "tesseract", | ||
| "ocr_threshold": 50, | ||
| "material_type": "slides", | ||
| "existing_topics": ["Climate Change", "Forest Ecology"] | ||
| } | ||
| ``` | ||
|
|
||
| Returns an [`ExtractResponse`](../python/services/text_extraction_service/app/schemas.py) with: | ||
|
|
||
| - `pages`: list of `{ page_number, content }` for each page with extracted text | ||
| - `page_count`: total number of pages in the PDF | ||
| - `topics`: list of `{ topic, score, source }` extracted from the document | ||
|
|
||
| ### `POST /refresh-topics` | ||
|
|
||
| Re-extracts topics from existing page content without re-running text extraction. Accepts a [`RefreshTopicsRequest`](../python/services/text_extraction_service/app/schemas.py) with: | ||
|
|
||
| - `pages` (list, required): list of `{ page_number, content }` objects from a previous extraction | ||
| - `material_type` (string, optional): `"slides"`, `"article"`, or `null` | ||
| - `existing_topics` (list of strings, default `[]`): course topics to match against | ||
|
|
||
| Example request: | ||
|
|
||
| ```json | ||
| { | ||
| "pages": [ | ||
| { "page_number": 1, "content": "Introduction to Forest Ecology" }, | ||
| { "page_number": 2, "content": "Climate Change Impacts" } | ||
| ], | ||
| "material_type": "article", | ||
| "existing_topics": ["Climate Change", "Forest Ecology"] | ||
| } | ||
| ``` | ||
|
|
||
| Returns the same `ExtractResponse` format as `/extract`. | ||
|
|
||
| ## Text Extraction | ||
|
|
||
| The extraction pipeline in [`document_extractor.py`](../python/services/text_extraction_service/app/services/text_readers/document_extractor.py) works as follows: | ||
|
|
||
| 1. PDF is opened with PyMuPDF | ||
| 2. For each page: | ||
| - If OCR is enabled and the page has little text (below `ocr_threshold`), the page is processed with Tesseract or Textract (see **OCR Engines** below). | ||
| - Otherwise, text is extracted directly from the text layer with font size and font weight.d | ||
| 3. Each page returns a list of lines with `{ text, size, bold }` metadata. The `size` and `bold` may be `None` depending on the extraction engine. | ||
|
|
||
| ### OCR Engines | ||
|
|
||
| - **Tesseract**: Renders page to image at 300 DPI, groups words into lines, estimates font size from word-box height. Estimating whether the font is bold or not is possible but quite complex, so isn't implemented for now. | ||
| - **AWS Textract**: Single-page PDF extraction uses a synchronous call. Multi-page PDFs are uploaded to S3, processed asynchronously, and the service polls for completion. Textract returns line text only, no font metadata. However, Textract is much faster than Tesseract for large PDFs. | ||
|
|
||
| ## Topic Extraction Pipeline | ||
|
|
||
| Topic extraction is handled by [`type_specific_handlers.py`](../python/services/text_extraction_service/app/services/topic_extraction/type_specific_handlers.py) with material-type-specific handlers. Currently, there is a specific handler for Slides, and one for Articles, as well as a default handler. | ||
|
|
||
| ### BERTopic Extraction | ||
|
|
||
| The BERTopic extractor in [`bertopic_extractor.py`](../python/services/text_extraction_service/app/services/topic_extraction/bertopic_extractor.py): | ||
|
|
||
| 1. Lemmatizes text with spaCy to collapse plurals | ||
| 2. Splits text into overlapping windows | ||
| 3. Fits BERTopic with the `all-mpnet-base-v2` embedding model. | ||
| 4. Extracts top keywords from each cluster | ||
| 5. Returns up to `TOPICS_COUNT` (currently 100) topics, then sorts by relevance. This limit should ideally never be reached, and is only there to prevent the database exploding with topics. We can increase the limit any time. | ||
|
Apps247 marked this conversation as resolved.
Outdated
|
||
|
|
||
| ## Runtime Dependencies | ||
|
|
||
| Outside of what is installed via `pip`, you will need two dependencies: | ||
|
|
||
| **Tesseract**: Local OCR engine. Install `tesseract-ocr` via package manager or from the [official Tesseract page](https://tesseract-ocr.github.io/tessdoc/Installation.html) and ensure `tesseract` is on your `PATH`. English language data (`eng`) must be included. | ||
|
|
||
| Verify Tesseract is reachable and has English data: | ||
|
|
||
| ``` | ||
| tesseract --version | ||
| tesseract --list-langs # eng must appear in output | ||
| ``` | ||
|
|
||
| **spaCy English model**: The English NLP model used by the BERTopic extraction logic for Text lematization. After running `pip install -r requirements.txt`, run | ||
| ``` | ||
| python -m spacy download en_core_web_sm | ||
| ``` | ||
| Note that `_sm` stands for 'small', and is the lightest model. Heavier models like `en_core_web_lg` may yield better results, but also use more resources and space. | ||
|
|
||
| ## Key Environment Variables | ||
|
|
||
| The environment variables are only used for Textract OCR. | ||
| If using Tesseract, none are needed. | ||
|
|
||
| - `AWS_ACCESS_KEY_ID`: AWS access key for Textract (optional) | ||
| - `AWS_SECRET_ACCESS_KEY`: AWS secret key for Textract (optional) | ||
| - `AWS_REGION`: AWS region for Textract (default: `ca-central-1`) | ||
| - `AWS_S3_BUCKET`: S3 bucket for async Textract jobs (default: `text-extraction-temp`) | ||
|
|
||
| ## Error Handling | ||
|
|
||
| Both `/extract` and `/refresh-topics` return HTTP 500 with the error message when processing fails. | ||
|
|
||
| ## Test Coverage | ||
| The `tests/` folder for this service contains tests for the various file handlers and postprocessor: | ||
| - **Default Material Handling** with topic matching | ||
| - **Slides Handling** with font-based extraction | ||
| - **Article Handling** with font-based extraction and soft assertion for BERTopic extraction | ||
| - **Postprocessing** with various patterns and edge cases | ||
|
|
||
| AWS Textract OCR isn't covered in the tests, as it is not available in LocalStack and costs credits to use. | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.