Przeglądaj źródła

First git commit

Axel Nordh 5 lat temu
commit
f52736aea1
65 zmienionych plików z 4045 dodań i 0 usunięć
  1. 25 0
      ATG/.classpath
  2. 2 0
      ATG/.gitignore
  3. 36 0
      ATG/.project
  4. 15 0
      ATG/.settings/org.eclipse.jdt.core.prefs
  5. 4 0
      ATG/.settings/org.eclipse.m2e.core.prefs
  6. BIN
      ATG/RScript/.RData
  7. 60 0
      ATG/RScript/.Rhistory
  8. BIN
      ATG/RScript/.Rproj.user/4A9417F8/explorer-cache/a18347cdb7424d3cab3620e2d6252bf1
  9. 9 0
      ATG/RScript/.Rproj.user/4A9417F8/pcs/files-pane.pper
  10. 3 0
      ATG/RScript/.Rproj.user/4A9417F8/pcs/source-pane.pper
  11. 14 0
      ATG/RScript/.Rproj.user/4A9417F8/pcs/windowlayoutstate.pper
  12. 6 0
      ATG/RScript/.Rproj.user/4A9417F8/pcs/workbench-pane.pper
  13. 5 0
      ATG/RScript/.Rproj.user/4A9417F8/rmd-outputs
  14. 1 0
      ATG/RScript/.Rproj.user/4A9417F8/saved_source_markers
  15. 33 0
      ATG/RScript/.Rproj.user/4A9417F8/sources/per/t/217802EA
  16. 0 0
      ATG/RScript/.Rproj.user/4A9417F8/sources/per/t/217802EA-contents
  17. 26 0
      ATG/RScript/.Rproj.user/4A9417F8/sources/per/t/26C4B4C3
  18. 163 0
      ATG/RScript/.Rproj.user/4A9417F8/sources/per/t/26C4B4C3-contents
  19. 33 0
      ATG/RScript/.Rproj.user/4A9417F8/sources/per/t/A60241BB
  20. 0 0
      ATG/RScript/.Rproj.user/4A9417F8/sources/per/t/A60241BB-contents
  21. 26 0
      ATG/RScript/.Rproj.user/4A9417F8/sources/per/t/D28E45BF
  22. 0 0
      ATG/RScript/.Rproj.user/4A9417F8/sources/per/t/D28E45BF-contents
  23. 5 0
      ATG/RScript/.Rproj.user/4A9417F8/sources/prop/70855DFE
  24. 1 0
      ATG/RScript/.Rproj.user/4A9417F8/sources/prop/INDEX
  25. BIN
      ATG/RScript/.Rproj.user/4A9417F8/viewer-cache/2D9C7EC.Rdata
  26. BIN
      ATG/RScript/.Rproj.user/4A9417F8/viewer-cache/E44B7244.Rdata
  27. 0 0
      ATG/RScript/.Rproj.user/shared/notebooks/patch-chunk-names
  28. 1 0
      ATG/RScript/.Rproj.user/shared/notebooks/paths
  29. 163 0
      ATG/RScript/AtgPerdictions.R
  30. 13 0
      ATG/RScript/RScript.Rproj
  31. 8 0
      ATG/build.fxbuild
  32. 51 0
      ATG/pom.xml
  33. 51 0
      ATG/src/application/ATGMain.java
  34. 1 0
      ATG/src/application/application.css
  35. 242 0
      ATG/src/controllers/AtgController.java
  36. 36 0
      ATG/src/controllers/DDTabController.java
  37. 792 0
      ATG/src/controllers/DatabaseContoller.java
  38. 40 0
      ATG/src/controllers/LDTabController.java
  39. 112 0
      ATG/src/controllers/MainController.java
  40. 165 0
      ATG/src/controllers/TestTabController.java
  41. 40 0
      ATG/src/controllers/UpdateTabController.java
  42. 38 0
      ATG/src/controllers/V4TabController.java
  43. 30 0
      ATG/src/controllers/V5TabController.java
  44. 36 0
      ATG/src/controllers/V64TabController.java
  45. 38 0
      ATG/src/controllers/V65TabController.java
  46. 36 0
      ATG/src/controllers/V75TabController.java
  47. 38 0
      ATG/src/controllers/V86TabController.java
  48. 92 0
      ATG/src/fxml/AtgMain.fxml
  49. 43 0
      ATG/src/fxml/DD.fxml
  50. 43 0
      ATG/src/fxml/LD.fxml
  51. 29 0
      ATG/src/fxml/RaceTableFxml.fxml
  52. 57 0
      ATG/src/fxml/TestTab.fxml
  53. 40 0
      ATG/src/fxml/UpdateTab.fxml
  54. 43 0
      ATG/src/fxml/V4.fxml
  55. 43 0
      ATG/src/fxml/V5.fxml
  56. 43 0
      ATG/src/fxml/V64.fxml
  57. 43 0
      ATG/src/fxml/V65.fxml
  58. 43 0
      ATG/src/fxml/V75.fxml
  59. 43 0
      ATG/src/fxml/V86.fxml
  60. 207 0
      ATG/src/objects/HorseRaceData.java
  61. 101 0
      ATG/src/objects/UpdateRaceRow.java
  62. 127 0
      ATG/src/parsers/AtgParser.java
  63. 57 0
      ATG/src/parsers/MyWebClient.java
  64. 51 0
      ATG/src/parsers/Parser.java
  65. 542 0
      ATG/src/parsers/TravsportParser.java

+ 25 - 0
ATG/.classpath

@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+	<classpathentry including="**/*.java" kind="src" output="target/classes" path="src">
+		<attributes>
+			<attribute name="optional" value="true"/>
+			<attribute name="maven.pomderived" value="true"/>
+		</attributes>
+	</classpathentry>
+	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-11">
+		<attributes>
+			<attribute name="maven.pomderived" value="true"/>
+		</attributes>
+	</classpathentry>
+	<classpathentry kind="con" path="org.eclipse.jdt.USER_LIBRARY/JavaFX">
+		<attributes>
+			<attribute name="module" value="true"/>
+		</attributes>
+	</classpathentry>
+	<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER">
+		<attributes>
+			<attribute name="maven.pomderived" value="true"/>
+		</attributes>
+	</classpathentry>
+	<classpathentry kind="output" path="target/classes"/>
+</classpath>

+ 2 - 0
ATG/.gitignore

@@ -0,0 +1,2 @@
+/bin/
+/target/

+ 36 - 0
ATG/.project

@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>ATG</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>org.eclipse.jdt.core.javabuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.xtext.ui.shared.xtextBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.m2e.core.maven2Builder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.statet.r.resourceProjects.RBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>org.eclipse.m2e.core.maven2Nature</nature>
+		<nature>org.eclipse.xtext.ui.shared.xtextNature</nature>
+		<nature>org.eclipse.jdt.core.javanature</nature>
+		<nature>org.eclipse.statet.ide.resourceProjects.Statet</nature>
+		<nature>org.eclipse.statet.r.resourceProjects.R</nature>
+	</natures>
+</projectDescription>

+ 15 - 0
ATG/.settings/org.eclipse.jdt.core.prefs

@@ -0,0 +1,15 @@
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=11
+org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
+org.eclipse.jdt.core.compiler.compliance=11
+org.eclipse.jdt.core.compiler.debug.lineNumber=generate
+org.eclipse.jdt.core.compiler.debug.localVariable=generate
+org.eclipse.jdt.core.compiler.debug.sourceFile=generate
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
+org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=warning
+org.eclipse.jdt.core.compiler.release=enabled
+org.eclipse.jdt.core.compiler.source=11

+ 4 - 0
ATG/.settings/org.eclipse.m2e.core.prefs

@@ -0,0 +1,4 @@
+activeProfiles=
+eclipse.preferences.version=1
+resolveWorkspaceProjects=true
+version=1

BIN
ATG/RScript/.RData


+ 60 - 0
ATG/RScript/.Rhistory

@@ -0,0 +1,60 @@
+sqlRacesIds = "SELECT GROUP_CONCAT(g.id) FROM (SELECT id, raceNumber, raceDate, Result FROM `Results` group BY RaceDate, RaceNumber HAVING count(RaceNumber) > 5 AND Result = 1 ORDER BY `Results`.`raceDate` DESC) AS g"
+test = dbSendQuery(sqlRacesIds)
+library(RMySQL)
+mydb = dbConnect(MySQL(), user="atg", password="CvWKY34DqtlVgjt_9", host="nordh.xyz", dbname="atg")
+sqlRacesWithEnoughParticipants = "SELECT raceNumber, raceDate, Result FROM `Results` group BY RaceDate, RaceNumber HAVING count(RaceNumber) > 5 AND Result = 1
+ORDER BY `Results`.`raceDate` DESC"
+sqlRacesIds = "SELECT GROUP_CONCAT(g.id) FROM (SELECT id, raceNumber, raceDate, Result FROM `Results` group BY RaceDate, RaceNumber HAVING count(RaceNumber) > 5 AND Result = 1 ORDER BY `Results`.`raceDate` DESC) AS g"
+test = dbSendQuery(sqlRacesIds)
+sqlRacesIds = "SELECT GROUP_CONCAT(g.id) FROM (SELECT id, raceNumber, raceDate, Result FROM `Results` group BY RaceDate, RaceNumber HAVING count(RaceNumber) > 5 AND Result = 1 ORDER BY `Results`.`raceDate` DESC) AS g"
+test = dbSendQuery(mydb, sqlRacesIds)
+View(test)
+View(test)
+testRs = fetch(test, n=-1)
+View(testRs)
+View(testRs)
+View(testRs)
+View(testRs)
+View(testRs)
+inspect(testRs)
+Inspect(testRs)
+print(testRs)
+sqlRacesIds = "SET SESSION group_concat_max_len = 1000000;SELECT GROUP_CONCAT(g.id) FROM (SELECT id, raceNumber, raceDate, Result FROM `Results` group BY RaceDate, RaceNumber HAVING count(RaceNumber) > 5 AND Result = 1 ORDER BY `Results`.`raceDate` DESC) AS g"
+test = dbSendQuery(mydb, sqlRacesIds)
+testRs = fetch(test, n=-1)
+print(testRs)
+dbSendQuery(mydb, "SET SESSION group_concat_max_len = 1000000")
+sqlRacesIds = "SELECT GROUP_CONCAT(g.id) FROM (SELECT id, rac
+""
+)
+""
+sqlRacesWithEnoughParticipants = "SELECT raceNumber, raceDate, Result FROM `Results` group BY RaceDate, RaceNumber HAVING count(RaceNumber) > 5 AND Result = 1
+ORDER BY `Results`.`raceDate` DESC"
+dbSendQuery(mydb, "SET SESSION group_concat_max_len = 1000000")
+sqlRacesIds = "SELECT GROUP_CONCAT(g.id) FROM (SELECT id, raceNumber, raceDate, Result FROM `Results` group BY RaceDate, RaceNumber HAVING count(RaceNumber) > 5 AND Result = 1 ORDER BY `Results`.`raceDate` DESC) AS g"
+test = dbSendQuery(mydb, sqlRacesIds)
+testRs = fetch(test, n=-1)
+print(testRs)
+View(testRs)
+View(testRs)
+newSql = "SELECT r1.*,
+ROUND(AVG(horseResults.time),4) as HorseAvgTime, ROUND(AVG(driverResults.time),4) horseAvgTime,
+ROUND(AVG(IF(horseResults.Distance = r1.Distance, horseResults.time, null)),4) driverAvgTime,
+ROUND(AVG(IF((horseResults.Distance = r1.Distance AND horseResults.Time > 0), horseResults.time, null)),4) horseAvgByDistance
+ROUND(AVG(IF((driverResults.Distance = r1.Distance AND driverResults.Time > 0), driverResults.time, null)),4) driverAvgByDistance
+FROM Results r1
+INNER JOIN Results horseResults ON r1.RaceDate > horseResults.RaceDate AND r1.HorseId = horseResults.HorseId
+INNER JOIN Results driverResults ON r1.RaceDate > driverResults.RaceDate AND r1.DriverId = driverResults.DriverId
+WHERE r1.raceNumber = 10 AND r1.RaceDate = '2021-02-08'
+GROUP BY r1.horseId"
+newSql = "SELECT r1.*, ROUND(AVG(horseResults.time),4) as HorseAvgTime, ROUND(AVG(driverResults.time),4) horseAvgTime, ROUND(AVG(IF(horseResults.Distance = r1.Distance, horseResults.time, null)),4) driverAvgTime,ROUND(AVG(IF((horseResults.Distance = r1.Distance AND horseResults.Time > 0), horseResults.time, null)),4) horseAvgByDistance, ROUND(AVG(IF((driverResults.Distance = r1.Distance AND driverResults.Time > 0), driverResults.time, null)),4) driverAvgByDistance, FROM Results r1 INNER JOIN Results horseResults ON r1.RaceDate > horseResults.RaceDate AND r1.HorseId = horseResults.HorseId INNER JOIN Results driverResults ON r1.RaceDate > driverResults.RaceDate AND r1.DriverId = driverResults.DriverId WHERE r1.raceNumber = 10 AND r1.RaceDate = '2021-02-08' GROUP BY r1.horseId"
+newRs = dbSendQuery(mydb, newSql)
+# With AVG per distance
+newSql = "SELECT r1.*, ROUND(AVG(horseResults.time),4) as HorseAvgTime, ROUND(AVG(driverResults.time),4) as horseAvgTime, ROUND(AVG(IF(horseResults.Distance = r1.Distance, horseResults.time, null)),4) as driverAvgTime, ROUND(AVG(IF((horseResults.Distance = r1.Distance AND horseResults.Time > 0), horseResults.time, null)),4) as horseAvgByDistance, ROUND(AVG(IF((driverResults.Distance = r1.Distance AND driverResults.Time > 0), driverResults.time, null)),4) as driverAvgByDistance, FROM Results r1 INNER JOIN Results horseResults ON r1.RaceDate > horseResults.RaceDate AND r1.HorseId = horseResults.HorseId INNER JOIN Results driverResults ON r1.RaceDate > driverResults.RaceDate AND r1.DriverId = driverResults.DriverId WHERE r1.raceNumber = 10 AND r1.RaceDate = '2021-02-08' GROUP BY r1.horseId"
+newSql = "SELECT r1.*, ROUND(AVG(horseResults.time),4) as HorseAvgTime, ROUND(AVG(driverResults.time),4) as horseAvgTime, ROUND(AVG(IF(horseResults.Distance = r1.Distance, horseResults.time, null)),4) as driverAvgTime, ROUND(AVG(IF((horseResults.Distance = r1.Distance AND horseResults.Time > 0), horseResults.time, null)),4) as horseAvgByDistance, ROUND(AVG(IF((driverResults.Distance = r1.Distance AND driverResults.Time > 0), driverResults.time, null)),4) as driverAvgByDistance, FROM Results r1 INNER JOIN Results horseResults ON r1.RaceDate > horseResults.RaceDate AND r1.HorseId = horseResults.HorseId INNER JOIN Results driverResults ON r1.RaceDate > driverResults.RaceDate AND r1.DriverId = driverResults.DriverId WHERE r1.raceNumber = 10 AND r1.RaceDate = '2021-02-08' GROUP BY r1.horseId"
+newRs = dbSendQuery(mydb, newSql)
+newSql = "SELECT r1.*, ROUND(AVG(horseResults.time),4) as HorseAvgTime, ROUND(AVG(driverResults.time),4) as horseAvgTime, ROUND(AVG(IF(horseResults.Distance = r1.Distance, horseResults.time, null)),4) as driverAvgTime, ROUND(AVG(IF((horseResults.Distance = r1.Distance AND horseResults.Time > 0), horseResults.time, null)),4) as horseAvgByDistance, ROUND(AVG(IF((driverResults.Distance = r1.Distance AND driverResults.Time > 0), driverResults.time, null)),4) as driverAvgByDistance FROM Results r1 INNER JOIN Results horseResults ON r1.RaceDate > horseResults.RaceDate AND r1.HorseId = horseResults.HorseId INNER JOIN Results driverResults ON r1.RaceDate > driverResults.RaceDate AND r1.DriverId = driverResults.DriverId WHERE r1.raceNumber = 10 AND r1.RaceDate = '2021-02-08' GROUP BY r1.horseId"
+newRs = dbSendQuery(mydb, newSql)
+newRes = fetch(newRs, -1)
+View(newRs)
+View(newRes)

BIN
ATG/RScript/.Rproj.user/4A9417F8/explorer-cache/a18347cdb7424d3cab3620e2d6252bf1


+ 9 - 0
ATG/RScript/.Rproj.user/4A9417F8/pcs/files-pane.pper

@@ -0,0 +1,9 @@
+{
+    "path" : "F:/Programming/JAVA/Odds/ATG/RScript",
+    "sortOrder" : [
+        {
+            "ascending" : true,
+            "columnIndex" : 2
+        }
+    ]
+}

+ 3 - 0
ATG/RScript/.Rproj.user/4A9417F8/pcs/source-pane.pper

@@ -0,0 +1,3 @@
+{
+    "activeTab" : 0
+}

+ 14 - 0
ATG/RScript/.Rproj.user/4A9417F8/pcs/windowlayoutstate.pper

@@ -0,0 +1,14 @@
+{
+    "left" : {
+        "panelheight" : 958,
+        "splitterpos" : 398,
+        "topwindowstate" : "NORMAL",
+        "windowheight" : 996
+    },
+    "right" : {
+        "panelheight" : 958,
+        "splitterpos" : 597,
+        "topwindowstate" : "NORMAL",
+        "windowheight" : 996
+    }
+}

+ 6 - 0
ATG/RScript/.Rproj.user/4A9417F8/pcs/workbench-pane.pper

@@ -0,0 +1,6 @@
+{
+    "TabSet1" : 0,
+    "TabSet2" : 0,
+    "TabZoom" : {
+    }
+}

+ 5 - 0
ATG/RScript/.Rproj.user/4A9417F8/rmd-outputs

@@ -0,0 +1,5 @@
+
+
+
+
+

+ 1 - 0
ATG/RScript/.Rproj.user/4A9417F8/saved_source_markers

@@ -0,0 +1 @@
+{"active_set":"","sets":[]}

+ 33 - 0
ATG/RScript/.Rproj.user/4A9417F8/sources/per/t/217802EA

@@ -0,0 +1,33 @@
+{
+    "collab_server" : "",
+    "contents" : "",
+    "created" : 1612960830815.000,
+    "dirty" : false,
+    "encoding" : "",
+    "folds" : "",
+    "hash" : "0",
+    "id" : "217802EA",
+    "lastKnownWriteTime" : 140696453462780,
+    "last_content_update" : 1612960830815,
+    "path" : null,
+    "project_path" : null,
+    "properties" : {
+        "cacheKey" : "2D9C7EC",
+        "caption" : "testRs",
+        "contentUrl" : "grid_resource/gridviewer.html?env=&obj=testRs&cache_key=2D9C7EC",
+        "displayedObservations" : "1",
+        "environment" : "",
+        "expression" : "testRs",
+        "object" : "testRs",
+        "preview" : "0",
+        "totalObservations" : "1",
+        "variables" : "1"
+    },
+    "read_only" : false,
+    "read_only_alternatives" : [
+    ],
+    "relative_order" : 2,
+    "source_on_save" : false,
+    "source_window" : "",
+    "type" : "r_dataframe"
+}

+ 0 - 0
ATG/RScript/.Rproj.user/4A9417F8/sources/per/t/217802EA-contents


+ 26 - 0
ATG/RScript/.Rproj.user/4A9417F8/sources/per/t/26C4B4C3

@@ -0,0 +1,26 @@
+{
+    "collab_server" : "",
+    "contents" : "",
+    "created" : 1609234407316.000,
+    "dirty" : false,
+    "encoding" : "UTF-8",
+    "folds" : "",
+    "hash" : "3961199316",
+    "id" : "26C4B4C3",
+    "lastKnownWriteTime" : 1613590019,
+    "last_content_update" : 1613590019029,
+    "path" : "F:/Programming/JAVA/Odds/ATG/RScript/AtgPerdictions.R",
+    "project_path" : "AtgPerdictions.R",
+    "properties" : {
+        "cursorPosition" : "6,160",
+        "scrollLine" : "0",
+        "tempName" : "Untitled1"
+    },
+    "read_only" : false,
+    "read_only_alternatives" : [
+    ],
+    "relative_order" : 1,
+    "source_on_save" : false,
+    "source_window" : "",
+    "type" : "r_source"
+}

+ 163 - 0
ATG/RScript/.Rproj.user/4A9417F8/sources/per/t/26C4B4C3-contents

@@ -0,0 +1,163 @@
+# Data Preprocessing
+
+# Importing the Dataset
+library(RMySQL)
+mydb = dbConnect(MySQL(), user="atg", password="CvWKY34DqtlVgjt_9", host="nordh.xyz", dbname="atg")
+
+sqlRacesWithEnoughParticipants = "SELECT raceNumber, raceDate, Result FROM `Results` group BY RaceDate, RaceNumber HAVING count(RaceNumber) > 5 AND Result = 1  
+ORDER BY `Results`.`raceDate` DESC"
+
+# With AVG per distance
+newSql = "SELECT r1.*, ROUND(AVG(horseResults.time),4) as HorseAvgTime, ROUND(AVG(driverResults.time),4) as driverAvgTime, ROUND(AVG(IF(horseResults.Distance = r1.Distance, horseResults.time, null)),4) as driverAvgTime, ROUND(AVG(IF((horseResults.Distance = r1.Distance AND horseResults.Time > 0), horseResults.time, null)),4) as horseAvgByDistance, ROUND(AVG(IF((driverResults.Distance = r1.Distance AND driverResults.Time > 0), driverResults.time, null)),4) as driverAvgByDistance FROM Results r1 INNER JOIN Results horseResults ON r1.RaceDate > horseResults.RaceDate AND r1.HorseId = horseResults.HorseId INNER JOIN Results driverResults ON r1.RaceDate > driverResults.RaceDate AND r1.DriverId = driverResults.DriverId WHERE r1.raceNumber = 10 AND r1.RaceDate = '2021-02-08' GROUP BY r1.horseId"
+newRs = dbSendQuery(mydb, newSql)
+newRes = fetch(newRs, -1)
+
+"WITH avg times" 
+"SELECT r1.*, ROUND(AVG(if (horseResults.time > 0, horseResults.time,null)),4) as HorseAvgTime, ROUND(AVG(IF((horseResults.Distance = r1.Distance AND horseResults.Time > 0), horseResults.time, null)),4) as horseAvgByDistance FROM Results r1 INNER JOIN Results horseResults ON r1.RaceDate > horseResults.RaceDate AND r1.HorseId = horseResults.HorseId INNER JOIN Results driverResults ON r1.RaceDate > driverResults.RaceDate AND r1.DriverId = driverResults.DriverId WHERE r1.raceNumber = 10 AND r1.RaceDate = '2021-02-08' GROUP BY r1.horseId"
+
+sqlForVoltStartLaneDistribution = "SELECT Lane, SUM(result)/Count(*), Count(*) as cnt FROM `Results` WHERE Result > 0 AND Lane > -1 AND TimeModifier NOT like "%a%" GROUP BY Lane HAVING cnt > 20 ORDER BY `SUM(result)/Count(*)` ASC"
+sqlForAutoStartLaneDistribution = "SELECT Lane, SUM(result)/Count(*), Count(*) as cnt FROM `Results` WHERE Result > 0 AND Lane > -1 AND TimeModifier like "%a%" GROUP BY Lane HAVING cnt > 20 ORDER BY `SUM(result)/Count(*)` ASC"
+
+country = 'iran'
+league = 'persian-gulf-pro-league'
+
+rs = dbSendQuery(mydb, sql)
+
+dataset = fetch(rs, n=-1)
+
+###Taking case of missing data
+dataset$avgHomeScore = ifelse(is.na(dataset$avgHomeScore), 0, dataset$avgHomeScore)
+dataset$avgAwayScore = ifelse(is.na(dataset$avgAwayScore), 0, dataset$avgAwayScore)
+#dataset$avgHomeScoreOneYearBack = ifelse(is.na(dataset$avgHomeScoreOneYearBack), 0, dataset$avgHomeScoreOneYearBack)
+#dataset$avgAwayScoreOneYearBack = ifelse(is.na(dataset$avgAwayScoreOneYearBack), 0, dataset$avgAwayScoreOneYearBack)
+
+# Encoding categorical data
+##dataset$overtime = factor(dataset$overtime, levels = c('1-0', '0-1', 'NULL'), labels = c(1, 1, 0))
+##dataset$result = factor(dataset$result, levels = c('1', 'X', '2'), labels = c(1, 0, 2))
+
+
+library(piratings)
+
+rs2 = dbSendQuery(mydb, piSql)
+
+piDataset = fetch(rs2, n=-1)
+
+teams <- as.matrix(piDataset[,c("homeTeamName", "awayTeamName")])
+outcomes <- as.matrix(piDataset[, c("homeScore", "awayScore")])
+
+# England - premiere League seq(0.06, 0.1, 0.005), seq(0.4, 0.9, 0.05)
+grid <- optimize_pi_ratings(teams, outcomes, seq(0.04, 0.1, 0.005), seq(0.3, 0.9, 0.05))
+
+colnames(grid)[1] <- "rating"
+gridOrdered = grid[order(grid$rating),]
+bestRes = head(gridOrdered,1)
+
+library(ggplot2)
+
+# midpoint England Premier-league 2.9
+# midpoint china Super-league 2.7
+# midpoint sweden Allsvenskan 2.8
+print("ggplot")
+ggplot(data = grid, aes(x = lambda, y = gamma, fill = rating)) + 
+  geom_tile() + scale_fill_gradient2(low = "blue", mid = "white", high = "red", midpoint = 2.7) + 
+  labs(x = "lambda", y = "gamma", title = "grid optimization", fill = "Mean \nsquared \nerror") + 
+  theme(plot.title = element_text(hjust = 0.5))
+
+
+# outcomes <- rbind(outcomes, c(0,0))
+# teams <- rbind(teams, c("Wolves", "Southampton"))
+
+# England 0.07, 0.7
+# China Super-league 0.07, 0.5
+# Sweden allsvenskan 0.075, 0.7
+pratings <- calculate_pi_ratings(teams, outcomes, bestRes$lambda, bestRes$gamma)
+
+
+print(tail(pratings, n = 15))
+
+# newDataset = dataset
+newDataset = piDataset
+newDataset = cbind(piDataset, pratings)
+
+colnames(newDataset)[9] <- "piOne"
+colnames(newDataset)[10] <- "piTwo"
+
+
+#test = cbind(dataset, pratings)
+#colnames(test)[10] <- "piOne"
+#colnames(test)[11] <- "piTwo"
+
+round_df <- function(df, digits) {
+  nums <- vapply(df, is.numeric, FUN.VALUE = logical(1))
+  
+  df[,nums] <- round(df[,nums], digits = digits)
+  
+  (df)
+}
+
+test = round_df(newDataset, digits = 3)
+
+test2 <- transform(test, piRes=test$piOne - test$piTwo)
+test2 <- transform(test2, bothSame=ifelse(test2$piRes > 0 & test2$avgHomeScore - test2$avgAwayScore > 0,TRUE,
+                                          ifelse(test2$piRes < 0 & test2$avgHomeScore - test2$avgAwayScore < 0, TRUE, 
+                                                 FALSE)))
+
+test2 <- transform(test2, correct=ifelse(test2$homeScore > test2$awayScore & test2$bothSame & test2$piRes > 0, TRUE,
+                                         ifelse(test2$homeScore < test2$awayScore & test2$bothSame & test2$piRes < 0, TRUE,
+                                                FALSE)))
+
+correct = test2[test2$correct == TRUE,]
+
+notSame = test2[test2$bothSame == FALSE,]
+
+draws = test2[test2$homeScore - test2$awayScore == 0,]
+
+drawsDiff = draws[draws$piRes < 0.09 & draws$piRes > -0.09,]
+
+### Regressor/ prediction thing
+
+# Splitting the dataset into the Training set and Test set
+# install.packages('caTools')
+#library(caTools)
+#set.seed(1111)
+# split = sample.split(dataset$result, SplitRatio = 0.8)
+
+smp_size <- floor(0.75 * nrow(newDataset))
+train_ind <- sample(seq_len(nrow(newDataset)), size = smp_size)
+train <- newDataset[1:smp_size,]
+test <- newDataset[smp_size:nrow(newDataset), ]
+
+smp_size <- floor(0.75 * nrow(test2))
+train_ind <- sample(seq_len(nrow(test2)), size = smp_size)
+train <- test2[1:smp_size,]
+test <- test2[smp_size:nrow(test2), ]
+
+
+
+# training_set = subset(dataset, split == TRUE)
+# test_set = subset(dataset, split == FALSE)
+
+
+# Feature scaling
+#training_set[,6:7] = scale(training_set[,6:7])
+#training_set[,9:11] = scale(training_set[,9:11])
+#test_set[,6:7] = scale(test_set[,6:7])
+#test_set[,9:11] = scale(test_set[,9:11])
+
+regressor = lm(formula = result ~ avgHomeScore + avgAwayScore + piOne + piTwo + piRes,
+               data = train)
+
+y_pred = predict(regressor, newdata = test)
+
+
+## Plot the result (probably not working)
+#library(ggplot2)
+#ggplot() +
+#  geom_point(aes(x = train$avgHomeScore, y = train$result), 
+#             colour = 'red') + 
+#  geom_line(aes(x = train$avgHomeScore, y = predict(regressor, newdata = train)),
+#            colour = 'blue') + 
+#  ggtitle('Result plot') + 
+#  xlab('Awarage Home Score') + 
+#  ylab('result')
+

+ 33 - 0
ATG/RScript/.Rproj.user/4A9417F8/sources/per/t/A60241BB

@@ -0,0 +1,33 @@
+{
+    "collab_server" : "",
+    "contents" : "",
+    "created" : 1612968573943.000,
+    "dirty" : false,
+    "encoding" : "",
+    "folds" : "",
+    "hash" : "0",
+    "id" : "A60241BB",
+    "lastKnownWriteTime" : 8026363914660965410,
+    "last_content_update" : 1612968573943,
+    "path" : null,
+    "project_path" : null,
+    "properties" : {
+        "cacheKey" : "E44B7244",
+        "caption" : "newRes",
+        "contentUrl" : "grid_resource/gridviewer.html?env=&obj=newRes&cache_key=E44B7244",
+        "displayedObservations" : 15,
+        "environment" : "",
+        "expression" : "newRes",
+        "object" : "newRes",
+        "preview" : 0,
+        "totalObservations" : 15,
+        "variables" : 21
+    },
+    "read_only" : false,
+    "read_only_alternatives" : [
+    ],
+    "relative_order" : 4,
+    "source_on_save" : false,
+    "source_window" : "",
+    "type" : "r_dataframe"
+}

+ 0 - 0
ATG/RScript/.Rproj.user/4A9417F8/sources/per/t/A60241BB-contents


+ 26 - 0
ATG/RScript/.Rproj.user/4A9417F8/sources/per/t/D28E45BF

@@ -0,0 +1,26 @@
+{
+    "collab_server" : "",
+    "contents" : "",
+    "created" : 1612968573416.000,
+    "dirty" : false,
+    "encoding" : "",
+    "folds" : "",
+    "hash" : "0",
+    "id" : "D28E45BF",
+    "lastKnownWriteTime" : 1889456614416,
+    "last_content_update" : 1612968573416,
+    "path" : null,
+    "project_path" : null,
+    "properties" : {
+        "id" : "a18347cdb7424d3cab3620e2d6252bf1",
+        "name" : "newRs",
+        "title" : "newRs"
+    },
+    "read_only" : false,
+    "read_only_alternatives" : [
+    ],
+    "relative_order" : 3,
+    "source_on_save" : false,
+    "source_window" : "",
+    "type" : "object_explorer"
+}

+ 0 - 0
ATG/RScript/.Rproj.user/4A9417F8/sources/per/t/D28E45BF-contents


+ 5 - 0
ATG/RScript/.Rproj.user/4A9417F8/sources/prop/70855DFE

@@ -0,0 +1,5 @@
+{
+    "cursorPosition" : "6,160",
+    "scrollLine" : "0",
+    "tempName" : "Untitled1"
+}

+ 1 - 0
ATG/RScript/.Rproj.user/4A9417F8/sources/prop/INDEX

@@ -0,0 +1 @@
+F%3A%2FProgramming%2FJAVA%2FOdds%2FATG%2FRScript%2FAtgPerdictions.R="70855DFE"

BIN
ATG/RScript/.Rproj.user/4A9417F8/viewer-cache/2D9C7EC.Rdata


BIN
ATG/RScript/.Rproj.user/4A9417F8/viewer-cache/E44B7244.Rdata


+ 0 - 0
ATG/RScript/.Rproj.user/shared/notebooks/patch-chunk-names


+ 1 - 0
ATG/RScript/.Rproj.user/shared/notebooks/paths

@@ -0,0 +1 @@
+F:/Programming/JAVA/Odds/ATG/RScript/AtgPerdictions.R="E8E7913D"

+ 163 - 0
ATG/RScript/AtgPerdictions.R

@@ -0,0 +1,163 @@
+# Data Preprocessing
+
+# Importing the Dataset
+library(RMySQL)
+mydb = dbConnect(MySQL(), user="atg", password="CvWKY34DqtlVgjt_9", host="nordh.xyz", dbname="atg")
+
+sqlRacesWithEnoughParticipants = "SELECT raceNumber, raceDate, Result FROM `Results` group BY RaceDate, RaceNumber HAVING count(RaceNumber) > 5 AND Result = 1  
+ORDER BY `Results`.`raceDate` DESC"
+
+# With AVG per distance
+newSql = "SELECT r1.*, ROUND(AVG(horseResults.time),4) as HorseAvgTime, ROUND(AVG(driverResults.time),4) as driverAvgTime, ROUND(AVG(IF(horseResults.Distance = r1.Distance, horseResults.time, null)),4) as driverAvgTime, ROUND(AVG(IF((horseResults.Distance = r1.Distance AND horseResults.Time > 0), horseResults.time, null)),4) as horseAvgByDistance, ROUND(AVG(IF((driverResults.Distance = r1.Distance AND driverResults.Time > 0), driverResults.time, null)),4) as driverAvgByDistance FROM Results r1 INNER JOIN Results horseResults ON r1.RaceDate > horseResults.RaceDate AND r1.HorseId = horseResults.HorseId INNER JOIN Results driverResults ON r1.RaceDate > driverResults.RaceDate AND r1.DriverId = driverResults.DriverId WHERE r1.raceNumber = 10 AND r1.RaceDate = '2021-02-08' GROUP BY r1.horseId"
+newRs = dbSendQuery(mydb, newSql)
+newRes = fetch(newRs, -1)
+
+"WITH avg times" 
+"SELECT r1.*, ROUND(AVG(if (horseResults.time > 0, horseResults.time,null)),4) as HorseAvgTime, ROUND(AVG(IF((horseResults.Distance = r1.Distance AND horseResults.Time > 0), horseResults.time, null)),4) as horseAvgByDistance FROM Results r1 INNER JOIN Results horseResults ON r1.RaceDate > horseResults.RaceDate AND r1.HorseId = horseResults.HorseId INNER JOIN Results driverResults ON r1.RaceDate > driverResults.RaceDate AND r1.DriverId = driverResults.DriverId WHERE r1.raceNumber = 10 AND r1.RaceDate = '2021-02-08' GROUP BY r1.horseId"
+
+sqlForVoltStartLaneDistribution = "SELECT Lane, SUM(result)/Count(*), Count(*) as cnt FROM `Results` WHERE Result > 0 AND Lane > -1 AND TimeModifier NOT like "%a%" GROUP BY Lane HAVING cnt > 20 ORDER BY `SUM(result)/Count(*)` ASC"
+sqlForAutoStartLaneDistribution = "SELECT Lane, SUM(result)/Count(*), Count(*) as cnt FROM `Results` WHERE Result > 0 AND Lane > -1 AND TimeModifier like "%a%" GROUP BY Lane HAVING cnt > 20 ORDER BY `SUM(result)/Count(*)` ASC"
+
+country = 'iran'
+league = 'persian-gulf-pro-league'
+
+rs = dbSendQuery(mydb, sql)
+
+dataset = fetch(rs, n=-1)
+
+###Taking case of missing data
+dataset$avgHomeScore = ifelse(is.na(dataset$avgHomeScore), 0, dataset$avgHomeScore)
+dataset$avgAwayScore = ifelse(is.na(dataset$avgAwayScore), 0, dataset$avgAwayScore)
+#dataset$avgHomeScoreOneYearBack = ifelse(is.na(dataset$avgHomeScoreOneYearBack), 0, dataset$avgHomeScoreOneYearBack)
+#dataset$avgAwayScoreOneYearBack = ifelse(is.na(dataset$avgAwayScoreOneYearBack), 0, dataset$avgAwayScoreOneYearBack)
+
+# Encoding categorical data
+##dataset$overtime = factor(dataset$overtime, levels = c('1-0', '0-1', 'NULL'), labels = c(1, 1, 0))
+##dataset$result = factor(dataset$result, levels = c('1', 'X', '2'), labels = c(1, 0, 2))
+
+
+library(piratings)
+
+rs2 = dbSendQuery(mydb, piSql)
+
+piDataset = fetch(rs2, n=-1)
+
+teams <- as.matrix(piDataset[,c("homeTeamName", "awayTeamName")])
+outcomes <- as.matrix(piDataset[, c("homeScore", "awayScore")])
+
+# England - premiere League seq(0.06, 0.1, 0.005), seq(0.4, 0.9, 0.05)
+grid <- optimize_pi_ratings(teams, outcomes, seq(0.04, 0.1, 0.005), seq(0.3, 0.9, 0.05))
+
+colnames(grid)[1] <- "rating"
+gridOrdered = grid[order(grid$rating),]
+bestRes = head(gridOrdered,1)
+
+library(ggplot2)
+
+# midpoint England Premier-league 2.9
+# midpoint china Super-league 2.7
+# midpoint sweden Allsvenskan 2.8
+print("ggplot")
+ggplot(data = grid, aes(x = lambda, y = gamma, fill = rating)) + 
+  geom_tile() + scale_fill_gradient2(low = "blue", mid = "white", high = "red", midpoint = 2.7) + 
+  labs(x = "lambda", y = "gamma", title = "grid optimization", fill = "Mean \nsquared \nerror") + 
+  theme(plot.title = element_text(hjust = 0.5))
+
+
+# outcomes <- rbind(outcomes, c(0,0))
+# teams <- rbind(teams, c("Wolves", "Southampton"))
+
+# England 0.07, 0.7
+# China Super-league 0.07, 0.5
+# Sweden allsvenskan 0.075, 0.7
+pratings <- calculate_pi_ratings(teams, outcomes, bestRes$lambda, bestRes$gamma)
+
+
+print(tail(pratings, n = 15))
+
+# newDataset = dataset
+newDataset = piDataset
+newDataset = cbind(piDataset, pratings)
+
+colnames(newDataset)[9] <- "piOne"
+colnames(newDataset)[10] <- "piTwo"
+
+
+#test = cbind(dataset, pratings)
+#colnames(test)[10] <- "piOne"
+#colnames(test)[11] <- "piTwo"
+
+round_df <- function(df, digits) {
+  nums <- vapply(df, is.numeric, FUN.VALUE = logical(1))
+  
+  df[,nums] <- round(df[,nums], digits = digits)
+  
+  (df)
+}
+
+test = round_df(newDataset, digits = 3)
+
+test2 <- transform(test, piRes=test$piOne - test$piTwo)
+test2 <- transform(test2, bothSame=ifelse(test2$piRes > 0 & test2$avgHomeScore - test2$avgAwayScore > 0,TRUE,
+                                          ifelse(test2$piRes < 0 & test2$avgHomeScore - test2$avgAwayScore < 0, TRUE, 
+                                                 FALSE)))
+
+test2 <- transform(test2, correct=ifelse(test2$homeScore > test2$awayScore & test2$bothSame & test2$piRes > 0, TRUE,
+                                         ifelse(test2$homeScore < test2$awayScore & test2$bothSame & test2$piRes < 0, TRUE,
+                                                FALSE)))
+
+correct = test2[test2$correct == TRUE,]
+
+notSame = test2[test2$bothSame == FALSE,]
+
+draws = test2[test2$homeScore - test2$awayScore == 0,]
+
+drawsDiff = draws[draws$piRes < 0.09 & draws$piRes > -0.09,]
+
+### Regressor/ prediction thing
+
+# Splitting the dataset into the Training set and Test set
+# install.packages('caTools')
+#library(caTools)
+#set.seed(1111)
+# split = sample.split(dataset$result, SplitRatio = 0.8)
+
+smp_size <- floor(0.75 * nrow(newDataset))
+train_ind <- sample(seq_len(nrow(newDataset)), size = smp_size)
+train <- newDataset[1:smp_size,]
+test <- newDataset[smp_size:nrow(newDataset), ]
+
+smp_size <- floor(0.75 * nrow(test2))
+train_ind <- sample(seq_len(nrow(test2)), size = smp_size)
+train <- test2[1:smp_size,]
+test <- test2[smp_size:nrow(test2), ]
+
+
+
+# training_set = subset(dataset, split == TRUE)
+# test_set = subset(dataset, split == FALSE)
+
+
+# Feature scaling
+#training_set[,6:7] = scale(training_set[,6:7])
+#training_set[,9:11] = scale(training_set[,9:11])
+#test_set[,6:7] = scale(test_set[,6:7])
+#test_set[,9:11] = scale(test_set[,9:11])
+
+regressor = lm(formula = result ~ avgHomeScore + avgAwayScore + piOne + piTwo + piRes,
+               data = train)
+
+y_pred = predict(regressor, newdata = test)
+
+
+## Plot the result (probably not working)
+#library(ggplot2)
+#ggplot() +
+#  geom_point(aes(x = train$avgHomeScore, y = train$result), 
+#             colour = 'red') + 
+#  geom_line(aes(x = train$avgHomeScore, y = predict(regressor, newdata = train)),
+#            colour = 'blue') + 
+#  ggtitle('Result plot') + 
+#  xlab('Awarage Home Score') + 
+#  ylab('result')
+

+ 13 - 0
ATG/RScript/RScript.Rproj

@@ -0,0 +1,13 @@
+Version: 1.0
+
+RestoreWorkspace: Default
+SaveWorkspace: Default
+AlwaysSaveHistory: Default
+
+EnableCodeIndexing: Yes
+UseSpacesForTab: Yes
+NumSpacesForTab: 2
+Encoding: UTF-8
+
+RnwWeave: Sweave
+LaTeX: pdfLaTeX

+ 8 - 0
ATG/build.fxbuild

@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="ASCII"?>
+<anttasks:AntTask xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI" xmlns:anttasks="http://org.eclipse.fx.ide.jdt/1.0" buildDirectory="${project}/build">
+  <deploy>
+    <application name="ATG"/>
+    <info/>
+  </deploy>
+  <signjar/>
+</anttasks:AntTask>

+ 51 - 0
ATG/pom.xml

@@ -0,0 +1,51 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <groupId>ATG</groupId>
+  <artifactId>ATG</artifactId>
+  <version>0.0.1-SNAPSHOT</version>
+  <build>
+    <sourceDirectory>src</sourceDirectory>
+    <resources>
+      <resource>
+        <directory>src</directory>
+        <excludes>
+          <exclude>**/*.java</exclude>
+        </excludes>
+      </resource>
+    </resources>
+    <plugins>
+      <plugin>
+        <artifactId>maven-compiler-plugin</artifactId>
+        <version>3.8.1</version>
+        <configuration>
+          <release>11</release>
+        </configuration>
+      </plugin>
+    </plugins>
+  </build>
+  <dependencies>
+     <dependency>
+	 <groupId>com.google.guava</groupId>
+	 <artifactId>guava</artifactId>
+	 <version>30.0-jre</version>
+	</dependency>
+	<dependency>
+ 	   <groupId>net.sourceforge.htmlunit</groupId>
+	    <artifactId>htmlunit</artifactId>
+	    <version>2.46.0</version>
+	</dependency>
+	
+	<dependency>
+    	<groupId>mysql</groupId>
+    	<artifactId>mysql-connector-java</artifactId>
+    	<version>8.0.22</version>
+	</dependency>
+
+	<dependency>
+	    <groupId>com.google.code.gson</groupId>
+	    <artifactId>gson</artifactId>
+	    <version>2.8.6</version>
+	</dependency>
+	
+  </dependencies>
+</project>

+ 51 - 0
ATG/src/application/ATGMain.java

@@ -0,0 +1,51 @@
+package application;
+
+import java.io.IOException;
+
+import javafx.application.Application;
+import javafx.fxml.FXMLLoader;
+import javafx.scene.Scene;
+import javafx.scene.layout.AnchorPane;
+import javafx.stage.Stage;
+
+
+public class ATGMain extends Application {
+
+	private Stage primaryStage;
+	private AnchorPane rootLayout;
+
+	@Override
+	public void start(Stage primaryStage) {
+		try {
+			this.primaryStage = primaryStage;
+			this.primaryStage.setTitle("ATG Odds");
+			this.primaryStage.setMaximized(true);
+
+			initRootLayout();
+		} catch(final Exception e) {
+			e.printStackTrace();
+		}
+	}
+
+	public void initRootLayout() {
+		try {
+			// Load root layout from fxml file.
+			final FXMLLoader loader = new FXMLLoader();
+			loader.setLocation(ATGMain.class.getResource("../fxml/AtgMain.fxml"));
+			rootLayout = (AnchorPane) loader.load();
+
+			// Show the scene containing the root layout.
+			final Scene scene = new Scene(rootLayout);
+			primaryStage.setScene(scene);
+			primaryStage.show();
+		} catch (final IOException e) {
+			e.printStackTrace();
+		}
+	}
+
+
+	public static void main(String[] args) {
+		launch(args);
+	}
+
+}

+ 1 - 0
ATG/src/application/application.css

@@ -0,0 +1 @@
+/* JavaFX CSS - Leave this comment until you have at least create one rule which uses -fx-Property */

+ 242 - 0
ATG/src/controllers/AtgController.java

@@ -0,0 +1,242 @@
+package controllers;
+
+import java.net.URL;
+import java.util.AbstractMap.SimpleEntry;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.ResourceBundle;
+
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+
+import javafx.collections.FXCollections;
+import javafx.collections.ObservableList;
+import javafx.fxml.FXML;
+import javafx.fxml.Initializable;
+import javafx.scene.control.TableColumn;
+import javafx.scene.control.TableView;
+import javafx.scene.control.cell.MapValueFactory;
+import objects.HorseRaceData;
+
+@SuppressWarnings("rawtypes")
+public class AtgController implements Initializable {
+
+	@FXML TableColumn<Map, Integer> raceColumn = new TableColumn<>("Race");
+	@FXML TableColumn<Map, String> horseNameColumn = new TableColumn<>("Horse name");
+	@FXML TableColumn<Map, Integer> laneColumn = new TableColumn<>("Lane");
+	@FXML TableColumn<Map, Integer> distanceColumn = new TableColumn<>("Distance");
+	@FXML TableColumn<Map, String> timeModifierColumn = new TableColumn<>("Time modifier");
+	@FXML TableColumn<Map, String> shoesColumn = new TableColumn<>("Shoes");
+	@FXML TableColumn<Map, String> driverNameColumn = new TableColumn<>("Driver name");
+	@FXML TableColumn<Map, String> trainerNameColumn = new TableColumn<>("Trainer name");
+	@FXML TableColumn<Map, Integer> resultColumn = new TableColumn<>("Result");
+	@FXML TableColumn<Map, String> horseAvgTimeColumn = new TableColumn<>("Horse avg time");
+	@FXML TableColumn<Map, String> horseAvgTimeDistanceColumn = new TableColumn<>("Horse avg time distance");
+	@FXML TableColumn<Map, String> driverAvgTimeColumn = new TableColumn<>("Driver avg time");
+	@FXML TableColumn<Map, String> driverAvgTimeDistanceColumn = new TableColumn<>("Driver Avg time distance");
+	@FXML TableColumn<Map, Float> avgTimeColumn = new TableColumn<>("Avg time");
+	@FXML TableColumn<Map, Float> winPercentColumn = new TableColumn<>("Lane win percent");
+	@FXML TableColumn<Map, String> horseGallopRateColumn = new TableColumn<>("Horse gallop rate");
+	@FXML TableColumn<Map, String> driverGallopRateColumn = new TableColumn<>("Driver gallop rate");
+	@FXML TableColumn<Map, Float> calculationColumn = new TableColumn<>("Calculated Value");
+	@FXML TableColumn<Map, Float> prevCalculationColumn = new TableColumn<>("Previous Calculated Value");
+	@FXML TableColumn<Map, String> trackColumn = new TableColumn<>("Track");
+
+	List<SimpleEntry<Integer, Float>> autostartPercentages;
+	List<SimpleEntry<Integer, Float>> voltstartPercentages;
+
+
+	@FXML
+	protected TableView<Map<String, Object>> racesTable;
+
+	protected List<HorseRaceData> todaysRaces = new ArrayList<>();
+
+	protected ObservableList<Map<String, Object>> tableRaces;
+	private final float horseDistanceAndTypeMultiplyer = 3f; // orig 3f
+	private final float horseDistanceMultiplier = 2f;// orig 2f
+	private final float driverDistanceAndTypeMultiplyer = 3f;// orig 3f
+	private final float driverDistanceMultiplier = 2f;// orig 2f
+
+	@Override
+	public void initialize(URL arg0, ResourceBundle arg1) {
+		trackColumn.setCellValueFactory(new MapValueFactory<>("track"));
+		raceColumn.setCellValueFactory(new MapValueFactory<>("race"));
+		horseNameColumn.setCellValueFactory(new MapValueFactory<>("horseName"));
+		laneColumn.setCellValueFactory(new MapValueFactory<>("lane"));
+		distanceColumn.setCellValueFactory(new MapValueFactory<>("distance"));
+		timeModifierColumn.setCellValueFactory(new MapValueFactory<>("timeModifier"));
+		shoesColumn.setCellValueFactory(new MapValueFactory<>("shoes"));
+		driverNameColumn.setCellValueFactory(new MapValueFactory<>("driverName"));
+		trainerNameColumn.setCellValueFactory(new MapValueFactory<>("trainerName"));
+		resultColumn.setCellValueFactory(new MapValueFactory<>("result"));
+		horseAvgTimeColumn.setCellValueFactory(new MapValueFactory<>("horseAvgTime"));
+		horseAvgTimeDistanceColumn.setCellValueFactory(new MapValueFactory<>("horseAvgTimeDistance"));
+		driverAvgTimeColumn.setCellValueFactory(new MapValueFactory<>("driverAvgTime"));
+		driverAvgTimeDistanceColumn.setCellValueFactory(new MapValueFactory<>("driverAvgTimeDistance"));
+		avgTimeColumn.setCellValueFactory(new MapValueFactory<>("avgTime"));
+		winPercentColumn.setCellValueFactory(new MapValueFactory<>("winPercent"));
+		horseGallopRateColumn.setCellValueFactory(new MapValueFactory<>("horseGallopRate"));
+		driverGallopRateColumn.setCellValueFactory(new MapValueFactory<>("driverGallopRate"));
+		calculationColumn.setCellValueFactory(new MapValueFactory<>("calculatedValue"));
+		prevCalculationColumn.setCellValueFactory(new MapValueFactory<>("prevCalculatedValue"));
+
+	}
+
+	public void updateRacesTable() {
+		racesTable.getItems().clear();
+		tableRaces = FXCollections.<Map<String, Object>>observableArrayList();
+		for (final HorseRaceData horseRaceData : todaysRaces) {
+			final Map<String, Object> row = Maps.newHashMap();
+			row.put("track", horseRaceData.getTrackName());
+			row.put("race", horseRaceData.getRaceNumber());
+			row.put("horseName", horseRaceData.getHorseName());
+			row.put("lane", horseRaceData.getLane());
+			row.put("distance", horseRaceData.getDistance());
+			row.put("timeModifier", horseRaceData.getTimeModifier());
+			row.put("shoes", horseRaceData.getShoes());
+			row.put("driverName", horseRaceData.getDriverName());
+			row.put("trainerName", horseRaceData.getTrainerName());
+			row.put("result", horseRaceData.getResult());
+			row.put("horseAvgTime", horseRaceData.getAvgHorseTime() + "(" + horseRaceData.getAvgHorseTimeCount() + ")");
+			row.put("driverAvgTime", horseRaceData.getAvgDriverTime() + "(" + horseRaceData.getAvgDriverTimeCount() + ")");
+
+			row.put("horseAvgTimeDistance", horseRaceData.getAvgHorseTimeByDistance() + "(" + horseRaceData.getAvgHorseTimeByDistanceCount() + ")");
+			row.put("driverAvgTimeDistance", horseRaceData.getAvgDriverTimeByDistance() + "(" + horseRaceData.getAvgDriverTimeByDistanceCount() + ")");
+
+
+			row.put("avgTime", (horseRaceData.getAvgHorseTime() + horseRaceData.getAvgDriverTime()) / 2f);
+
+			final ArrayList<String> distances = Lists.newArrayList();
+			todaysRaces.stream()
+			.filter(hr -> hr.getRaceNumber() == horseRaceData.getRaceNumber())
+			.forEach(hr -> { if (!distances.contains(String.valueOf(horseRaceData.getDistance()))) {
+				distances.add(String.valueOf(hr.getDistance()));
+			}
+			});
+
+			final float laneWinPercent = getLaneWinPercent(horseRaceData, distances);
+			try {
+				if (horseRaceData.getLane() > 0 && horseRaceData.getTimeModifier().contains("a")) {
+					row.put("winPercent", autostartPercentages.get(horseRaceData.getLane()-1).getValue());
+				} else {
+					row.put("winPercent", voltstartPercentages.get(horseRaceData.getLane()-1).getValue());
+				}
+			} catch (final IndexOutOfBoundsException e) {
+				row.put("winPercent", 0f);
+			}
+
+			final SimpleEntry<Integer,Integer> numHorseGallop = DatabaseContoller.getInstance().getNumHorseGallop(horseRaceData.getHorseId());
+			final SimpleEntry<Integer,Integer> numDriverGallop = DatabaseContoller.getInstance().getNumDriverGallop(horseRaceData.getDriverId());
+			row.put("horseGallopRate", numHorseGallop.getValue() + " ("+Math.round((numHorseGallop.getValue()/Float.valueOf(numHorseGallop.getKey()))*10000)/100+"%) ");
+			row.put("driverGallopRate", numDriverGallop.getValue() + " (" + Math.round((numDriverGallop.getValue()/Float.valueOf(numDriverGallop.getKey()))*10000)/100 + "%) ");
+
+
+			float calcValue = prevCalculation(horseRaceData, laneWinPercent);
+			row.put("prevCalculatedValue", calcValue);
+
+			calcValue = calculatedValue(horseRaceData, calcValue);
+			row.put("calculatedValue", calcValue);
+
+			tableRaces.add(row);
+		}
+
+		racesTable.getItems().addAll(tableRaces);
+	}
+
+	protected float getLaneWinPercent(HorseRaceData horseRaceData, ArrayList<String> distances) {
+		float laneWinPercent = -1;
+		if (horseRaceData.getLane() > 0 && horseRaceData.getTimeModifier().contains("a")) {
+			if (autostartPercentages == null) { // || prevRaceDistance != horseRaceData.getDistance()) {
+				autostartPercentages = DatabaseContoller.getInstance().getAutostartWinPercents(distances);
+			}
+			if (horseRaceData.getLane() < autostartPercentages.size()) {
+				laneWinPercent = autostartPercentages.get(horseRaceData.getLane()-1).getValue();
+			}
+		} else if (horseRaceData.getLane() > 0 && !horseRaceData.getTimeModifier().contains("a")) {
+			if (voltstartPercentages == null) { // || prevRaceDistance != horseRaceData.getDistance()) {
+				voltstartPercentages = DatabaseContoller.getInstance().getVoltstartWinPercents(distances);
+			}
+			if (horseRaceData.getLane() < voltstartPercentages.size()) {
+				laneWinPercent = voltstartPercentages.get(horseRaceData.getLane()-1).getValue();
+			}
+		}
+		return laneWinPercent;
+	}
+
+	protected float calculatedValue(final HorseRaceData horseRaceData, float calcValue) {
+		float returnValue = calcValue;
+		returnValue += calculateCombinedDriverValue(horseRaceData)/2;
+		return returnValue;
+
+	}
+
+	protected float prevCalculation(final HorseRaceData horseRaceData, float laneWinPercent) {
+		// Calculate Comparison
+		float calcValue = calculateCombinedHorseValue(horseRaceData);
+
+		if (laneWinPercent > -1) {
+			calcValue -= laneWinPercent / 10;
+		}
+		return calcValue;
+	}
+
+	protected float calculateCombinedHorseValue(HorseRaceData horseRaceData) {
+		float returnValue = 0f;
+		boolean distanceAndType = false;
+		boolean distance = false;
+		returnValue += horseRaceData.getAvgHorseTime();
+		float divideBy = 1f;
+		if (horseRaceData.getAvgHorseTimeByDistanceAndTypeCount() > 0) {
+			distanceAndType = true;
+			divideBy += horseDistanceAndTypeMultiplyer ;
+		}
+		if (horseRaceData.getAvgHorseTimeByDistanceCount() > 0) {
+			distance = true;
+			divideBy += horseDistanceMultiplier;
+		}
+
+		if (distanceAndType) {
+			returnValue += horseRaceData.getAvgHorseTimeByDistanceAndType() * horseRaceData.getAvgHorseTimeByDistanceAndTypeCount();//* horseDistanceAndTypeMultiplyer;//
+			divideBy += horseRaceData.getAvgHorseTimeByDistanceAndTypeCount();
+		}
+		if (distance) {
+			returnValue += horseRaceData.getAvgHorseTimeByDistance() * horseRaceData.getAvgHorseTimeByDistanceCount(); // * horseDistanceMultiplier;//
+			divideBy += horseRaceData.getAvgHorseTimeByDistanceCount();
+		}
+
+		return returnValue / divideBy;
+	}
+
+	protected float calculateCombinedDriverValue(HorseRaceData horseRaceData) {
+		float returnValue = 0f;
+		boolean distanceAndType = false;
+		boolean distance = false;
+		returnValue += horseRaceData.getAvgDriverTime();
+		float divideBy = 1f;
+		if (horseRaceData.getAvgDriverTimeByDistanceAndTypeCount() > 0) {
+			distanceAndType = true;
+			divideBy += driverDistanceAndTypeMultiplyer;
+		}
+		if (horseRaceData.getAvgDriverTimeByDistanceCount() > 0) {
+			distance = true;
+			divideBy += driverDistanceMultiplier;
+		}
+
+		if (distanceAndType) {
+			returnValue += horseRaceData.getAvgDriverTimeByDistanceAndType() * horseRaceData.getAvgDriverTimeByDistanceAndTypeCount();// * driverDistanceAndTypeMultiplyer;//
+			divideBy += horseRaceData.getAvgDriverTimeByDistanceAndTypeCount();
+		}
+		if (distance) {
+			returnValue += horseRaceData.getAvgDriverTimeByDistance() * horseRaceData.getAvgDriverTimeByDistanceCount();// * driverDistanceMultiplier;//
+			divideBy += horseRaceData.getAvgDriverTimeByDistanceCount();
+		}
+
+		return returnValue / divideBy;
+	}
+
+	protected void resetLaneWinPercentages() {
+		autostartPercentages = null;
+		voltstartPercentages = null;
+	}
+}

+ 36 - 0
ATG/src/controllers/DDTabController.java

@@ -0,0 +1,36 @@
+package controllers;
+
+import java.net.URL;
+import java.util.Date;
+import java.util.Map;
+import java.util.ResourceBundle;
+
+import javafx.collections.ObservableList;
+import javafx.fxml.FXML;
+import javafx.scene.control.TableView;
+import javafx.scene.layout.Pane;
+
+public class DDTabController extends AtgController {
+
+	@FXML Pane DDButtonPane;
+	@FXML Pane DDResultPane;
+	@FXML private TableView<Map<String, Object>> racesTable;
+
+	ObservableList<Map<String, Object>> tableRaces;
+
+	@Override
+	public void initialize(URL arg0, ResourceBundle arg1) {
+		super.initialize(arg0, arg1);
+		todaysRaces = DatabaseContoller.getInstance().getTodaysRaces("DD", MainController.DATE_FORMAT.format(new Date()));
+		updateRacesTable();
+	}
+
+	@FXML
+	public void GetTodaysRacesAction() {
+		//		if (racesTable.getItems().size() <= 0) { // Hämta statistik från ATG för hästnamn och från travsport för data
+		//		AtgParser.getInstance().getTodaysStartsFromATG("dd");
+		//		}
+		todaysRaces = DatabaseContoller.getInstance().getTodaysRaces("DD", MainController.DATE_FORMAT.format(new Date()));
+		updateRacesTable();
+	}
+}

+ 792 - 0
ATG/src/controllers/DatabaseContoller.java

@@ -0,0 +1,792 @@
+package controllers;
+
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.AbstractMap.SimpleEntry;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+import com.google.common.base.Strings;
+import com.google.common.collect.Lists;
+
+import objects.HorseRaceData;
+import objects.UpdateRaceRow;
+import parsers.TravsportParser;
+
+public class DatabaseContoller {
+
+	private static final DatabaseContoller instance = new DatabaseContoller();
+
+	private static final String username = "atg";
+	private static final String password = "CvWKY34DqtlVgjt_9";
+	private static final String database = "atg";
+	private static final String url = "jdbc:mysql://nordh.xyz:3306/";
+	private static final String timezoneFix = "?useUnicode=true&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=UTC";
+
+	private Connection conn;
+
+	protected DatabaseContoller() {
+		getConnection();
+	}
+
+	public static DatabaseContoller getInstance() {
+		return instance;
+	}
+
+	protected Connection getConnection() {
+		if (conn == null) {
+			try {
+				conn = DriverManager.getConnection(url + database + timezoneFix, username, password);
+			} catch (final SQLException e) {
+				e.printStackTrace();
+			}
+		}
+		return conn;
+	}
+
+	public void insertResult(String trackShortCode, String date, int raceNumber, int lane, int distance, int result,
+			float time, String timeModifier, int shoes, int sulky, String driver, String trainer, String horseName,
+			int travsportIdHorse, int travsportIdTrainer, int travsportIdDriver, String raceType, int raceId) {
+		final String sql;
+		final boolean raceToday = date.equals(MainController.DATE_FORMAT.format(new Date()));
+		horseName = horseName.replaceAll("[^a-zåäöA-ZÅÄÖ0-9 ()\\.']", "");
+		driver = driver.replaceAll("[^a-zåäöA-ZÅÄÖ0-9] ()\\.'", "");
+		if (raceToday) {
+			sql = "INSERT INTO "
+					+ "`Results`(`TrackId`, `RaceDate`, `RaceNumber`, `Lane`, `Distance`, `Result`, `Time`, `TimeModifier`, "
+					+ "`Shoes`, `Sulky`, `DriverId`, `TrainerId`, `HorseId`, raceId, RaceType) "
+					+ "VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?) ON DUPLICATE KEY UPDATE id=id, Result = ?, Time = ?, RaceType = CONCAT(RaceType, ?)";
+		} else {
+			sql = "INSERT INTO "
+					+ "`Results`(`TrackId`, `RaceDate`, `RaceNumber`, `Lane`, `Distance`, `Result`, `Time`, `TimeModifier`, "
+					+ "`Shoes`, `Sulky`, `DriverId`, `TrainerId`, `HorseId`, raceId) "
+					+ "VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?) ON DUPLICATE KEY UPDATE id=id, Result = ?, Time = ?";
+		}
+
+		final int horseId = getHorseId(horseName, travsportIdHorse);
+		final int driverId = getDriverId(driver, travsportIdDriver);
+		final int trainerId = getTrainerId(trainer, travsportIdTrainer);
+		final int trackId = getTrackIdFromShortcode(trackShortCode);
+
+		try {
+			final PreparedStatement stat = getConnection().prepareStatement(sql);
+
+			stat.setInt(1, trackId);
+			stat.setString(2, date);
+			stat.setInt(3, raceNumber);
+			stat.setInt(4, lane);
+			stat.setInt(5, distance);
+			stat.setInt(6, result);
+			stat.setFloat(7, time);
+			stat.setString(8, timeModifier);
+			stat.setInt(9, shoes);
+			stat.setInt(10, sulky);
+			stat.setInt(11, driverId);
+			stat.setInt(12, trainerId);
+			stat.setInt(13, horseId);
+			stat.setInt(14, raceId);
+
+			if (raceToday) {
+				stat.setString(15, raceType);
+
+				stat.setInt(16, result);
+				stat.setFloat(17, time);
+				stat.setString(18, ", " + raceType);
+			} else {
+				stat.setInt(15, result);
+				stat.setFloat(16, time);
+			}
+			stat.executeUpdate();
+
+		} catch (final SQLException e) {
+			e.printStackTrace();
+		}
+	}
+
+	private int insertNewName(String table, String name, int travsportId) { // travsportId inte generell, behövs en bättre lösning
+		final String sql = "INSERT INTO " + table + " (name, travsportId) VALUES (?, ?)";
+		int last_id = -1;
+		try {
+			final PreparedStatement stat = getConnection().prepareStatement(sql, PreparedStatement.RETURN_GENERATED_KEYS);
+			stat.setString(1, name);
+			stat.setInt(2, travsportId);
+
+			stat.executeUpdate();
+			final ResultSet generatedId = stat.getGeneratedKeys();
+			while (generatedId.next()) {
+				last_id = generatedId.getInt(1);
+			}
+
+		} catch (final SQLException e) {
+			e.printStackTrace();
+		}
+		return last_id;
+	}
+
+	private int getTrackIdFromShortcode(String shortcode) {
+		final String trackSql = "SELECT id FROM Track WHERE shortCode = ?";
+		int trackId = -1;
+		try {
+			final PreparedStatement stat = getConnection().prepareStatement(trackSql);
+			stat.setString(1, shortcode);
+
+			final ResultSet rs = stat.executeQuery();
+			while (rs.next()) {
+				trackId = rs.getInt("id");
+			}
+		} catch (final SQLException e) {
+			e.printStackTrace();
+		}
+
+		return trackId;
+	}
+
+	public int getHorseId(String horseName, int travsportId) {
+		horseName = horseName.replaceAll("[^a-zåäöA-ZÅÄÖ0-9 ()\\.']", "");
+		final String horseSql = "SELECT id FROM Horse WHERE name = ? OR travsportId = ?";
+		int horseId = -1;
+		try {
+			final PreparedStatement stat = getConnection().prepareStatement(horseSql);
+			stat.setString(1, horseName);
+			stat.setInt(2, travsportId);
+
+			final ResultSet rs = stat.executeQuery();
+			while (rs.next()) {
+				horseId = rs.getInt("id");
+			}
+			if (horseId == -1) {
+				horseId = insertNewName("Horse", horseName, travsportId);
+			}
+		} catch (final SQLException e) {
+			e.printStackTrace();
+		}
+
+		return horseId;
+	}
+
+	public int getHorseTravsportId(String horseName) {
+		final String sql = "SELECT travsportId FROM Horse WHERE name = ?";
+		int id = -1;
+
+		try {
+			final PreparedStatement stat = getConnection().prepareStatement(sql);
+			stat.setString(1, horseName);
+
+			final ResultSet rs = stat.executeQuery();
+
+			while (rs.next()) {
+				id = rs.getInt("travsportId");
+			}
+		} catch (final SQLException e) {
+			e.printStackTrace();
+		}
+
+		return id;
+	}
+
+	public int getDriverId(String driverName, int travsportId) {
+		driverName = driverName.replaceAll("[^a-zA-Z0-9 ]", "");
+		final String driverSql = "SELECT id FROM Driver WHERE name = ? OR travsportId = ?";
+		int driverId = -1;
+		try {
+			final PreparedStatement stat = getConnection().prepareStatement(driverSql);
+			stat.setString(1, driverName);
+			stat.setInt(2, travsportId);
+
+			final ResultSet rs = stat.executeQuery();
+			while (rs.next()) {
+				driverId = rs.getInt("id");
+			}
+			if (driverId == -1) {
+				driverId = insertNewName("Driver", driverName, travsportId);
+			}
+		} catch (final SQLException e) {
+			e.printStackTrace();
+		}
+
+		return driverId;
+	}
+
+	private int getTrainerId(String trainerName, int travsportId) {
+		trainerName = trainerName.replaceAll("[^a-zA-Z0-9 ]", "");
+		final String trainerSql = "SELECT id FROM Trainer WHERE name = ? OR travsportId = ?";
+		int trainerId = -1;
+		try {
+			final PreparedStatement stat = getConnection().prepareStatement(trainerSql);
+			stat.setString(1, trainerName);
+			stat.setInt(2, travsportId);
+
+			final ResultSet rs = stat.executeQuery();
+			while (rs.next()) {
+				trainerId = rs.getInt("id");
+			}
+			if (trainerId == -1) {
+				trainerId = insertNewName("Trainer", trainerName, travsportId);
+			}
+		} catch (final SQLException e) {
+			e.printStackTrace();
+		}
+
+		return trainerId;
+	}
+
+	public void setHorseTravsportId(int horseId, String horseName) {
+		final String sql = "UPDATE Horse SET travsportId = ? WHERE name = ?";
+
+		try {
+			final PreparedStatement stat = getConnection().prepareStatement(sql);
+			stat.setInt(1, horseId);
+			stat.setString(2, horseName);
+
+			stat.executeUpdate();
+		} catch (final SQLException e) {
+			e.printStackTrace();
+		}
+	}
+
+	// Lane stats SELECT Lane, Count(*) FROM `Results` WHERE Result = 1 AND TimeModifier NOT like "%a%" GROUP BY Lane ORDER BY `Results`.`Lane` ASC
+	// SELECT Lane, (Count(*) / SUM(COUNT(*)) OVER()) * 100 AS Percent, Distance FROM `Results` WHERE Result = 1 AND TimeModifier NOT like '%a%' AND Lane > 0 AND Distance IN (2140,2160) GROUP BY Lane, Distance ORDER BY `Results`.`Lane` ASC
+
+	public List<SimpleEntry<Integer, Float>> getAutostartWinPercents(ArrayList<String> distance) {
+		final List<SimpleEntry<Integer, Float>> resultList = Lists.newArrayList();
+		final String sql = "SELECT Lane, (Count(*) / SUM(COUNT(*)) OVER()) * 100 AS Percent, distance FROM `Results` WHERE Result = 1 AND TimeModifier like '%a%' AND Lane > 0 AND Distance IN (?) GROUP BY Lane, distance ORDER BY `Results`.`Lane` ASC";
+		try {
+			final PreparedStatement stat = getConnection().prepareStatement(sql);
+			stat.setString(1, String.join(",", distance));
+			final ResultSet rs = stat.executeQuery();
+
+			while (rs.next()) {
+				resultList.add(new SimpleEntry<>(rs.getInt("Lane"), rs.getFloat("Percent")));
+			}
+
+		} catch (final SQLException e) {
+			e.printStackTrace();
+		}
+
+		return resultList;
+	}
+
+	public List<SimpleEntry<Integer, Float>> getVoltstartWinPercents(ArrayList<String> distance) {
+		final List<SimpleEntry<Integer, Float>> resultList = Lists.newArrayList();
+		final String sql = "SELECT Lane, (Count(*) / SUM(COUNT(*)) OVER()) * 100 AS Percent FROM `Results` WHERE Result = 1 AND TimeModifier NOT like '%a%' AND Lane > 0 AND Distance = ? GROUP BY Lane ORDER BY `Results`.`Lane` ASC";
+		try {
+			final PreparedStatement stat = getConnection().prepareStatement(sql);
+			stat.setString(1, String.join(",",distance));
+			final ResultSet rs = stat.executeQuery();
+
+			while (rs.next()) {
+				resultList.add(new SimpleEntry<>(rs.getInt("Lane"), rs.getFloat("Percent")));
+			}
+
+		} catch (final SQLException e) {
+			e.printStackTrace();
+		}
+
+		return resultList;
+	}
+
+	public List<HorseRaceData> getTodaysRaces(String type, String date) {
+		final String sql;
+		if (Strings.isNullOrEmpty(type)) {
+			sql = "SELECT r1.*, driver.name as DriverName, horse.name as HorseName, track.name as TrackName, "
+					+ "ROUND(AVG(if (horseResults.time > 0, horseResults.time,null)),4) horseAvg, "
+					+ "ROUND(AVG(IF(driverResults.time > 0, driverResults.time, null)),4) driverAvg, "
+					+ "ROUND(AVG(IF((horseResults.Distance = r1.Distance AND horseResults.Time > 0), horseResults.time, null)),4) horseAvgByDistance, "
+					+ "ROUND(AVG(IF((driverResults.Distance = r1.Distance AND driverResults.Time > 0), driverResults.time, null)),4) driverAvgByDistance "
+					+ "FROM Results r1 "
+					+ "				INNER JOIN Driver as driver ON driverId = driver.id "
+					+ "				INNER JOIN Horse as horse ON HorseId = horse.id "
+					+ "				INNER JOIN Track as track ON TrackId = track.id "
+					+ " INNER JOIN Results horseResults ON r1.RaceDate > horseResults.RaceDate AND r1.HorseId = horseResults.HorseId "
+					+ " INNER JOIN Results driverResults ON r1.RaceDate > driverResults.RaceDate AND r1.DriverId = driverResults.DriverId "
+					/*+ " WHERE r1.RaceDate = ? AND r1.RaceNumber IN (9,10)"*/
+					+ " WHERE r1.RaceDate = ? "
+					+ " GROUP BY r1.horseId "
+					+ "ORDER BY `r1`.`RaceNumber` ASC";
+		} else {
+			sql = "SELECT r1.*, driver.name as DriverName, horse.name as HorseName, track.name as TrackName, "
+					+ "ROUND(AVG(if (horseResults.time > 0, horseResults.time,null)),4) horseAvg, "
+					+ "ROUND(AVG(IF(driverResults.time > 0, driverResults.time, null)),4) driverAvg, "
+					+ "ROUND(AVG(IF((horseResults.Distance = r1.Distance AND horseResults.Time > 0), horseResults.time, null)),4) horseAvgByDistance, "
+					+ "ROUND(AVG(IF((driverResults.Distance = r1.Distance AND driverResults.Time > 0), driverResults.time, null)),4) driverAvgByDistance "
+					+ "FROM Results r1 "
+					+ "				INNER JOIN Driver as driver ON driverId = driver.id "
+					+ "				INNER JOIN Horse as horse ON HorseId = horse.id "
+					+ "				INNER JOIN Track as track ON TrackId = track.id "
+					+ " INNER JOIN Results horseResults ON r1.RaceDate > horseResults.RaceDate AND r1.HorseId = horseResults.HorseId "
+					+ " INNER JOIN Results driverResults ON r1.RaceDate > driverResults.RaceDate AND r1.DriverId = driverResults.DriverId "
+					+ " WHERE r1.RaceDate = ? "
+					+ " AND r1.RaceType LIKE ? "
+					+ " GROUP BY r1.horseId "
+					+ "ORDER BY `r1`.`RaceNumber` ASC";
+		}
+
+
+		final List<HorseRaceData> results = new ArrayList<>();
+		try {
+			final PreparedStatement stat = getConnection().prepareStatement(sql);
+			if (Strings.isNullOrEmpty(type)) {
+				stat.setString(1, date);
+			} else {
+				stat.setString(1, date);
+				stat.setString(2, "%" + type + "%");
+			}
+
+			final ResultSet rs = stat.executeQuery();
+			while (rs.next()) {
+				final HorseRaceData rd = new HorseRaceData();
+				rd.setDistance(rs.getInt("Distance"));
+				rd.setDriverId(rs.getInt("DriverId"));
+				rd.setDriverName(rs.getString("DriverName"));
+				rd.setHorseName(rs.getString("HorseName"));
+				rd.setHorseId(rs.getInt("HorseId"));
+				rd.setTrainerId(rs.getInt("TrainerId"));
+				rd.setRaceNumber(rs.getInt("RaceNumber"));
+				rd.setTrackName(rs.getString("TrackName"));
+				rd.setRaceDate(rs.getString("RaceDate"));
+				rd.setLane(rs.getInt("Lane"));
+				rd.setResult(rs.getInt("Result"));
+				rd.setTime(rs.getFloat("Time"));
+				rd.setTimeModifier(rs.getString("TimeModifier"));
+				rd.setShoes(rs.getInt("Shoes"));
+				rd.setSulky(rs.getInt("Sulky"));
+
+				rd.setAvgHorseTime(rs.getFloat("horseAvg"), getHorseRaceCount(rd.getHorseId(), 0));
+				rd.setAvgDriverTime(rs.getFloat("driverAvg"), getDriverRaceCount(rd.getDriverId(), 0));
+				rd.setAvgHorseTimeByDistance(rs.getFloat("horseAvgByDistance"), getHorseRaceCount(rd.getHorseId(), rd.getDistance()));
+				rd.setAvgDriverTimeByDistance(rs.getFloat("driverAvgByDistance"), getDriverRaceCount(rd.getDriverId(), rd.getDistance()));
+
+				if (rd.getAvgDriverTimeCount() < 20) {
+					System.out.println("Uppdating driver " + rd.getDriverName() + "(" + rd.getDriverId() + ") travsport id: " +
+							getDriverTravsportId(rd.getDriverName()) + "because race count was " + rd.getAvgDriverTimeCount());
+					TravsportParser.getInstance().getDriverStatById(getDriverTravsportId(rd.getDriverName()), rd.getDriverName());
+				}
+
+				if (rd.getAvgHorseTimeCount() < 2) {
+					System.out.println("Uppdating horse " + rd.getHorseName() + "( " + rd.getHorseId() + ") travsport id: " +
+							getHorseTravsportId(rd.getHorseName()) + " because race count was " + rd.getAvgHorseTimeCount());
+					TravsportParser.getInstance().getHorseStatByIdJson(getHorseTravsportId(rd.getHorseName()), rd.getHorseName(), "");
+				}
+
+				results.add(rd);
+			}
+		} catch (final SQLException e) {
+			e.printStackTrace();
+		}
+
+		return results;
+	}
+
+	private int getHorseRaceCount(int horseId, int distance) {
+		int returnValue = 0;
+		final String sql;
+		if (distance > 0) {
+			sql = "SELECT Count(*) as c FROM Results WHERE HorseId = ? AND Distance = ? AND Time > 0";
+		} else {
+			sql = "SELECT Count(*) as c FROM Results WHERE HorseId = ? AND Time > 0";
+		}
+		try {
+			final PreparedStatement stat = getConnection().prepareStatement(sql);
+			if (distance > 0) {
+				stat.setInt(1, horseId);
+				stat.setInt(2, distance);
+			} else {
+				stat.setInt(1, horseId);
+			}
+			final ResultSet rs = stat.executeQuery();
+			while (rs.next()) {
+				returnValue = rs.getInt("c");
+			}
+		} catch (final SQLException e) {
+			e.printStackTrace();
+		}
+		return returnValue;
+	}
+
+	private int getDriverRaceCount(int driverId, int distance) {
+		int returnValue = 0;
+		final String sql;
+		if (distance > 0) {
+			sql = "SELECT Count(*) as c FROM Results WHERE DriverId = ? AND Distance = ? AND Time > 0";
+		} else {
+			sql = "SELECT Count(*) as c FROM Results WHERE DriverId = ? AND Time > 0";
+		}
+		try {
+			final PreparedStatement stat = getConnection().prepareStatement(sql);
+			if (distance > 0) {
+				stat.setInt(1, driverId);
+				stat.setInt(2, distance);
+			} else {
+				stat.setInt(1, driverId);
+			}
+			final ResultSet rs = stat.executeQuery();
+			while (rs.next()) {
+				returnValue = rs.getInt("c");
+			}
+		} catch (final SQLException e) {
+			e.printStackTrace();
+		}
+		return returnValue;
+	}
+
+	// Hämta statistik för häst, förare, kombinationen av dessa två, distans....
+	public SimpleEntry<Integer, Integer> getNumHorseGallop(int horseId) {
+		final String sql = "SELECT COUNT(*) as total, SUM(IF(TimeModifier LIKE '%g%', 1, 0)) as gallop FROM `Results` WHERE HorseId = ?";
+		SimpleEntry<Integer, Integer> returnValue = null;
+
+		try {
+			final PreparedStatement stat = getConnection().prepareStatement(sql);
+			stat.setInt(1, horseId);
+
+			final ResultSet rs = stat.executeQuery();
+			while (rs.next()) {
+				returnValue = new SimpleEntry<>(rs.getInt("total"), rs.getInt("gallop"));
+			}
+		} catch (final SQLException e) {
+			e.printStackTrace();
+		}
+
+		return returnValue;
+	}
+
+	// Hämta statistik för häst, förare, kombinationen av dessa två, distans....
+	public SimpleEntry<Integer, Integer> getNumDriverGallop(int driverId) {
+		final String sql = "SELECT COUNT(*) as total, SUM(IF(TimeModifier LIKE '%g%', 1, 0)) as gallop FROM `Results` WHERE DriverId = ?";
+		SimpleEntry<Integer, Integer> returnValue = null;
+
+		try {
+			final PreparedStatement stat = getConnection().prepareStatement(sql);
+			stat.setInt(1, driverId);
+
+			final ResultSet rs = stat.executeQuery();
+			while (rs.next()) {
+				returnValue = new SimpleEntry<>(rs.getInt("total"), rs.getInt("gallop"));
+			}
+		} catch (final SQLException e) {
+			e.printStackTrace();
+		}
+
+		return returnValue;
+	}
+
+	public int getDriverTravsportId(String driverFirstName, String driverLastName) {
+		int returnValue = -1;
+		final String sql = "SELECT travsportId FROM Driver WHERE name = ?";
+		try {
+			final PreparedStatement stat = getConnection().prepareStatement(sql);
+			stat.setString(1, driverLastName + " " + driverFirstName);
+
+			final ResultSet rs = stat.executeQuery();
+
+			while (rs.next()) {
+				returnValue = rs.getInt("travsportId");
+			}
+		} catch (final SQLException e) {
+			e.printStackTrace();
+		}
+		return returnValue;
+	}
+
+	public int getDriverTravsportId(String driverName) {
+		int returnValue = -1;
+		final String sql = "SELECT travsportId FROM Driver WHERE name = ?";
+		try {
+			final PreparedStatement stat = getConnection().prepareStatement(sql);
+			stat.setString(1, driverName);
+
+			final ResultSet rs = stat.executeQuery();
+
+			while (rs.next()) {
+				returnValue = rs.getInt("travsportId");
+			}
+		} catch (final SQLException e) {
+			e.printStackTrace();
+		}
+		return returnValue;
+	}
+
+	public void setDriverTravsportId(int driverId, String firstName, String lastName) {
+		final String sql = "UPDATE Driver SET travsportId = ? WHERE name = ?";
+		try {
+			final PreparedStatement stat = getConnection().prepareStatement(sql);
+			stat.setInt(1, driverId);
+			stat.setString(2, lastName + " " + firstName);
+
+			stat.executeUpdate();
+		} catch (final SQLException e) {
+			e.printStackTrace();
+		}
+	}
+
+	public float getTrendLast5(HorseRaceData horseRaceData) {
+		final String combinedTrendSql = "SELECT Time, (SELECT AVG(Time) FROM Results WHERE HorseId = ? AND DriverId = ? AND Distance = ?) as avgTime FROM Results WHERE HorseId = ? AND DriverId = ? AND Distance = ? AND Time > -1 ORDER BY RaceDate ASC limit 5";
+		final String DriverTrendSql = "SELECT Time, (SELECT AVG(Time) FROM Results WHERE DriverId = ? AND Distance = ?) as avgTime FROM Results WHERE DriverId = ? AND Distance = ? AND Time > -1 ORDER BY RaceDate ASC limit 5";
+		final String horseTrendSql = "SELECT Time, (SELECT AVG(Time) FROM Results WHERE HorseId = ? AND Distance = ?) as avgTime FROM Results WHERE HorseId = ? AND Distance = ? AND Time > -1  ORDER BY RaceDate ASC limit 5";
+
+		final int horseId = horseRaceData.getHorseId();
+		final int driverId = horseRaceData.getDriverId();
+		final int distance = horseRaceData.getDistance();
+
+		float avgHorseTime = -1f;
+		float avgDriverTime = -1f;
+		float avgCombinedTime = -1f;
+		float startHorseAvgTime = -1;
+		float startDriverAvgTime = -1;
+		float startCombinedAvgTime = -1;
+		try {
+			final PreparedStatement horseStat = getConnection().prepareStatement(horseTrendSql, ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
+			horseStat.setInt(1, horseId);
+			horseStat.setInt(2, distance);
+			horseStat.setInt(3, horseId);
+			horseStat.setInt(4, distance);
+
+			final ResultSet horseRs = horseStat.executeQuery();
+			if (horseRs.next()) {
+				startHorseAvgTime = horseRs.getFloat("avgTime");;
+				horseRs.beforeFirst();
+				avgHorseTime = getTrendAvgTime(horseRs);
+				// This should be the return value for horse
+				System.out.println("Horse trend is " + (avgHorseTime - startHorseAvgTime));
+			} else {
+				System.out.println("No Horse trend found");
+			}
+
+			horseRs.close();
+			horseStat.close();
+
+			final PreparedStatement driverStat = conn.prepareStatement(DriverTrendSql, ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
+			driverStat.setInt(1, driverId);
+			driverStat.setInt(2, distance);
+			driverStat.setInt(3, driverId);
+			driverStat.setInt(4, distance);
+			final ResultSet driverRs = driverStat.executeQuery();
+
+			if (driverRs.next()) {
+				startDriverAvgTime = driverRs.getFloat("avgTime");;
+				driverRs.beforeFirst();
+				avgDriverTime = getTrendAvgTime(driverRs);
+				System.out.println("Driver trend is " + (avgDriverTime - startDriverAvgTime));
+			} else {
+				System.out.println("No driver trend found");
+			}
+
+			final PreparedStatement combinedStat = conn.prepareStatement(combinedTrendSql, ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
+
+			combinedStat.setInt(1, horseId);
+			combinedStat.setInt(2, driverId);
+			combinedStat.setInt(3, distance);
+			combinedStat.setInt(4, horseId);
+			combinedStat.setInt(5, driverId);
+			combinedStat.setInt(6, distance);
+			final ResultSet combinedRs = combinedStat.executeQuery();
+
+
+			if (combinedRs.next()) {
+				startCombinedAvgTime = combinedRs.getFloat("avgTime");;
+				combinedRs.beforeFirst();
+				avgCombinedTime = getTrendAvgTime(combinedRs);
+				System.out.println("Combined trend is " + (avgCombinedTime - startCombinedAvgTime));
+			} else {
+				System.out.println("No combined trend available");
+			}
+
+			if (avgHorseTime > -1 && avgDriverTime > -1) {
+				System.out.println("Combination of horse and driver: " + ((avgHorseTime - startHorseAvgTime) + (avgDriverTime - startDriverAvgTime)));
+			}
+			System.out.println("------");
+
+		} catch (final SQLException e) {
+			e.printStackTrace();
+		}
+
+		return ((avgHorseTime - startHorseAvgTime) + (avgDriverTime - startDriverAvgTime)) / 2f;
+	}
+
+	private float getTrendAvgTime(final ResultSet rs) throws SQLException {
+		float returnValue = 0.0f;
+		while (rs.next()) {
+			if (returnValue == 0.0f) {
+				returnValue = rs.getFloat("avgTime");
+			}
+
+			if (returnValue == 0f) {
+				returnValue = rs.getFloat("Time");
+			}
+			final float diff = -(returnValue - rs.getFloat("Time"));
+			returnValue = returnValue + diff;
+		}
+		return returnValue;
+	}
+
+	public void updateResult(String horseName, String driverName, int result, String timeModifier, float timeValue,
+			String date, String type) {
+		final String sql = "UPDATE Results "
+				+ "SET "
+				+ "Result = ?, Time = ?, TimeModifier = ?"
+				+ "WHERE "
+				+ "HorseId = (SELECT id FROM Horse WHERE name = ?) AND "
+				+ "DriverId = (SELECT id FROM Driver WHERE name = ?) AND"
+				+ "RaceDate = ?";
+		try {
+			final PreparedStatement stat = getConnection().prepareStatement(sql);
+			stat.setInt(1, result);
+			stat.setFloat(2, timeValue);
+			stat.setString(3, timeModifier);
+			stat.setString(4, horseName);
+			stat.setString(5, driverName);
+			stat.setString(6, date);
+
+			final int res = stat.executeUpdate();
+
+			if (res != 1) {
+				System.out.println("Updated " + res + " results for horse " + horseName + " and driver " + driverName + " at date " + date);
+			}
+
+		} catch (final SQLException e) {
+			e.printStackTrace();
+		}
+
+	}
+
+	public String getTrackShortCodeByName(String trackName) {
+		final String sql = "SELECT shortcode FROM Track WHERE name = ?";
+		String trackcode = null;
+		try {
+			final PreparedStatement stat = getConnection().prepareStatement(sql);
+			stat.setString(1, trackName);
+
+			final ResultSet rs = stat.executeQuery();
+			while (rs.next()) {
+				trackcode = rs.getString("shortcode");
+			}
+		} catch (final SQLException e) {
+			e.printStackTrace();
+		}
+		return trackcode;
+	}
+
+	public int getRaceCount(String what, int id) {
+		int returnValue = 0;
+		final String sql;
+		if (what.equals("Horse")) {
+			sql = "SELECT Count(*) as count FROM Results WHERE HorseId = ?";
+		} else if (what.equals("Driver")) {
+			sql = "SELECT Count(*) as count FROM Results WHERE DriverId = ?";
+		} else {
+			sql = "";
+		}
+
+		if (!Strings.isNullOrEmpty(sql)) {
+			try {
+				final PreparedStatement stat = getConnection().prepareStatement(sql);
+				stat.setInt(1, id);
+
+				final ResultSet rs = stat.executeQuery();
+				while (rs.next()) {
+					returnValue = rs.getInt("count");
+				}
+
+			} catch (final SQLException e) {
+				e.printStackTrace();
+			}
+		}
+		return returnValue;
+	}
+
+	public int getRaceId() {
+		final String sql = "SELECT raceId FROM Results where raceId > -1 AND RaceDate < DATE(NOW()) ORDER BY RaceDate ASC LIMIT 1";
+		int returnValue = -1;
+		try {
+			final PreparedStatement stat = getConnection().prepareStatement(sql);
+			final ResultSet rs = stat.executeQuery();
+			while (rs.next()) {
+				returnValue = rs.getInt("raceId");
+			}
+		} catch (final SQLException e) {
+			e.printStackTrace();
+		}
+
+		return returnValue;
+
+	}
+
+	public List<UpdateRaceRow> getRacesToUpdate(String date) {
+		final List<UpdateRaceRow> racesToUpdate = Lists.newArrayList();
+		final String sql = "SELECT r.*, t.name as TrackName FROM Results r INNER JOIN Track t ON r.trackId = t.id WHERE r.RaceDate = ? GROUP BY r.RaceDate, t.name, r.RaceNumber";
+
+		try {
+			final PreparedStatement stat = getConnection().prepareStatement(sql);
+			stat.setString(1, date);
+
+			final ResultSet rs = stat.executeQuery();
+
+			while (rs.next()) {
+				racesToUpdate.add(new UpdateRaceRow(rs.getInt("RaceId"), rs.getString("RaceDate"), rs.getString("RaceType"),
+						rs.getString("TimeModifier"), rs.getInt("RaceNumber"), rs.getString("TrackName")));
+			}
+		} catch (final SQLException e) {
+			e.printStackTrace();
+		}
+
+		return racesToUpdate;
+
+	}
+
+	public void updateRaceInfo(int raceId, String timeModifier, String raceType, int raceNumber) {
+		final String sql = "UPDATE Results SET RaceType = ?, TimeModifier = ? WHERE RaceId = ? AND RaceNumber = ?";
+
+		try {
+			final PreparedStatement stat = getConnection().prepareStatement(sql);
+			stat.setString(1, raceType);
+			stat.setString(2, timeModifier);
+			stat.setInt(3, raceId);
+			stat.setInt(4, raceNumber);
+
+			stat.executeUpdate();
+		} catch (final SQLException e) {
+			e.printStackTrace();
+		}
+
+	}
+
+	public void updateFromRaceResults(int raceId, int result, float time, String timeModifier, int lane, int distance,
+			int horseTravsportId, int driverTravsportId, String raceDate, int raceNumber) {
+		final String sql = "UPDATE Results SET Result = ?, Time = ?, TimeModifier = ?, raceId = -1 WHERE RaceDate = ? AND RaceId = ? AND RaceNumber = ? AND Lane = ?";
+
+		try {
+			final PreparedStatement stat = getConnection().prepareStatement(sql);
+			stat.setInt(1, result);
+			stat.setFloat(2, time);
+			stat.setString(3, timeModifier);
+			stat.setString(4, raceDate);
+			stat.setInt(5, raceId);
+			stat.setInt(6, raceNumber);
+			stat.setInt(7, lane);
+
+			stat.executeUpdate();
+
+		} catch (final SQLException e) {
+			e.printStackTrace();
+		}
+
+
+	}
+
+	public void removeRaceIds(int raceId) {
+		final String sql = "UPDATE Results SET raceId = -1 WHERE RaceId = ?";
+		try {
+			final PreparedStatement statement = getConnection().prepareStatement(sql);
+			statement.setInt(1, raceId);
+
+			statement.executeUpdate();
+		} catch (final SQLException e) {
+			e.printStackTrace();
+		}
+
+	}
+
+}

+ 40 - 0
ATG/src/controllers/LDTabController.java

@@ -0,0 +1,40 @@
+package controllers;
+
+import java.net.URL;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Map;
+import java.util.ResourceBundle;
+
+import javafx.collections.ObservableList;
+import javafx.fxml.FXML;
+import javafx.scene.control.TableView;
+import javafx.scene.layout.Pane;
+
+public class LDTabController extends AtgController {
+
+	@FXML Pane LDButtonPane;
+	@FXML Pane LDResultPane;
+	@FXML private TableView<Map<String, Object>> racesTable;
+
+	final SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
+	ObservableList<Map<String, Object>> tableRaces;
+
+	@Override
+	public void initialize(URL arg0, ResourceBundle arg1) {
+		super.initialize(arg0, arg1);
+
+		todaysRaces = DatabaseContoller.getInstance().getTodaysRaces("ld", simpleDateFormat.format(new Date()));
+		updateRacesTable();
+	}
+
+	@FXML
+	public void GetTodaysRacesAction() {
+		//		if (racesTable.getItems().size() <= 0) { // Hämta statistik från ATG för hästnamn och från travsport för data
+		//		AtgParser.getInstance().getTodaysStartsFromATG("ld");
+		//		}
+		todaysRaces = DatabaseContoller.getInstance().getTodaysRaces("LD", simpleDateFormat.format(new Date()));
+		updateRacesTable();
+	}
+
+}

+ 112 - 0
ATG/src/controllers/MainController.java

@@ -0,0 +1,112 @@
+package controllers;
+
+import java.net.URL;
+import java.text.SimpleDateFormat;
+import java.util.ResourceBundle;
+
+import javafx.beans.value.ChangeListener;
+import javafx.beans.value.ObservableValue;
+import javafx.fxml.FXML;
+import javafx.fxml.Initializable;
+import javafx.scene.control.Button;
+import javafx.scene.control.DatePicker;
+import javafx.scene.control.TextField;
+import parsers.AtgParser;
+import parsers.TravsportParser;
+
+public class MainController implements Initializable {
+
+	@FXML private Button getHorseStatsButton;
+	@FXML private Button getDriverStatsButton;
+	@FXML private Button racesByDate;
+	@FXML private TextField horseNameTextField;
+	@FXML private TextField driverFirstNameTextField;
+	@FXML private TextField driverLastNameTextField;
+	@FXML private Button testButton;
+	@FXML private Button updateResultsButton;
+	@FXML private V75TabController V75TabController;
+	@FXML private LDTabController LDTabController;
+	@FXML public DatePicker datePicker;
+
+	public static SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd");
+	private int mRaceId;
+
+	@Override
+	public void initialize(URL arg0, ResourceBundle arg1) {
+		updateRaceId();
+
+		horseNameTextField.textProperty().addListener(new ChangeListener<String>() {
+			@Override
+			public void changed(ObservableValue<? extends String> ov, String oldValue, String newValue) {
+				if (newValue.length() > 1) {
+					getHorseStatsButton.setDisable(false);
+				} else {
+					getHorseStatsButton.setDisable(true);
+				}
+			}
+		});
+
+		driverFirstNameTextField.textProperty().addListener(new ChangeListener<String>() {
+			@Override
+			public void changed(ObservableValue<? extends String> ov, String oldValue, String newValue) {
+				if (newValue.length() > 1) {
+					getDriverStatsButton.setDisable(false);
+				} else {
+					getDriverStatsButton.setDisable(true);
+				}
+			}
+
+		});
+		driverLastNameTextField.textProperty().addListener(new ChangeListener<String>() {
+			@Override
+			public void changed(ObservableValue<? extends String> ov, String oldValue, String newValue) {
+				if (newValue.length() > 1) {
+					getDriverStatsButton.setDisable(false);
+				} else {
+					getDriverStatsButton.setDisable(true);
+				}
+			}
+
+		});
+
+	}
+
+	private void updateRaceId() {
+		mRaceId = DatabaseContoller.getInstance().getRaceId();
+		if (mRaceId != -1) {
+			updateResultsButton.setDisable(false);
+		} else {
+			updateResultsButton.setDisable(true);
+		}
+	}
+
+	@FXML
+	public void GetHorseStatsAction() { // Test class
+		final int horseId = TravsportParser.getInstance().getHorseIdByName(horseNameTextField.getText());
+		System.out.println("Getting stats for horse " + horseNameTextField.getText() + " with id: " + horseId);
+		TravsportParser.getInstance().getHorseStatByIdJson(horseId, horseNameTextField.getText(), "");
+	}
+
+	@FXML
+	public void GetDriverStatsAction() { // Test class
+		final String firstName = driverFirstNameTextField.getText().trim();
+		final String lastName = driverLastNameTextField.getText().trim();
+		int driverId = DatabaseContoller.getInstance().getDriverTravsportId(firstName, lastName);
+		if (driverId <= 0) {
+			driverId = TravsportParser.getInstance().getDriverIdByName(firstName, lastName);
+		}
+		TravsportParser.getInstance().getDriverStatById(driverId, lastName + " " + firstName);
+	}
+
+	@FXML
+	public void TestAction() {
+		AtgParser.getInstance().getTodaysStartsFromATG("ld");
+	}
+
+	@FXML public void UpdateResults() {
+		TravsportParser.getInstance().updateRaceResults(mRaceId);
+		DatabaseContoller.getInstance().removeRaceIds(mRaceId);
+		updateRaceId();
+	}
+
+}

+ 165 - 0
ATG/src/controllers/TestTabController.java

@@ -0,0 +1,165 @@
+package controllers;
+
+import java.net.URL;
+import java.time.LocalDate;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.ResourceBundle;
+import java.util.stream.Collectors;
+
+import com.google.common.collect.Lists;
+
+import javafx.collections.FXCollections;
+import javafx.collections.ObservableList;
+import javafx.fxml.FXML;
+import javafx.scene.control.Button;
+import javafx.scene.control.ChoiceBox;
+import javafx.scene.control.DatePicker;
+import javafx.scene.control.TableColumn;
+import javafx.scene.control.TableView;
+import javafx.scene.control.cell.MapValueFactory;
+import javafx.scene.layout.Pane;
+import objects.HorseRaceData;
+import parsers.AtgParser;
+import parsers.TravsportParser;
+
+@SuppressWarnings("rawtypes")
+public class TestTabController extends AtgController {
+
+	@FXML Pane DDButtonPane;
+	@FXML Pane DDResultPane;
+	@FXML DatePicker datePicker;
+	@FXML Button getRacesButton;
+	@FXML Button getRacesForDateButton;
+	@FXML Button getRaceCalendarButton;
+	@FXML ChoiceBox<String> raceTypeSelector;
+
+	@FXML TableColumn<Map, Float> diffCalculationColumn = new TableColumn<>("Diff Calculated");
+	@FXML TableColumn<Map, Float> diffPrevCalculationColumn = new TableColumn<>("Prev Diff Calculated");
+
+	@FXML TableColumn<Map, Float> trendLast5DistColumn = new TableColumn<>("Trend last 5");
+	@FXML TableColumn<Map, Float> trendLast5Column = new TableColumn<>("Trend last 5 Distance");
+
+
+
+	@FXML private TableView<Map<String, Object>> racesTable;
+
+	ObservableList<Map<String, Object>> tableRaces;
+	ObservableList<String> raceTypes;
+
+	@Override
+	public void initialize(URL arg0, ResourceBundle arg1) {
+		super.initialize(arg0, arg1);
+
+		diffCalculationColumn.setCellValueFactory(new MapValueFactory<>("diffCalculated"));
+		diffPrevCalculationColumn.setCellValueFactory(new MapValueFactory<>("diffPrevCalculatedValue"));
+		trendLast5Column.setCellValueFactory(new MapValueFactory<>("trendLast5"));
+		trendLast5DistColumn.setCellValueFactory(new MapValueFactory<>("trendLast5Distance"));
+
+		raceTypes = FXCollections.observableArrayList();
+		raceTypes.addAll(Lists.newArrayList("V75","dd","ld"));
+		raceTypeSelector.setItems(raceTypes);
+
+		datePicker.valueProperty().addListener((observable, oldValue, newValue) -> {
+			if (newValue == null) {
+				getRacesButton.setDisable(true);
+				getRacesForDateButton.setDisable(true);
+				getRaceCalendarButton.setDisable(true);
+			} else if (newValue.isBefore(LocalDate.now())) {
+				getRacesButton.setDisable(false);
+				getRacesForDateButton.setDisable(false);
+				getRaceCalendarButton.setDisable(false);
+			} else if (newValue.isEqual(LocalDate.now())){
+				getRacesButton.setDisable(false);
+				getRacesForDateButton.setDisable(false);
+				getRaceCalendarButton.setDisable(false);
+			} else {
+				//				getRacesButton.setDisable(true);
+				//				getRacesForDateButton.setDisable(true);
+			}
+		});
+	}
+
+	@Override
+	public void updateRacesTable() {
+		super.updateRacesTable();
+
+		final List<Integer> raceNumbers = todaysRaces.stream().map(m -> m.getRaceNumber()).distinct().collect(Collectors.toList());
+
+		for (int i = 0; i < raceNumbers.size(); i++) {
+			final int num = i;
+
+			final ArrayList<String> distances = Lists.newArrayList();
+
+			final List<HorseRaceData> races = todaysRaces.stream().filter(tr -> tr.getRaceNumber() == raceNumbers.get(num)).collect(Collectors.toList());
+			final List<HorseRaceData> prevRaces = races.stream().collect(Collectors.toList());
+
+			races.stream()
+			.filter(hr -> hr.getRaceNumber() == prevRaces.get(0).getRaceNumber())
+			.forEach(hr -> {
+				if (!distances.contains(String.valueOf(hr.getDistance()))) {
+					distances.add(String.valueOf(hr.getDistance()));
+				}
+			});
+
+			prevRaces.sort((hr1, hr2) -> Float.compare(prevCalculation(hr1, getLaneWinPercent(hr1, distances)), prevCalculation(hr2, getLaneWinPercent(hr2, distances))));
+			races.sort((hr1, hr2) -> Float.compare(calculatedValue(hr1, prevCalculation(hr1, getLaneWinPercent(hr1, distances))),
+					calculatedValue(hr2, prevCalculation(hr2, getLaneWinPercent(hr2,distances)))));
+
+			for (int j = 0; j < prevRaces.size() - 1; j++) {
+				final HorseRaceData horseRaceData = prevRaces.get(j);
+				todaysRaces.stream()
+				.filter(ht -> ht.equals(horseRaceData))
+				.findFirst().get()
+				.setTimeToHorseBehindPrevCalculation(prevCalculation(prevRaces.get(j+1),getLaneWinPercent(prevRaces.get(j+1), distances)) -
+						prevCalculation(horseRaceData,getLaneWinPercent(horseRaceData, distances)));
+			}
+
+			todaysRaces.stream().filter(ht -> ht.equals(prevRaces.get(prevRaces.size() -1))).findFirst().get().setTimeToHorseBehindPrevCalculation(999);
+
+			for (int j = 0; j < races.size() - 1; j++) {
+				final HorseRaceData horseRaceData = races.get(j);
+				todaysRaces.stream()
+				.filter(ht -> ht.equals(horseRaceData))
+				.findFirst().get()
+				.setTimeToHorseBehindCalculation(calculatedValue(races.get(j+1),prevCalculation(races.get(j+1), getLaneWinPercent(races.get(j+1), distances))) -
+						calculatedValue(horseRaceData,prevCalculation(horseRaceData, getLaneWinPercent(horseRaceData, distances))));
+			}
+
+			todaysRaces.stream().filter(ht -> ht.equals(races.get(races.size() -1))).findFirst().get().setTimeToHorseBehindCalculation(999);
+
+		}
+
+		for (int i = 0; i < todaysRaces.size(); i++) {
+			final Map<String, Object> currentRow = racesTable.getItems().get(i);
+			final HorseRaceData horseRaceData = todaysRaces.get(i);
+			currentRow.put("diffPrevCalculatedValue", horseRaceData.getTimeToHorseBehindPrevCalculation());
+			currentRow.put("diffCalculated", horseRaceData.getTimeToHorseBehindCalculation());
+			//			System.out.println("Horse: " + horseRaceData.getHorseName() + " Driver: " + horseRaceData.getDriverName());
+			//			currentRow.put("trendLast5", ((horseRaceData.getAvgHorseTime() + horseRaceData.getAvgDriverTime()) / 2f) +
+			//					DatabaseContoller.getInstance().getTrendLast5(horseRaceData));
+		}
+
+	}
+
+	@FXML
+	public void GetRacesAction() {
+		todaysRaces = DatabaseContoller.getInstance().getTodaysRaces("", datePicker.getValue().toString());
+		updateRacesTable();
+	}
+
+	@FXML
+	public void GetRacesForDate() {
+		System.out.println("Getting races for " + raceTypeSelector.getSelectionModel().getSelectedItem() + " at date " + datePicker.getValue().toString());
+		resetLaneWinPercentages();
+		AtgParser.getInstance().getStartsForDay(datePicker.getValue().toString(), raceTypeSelector.getSelectionModel().getSelectedItem());
+		GetRacesAction();
+	}
+
+	public void GetRaceCalendar() {
+		TravsportParser.getInstance().getRaceCalendar();
+		//		AtgParser.getInstance().getResultsForDate(raceTypeSelector.getSelectionModel().getSelectedItem(), datePicker.getValue().toString());
+		//		GetRacesAction();
+	}
+}

+ 40 - 0
ATG/src/controllers/UpdateTabController.java

@@ -0,0 +1,40 @@
+package controllers;
+
+import java.net.URL;
+import java.util.List;
+import java.util.ResourceBundle;
+
+import javafx.fxml.FXML;
+import javafx.fxml.Initializable;
+import javafx.scene.control.Button;
+import javafx.scene.control.DatePicker;
+import javafx.scene.control.ScrollPane;
+import javafx.scene.layout.FlowPane;
+import objects.UpdateRaceRow;
+
+public class UpdateTabController implements Initializable {
+
+	@FXML FlowPane flowPaneRowContainer;
+	@FXML Button getRacesButton;
+	@FXML DatePicker datePicker;
+	@FXML ScrollPane RacesTable;
+
+	@Override
+	public void initialize(URL arg0, ResourceBundle arg1) {
+	}
+
+
+	@FXML void getRacesAction() {
+		final List<UpdateRaceRow> racesToUpdate = DatabaseContoller.getInstance().getRacesToUpdate(datePicker.getValue().toString());
+
+		flowPaneRowContainer.getChildren().clear();
+		if (flowPaneRowContainer.getChildren().size() < 1) {
+			final UpdateRaceRow header = new UpdateRaceRow(0, "RaceDate", "RaceType", "StartType", 0, "Track Name");
+			flowPaneRowContainer.getChildren().add(header.getGui(true));
+		}
+		for (final UpdateRaceRow updateRaceRow : racesToUpdate) {
+			flowPaneRowContainer.getChildren().add(updateRaceRow.getGui(false));
+		}
+
+	}
+}

+ 38 - 0
ATG/src/controllers/V4TabController.java

@@ -0,0 +1,38 @@
+package controllers;
+
+import java.net.URL;
+import java.util.Date;
+import java.util.Map;
+import java.util.ResourceBundle;
+
+import javafx.collections.ObservableList;
+import javafx.fxml.FXML;
+import javafx.scene.control.TableView;
+import javafx.scene.layout.Pane;
+import parsers.AtgParser;
+
+public class V4TabController extends AtgController {
+
+	@FXML Pane V4ButtonPane;
+	@FXML Pane V4ResultPane;
+	@FXML private TableView<Map<String, Object>> racesTable;
+
+	ObservableList<Map<String, Object>> tableRaces;
+
+	@Override
+	public void initialize(URL arg0, ResourceBundle arg1) {
+		super.initialize(arg0, arg1);
+		todaysRaces = DatabaseContoller.getInstance().getTodaysRaces("V4", MainController.DATE_FORMAT.format(new Date()));
+		updateRacesTable();
+	}
+
+	@FXML
+	public void GetTodaysRacesAction() {
+		//		if (racesTable.getItems().size() <= 0) { // Hämta statistik från ATG för hästnamn och från travsport för data
+		AtgParser.getInstance().getTodaysStartsFromATG("V4");
+		//		}
+		todaysRaces = DatabaseContoller.getInstance().getTodaysRaces("V4", MainController.DATE_FORMAT.format(new Date()));
+		updateRacesTable();
+	}
+
+}

+ 30 - 0
ATG/src/controllers/V5TabController.java

@@ -0,0 +1,30 @@
+package controllers;
+
+import java.net.URL;
+import java.util.Date;
+import java.util.ResourceBundle;
+
+import javafx.fxml.FXML;
+import javafx.scene.layout.Pane;
+import parsers.AtgParser;
+
+public class V5TabController extends AtgController {
+
+	@FXML Pane V5ButtonPane;
+	@FXML Pane V5ResultPane;
+	@Override
+	public void initialize(URL arg0, ResourceBundle arg1) {
+		super.initialize(arg0, arg1);
+		todaysRaces = DatabaseContoller.getInstance().getTodaysRaces("V5", MainController.DATE_FORMAT.format(new Date()));
+		updateRacesTable();
+	}
+
+	@FXML
+	public void GetTodaysRacesAction() {
+		if (racesTable.getItems().size() <= 0) { // Hämta statistik från ATG för hästnamn och från travsport för data
+			AtgParser.getInstance().getTodaysStartsFromATG("V5");
+		}
+		todaysRaces = DatabaseContoller.getInstance().getTodaysRaces("V5", MainController.DATE_FORMAT.format(new Date()));
+		updateRacesTable();
+	}
+}

+ 36 - 0
ATG/src/controllers/V64TabController.java

@@ -0,0 +1,36 @@
+package controllers;
+
+import java.net.URL;
+import java.util.Date;
+import java.util.Map;
+import java.util.ResourceBundle;
+
+import javafx.collections.ObservableList;
+import javafx.fxml.FXML;
+import javafx.scene.control.TableView;
+import javafx.scene.layout.Pane;
+import parsers.AtgParser;
+
+public class V64TabController extends AtgController {
+
+	@FXML Pane V64ButtonPane;
+	@FXML Pane V64ResultPane;
+	@FXML private TableView<Map<String, Object>> racesTable;
+	ObservableList<Map<String, Object>> tableRaces;
+
+	@Override
+	public void initialize(URL arg0, ResourceBundle arg1) {
+		super.initialize(arg0, arg1);
+		todaysRaces = DatabaseContoller.getInstance().getTodaysRaces("V64", MainController.DATE_FORMAT.format(new Date()));
+		updateRacesTable();
+	}
+
+	@FXML
+	public void GetTodaysRacesAction() {
+		if (racesTable.getItems().size() <= 0) { // Hämta statistik från ATG för hästnamn och från travsport för data
+			AtgParser.getInstance().getTodaysStartsFromATG("V64");
+		}
+		todaysRaces = DatabaseContoller.getInstance().getTodaysRaces("V64", MainController.DATE_FORMAT.format(new Date()));
+		updateRacesTable();
+	}
+}

+ 38 - 0
ATG/src/controllers/V65TabController.java

@@ -0,0 +1,38 @@
+package controllers;
+
+import java.net.URL;
+import java.util.Date;
+import java.util.Map;
+import java.util.ResourceBundle;
+
+import javafx.collections.ObservableList;
+import javafx.fxml.FXML;
+import javafx.scene.control.TableView;
+import javafx.scene.layout.Pane;
+import parsers.AtgParser;
+
+public class V65TabController extends AtgController {
+
+	@FXML Pane V65ButtonPane;
+	@FXML Pane V65ResultPane;
+	@FXML private TableView<Map<String, Object>> racesTable;
+
+	ObservableList<Map<String, Object>> tableRaces;
+
+	@Override
+	public void initialize(URL arg0, ResourceBundle arg1) {
+		super.initialize(arg0, arg1);
+		todaysRaces = DatabaseContoller.getInstance().getTodaysRaces("V65", MainController.DATE_FORMAT.format(new Date()));
+		updateRacesTable();
+	}
+
+	@FXML
+	public void GetTodaysRacesAction() {
+		if (racesTable.getItems().size() <= 0) { // Hämta statistik från ATG för hästnamn och från travsport för data
+			AtgParser.getInstance().getTodaysStartsFromATG("V65");
+		}
+		todaysRaces = DatabaseContoller.getInstance().getTodaysRaces("V65", MainController.DATE_FORMAT.format(new Date()));
+		updateRacesTable();
+	}
+
+}

+ 36 - 0
ATG/src/controllers/V75TabController.java

@@ -0,0 +1,36 @@
+package controllers;
+
+import java.net.URL;
+import java.util.Date;
+import java.util.Map;
+import java.util.ResourceBundle;
+
+import javafx.collections.ObservableList;
+import javafx.fxml.FXML;
+import javafx.scene.control.TableView;
+import javafx.scene.layout.Pane;
+
+public class V75TabController extends AtgController {
+
+	@FXML Pane V75ButtonPane;
+	@FXML Pane V75ResultPane;
+	@FXML private TableView<Map<String, Object>> racesTable;
+
+	ObservableList<Map<String, Object>> tableRaces;
+
+	@Override
+	public void initialize(URL arg0, ResourceBundle arg1) {
+		super.initialize(arg0, arg1);
+		todaysRaces = DatabaseContoller.getInstance().getTodaysRaces("V75", MainController.DATE_FORMAT.format(new Date()));
+		updateRacesTable();
+	}
+
+	@FXML
+	public void GetTodaysRacesAction() {
+		//		if (racesTable.getItems().size() <= 0) { // Hämta statistik från ATG för hästnamn och från travsport för data
+		//		AtgParser.getInstance().getTodaysStartsFromATG("V75");
+		//		}
+		todaysRaces = DatabaseContoller.getInstance().getTodaysRaces("V75", MainController.DATE_FORMAT.format(new Date()));
+		updateRacesTable();
+	}
+}

+ 38 - 0
ATG/src/controllers/V86TabController.java

@@ -0,0 +1,38 @@
+package controllers;
+
+import java.net.URL;
+import java.util.Date;
+import java.util.Map;
+import java.util.ResourceBundle;
+
+import javafx.collections.ObservableList;
+import javafx.fxml.FXML;
+import javafx.scene.control.TableView;
+import javafx.scene.layout.Pane;
+import parsers.AtgParser;
+
+public class V86TabController extends AtgController {
+
+	@FXML Pane V86ButtonPane;
+	@FXML Pane V86ResultPane;
+	@FXML private TableView<Map<String, Object>> racesTable;
+
+	ObservableList<Map<String, Object>> tableRaces;
+
+	@Override
+	public void initialize(URL arg0, ResourceBundle arg1) {
+		super.initialize(arg0, arg1);
+		todaysRaces = DatabaseContoller.getInstance().getTodaysRaces("V86", MainController.DATE_FORMAT.format(new Date()));
+		updateRacesTable();
+	}
+
+	@FXML
+	public void GetTodaysRacesAction() {
+		if (racesTable.getItems().size() <= 0) { // Hämta statistik från ATG för hästnamn och från travsport för data
+			AtgParser.getInstance().getTodaysStartsFromATG("V86");
+		}
+		todaysRaces = DatabaseContoller.getInstance().getTodaysRaces("V86", MainController.DATE_FORMAT.format(new Date()));
+		updateRacesTable();
+	}
+
+}

+ 92 - 0
ATG/src/fxml/AtgMain.fxml

@@ -0,0 +1,92 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<?import javafx.scene.control.Button?>
+<?import javafx.scene.control.Tab?>
+<?import javafx.scene.control.TabPane?>
+<?import javafx.scene.control.TextField?>
+<?import javafx.scene.layout.AnchorPane?>
+
+<AnchorPane prefHeight="800.0" prefWidth="1600.0" xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="controllers.MainController">
+   <children>
+      <TabPane prefHeight="800.0" prefWidth="1600.0" tabClosingPolicy="UNAVAILABLE" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
+         <tabs>
+            <Tab text="Test Tab">
+              <content>
+                <AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="180.0" prefWidth="200.0">
+                     <children>
+                        <TextField fx:id="horseNameTextField" layoutX="425.0" layoutY="81.0" promptText="Horse name" />
+                        <TextField fx:id="driverFirstNameTextField" layoutX="577.0" layoutY="81.0" promptText="Driver FIRST name" />
+                        <TextField fx:id="driverLastNameTextField" layoutX="726.0" layoutY="81.0" promptText="Driver LAST name" />
+                        <Button fx:id="getHorseStatsButton" disable="true" layoutY="157.0" mnemonicParsing="false" onAction="#GetHorseStatsAction" prefHeight="200.0" prefWidth="1200.0" text="Get Horse stats" />
+                        <Button fx:id="getDriverStatsButton" disable="true" layoutY="357.0" mnemonicParsing="false" onAction="#GetDriverStatsAction" prefHeight="200.0" prefWidth="1200.0" text="Get Driver stats" />
+                        <Button fx:id="updateResultsButton" disable="true" layoutX="909.0" layoutY="81.0" mnemonicParsing="false" onAction="#UpdateResults" text="Rätta Resultat" />
+                     </children>
+                  </AnchorPane>
+              </content>
+            </Tab>
+            <Tab text="Atg">
+              <content>
+                <AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="180.0" prefWidth="200.0">
+                     <children>
+                        <TabPane prefHeight="571.0" prefWidth="1200.0" tabClosingPolicy="UNAVAILABLE" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
+                           <tabs>
+                              <Tab text="Dagens Dubbel">
+                                 <content>
+                                    <fx:include fx:id="DDTab" source="DD.fxml" />
+                                 </content>
+                              </Tab>
+                              <Tab text="Lunch Dubbel">
+                                 <content>
+                                    <fx:include fx:id="LDTab" source="LD.fxml" />
+                                 </content>
+                              </Tab>
+                              <Tab text="V75">
+                                <content>
+                                  <fx:include fx:id="V75Tab" source="V75.fxml" />
+                                </content>
+                              </Tab>
+<!--                               <Tab text="V86"> -->
+<!--                                  <content> -->
+<!--                                  	<fx:include fx:id="V86Tab" source="V86.fxml" /> -->
+<!--                                  </content> -->
+<!--                               </Tab> -->
+<!--                               <Tab text="V64"> -->
+<!--                                  <content> -->
+<!--                                     <fx:include fx:id="V64Tab" source="V64.fxml" /> -->
+<!--                                  </content> -->
+<!--                               </Tab> -->
+<!--                               <Tab text="V65"> -->
+<!--                                  <content> -->
+<!--                                     <fx:include fx:id="V65Tab" source="V65.fxml" /> -->
+<!--                                  </content> -->
+<!--                               </Tab> -->
+<!--                               <Tab text="V5"> -->
+<!--                                  <content> -->
+<!--                                     <fx:include fx:id="V5Tab" source="V5.fxml" /> -->
+<!--                                  </content> -->
+<!--                               </Tab> -->
+<!--                               <Tab text="V4"> -->
+<!--                                  <content> -->
+<!--                                     <fx:include fx:id="V4Tab" source="V4.fxml" /> -->
+<!--                                  </content> -->
+<!--                               </Tab> -->
+                              <Tab text="TestTab">
+                                 <content>
+                                    <fx:include fx:id="TestTab" source="TestTab.fxml" />
+                                 </content>
+                              </Tab>
+                           </tabs>
+                        </TabPane>
+                     </children>
+                  </AnchorPane>
+              </content>
+            </Tab>
+            <Tab text="Update races">
+              <content>
+        		<fx:include fx:id="UpdateTab" source="UpdateTab.fxml"/>
+              </content>
+            </Tab>
+         </tabs>
+      </TabPane>
+   </children>
+</AnchorPane>

+ 43 - 0
ATG/src/fxml/DD.fxml

@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<?import javafx.scene.control.Button?>
+<?import javafx.scene.control.TableColumn?>
+<?import javafx.scene.control.TableView?>
+<?import javafx.scene.layout.AnchorPane?>
+<?import javafx.scene.layout.BorderPane?>
+
+<AnchorPane prefHeight="800.0" prefWidth="1600.0" xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="controllers.DDTabController">
+   <children>
+      <BorderPane prefHeight="800.0" prefWidth="1600.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
+         <top>
+            <Button alignment="CENTER" contentDisplay="CENTER" mnemonicParsing="false" onAction="#GetTodaysRacesAction" text="GetTodaysRaces" BorderPane.alignment="CENTER" />
+         </top>
+         <center>
+            <TableView fx:id="racesTable" prefHeight="200.0" prefWidth="200.0" BorderPane.alignment="CENTER">
+              <columns>
+                <TableColumn fx:id="trackColumn" prefWidth="40.0" text="Track" />
+                <TableColumn fx:id="raceColumn" prefWidth="40.0" text="Race" />
+              	<TableColumn fx:id="horseNameColumn" prefWidth="150.0" text="Horse name" />
+               	<TableColumn fx:id="laneColumn" prefWidth="50.0" text="Lane" />
+                <TableColumn fx:id="distanceColumn" prefWidth="60.0" text="Distance" />
+                <TableColumn fx:id="timeModifierColumn" prefWidth="100.0" text="Time modifier" />
+                <TableColumn fx:id="shoesColumn" prefWidth="60.0" text="Shoes" />
+                <TableColumn fx:id="driverNameColumn" prefWidth="100.0" text="Driver name" />
+                <TableColumn fx:id="trainerNameColumn" prefWidth="100.0" text="Traniner name" />
+                <TableColumn fx:id="resultColumn" prefWidth="100.0" text="Result" />
+                <TableColumn fx:id="horseAvgTimeColumn" prefWidth="100.0" text="Horse avg time" />
+                <TableColumn fx:id="horseAvgTimeDistanceColumn" prefWidth="100.0" text="Horse avg time distance" visible="false" />
+                <TableColumn fx:id="horseGallopRateColumn" prefWidth="60.0" text="Horse gallop rate" />
+                <TableColumn fx:id="driverAvgTimeColumn" prefWidth="100.0" text="Driver avg time" />
+                <TableColumn fx:id="driverAvgTimeDistanceColumn" prefWidth="100.0" text="Driver avg time distance" visible="false" />
+                <TableColumn fx:id="driverGallopRateColumn" prefWidth="60.0" text="Driver gallop rate" />
+                <TableColumn fx:id="avgTimeColumn" prefWidth="60.0" text="Avg time" />
+                <TableColumn fx:id="calculationColumn" prefWidth="60.0" text="CalculatedValue" />
+                <TableColumn fx:id="prevCalculationColumn" prefWidth="60.0" text="PrevCalculatedValue" />
+                <TableColumn fx:id="winPercentColumn" prefWidth="60.0" text="Lane win percent" />
+              </columns>
+            </TableView>
+         </center>
+      </BorderPane>
+   </children>
+</AnchorPane>

+ 43 - 0
ATG/src/fxml/LD.fxml

@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<?import javafx.scene.control.Button?>
+<?import javafx.scene.control.TableColumn?>
+<?import javafx.scene.control.TableView?>
+<?import javafx.scene.layout.AnchorPane?>
+<?import javafx.scene.layout.BorderPane?>
+
+<AnchorPane prefHeight="600.0" prefWidth="800.0" xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="controllers.LDTabController">
+   <children>
+      <BorderPane prefHeight="600.0" prefWidth="800.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
+         <top>
+            <Button onAction="#GetTodaysRacesAction" alignment="CENTER" contentDisplay="CENTER" mnemonicParsing="false" text="GetTodaysRaces" BorderPane.alignment="CENTER" />
+         </top>
+         <center>
+            <TableView fx:id="racesTable" prefHeight="200.0" prefWidth="200.0" BorderPane.alignment="CENTER">
+              <columns>
+                <TableColumn fx:id="trackColumn" prefWidth="40.0" text="Track" />
+                <TableColumn fx:id="raceColumn" prefWidth="40.0" text="Race" />
+              	<TableColumn fx:id="horseNameColumn" prefWidth="150.0" text="Horse name" />
+               	<TableColumn fx:id="laneColumn" prefWidth="50.0" text="Lane" />
+                <TableColumn fx:id="distanceColumn" prefWidth="60.0" text="Distance" />
+                <TableColumn fx:id="timeModifierColumn" prefWidth="100.0" text="Time modifier" />
+                <TableColumn fx:id="shoesColumn" prefWidth="60.0" text="Shoes" />
+                <TableColumn fx:id="driverNameColumn" prefWidth="100.0" text="Driver name" />
+                <TableColumn fx:id="trainerNameColumn" prefWidth="100.0" text="Traniner name" />
+                <TableColumn fx:id="resultColumn" prefWidth="100.0" text="Result" />
+                <TableColumn fx:id="horseAvgTimeColumn" prefWidth="100.0" text="Horse avg time" />
+                <TableColumn fx:id="horseAvgTimeDistanceColumn" prefWidth="100.0" text="Horse avg time distance" visible="false" />
+                <TableColumn fx:id="horseGallopRateColumn" prefWidth="60.0" text="Horse gallop rate" />
+                <TableColumn fx:id="driverAvgTimeColumn" prefWidth="100.0" text="Driver avg time" />
+                <TableColumn fx:id="driverAvgTimeDistanceColumn" prefWidth="100.0" text="Driver avg time distance" visible="false" />
+                <TableColumn fx:id="driverGallopRateColumn" prefWidth="60.0" text="Driver gallop rate" />
+                <TableColumn fx:id="avgTimeColumn" prefWidth="60.0" text="Avg time" />
+                <TableColumn fx:id="calculationColumn" prefWidth="60.0" text="CalculatedValue" />
+                <TableColumn fx:id="prevCalculationColumn" prefWidth="60.0" text="PrevCalculatedValue" />
+                <TableColumn fx:id="winPercentColumn" prefWidth="60.0" text="Lane win percent" />
+              </columns>
+            </TableView>
+         </center>
+      </BorderPane>
+   </children>
+</AnchorPane>

+ 29 - 0
ATG/src/fxml/RaceTableFxml.fxml

@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<?import javafx.scene.control.Button?>
+<?import javafx.scene.control.TableColumn?>
+<?import javafx.scene.control.TableView?>
+<?import javafx.scene.layout.BorderPane?>
+
+<BorderPane prefHeight="600.0" prefWidth="800.0" xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com/fxml/1">
+   <top>
+      <Button alignment="CENTER" contentDisplay="CENTER" mnemonicParsing="false" onAction="#GetTodaysRacesAction" text="GetTodaysRaces" BorderPane.alignment="CENTER" />
+   </top>
+   <center>
+      <TableView fx:id="racesTable" prefHeight="200.0" prefWidth="200.0" BorderPane.alignment="CENTER">
+         <columns>
+            <TableColumn fx:id="raceColumn" prefWidth="50.0" text="Race" />
+            <TableColumn fx:id="horseNameColumn" prefWidth="100.0" text="Horse name" />
+            <TableColumn fx:id="laneColumn" prefWidth="50.0" text="Lane" />
+            <TableColumn fx:id="distanceColumn" prefWidth="60.0" text="Distance" />
+            <TableColumn fx:id="timeColumn" prefWidth="50.0" text="Time" />
+            <TableColumn fx:id="timeModifierColumn" prefWidth="100.0" text="Time modifier" />
+            <TableColumn fx:id="shoesColumn" prefWidth="60.0" text="Shoes" />
+            <TableColumn fx:id="sulkyColumn" prefWidth="75.0" text="Sulky" />
+            <TableColumn fx:id="driverNameColumn" prefWidth="100.0" text="Driver name" />
+            <TableColumn fx:id="trainerNameColumn" prefWidth="100.0" text="Traniner name" />
+            <TableColumn fx:id="resultColumn" prefWidth="60.0" text="Result" />
+         </columns>
+      </TableView>
+   </center>
+</BorderPane>

+ 57 - 0
ATG/src/fxml/TestTab.fxml

@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<?import javafx.scene.control.Button?>
+<?import javafx.scene.control.ChoiceBox?>
+<?import javafx.scene.control.DatePicker?>
+<?import javafx.scene.control.TableColumn?>
+<?import javafx.scene.control.TableView?>
+<?import javafx.scene.layout.AnchorPane?>
+<?import javafx.scene.layout.BorderPane?>
+<?import javafx.scene.layout.FlowPane?>
+
+<AnchorPane prefHeight="800.0" prefWidth="1600.0" xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="controllers.TestTabController">
+   <children>
+      <BorderPane prefHeight="800.0" prefWidth="1600.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
+         <center>
+            <TableView fx:id="racesTable" prefHeight="200.0" prefWidth="200.0" BorderPane.alignment="CENTER">
+              <columns>
+                <TableColumn fx:id="trackColumn" prefWidth="40.0" text="Track" />
+                <TableColumn fx:id="raceColumn" prefWidth="40.0" text="Race" />
+              	<TableColumn fx:id="horseNameColumn" prefWidth="150.0" text="Horse name" />
+               	<TableColumn fx:id="laneColumn" prefWidth="50.0" text="Lane" />
+                <TableColumn fx:id="distanceColumn" prefWidth="60.0" text="Distance" />
+                <TableColumn fx:id="timeModifierColumn" prefWidth="100.0" text="Time modifier" />
+                <TableColumn fx:id="driverNameColumn" prefWidth="100.0" text="Driver name" />
+                <TableColumn fx:id="trainerNameColumn" prefWidth="100.0" text="Traniner name" />
+                <TableColumn fx:id="resultColumn" prefWidth="100.0" text="Result" />
+                <TableColumn fx:id="horseAvgTimeColumn" prefWidth="100.0" text="Horse avg time" />
+                <TableColumn fx:id="horseAvgTimeDistanceColumn" prefWidth="100.0" text="Horse avg time distance" />
+                <TableColumn fx:id="horseGallopRateColumn" prefWidth="60.0" text="Horse gallop rate" />
+                <TableColumn fx:id="driverAvgTimeColumn" prefWidth="100.0" text="Driver avg time" />
+                <TableColumn fx:id="driverAvgTimeDistanceColumn" prefWidth="100.0" text="Driver avg time distance" />
+                <TableColumn fx:id="driverGallopRateColumn" prefWidth="60.0" text="Driver gallop rate" />
+                <TableColumn fx:id="avgTimeColumn" prefWidth="60.0" text="Avg time" />
+                <TableColumn fx:id="calculationColumn" prefWidth="60.0" text="CalculatedValue" />
+                <TableColumn fx:id="diffCalculationColumn" prefWidth="60.0" text="Diff to next" />
+                <TableColumn fx:id="prevCalculationColumn" prefWidth="60.0" text="PrevCalculatedValue" />
+                <TableColumn fx:id="diffPrevCalculationColumn" prefWidth="60.0" text="Diff to next prev" />
+                <TableColumn fx:id="winPercentColumn" prefWidth="60.0" text="Lane win percent" />
+                <TableColumn fx:id="trendLast5Column" prefWidth="60.0" text="Trend last 5" />
+                <TableColumn fx:id="trendLast5DistColumn" prefWidth="60.0" text="Trend Last 5 Distance" />
+              </columns>
+            </TableView>
+         </center>
+         <top>
+            <FlowPane BorderPane.alignment="CENTER">
+               <children>
+                  <DatePicker fx:id="datePicker" promptText="yyyy-MM-dd" />
+                  <Button fx:id="getRacesButton" alignment="CENTER" contentDisplay="CENTER" mnemonicParsing="false" onAction="#GetRacesAction" text="GetRaces" />
+                  <ChoiceBox fx:id="raceTypeSelector" prefWidth="150.0" />
+                  <Button fx:id="getRacesForDateButton" mnemonicParsing="false" onAction="#GetRacesForDate" text="Parse Races For Date" />
+                  <Button fx:id="getRaceCalendarButton" mnemonicParsing="false" onAction="#GetRaceCalendar" text="Get race calendar" />
+               </children>
+            </FlowPane>
+         </top>
+      </BorderPane>
+   </children>
+</AnchorPane>

+ 40 - 0
ATG/src/fxml/UpdateTab.fxml

@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<?import javafx.scene.control.Button?>
+<?import javafx.scene.control.DatePicker?>
+<?import javafx.scene.control.ScrollPane?>
+<?import javafx.scene.layout.AnchorPane?>
+<?import javafx.scene.layout.BorderPane?>
+<?import javafx.scene.layout.FlowPane?>
+<?import javafx.scene.layout.Pane?>
+
+<AnchorPane prefHeight="800.0" prefWidth="1600.0" xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="controllers.UpdateTabController">
+   <children>
+      <BorderPane prefHeight="800.0" prefWidth="1600.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
+         <center>
+            <Pane BorderPane.alignment="CENTER">
+               <children>
+                  <ScrollPane fx:id="RacesTable">
+                    <content>
+                      <AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="774.0" prefWidth="1606.0">
+                           <children>
+                              <FlowPane fx:id="flowPaneRowContainer" orientation="VERTICAL" prefHeight="774.0" prefWidth="1606.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0" />
+                           </children>
+                        </AnchorPane>
+                    </content>
+                  </ScrollPane>
+               </children>
+            </Pane>
+         </center>
+         <top>
+            <FlowPane alignment="CENTER" BorderPane.alignment="CENTER">
+               <children>
+                  <DatePicker fx:id="datePicker" />
+                  <Button fx:id="getRacesButton" mnemonicParsing="false" onAction="#getRacesAction" text="Get Races" />
+                  <Button disable="true" mnemonicParsing="false" text="Update races" />
+               </children>
+            </FlowPane>
+         </top>
+      </BorderPane>
+   </children>
+</AnchorPane>

+ 43 - 0
ATG/src/fxml/V4.fxml

@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<?import javafx.scene.control.Button?>
+<?import javafx.scene.control.TableColumn?>
+<?import javafx.scene.control.TableView?>
+<?import javafx.scene.layout.AnchorPane?>
+<?import javafx.scene.layout.BorderPane?>
+
+<AnchorPane prefHeight="600.0" prefWidth="800.0" xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="controllers.V4TabController">
+   <children>
+      <BorderPane prefHeight="600.0" prefWidth="800.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
+         <top>
+            <Button alignment="CENTER" contentDisplay="CENTER" mnemonicParsing="false" onAction="#GetTodaysRacesAction" text="GetTodaysRaces" BorderPane.alignment="CENTER" />
+         </top>
+         <center>
+            <TableView fx:id="racesTable" prefHeight="200.0" prefWidth="200.0" BorderPane.alignment="CENTER">
+              <columns>
+                <TableColumn fx:id="trackColumn" prefWidth="40.0" text="Track" />
+                <TableColumn fx:id="raceColumn" prefWidth="40.0" text="Race" />
+              	<TableColumn fx:id="horseNameColumn" prefWidth="150.0" text="Horse name" />
+               	<TableColumn fx:id="laneColumn" prefWidth="50.0" text="Lane" />
+                <TableColumn fx:id="distanceColumn" prefWidth="60.0" text="Distance" />
+                <TableColumn fx:id="timeModifierColumn" prefWidth="100.0" text="Time modifier" />
+                <TableColumn fx:id="shoesColumn" prefWidth="60.0" text="Shoes" />
+                <TableColumn fx:id="driverNameColumn" prefWidth="100.0" text="Driver name" />
+                <TableColumn fx:id="trainerNameColumn" prefWidth="100.0" text="Traniner name" />
+                <TableColumn fx:id="resultColumn" prefWidth="100.0" text="Result" />
+                <TableColumn fx:id="horseAvgTimeColumn" prefWidth="100.0" text="Horse avg time" />
+                <TableColumn fx:id="horseAvgTimeDistanceColumn" prefWidth="100.0" text="Horse avg time distance" visible="false" />
+                <TableColumn fx:id="horseGallopRateColumn" prefWidth="60.0" text="Horse gallop rate" />
+                <TableColumn fx:id="driverAvgTimeColumn" prefWidth="100.0" text="Driver avg time" />
+                <TableColumn fx:id="driverAvgTimeDistanceColumn" prefWidth="100.0" text="Driver avg time distance" visible="false" />
+                <TableColumn fx:id="driverGallopRateColumn" prefWidth="60.0" text="Driver gallop rate" />
+                <TableColumn fx:id="avgTimeColumn" prefWidth="60.0" text="Avg time" />
+                <TableColumn fx:id="calculationColumn" prefWidth="60.0" text="CalculatedValue" />
+                <TableColumn fx:id="prevCalculationColumn" prefWidth="60.0" text="PrevCalculatedValue" />
+                <TableColumn fx:id="winPercentColumn" prefWidth="60.0" text="Lane win percent" />
+              </columns>
+            </TableView>
+         </center>
+      </BorderPane>
+   </children>
+</AnchorPane>

+ 43 - 0
ATG/src/fxml/V5.fxml

@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<?import javafx.scene.control.Button?>
+<?import javafx.scene.control.TableColumn?>
+<?import javafx.scene.control.TableView?>
+<?import javafx.scene.layout.AnchorPane?>
+<?import javafx.scene.layout.BorderPane?>
+
+<AnchorPane prefHeight="600.0" prefWidth="800.0" xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="controllers.V5TabController">
+   <children>
+      <BorderPane prefHeight="600.0" prefWidth="800.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
+         <top>
+            <Button alignment="CENTER" contentDisplay="CENTER" mnemonicParsing="false" onAction="#GetTodaysRacesAction" text="GetTodaysRaces" BorderPane.alignment="CENTER" />
+         </top>
+         <center>
+            <TableView fx:id="racesTable" prefHeight="200.0" prefWidth="200.0" BorderPane.alignment="CENTER">
+              <columns>
+                <TableColumn fx:id="trackColumn" prefWidth="40.0" text="Track" />
+                <TableColumn fx:id="raceColumn" prefWidth="40.0" text="Race" />
+              	<TableColumn fx:id="horseNameColumn" prefWidth="150.0" text="Horse name" />
+               	<TableColumn fx:id="laneColumn" prefWidth="50.0" text="Lane" />
+                <TableColumn fx:id="distanceColumn" prefWidth="60.0" text="Distance" />
+                <TableColumn fx:id="timeModifierColumn" prefWidth="100.0" text="Time modifier" />
+                <TableColumn fx:id="shoesColumn" prefWidth="60.0" text="Shoes" />
+                <TableColumn fx:id="driverNameColumn" prefWidth="100.0" text="Driver name" />
+                <TableColumn fx:id="trainerNameColumn" prefWidth="100.0" text="Traniner name" />
+                <TableColumn fx:id="resultColumn" prefWidth="100.0" text="Result" />
+                <TableColumn fx:id="horseAvgTimeColumn" prefWidth="100.0" text="Horse avg time" />
+                <TableColumn fx:id="horseAvgTimeDistanceColumn" prefWidth="100.0" text="Horse avg time distance" visible="false" />
+                <TableColumn fx:id="horseGallopRateColumn" prefWidth="60.0" text="Horse gallop rate" />
+                <TableColumn fx:id="driverAvgTimeColumn" prefWidth="100.0" text="Driver avg time" />
+                <TableColumn fx:id="driverAvgTimeDistanceColumn" prefWidth="100.0" text="Driver avg time distance" visible="false" />
+                <TableColumn fx:id="driverGallopRateColumn" prefWidth="60.0" text="Driver gallop rate" />
+                <TableColumn fx:id="avgTimeColumn" prefWidth="60.0" text="Avg time" />
+                <TableColumn fx:id="calculationColumn" prefWidth="60.0" text="CalculatedValue" />
+                <TableColumn fx:id="prevCalculationColumn" prefWidth="60.0" text="PrevCalculatedValue" />
+                <TableColumn fx:id="winPercentColumn" prefWidth="60.0" text="Lane win percent" />
+              </columns>
+            </TableView>
+         </center>
+      </BorderPane>
+   </children>
+</AnchorPane>

+ 43 - 0
ATG/src/fxml/V64.fxml

@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<?import javafx.scene.control.Button?>
+<?import javafx.scene.control.TableColumn?>
+<?import javafx.scene.control.TableView?>
+<?import javafx.scene.layout.AnchorPane?>
+<?import javafx.scene.layout.BorderPane?>
+
+<AnchorPane prefHeight="600.0" prefWidth="800.0" xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="controllers.V64TabController">
+   <children>
+      <BorderPane prefHeight="600.0" prefWidth="800.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
+         <top>
+            <Button alignment="CENTER" contentDisplay="CENTER" mnemonicParsing="false" onAction="#GetTodaysRacesAction" text="GetTodaysRaces" BorderPane.alignment="CENTER" />
+         </top>
+         <center>
+            <TableView fx:id="racesTable" prefHeight="200.0" prefWidth="200.0" BorderPane.alignment="CENTER">
+              <columns>
+                <TableColumn fx:id="trackColumn" prefWidth="40.0" text="Track" />
+                <TableColumn fx:id="raceColumn" prefWidth="40.0" text="Race" />
+              	<TableColumn fx:id="horseNameColumn" prefWidth="150.0" text="Horse name" />
+               	<TableColumn fx:id="laneColumn" prefWidth="50.0" text="Lane" />
+                <TableColumn fx:id="distanceColumn" prefWidth="60.0" text="Distance" />
+                <TableColumn fx:id="timeModifierColumn" prefWidth="100.0" text="Time modifier" />
+                <TableColumn fx:id="shoesColumn" prefWidth="60.0" text="Shoes" />
+                <TableColumn fx:id="driverNameColumn" prefWidth="100.0" text="Driver name" />
+                <TableColumn fx:id="trainerNameColumn" prefWidth="100.0" text="Traniner name" />
+                <TableColumn fx:id="resultColumn" prefWidth="100.0" text="Result" />
+                <TableColumn fx:id="horseAvgTimeColumn" prefWidth="100.0" text="Horse avg time" />
+                <TableColumn fx:id="horseAvgTimeDistanceColumn" prefWidth="100.0" text="Horse avg time distance" visible="false" />
+                <TableColumn fx:id="horseGallopRateColumn" prefWidth="60.0" text="Horse gallop rate" />
+                <TableColumn fx:id="driverAvgTimeColumn" prefWidth="100.0" text="Driver avg time" />
+                <TableColumn fx:id="driverAvgTimeDistanceColumn" prefWidth="100.0" text="Driver avg time distance" visible="false" />
+                <TableColumn fx:id="driverGallopRateColumn" prefWidth="60.0" text="Driver gallop rate" />
+                <TableColumn fx:id="avgTimeColumn" prefWidth="60.0" text="Avg time" />
+                <TableColumn fx:id="calculationColumn" prefWidth="60.0" text="CalculatedValue" />
+                <TableColumn fx:id="prevCalculationColumn" prefWidth="60.0" text="PrevCalculatedValue" />
+                <TableColumn fx:id="winPercentColumn" prefWidth="60.0" text="Lane win percent" />
+              </columns>
+            </TableView>
+         </center>
+      </BorderPane>
+   </children>
+</AnchorPane>

+ 43 - 0
ATG/src/fxml/V65.fxml

@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<?import javafx.scene.control.Button?>
+<?import javafx.scene.control.TableColumn?>
+<?import javafx.scene.control.TableView?>
+<?import javafx.scene.layout.AnchorPane?>
+<?import javafx.scene.layout.BorderPane?>
+
+<AnchorPane prefHeight="600.0" prefWidth="800.0" xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="controllers.V65TabController">
+   <children>
+      <BorderPane prefHeight="600.0" prefWidth="800.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
+         <top>
+            <Button alignment="CENTER" contentDisplay="CENTER" mnemonicParsing="false" onAction="#GetTodaysRacesAction" text="GetTodaysRaces" BorderPane.alignment="CENTER" />
+         </top>
+         <center>
+            <TableView fx:id="racesTable" prefHeight="200.0" prefWidth="200.0" BorderPane.alignment="CENTER">
+              <columns>
+                <TableColumn fx:id="trackColumn" prefWidth="40.0" text="Track" />
+                <TableColumn fx:id="raceColumn" prefWidth="40.0" text="Race" />
+              	<TableColumn fx:id="horseNameColumn" prefWidth="150.0" text="Horse name" />
+               	<TableColumn fx:id="laneColumn" prefWidth="50.0" text="Lane" />
+                <TableColumn fx:id="distanceColumn" prefWidth="60.0" text="Distance" />
+                <TableColumn fx:id="timeModifierColumn" prefWidth="100.0" text="Time modifier" />
+                <TableColumn fx:id="shoesColumn" prefWidth="60.0" text="Shoes" />
+                <TableColumn fx:id="driverNameColumn" prefWidth="100.0" text="Driver name" />
+                <TableColumn fx:id="trainerNameColumn" prefWidth="100.0" text="Traniner name" />
+                <TableColumn fx:id="resultColumn" prefWidth="100.0" text="Result" />
+                <TableColumn fx:id="horseAvgTimeColumn" prefWidth="100.0" text="Horse avg time" />
+                <TableColumn fx:id="horseAvgTimeDistanceColumn" prefWidth="100.0" text="Horse avg time distance" visible="false" />
+                <TableColumn fx:id="horseGallopRateColumn" prefWidth="60.0" text="Horse gallop rate" />
+                <TableColumn fx:id="driverAvgTimeColumn" prefWidth="100.0" text="Driver avg time" />
+                <TableColumn fx:id="driverAvgTimeDistanceColumn" prefWidth="100.0" text="Driver avg time distance" visible="false" />
+                <TableColumn fx:id="driverGallopRateColumn" prefWidth="60.0" text="Driver gallop rate" />
+                <TableColumn fx:id="avgTimeColumn" prefWidth="60.0" text="Avg time" />
+                <TableColumn fx:id="calculationColumn" prefWidth="60.0" text="CalculatedValue" />
+                <TableColumn fx:id="prevCalculationColumn" prefWidth="60.0" text="PrevCalculatedValue" />
+                <TableColumn fx:id="winPercentColumn" prefWidth="60.0" text="Lane win percent" />
+              </columns>
+            </TableView>
+         </center>
+      </BorderPane>
+   </children>
+</AnchorPane>

+ 43 - 0
ATG/src/fxml/V75.fxml

@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<?import javafx.scene.control.Button?>
+<?import javafx.scene.control.TableColumn?>
+<?import javafx.scene.control.TableView?>
+<?import javafx.scene.layout.AnchorPane?>
+<?import javafx.scene.layout.BorderPane?>
+
+<AnchorPane prefHeight="600.0" prefWidth="800.0" xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="controllers.V75TabController">
+   <children>
+      <BorderPane prefHeight="600.0" prefWidth="800.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
+         <top>
+            <Button alignment="CENTER" contentDisplay="CENTER" mnemonicParsing="false" onAction="#GetTodaysRacesAction" text="GetTodaysRaces" BorderPane.alignment="CENTER" />
+         </top>
+         <center>
+            <TableView fx:id="racesTable" prefHeight="200.0" prefWidth="200.0" BorderPane.alignment="CENTER">
+              <columns>
+                <TableColumn fx:id="trackColumn" prefWidth="40.0" text="Track" />
+                <TableColumn fx:id="raceColumn" prefWidth="40.0" text="Race" />
+              	<TableColumn fx:id="horseNameColumn" prefWidth="150.0" text="Horse name" />
+               	<TableColumn fx:id="laneColumn" prefWidth="50.0" text="Lane" />
+                <TableColumn fx:id="distanceColumn" prefWidth="60.0" text="Distance" />
+                <TableColumn fx:id="timeModifierColumn" prefWidth="100.0" text="Time modifier" />
+                <TableColumn fx:id="shoesColumn" prefWidth="60.0" text="Shoes" />
+                <TableColumn fx:id="driverNameColumn" prefWidth="100.0" text="Driver name" />
+                <TableColumn fx:id="trainerNameColumn" prefWidth="100.0" text="Traniner name" />
+                <TableColumn fx:id="resultColumn" prefWidth="100.0" text="Result" />
+                <TableColumn fx:id="horseAvgTimeColumn" prefWidth="100.0" text="Horse avg time" />
+                <TableColumn fx:id="horseAvgTimeDistanceColumn" prefWidth="100.0" text="Horse avg time distance" visible="false" />
+                <TableColumn fx:id="horseGallopRateColumn" prefWidth="60.0" text="Horse gallop rate" />
+                <TableColumn fx:id="driverAvgTimeColumn" prefWidth="100.0" text="Driver avg time" />
+                <TableColumn fx:id="driverAvgTimeDistanceColumn" prefWidth="100.0" text="Driver avg time distance" visible="false" />
+                <TableColumn fx:id="driverGallopRateColumn" prefWidth="60.0" text="Driver gallop rate" />
+                <TableColumn fx:id="avgTimeColumn" prefWidth="60.0" text="Avg time" />
+                <TableColumn fx:id="calculationColumn" prefWidth="60.0" text="CalculatedValue" />
+                <TableColumn fx:id="prevCalculationColumn" prefWidth="60.0" text="PrevCalculatedValue" />
+                <TableColumn fx:id="winPercentColumn" prefWidth="60.0" text="Lane win percent" />
+              </columns>
+            </TableView>
+         </center>
+      </BorderPane>
+   </children>
+</AnchorPane>

+ 43 - 0
ATG/src/fxml/V86.fxml

@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<?import javafx.scene.control.Button?>
+<?import javafx.scene.control.TableColumn?>
+<?import javafx.scene.control.TableView?>
+<?import javafx.scene.layout.AnchorPane?>
+<?import javafx.scene.layout.BorderPane?>
+
+<AnchorPane prefHeight="600.0" prefWidth="800.0" xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="controllers.V86TabController">
+   <children>
+      <BorderPane prefHeight="600.0" prefWidth="800.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
+         <top>
+            <Button alignment="CENTER" contentDisplay="CENTER" mnemonicParsing="false" onAction="#GetTodaysRacesAction" text="GetTodaysRaces" BorderPane.alignment="CENTER" />
+         </top>
+         <center>
+            <TableView fx:id="racesTable" prefHeight="200.0" prefWidth="200.0" BorderPane.alignment="CENTER">
+              <columns>
+                <TableColumn fx:id="trackColumn" prefWidth="40.0" text="Track" />
+                <TableColumn fx:id="raceColumn" prefWidth="40.0" text="Race" />
+              	<TableColumn fx:id="horseNameColumn" prefWidth="150.0" text="Horse name" />
+               	<TableColumn fx:id="laneColumn" prefWidth="50.0" text="Lane" />
+                <TableColumn fx:id="distanceColumn" prefWidth="60.0" text="Distance" />
+                <TableColumn fx:id="timeModifierColumn" prefWidth="100.0" text="Time modifier" />
+                <TableColumn fx:id="shoesColumn" prefWidth="60.0" text="Shoes" />
+                <TableColumn fx:id="driverNameColumn" prefWidth="100.0" text="Driver name" />
+                <TableColumn fx:id="trainerNameColumn" prefWidth="100.0" text="Traniner name" />
+                <TableColumn fx:id="resultColumn" prefWidth="100.0" text="Result" />
+                <TableColumn fx:id="horseAvgTimeColumn" prefWidth="100.0" text="Horse avg time" />
+                <TableColumn fx:id="horseAvgTimeDistanceColumn" prefWidth="100.0" text="Horse avg time distance" visible="false" />
+                <TableColumn fx:id="horseGallopRateColumn" prefWidth="60.0" text="Horse gallop rate" />
+                <TableColumn fx:id="driverAvgTimeColumn" prefWidth="100.0" text="Driver avg time" />
+                <TableColumn fx:id="driverAvgTimeDistanceColumn" prefWidth="100.0" text="Driver avg time distance" visible="false" />
+                <TableColumn fx:id="driverGallopRateColumn" prefWidth="60.0" text="Driver gallop rate" />
+                <TableColumn fx:id="avgTimeColumn" prefWidth="60.0" text="Avg time" />
+                <TableColumn fx:id="calculationColumn" prefWidth="60.0" text="Calculated Value" />
+                <TableColumn fx:id="prevCalculationColumn" prefWidth="60.0" text="PrevCalculatedValue" />
+                <TableColumn fx:id="winPercentColumn" prefWidth="60.0" text="Lane win percent" />
+              </columns>
+            </TableView>
+         </center>
+      </BorderPane>
+   </children>
+</AnchorPane>

+ 207 - 0
ATG/src/objects/HorseRaceData.java

@@ -0,0 +1,207 @@
+package objects;
+
+public class HorseRaceData {
+
+	private String trackName;
+	private String raceDate;
+	private int horseId;
+	private String horseName;
+	private int raceNumber;
+	private int lane;
+	private int distance;
+	private int result;
+	private float time;
+	private String timeModifier;
+	private int Shoes;
+	private int Sulky;
+	private int trainerId;
+	private int driverId;
+	private String trainerName;
+	private String driverName;
+
+	private float avgHorseTime;
+	private float avgDriverTime;
+	private float avgHorseTimeByDistance;
+	private float avgDriverTimeByDistance;
+	private float avgHorseTimeByDistanceAndType;
+	private float avgDriverTimeByDistanceAndType;
+
+	private float timeToHorseBehindPrevCalculation;
+	private float timeToHorseBehindCalculation;
+
+	private int avgHorseTimeCount;
+	private int avgDriverTimeCount;
+	private int avgHorseTimeByDistanceCount;
+	private int avgDriverTimeByDistanceCount;
+	private int avgHorseTimeByDistanceAndTypeCount;
+	private int avgDriverTimeByDistanceAndTypeCount;
+
+	public String getTrackName() {
+		return trackName;
+	}
+	public String getRaceDate() {
+		return raceDate;
+	}
+	public int getRaceNumber() {
+		return raceNumber;
+	}
+	public int getLane() {
+		return lane;
+	}
+	public int getDistance() {
+		return distance;
+	}
+	public int getResult() {
+		return result;
+	}
+	public float getTime() {
+		return time;
+	}
+	public String getTimeModifier() {
+		return timeModifier;
+	}
+	public int getShoes() {
+		return Shoes;
+	}
+	public int getSulky() {
+		return Sulky;
+	}
+	public int getTrainerId() {
+		return trainerId;
+	}
+	public int getDriverId() {
+		return driverId;
+	}
+	public String getTrainerName() {
+		return trainerName;
+	}
+	public String getDriverName() {
+		return driverName;
+	}
+	public void setTrackName(String trackName) {
+		this.trackName = trackName;
+	}
+	public void setRaceDate(String raceDate) {
+		this.raceDate = raceDate;
+	}
+	public void setRaceNumber(int raceNumber) {
+		this.raceNumber = raceNumber;
+	}
+	public void setLane(int lane) {
+		this.lane = lane;
+	}
+	public void setDistance(int distance) {
+		this.distance = distance;
+	}
+	public void setResult(int result) {
+		this.result = result;
+	}
+	public void setTime(float time) {
+		this.time = time;
+	}
+	public void setTimeModifier(String timeModifier) {
+		this.timeModifier = timeModifier;
+	}
+	public void setShoes(int shoes) {
+		Shoes = shoes;
+	}
+	public void setSulky(int sulky) {
+		Sulky = sulky;
+	}
+	public void setTrainerId(int trainerId) {
+		this.trainerId = trainerId;
+	}
+	public void setDriverId(int driverId) {
+		this.driverId = driverId;
+	}
+	public void setTrainerName(String trainerName) {
+		this.trainerName = trainerName;
+	}
+	public void setDriverName(String driverName) {
+		this.driverName = driverName;
+	}
+	public String getHorseName() {
+		return horseName;
+	}
+	public void setHorseName(String horseName) {
+		this.horseName = horseName;
+	}
+	public int getHorseId() {
+		return horseId;
+	}
+	public void setHorseId(int horseId) {
+		this.horseId = horseId;
+	}
+	public float getAvgHorseTime() {
+		return avgHorseTime;
+	}
+	public float getAvgDriverTime() {
+		return avgDriverTime;
+	}
+	public void setAvgHorseTime(float avgHorseTime, int count) {
+		this.avgHorseTime = avgHorseTime;
+		this.avgHorseTimeCount = count;
+	}
+	public void setAvgDriverTime(float avgDriverTime, int count) {
+		this.avgDriverTime = avgDriverTime;
+		this.avgDriverTimeCount = count;
+	}
+	public float getAvgHorseTimeByDistance() {
+		return avgHorseTimeByDistance;
+	}
+	public float getAvgDriverTimeByDistance() {
+		return avgDriverTimeByDistance;
+	}
+	public float getAvgHorseTimeByDistanceAndType() {
+		return avgHorseTimeByDistanceAndType;
+	}
+	public float getAvgDriverTimeByDistanceAndType() {
+		return avgDriverTimeByDistanceAndType;
+	}
+	public void setAvgHorseTimeByDistance(float avgHorseTimeByDistance, int count) {
+		this.avgHorseTimeByDistance = avgHorseTimeByDistance;
+		this.avgHorseTimeByDistanceCount = count;
+	}
+	public void setAvgDriverTimeByDistance(float avgDriverTimeByDistance, int count) {
+		this.avgDriverTimeByDistance = avgDriverTimeByDistance;
+		this.avgDriverTimeByDistanceCount = count;
+	}
+	public void setAvgHorseTimeByDistanceAndType(float avgHorseTimeByDistanceAndType, int count) {
+		this.avgHorseTimeByDistanceAndType = avgHorseTimeByDistanceAndType;
+		this.avgHorseTimeByDistanceAndTypeCount = count;
+	}
+	public void setAvgDriverTimeByDistanceAndType(float avgDriverTimeByDistanceAndType, int count) {
+		this.avgDriverTimeByDistanceAndType = avgDriverTimeByDistanceAndType;
+		this.avgDriverTimeByDistanceAndTypeCount = count;
+	}
+	public int getAvgHorseTimeCount() {
+		return avgHorseTimeCount;
+	}
+	public int getAvgDriverTimeCount() {
+		return avgDriverTimeCount;
+	}
+	public int getAvgHorseTimeByDistanceCount() {
+		return avgHorseTimeByDistanceCount;
+	}
+	public int getAvgDriverTimeByDistanceCount() {
+		return avgDriverTimeByDistanceCount;
+	}
+	public int getAvgHorseTimeByDistanceAndTypeCount() {
+		return avgHorseTimeByDistanceAndTypeCount;
+	}
+	public int getAvgDriverTimeByDistanceAndTypeCount() {
+		return avgDriverTimeByDistanceAndTypeCount;
+	}
+	public void setTimeToHorseBehindPrevCalculation(float value) {
+		timeToHorseBehindPrevCalculation = value;
+	}
+	public float getTimeToHorseBehindPrevCalculation() {
+		return timeToHorseBehindPrevCalculation;
+	}
+	public void setTimeToHorseBehindCalculation(float value) {
+		timeToHorseBehindCalculation = value;
+	}
+	public float getTimeToHorseBehindCalculation() {
+		return timeToHorseBehindCalculation;
+	}
+}

+ 101 - 0
ATG/src/objects/UpdateRaceRow.java

@@ -0,0 +1,101 @@
+package objects;
+
+import java.util.List;
+
+import com.google.common.collect.Lists;
+
+import controllers.DatabaseContoller;
+import javafx.event.EventHandler;
+import javafx.geometry.Orientation;
+import javafx.scene.control.Button;
+import javafx.scene.control.TextField;
+import javafx.scene.layout.FlowPane;
+
+public class UpdateRaceRow {
+
+	int raceId;
+	String raceDate;
+	List<String> raceTypes = Lists.newArrayList("DD", "LD", "V75", "V65");
+	String raceType;
+	String startType;
+	int raceNumber;
+	String trackName;
+
+	Button applyButton = new Button("Apply changes");
+	private TextField raceIdTextField;
+	private TextField raceDateTextField;
+	private TextField raceTypeTextField;
+	private TextField startTypeTextField;
+	private TextField raceNumberTextField;
+	private TextField trackNameTextField;
+
+	public UpdateRaceRow(int raceId, String raceDate, String raceType, String startType, int raceNumber, String trackName) {
+		this.raceDate = raceDate;
+		this.raceId = raceId;
+		this.startType = startType;
+		this.raceType = raceType;
+		this.raceNumber = raceNumber;
+		this.trackName = trackName;
+
+		applyButton.setOnAction(new ApplyAction());
+	}
+
+	public FlowPane getGui(boolean isHeader) {
+		final FlowPane returnValue = new FlowPane(Orientation.HORIZONTAL);
+		returnValue.setDisable(false);
+		returnValue.setVisible(true);
+		returnValue.setPrefWrapLength(4000);
+
+		raceIdTextField = new TextField(String.valueOf(raceId));
+		raceDateTextField = new TextField(raceDate);
+		raceTypeTextField = new TextField(raceType);
+		startTypeTextField = new TextField(startType);
+		raceNumberTextField = new TextField(String.valueOf(raceNumber));
+		trackNameTextField = new TextField(trackName);
+
+		if (isHeader) {
+			raceIdTextField.setText("Race Id");
+			raceNumberTextField.setText("Race Number");
+			raceIdTextField.setDisable(true);
+			raceDateTextField.setDisable(true);
+			raceTypeTextField.setDisable(true);
+			startTypeTextField.setDisable(true);
+			raceNumberTextField.setDisable(true);
+			trackNameTextField.setDisable(true);
+		}
+		trackNameTextField.setEditable(false);
+		raceDateTextField.setEditable(false);
+		raceNumberTextField.setEditable(false);
+
+		returnValue.getChildren().add(trackNameTextField);
+		returnValue.getChildren().add(raceIdTextField);
+		returnValue.getChildren().add(raceDateTextField);
+		returnValue.getChildren().add(raceNumberTextField);
+		returnValue.getChildren().add(raceTypeTextField);
+		returnValue.getChildren().add(startTypeTextField);
+		returnValue.getChildren().add(applyButton);
+
+		return returnValue;
+	}
+
+	private class ApplyAction implements EventHandler<javafx.event.ActionEvent> {
+
+		@Override
+		public void handle(javafx.event.ActionEvent arg0) {
+			if (!startTypeTextField.getText().equals(startType) ||
+					!raceTypeTextField.getText().equals(raceType)) {
+				try {
+					final int raceIdInt = Integer.parseInt(raceIdTextField.getText());
+					final int raceNumberInt = Integer.parseInt(raceNumberTextField.getText());
+
+					DatabaseContoller.getInstance().updateRaceInfo(raceIdInt, startTypeTextField.getText(), raceTypeTextField.getText(), raceNumberInt);
+
+					startType = startTypeTextField.getText();
+					raceType = raceTypeTextField.getText();
+				} catch (final NumberFormatException e) {
+					System.err.print("Cant parse " + raceIdTextField.getText() + " as integer");
+				}
+			}
+		}
+	}
+}

+ 127 - 0
ATG/src/parsers/AtgParser.java

@@ -0,0 +1,127 @@
+package parsers;
+
+import java.io.IOException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import com.gargoylesoftware.htmlunit.FailingHttpStatusCodeException;
+import com.gargoylesoftware.htmlunit.WebClient;
+import com.gargoylesoftware.htmlunit.html.HtmlPage;
+import com.gargoylesoftware.htmlunit.html.HtmlSpan;
+import com.gargoylesoftware.htmlunit.html.HtmlTable;
+import com.gargoylesoftware.htmlunit.html.HtmlTableRow;
+import com.google.common.base.Strings;
+
+import controllers.DatabaseContoller;
+
+public class AtgParser implements Parser {
+
+	private AtgParser() {};
+
+	private static final AtgParser instance = new AtgParser();
+
+	private static final String defaultPath = "https://www.atg.se/spel/";
+
+	public static AtgParser getInstance() {
+		return instance;
+	}
+
+	/**
+	 *
+	 * @param type - V75, V65, ...
+	 */
+	public void getTodaysStartsFromATG(String type) {
+		final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
+		final String currentDate = dateFormat.format(new Date());
+		getAtgRaces(type, currentDate);
+	}
+
+	private void getAtgRaces(String type, final String date) {
+		final WebClient webclient = MyWebClient.getInstance().getWebclient();
+
+		final String url = defaultPath + date + "/" + type;
+
+		try {
+			final HtmlPage page = webclient.getPage(url);
+			webclient.waitForBackgroundJavaScript(10000);
+			webclient.waitForBackgroundJavaScriptStartingBefore(10000);
+
+			final List<HtmlSpan> spans = page.getByXPath("//span[contains(@class,'horse-name')]");
+
+			int i = 1;
+			for (final HtmlSpan hs : spans) {
+				final String horseName = hs.getTextContent().trim();
+				System.out.println(i++ + "/" + spans.size() + " Getting horse with name: " + horseName);
+				final int horseId = TravsportParser.getInstance().getHorseIdByName(horseName);
+				//				TravsportParser.getInstance().getHorseStats(type, "ts" + horseId);
+				TravsportParser.getInstance().getHorseStatByIdJson(horseId, horseName, type);
+			}
+		} catch (FailingHttpStatusCodeException | IOException e) {
+			// TODO Auto-generated catch block
+			e.printStackTrace();
+		}
+	}
+
+	public void getStartsForDay(String date, String type) {
+		if (!date.matches("\\d{4}-\\d{2}-\\d{2}")) {
+			System.out.println(date + " is incorrect, should be yyyy-MM-dd");
+			return;
+		}
+
+		getAtgRaces(type, date);
+	}
+
+	public void getResultsForDate(String type, String date) {
+		final WebClient webclient = MyWebClient.getInstance().getWebclient();
+		final String url = defaultPath + date + "/" + type + "/resultat";
+		try {
+			final HtmlPage page = webclient.getPage(url);
+			webclient.waitForBackgroundJavaScript(10000);
+			webclient.waitForBackgroundJavaScriptStartingBefore(10000);
+
+			final List<HtmlTable> tables = page.getByXPath("//table[@class='game-table']");
+
+			for (final HtmlTable htmlTable : tables) {
+				final List<HtmlTableRow> rows = htmlTable.getRows();
+				for (final HtmlTableRow tr : rows) {
+					final String placement = tr.getCell(0).getTextContent();
+
+					final int result = Strings.isNullOrEmpty(placement.replaceAll("\\D", ""))?-1:Integer.valueOf(placement.replaceAll("\\D", ""));
+
+					final String horseName = ((HtmlSpan)tr.getCell(1).getByXPath("//span[contains(@class,'horse-name'")).getTextContent();
+					final String driverName =  ((HtmlSpan)tr.getCell(2).getFirstChild()).getTextContent();
+
+					final String time = tr.getCell(5).getTextContent();
+
+					final String timeModifier = getTimeModifierFromTimeCellValue(time);
+					final float timeValue = getTimeFromTimeCellValue(time);
+
+					DatabaseContoller.getInstance().updateResult(horseName, driverName, result,timeModifier, timeValue, date, type);
+
+				}
+			}
+
+		} catch (FailingHttpStatusCodeException | IOException e) {
+			e.printStackTrace();
+		}
+
+	}
+
+	@Override
+	public float getTimeFromTimeCellValue(String timeCell) {
+		final float returnValue;
+		final Pattern timePattern = Pattern.compile("\\d\\.\\d{0,2}");
+		final Matcher matcher = timePattern.matcher(timeCell);
+		if (matcher.find()) {
+			returnValue = Float.valueOf(matcher.group());
+		} else {
+			returnValue = -1f;
+		}
+
+		return returnValue;
+	}
+
+}

+ 57 - 0
ATG/src/parsers/MyWebClient.java

@@ -0,0 +1,57 @@
+package parsers;
+
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import com.gargoylesoftware.htmlunit.BrowserVersion;
+import com.gargoylesoftware.htmlunit.WebClient;
+
+public class MyWebClient {
+	private static final String[] DISALLOWED_URLS = {
+			"*push.atg.se/*"
+	};
+
+	private MyWebClient() {
+
+	}
+
+	private static final MyWebClient instance = new MyWebClient();
+
+	public static MyWebClient getInstance() {
+		return instance;
+	}
+
+	public WebClient getWebclient() {
+		final WebClient webClient = new WebClient(BrowserVersion.CHROME);
+		webClient.getOptions().setUseInsecureSSL(true);
+		webClient.getOptions().setCssEnabled(true);
+		webClient.getOptions().setJavaScriptEnabled(true);
+		webClient.getOptions().setThrowExceptionOnScriptError(false);
+		webClient.getOptions().setActiveXNative(true);
+
+		//		webClient.getCache().setMaxSize(5);
+
+		webClient.getOptions().setUseInsecureSSL(true);
+		webClient.getOptions().setDoNotTrackEnabled(true);
+
+		Logger.getLogger("com.gargoylesoftware").setLevel(Level.OFF);
+
+		webClient.waitForBackgroundJavaScript(3000);
+		return webClient;
+	}
+
+	public WebClient getJsonWebClient() {
+		final WebClient webClient = new WebClient(BrowserVersion.CHROME);
+		webClient.getOptions().setUseInsecureSSL(true);
+		webClient.getOptions().setCssEnabled(false);
+		webClient.getOptions().setJavaScriptEnabled(false);
+		webClient.getOptions().setThrowExceptionOnScriptError(false);
+		webClient.getOptions().setActiveXNative(false);
+
+		Logger.getLogger("com.gargoylesoftware").setLevel(Level.OFF);
+
+		return webClient;
+
+	}
+
+}

+ 51 - 0
ATG/src/parsers/Parser.java

@@ -0,0 +1,51 @@
+package parsers;
+
+import java.text.SimpleDateFormat;
+import java.time.LocalDate;
+import java.time.ZoneId;
+import java.time.format.DateTimeFormatter;
+import java.time.temporal.TemporalAdjusters;
+import java.util.Date;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public interface Parser {
+
+	public static final String df = "yyyy-MM-dd";
+	static final Pattern timePattern = Pattern.compile("\\d+[,]{0,1}\\d+");
+	static final Pattern timeModifierPattern = Pattern.compile("[A-Za-z]+$");
+
+	public static SimpleDateFormat DATE_FORMAT = new SimpleDateFormat(df);
+
+	default String getTimeModifierFromTimeCellValue(final String timeCell) {
+		final String timeModifier;
+		final Matcher timeModifierMatcher = timeModifierPattern.matcher(timeCell);
+		if (timeModifierMatcher.find()) {
+			timeModifier = timeModifierMatcher.group();
+		} else {
+			timeModifier = "";
+		}
+		return timeModifier;
+	}
+
+	default float getTimeFromTimeCellValue(final String timeCell) {
+		final float time;
+		final Matcher timeMatcher = timePattern.matcher(timeCell);
+		if (timeMatcher.find()) {
+			time = Float.valueOf(timeMatcher.group().replace(",", "."));
+		} else {
+			time = -1f;
+		}
+		return time;
+	}
+
+	default String getTodaysDate() {
+		return DATE_FORMAT.format(new Date());
+	}
+
+	default String getLastDayOfMonth() {
+		final LocalDate lastDayOfMonth = LocalDate.parse(getTodaysDate(), DateTimeFormatter.ofPattern(df)).with(TemporalAdjusters.lastDayOfMonth());
+
+		return DATE_FORMAT.format(Date.from(lastDayOfMonth.atStartOfDay().atZone(ZoneId.systemDefault()).toInstant()));
+	}
+}

+ 542 - 0
ATG/src/parsers/TravsportParser.java

@@ -0,0 +1,542 @@
+package parsers;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
+
+import com.gargoylesoftware.htmlunit.FailingHttpStatusCodeException;
+import com.gargoylesoftware.htmlunit.Page;
+import com.gargoylesoftware.htmlunit.WebClient;
+import com.gargoylesoftware.htmlunit.html.DomElement;
+import com.gargoylesoftware.htmlunit.html.DomNodeList;
+import com.gargoylesoftware.htmlunit.html.HtmlDivision;
+import com.gargoylesoftware.htmlunit.html.HtmlElement;
+import com.gargoylesoftware.htmlunit.html.HtmlHeading1;
+import com.gargoylesoftware.htmlunit.html.HtmlPage;
+import com.gargoylesoftware.htmlunit.html.HtmlSpan;
+import com.gargoylesoftware.htmlunit.html.HtmlTable;
+import com.gargoylesoftware.htmlunit.html.HtmlTableCell;
+import com.google.common.base.Strings;
+import com.google.common.collect.Lists;
+import com.google.gson.Gson;
+import com.google.gson.JsonArray;
+import com.google.gson.JsonObject;
+
+import controllers.DatabaseContoller;
+
+public class TravsportParser implements Parser {
+
+	private static final TravsportParser instance = new TravsportParser();
+	private final DatabaseContoller databaseController;
+
+	private TravsportParser() {
+		databaseController = DatabaseContoller.getInstance();
+	};
+
+	public static TravsportParser getInstance() {
+		return instance;
+	}
+
+	/**
+	 * Förklaringar
+	 * tid - 'a' betyder "autoStart" vilket get snabbare tid
+	 * tid - 'g' betyder "gallopp" vilker ger långsammare tid
+	 * tid - 'd' betyder "Diskad" två galopper eller gallopp över mållinje eller längre än 150 meter
+	 * tid - 'it' betyder "Ingen tid" kan vara fel på banans tidtagning eller liknande så det finns ingen tid.
+	 * @param horseId
+	 */
+	public void getHorseStats(String raceType, String horseId) {
+		final WebClient webclient = MyWebClient.getInstance().getWebclient();
+		HtmlPage page;
+		try {
+			page = webclient.getPage("https://sportapp.travsport.se/sportinfo/horse/"+horseId+"/results");
+			webclient.waitForBackgroundJavaScript(3000);
+			webclient.waitForBackgroundJavaScriptStartingBefore(3000);
+			// Hitta div med data-testid="sportinfo-horse-details" för häst info
+
+			final HtmlDivision detailsDiv = (HtmlDivision) page.getBody().getByXPath("//div[@data-testid='sportinfo-horse-details']").get(0);
+
+			final HtmlHeading1 nameHeader = (HtmlHeading1)detailsDiv.getByXPath("//h1").get(0);
+			final String horseName = nameHeader.getTextContent().trim();
+			// Hitta div med data-testid="sportinfo-horse-results" för årliga resultat är första tabellen, andra tabellen är lopp resultat
+			final HtmlDivision resultsDiv = (HtmlDivision) page.getBody().getByXPath("//div[@data-testid='sportinfo-horse-results']").get(0);
+			final ArrayList<DomElement> resultsDivs = Lists.newArrayList(resultsDiv.getChildElements());
+
+			final HtmlDivision raceResults = (HtmlDivision) resultsDivs.get(4);
+
+			final HtmlTable raceTable = (HtmlTable) raceResults.getFirstChild();
+
+			final List<HtmlTableCell> tableHeader = raceTable.getRow(0).getCells();
+
+			int trackPos = -1;
+			int datePos = -1;
+			int lanePos = -1;
+			int distancePos = -1;
+			int resultPos = -1;
+			int timePos = -1;
+			int shoePos = -1;
+			int sulkyPos = -1;
+			int driverPos = -1;
+			int trainerPos = -1;
+			int oddsPos = -1;
+			int pos = 0;
+			for (final HtmlTableCell th : tableHeader) {
+				final HtmlSpan span = (HtmlSpan) th.getFirstChild();
+				final String text = span.getTextContent().toLowerCase();
+				if (text.equals("bana")) {
+					trackPos = pos;
+				} else if (text.equals("datum")) {
+					datePos = pos;
+				} else if (text.equals("spår")) {
+					lanePos = pos;
+				} else if (text.equals("dist.")) {
+					distancePos = pos;
+				} else if (text.equals("res.")) {
+					resultPos = pos;
+				} else if (text.equals("tid")) {
+					timePos = pos;
+				} else if (text.equals("skor")) {
+					shoePos = pos;
+				} else if (text.equals("vagn")) {
+					sulkyPos = pos;
+				} else if (text.equals("kusk")) {
+					driverPos = pos;
+				} else if (text.equals("tränare")) {
+					trainerPos = pos;
+				} else if (text.equals("odds")) {
+					oddsPos = pos;
+				} else if (text.equals("kategori")) {
+					//
+				}  else if (text.equals("vinstsumma")) {
+					//
+				}  else {
+					System.err.println("What should I do with " + text);
+				}
+
+				pos++;
+			}
+
+			for (int i = 1; i < raceTable.getRowCount(); i++) {
+				final List<HtmlTableCell> cells = raceTable.getRow(i).getCells();
+				final String timeCell = cells.get(6).getTextContent();
+				final float time;
+				final String timeModifier;
+
+				time = getTimeFromTimeCellValue(timeCell);
+
+				timeModifier = getTimeModifierFromTimeCellValue(timeCell);
+
+				final String trackShortCode = cells.get(trackPos).getTextContent();
+				final String date = cells.get(datePos).getTextContent().split("-")[0];
+				final int raceNumber = Integer.valueOf(cells.get(datePos).getTextContent().split("-")[1]);
+				final int startLine = Strings.isNullOrEmpty(cells.get(lanePos).getTextContent())?-1:Integer.valueOf(cells.get(lanePos).getTextContent());
+				final int distance = Integer.valueOf(cells.get(distancePos).getTextContent().replaceAll("\\D", ""));
+				final int result = Strings.isNullOrEmpty(cells.get(resultPos).getTextContent().replaceAll("\\D", ""))?-1:Integer.valueOf(cells.get(resultPos).getTextContent().replaceAll("\\D", ""));
+				final int shooes;
+				final DomNodeList<HtmlElement> shoeSpans = cells.get(shoePos).getElementsByTagName("span");
+				if (shoeSpans.getLength() == 0) { // NO SHOES
+					shooes = 0;
+				} else {
+					// show title possible values {"Barfota fram", "Skor runt om", "barfota bak", "barfota runt om"}
+					final String shoeTitle = shoeSpans.get(0).getAttribute("title").toLowerCase();
+					if (shoeTitle.equals("barfota fram")) {
+						shooes = 1;
+					} else if (shoeTitle.equals("skor runt om")) {
+						shooes = 2;
+					} else if (shoeTitle.equals("barfota runt om")){
+						shooes = 0;
+					} else if (shoeTitle.equals("barfota bak")) {
+						shooes = 3;
+					} else {
+						shooes = -1;
+						System.err.println("Dont know the value of " + shoeTitle);
+					}
+				}
+
+				final String driverName = cells.get(driverPos).getTextContent();
+				final String trainerName = cells.get(trainerPos).getTextContent();
+
+				DatabaseContoller.getInstance().insertResult(trackShortCode, date, raceNumber, startLine, distance, result, time, timeModifier, shooes, -1, driverName, trainerName, horseName, 0, 0, 0, raceType, -1);
+			}
+
+		} catch (final ClassCastException e) {
+			System.out.println("Failed to cast " + e.getMessage());
+		} catch (FailingHttpStatusCodeException | IOException e) {
+			e.printStackTrace();
+		} catch (final Exception e) {
+			System.err.println("Failed with exception " + e.getMessage() + " Horse id " + horseId);
+		}
+		webclient.close();
+	}
+
+
+	// Get raceDayResultsJSon https://api.travsport.se/webapi/raceinfo/results/organisation/TROT/sourceofdata/SPORT/racedayid/590412
+	public void getRaceCalendar() {
+		// JSON för race dagen https://api.travsport.se/webapi/raceinfo/startlists/organisation/TROT/sourceofdata/SPORT/racedayid/590412
+		// JSON för calendern https://api.travsport.se/webapi/raceinfo/organisation/TROT/sourceofdata/BOTH?fromracedate=2021-02-01&tosubmissiondate=2021-02-28&toracedate=2021-02-28
+
+		final String calendarUrl = "https://api.travsport.se/webapi/raceinfo/organisation/TROT/sourceofdata/BOTH?fromracedate="+getTodaysDate()+"&tosubmissiondate="+getLastDayOfMonth()+"&toracedate=" + getLastDayOfMonth();
+		final String raceUrl = "https://api.travsport.se/webapi/raceinfo/startlists/organisation/TROT/sourceofdata/SPORT/racedayid/";
+
+		final ArrayList<JsonObject> jsonObjects = getJsonObjects(calendarUrl);
+
+		final List<JsonObject> withStartlist = jsonObjects.stream().filter(j -> j.get("trackProgramExists").getAsBoolean()).collect(Collectors.toList());
+
+		for (final JsonObject jo : withStartlist) {
+			final int raceDayId = jo.get("raceDayId").getAsInt();
+			final String raceDate = jo.get("raceDayDate").getAsString();
+			final String trackName = jo.get("trackName").getAsString();
+			final String trackCode = databaseController.getTrackShortCodeByName(trackName);
+
+			final ArrayList<JsonObject> raceJsonObjects = Lists.newArrayList();
+			try {
+				final Page page = MyWebClient.getInstance().getJsonWebClient().getPage(raceUrl + raceDayId);
+				final String content = new String(page.getWebResponse().getContentAsString()); //.getBytes("ISO-8859-1"), "UTF-8");
+				//				final String content = page.getWebResponse().getContentAsString();
+				final Gson gson = new Gson();
+				final JsonObject raceObject = gson.fromJson(content, JsonObject.class);
+				final JsonArray raceListData = raceObject.get("raceList").getAsJsonArray();
+				raceListData.forEach(e -> raceJsonObjects.add(e.getAsJsonObject()));
+
+				for (final JsonObject race : raceJsonObjects) {
+					final int raceNumber = race.get("raceNumber").getAsInt();
+					final int raceId = race.get("raceId").getAsInt();
+					final int distance = race.get("distance").getAsInt();
+					final String raceStartType = ""; //race.get("raceType").getAsJsonObject().get("code").getAsString();
+					final JsonArray horses = race.get("horses").getAsJsonArray();
+					final ArrayList<JsonObject> horseData = Lists.newArrayList();
+
+					horses.forEach(h -> horseData.add(h.getAsJsonObject()));
+					for (final JsonObject horse : horseData) {
+						final int horseTravsportId = horse.get("id").getAsInt();
+						final String horseName = horse.get("name").getAsString();
+						final JsonObject driverObject = horse.get("driver").getAsJsonObject();
+						final String driverName = driverObject.get("name").getAsString();
+						final int driverId = driverObject.get("licenseId").getAsInt();
+						final JsonObject trainerObject = horse.get("trainer").getAsJsonObject();
+						final String trainerName = trainerObject.get("name").getAsString();
+						final int trainerId = trainerObject.get("licenseId").getAsInt();
+						final JsonObject ownerObject = horse.get("owner").getAsJsonObject();
+						final String ownerName = ownerObject.get("name").getAsString();
+						final int ownerId = ownerObject.get("licenseId").getAsInt();
+						final int lane = horse.get("startPosition").getAsInt();
+						final int actualDistance = horse.get("actualDistance").getAsInt();
+						final int shoe = horse.get("shoeOption").getAsJsonObject().get("code").getAsInt();
+
+						databaseController.insertResult(trackCode, raceDate, raceNumber, lane, actualDistance, -2, -1, raceStartType, shoe, -1, driverName, trainerName, horseName, horseTravsportId, trainerId, driverId, "", raceDayId);
+						final int horseIdByName = databaseController.getHorseId(horseName, horseTravsportId);
+						final int horseRaceCount = databaseController.getRaceCount("Horse", horseIdByName);
+						if (horseRaceCount <= 2) {
+							getHorseStatByIdJson(horseTravsportId, horseName, "");
+						}
+
+						final int driverIdByName = databaseController.getDriverId(driverName, driverId);
+						final int driverRaceCount = databaseController.getRaceCount("Driver", driverIdByName);
+						if (driverRaceCount <= 20) {
+							getDriverStatById(driverId, driverName);
+						}
+					}
+				}
+			} catch (FailingHttpStatusCodeException | IOException e1) {
+				// TODO Auto-generated catch block
+				e1.printStackTrace();
+			}
+
+		}
+
+
+		// Xpath data-testid = racecalendar-button-startlist
+
+	}
+
+	public int getHorseIdByName(String horseName) {
+		/*
+		 * Uppdatera med detta anrop, kommer tillbaka en JSON sträng som man kan ta utan css och allt.
+		 * https://api.travsport.se/webapi/horses/results/organisation/TROT/sourceofdata/SPORT/horseid/781845
+		 *
+		 * */
+		/* Search String https://api.travsport.se/webapi/horses/search/organisation/TROT?age=0&gender=BOTH&horseName=molly&trotBreed=ALL&autoSuffixWildcard=true
+
+		Svarar med lista ex.
+		[{"organisation":"TROT","sourceOfData":"SPORT","horseId":58291,"name":"GINA II","yearOfBirth":"1945","horseGender":{"code":"S","text":"sto"},"horseBreed":{"code":"K","text":"kallblodig travare"}},
+		{"organisation":"TROT","sourceOfData":"SPORT","horseId":58264,"name":"GINA","yearOfBirth":"1935","horseGender":{"code":"S","text":"sto"},"horseBreed":{"code":"K","text":"kallblodig travare"}}]
+		 */
+
+		int horseId = DatabaseContoller.getInstance().getHorseTravsportId(horseName);
+		if (horseId <= 0) {
+			System.out.println("Getting horse id from travsport");
+			final String url = "https://api.travsport.se/webapi/horses/search/organisation/TROT?age=0&gender=BOTH&horseName="+horseName+"&trotBreed=ALL&autoSuffixWildcard=false";
+
+			final WebClient webclient = MyWebClient.getInstance().getWebclient();
+			try {
+
+				final Page page = webclient.getPage(url);
+
+				final String content = page.getWebResponse().getContentAsString();
+				final Gson gson = new Gson();
+				final JsonArray fromJson = gson.fromJson(content, JsonArray.class);
+				final ArrayList<JsonObject> jsonObjects = Lists.newArrayList();
+				fromJson.forEach(e -> jsonObjects.add(e.getAsJsonObject()));
+
+				Collections.sort(jsonObjects, new Comparator<JsonObject>() {
+
+					@Override
+					public int compare(JsonObject o1, JsonObject o2) {
+						return o2.get("horseId").getAsString().compareTo(o1.get("horseId").getAsString());
+					}
+
+				});
+				horseId = jsonObjects.get(0).get("horseId").getAsInt();
+				DatabaseContoller.getInstance().setHorseTravsportId(Integer.valueOf(horseId), horseName);
+			} catch (FailingHttpStatusCodeException | IOException e) {
+				e.printStackTrace();
+			} catch (final IndexOutOfBoundsException e) {
+				System.out.println("Horse with name " + horseName + " not found at travsport");
+			}
+
+		}
+		return horseId;
+	}
+
+	private ArrayList<JsonObject> getJsonObjects(String url) {
+		final ArrayList<JsonObject> jsonObjects = Lists.newArrayList();
+		try (WebClient wc = MyWebClient.getInstance().getJsonWebClient();){
+
+			final Page page = wc.getPage(url);
+			final String content = page.getWebResponse().getContentAsString();
+			final Gson gson = new Gson();
+			final JsonArray fromJson = gson.fromJson(content, JsonArray.class);
+			fromJson.forEach(e -> jsonObjects.add(e.getAsJsonObject()));
+		} catch (FailingHttpStatusCodeException | IOException e) {
+			// TODO Auto-generated catch block
+			e.printStackTrace();
+		}
+		return jsonObjects;
+	}
+
+	public void getHorseStatByIdJson(int horseTravsportId, String horseName, String raceType) {
+		final String url = "https://api.travsport.se/webapi/horses/results/organisation/TROT/sourceofdata/SPORT/horseid/" + horseTravsportId;
+
+		final ArrayList<JsonObject> jsonObjects = getJsonObjects(url);
+		int i = 0;
+		final int rows = jsonObjects.size();
+		for (final JsonObject jo : jsonObjects) {
+			final String trackCode = jo.get("trackCode").getAsString();
+
+			final JsonObject raceInfo = jo.get("raceInformation").getAsJsonObject();
+			final String raceDate = raceInfo.get("date").getAsString();
+			final int raceNumber = raceInfo.get("raceNumber").getAsInt();
+
+			int lane;
+			try {
+				lane = Integer.valueOf(jo.get("startPosition").getAsJsonObject().get("displayValue").getAsString());
+			} catch (final NumberFormatException e) {
+				lane = -1;
+			}
+
+			final int distance = Integer.valueOf(jo.get("distance").getAsJsonObject().get("displayValue").getAsString());
+
+			final String timeValue = jo.get("kilometerTime").getAsJsonObject().get("displayValue").getAsString();
+
+			final float time = getTimeFromTimeCellValue(timeValue);
+			final String timeModifier = getTimeModifierFromTimeCellValue(timeValue);
+
+			if (jo.get("shoeInfo") != null) {
+				final JsonObject shoeInfo = jo.get("shoeInfo").getAsJsonObject();
+				final boolean frontShoe = shoeInfo.get("front").getAsBoolean();
+				final boolean backShoe = shoeInfo.get("back").getAsBoolean();
+				// konvertera till värdet som det ska vara...
+			}
+
+			final JsonObject placementInfo = jo.get("placement").getAsJsonObject();
+			final String resultString = placementInfo.get("displayValue").getAsString();
+			final int result = Strings.isNullOrEmpty(resultString.replaceAll("\\D", ""))?-1:Integer.valueOf(resultString.replaceAll("\\D", ""));
+
+			final JsonObject driverInfo = jo.get("driver").getAsJsonObject();
+			final int driverId = driverInfo.get("id").getAsInt();
+			final String driverName = driverInfo.get("name").getAsString();
+
+			final JsonObject trainerInfo = jo.get("trainer").getAsJsonObject();
+			final int trainerId = trainerInfo.get("id").getAsInt();
+			final String trainerName = trainerInfo.get("name").getAsString();
+
+			//				final String sulkyString = jo.get("suklyOptions").getAsJsonObject().get("description").getAsString();
+
+			DatabaseContoller.getInstance().insertResult(trackCode, raceDate, raceNumber, lane, distance, result, time, timeModifier, -1, -1, driverName, trainerName, horseName, horseTravsportId, trainerId, driverId, raceType, -1);
+			System.out.println("Done inserting result for horse " + ++i + "/" + rows);
+		}
+	}
+
+	public void getDriverStatById(int driverTravsportId, String driverName) {
+		// https://api.travsport.se/webapi/licenseholder/drivers/results/organisation/TROT/sourceofdata/SPORT/driverid/70381 JSON resultat
+
+		final String url = "https://api.travsport.se/webapi/licenseholder/drivers/results/organisation/TROT/sourceofdata/SPORT/driverid/" + driverTravsportId;
+		final WebClient wc = MyWebClient.getInstance().getJsonWebClient();
+
+		try {
+			final Page page = wc.getPage(url);
+
+			final String content = page.getWebResponse().getContentAsString();
+			final Gson gson = new Gson();
+			final JsonArray fromJson = gson.fromJson(content, JsonArray.class);
+			final ArrayList<JsonObject> jsonObjects = Lists.newArrayList();
+			fromJson.forEach(e -> jsonObjects.add(e.getAsJsonObject()));
+
+			int i = 0;
+			final int rows = jsonObjects.size();
+			for (final JsonObject jo : jsonObjects) {
+
+				final String trackCode = jo.get("trackCode").getAsString();
+
+				final JsonObject raceInfo = jo.get("raceInformation").getAsJsonObject();
+				final String raceDate = raceInfo.get("date").getAsString();
+				final int raceNumber = raceInfo.get("raceNumber").getAsInt();
+				int lane;
+				try {
+					lane = Integer.valueOf(jo.get("startPosition").getAsJsonObject().get("displayValue").getAsString());
+				} catch (final NumberFormatException e) {
+					lane = -1;
+				}
+				final int distance = Integer.valueOf(jo.get("distance").getAsJsonObject().get("displayValue").getAsString());
+
+				final String timeValue = jo.get("kilometerTime").getAsJsonObject().get("displayValue").getAsString();
+				final float time = getTimeFromTimeCellValue(timeValue);
+				final String timeModifier = getTimeModifierFromTimeCellValue(timeValue);
+
+				final String resultString = jo.get("placement").getAsJsonObject().get("displayValue").getAsString();
+				final int result = Strings.isNullOrEmpty(resultString.replaceAll("\\D", ""))?-1:Integer.valueOf(resultString.replaceAll("\\D", ""));
+
+				final JsonObject horseObject = jo.get("horse").getAsJsonObject();
+				final String horseName = horseObject.get("name").getAsString();
+				final int travsportIdHorse = horseObject.get("id").getAsInt();
+				String trainerName = "";
+				int trainerId = -1;
+				if (jo.get("trainer") != null) {
+					final JsonObject trainerObject = jo.get("trainer").getAsJsonObject();
+					trainerName = trainerObject.get("name").getAsString();
+					trainerId = trainerObject.get("id").getAsInt();
+				}
+
+				DatabaseContoller.getInstance().insertResult(trackCode, raceDate, raceNumber, lane, distance, result, time, timeModifier, -1, -1, driverName, trainerName, horseName, travsportIdHorse, trainerId, driverTravsportId, "", -1);
+				System.out.println("Done inserting result for driver " + ++i + "/" + rows);
+			}
+
+		} catch (FailingHttpStatusCodeException | IOException e) {
+			// TODO Auto-generated catch block
+			e.printStackTrace();
+		}
+	}
+
+	public int getDriverIdByName(String firstName, String lastName) {
+		System.out.println("Getting driver id from travsport by name " + firstName + " " + lastName);
+		final String url = "https://api.travsport.se/webapi/licenseholder/search/organisation/TROT?firstName="+firstName+"&lastName="+lastName+"&showAll=false";
+		int driverId = 0;
+		final WebClient webclient = MyWebClient.getInstance().getJsonWebClient();
+		try {
+
+			final Page page = webclient.getPage(url);
+
+			final String content = page.getWebResponse().getContentAsString();
+			final Gson gson = new Gson();
+			final JsonArray fromJson = gson.fromJson(content, JsonArray.class);
+			final ArrayList<JsonObject> jsonObjects = Lists.newArrayList();
+			fromJson.forEach(e -> jsonObjects.add(e.getAsJsonObject()));
+
+			Collections.sort(jsonObjects, new Comparator<JsonObject>() {
+
+				@Override
+				public int compare(JsonObject o1, JsonObject o2) {
+					return o2.get("id").getAsString().compareTo(o1.get("id").getAsString());
+				}
+
+			});
+			driverId = jsonObjects.get(0).get("id").getAsInt();
+			DatabaseContoller.getInstance().setDriverTravsportId(driverId, firstName, lastName);
+
+		} catch (FailingHttpStatusCodeException | IOException e) {
+			e.printStackTrace();
+		} catch (final IndexOutOfBoundsException e) {
+			System.out.println("Driver with name " + firstName + " " + lastName + " not found at travsport");
+		}
+		return driverId;
+	}
+
+	public void updateRaceResults(int raceId) {
+		// https://api.travsport.se/webapi/raceinfo/results/organisation/TROT/sourceofdata/SPORT/racedayid/<raceId>
+		final String url = "https://api.travsport.se/webapi/raceinfo/results/organisation/TROT/sourceofdata/SPORT/racedayid/" + raceId;
+
+		try {
+			final Page page = MyWebClient.getInstance().getJsonWebClient().getPage(url);
+			final String content = new String(page.getWebResponse().getContentAsString()); //.getBytes("ISO-8859-1"), "UTF-8");
+			final Gson gson = new Gson();
+			final JsonObject jo = gson.fromJson(content, JsonObject.class);
+
+			final ArrayList<JsonObject> jsonObjects = Lists.newArrayList();
+			final JsonArray results = jo.get("racesWithReadyResult").getAsJsonArray();
+			results.forEach(j -> jsonObjects.add(j.getAsJsonObject()));
+
+			for (final JsonObject json : jsonObjects) {
+				final JsonObject generalInfo = json.get("generalInfo").getAsJsonObject();
+				final int raceNumber = generalInfo.get("raceNumber").getAsInt();
+
+				final String startTime = generalInfo.get("startTime").getAsString();
+				final Pattern pattern = Pattern.compile("\\d{4}-\\d{2}-\\d{2}");
+				final Matcher matcher = pattern.matcher(startTime);
+				String raceDate;
+				if (matcher.find()) {
+					raceDate = matcher.group();
+				} else {
+					throw new RuntimeException("Could not find date from " + startTime);
+				}
+
+				try {
+					final JsonArray raceResultRows = json.get("raceResultRows").getAsJsonArray();
+
+					for (int i = 0; i < raceResultRows.size(); i++) {
+						final JsonObject res = raceResultRows.get(i).getAsJsonObject();
+						final int result = res.get("placementNumber").getAsInt();
+						final JsonObject horse = res.get("horse").getAsJsonObject();
+						final int horseTravsportId = horse.get("id").getAsInt();
+						final String horseName = horse.get("name").getAsString();
+
+						final JsonObject driver = res.get("driver").getAsJsonObject();
+						final int driverTravsportId = driver.get("id").getAsInt();
+						final String driverName = driver.get("name").getAsString();
+
+						final JsonObject trainer = res.get("trainer").getAsJsonObject();
+						final int trainerTravsportId = trainer.get("id").getAsInt();
+						final String trainerName = trainer.get("name").getAsString();
+						int shoeInfo = 0;
+						try {
+							shoeInfo = res.get("shoeInfo").getAsJsonObject().get("sortValue").getAsInt();
+						} catch (final NullPointerException e) {
+							// ignore
+						}
+
+						final String timeString = res.get("time").getAsString();
+						final float time = getTimeFromTimeCellValue(timeString);
+						final String timeModifier = getTimeModifierFromTimeCellValue(timeString);
+
+						final String[] startPosAndDistance = res.get("startPositionAndDistance").getAsString().split("/");
+						final int lane = Integer.valueOf(startPosAndDistance[0]);
+						final int distance = Integer.valueOf(startPosAndDistance[1]);
+
+						DatabaseContoller.getInstance().updateFromRaceResults(raceId, result, time, timeModifier, lane, distance, horseTravsportId, driverTravsportId, raceDate, raceNumber);
+						System.out.println("Updated raceId " + raceId + " horse " + horseName + " driver " + driverName + " at date " + raceDate + " with result " + result + " time " + time + " time mod " + timeModifier);
+					}
+				} catch (final NullPointerException e) {
+					continue;
+				}
+			}
+		} catch (FailingHttpStatusCodeException | IOException e) {
+			// TODO Auto-generated catch block
+			e.printStackTrace();
+		}
+	}
+
+}