From 121dd6ac62226e0cc6369e0b3db70de37786e3d2 Mon Sep 17 00:00:00 2001 From: Thibault Duplessis Date: Fri, 8 Nov 2024 10:25:54 +0100 Subject: [PATCH] add game source to insights dimensions - closes #16363 --- modules/core/src/main/game/misc.scala | 3 ++- modules/game/src/main/BSONHandlers.scala | 6 ++--- modules/insight/src/main/BSONHandlers.scala | 4 ++++ .../insight/src/main/InsightDimension.scala | 22 +++++++++++++++++-- modules/insight/src/main/InsightEntry.scala | 3 +++ modules/insight/src/main/JsonQuestion.scala | 2 ++ modules/insight/src/main/JsonView.scala | 3 ++- modules/insight/src/main/PovToEntry.scala | 1 + ui/insight/src/table.ts | 8 +++---- 9 files changed, 41 insertions(+), 11 deletions(-) diff --git a/modules/core/src/main/game/misc.scala b/modules/core/src/main/game/misc.scala index c9cb32276f1a..6c503f646657 100644 --- a/modules/core/src/main/game/misc.scala +++ b/modules/core/src/main/game/misc.scala @@ -66,13 +66,14 @@ enum Source(val id: Int) derives Eq: case Arena extends Source(id = 5) case Position extends Source(id = 6) case Import extends Source(id = 7) - case ImportLive extends Source(id = 9) + case ImportLive extends Source(id = 9) // wut? case Simul extends Source(id = 10) case Pool extends Source(id = 12) case Swiss extends Source(id = 13) object Source: val byId = values.mapBy(_.id) + val byName = values.mapBy(_.name) val searchable = List(Lobby, Friend, Ai, Position, Arena, Simul, Pool, Swiss) val expirable = Set(Lobby, Arena, Pool, Swiss) def apply(id: Int): Option[Source] = byId.get(id) diff --git a/modules/game/src/main/BSONHandlers.scala b/modules/game/src/main/BSONHandlers.scala index 1ba0169bc172..c2de8f3ab07b 100644 --- a/modules/game/src/main/BSONHandlers.scala +++ b/modules/game/src/main/BSONHandlers.scala @@ -56,7 +56,7 @@ object BSONHandlers: given BSONHandler[GameRule] = valueMapHandler[String, GameRule](GameRule.byKey)(_.toString) - given BSONHandler[Source] = valueMapHandler[Int, Source](Source.byId)(_.id) + given sourceHandler: BSONHandler[Source] = valueMapHandler[Int, Source](Source.byId)(_.id) private[game] given crazyhouseDataHandler: BSON[Crazyhouse.Data] with import Crazyhouse.* @@ -221,9 +221,9 @@ object BSONHandlers: F.status -> o.status, F.turns -> o.chess.ply, F.startedAtTurn -> w.intO(o.chess.startedAtPly.value), - F.clock -> (o.chess.clock.flatMap { c => + F.clock -> o.chess.clock.flatMap { c => clockBSONWrite(o.createdAt, c).toOption - }), + }, F.daysPerTurn -> o.daysPerTurn, F.moveTimes -> o.binaryMoveTimes, F.whiteClockHistory -> clockHistory(Color.White, o.clockHistory, o.chess.clock, o.flagged), diff --git a/modules/insight/src/main/BSONHandlers.scala b/modules/insight/src/main/BSONHandlers.scala index 2276fe199b2e..69b801f2a0f3 100644 --- a/modules/insight/src/main/BSONHandlers.scala +++ b/modules/insight/src/main/BSONHandlers.scala @@ -9,6 +9,8 @@ import lila.db.BSON import lila.db.dsl.{ *, given } import lila.rating.BSONHandlers.perfTypeIdHandler import lila.rating.PerfType +import lila.core.game.Source +import lila.game.BSONHandlers.sourceHandler object BSONHandlers: @@ -102,6 +104,7 @@ object BSONHandlers: ratingDiff = r.get[IntRatingDiff](ratingDiff), analysed = r.boolD(analysed), provisional = r.boolD(provisional), + source = r.getO[Source](source), date = r.date(date) ) def writes(w: BSON.Writer, e: InsightEntry) = @@ -124,5 +127,6 @@ object BSONHandlers: ratingDiff -> e.ratingDiff, analysed -> w.boolO(e.analysed), provisional -> w.boolO(e.provisional), + source -> e.source, date -> e.date ) diff --git a/modules/insight/src/main/InsightDimension.scala b/modules/insight/src/main/InsightDimension.scala index d3d9e6e33549..4a2c5fe88cf6 100644 --- a/modules/insight/src/main/InsightDimension.scala +++ b/modules/insight/src/main/InsightDimension.scala @@ -12,7 +12,9 @@ import lila.db.dsl.{ *, given } import lila.insight.BSONHandlers.given import lila.insight.InsightEntry.BSONFields as F import lila.rating.BSONHandlers.perfTypeIdHandler +import lila.game.BSONHandlers.sourceHandler import lila.rating.PerfType +import lila.core.game.Source enum InsightDimension[A]( val key: String, @@ -231,6 +233,15 @@ enum InsightDimension[A]( "Time left on the player clock, accounting for increment. 100% = full clock, 0% = flagging." ) + case GameSource + extends InsightDimension[Source]( + "source", + "Game source", + F.source, + InsightPosition.Game, + "How the game was created." + ) + object InsightDimension: def requiresStableRating(d: InsightDimension[?]) = d match @@ -260,6 +271,10 @@ object InsightDimension: case ClockPercentRange => lila.insight.ClockPercentRange.all.toList case Blur => lila.insight.Blur.values.toIndexedSeq case TimeVariance => lila.insight.TimeVariance.values.toIndexedSeq + case GameSource => + Source.values.toIndexedSeq.filter: + case Source.Ai | Source.Import | Source.ImportLive => false + case _ => true def valueByKey[X](d: InsightDimension[X], key: String): Option[X] = d match case Period => key.toIntOption.map(lila.insight.Period.apply) @@ -284,6 +299,7 @@ object InsightDimension: case ClockPercentRange => key.toIntOption.flatMap(lila.insight.ClockPercentRange.byPercent.get) case Blur => lila.insight.Blur(key == "true").some case TimeVariance => key.toFloatOption.map(lila.insight.TimeVariance.byId) + case GameSource => Source.byName.get(key) def valueToJson[X](d: InsightDimension[X])(v: X)(using Translate): JsObject = Json.obj( @@ -292,7 +308,7 @@ object InsightDimension: ) def valueKey[X](d: InsightDimension[X])(v: X): String = - (d match + d.match case Date => v.toString case Period => v.days.toString case Perf => v.key @@ -315,7 +331,8 @@ object InsightDimension: case ClockPercentRange => v.bottom.toInt case Blur => v.id case TimeVariance => v.id - ).toString + case GameSource => v.name + .toString def valueJson[X](d: InsightDimension[X])(v: X)(using Translate): JsValue = d match @@ -341,6 +358,7 @@ object InsightDimension: case ClockPercentRange => JsString(v.name) case Blur => JsString(v.name) case TimeVariance => JsString(v.name) + case GameSource => JsString(v.toString) def filtersOf[X](d: InsightDimension[X], selected: List[X]): Bdoc = diff --git a/modules/insight/src/main/InsightEntry.scala b/modules/insight/src/main/InsightEntry.scala index b615ad779f81..5a479feeeca6 100644 --- a/modules/insight/src/main/InsightEntry.scala +++ b/modules/insight/src/main/InsightEntry.scala @@ -1,6 +1,7 @@ package lila.insight import lila.common.SimpleOpening +import lila.core.game.Source case class InsightEntry( id: String, // gameId + w/b @@ -20,6 +21,7 @@ case class InsightEntry( ratingDiff: IntRatingDiff, analysed: Boolean, provisional: Boolean, + source: Option[Source], date: Instant ) @@ -48,4 +50,5 @@ case object InsightEntry: val ratingDiff = "rd" val analysed = "a" val provisional = "pr" + val source = "so" val date = "d" diff --git a/modules/insight/src/main/JsonQuestion.scala b/modules/insight/src/main/JsonQuestion.scala index 9c92eed00038..1c4acefc9d91 100644 --- a/modules/insight/src/main/JsonQuestion.scala +++ b/modules/insight/src/main/JsonQuestion.scala @@ -46,6 +46,7 @@ case class JsonQuestion( case ClockPercentRange.key => build(ClockPercentRange) case Blur.key => build(Blur) case TimeVariance.key => build(TimeVariance) + case GameSource.key => build(GameSource) case _ => none } .filterNot(_.isEmpty) @@ -75,6 +76,7 @@ case class JsonQuestion( case AccuracyPercentRange.key => build(AccuracyPercentRange) case Blur.key => build(Blur) case TimeVariance.key => build(TimeVariance) + case GameSource.key => build(GameSource) case _ => none yield question diff --git a/modules/insight/src/main/JsonView.scala b/modules/insight/src/main/JsonView.scala index b54d7af4459d..9785020dedb6 100644 --- a/modules/insight/src/main/JsonView.scala +++ b/modules/insight/src/main/JsonView.scala @@ -38,7 +38,8 @@ final class JsonView: dimWrites.writes(D.Period), dimWrites.writes(D.Perf), dimWrites.writes(D.Color), - dimWrites.writes(D.OpponentStrength) + dimWrites.writes(D.OpponentStrength), + dimWrites.writes(D.GameSource) ) ), Categ( diff --git a/modules/insight/src/main/PovToEntry.scala b/modules/insight/src/main/PovToEntry.scala index 934fc4a22dc8..615988e826cb 100644 --- a/modules/insight/src/main/PovToEntry.scala +++ b/modules/insight/src/main/PovToEntry.scala @@ -205,6 +205,7 @@ final private class PovToEntry( ratingDiff = ~pov.player.ratingDiff, analysed = analysis.isDefined, provisional = provisional, + source = game.source, date = game.createdAt ) diff --git a/ui/insight/src/table.ts b/ui/insight/src/table.ts index b5af62f82474..6ef068a61581 100644 --- a/ui/insight/src/table.ts +++ b/ui/insight/src/table.ts @@ -30,13 +30,13 @@ export function vert(ctrl: Ctrl, attrs: any = null) { ), h( 'tbody', - answer.xAxis.categories.map((c, i) => { - return h('tr', [ + answer.xAxis.categories.map((c, i) => + h('tr', [ h('th', formatSerieName(answer.xAxis.dataType, c)), ...answer.series.map(serie => h('td.data', formatNumber(serie.dataType, serie.data[i]))), h('td.size', formatNumber(answer.sizeSerie.dataType, answer.sizeSerie.data[i])), - ]); - }), + ]), + ), ), ]), );