Skip to content

Refactor lane module into smaller, focused components#3001

Merged
jonathangreen merged 3 commits intomainfrom
chore/refactor-lane-module
Jan 23, 2026
Merged

Refactor lane module into smaller, focused components#3001
jonathangreen merged 3 commits intomainfrom
chore/refactor-lane-module

Conversation

@jonathangreen
Copy link
Member

@jonathangreen jonathangreen commented Jan 22, 2026

Description

Break up the large lane.py file (~3200 lines) into smaller, more maintainable modules. This PR contains only code movement and import updates - no logic changes were made.

New Structure

I'm not 100% happy with the new structure, and the distinction / location of all the files. But I think its an improvement over what was there before, and now that the files are broken out its easier to move them around if we have a better idea for organization in the future.

feed/facets/ package:

  • constants.py - FacetConstants, FacetConfig (moved from core/facets.py)
  • base.py - BaseFacets, FacetsWithEntryPoint
  • feed.py - Facets, DefaultSortOrderFacets, FeaturedFacets
  • search.py - SearchFacets
  • database.py - DatabaseBackedFacets

Facets primarily control how feeds are filtered, sorted, and presented. They inherit from FacetConstants and are consumed by feed generation code. Placing them in feed/ keeps feed-related concepts together. We avoided core/ since we're trying to move away from that catch-all package.

feed/worklist/ package:

  • base.py - WorkList
  • database.py - DatabaseBackedWorkList
  • hierarchy.py - HierarchyWorkList
  • top_level.py - TopLevelWorkList
  • specific.py - SpecificWorkList

WorkLists are used to generate OPDS feeds - they define collections of works that appear in feeds. The search filter creation (Filter.from_worklist()) is an implementation detail of how feeds are populated.

search/pagination.py:

  • Consolidated Pagination (moved from lane.py) with existing SortKeyPagination

SortKeyPagination already existed here and inherits from Pagination. Consolidating them in one location makes the inheritance relationship clear.

sqlalchemy/model/lane.py:

  • Slimmed to ~730 lines containing only Lane, LaneGenre, and lanes_customlists

Motivation and Context

This refactoring was done while investigating potential search improvements for PP-3472. The large lane.py file made it difficult to understand the relationships between facets, worklists, pagination, and the Lane model. Breaking it into focused modules improves maintainability and makes the codebase easier to navigate.

How Has This Been Tested?

  • All imports verified to work correctly
  • Circular dependency issues identified and resolved with lazy imports where needed
  • Tests moved to mirror the new source structure

Checklist

  • I have updated the documentation accordingly.
  • All new and existing tests passed.

Break up the large lane.py file (~3200 lines) into smaller, more
maintainable modules:

- Create feed/facets/ package with base.py, constants.py, database.py,
  feed.py, and search.py for facet-related classes
- Create feed/worklist/ package with base.py, database.py, hierarchy.py,
  specific.py, and top_level.py for WorkList classes
- Consolidate Pagination and SortKeyPagination in search/pagination.py
- Slim lane.py to ~730 lines containing only Lane, LaneGenre, and
  lanes_customlists SQLAlchemy models

Move corresponding tests to mirror the new source structure.

Update imports throughout the codebase to use the new module locations.
Move sqlalchemy model imports to TYPE_CHECKING blocks and use local
imports at runtime to avoid circular dependency when lane.py is
loaded by sqlalchemy/model/__init__.py.
@jonathangreen jonathangreen requested a review from a team January 22, 2026 14:58
@codecov
Copy link

codecov bot commented Jan 22, 2026

Codecov Report

❌ Patch coverage is 98.01700% with 21 lines in your changes missing coverage. Please review.
✅ Project coverage is 92.99%. Comparing base (1676f62) to head (17161f0).
⚠️ Report is 3 commits behind head on main.

Files with missing lines Patch % Lines
src/palace/manager/feed/facets/base.py 92.53% 5 Missing ⚠️
src/palace/manager/feed/facets/search.py 95.76% 2 Missing and 3 partials ⚠️
src/palace/manager/feed/worklist/base.py 98.44% 2 Missing and 3 partials ⚠️
src/palace/manager/feed/facets/feed.py 98.62% 0 Missing and 3 partials ⚠️
src/palace/manager/feed/facets/database.py 95.45% 1 Missing and 1 partial ⚠️
src/palace/manager/feed/worklist/database.py 99.25% 0 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #3001      +/-   ##
==========================================
+ Coverage   92.97%   92.99%   +0.01%     
==========================================
  Files         459      468       +9     
  Lines       43276    43370      +94     
  Branches     6034     6034              
==========================================
+ Hits        40238    40330      +92     
- Misses       1966     1967       +1     
- Partials     1072     1073       +1     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Copy link
Contributor

@tdilauro tdilauro left a comment

Choose a reason for hiding this comment

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

🔀 🚀

I noticed a few inconsistencies unrelated to the refactor along the way, so noted them here for the future. No need to fix for this PR.

"""
return filter

def modify_database_query(cls, _db, qu):
Copy link
Contributor

Choose a reason for hiding this comment

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

Instance method, cls param mismatch. But no use, so could be @staticmethod or function.

Comment on lines +48 to +49
@classmethod
def top_level_for_library(self, _db, library, collection_ids=None):
Copy link
Contributor

Choose a reason for hiding this comment

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

@classmethod, self param mismatch. But no use, so could be @staticmethod or function.

"""Create a filter clause that only books that are on one of the
CustomLists allowed by Lane configuration.

:return: A 3-tuple (query, clauses).
Copy link
Contributor

Choose a reason for hiding this comment

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

Minor - "2-tuple" of (query, clauses), rather than "3-tuple"

@jonathangreen jonathangreen merged commit 17b873e into main Jan 23, 2026
19 checks passed
@jonathangreen jonathangreen deleted the chore/refactor-lane-module branch January 23, 2026 22:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants