Skip to content

Commit

Permalink
check config for missing sections and fields
Browse files Browse the repository at this point in the history
  • Loading branch information
uberfastman committed Oct 18, 2020
1 parent 4edb145 commit 3f784dc
Show file tree
Hide file tree
Showing 14 changed files with 195 additions and 85 deletions.
86 changes: 34 additions & 52 deletions EXAMPLE-config.ini
Original file line number Diff line number Diff line change
@@ -1,72 +1,28 @@
[Configuration]
; fantasy football platform for which you are running the report (see below "supported_platforms" for options)
[Settings]
; fantasy football platform for which you are running the report: yahoo,espn,sleeper,fleaflicker
platform = yahoo

; supported fantasy football platforms (comma-delimited list with no spaces)
supported_platforms = yahoo,espn,sleeper,fleaflicker

; example public league archive for reference: https://archive.fantasysports.yahoo.com/nfl/2014/729259
league_id = 729259

; Yahoo: game_id can be either `nfl`, in which case Yahoo defaults to using the current season, or it can be a specific
; Yahoo game id for a specific season, such as 331 (2014 NFL season), 380 (2018 NFL season), or 390 (2019 nfl season)
game_id = nfl

; Fleaflicker/ESPN: season must be an eligible year for fantasy football
season = 2020

; Sleeper/Fleaflicker: default if unable to retrieve the current NFL week from Fox Sports (for Sleeper) or Fleaflicker
current_week = 1

; logger output level: notset, debug, info, warning, error, critical
log_level = info

; output directories can be set to store your saved data and generated reports wherever you want
data_dir = output/data
output_dir = output/reports

; week_for_report accepts "default", or "#", where # is an integer between 1 and 17 defining the chosen week
; value can be "default" or #, where # is an integer between 1 and 17 defining the chosen week
week_for_report = default

; select how many Monte Carlo simulations are used for playoff predictions, keeping in mind that while more simulations
; improves the quality of the playoff predictions, it also make this step of the report take longer to complete
num_playoff_simulations = 100000

; prohibited player statuses to check team coaching efficiency eligibility (if dq_ce = True)
; (comma-delimited list with no spaces)
; OPTIONS:
; D: Doubtful
; NA: Inactive: Coach's Decision or Not on Roster
; IR: Injured Reserve
; IR-R: Injured Reserve - Designated for Return
; NFI: Non-Football Injury
; NFI-A: Non-Football Injury (Active)
; NFI-R: Non-Football Injury (Reserve)
; O: Out
; PUP-P: Physically Unable to Perform (Preseason)
; PUP-R: Physically Unable to Perform (Regular Season)
; Q: Questionable
; COVID-19: Reserve: COVID-19
; CEL: Reserve: Commissioner Exempt List
; DNR: Reserve: Did Not Report
; EX: Reserve: Exemption
; RET: Reserve: Retired
; SUSP: Suspended
prohibited_statuses = NA,IR,IR-R,NFI,NFI-R,O,PUP-P,PUP-R,COVID-19,CEL,DNR,EX,RET,SUSP,INACTIVE

; Yahoo: default FAAB since the initial/starting FAAB is not exposed in the API
initial_faab_budget = 100

; Fleaflicker: default if # playoff slots can't be scraped
; (or for leagues with non-standard playoff structures, which is not currently supported)
; Fleaflicker: default if number of playoff slots can't be scraped
num_playoff_slots = 6

; number of top ranked players that make the playoffs from each division (for leagues with divisions)
; number of top ranked teams that make the playoffs from each division (for leagues with divisions)
num_playoff_slots_per_division = 1

; Fleaflicker and Sleeper: default if # of regular season weeks can't be scraped/is not available
; Fleaflicker/Sleeper: default if number of regular season weeks can't be scraped/retrieved
num_regular_season_weeks = 13

; multiple teams can be manually disqualified from coaching efficiency eligibility
; (comma-delimited list with no spaces)
; example:
Expand Down Expand Up @@ -94,18 +50,44 @@ team_points_by_position_charts = True
team_bad_boy_stats = True
team_beef_stats = True
team_boom_or_bust = True

; set font and font size of report (defaults to helvetica)
font = helvetica
; supported fonts (comma-delimited list with no spaces)
supported_fonts = helvetica,times,symbola,opensansemoji,sketchcollege,leaguegothic
; set base font size (certain report element fonts resize dynamically based on the base font size)
font_size = 12

; specify player headshot image quality in percent (default: 75%)
; higher quality (-> 100%) results in the PDF report file being larger
image_quality = 75

[Configuration]
; logger output level: notset, debug, info, warning, error, critical
log_level = info
; output directories can be set to store your saved data and generated reports wherever you want
data_dir = output/data
output_dir = output/reports
; prohibited player statuses to check team coaching efficiency eligibility (if dq_ce = True)
; (comma-delimited list with no spaces)
; OPTIONS:
; D: Doubtful
; NA: Inactive: Coach's Decision or Not on Roster
; IR: Injured Reserve
; IR-R: Injured Reserve - Designated for Return
; NFI: Non-Football Injury
; NFI-A: Non-Football Injury (Active)
; NFI-R: Non-Football Injury (Reserve)
; O: Out
; PUP-P: Physically Unable to Perform (Preseason)
; PUP-R: Physically Unable to Perform (Regular Season)
; Q: Questionable
; COVID-19: Reserve: COVID-19
; CEL: Reserve: Commissioner Exempt List
; DNR: Reserve: Did Not Report
; EX: Reserve: Exemption
; RET: Reserve: Retired
; SUSP: Suspended
prohibited_statuses = NA,IR,IR-R,NFI,NFI-R,O,PUP-P,PUP-R,COVID-19,CEL,DNR,EX,RET,SUSP,INACTIVE

[Yahoo]
yahoo_auth_dir = auth/yahoo

Expand Down
6 changes: 3 additions & 3 deletions calculate/playoff_probabilities.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ def __init__(self, config, simulations, num_weeks, num_playoff_slots, data_dir,

self.config = config
self.simulations = int(simulations) if simulations is not None else self.config.getint(
"Configuration", "num_playoff_simulations", fallback=100000)
"Settings", "num_playoff_simulations", fallback=100000)
self.num_weeks = int(num_weeks)
self.num_playoff_slots = int(num_playoff_slots)
self.data_dir = data_dir
Expand Down Expand Up @@ -94,7 +94,7 @@ def calculate(self, week, week_for_report, standings, remaining_matchups):
sorted_divisions = self.group_by_division(teams_for_playoff_probs)

num_playoff_slots_per_division_without_leader = self.config.getint(
"Configuration", "num_playoff_slots_per_division", fallback=1) - 1
"Settings", "num_playoff_slots_per_division", fallback=1) - 1

# pick the teams making the playoffs
division_winners = []
Expand Down Expand Up @@ -189,7 +189,7 @@ def calculate(self, week, week_for_report, standings, remaining_matchups):
sorted_divisions = self.group_by_division(teams_for_playoff_probs)

num_playoff_slots_per_division_without_leader = self.config.getint(
"Configuration", "num_playoff_slots_per_division", fallback=1) - 1
"Settings", "num_playoff_slots_per_division", fallback=1) - 1

for division in sorted_divisions.values():
ranked_division = sorted(
Expand Down
3 changes: 2 additions & 1 deletion dao/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
from dao import base, yahoo, fleaflicker, sleeper, espn
from dao import base
from dao.platforms import yahoo, fleaflicker, sleeper, espn
Empty file added dao/platforms/__init__.py
Empty file.
File renamed without changes.
6 changes: 3 additions & 3 deletions dao/fleaflicker.py → dao/platforms/fleaflicker.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,11 +95,11 @@ def __init__(self,
self.num_regular_season_weeks = int(txt.split("-")[0]) - 1
else:
self.num_regular_season_weeks = config.getint(
"Configuration", "num_regular_season_weeks", fallback=13)
"Settings", "num_regular_season_weeks", fallback=13)
break
else:
self.num_playoff_slots = config.getint("Configuration", "num_playoff_slots", fallback=6)
self.num_regular_season_weeks = config.getint("Configuration", "num_regular_season_weeks", fallback=13)
self.num_playoff_slots = config.getint("Settings", "num_playoff_slots", fallback=6)
self.num_regular_season_weeks = config.getint("Settings", "num_regular_season_weeks", fallback=13)

# validate user selection of week for which to generate report
self.week_for_report = week_validation_function(self.config, week_for_report, self.current_week, self.season)
Expand Down
4 changes: 2 additions & 2 deletions dao/sleeper.py → dao/platforms/sleeper.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,15 +69,15 @@ def __init__(self,

self.current_week = get_current_nfl_week_function(self.config, self.dev_offline)

self.current_week = self.config.getint("Configuration", "current_week")
self.current_week = self.config.getint("Settings", "current_week")

# validate user selection of week for which to generate report
self.week_for_report = week_validation_function(self.config, week_for_report, self.current_week, self.season)

self.num_playoff_slots = self.league_settings.get("playoff_teams")
self.num_regular_season_weeks = (int(self.league_settings.get("playoff_week_start")) - 1) \
if self.league_settings.get("playoff_week_start") > 0 \
else self.config.get("Configuration", "num_regular_season_weeks")
else self.config.get("Settings", "num_regular_season_weeks")
self.roster_positions = dict(Counter(self.league_info.get("roster_positions")))
self.has_median_matchup = bool(self.league_settings.get("league_average_match"))
self.median_score_by_week = {}
Expand Down
2 changes: 1 addition & 1 deletion dao/yahoo.py → dao/platforms/yahoo.py
Original file line number Diff line number Diff line change
Expand Up @@ -259,7 +259,7 @@ def map_data_to_base(self, base_league_class):
league.median_score = 0
league.is_faab = True if int(self.league_info.settings.uses_faab) == 1 else False
if league.is_faab:
league.faab_budget = self.config.getint("Configuration", "initial_faab_budget", fallback=100)
league.faab_budget = self.config.getint("Settings", "initial_faab_budget", fallback=100)
league.url = self.league_info.url

league.player_data_by_week_function = self.get_player_data
Expand Down
2 changes: 1 addition & 1 deletion main.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ def main(argv):
logger.error("Error: {0}\n{1}".format(dnfe, traceback.format_exc()))
logger.error(
"MISSING DEPENDENCY: {0}. Please run `pip install {1}` and retry the report generation.".format(
dependency, re.split("\W+", dependency)[0]))
dependency, re.split("\\W+", dependency)[0]))
except VersionConflict as vce:
missing_dependency_count += 1
logger.error("Error: {0}\n{1}".format(vce, traceback.format_exc()))
Expand Down
10 changes: 5 additions & 5 deletions report/builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,20 +46,20 @@ def __init__(self,
self.platform = platform
self.platform_str = str.capitalize(platform)
else:
self.platform = self.config.get("Configuration", "platform")
self.platform_str = str.capitalize(self.config.get("Configuration", "platform"))
self.platform = self.config.get("Settings", "platform")
self.platform_str = str.capitalize(self.config.get("Settings", "platform"))
if league_id:
self.league_id = league_id
else:
self.league_id = self.config.get("Configuration", "league_id")
self.league_id = self.config.get("Settings", "league_id")
if game_id:
self.game_id = game_id
else:
self.game_id = self.config.get("Configuration", "game_id")
self.game_id = self.config.get("Settings", "game_id")
if season:
self.season = season
else:
self.season = self.config.get("Configuration", "season")
self.season = self.config.get("Settings", "season")

self.save_data = save_data
# refresh data pulled from external web sources: bad boy data from USA Today, beef data from Fox Sports
Expand Down
2 changes: 1 addition & 1 deletion report/data.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ def __init__(self, config, league: BaseLeague, season_weekly_teams_results, week
# option to disqualify manually configured team(s) (in config.ini) for current week of coaching efficiency
self.coaching_efficiency_dqs = {}
if int(week_counter) == int(week_for_report):
disqualified_teams = config.get("Configuration", "coaching_efficiency_disqualified_teams")
disqualified_teams = config.get("Settings", "coaching_efficiency_disqualified_teams")
if disqualified_teams:
for team in disqualified_teams.split(","):
self.coaching_efficiency_dqs[team] = -2
Expand Down
4 changes: 2 additions & 2 deletions report/pdf/generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -1295,15 +1295,15 @@ def generate_pdf(self, filename_with_path, line_chart_data_list):
"team performances through the end of the regular fantasy season." %
"{0:,}".format(
int(self.playoff_prob_sims) if self.playoff_prob_sims is not None else
self.config.getint("Configuration", "num_playoff_simulations",
self.config.getint("Settings", "num_playoff_simulations",
fallback=100000)) + (
"\nProbabilities account for division winners in addition to overall "
"win/loss/tie record." if self.report_data.has_divisions else ""),
metric_type="playoffs",
footer_text="† Predicted Division Leaders{0}".format(
"<br></br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"
"‡ Predicted Division Qualifiers" if self.config.getint(
"Configuration", "num_playoff_slots_per_division", fallback=1) > 1 else "")
"Settings", "num_playoff_slots_per_division", fallback=1) > 1 else "")
if self.report_data.has_divisions else None
))

Expand Down
75 changes: 75 additions & 0 deletions utils/app_config_parser.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import logging
import os
import re
from collections import defaultdict
from configparser import ConfigParser, NoOptionError, NoSectionError

logger = logging.getLogger(__name__)
Expand All @@ -8,9 +11,18 @@
# a valid fallback value.
_UNSET = object()

_default_dict = dict
DEFAULTSECT = "DEFAULT"


class AppConfigParser(ConfigParser):

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)

self.comment_map = defaultdict(dict)

# noinspection PyShadowingBuiltins
def get(self, section, option, *, raw=False, vars=None, fallback=_UNSET):
"""Get an option value for a given section.
Expand Down Expand Up @@ -56,3 +68,66 @@ def get(self, section, option, *, raw=False, vars=None, fallback=_UNSET):
return value
else:
return self._interpolation.before_get(self, section, option, value, d)

def read(self, filenames, encoding=None):
"""Read and parse a filename or an iterable of filenames.
Files that cannot be opened are silently ignored; this is
designed so that you can specify an iterable of potential
configuration file locations (e.g. current directory, user's
home directory, systemwide directory), and all existing
configuration files in the iterable will be read. A single
filename may also be given.
Return list of successfully read files.
"""
if isinstance(filenames, (str, bytes, os.PathLike)):
filenames = [filenames]
read_ok = []
for filename in filenames:
try:
with open(filename, encoding=encoding) as fp:
section = None
key_comments = []
lines = fp.readlines()
for line in lines:
if str(line).startswith("["):
section = re.sub("[\\W_]+", "", line)
self.comment_map[section] = {}
else:
if str(line).startswith(";"):
key_comments.append(line)
else:
if "=" in line:
key = line.split("=")[0].strip()
self.comment_map[section][key] = key_comments
key_comments = []
fp.seek(0)
self._read(fp, filename)
except OSError:
continue
if isinstance(filename, os.PathLike):
filename = os.fspath(filename)
read_ok.append(filename)
return read_ok

def _write_section(self, fp, section_name, section_items, delimiter):
"""Write a single section to the specified `fp'."""
fp.write("[{}]\n".format(section_name))
section_comments_map = self.comment_map.get(section_name)

for key, value in section_items:
value = self._interpolation.before_write(self, section_name, key, value)
if value is not None or not self._allow_no_value:
value = delimiter + str(value).replace('\n', '\n\t')
else:
value = ""

if key in section_comments_map.keys():

key_comments = section_comments_map.get(key)
for comment in key_comments:
fp.write(comment)

fp.write("{}{}\n".format(key, value))
fp.write("\n")
Loading

0 comments on commit 3f784dc

Please sign in to comment.