1 {-# LANGUAGE FlexibleInstances #-}
3 {-# LANGUAGE QuasiQuotes #-}
4 {-# LANGUAGE RecordWildCards #-}
5 {-# LANGUAGE TemplateHaskell #-}
6 {-# LANGUAGE TypeFamilies #-}
8 -- | Parse TSN XML for the DTD \"jfilexml.dtd\". There's a top-level
9 -- \<message\>, containing a \<gamelist\>, containing
10 -- \<game\>s. Those games contain a bunch of other stuff. The
11 -- \<gamelist\> is pretty irrelevant; we ignore it and pretend that
12 -- a message contains a bunch of games.
14 module TSN.XML.JFile (
19 import Data.Time ( UTCTime(..) )
20 import Data.Tuple.Curry ( uncurryN )
21 import Database.Groundhog ( migrate )
22 import Database.Groundhog.Core ( DefaultKey )
23 import Database.Groundhog.TH (
26 import Text.XML.HXT.Core (
45 import TSN.Codegen ( tsn_codegen_config )
46 import TSN.DbImport ( DbImport(..), ImportResult(..), run_dbmigrate )
47 import TSN.Picklers ( xp_date_padded, xp_time, xp_time_stamp )
48 import TSN.Team ( Team(..) )
49 import TSN.XmlImport (
60 -- | The DTD to which this module corresponds. Used to invoke dbimport.
71 -- | Database representation of a 'Message'.
75 db_xml_file_id :: Int,
77 db_category :: String,
79 db_time_stamp :: UTCTime }
83 -- | XML Representation of an 'JFile'.
87 xml_xml_file_id :: Int,
88 xml_heading :: String,
89 xml_category :: String,
91 xml_gamelist :: JFileGameListXml,
92 xml_time_stamp :: UTCTime }
96 instance ToDb Message where
97 -- | The database analogue of a 'Message' is a 'JFile'.
99 type Db Message = JFile
102 -- | The 'FromXml' instance for 'Message' is required for the
103 -- 'XmlImport' instance.
105 instance FromXml Message where
106 -- | To convert a 'Message' to an 'JFile', we just drop
107 -- the 'xml_gamelist'.
109 from_xml Message{..} =
111 db_xml_file_id = xml_xml_file_id,
112 db_heading = xml_heading,
113 db_category = xml_category,
114 db_sport = xml_sport,
115 db_time_stamp = xml_time_stamp }
118 -- | This allows us to insert the XML representation 'Message'
121 instance XmlImport Message
124 -- * JFileGameAwayTeamXml / JFileGameHomeTeamXml
126 -- | The XML representation of a JFile away team. Its corresponding
127 -- database representation (along with that of the home team) is a
128 -- TSN.Team, but their XML representations are different.
129 data JFileGameAwayTeamXml =
130 JFileGameAwayTeamXml {
131 away_team_id :: String,
132 away_team_abbreviation :: String,
133 away_team_name :: String }
136 instance ToDb JFileGameAwayTeamXml where
137 -- | The database analogue of an 'JFileGameAwayTeamXml' is
140 type Db JFileGameAwayTeamXml = Team
142 instance FromXml JFileGameAwayTeamXml where
143 -- | To convert a 'JFileGameAwayTeamXml' to a 'Team', we do just
146 from_xml JFileGameAwayTeamXml{..} =
148 team_id = away_team_id,
149 team_abbreviation = away_team_abbreviation,
150 team_name = away_team_name }
152 -- | Allow us to import JFileGameAwayTeamXml directly.
153 instance XmlImport JFileGameAwayTeamXml
156 -- | The XML representation of a JFile home team. Its corresponding
157 -- database representation (along with that of the away team) is a
158 -- TSN.Team, but their XML representations are different.
159 data JFileGameHomeTeamXml =
160 JFileGameHomeTeamXml {
161 home_team_id :: String,
162 home_team_abbreviation :: String,
163 home_team_name :: String }
166 instance ToDb JFileGameHomeTeamXml where
167 -- | The database analogue of an 'JFileGameHomeTeamXml' is
170 type Db JFileGameHomeTeamXml = Team
172 instance FromXml JFileGameHomeTeamXml where
173 -- | To convert a 'JFileGameHomeTeamXml' to a 'Team', we do just
176 from_xml JFileGameHomeTeamXml{..} =
178 team_id = home_team_id,
179 team_abbreviation = home_team_abbreviation,
180 team_name = home_team_name }
182 -- | Allow us to import JFileGameHomeTeamXml directly.
183 instance XmlImport JFileGameHomeTeamXml
186 -- * JFileGame/JFileGameXml
188 -- | This is an embedded type within each JFileGame. It has its own
189 -- element, \<Odds_Info\>, but there's only one of them per game. So
190 -- essentially all of these fields belong to a 'JFileGame'. Aaaannnd
191 -- most of them are redundant. We'll (un)pickle them for good
192 -- measure, but in the conversion to the database type, we can drop
193 -- all of the redundant information.
195 data JFileGameOddsInfo =
197 db_list_date :: UTCTime,
198 db_home_team_id :: String, -- redundant (Team)
199 db_away_team_id :: String, -- redundant (Team)
200 db_home_abbr :: String, -- redundant (Team)
201 db_away_abbr :: String, -- redundant (Team)
202 db_home_team_name :: String, -- redundant (Team)
203 db_away_team_name :: String, -- redundant (Team)
204 db_home_starter :: String,
205 db_away_starter :: String,
206 db_game_date :: UTCTime, -- redundant (JFileGame)
207 db_home_game_key :: Int,
208 db_away_game_key :: Int,
209 db_current_timestamp :: UTCTime,
215 -- | Another embedded type within 'JFileGame'. These look like,
216 -- \<status numeral=\"4\"\>FINAL\</status\> within the XML, but
217 -- they're in one-to-one correspondence with the games.
219 data JFileGameStatus =
221 db_status_numeral :: Int,
222 db_status :: String }
226 -- | Database representation of a \<game\> contained within a
227 -- \<message\>, and, implicitly, a \<gamelist\>.
229 -- We've left out the game date, opting instead to combine the
230 -- date/time into the 'db_game_time' field.
234 db_jfile_id :: DefaultKey JFile,
236 db_schedule_id :: Int,
237 db_odds_info :: JFileGameOddsInfo,
238 db_season_type :: String,
239 db_game_time :: UTCTime,
240 db_vleague :: Maybe String,
241 db_hleague :: Maybe String,
244 db_time_remaining :: Maybe String,
245 db_game_status :: JFileGameStatus }
248 -- | XML representation of a \<game\> contained within a \<message\>,
249 -- and a \<gamelist\>. The Away/Home teams seem to coincide with
250 -- those of 'OddsGame', so we're reusing the DB type via the common
251 -- 'TSN.Team' structure. But the XML types are different, because
252 -- they have different picklers!
257 xml_schedule_id :: Int,
258 xml_odds_info :: JFileGameOddsInfo,
259 xml_season_type :: String,
260 xml_game_date :: UTCTime,
261 xml_game_time :: UTCTime,
262 xml_vteam :: JFileGameAwayTeamXml,
263 xml_vleague :: Maybe String,
264 xml_hteam :: JFileGameHomeTeamXml,
265 xml_hleague :: Maybe String,
268 xml_time_remaining :: Maybe String,
269 xml_game_status :: JFileGameStatus }
273 -- * JFileGameListXml
275 -- | The XML representation of \<message\> -> \<gamelist\>. This
276 -- element serves only to contain \<game\>s, so we don't store the
277 -- intermediate table in the database.
279 newtype JFileGameListXml =
286 instance ToDb JFileGameXml where
287 -- | The database analogue of an 'JFileGameXml' is
290 type Db JFileGameXml = JFileGame
292 instance FromXmlFk JFileGameXml where
293 -- | Each 'JFileGameXml' is contained in (i.e. has a foreign key to)
296 type Parent JFileGameXml = JFile
298 -- | To convert an 'JFileGameXml' to an 'JFileGame', we add the
299 -- foreign key and drop the 'xml_vteam'/'xml_hteam'. We also mash
300 -- the date/time together into one field.
302 from_xml_fk fk JFileGameXml{..} =
305 db_game_id = xml_game_id,
306 db_schedule_id = xml_schedule_id,
307 db_odds_info = xml_odds_info,
308 db_season_type = xml_season_type,
309 db_game_time = xml_game_time,
310 db_vleague = xml_vleague,
311 db_hleague = xml_hleague,
312 db_vscore = xml_vscore,
313 db_hscore = xml_hscore,
314 db_time_remaining = xml_time_remaining,
315 db_game_status = xml_game_status }
317 -- | Make the database \"game time\" from the XML
318 -- date/time. Simply take the day part from one and the time
321 make_game_time d Nothing = d
322 make_game_time d (Just t) = UTCTime (utctDay d) (utctDayTime t)
325 -- | This allows us to insert the XML representation
326 -- 'JFileGameXml' directly.
328 instance XmlImportFk JFileGameXml
333 -- | Database mapping between games and their home/away teams.
335 data JFileGame_Team =
337 jgt_jfile_games_id :: DefaultKey JFileGame,
338 jgt_away_team_id :: DefaultKey Team,
339 jgt_home_team_id :: DefaultKey Team }
346 instance DbImport Message where
349 migrate (undefined :: Team)
350 migrate (undefined :: JFile)
351 migrate (undefined :: JFileGame)
352 migrate (undefined :: JFileGame_Team)
354 dbimport m = return ImportSucceeded
357 mkPersist tsn_codegen_config [groundhog|
365 # Prevent multiple imports of the same message.
366 fields: [db_xml_file_id]
368 - embedded: JFileGameStatus
370 - name: db_status_numeral
371 dbName: status_numeral
375 # Many of the JFileGameOddsInfo fields are redundant and have
377 - embedded: JFileGameOddsInfo
381 - name: db_home_starter
383 - name: db_home_game_key
384 dbName: home_game_key
385 - name: db_away_game_key
386 dbName: away_game_key
387 - name: db_current_timestamp
388 dbName: current_timestamp
404 - {name: list_date, dbName: list_date}
405 - {name: home_starter, dbName: home_starter}
406 - {name: away_starter, dbName: away_starter}
407 - {name: home_game_key, dbName: home_game_key}
408 - {name: away_game_key, dbName: home_game_key}
409 - {name: current_timestamp, dbName: current_timestamp}
410 - {name: live, dbName: live}
411 - {name: notes, dbName: notes}
412 - name: db_game_status
414 - {name: status_numeral, dbName: status_numeral}
415 - {name: status, dbName: status}
417 - entity: JFileGame_Team
418 dbName: jfile_games__teams
420 - name: JFileGame_Team
422 - name: jgt_jfile_games_id
425 - name: jgt_away_team_id
428 - name: jgt_home_team_id
439 -- | Pickler for the top-level 'Message'.
441 pickle_message :: PU Message
444 xpWrap (from_tuple, to_tuple) $
445 xp6Tuple (xpElem "XML_File_ID" xpInt)
446 (xpElem "heading" xpText)
447 (xpElem "category" xpText)
448 (xpElem "sport" xpText)
450 (xpElem "time_stamp" xp_time_stamp)
452 from_tuple = uncurryN Message
453 to_tuple m = (xml_xml_file_id m,
460 pickle_gamelist :: PU JFileGameListXml
463 xpWrap (to_result, from_result) $ xpList pickle_game
465 to_result = JFileGameListXml
466 from_result = xml_games
471 pickle_game :: PU JFileGameXml
474 xpWrap (from_tuple, to_tuple) $
475 xp14Tuple (xpElem "game_id" xpInt)
476 (xpElem "schedule_id" xpInt)
478 (xpElem "seasontype" xpText)
479 (xpElem "Game_Date" xp_date_padded)
480 (xpElem "Game_Time" xp_time)
482 (xpOption $ xpElem "vleague" xpText)
484 (xpOption $ xpElem "hleague" xpText)
485 (xpElem "vscore" xpInt)
486 (xpElem "hscore" xpInt)
487 (xpOption $ xpElem "time_r" xpText)
490 from_tuple = uncurryN JFileGameXml
491 to_tuple m = (xml_game_id m,
503 xml_time_remaining m,
506 pickle_odds_info = undefined
509 pickle_home_team :: PU JFileGameHomeTeamXml
512 xpWrap (from_tuple, to_tuple) $
513 xpTriple (xpAttr "teamid" xpText) -- Yeah, they're text.
514 (xpAttr "abbr" xpText)
517 from_tuple = uncurryN JFileGameHomeTeamXml
518 to_tuple t = (home_team_id t,
519 home_team_abbreviation t,
523 pickle_away_team :: PU JFileGameAwayTeamXml
526 xpWrap (from_tuple, to_tuple) $
527 xpTriple (xpAttr "teamid" xpText) -- Yeah, they're text.
528 (xpAttr "abbr" xpText)
531 from_tuple = uncurryN JFileGameAwayTeamXml
532 to_tuple t = (away_team_id t,
533 away_team_abbreviation t,
537 pickle_status :: PU JFileGameStatus
540 xpWrap (from_tuple, to_tuple) $
541 xpPair (xpAttr "numeral" xpInt)
544 from_tuple = uncurry JFileGameStatus
545 to_tuple s = (db_status_numeral s,