Merge branch 'master' of git://git.assembla.com/fpdb-sql.git

This commit is contained in:
Gerko de Roo 2010-02-01 17:26:24 +01:00
commit 0081337a92
13 changed files with 174 additions and 103 deletions

View File

@ -52,44 +52,41 @@ def get_default_config_path():
return config_path
def get_exec_path():
"""Returns the path to the fpdb.(py|exe) file we are executing"""
"""Returns the path to the fpdb(dir|.exe) file we are executing"""
if hasattr(sys, "frozen"): # compiled by py2exe
return os.path.dirname(sys.executable)
else:
return sys.path[0]
return os.path.dirname(sys.path[0]) # should be path to /fpdb
def get_config(file_name, fallback = True):
"""Looks in cwd and in self.default_config_path for a config file."""
exec_dir = get_exec_path()
config_path = os.path.join(exec_dir, file_name)
if file_name == 'logging.conf':
config_path = os.path.join(exec_dir, 'pyfpdb', file_name)
else:
config_path = os.path.join(exec_dir, file_name)
# print "config_path=", config_path
if os.path.exists(config_path): # there is a file in the cwd
return config_path # so we use it
return (config_path,False) # so we use it
else: # no file in the cwd, look where it should be in the first place
default_dir = get_default_config_path()
config_path = os.path.join(default_dir, file_name)
# print "config path 2=", config_path
if os.path.exists(config_path):
return config_path
return (config_path,False)
# No file found
if not fallback:
return False
return (False,False)
# OK, fall back to the .example file, should be in the start dir
if os.path.exists(file_name + ".example"):
try:
print ""
if not os.path.isdir(default_dir):
msg = "Creating directory: '%s'" % (default_dir)
print msg
logging.info(msg)
os.mkdir(default_dir)
check_dir(default_dir)
shutil.copyfile(file_name + ".example", config_path)
msg = "No %s found in %s or %s\n" % (file_name, exec_dir, default_dir) \
+ "Config file has been created at %s.\n" % config_path \
+ "Edit your screen_name and hand history path in the supported_sites "\
+ "section of the \nPreferences window (Main menu) before trying to import hands."
msg = "No %s found\n in %s\n or %s\n" % (file_name, exec_dir, default_dir) \
+ "Config file has been created at %s.\n" % config_path
print msg
logging.info(msg)
file_name = config_path
@ -102,13 +99,19 @@ def get_config(file_name, fallback = True):
print "No %s found, cannot fall back. Exiting.\n" % file_name
sys.stderr.write("No %s found, cannot fall back. Exiting.\n" % file_name)
sys.exit()
return file_name
return (file_name,True)
def get_logger(file_name, config = "config", fallback = False):
conf = get_config(file_name, fallback = fallback)
if conf:
def get_logger(file_name, config = "config", fallback = False, log_dir=None):
(conf_file,copied) = get_config(file_name, fallback = fallback)
if conf_file:
try:
logging.config.fileConfig(conf)
if log_dir is None:
log_dir = os.path.join(get_exec_path(), 'log')
check_dir(log_dir)
file = os.path.join(log_dir, 'logging.out')
file = file.replace('\\', '\\\\') # replace each \ with \\
# print " ="+file+" "+ str(type(file))+" len="+str(len(file))+"\n"
logging.config.fileConfig(conf_file, {"logFile":file})
log = logging.getLogger(config)
log.debug("%s logger initialised" % config)
return log
@ -120,8 +123,23 @@ def get_logger(file_name, config = "config", fallback = False):
log.debug("config logger initialised")
return log
def check_dir(path, create = True):
"""Check if a dir exists, optionally creates if not."""
if os.path.exists(path):
if os.path.isdir(path):
return path
else:
return False
if create:
msg = "Creating directory: '%s'" % (path)
print msg
log.info(msg)
os.mkdir(path)
else:
return False
# find a logging.conf file and set up logging
log = get_logger("logging.conf")
log = get_logger("logging.conf", "config")
########################################################################
# application wide consts
@ -421,11 +439,11 @@ class Tv:
class Config:
def __init__(self, file = None, dbname = ''):
# "file" is a path to an xml file with the fpdb/HUD configuration
# we check the existence of "file" and try to recover if it doesn't exist
# self.default_config_path = self.get_default_config_path()
self.example_copy = False
if file is not None: # config file path passed in
file = os.path.expanduser(file)
if not os.path.exists(file):
@ -433,7 +451,14 @@ class Config:
sys.stderr.write("Configuration file %s not found. Using defaults." % (file))
file = None
if file is None: file = get_config("HUD_config.xml", True)
if file is None: (file,self.example_copy) = get_config("HUD_config.xml", True)
self.file = file
self.dir_self = get_exec_path()
self.dir_config = os.path.dirname(self.file)
self.dir_log = os.path.join(self.dir_config, 'log')
self.dir_database = os.path.join(self.dir_config, 'database')
log = get_logger("logging.conf", "config", log_dir=self.dir_log)
# Parse even if there was no real config file found and we are using the example
# If using the example, we'll edit it later
@ -449,9 +474,6 @@ class Config:
sys.exit()
self.doc = doc
self.file = file
self.dir = os.path.dirname(self.file)
self.dir_databases = os.path.join(self.dir, 'database')
self.supported_sites = {}
self.supported_games = {}
self.supported_databases = {} # databaseName --> Database instance

View File

@ -224,6 +224,7 @@ class Database:
def __init__(self, c, sql = None):
log = Configuration.get_logger("logging.conf", "db", log_dir=c.dir_log)
log.info("Creating Database instance, sql = %s" % sql)
self.config = c
self.__connected = False
@ -379,11 +380,11 @@ class Database:
log.warning("SQLite won't work well without 'sqlalchemy' installed.")
if database != ":memory:":
if not os.path.isdir(self.config.dir_databases):
print "Creating directory: '%s'" % (self.config.dir_databases)
log.info("Creating directory: '%s'" % (self.config.dir_databases))
os.mkdir(self.config.dir_databases)
database = os.path.join(self.config.dir_databases, database)
if not os.path.isdir(self.config.dir_database):
print "Creating directory: '%s'" % (self.config.dir_database)
log.info("Creating directory: '%s'" % (self.config.dir_database))
os.mkdir(self.config.dir_database)
database = os.path.join(self.config.dir_database, database)
log.info("Connecting to SQLite: %(database)s" % {'database':database})
print "Connecting to SQLite: %(database)s" % {'database':database}
self.connection = sqlite3.connect(database, detect_types=sqlite3.PARSE_DECLTYPES )
@ -787,9 +788,11 @@ class Database:
def get_player_id(self, config, site, player_name):
c = self.connection.cursor()
print "get_player_id: player_name =", player_name, type(player_name)
p_name = Charset.to_utf8(player_name)
c.execute(self.sql.query['get_player_id'], (p_name, site))
row = c.fetchone()
print "player id =", row
if row:
return row[0]
else:

View File

@ -297,7 +297,8 @@ class Filters(threading.Thread):
def __set_hero_name(self, w, site):
_name = w.get_text()
_guiname = Charset.to_gui(_name)
# get_text() returns a str but we want internal variables to be unicode:
_guiname = unicode(_name)
self.heroes[site] = _guiname
# print "DEBUG: setting heroes[%s]: %s"%(site, self.heroes[site])
@ -593,7 +594,7 @@ class Filters(threading.Thread):
vbox.pack_start(vbox1, False, False, 0)
self.boxes['limits'] = vbox1
self.cursor.execute(self.sql.query['getLimits2'])
self.cursor.execute(self.sql.query['getLimits3'])
# selects limitType, bigBlind
result = self.db.cursor.fetchall()
found = {'nl':False, 'fl':False, 'pl':False, 'ring':False, 'tour':False}
@ -614,7 +615,7 @@ class Filters(threading.Thread):
vbox2.pack_start(hbox, False, False, 0)
else:
vbox3.pack_start(hbox, False, False, 0)
if line[0] == 'ring':
if True: #line[0] == 'ring':
if line[1] == 'fl':
name = str(line[2])
found['fl'] = True

View File

@ -194,7 +194,8 @@ class GuiAutoImport (threading.Thread):
widget.set_label(u' _Stop Autoimport ')
if self.pipe_to_hud is None:
if os.name == 'nt':
command = "python "+sys.path[0]+"\\HUD_main.py " + self.settings['cl_options']
path = sys.path[0].replace('\\','\\\\')
command = 'python "'+path+'\\HUD_main.py" ' + self.settings['cl_options']
bs = 0
else:
command = os.path.join(sys.path[0], 'HUD_main.py')

View File

@ -319,7 +319,7 @@ class GuiGraphViewer (threading.Thread):
winnings = self.db.cursor.fetchall()
self.db.rollback()
if winnings == ():
if len(winnings) == 0:
return (None, None, None)
green = map(lambda x:float(x[1]), winnings)

View File

@ -526,7 +526,7 @@ class GuiPlayerStats (threading.Thread):
if type == 'ring':
bbtest = bbtest + " and gt.type = 'ring' "
elif type == 'tour':
bbtest = bbtest + " and gt.type = 'tour' "
bbtest = " and gt.type = 'tour' "
query = query.replace("<gtbigBlind_test>", bbtest)
if holecards: # re-use level variables for hole card query

View File

@ -51,10 +51,8 @@ import gobject
# FreePokerTools modules
import Configuration
print "start logging"
log = Configuration.get_logger("logging.conf", config = 'hud')
log.debug("%s logger initialized." % "dud")
print "logging started"
log.debug("%s logger initialized." % "hud")
import Database
@ -68,36 +66,40 @@ elif os.name == 'nt':
import Hud
log = Configuration.get_logger("logging.conf")
class HUD_main(object):
"""A main() object to own both the read_stdin thread and the gui."""
# This class mainly provides state for controlling the multiple HUDs.
def __init__(self, db_name = 'fpdb'):
self.db_name = db_name
self.log = log
self.config = Configuration.Config(file=options.config, dbname=options.dbname)
self.hud_dict = {}
self.hud_params = self.config.get_hud_ui_parameters()
try:
print "HUD_main: starting ..."
self.db_name = db_name
self.config = Configuration.Config(file=options.config, dbname=options.dbname)
log = Configuration.get_logger("logging.conf", "hud", log_dir=self.config.dir_log)
log.debug("starting ...")
self.hud_dict = {}
self.hud_params = self.config.get_hud_ui_parameters()
# a thread to read stdin
gobject.threads_init() # this is required
thread.start_new_thread(self.read_stdin, ()) # starts the thread
# a thread to read stdin
gobject.threads_init() # this is required
thread.start_new_thread(self.read_stdin, ()) # starts the thread
# a main window
self.main_window = gtk.Window()
self.main_window.connect("destroy", self.destroy)
self.vb = gtk.VBox()
self.label = gtk.Label('Closing this window will exit from the HUD.')
self.vb.add(self.label)
self.main_window.add(self.vb)
self.main_window.set_title("HUD Main Window")
self.main_window.show_all()
except:
log.debug("commit "+str(i)+" failed: info=" + str(sys.exc_info())
+ " value=" + str(sys.exc_value))
# a main window
self.main_window = gtk.Window()
self.main_window.connect("destroy", self.destroy)
self.vb = gtk.VBox()
self.label = gtk.Label('Closing this window will exit from the HUD.')
self.vb.add(self.label)
self.main_window.add(self.vb)
self.main_window.set_title("HUD Main Window")
self.main_window.show_all()
def destroy(self, *args): # call back for terminating the main eventloop
self.log.info("Terminating normally.")
log.info("Terminating normally.")
gtk.main_quit()
def kill_hud(self, event, table):
@ -205,7 +207,7 @@ class HUD_main(object):
t0 = time.time()
t1 = t2 = t3 = t4 = t5 = t6 = t0
new_hand_id = string.rstrip(new_hand_id)
self.log.debug("Received hand no %s" % new_hand_id)
log.debug("Received hand no %s" % new_hand_id)
if new_hand_id == "": # blank line means quit
self.destroy()
break # this thread is not always killed immediately with gtk.main_quit()
@ -217,7 +219,7 @@ class HUD_main(object):
(table_name, max, poker_game, type, site_id, site_name, num_seats, tour_number, tab_number) = \
self.db_connection.get_table_info(new_hand_id)
except Exception, err:
self.log.error("db error: skipping %s" % new_hand_id)
log.error("db error: skipping %s" % new_hand_id)
continue
t1 = time.time()
@ -276,7 +278,7 @@ class HUD_main(object):
if type == "tour":
table_name = "%s %s" % (tour_number, tab_number)
# sys.stderr.write("HUD create: table name "+table_name+" not found, skipping.\n")
self.log.error("HUD create: table name %s not found, skipping." % table_name)
log.error("HUD create: table name %s not found, skipping." % table_name)
else:
tablewindow.max = max
tablewindow.site = site_name

View File

@ -28,12 +28,14 @@ from decimal import Decimal
import operator
import time,datetime
from copy import deepcopy
from Exceptions import *
import pprint
import Configuration
from Exceptions import *
import DerivedStats
import Card
log = logging.getLogger("parser")
log = Configuration.get_logger("logging.conf", "parser")
class Hand(object):
@ -46,7 +48,9 @@ class Hand(object):
SITEIDS = {'Fulltilt':1, 'PokerStars':2, 'Everleaf':3, 'Win2day':4, 'OnGame':5, 'UltimateBet':6, 'Betfair':7, 'Absolute':8, 'PartyPoker':9, 'Partouche':10, 'Carbon':11 }
def __init__(self, sitename, gametype, handText, builtFrom = "HHC"):
def __init__(self, config, sitename, gametype, handText, builtFrom = "HHC"):
self.config = config
log = Configuration.get_logger("logging.conf", "db", log_dir=self.config.dir_log)
self.sitename = sitename
self.siteId = self.SITEIDS[sitename]
self.stats = DerivedStats.DerivedStats(self)
@ -617,7 +621,8 @@ Map the tuple self.gametype onto the pokerstars string describing it
class HoldemOmahaHand(Hand):
def __init__(self, hhc, sitename, gametype, handText, builtFrom = "HHC", handid=None):
def __init__(self, config, hhc, sitename, gametype, handText, builtFrom = "HHC", handid=None):
self.config = config
if gametype['base'] != 'hold':
pass # or indeed don't pass and complain instead
log.debug("HoldemOmahaHand")
@ -625,7 +630,7 @@ class HoldemOmahaHand(Hand):
self.holeStreets = ['PREFLOP']
self.communityStreets = ['FLOP', 'TURN', 'RIVER']
self.actionStreets = ['BLINDSANTES','PREFLOP','FLOP','TURN','RIVER']
Hand.__init__(self, sitename, gametype, handText, builtFrom = "HHC")
Hand.__init__(self, self.config, sitename, gametype, handText, builtFrom = "HHC")
self.sb = gametype['sb']
self.bb = gametype['bb']
@ -916,7 +921,8 @@ class HoldemOmahaHand(Hand):
print >>fh, "\n\n"
class DrawHand(Hand):
def __init__(self, hhc, sitename, gametype, handText, builtFrom = "HHC"):
def __init__(self, config, hhc, sitename, gametype, handText, builtFrom = "HHC"):
self.config = config
if gametype['base'] != 'draw':
pass # or indeed don't pass and complain instead
self.streetList = ['BLINDSANTES', 'DEAL', 'DRAWONE', 'DRAWTWO', 'DRAWTHREE']
@ -924,7 +930,7 @@ class DrawHand(Hand):
self.holeStreets = ['DEAL', 'DRAWONE', 'DRAWTWO', 'DRAWTHREE']
self.actionStreets = ['BLINDSANTES', 'DEAL', 'DRAWONE', 'DRAWTWO', 'DRAWTHREE']
self.communityStreets = []
Hand.__init__(self, sitename, gametype, handText)
Hand.__init__(self, self.config, sitename, gametype, handText)
self.sb = gametype['sb']
self.bb = gametype['bb']
# Populate the draw hand.
@ -1108,7 +1114,8 @@ class DrawHand(Hand):
class StudHand(Hand):
def __init__(self, hhc, sitename, gametype, handText, builtFrom = "HHC"):
def __init__(self, config, hhc, sitename, gametype, handText, builtFrom = "HHC"):
self.config = config
if gametype['base'] != 'stud':
pass # or indeed don't pass and complain instead
@ -1118,7 +1125,7 @@ class StudHand(Hand):
self.streetList = ['BLINDSANTES','THIRD','FOURTH','FIFTH','SIXTH','SEVENTH'] # a list of the observed street names in order
self.holeStreets = ['THIRD','FOURTH','FIFTH','SIXTH','SEVENTH']
Hand.__init__(self, sitename, gametype, handText)
Hand.__init__(self, self.config, sitename, gametype, handText)
self.sb = gametype['sb']
self.bb = gametype['bb']
#Populate the StudHand
@ -1511,7 +1518,8 @@ limit 1""", {'handid':handid})
#TODO: siteid should be in hands table - we took the scenic route through players here.
res = c.fetchone()
gametype = {'category':res[1],'base':res[2],'type':res[3],'limitType':res[4],'hilo':res[5],'sb':res[6],'bb':res[7], 'currency':res[10]}
h = HoldemOmahaHand(hhc = None, sitename=res[0], gametype = gametype, handText=None, builtFrom = "DB", handid=handid)
c = Configuration.Config()
h = HoldemOmahaHand(config = c, hhc = None, sitename=res[0], gametype = gametype, handText=None, builtFrom = "DB", handid=handid)
cards = map(Card.valueSuitFromCard, res[11:16] )
if cards[0]:
h.setCommunityCards('FLOP', cards[0:3])

View File

@ -16,8 +16,6 @@
#In the "official" distribution you can find the license in
#agpl-3.0.txt in the docs folder of the package.
import Hand
import Tourney
import re
import sys
import traceback
@ -31,13 +29,16 @@ import operator
from xml.dom.minidom import Node
import time
import datetime
import Hand
import Tourney
from Exceptions import FpdbParseError
import Configuration
import gettext
gettext.install('fpdb')
log = Configuration.get_logger("logging.conf")
log = Configuration.get_logger("logging.conf", "parser")
import pygtk
import gtk
@ -57,12 +58,14 @@ class HandHistoryConverter():
codepage = "cp1252"
def __init__(self, in_path = '-', out_path = '-', follow=False, index=0, autostart=True, starsArchive=False):
def __init__(self, config, in_path = '-', out_path = '-', follow=False, index=0, autostart=True, starsArchive=False):
"""\
in_path (default '-' = sys.stdin)
out_path (default '-' = sys.stdout)
follow : whether to tail -f the input"""
self.config = config
log = Configuration.get_logger("logging.conf", "parser", log_dir=self.config.dir_log)
log.info("HandHistory init - %s subclass, in_path '%s'; out_path '%s'" % (self.sitename, in_path, out_path) )
self.index = index
@ -283,11 +286,11 @@ which it expects to find at self.re_TailSplitHands -- see for e.g. Everleaf.py.
if l in self.readSupportedGames():
if gametype['base'] == 'hold':
log.debug("hand = Hand.HoldemOmahaHand(self, self.sitename, gametype, handtext)")
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, self.sitename, gametype, handText)
hand = Hand.StudHand(self.config, self, self.sitename, gametype, handText)
elif gametype['base'] == 'draw':
hand = Hand.DrawHand(self, self.sitename, gametype, handText)
hand = Hand.DrawHand(self.config, self, self.sitename, gametype, handText)
else:
log.info("Unsupported game type: %s" % gametype)
@ -417,7 +420,7 @@ or None if we fail to get the info """
list.pop() #Last entry is empty
for l in list:
# print "'" + l + "'"
hands = hands + [Hand.Hand(self.sitename, self.gametype, l)]
hands = hands + [Hand.Hand(self.config, self.sitename, self.gametype, l)]
# TODO: This looks like it could be replaced with a list comp.. ?
return hands

View File

@ -1848,6 +1848,15 @@ class Sql:
self.query['getLimits2'] = """SELECT DISTINCT type, limitType, bigBlind
from Gametypes
ORDER by type, limitType DESC, bigBlind DESC"""
self.query['getLimits3'] = """select DISTINCT type
, limitType
, case type
when 'ring' then bigBlind
else buyin
end as bb_or_buyin
from Gametypes gt
cross join TourneyTypes tt
order by type, limitType DESC, bb_or_buyin DESC"""
if db_server == 'mysql':
self.query['playerDetailedStats'] = """
@ -2021,6 +2030,7 @@ class Sql:
elif db_server == 'sqlite':
self.query['playerDetailedStats'] = """
select <hgameTypeId> AS hgametypeid
,<playerName> AS pname
,gt.base
,gt.category AS category
,upper(gt.limitType) AS limittype
@ -2072,6 +2082,7 @@ class Sql:
inner join Hands h on (h.id = hp.handId)
inner join Gametypes gt on (gt.Id = h.gameTypeId)
inner join Sites s on (s.Id = gt.siteId)
inner join Players p on (p.Id = hp.playerId)
where hp.playerId in <player_test>
<game_test>
/*and hp.tourneysPlayersId IS NULL*/

View File

@ -695,6 +695,12 @@ class fpdb:
def load_profile(self):
"""Loads profile from the provided path name."""
self.config = Configuration.Config(file=options.config, dbname=options.dbname)
log = Configuration.get_logger("logging.conf", "fpdb", log_dir=self.config.dir_log)
if self.config.example_copy:
self.info_box( "Config file"
, "has been created at:\n%s.\n" % self.config.file
+ "Edit your screen_name and hand history path in the supported_sites "
+ "section of the Preferences window (Main menu) before trying to import hands.")
self.settings = {}
self.settings['global_lock'] = self.lock
if (os.sep=="/"):
@ -962,16 +968,24 @@ This program is licensed under the AGPL3, see docs"""+os.sep+"agpl-3.0.txt")
self.window.show()
self.window.present()
def info_box(self, str1, str2):
diapath = gtk.MessageDialog( parent=None, flags=0, type=gtk.MESSAGE_INFO
, buttons=(gtk.BUTTONS_OK), message_format=str1 )
diapath.format_secondary_text(str2)
response = diapath.run()
diapath.destroy()
return response
def warning_box(self, str, diatitle="FPDB WARNING"):
diaWarning = gtk.Dialog(title=diatitle, parent=None, flags=0, buttons=(gtk.STOCK_OK,gtk.RESPONSE_OK))
diaWarning = gtk.Dialog(title=diatitle, parent=None, flags=0, buttons=(gtk.STOCK_OK,gtk.RESPONSE_OK))
label = gtk.Label(str)
diaWarning.vbox.add(label)
label.show()
label = gtk.Label(str)
diaWarning.vbox.add(label)
label.show()
response = diaWarning.run()
diaWarning.destroy()
return response
response = diaWarning.run()
diaWarning.destroy()
return response
def validate_config(self):
hhbase = self.config.get_import_parameters().get("hhArchiveBase")

View File

@ -65,6 +65,7 @@ class Importer:
self.config = config
self.sql = sql
log = Configuration.get_logger("logging.conf", "importer", log_dir=self.config.dir_log)
self.filelist = {}
self.dirlist = {}
self.siteIds = {}
@ -429,7 +430,7 @@ class Importer:
idx = self.pos_in_file[file]
else:
self.pos_in_file[file] = 0
hhc = obj(in_path = file, out_path = out_path, index = idx, starsArchive = self.settings['starsArchive'])
hhc = obj(self.config, in_path = file, out_path = out_path, index = idx, starsArchive = self.settings['starsArchive'])
if hhc.getStatus():
handlist = hhc.getProcessedHands()
self.pos_in_file[file] = hhc.getLastCharacterRead()

View File

@ -1,64 +1,69 @@
[loggers]
keys=root,parser,importer,config,db
keys=root,fpdb,logview,parser,importer,config,db,hud
[handlers]
keys=consoleHandler,fileHandler
keys=consoleHandler,rotatingFileHandler
[formatters]
keys=fileFormatter,stderrFormatter
[logger_root]
level=INFO
handlers=consoleHandler,fileHandler
handlers=consoleHandler,rotatingFileHandler
[logger_fpdb]
level=INFO
handlers=consoleHandler,fileHandler
handlers=consoleHandler,rotatingFileHandler
qualname=fpdb
propagate=0
[logger_logview]
level=INFO
handlers=consoleHandler,fileHandler
handlers=consoleHandler,rotatingFileHandler
qualname=logview
propagate=0
[logger_parser]
level=INFO
handlers=consoleHandler,fileHandler
handlers=consoleHandler,rotatingFileHandler
qualname=parser
propagate=0
[logger_importer]
level=DEBUG
handlers=consoleHandler,fileHandler
handlers=consoleHandler,rotatingFileHandler
qualname=importer
propagate=0
[logger_config]
level=INFO
handlers=consoleHandler,fileHandler
handlers=consoleHandler,rotatingFileHandler
qualname=config
propagate=0
[logger_db]
level=DEBUG
handlers=consoleHandler,fileHandler
handlers=consoleHandler,rotatingFileHandler
qualname=db
propagate=0
[logger_hud]
level=DEBUG
handlers=consoleHandler,rotatingFileHandler
qualname=hud
propagate=0
[handler_consoleHandler]
class=StreamHandler
level=ERROR
formatter=stderrFormatter
args=(sys.stderr,)
[handler_fileHandler]
class=FileHandler
[handler_rotatingFileHandler]
class=handlers.RotatingFileHandler
level=DEBUG
formatter=fileFormatter
args=('logging.out', 'a')
args=('%(logFile)s', 'a', 100000000, 5)
[formatter_fileFormatter]
format=%(asctime)s - %(name)-12s %(levelname)-8s %(message)s