diff --git a/pom.xml b/pom.xml
index 39b28cd..6d3fb39 100644
--- a/pom.xml
+++ b/pom.xml
@@ -16,8 +16,8 @@
official
1.0.0
4.12
- 10
- ${java.version}
+ 1.8
+
${java.version}
${java.version}
@@ -44,6 +44,11 @@
0.11.2
+
+ com.sportradar
+ sportradar-common
+ 2.0.1
+
org.jetbrains.kotlin
kotlin-test-junit
diff --git a/src/main/kotlin/sportradar/history/recover/HistoryRecoveryTool.kt b/src/main/kotlin/sportradar/history/recover/HistoryRecoveryTool.kt
new file mode 100644
index 0000000..aa29326
--- /dev/null
+++ b/src/main/kotlin/sportradar/history/recover/HistoryRecoveryTool.kt
@@ -0,0 +1,106 @@
+package sportradar.history.recover
+
+import com.betradar.database.HistoryDB
+import com.betradar.database.SystemDB
+import com.betradar.util.LogUtil
+import org.apache.log4j.Logger
+import sportradar.history.recover.util.RequestUtil
+import sportradar.kotlin.workshop.examples._14LambdaWithReceiver.use
+import java.sql.SQLException
+import java.time.Instant
+import java.time.LocalDateTime
+import java.time.ZoneId
+
+
+/**
+ * converted from https://gitlab.sportradar.ag/livescore/common/tree/master/src/main/java/com/oddsarbitrage/livescore/history/recover
+ * using mostly intellij's convert java file to kotlin ctrl + alt + shift + k
+ * @author j.kalvatn
+ */
+class HistoryRecoveryTool {
+ private val loader: Loader = Loader()
+ private val saver: Saver = Saver()
+ private val log = Logger.getLogger(HistoryRecoveryTool::class.java)
+
+
+ fun runHBLRecovery() {
+ // 3299, 212, 3468, 3469, 688, 95, 4473,16956
+ val allStarGame = RecoveryTask(
+ tournamentId = 3299,
+ fromDate = LocalDateTime.of(2017, 2, 3, 0, 0).atZone(ZoneId.systemDefault()).toInstant(),
+ toDate = Instant.now()
+ )
+// val dhbPokal = RecoveryTask(
+// tournamentId = 212,
+// fromDate = LocalDateTime.of(2017, 8, 18, 0, 0).atZone(ZoneId.systemDefault()).toInstant(),
+// toDate = Instant.now()
+// )
+
+
+ runRecoveryTasks(listOf(allStarGame))
+ }
+
+ private fun runRecoveryTasks(tasks: List) {
+ try {
+ HistoryDB.instance().borrowConnection().use { historyConn ->
+ SystemDB.instance().borrowConnection().use { writeConn ->
+ for (task in tasks) {
+ log.info(task)
+ val tournamentId = task.tournamentId
+ val fromDate = task.fromDate
+ val toDate = task.toDate
+
+ val matches = loader.loadMatches(historyConn, fromDate, toDate, tournamentId)
+ val matchIds = matches.keys
+
+ val matchExtras = loader.loadMatchExtra(historyConn, matchIds)
+ val scoutEvents = loader.loadScoutEvents(historyConn, matchIds)
+ val events = loader.loadEvents(historyConn, matchIds)
+
+ val eventIds = events.keys
+ val flags = loader.loadEventFlags(historyConn, eventIds)
+ val players = loader.loadEventPlayers(historyConn, eventIds)
+ val positions = loader.loadEventPositions(historyConn, eventIds)
+
+ with(log) {
+ info(String.format("livescore_match : %d", matches.size))
+ info(String.format("livescore_match_extra : %d", matchExtras.size))
+ info(String.format("livescore_scoutevent : %d", scoutEvents.size))
+ info(String.format("livescore_event_top_backup : %d", events.size))
+ info(String.format("livescore_event_top_flag : %d", flags.size))
+ info(String.format("livescore_event_top_player : %d", players.size))
+ info(String.format("livescore_event_top_position : %d", positions.size))
+ }
+
+ with(saver) {
+ saveMatches(writeConn, matches)
+ saveMatchExtras(writeConn, matchExtras)
+ saveScoutEvents(writeConn, scoutEvents)
+ saveEvents(writeConn, events)
+ saveFlags(writeConn, flags)
+ savePlayers(writeConn, players)
+ savePositions(writeConn, positions)
+
+ }
+
+ log.info(String.format("generating requests for %d matches", matchIds.size))
+ val requests = matchIds
+ .map { matchId -> RequestUtil.createMatchRequest(5704, matchId) }
+ saver.saveRequests(writeConn, requests)
+ }
+ }
+ }
+ } catch (e: SQLException) {
+ log.error(e, e)
+ }
+
+ }
+
+}
+
+fun main(args: Array) {
+ LogUtil.setRootAppender();
+ val historyRecoveryTool = HistoryRecoveryTool()
+ historyRecoveryTool.runHBLRecovery()
+}
+
diff --git a/src/main/kotlin/sportradar/history/recover/Loader.kt b/src/main/kotlin/sportradar/history/recover/Loader.kt
new file mode 100644
index 0000000..a38240c
--- /dev/null
+++ b/src/main/kotlin/sportradar/history/recover/Loader.kt
@@ -0,0 +1,363 @@
+package sportradar.history.recover
+
+import org.apache.log4j.Logger
+import sportradar.history.recover.model.*
+import sportradar.history.recover.util.QueryUtil
+import sportradar.kotlin.workshop.examples._14LambdaWithReceiver.use
+import java.sql.Connection
+import java.sql.SQLException
+import java.sql.Timestamp
+import java.time.Instant
+import java.util.*
+
+internal class Loader {
+
+ fun loadMatches(
+ conn: Connection, fromDate: Instant, toDate: Instant, tournamentId: Int?
+ ): Map {
+ log.info(
+ String.format(
+ "loading livescore_match for tournament %d between %s -> %s",
+ tournamentId, fromDate, toDate
+ )
+ )
+
+ val matches = HashMap()
+
+ try {
+ conn.prepareStatement(QUERY_MATCHES).use { ps ->
+ ps.setTimestamp(1, Timestamp.from(fromDate))
+ ps.setTimestamp(2, Timestamp.from(toDate))
+ ps.setInt(3, tournamentId!!)
+ ps.executeQuery().use { rs ->
+ while (rs.next()) {
+ val matchId = rs.getLong("matchid")
+
+ val match = Match(
+ matchid = matchId,
+ betradarmatchid = rs.getLong("betradarmatchid"),
+ sportid = rs.getInt("sportid"),
+ realcategoryid = rs.getInt("realcategoryid"),
+ tournamentid = rs.getInt("tournamentid"),
+ teamidhome = rs.getInt("teamidhome"),
+ teamidaway = rs.getInt("teamidaway"),
+ dateofmatch = rs.getTimestamp("dateofmatch"),
+ isStarted = rs.getBoolean("started"),
+ currentperiod = rs.getInt("currentperiod"),
+ currentperiod_time = rs.getTimestamp("currentperiod_time"),
+ isEnded = rs.getBoolean("ended"),
+ winner = rs.getInt("winner"),
+ score = rs.getString("score"),
+ period1 = rs.getString("period1"),
+ period2 = rs.getString("period2"),
+ period3 = rs.getString("period3"),
+ period4 = rs.getString("period4"),
+ period5 = rs.getString("period5"),
+ period6 = rs.getString("period6"),
+ period7 = rs.getString("period7"),
+ period8 = rs.getString("period8"),
+ period9 = rs.getString("period9"),
+ period10 = rs.getString("period10"),
+ period11 = rs.getString("period11"),
+ period12 = rs.getString("period12"),
+ period13 = rs.getString("period13"),
+ normaltime = rs.getString("normaltime"),
+ extra1 = rs.getString("extra1"),
+ extra2 = rs.getString("extra2"),
+ overtime = rs.getString("overtime"),
+ penalties = rs.getString("penalties"),
+ isCancelled = rs.getBoolean("cancelled"),
+ isPostponed = rs.getBoolean("postponed"),
+ isAbandoned = rs.getBoolean("abandoned"),
+ status = rs.getInt("status"),
+ isHas_injuries = rs.getBoolean("has_injuries"),
+ comment = rs.getString("comment"),
+ lastgoal_time = rs.getTimestamp("lastgoal_time"),
+ lastgoal_team = rs.getInt("lastgoal_team"),
+ manualdate = rs.getInt("manualdate"),
+ isHidden = rs.getBoolean("hidden"),
+ lastupdate = rs.getTimestamp("lastupdate"),
+ isPublish = rs.getBoolean("publish"),
+ lineups_status = rs.getInt("lineups_status"),
+ isFormations_status = rs.getBoolean("formations_status"),
+ isIsontv = rs.getBoolean("isontv"),
+ isOnly_result = rs.getBoolean("only_result"),
+ isMulticast = rs.getBoolean("multicast"),
+ livetableid = rs.getInt("livetableid"),
+ stage = rs.getInt("stage"),
+ isFds_notes_checked = rs.getBoolean("fds_notes_checked"),
+ scoutmatch = rs.getInt("scoutmatch"),
+ lineup_manager_status = rs.getInt("lineup_manager_status"),
+ lineup_team_official_status = rs.getInt("lineup_team_official_status")
+ )
+ matches[matchId] = match
+ }
+ }
+ }
+ } catch (e: SQLException) {
+ log.error("error loading matches", e)
+ }
+
+ return matches
+ }
+
+ fun loadMatchExtra(conn: Connection, matchIds: Set): Map {
+ log.info(String.format("loading livescore_match_extra for %d matches", matchIds.size))
+
+ if (matchIds.isEmpty()) {
+ return emptyMap()
+ }
+
+ val matchExtras = HashMap()
+
+ val sql = String.format(QUERY_MATCH_EXTRA, QueryUtil.createPlaceHolders(matchIds))
+
+ try {
+ conn.prepareStatement(sql).use { ps ->
+ var i = 1
+ for (matchId in matchIds) {
+ ps.setLong(i++, matchId)
+ }
+ ps.executeQuery().use { rs ->
+ while (rs.next()) {
+ val matchId = rs.getLong("matchid")
+ val matchExtra = MatchExtra(
+ matchid = rs.getLong("matchid"),
+ seasonid = rs.getInt("seasonid"),
+ isIsontv_internal = rs.getBoolean("isontv_internal"),
+ first_leg = rs.getString("1st_leg"),
+ visibility_change = rs.getTimestamp("visibility_change"),
+ stadiumid = rs.getInt("stadiumid"),
+ startingtime_changestatus = rs.getInt("startingtime_changestatus"),
+ first_leg_matchid = rs.getLong("1st_leg_matchid"),
+ isShirtnumbers = rs.getBoolean("shirtnumbers"),
+ isInternal_test_match = rs.getBoolean("internal_test_match"),
+ current_server = rs.getInt("current_server"),
+ current_game_points = rs.getString("current_game_points"),
+ match_seconds = rs.getInt("match_seconds"),
+ match_start = rs.getInt("match_start"),
+ start_type = rs.getInt("start_type"),
+ tiebreak_scores = rs.getString("tiebreak_scores"),
+ conditions = rs.getString("conditions"),
+ isDeeper_coverage = rs.getBoolean("deeper_coverage"),
+ isBallspotting = rs.getBoolean("ballspotting"),
+ isTactical_lineup = rs.getBoolean("tactical_lineup"),
+ time_info = rs.getString("time_info"),
+ suspension_status = rs.getString("suspension_status"),
+ isScout_connection = rs.getBoolean("scout_connection"),
+ coverage_level = rs.getInt("coverage_level"),
+ isManually_hidden = rs.getBoolean("manually_hidden"),
+ isMedia_coverage = rs.getBoolean("media_coverage"),
+ isUnavailable = rs.getBoolean("unavailable"),
+ isOfficial_data_locked = rs.getBoolean("official_data_locked"),
+ current_period_seconds_left = rs.getInt("current_period_seconds_left")
+ )
+
+ matchExtras[matchId] = matchExtra
+ }
+ }
+ }
+ } catch (e: SQLException) {
+ log.error("error loading match extras", e)
+ }
+
+ return matchExtras
+ }
+
+ fun loadScoutEvents(conn: Connection, matchIds: Set): Map {
+ log.info(String.format("loading livescore_scoutevent for %d matches", matchIds.size))
+
+ if (matchIds.isEmpty()) {
+ return emptyMap()
+ }
+
+ val events = HashMap()
+ val sql = String.format(QUERY_SCOUT_EVENTS, QueryUtil.createPlaceHolders(matchIds))
+ try {
+ conn.prepareStatement(sql).use { ps ->
+ var i = 1
+ for (matchId in matchIds) {
+ ps.setLong(i++, matchId)
+ }
+ ps.executeQuery().use { rs ->
+ while (rs.next()) {
+ val id = rs.getString("id")
+ val event = ScoutEvent(
+ id = id,
+ matchid = rs.getLong("matchid"),
+ eventtype = rs.getInt("eventtype"),
+ time = rs.getInt("time"),
+ param1 = rs.getString("param1"),
+ param2 = rs.getString("param2"),
+ param3 = rs.getString("param3"),
+ param4 = rs.getString("param4"),
+ text = rs.getString("text"),
+ eventdate = rs.getTimestamp("eventdate"),
+ injurytime = rs.getInt("injurytime")
+ )
+ events[id] = event
+ }
+ }
+ }
+ } catch (e: SQLException) {
+ log.error("error loading scout events", e)
+ }
+
+ return events
+ }
+
+ fun loadEvents(conn: Connection, matchIds: Set): Map {
+ log.info(String.format("loading livescore_event_top_backup for %d matches", matchIds.size))
+ if (matchIds.isEmpty()) {
+ return emptyMap()
+ }
+
+ val eventIds = HashMap()
+ val sql = String.format(QUERY_EVENTS, QueryUtil.createPlaceHolders(matchIds))
+ try {
+ conn.prepareStatement(sql).use { ps ->
+ var i = 1
+ for (matchId in matchIds) {
+ ps.setLong(i++, matchId)
+ }
+ ps.executeQuery().use { rs ->
+ while (rs.next()) {
+ val eventId = rs.getLong("id")
+ val event = Event(
+ id = eventId,
+ sourceId = rs.getInt("sourceid"),
+ matchId = rs.getLong("matchid"),
+ eventType = rs.getInt("eventtype"),
+ time = rs.getInt("time"),
+ matchTime = rs.getInt("matchtime"),
+ param1 = rs.getString("param1"),
+ param2 = rs.getString("param2"),
+ param3 = rs.getString("param3"),
+ param4 = rs.getString("param4"),
+ param5 = rs.getString("param5"),
+ text = rs.getString("text"),
+ eventDate = rs.getTimestamp("eventdate"),
+ updateDate = rs.getTimestamp("updatedate"),
+ isDisabled = rs.getBoolean("disabled"),
+ injuryTime = rs.getInt("injurytime"),
+ injuryMatchTime = rs.getInt("injurymatchtime"),
+ scoutEventId = rs.getLong("scouteventid")
+ )
+ eventIds[eventId] = event
+ }
+ }
+ }
+ } catch (e: SQLException) {
+ log.error("error loading events", e)
+ }
+
+ return eventIds
+ }
+
+ fun loadEventFlags(conn: Connection, eventIds: Set): List {
+ log.info(String.format("loading livescore_event_top_flag for %d events", eventIds.size))
+ if (eventIds.isEmpty()) {
+ return emptyList()
+ }
+
+ val flags = ArrayList()
+ val sql = String.format(QUERY_EVENT_FLAGS, QueryUtil.createPlaceHolders(eventIds))
+ try {
+ conn.prepareStatement(sql).use { ps ->
+ var i = 1
+ for (eventId in eventIds) {
+ ps.setLong(i++, eventId)
+ }
+ ps.executeQuery().use { rs ->
+ while (rs.next()) {
+ val flag = Flag(
+ eventId = rs.getLong("eventid"),
+ flagType = rs.getInt("flagtype")
+ )
+ flags.add(flag)
+ }
+ }
+ }
+ } catch (e: SQLException) {
+ log.error("error loading event flags", e)
+ }
+
+ return flags
+ }
+
+ fun loadEventPlayers(conn: Connection, eventIds: Set): List {
+ log.info(String.format("loading livescore_event_top_player for %d events", eventIds.size))
+ if (eventIds.isEmpty()) {
+ return emptyList()
+ }
+
+ val players = ArrayList()
+ val sql = String.format(QUERY_EVENT_PLAYERS, QueryUtil.createPlaceHolders(eventIds))
+ try {
+ conn.prepareStatement(sql).use { ps ->
+ var i = 1
+ for (eventId in eventIds) {
+ ps.setLong(i++, eventId)
+ }
+ ps.executeQuery().use { rs ->
+ while (rs.next()) {
+ val player = Player(
+ eventId = rs.getLong("eventid"),
+ playerId = rs.getInt("playerid"),
+ number = rs.getInt("number")
+ )
+ players.add(player)
+ }
+ }
+ }
+ } catch (e: SQLException) {
+ log.error("error loading event players", e)
+ }
+
+ return players
+ }
+
+ fun loadEventPositions(conn: Connection, eventIds: Set): List {
+ log.info(String.format("loading livescore_event_top_position for %d events", eventIds.size))
+ if (eventIds.isEmpty()) {
+ return emptyList()
+ }
+
+ val positions = ArrayList()
+ val sql = String.format(QUERY_EVENT_POSITIONS, QueryUtil.createPlaceHolders(eventIds))
+ try {
+ conn.prepareStatement(sql).use { ps ->
+ var i = 1
+ for (eventId in eventIds) {
+ ps.setLong(i++, eventId)
+ }
+ ps.executeQuery().use { rs ->
+ while (rs.next()) {
+ val position = Position(
+ eventId = rs.getLong("eventid"),
+ posX = rs.getInt("posx"),
+ posY = rs.getInt("posy")
+ )
+ positions.add(position)
+ }
+ }
+ }
+ } catch (e: SQLException) {
+ log.error("error loading event positions", e)
+ }
+
+ return positions
+ }
+
+ companion object {
+ private val log = Logger.getLogger(Loader::class.java)
+ private const val QUERY_MATCHES =
+ "select * from livescore_match lm where lm.dateofmatch BETWEEN ? and ? and lm.tournamentid = ?"
+ private const val QUERY_MATCH_EXTRA = "select * from livescore_match_extra lme where lme.matchid in (%s)"
+ private const val QUERY_EVENTS = "select * from livescore_event_top_backup e where e.matchid in (%s)"
+ private const val QUERY_SCOUT_EVENTS = "select * from livescore_scoutevent e where e.matchid in (%s)"
+ private const val QUERY_EVENT_FLAGS = "select * from livescore_event_top_flag f where f.eventid in (%s)"
+ private const val QUERY_EVENT_PLAYERS = "select * from livescore_event_top_player p where p.eventid in (%s)"
+ private const val QUERY_EVENT_POSITIONS = "select * from livescore_event_top_position p where p.eventid in (%s)"
+ }
+}
diff --git a/src/main/kotlin/sportradar/history/recover/RecoveryTask.kt b/src/main/kotlin/sportradar/history/recover/RecoveryTask.kt
new file mode 100644
index 0000000..1611ee9
--- /dev/null
+++ b/src/main/kotlin/sportradar/history/recover/RecoveryTask.kt
@@ -0,0 +1,21 @@
+package sportradar.history.recover
+
+import com.google.common.base.MoreObjects
+
+import java.time.Instant
+
+data class RecoveryTask(
+ val tournamentId: Int,
+ val fromDate: Instant,
+ val toDate: Instant
+) {
+
+
+ override fun toString(): String {
+ return MoreObjects.toStringHelper(this)
+ .add("tournamentId", tournamentId)
+ .add("fromDate", fromDate)
+ .add("toDate", toDate)
+ .toString()
+ }
+}
diff --git a/src/main/kotlin/sportradar/history/recover/Saver.kt b/src/main/kotlin/sportradar/history/recover/Saver.kt
new file mode 100644
index 0000000..413bf17
--- /dev/null
+++ b/src/main/kotlin/sportradar/history/recover/Saver.kt
@@ -0,0 +1,447 @@
+package sportradar.history.recover
+
+import org.apache.log4j.Logger
+import sportradar.history.recover.model.*
+import sportradar.history.recover.util.QueryUtil
+import sportradar.kotlin.workshop.examples._14LambdaWithReceiver.use
+import java.sql.Connection
+import java.sql.SQLException
+import java.util.*
+
+internal class Saver {
+
+ fun saveMatches(conn: Connection, matches: Map) {
+ log.info(String.format("saving %d livescore_match rows", matches.keys.size))
+ if (matches.isEmpty()) {
+ return
+ }
+ val fields = Arrays.asList(
+ "matchid",
+ "betradarmatchid",
+ "sportid",
+ "realcategoryid",
+ "tournamentid",
+ "teamidhome",
+ "teamidaway",
+ "dateofmatch",
+ "started",
+ "currentperiod",
+ "currentperiod_time",
+ "ended",
+ "winner",
+ "score",
+ "period1",
+ "period2",
+ "period3",
+ "period4",
+ "period5",
+ "period6",
+ "period7",
+ "period8",
+ "period9",
+ "period10",
+ "period11",
+ "period12",
+ "period13",
+ "normaltime",
+ "extra1",
+ "extra2",
+ "overtime",
+ "penalties",
+ "cancelled",
+ "postponed",
+ "abandoned",
+ "status",
+ "has_injuries",
+ "comment",
+ "lastgoal_time",
+ "lastgoal_team",
+ "manualdate",
+ "hidden",
+ "lastupdate",
+ "publish",
+ "lineups_status",
+ "formations_status",
+ "isontv",
+ "only_result",
+ "multicast",
+ "livetableid",
+ "Stage",
+ "fds_notes_checked",
+ "scoutmatch",
+ "lineup_manager_status",
+ "lineup_team_official_status"
+ )
+ val sql = QueryUtil.createInsertQuery("livescore_match", fields)
+
+ try {
+ conn.prepareStatement(sql).use { ps ->
+ for ((_, match) in matches) {
+ var i = 1
+ ps.setLong(i++, match.matchid)
+ ps.setLong(i++, match.betradarmatchid)
+ ps.setInt(i++, match.sportid)
+ ps.setInt(i++, match.realcategoryid)
+ ps.setInt(i++, match.tournamentid)
+ ps.setInt(i++, match.teamidhome)
+ ps.setInt(i++, match.teamidaway)
+ ps.setTimestamp(i++, match.dateofmatch)
+ ps.setBoolean(i++, match.isStarted)
+ ps.setInt(i++, match.currentperiod)
+ ps.setTimestamp(i++, match.currentperiod_time)
+ ps.setBoolean(i++, match.isEnded)
+ ps.setInt(i++, match.winner)
+ ps.setString(i++, match.score)
+ ps.setString(i++, match.period1)
+ ps.setString(i++, match.period2)
+ ps.setString(i++, match.period3)
+ ps.setString(i++, match.period4)
+ ps.setString(i++, match.period5)
+ ps.setString(i++, match.period6)
+ ps.setString(i++, match.period7)
+ ps.setString(i++, match.period8)
+ ps.setString(i++, match.period9)
+ ps.setString(i++, match.period10)
+ ps.setString(i++, match.period11)
+ ps.setString(i++, match.period12)
+ ps.setString(i++, match.period13)
+ ps.setString(i++, match.normaltime)
+ ps.setString(i++, match.extra1)
+ ps.setString(i++, match.extra2)
+ ps.setString(i++, match.overtime)
+ ps.setString(i++, match.penalties)
+ ps.setBoolean(i++, match.isCancelled)
+ ps.setBoolean(i++, match.isPostponed)
+ ps.setBoolean(i++, match.isAbandoned)
+ ps.setInt(i++, match.status)
+ ps.setBoolean(i++, match.isHas_injuries)
+ ps.setString(i++, match.comment)
+ ps.setTimestamp(i++, match.lastgoal_time)
+ ps.setInt(i++, match.lastgoal_team)
+ ps.setInt(i++, match.manualdate)
+ ps.setBoolean(i++, match.isHidden)
+ ps.setTimestamp(i++, match.lastupdate)
+ ps.setBoolean(i++, match.isPublish)
+ ps.setInt(i++, match.lineups_status)
+ ps.setBoolean(i++, match.isFormations_status)
+ ps.setBoolean(i++, match.isIsontv)
+ ps.setBoolean(i++, match.isOnly_result)
+ ps.setBoolean(i++, match.isMulticast)
+ ps.setInt(i++, match.livetableid)
+ ps.setInt(i++, match.stage)
+ ps.setBoolean(i++, match.isFds_notes_checked)
+ ps.setInt(i++, match.scoutmatch)
+ ps.setInt(i++, match.lineup_manager_status)
+ ps.setInt(i, match.lineup_team_official_status)
+ ps.addBatch()
+ }
+ log.info(QueryUtil.psBatchUpdateToString(ps.executeBatch()))
+ }
+ } catch (e: SQLException) {
+ log.error("error inserting matches", e)
+ }
+
+ }
+
+ fun saveMatchExtras(conn: Connection, matchExtras: Map) {
+ log.info(String.format("saving %d livescore_match_extra rows", matchExtras.keys.size))
+ if (matchExtras.isEmpty()) {
+ return
+ }
+ val fields = Arrays.asList(
+ "matchid",
+ "seasonid",
+ "isontv_internal",
+ "1st_leg",
+ "visibility_change",
+ "stadiumid",
+ "startingtime_changestatus",
+ "1st_leg_matchid",
+ "shirtnumbers",
+ "internal_test_match",
+ "current_server",
+ "current_game_points",
+ "match_seconds",
+ "match_start",
+ "start_type",
+ "tiebreak_scores",
+ "conditions",
+ "deeper_coverage",
+ "ballspotting",
+ "tactical_lineup",
+ "time_info",
+ "suspension_status",
+ "scout_connection",
+ "coverage_level",
+ "manually_hidden",
+ "media_coverage",
+ "unavailable",
+ "official_data_locked",
+ "current_period_seconds_left"
+ )
+ val sql = QueryUtil.createInsertQuery("livescore_match_extra", fields)
+
+ try {
+ conn.prepareStatement(sql).use { ps ->
+ for ((_, matchExtra) in matchExtras) {
+ var i = 1
+ ps.setLong(i++, matchExtra.matchid)
+ ps.setInt(i++, matchExtra.seasonid)
+ ps.setBoolean(i++, matchExtra.isIsontv_internal)
+ ps.setString(i++, matchExtra.first_leg)
+ ps.setTimestamp(i++, matchExtra.visibility_change)
+ ps.setInt(i++, matchExtra.stadiumid)
+ ps.setInt(i++, matchExtra.startingtime_changestatus)
+ ps.setLong(i++, matchExtra.first_leg_matchid)
+ ps.setBoolean(i++, matchExtra.isShirtnumbers)
+ ps.setBoolean(i++, matchExtra.isInternal_test_match)
+ ps.setInt(i++, matchExtra.current_server)
+ ps.setString(i++, matchExtra.current_game_points)
+ ps.setInt(i++, matchExtra.match_seconds)
+ ps.setInt(i++, matchExtra.match_start)
+ ps.setInt(i++, matchExtra.start_type)
+ ps.setString(i++, matchExtra.tiebreak_scores)
+ ps.setString(i++, matchExtra.conditions)
+ ps.setBoolean(i++, matchExtra.isDeeper_coverage)
+ ps.setBoolean(i++, matchExtra.isBallspotting)
+ ps.setBoolean(i++, matchExtra.isTactical_lineup)
+ ps.setString(i++, matchExtra.time_info)
+ ps.setString(i++, matchExtra.suspension_status)
+ ps.setBoolean(i++, matchExtra.isScout_connection)
+ ps.setInt(i++, matchExtra.coverage_level)
+ ps.setBoolean(i++, matchExtra.isManually_hidden)
+ ps.setBoolean(i++, matchExtra.isMedia_coverage)
+ ps.setBoolean(i++, matchExtra.isUnavailable)
+ ps.setBoolean(i++, matchExtra.isOfficial_data_locked)
+ ps.setInt(i, matchExtra.current_period_seconds_left)
+ ps.addBatch()
+ }
+ log.info(QueryUtil.psBatchUpdateToString(ps.executeBatch()))
+ }
+ } catch (e: SQLException) {
+ log.error("error inserting match extras", e)
+ }
+
+ }
+
+ fun saveEvents(conn: Connection, events: Map) {
+ log.info(String.format("saving %d livescore_event_top_backup rows", events.keys.size))
+ if (events.isEmpty()) {
+ return
+ }
+ val fields = Arrays.asList(
+ "id",
+ "sourceid",
+ "matchid",
+ "eventtype",
+ "time",
+ "matchtime",
+ "param1",
+ "param2",
+ "param3",
+ "param4",
+ "param5",
+ "text",
+ "eventdate",
+ "updatedate",
+ "disabled",
+ "injurytime",
+ "injurymatchtime",
+ "scouteventid"
+ )
+ val sql = QueryUtil.createInsertQuery("livescore_event_top_backup", fields)
+
+ try {
+ conn.prepareStatement(sql).use { ps ->
+ for ((_, event) in events) {
+ var i = 1
+ ps.setLong(i++, event.id)
+ ps.setInt(i++, event.sourceId)
+ ps.setLong(i++, event.matchId)
+ ps.setInt(i++, event.eventType)
+ ps.setInt(i++, event.time)
+ ps.setInt(i++, event.matchTime)
+ ps.setString(i++, event.param1)
+ ps.setString(i++, event.param2)
+ ps.setString(i++, event.param3)
+ ps.setString(i++, event.param4)
+ ps.setString(i++, event.param5)
+ ps.setString(i++, event.text)
+ ps.setTimestamp(i++, event.eventDate)
+ ps.setTimestamp(i++, event.updateDate)
+ ps.setBoolean(i++, event.isDisabled)
+ ps.setInt(i++, event.injuryTime)
+ ps.setInt(i++, event.injuryMatchTime)
+ ps.setLong(i, event.scoutEventId)
+ ps.addBatch()
+ }
+ log.info(QueryUtil.psBatchUpdateToString(ps.executeBatch()))
+ }
+ } catch (e: SQLException) {
+ log.error("error inserting events", e)
+ }
+
+ }
+
+ fun saveScoutEvents(conn: Connection, events: Map) {
+ log.info(String.format("saving %d livescore_scoutevent rows", events.keys.size))
+ if (events.isEmpty()) {
+ return
+ }
+ val fields = Arrays.asList(
+ "id",
+ "matchid",
+ "eventtype",
+ "time",
+ "param1",
+ "param2",
+ "param3",
+ "param4",
+ "text",
+ "eventdate",
+ "injurytime"
+ )
+ val sql = QueryUtil.createInsertQuery("livescore_scoutevent", fields)
+
+ try {
+ conn.prepareStatement(sql).use { ps ->
+ for ((_, event) in events) {
+ var i = 1
+ ps.setString(i++, event.id)
+ ps.setLong(i++, event.matchid)
+ ps.setInt(i++, event.eventtype)
+ ps.setInt(i++, event.time)
+ ps.setString(i++, event.param1)
+ ps.setString(i++, event.param2)
+ ps.setString(i++, event.param3)
+ ps.setString(i++, event.param4)
+ ps.setString(i++, event.text)
+ ps.setTimestamp(i++, event.eventdate)
+ ps.setInt(i, event.injurytime)
+ ps.addBatch()
+ }
+ log.info(QueryUtil.psBatchUpdateToString(ps.executeBatch()))
+ }
+ } catch (e: SQLException) {
+ log.error("error inserting scout events", e)
+ }
+
+ }
+
+ fun saveFlags(conn: Connection, flags: List) {
+ log.info(String.format("saving %d livescore_event_top_flag rows", flags.size))
+ if (flags.isEmpty()) {
+ return
+ }
+ val fields = Arrays.asList("eventid", "flagtype")
+ val sql = QueryUtil.createInsertQuery("livescore_event_top_flag", fields)
+
+ try {
+ conn.prepareStatement(sql).use { ps ->
+ for (flag in flags) {
+ var i = 1
+ ps.setLong(i++, flag.eventId)
+ ps.setInt(i, flag.flagType)
+ ps.addBatch()
+ }
+ log.info(QueryUtil.psBatchUpdateToString(ps.executeBatch()))
+ }
+ } catch (e: SQLException) {
+ log.error("error inserting flags", e)
+ }
+
+ }
+
+ fun savePlayers(conn: Connection, players: List) {
+ log.info(String.format("saving %d livescore_event_top_player rows", players.size))
+ if (players.isEmpty()) {
+ return
+ }
+ val fields = Arrays.asList("eventid", "playerid", "number")
+ val sql = QueryUtil.createInsertQuery("livescore_event_top_player", fields)
+
+ try {
+ conn.prepareStatement(sql).use { ps ->
+ for (player in players) {
+ var i = 1
+ ps.setLong(i++, player.eventId)
+ ps.setInt(i++, player.playerId)
+ ps.setInt(i, player.number)
+ ps.addBatch()
+ }
+ log.info(QueryUtil.psBatchUpdateToString(ps.executeBatch()))
+ }
+ } catch (e: SQLException) {
+ log.error("error inserting players", e)
+ }
+
+ }
+
+ fun savePositions(conn: Connection, positions: List) {
+ log.info(String.format("saving %d livescore_event_top_position rows", positions.size))
+ if (positions.isEmpty()) {
+ return
+ }
+ val fields = Arrays.asList("eventid", "posx", "posy")
+ val sql = QueryUtil.createInsertQuery("livescore_event_top_position", fields)
+
+ try {
+ conn.prepareStatement(sql).use { ps ->
+ for (position in positions) {
+ var i = 1
+ ps.setLong(i++, position.eventId)
+ ps.setInt(i++, position.posX)
+ ps.setInt(i, position.posY)
+ ps.addBatch()
+ }
+ log.info(QueryUtil.psBatchUpdateToString(ps.executeBatch()))
+ }
+ } catch (e: SQLException) {
+ log.error("error inserting positions", e)
+ }
+
+ }
+
+ fun saveRequests(conn: Connection, requests: List) {
+ log.info(String.format("saving %d livescore_filesender_requests rows", requests.size))
+ if (requests.isEmpty()) {
+ return
+ }
+ val fields = Arrays.asList(
+ "requestedat",
+ "userid",
+ "`from`",
+ "`to`",
+ "target_type",
+ "target_details",
+ "recipientid",
+ "matchids"
+ )
+ val sql = QueryUtil.createInsertQuery("livescore_filesender_requests", fields)
+ try {
+ conn.prepareStatement(sql).use { ps ->
+ for (request in requests) {
+ var i = 1
+ ps.setTimestamp(i++, request.requestedat)
+ ps.setInt(i++, request.userid)
+ ps.setString(i++, request.from)
+ ps.setString(i++, request.to)
+ ps.setInt(i++, request.target_type)
+ ps.setString(i++, request.target_details)
+ ps.setInt(i++, request.recipientid)
+ ps.setString(i, request.matchids)
+ ps.addBatch()
+ }
+ log.info(QueryUtil.psBatchUpdateToString(ps.executeBatch()))
+
+ }
+ } catch (e: SQLException) {
+ log.error("error inserting requests", e)
+ }
+
+ }
+
+ companion object {
+ private val log = Logger.getLogger(Saver::class.java)
+ }
+}
diff --git a/src/main/kotlin/sportradar/history/recover/model/Event.kt b/src/main/kotlin/sportradar/history/recover/model/Event.kt
new file mode 100644
index 0000000..b206c8c
--- /dev/null
+++ b/src/main/kotlin/sportradar/history/recover/model/Event.kt
@@ -0,0 +1,24 @@
+package sportradar.history.recover.model
+
+import java.sql.Timestamp
+
+data class Event(
+ val id: Long,
+ val sourceId: Int,
+ val matchId: Long,
+ val eventType: Int,
+ val time: Int,
+ val matchTime: Int,
+ val param1: String?,
+ val param2: String?,
+ val param3: String?,
+ val param4: String?,
+ val param5: String?,
+ val text: String?,
+ val eventDate: Timestamp?,
+ val updateDate: Timestamp?,
+ val isDisabled: Boolean,
+ val injuryTime: Int,
+ val injuryMatchTime: Int,
+ val scoutEventId: Long
+)
diff --git a/src/main/kotlin/sportradar/history/recover/model/Flag.kt b/src/main/kotlin/sportradar/history/recover/model/Flag.kt
new file mode 100644
index 0000000..3681981
--- /dev/null
+++ b/src/main/kotlin/sportradar/history/recover/model/Flag.kt
@@ -0,0 +1,7 @@
+package sportradar.history.recover.model
+
+data class Flag(
+ val eventId: Long,
+ val flagType: Int
+)
+
diff --git a/src/main/kotlin/sportradar/history/recover/model/LSFSRequest.kt b/src/main/kotlin/sportradar/history/recover/model/LSFSRequest.kt
new file mode 100644
index 0000000..804b9bd
--- /dev/null
+++ b/src/main/kotlin/sportradar/history/recover/model/LSFSRequest.kt
@@ -0,0 +1,16 @@
+package sportradar.history.recover.model
+
+import java.sql.Timestamp
+
+data class LSFSRequest(
+ val requestedat: Timestamp,
+ val userid: Int,
+ val from: String = "",
+ val to: String = "",
+ val target_type: Int,
+ val target_details: String,
+ val recipientid: Int,
+
+ val matchids: String
+)
+
diff --git a/src/main/kotlin/sportradar/history/recover/model/Match.kt b/src/main/kotlin/sportradar/history/recover/model/Match.kt
new file mode 100644
index 0000000..085f594
--- /dev/null
+++ b/src/main/kotlin/sportradar/history/recover/model/Match.kt
@@ -0,0 +1,61 @@
+package sportradar.history.recover.model
+
+import java.sql.Timestamp
+
+data class Match(
+ val matchid: Long,
+ val betradarmatchid: Long,
+ val sportid: Int,
+ val realcategoryid: Int,
+ val tournamentid: Int,
+ val teamidhome: Int,
+ val teamidaway: Int,
+ val dateofmatch: Timestamp?,
+ val isStarted: Boolean,
+ val currentperiod: Int,
+ val currentperiod_time: Timestamp?,
+ val isEnded: Boolean,
+ val winner: Int,
+ val score: String?,
+ val period1: String?,
+ val period2: String?,
+ val period3: String?,
+ val period4: String?,
+ val period5: String?,
+ val period6: String?,
+ val period7: String?,
+ val period8: String?,
+ val period9: String?,
+ val period10: String?,
+ val period11: String?,
+ val period12: String?,
+ val period13: String?,
+ val normaltime: String?,
+ val extra1: String?,
+ val extra2: String?,
+ val overtime: String?,
+ val penalties: String?,
+ val isCancelled: Boolean,
+ val isPostponed: Boolean,
+ val isAbandoned: Boolean,
+ val status: Int,
+ val isHas_injuries: Boolean,
+ val comment: String?,
+ val lastgoal_time: Timestamp?,
+ val lastgoal_team: Int,
+ val manualdate: Int, // 0, 1, 255 ???
+ val isHidden: Boolean,
+ val lastupdate: Timestamp?,
+ val isPublish: Boolean,
+ val lineups_status: Int,
+ val isFormations_status: Boolean,
+ val isIsontv: Boolean,
+ val isOnly_result: Boolean,
+ val isMulticast: Boolean,
+ val livetableid: Int,
+ val stage: Int,
+ val isFds_notes_checked: Boolean,
+ val scoutmatch: Int, // 0, 1, 2
+ val lineup_manager_status: Int, // 0, 1, 2
+ val lineup_team_official_status: Int // 0, 1, 2
+)
diff --git a/src/main/kotlin/sportradar/history/recover/model/MatchExtra.kt b/src/main/kotlin/sportradar/history/recover/model/MatchExtra.kt
new file mode 100644
index 0000000..5ba73da
--- /dev/null
+++ b/src/main/kotlin/sportradar/history/recover/model/MatchExtra.kt
@@ -0,0 +1,35 @@
+package sportradar.history.recover.model
+
+import java.sql.Timestamp
+
+data class MatchExtra(
+ val matchid: Long,
+ val seasonid: Int,
+ val isIsontv_internal: Boolean,
+ val first_leg: String?,
+ val visibility_change: Timestamp?,
+ val stadiumid: Int,
+ val startingtime_changestatus: Int,
+ val first_leg_matchid: Long,
+ val isShirtnumbers: Boolean,
+ val isInternal_test_match: Boolean,
+ val current_server: Int, // 0, 1, 2
+ val current_game_points: String?,
+ val match_seconds: Int,
+ val match_start: Int,
+ val start_type: Int,
+ val tiebreak_scores: String?,
+ val conditions: String?,
+ val isDeeper_coverage: Boolean,
+ val isBallspotting: Boolean,
+ val isTactical_lineup: Boolean,
+ val time_info: String?,
+ val suspension_status: String?,
+ val isScout_connection: Boolean,
+ val coverage_level: Int, // 0, 1, 2, 3, 4
+ val isManually_hidden: Boolean,
+ val isMedia_coverage: Boolean,
+ val isUnavailable: Boolean,
+ val isOfficial_data_locked: Boolean,
+ val current_period_seconds_left: Int
+)
diff --git a/src/main/kotlin/sportradar/history/recover/model/Player.kt b/src/main/kotlin/sportradar/history/recover/model/Player.kt
new file mode 100644
index 0000000..2ce568d
--- /dev/null
+++ b/src/main/kotlin/sportradar/history/recover/model/Player.kt
@@ -0,0 +1,7 @@
+package sportradar.history.recover.model
+
+data class Player(
+ val eventId: Long,
+ val playerId: Int,
+ val number: Int
+)
diff --git a/src/main/kotlin/sportradar/history/recover/model/Position.kt b/src/main/kotlin/sportradar/history/recover/model/Position.kt
new file mode 100644
index 0000000..0acf2db
--- /dev/null
+++ b/src/main/kotlin/sportradar/history/recover/model/Position.kt
@@ -0,0 +1,7 @@
+package sportradar.history.recover.model
+
+data class Position(
+ val eventId: Long,
+ val posX: Int,
+ val posY: Int
+)
diff --git a/src/main/kotlin/sportradar/history/recover/model/ScoutEvent.kt b/src/main/kotlin/sportradar/history/recover/model/ScoutEvent.kt
new file mode 100644
index 0000000..68956d4
--- /dev/null
+++ b/src/main/kotlin/sportradar/history/recover/model/ScoutEvent.kt
@@ -0,0 +1,17 @@
+package sportradar.history.recover.model
+
+import java.sql.Timestamp
+
+data class ScoutEvent(
+ val id: String?,
+ val matchid: Long,
+ val eventtype: Int,
+ val time: Int,
+ val param1: String?,
+ val param2: String?,
+ val param3: String?,
+ val param4: String?,
+ val text: String?,
+ val eventdate: Timestamp?,
+ val injurytime: Int
+)
diff --git a/src/main/kotlin/sportradar/history/recover/util/QueryUtil.kt b/src/main/kotlin/sportradar/history/recover/util/QueryUtil.kt
new file mode 100644
index 0000000..de0053c
--- /dev/null
+++ b/src/main/kotlin/sportradar/history/recover/util/QueryUtil.kt
@@ -0,0 +1,29 @@
+package sportradar.history.recover.util
+
+import java.sql.PreparedStatement
+import java.util.*
+
+object QueryUtil {
+
+ fun createPlaceHolders(coll: Collection<*>): String {
+ return Collections.nCopies(coll.size, "?").joinToString(",")
+ }
+
+ fun createInsertQuery(tablename: String, fields: Collection): String {
+ return String.format(
+ "insert ignore into %s (%s) VALUES (%s)",
+ tablename, fields.joinToString(","), createPlaceHolders(fields)
+ )
+ }
+
+ fun psToString(ps: PreparedStatement): String {
+ val s = ps.toString()
+ return s.substring(s.indexOf(':'))
+ }
+
+ fun psBatchUpdateToString(batchResult: IntArray): String {
+ val total = batchResult.size
+ val countUpdated = Arrays.stream(batchResult).boxed().filter { i -> i > 0 }.count()
+ return String.format("%d inserted of %d total", countUpdated, total)
+ }
+}
diff --git a/src/main/kotlin/sportradar/history/recover/util/RequestUtil.kt b/src/main/kotlin/sportradar/history/recover/util/RequestUtil.kt
new file mode 100644
index 0000000..9eba5ac
--- /dev/null
+++ b/src/main/kotlin/sportradar/history/recover/util/RequestUtil.kt
@@ -0,0 +1,29 @@
+package sportradar.history.recover.util
+
+import sportradar.history.recover.model.LSFSRequest
+import java.sql.Timestamp
+import java.time.Instant
+
+object RequestUtil {
+
+ fun createMatchRequest(filesenderId: Int, matchId: Long): LSFSRequest {
+// return LSFSRequest.newLSFSRequest()
+// .userid(2586)
+// .recipientid(filesenderId)
+// .requestedat(Timestamp.from(Instant.now()))
+// .from("")
+// .to("")
+// .target_type(2)
+// .target_details(String.format("update-%d", matchId))
+// .matchids(matchId.toString())
+// .build()
+ return LSFSRequest(
+ userid = 2586,
+ recipientid = filesenderId,
+ requestedat = Timestamp.from(Instant.now()),
+ target_type = 2,
+ target_details = "update-$matchId",
+ matchids = matchId.toString()
+ )
+ }
+}
diff --git a/src/main/resources/conf/general.properties b/src/main/resources/conf/general.properties
new file mode 100644
index 0000000..c98d414
--- /dev/null
+++ b/src/main/resources/conf/general.properties
@@ -0,0 +1,2 @@
+Environment = @env@
+
diff --git a/src/main/resources/conf/general.properties.@env@ b/src/main/resources/conf/general.properties.@env@
new file mode 100644
index 0000000..4c3c51e
--- /dev/null
+++ b/src/main/resources/conf/general.properties.@env@
@@ -0,0 +1,9 @@
+
+MySqlDatabaseURL = jdbc:mysql://SELECT_URL:3306/odds
+MySqlSystemUserName =
+MySqlSystemPassword =
+
+
+backupdatabaseURL = jdbc:mysql://HISTORY_URL:3306/backup
+backupdatabaseUsername =
+backupdatabasePassword =
diff --git a/src/main/resources/log4j.xml b/src/main/resources/log4j.xml
new file mode 100644
index 0000000..1726638
--- /dev/null
+++ b/src/main/resources/log4j.xml
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+