fpdb/pyfpdb/HandHistoryConverter.py

708 lines
27 KiB
Python
Raw Normal View History

#!/usr/bin/env python
2009-08-06 19:26:55 +02:00
# -*- coding: utf-8 -*-
2011-03-10 06:16:31 +01:00
#Copyright 2008-2011 Carl Gherardi
#This program is free software: you can redistribute it and/or modify
#it under the terms of the GNU Affero General Public License as published by
#the Free Software Foundation, version 3 of the License.
#
#This program is distributed in the hope that it will be useful,
#but WITHOUT ANY WARRANTY; without even the implied warranty of
#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
#GNU General Public License for more details.
#
#You should have received a copy of the GNU Affero General Public License
#along with this program. If not, see <http://www.gnu.org/licenses/>.
#In the "official" distribution you can find the license in agpl-3.0.txt.
2010-09-23 08:10:30 +02:00
import L10n
_ = L10n.get_translation()
import re
import sys
import traceback
from optparse import OptionParser
import os
import os.path
import xml.dom.minidom
import codecs
from decimal_wrapper import Decimal
import operator
from xml.dom.minidom import Node
import time
import datetime
2011-02-26 02:40:04 +01:00
try:
from pytz import timezone
import pytz
except ImportError:
print _("ImportError: Unable to import PYTZ library. Please install PYTZ from http://pypi.python.org/pypi/pytz/")
raw_input(_("Press ENTER to continue."))
exit()
import logging
# logging has been set up in fpdb.py or HUD_main.py, use their settings:
log = logging.getLogger("parser")
import Hand
from Exceptions import FpdbParseError
2009-11-10 01:30:23 +01:00
import Configuration
import pygtk
import gtk
2009-03-12 13:24:23 +01:00
class HandHistoryConverter():
READ_CHUNK_SIZE = 10000 # bytes to read at a time from file in tail mode
# filetype can be "text" or "xml"
# so far always "text"
# subclass HHC_xml for xml parsing
filetype = "text"
# codepage indicates the encoding of the text file.
# cp1252 is a safe default
# "utf_8" is more likely if there are funny characters
codepage = "cp1252"
2010-08-21 22:10:21 +02:00
re_tzOffset = re.compile('^\w+[+-]\d{4}$')
First attempt to parsing summary files from FTP modified: FulltiltToFpdb.py * Modified re_HandInfo regex to take into account Matrix Tournament Hands Histories * Add Regex for Tourney Summaries files parsing * New methods : readSummaryInfo, determineTourneyType, getPlayersPositionsAndWinnings modified: HandHistoryConverter.py * Add a Tourney object in the attributes to allow storing the summary info retrieved and the ability to hand it over after parsing * Add a new attribut parsedObjectType (string : "HH" or "Summary") * In follow = False mode : read the first line to swicth between HH & Summary parsing * TO DO : Deal with parsing summary files in follow = True mode * New methods added : isSummary, getParsedObjectType (to be called in fpdb_import), readSummaryInfo : abstract (to be implemented in each specific HHC), getTourney (returns the new attribute) new file: Tourney.py * New object * Lots of attributes regarding the tourney info (buy-in, fee, entries, speed, Rebuy/add-on info, starting chips, KnockOut info, isHeadsUp, isShootout, isMatrix, ... * List of players with finishing positions (when available) and winnings (when available) * Methods : ** addPlayer(self, rank, name, winnings) ** incrementPlayerWinnings(self, name, additionnalWinnings): used for KO tourneys when KO occured (for Hero only) ** calculatePayinAmount : unused yet, should be necessary for DB storage ** some Hand methods copied that are still to be done including : assemble and insert ** TO DO : write the insert method for the object to be stored in DB. This will have to deal with the fact that the DB write can occur both before (Bulk Import) or after the HH file info has been stored (tourney might or might not already exist) modified: fpdb_import.py * import_file_dict modified : ** after the construction of the hhc, it now calls the new getParsedObjectType method of HHC in order to know what has been parsed by the HHC ** If it's a hand history file (actual expected behaviour) : do as before ** If it's a summary file, gets the tourney object that has been built from the hhc and calls the insert method on it (similar to NEWIMPORT=True for HH)
2009-08-22 00:09:34 +02:00
# maybe archive params should be one archive param, then call method in specific converter. if archive: convert_archive()
def __init__( self, config, in_path = '-', out_path = '-', follow=False, index=0
, autostart=True, starsArchive=False, ftpArchive=False, sitename="PokerStars" ):
"""\
in_path (default '-' = sys.stdin)
out_path (default '-' = sys.stdout)
follow : whether to tail -f the input"""
self.config = config
self.import_parameters = self.config.get_import_parameters()
self.sitename = sitename
#log = Configuration.get_logger("logging.conf", "parser", log_dir=self.config.dir_log)
log.info("HandHistory init - %s site, %s subclass, in_path '%s'; out_path '%s'"
% (self.sitename, self.__class__, in_path, out_path) ) # should use self.filter, not self.sitename
2010-02-01 14:48:28 +01:00
self.index = index
self.starsArchive = starsArchive
self.ftpArchive = ftpArchive
2009-07-15 00:34:47 +02:00
self.in_path = in_path
self.out_path = out_path
self.processedHands = []
self.numHands = 0
self.numErrors = 0
First attempt to parsing summary files from FTP modified: FulltiltToFpdb.py * Modified re_HandInfo regex to take into account Matrix Tournament Hands Histories * Add Regex for Tourney Summaries files parsing * New methods : readSummaryInfo, determineTourneyType, getPlayersPositionsAndWinnings modified: HandHistoryConverter.py * Add a Tourney object in the attributes to allow storing the summary info retrieved and the ability to hand it over after parsing * Add a new attribut parsedObjectType (string : "HH" or "Summary") * In follow = False mode : read the first line to swicth between HH & Summary parsing * TO DO : Deal with parsing summary files in follow = True mode * New methods added : isSummary, getParsedObjectType (to be called in fpdb_import), readSummaryInfo : abstract (to be implemented in each specific HHC), getTourney (returns the new attribute) new file: Tourney.py * New object * Lots of attributes regarding the tourney info (buy-in, fee, entries, speed, Rebuy/add-on info, starting chips, KnockOut info, isHeadsUp, isShootout, isMatrix, ... * List of players with finishing positions (when available) and winnings (when available) * Methods : ** addPlayer(self, rank, name, winnings) ** incrementPlayerWinnings(self, name, additionnalWinnings): used for KO tourneys when KO occured (for Hero only) ** calculatePayinAmount : unused yet, should be necessary for DB storage ** some Hand methods copied that are still to be done including : assemble and insert ** TO DO : write the insert method for the object to be stored in DB. This will have to deal with the fact that the DB write can occur both before (Bulk Import) or after the HH file info has been stored (tourney might or might not already exist) modified: fpdb_import.py * import_file_dict modified : ** after the construction of the hhc, it now calls the new getParsedObjectType method of HHC in order to know what has been parsed by the HHC ** If it's a hand history file (actual expected behaviour) : do as before ** If it's a summary file, gets the tourney object that has been built from the hhc and calls the insert method on it (similar to NEWIMPORT=True for HH)
2009-08-22 00:09:34 +02:00
# Tourney object used to store TourneyInfo when called to deal with a Summary file
self.tourney = None
2010-02-01 14:48:28 +01:00
2009-07-15 00:34:47 +02:00
if in_path == '-':
self.in_fh = sys.stdin
self.out_fh = get_out_fh(out_path, self.import_parameters)
2009-07-15 00:34:47 +02:00
self.follow = follow
self.compiledPlayers = set()
self.maxseats = 10
2010-02-01 14:48:28 +01:00
First attempt to parsing summary files from FTP modified: FulltiltToFpdb.py * Modified re_HandInfo regex to take into account Matrix Tournament Hands Histories * Add Regex for Tourney Summaries files parsing * New methods : readSummaryInfo, determineTourneyType, getPlayersPositionsAndWinnings modified: HandHistoryConverter.py * Add a Tourney object in the attributes to allow storing the summary info retrieved and the ability to hand it over after parsing * Add a new attribut parsedObjectType (string : "HH" or "Summary") * In follow = False mode : read the first line to swicth between HH & Summary parsing * TO DO : Deal with parsing summary files in follow = True mode * New methods added : isSummary, getParsedObjectType (to be called in fpdb_import), readSummaryInfo : abstract (to be implemented in each specific HHC), getTourney (returns the new attribute) new file: Tourney.py * New object * Lots of attributes regarding the tourney info (buy-in, fee, entries, speed, Rebuy/add-on info, starting chips, KnockOut info, isHeadsUp, isShootout, isMatrix, ... * List of players with finishing positions (when available) and winnings (when available) * Methods : ** addPlayer(self, rank, name, winnings) ** incrementPlayerWinnings(self, name, additionnalWinnings): used for KO tourneys when KO occured (for Hero only) ** calculatePayinAmount : unused yet, should be necessary for DB storage ** some Hand methods copied that are still to be done including : assemble and insert ** TO DO : write the insert method for the object to be stored in DB. This will have to deal with the fact that the DB write can occur both before (Bulk Import) or after the HH file info has been stored (tourney might or might not already exist) modified: fpdb_import.py * import_file_dict modified : ** after the construction of the hhc, it now calls the new getParsedObjectType method of HHC in order to know what has been parsed by the HHC ** If it's a hand history file (actual expected behaviour) : do as before ** If it's a summary file, gets the tourney object that has been built from the hhc and calls the insert method on it (similar to NEWIMPORT=True for HH)
2009-08-22 00:09:34 +02:00
self.status = True
self.parsedObjectType = "HH" #default behaviour : parsing HH files, can be "Summary" if the parsing encounters a Summary File
2008-12-06 15:15:41 +01:00
if autostart:
self.start()
2008-12-06 15:15:41 +01:00
def __str__(self):
2009-07-15 00:34:47 +02:00
return """
HandHistoryConverter: '%(sitename)s'
filetype '%(filetype)s'
in_path '%(in_path)s'
out_path '%(out_path)s'
follow '%(follow)s'
2010-02-01 14:48:28 +01:00
""" % locals()
2008-12-06 15:15:41 +01:00
2009-03-12 13:24:23 +01:00
def start(self):
"""Process a hand at a time from the input specified by in_path.
If in follow mode, wait for more data to turn up.
Otherwise, finish at EOF.
"""
while gtk.events_pending():
gtk.main_iteration(False)
starttime = time.time()
if not self.sanityCheck():
2010-08-14 18:51:42 +02:00
log.warning(_("Failed sanity check"))
return
try:
self.numHands = 0
self.numErrors = 0
if self.follow:
2010-02-01 14:48:28 +01:00
#TODO: See how summary files can be handled on the fly (here they should be rejected as before)
2010-08-14 18:51:42 +02:00
log.info(_("Tailing '%s'") % self.in_path)
for handText in self.tailHands():
try:
self.processHand(handText)
self.numHands += 1
except FpdbParseError, e:
self.numErrors += 1
2010-08-14 18:51:42 +02:00
log.warning(_("HHC.start(follow): processHand failed: Exception msg: '%s'") % e)
log.debug(handText)
else:
handsList = self.allHandsAsList()
2010-08-21 14:44:01 +02:00
log.debug( _("handsList is ") + str(handsList) )
log.info("Parsing %d hands" % len(handsList))
First attempt to parsing summary files from FTP modified: FulltiltToFpdb.py * Modified re_HandInfo regex to take into account Matrix Tournament Hands Histories * Add Regex for Tourney Summaries files parsing * New methods : readSummaryInfo, determineTourneyType, getPlayersPositionsAndWinnings modified: HandHistoryConverter.py * Add a Tourney object in the attributes to allow storing the summary info retrieved and the ability to hand it over after parsing * Add a new attribut parsedObjectType (string : "HH" or "Summary") * In follow = False mode : read the first line to swicth between HH & Summary parsing * TO DO : Deal with parsing summary files in follow = True mode * New methods added : isSummary, getParsedObjectType (to be called in fpdb_import), readSummaryInfo : abstract (to be implemented in each specific HHC), getTourney (returns the new attribute) new file: Tourney.py * New object * Lots of attributes regarding the tourney info (buy-in, fee, entries, speed, Rebuy/add-on info, starting chips, KnockOut info, isHeadsUp, isShootout, isMatrix, ... * List of players with finishing positions (when available) and winnings (when available) * Methods : ** addPlayer(self, rank, name, winnings) ** incrementPlayerWinnings(self, name, additionnalWinnings): used for KO tourneys when KO occured (for Hero only) ** calculatePayinAmount : unused yet, should be necessary for DB storage ** some Hand methods copied that are still to be done including : assemble and insert ** TO DO : write the insert method for the object to be stored in DB. This will have to deal with the fact that the DB write can occur both before (Bulk Import) or after the HH file info has been stored (tourney might or might not already exist) modified: fpdb_import.py * import_file_dict modified : ** after the construction of the hhc, it now calls the new getParsedObjectType method of HHC in order to know what has been parsed by the HHC ** If it's a hand history file (actual expected behaviour) : do as before ** If it's a summary file, gets the tourney object that has been built from the hhc and calls the insert method on it (similar to NEWIMPORT=True for HH)
2009-08-22 00:09:34 +02:00
# Determine if we're dealing with a HH file or a Summary file
# quick fix : empty files make the handsList[0] fail ==> If empty file, go on with HH parsing
if len(handsList) == 0 or self.isSummary(handsList[0]) == False:
First attempt to parsing summary files from FTP modified: FulltiltToFpdb.py * Modified re_HandInfo regex to take into account Matrix Tournament Hands Histories * Add Regex for Tourney Summaries files parsing * New methods : readSummaryInfo, determineTourneyType, getPlayersPositionsAndWinnings modified: HandHistoryConverter.py * Add a Tourney object in the attributes to allow storing the summary info retrieved and the ability to hand it over after parsing * Add a new attribut parsedObjectType (string : "HH" or "Summary") * In follow = False mode : read the first line to swicth between HH & Summary parsing * TO DO : Deal with parsing summary files in follow = True mode * New methods added : isSummary, getParsedObjectType (to be called in fpdb_import), readSummaryInfo : abstract (to be implemented in each specific HHC), getTourney (returns the new attribute) new file: Tourney.py * New object * Lots of attributes regarding the tourney info (buy-in, fee, entries, speed, Rebuy/add-on info, starting chips, KnockOut info, isHeadsUp, isShootout, isMatrix, ... * List of players with finishing positions (when available) and winnings (when available) * Methods : ** addPlayer(self, rank, name, winnings) ** incrementPlayerWinnings(self, name, additionnalWinnings): used for KO tourneys when KO occured (for Hero only) ** calculatePayinAmount : unused yet, should be necessary for DB storage ** some Hand methods copied that are still to be done including : assemble and insert ** TO DO : write the insert method for the object to be stored in DB. This will have to deal with the fact that the DB write can occur both before (Bulk Import) or after the HH file info has been stored (tourney might or might not already exist) modified: fpdb_import.py * import_file_dict modified : ** after the construction of the hhc, it now calls the new getParsedObjectType method of HHC in order to know what has been parsed by the HHC ** If it's a hand history file (actual expected behaviour) : do as before ** If it's a summary file, gets the tourney object that has been built from the hhc and calls the insert method on it (similar to NEWIMPORT=True for HH)
2009-08-22 00:09:34 +02:00
self.parsedObjectType = "HH"
for handText in handsList:
try:
self.processedHands.append(self.processHand(handText))
except FpdbParseError, e:
self.numErrors += 1
2010-08-14 18:51:42 +02:00
log.warning(_("HHC.start(): processHand failed: Exception msg: '%s'") % e)
First attempt to parsing summary files from FTP modified: FulltiltToFpdb.py * Modified re_HandInfo regex to take into account Matrix Tournament Hands Histories * Add Regex for Tourney Summaries files parsing * New methods : readSummaryInfo, determineTourneyType, getPlayersPositionsAndWinnings modified: HandHistoryConverter.py * Add a Tourney object in the attributes to allow storing the summary info retrieved and the ability to hand it over after parsing * Add a new attribut parsedObjectType (string : "HH" or "Summary") * In follow = False mode : read the first line to swicth between HH & Summary parsing * TO DO : Deal with parsing summary files in follow = True mode * New methods added : isSummary, getParsedObjectType (to be called in fpdb_import), readSummaryInfo : abstract (to be implemented in each specific HHC), getTourney (returns the new attribute) new file: Tourney.py * New object * Lots of attributes regarding the tourney info (buy-in, fee, entries, speed, Rebuy/add-on info, starting chips, KnockOut info, isHeadsUp, isShootout, isMatrix, ... * List of players with finishing positions (when available) and winnings (when available) * Methods : ** addPlayer(self, rank, name, winnings) ** incrementPlayerWinnings(self, name, additionnalWinnings): used for KO tourneys when KO occured (for Hero only) ** calculatePayinAmount : unused yet, should be necessary for DB storage ** some Hand methods copied that are still to be done including : assemble and insert ** TO DO : write the insert method for the object to be stored in DB. This will have to deal with the fact that the DB write can occur both before (Bulk Import) or after the HH file info has been stored (tourney might or might not already exist) modified: fpdb_import.py * import_file_dict modified : ** after the construction of the hhc, it now calls the new getParsedObjectType method of HHC in order to know what has been parsed by the HHC ** If it's a hand history file (actual expected behaviour) : do as before ** If it's a summary file, gets the tourney object that has been built from the hhc and calls the insert method on it (similar to NEWIMPORT=True for HH)
2009-08-22 00:09:34 +02:00
log.debug(handText)
self.numHands = len(handsList)
First attempt to parsing summary files from FTP modified: FulltiltToFpdb.py * Modified re_HandInfo regex to take into account Matrix Tournament Hands Histories * Add Regex for Tourney Summaries files parsing * New methods : readSummaryInfo, determineTourneyType, getPlayersPositionsAndWinnings modified: HandHistoryConverter.py * Add a Tourney object in the attributes to allow storing the summary info retrieved and the ability to hand it over after parsing * Add a new attribut parsedObjectType (string : "HH" or "Summary") * In follow = False mode : read the first line to swicth between HH & Summary parsing * TO DO : Deal with parsing summary files in follow = True mode * New methods added : isSummary, getParsedObjectType (to be called in fpdb_import), readSummaryInfo : abstract (to be implemented in each specific HHC), getTourney (returns the new attribute) new file: Tourney.py * New object * Lots of attributes regarding the tourney info (buy-in, fee, entries, speed, Rebuy/add-on info, starting chips, KnockOut info, isHeadsUp, isShootout, isMatrix, ... * List of players with finishing positions (when available) and winnings (when available) * Methods : ** addPlayer(self, rank, name, winnings) ** incrementPlayerWinnings(self, name, additionnalWinnings): used for KO tourneys when KO occured (for Hero only) ** calculatePayinAmount : unused yet, should be necessary for DB storage ** some Hand methods copied that are still to be done including : assemble and insert ** TO DO : write the insert method for the object to be stored in DB. This will have to deal with the fact that the DB write can occur both before (Bulk Import) or after the HH file info has been stored (tourney might or might not already exist) modified: fpdb_import.py * import_file_dict modified : ** after the construction of the hhc, it now calls the new getParsedObjectType method of HHC in order to know what has been parsed by the HHC ** If it's a hand history file (actual expected behaviour) : do as before ** If it's a summary file, gets the tourney object that has been built from the hhc and calls the insert method on it (similar to NEWIMPORT=True for HH)
2009-08-22 00:09:34 +02:00
endtime = time.time()
2010-08-14 18:51:42 +02:00
log.info(_("Read %d hands (%d failed) in %.3f seconds") % (self.numHands, self.numErrors, endtime - starttime))
First attempt to parsing summary files from FTP modified: FulltiltToFpdb.py * Modified re_HandInfo regex to take into account Matrix Tournament Hands Histories * Add Regex for Tourney Summaries files parsing * New methods : readSummaryInfo, determineTourneyType, getPlayersPositionsAndWinnings modified: HandHistoryConverter.py * Add a Tourney object in the attributes to allow storing the summary info retrieved and the ability to hand it over after parsing * Add a new attribut parsedObjectType (string : "HH" or "Summary") * In follow = False mode : read the first line to swicth between HH & Summary parsing * TO DO : Deal with parsing summary files in follow = True mode * New methods added : isSummary, getParsedObjectType (to be called in fpdb_import), readSummaryInfo : abstract (to be implemented in each specific HHC), getTourney (returns the new attribute) new file: Tourney.py * New object * Lots of attributes regarding the tourney info (buy-in, fee, entries, speed, Rebuy/add-on info, starting chips, KnockOut info, isHeadsUp, isShootout, isMatrix, ... * List of players with finishing positions (when available) and winnings (when available) * Methods : ** addPlayer(self, rank, name, winnings) ** incrementPlayerWinnings(self, name, additionnalWinnings): used for KO tourneys when KO occured (for Hero only) ** calculatePayinAmount : unused yet, should be necessary for DB storage ** some Hand methods copied that are still to be done including : assemble and insert ** TO DO : write the insert method for the object to be stored in DB. This will have to deal with the fact that the DB write can occur both before (Bulk Import) or after the HH file info has been stored (tourney might or might not already exist) modified: fpdb_import.py * import_file_dict modified : ** after the construction of the hhc, it now calls the new getParsedObjectType method of HHC in order to know what has been parsed by the HHC ** If it's a hand history file (actual expected behaviour) : do as before ** If it's a summary file, gets the tourney object that has been built from the hhc and calls the insert method on it (similar to NEWIMPORT=True for HH)
2009-08-22 00:09:34 +02:00
else:
self.parsedObjectType = "Summary"
summaryParsingStatus = self.readSummaryInfo(handsList)
endtime = time.time()
if summaryParsingStatus :
2010-08-14 18:51:42 +02:00
log.info(_("Summary file '%s' correctly parsed (took %.3f seconds)") % (self.in_path, endtime - starttime))
2010-02-01 14:48:28 +01:00
else :
2010-08-14 18:51:42 +02:00
log.warning(_("Error converting summary file '%s' (took %.3f seconds)") % (self.in_path, endtime - starttime))
First attempt to parsing summary files from FTP modified: FulltiltToFpdb.py * Modified re_HandInfo regex to take into account Matrix Tournament Hands Histories * Add Regex for Tourney Summaries files parsing * New methods : readSummaryInfo, determineTourneyType, getPlayersPositionsAndWinnings modified: HandHistoryConverter.py * Add a Tourney object in the attributes to allow storing the summary info retrieved and the ability to hand it over after parsing * Add a new attribut parsedObjectType (string : "HH" or "Summary") * In follow = False mode : read the first line to swicth between HH & Summary parsing * TO DO : Deal with parsing summary files in follow = True mode * New methods added : isSummary, getParsedObjectType (to be called in fpdb_import), readSummaryInfo : abstract (to be implemented in each specific HHC), getTourney (returns the new attribute) new file: Tourney.py * New object * Lots of attributes regarding the tourney info (buy-in, fee, entries, speed, Rebuy/add-on info, starting chips, KnockOut info, isHeadsUp, isShootout, isMatrix, ... * List of players with finishing positions (when available) and winnings (when available) * Methods : ** addPlayer(self, rank, name, winnings) ** incrementPlayerWinnings(self, name, additionnalWinnings): used for KO tourneys when KO occured (for Hero only) ** calculatePayinAmount : unused yet, should be necessary for DB storage ** some Hand methods copied that are still to be done including : assemble and insert ** TO DO : write the insert method for the object to be stored in DB. This will have to deal with the fact that the DB write can occur both before (Bulk Import) or after the HH file info has been stored (tourney might or might not already exist) modified: fpdb_import.py * import_file_dict modified : ** after the construction of the hhc, it now calls the new getParsedObjectType method of HHC in order to know what has been parsed by the HHC ** If it's a hand history file (actual expected behaviour) : do as before ** If it's a summary file, gets the tourney object that has been built from the hhc and calls the insert method on it (similar to NEWIMPORT=True for HH)
2009-08-22 00:09:34 +02:00
except IOError, ioe:
2010-08-14 18:51:42 +02:00
log.exception(_("Error converting '%s'") % self.in_path)
finally:
if self.out_fh != sys.stdout:
self.out_fh.close()
def tailHands(self):
"""Generator of handTexts from a tailed file:
Tail the in_path file and yield handTexts separated by re_SplitHands.
This requires a regex that greedily groups and matches the 'splitter' between hands,
which it expects to find at self.re_TailSplitHands -- see for e.g. Everleaf.py.
"""
2009-11-03 21:14:20 +01:00
if self.in_path == '-':
raise StopIteration
interval = 1.0 # seconds to sleep between reads for new data
2009-03-12 13:24:23 +01:00
fd = codecs.open(self.in_path,'r', self.codepage)
data = ''
while 1:
where = fd.tell()
newdata = fd.read(self.READ_CHUNK_SIZE)
if not newdata:
fd_results = os.fstat(fd.fileno())
try:
2009-03-12 13:24:23 +01:00
st_results = os.stat(self.in_path)
except OSError:
st_results = fd_results
if st_results[1] == fd_results[1]:
time.sleep(interval)
fd.seek(where)
else:
2010-08-14 18:51:42 +02:00
log.debug(_("%s changed inode numbers from %d to %d") % (self.in_path, fd_results[1], st_results[1]))
2009-03-12 13:24:23 +01:00
fd = codecs.open(self.in_path, 'r', self.codepage)
fd.seek(where)
else:
# yield hands
data = data + newdata
result = self.re_TailSplitHands.split(data)
result = iter(result)
data = ''
# --x data (- is bit of splitter, x is paragraph) yield,...,keep
# [,--,x] result of re.split (with group around splitter)
# ,x our output: yield nothing, keep x
#
# --x--x [,--,x,--,x] x,x
# -x--x [-x,--,x] x,x
# x- [x-] ,x-
# x-- [x,--,] x,--
# x--x [x,--,x] x,x
# x--x-- [x,--,x,--,] x,x,--
2010-02-01 14:48:28 +01:00
# The length is always odd.
# 'odd' indices are always splitters.
# 'even' indices are always paragraphs or ''
# We want to discard all the ''
# We want to discard splitters unless the final item is '' (because the splitter could grow with new data)
# We want to yield all paragraphs followed by a splitter, i.e. all even indices except the last.
for para in result:
try:
result.next()
splitter = True
except StopIteration:
splitter = False
if splitter: # para is followed by a splitter
if para: yield para # para not ''
else:
data = para # keep final partial paragraph
def allHandsAsList(self):
"""Return a list of handtexts in the file at self.in_path"""
#TODO : any need for this to be generator? e.g. stars support can email one huge file of all hands in a year. Better to read bit by bit than all at once.
self.readFile()
self.obs = self.obs.strip()
self.obs = self.obs.replace('\r\n', '\n')
# maybe archive params should be one archive param, then call method in specific converter?
# if self.archive:
# self.obs = self.convert_archive(self.obs)
if self.starsArchive == True:
2010-08-14 18:51:42 +02:00
log.debug(_("Converting starsArchive format to readable"))
m = re.compile('^Hand #\d+', re.MULTILINE)
self.obs = m.sub('', self.obs)
if self.ftpArchive == True:
2010-08-14 18:51:42 +02:00
log.debug(_("Converting ftpArchive format to readable"))
# Remove ******************** # 1 *************************
m = re.compile('\*{20}\s#\s\d+\s\*{20,25}\s+', re.MULTILINE)
self.obs = m.sub('', self.obs)
if self.obs is None or self.obs == "":
log.error(_("Read no hands."))
return []
handlist = re.split(self.re_SplitHands, self.obs)
# Some HH formats leave dangling text after the split
# ie. </game> (split) </session>EOL
# Remove this dangler if less than 50 characters and warn in the log
if len(handlist[-1]) <= 50:
handlist.pop()
log.warn(_("Removing text < 50 characters"))
return handlist
2010-02-01 14:48:28 +01:00
def processHand(self, handText):
gametype = self.determineGameType(handText)
log.debug("gametype %s" % gametype)
hand = None
2009-11-29 09:54:15 +01:00
l = None
2010-02-01 14:48:28 +01:00
if gametype is None:
gametype = "unmatched"
# TODO: not ideal, just trying to not error. Throw ParseException?
self.numErrors += 1
else:
# See if gametype is supported.
type = gametype['type']
base = gametype['base']
limit = gametype['limitType']
l = [type] + [base] + [limit]
if l in self.readSupportedGames():
if gametype['base'] == 'hold':
log.debug("hand = Hand.HoldemOmahaHand(self, self.sitename, gametype, handtext)")
hand = Hand.HoldemOmahaHand(self.config, self, self.sitename, gametype, handText)
elif gametype['base'] == 'stud':
hand = Hand.StudHand(self.config, self, self.sitename, gametype, handText)
elif gametype['base'] == 'draw':
hand = Hand.DrawHand(self.config, self, self.sitename, gametype, handText)
else:
log.error(_("Unsupported game type: %s") % gametype)
raise FpdbParseError(_("Unsupported game type: %s") % gametype)
if hand:
#hand.writeHand(self.out_fh)
return hand
else:
log.error(_("Unsupported game type: %s") % gametype)
# TODO: pity we don't know the HID at this stage. Log the entire hand?
2008-12-06 15:15:41 +01:00
# These functions are parse actions that may be overridden by the inheriting class
# This function should return a list of lists looking like:
# return [["ring", "hold", "nl"], ["tour", "hold", "nl"]]
# Showing all supported games limits and types
2010-02-01 14:48:28 +01:00
2008-12-06 15:15:41 +01:00
def readSupportedGames(self): abstract
# should return a list
# type base limit
# [ ring, hold, nl , sb, bb ]
# Valid types specified in docs/tabledesign.html in Gametypes
def determineGameType(self, handText): abstract
"""return dict with keys/values:
'type' in ('ring', 'tour')
'limitType' in ('nl', 'cn', 'pl', 'cp', 'fl')
'base' in ('hold', 'stud', 'draw')
'category' in ('holdem', 'omahahi', omahahilo', 'razz', 'studhi', 'studhilo', 'fivedraw', '27_1draw', '27_3draw', 'badugi')
'hilo' in ('h','l','s')
'smallBlind' int?
'bigBlind' int?
'smallBet'
'bigBet'
'currency' in ('USD', 'EUR', 'T$', <countrycode>)
or None if we fail to get the info """
#TODO: which parts are optional/required?
2008-12-06 15:15:41 +01:00
def readHandInfo(self, hand): abstract
2010-08-20 12:52:00 +02:00
"""Read and set information about the hand being dealt, and set the correct
variables in the Hand object 'hand
* hand.startTime - a datetime object
* hand.handid - The site identified for the hand - a string.
* hand.tablename
* hand.buttonpos
* hand.maxseats
* hand.mixed
Tournament fields:
* hand.tourNo - The site identified tournament id as appropriate - a string.
* hand.buyin
* hand.fee
* hand.buyinCurrency
* hand.koBounty
* hand.isKO
* hand.level
"""
#TODO: which parts are optional/required?
2008-12-06 15:15:41 +01:00
def readPlayerStacks(self, hand): abstract
2010-08-20 13:48:37 +02:00
"""This function is for identifying players at the table, and to pass the
information on to 'hand' via Hand.addPlayer(seat, name, chips)
At the time of writing the reference function in the PS converter is:
log.debug("readPlayerStacks")
m = self.re_PlayerInfo.finditer(hand.handText)
for a in m:
hand.addPlayer(int(a.group('SEAT')), a.group('PNAME'), a.group('CASH'))
Which is pretty simple because the hand history format is consistent. Other hh formats aren't so nice.
This is the appropriate place to identify players that are sitting out and ignore them
*** NOTE: You may find this is a more appropriate place to set hand.maxseats ***
"""
2010-02-01 14:48:28 +01:00
def compilePlayerRegexs(self): abstract
2010-08-20 14:09:44 +02:00
"""Compile dynamic regexes -- compile player dependent regexes.
Depending on the ambiguity of lines you may need to match, and the complexity of
player names - we found that we needed to recompile some regexes for player actions so that they actually contained the player names.
eg.
We need to match the ante line:
<Player> antes $1.00
But <Player> is actually named
YesI antes $4000 - A perfectly legal playername
Giving:
YesI antes $4000 antes $1.00
Which without care in your regexes most people would match 'YesI' and not 'YesI antes $4000'
"""
2010-02-01 14:48:28 +01:00
2008-12-06 15:15:41 +01:00
# Needs to return a MatchObject with group names identifying the streets into the Hand object
# so groups are called by street names 'PREFLOP', 'FLOP', 'STREET2' etc
# blinds are done seperately
2008-12-06 15:15:41 +01:00
def markStreets(self, hand): abstract
2010-08-20 15:05:33 +02:00
"""For dividing the handText into sections.
The function requires you to pass a MatchObject with groups specifically labeled with
the 'correct' street names.
The Hand object will use the various matches for assigning actions to the correct streets.
Flop Based Games:
PREFLOP, FLOP, TURN, RIVER
Draw Based Games:
PREDEAL, DEAL, DRAWONE, DRAWTWO, DRAWTHREE
Stud Based Games:
ANTES, THIRD, FOURTH, FIFTH, SIXTH, SEVENTH
The Stars HHC has a good reference implementation
"""
2008-12-06 15:15:41 +01:00
#Needs to return a list in the format
2010-02-01 14:48:28 +01:00
# ['player1name', 'player2name', ...] where player1name is the sb and player2name is bb,
2008-12-06 15:15:41 +01:00
# addtional players are assumed to post a bb oop
def readBlinds(self, hand): abstract
2010-08-21 11:35:32 +02:00
"""Function for reading the various blinds from the hand history.
Pass any small blind to hand.addBlind(<name>, "small blind", <value>)
- unless it is a single dead small blind then use:
hand.addBlind(<name>, 'secondsb', <value>)
Pass any big blind to hand.addBlind(<name>, "big blind", <value>)
Pass any play posting both big and small blinds to hand.addBlind(<name>, 'both', <vale>)
"""
def readAntes(self, hand): abstract
2010-08-21 11:35:32 +02:00
"""Function for reading the antes from the hand history and passing the hand.addAnte"""
def readBringIn(self, hand): abstract
def readButton(self, hand): abstract
2008-12-06 15:15:41 +01:00
def readHeroCards(self, hand): abstract
2009-02-25 11:32:12 +01:00
def readPlayerCards(self, hand, street): abstract
2008-12-06 15:15:41 +01:00
def readAction(self, hand, street): abstract
2008-12-09 16:32:37 +01:00
def readCollectPot(self, hand): abstract
def readShownCards(self, hand): abstract
# Some sites do odd stuff that doesn't fall in to the normal HH parsing.
2010-02-01 14:48:28 +01:00
# e.g., FTP doesn't put mixed game info in the HH, but puts in in the
# file name. Use readOther() to clean up those messes.
def readOther(self, hand): pass
2010-02-01 14:48:28 +01:00
2008-12-06 15:15:41 +01:00
# Some sites don't report the rake. This will be called at the end of the hand after the pot total has been calculated
# an inheriting class can calculate it for the specific site if need be.
def getRake(self, hand):
hand.rake = hand.totalpot - hand.totalcollected # * Decimal('0.05') # probably not quite right
2010-02-01 14:48:28 +01:00
2008-12-06 15:15:41 +01:00
def sanityCheck(self):
"""Check we aren't going to do some stupid things"""
sane = False
2008-12-06 15:15:41 +01:00
base_w = False
# Make sure input and output files are different or we'll overwrite the source file
if True: # basically.. I don't know
sane = True
2010-02-01 14:48:28 +01:00
2009-11-03 21:14:20 +01:00
if self.in_path != '-' and self.out_path == self.in_path:
2010-08-14 18:51:42 +02:00
print _("HH Sanity Check: output and input files are the same, check config")
sane = False
2008-12-06 15:15:41 +01:00
return sane
# Functions not necessary to implement in sub class
def setFileType(self, filetype = "text", codepage='utf8'):
2008-12-06 15:15:41 +01:00
self.filetype = filetype
self.codepage = codepage
2008-12-06 15:15:41 +01:00
def __listof(self, x):
2009-11-03 21:14:20 +01:00
if isinstance(x, list) or isinstance(x, tuple):
return x
else:
return [x]
def readFile(self):
"""Open in_path according to self.codepage. Exceptions caught further up"""
2010-02-01 14:48:28 +01:00
2009-11-03 21:14:20 +01:00
if self.filetype == "text":
if self.in_path == '-':
# read from stdin
2010-08-14 18:51:42 +02:00
log.debug(_("Reading stdin with %s") % self.codepage) # is this necessary? or possible? or what?
in_fh = codecs.getreader('cp1252')(sys.stdin)
else:
for kodec in self.__listof(self.codepage):
#print "trying", kodec
try:
in_fh = codecs.open(self.in_path, 'r', kodec)
whole_file = in_fh.read()
in_fh.close()
self.obs = whole_file[self.index:]
self.index = len(whole_file)
break
except:
pass
else:
2010-08-14 18:51:42 +02:00
print _("unable to read file with any codec in list!"), self.in_path
self.obs = ""
2009-11-03 21:14:20 +01:00
elif self.filetype == "xml":
doc = xml.dom.minidom.parse(filename)
self.doc = doc
2008-12-06 15:15:41 +01:00
def guessMaxSeats(self, hand):
"""Return a guess at maxseats when not specified in HH."""
# if some other code prior to this has already set it, return it
if self.maxseats > 1 and self.maxseats < 11:
return self.maxseats
mo = self.maxOccSeat(hand)
if mo == 10: return 10 #that was easy
if hand.gametype['base'] == 'stud':
if mo <= 8: return 8
2010-02-01 14:48:28 +01:00
else: return mo
if hand.gametype['base'] == 'draw':
if mo <= 6: return 6
else: return mo
if mo == 2: return 2
if mo <= 6: return 6
return 10
def maxOccSeat(self, hand):
max = 0
for player in hand.players:
2009-11-03 21:14:20 +01:00
if player[0] > max:
max = player[0]
return max
def getStatus(self):
#TODO: Return a status of true if file processed ok
First attempt to parsing summary files from FTP modified: FulltiltToFpdb.py * Modified re_HandInfo regex to take into account Matrix Tournament Hands Histories * Add Regex for Tourney Summaries files parsing * New methods : readSummaryInfo, determineTourneyType, getPlayersPositionsAndWinnings modified: HandHistoryConverter.py * Add a Tourney object in the attributes to allow storing the summary info retrieved and the ability to hand it over after parsing * Add a new attribut parsedObjectType (string : "HH" or "Summary") * In follow = False mode : read the first line to swicth between HH & Summary parsing * TO DO : Deal with parsing summary files in follow = True mode * New methods added : isSummary, getParsedObjectType (to be called in fpdb_import), readSummaryInfo : abstract (to be implemented in each specific HHC), getTourney (returns the new attribute) new file: Tourney.py * New object * Lots of attributes regarding the tourney info (buy-in, fee, entries, speed, Rebuy/add-on info, starting chips, KnockOut info, isHeadsUp, isShootout, isMatrix, ... * List of players with finishing positions (when available) and winnings (when available) * Methods : ** addPlayer(self, rank, name, winnings) ** incrementPlayerWinnings(self, name, additionnalWinnings): used for KO tourneys when KO occured (for Hero only) ** calculatePayinAmount : unused yet, should be necessary for DB storage ** some Hand methods copied that are still to be done including : assemble and insert ** TO DO : write the insert method for the object to be stored in DB. This will have to deal with the fact that the DB write can occur both before (Bulk Import) or after the HH file info has been stored (tourney might or might not already exist) modified: fpdb_import.py * import_file_dict modified : ** after the construction of the hhc, it now calls the new getParsedObjectType method of HHC in order to know what has been parsed by the HHC ** If it's a hand history file (actual expected behaviour) : do as before ** If it's a summary file, gets the tourney object that has been built from the hhc and calls the insert method on it (similar to NEWIMPORT=True for HH)
2009-08-22 00:09:34 +02:00
return self.status
def getProcessedHands(self):
return self.processedHands
def getProcessedFile(self):
return self.out_path
def getLastCharacterRead(self):
return self.index
First attempt to parsing summary files from FTP modified: FulltiltToFpdb.py * Modified re_HandInfo regex to take into account Matrix Tournament Hands Histories * Add Regex for Tourney Summaries files parsing * New methods : readSummaryInfo, determineTourneyType, getPlayersPositionsAndWinnings modified: HandHistoryConverter.py * Add a Tourney object in the attributes to allow storing the summary info retrieved and the ability to hand it over after parsing * Add a new attribut parsedObjectType (string : "HH" or "Summary") * In follow = False mode : read the first line to swicth between HH & Summary parsing * TO DO : Deal with parsing summary files in follow = True mode * New methods added : isSummary, getParsedObjectType (to be called in fpdb_import), readSummaryInfo : abstract (to be implemented in each specific HHC), getTourney (returns the new attribute) new file: Tourney.py * New object * Lots of attributes regarding the tourney info (buy-in, fee, entries, speed, Rebuy/add-on info, starting chips, KnockOut info, isHeadsUp, isShootout, isMatrix, ... * List of players with finishing positions (when available) and winnings (when available) * Methods : ** addPlayer(self, rank, name, winnings) ** incrementPlayerWinnings(self, name, additionnalWinnings): used for KO tourneys when KO occured (for Hero only) ** calculatePayinAmount : unused yet, should be necessary for DB storage ** some Hand methods copied that are still to be done including : assemble and insert ** TO DO : write the insert method for the object to be stored in DB. This will have to deal with the fact that the DB write can occur both before (Bulk Import) or after the HH file info has been stored (tourney might or might not already exist) modified: fpdb_import.py * import_file_dict modified : ** after the construction of the hhc, it now calls the new getParsedObjectType method of HHC in order to know what has been parsed by the HHC ** If it's a hand history file (actual expected behaviour) : do as before ** If it's a summary file, gets the tourney object that has been built from the hhc and calls the insert method on it (similar to NEWIMPORT=True for HH)
2009-08-22 00:09:34 +02:00
def isSummary(self, topline):
return " Tournament Summary " in topline
def getParsedObjectType(self):
return self.parsedObjectType
2010-02-01 14:48:28 +01:00
#returns a status (True/False) indicating wether the parsing could be done correctly or not
First attempt to parsing summary files from FTP modified: FulltiltToFpdb.py * Modified re_HandInfo regex to take into account Matrix Tournament Hands Histories * Add Regex for Tourney Summaries files parsing * New methods : readSummaryInfo, determineTourneyType, getPlayersPositionsAndWinnings modified: HandHistoryConverter.py * Add a Tourney object in the attributes to allow storing the summary info retrieved and the ability to hand it over after parsing * Add a new attribut parsedObjectType (string : "HH" or "Summary") * In follow = False mode : read the first line to swicth between HH & Summary parsing * TO DO : Deal with parsing summary files in follow = True mode * New methods added : isSummary, getParsedObjectType (to be called in fpdb_import), readSummaryInfo : abstract (to be implemented in each specific HHC), getTourney (returns the new attribute) new file: Tourney.py * New object * Lots of attributes regarding the tourney info (buy-in, fee, entries, speed, Rebuy/add-on info, starting chips, KnockOut info, isHeadsUp, isShootout, isMatrix, ... * List of players with finishing positions (when available) and winnings (when available) * Methods : ** addPlayer(self, rank, name, winnings) ** incrementPlayerWinnings(self, name, additionnalWinnings): used for KO tourneys when KO occured (for Hero only) ** calculatePayinAmount : unused yet, should be necessary for DB storage ** some Hand methods copied that are still to be done including : assemble and insert ** TO DO : write the insert method for the object to be stored in DB. This will have to deal with the fact that the DB write can occur both before (Bulk Import) or after the HH file info has been stored (tourney might or might not already exist) modified: fpdb_import.py * import_file_dict modified : ** after the construction of the hhc, it now calls the new getParsedObjectType method of HHC in order to know what has been parsed by the HHC ** If it's a hand history file (actual expected behaviour) : do as before ** If it's a summary file, gets the tourney object that has been built from the hhc and calls the insert method on it (similar to NEWIMPORT=True for HH)
2009-08-22 00:09:34 +02:00
def readSummaryInfo(self, summaryInfoList): abstract
2010-02-01 14:48:28 +01:00
First attempt to parsing summary files from FTP modified: FulltiltToFpdb.py * Modified re_HandInfo regex to take into account Matrix Tournament Hands Histories * Add Regex for Tourney Summaries files parsing * New methods : readSummaryInfo, determineTourneyType, getPlayersPositionsAndWinnings modified: HandHistoryConverter.py * Add a Tourney object in the attributes to allow storing the summary info retrieved and the ability to hand it over after parsing * Add a new attribut parsedObjectType (string : "HH" or "Summary") * In follow = False mode : read the first line to swicth between HH & Summary parsing * TO DO : Deal with parsing summary files in follow = True mode * New methods added : isSummary, getParsedObjectType (to be called in fpdb_import), readSummaryInfo : abstract (to be implemented in each specific HHC), getTourney (returns the new attribute) new file: Tourney.py * New object * Lots of attributes regarding the tourney info (buy-in, fee, entries, speed, Rebuy/add-on info, starting chips, KnockOut info, isHeadsUp, isShootout, isMatrix, ... * List of players with finishing positions (when available) and winnings (when available) * Methods : ** addPlayer(self, rank, name, winnings) ** incrementPlayerWinnings(self, name, additionnalWinnings): used for KO tourneys when KO occured (for Hero only) ** calculatePayinAmount : unused yet, should be necessary for DB storage ** some Hand methods copied that are still to be done including : assemble and insert ** TO DO : write the insert method for the object to be stored in DB. This will have to deal with the fact that the DB write can occur both before (Bulk Import) or after the HH file info has been stored (tourney might or might not already exist) modified: fpdb_import.py * import_file_dict modified : ** after the construction of the hhc, it now calls the new getParsedObjectType method of HHC in order to know what has been parsed by the HHC ** If it's a hand history file (actual expected behaviour) : do as before ** If it's a summary file, gets the tourney object that has been built from the hhc and calls the insert method on it (similar to NEWIMPORT=True for HH)
2009-08-22 00:09:34 +02:00
def getTourney(self):
return self.tourney
@staticmethod
def changeTimezone(time, givenTimezone, wantedTimezone):
2010-08-21 22:10:21 +02:00
"""Takes a givenTimezone in format AAA or AAA+HHMM where AAA is a standard timezone
and +HHMM is an optional offset (+/-) in hours (HH) and minutes (MM)
(See OnGameToFpdb.py for example use of the +HHMM part)
Tries to convert the time parameter (with no timezone) from the givenTimezone to
the wantedTimeZone (currently only allows "UTC")
"""
2010-08-21 14:44:01 +02:00
log.debug( _("raw time:")+str(time) + _(" given TZ:")+str(givenTimezone) )
if wantedTimezone=="UTC":
wantedTimezone = pytz.utc
else:
raise Error #TODO raise appropriate error
2010-08-21 14:44:01 +02:00
givenTZ = None
2010-08-21 22:10:21 +02:00
if HandHistoryConverter.re_tzOffset.match(givenTimezone):
offset = int(givenTimezone[-5:])
givenTimezone = givenTimezone[0:-5]
log.debug( _("changeTimeZone: offset=") + str(offset) )
2010-08-22 07:39:49 +02:00
else: offset=0
2010-08-21 14:44:01 +02:00
if givenTimezone=="ET":
2010-08-21 14:44:01 +02:00
givenTZ = timezone('US/Eastern')
elif givenTimezone=="CET":
2010-08-21 14:44:01 +02:00
givenTZ = timezone('Europe/Berlin')
#Note: Daylight Saving Time is standardised across the EU so this should be fine
2010-08-21 14:44:01 +02:00
elif givenTimezone == 'GMT': # Greenwich Mean Time (same as UTC - no change to time)
givenTZ = timezone('GMT')
elif givenTimezone == 'HST': # Hawaiian Standard Time
pass
elif givenTimezone == 'AKT': # Alaska Time
pass
elif givenTimezone == 'PT': # Pacific Time
pass
elif givenTimezone == 'MT': # Mountain Time
pass
elif givenTimezone == 'CT': # Central Time
pass
elif givenTimezone == 'AT': # Atlantic Time
pass
elif givenTimezone == 'NT': # Newfoundland Time
pass
elif givenTimezone == 'ART': # Argentinian Time
pass
elif givenTimezone == 'BRT': # Brasilia Time
pass
elif givenTimezone == 'AKT': # Alaska Time
pass
elif givenTimezone == 'WET': # Western European Time
pass
elif givenTimezone == 'EET': # Eastern European Time
pass
elif givenTimezone == 'MSK': # Moscow Standard Time
pass
elif givenTimezone == 'IST': # India Standard Time
pass
elif givenTimezone == 'CCT': # China Coast Time
givenTZ = timezone('Australia/West')
elif givenTimezone == 'JST': # Japan Standard Time
pass
elif givenTimezone == 'AWST': # Australian Western Standard Time
2010-08-21 14:44:01 +02:00
givenTZ = timezone('Australia/West')
elif givenTimezone == 'ACST': # Australian Central Standard Time
2010-08-21 14:44:01 +02:00
givenTZ = timezone('Australia/Darwin')
elif givenTimezone == 'AEST': # Australian Eastern Standard Time
# Each State on the East Coast has different DSTs.
# Melbournce is out because I don't like AFL, Queensland doesn't have DST
# ACT is full of politicians and Tasmania will never notice.
# Using Sydney.
2010-08-21 14:44:01 +02:00
givenTZ = timezone('Australia/Sydney')
elif givenTimezone == 'NZT': # New Zealand Time
pass
else:
raise Error #TODO raise appropriate error
2010-08-21 22:10:21 +02:00
if givenTZ is None:
raise Error #TODO raise appropriate error
# (or just return time unchanged?)
2010-08-21 14:44:01 +02:00
localisedTime = givenTZ.localize(time)
2010-08-21 22:10:21 +02:00
utcTime = localisedTime.astimezone(wantedTimezone) + datetime.timedelta(seconds=-3600*(offset/100)-60*(offset%100))
2010-08-21 14:44:01 +02:00
log.debug( _("utcTime:")+str(utcTime) )
return utcTime
#end @staticmethod def changeTimezone
@staticmethod
def getTableTitleRe(type, table_name=None, tournament = None, table_number=None):
"Returns string to search in windows titles"
if type=="tour":
return "%s.+Table %s" % (tournament, table_number)
else:
return table_name
@staticmethod
def getTableNoRe(tournament):
"Returns string to search window title for tournament table no."
# Full Tilt: $30 + $3 Tournament (181398949), Table 1 - 600/1200 Ante 100 - Limit Razz
# PokerStars: WCOOP 2nd Chance 02: $1,050 NLHE - Tournament 307521826 Table 1 - Blinds $30/$60
return "%s.+(?:Table|Torneo) (\d+)" % (tournament, )
@staticmethod
def clearMoneyString(money):
"Renders 'numbers' like '1 200' and '2,000'"
return money.replace(' ', '').replace(',', '')
def getTableTitleRe(config, sitename, *args, **kwargs):
"Returns string to search in windows titles for current site"
return getSiteHhc(config, sitename).getTableTitleRe(*args, **kwargs)
def getTableNoRe(config, sitename, *args, **kwargs):
"Returns string to search window titles for tournament table no."
return getSiteHhc(config, sitename).getTableNoRe(*args, **kwargs)
def getSiteHhc(config, sitename):
"Returns HHC class for current site"
hhcName = config.supported_sites[sitename].converter
hhcModule = __import__(hhcName)
return getattr(hhcModule, hhcName[:-6])
def get_out_fh(out_path, parameters):
if out_path == '-':
return(sys.stdout)
elif parameters['saveStarsHH']:
out_dir = os.path.dirname(out_path)
if not os.path.isdir(out_dir) and out_dir != '':
try:
os.makedirs(out_dir)
except: # we get a WindowsError here in Windows.. pretty sure something else for Linux :D
2010-08-14 18:51:42 +02:00
log.error(_("Unable to create output directory %s for HHC!") % out_dir)
print _("*** ERROR: UNABLE TO CREATE OUTPUT DIRECTORY"), out_dir
else:
2010-08-14 18:51:42 +02:00
log.info(_("Created directory '%s'") % out_dir)
try:
return(codecs.open(out_path, 'w', 'utf8'))
except:
2010-08-14 18:51:42 +02:00
log.error(_("out_path %s couldn't be opened") % (out_path))
else:
return(sys.stdout)