summaryrefslogtreecommitdiff
path: root/f1elo
diff options
context:
space:
mode:
Diffstat (limited to 'f1elo')
-rw-r--r--f1elo/db.py4
-rw-r--r--f1elo/elo.py85
-rw-r--r--f1elo/interface.py34
-rw-r--r--f1elo/model.py57
4 files changed, 116 insertions, 64 deletions
diff --git a/f1elo/db.py b/f1elo/db.py
index 58e7a8a..1991e77 100644
--- a/f1elo/db.py
+++ b/f1elo/db.py
@@ -9,12 +9,14 @@ config = json.load(
open(path.join(path.dirname(__main__.__file__), 'config', 'db.json')))
if config['engine'] == 'mysql':
engine = create_engine(
- "mysql://{0[user]}:{0[pass]}@{0[host]}/{0[db]}?charset=utf8".format(config))
+ "mysql://{0[user]}:{0[pass]}@{0[host]}/{0[db]}?charset=utf8".format(
+ config))
elif config['engine'] == 'sqlite':
engine = create_engine("sqlite:///{0[file]}".format(config))
def fk_pragma(conn, record):
conn.execute('PRAGMA FOREIGN_KEYS=ON')
+
event.listen(engine, 'connect', fk_pragma)
Session = sessionmaker(bind=engine)
diff --git a/f1elo/elo.py b/f1elo/elo.py
index d864e6b..588cc9d 100644
--- a/f1elo/elo.py
+++ b/f1elo/elo.py
@@ -1,18 +1,26 @@
-import json, dateutil
-from sqlalchemy import func
+import json
from itertools import combinations
from os import path
import __main__
+import dateutil
+from sqlalchemy import func
from f1elo.model import *
-class Elo:
+class Elo(object):
def __init__(self, session):
self.session = session
self.config = json.load(
- open(path.join(path.dirname(__main__.__file__), 'config', 'elo.json')))
+ open(
+ path.join(
+ path.dirname(__main__.__file__),
+ 'config',
+ 'elo.json'
+ )
+ )
+ )
def get_ranking(self, driver, rank_date=None):
rank = driver.get_ranking(rank_date)
@@ -21,12 +29,15 @@ class Elo:
return self.config['initial_ranking']
def get_entry_ranking(self, entry, date=None):
- return sum([self.get_ranking(d, date) for d in entry.drivers]) / len(entry.drivers)
+ return sum(
+ [self.get_ranking(d, date) for d in entry.drivers]
+ ) / len(entry.drivers)
def get_race_disparity(self, race):
race_disparity = self.config['disparity']['base_disparity']
if self.config['disparity']['adjust']:
- recent_date = race.date - dateutil.relativedelta.relativedelta(months=3)
+ recent_date = race.date - dateutil.relativedelta.relativedelta(
+ months=3)
recent_ratings = self.session.query(
func.min(Ranking.ranking).label('min'),
func.max(Ranking.ranking).label('max')
@@ -37,14 +48,26 @@ class Elo:
)
changes_query = self.session.query(
func.avg(
- recent_ratings.subquery().columns.max - recent_ratings.subquery().columns.min
+ recent_ratings.subquery().columns.max
+ - recent_ratings.subquery().columns.min
)
)
recent_rank_change = changes_query.scalar()
if not recent_rank_change:
recent_rank_change = 0
- recent_rank_change = min(self.config['disparity']['base_rating_change'], recent_rank_change)
- race_disparity *= (2.5 + (self.config['disparity']['base_rating_change']/(recent_rank_change - 2.0 * self.config['disparity']['base_rating_change']))) * 0.5
+ recent_rank_change = min(
+ self.config['disparity']['base_rating_change'],
+ recent_rank_change)
+ race_disparity *= (
+ 2.5
+ + (
+ self.config['disparity']['base_rating_change']
+ / (
+ recent_rank_change
+ - 2.0 * self.config['disparity']['base_rating_change']
+ )
+ )
+ ) * 0.5
return race_disparity
def rank_race(self, race):
@@ -53,22 +76,22 @@ class Elo:
entries_to_compare = []
rankings = {}
new_rankings = {}
- for e in entries:
- rankings[e] = self.get_entry_ranking(e, race.date)
- new_rankings[e] = 0.0
- if e.result_group:
- entries_to_compare.append(e)
- for c in combinations(entries_to_compare, 2):
- score = self.get_score(
- rankings[c[0]] - rankings[c[1]],
- self.get_outcome(c),
+ for entry in entries:
+ rankings[entry] = self.get_entry_ranking(entry, race.date)
+ new_rankings[entry] = 0.0
+ if entry.result_group:
+ entries_to_compare.append(entry)
+ for combo in combinations(entries_to_compare, 2):
+ score = get_score(
+ rankings[combo[0]] - rankings[combo[1]],
+ get_outcome(combo),
self.get_importance(race,
- [rankings[c[0]],
- rankings[c[1]]]),
+ [rankings[combo[0]],
+ rankings[combo[1]]]),
race_disparity
)
- new_rankings[c[0]] += score
- new_rankings[c[1]] -= score
+ new_rankings[combo[0]] += score
+ new_rankings[combo[1]] -= score
return new_rankings
def get_importance(self, race, rankings):
@@ -80,12 +103,14 @@ class Elo:
return base_importance * 0.75
return base_importance / 2
- def get_outcome(self, entries):
- if entries[0].result_group < entries[1].result_group:
- return 1
- elif entries[0].result_group > entries[1].result_group:
- return 0
- return 0.5
- def get_score(self, difference, outcome, importance, disparity):
- return importance * (outcome - 1 / (1 + (10 ** (-difference / disparity))))
+def get_outcome(entries):
+ if entries[0].result_group < entries[1].result_group:
+ return 1
+ elif entries[0].result_group > entries[1].result_group:
+ return 0
+ return 0.5
+
+
+def get_score(difference, outcome, importance, disparity):
+ return importance * (outcome - 1 / (1 + (10 ** (-difference / disparity))))
diff --git a/f1elo/interface.py b/f1elo/interface.py
index da1b77f..3ba1cb8 100644
--- a/f1elo/interface.py
+++ b/f1elo/interface.py
@@ -9,10 +9,9 @@ import dateutil.relativedelta
from f1elo.db import Session
from f1elo.elo import Elo
from f1elo.model import *
-from sqlalchemy import MetaData
-class Interface:
+class Interface(object):
def __init__(self, date=None):
self.session = Session()
@@ -23,7 +22,8 @@ class Interface:
if force:
Base.metadata.drop_all(self.session.get_bind())
Base.metadata.create_all(self.session.get_bind())
- with open(path.join(path.dirname(__main__.__file__), 'sql', 'results.sql')) as result_dump:
+ with open(path.join(path.dirname(__main__.__file__),
+ 'sql', 'results.sql')) as result_dump:
for line in result_dump.readlines():
line = line.strip()
if line and line[0:2] != '--':
@@ -62,7 +62,7 @@ class Interface:
date = datetime.date.today()
elo = Elo(self.session)
- race_query = self.session.query(Race).filter(Race.ranked == False)
+ race_query = self.session.query(Race).filter(Race.ranked.is_(False))
if date is not None:
race_query = race_query.filter(Race.date <= date)
races = race_query.order_by(Race.date, Race.id).all()
@@ -84,7 +84,9 @@ class Interface:
for driver, rank in driver_ranks.iteritems():
ranking = Ranking()
ranking.rank_date = race.date
- ranking.ranking = round(elo.get_ranking(driver, race.date) + rank, 2)
+ ranking.ranking = round(
+ elo.get_ranking(driver, race.date) + rank,
+ 2)
self.session.add(ranking)
driver.rankings.append(ranking)
@@ -95,8 +97,10 @@ class Interface:
rating_change_sum = 0
changed_ratings = 0
for entry in sorted(race.entries):
- old_rating = elo.get_entry_ranking(entry,
- race.date - dateutil.relativedelta.relativedelta(days=1))
+ old_rating = elo.get_entry_ranking(
+ entry,
+ race.date
+ - dateutil.relativedelta.relativedelta(days=1))
new_rating = elo.get_entry_ranking(entry)
rating_sum += old_rating
if entry.result_group != 0:
@@ -107,13 +111,17 @@ class Interface:
old_rating,
new_rating,
file=sys.stderr)
- if entry.result in ['1','2','3']:
+ if entry.result in ['1', '2', '3']:
podium_rating += old_rating
podium_rating_after += new_rating
print('', file=sys.stderr)
- print('Podium rating: ', podium_rating, podium_rating_after, file=sys.stderr)
- print('Average rating: ', rating_sum / len(race.entries), file=sys.stderr)
- print('Average rating change: ', rating_change_sum / changed_ratings, file=sys.stderr)
+ print('Podium rating: ', podium_rating, podium_rating_after,
+ file=sys.stderr)
+ print('Average rating: ', rating_sum / len(race.entries),
+ file=sys.stderr)
+ print('Average rating change: ',
+ rating_change_sum / changed_ratings,
+ file=sys.stderr)
print('', file=sys.stderr)
race.ranked = True
@@ -151,4 +159,6 @@ class Interface:
self.date = date
- return sorted(drivers.values(), key=lambda rank: rank.ranking, reverse=True)
+ return sorted(drivers.values(),
+ key=lambda rank: rank.ranking,
+ reverse=True)
diff --git a/f1elo/model.py b/f1elo/model.py
index c450353..b42cc08 100644
--- a/f1elo/model.py
+++ b/f1elo/model.py
@@ -1,13 +1,14 @@
+import sys
+from functools import total_ordering
+
from sqlalchemy import Column, ForeignKey, Table
from sqlalchemy.ext.declarative import declarative_base
-from sqlalchemy.orm import backref, relationship, sessionmaker
+from sqlalchemy.orm import relationship
from sqlalchemy.types import Boolean, Date, Float, Integer, String
-from functools import total_ordering
-import sys
-
Base = declarative_base()
+
class Country(Base):
__tablename__ = 'countries'
@@ -29,6 +30,7 @@ class Country(Base):
session.add(country)
return country
+
class Driver(Base):
__tablename__ = 'drivers'
@@ -74,21 +76,22 @@ class Driver(Base):
session.add(driver)
return driver
+
driver_entry = Table('driver_entries', Base.metadata,
Column(
- '_driver',
- Integer,
- ForeignKey(
- 'drivers.id',
- onupdate="CASCADE",
- ondelete="CASCADE")),
+ '_driver',
+ Integer,
+ ForeignKey(
+ 'drivers.id',
+ onupdate="CASCADE",
+ ondelete="CASCADE")),
Column(
- '_entry',
- Integer,
- ForeignKey(
- 'entries.id',
- onupdate="CASCADE",
- ondelete="CASCADE")),
+ '_entry',
+ Integer,
+ ForeignKey(
+ 'entries.id',
+ onupdate="CASCADE",
+ ondelete="CASCADE")),
Column('id', Integer, primary_key=True))
@@ -119,7 +122,12 @@ class Entry(Base):
passive_deletes=True)
def __repr__(self):
- return ('#%s (%s) %s[%d]' % (self.car_no, ', '.join([driver.__repr__().decode('utf8') for driver in self.drivers]), self.result, self.result_group)).encode('utf8')
+ return ('#%s (%s) %s[%d]' % (
+ self.car_no,
+ ', '.join([driver.__repr__().decode('utf8')
+ for driver in self.drivers]),
+ self.result,
+ self.result_group)).encode('utf8')
def __eq__(self, other):
return self.id == other.id
@@ -141,13 +149,17 @@ class Entry(Base):
else:
return self_result < other_result
+
class Race(Base):
__tablename__ = 'races'
id = Column(Integer, primary_key=True)
race = Column(String(1024))
date = Column(Date, index=True)
- ranked = Column(Boolean, nullable=False, server_default='0', default=False, index=True)
+ ranked = Column(Boolean,
+ nullable=False,
+ server_default='0', default=False,
+ index=True)
_type = Column(
Integer,
@@ -204,9 +216,12 @@ class Ranking(Base):
driver = relationship(
'Driver',
back_populates='rankings',
- order_by=rank_date)
+ order_by=rank_date)
def __repr__(self):
- return ("%s: %0.2f (%s)" % (self.driver.__repr__().decode('utf8'), self.ranking, self. rank_date)).encode('utf8')
+ return ("%s: %0.2f (%s)" % (
+ self.driver.__repr__().decode('utf8'),
+ self.ranking, self. rank_date)).encode('utf8')
+
-__all__ = ['Country', 'Driver', 'Entry', 'Ranking', 'Race', 'RaceType']
+__all__ = ('Country', 'Driver', 'Entry', 'Ranking', 'Race', 'RaceType')