/* * Copyright 2010 Chad Retz * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of * the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations under * the License. */ package org.statmantis.mport.retro.game; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.sql.Date; import java.text.DateFormat; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.statmantis.model.Forfeiture; import org.statmantis.model.Game; import org.statmantis.model.GameAccuracy; import org.statmantis.model.GameCompletion; import org.statmantis.model.GameDayNumber; import org.statmantis.model.GamePlayer; import org.statmantis.model.GamePosition; import org.statmantis.model.Position; import org.statmantis.model.Protest; import org.statmantis.model.TeamGameInfo; import org.statmantis.mport.retro.RetrosheetContext; import org.statmantis.mport.retro.RetrosheetRuntimeException; import org.statmantis.mport.retro.RetrosheetYearReader; import org.statmantis.util.CsvUtils; import org.statmantis.util.GameUtils; /** * Game log reader * * @author Chad Retz */ @SuppressWarnings("unused") public class RetrosheetGameLogReader extends RetrosheetYearReader<Map<String, Game>> { private static final Logger logger = LoggerFactory.getLogger( RetrosheetGameLogReader.class); private final int yearBegin; private final int yearEnd; private final String directory; private final RetrosheetContext retroContext; public RetrosheetGameLogReader(int yearBegin, int yearEnd, RetrosheetContext retroContext) { this(yearBegin, yearEnd, null, retroContext); } public RetrosheetGameLogReader(int yearBegin, int yearEnd, String directory, RetrosheetContext retroContext) { this.yearBegin = yearBegin; this.yearEnd = yearEnd; this.directory = directory; this.retroContext = retroContext; } @Override public Map<String, Game> read() throws IOException { // TODO Auto-generated method stub return null; } @Override public Map<String, Game> read(InputStream stream) throws IOException { Map<String, Game> games = new HashMap<String, Game>(); ZipInputStream zis = new ZipInputStream(stream); try { ZipEntry entry; do { entry = zis.getNextEntry(); if (entry == null) { break; } games.putAll(readGameLog(new BufferedReader(new InputStreamReader(zis)))); } while (true); } finally { try { zis.close(); } catch (Exception e) { //no-op } } return games; } protected Map<String, Game> readGameLog(BufferedReader reader) throws IOException { List<Map<GameLogColumn, String>> lines = CsvUtils.readCsv( GameLogColumn.class, reader, false); Map<String, Game> ret = new HashMap<String, Game>(); DateFormat dateFormat = new SimpleDateFormat("yyyyMMdd"); for (int i = 0; i < lines.size(); i++) { Map<GameLogColumn, String> line = lines.get(i); Game game = new Game(); //start date try { game.setStartDate(new Date(dateFormat.parse(line.get( GameLogColumn.DATE)).getTime())); } catch (Exception e) { if (logger.isWarnEnabled()) { logger.warn("Unable to parse date for line: " + line); } continue; } //game number try { game.setDayNumber(GameDayNumber.NUMBERS.get(line.get( GameLogColumn.GAME_NUMBER))); if (game.getDayNumber() == null) { throw new RetrosheetRuntimeException("No game number!"); } } catch (Exception e) { if (logger.isWarnEnabled()) { logger.warn("Unable to find the proper game day number for line: " + line, e); } continue; } //we don't care about day of week; // we can figure that out on our own if necessary //visiting team try { game.setVisitingTeam(retroContext.getTeams().get( line.get(GameLogColumn.VISITING_TEAM))); if (game.getVisitingTeam() == null) { throw new RetrosheetRuntimeException("Visiting team not found"); } //let's check the league too if (!game.getVisitingTeam().getLeague().getRetroId().equals( line.get(GameLogColumn.VISITING_LEAGUE))) { throw new RetrosheetRuntimeException("Visiting team from strange league!"); } } catch (Exception e) { if (logger.isWarnEnabled()) { logger.warn("Unable to get visiting team for line: " + line, e); } continue; } //home team try { game.setHomeTeam(retroContext.getTeams().get( line.get(GameLogColumn.HOME_TEAM))); if (game.getHomeTeam() == null) { throw new RetrosheetRuntimeException("Home team not found"); } //let's check the league too if (!game.getHomeTeam().getLeague().getRetroId().equals( line.get(GameLogColumn.HOME_LEAGUE))) { throw new RetrosheetRuntimeException("Home team from strange league!"); } } catch (Exception e) { if (logger.isWarnEnabled()) { logger.warn("Unable to get home team for line: " + line, e); } continue; } //visiting team info try { game.setVisitorInfo(getTeamInfo(line, false)); } catch (Exception e) { if (logger.isWarnEnabled()) { logger.warn("Unable to populate visiting team info for line: " + line, e); } continue; } //home team info try { game.setHomeInfo(getTeamInfo(line, true)); } catch (Exception e) { if (logger.isWarnEnabled()) { logger.warn("Unable to populate home team info for line: " + line, e); } continue; } //total outs try { game.setOuts(Integer.valueOf(line.get(GameLogColumn.TOTAL_OUTS))); } catch (Exception e) { if (logger.isWarnEnabled()) { logger.warn("Unable to populate outs: " + line, e); } continue; } //day/night try { String dayOrNight = line.get(GameLogColumn.DAY_OR_NIGHT); if ("D".equals(dayOrNight)) { game.setDayGame(true); } else if ("N".equals(dayOrNight)) { game.setDayGame(false); } else { throw new RetrosheetRuntimeException("Unrecognized day/night: " + dayOrNight); } } catch (Exception e) { if (logger.isWarnEnabled()) { logger.warn("Unable to populate day/night: " + line, e); } continue; } //completion try { game.setCompletion(getCompletionInfo(dateFormat, line)); } catch (Exception e) { if (logger.isWarnEnabled()) { logger.warn("Unable to populate completion: " + line, e); } continue; } //forfeit try { game.setForfeiture(Forfeiture.FORFEITURES.get(line.get( GameLogColumn.FORFEIT_INFO))); } catch (Exception e) { if (logger.isWarnEnabled()) { logger.warn("Unable to populate forfeiture: " + line, e); } continue; } //protest try { game.setProtests(getProtests(line)); } catch (Exception e) { if (logger.isWarnEnabled()) { logger.warn("Unable to populate protests: " + line, e); } continue; } //park try { game.setPark(retroContext.getParks().get(line.get(GameLogColumn.PARK))); if (game.getPark() == null) { throw new RetrosheetRuntimeException("Can't find park: " + line.get(GameLogColumn.PARK)); } } catch (Exception e) { if (logger.isWarnEnabled()) { logger.warn("Unable to populate park: " + line, e); } continue; } //attendance try { game.setAttendance(Integer.valueOf(line.get(GameLogColumn.ATTENDANCE))); } catch (Exception e) { if (logger.isWarnEnabled()) { logger.warn("Unable to populate attendance: " + line, e); } continue; } //time in minutes try { game.setTimeInMinutes(Integer.valueOf(line.get(GameLogColumn.TIME_IN_MINUTES))); } catch (Exception e) { if (logger.isWarnEnabled()) { logger.warn("Unable to populate time in minutes: " + line, e); } continue; } //additional info try { game.setHomeTeamBattedFirst("HTBF".equals(line.get( GameLogColumn.ADDITIONAL_INFO))); } catch (Exception e) { if (logger.isWarnEnabled()) { logger.warn("Unable to populate additional info: " + line, e); } continue; } //acquisition information try { game.setAccuracy(GameAccuracy.ACCURACIES.get(line.get( GameLogColumn.ACQUISITION_INFO))); if (game.getAccuracy() == null) { throw new RetrosheetRuntimeException("No accuracy info found"); } } catch (Exception e) { if (logger.isWarnEnabled()) { logger.warn("Unable to populate acquisition info: " + line, e); } continue; } //add ret.put(GameUtils.generateUniqueId(game), game); } return ret; } private List<Protest> getProtests(Map<GameLogColumn, String> line) { List<Protest> ret = new ArrayList<Protest>(); String protests = line.get(GameLogColumn.PROTEST_INFO); if (protests != null && !protests.isEmpty()) { for (int i = 0; i < protests.length(); i++) { ret.add(Protest.valueOf("" + protests.charAt(i))); } } return ret; } private GameCompletion getCompletionInfo(DateFormat dateFormat, Map<GameLogColumn, String> line) throws ParseException { GameCompletion ret = new GameCompletion(); String[] pieces = line.get(GameLogColumn.COMPLETION_INFO).split(","); ret.setCompletionDate(new Date(dateFormat.parse(pieces[0]).getTime())); ret.setPark(retroContext.getParks().get(pieces[1])); ret.setVisitorScoreAtInterruption(Integer.valueOf(pieces[2])); ret.setHomeScoreAtInterruption(Integer.valueOf(pieces[3])); ret.setOutsAtInterruption(Integer.valueOf(pieces[4])); return ret; } private TeamGameInfo getTeamInfo(Map<GameLogColumn, String> line, boolean homeTeam) { TeamGameInfo ret = new TeamGameInfo(); String prefix = homeTeam ? "HOME" : "VISITING"; ret.setGameNumber(Integer.valueOf(line.get(GameLogColumn.valueOf( prefix + "_GAME_NUMBER")))); ret.setScore(Integer.valueOf(line.get(GameLogColumn.valueOf( prefix + "_SCORE")))); ret.setInningScores(GameUtils.parseLineScore(line.get( GameLogColumn.valueOf(prefix + "_LINE_SCORE")))); ret.setAtBats(Integer.valueOf(line.get(GameLogColumn.valueOf( prefix + "_AT_BATS")))); ret.setHits(Integer.valueOf(line.get(GameLogColumn.valueOf( prefix + "_HITS")))); ret.setDoubles(Integer.valueOf(line.get(GameLogColumn.valueOf( prefix + "_DOUBLES")))); ret.setTriples(Integer.valueOf(line.get(GameLogColumn.valueOf( prefix + "_TRIPLES")))); ret.setHomeruns(Integer.valueOf(line.get(GameLogColumn.valueOf( prefix + "_HOMERUNS")))); ret.setRbi(Integer.valueOf(line.get(GameLogColumn.valueOf( prefix + "_RBI")))); ret.setSacrificeHits(Integer.valueOf(line.get(GameLogColumn.valueOf( prefix + "_SACRIFICE_HITS")))); ret.setSacrificeFlies(Integer.valueOf(line.get(GameLogColumn.valueOf( prefix + "_SACRIFICE_FLIES")))); ret.setHitByPitch(Integer.valueOf(line.get(GameLogColumn.valueOf( prefix + "_HIT_BY_PITCH")))); ret.setWalks(Integer.valueOf(line.get(GameLogColumn.valueOf( prefix + "_WALKS")))); ret.setIntentionalWalks(Integer.valueOf(line.get(GameLogColumn.valueOf( prefix + "_INTENTIONAL_WALKS")))); ret.setStrikeouts(Integer.valueOf(line.get(GameLogColumn.valueOf( prefix + "_STRIKEOUTS")))); ret.setStolenBases(Integer.valueOf(line.get(GameLogColumn.valueOf( prefix + "_STOLEN_BASES")))); ret.setCaughtStealing(Integer.valueOf(line.get(GameLogColumn.valueOf( prefix + "_CAUGHT_STEALING")))); ret.setGroundedIntoDoublePlays(Integer.valueOf(line.get(GameLogColumn.valueOf( prefix + "_GROUNDED_INTO_DOUBLE_PLAYS")))); ret.setOpponentCatcherInterferences(Integer.valueOf(line.get(GameLogColumn.valueOf( prefix + "_FIRST_ON_CATCHER_INTERFERENCE")))); ret.setLeftOnBase(Integer.valueOf(line.get(GameLogColumn.valueOf( prefix + "_LEFT_ON_BASE")))); ret.setPitchersUsed(Integer.valueOf(line.get(GameLogColumn.valueOf( prefix + "_PITCHERS_USED")))); ret.setIndividualEarnedRuns(Integer.valueOf(line.get(GameLogColumn.valueOf( prefix + "_INDIVIDUAL_EARNED_RUNS")))); ret.setTeamEarnedRuns(Integer.valueOf(line.get(GameLogColumn.valueOf( prefix + "_TEAM_EARNED_RUNS")))); ret.setWildPitches(Integer.valueOf(line.get(GameLogColumn.valueOf( prefix + "_WILD_PITCHES")))); ret.setBalks(Integer.valueOf(line.get(GameLogColumn.valueOf( prefix + "_BALKS")))); ret.setPutouts(Integer.valueOf(line.get(GameLogColumn.valueOf( prefix + "_PUTOUTS")))); ret.setAssists(Integer.valueOf(line.get(GameLogColumn.valueOf( prefix + "_ASSISTS")))); ret.setErrors(Integer.valueOf(line.get(GameLogColumn.valueOf( prefix + "_ERRORS")))); ret.setPassedBalls(Integer.valueOf(line.get(GameLogColumn.valueOf( prefix + "_PASSED_BALLS")))); ret.setDoublePlays(Integer.valueOf(line.get(GameLogColumn.valueOf( prefix + "_DOUBLE_PLAYS")))); ret.setTriplePlays(Integer.valueOf(line.get(GameLogColumn.valueOf( prefix + "_TRIPLE_PLAYS")))); ret.setManager(retroContext.getPersons().get(line.get( GameLogColumn.valueOf(prefix + "_MANAGER_ID")))); if (ret.getManager() == null) { throw new RetrosheetRuntimeException("Manager '" + line.get(GameLogColumn.valueOf(prefix + "_MANAGER_ID")) + "' not found"); } //build up the game players Map<String, GamePlayer> players = new HashMap<String, GamePlayer>(9); for (int i = 1; i < 10; i++) { GamePlayer player = new GamePlayer(); player.setPlayer(retroContext.getPersons().get(line.get( GameLogColumn.valueOf(prefix + "_PLAYER_" + i + "_ID")))); if (player.getPlayer() == null) { throw new RetrosheetRuntimeException("Can't find player: " + line.get(GameLogColumn.valueOf(prefix + "_PLAYER_" + i + "_ID"))); } player.setPositions(new ArrayList<GamePosition>()); GamePosition position = new GamePosition(); position.setBeginningInning(1); position.setLineupPosition(i); position.setPosition(Position.fromRetroId(Integer.valueOf(line.get( GameLogColumn.valueOf(prefix + "_PLAYER_" + i + "_POSITION"))))); player.getPositions().add(position); players.put(player.getPlayer().getRetroId(), player); } ret.setGamePlayers(new ArrayList<GamePlayer>(players.values())); //starting pitcher (only needed as a game player, rest can be figured out) String startingPitcher = line.get(GameLogColumn.valueOf( prefix + "_STARTING_PITCHER_ID")); GamePlayer starter = players.get(startingPitcher); if (starter == null) { starter = new GamePlayer(); starter.setPlayer(retroContext.getPersons().get(startingPitcher)); starter.setPositions(new ArrayList<GamePosition>()); GamePosition position = new GamePosition(); position.setBeginningInning(1); position.setPosition(Position.PITCHER); starter.getPositions().add(position); players.put(startingPitcher, starter); ret.getGamePlayers().add(starter); } //now the winning/losing/saving pitchers (and RBI getters) int opponentScore = Integer.valueOf(line.get(GameLogColumn.valueOf( (homeTeam ? "VISITING" : "HOME") + "_SCORE"))); if (opponentScore > ret.getScore()) { //losing pitcher ret.setWinner(false); String losingPitcher = line.get(GameLogColumn.LOSING_PITCHER_ID); GamePlayer player = players.get(losingPitcher); if (player == null) { player = new GamePlayer(); player.setPlayer(retroContext.getPersons().get(losingPitcher)); if (player.getPlayer() == null) { throw new RetrosheetRuntimeException("Can't find losing pitcher: " + losingPitcher); } ret.getGamePlayers().add(player); } ret.setDecidingPitcher(player); } else if (opponentScore < ret.getScore()) { //winning pitcher ret.setWinner(true); String winningPitcher = line.get(GameLogColumn.WINNING_PITCHER_ID); GamePlayer player = players.get(winningPitcher); if (player == null) { player = new GamePlayer(); player.setPlayer(retroContext.getPersons().get(winningPitcher)); if (player.getPlayer() == null) { throw new RetrosheetRuntimeException("Can't find winning pitcher: " + winningPitcher); } players.put(winningPitcher, player); ret.getGamePlayers().add(player); } ret.setDecidingPitcher(player); //saving pitcher String savingPitcher = line.get(GameLogColumn.SAVING_PITCHER_ID); if (savingPitcher != null) { if (!savingPitcher.equals(winningPitcher)) { player = players.get(savingPitcher); if (player == null) { player = new GamePlayer(); player.setPlayer(retroContext.getPersons().get(savingPitcher)); if (player.getPlayer() == null) { throw new RetrosheetRuntimeException("Can't find saving pitcher: " + winningPitcher); } players.put(savingPitcher, player); ret.getGamePlayers().add(player); } } ret.setSavingPitcher(player); } //game winning rbi (don't need to add, can be figured out) String gameWinner = line.get(GameLogColumn.GAME_WINNING_RBI_PLAYER_ID); if (gameWinner != null) { player = players.get(gameWinner); if (player == null) { player = new GamePlayer(); player.setPlayer(retroContext.getPersons().get(gameWinner)); if (player.getPlayer() == null) { throw new RetrosheetRuntimeException("Can't find winning rbi guy: " + winningPitcher); } players.put(gameWinner, player); ret.getGamePlayers().add(player); } } } else { throw new RetrosheetRuntimeException("Game ended in tie, " + "can't tell winning/losing pitcher"); } return ret; } }