Skip to content

fix: preserve requested pagination filters and add ReviewsBatch.next_batch - #41

Merged
qvvonk merged 3 commits into
devfrom
fix/reviews-pagination
Sep 9, 2026
Merged

qvvonk merged 3 commits into
devfrom
fix/reviews-pagination

Conversation

@Flummy1

@Flummy1 Flummy1 commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

Проблема

ReviewsParser и TransactionPreviewsParser читают filter и user_id из
скрытых инпутов ответа:

filter_ = self.tree.css('input[type="hidden"][name="filter"]')
...
filter=filter_[0].attributes.get('value') if filter_ else None,

FunPay отдаёт эти инпуты не во всех ответах users/reviews и
users/transactions — в остальных случаях в батч попадает None. Любая
пагинация, опирающаяся на спарсенное значение, молча теряла запрошенный фильтр:

  • TransactionPreviewsBatch.next_batch() подставлял self.filter or '',
    то есть после первой страницы фильтр по типу транзакций сбрасывался на «все»;
  • у отзывов пагинации не было вовсе, а нужный для неё user_id терялся так же.

Отдельная деталь: GetReviews в репозитории есть, но он не экспортирован из
funpaybotengine.methods и недоступен через Bot — то есть метод написан,
но пользователю его не достать.

Решение

Парсеры возвращают обычные мутабельные датаклассы, поэтому метод проставляет
запрошенные значения сразу после разбора — рядом с полем, которое перекрывает:

class GetReviews(FunPayMethod[ReviewsBatch]):
    async def parse_result(self, response: RawResponse[Any]) -> ParsedReviewsBatch:
        result: ParsedReviewsBatch = await super().parse_result(response)
        result.user_id = self.user_id
        result.filter = self.filter
        return result

То же самое в GetTransactions.parse_result для filter. Модели при этом не
трогаются вовсе — единственное добавление в них это ReviewsBatch.next_batch(),
аналог того, что уже есть у TransactionPreviewsBatch и OrderPreviewsBatch:

  • ValueError('Last batch.'), если next_review_id пуст;
  • ValueError('Unknown user id.'), если user_id неизвестен (в отличие от
    транзакций, отзывам нужен явный id профиля — батч со страницы заказа
    пагинировать нечем);
  • сохранённый фильтр уходит в следующий запрос.

Чтобы next_batch() было чем выполнять, GetReviews экспортирован из
funpaybotengine.methods и доступен как Bot.get_reviews().

Что изменилось по сравнению с исходной версией PR

Исходная версия переопределяла transform_result и правила поля батча после
построения модели — на 0.10.0rc3 такого хука уже нет, решение переписано под
текущий API.

Сюда же переехал фикс фильтра транзакций из
#40 — он был влит в
ветку #39, но на dev не попал, а баг там ровно тот же.

Использование

batch = await bot.get_reviews(user_id=1234, filter='5')
while True:
    for review in batch.reviews:
        print(review.rating, review.text)
    if not batch.next_review_id:
        break
    batch = await batch.next_batch()   # фильтр '5' сохраняется

Обратная совместимость

Ломающих изменений нет: next_batch() и Bot.get_reviews() — новые, а
предпочтение запрошенного фильтра спарсенному меняет поведение только там, где
раньше значение терялось.

Проверки

uv run ruff check funpaybotengine tests     # чисто (кроме предсуществующего offers.py — см. #39)
uv run mypy funpaybotengine                 # no issues found
uv run pytest tests                         # 21 passed

tests/pagination_filters_test.py фиксирует и предпосылку бага (парсер отдаёт
None, когда скрытых инпутов нет), и то, что parse_result проставляет
запрошенные значения, и то, что фильтр с курсором доезжают до следующего
запроса.

🤖 Generated with Claude Code

Stamp the requested filter and user_id onto ReviewsBatch in
GetReviews.transform_result, since FunPay omits the hidden filter/user_id
inputs in some responses. Add ReviewsBatch.next_batch() to paginate while
preserving the filter, mirroring the transactions pagination fix.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@Flummy1
Flummy1 requested a review from qvvonk as a code owner July 16, 2026 09:43
Reworked on top of 0.10.0rc3: the requested filters now travel through the
`context` channel the method base gained on dev, instead of the removed
`transform_result` override.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@Flummy1 Flummy1 changed the title fix: preserve requested review filter and add ReviewsBatch.next_batch fix: preserve requested pagination filters and add ReviewsBatch.next_batch Sep 9, 2026

@qvvonk qvvonk left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Передача фильтров через context крайне неявна: будет сложно понять, откуда у модели появились значения, необходимые для пагинации.

Лучше явно устанавливать соответствующие поля в GetReview.parse_result, например:

async def parse_result(self, response: ...) -> ...:
    result = await super().parse_result(response)
    result.filter = ...
    result.user_id = ...
    return result

…l context

Review feedback: passing the filters through `context` makes it hard to see
where the values on the model came from. The parsers return plain mutable
dataclasses, so the methods can set the fields right after parsing — the
requested value sits next to the field it overrides, and both models go back to
being untouched except for the new `ReviewsBatch.next_batch()`.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@Flummy1

Flummy1 commented Sep 9, 2026

Copy link
Copy Markdown
Contributor Author

Согласен, поправил — b582777.

Значения теперь проставляются прямо в parse_result, рядом с полем, которое
перекрывают:

class GetReviews(FunPayMethod[ReviewsBatch]):
    async def parse_result(self, response: RawResponse[Any]) -> ParsedReviewsBatch:
        result: ParsedReviewsBatch = await super().parse_result(response)

        # FunPay omits the hidden ``user_id`` / ``filter`` inputs in a part of the
        # ``users/reviews`` responses, so the parsed values are unreliable. The requested
        # ones are known here and are what the next batch has to be asked with.
        result.user_id = self.user_id
        result.filter = self.filter
        return result

То же самое в GetTransactions.parse_result для filter (там уходит
self.filter.value, чтобы не ломать тип поля датакласса — str | None).

Побочный плюс: model_post_init из ReviewsBatch и TransactionPreviewsBatch
ушли совсем, обе модели вернулись к состоянию dev, и единственное добавление
в них — ReviewsBatch.next_batch().

Тесты переписаны под этот путь: теперь они дёргают сам parse_result, а не
проверяют содержимое контекста. ruff / mypy / pytest — зелёные (21 тест).

@qvvonk
qvvonk merged commit b02137d into dev Sep 9, 2026
@qvvonk
qvvonk deleted the fix/reviews-pagination branch September 9, 2026 10:41
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