Skip to content

Commit c98e22a

Browse files
committed
fix opendota parsing
1 parent b6fc991 commit c98e22a

3 files changed

Lines changed: 37 additions & 13 deletions

File tree

meta/spec/matches/UC-014-parse-match.md

Lines changed: 32 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -25,26 +25,50 @@ Feature: Parse Match
2525
Given a match is in the "Parsing" state
2626
When OpenDota finishes parsing the match
2727
Then the per-minute CS data is stored
28-
Then the per-minute networth data for all 10 players is stored
28+
Then the per-minute gold (gold_t) data for all 10 players is stored
2929
And item purchase timings are stored
3030
And lane role is detected and stored
3131
And the match parse_state changes to "Parsed"
32-
And the UI updates to show the "Parsed" state
32+
And the UI updates to show goal result chips for the match
3333
34-
Scenario: Match parsing fails
34+
Scenario: Match parsing fails with pending status
3535
Given a match has been requested for parsing
36-
When OpenDota returns an error or the match cannot be parsed
36+
When OpenDota has not yet finished processing the match (lh_t/dn_t not available)
3737
Then the match parse_state changes to "Failed"
38-
And the UI shows a "Failed" indicator
38+
And the Goals cell shows a grey clock icon (🕐)
39+
And hovering the icon shows "not yet parsed / will be picked up on next sync"
40+
41+
Scenario: Match parsing fails with an unexpected error
42+
Given a match has been requested for parsing
43+
When OpenDota returns a non-recoverable error (network failure, unexpected response)
44+
Then the match parse_state changes to "Failed"
45+
And the Goals cell shows a red warning icon (⚠)
46+
And hovering the icon shows the specific error message
47+
48+
Scenario: Failed match shows Retry button
49+
Given a match has parse_state = Failed
50+
When the user views the match list
51+
Then the Goals cell shows a "↺ Retry" button
52+
And clicking it re-attempts the full parse flow
53+
54+
Scenario: Match visible as parsed on OpenDota but Unparsed locally
55+
Given a match exists in the local database with parse_state = Unparsed
56+
And the match IS fully parsed on OpenDota (lh_t, dn_t and gold_t data available)
57+
When the user clicks "Parse" or auto-parse runs
58+
Then the parse succeeds and the match transitions to parse_state = Parsed
59+
And goal chips are shown in the Goals cell
3960
4061
Scenario: Automatic retry polling
41-
Given one or more matches have parse_state = Unparsed
42-
Then the application polls every 30 seconds for recent unparsed matches
43-
And automatically requests parsing for each unparsed match found
62+
Given one or more matches have parse_state = Unparsed or Failed
63+
Then the application polls every 30 seconds for recent unparsed/failed matches
64+
And automatically requests parsing for each one found
4465
```
4566

4667
## Notes
4768
- Parsing is automatic and does not require manual user action
4869
- The UI updates in real-time using a match-state-changed event
4970
- Parsed data enables per-minute charts and item timing goals
5071
- The poll interval is 30 seconds
72+
- Per-match parse errors are shown inline (icon + tooltip) — no global error banner
73+
- Per-minute gold data is stored from the OpenDota `gold_t` field (not `net_worth`, which is a scalar)
74+
- A grey clock indicates a transient/pending state; a red warning indicates an actual error

src-tauri/src/lib.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -924,7 +924,7 @@ async fn parse_match(app: tauri::AppHandle, match_id: i64, steam_id: String) ->
924924

925925
// Store per-minute networth for all players (used by PartnerNetworth goals)
926926
for p in &detailed_match.players {
927-
if let Some(nw_t) = &p.net_worth {
927+
if let Some(nw_t) = &p.gold_t {
928928
let _ = insert_player_networth(&conn, match_id, p.player_slot, nw_t);
929929
}
930930
}
@@ -1145,7 +1145,7 @@ async fn backfill_historical_matches(
11451145

11461146
// Store per-minute networth for all players (used by PartnerNetworth goals)
11471147
for p in &detailed_match.players {
1148-
if let Some(nw_t) = &p.net_worth {
1148+
if let Some(nw_t) = &p.gold_t {
11491149
let _ = insert_player_networth(&conn, m.match_id, p.player_slot, nw_t);
11501150
}
11511151
}
@@ -1306,7 +1306,7 @@ async fn reparse_pending_matches(
13061306

13071307
// Store per-minute networth for all players (used by PartnerNetworth goals)
13081308
for p in &detailed_match.players {
1309-
if let Some(nw_t) = &p.net_worth {
1309+
if let Some(nw_t) = &p.gold_t {
13101310
let _ = insert_player_networth(&conn, m.match_id, p.player_slot, nw_t);
13111311
}
13121312
}

src-tauri/src/opendota.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ pub struct DetailedPlayer {
8686
pub lane_role: Option<i32>, // 1=carry, 2=mid, 3=offlane, 4=soft support, 5=hard support
8787
pub lh_t: Option<Vec<i32>>, // Last hits at each minute
8888
pub dn_t: Option<Vec<i32>>, // Denies at each minute
89-
pub net_worth: Option<Vec<i32>>, // Networth at each minute
89+
pub gold_t: Option<Vec<i32>>, // Gold (net worth) at each minute — OpenDota field name
9090
pub purchase_log: Option<Vec<PurchaseLogEntry>>, // Item purchases
9191
}
9292

@@ -306,7 +306,7 @@ pub async fn fetch_match_details(match_id: i64) -> Result<DetailedMatch, String>
306306
let match_details: DetailedMatch = response
307307
.json()
308308
.await
309-
.map_err(|_| "This match hasn't been fully parsed by OpenDota yet. It will be picked up on the next sync.".to_string())?;
309+
.map_err(|e| format!("Unexpected response from OpenDota for this match ({}). Try again or check https://www.opendota.com/matches/{}", e, match_id))?;
310310

311311
Ok(match_details)
312312
}

0 commit comments

Comments
 (0)