Skip to content

feat: 增加章节学习次数功能#616

Open
macarons-me wants to merge 3 commits into
Samueli924:mainfrom
macarons-me:feat/add-chapter-study-count
Open

feat: 增加章节学习次数功能#616
macarons-me wants to merge 3 commits into
Samueli924:mainfrom
macarons-me:feat/add-chapter-study-count

Conversation

@macarons-me

@macarons-me macarons-me commented Jun 24, 2026

Copy link
Copy Markdown

问题

在学习时发现部分课程有章节学习次数的考核目标,比较变态

解决

通过分析章节页面的网络行为,并结合其他同学对该考核目标的反馈,成功推测并模拟了前端的关键请求行为,从而实现了该自动化功能

Summary by CodeRabbit

  • New Features
    • Added an optional “post-study chapter learning count” mode that runs after finishing course task points.
    • Introduced add_learning_count and target_count settings (with matching CLI options) to control whether the extra step runs and the total target count.
  • Improvements
    • Enhanced the chapter count-increase workflow with more robust heartbeat/recording and safer handling of transient request failures.
    • Added clearer success/failure reporting and an automatic stop after repeated chapter-access issues.
  • Documentation
    • Updated the configuration guide, including the execution order for the new settings.

fix: 优化章节学习次数访问表现

feat: 添加章节学习次数配置说明及失败处理逻辑

fix: 恢复意外的修改
@coderabbitai

coderabbitai Bot commented Jun 24, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 0afd4fdf-a153-472f-ad7a-9ed8df2617c4

📥 Commits

Reviewing files that changed from the base of the PR and between bd00515 and cfd702d.

📒 Files selected for processing (1)
  • api/base.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • api/base.py

📝 Walkthrough

Walkthrough

Adds an optional post-study phase that, when enabled, iterates courses after normal task completion, fetches chapter data, runs setlog, sends two monitor heartbeats per visit, and stops at a configurable target count. CLI, config, docs, and orchestration are updated together.

Changes

Chapter Learning Count Increase

Layer / File(s) Summary
Configuration and CLI wiring
config_template.ini, README.md, main.py
Adds add_learning_count and target_count to config and CLI handling, and documents the new chapter-learning-count flow and execution order.
Chapter count API flow
api/base.py
Adds helpers to fetch chapter HTML, execute setlog, and send monitor heartbeats, then updates chapter count incrementing to use them with failure tracking.
Post-study orchestration
api/process.py, main.py
Adds per-course learning-count processing and wires a second pass in main() that runs after normal study completion.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Suggested reviewers

  • Samueli924

Poem

🐇 Hop, hop, through chapters I roam,
setlog sings while heartbeats boomerang home,
Thirty seconds later, another gentle tap,
target_count climbs with a happy little clap.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: adding chapter learning count functionality.
Docstring Coverage ✅ Passed Docstring coverage is 83.33% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@coderabbitai coderabbitai 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.

Actionable comments posted: 2

🧹 Nitpick comments (2)
main.py (1)

552-559: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Hoist target_count assignment out of the loop.

common_config["target_count"] = target_count is invariant across iterations and only needs to be set once before the loop. Minor cleanup.

♻️ Proposed tidy-up
         if add_learning_count:
             logger.info("刷课完成,开始增加章节学习次数...")
+            common_config["target_count"] = target_count
             for course in course_task:
-                common_config["target_count"] = target_count
                 increase_learning_count_for_course(chaoxing, course, common_config)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@main.py` around lines 552 - 559, Hoist the invariant target_count assignment
out of the course loop in the post-processing block in main.py. Move
common_config["target_count"] = target_count to just before the for course in
course_task loop, and keep increase_learning_count_for_course and the completion
logging unchanged so each iteration reuses the same config value without
redundant reassignment.
api/base.py (1)

1214-1215: 🚀 Performance & Scalability | 🔵 Trivial

Heads-up: runtime scales linearly with target_count at ~30s each.

Each chapter visit blocks for a hard-coded time.sleep(30), so with the default target_count=100 a single course takes ~50 minutes (plus per-request overhead), repeated for every course. Consider making the interval configurable or documenting the expected duration so users aren't surprised.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@api/base.py` around lines 1214 - 1215, The chapter-visit loop in the relevant
course-processing flow uses a hard-coded 30-second sleep, which makes runtime
scale linearly with target_count and can surprise users. Update the logic around
the time.sleep(30) step to use a configurable interval or expose it as a
parameter in the surrounding function, and make sure the default behavior is
clearly documented in the method handling chapter traversal.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@api/base.py`:
- Around line 1122-1134: The GET request in the student study Ajax call is
missing a timeout, so it can hang indefinitely if the connection stalls. Update
the _session.get call inside the relevant request path to pass a timeout value
consistent with _send_monitor_heartbeat and _extract_and_send_setlog (use the
same 5-second timeout) so the request fails fast like the other network calls.
- Around line 1190-1193: The loop in the reusable point-replication logic can
spin forever when points is empty, because the outer while in the method that
processes points never makes progress. Update the method containing the
while/for iteration to fail safe on an empty points input before entering the
loop, so total cannot stay stuck at 0; keep the fix localized to that helper
rather than relying on increase_learning_count_for_course to guard it.

---

Nitpick comments:
In `@api/base.py`:
- Around line 1214-1215: The chapter-visit loop in the relevant
course-processing flow uses a hard-coded 30-second sleep, which makes runtime
scale linearly with target_count and can surprise users. Update the logic around
the time.sleep(30) step to use a configurable interval or expose it as a
parameter in the surrounding function, and make sure the default behavior is
clearly documented in the method handling chapter traversal.

In `@main.py`:
- Around line 552-559: Hoist the invariant target_count assignment out of the
course loop in the post-processing block in main.py. Move
common_config["target_count"] = target_count to just before the for course in
course_task loop, and keep increase_learning_count_for_course and the completion
logging unchanged so each iteration reuses the same config value without
redundant reassignment.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: b4c6f582-40e7-4d91-9850-d4bda6ed49e0

📥 Commits

Reviewing files that changed from the base of the PR and between 76fd385 and 38a2698.

📒 Files selected for processing (5)
  • README.md
  • api/base.py
  • api/process.py
  • config_template.ini
  • main.py

Comment thread api/base.py
Comment thread api/base.py

@coderabbitai coderabbitai 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.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
api/base.py (2)

1125-1138: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Keep transient request errors inside the failure counter.

A timeout/connection error from this GET bypasses consecutive_failures and aborts the whole count phase. Catch RequestException here and return None, matching the non-200 path.

🛡️ Proposed fix
-        _resp = _session.get(
-            url="https://mooc1.chaoxing.com/mooc-ans/mycourse/studentstudyAjax",
-            params={
-                "courseId": _course["courseId"],
-                "clazzid": _course["clazzId"],
-                "chapterId": point["id"],
-                "cpi": _course["cpi"],
-                "verificationcode": "",
-                "mooc2": 1,
-                "microTopicId": 0,
-                "editorPreview": 0,
-            },
-            timeout=8,
-        )
+        try:
+            _resp = _session.get(
+                url="https://mooc1.chaoxing.com/mooc-ans/mycourse/studentstudyAjax",
+                params={
+                    "courseId": _course["courseId"],
+                    "clazzid": _course["clazzId"],
+                    "chapterId": point["id"],
+                    "cpi": _course["cpi"],
+                    "verificationcode": "",
+                    "mooc2": 1,
+                    "microTopicId": 0,
+                    "editorPreview": 0,
+                },
+                timeout=8,
+            )
+        except RequestException as exc:
+            logger.error(f"章节访问失败 -> [{point['title']}]: {exc}")
+            return None
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@api/base.py` around lines 1125 - 1138, The GET in the course study flow
bypasses the failure counter when it raises a transient request error instead of
returning a non-200 response. Update the request handling around _session.get in
the student study fetch path to catch RequestException, treat it like the
existing non-200 branch, and return None so the caller’s consecutive_failures
logic continues to count the failure. Preserve the current behavior for
successful responses and keep the change localized to the method that performs
the study-page fetch.

1146-1168: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Only increment total after setlog succeeds.

_extract_and_send_setlog() currently has no success signal, so missing setlog, HTTP failures, and exceptions still lead to total += 1. Since this is the count-recording API, treat failure as a failed visit.

🛡️ Proposed fix
-    def _extract_and_send_setlog(self, html_text):
+    def _extract_and_send_setlog(self, html_text) -> bool:
@@
         if not match:
             logger.trace("未在响应中找到 setlog URL")
-            return
+            return False
 
         setlog_url = match.group(1)
         try:
             session = SessionManager.get_session()
             resp = session.get(setlog_url, timeout=5)
             logger.trace(f"Setlog sent -> {resp.status_code}")
-        except Exception as e:
+            return resp.ok
+        except RequestException as e:
             logger.trace(f"Setlog failed (non-critical): {e}")
+            return False
@@
                 # 第 1 步:从 HTML 中提取 setlog URL 并执行(真正的计次 API)
-                self._extract_and_send_setlog(html_text)
+                if not self._extract_and_send_setlog(html_text):
+                    logger.error(f"章节 setlog 发送失败, 当前章节: {point['title']}")
+                    consecutive_failures += 1
+                    if consecutive_failures >= max_consecutive_failures:
+                        logger.error(
+                            f"章节学习次数增加连续失败 {consecutive_failures} 次, 终止任务"
+                        )
+                        return StudyResult.ERROR
+                    continue
+
+                consecutive_failures = 0
 
                 # 第 2 步:立即发送 monitor 心跳包(模拟 fn())
                 self._send_monitor_heartbeat(course, point)

Also applies to: 1215-1228

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@api/base.py` around lines 1146 - 1168, The _extract_and_send_setlog helper
currently logs failures but does not tell callers whether the setlog call
actually succeeded, so total can be incremented even when the visit record was
not sent. Change _extract_and_send_setlog to return a success/failure signal
based on finding the setlog URL, the session.get call, and the HTTP response,
and then update the callers around total += 1 to only increment when
_extract_and_send_setlog reports success. Keep the logic aligned with the
existing SessionManager.get_session and logger.trace flow.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@api/base.py`:
- Around line 1125-1138: The GET in the course study flow bypasses the failure
counter when it raises a transient request error instead of returning a non-200
response. Update the request handling around _session.get in the student study
fetch path to catch RequestException, treat it like the existing non-200 branch,
and return None so the caller’s consecutive_failures logic continues to count
the failure. Preserve the current behavior for successful responses and keep the
change localized to the method that performs the study-page fetch.
- Around line 1146-1168: The _extract_and_send_setlog helper currently logs
failures but does not tell callers whether the setlog call actually succeeded,
so total can be incremented even when the visit record was not sent. Change
_extract_and_send_setlog to return a success/failure signal based on finding the
setlog URL, the session.get call, and the HTTP response, and then update the
callers around total += 1 to only increment when _extract_and_send_setlog
reports success. Keep the logic aligned with the existing
SessionManager.get_session and logger.trace flow.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 26917f4c-7839-4388-9c48-340fd4480c1d

📥 Commits

Reviewing files that changed from the base of the PR and between 38a2698 and bd00515.

📒 Files selected for processing (3)
  • api/base.py
  • api/process.py
  • main.py
🚧 Files skipped from review as they are similar to previous changes (2)
  • api/process.py
  • main.py

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.

1 participant