cricinfo-cli-go 0.1.1 → 0.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/internal/cli/matches.go +126 -2
- package/internal/cli/matches_test.go +82 -0
- package/internal/cli/search.go +156 -0
- package/internal/cricinfo/analysis.go +393 -93
- package/internal/cricinfo/analysis_phase15_test.go +38 -0
- package/internal/cricinfo/client.go +23 -2
- package/internal/cricinfo/coverage_ledger_test.go +2 -22
- package/internal/cricinfo/entity_index.go +27 -0
- package/internal/cricinfo/historical_hydration.go +82 -42
- package/internal/cricinfo/matches.go +1641 -88
- package/internal/cricinfo/matches_phase7_test.go +11 -4
- package/internal/cricinfo/normalize_entities.go +83 -35
- package/internal/cricinfo/players.go +236 -2
- package/internal/cricinfo/render_contract.go +191 -49
- package/internal/cricinfo/renderer.go +613 -19
- package/internal/cricinfo/resolver.go +134 -13
- package/internal/cricinfo/teams.go +109 -6
- package/internal/cricinfo/testdata/coverage/cricinfo-field-path-catalog.txt +2536 -0
- package/internal/cricinfo/testdata/coverage/cricinfo-working-templates.tsv +56 -0
- package/package.json +1 -1
|
@@ -137,7 +137,7 @@ func TestFieldPathFamilyCoverageLedgerKnownFamiliesMapped(t *testing.T) {
|
|
|
137
137
|
func researchedTemplatesFromTSV(t *testing.T) []string {
|
|
138
138
|
t.Helper()
|
|
139
139
|
|
|
140
|
-
path := filepath.Join(
|
|
140
|
+
path := filepath.Join("testdata", "coverage", "cricinfo-working-templates.tsv")
|
|
141
141
|
file, err := os.Open(path)
|
|
142
142
|
if err != nil {
|
|
143
143
|
t.Fatalf("open working templates TSV %q: %v", path, err)
|
|
@@ -177,7 +177,7 @@ func researchedTemplatesFromTSV(t *testing.T) []string {
|
|
|
177
177
|
func researchedFieldPathFamilies(t *testing.T) map[string]struct{} {
|
|
178
178
|
t.Helper()
|
|
179
179
|
|
|
180
|
-
path := filepath.Join(
|
|
180
|
+
path := filepath.Join("testdata", "coverage", "cricinfo-field-path-catalog.txt")
|
|
181
181
|
file, err := os.Open(path)
|
|
182
182
|
if err != nil {
|
|
183
183
|
t.Fatalf("open field-path catalog %q: %v", path, err)
|
|
@@ -231,23 +231,3 @@ func isNumericToken(value string) bool {
|
|
|
231
231
|
}
|
|
232
232
|
return true
|
|
233
233
|
}
|
|
234
|
-
|
|
235
|
-
func repoRoot(t *testing.T) string {
|
|
236
|
-
t.Helper()
|
|
237
|
-
|
|
238
|
-
dir, err := os.Getwd()
|
|
239
|
-
if err != nil {
|
|
240
|
-
t.Fatalf("getwd: %v", err)
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
for {
|
|
244
|
-
if _, err := os.Stat(filepath.Join(dir, "go.mod")); err == nil {
|
|
245
|
-
return dir
|
|
246
|
-
}
|
|
247
|
-
parent := filepath.Dir(dir)
|
|
248
|
-
if parent == dir {
|
|
249
|
-
t.Fatalf("unable to locate repository root from %q", dir)
|
|
250
|
-
}
|
|
251
|
-
dir = parent
|
|
252
|
-
}
|
|
253
|
-
}
|
|
@@ -472,12 +472,39 @@ func mergeAliasSlices(slices ...[]string) []string {
|
|
|
472
472
|
}
|
|
473
473
|
seen[normalized] = struct{}{}
|
|
474
474
|
out = append(out, value)
|
|
475
|
+
if acronym := aliasAcronym(normalized); acronym != "" {
|
|
476
|
+
if _, ok := seen[acronym]; !ok {
|
|
477
|
+
seen[acronym] = struct{}{}
|
|
478
|
+
out = append(out, acronym)
|
|
479
|
+
}
|
|
480
|
+
}
|
|
475
481
|
}
|
|
476
482
|
}
|
|
477
483
|
sort.Strings(out)
|
|
478
484
|
return out
|
|
479
485
|
}
|
|
480
486
|
|
|
487
|
+
func aliasAcronym(normalized string) string {
|
|
488
|
+
tokens := strings.Fields(strings.TrimSpace(normalized))
|
|
489
|
+
if len(tokens) < 2 {
|
|
490
|
+
return ""
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
var builder strings.Builder
|
|
494
|
+
for _, token := range tokens {
|
|
495
|
+
if token == "" {
|
|
496
|
+
continue
|
|
497
|
+
}
|
|
498
|
+
builder.WriteByte(token[0])
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
acronym := strings.TrimSpace(builder.String())
|
|
502
|
+
if len(acronym) < 2 {
|
|
503
|
+
return ""
|
|
504
|
+
}
|
|
505
|
+
return acronym
|
|
506
|
+
}
|
|
507
|
+
|
|
481
508
|
func aliasSet(aliases []string) map[string]struct{} {
|
|
482
509
|
set := map[string]struct{}{}
|
|
483
510
|
for _, alias := range aliases {
|
|
@@ -6,6 +6,7 @@ import (
|
|
|
6
6
|
"sort"
|
|
7
7
|
"strconv"
|
|
8
8
|
"strings"
|
|
9
|
+
"sync"
|
|
9
10
|
"time"
|
|
10
11
|
)
|
|
11
12
|
|
|
@@ -133,6 +134,8 @@ type HistoricalScopeSession struct {
|
|
|
133
134
|
partnershipWarnByMatch map[string][]string
|
|
134
135
|
|
|
135
136
|
metrics HydrationMetrics
|
|
137
|
+
|
|
138
|
+
resolveMu sync.Mutex
|
|
136
139
|
}
|
|
137
140
|
|
|
138
141
|
func newHistoricalScopeSession(client *Client, resolver *Resolver, opts HistoricalScopeOptions) *HistoricalScopeSession {
|
|
@@ -292,52 +295,78 @@ func (s *HistoricalScopeSession) HydratePlayerMatchSummaries(ctx context.Context
|
|
|
292
295
|
continue
|
|
293
296
|
}
|
|
294
297
|
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
298
|
+
type playerHydrationResult struct {
|
|
299
|
+
item *PlayerMatch
|
|
300
|
+
warning string
|
|
301
|
+
}
|
|
302
|
+
results := make([]playerHydrationResult, len(entries))
|
|
303
|
+
sem := make(chan struct{}, detailSubresourceFetchConcurrency)
|
|
304
|
+
var wg sync.WaitGroup
|
|
305
|
+
for i, entry := range entries {
|
|
306
|
+
wg.Add(1)
|
|
307
|
+
go func(index int, entry TeamRosterEntry, team Team) {
|
|
308
|
+
defer wg.Done()
|
|
309
|
+
sem <- struct{}{}
|
|
310
|
+
defer func() { <-sem }()
|
|
304
311
|
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
312
|
+
playerID := strings.TrimSpace(entry.PlayerID)
|
|
313
|
+
if playerID == "" {
|
|
314
|
+
return
|
|
315
|
+
}
|
|
316
|
+
if strings.TrimSpace(entry.DisplayName) == "" && s.resolver != nil {
|
|
317
|
+
_ = s.resolver.seedPlayerByID(ctx, playerID, match.LeagueID, match.ID)
|
|
318
|
+
}
|
|
319
|
+
entry = s.enrichRosterEntryFromIndex(entry)
|
|
310
320
|
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
321
|
+
statsRef := rosterPlayerStatisticsRef(match, team, entry)
|
|
322
|
+
if statsRef == "" {
|
|
323
|
+
results[index] = playerHydrationResult{warning: fmt.Sprintf("player %s has no match statistics route", playerID)}
|
|
324
|
+
return
|
|
325
|
+
}
|
|
316
326
|
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
327
|
+
statsDoc, err := s.resolve(ctx, statsRef)
|
|
328
|
+
if err != nil {
|
|
329
|
+
results[index] = playerHydrationResult{warning: fmt.Sprintf("player statistics %s: %v", statsRef, err)}
|
|
330
|
+
return
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
categories, err := NormalizeStatCategories(statsDoc.Body)
|
|
334
|
+
if err != nil {
|
|
335
|
+
results[index] = playerHydrationResult{warning: fmt.Sprintf("player statistics %s: %v", statsDoc.CanonicalRef, err)}
|
|
336
|
+
return
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
batting, bowling, fielding := splitPlayerStatCategories(categories)
|
|
340
|
+
playerMatch := PlayerMatch{
|
|
341
|
+
PlayerID: playerID,
|
|
342
|
+
PlayerRef: entry.PlayerRef,
|
|
343
|
+
PlayerName: nonEmpty(entry.DisplayName, "Unknown Player"),
|
|
344
|
+
MatchID: match.ID,
|
|
345
|
+
CompetitionID: nonEmpty(match.CompetitionID, match.ID),
|
|
346
|
+
EventID: match.EventID,
|
|
347
|
+
LeagueID: match.LeagueID,
|
|
348
|
+
TeamID: team.ID,
|
|
349
|
+
TeamName: nonEmpty(team.ShortName, team.Name, "Unknown Team"),
|
|
350
|
+
StatisticsRef: statsDoc.CanonicalRef,
|
|
351
|
+
LinescoresRef: rosterPlayerLinescoresRef(match, team, entry),
|
|
352
|
+
Batting: batting,
|
|
353
|
+
Bowling: bowling,
|
|
354
|
+
Fielding: fielding,
|
|
355
|
+
Summary: summarizePlayerMatchCategories(categories),
|
|
356
|
+
}
|
|
357
|
+
results[index] = playerHydrationResult{item: &playerMatch}
|
|
358
|
+
}(i, entry, team)
|
|
359
|
+
}
|
|
360
|
+
wg.Wait()
|
|
361
|
+
|
|
362
|
+
for _, result := range results {
|
|
363
|
+
if strings.TrimSpace(result.warning) != "" {
|
|
364
|
+
warnings = append(warnings, result.warning)
|
|
320
365
|
continue
|
|
321
366
|
}
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
PlayerID: playerID,
|
|
326
|
-
PlayerRef: entry.PlayerRef,
|
|
327
|
-
PlayerName: nonEmpty(entry.DisplayName, "Unknown Player"),
|
|
328
|
-
MatchID: match.ID,
|
|
329
|
-
CompetitionID: nonEmpty(match.CompetitionID, match.ID),
|
|
330
|
-
EventID: match.EventID,
|
|
331
|
-
LeagueID: match.LeagueID,
|
|
332
|
-
TeamID: team.ID,
|
|
333
|
-
TeamName: nonEmpty(team.ShortName, team.Name, "Unknown Team"),
|
|
334
|
-
StatisticsRef: statsDoc.CanonicalRef,
|
|
335
|
-
LinescoresRef: rosterPlayerLinescoresRef(match, team, entry),
|
|
336
|
-
Batting: batting,
|
|
337
|
-
Bowling: bowling,
|
|
338
|
-
Fielding: fielding,
|
|
339
|
-
Summary: summarizePlayerMatchCategories(categories),
|
|
340
|
-
})
|
|
367
|
+
if result.item != nil {
|
|
368
|
+
items = append(items, *result.item)
|
|
369
|
+
}
|
|
341
370
|
}
|
|
342
371
|
}
|
|
343
372
|
|
|
@@ -1397,23 +1426,34 @@ func (s *HistoricalScopeSession) resolve(ctx context.Context, ref string) (*Reso
|
|
|
1397
1426
|
return nil, fmt.Errorf("ref is empty")
|
|
1398
1427
|
}
|
|
1399
1428
|
|
|
1429
|
+
s.resolveMu.Lock()
|
|
1400
1430
|
if cached, ok := s.resolvedDocs[ref]; ok {
|
|
1401
1431
|
s.metrics.ResolveCacheHits++
|
|
1432
|
+
s.resolveMu.Unlock()
|
|
1402
1433
|
return cached, nil
|
|
1403
1434
|
}
|
|
1435
|
+
s.resolveMu.Unlock()
|
|
1404
1436
|
|
|
1405
1437
|
resolved, err := s.client.ResolveRefChain(ctx, ref)
|
|
1406
1438
|
if err != nil {
|
|
1407
1439
|
return nil, err
|
|
1408
1440
|
}
|
|
1409
|
-
s.metrics.ResolveCacheMisses++
|
|
1410
1441
|
|
|
1411
1442
|
copied := *resolved
|
|
1412
1443
|
pointer := &copied
|
|
1413
1444
|
keys := compactWarnings([]string{ref, copied.RequestedRef, copied.CanonicalRef})
|
|
1445
|
+
|
|
1446
|
+
s.resolveMu.Lock()
|
|
1447
|
+
if cached, ok := s.resolvedDocs[ref]; ok {
|
|
1448
|
+
s.metrics.ResolveCacheHits++
|
|
1449
|
+
s.resolveMu.Unlock()
|
|
1450
|
+
return cached, nil
|
|
1451
|
+
}
|
|
1452
|
+
s.metrics.ResolveCacheMisses++
|
|
1414
1453
|
for _, key := range keys {
|
|
1415
1454
|
s.resolvedDocs[key] = pointer
|
|
1416
1455
|
}
|
|
1456
|
+
s.resolveMu.Unlock()
|
|
1417
1457
|
return pointer, nil
|
|
1418
1458
|
}
|
|
1419
1459
|
|