cricinfo-cli-go 0.1.0 → 0.1.2
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 +30 -2
- package/internal/cli/matches_test.go +11 -0
- package/internal/cricinfo/analysis.go +216 -46
- package/internal/cricinfo/analysis_phase15_test.go +38 -0
- package/internal/cricinfo/historical_hydration.go +82 -42
- package/internal/cricinfo/matches.go +582 -43
- package/internal/cricinfo/normalize_entities.go +16 -0
- package/internal/cricinfo/render_contract.go +52 -12
- package/internal/cricinfo/renderer.go +196 -11
- package/internal/cricinfo/resolver.go +53 -9
- package/package.json +1 -1
|
@@ -1033,6 +1033,16 @@ func NormalizePartnership(data []byte) (*Partnership, error) {
|
|
|
1033
1033
|
),
|
|
1034
1034
|
}
|
|
1035
1035
|
|
|
1036
|
+
if partnership.WicketNumber == 0 {
|
|
1037
|
+
partnership.WicketNumber = parseInt(ids["partnershipId"])
|
|
1038
|
+
}
|
|
1039
|
+
if partnership.Runs == 0 && partnership.End.Runs > partnership.Start.Runs {
|
|
1040
|
+
partnership.Runs = partnership.End.Runs - partnership.Start.Runs
|
|
1041
|
+
}
|
|
1042
|
+
if partnership.Overs == 0 && partnership.End.Overs > partnership.Start.Overs {
|
|
1043
|
+
partnership.Overs = partnership.End.Overs - partnership.Start.Overs
|
|
1044
|
+
}
|
|
1045
|
+
|
|
1036
1046
|
return partnership, nil
|
|
1037
1047
|
}
|
|
1038
1048
|
|
|
@@ -1097,6 +1107,12 @@ func NormalizeFallOfWicket(data []byte) (*FallOfWicket, error) {
|
|
|
1097
1107
|
if fow.WicketNumber == 0 {
|
|
1098
1108
|
fow.WicketNumber = parseInt(ids["fowId"])
|
|
1099
1109
|
}
|
|
1110
|
+
if fow.Runs == 0 && fow.RunsScored > 0 {
|
|
1111
|
+
fow.Runs = fow.RunsScored
|
|
1112
|
+
}
|
|
1113
|
+
if fow.RunsScored == 0 && fow.Runs > 0 {
|
|
1114
|
+
fow.RunsScored = fow.Runs
|
|
1115
|
+
}
|
|
1100
1116
|
|
|
1101
1117
|
return fow, nil
|
|
1102
1118
|
}
|
|
@@ -16,6 +16,7 @@ const (
|
|
|
16
16
|
EntityMatch EntityKind = "match"
|
|
17
17
|
EntityMatchScorecard EntityKind = "match_scorecard"
|
|
18
18
|
EntityMatchSituation EntityKind = "match_situation"
|
|
19
|
+
EntityMatchPhases EntityKind = "match_phases"
|
|
19
20
|
EntityCompetition EntityKind = "competition"
|
|
20
21
|
EntityCompOfficial EntityKind = "competition_official"
|
|
21
22
|
EntityCompBroadcast EntityKind = "competition_broadcast"
|
|
@@ -118,6 +119,43 @@ type MatchScorecard struct {
|
|
|
118
119
|
Extensions map[string]any `json:"extensions,omitempty"`
|
|
119
120
|
}
|
|
120
121
|
|
|
122
|
+
// MatchPhases is a fan-oriented phase/momentum breakdown for each innings.
|
|
123
|
+
type MatchPhases struct {
|
|
124
|
+
MatchID string `json:"matchId,omitempty"`
|
|
125
|
+
LeagueID string `json:"leagueId,omitempty"`
|
|
126
|
+
EventID string `json:"eventId,omitempty"`
|
|
127
|
+
CompetitionID string `json:"competitionId,omitempty"`
|
|
128
|
+
Fixture string `json:"fixture,omitempty"`
|
|
129
|
+
Result string `json:"result,omitempty"`
|
|
130
|
+
Innings []MatchPhaseInning `json:"innings,omitempty"`
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// MatchPhaseInning captures phase splits and momentum points for one innings.
|
|
134
|
+
type MatchPhaseInning struct {
|
|
135
|
+
TeamID string `json:"teamId,omitempty"`
|
|
136
|
+
TeamName string `json:"teamName,omitempty"`
|
|
137
|
+
InningsNumber int `json:"inningsNumber,omitempty"`
|
|
138
|
+
Period int `json:"period,omitempty"`
|
|
139
|
+
Score string `json:"score,omitempty"`
|
|
140
|
+
Target int `json:"target,omitempty"`
|
|
141
|
+
Powerplay PhaseSummary `json:"powerplay,omitempty"`
|
|
142
|
+
Middle PhaseSummary `json:"middle,omitempty"`
|
|
143
|
+
Death PhaseSummary `json:"death,omitempty"`
|
|
144
|
+
BestScoringOver int `json:"bestScoringOver,omitempty"`
|
|
145
|
+
BestScoringOverRuns int `json:"bestScoringOverRuns,omitempty"`
|
|
146
|
+
CollapseOver int `json:"collapseOver,omitempty"`
|
|
147
|
+
CollapseWickets int `json:"collapseWickets,omitempty"`
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// PhaseSummary is a normalized run/wicket split across a phase bucket.
|
|
151
|
+
type PhaseSummary struct {
|
|
152
|
+
Name string `json:"name,omitempty"`
|
|
153
|
+
Runs int `json:"runs,omitempty"`
|
|
154
|
+
Wickets int `json:"wickets,omitempty"`
|
|
155
|
+
Overs float64 `json:"overs,omitempty"`
|
|
156
|
+
RunRate float64 `json:"runRate,omitempty"`
|
|
157
|
+
}
|
|
158
|
+
|
|
121
159
|
// BattingCard is a normalized batting card section from matchcards.
|
|
122
160
|
type BattingCard struct {
|
|
123
161
|
InningsNumber int `json:"inningsNumber,omitempty"`
|
|
@@ -774,18 +812,18 @@ type FallOfWicket struct {
|
|
|
774
812
|
|
|
775
813
|
// AnalysisScope captures the resolved analysis traversal scope.
|
|
776
814
|
type AnalysisScope struct {
|
|
777
|
-
Mode
|
|
778
|
-
RequestedLeagueID string
|
|
779
|
-
LeagueID
|
|
780
|
-
LeagueName
|
|
781
|
-
Seasons
|
|
782
|
-
MatchIDs
|
|
783
|
-
MatchCount
|
|
784
|
-
DateFrom
|
|
785
|
-
DateTo
|
|
786
|
-
TypeQuery
|
|
787
|
-
GroupQuery
|
|
788
|
-
HydrationMetric
|
|
815
|
+
Mode string `json:"mode"`
|
|
816
|
+
RequestedLeagueID string `json:"requestedLeagueId,omitempty"`
|
|
817
|
+
LeagueID string `json:"leagueId,omitempty"`
|
|
818
|
+
LeagueName string `json:"leagueName,omitempty"`
|
|
819
|
+
Seasons []string `json:"seasons,omitempty"`
|
|
820
|
+
MatchIDs []string `json:"matchIds,omitempty"`
|
|
821
|
+
MatchCount int `json:"matchCount"`
|
|
822
|
+
DateFrom string `json:"dateFrom,omitempty"`
|
|
823
|
+
DateTo string `json:"dateTo,omitempty"`
|
|
824
|
+
TypeQuery string `json:"type,omitempty"`
|
|
825
|
+
GroupQuery string `json:"group,omitempty"`
|
|
826
|
+
HydrationMetric HydrationMetrics `json:"hydrationMetrics,omitempty"`
|
|
789
827
|
}
|
|
790
828
|
|
|
791
829
|
// AnalysisFilters captures user-level row filters.
|
|
@@ -924,6 +962,8 @@ func kindPlural(kind EntityKind) string {
|
|
|
924
962
|
return "match scorecards"
|
|
925
963
|
case EntityMatchSituation:
|
|
926
964
|
return "match situations"
|
|
965
|
+
case EntityMatchPhases:
|
|
966
|
+
return "match phase reports"
|
|
927
967
|
case EntityCompetition:
|
|
928
968
|
return "competitions"
|
|
929
969
|
case EntityCompOfficial:
|
|
@@ -255,6 +255,15 @@ func renderText(w io.Writer, result NormalizedResult, opts RenderOptions) error
|
|
|
255
255
|
lines = append(lines, formatMatchView(itemMap)...)
|
|
256
256
|
return writeTextLines(w, lines)
|
|
257
257
|
}
|
|
258
|
+
if result.Kind == EntityMatchPhases {
|
|
259
|
+
itemMap, err := toMap(result.Data, opts.AllFields)
|
|
260
|
+
if err != nil {
|
|
261
|
+
return err
|
|
262
|
+
}
|
|
263
|
+
lines = append(lines, "Match Phases")
|
|
264
|
+
lines = append(lines, formatMatchPhases(itemMap)...)
|
|
265
|
+
return writeTextLines(w, lines)
|
|
266
|
+
}
|
|
258
267
|
|
|
259
268
|
itemMap, err := toMap(result.Data, opts.AllFields)
|
|
260
269
|
if err != nil {
|
|
@@ -274,13 +283,41 @@ func renderText(w io.Writer, result NormalizedResult, opts RenderOptions) error
|
|
|
274
283
|
return writeTextLines(w, lines)
|
|
275
284
|
}
|
|
276
285
|
|
|
277
|
-
|
|
278
|
-
|
|
286
|
+
if result.Kind == EntityDeliveryEvent || result.Kind == EntityPlayerDelivery {
|
|
287
|
+
lines = append(lines, fmt.Sprintf("%s (%d)", titleize(kindPlural(result.Kind)), len(result.Items)))
|
|
288
|
+
for i, item := range result.Items {
|
|
289
|
+
summary := summarizeDeliveryListItem(item, result.Kind)
|
|
290
|
+
if strings.TrimSpace(summary) == "" {
|
|
291
|
+
continue
|
|
292
|
+
}
|
|
293
|
+
lines = append(lines, fmt.Sprintf("%d. %s", i+1, summary))
|
|
294
|
+
}
|
|
295
|
+
return writeTextLines(w, lines)
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
summaries := make([]string, 0, len(result.Items))
|
|
299
|
+
for _, item := range result.Items {
|
|
279
300
|
itemMap, err := toMap(item, opts.AllFields)
|
|
280
301
|
if err != nil {
|
|
281
302
|
return err
|
|
282
303
|
}
|
|
283
|
-
|
|
304
|
+
summary := summarizeEntity(itemMap, result.Kind, opts.Verbose)
|
|
305
|
+
if strings.TrimSpace(summary) == "" {
|
|
306
|
+
continue
|
|
307
|
+
}
|
|
308
|
+
summaries = append(summaries, summary)
|
|
309
|
+
}
|
|
310
|
+
if len(summaries) == 0 {
|
|
311
|
+
message := result.Message
|
|
312
|
+
if message == "" {
|
|
313
|
+
message = fmt.Sprintf("No %s found.", kindPlural(result.Kind))
|
|
314
|
+
}
|
|
315
|
+
lines = append(lines, sentenceCase(message))
|
|
316
|
+
return writeTextLines(w, lines)
|
|
317
|
+
}
|
|
318
|
+
lines = append(lines, fmt.Sprintf("%s (%d)", titleize(kindPlural(result.Kind)), len(summaries)))
|
|
319
|
+
for i, summary := range summaries {
|
|
320
|
+
lines = append(lines, fmt.Sprintf("%d. %s", i+1, summary))
|
|
284
321
|
}
|
|
285
322
|
|
|
286
323
|
return writeTextLines(w, lines)
|
|
@@ -295,6 +332,53 @@ func writeTextLines(w io.Writer, lines []string) error {
|
|
|
295
332
|
return nil
|
|
296
333
|
}
|
|
297
334
|
|
|
335
|
+
func summarizeDeliveryListItem(item any, kind EntityKind) string {
|
|
336
|
+
switch typed := item.(type) {
|
|
337
|
+
case DeliveryEvent:
|
|
338
|
+
short := firstNonEmpty(strings.TrimSpace(typed.ShortText), strings.TrimSpace(typed.Text))
|
|
339
|
+
if short == "" {
|
|
340
|
+
short = joinParts("over "+intString(typed.OverNumber), "ball "+intString(typed.BallNumber))
|
|
341
|
+
}
|
|
342
|
+
if kind == EntityPlayerDelivery {
|
|
343
|
+
return joinParts(short, strings.Join(typed.Involvement, ","), overBallString(typed.OverNumber, typed.BallNumber))
|
|
344
|
+
}
|
|
345
|
+
return short
|
|
346
|
+
case map[string]any:
|
|
347
|
+
short := firstNonEmpty(valueString(typed, "shortText"), valueString(typed, "text"))
|
|
348
|
+
if short == "" {
|
|
349
|
+
short = joinParts("over "+valueString(typed, "overNumber"), "ball "+valueString(typed, "ballNumber"))
|
|
350
|
+
}
|
|
351
|
+
if kind == EntityPlayerDelivery {
|
|
352
|
+
return joinParts(short, involvementLabel(typed), overBallLabel(typed))
|
|
353
|
+
}
|
|
354
|
+
return short
|
|
355
|
+
default:
|
|
356
|
+
return ""
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
func overBallString(over, ball int) string {
|
|
361
|
+
if over <= 0 || ball <= 0 {
|
|
362
|
+
return ""
|
|
363
|
+
}
|
|
364
|
+
return fmt.Sprintf("over %d.%d", over, ball)
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
func intString(value int) string {
|
|
368
|
+
if value <= 0 {
|
|
369
|
+
return ""
|
|
370
|
+
}
|
|
371
|
+
return fmt.Sprintf("%d", value)
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
func defaultNumeric(raw string) string {
|
|
375
|
+
raw = strings.TrimSpace(raw)
|
|
376
|
+
if raw == "" {
|
|
377
|
+
return "0"
|
|
378
|
+
}
|
|
379
|
+
return raw
|
|
380
|
+
}
|
|
381
|
+
|
|
298
382
|
func sanitizeValue(value any, allFields bool) (any, error) {
|
|
299
383
|
blob, err := json.Marshal(value)
|
|
300
384
|
if err != nil {
|
|
@@ -380,6 +464,11 @@ func summarizeEntity(entity map[string]any, kind EntityKind, verbose bool) strin
|
|
|
380
464
|
return joinParts("situation", data)
|
|
381
465
|
}
|
|
382
466
|
return joinParts("situation", valueString(entity, "competitionId"))
|
|
467
|
+
case EntityMatchPhases:
|
|
468
|
+
return joinParts(
|
|
469
|
+
"match "+firstNonEmpty(valueString(entity, "matchId"), valueString(entity, "competitionId")),
|
|
470
|
+
fmt.Sprintf("%d innings", len(sliceValue(entity, "innings"))),
|
|
471
|
+
)
|
|
383
472
|
case EntityCompetition:
|
|
384
473
|
return joinParts(
|
|
385
474
|
firstNonEmpty(valueString(entity, "shortDescription"), valueString(entity, "description"), valueString(entity, "id")),
|
|
@@ -470,16 +559,26 @@ func summarizeEntity(entity map[string]any, kind EntityKind, verbose bool) strin
|
|
|
470
559
|
case EntityStandingsGroup:
|
|
471
560
|
return joinParts(valueString(entity, "id"), "season "+valueString(entity, "seasonId"))
|
|
472
561
|
case EntityInnings:
|
|
562
|
+
if valueString(entity, "score") == "" &&
|
|
563
|
+
valueString(entity, "runs") == "" &&
|
|
564
|
+
valueString(entity, "wickets") == "" &&
|
|
565
|
+
valueString(entity, "target") == "" &&
|
|
566
|
+
valueString(entity, "isBatting") == "false" {
|
|
567
|
+
return ""
|
|
568
|
+
}
|
|
473
569
|
score := valueString(entity, "score")
|
|
474
570
|
if score == "" {
|
|
475
571
|
score = joinParts(valueString(entity, "runs")+"/"+valueString(entity, "wickets"), valueString(entity, "overs")+" ov")
|
|
476
572
|
}
|
|
477
|
-
|
|
573
|
+
parts := []string{
|
|
478
574
|
firstNonEmpty(valueString(entity, "teamName"), valueString(entity, "teamId")),
|
|
479
|
-
"innings "+valueString(entity, "inningsNumber")+"/"+valueString(entity, "period"),
|
|
575
|
+
"innings " + valueString(entity, "inningsNumber") + "/" + valueString(entity, "period"),
|
|
480
576
|
score,
|
|
481
|
-
|
|
482
|
-
)
|
|
577
|
+
}
|
|
578
|
+
if wc := len(sliceValue(entity, "wicketTimeline")); wc > 0 {
|
|
579
|
+
parts = append(parts, fmt.Sprintf("%d wickets", wc))
|
|
580
|
+
}
|
|
581
|
+
return joinParts(parts...)
|
|
483
582
|
case EntityDeliveryEvent:
|
|
484
583
|
short := firstNonEmpty(valueString(entity, "shortText"), valueString(entity, "text"))
|
|
485
584
|
if short == "" {
|
|
@@ -489,16 +588,32 @@ func summarizeEntity(entity map[string]any, kind EntityKind, verbose bool) strin
|
|
|
489
588
|
case EntityStatCategory:
|
|
490
589
|
return joinParts(firstNonEmpty(valueString(entity, "displayName"), valueString(entity, "name")), fmt.Sprintf("%d stats", len(sliceValue(entity, "stats"))))
|
|
491
590
|
case EntityPartnership:
|
|
591
|
+
runsText := ""
|
|
592
|
+
if runs := valueString(entity, "runs"); runs != "" {
|
|
593
|
+
runsText = runs + " runs"
|
|
594
|
+
} else if valueString(entity, "overs") != "" {
|
|
595
|
+
runsText = "0 runs"
|
|
596
|
+
}
|
|
597
|
+
oversText := ""
|
|
598
|
+
if overs := valueString(entity, "overs"); overs != "" {
|
|
599
|
+
oversText = overs + " ov"
|
|
600
|
+
}
|
|
492
601
|
return joinParts(
|
|
493
602
|
firstNonEmpty(valueString(entity, "wicketName"), "partnership "+valueString(entity, "id")),
|
|
494
|
-
|
|
495
|
-
|
|
603
|
+
runsText,
|
|
604
|
+
oversText,
|
|
496
605
|
"innings "+valueString(entity, "inningsId")+"/"+valueString(entity, "period"),
|
|
497
606
|
)
|
|
498
607
|
case EntityFallOfWicket:
|
|
608
|
+
scoreText := ""
|
|
609
|
+
if runs := valueString(entity, "runs"); runs != "" {
|
|
610
|
+
scoreText = runs + "/" + valueString(entity, "wicketNumber")
|
|
611
|
+
} else if valueString(entity, "wicketNumber") == "1" {
|
|
612
|
+
scoreText = "0/1"
|
|
613
|
+
}
|
|
499
614
|
return joinParts(
|
|
500
615
|
"wicket "+valueString(entity, "wicketNumber"),
|
|
501
|
-
|
|
616
|
+
scoreText,
|
|
502
617
|
valueString(entity, "wicketOver")+" ov",
|
|
503
618
|
"innings "+valueString(entity, "inningsId")+"/"+valueString(entity, "period"),
|
|
504
619
|
)
|
|
@@ -609,6 +724,8 @@ func formatSingleEntity(entity map[string]any, kind EntityKind, opts RenderOptio
|
|
|
609
724
|
order = []string{"matchId", "competitionId", "eventId", "leagueId", "battingCards", "bowlingCards", "partnershipCards"}
|
|
610
725
|
case EntityMatchSituation:
|
|
611
726
|
order = []string{"matchId", "competitionId", "eventId", "leagueId", "oddsRef", "data"}
|
|
727
|
+
case EntityMatchPhases:
|
|
728
|
+
order = []string{"matchId", "competitionId", "eventId", "leagueId", "fixture", "result", "innings"}
|
|
612
729
|
case EntityStatCategory:
|
|
613
730
|
order = []string{"name", "displayName", "abbreviation"}
|
|
614
731
|
case EntityPartnership:
|
|
@@ -715,7 +832,7 @@ func formatInningsTimelines(entity map[string]any) []string {
|
|
|
715
832
|
row := joinParts(
|
|
716
833
|
"Over "+valueString(over, "number"),
|
|
717
834
|
valueString(over, "runs")+" runs",
|
|
718
|
-
valueString(over, "wicketCount")
|
|
835
|
+
wicketCountLabel(valueString(over, "wicketCount")),
|
|
719
836
|
)
|
|
720
837
|
if row != "" {
|
|
721
838
|
lines = append(lines, " "+row)
|
|
@@ -754,6 +871,14 @@ func formatInningsTimelines(entity map[string]any) []string {
|
|
|
754
871
|
return lines
|
|
755
872
|
}
|
|
756
873
|
|
|
874
|
+
func wicketCountLabel(raw string) string {
|
|
875
|
+
raw = strings.TrimSpace(raw)
|
|
876
|
+
if raw == "" || raw == "0" {
|
|
877
|
+
return ""
|
|
878
|
+
}
|
|
879
|
+
return raw + " wkts"
|
|
880
|
+
}
|
|
881
|
+
|
|
757
882
|
func formatPlayerProfile(entity map[string]any) []string {
|
|
758
883
|
lines := make([]string, 0, 16)
|
|
759
884
|
if name := firstNonEmpty(valueString(entity, "displayName"), valueString(entity, "fullName"), valueString(entity, "name")); name != "" {
|
|
@@ -867,6 +992,66 @@ func formatMatchView(entity map[string]any) []string {
|
|
|
867
992
|
return lines
|
|
868
993
|
}
|
|
869
994
|
|
|
995
|
+
func formatMatchPhases(entity map[string]any) []string {
|
|
996
|
+
lines := make([]string, 0, 64)
|
|
997
|
+
if matchID := firstNonEmpty(valueString(entity, "matchId"), valueString(entity, "competitionId")); matchID != "" {
|
|
998
|
+
lines = append(lines, "Match: "+matchID)
|
|
999
|
+
}
|
|
1000
|
+
if fixture := valueString(entity, "fixture"); fixture != "" {
|
|
1001
|
+
lines = append(lines, "Fixture: "+fixture)
|
|
1002
|
+
}
|
|
1003
|
+
if result := valueString(entity, "result"); result != "" {
|
|
1004
|
+
lines = append(lines, "Result: "+result)
|
|
1005
|
+
}
|
|
1006
|
+
|
|
1007
|
+
for _, raw := range sliceValue(entity, "innings") {
|
|
1008
|
+
inn, ok := raw.(map[string]any)
|
|
1009
|
+
if !ok {
|
|
1010
|
+
continue
|
|
1011
|
+
}
|
|
1012
|
+
lines = append(lines, "")
|
|
1013
|
+
lines = append(lines, joinParts(
|
|
1014
|
+
firstNonEmpty(valueString(inn, "teamName"), valueString(inn, "teamId")),
|
|
1015
|
+
"innings "+valueString(inn, "inningsNumber")+"/"+valueString(inn, "period"),
|
|
1016
|
+
valueString(inn, "score"),
|
|
1017
|
+
))
|
|
1018
|
+
|
|
1019
|
+
lines = append(lines, " Phases")
|
|
1020
|
+
for _, key := range []string{"powerplay", "middle", "death"} {
|
|
1021
|
+
phaseMap, ok := inn[key].(map[string]any)
|
|
1022
|
+
if !ok {
|
|
1023
|
+
continue
|
|
1024
|
+
}
|
|
1025
|
+
name := firstNonEmpty(valueString(phaseMap, "name"), strings.Title(key))
|
|
1026
|
+
runs := defaultNumeric(valueString(phaseMap, "runs"))
|
|
1027
|
+
wickets := defaultNumeric(valueString(phaseMap, "wickets"))
|
|
1028
|
+
overs := defaultNumeric(valueString(phaseMap, "overs"))
|
|
1029
|
+
runRate := defaultNumeric(valueString(phaseMap, "runRate"))
|
|
1030
|
+
phaseLine := joinParts(
|
|
1031
|
+
name,
|
|
1032
|
+
"runs "+runs,
|
|
1033
|
+
"wkts "+wickets,
|
|
1034
|
+
"ov "+overs,
|
|
1035
|
+
"rr "+runRate,
|
|
1036
|
+
)
|
|
1037
|
+
lines = append(lines, " - "+phaseLine)
|
|
1038
|
+
}
|
|
1039
|
+
|
|
1040
|
+
bestOver := valueString(inn, "bestScoringOver")
|
|
1041
|
+
bestRuns := valueString(inn, "bestScoringOverRuns")
|
|
1042
|
+
if bestOver != "" && bestRuns != "" {
|
|
1043
|
+
lines = append(lines, " Best Over: over "+bestOver+" ("+bestRuns+" runs)")
|
|
1044
|
+
}
|
|
1045
|
+
collapseOver := valueString(inn, "collapseOver")
|
|
1046
|
+
collapseWickets := valueString(inn, "collapseWickets")
|
|
1047
|
+
if collapseOver != "" && collapseWickets != "" && collapseWickets != "0" {
|
|
1048
|
+
lines = append(lines, " Pressure Over: over "+collapseOver+" ("+collapseWickets+" wickets)")
|
|
1049
|
+
}
|
|
1050
|
+
}
|
|
1051
|
+
|
|
1052
|
+
return lines
|
|
1053
|
+
}
|
|
1054
|
+
|
|
870
1055
|
func formatPlayerStatistics(entity map[string]any) []string {
|
|
871
1056
|
lines := make([]string, 0, 64)
|
|
872
1057
|
|
|
@@ -375,6 +375,15 @@ func (r *Resolver) seedCompetitionMap(ctx context.Context, comp map[string]any,
|
|
|
375
375
|
matchName := nonEmpty(stringField(comp, "shortDescription"), stringField(comp, "description"), stringField(comp, "note"), eventName, stringField(comp, "date"))
|
|
376
376
|
matchShort := nonEmpty(stringField(comp, "shortDescription"), eventName)
|
|
377
377
|
|
|
378
|
+
competitors := mapSliceField(comp, "competitors")
|
|
379
|
+
teamAliases := make([]string, 0)
|
|
380
|
+
for _, competitor := range competitors {
|
|
381
|
+
if err := r.seedCompetitorMap(ctx, competitor, leagueID, eventID, competitionID); err != nil {
|
|
382
|
+
return err
|
|
383
|
+
}
|
|
384
|
+
teamAliases = append(teamAliases, r.matchTeamAliasesFromCompetitor(ctx, competitor, leagueID, competitionID)...)
|
|
385
|
+
}
|
|
386
|
+
|
|
378
387
|
if err := r.index.Upsert(IndexedEntity{
|
|
379
388
|
Kind: EntityMatch,
|
|
380
389
|
ID: competitionID,
|
|
@@ -384,32 +393,67 @@ func (r *Resolver) seedCompetitionMap(ctx context.Context, comp map[string]any,
|
|
|
384
393
|
LeagueID: leagueID,
|
|
385
394
|
EventID: eventID,
|
|
386
395
|
MatchID: competitionID,
|
|
387
|
-
Aliases: []string{
|
|
396
|
+
Aliases: append([]string{
|
|
388
397
|
stringField(comp, "description"),
|
|
389
398
|
stringField(comp, "shortDescription"),
|
|
390
399
|
stringField(comp, "note"),
|
|
391
400
|
eventName,
|
|
392
401
|
competitionID,
|
|
393
402
|
eventID,
|
|
394
|
-
},
|
|
403
|
+
}, teamAliases...),
|
|
395
404
|
UpdatedAt: r.now(),
|
|
396
405
|
}); err != nil {
|
|
397
406
|
return err
|
|
398
407
|
}
|
|
399
408
|
|
|
400
|
-
competitors := mapSliceField(comp, "competitors")
|
|
401
|
-
for _, competitor := range competitors {
|
|
402
|
-
if err := r.seedCompetitorMap(ctx, competitor, leagueID, eventID, competitionID); err != nil {
|
|
403
|
-
return err
|
|
404
|
-
}
|
|
405
|
-
}
|
|
406
|
-
|
|
407
409
|
if compRef != "" {
|
|
408
410
|
r.markHydrated(r.absoluteRef(compRef))
|
|
409
411
|
}
|
|
410
412
|
return nil
|
|
411
413
|
}
|
|
412
414
|
|
|
415
|
+
func (r *Resolver) matchTeamAliasesFromCompetitor(ctx context.Context, competitor map[string]any, leagueID, matchID string) []string {
|
|
416
|
+
aliases := []string{
|
|
417
|
+
stringField(mapField(competitor, "team"), "displayName"),
|
|
418
|
+
stringField(mapField(competitor, "team"), "name"),
|
|
419
|
+
stringField(mapField(competitor, "team"), "shortDisplayName"),
|
|
420
|
+
stringField(mapField(competitor, "team"), "shortName"),
|
|
421
|
+
stringField(mapField(competitor, "team"), "abbreviation"),
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
teamRef := refFromField(competitor, "team")
|
|
425
|
+
teamID := nonEmpty(
|
|
426
|
+
refIDs(teamRef)["teamId"],
|
|
427
|
+
stringField(mapField(competitor, "team"), "id"),
|
|
428
|
+
stringField(competitor, "id"),
|
|
429
|
+
)
|
|
430
|
+
if teamID == "" {
|
|
431
|
+
return aliases
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
if existing, ok := r.index.FindByID(EntityTeam, teamID); ok {
|
|
435
|
+
aliases = append(aliases, existing.Name, existing.ShortName)
|
|
436
|
+
}
|
|
437
|
+
if hasNonEmptyAlias(aliases...) {
|
|
438
|
+
return aliases
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
_ = r.seedTeamByID(ctx, teamID, leagueID, matchID)
|
|
442
|
+
if existing, ok := r.index.FindByID(EntityTeam, teamID); ok {
|
|
443
|
+
aliases = append(aliases, existing.Name, existing.ShortName)
|
|
444
|
+
}
|
|
445
|
+
return aliases
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
func hasNonEmptyAlias(values ...string) bool {
|
|
449
|
+
for _, value := range values {
|
|
450
|
+
if strings.TrimSpace(value) != "" {
|
|
451
|
+
return true
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
return false
|
|
455
|
+
}
|
|
456
|
+
|
|
413
457
|
func (r *Resolver) seedCompetitorMap(ctx context.Context, competitor map[string]any, leagueID, eventID, matchID string) error {
|
|
414
458
|
competitorRef := stringField(competitor, "$ref")
|
|
415
459
|
teamRef := refFromField(competitor, "team")
|