]> gitweb.michael.orlitzky.com - dead/htsn-import.git/blobdiff - src/TSN/XML/JFile.hs
Update JFile to have a direct relationship between the games/teams.
[dead/htsn-import.git] / src / TSN / XML / JFile.hs
index 5642c8957dc8b566902c15b09b3875d3f8959e1f..a0fa43297f3c376fec8fae6644417afd434bc687 100644 (file)
@@ -18,8 +18,7 @@ module TSN.XML.JFile (
   jfile_tests,
   -- * WARNING: these are private but exported to silence warnings
   JFileConstructor(..),
-  JFileGameConstructor(..),
-  JFileGame_TeamConstructor(..) )
+  JFileGameConstructor(..) )
 where
 
 -- System imports
@@ -31,7 +30,6 @@ import Data.Tuple.Curry ( uncurryN )
 import Database.Groundhog (
   countAll,
   deleteAll,
-  insert_,
   migrate,
   runMigration,
   silentMigrationLogger )
@@ -74,10 +72,11 @@ import TSN.Picklers (
 import TSN.Team ( Team(..) )
 import TSN.XmlImport (
   XmlImport(..),
-  XmlImportFk(..) )
+  XmlImportFkTeams(..) )
 import Xml (
+  Child(..),
   FromXml(..),
-  FromXmlFk(..),
+  FromXmlFkTeams(..),
   ToDb(..),
   pickle_unpickle,
   unpickleable,
@@ -157,8 +156,8 @@ instance XmlImport Message
 data JFileGameAwayTeamXml =
   JFileGameAwayTeamXml {
     away_team_id :: String,
-    away_team_abbreviation :: String,
-    away_team_name :: String }
+    away_team_abbreviation :: Maybe String,
+    away_team_name :: Maybe String }
   deriving (Eq, Show)
 
 instance ToDb JFileGameAwayTeamXml where
@@ -174,8 +173,8 @@ instance FromXml JFileGameAwayTeamXml where
   from_xml JFileGameAwayTeamXml{..} =
     Team {
       team_id = away_team_id,
-      team_abbreviation = away_team_abbreviation,
-      team_name  = away_team_name }
+      abbreviation = away_team_abbreviation,
+      name  = away_team_name }
 
 -- | Allow us to import JFileGameAwayTeamXml directly.
 instance XmlImport JFileGameAwayTeamXml
@@ -187,8 +186,8 @@ instance XmlImport JFileGameAwayTeamXml
 data JFileGameHomeTeamXml =
   JFileGameHomeTeamXml {
     home_team_id :: String,
-    home_team_abbreviation :: String,
-    home_team_name :: String }
+    home_team_abbreviation :: Maybe String,
+    home_team_name :: Maybe String }
   deriving (Eq, Show)
 
 instance ToDb JFileGameHomeTeamXml where
@@ -204,8 +203,8 @@ instance FromXml JFileGameHomeTeamXml where
   from_xml JFileGameHomeTeamXml{..} =
     Team {
       team_id = home_team_id,
-      team_abbreviation = home_team_abbreviation,
-      team_name  = home_team_name }
+      abbreviation = home_team_abbreviation,
+      name  = home_team_name }
 
 -- | Allow us to import JFileGameHomeTeamXml directly.
 instance XmlImport JFileGameHomeTeamXml
@@ -223,11 +222,14 @@ instance XmlImport JFileGameHomeTeamXml
 --   All of these are optional because TSN does actually leave the
 --   whole thing empty from time to time.
 --
+--   We stick \"info\" on the home/away team ids to avoid a name clash
+--   with the game itself.
+--
 data JFileGameOddsInfo =
   JFileGameOddsInfo {
     db_list_date :: Maybe UTCTime,
-    db_home_team_id :: Maybe String, -- redundant (Team)
-    db_away_team_id :: Maybe String, -- redundant (Team)
+    db_info_home_team_id :: Maybe String, -- redundant (Team)
+    db_info_away_team_id :: Maybe String, -- redundant (Team)
     db_home_abbr :: Maybe String, -- redundant (Team)
     db_away_abbr :: Maybe String, -- redundant (Team)
     db_home_team_name :: Maybe String, -- redundant (Team)
@@ -263,6 +265,8 @@ data JFileGameStatus =
 data JFileGame =
   JFileGame {
     db_jfile_id :: DefaultKey JFile,
+    db_away_team_id :: DefaultKey Team,
+    db_home_team_id :: DefaultKey Team,
     db_game_id :: Int,
     db_schedule_id :: Int,
     db_odds_info :: JFileGameOddsInfo,
@@ -320,19 +324,24 @@ instance ToDb JFileGameXml where
   --
   type Db JFileGameXml = JFileGame
 
-instance FromXmlFk JFileGameXml where
+
+instance Child JFileGameXml where
   -- | Each 'JFileGameXml' is contained in (i.e. has a foreign key to)
   --   a 'JFile'.
   --
   type Parent JFileGameXml = JFile
 
+
+instance FromXmlFkTeams JFileGameXml where
   -- | To convert an 'JFileGameXml' to an 'JFileGame', we add the
-  --   foreign key and drop the 'xml_vteam'/'xml_hteam'. We also mash
+  --   foreign keys for JFile and the home/away teams. We also mash
   --   the date/time together into one field.
   --
-  from_xml_fk fk JFileGameXml{..} =
+  from_xml_fk_teams fk fk_away fk_home JFileGameXml{..} =
     JFileGame {
       db_jfile_id = fk,
+      db_away_team_id = fk_away,
+      db_home_team_id = fk_home,
       db_game_id = xml_game_id,
       db_schedule_id = xml_schedule_id,
       db_odds_info = xml_odds_info,
@@ -355,18 +364,7 @@ instance FromXmlFk JFileGameXml where
 -- | This allows us to insert the XML representation
 --   'JFileGameXml' directly.
 --
-instance XmlImportFk JFileGameXml
-
-
--- * JFileGame_Team
-
--- | Database mapping between games and their home/away teams.
---
-data JFileGame_Team =
-  JFileGame_Team {
-    jgt_jfile_games_id :: DefaultKey JFileGame,
-    jgt_away_team_id  :: DefaultKey Team,
-    jgt_home_team_id  :: DefaultKey Team }
+instance XmlImportFkTeams JFileGameXml
 
 
 ---
@@ -379,7 +377,6 @@ instance DbImport Message where
       migrate (undefined :: Team)
       migrate (undefined :: JFile)
       migrate (undefined :: JFileGame)
-      migrate (undefined :: JFileGame_Team)
 
   dbimport m = do
     -- Insert the top-level message
@@ -387,22 +384,12 @@ instance DbImport Message where
 
     -- Now loop through the message's games
     forM_ (xml_games $ xml_gamelist m) $ \game -> do
-
-      -- Next, we insert the home and away teams. We do this before
-      -- inserting the game itself because the game has two foreign keys
-      -- pointing to "teams".
+      -- First we insert the home and away teams.
       away_team_id <- insert_xml_or_select (xml_vteam game)
       home_team_id <- insert_xml_or_select (xml_hteam game)
 
-      game_id <- insert_xml_fk msg_id game
-
-      -- Insert a record into jfile_games__teams mapping the
-      -- home/away teams to this game. Use the full record syntax
-      -- because the types would let us mix up the home/away teams.
-      insert_ JFileGame_Team {
-                jgt_jfile_games_id = game_id,
-                jgt_away_team_id = away_team_id,
-                jgt_home_team_id = home_team_id }
+            -- First insert the game, keyed to the "jfile",
+      insert_xml_fk_teams_ msg_id away_team_id home_team_id game
 
 
     return ImportSucceeded
@@ -453,6 +440,12 @@ mkPersist tsn_codegen_config [groundhog|
         - name: db_jfile_id
           reference:
             onDelete: cascade
+        - name: db_away_team_id
+          reference:
+            onDelete: cascade
+        - name: db_home_team_id
+          reference:
+            onDelete: cascade
         - name: db_odds_info
           embeddedType:
             - {name: list_date, dbName: list_date}
@@ -468,20 +461,6 @@ mkPersist tsn_codegen_config [groundhog|
             - {name: status_numeral, dbName: status_numeral}
             - {name: status, dbName: status}
 
-- entity: JFileGame_Team
-  dbName: jfile_games__teams
-  constructors:
-    - name: JFileGame_Team
-      fields:
-        - name: jgt_jfile_games_id
-          reference:
-            onDelete: cascade
-        - name: jgt_away_team_id
-          reference:
-            onDelete: cascade
-        - name: jgt_home_team_id
-          reference:
-            onDelete: cascade
 |]
 
 
@@ -587,8 +566,8 @@ pickle_odds_info =
         notes = intercalate "\n" [n1,n2,n3,n4,n5]
 
     to_tuple o = (db_list_date o,
-                  db_home_team_id o,
-                  db_away_team_id o,
+                  db_info_home_team_id o,
+                  db_info_away_team_id o,
                   db_home_abbr o,
                   db_away_abbr o,
                   db_home_team_name o,
@@ -624,8 +603,8 @@ pickle_home_team =
   xpElem "hteam" $
     xpWrap (from_tuple, to_tuple) $
     xpTriple (xpAttr "teamid" xpText) -- Yeah, they're text.
-             (xpAttr "abbr" xpText)
-             xpText
+             (xpAttr "abbr" (xpOption xpText)) -- Some are blank
+             (xpOption xpText) -- Yup, some are nameless
   where
     from_tuple = uncurryN JFileGameHomeTeamXml
     to_tuple t = (home_team_id t,
@@ -638,8 +617,8 @@ pickle_away_team =
   xpElem "vteam" $
     xpWrap (from_tuple, to_tuple) $
     xpTriple (xpAttr "teamid" xpText) -- Yeah, they're text.
-             (xpAttr "abbr" xpText)
-             xpText
+             (xpAttr "abbr" (xpOption xpText)) -- Some are blank
+             (xpOption xpText) -- Yup, some are nameless
   where
     from_tuple = uncurryN JFileGameAwayTeamXml
     to_tuple t = (away_team_id t,
@@ -680,20 +659,27 @@ jfile_tests =
 --   test does not mean that unpickling succeeded.
 --
 test_pickle_of_unpickle_is_identity :: TestTree
-test_pickle_of_unpickle_is_identity =
-  testCase "pickle composed with unpickle is the identity" $ do
-    let path = "test/xml/jfilexml.xml"
-    (expected, actual) <- pickle_unpickle pickle_message path
-    actual @?= expected
+test_pickle_of_unpickle_is_identity = testGroup "pickle-unpickle tests"
+  [ check "pickle composed with unpickle is the identity"
+          "test/xml/jfilexml.xml",
+    check "pickle composed with unpickle is the identity (missing fields)"
+          "test/xml/jfilexml-missing-fields.xml" ]
+  where
+    check desc path = testCase desc $ do
+      (expected, actual) <- pickle_unpickle pickle_message path
+      actual @?= expected
 
 
 
 -- | Make sure we can actually unpickle these things.
 --
 test_unpickle_succeeds :: TestTree
-test_unpickle_succeeds =
-  testCase "unpickling succeeds" $ do
-    let path = "test/xml/jfilexml.xml"
+test_unpickle_succeeds = testGroup "unpickle tests"
+  [ check "unpickling succeeds" "test/xml/jfilexml.xml",
+    check "unpickling succeeds (missing fields)"
+          "test/xml/jfilexml-missing-fields.xml" ]
+  where
+    check desc path = testCase desc $ do
     actual <- unpickleable path pickle_message
 
     let expected = True
@@ -705,27 +691,29 @@ test_unpickle_succeeds =
 --   record.
 --
 test_on_delete_cascade :: TestTree
-test_on_delete_cascade =
-  testCase "deleting auto_racing_results deletes its children" $ do
-    let path = "test/xml/jfilexml.xml"
-    results <- unsafe_unpickle path pickle_message
-    let a = undefined :: Team
-    let b = undefined :: JFile
-    let c = undefined :: JFileGame
-    let d = undefined :: JFileGame_Team
-
-    actual <- withSqliteConn ":memory:" $ runDbConn $ do
-                runMigration silentMigrationLogger $ do
-                  migrate a
-                  migrate b
-                  migrate c
-                  migrate d
-                _ <- dbimport results
-                deleteAll b
-                count_a <- countAll a
-                count_b <- countAll b
-                count_c <- countAll c
-                count_d <- countAll d
-                return $ sum [count_a, count_b, count_c, count_d]
-    let expected = 20 -- Twenty teams should be left over
-    actual @?= expected
+test_on_delete_cascade = testGroup "cascading delete tests"
+  [ check "deleting auto_racing_results deletes its children"
+          "test/xml/jfilexml.xml"
+          20,
+    check "deleting auto_racing_results deletes its children (missing fields)"
+          "test/xml/jfilexml-missing-fields.xml"
+          44 ]
+  where
+    check desc path expected = testCase desc $ do
+      results <- unsafe_unpickle path pickle_message
+      let a = undefined :: Team
+      let b = undefined :: JFile
+      let c = undefined :: JFileGame
+
+      actual <- withSqliteConn ":memory:" $ runDbConn $ do
+                  runMigration silentMigrationLogger $ do
+                    migrate a
+                    migrate b
+                    migrate c
+                  _ <- dbimport results
+                  deleteAll b
+                  count_a <- countAll a
+                  count_b <- countAll b
+                  count_c <- countAll c
+                  return $ sum [count_a, count_b, count_c]
+      actual @?= expected