-
Notifications
You must be signed in to change notification settings - Fork 9
Don't let unrecognized BISAC codes classify fiction as nonfiction (PP-4849) #3726
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
Changes from all commits
06675d3
78c4544
e35ca3a
99a4e92
2925881
a0e3467
412dcc3
0f14c03
ce7e236
9cef011
4146fdd
55bf453
0be2ca9
e367b9a
5395f9b
e9ec925
4d1593e
a8391b0
e3a6fcb
8fdbb75
dc8c7b9
2d90632
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| #!/usr/bin/env python | ||
| """Queue the reset_non_bisac_nonfiction_subjects Celery task. | ||
|
|
||
| Convenience wrapper that manually dispatches the repair for BISAC subjects | ||
| stored as nonfiction in error (the `reset_non_bisac_nonfiction_subjects` Celery | ||
| task) for a worker to process. | ||
| """ | ||
|
|
||
| from palace.manager.scripts.work import ResetNonBisacNonfictionSubjectsScript | ||
|
|
||
| ResetNonBisacNonfictionSubjectsScript().run() |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -11,6 +11,7 @@ | |
| from palace.manager.celery.tasks.work import ( | ||
| classify_unchecked_subjects, | ||
| reclassify_null_audience_works, | ||
| reset_non_bisac_nonfiction_subjects, | ||
| ) | ||
| from palace.manager.data_layer.policy.presentation import ( | ||
| PresentationCalculationPolicy, | ||
|
|
@@ -252,6 +253,25 @@ class WorkOPDSScript(WorkPresentationScript): | |
| ) | ||
|
|
||
|
|
||
| class ResetNonBisacNonfictionSubjectsScript(Script): | ||
| """Manually dispatch the ``reset_non_bisac_nonfiction_subjects`` Celery task. | ||
|
|
||
| The work itself happens in the Celery task; this script just queues it. It | ||
| exists so the repair can be applied again on demand, in case its reset was | ||
| consumed by old code before the new classifier was live everywhere. | ||
|
|
||
| TODO: Remove this script when the ``reset_non_bisac_nonfiction_subjects`` | ||
| Celery task is removed. | ||
| """ | ||
|
|
||
| def do_run(self, *args: Any, **kwargs: Any) -> None: | ||
| reset_non_bisac_nonfiction_subjects.delay() | ||
|
Comment on lines
+256
to
+268
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
This change adds Context Used: CLAUDE.md (source)
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Declining, and please do remember this one — it is the third PR in a row where it has fired. There is no supported command framework to move this to. Concretely:
The pattern is also still actively used after the deprecation label was written. That text entered The startup-task framework does not replace this. It runs once per deployment and has no on-demand path, which is the entire reason this script exists — a reviewer asked for something we could run by hand "if we needed to have a do over". The repo treats the two as complementary rather than substitutive: The intent behind the deprecation is that new business logic should not grow these packages. It does not here: the logic is in Separately,
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Understood — I’ve saved this as a review rule. I won’t flag thin, temporary |
||
| self.log.info( | ||
| 'The "reset_non_bisac_nonfiction_subjects" task has been queued for ' | ||
| "execution. See the celery logs for details about task execution." | ||
| ) | ||
|
|
||
|
|
||
| class ReclassifyNullAudienceWorksScript(Script): | ||
| """Manually dispatch the ``reclassify_null_audience_works`` Celery task. | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,54 @@ | ||
| """Repair BISAC subjects stored as nonfiction because their code did not resolve. | ||
|
|
||
| Everything on the Palace Marketplace / Feedbooks category scheme is stored with | ||
| ``type='BISAC'``, including codes that are not BISAC at all -- language and | ||
| territory categories such as ``INFEN000`` ("English literature"). Those cannot | ||
| be resolved to a canonical heading, so classification used to infer nonfiction | ||
| from the distributor's name and store ``fiction=False``. The classifier no | ||
| longer does that, which leaves the stored values stale. | ||
|
|
||
| Subjects are only re-examined when ``checked`` is false, so this dispatches two | ||
| steps: ``reset_non_bisac_nonfiction_subjects`` marks the stale ones unchecked, | ||
| then ``classify_unchecked_subjects`` re-scores them and recalculates their | ||
| works. The second signature is immutable so the chain does not pass the first | ||
| task's return value into it. | ||
|
|
||
| Doing both here matters. The reset on its own is exposed: anything reaching | ||
| ``Subject.assign_to_genre`` before the re-score consumes it, and code running | ||
| the superseded rules re-stamps ``checked=True`` with the same wrong value. | ||
| Nothing errors and nothing revisits the subject afterwards, so the repair | ||
| silently did nothing, having paid for a reindex to do it. Chaining the re-score | ||
| closes that gap to seconds rather than waiting for the nightly run. | ||
|
|
||
| The timing works out. ``helpers/migrate.yml`` stops the scripts container -- | ||
| where every Celery worker and beat run -- before migrating, and starts it again | ||
| from the new image afterwards, so the worker that picks this up is necessarily | ||
| new code. | ||
|
|
||
| Web containers are the remaining exposure: the deploy recycles them after the | ||
| migration step, and they can reach ``assign_to_genre`` through a presentation | ||
| recalculation. Fargate deployments are not governed by that playbook at all. A | ||
| second startup task re-applies the reset a release later, once no old code is | ||
| running anywhere. | ||
|
|
||
| TODO: Remove this task once it has run on all deployments (PP-5129).""" | ||
|
|
||
| from __future__ import annotations | ||
|
|
||
| import logging | ||
|
|
||
| from celery.canvas import Signature, chain | ||
| from sqlalchemy.orm import Session | ||
|
|
||
| from palace.manager.celery.tasks.work import ( | ||
| classify_unchecked_subjects, | ||
| reset_non_bisac_nonfiction_subjects, | ||
| ) | ||
| from palace.manager.service.container import Services | ||
|
|
||
|
|
||
| def run(services: Services, session: Session, log: logging.Logger) -> Signature | None: | ||
| return chain( | ||
| reset_non_bisac_nonfiction_subjects.s(), | ||
| classify_unchecked_subjects.si(), | ||
| ) | ||
|
dbernstein marked this conversation as resolved.
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This adds
TOP_LEVEL_HEADINGS,_has_canonical_heading,contradicts_stored_fiction, and_apply_rulesetsunderpalace.manager.core. The repository architecture guide marks/coreas deprecated and prohibits new code there. This repository requirement must be satisfied before merging by placing the new classification behavior in a supported package.Context Used: CLAUDE.md (source)
Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Declining, and please remember this one too.
Taken literally this asks me to relocate the BISAC classifier.
TOP_LEVEL_HEADINGS,_has_canonical_headingand_apply_rulesetsare not a helper bolted ontocore— they are the fix, and they are internals ofBISACClassifier, which lives inpalace/manager/core/classifier/along with the 5,984-code table and both rulesets. Moving them somewhere else would split one class across two packages.core/classifier/is also the one part ofcore/that is not being retired. Over the last 12 monthscore/excluding the classifier is net -2,001 lines: the CoverageProvider machinery removed in7a3274ea1, monitors in963b651b2, facets in17b873e4d, equivalents coverage in49bc00071.core/classifier/over the same period is net +131 and is the only part still receiving behaviour changes, including7d5b8a06aandff2432b7ethis April. The deprecation label reflects a package being drained; the classifier is the part that is not.There is nowhere else for it to go.
packages/contains onlypalace-opdsandpalace-util, neither of which has any classification code;palace/manager/service/is dependency-injection wiring. Exactly one module outside the classifier package referencesBISACClassifierat all — the Celery task in this PR.On
contradicts_stored_fictionspecifically, the placement was deliberate and is explained ina8391b04f: it reproduces exactly the pathClassifier.classifytakes,scrub_identifier_and_namethenis_fiction, so the repair and the runtime cannot disagree about which rows are affected. An earlier revision of this work did approximate the predicate elsewhere, with a pattern on the identifier, and it was wrong —FBZZZ000000is shape-valid but does not exist, and it slipped through. That is why the definition was moved onto the class that owns the answer. Note also that the migration your comment refers to no longer exists; the Celery task is the only non-test caller.It is temporary regardless: the PP-5129 cleanup removes
contradicts_stored_fictionalong with the task, the script and thebin/wrapper.Separately,
CLAUDE.mdis inaccurate as written and I am fixing it, so this stops misfiring.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed.
palace/manager/core/classifier/is an active exception to the broadercore/deprecation guidance, so classifier internals should remain colocated withBISACClassifierunless the repository guidance changes.I’ve remembered this rule for future reviews:
View rule