1 {-# LANGUAGE DeriveGeneric #-}
2 {-# LANGUAGE FlexibleInstances #-}
4 {-# LANGUAGE QuasiQuotes #-}
5 {-# LANGUAGE RecordWildCards #-}
6 {-# LANGUAGE TemplateHaskell #-}
7 {-# LANGUAGE TypeFamilies #-}
9 -- | Parse TSN XML for the DTD \"MLB_Boxscore_XML.dtd\".
11 module TSN.XML.MLBBoxScore (
15 -- auto_racing_results_tests,
16 -- * WARNING: these are private but exported to silence warnings
17 MLBBoxScore_MLBBoxScoreTeamBreakdownConstructor(..),
18 MLBBoxScoreConstructor(..),
19 MLBBoxScoreHomerunStats(..),
20 MLBBoxScoreMiscellaneousGameInfo(..),
21 MLBBoxScoreMiscPitchingStats(..),
22 MLBBoxScoreRunsByInningsConstructor(..),
23 MLBBoxScoreTeamBreakdownConstructor(..),
24 MLBBoxScoreTeamSummary(..)
26 -- AutoRacingResultsListingConstructor(..),
27 -- AutoRacingResultsRaceInformationConstructor(..) )
31 import Control.Monad ( forM_ )
32 import Data.Time ( UTCTime(..) )
33 import Data.Tuple.Curry ( uncurryN )
34 import Database.Groundhog (
38 import Database.Groundhog.Core ( DefaultKey )
39 import Database.Groundhog.TH (
42 import qualified GHC.Generics as GHC ( Generic )
43 import Text.XML.HXT.Core (
59 import Generics ( Generic(..), to_tuple )
60 import TSN.Codegen ( tsn_codegen_config )
61 import TSN.DbImport ( DbImport(..), ImportResult(..), run_dbmigrate )
66 import TSN.Team ( Team(..), FromXmlFkTeams(..) )
67 import TSN.XmlImport ( XmlImport(..), XmlImportFk(..) )
75 -- | The DTD to which this module corresponds. Used to invoke dbimport.
78 dtd = "MLB_Boxscore_XML.dtd"
81 -- * DB/XML data types
84 -- MLBBoxScore/Message
86 -- | Database representation of a 'Message'. The vteam/hteam have been
87 -- removed since they use the TSN.Team representation. The
88 -- 'xml_game_date' and 'xml_game_time' fields have also been
89 -- combined into 'db_game_time'. Finally, the summaries are missing
90 -- since they'll be keyed to us.
94 db_xml_file_id :: Int,
96 db_category :: String,
99 db_schedule_id :: Int,
100 db_vteam_id :: DefaultKey Team,
101 db_hteam_id :: DefaultKey Team,
103 db_season_type :: String,
104 db_game_time :: UTCTime,
105 db_game_number :: Int,
108 db_time_stamp :: UTCTime }
113 -- | XML Representation of an 'MBLBoxScore'. It has the same fields,
114 -- but in addition contains the hteam/vteams and a game_date that
115 -- will eventually be combined with the time. It also has a list of
120 xml_xml_file_id :: Int,
121 xml_heading :: String,
122 xml_category :: String,
125 xml_schedule_id :: Int,
128 xml_vteam_id :: String,
129 xml_hteam_id :: String,
130 xml_season :: String,
131 xml_season_type :: String,
133 xml_game_date :: UTCTime,
134 xml_game_time :: UTCTime,
135 xml_game_number :: Int,
137 xml_game_breakdown :: MLBBoxScoreGameBreakdownXml,
138 xml_team_summaries :: [MLBBoxScoreTeamSummaryXml],
139 xml_misc_pitching_stats :: MLBBoxScoreMiscPitchingStatsXml,
140 xml_homerun_stats :: Maybe MLBBoxScoreHomerunStatsXml,
141 xml_miscellaneous_game_info :: MLBBoxScoreMiscellaneousGameInfoXml,
142 xml_time_stamp :: UTCTime }
143 deriving (Eq, GHC.Generic, Show)
146 -- | For 'Generics.to_tuple'.
148 instance Generic Message
150 instance ToDb Message where
151 -- | The database analogue of a 'Message' is a 'MLBBoxScore'.
153 type Db Message = MLBBoxScore
157 -- | This ugly hack allows us to make 'Message' an instance of
158 -- 'FromXmlFkTeams'. That class usually requires that its instances
159 -- have a parent, but 'Message' does not. So we declare it the
160 -- parent of itself, and then ignore it.
161 instance Child Message where
162 type Parent Message = MLBBoxScore
164 -- | The 'FromXmlFk' instance for 'Message' is required for the
165 -- 'XmlImport' instance.
166 instance FromXmlFkTeams Message where
167 -- | To convert a 'Message' to an 'MLBBoxScore', we drop the
168 -- teams/summaries and combine the date/time. Also missing are the
169 -- embedded elements game_breakdown, homerun_stats, and
170 -- miscellaneous_game_info.
172 from_xml_fk_teams _ vteam_id hteam_id Message{..} =
174 db_xml_file_id = xml_xml_file_id,
175 db_heading = xml_heading,
176 db_category = xml_category,
177 db_sport = xml_sport,
178 db_game_id = xml_game_id,
179 db_schedule_id = xml_schedule_id,
180 db_vteam_id = vteam_id,
181 db_hteam_id = hteam_id,
182 db_season = xml_season,
183 db_season_type = xml_season_type,
184 db_game_time = make_game_time,
185 db_game_number = xml_game_number,
186 db_capacity = xml_capacity,
187 db_title = xml_title,
188 db_time_stamp = xml_time_stamp }
191 UTCTime (utctDay xml_game_date) (utctDayTime xml_game_time)
194 data MLBBoxScoreTeamSummary = MLBBoxScoreTeamSummary
195 data MLBBoxScoreTeamSummaryXml = MLBBoxScoreTeamSummaryXml deriving (Eq, Show)
197 data MLBBoxScoreGameBreakdownXml =
198 MLBBoxScoreGameBreakdownXml {
199 xml_away_team :: MLBBoxScoreTeamBreakdownXml,
200 xml_home_team :: MLBBoxScoreTeamBreakdownXml }
201 deriving (Eq, GHC.Generic, Show)
203 -- | For 'Generics.to_tuple'
205 instance Generic MLBBoxScoreGameBreakdownXml
208 data MLBBoxScoreHomerunStats = MLBBoxScoreHomerunStats
209 data MLBBoxScoreHomerunStatsXml = MLBBoxScoreHomerunStatsXml
212 data MLBBoxScoreMiscellaneousGameInfo = MLBBoxScoreMiscellaneousGameInfo
213 data MLBBoxScoreMiscellaneousGameInfoXml = MLBBoxScoreMiscellaneousGameInfoXml
218 data MLBBoxScoreTeamBreakdown =
219 MLBBoxScoreTeamBreakdown {
223 data MLBBoxScoreTeamBreakdownXml =
224 MLBBoxScoreTeamBreakdownXml {
225 xml_runs_by_innings :: [MLBBoxScoreRunsByInningsXml],
229 deriving (Eq, GHC.Generic, Show)
232 -- | For 'Generics.to_tuple'.
233 instance Generic MLBBoxScoreTeamBreakdownXml
235 instance ToDb MLBBoxScoreTeamBreakdownXml where
236 -- | The database analogue of a 'MLBBoxScoreTeamBreakdownXml' is
237 -- a 'MLBBoxScoreTeamBreakdown'.
239 type Db MLBBoxScoreTeamBreakdownXml = MLBBoxScoreTeamBreakdown
242 -- | The 'FromXml' instance for 'MLBBoxScoreTeamBreakdownXml' is
243 -- required for the 'XmlImport' instance.
245 instance FromXml MLBBoxScoreTeamBreakdownXml where
246 -- | To convert a 'MLBBoxScoreTeamBreakdownXml' to an
247 -- 'MLBBoxScoreTeamBreakdown', we just drop the
248 -- 'xml_runs_by_innings'.
250 from_xml MLBBoxScoreTeamBreakdownXml{..} =
251 MLBBoxScoreTeamBreakdown {
254 db_errors = xml_errors }
256 instance XmlImport MLBBoxScoreTeamBreakdownXml
259 data MLBBoxScoreRunsByInnings =
260 MLBBoxScoreRunsByInnings {
261 db_mlb_box_scores_team_breakdowns_id :: DefaultKey
262 MLBBoxScoreTeamBreakdown,
263 db_runs_by_innings_inning_number :: Int,
264 db_runs_by_innings_runs :: Int }
266 data MLBBoxScoreRunsByInningsXml =
267 MLBBoxScoreRunsByInningsXml {
268 xml_runs_by_innings_inning_number :: Int,
269 xml_runs_by_innings_runs :: Int }
270 deriving (Eq, GHC.Generic, Show)
273 -- * MLBBoxScore_MLBBoxScoreTeamSummary
275 -- | Mapping between 'MLBBoxScore' records and
276 -- 'MLBBoxScoreTeamSummary' records in the database. We don't use
277 -- the names anywhere, so we let Groundhog choose them.
279 data MLBBoxScore_MLBBoxScoreTeamBreakdown =
280 MLBBoxScore_MLBBoxScoreTeamBreakdown
281 (DefaultKey MLBBoxScore)
282 (DefaultKey MLBBoxScoreTeamBreakdown) -- Away team
283 (DefaultKey MLBBoxScoreTeamBreakdown) -- Home team
287 -- | For 'Generics.to_tuple'.
289 instance Generic MLBBoxScoreRunsByInningsXml
292 instance ToDb MLBBoxScoreRunsByInningsXml where
293 -- | The database analogue of a 'MLBBoxScoreRunsByInningsXml' is
294 -- a 'MLBBoxScoreRunsByInnings'.
296 type Db MLBBoxScoreRunsByInningsXml = MLBBoxScoreRunsByInnings
299 instance Child MLBBoxScoreRunsByInningsXml where
300 -- | Each 'MLBBoxScoreRunsByInningsXml' is contained in (i.e. has a
301 -- foreign key to) a 'MLBBoxScoreTeamBreakdownXml'.
303 type Parent MLBBoxScoreRunsByInningsXml = MLBBoxScoreTeamBreakdown
306 instance FromXmlFk MLBBoxScoreRunsByInningsXml where
307 -- | To convert an 'MLBBoxScoreRunsByInningsXml' to an
308 -- 'MLBBoxScoreRunsByInnings', we add the foreign key and copy
309 -- everything else verbatim.
311 from_xml_fk fk MLBBoxScoreRunsByInningsXml{..} =
312 MLBBoxScoreRunsByInnings {
313 db_mlb_box_scores_team_breakdowns_id = fk,
314 db_runs_by_innings_inning_number = xml_runs_by_innings_inning_number,
315 db_runs_by_innings_runs = xml_runs_by_innings_runs }
318 -- | This allows us to insert the XML representation
319 -- 'MLBBoxScoreRunsByInningsXml' directly.
321 instance XmlImportFk MLBBoxScoreRunsByInningsXml
326 data MLBBoxScoreMiscPitchingStats = MLBBoxScoreMiscPitchingStats
327 data MLBBoxScoreMiscPitchingStatsXml =
328 MLBBoxScoreMiscPitchingStatsXml {
329 xml_wild_pitches :: Maybe Int,
330 xml_intentional_walks :: [MLBBoxScoreMiscPitchingStatsIntentionalWalkXml],
331 xml_hits_by_pitch :: [MLBBoxScoreMiscPitchingStatsHitByPitchXml] }
332 deriving (Eq, GHC.Generic, Show)
335 -- | For 'Generics.to_tuple'.
336 instance Generic MLBBoxScoreMiscPitchingStatsXml
339 data MLBBoxScoreMiscPitchingStatsIntentionalWalkXml =
340 MLBBoxScoreMiscPitchingStatsIntentionalWalkXml {
341 xml_iw_batter_id :: Int,
342 xml_iw_pitcher_id :: Int,
343 xml_iw_number_of_times_walked :: Int }
344 deriving (Eq, GHC.Generic, Show)
347 -- | For 'Generics.to_tuple'.
348 instance Generic MLBBoxScoreMiscPitchingStatsIntentionalWalkXml
351 data MLBBoxScoreMiscPitchingStatsHitByPitchXml =
352 MLBBoxScoreMiscPitchingStatsHitByPitchXml {
353 xml_hbp_batter_id :: Int,
354 xml_hbp_pitcher_id :: Int,
355 xml_hbp_number_of_times_hit :: Int }
356 deriving (Eq, GHC.Generic, Show)
359 -- | For 'Generics.to_tuple'.
361 instance Generic MLBBoxScoreMiscPitchingStatsHitByPitchXml
368 instance DbImport Message where
371 migrate (undefined :: MLBBoxScore)
373 -- | We insert the message.
375 -- First, get the vteam/hteam out of the XML message.
376 let vteam = Team (xml_vteam_id m) Nothing (Just $ xml_vteam m)
377 let hteam = Team (xml_hteam_id m) Nothing (Just $ xml_hteam m)
380 vteam_fk <- insert vteam
381 hteam_fk <- insert hteam
383 -- Now we can key the message to the teams/breakdowns we just
385 let db_msg = from_xml_fk_teams undefined vteam_fk hteam_fk m
386 msg_id <- insert db_msg
388 -- Next, the vteam/hteam breakdowns, also needed to construct the
389 -- main message record
390 let vteam_bd = xml_away_team $ xml_game_breakdown m
391 let hteam_bd = xml_home_team $ xml_game_breakdown m
393 vteam_bd_fk <- insert_xml vteam_bd
394 hteam_bd_fk <- insert_xml hteam_bd
396 -- Insert the runs-by-innings associated with the vteam/hteam
398 forM_ (xml_runs_by_innings vteam_bd) $ insert_xml_fk_ vteam_bd_fk
399 forM_ (xml_runs_by_innings hteam_bd) $ insert_xml_fk_ hteam_bd_fk
401 -- Now the join table record that ties the message to its two team
403 let msg__breakdown = MLBBoxScore_MLBBoxScoreTeamBreakdown
408 insert_ msg__breakdown
410 return ImportSucceeded
414 mkPersist tsn_codegen_config [groundhog|
415 - entity: MLBBoxScore
416 dbName: mlb_box_scores
420 - name: unique_mlb_box_scores
422 # Prevent multiple imports of the same message.
423 fields: [db_xml_file_id]
426 - entity: MLBBoxScoreTeamBreakdown
427 dbName: mlb_box_scores_team_breakdowns
429 - name: MLBBoxScoreTeamBreakdown
431 - entity: MLBBoxScoreRunsByInnings
432 dbName: mlb_box_scores_team_breakdowns_runs_by_innings
434 - name: MLBBoxScoreRunsByInnings
436 - name: db_mlb_box_scores_team_breakdowns_id
441 - entity: MLBBoxScore_MLBBoxScoreTeamBreakdown
442 dbName: mlb_box_scores__mlb_box_scores_team_breakdowns
444 - name: MLBBoxScore_MLBBoxScoreTeamBreakdown
446 - name: mLBBoxScore_MLBBoxScoreTeamBreakdown0
447 dbName: mlb_box_scores_id
450 - name: mLBBoxScore_MLBBoxScoreTeamBreakdown1
451 dbName: mlb_box_scores_team_breakdowns_away_team_id
454 - name: mLBBoxScore_MLBBoxScoreTeamBreakdown2
455 dbName: db_mlb_box_scores_team_breakdowns_home_team_id
466 pickle_message :: PU Message
469 xpWrap (from_tuple, to_tuple) $
470 xp23Tuple (xpElem "XML_File_ID" xpInt)
471 (xpElem "heading" xpText)
472 (xpElem "category" xpText)
473 (xpElem "sport" xpText)
474 (xpElem "game_id" xpInt)
475 (xpElem "schedule_id" xpInt)
476 (xpElem "vteam" xpText)
477 (xpElem "hteam" xpText)
478 (xpElem "vteam_id" xpText)
479 (xpElem "hteam_id" xpText)
480 (xpElem "Season" xpText)
481 (xpElem "SeasonType" xpText)
482 (xpElem "title" xpText)
483 (xpElem "Game_Date" xp_date)
484 (xpElem "Game_Time" xp_time)
485 (xpElem "GameNumber" xpInt)
486 (xpElem "Capacity" xpInt)
487 pickle_game_breakdown
488 (xpList pickle_team_summary)
489 pickle_misc_pitching_stats
491 pickle_miscellaneous_game_info
492 (xpElem "time_stamp" xp_time_stamp)
494 from_tuple = uncurryN Message
497 pickle_team_summary :: PU MLBBoxScoreTeamSummaryXml
498 pickle_team_summary =
499 xpElem "Team_Summary" $ xpWrap (from_tuple, to_tuple') $ xpUnit
501 from_tuple _ = MLBBoxScoreTeamSummaryXml
504 pickle_game_breakdown :: PU MLBBoxScoreGameBreakdownXml
505 pickle_game_breakdown =
506 xpElem "Game_Breakdown" $
507 xpWrap (from_tuple, to_tuple) $
508 xpPair pickle_away_team
511 from_tuple = uncurry MLBBoxScoreGameBreakdownXml
514 pickle_runs_by_innings :: PU MLBBoxScoreRunsByInningsXml
515 pickle_runs_by_innings =
516 xpElem "Runs_By_Innings" $
517 xpWrap (from_tuple, to_tuple) $
518 xpPair (xpAttr "Inning" xpInt)
521 from_tuple = uncurry MLBBoxScoreRunsByInningsXml
524 pickle_team :: PU MLBBoxScoreTeamBreakdownXml
526 xpWrap (from_tuple, to_tuple) $
527 xp4Tuple (xpList pickle_runs_by_innings)
528 (xpElem "Runs" xpInt)
529 (xpElem "Hits" xpInt)
530 (xpElem "Errors" xpInt)
532 from_tuple = uncurryN MLBBoxScoreTeamBreakdownXml
535 pickle_away_team :: PU MLBBoxScoreTeamBreakdownXml
537 xpElem "AwayTeam" pickle_team
539 pickle_home_team :: PU MLBBoxScoreTeamBreakdownXml
541 xpElem "HomeTeam" pickle_team
543 pickle_homerun_stats :: PU (Maybe MLBBoxScoreHomerunStatsXml)
544 pickle_homerun_stats =
545 xpOption $ xpElem "Homerun_Stats" $ xpWrap (from_tuple, to_tuple') $ xpUnit
547 from_tuple _ = MLBBoxScoreHomerunStatsXml
551 pickle_misc_pitching_stats :: PU MLBBoxScoreMiscPitchingStatsXml
552 pickle_misc_pitching_stats =
553 xpElem "Misc_Pitching_Stats" $
554 xpWrap (from_tuple, to_tuple) $
555 xpTriple (xpOption $ xpElem "Wild_Pitches" xpInt)
556 pickle_intentional_walks
559 from_tuple = uncurryN MLBBoxScoreMiscPitchingStatsXml
563 pickle_intentional_walks :: PU [MLBBoxScoreMiscPitchingStatsIntentionalWalkXml]
564 pickle_intentional_walks =
565 xpElem "Intentional_Walks" $ xpList $ xpElem "IW_Listing" $
566 xpWrap (from_tuple, to_tuple) $
567 xpTriple (xpElem "IW_Batter_ID" xpInt)
568 (xpElem "IW_Pitcher_ID" xpInt)
569 (xpElem "IW_Number_Of_Times_Walked" xpInt)
571 from_tuple = uncurryN MLBBoxScoreMiscPitchingStatsIntentionalWalkXml
575 pickle_hits_by_pitch :: PU [MLBBoxScoreMiscPitchingStatsHitByPitchXml]
576 pickle_hits_by_pitch =
577 xpElem "Hit_By_Pitch" $ xpList $ xpElem "HBP_Listing" $
578 xpWrap (from_tuple, to_tuple) $
579 xpTriple (xpElem "HBP_Batter_ID" xpInt)
580 (xpElem "HBP_Pitcher_ID" xpInt)
581 (xpElem "HBP_Number_Of_Times_Hit" xpInt)
583 from_tuple = uncurryN MLBBoxScoreMiscPitchingStatsHitByPitchXml
587 pickle_miscellaneous_game_info :: PU MLBBoxScoreMiscellaneousGameInfoXml
588 pickle_miscellaneous_game_info =
589 xpElem "Miscelaneous_Game_Info" $ xpWrap (from_tuple, to_tuple') $ xpUnit
591 from_tuple _ = MLBBoxScoreMiscellaneousGameInfoXml