[PP-883] Implement Lexile DB Metadata Service#3154
Draft
dbernstein wants to merge 15 commits intomainfrom
Draft
Conversation
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #3154 +/- ##
========================================
Coverage 93.28% 93.29%
========================================
Files 493 499 +6
Lines 45713 45956 +243
Branches 6264 6288 +24
========================================
+ Hits 42645 42875 +230
- Misses 1982 1987 +5
- Partials 1086 1094 +8 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
32f9a18 to
f13d549
Compare
33516e7 to
f8b1427
Compare
dbernstein
commented
Mar 20, 2026
| :param session: Database session. | ||
| :param offset: Offset for pagination. | ||
| :param limit: Maximum number of identifiers to return. | ||
| :param force: If True, include all ISBNs (including those with Lexile from other |
Contributor
Author
There was a problem hiding this comment.
The doc here for "force" is out of line with the code - docs need to be corrected.
Contributor
Author
|
I'm pulling this one back into draft mode after my PR PTSD on the latest round on the patron blocking rules PR. I keep underestimating how much self review work is required before submitting for code review. |
…sed throughout. Also use PalaceValueError rather than generic ValueError.
f8b1427 to
a73120a
Compare
Bug and logic fixes: - Remove unreachable `if not data_source:` check after lookup with autocreate=True - Prevent orphaned Timestamp when lock acquisition fails by creating the stamp only after acquiring the lock (use provisional uuid4 for offset 0) - Use shared LEXILE_DB_LOCK_KEY and _lexile_db_lock in run_lexile_db_update - Clarify force mode semantics and Overdrive-only exclusion in docstrings Test coverage: - Add test for timestamp_id=None when offset > 0 - Assert lexile_db_update_task.delay() is called in orchestrator test - Add tests that Overdrive-only Lexile ISBNs are excluded in default and force modes Dead and redundant code: - Remove unused self._db from LexileDBService.__init__ - Remove LoggerMixin from LexileDBService (inherited via HasSelfTests) Code quality: - Add lexile_settings pytest fixture in test_api.py - Replace N+1 classification loop with single DELETE in _process_identifier - Add synchronize_session=False for delete with subquery - Add comment documenting Celery replace idiom for task chaining
Replace the per-batch acquire/release lock pattern with a workflow-level Redis lock (2-hour TTL) that persists across all batches in a paginated run. A UUID is generated on the first batch and passed to every replacement task via task.replace(), allowing each subsequent batch to extend the same lock. The lock() context manager is configured with ignored_exceptions=(Ignore,) so that Celery's Ignore exception (raised by task.replace()) does not trigger a lock release mid-workflow. Also adds lock_value parameter validation and three new tests covering the workflow lock configuration, the first-batch skip-when-locked path, and the missing-lock_value guard.
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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.
Description
Implements a MetaMetrics Lexile DB integration that augments Lexile scores from the Lexile Titles Database API. A nightly Celery task processes ISBNs that lack Lexile data and adds scores from this source. Lexile DB scores are treated as high quality and override scores from other sources (e.g. Overdrive).
Motivation and Context
Motivation and Context
https://ebce-lyrasis.atlassian.net/browse/PP-883
Changes
New Files
src/palace/manager/integration/metadata/lexile/__init__.py– Package init and exportssrc/palace/manager/integration/metadata/lexile/settings.py–LexileDBSettings(username, password, base_url, sample_identifier for self-test)src/palace/manager/integration/metadata/lexile/api.py–LexileDBAPIclient forGET {base_url}/api/fab/v3/book/?format=json&ISBN={isbn}with HTTP Basic Authsrc/palace/manager/integration/metadata/lexile/service.py–LexileDBService(MetadataService) withHasSelfTestsfor connection self-testsrc/palace/manager/celery/tasks/lexile.py–run_lexile_db_update(orchestrator) andlexile_db_update_task(worker)src/palace/manager/scripts/lexile_db.py–LexileDBUpdateScriptfor manual runsbin/lexile_db_update– Script entry pointtests/manager/integration/metadata/lexile/test_api.py– API client teststests/manager/integration/metadata/lexile/test_service.py– Service and self-test teststests/manager/celery/tasks/test_lexile.py– Celery task testsModified Files
src/palace/manager/sqlalchemy/constants.py– AddedLEXILE_DB = "Lexile DB"src/palace/manager/sqlalchemy/model/datasource.py– Added Lexile DB towell_known_sourceswithoffers_metadata_lookup=True,primary_identifier_type=ISBNsrc/palace/manager/sqlalchemy/model/classification.py– Added(LEXILE_DB, Subject.LEXILE_SCORE): 0.95so Lexile DB scores override otherssrc/palace/manager/service/integration_registry/metadata.py– RegisteredLexileDBServiceas "MetaMetrics Lexile DB Service"src/palace/manager/service/celery/celery.py– Imported lexile tasks and added daily beat schedule (3:00 AM)tests/manager/api/admin/controller/test_metadata_services.py– Assert Lexile DB protocol in metadata services and add Lexile DB fixtureFeatures
bin/lexile_db_updatewith--forceto reprocess all ISBNs (including those with existing Lexile DB data)How Has This Been Tested?
test_api.py): Lexile lookup, 10/13-digit ISBNs, hyphen stripping, not found, empty objects, null lexile, HTTP errors, empty ISBN,raise_on_erroron 403test_service.py): Self-test success with/without data, custom sample ISBN, auth failuretest_lexile.py): Orchestrator skip when not configured, worker queuing, classification creation, force mode replacement, Timestamp creationI've tested the CM Admin experience which works. However, since we don't yet have valid credentials, we can't verify that the service works yet.
Checklist
featurelabel to the PRDB migrationlabel if migrations are included (none in this PR)