da0e6d1ee2
Build & Test (NowChessSystems) TeamCity build failed
Remove isPgnMode JDBC guard from all 4 original jobs so staging (Lichess PGN mode) and production (game_records JDBC mode) both persist analytics results to the DB. Add JDBC write-back to all 7 new jobs: - GameLengthJob → analytics_game_length_distribution + analytics_game_length_by_result - ColorAdvantageJob → analytics_color_advantage - EloDistributionJob → analytics_elo_distribution - TimeControlJob → analytics_time_control_stats - DailyActivityJob → analytics_hourly_activity + analytics_weekly_activity - RatingMismatchJob → analytics_rating_mismatch - TerminationStatsJob → analytics_termination_stats Add analytics_component_sizes JDBC write to PlayerGraphJob. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
69 lines
2.5 KiB
Scala
69 lines
2.5 KiB
Scala
package de.nowchess.analytics
|
|
|
|
import org.apache.spark.sql.SparkSession
|
|
import org.apache.spark.sql.functions as F
|
|
|
|
object TimeControlJob:
|
|
|
|
def main(args: Array[String]): Unit =
|
|
val jdbcUrl = sys.env.getOrElse("NOWCHESS_JDBC_URL", "jdbc:postgresql://localhost:5432/nowchess")
|
|
val dbUser = sys.env.getOrElse("NOWCHESS_DB_USER", "nowchess")
|
|
val dbPass = sys.env.getOrElse("NOWCHESS_DB_PASS", "nowchess")
|
|
val outputDir = if args.length > 0 then args(0) else "/tmp/nowchess-time-control"
|
|
|
|
val spark = SparkSession
|
|
.builder()
|
|
.appName("NowChess Time Control")
|
|
.getOrCreate()
|
|
|
|
run(spark, jdbcUrl, dbUser, dbPass, outputDir)
|
|
spark.stop()
|
|
|
|
def run(spark: SparkSession, jdbcUrl: String, dbUser: String, dbPass: String, outputDir: String): Unit =
|
|
val games = GameSource
|
|
.loadExtended(spark, jdbcUrl, dbUser, dbPass)
|
|
.select("result", "time_control")
|
|
.filter(
|
|
F.col("time_control").isNotNull
|
|
.and(F.col("time_control") =!= "")
|
|
.and(F.col("time_control") =!= "-"),
|
|
)
|
|
|
|
val baseSeconds = F.regexp_extract(F.col("time_control"), "^(?:\\d+/)?(\\d+)", 1).cast("int")
|
|
val category = F
|
|
.when(baseSeconds < 30, "UltraBullet")
|
|
.when(baseSeconds < 180, "Bullet")
|
|
.when(baseSeconds < 480, "Blitz")
|
|
.when(baseSeconds < 1500, "Rapid")
|
|
.when(baseSeconds < 86400, "Classical")
|
|
.otherwise("Correspondence")
|
|
|
|
val stats = games
|
|
.withColumn("category", category)
|
|
.groupBy("category")
|
|
.agg(
|
|
F.count("*").as("total_games"),
|
|
F.sum(F.when(F.col("result") === "white", 1).otherwise(0)).as("white_wins"),
|
|
F.sum(F.when(F.col("result") === "black", 1).otherwise(0)).as("black_wins"),
|
|
F.sum(F.when(F.col("result") === "draw", 1).otherwise(0)).as("draws"),
|
|
)
|
|
.withColumn("white_win_rate", F.round(F.col("white_wins") / F.col("total_games").cast("double"), 3))
|
|
.withColumn("draw_rate", F.round(F.col("draws") / F.col("total_games").cast("double"), 3))
|
|
.orderBy(F.desc("total_games"))
|
|
.select("category", "total_games", "white_wins", "black_wins", "draws", "white_win_rate", "draw_rate")
|
|
|
|
stats.write
|
|
.mode("overwrite")
|
|
.option("header", "true")
|
|
.csv(s"$outputDir/time_control_stats")
|
|
|
|
stats.write
|
|
.mode("overwrite")
|
|
.format("jdbc")
|
|
.option("url", jdbcUrl)
|
|
.option("dbtable", "analytics_time_control_stats")
|
|
.option("user", dbUser)
|
|
.option("password", dbPass)
|
|
.option("driver", "org.postgresql.Driver")
|
|
.save()
|