From 197ecb5a10ed755ba03ca9a21c1b0eabef05132a Mon Sep 17 00:00:00 2001 From: shnee Date: Mon, 26 Sep 2022 15:16:05 -0400 Subject: [PATCH] init commit --- draft_kings_lineup.py | 432 +++++++++++++++++++++++++++++++++++++++++ fantasypros_proj.py | 437 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 869 insertions(+) create mode 100755 draft_kings_lineup.py create mode 100644 fantasypros_proj.py diff --git a/draft_kings_lineup.py b/draft_kings_lineup.py new file mode 100755 index 0000000..a716a73 --- /dev/null +++ b/draft_kings_lineup.py @@ -0,0 +1,432 @@ +#!/usr/bin/python3 + +import re +from threading import Thread, Semaphore +from draft_kings import Client +from fantasypros_proj import FantasyProsProjections + +MIN_PERPOINT = 50 +MAX_PERPOINT = 2000 +# MIN_PERPOINT = 50 +# MAX_PERPOINT = 1000 +# MIN_PERPOINT = 200 +# MAX_PERPOINT = 800 +MAX_SALARY = 50000 +LINEUP_POS = ['qb', 'rb', 'wr', 'te', 'dst'] +MAX_THREADS = 3 +lock = Semaphore(value=MAX_THREADS) + +class DraftKingsDraft: + + def __init__(self, draft_group_id): + self._draft_group_id = draft_group_id + self._proj = {} + self._get_fpros_proj() + self._players = ( + Client(). + available_players(draft_group_id=draft_group_id). + players) + self._pos = { + 'qb': DraftKingsDraftPosition([], 'qb', num_in_lineup=1), + 'rb': DraftKingsDraftPosition([], 'rb', num_in_lineup=3), + 'wr': DraftKingsDraftPosition([], 'wr', num_in_lineup=3), + 'te': DraftKingsDraftPosition([], 'te', num_in_lineup=1), + 'dst': DraftKingsDraftPosition([], 'dst', num_in_lineup=1), + 'k': DraftKingsDraftPosition([], 'k', num_in_lineup=0)} + print('DEBUG: Sorting players by position.') + self._sort_players_by_pos() + + # Initialize position groups for making lineups. + self._lineup_pos_combo_index = {} + threads = {} + for pos in LINEUP_POS: + threads[pos] = Thread(target=self.run_init_lineup, args=(pos,)) + threads[pos].start() + self._lineup_pos_combo_index[pos] = 0 + for pos in LINEUP_POS: + threads[pos].join() + + # self._find_good_subsets() + self._gen_lineup() + prev_lineup = self._lineup + while self._lineup.salary <= MAX_SALARY: + prev_lineup = self._lineup + print(self._lineup.pretty_str()) + success = self._improve_lineup() + if not success: + print('**** Failed') + break + self._gen_lineup() + self._lineup = prev_lineup + + def run_init_lineup(self, pos): + lock.acquire() + print(f'DEBUG: Start \'{pos}\' thread.') + self._pos[pos].init_lineup() + lock.release() + print(f'DEBUG: \'{pos}\' thread ended.') + + def draft_kings_players(self): + """ Returns all players in the draft_kings module format. """ + return self._players + + def quarter_backs(self): + return self.qbs() + def qbs(self): + return self._get_pos_players('qb') + def running_backs(self): + return self.rbs() + def rbs(self): + return self._get_pos_players('rb') + def wide_reveivers(self): + return self.wrs() + def wrs(self): + return self._get_pos_players('wr') + def tight_ends(self): + return self.tes() + def tes(self): + return self._get_pos_players('te') + def defenses(self): + return self.dsts() + def dsts(self): + return self._get_pos_players('dst') + def kickers(self): + return self.ks() + def ks(self): + return self._get_pos_players('k') + + def _get_pos_players(self, pos): + """ TODO REM is this useful? """ + return self._pos[pos]._players + + def _get_fpros_proj(self): + for pos in LINEUP_POS: + self._proj[pos] = FantasyProsProjections(pos) + + def _sort_players_by_pos(self): + + for player in self._players: + pos = player.position_details.name.lower() + + if pos not in self._pos: + raise Exception("Can't handle position: " + + player.position_details.name) + + fpros_proj = self._proj[pos].get_projection(player.first_name, player.last_name) + + self._pos[pos]._players.append(DraftKingsDraftPlayer(player, fpros_proj=fpros_proj)) + + def _get_player_for_lineup(self, pos, lineup_slot): + return self._get_pos_players(pos)[self._pos[pos]. + lineup_indexes[lineup_slot]] + + def _gen_lineup(self): + players = {} + for pos in LINEUP_POS: + players[pos] = ( + list(self._pos[pos] + .pos_combos[self._lineup_pos_combo_index[pos]] + .players)) + + self._lineup = DraftKingsDraftLineup( + qb=players['qb'][0], + rb1=players['rb'][0], + rb2=players['rb'][1], + wr1=players['wr'][0], + wr2=players['wr'][1], + wr3=players['wr'][2], + te=players['te'][0], + flex=players['rb'][2], + dst=players['dst'][0]) + + def lineup(self): + return self._lineup + + def _improve_lineup(self): + lowest = {'pos': LINEUP_POS[0], 'perpoint': MAX_PERPOINT} + + # Find lowest perpoint for a position group. + for pos_key in LINEUP_POS: + + pos = self._pos[pos_key] + + # Make sure this position isn't already the highest possible. + if pos.highest: + continue + + # lineup_indexes = pos.lineup_indexes + # for player in pos._players[lineup_indexes[-1]:]: + # if player.perpoint < lowest['perpoint']: + # lowest['pos'] = pos_key + # lowest['perpoint'] = player.perpoint + + index = self._lineup_pos_combo_index[pos_key] + print("{} perpoint {}".format(pos_key, pos.pos_combos[index].fpros_perpoint)) + if pos.pos_combos[index].fpros_perpoint < lowest['perpoint']: + lowest['perpoint'] = pos.pos_combos[index].fpros_perpoint + lowest['pos'] = pos_key + + # If we didn't find a pos group that isn't allready highest then we + # can't improve. This is, however, very unlikely so we log a warning. + if lowest['perpoint'] == MAX_PERPOINT: + print("WARNING: All position groups are already maxxed! " + + "Something probably went wrong.") + return False + + # Bump up lowest pos. + index = self._lineup_pos_combo_index[lowest['pos']] + if (index + 1) == len(self._pos[lowest['pos']].pos_combos): + self._pos[lowest['pos']].highest = True + return self._improve_lineup() + else: + self._lineup_pos_combo_index[lowest['pos']] += 1 + return True + +class DraftKingsDraftPositionCombo: + def __init__(self, players): + self.players = players + self.salary = 0 + self.points = 0.0 + self.fpros_points = 0.0 + for player in players: + self.salary += player.salary + self.points += player.points + self.fpros_points += player.fpros_proj + self.perpoint = self.salary / self.points if self.points != 0.0 else 0.0 + self.fpros_perpoint = self.salary / self.fpros_points if self.fpros_points != 0.0 else 0.0 + + def __str__(self): + combo = (f'{{ fpros_perpoint: {self.fpros_perpoint}, ' + + f'fpros_points: {self.fpros_points}, ' + + f'perpoint: {self.perpoint}, points: {self.points}, ' + + f'salary: {self.salary}, players: [ ') + players = [] + for player in self.players: + players.append(player.last_name) + combo += ', '.join(players) + ' ] }' + return combo + +class DraftKingsDraftPosition: + def __init__(self, + players, + pos_str, + start_index=0, + stop_index=0, + num_in_lineup=1): + + self._players = players + self._sort_players(lambda p: p.perpoint) + self.pos = pos_str + + # If start_index or stop_index was manually set then use those values, + # if not, then calculate the indexes based on MIN_PERPOINT and + # MAX_PERPOINT. + if start_index != 0 or stop_index != 0: + self.start_index = start_index + self.stop_index = stop_index + else: + self.start_index = 0 + self.stop_index = 0 + self._calc_start_index() + self._calc_stop_index() + print(f'DEBUG: Using {self.stop_index-self.start_index} players in ' + + f'the \'{pos_str}\' group.') + + self.num_in_lineup = num_in_lineup + self.highest = False + self.lineup_indexes = [] + self.pos_combos = [] + self.pos_combos_sets = set() + + # TODO This is where we initialized lineup indexes, but its not clear + # if thats needed going forward. + # self. + # if len(players) == 0: + # self._init_lineup_indexes() + + # def players(self, new_players=None): + # if new_players is None: + # return self._players + # else: + # self._players = new_players + # self._calc_start_index() + # self._calc_stop_index() + + def init_lineup(self): + + self._sort_players(lambda p: p.fpros_perpoint) + self._calc_start_index() + self._calc_stop_index() + + self.lineup_indexes = [] + for i in range(self.num_in_lineup): + self.lineup_indexes.append(self.start_index + i) + + self.pos_combos_sets = set() + self.pos_combos = [] + print(f'DEBUG: Calculating position combo sets for {self.pos}.') + self._calc_pos_combo_sets(set(), self.num_in_lineup) + for combo_set in self.pos_combos_sets: + self.pos_combos.append(DraftKingsDraftPositionCombo(combo_set)) + # self.pos_combos = sorted(self.pos_combos, key=lambda c: c.perpoint) + print(f'DEBUG: Sorting position combo sets for {self.pos}.') + self.pos_combos = sorted(self.pos_combos, key=lambda c: c.fpros_perpoint) + self.pos_combos_sets = set() + + # Strip out all entries in pos_combos that aren't increasing in points. + stripped_pos_combos = [] + points_to_beat = 0 + print(f'DEBUG: Stripping position combo sets for {self.pos}.') + for combo in self.pos_combos: + if combo.fpros_perpoint > MIN_PERPOINT and combo.fpros_points > points_to_beat: + stripped_pos_combos.append(combo) + points_to_beat = combo.fpros_points + self.pos_combos = stripped_pos_combos + + def _calc_pos_combo_sets(self, starting_perm, final_size): + + players_avail = self._players[self.start_index:self.stop_index] + for player in starting_perm: + players_avail = ( + list(filter( + lambda p: p.object.player_id != player.object.player_id, + players_avail))) + + done_recursing = len(starting_perm) == (final_size - 1) + for player in players_avail: + new_perm = starting_perm.copy() + new_perm.add(player) + if done_recursing: + self.pos_combos_sets.add(frozenset(new_perm)) + else: + self._calc_pos_combo_sets(new_perm, final_size) + + # def get_next_best_players_index(self, starting_index): + # player1 = self._players[starting_index] + # for i in range(starting_index + 1, len(self._players)): + # tmp_player = self._players[i] + # if tmp_player.points > player1.points: + # return i + + def _calc_start_index(self): + """ Assumes players are already sorted. """ + self.start_index = 0 + for i, player in enumerate(self._players): + if player.fpros_perpoint > MIN_PERPOINT: + self.start_index = i + break + print(f'DEBUG: Set \'{self.pos}\' start_index to {self.start_index}.' + + f' Using {self.stop_index-self.start_index} players.') + + def _calc_stop_index(self): + """ Assumes players are already sorted. """ + self.stop_index = len(self._players) + for i, player in enumerate(self._players): + if player.fpros_perpoint > MAX_PERPOINT: + self.stop_index = i + break + print(f'DEBUG: Set \'{self.pos}\' stop_index to {self.stop_index}.' + + f' Using {self.stop_index-self.start_index} players.') + + def _sort_players(self, key): + self._players = sorted(self._players, key=key) + +class DraftKingsDraftPlayer: + def __init__(self, player, fpros_proj=0.0): + self.first_name = player.first_name + self.last_name = player.last_name + self.fpros_proj = fpros_proj + self.salary = player.draft_details.salary + self.points = player.points_per_game + self.perpoint = self.salary / self.points if self.points != 0 else 0 + self.fpros_perpoint = self.salary / self.fpros_proj if self.fpros_proj != 0 else 0 + self.pos = player.position_details.name + self.object = player + + def name(self): + return "%s %s" % (self.first_name, self.last_name) + + def __str__(self): + return ("{ fpros_perpoint: %f, perpoint: %f, name: \"%s\", salary: %d, fpros_proj: %f, points: %f, " + + "pos: %s}") % ( + self.fpros_perpoint, + self.perpoint, + self.name(), + self.salary, + self.fpros_proj, + self.points, + self.pos) + +class DraftKingsDraftLineup: + def __init__(self, qb, rb1, rb2, wr1, wr2, wr3, te, flex, dst): + self._qb = qb + self._rb1 = rb1 + self._rb2 = rb2 + self._wr1 = wr1 + self._wr2 = wr2 + self._wr3 = wr3 + self._te = te + self._flex = flex + self._dst = dst + self.points = self.calc_points() + self.salary = self.calc_salary() + + def __str__(self): + return "lineup: { points: %f, salary: %d }" % ( + self.points, + self.salary) + + def pretty_str(self): + col_width = [0, 0, 0, 0, 0] + rows = [] + lineup_str = (f'Lineup Total-Points: {self.points} ' + + f'Total-Salary: {self.salary}\n') + for player in [self._qb, self._rb1, self._rb2, self._wr1, + self._wr2, self._wr3, self._te, self._flex, self._dst]: + + row = [player.pos.upper(), player.name(), str(player.fpros_proj), + str(player.salary), str(player.fpros_perpoint)] + for i, val in enumerate(row): + if len(val) > col_width[i]: + col_width[i] = len(val) + rows.append(row) + + for row in rows: + for i, val in enumerate(row): + lineup_str += val + ' ' + (' ' * (col_width[i]-len(val))) + lineup_str += '\n' + + return lineup_str + + def calc_dk_points(self): + return (self._qb.points + self._rb1.points + self._rb2.points + + self._wr1.points + self._wr2.points + self._wr3.points + + self._te.points + self._flex.points + self._dst.points) + + def calc_points(self): + return (self._qb.fpros_proj + self._rb1.fpros_proj + + self._rb2.fpros_proj + self._wr1.fpros_proj + + self._wr2.fpros_proj + self._wr3.fpros_proj + + self._te.fpros_proj + self._flex.fpros_proj + + self._dst.fpros_proj) + + def calc_salary(self): + return (self._qb.salary + self._rb1.salary + self._rb2.salary + + self._wr1.salary + self._wr2.salary + self._wr3.salary + + self._te.salary + self._flex.salary + self._dst.salary) + +def run(): + draft = DraftKingsDraft(74624) + for i, wr, in enumerate(draft.wrs()): + print("%s %s" % (wr.perpoint, wr.points)) + +# TODO REM STARTHERE we need to make all 'points' and 'perpoint' to have +# 'fpros' or 'dk' prefix. +def debug(): + draft = DraftKingsDraft(74624) + return draft + + +if __name__ == '__main__': + # run() + debug() diff --git a/fantasypros_proj.py b/fantasypros_proj.py new file mode 100644 index 0000000..a01ac5f --- /dev/null +++ b/fantasypros_proj.py @@ -0,0 +1,437 @@ +#!/usr/bin/python3 + +import pandas +import re + +class FantasyProsProjections: + + # TODO REM need to add week # and scoring. + PROJ_URL = 'https://www.fantasypros.com/nfl/projections/{pos}.php' + ROS_URL = 'https://www.fantasypros.com/nfl/rankings/ros-ppr-overall.php' + PLAYER_URL = ('https://www.fantasypros.com/nfl/projections/{name}.php?' + + 'week={week}') + + # TEMPORARY CONSTANT TODO + WEEK_NUM = 1 + + def __init__(self, pos): + self.pos = pos + self.week = self.WEEK_NUM + self.data = {} + self._get_proj_data() + + def get_projection(self, first_name, last_name): + for fpros_name, proj in self.data.items(): + if (re.match(f'.*{first_name}.*', fpros_name) and + re.match(f'.*{last_name}.*', fpros_name)): + return proj + + # If we got here then we couldn't find the name. This can happen if the + # player is out for the week, therefore we look.... TODO + site_proj = self._get_proj_from_player_site( + f'{first_name} {last_name}') + + # TODO In the future maybe we can keep IGNORED_PLAYERS empty and + # populate it based on these if statements. + # if site_proj > 0: + # print(f'DEBUG: Projection from site for {first_name} ' + + # f'{last_name} was {site_proj}.') + # if site_proj < 2: + # print(f'IGNORE: {first_name} {last_name}XXX') + return site_proj + + def _get_proj_from_player_site(self, name): + if name in self.IGNORED_PLAYERS: + return 0.0 + elif name in self.FPROS_NAME_TO_URL_PARAM: + name_param = self.FPROS_NAME_TO_URL_PARAM[name] + else: + print(f'DEBUG: Getting projection for {name} from player site.') + # Convert the name to our URL arg by: + # - Strip 'II' 'III' at the end of the name. + # - Replace spaces with '-'s. + # - Convert to lowercase. + name_param = (re.sub(r' [IV]+$', '', name) + .replace(' ', '-') + .replace('.', '') + .replace('\'', '') + .lower()) + name_param = re.sub(r'-[js]r$', '', name_param) + + # TODO REM STARTHERE We still need to figure out how to detect when + # this fails.... + html = pandas.read_html(self.PLAYER_URL.format(name=name_param, + week=self.week)) + proj_points = html[0]['Points'].get(0) + if (isinstance(proj_points, str) and + re.match("^Player does not have", proj_points)): + return 0.0 + return proj_points + + def _get_proj_data(self): + html = pandas.read_html(self.PROJ_URL.format(pos=self.pos)) + data = html[0] + drop_keys = [] + for key in data.keys(): + if isinstance(data.keys(), pandas.core.indexes.multi.MultiIndex): + keyname = key[1] + else: + keyname = key + if keyname != 'Player' and keyname != 'FPTS': + drop_keys.append(key) + data2 = data.drop(drop_keys, axis=1) + for elem in data2.itertuples(): + # self.data.append( + # FantasyProsPlayerProjection( + # name=sub(r' \w+$', '', elem[1]), + # proj_points=elem[2])) + name = '' + if self.pos == 'dst': + name = elem[1] + else: + name = re.sub(r' \w+$', '', elem[1]) + self.data[name] = elem[2] + + # def _get_ros_data(self): + + # These players are ignored. + # + # TODO only Cal Adomitis do we really want to ignore. We ignore the other + # to speed things up and to potentially avoid rate limiting (if there is + # any) for fpros. + # IGNORED_PLAYERS = { + # 'Cal Adomitis', + # 'Jack Coco', + # 'Damarea Crockett', # This guy has no projection and doesn't work, + # # not sure why. + # 'Bisi Johnson', + # 'Brian Robinson Jr.', + # 'Cameron Batson', + # 'Drew Ogletree', + # 'Dylan Parham', + # 'Gerrit Prince', + # 'Johnny Johnson III', + # 'Kevin Rader', + # 'P.J. Walker', + # 'Pierre Strong Jr.', + # 'Ricky Person Jr.', + # 'Ronald Jones II', + # 'Scotty Miller', + # 'Spencer Brown', + # 'Troy Hairston', + # 'Velus Jones Jr.', + # 'Willie Johnson' + # } + # # Purposely not ignoring Gabe Davis because he's projected to score + # # points. + IGNORED_PLAYERS = { + 'Mike Evans', + 'Deshaun Watson', + 'DeAndre Hopkins', + 'Jameson Williams', + 'Tyrion Davis-Price', + 'Zach Wilson', + 'Kenneth Walker III', + 'Sam Darnold', + 'Colt McCoy', + 'Blaine Gabbert', + 'Gus Edwards', + 'Jeff Driskel', + 'Tim Boyle', + 'Giovani Bernard', + 'Mason Rudolph', + 'Elijah Mitchell', + 'Brian Robinson Jr.', + 'Ryan Griffin', + 'Danny Etling', + 'Kenjon Barner', + 'Patrick Laird', + 'Kylin Hill', + 'Tyler Goodson', + 'Bailey Zappe', + 'Ben Mason', + 'Ricky Person Jr.', + 'Tyler Badie', + 'J.J. Taylor', + 'Kevin Harris', + 'Shane Buechele', + 'Chris Oladokun', + 'Sam Ehlinger', + 'D\'vonte Price', + 'Phillip Lindsay', + 'Kellen Mond', + 'Josh Rosen', + 'Jeremy McNichols', + 'Anthony McFarland Jr.', + 'Jason Huntley', + 'John Kelly Jr.', + 'P.J. Walker', + 'Matt Corral', + 'Jacob Eason', + 'Jake Luton', + 'John Lovett', + 'Latavius Murray', + 'Nathan Peterman', + 'Darrynton Evans', + 'Darius Anderson', + 'Royce Freeman', + 'Gerrid Doaks', + 'Skylar Thompson', + 'Matt Barkley', + 'John Lovett', + 'ZaQuandre White', + 'Raheem Blackshear', + 'Duke Johnson', + 'David Blough', + 'Ty Chandler', + 'Bryant Koback', + 'Greg Bell', + 'Jermar Jefferson', + 'Jason Cabinda', + 'Chris Streveler', + 'Jake Browning', + 'Nick Bawden', + 'Zonovan Knight', + 'Trayveon Williams', + 'Elijah Holyfield', + 'Chase Garbers', + 'Logan Woodside', + 'Sincere McCormick', + 'Austin Walter', + 'Trenton Cannon', + 'Sam Howell', + 'Ian Book', + 'Jaret Patterson', + 'Kennedy Brooks', + 'E.J. Perry', + 'Easton Stick', + 'Mekhi Sargent', + 'Larry Rountree III', + 'Jarrett Guarantano', + 'Bryce Perkins', + 'Jonathan Ward', + 'Kyren Williams', + 'Ronnie Rivers', + 'Sean Mannion', + 'Darwin Thompson', + 'Damien Williams', + 'B.J. Baylor', + 'Trey Lance', + 'Josh Johnson', + 'Damarea Crockett', + 'Devine Ozigbo', + 'Davis Webb', + 'Sandro Platzgummer', + 'Jashaun Corbin', + 'Malik Davis', + 'Qadree Ollison', + 'Tre\'Quan Smith', + 'Antoine Wesley', + 'Deven Thompkins', + 'Samori Toure', + 'Travis Fulgham', + 'Juwann Winfree', + 'Slade Bolden', + 'Makai Polk', + 'Binjimen Victor', + 'Ty Montgomery', + 'Tyquan Thornton', + 'Kristian Wilkerson', + 'Laquon Treadwell', + 'Lynn Bowden Jr.', + 'Tre Nixon', + 'Justyn Ross', + 'Corey Coleman', + 'Daurice Fountain', + 'Jerrion Ealy', + 'Cornell Powell', + 'Ethan Fernea', + 'Steven Sims Jr.', + 'Calvin Austin III', + 'Anthony Miller', + 'Cody White', + 'Michael Woods II', + 'Jakeem Grant Sr.', + 'Isaiah Weston', + 'Chester Rogers', + 'Mike Harley Jr.', + 'Daylen Baldwin', + 'Andre Roberts', + 'C.J. Saunders', + 'Preston Williams', + 'Derek Wright', + 'Kawaan Baker', + 'Rashid Shaheed', + 'Kirk Merritt', + 'N\'Keal Harry', + 'Nsimba Webster', + 'Isaiah Coulter', + 'John Metchie III', + 'Drew Estrada', + 'Johnny Johnson III', + 'Jalen Camp', + 'Erik Ezukanma', + 'Braylon Sanders', + 'Freddie Swain', + 'Marquez Stevenson', + 'Tavon Austin', + 'Isaiah Hodgins', + 'Blake Proehl', + 'Bisi Johnson', + 'Thomas Hennigan', + 'Travis Toivonen', + 'Trishton Jackson', + 'Dan Chisena', + 'Tom Kennedy', + 'Maurice Alexander', + 'Denzel Mims', + 'Rodney Adams', + 'Irvin Charles', + 'Tarik Black', + 'Trenton Irwin', + 'Kwamie Lassiter II', + 'DJ Turner', + 'Isaiah Zuber', + 'Chris Lacy', + 'Racey McMath', + 'Dez Fitzpatrick', + 'Mason Kinsey', + 'Kyric McGowan', + 'Alex Erickson', + 'Marken Michel', + 'Deon Cain', + 'Devon Allen', + 'Auden Tate', + 'Kendric Pryor', + 'Willie Johnson', + 'Kevin Austin Jr.', + 'Seth Williams', + 'Jaylon Moore', + 'Joe Reed', + 'Michael Bandy', + 'Jason Moore Jr.', + 'Javon Wims', + 'Jeff Cotton Jr.', + 'Lance McCutcheon', + 'J.J. Koski', + 'Austin Trammell', + 'Dareke Young', + 'Cody Thompson', + 'J.J. Arcega-Whiteside', + 'Cade Johnson', + 'Bo Melton', + 'Calvin Ridley', + 'Josh Ali', + 'Frank Darby', + 'Cameron Batson', + 'Willie Snead IV', + 'Malik Turner', + 'Tay Martin', + 'Jalen Virgil', + 'Tim Patrick', + 'Vyncint Smith', + 'Darrius Shepherd', + 'James Washington', + 'Collin Johnson', + 'Marcus Johnson', + 'Kalil Pimpleton', + 'Brandon Smith', + 'Dontario Drummond', + 'David Wells', + 'Tyler Davis', + 'Shaun Beyer', + 'Jack Coco', + 'Charlie Kolar', + 'Jalen Wydermyer', + 'Matt Sokol', + 'Blake Bell', + 'Jordan Franks', + 'Kendall Blanton', + 'James Winchester', + 'Drew Ogletree', + 'Jared Scott', + 'Nikola Kalinic', + 'Rodney Williams II', + 'Jesse James', + 'Miller Forristall', + 'Zaire Mitchell-Paden', + 'Colin Thompson', + 'Josh Babicz', + 'Lucas Krull', + 'Zach Wood', + 'J.P. Holtz', + 'Patrick Scales', + 'Chase Allen', + 'Teagan Quitoriano', + 'Mason Schreck', + 'Adam Shaheen', + 'Zach Davidson', + 'Andrew DePaola', + 'Nick Muse', + 'Garrett Griffin', + 'Lawrence Cager', + 'Cal Adomitis', + 'Clark Harris', + 'Nick Bowers', + 'Cole Fotheringham', + 'Tommy Hudson', + 'Thomas Odukoya', + 'Kevin Rader', + 'Curtis Hodges', + 'Eli Wolf', + 'Tyree Jackson', + 'Dalton Keene', + 'Gerrit Prince', + 'Stone Smartt', + 'Hunter Kampmoyer', + 'Bernhard Seikovits', + 'Jared Pinkney', + 'Roger Carter Jr.', + 'Tyler Mabry', + 'Tyler Ott', + 'Feleipe Franks', + 'John FitzPatrick', + 'MyCole Pruitt', + 'Beau Brinkley', + 'Tucker Fisk', + 'Jordan Matthews', + 'Troy Fumagalli', + 'Greg Dulcich', + 'Dominique Dafney', + 'Austin Allen', + 'Ian Bunting', + 'Sean McKeon' + } + + # All of these players don't have the expected url param. + # TODO Is this actually DRAFT_KINGS_NAME_TO_FPROS_URL_PARAM? + FPROS_NAME_TO_URL_PARAM = { + 'Bisi Johnson': 'olabisi-johnson', + 'Brian Robinson Jr.': 'brian-robinson-jr', + 'Cameron Batson': 'cam-batson', + 'Drew Ogletree': 'andrew-ogletree', + 'Dylan Parham': 'dylan-parham-te', + 'Gabe Davis': 'gabriel-davis', + 'Gerrit Prince': 'gerrit-prince-te', + 'Johnny Johnson III': 'johnny-johnson-iii', + 'Kevin Rader': 'kevin-radar', + 'P.J. Walker': 'phillip-walker', + 'Pierre Strong Jr.': 'pierre-strong-jr', + 'Ricky Person Jr.': 'ricky-person-jr', + 'Ronald Jones II': 'ronald-jones-ii', + 'Scotty Miller': 'scott-miller', + 'Spencer Brown': 'spencer-brown-rb', + 'Troy Hairston': 'troy-hairston-ii', + 'Velus Jones Jr.': 'velus-jonesjr', + 'Willie Johnson': 'willie-jackson' + } + + +class FantasyProsPlayerProjection: + + def __init__(self, name, proj_points): + self.name = name + self.proj_points = proj_points + + def __str__(self): + return str(self.__dict__)