Skip to content

Conversation

@SXP-Simon
Copy link
Owner

@SXP-Simon SXP-Simon commented Jan 13, 2026

Summary by Sourcery

Add avatar support and refreshed layout for scrapbook golden quote items across HTML, PDF, and analysis pipeline.

New Features:

  • Display user avatars alongside golden quotes when QQ IDs are available in scrapbook reports.

Enhancements:

  • Update golden quote bubble and analysis note styling for a chat-like layout in image and PDF scrapbook templates.
  • Propagate QQ identifiers through golden quote analysis and report generation to support avatar lookup.
  • Extend debug rendering data to include QQ IDs for sample golden quotes.

@sourcery-ai
Copy link
Contributor

sourcery-ai bot commented Jan 13, 2026

Reviewer's Guide

Adds avatar support and a chat-style layout for golden quotes in scrapbook templates, propagating QQ IDs from message analysis through data models and generators to templates so each quote can render an optional user avatar and styled sender name.

Class diagram for updated GoldenQuote model and usage

classDiagram
    class GoldenQuote {
        +str content
        +str sender
        +str reason
        +int qq
    }

    class GoldenQuoteAnalyzer {
        +extract_interesting_messages(messages)
        +analyze_golden_quotes(messages, umo)
    }

    class ReportGenerator {
        +_prepare_render_data(analysis_result)
        +_get_user_avatar(user_id)
    }

    class QuoteTemplateContext {
        +str content
        +str sender
        +str reason
        +str avatar_url
    }

    GoldenQuoteAnalyzer ..> GoldenQuote : creates
    GoldenQuoteAnalyzer ..> ReportGenerator : provides_golden_quotes
    ReportGenerator ..> GoldenQuote : reads
    ReportGenerator ..> QuoteTemplateContext : builds
    QuoteTemplateContext ..> GoldenQuote : derived_from
Loading

Flow diagram for QQ avatar propagation into scrapbook templates

flowchart LR
    A["Raw chat messages
    list[dict]"] --> B["GoldenQuoteAnalyzer
extract_interesting_messages"]
    B --> C["interesting_messages
with sender, time, content, qq"]
    C --> D["GoldenQuoteAnalyzer
analyze_golden_quotes"]
    D --> E["GoldenQuote objects
(content, sender, reason, qq)"]
    E --> F["ReportGenerator
_prepare_render_data"]
    F --> G["_get_user_avatar(qq)
returns avatar_url or None"]
    G --> H["quotes_list items
(content, sender, reason, avatar_url)"]
    H --> I["quote_item.html
uses avatar_url, sender,
content, reason"]
    I --> J["image_template.html /
pdf_template.html
render chat-style bubbles
with optional avatars"]
Loading

File-Level Changes

Change Details Files
Introduce avatar-based, chat-style layout for golden quote items in scrapbook HTML/PDF templates.
  • Expand .quote-wrapper to full-width with a new flex-based internal layout separating avatar and content columns.
  • Add new CSS classes (.q-flex-container, .q-user-col, .q-avatar-box, .q-avatar, .q-content-col, .q-sender-name) to render an optional circular avatar and styled sender name with alternating alignment and color accents.
  • Adjust bubble and note styling (padding, shadows, border radii, widths, margins) and hide the decorative quote mark to better match the new chat-style design.
  • Update quote_item markup to wrap each quote in the new layout, conditionally render avatar image when an avatar_url is provided, and move sender display into the new name element above the bubble.
src/reports/templates/scrapbook/image_template.html
src/reports/templates/scrapbook/pdf_template.html
src/reports/templates/scrapbook/quote_item.html
Propagate QQ user IDs through golden quote analysis so avatars can be resolved during report generation.
  • Include qq (user_id) when extracting interesting messages in GoldenQuoteAnalyzer to preserve the original sender identifier.
  • After LLM analysis, backfill qq onto resulting GoldenQuote objects by matching quote content and sender to the source messages, handling minor LLM text variations using contains checks.
  • Extend GoldenQuote dataclass to include a qq field with a default of 0, and update debug_render to populate sample qq values for test data.
  • In report generation, resolve avatar_url for each golden quote using _get_user_avatar with the qq value and pass avatar_url alongside content, sender, and reason to the templates.
src/analysis/analyzers/golden_quote_analyzer.py
src/reports/generators.py
scripts/debug_render.py
src/models/data_models.py

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@SXP-Simon SXP-Simon merged commit fd5d4b9 into main Jan 13, 2026
1 of 2 checks passed
Copy link
Contributor

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

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

Hey - I've found 1 issue, and left some high level feedback:

  • In analyze_golden_quotes, the logger.info(f"开始从 {len(interesting_messages)} 条圣经消息中提取金句") line is logged twice in a row; consider removing one to avoid redundant log noise.
  • The QQ backfill loop in analyze_golden_quotes relies on substring matching of content, which may misassign when different messages share similar text; if possible, prefer using a stable identifier (e.g., message ID) or a stronger matching key to link quotes back to interesting_messages.
  • The CSS for .q-sender-name differs between image_template.html (uses var(--font-hand)) and pdf_template.html (hardcodes 'Long Cang', cursive); if this isn’t intentional, aligning them to the same variable-based font definition would keep the two renderings visually consistent.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In `analyze_golden_quotes`, the `logger.info(f"开始从 {len(interesting_messages)} 条圣经消息中提取金句")` line is logged twice in a row; consider removing one to avoid redundant log noise.
- The QQ backfill loop in `analyze_golden_quotes` relies on substring matching of `content`, which may misassign when different messages share similar text; if possible, prefer using a stable identifier (e.g., message ID) or a stronger matching key to link quotes back to `interesting_messages`.
- The CSS for `.q-sender-name` differs between `image_template.html` (uses `var(--font-hand)`) and `pdf_template.html` (hardcodes `'Long Cang', cursive`); if this isn’t intentional, aligning them to the same variable-based font definition would keep the two renderings visually consistent.

## Individual Comments

### Comment 1
<location> `src/analysis/analyzers/golden_quote_analyzer.py:194-204` </location>
<code_context>
+                for msg in interesting_messages:
+                    # 尝试匹配内容和发送者
+                    # 注意:LLM 可能会微调内容,这里使用包含匹配或精确匹配
+                    if (
+                        quote.content in msg["content"]
+                        or msg["content"] in quote.content
+                    ) and quote.sender == msg["sender"]:
+                        quote.qq = msg.get("qq", 0)
+                        break
</code_context>

<issue_to_address>
**suggestion (bug_risk):** The content containment matching for backfilling QQ IDs may incorrectly associate quotes when messages share overlapping content.

The containment check (`quote.content in msg["content"] or msg["content"] in quote.content`) is likely to match the wrong message when content is short or shared across multiple messages (repetitions, common phrases, quotes), which can backfill an incorrect `qq`. To reduce false matches, consider tightening the criteria (e.g., prefer exact equality, add timestamp proximity or minimum-length requirements, and short‑circuit on exact match before using containment).

```suggestion
            # 回填QQ号
            for quote in quotes:
                for msg in interesting_messages:
                    # 先严格匹配:内容完全一致且发送者一致
                    if quote.sender == msg["sender"] and quote.content == msg["content"]:
                        quote.qq = msg.get("qq", 0)
                        break

                    # 再进行保守的包含匹配:
                    # - 仍然要求发送者一致
                    # - 只对较长内容启用(避免短语/常用语误匹配)
                    # - 使用双向包含作为兜底
                    if quote.sender == msg["sender"]:
                        content_quote = quote.content or ""
                        content_msg = msg.get("content") or ""
                        min_length = 20  # 避免短内容造成大量误匹配

                        if (
                            len(content_quote) >= min_length
                            and len(content_msg)   >= min_length
                            and (
                                content_quote in content_msg
                                or content_msg in content_quote
                            )
                        ):
                            quote.qq = msg.get("qq", 0)
                            break
```
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment on lines +194 to +204
# 回填QQ号
for quote in quotes:
for msg in interesting_messages:
# 尝试匹配内容和发送者
# 注意:LLM 可能会微调内容,这里使用包含匹配或精确匹配
if (
quote.content in msg["content"]
or msg["content"] in quote.content
) and quote.sender == msg["sender"]:
quote.qq = msg.get("qq", 0)
break
Copy link
Contributor

Choose a reason for hiding this comment

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

suggestion (bug_risk): The content containment matching for backfilling QQ IDs may incorrectly associate quotes when messages share overlapping content.

The containment check (quote.content in msg["content"] or msg["content"] in quote.content) is likely to match the wrong message when content is short or shared across multiple messages (repetitions, common phrases, quotes), which can backfill an incorrect qq. To reduce false matches, consider tightening the criteria (e.g., prefer exact equality, add timestamp proximity or minimum-length requirements, and short‑circuit on exact match before using containment).

Suggested change
# 回填QQ号
for quote in quotes:
for msg in interesting_messages:
# 尝试匹配内容和发送者
# 注意:LLM 可能会微调内容,这里使用包含匹配或精确匹配
if (
quote.content in msg["content"]
or msg["content"] in quote.content
) and quote.sender == msg["sender"]:
quote.qq = msg.get("qq", 0)
break
# 回填QQ号
for quote in quotes:
for msg in interesting_messages:
# 先严格匹配:内容完全一致且发送者一致
if quote.sender == msg["sender"] and quote.content == msg["content"]:
quote.qq = msg.get("qq", 0)
break
# 再进行保守的包含匹配:
# - 仍然要求发送者一致
# - 只对较长内容启用(避免短语/常用语误匹配)
# - 使用双向包含作为兜底
if quote.sender == msg["sender"]:
content_quote = quote.content or ""
content_msg = msg.get("content") or ""
min_length = 20 # 避免短内容造成大量误匹配
if (
len(content_quote) >= min_length
and len(content_msg) >= min_length
and (
content_quote in content_msg
or content_msg in content_quote
)
):
quote.qq = msg.get("qq", 0)
break

@SXP-Simon SXP-Simon deleted the feat/scrapbook-avatar branch January 16, 2026 08:45
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