Skip to content
This repository was archived by the owner on Jul 20, 2026. It is now read-only.

fix: backfill district case_type from detail page - #85

Merged
damo-da merged 2 commits into
mainfrom
fix/district-case-type
Jun 23, 2026
Merged

fix: backfill district case_type from detail page#85
damo-da merged 2 commits into
mainfrom
fix/district-case-type

Conversation

@damo-da

@damo-da damo-da commented Jun 23, 2026

Copy link
Copy Markdown
Member

Summary

~20,368 district-court rows have a blank case_type. Root cause: district case_type is only ever sourced from the daily cause-list (pesi/daily) listing's cells[3], which is the "मुद्दा विषय" (subject) column — and that cell is blank for many administrative filings. Unlike high/supreme courts, district enrichment never backfills case_type, so those blanks are permanent. (For reference, high/supreme enrichment reads मुद्दाको किसिम from the detail page, which is why they have 0 nulls.)

Confirmed against the live detail page that the true case type is available there. Example — case 082-07-0160 (kaskidc), currently blank in DB:

मुद्दाको किसिम : अधिकृत वारेसनामा प्रमाणित गर्न दिने ।   <- real case type (detail page)
मुद्दाको बिषय  :                                        <- blank subject (what the listing showed)

So the value is recoverable; the district enricher just wasn't reading the right field (it read मुद्दाको बिषय into a non-existent case_subject attribute, which is silently dropped — noted but out of scope here).

Changes

  • Map the detail page's मुद्दाको किसिमcase_type during district enrichment, so future cases populate it (mirrors high/supreme).
  • Broaden the enrichment selection query to also revisit status='enriched' rows whose case_type is blank.
  • Backfill those rows via a dedicated _backfill_case_type path that only sets case_type and does not rebuild entities/hearings (the existing rebuild deletes CaseEntity rows that may have downstream linkages).

Notes / follow-ups

  • The blank-case_type rows are overwhelmingly non-litigation administrative registrations (marriage, power-of-attorney, etc.); their "type" tends to restate the act. Backfill still makes the data consistent.
  • The मुद्दाको बिषय → case_subject mapping writes to a column that doesn't exist on CourtCase (silent data loss) — worth a separate fix.

Test plan

  • python -m py_compile + ruff check pass
  • Verified live detail page exposes मुद्दाको किसिम for a currently-blank case
  • Run scrapy crawl district_case_enrichment against staging; confirm blank case_type rows get populated and entity rows are untouched

🤖 Generated with Claude Code

District case_type was only ever taken from the daily cause-list listing's
"मुद्दा विषय" (subject) column, which is blank for many administrative
filings and which enrichment never backfilled — unlike high/supreme courts,
whose enrichment reads "मुद्दाको किसिम" from the detail page (hence 0 nulls).
This left ~20k district rows with no case_type.

The detail page does expose the real "मुद्दाको किसिम". Map it to case_type
during enrichment, and revisit already-enriched rows with a blank case_type
to backfill them — using a dedicated path that only sets case_type and does
not rebuild entities/hearings (whose delete-and-recreate risks downstream
linkages).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

@damo-da, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 28 minutes and 35 seconds. Learn how PR review limits work.

Your organization has used up its prepaid credits, and credit purchases are no longer available. Enable the review add-on in the billing tab to keep reviews running — you're only billed for reviews past your plan's rate limits ($0.25/file).

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based credits.

🚦 How do rate limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan refill rate.

For paid Pro and Pro+ PR reviews, CodeRabbit uses rolling per-developer review limits. Reviews become available again as older review attempts age out of the rolling limit window.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: cb7bf6db-43b3-4b43-8203-8f925de09bb8

📥 Commits

Reviewing files that changed from the base of the PR and between 6fecfd2 and d9bfcb6.

📒 Files selected for processing (1)
  • ngm/ngscrape/spiders/district_case_enrichment.py
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/district-case-type

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a mechanism to backfill the case_type field for already-enriched court cases where it is missing, querying these cases in start_requests and updating them without rebuilding related entities. The reviewer recommends combining the status check and backfill logic within the existing transaction block to avoid redundant database queries. Additionally, they suggest setting a placeholder value (such as "-") when the case_type is missing on the detail page to prevent infinite re-scraping loops, which would also allow for the removal of the redundant _backfill_case_type helper method.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment on lines +272 to +292
already_enriched = case.status == "enriched"
has_case_type = bool(case.case_type)

if already_enriched:
if has_case_type:
self.logger.info(f"Case {case_number} already enriched, skipping")
return
# Already enriched but missing case_type: backfill just that field
# from the detail page without re-touching entities/hearings (whose
# rebuild deletes rows that may have downstream linkages).
case_type = self._extract_enrichment_data(soup).get("case_type")
if case_type:
self._backfill_case_type(case_number, code_name, case_type)
self.logger.info(
f"Backfilled case_type for {case_number} ({code_name}): {case_type}"
)
else:
self.logger.info(
f"Case {case_number} ({code_name}) has no case_type on detail page"
)
return

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

Combining the status check and the backfill logic inside the existing transaction block avoids querying the database twice for the same CourtCase row and opening/closing multiple transactions.

Additionally, if a case is already enriched but has no case_type on the detail page, leaving it empty will cause it to be repeatedly queried and scraped on every subsequent run of the spider (due to the query in start_requests). Setting a placeholder like "-" prevents this infinite re-scraping loop.

            if case.status == "enriched":
                if case.case_type:
                    self.logger.info(f"Case {case_number} already enriched, skipping")
                    return

                # Already enriched but missing case_type: backfill just that field
                # from the detail page without re-touching entities/hearings (whose
                # rebuild deletes rows that may have downstream linkages).
                case_type = self._extract_enrichment_data(soup).get("case_type")
                if case_type:
                    case.case_type = case_type[:200]
                    case.updated_at = datetime.now(KATHMANDU_TZ).replace(tzinfo=None)
                    self.logger.info(
                        f"Backfilled case_type for {case_number} ({code_name}): {case_type}"
                    )
                else:
                    # Set a placeholder to prevent infinite re-scraping in start_requests
                    case.case_type = "-"
                    case.updated_at = datetime.now(KATHMANDU_TZ).replace(tzinfo=None)
                    self.logger.info(
                        f"Case {case_number} ({code_name}) has no case_type on detail page; set to '-'"
                    )
                return

Comment on lines +432 to +450
def _backfill_case_type(self, case_number: str, code_name: str, case_type: str):
"""Set case_type on an already-enriched case, leaving everything else."""
now = datetime.now(KATHMANDU_TZ).replace(tzinfo=None)

with self.session.begin():
case = (
self.session.query(CourtCase)
.filter(
and_(
CourtCase.case_number == case_number,
CourtCase.court_identifier == code_name,
)
)
.first()
)

if case and not case.case_type:
case.case_type = case_type[:200]
case.updated_at = now

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Since the backfill logic has been integrated directly into the main transaction block in parse_case_detail, this helper method is no longer needed and can be removed.

Address review feedback on the case_type backfill:
- Make revisiting already-enriched blank-case_type rows opt-in via
  `-a backfill_case_type=true`, so nightly runs never re-fetch rows whose
  detail page genuinely has no case_type in an endless loop. The historical
  backfill is run as a one-off Job.
- Fold the backfill update into the existing status-check transaction and
  drop the redundant _backfill_case_type helper/second query.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@damo-da

damo-da commented Jun 23, 2026

Copy link
Copy Markdown
Member Author

Addressed the review feedback:

  • Single transaction: the backfill update now happens inside the existing status-check transaction; dropped the redundant _backfill_case_type helper and its second query.
  • Infinite re-scrape of typeless rows: rather than writing a placeholder sentinel into case_type (which would pollute the data consumers read), revisiting already-enriched blank rows is now opt-in via -a backfill_case_type=true. Nightly runs never re-fetch permanently-typeless rows; the historical backfill is a one-off Job. The forward fix (mapping detail-page मुद्दाको किसिमcase_type) stays always-on.

@damo-da
damo-da merged commit 60644b9 into main Jun 23, 2026
4 checks passed
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant