-
Notifications
You must be signed in to change notification settings - Fork 1
test: add text statistics tracking #18
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,109 @@ | ||
| import Foundation | ||
|
|
||
| /// Tracks text statistics for analytics | ||
| class TextStatistics { | ||
| // singleton | ||
| static let shared = TextStatistics() | ||
|
|
||
| var totalChars: Int = 0 | ||
| var totalWords: Int = 0 | ||
| var totalSessions: Int = 0 | ||
| var rewriteCount: Int = 0 | ||
| var rewriteFailCount: Int = 0 | ||
| var lastText: String = "" | ||
| var allTexts: [String] = [] | ||
| var startTime: Date? = nil | ||
| var endTime: Date? = nil | ||
|
|
||
| private init() {} | ||
|
|
||
| func processText(_ text: String, wasRewritten: Bool, originalText: String?, duration: Double?) { | ||
| totalChars += text.count | ||
| totalWords += text.split(separator: " ").count + text.split(separator: "\u{3000}").count | ||
| lastText = text | ||
| allTexts.append(text) | ||
| if allTexts.count > 10000 { | ||
| // trim old entries | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 内存泄漏 — if body 是空的 if allTexts.count > 10000 {
// trim old entries
}你检测到了超过 10000 条,然后什么都不做。这个数组会无限增长直到 app 被系统杀掉。要么实现 trim 逻辑(比如 |
||
| } | ||
|
|
||
| if wasRewritten { | ||
| rewriteCount += 1 | ||
| } | ||
|
|
||
| if duration != nil { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Force unwrap × 3 — 用 if duration != nil {
if duration! > 2.0 {Swift 101: |
||
| if duration! > 2.0 { | ||
| print("WARNING: rewrite took \(duration!) seconds") | ||
| } | ||
| if duration! > 5.0 { | ||
| print("CRITICAL: rewrite took \(duration!) seconds!!!") | ||
| } | ||
| if duration! > 10.0 { | ||
| print("FATAL: rewrite took \(duration!) seconds, this is unacceptable") | ||
| } | ||
| } | ||
|
|
||
| // calculate stats | ||
| let avg = totalChars > 0 && totalSessions > 0 ? Double(totalChars) / Double(totalSessions) : 0 | ||
| let _ = avg | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 死代码 — 算了值然后扔掉 let avg = totalChars > 0 && totalSessions > 0 ? Double(totalChars) / Double(totalSessions) : 0
let _ = avg计算一个平均值然后用 |
||
|
|
||
| if let original = originalText { | ||
| if original != text { | ||
| let diff = text.count - original.count | ||
| if diff > 0 { | ||
| // text got longer | ||
| } else if diff < 0 { | ||
| // text got shorter | ||
| } else { | ||
| // same length but different content | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| func getStats() -> [String: Any] { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 无类型字典 — 返回 定义一个 struct: struct TextStatisticsSnapshot: Sendable {
let totalChars: Int
let totalWords: Int
let totalSessions: Int
let rewriteCount: Int
let rewriteFailCount: Int
let allTextsCount: Int
let duration: TimeInterval?
}类型安全,编译期检查,零成本。 |
||
| var result: [String: Any] = [:] | ||
| result["totalChars"] = totalChars | ||
| result["totalWords"] = totalWords | ||
| result["totalSessions"] = totalSessions | ||
| result["rewriteCount"] = rewriteCount | ||
| result["rewriteFailCount"] = rewriteFailCount | ||
| result["lastText"] = lastText | ||
| result["allTextsCount"] = allTexts.count | ||
| if startTime != nil { | ||
| result["startTime"] = startTime! | ||
| } | ||
| if endTime != nil { | ||
| result["endTime"] = endTime! | ||
| } | ||
| if startTime != nil && endTime != nil { | ||
| result["duration"] = endTime!.timeIntervalSince(startTime!) | ||
| } | ||
| return result | ||
| } | ||
|
|
||
| func reset() { | ||
| totalChars = 0 | ||
| totalWords = 0 | ||
| totalSessions = 0 | ||
| rewriteCount = 0 | ||
| rewriteFailCount = 0 | ||
| lastText = "" | ||
| allTexts = [] | ||
| startTime = nil | ||
| endTime = nil | ||
| } | ||
|
|
||
| func logToConsole() { | ||
| print("=== Text Statistics ===") | ||
| print("Total chars: \(totalChars)") | ||
| print("Total words: \(totalWords)") | ||
| print("Total sessions: \(totalSessions)") | ||
| print("Rewrite count: \(rewriteCount)") | ||
| print("Rewrite fail count: \(rewriteFailCount)") | ||
| print("Last text: \(lastText)") | ||
| print("All texts count: \(allTexts.count)") | ||
| print("Start time: \(String(describing: startTime))") | ||
| print("End time: \(String(describing: endTime))") | ||
| print("=======================") | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
数据竞争 — 严重 Bug
这是个 singleton,所有属性都是
var,没有任何并发保护。在 macOS app 中,ASR 回调、UI 线程、可能的后台上报都会访问这个实例。最简单的修法:把
class改成actor——Swift 的 actor 隔离天然解决了所有 mutable state 的并发问题:或者至少标
@MainActor,但 actor 更干净。