htsn-import 0.0.9 → 0.1.0
raw patch · 20 files changed
+364/−451 lines, 20 files
Files
- doc/dbschema/game_info.png binary
- htsn-import.cabal +1/−1
- schema/Matchup_NBA_NHL_XML.dtd +31/−42
- schemagen/Matchup_NBA_NHL_XML/20901524.xml +1/−0
- schemagen/Matchup_NBA_NHL_XML/Matchup_NBA_NHL_XML.xml +0/−139
- src/Main.hs +3/−2
- src/TSN/Parse.hs +216/−45
- src/TSN/Team.hs +22/−0
- src/TSN/XML/AutoRacingResults.hs +3/−3
- src/TSN/XML/GameInfo.hs +36/−7
- src/TSN/XML/JFile.hs +5/−2
- src/TSN/XML/Odds.hs +1/−2
- src/TSN/XML/Scores.hs +5/−2
- src/TSN/XML/SportInfo.hs +3/−2
- src/TSN/XmlImport.hs +1/−2
- src/Xml.hs +1/−21
- test/TestSuite.hs +2/−0
- test/xml/gameinfo/Matchup_NBA_NHL_XML.dtd +31/−42
- test/xml/gameinfo/Matchup_NBA_NHL_XML.xml +1/−139
- test/xml/gameinfo/recapxml-no-game-schedule-ids.xml +1/−0
doc/dbschema/game_info.png view
binary file changed (15090 → 17916 bytes)
htsn-import.cabal view
@@ -1,5 +1,5 @@ name: htsn-import-version: 0.0.9+version: 0.1.0 cabal-version: >= 1.8 author: Michael Orlitzky maintainer: Michael Orlitzky <michael@orlitzky.com>
schema/Matchup_NBA_NHL_XML.dtd view
@@ -1,42 +1,31 @@-<!ELEMENT message (XML_File_ID, heading , category , sport , Title , Matchup_Info , Head_To_Head_Record+ , Home_Record , Away_Record , Current_Streak? , Division_Conference_Records , Meetings+ , Matchup_Notes , Last_Five+ , time_stamp )> - -<!ELEMENT XML_File_ID (#PCDATA )> - -<!ELEMENT heading (#PCDATA )> - -<!ELEMENT category (#PCDATA )> - -<!ELEMENT sport (#PCDATA )> - -<!ELEMENT Title (#PCDATA )> - -<!ELEMENT Matchup_Info (#PCDATA )> - -<!ELEMENT Head_To_Head_Record (Record+ )> - -<!ELEMENT Record (#PCDATA )> -<!ATTLIST Record type CDATA #REQUIRED > -<!ELEMENT Home_Record (#PCDATA )> - -<!ELEMENT Away_Record (#PCDATA )> - -<!ELEMENT Current_Streak (#PCDATA )> - -<!ELEMENT Division_Conference_Records (Record_Vs+ )> - -<!ELEMENT Record_Vs (#PCDATA )> -<!ATTLIST Record_Vs Record_Type CDATA #REQUIRED > -<!ELEMENT Meetings (Game+ )> -<!ATTLIST Meetings Season CDATA #REQUIRED > -<!ELEMENT Game (#PCDATA )> - -<!ELEMENT Matchup_Notes (Note+ )> - -<!ELEMENT Note (#PCDATA )> - -<!ELEMENT Last_Five (LF_Game+ )> -<!ATTLIST Last_Five team CDATA #REQUIRED > -<!ELEMENT LF_Game (#PCDATA )> - -<!ELEMENT time_stamp (#PCDATA )> - +<!ELEMENT XML_File_ID (#PCDATA)>+<!ELEMENT heading (#PCDATA)>+<!ELEMENT category (#PCDATA)>+<!ELEMENT sport (#PCDATA)>+<!ELEMENT game_id (#PCDATA)>+<!ELEMENT schedule_id (#PCDATA)>+<!ELEMENT vteam (#PCDATA)>+<!ELEMENT hteam (#PCDATA)>+<!ELEMENT vteam_id (#PCDATA)>+<!ELEMENT hteam_id (#PCDATA)>+<!ELEMENT Title (#PCDATA)>+<!ELEMENT Matchup_Info (#PCDATA)>+<!ELEMENT Record (#PCDATA)>+<!ELEMENT Head_To_Head_Record ( Record )>+<!ELEMENT Home_Record (#PCDATA)>+<!ELEMENT Away_Record (#PCDATA)>+<!ELEMENT Record_Vs (#PCDATA)>+<!ELEMENT Division_Conference_Records ( Record_Vs* )>+<!ELEMENT Game (#PCDATA)>+<!ELEMENT Meetings ( Game* )>+<!ELEMENT Note (#PCDATA)>+<!ELEMENT Matchup_Notes ( Note* )>+<!ELEMENT LF_Game (#PCDATA)>+<!ELEMENT Last_FiveOrMore ( LF_Game* )>+<!ELEMENT time_stamp (#PCDATA)>+<!ELEMENT message ( ( XML_File_ID, heading, category, sport, game_id, schedule_id, vteam, hteam, vteam_id, hteam_id, Title, Matchup_Info, Head_To_Head_Record*, Home_Record, Away_Record, Division_Conference_Records, Meetings*, Matchup_Notes, Last_FiveOrMore*, time_stamp ) )>++<!ATTLIST Record type CDATA #REQUIRED>+<!ATTLIST Record_Vs Record_Type CDATA #REQUIRED>+<!ATTLIST Meetings Season CDATA #REQUIRED>+<!ATTLIST Last_FiveOrMore team CDATA #REQUIRED>
+ schemagen/Matchup_NBA_NHL_XML/20901524.xml view
@@ -0,0 +1,1 @@+<?xml version="1.0" standalone="no"?><!DOCTYPE message PUBLIC "-//TSN//DTD Statistics 1.0/EN" "Matchup_NBA_NHL_XML.dtd"><message><XML_File_ID>20901524</XML_File_ID><heading>ADX%MATCHUP-CLB-TAM</heading><category>Statistics</category><sport>NHL</sport><game_id>18827</game_id><schedule_id>18827</schedule_id><vteam>Columbus</vteam><hteam>Tampa Bay</hteam><vteam_id>148</vteam_id><hteam_id>144</hteam_id><Title>NHL Matchup - Columbus at Tampa Bay</Title><Matchup_Info>Columbus Blue Jackets (42-31-7) at Tampa Bay Lightning (44-27-9), 7:30 p.m.</Matchup_Info><Head_To_Head_Record><Record type="2013-14">Columbus 2-0 (2-0 at Clb; 0-0 at TB)</Record></Head_To_Head_Record><Head_To_Head_Record><Record type="2012-13">None</Record></Head_To_Head_Record><Head_To_Head_Record><Record type="2011-12">Tampa Bay 2-0 (1-0 at Clb; 1-0 at TB)</Record></Head_To_Head_Record><Head_To_Head_Record><Record type="2010-11">Tampa Bay 1-0 (0-0 at Clb; 1-0 at TB)</Record></Head_To_Head_Record><Home_Record>Columbus 22-15-4; Tampa Bay 24-10-6</Home_Record><Away_Record>Columbus 20-16-3; Tampa Bay 20-17-3</Away_Record><Division_Conference_Records><Record_Vs Record_Type="Atlantic Division">Columbus 13-7-2</Record_Vs><Record_Vs Record_Type="Metropolitan Division">Tampa Bay 11-8-3</Record_Vs><Record_Vs Record_Type="Eastern Conference">Columbus 29-20-3; Tampa Bay 30-16-6</Record_Vs></Division_Conference_Records><Meetings Season="2013-2014"><Game>12/03 - Columbus, 1-0 (at Clb)</Game><Game>01/13 - Columbus, 3-2 (at Clb)</Game></Meetings><Meetings Season="2011-2012"><Game>12/17 - Tampa Bay, 3-2 (at Clb)</Game><Game>01/24 - Tampa Bay, 4-2 (at TB)</Game></Meetings><Meetings Season="2010-2011"><Game>01/18 - Tampa Bay, 3-2 so (at TB)</Game></Meetings><Meetings Season="2009-2010"><Game>03/30 - Columbus, 3-2 (at Clb)</Game></Meetings><Meetings Season="2008-2009"><Game>03/24 - Tampa Bay, 2-1 ot (at TB)</Game></Meetings><Meetings Season="2007-2008"><Game>03/09 - Columbus, 5-3 (at Clb)</Game></Meetings><Meetings Season="2005-2006"><Game>01/13 - Tampa Bay, 4-2 (at TB)</Game></Meetings><Meetings Season="2003-2004"><Game>10/23 - Tampa Bay, 1-0 (at Clb)</Game><Game>01/02 - Columbus, 2-0 (at TB)</Game></Meetings><Meetings Season="2002-2003"><Game>10/23 - Tied, 2-2 ot (at Clb)</Game></Meetings><Meetings Season="2001-2002"><Game>12/06 - Tampa Bay, 1-0 (at TB)</Game></Meetings><Matchup_Notes><Note>Columbus has won three straight and six of its last seven road games.</Note><Note>Tampa Bay is 14-1-2 when leading after two periods at home this season.</Note><Note>Columbus has won two straight after losing the last three meetings.</Note><Note>Tampa Bay has won four of seven and five of the last nine meetings.</Note><Note>Tampa Bay is 8-5-1 in the last 14 meetings.</Note><Note>Columbus has lost four straight at Tampa Bay.</Note><Note>Columbus has lost six of the seven meetings at Tampa Bay.</Note><Note>Tampa Bay has lost two straight and four of its last five at Columbus.</Note><Note>Tampa Bay is 2-5-1 in the eight meetings at Columbus.</Note></Matchup_Notes><Last_FiveOrMore team="Columbus"><LF_Game>04/03 - W at Philadelphia, 2-0</LF_Game><LF_Game>04/04 - L vs. Chicago, 3-4</LF_Game><LF_Game>04/06 - W vs. NY Islanders, 4-0</LF_Game><LF_Game>04/08 - W vs. Phoenix, 4-3 (OT)</LF_Game><LF_Game>04/09 - W at Dallas, 3-1</LF_Game></Last_FiveOrMore><Last_FiveOrMore team="Tampa Bay"><LF_Game>04/01 - W vs. Montreal, 3-1</LF_Game><LF_Game>04/03 - L vs. Calgary, 1-4</LF_Game><LF_Game>04/05 - L vs. Dallas, 2-5</LF_Game><LF_Game>04/08 - W vs. Toronto, 3-0</LF_Game><LF_Game>04/10 - W vs. Philadelphia, 4-2</LF_Game></Last_FiveOrMore><time_stamp> April 11, 2014, at 12:20 PM ET </time_stamp></message>
− schemagen/Matchup_NBA_NHL_XML/Matchup_NBA_NHL_XML.xml
@@ -1,139 +0,0 @@-<?xml version="1.0" standalone="no" ?> -<!DOCTYPE message PUBLIC "-//TSN//DTD Statistics 1.0/EN" "Matchup_NBA_NHL_XML.dtd"> -<message> -<XML_File_ID>235677</XML_File_ID> -<heading>ADX%MATCHUP-COL-VAN</heading> -<category>Statistics</category> -<sport>NHL</sport> -<Title>NHL Matchup - Colorado at Vancouver</Title> -<Matchup_Info> -Colorado Avalanche (34-17-12-5) at Vancouver Canucks (36-20-8-4), 10 p.m. -</Matchup_Info> -<Head_To_Head_Record> -<Record type="2003-04">Vancouver 2-1-2 (2-0-1 at Col; 0-1-1 at Van)</Record> -</Head_To_Head_Record> -<Head_To_Head_Record> -<Record type="2002-03">Vancouver 3-2 (1-1 at Col; 2-1 at Van)</Record> -</Head_To_Head_Record> -<Head_To_Head_Record> -<Record type="2001-02">Colorado 3-2 (2-1 at Col; 1-1 at Van)</Record> -</Head_To_Head_Record> -<Head_To_Head_Record> -<Record type="2000-01">Colorado 3-2 (2-0 at Col; 1-2 at Van)</Record> -</Head_To_Head_Record> -<Home_Record>Colorado 17-13-5-1; Vancouver 17-10-5-0</Home_Record> -<Away_Record>Colorado 17-4-7-4; Vancouver 19-10-3-4</Away_Record> -<Division_Conference_Records> -<Record_Vs Record_Type="Northwest Division"> -Colorado 10-7-4-0; Vancouver 8-6-5-1</Record_Vs> -<Record_Vs Record_Type="Western Conference"> -Colorado 26-13-9-5; Vancouver 27-16-7-1</Record_Vs> -</Division_Conference_Records> -<Meetings Season="2003-2004"> -<Game>12/11 - Tied, 1-1 ot (at Van)</Game> -<Game>12/29 - Vancouver, 3-2 (at Col)</Game> -<Game>01/02 - Colorado, 4-2 (at Van)</Game> -<Game>02/16 - Vancouver, 1-0 (at Col)</Game> -<Game>03/03 - Tied, 5-5 ot (at Col)</Game> -</Meetings> -<Meetings Season="2002-2003"> -<Game>10/31 - Colorado, 5-1 (at Van)</Game> -<Game>11/04 - Vancouver, 4-2 (at Col)</Game> -<Game>12/11 - Vancouver, 3-1 (at Van)</Game> -<Game>12/23 - Colorado, 5-3 (at Col)</Game> -<Game>02/13 - Vancouver, 2-1 ot (at Van)</Game> -</Meetings> -<Meetings Season="2001-2002"> -<Game>10/09 - Colorado, 5-4 (at Col)</Game> -<Game>10/13 - Vancouver, 4-0 (at Van)</Game> -<Game>10/25 - Colorado, 4-1 (at Col)</Game> -<Game>11/30 - Colorado, 5-2 (at Van)</Game> -<Game>04/09 - Vancouver, 2-1 (at Col)</Game> -</Meetings> -<Matchup_Notes> -<Note> -Vancouver is unbeaten (1-0-1) in the last two meetings. -</Note> -<Note> -Vancouver is 3-1-2 in the last six meetings. -</Note> -<Note> -Vancouver is 5-2-2 in the last nine and 6-3-2 in the last 11 meetings. -</Note> -<Note> -Vancouver is 7-6-2 in the last 15 meetings. -</Note> -<Note> -These two teams are 9-9-2 in the last 20 meetings. -</Note> -<Note> -Colorado is 15-10-4 in the last 29 meetings. -</Note> -<Note> -Colorado snapped a three-game series road winless streak on 01/02/04. -</Note> -<Note> -Colorado is unbeaten (1-0-1) in its last two at Vancouver. -</Note> -<Note> -Colorado is 1-2-1 in its last four at Vancouver. -</Note> -<Note> -Colorado is 3-2-1 in its last six at Vancouver. -</Note> -<Note> -Colorado is 3-5-1 in its last nine at Vancouver. -</Note> -<Note> -Colorado is 7-5-2 in its last 14 at Vancouver. -</Note> -<Note> -Vancouver is unbeaten (2-0-1) in its last three at Colorado. -</Note> -<Note> -Vancouver is 4-1-1 in its last six at Colorado. -</Note> -<Note> -Vancouver is 4-5-1 in its last 10 and 5-8-2 in its last 15 at Colorado. -</Note> -<Note> -Vancouver is 6-13-4 in its last 23 at Colorado. -</Note> -<Note> -Colorado is 1-3-2-1 in its last seven and 2-6-2-1 in its last 11 games. -</Note> -<Note> -Colorado is 8-1-2-2 in its last 13 road games. -</Note> -<Note> -Colorado is 23-2-3 when leading after two periods this season. -</Note> -<Note> -Vancouver is 4-1-1 in its last six and 5-2-1-1 in its last nine games. -</Note> -<Note> -Vancouver has won three straight after losing its last three home games. -</Note> -<Note> -Vancouver has won six of its last nine home games. -</Note> -<Note> -Vancouver is 22-2-0 when leading after two periods this season. -</Note> -</Matchup_Notes> -<Last_Five team="Colorado"> -<LF_Game>02/28 - L at Columbus, 4-5 ot</LF_Game> -<LF_Game>03/01 - L vs. Tampa Bay, 0-3</LF_Game> -<LF_Game>03/03 - T vs. Vancouver, 5-5 ot</LF_Game> -<LF_Game>03/05 - W vs. San Jose, 5-1</LF_Game> -<LF_Game>03/07 - L vs. Calgary, 1-7</LF_Game> -</Last_Five> -<Last_Five team="Vancouver"> -<LF_Game>02/26 - W vs. San Jose, 3-2 ot</LF_Game> -<LF_Game>02/28 - W vs. St. Louis, 2-0</LF_Game> -<LF_Game>03/03 - T at Colorado, 5-5 ot</LF_Game> -<LF_Game>03/05 - L at Detroit, 1-3</LF_Game> -<LF_Game>03/06 - W at Columbus, 4-0</LF_Game> -</Last_Five> -<time_stamp> March 8, 2004, at 12:51 PM ET </time_stamp> -</message>
src/Main.hs view
@@ -45,6 +45,7 @@ report_info, report_error ) import TSN.DbImport ( DbImport(..), ImportResult(..) )+import TSN.Parse ( format_parse_error ) import qualified TSN.XML.AutoRacingResults as AutoRacingResults ( dtd, pickle_message )@@ -250,7 +251,7 @@ case either_m of -- This might give us a slightly better error -- message than the default 'errmsg'.- Left err -> return $ ImportFailed err+ Left err -> return $ ImportFailed (format_parse_error err) Right m -> migrate_and_import m | dtd `elem` SportInfo.dtds = do@@ -258,7 +259,7 @@ case either_m of -- This might give us a slightly better error -- message than the default 'errmsg'.- Left err -> return $ ImportFailed err+ Left err -> return $ ImportFailed (format_parse_error err) Right m -> migrate_and_import m | otherwise = do
src/TSN/Parse.hs view
@@ -1,5 +1,10 @@ module TSN.Parse (+ ParseError,+ format_parse_error,+ parse_game_id, parse_message,+ parse_schedule_id,+ parse_tests, parse_time_stamp, parse_xml_time_stamp, parse_xmlfid,@@ -11,6 +16,8 @@ import Data.Time.Clock ( UTCTime ) import Data.Time.Format ( parseTime ) import System.Locale ( defaultTimeLocale )+import Test.Tasty ( TestTree, testGroup )+import Test.Tasty.HUnit ( (@?=), testCase ) import Text.Read ( readMaybe ) import Text.XML.HXT.Core ( XmlTree,@@ -21,17 +28,49 @@ hasName, runLA ) +-- Local imports+import Xml ( unsafe_read_document ) --- | Parse the \"message\" element out of a document tree and return--- it as an 'XmlTree'. We use an Either for consistency.++-- | When parsing an element from an XML document (like the+-- XML_File_ID), there are a few things that can happen. First of+-- all, it can work. Good for you. ----- Note: It's more trouble than it's worth to attempt to use this as--- the basis for parse_xmlfid and parse_xml_time_stamp.+-- Or, you may find nothing. Like, the element is missing. We+-- represent that with a 'ParseNotFound' containing the name of+-- thing thing not-found as a 'String'. ---parse_message :: XmlTree -> Either String XmlTree+-- Finally, you could find something, but be unable to interpret it+-- as the type you were expecting. For example, if you parse+-- \"WHATSUP\" out of a \<game_id\> which is supposed to contain+-- integers. We represent this case with a 'ParseMismatch'+-- containing the name of the thing that you were looking for, the+-- value that had the unexpected type, and finally the name of the+-- expected type (used in error messages).+--+data ParseError =+ ParseNotFound String | ParseMismatch String String String+ deriving (Eq, Show)+++-- | Take a 'ParseError' and turn it into a human-readable description+-- of the problem.+--+format_parse_error :: ParseError -> String+format_parse_error (ParseNotFound item) =+ "No " ++ item ++ " elements found."+format_parse_error (ParseMismatch item val expected_type) =+ "Could not parse " ++ item ++ " " ++ val ++ " as " ++ expected_type ++ "."++++-- | Parse the \"message\" element out of a document tree and return+-- it as an 'XmlTree'. We use an 'Either' for consistency.+--+parse_message :: XmlTree -> Either ParseError XmlTree parse_message xmltree = case elements of- [] -> Left "No message elements found."+ [] -> Left $ ParseNotFound "message" (x:_) -> Right x where parse :: XmlTree -> [XmlTree]@@ -41,55 +80,110 @@ --- | Extract the \"XML_File_ID\" element from a document. If we fail--- to parse an XML_File_ID, we return the reason wrapped in a 'Left'--- constructor. The reason should be one of two things:------ 1. No XML_File_ID elements were found.------ 2. An XML_File_ID element was found, but it could not be read--- into an Integer.------ We use an Either rather than a Maybe because we do expect some--- non-integer XML_File_IDs. In the examples, you will see--- NHL_DepthChart_XML.XML with an XML_File_ID of \"49618.61\" and--- CFL_Boxscore_XML1.xml with an XML_File_ID of--- \"R28916\". According to Brijesh Patel of TSN, these are special--- category files and not part of the usual feed.+++-- | Parse an 'Int' from a direct descendent of the (top-level)+-- \<message\> element in an XmlTree. This is used to implement the+-- XML_File_ID, game_id, and schedule_id (the last two are specific+-- to "TSN.XML.GameInfo") parsers. ----- TODO: This should eventually be combined with XML.parse_xmlfid--- from the htsn package.+-- If the parse fails, we return the corresponding 'ParseError'+-- wrapped in a 'Left'. Otherwise the parsed value is returned in a+-- 'Right'. ---parse_xmlfid :: XmlTree -> Either String Integer-parse_xmlfid xmltree =+parse_message_int :: String -> XmlTree -> Either ParseError Int+parse_message_int child xmltree = case parse_results of- [] -> Left "No XML_File_ID elements found."+ [] -> Left $ ParseNotFound child (x:_) -> x where parse :: XmlTree -> [String] parse = runLA $ hasName "/" /> hasName "message"- /> hasName "XML_File_ID"+ /> hasName child >>> getChildren >>> getText - read_either_integer :: String -> Either String Integer- read_either_integer s =- let msg = "Could not parse XML_File_ID " ++ s ++ " as an integer."- in- maybeToEither msg (readMaybe s)+ read_either_int :: String -> Either ParseError Int+ read_either_int s =+ maybeToEither (ParseMismatch child s "integer") (readMaybe s) elements = parse xmltree- parse_results = map read_either_integer elements+ parse_results = map read_either_int elements +-- | Parse an optional 'Int' from a direct descendent of the+-- (top-level) \<message\> element in an XmlTree. This is just like+-- 'parse_message_int', except we expect the element/value to be+-- missing sometimes.+--+-- To handle the fact that the element/value is optional, we pattern+-- match on the 'ParseError' that comes back in case of failure. If+-- we didn't find anything, we turn that into a \"successful+-- nothing\". But if we find a value and it can't be parsed, we let+-- the error propagate, because that shouldn't happen. Of course, if+-- the parse worked, that's nice too: we wrap the parsed value in a+-- 'Just' and return that wrapped in a 'Right'+--+parse_message_int_optional :: String+ -> XmlTree+ -> Either ParseError (Maybe Int)+parse_message_int_optional child xmltree =+ case (parse_message_int child xmltree) of+ Left (ParseNotFound _) -> Right Nothing+ Left pm@(ParseMismatch {}) -> Left pm+ Right whatever -> Right (Just whatever)++++-- | Extract the \"XML_File_ID\" element from a document. If we fail+-- to parse an XML_File_ID, we return an appropriate 'ParseError'+-- wrapped in a 'Left' constructor. The reason should be one of two+-- things:+--+-- 1. No XML_File_ID elements were found.+--+-- 2. An XML_File_ID element was found, but it could not be read+-- into an Int.+--+-- In general we expect some non-integer XML_File_IDs, because they+-- appear on the feed. But the htsn daemon refuses to save them at+-- the moment, so if we ever see an XML_File_ID that we can't parse,+-- it's truly an error.+--+parse_xmlfid :: XmlTree -> Either ParseError Int+parse_xmlfid = parse_message_int "XML_File_ID"++++-- | Extract the \<game_id\> element from within the top-level+-- \<message\> of a document. These appear in the "TSN.XML.GameInfo"+-- documents. Unlike the \<XML_File_ID\> elements, the \<game_id\>+-- can be missing from GameInfo documents, so for our implementation+-- we use 'parse_message_int_optional' instead.+--+parse_game_id :: XmlTree -> Either ParseError (Maybe Int)+parse_game_id = parse_message_int_optional "game_id"++++-- | Extract the \<schedule_id\> element from within the top-level+-- \<message\> of a document. Identical to 'parse_game_id' except+-- for the element name.+--+parse_schedule_id :: XmlTree -> Either ParseError (Maybe Int)+parse_schedule_id = parse_message_int_optional "schedule_id"+++ -- | The format string for times appearing in the feed. -- time_format :: String time_format = "%I:%M %p" + -- | The format string for a time_stamp. We keep the leading/trailing -- space so that parseTime and formatTime are inverses are one -- another, even though there is some confusion as to how these two@@ -112,23 +206,23 @@ --- | Extract the \"time_stamp\" element from a document. If we fail--- to parse a time_stamp, we return the reason wrapped in a 'Left'--- constructor. The reason should be one of two things:+-- | Extract the \"time_stamp\" element from a document. If we fail to+-- parse a time_stamp, we return an appropriate 'ParseError'. The+-- reason should be one of two things: -- -- 1. No time_Stamp elements were found. -- -- 2. A time_stamp element was found, but it could not be read -- into a UTCTime. ----- Unline 'parse_xmlfid', we don't expect to run into any time_stamps--- that we can't parse. But since parse_xmlfid returns an Either, we--- do for consistency.+-- We don't expect to run into any time_stamps that we can't parse,+-- and they can never be missing, so both conditions are truly+-- errors. ---parse_xml_time_stamp :: XmlTree -> Either String UTCTime+parse_xml_time_stamp :: XmlTree -> Either ParseError UTCTime parse_xml_time_stamp xmltree = case parse_results of- [] -> Left "No time_stamp elements found."+ [] -> Left $ ParseNotFound "time_stamp" (x:_) -> x where parse :: XmlTree -> [String]@@ -138,11 +232,88 @@ >>> getChildren >>> getText - read_either_utctime :: String -> Either String UTCTime+ read_either_utctime :: String -> Either ParseError UTCTime read_either_utctime s =- let msg = "Could not parse time_stamp " ++ s ++ " as a date/time."- in- maybeToEither msg (parse_time_stamp s)+ maybeToEither (ParseMismatch "time_stamp" s "date/time")+ (parse_time_stamp s) elements = parse xmltree parse_results = map read_either_utctime elements++++--+-- * Tests+--++-- | A list of all tests for this module.+--+parse_tests :: TestTree+parse_tests =+ testGroup+ "TSN.Parse tests"+ [ test_parse_game_id,+ test_parse_missing_game_id,+ test_parse_missing_schedule_id,+ test_parse_schedule_id,+ test_parse_xmlfid ]+ where+ sample_path :: String+ sample_path = "test/xml/gameinfo/CBASK_Lineup_XML.xml"++ desc :: String -> String+ desc child = "a known " ++ child ++ " is parsed correctly"+++ -- | Actual implementation of the test for parse_xmlfid,+ -- parse_game_id, and parse_schedule_id.+ --+ test_child :: String -> Int -> TestTree+ test_child child expected =+ testCase (desc child) $ do+ xmltree <- unsafe_read_document sample_path+ let actual = parse_message_int child xmltree+ actual @?= (Right expected)+++ -- | Make sure we can parse a game_id into the expected value.+ --+ test_parse_game_id :: TestTree+ test_parse_game_id = test_child "game_id" 97865+++ -- | Make sure we can parse a schedule_id (different from the+ -- game_id) into the expected value.+ --+ test_parse_schedule_id :: TestTree+ test_parse_schedule_id = test_child "schedule_id" 10199+++ -- | Make sure we can parse an XML_File_ID into the expected value.+ --+ test_parse_xmlfid :: TestTree+ test_parse_xmlfid = test_child "XML_File_ID" 17++++-- | The game_id element can be missing, so we test that too.+--+test_parse_missing_game_id :: TestTree+test_parse_missing_game_id =+ testCase "missing game_id is not an error" $ do+ xmltree <- unsafe_read_document "test/xml/gameinfo/MLB_Matchup_XML.xml"+ let actual = parse_game_id xmltree+ let expected = Right Nothing+ actual @?= expected+++-- | The schedule_id element can be missing, so we test that too.+--+test_parse_missing_schedule_id :: TestTree+test_parse_missing_schedule_id =+ testCase "missing schedule_id is not an error" $ do+ let path = "test/xml/gameinfo/recapxml-no-game-schedule-ids.xml"+ xmltree <- unsafe_read_document path+ let actual = parse_schedule_id xmltree+ let expected = Right Nothing+ actual @?= expected
src/TSN/Team.hs view
@@ -14,6 +14,7 @@ -- module TSN.Team ( HTeam(..),+ FromXmlFkTeams(..), Team(..), VTeam(..), -- * WARNING: these are private but exported to silence warnings@@ -22,12 +23,16 @@ -- System imports import Database.Groundhog () -- Required for some String instance+import Database.Groundhog.Core ( PersistEntity(..) ) import Database.Groundhog.TH ( defaultCodegenConfig, groundhog, mkPersist ) +-- Local imports+import Xml ( Child(..), Db(..) ) + -- * Team -- | The database representation of a team. The 'team_id' is a@@ -76,3 +81,20 @@ type: constraint fields: [team_id] |]++++-- | A further refinement of 'FromXmlFk'. These types need not only a+-- foreign key to a parent in order to make the XML -> DB+-- conversion, but also two foreign keys to away/home teams (as+-- represented in "TSN.Team").+--+class (Child a, ToDb a) => FromXmlFkTeams a where+ -- | The function that produces a @Db a@ out of a parent foreign+ -- key, two team foreign keys, and an @a@. The parameter order makes+ -- it easier to map this function over a bunch of things.+ from_xml_fk_teams :: DefaultKey (Parent a)+ -> DefaultKey Team -- ^ The away team FK+ -> DefaultKey Team -- ^ The home team FK+ -> a+ -> Db a
src/TSN/XML/AutoRacingResults.hs view
@@ -584,7 +584,7 @@ -- test does not mean that unpickling succeeded. -- test_pickle_of_unpickle_is_identity :: TestTree-test_pickle_of_unpickle_is_identity = testGroup "pickle-unpickle tests" $+test_pickle_of_unpickle_is_identity = testGroup "pickle-unpickle tests" [ check "pickle composed with unpickle is the identity" "test/xml/AutoRacingResultsXML.xml", @@ -600,7 +600,7 @@ -- | Make sure we can actually unpickle these things. -- test_unpickle_succeeds :: TestTree-test_unpickle_succeeds = testGroup "unpickle tests" $+test_unpickle_succeeds = testGroup "unpickle tests" [ check "unpickling succeeds" "test/xml/AutoRacingResultsXML.xml", @@ -618,7 +618,7 @@ -- record. -- test_on_delete_cascade :: TestTree-test_on_delete_cascade = testGroup "cascading delete tests" $+test_on_delete_cascade = testGroup "cascading delete tests" [ check "deleting auto_racing_results deletes its children" "test/xml/AutoRacingResultsXML.xml",
src/TSN/XML/GameInfo.hs view
@@ -46,7 +46,10 @@ ImportResult(..), run_dbmigrate ) import TSN.Parse (+ ParseError,+ parse_game_id, parse_message,+ parse_schedule_id, parse_xmlfid, parse_xml_time_stamp ) import Xml ( unsafe_read_document )@@ -84,10 +87,24 @@ -- | This serves as both the database and XML representation of a -- GameInfo \<message\>. --+-- The 'game_id' and 'schedule_id' fields are foreign keys, but they+-- key into multiple tables and key on records which may not exist+-- when we import the GameInfo document. We therefore don't declare+-- them as foreign keys; i.e. we don't require them to point+-- anywhere in particular. But if they do, that's nice.+-- data GameInfo = GameInfo { dtd :: String, xml_file_id :: Int,+ game_id :: Maybe Int, -- ^ These are optional because they are missing+ -- from at least the MLB_Matchup_XML.dtd documents.+ -- They provide foreign keys into any tables storing+ -- games with their IDs.++ schedule_id :: Maybe Int, -- ^ Optional key into any table storing a+ -- schedule along with its ID. We've noticed+ -- them missing in e.g. recapxml.dtd documents. time_stamp :: UTCTime, xml :: String } deriving (Eq, Show)@@ -96,15 +113,23 @@ -- | Attempt to parse a 'GameInfo' from an 'XmlTree'. If we cannot, -- we fail with an error message. ---parse_xml :: String -> XmlTree -> Either String GameInfo+parse_xml :: String -> XmlTree -> Either ParseError GameInfo parse_xml dtdname xmltree = do xmlfid <- parse_xmlfid xmltree+ game_id <- parse_game_id xmltree+ schedule_id <- parse_schedule_id xmltree timestamp <- parse_xml_time_stamp xmltree message <- parse_message xmltree- return $ GameInfo dtdname (fromInteger xmlfid) timestamp (xshow [message])+ return $ GameInfo+ dtdname+ xmlfid+ game_id+ schedule_id+ timestamp+ (xshow [message]) ----- Database code+-- * Database code -- instance DbImport GameInfo where@@ -163,10 +188,14 @@ let ex2 = 21201550 let a3 = show $ time_stamp t let ex3 = "2014-05-31 15:13:00 UTC"- let a4 = take 9 (xml t)- let ex4 = "<message>"- let actual = (a1,a2,a3,a4)- let expected = (ex1,ex2,ex3,ex4)+ let a4 = game_id t+ let ex4 = Just 39978+ let a5 = schedule_id t+ let ex5 = Just 39978+ let a6 = take 9 (xml t)+ let ex6 = "<message>"+ let actual = (a1,a2,a3,a4,a5,a6)+ let expected = (ex1,ex2,ex3,ex4,ex5,ex6) actual @?= expected
src/TSN/XML/JFile.hs view
@@ -70,14 +70,17 @@ xp_time, xp_time_dots, xp_time_stamp )-import TSN.Team ( Team(..), HTeam(..), VTeam(..) )+import TSN.Team (+ FromXmlFkTeams(..),+ HTeam(..),+ Team(..),+ VTeam(..) ) import TSN.XmlImport ( XmlImport(..), XmlImportFkTeams(..) ) import Xml ( Child(..), FromXml(..),- FromXmlFkTeams(..), ToDb(..), pickle_unpickle, unpickleable,
src/TSN/XML/Odds.hs view
@@ -65,12 +65,11 @@ tsn_codegen_config ) import TSN.DbImport ( DbImport(..), ImportResult(..), run_dbmigrate ) import TSN.Picklers ( xp_date_padded, xp_time, xp_time_stamp )-import TSN.Team ( Team(..) )+import TSN.Team ( FromXmlFkTeams(..), Team(..) ) import TSN.XmlImport ( XmlImport(..), XmlImportFkTeams(..) ) import Xml ( Child(..), FromXml(..),- FromXmlFkTeams(..), ToDb(..), pickle_unpickle, unpickleable,
src/TSN/XML/Scores.hs view
@@ -61,12 +61,15 @@ import TSN.DbImport ( DbImport(..), ImportResult(..), run_dbmigrate ) import TSN.Location ( Location(..), pickle_location ) import TSN.Picklers ( xp_time_stamp )-import TSN.Team ( Team(..), HTeam(..), VTeam(..) )+import TSN.Team (+ FromXmlFkTeams(..),+ HTeam(..),+ Team(..),+ VTeam(..) ) import TSN.XmlImport ( XmlImport(..), XmlImportFkTeams(..) ) import Xml ( Child(..), FromXml(..),- FromXmlFkTeams(..), ToDb(..), pickle_unpickle, unpickleable,
src/TSN/XML/SportInfo.hs view
@@ -50,6 +50,7 @@ ImportResult(..), run_dbmigrate ) import TSN.Parse (+ ParseError, parse_message, parse_xmlfid, parse_xml_time_stamp )@@ -208,12 +209,12 @@ -- | Attempt to parse a 'SportInfo' from an 'XmlTree'. If we cannot, -- we fail with an error message. ---parse_xml :: String -> XmlTree -> Either String SportInfo+parse_xml :: String -> XmlTree -> Either ParseError SportInfo parse_xml dtdname xmltree = do xmlfid <- parse_xmlfid xmltree timestamp <- parse_xml_time_stamp xmltree message <- parse_message xmltree- return $ SportInfo dtdname (fromInteger xmlfid) timestamp (xshow [message])+ return $ SportInfo dtdname xmlfid timestamp (xshow [message]) --
src/TSN/XmlImport.hs view
@@ -20,12 +20,11 @@ -- Local imports.-import TSN.Team ( Team(..) )+import TSN.Team ( FromXmlFkTeams(..), Team(..) ) import Xml ( Child(..), FromXml(..), FromXmlFk(..),- FromXmlFkTeams(..), ToDb(..) )
src/Xml.hs view
@@ -8,7 +8,6 @@ DtdName(..), FromXml(..), FromXmlFk(..),- FromXmlFkTeams(..), ToDb(..), parse_opts, pickle_unpickle,@@ -39,10 +38,7 @@ yes ) --- Local imports.-import TSN.Team ( Team(..) ) - -- | Common associated type shared by 'FromXml' and 'FromXmlFk'. This -- basically just forces the client to define the \"database -- version\" of his type.@@ -70,8 +66,7 @@ -- | A class for XML representations which are children of other -- elements. The foal is to associate a child XML element with its -- parent element's database type. This is required to construct the--- database analogue of @a@ in the 'FromXmlFk' and 'FromXmlFkTeams'--- classes.+-- database analogue of @a@ in 'FromXmlFk'. -- class Child a where -- | The type of our parent object, i.e. to the type to whom our@@ -90,21 +85,6 @@ -- over a bunch of things. from_xml_fk :: DefaultKey (Parent a) -> a -> Db a ---- | A further refinement of 'FromXmlFk'. These types need not only a--- foreign key to a parent in order to make the XML -> DB--- conversion, but also two foreign keys to away/home teams (as--- represented in "TSN.Team").----class (Child a, ToDb a) => FromXmlFkTeams a where- -- | The function that produces a @Db a@ out of a parent foreign- -- key, two team foreign keys, and an @a@. The parameter order makes- -- it easier to map this function over a bunch of things.- from_xml_fk_teams :: DefaultKey (Parent a)- -> DefaultKey Team -- ^ The away team FK- -> DefaultKey Team -- ^ The home team FK- -> a- -> Db a -- | Represents the DTD filename (\"SYSTEM\") part of the DOCTYPE
test/TestSuite.hs view
@@ -1,5 +1,6 @@ import Test.Tasty ( TestTree, defaultMain, testGroup ) +import TSN.Parse ( parse_tests ) import TSN.Picklers ( pickler_tests ) import TSN.XML.AutoRacingResults ( auto_racing_results_tests ) import TSN.XML.AutoRacingSchedule ( auto_racing_schedule_tests )@@ -31,6 +32,7 @@ mlb_early_line_tests, news_tests, odds_tests,+ parse_tests, pickler_tests, schedule_changes_tests, scores_tests,
test/xml/gameinfo/Matchup_NBA_NHL_XML.dtd view
@@ -1,42 +1,31 @@-<!ELEMENT message (XML_File_ID, heading , category , sport , Title , Matchup_Info , Head_To_Head_Record+ , Home_Record , Away_Record , Current_Streak? , Division_Conference_Records , Meetings+ , Matchup_Notes , Last_Five+ , time_stamp )> - -<!ELEMENT XML_File_ID (#PCDATA )> - -<!ELEMENT heading (#PCDATA )> - -<!ELEMENT category (#PCDATA )> - -<!ELEMENT sport (#PCDATA )> - -<!ELEMENT Title (#PCDATA )> - -<!ELEMENT Matchup_Info (#PCDATA )> - -<!ELEMENT Head_To_Head_Record (Record+ )> - -<!ELEMENT Record (#PCDATA )> -<!ATTLIST Record type CDATA #REQUIRED > -<!ELEMENT Home_Record (#PCDATA )> - -<!ELEMENT Away_Record (#PCDATA )> - -<!ELEMENT Current_Streak (#PCDATA )> - -<!ELEMENT Division_Conference_Records (Record_Vs+ )> - -<!ELEMENT Record_Vs (#PCDATA )> -<!ATTLIST Record_Vs Record_Type CDATA #REQUIRED > -<!ELEMENT Meetings (Game+ )> -<!ATTLIST Meetings Season CDATA #REQUIRED > -<!ELEMENT Game (#PCDATA )> - -<!ELEMENT Matchup_Notes (Note+ )> - -<!ELEMENT Note (#PCDATA )> - -<!ELEMENT Last_Five (LF_Game+ )> -<!ATTLIST Last_Five team CDATA #REQUIRED > -<!ELEMENT LF_Game (#PCDATA )> - -<!ELEMENT time_stamp (#PCDATA )> - +<!ELEMENT XML_File_ID (#PCDATA)>+<!ELEMENT heading (#PCDATA)>+<!ELEMENT category (#PCDATA)>+<!ELEMENT sport (#PCDATA)>+<!ELEMENT game_id (#PCDATA)>+<!ELEMENT schedule_id (#PCDATA)>+<!ELEMENT vteam (#PCDATA)>+<!ELEMENT hteam (#PCDATA)>+<!ELEMENT vteam_id (#PCDATA)>+<!ELEMENT hteam_id (#PCDATA)>+<!ELEMENT Title (#PCDATA)>+<!ELEMENT Matchup_Info (#PCDATA)>+<!ELEMENT Record (#PCDATA)>+<!ELEMENT Head_To_Head_Record ( Record )>+<!ELEMENT Home_Record (#PCDATA)>+<!ELEMENT Away_Record (#PCDATA)>+<!ELEMENT Record_Vs (#PCDATA)>+<!ELEMENT Division_Conference_Records ( Record_Vs* )>+<!ELEMENT Game (#PCDATA)>+<!ELEMENT Meetings ( Game* )>+<!ELEMENT Note (#PCDATA)>+<!ELEMENT Matchup_Notes ( Note* )>+<!ELEMENT LF_Game (#PCDATA)>+<!ELEMENT Last_FiveOrMore ( LF_Game* )>+<!ELEMENT time_stamp (#PCDATA)>+<!ELEMENT message ( ( XML_File_ID, heading, category, sport, game_id, schedule_id, vteam, hteam, vteam_id, hteam_id, Title, Matchup_Info, Head_To_Head_Record*, Home_Record, Away_Record, Division_Conference_Records, Meetings*, Matchup_Notes, Last_FiveOrMore*, time_stamp ) )>++<!ATTLIST Record type CDATA #REQUIRED>+<!ATTLIST Record_Vs Record_Type CDATA #REQUIRED>+<!ATTLIST Meetings Season CDATA #REQUIRED>+<!ATTLIST Last_FiveOrMore team CDATA #REQUIRED>
test/xml/gameinfo/Matchup_NBA_NHL_XML.xml view
@@ -1,139 +1,1 @@-<?xml version="1.0" standalone="no" ?> -<!DOCTYPE message PUBLIC "-//TSN//DTD Statistics 1.0/EN" "Matchup_NBA_NHL_XML.dtd"> -<message> -<XML_File_ID>235677</XML_File_ID> -<heading>ADX%MATCHUP-COL-VAN</heading> -<category>Statistics</category> -<sport>NHL</sport> -<Title>NHL Matchup - Colorado at Vancouver</Title> -<Matchup_Info> -Colorado Avalanche (34-17-12-5) at Vancouver Canucks (36-20-8-4), 10 p.m. -</Matchup_Info> -<Head_To_Head_Record> -<Record type="2003-04">Vancouver 2-1-2 (2-0-1 at Col; 0-1-1 at Van)</Record> -</Head_To_Head_Record> -<Head_To_Head_Record> -<Record type="2002-03">Vancouver 3-2 (1-1 at Col; 2-1 at Van)</Record> -</Head_To_Head_Record> -<Head_To_Head_Record> -<Record type="2001-02">Colorado 3-2 (2-1 at Col; 1-1 at Van)</Record> -</Head_To_Head_Record> -<Head_To_Head_Record> -<Record type="2000-01">Colorado 3-2 (2-0 at Col; 1-2 at Van)</Record> -</Head_To_Head_Record> -<Home_Record>Colorado 17-13-5-1; Vancouver 17-10-5-0</Home_Record> -<Away_Record>Colorado 17-4-7-4; Vancouver 19-10-3-4</Away_Record> -<Division_Conference_Records> -<Record_Vs Record_Type="Northwest Division"> -Colorado 10-7-4-0; Vancouver 8-6-5-1</Record_Vs> -<Record_Vs Record_Type="Western Conference"> -Colorado 26-13-9-5; Vancouver 27-16-7-1</Record_Vs> -</Division_Conference_Records> -<Meetings Season="2003-2004"> -<Game>12/11 - Tied, 1-1 ot (at Van)</Game> -<Game>12/29 - Vancouver, 3-2 (at Col)</Game> -<Game>01/02 - Colorado, 4-2 (at Van)</Game> -<Game>02/16 - Vancouver, 1-0 (at Col)</Game> -<Game>03/03 - Tied, 5-5 ot (at Col)</Game> -</Meetings> -<Meetings Season="2002-2003"> -<Game>10/31 - Colorado, 5-1 (at Van)</Game> -<Game>11/04 - Vancouver, 4-2 (at Col)</Game> -<Game>12/11 - Vancouver, 3-1 (at Van)</Game> -<Game>12/23 - Colorado, 5-3 (at Col)</Game> -<Game>02/13 - Vancouver, 2-1 ot (at Van)</Game> -</Meetings> -<Meetings Season="2001-2002"> -<Game>10/09 - Colorado, 5-4 (at Col)</Game> -<Game>10/13 - Vancouver, 4-0 (at Van)</Game> -<Game>10/25 - Colorado, 4-1 (at Col)</Game> -<Game>11/30 - Colorado, 5-2 (at Van)</Game> -<Game>04/09 - Vancouver, 2-1 (at Col)</Game> -</Meetings> -<Matchup_Notes> -<Note> -Vancouver is unbeaten (1-0-1) in the last two meetings. -</Note> -<Note> -Vancouver is 3-1-2 in the last six meetings. -</Note> -<Note> -Vancouver is 5-2-2 in the last nine and 6-3-2 in the last 11 meetings. -</Note> -<Note> -Vancouver is 7-6-2 in the last 15 meetings. -</Note> -<Note> -These two teams are 9-9-2 in the last 20 meetings. -</Note> -<Note> -Colorado is 15-10-4 in the last 29 meetings. -</Note> -<Note> -Colorado snapped a three-game series road winless streak on 01/02/04. -</Note> -<Note> -Colorado is unbeaten (1-0-1) in its last two at Vancouver. -</Note> -<Note> -Colorado is 1-2-1 in its last four at Vancouver. -</Note> -<Note> -Colorado is 3-2-1 in its last six at Vancouver. -</Note> -<Note> -Colorado is 3-5-1 in its last nine at Vancouver. -</Note> -<Note> -Colorado is 7-5-2 in its last 14 at Vancouver. -</Note> -<Note> -Vancouver is unbeaten (2-0-1) in its last three at Colorado. -</Note> -<Note> -Vancouver is 4-1-1 in its last six at Colorado. -</Note> -<Note> -Vancouver is 4-5-1 in its last 10 and 5-8-2 in its last 15 at Colorado. -</Note> -<Note> -Vancouver is 6-13-4 in its last 23 at Colorado. -</Note> -<Note> -Colorado is 1-3-2-1 in its last seven and 2-6-2-1 in its last 11 games. -</Note> -<Note> -Colorado is 8-1-2-2 in its last 13 road games. -</Note> -<Note> -Colorado is 23-2-3 when leading after two periods this season. -</Note> -<Note> -Vancouver is 4-1-1 in its last six and 5-2-1-1 in its last nine games. -</Note> -<Note> -Vancouver has won three straight after losing its last three home games. -</Note> -<Note> -Vancouver has won six of its last nine home games. -</Note> -<Note> -Vancouver is 22-2-0 when leading after two periods this season. -</Note> -</Matchup_Notes> -<Last_Five team="Colorado"> -<LF_Game>02/28 - L at Columbus, 4-5 ot</LF_Game> -<LF_Game>03/01 - L vs. Tampa Bay, 0-3</LF_Game> -<LF_Game>03/03 - T vs. Vancouver, 5-5 ot</LF_Game> -<LF_Game>03/05 - W vs. San Jose, 5-1</LF_Game> -<LF_Game>03/07 - L vs. Calgary, 1-7</LF_Game> -</Last_Five> -<Last_Five team="Vancouver"> -<LF_Game>02/26 - W vs. San Jose, 3-2 ot</LF_Game> -<LF_Game>02/28 - W vs. St. Louis, 2-0</LF_Game> -<LF_Game>03/03 - T at Colorado, 5-5 ot</LF_Game> -<LF_Game>03/05 - L at Detroit, 1-3</LF_Game> -<LF_Game>03/06 - W at Columbus, 4-0</LF_Game> -</Last_Five> -<time_stamp> March 8, 2004, at 12:51 PM ET </time_stamp> -</message> +<?xml version="1.0" standalone="no"?><!DOCTYPE message PUBLIC "-//TSN//DTD Statistics 1.0/EN" "Matchup_NBA_NHL_XML.dtd"><message><XML_File_ID>20901524</XML_File_ID><heading>ADX%MATCHUP-CLB-TAM</heading><category>Statistics</category><sport>NHL</sport><game_id>18827</game_id><schedule_id>18827</schedule_id><vteam>Columbus</vteam><hteam>Tampa Bay</hteam><vteam_id>148</vteam_id><hteam_id>144</hteam_id><Title>NHL Matchup - Columbus at Tampa Bay</Title><Matchup_Info>Columbus Blue Jackets (42-31-7) at Tampa Bay Lightning (44-27-9), 7:30 p.m.</Matchup_Info><Head_To_Head_Record><Record type="2013-14">Columbus 2-0 (2-0 at Clb; 0-0 at TB)</Record></Head_To_Head_Record><Head_To_Head_Record><Record type="2012-13">None</Record></Head_To_Head_Record><Head_To_Head_Record><Record type="2011-12">Tampa Bay 2-0 (1-0 at Clb; 1-0 at TB)</Record></Head_To_Head_Record><Head_To_Head_Record><Record type="2010-11">Tampa Bay 1-0 (0-0 at Clb; 1-0 at TB)</Record></Head_To_Head_Record><Home_Record>Columbus 22-15-4; Tampa Bay 24-10-6</Home_Record><Away_Record>Columbus 20-16-3; Tampa Bay 20-17-3</Away_Record><Division_Conference_Records><Record_Vs Record_Type="Atlantic Division">Columbus 13-7-2</Record_Vs><Record_Vs Record_Type="Metropolitan Division">Tampa Bay 11-8-3</Record_Vs><Record_Vs Record_Type="Eastern Conference">Columbus 29-20-3; Tampa Bay 30-16-6</Record_Vs></Division_Conference_Records><Meetings Season="2013-2014"><Game>12/03 - Columbus, 1-0 (at Clb)</Game><Game>01/13 - Columbus, 3-2 (at Clb)</Game></Meetings><Meetings Season="2011-2012"><Game>12/17 - Tampa Bay, 3-2 (at Clb)</Game><Game>01/24 - Tampa Bay, 4-2 (at TB)</Game></Meetings><Meetings Season="2010-2011"><Game>01/18 - Tampa Bay, 3-2 so (at TB)</Game></Meetings><Meetings Season="2009-2010"><Game>03/30 - Columbus, 3-2 (at Clb)</Game></Meetings><Meetings Season="2008-2009"><Game>03/24 - Tampa Bay, 2-1 ot (at TB)</Game></Meetings><Meetings Season="2007-2008"><Game>03/09 - Columbus, 5-3 (at Clb)</Game></Meetings><Meetings Season="2005-2006"><Game>01/13 - Tampa Bay, 4-2 (at TB)</Game></Meetings><Meetings Season="2003-2004"><Game>10/23 - Tampa Bay, 1-0 (at Clb)</Game><Game>01/02 - Columbus, 2-0 (at TB)</Game></Meetings><Meetings Season="2002-2003"><Game>10/23 - Tied, 2-2 ot (at Clb)</Game></Meetings><Meetings Season="2001-2002"><Game>12/06 - Tampa Bay, 1-0 (at TB)</Game></Meetings><Matchup_Notes><Note>Columbus has won three straight and six of its last seven road games.</Note><Note>Tampa Bay is 14-1-2 when leading after two periods at home this season.</Note><Note>Columbus has won two straight after losing the last three meetings.</Note><Note>Tampa Bay has won four of seven and five of the last nine meetings.</Note><Note>Tampa Bay is 8-5-1 in the last 14 meetings.</Note><Note>Columbus has lost four straight at Tampa Bay.</Note><Note>Columbus has lost six of the seven meetings at Tampa Bay.</Note><Note>Tampa Bay has lost two straight and four of its last five at Columbus.</Note><Note>Tampa Bay is 2-5-1 in the eight meetings at Columbus.</Note></Matchup_Notes><Last_FiveOrMore team="Columbus"><LF_Game>04/03 - W at Philadelphia, 2-0</LF_Game><LF_Game>04/04 - L vs. Chicago, 3-4</LF_Game><LF_Game>04/06 - W vs. NY Islanders, 4-0</LF_Game><LF_Game>04/08 - W vs. Phoenix, 4-3 (OT)</LF_Game><LF_Game>04/09 - W at Dallas, 3-1</LF_Game></Last_FiveOrMore><Last_FiveOrMore team="Tampa Bay"><LF_Game>04/01 - W vs. Montreal, 3-1</LF_Game><LF_Game>04/03 - L vs. Calgary, 1-4</LF_Game><LF_Game>04/05 - L vs. Dallas, 2-5</LF_Game><LF_Game>04/08 - W vs. Toronto, 3-0</LF_Game><LF_Game>04/10 - W vs. Philadelphia, 4-2</LF_Game></Last_FiveOrMore><time_stamp> April 11, 2014, at 12:20 PM ET </time_stamp></message>
+ test/xml/gameinfo/recapxml-no-game-schedule-ids.xml view
@@ -0,0 +1,1 @@+<?xml version="1.0" standalone="no" ?> <!DOCTYPE message PUBLIC "-//TSN//DTD Recap 1.0/EN" "recapxml.dtd"> <message> <XML_File_ID>21246048</XML_File_ID> <heading>AAD;RECAP-SEA-TAM-P1</heading> <game_id></game_id> <schedule_id></schedule_id> <category>Recaps</category> <sport>MLB</sport> <title>American League Game Summary - Seattle at Tampa Bay</title> <final> <vteam>Seattle</vteam><vscore></vscore> <hteam></hteam><hscore></hscore> </final> <body> <P> St. Petersburg, FL (SportsNetwork.com) - Endy Chavez's two-out single broke a scoreless tie and started a five-run ninth inning that helped the Seattle Mariners beat the Tampa Bay Rays, 5-0, in the rubber match of a three-game series at Tropicana Field on Sunday. </P> <P> Brad Miller smacked a two-out triple into the right-field corner on an 0-2 slider from Grant Balfour (0-2). After a Willie Bloomquist walk, Chavez's sharp grounder on another 0-2 pitch went under the glove of diving shortstop Yunel Escobar. James Jones then tripled over the head of right fielder Kevin Kiermaier, who was playing shallow, to make it a 3-0 advantage. </P> <P> Robinson Cano was walked on four pitches before Kyle Seager's two-run double to right increased the cushion to five for the Mariners, winners in seven of their last eight contests. </P> <P> Yoervis Medina (3-1) walked one in the eighth inning to earn the win after taking over for Seattle starter Felix Hernandez. King Felix struck out a career-high 15 batters and allowed just four hits in seven innings in a no- decision. </P> </body> <gamenotes> </gamenotes> <time_stamp> June 8, 2014, at 05:00 PM ET </time_stamp> </message>