import pandas as pd
pd.options.mode.chained_assignment = None
import numpy as np
import requests
import os
import datetime as dt
import random
from functools import reduce
import math
import itertools
import json
import sys
from scipy.stats import iqr

# %% Data Setup
def load_data(league_id, season, swid="", espn=""):
    '''
    Pull ESPN API data for a particular season (current API version only goes back to 2018).
    '''

    url = 'http://fantasy.espn.com/apis/v3/games/ffl/seasons/' + str(int(season)) + '/segments/0/leagues/' + str(int(league_id)) + '?view=mMatchupScore&view=mTeam&view=mSettings'
    r = requests.get(url,
                     cookies = {'SWID': swid,
                                'espn_s2': espn},
                                params={'view':'mMatchup'})
    d = r.json()

    #json = json.dumps(d)
    return d

def get_params(d):
    '''
    Returns general league information used throughout the analysis
    '''
    params = {}

    # general league setup
    league_size = d['settings']['size']
    roster_size = sum(d['settings']['rosterSettings']['lineupSlotCounts'].values())

    regular_season_end = d['settings']['scheduleSettings']['matchupPeriodCount']
    current_week = d['scoringPeriodId'] - 1
    matchup_week = d['scoringPeriodId']
    weeks_left = regular_season_end - current_week
    playoff_teams = d['settings']['scheduleSettings']['playoffTeamCount']
    playoff_matchup_length = d['settings']['scheduleSettings']['playoffMatchupPeriodLength']

    # roster construction
    # need to figure out rest of position codes
    slotcodes = {
        0: 'QB', 1: 'QB',
        2: 'RB', 3: 'Flex',
        4: 'WR', 5: 'Flex',
        6: 'TE',
        10: 'LB', 11: 'DL',
        14: 'DB',
        16: 'DST', 17: 'K',
        18: 'P', 19: 'HC',
        20: 'Bench', 21: 'IR',
        23: 'Flex'}
    lineup_slots = d['settings']['rosterSettings']['lineupSlotCounts']
    lineup_slots_df = pd.DataFrame.from_dict(lineup_slots, orient='index').rename(columns={0:'limit'})
    lineup_slots_df['posID'] = lineup_slots_df.index.astype('int')
    lineup_slots_df = lineup_slots_df[lineup_slots_df.limit > 0]
    lineup_slots_df['pos'] = lineup_slots_df.replace({'posID': slotcodes}).posID

    # Mapping team ID to team name
    prim_owner = [[game['primaryOwner'], game['id']] for game in d['teams']]
    prim_owner = pd.DataFrame(prim_owner).rename({0:"id_string", 1:"id"}, axis=1)
    owner_map = [[game['id'], game['firstName'], game['lastName'][:1]] for game in d['members']]
    owner_map = pd.DataFrame(owner_map).rename({0:"id_string", 1:"first_nm", 2:"last_init"}, axis=1)
    owner_map["team"] = owner_map.first_nm + " " + owner_map.last_init
    team2 = pd.merge(prim_owner, owner_map, on="id_string")
    team2["team"] = team2.team.str.title().str.replace(" ", "")
    team_map2 = dict(zip(team2.id, team2.team))

    # Get weekly matchups
    df = pd.DataFrame()
    for game in d['schedule']:
        if game['matchupPeriodId'] > d['settings']['scheduleSettings']['matchupPeriodCount']:
            continue
        if game['matchupPeriodId'] <= d['settings']['scheduleSettings']['matchupPeriodCount']:
            week = game['matchupPeriodId']
            team1 = game['home']['teamId']
            score1 = game['home']['totalPoints']
            team2 = game['away']['teamId']
            score2 = game['away']['totalPoints']
            matchups = pd.DataFrame([[week, team1, score1, team2, score2]],
                                    columns=["week", "team1", "score1", "team2", "score2"])
            df = df.append(matchups)
    df = df.replace({'team1':team_map2, 'team2':team_map2})

    #position = lineup_slots_df.pos.str.lower().drop(labels=['20','21']).tolist()
    position = lineup_slots_df.pos.str.lower().to_list()
    position = np.setdiff1d(position, ['bench', 'ir']).tolist()
    teams = list(team_map2.values())

    # get FAAB budget remaining
    faab = d["settings"]["acquisitionSettings"]["acquisitionBudget"]
    budget = pd.DataFrame()
    for tm in d["teams"]:
        teamid = tm["id"]
        faab_left = faab - tm["transactionCounter"]["acquisitionBudgetSpent"]
        df_tm = pd.DataFrame([[teamid, faab_left]],
                          columns=["team", "faab_left"])
        budget = budget.append(df_tm)
    budget = budget.replace({"team":team_map2})

    params = {"league_size": league_size,
              "roster_size": roster_size,
              "regular_season_end": regular_season_end,
              "current_week": current_week,
              "matchup_week": matchup_week,
              "weeks_left": weeks_left,
              "playoff_teams": playoff_teams,
              "team_map": team_map2,
              "matchup_df": df,
              "positions": position,
              "teams": teams,
              "slotcodes": slotcodes,
              "lineup_slots_df": lineup_slots_df,
              "playoff_matchup_length": playoff_matchup_length,
              "faab_remaining": budget
              }

    return params

def power_rank(d, league_id, season, week=None):
    '''
    Calculates a weekly power ranking and power score for each team
    '''

    # return parameters
    regular_season_end = get_params(d)["regular_season_end"]
    current_week = get_params(d)["current_week"]
    matchup_week = get_params(d)["matchup_week"]

    # get weekly matchups and scores
    df = get_params(d)["matchup_df"]

    # Calculate W/L
    df['team1_result'] = np.where(df['score1'] > df['score2'], 1, 0)
    df['team2_result'] = np.where(df['score2'] > df['score1'], 1, 0)

    # Account for ties
    mask = (df.score1 == df.score2) & (df.score1 > 0) & (df.score2 > 0)
    df.loc[mask, ['team1_result', 'team2_result']] = 0.5

    # convert dataframe to long format so each row is a team week, not matchup
    home = df.iloc[:,[0,1,2,5]].rename(columns={'team1':'team', 'score1':'score', 'team1_result':'result'})
    home['id'] = home['team'].astype(str) + home['week'].astype(str)
    away = df.iloc[:,[0,3,4,6]].rename(columns={'team2':'team', 'score2':'score', 'team2_result':'result'})
    away['id'] = away['team'].astype(str) + away['week'].astype(str)

    df_current = pd.concat([home, away])
    season_wins = (df_current.groupby(['team', 'week'])
                        .sum()
                        .groupby(level=0)
                        .cumsum()
                        .rename(columns={'result':'wins'})
                        .reset_index())
    season_wins['id'] = season_wins['team'].astype(str) + season_wins['week'].astype(str)
    season_wins = season_wins.drop(['team', 'week', 'score'], axis=1)
    df_current = pd.merge(df_current, season_wins, on='id')

    # Calculate total season points by team
    cumul_score = (df_current.groupby(['team', 'week'])
                             .sum()
                             .groupby(level=0)
                             .cumsum()
                             .reset_index())
    cumul_score['id'] = cumul_score['team'].astype(str) + cumul_score['week'].astype(str)

    cumul_score = (cumul_score.drop(['team', 'week', 'wins', 'result'], axis=1)
                             .rename(columns={'score':'total_pf'}))

    df_current = pd.merge(df_current, cumul_score, on='id')
    df_current['ppg'] = df_current['total_pf'] / df_current['week']

    # Calculate median scores
    median_score = (df_current.groupby(['week'])
                            .agg(np.median)
                            .rename(columns={'score':'score_med', 'total_pf':'total_pf_med', 'ppg':'ppg_med'})
                            .drop(['wins', 'result'], axis=1))
    df_current = pd.merge(df_current, median_score, how='left', on='week')

    # Calculate rolling standard deviation by team
    st_dev = df_current[['week', 'team', 'score', 'result', 'id']]
    st_dev = (st_dev.set_index('week')
            .sort_index()
            .groupby(['team'])['score']
            .expanding()
            .std()
            .to_frame()
            .rename(columns={'score':'sd'})
            .reset_index())
    st_dev['id'] = st_dev['team'].astype(str) + st_dev['week'].astype(str)
    df_current = st_dev.drop(['team','week'], axis=1).fillna(0).merge(df_current, on='id')

    # Calculate luck factor
    # actual w/l result vs your week score rank
    # positive means more lucky (win but lower score)
    # negative means more UNlucky (loss but higher score)
    df_current['week_luck'] = df_current['result'] - (df_current.groupby('week')['score'].rank() - 1) / 9
    luck_sum = (df_current.groupby(['team', 'week'])
                        .sum()
                        .groupby(level=0)
                        .cumsum()
                        .reset_index()
                        .loc[:,['team','week','week_luck']])
    luck_sum['luck_index'] = luck_sum['week_luck'] / luck_sum['week']
    luck_sum['id'] = luck_sum['team'].astype(str) + luck_sum['week'].astype(str)
    df_current = luck_sum.drop(['team', 'week', 'week_luck'], axis=1).merge(df_current, on='id')

    # Calculate ranking metrics
    exp_win = df_current[['week', 'team', 'result', 'week_luck']]
    exp_win['xwins'] = exp_win['result'] - exp_win['week_luck']
    exp_win = (exp_win.groupby(['team', 'week'])
              .sum()
              .groupby(level=0)
              .cumsum()
              .reset_index()
              .drop(['result', 'week_luck'], axis=1))
    exp_win['id'] = exp_win['team'].astype(str) + exp_win['week'].astype(str)
    df_current = exp_win.drop(['team', 'week'], axis=1).merge(df_current, on='id')

    three_wk_avg = (df_current.groupby('team')['score']
                    .transform(lambda x: x.rolling(3, 1)
                    .mean())
                    .rename('three_wk_avg'))
    df_current = pd.merge(df_current, three_wk_avg, left_index = True, right_index=True)

    three_wk_med = (df_current[['week', 'three_wk_avg']]
                    .groupby('week')
                    .agg(np.median)
                    .reset_index()
                    .rename(columns={'three_wk_avg':'three_wk_med'}))
    df_current = pd.merge(df_current, three_wk_med, how='left', on='week')

    # calculate indexes for power score
    df_current['win_index'] = (df_current['wins'] / (df_current['week']))
    df_current['score_index'] = (df_current['three_wk_avg'] / df_current['three_wk_med'])
    df_current['season_index'] = (df_current['total_pf'] / df_current['total_pf_med'])
    df_current['consistency'] = (1 - (df_current['sd'] / df_current['ppg'])) * df_current['season_index']

    cons_med = df_current[['week', 'consistency']]
    cons_med = cons_med.groupby('week').agg(np.median).rename(columns={'consistency':'consistency_med'})
    df_current = pd.merge(df_current, cons_med, on='week')
    df_current['consistency_index'] = df_current['consistency'] / df_current['consistency_med']

    df_current['power_rank_score'] = ((df_current['win_index'] * 0.75)
                                   - (df_current['luck_index'] * 0.2)
                                   + (df_current['consistency_index'] * 1.2)
                                   + (df_current['score_index'] * 0.9)
                                   + (df_current['season_index'] * 1.2))

    # Standardize so average=100
    pr_avg = df_current.loc[:,['week', 'power_rank_score']]
    pr_avg = pr_avg.groupby('week').agg(np.mean).rename(columns={'power_rank_score':'power_rank_avg'})
    df_current = pd.merge(df_current, pr_avg, on='week')
    df_current['power_score'] = (df_current.power_rank_score / df_current.power_rank_avg) * 100
    df_current['power_rank'] = df_current.groupby(['week'])['power_rank_score'].rank(ascending=False)
    df_current['rank_change'] = df_current.groupby(['team'])['power_rank'].diff() * -1
    df_current['rank_change'] = df_current.rank_change.fillna(0)
    df_current['score_change'] = df_current.groupby(['team'])['power_score'].diff()
    df_current['score_change'] = df_current.score_change.fillna(0)

    df_current = df_current[df_current.week < matchup_week]

    return df_current

# %% Simulate Current Week

def sim_week(d, positions, matchup_week, league_size, team_map, matchups, teams, slotcodes, pos_df, n_sim=10):
    '''
    Simulates current week matchups using aggregate projections from FantasyPros
    '''

    # get aggregate projections from FantasyPros
    positions.remove("flex")

    projections = pd.DataFrame()
    # Return current week's projections for all positions
    for pos in positions:
        url = 'https://www.fantasypros.com/nfl/projections/' + pos + '.php?scoring=HALF&week=' + str(matchup_week)
        df = pd.read_html(url)[0]

        # drop multi index column
        if isinstance(df.columns, pd.MultiIndex):
            df.columns = df.columns.droplevel()

        df['POSITION'] = pos
        df = df[['Player', 'FPTS', 'POSITION']]

        # remove team from player name
        if pos != 'dst':
            df['Player'] = df['Player'].str[:-3]
            df['Player'] = df['Player'].str.rstrip()

        if pos == 'dst':
            df['Player'] = df['Player'].str.split().str[-1] + ' DST'

        df['Player'] = df.Player.str.lower()
        df.columns = df.columns.str.lower()
        df['player'] = df['player'].str.replace('.', '', regex=True)
        df['player'] = df['player'].str.replace('\'', '', regex=True)
        df['player'] = df['player'].str.replace(' iii', '', regex=True)
        df['player'] = df['player'].str.replace(' ii', '', regex=True)
        df['player'] = df['player'].str.replace(' jr', '', regex=True)

        projections = projections.append(df)

    # fix wft name
    projections['player'] = projections['player'].replace(['team dst'],'washington dst')

    # load parameters
    df = matchups
    posns = pos_df.pos.str.lower().to_list()
    struc = pos_df.limit.to_list()

    # get actual and projected scores from espn
    # code thanks to Steven Morse: https://stmorse.github.io/journal/espn-fantasy-projections.html
    data = []
    week = matchup_week

    for tm in d['teams']:
        tmid = tm['id']
        for p in tm['roster']['entries']:
            name = p['playerPoolEntry']['player']['fullName']
            slot_id = p['lineupSlotId']
            slot  = slotcodes[slot_id]

            # injured status (try/except bc dst cannot be injured)
            inj = 'NA'
            try:
                inj = p['playerPoolEntry']['player']['injuryStatus']
            except:
                pass

            # projected/actual points
            proj, act = None, None
            for stat in p['playerPoolEntry']['player']['stats']:
                if stat['scoringPeriodId'] != week:
                    continue
                if stat['statSourceId'] == 0:
                    act = stat['appliedTotal']
                elif stat['statSourceId'] == 1:
                    proj = stat['appliedTotal']

            data.append([
                week, tmid, name, slot_id, slot, inj, proj, act
            ])

    proj = pd.DataFrame(data,
                        columns=['week', 'team', 'player', 'slot_id',
                                 'slot', 'status', 'proj', 'actual'])
    proj = proj.apply(lambda x: x.astype(str).str.lower() if x.dtype=='object' else x)
    proj = proj.replace({"team": team_map})
    proj = proj[(proj['slot'] != 'ir')]
    proj = proj.drop(['proj'], axis=1)
    proj['actual'] = np.where(proj['actual'] == 'none', np.nan, proj['actual'])

    # fix player names
    proj['player'] = proj['player'].str.replace('.', '', regex=True)
    proj['player'] = proj['player'].str.replace('\'', '', regex=True)
    proj['player'] = proj['player'].str.replace('/', '', regex=True)
    proj['player'] = proj['player'].str.replace(' iii', '', regex=True)
    proj['player'] = proj['player'].str.replace(' ii', '', regex=True)
    proj['player'] = proj['player'].str.replace(' jr', '', regex=True)

    # replace espn projections with aggregate projections
    proj = pd.merge(proj, projections, how='left', on='player').rename(columns={'fpts':'projected'})
    proj['projected'] = np.where(proj['projected'].isnull(), 0, proj['projected'])

    # add standard deviations (same as get_ros_projections)
    proj['sd'] = np.where(proj['position'] == 'qb', proj['projected']*0.2, proj['projected']*0.4)

    # get current week matchups
    matchups = df[['week', 'team1', 'score1', 'team2', 'score2']]
    matchups = matchups[matchups['week'] == matchup_week]
    matchups['team1'] = matchups.team1
    matchups['team2'] = matchups.team2
    matchups['game_id'] = range(1, int(league_size/2)+1)
    matchups = matchups.reset_index(drop=True)

    # initialize dicionaries for counts
    teams = list(team_map.values())
    teams_dict = {key: 0 for key in teams}
    n_wins = {key: 0 for key in teams}
    n_highest = {key: 0 for key in teams}
    n_lowest = {key: 0 for key in teams}

    # simulate current week scores
    projections["sd"] = np.where(projections['fpts'] == 'qb', projections['fpts']*0.2, projections['fpts']*0.4)
    ref = projections[["position", "fpts", "sd"]].groupby("position").quantile(0.75).reset_index()
    score_df = pd.DataFrame(columns={'team', 'score'})
    for sim in range(n_sim):
        # get starting lineeup of highest projected player by position
        # for each team, select top: 1QB, 2RB, 2WR, 1TE, 1FLEX, 1DST, 1K
        for_sim = pd.DataFrame()
        for tm in teams:
            for pos, num in zip(posns, struc):
                if pos != "flex":
                    starter = proj.query('team == @tm & slot == @pos & ~actual.isnull()')
                    for_sim = for_sim.append(starter)
                    if len(starter) < num:
                        selection = proj.query('team == @tm & position == @pos & actual.isnull()').sort_values(by="projected", ascending=False).head(num-len(starter))
                        selection['proj_slot'] = pos
                        if len(selection) < num - len(starter):
                            # use 75th percentile player if position is not filled
                            num_left = num - len(selection)
                            ref_proj = ref.loc[ref.query('position == @pos').index.repeat(num_left)].rename(columns={"fpts":"projected"})
                            ref_proj["team"] = tm
                            ref_proj['proj_slot'] = pos
                            for_sim = for_sim.append(ref_proj)
                        else:
                            for_sim = for_sim.append(selection)
                    pl_list = for_sim.player.tolist()

                if pos == "flex":
                    fl_starter = proj.query('team == @tm & slot == @pos & ~actual.isnull()')
                    if len(fl_starter) < num:
                        # select flex: 3rd RB/WR or 2nd TE
                        fpos = ["rb", "wr"]
                        fnum = [2, 2]
                        flex = pd.DataFrame()
                        for a, b in zip(fpos, fnum):
                            selection = proj.query('team == @ tm & position == @a & actual.isnull()').sort_values(by="projected", ascending=False)
                            selection = selection[~selection.player.isin(pl_list)]
                            selection = selection.groupby("position").head(1)
                            flex = flex.append(selection)
                        # select flex player
                        flex = flex.sort_values(by="projected", ascending=False).head(1)
                        flex['proj_slot'] = pos
                        for_sim = for_sim.append(flex)
                    else:
                        for_sim = for_sim.append(fl_starter)
        for_sim = for_sim.reset_index(drop=True)

        # if player hasn't played, simulate score otherwise use actual scores
        for index, row in for_sim['actual'].iteritems():
            try:
                if pd.isnull(row):
                    for_sim.at[index,'score'] = ((random.gauss(for_sim['projected'][index], for_sim['sd'][index]))
                                               + (random.uniform(-1, 1))) * 0.97
                else:
                    for_sim.at[index,'score'] = for_sim['actual'][index]
            except:
                pass

        for team in teams:
            teams_dict[team] = for_sim[for_sim['team'] == team].score.sum()
            score_df = score_df.append(for_sim[for_sim['team'] == team].groupby('team').score.sum().reset_index())

        a = matchups.filter(like='team').columns
        matchups['score' + a.str.lstrip('team')] = matchups[a].stack().map(teams_dict).unstack()

        # calculate wins and losses
        matchups['team1_result'] = np.where(matchups['score1'] > matchups['score2'], 1, 0)
        matchups['team2_result'] = np.where(matchups['score2'] > matchups['score1'], 1, 0)

        # account for ties
        mask = (matchups.score1 == matchups.score2)
        matchups.loc[mask, ['team1_result', 'team2_result']] = 0.5

        # convert dataframe to long format so each row is a team week, not matchup
        home = matchups.iloc[:,[0,1,2,5,6]].rename(columns={'team1':'team', 'score1':'score', 'team1_result':'wins'})
        away = matchups.iloc[:,[0,3,4,5,7]].rename(columns={'team2':'team', 'score2':'score', 'team2_result':'wins'})
        df_sim = pd.concat([home, away]).iloc[:,[1,2,3,4]]

        for team in teams:
            n_wins[team] += df_sim[df_sim['team'] == team].wins.values[0].astype(int)

        # get highest/lowest scorer and teams in top half (need to add this)
        high = df_sim.sort_values(by='score', ascending=False).iloc[0,0]
        low = df_sim.sort_values(by='score').iloc[0,0]
        n_highest[high] += 1
        n_lowest[low] += 1

    # convert dicts to df and combine
    game_id = df_sim.loc[:,['team', 'game_id']]
    wins = pd.DataFrame(n_wins.items(), columns=['team', 'n_wins'])
    highest = pd.DataFrame(n_highest.items(), columns=['team', 'n_highest'])
    lowest = pd.DataFrame(n_lowest.items(), columns=['team', 'n_lowest'])

    dfs = [game_id, wins, highest, lowest]

    week_sim = reduce(lambda left, right: pd.merge(left, right, on='team'), dfs)

    return week_sim, score_df

#%% Simulate Season

def get_ros_proj(d, current_week, matchup_week, league_size, roster_size, team_map):
    '''
    Returns rest of season projections for all rostered players
    Simulates a weekly score for each team using current roster.
    Used for season and playoff simulations, NOT current week.
    This *should* be flexible enough to fit almost every league.
    '''

    # Map players to teams
    players = []
    teams = []

    for i in range(league_size):
        for player in range(roster_size):
            try:
                players.append(d['teams'][i]['roster']['entries'][player]['playerPoolEntry']['player']['fullName'])
                teams.append(d['teams'][i]['id'])
            except IndexError:
                pass

    # Fix player names
    rosters = pd.DataFrame({'team': teams, 'player': players}).apply(lambda x: x.astype(str).str.lower())
    rosters = rosters.replace({'player':[',', '\(', '\)', '\'', ' jr.', '\.']}, {'player':''}, regex=True)
    rosters = rosters.replace(['michael '], 'mike ', regex=True)

    rosters = rosters.replace('william fuller v', 'will fuller')
    rosters['player'] = rosters['player'].str.replace(' iii', '')
    rosters['player'] = rosters['player'].str.replace(' ii', '')
    rosters['player'] = rosters['player'].str.replace(' iv', '')
    #rosters['player'] = rosters['player'].str.replace(' v', '')
    rosters['player'] = rosters['player'].str.replace('d/st', 'dst')

    # Add rest of season projections to players
    positions = get_params(d)["positions"]
    positions.remove("flex")
    positions = ['d' if i=='dst' else i for i in positions]
    d_map = {"Los Angeles Rams":"Rams", "Tampa Bay":"Buccaneers", "Buffalo":"Bills", "Miami":"Dolphins",
             "San Francisco":"49ers", "Dallas":"Cowboys", "Jacksonville":"Jaguars", "Washington":"Washington",
             "New England":"Patriots", "Baltimore":"Ravens", "Green Bay":"Packers", "Philadelphia":"Eagles",
             "New Orleans":"Saints", "Kansas City":"Chiefs", "New York Giants":"Giants", "New York Jets":"Jets",
             "Arizona":"Cardinals", "Pittsburgh":"Steelers", "Seattle":"Seahawks", "Carolina":"Panthers",
             "Atlanta":"Falcons", "Cleveland":"Browns", "Indianapolis":"Colts", "Denver":"Broncos",
             "Los Angeles Chargers":"Chargers", "Tennessee":"Titans", "Chicago":"Bears", "Minnesota":"Vikings",
             "Cincinnati":"Bengals", "Houston":"Texans", "Las Vegas":"Raiders", "Detroit":"Lions"}

    player_data = pd.DataFrame()
    proj = pd.DataFrame()
    for pos in positions:
        url = 'https://www.numberfire.com/nfl/fantasy/remaining-projections' + '/' + pos

        data = pd.read_html(url, header=None)[0]
        data = pd.DataFrame(np.vstack([data.columns, data]))
        data.columns = ["player"]
        if pos == "d":
            # replace DST with mascot name
            dst = data.player.str.split(" D/ST", expand=True)
            dst = dst.replace({0:d_map})
            dst[1] = "DST"
            dst["player"] = dst[0] + " " + dst[1]
            data = dst.player.to_frame()
        player_data = player_data.append(data, ignore_index=True)

        df = pd.read_html(url)[1]
        proj = proj.append(df, ignore_index=True)

    # Fix player names
    player_data[player_data.player.str.startswith("Amon-Ra")] = "Amon-Ra StBrown  A. St.Brown  WR DET"
    player_data[player_data.player.str.startswith("Equanimeous")] = "Equanimeous StBrown  E. St.Brown  WR GB"
    player_data = player_data.replace([',', '\(', '\)', '\'', ' Jr.', '\.', ' III', ' II', ' IV'], '', regex=True)
    player_data = player_data.replace(['Michael '],'Mike ', regex=True)
    player_data = player_data.iloc[:,0].str.split(expand=True)
    player_data['name'] = player_data[0].str.cat(player_data[1],sep=" ")

    proj.columns = proj.columns.get_level_values(1)
    proj = proj.fillna(0)
    proj["FP"] = proj.FP + (proj.Rec * 0.5)  # add half ppr scoring
    proj = proj.loc[:,"FP"].to_frame()

    # Combine datasets
    player_projections = pd.concat([player_data.iloc[:,[6,4,5]], proj], axis=1)
    player_projections.columns = ['player', 'position', "team", 'ros_proj']
    player_projections["position"].fillna("DST", inplace=True)
    player_projections = player_projections.apply(lambda x: x.astype(str).str.lower())

    # fix ESB and ARSB
    player_projections["player"][player_projections.player.str.startswith("amon-ra")] = "amon-ra st brown"
    player_projections["player"][player_projections.player.str.startswith("equanimeous")] = "equanimeous st brown"

    # get team values for defenses to merge on later
    player_projections['team'] = (np.where(player_projections.team == "none",
                      player_projections['player'].apply(lambda x: x.split(" ")[0]),
                      player_projections['team']))

    # fix team names to match CSV
    player_projections["team"] = player_projections.team.replace({"jac": "jax", "la":"lar", "wsh":"was"})

    # get injury data and expected return
    injuries = pd.DataFrame()
    df = pd.read_html("https://www.vegasinsider.com/nfl/injuries/")
    for i in range(len(df)):
        if len(df[i].columns) == 6:
            df[i] = df[i].rename(columns=df[i].iloc[0]).iloc[1]
            injuries = injuries.append(df[i])
    injuries = injuries[injuries.Pos.str.lower().isin(positions)]
    injuries["Player"] = injuries.Player.replace([',', '\(', '\)', '\'', ' Jr.', '\.', ' III', ' II', ' IV'], '', regex=True).str.lower()
    injuries["Pos"] = injuries.Pos.str.lower()
    injuries["Expected Return"] = pd.to_datetime(injuries["Expected Return"])
    injuries["wks_out"] = (injuries["Expected Return"] - pd.to_datetime(dt.datetime.now())).dt.days / 7
    injuries["wks_out"] = np.floor(injuries.wks_out)
    injuries["return_wk"] = injuries.wks_out + matchup_week
    injuries = injuries[["Player", "Pos", "wks_out", "return_wk"]]

    # get team  bye weeks
    tm_map = pd.read_csv("https://gist.githubusercontent.com/cnizzardini/13d0a072adb35a0d5817/raw/dbda01dcd8c86101e68cbc9fbe05e0aa6ca0305b/nfl_teams.csv").iloc[:,[1,2]]
    tm_map["Name"] = tm_map.Name.replace({'NY':'New York'}, regex=True)
    byes = pd.read_html("https://www.footballdiehards.com/nfl-bye-weeks.cfm")[0].dropna()
    byes = byes.replace({"Giants Giants":"Giants", "Jets Jets":"Jets"}, regex=True)
    byes = byes.assign(teams=byes['Teams'].str.split(',')).explode('teams').drop("Teams", axis=1)
    byes["teams"] = byes.teams.str.strip()   # remove whitespace
    byes = pd.merge(byes, tm_map, left_on="teams", right_on="Name")
    byes['dst_name'] = byes['Name'].apply(lambda x: x.split(" ")[-1])
    byes.loc[byes.dst_name == "Team", "dst_name"] = "Washington"    # replace WFT name
    byes["week"] = byes['week'].apply(lambda x: x.split(" ")[-1]).astype(int)
    byes["Abbreviation"] = byes.Abbreviation.str.lower()
    byes["dst_name"] = byes.dst_name.str.lower()
    byes = byes.rename({"week":"bye"}, axis=1)
    byes = byes[["bye", "teams", "Abbreviation", "dst_name"]]

    # merge byes with player projections
    pl_byes = pd.merge(player_projections, byes, left_on="team", right_on="Abbreviation")
    # merge dst byes
    dst_byes = pd.merge(player_projections[player_projections.position == "dst"], byes, left_on="team", right_on="dst_name")
    player_projections = pl_byes.append(dst_byes, ignore_index=True)

    # merge injured players
    player_projections = pd.merge(player_projections, injuries, left_on=["player", "position"], right_on=["Player", "Pos"], how="left")
    player_projections = player_projections[["player", "position", "ros_proj", "bye", "wks_out", "return_wk"]]
    player_projections['ros_proj'] = player_projections.ros_proj.astype(float)

    # Calculate points per game
    player_projections["bye_occ"] = np.where(player_projections.bye >= current_week, 1, 0)    # is the bye week in the future?
    player_projections["wks_out"] = player_projections.wks_out.fillna(0)
    player_projections["gms_rem"] = 18 - current_week - player_projections.bye_occ - player_projections.wks_out
    player_projections['ppg'] = player_projections.ros_proj / player_projections.gms_rem

    # set standard deviations
    # qb's have less variance than other positions
    player_projections['sd'] = np.where(player_projections['position'] == 'qb', player_projections['ppg']*0.2, player_projections['ppg']*0.4)

    team_projections = pd.merge(rosters, player_projections, how='left', on='player')
    team_projections["team"] = team_projections.team.astype(int)
    team_projections = team_projections.replace({"team":team_map})

    params = [team_projections, player_projections]

    return params

def sim_scores(d, df_current, current_week, curr_sim_wk, teams, pos_df, ros):
    # get parameters
    posns = pos_df.pos.str.lower().to_list()
    struc = pos_df.limit.to_list()
    ref = ros[1][["position", "ppg", "sd"]].groupby("position").quantile(0.75).reset_index()

    # if current sim week is a bye or player is injured, set proj ppg to 0
    team_ros = ros[0]
    team_ros["ppg"] = team_ros.ros_proj / team_ros.gms_rem
    team_ros["ppg"] = np.where((curr_sim_wk == team_ros.bye) | (curr_sim_wk < team_ros.return_wk),
                                        0,
                                        team_ros.ppg)
    team_ros = team_ros[team_ros.ppg >= 4]

    # assign weights for projections
    if current_week < 4:
        ros_wt = 0.65
        rand_wt = 0.35
    else:
        season_wt = 0.15
        ros_wt = 0.5
        rand_wt = 0.35

    # start simulation
    score_proj_df = pd.DataFrame()
    for tm in teams:
        pts = {'qb.proj':0, 'rb.proj':0,
               'wr.proj':0, 'te.proj':0,
               'dst.proj':0, 'dl.proj':0,
               'lb.proj':0, 'db.proj':0,
               'hc.proj':0, 'p.proj':0,
               'flex.proj':0, 'k.proj':0}

        # simulate using actual season data
        team_stats = df_current[(df_current['week'] == (current_week)) & (df_current['team'] == tm)].reset_index(drop=True)
        team_stats = team_stats[['team', 'ppg', 'sd']]

        # simulate using mean and sd, then add weight
        if current_week < 4:
            season_proj = 0
        else:
            season_proj = random.gauss(team_stats['ppg'], team_stats['sd'])[0] * season_wt

        # simulate through each position
        for pos, num in zip(posns, struc):
            if pos == "flex":
                # get 3rd highest rb/wr and 2nd highest te
                fpos = ["rb", "wr"]
                fnum = [2, 2]
                flex = pd.DataFrame()
                for a, b in zip(fpos, fnum):
                    df = team_ros.query('team == @ tm & position == @a').sort_values(by="ppg", ascending=False)
                    df = df.groupby("position").nth(b)
                    if df.empty:
                        # add "free agent" reference player if flex position is empty
                        ref_flex = ref.query('position == @a')
                        flex = flex.append(ref_flex)
                    else:
                        flex = flex.append(df)
                # select random flex player
                flex_play = flex.sample(n=1)
                flex_proj = flex_play.ppg
                flex_sd = flex_play.sd
                pts[pos+'.proj'] = np.random.normal(flex_proj, flex_sd)[0]
            else:
                week_proj = team_ros.query('team == @tm & position == @pos').sort_values(by=["ppg"], ascending=False)
                if len(week_proj) < num:
                    num_left = num - len(week_proj)
                    # take 75th percentile player if position is not filled
                    ref_proj = ref.loc[ref.query('position == @pos').index.repeat(num_left)]
                    week_proj = week_proj.append(ref_proj)
                    proj = week_proj.ppg
                    sd = week_proj.sd
                    pts[pos+'.proj'] = np.random.normal(proj, sd).sum()
                else:
                    proj = week_proj[:num].ppg
                    sd = team_ros.query('team == @tm & position == @pos').sort_values(by=["ppg"], ascending=False).sd[:num]
                    pts[pos+'.proj'] = np.random.normal(proj, sd).sum()

        # weight scores
        ros_proj = sum(pts.values()) * ros_wt

        # add general randomness, because fantasy football be like that
        rand_pts = random.uniform(90, 130) * rand_wt

        # combine projections
        total_proj_pts = season_proj + ros_proj + rand_pts
        row = {'team':tm,'score': total_proj_pts}
        rowdf = pd.DataFrame(data=[row])

        score_proj_df = pd.concat([score_proj_df, rowdf])

    score_proj_df = score_proj_df.reset_index(drop=True)

    return score_proj_df

def sim_matchups(d, df_current, current_week, teams, pos_df, regular_season_end, matchup_week, matchups, ros):
    '''
    Simulates rest of season matchups (head to head) using scores from simulate_scores
    and returns final standings
    '''

    df = matchups

    if matchup_week <= regular_season_end:
        # if current week is not in playoffs, simulate through matchups

        # get weekly matchups
        matchups = df[['week', 'team1', 'score1', 'team2', 'score2']]
        #matchups['team1'] = matchups.team1
        #matchups['team2'] = matchups.team2

        # create separate df for past weeks and append future weeks later
        matchups2 = matchups[matchups['week'] < matchup_week]

        # simulate scores for future weeks
        for curr_sim_wk in range(matchup_week, regular_season_end+1):
            score_sim = sim_scores(d, df_current, current_week, curr_sim_wk, teams, pos_df, ros)
            score_dict = dict(zip(score_sim.team, score_sim.score))
            matchups_new = matchups[matchups['week'] == curr_sim_wk]
            a = matchups_new.filter(like='team').columns
            matchups_new['score' + a.str.lstrip('team')] = matchups_new[a].stack().map(score_dict).unstack()
            matchups2 = matchups2.append(matchups_new)

        # calculate W/L
        matchups2['team1_result'] = np.where(matchups2['score1'] > matchups2['score2'], 1, 0)
        matchups2['team2_result'] = np.where(matchups2['score2'] > matchups2['score1'], 1, 0)

        # account for ties
        mask = (matchups2.score1 == matchups2.score2)
        matchups2.loc[mask, ['team1_result', 'team2_result']] = 0.5

        # convert dataframe to long format so each row is a team week, not matchup
        home = matchups2.iloc[:,[0,1,2,5]].rename(columns={'team1':'team', 'score1':'score', 'team1_result':'wins'})
        away = matchups2.iloc[:,[0,3,4,6]].rename(columns={'team2':'team', 'score2':'score', 'team2_result':'wins'})
        df_sim = pd.concat([home, away]).iloc[:,[1,2,3]]

        final_results = df_sim.groupby('team').agg({'wins':'sum', 'score':'sum'}).reset_index()

        return final_results

    else:
        # if current week is after regular season, return final standings
        data = []
        for tm in d['teams']:
            tmid = tmid = tm['abbrev'].lower()
            wins = tm['record']['overall']['wins']
            losses = tm['record']['overall']['losses']
            record = str(wins) + '-' + str(losses)
            score = tm['record']['overall']['pointsFor']
            pa = tm['record']['overall']['pointsAgainst']
            data.append([tmid, wins, losses, record, score, pa])

        standings = pd.DataFrame(data,
                                 columns = ["team", "wins", "losses", "record", "pf", "pa"])
        standings = standings.sort_values(by=['wins', 'pf'], ascending=False)
        standings = standings[['team', 'record', 'pf', 'pa']]
        standings = standings.rename(columns={"team":"Team", "record":"Record", "pf":"PF", "pa":"PA"})

        return standings

def sim_season(d, league_id, season, current_week, matchup_week, regular_season_end, df_current, teams, matchups, ros, n_sim=1):
    '''
    Simulates regular season and playoffs. Returns number projected standings, win/rank distributions, and playoff projections
    '''

    lineup_slots_df = get_params(d)["lineup_slots_df"]
    pos_df = lineup_slots_df[(lineup_slots_df.posID != 20) & (lineup_slots_df.posID != 21)]

    if matchup_week < regular_season_end:
        # if current week is not in playoffs, simulate season
        # initialize empty table to count wins and ranks
        table = (pd.DataFrame(index=teams,
                               columns=range(regular_season_end + 1))
                .fillna(0)
                .rename(columns={'index':'team'}))

        rank_table = (pd.DataFrame(index=teams,
                               columns=range(1, len(teams) + 1))
                .fillna(0)
                .rename(columns={'index':'team'}))

        # initialize dictionaries to count number of occurances for each team
        n_playoffs = {key: 0 for key in teams}
        n_finals = {key: 0 for key in teams}
        n_champ = {key: 0 for key in teams}
        n_second = {key: 0 for key in teams}
        n_third = {key: 0 for key in teams}

        df = pd.DataFrame()
        for sim in range(n_sim):
            # simulate regular season
            results = sim_matchups(d, df_current, current_week, teams, pos_df, regular_season_end, matchup_week, matchups, ros)
            results = results.set_index('team')

            # top 5 by record, 6th by most points, rest by record
            top5 = results.sort_values(['wins', 'score'], ascending=False).head(5)
            sixth = results[~results.isin(top5)].dropna().sort_values(['score'], ascending=False).head(1)
            playoffs = pd.concat([top5, sixth])
            bot4 = results[~results.isin(playoffs)].dropna().sort_values(['wins'], ascending=False)
            results = pd.concat([playoffs, bot4])
            results['rank'] =  np.arange(len(results)) + 1

            df = df.append(results)
            for index, row in results.iterrows():
                wins = row['wins']
                table[wins][index] += 1

                rank = row['rank']
                rank_table[rank][index] += 1

            # simulate plaoffs
            # get playoff teams
            p_teams = (results.reset_index().sort_values(["rank"])
                       .head(6)
                       .loc[:,"team"])

            # count playoff appearances
            for team in p_teams:
                n_playoffs[team] += 1

            # simulate 1 week of semifinals
            # top 2 teams get bye
            byes = p_teams.head(2).values
            quarter_teams = p_teams.iloc[2:]
            quarter_scores = (sim_scores(d, df_current, current_week, 14, quarter_teams, pos_df, ros)
                              .set_index('team')
                              .reset_index()
                              .rename(columns={0:'score'}))

            # get quarterfinals matchups and winners
            # lower seed needs to win by 10 or more points
            diff = 10
            quarter_1 = quarter_scores.iloc[[0,3],:]
            quarter_1 = np.where(quarter_1.score.iloc[1] - quarter_1.score.iloc[0] > diff, quarter_1.team.iloc[1], quarter_1.team.iloc[0]).astype(object)
            quarter_2 = quarter_scores.iloc[[1,2],:]
            quarter_2 = np.where(quarter_2.score.iloc[1] - quarter_2.score.iloc[0] > diff, quarter_2.team.iloc[1], quarter_2.team.iloc[0]).astype(object)

            # get semifinals matchups and winners
            semi_teams = np.stack((quarter_1.item(), quarter_2.item())).astype(object)
            semi_teams = np.concatenate((byes, semi_teams)).tolist()

            semi_scores = (sim_scores(d, df_current, current_week, 15, semi_teams, pos_df, ros)
                          .set_index('team')
                          .reset_index()
                          .rename(columns={0:'score'}))

            semi_1 = semi_scores.iloc[[0,3]]
            semi_2 = semi_scores.iloc[[1,2]]

            # get finals matchup
            final_1 = semi_1.sort_values(by='score', ascending=False).iloc[0,0]
            final_2 = semi_2.sort_values(by='score', ascending=False).iloc[0,0]
            finals = [final_1, final_2]

            # count finals appearances
            for team in finals:
                n_finals[team] +=1

            third_1 = semi_1.sort_values(by='score').iloc[0,0]
            third_2 = semi_2.sort_values(by='score').iloc[0,0]
            third = [third_1, third_2]
            finals_teams = finals + third

            # simulate 2 weeks of finals matchups
            final_scores = (sim_scores(d, df_current, current_week, 16, finals_teams, pos_df, ros)
                           .set_index('team')
                           .merge(sim_scores(d, df_current, current_week, 17, finals_teams, pos_df, ros)
                           .set_index('team'), on='team')
                           .sum(axis=1)
                           .reset_index()
                           .rename(columns={0:'score'}))

            champ = final_scores[final_scores.team.isin(finals)].sort_values(by='score', ascending=False).iloc[0,0]

            # count championships and runner up
            n_champ[champ] += 1

            runner = final_scores[final_scores.team.isin(finals)].sort_values(by='score').iloc[0,0]
            n_second[runner] += 1

            third_pl = final_scores[final_scores.team.isin(third)].sort_values(by='score', ascending=False).iloc[0,0]

            # count third place
            n_third[third_pl] += 1

        # calculate averages and standard deviations
        avg = df.groupby('team').mean().reset_index().rename(columns={'wins':'avg_w', 'score':'avg_pts', 'rank':"avg_rnk"})
        #round(df.groupby('team')[['wins', 'score', 'rank']].agg(iqr), 1)
        sd = df.groupby('team').std().reset_index().rename(columns={'wins':'sd_w', 'score':'sd_pts', 'rank':'sd_rnk'})
        df = pd.merge(avg, sd, how='left', on='team').set_index('team')
        df = df.sort_values(by=['avg_w', 'avg_pts'], ascending=False)

        # get playoff table
        # convert dictionary counts to dataframes and combine
        playoffs = pd.DataFrame(n_playoffs.items(), columns=['team', 'n_playoffs'])
        finals = pd.DataFrame(n_finals.items(), columns=['team', 'n_finals'])
        champs = pd.DataFrame(n_champ.items(), columns=['team', 'n_champ'])
        runners = pd.DataFrame(n_second.items(), columns=['team', 'n_second'])
        thirds = pd.DataFrame(n_third.items(), columns=['team', 'n_third'])

        dfs = [playoffs, finals, champs, runners, thirds]

        playoff_sim = reduce(lambda left, right: pd.merge(left, right, on='team'), dfs)
        playoff_sim = playoff_sim.set_index('team')

        return table, df, rank_table, playoff_sim

    else:
        standings = sim_matchups(d, df_current, current_week, teams, pos_df, regular_season_end, matchup_week, matchups, ros)

        return standings

def scenarios(d, pr):
    '''
    Analyzes how a team would have performed in 3 scenarios:
        1: record vs every team every week
        2: record if team had another team's schedule
        3: record vs each team (1 matchup per week, loop for every team)
    '''

    regular_season_end = get_params(d)["regular_season_end"]
    df = get_params(d)["matchup_df"]
    teams = get_params(d)["teams"]
    teams = [x[:-1] for x in teams]
    teams = [x[:4] for x in teams]

    # set up data
    df['week'] = df['week'].astype(str).astype(int)
    df['score1'] = df['score1'].astype(str).astype(float)
    df['score2'] = df['score2'].astype(str).astype(float)
    df = df[(df.week <= regular_season_end) & (df.score1 > 0)]
    df["team1"] = df.team1.str[:-1].str[:4]
    df["team2"] = df.team2.str[:-1].str[:4]

    ### 1. Calculate record vs every team
    wins_vs_league = pr[['team', 'week', 'score']]
    wins_vs_league['teams_beat'] = wins_vs_league.groupby('week')['score'].rank() - 1

    ### 2. Calculate record given another team's schedule
    # set up matrix
    switched_sched = (pd.DataFrame(index=teams,
                               columns=teams)
                               .fillna(0))

    # set up dataframe to return team scores
    hm = df[['week', 'team1', 'score1']].rename(columns={'team1':'team', 'score1':'score'})
    aw = df[['week', 'team2', 'score2']].rename(columns={'team2':'team', 'score2':'score'})
    return_score = hm.append(aw)

    # for each team in list (team 1), go through every other team (team 2)
    # and replace team 1 score with team 2
    # if opponent in a week is the same as team 2, keep original schedule
    for tm1 in teams:
        #tm1 = 'bron'
        # return team 1's schedule
        hm = df[df.team1 == tm1]
        aw = df[df.team2 == tm1]
        aw = aw.rename(columns={"team1":"team2",
                          "score1":"score2",
                          "team2":"team1",
                          "score2":"score1"})
        sched = hm.append(aw).sort_values("week")

        # get team 2's score for the current week to replace with team 1
        for tm2 in teams:
            for wk in sched.week:
                # return replacement team score
                sched_slice = sched[(sched.week==wk)]
                score = return_score[(return_score.week==wk) & (return_score.team==tm2)]

                # replace scores
                # if current week's schedule remains the same, return record; otherwise repace scores
                if sched_slice.team2.values == tm2:
                    result = np.where(sched_slice.score2 > sched_slice.score1, 1, 0)
                else:
                    sched_slice['team1'] = tm2
                    sched_slice['score1'] = score.score.values

                # get result of hypothetica matchup if team 1 does not equal team 2
                if sched_slice.team2.values != tm2:
                    result = np.where(sched_slice.score1 > sched_slice.score2, 1, 0)

                switched_sched.loc[tm1, tm2] += result

    ### 3: record vs each opponent
    wins_vs_opp = (pd.DataFrame(index=teams,
                               columns=teams)
                               .fillna(0))

    hm = df[['week', 'team1', 'score1']].rename(columns={'team1':'team', 'score1':'score'})
    aw = df[['week', 'team2', 'score2']].rename(columns={'team2':'team', 'score2':'score'})
    return_score = hm.append(aw)

    # for each team (team 1), find how many times they would have beaten team 2
    for tm1 in teams:
        #tm1 = 'bron'
        # return team 1 scores
        hm = df[df.team1 == tm1].iloc[:,0:3]
        aw = df[df.team2 == tm1].iloc[:,[0,3,4]]
        aw = aw.rename(columns={"team1":"team2",
                          "score1":"score2",
                          "team2":"team1",
                          "score2":"score1"})
        sched = hm.append(aw).sort_values("week")

        teams2 = [x for x in teams if x != tm1]
        for tm2 in teams2:
            #tm2 = "gupt"
            # return team 2 scores
            hm2 = df[df.team1 == tm2].iloc[:,0:3]
            hm2 = hm2.rename(columns={"team1":"team2",
                      "score1":"score2"})
            aw2 = df[df.team2 == tm2].iloc[:,[0,3,4]]
            sched2 = hm2.append(aw2).sort_values("week")

            # combine schedules and find result for team 1
            matchups = pd.merge(sched, sched2, how="inner", on="week")
            wins = sum(np.where(matchups.score1 > matchups.score2, 1, 0))

            wins_vs_opp.loc[tm1, tm2] += wins

    return wins_vs_league, switched_sched, wins_vs_opp

# %% Lineup Efficiency
# code thanks again to steven morse

def get_matchups(league_id, season, week, swid='', espn=''):
    '''
    Pull full JSON of matchup data from ESPN API for a particular week.
    '''

    url = 'https://fantasy.espn.com/apis/v3/games/ffl/seasons/' + str(season) + '/segments/0/leagues/' + str(league_id)

    r = requests.get(url + '?view=mMatchup&view=mMatchupScore',
                     params={'scoringPeriodId': week, 'matchupPeriodId': week},
                     cookies={"SWID": swid, "espn_s2": espn})

    d = r.json()

def opt_points(league_id, season, week, swid='', espn=''):
    '''
    Pull full JSON of matchup data from ESPN API for a particular week.
    '''

    url = 'https://fantasy.espn.com/apis/v3/games/ffl/seasons/' + str(season) + '/segments/0/leagues/' + str(league_id)
    r = requests.get(url + '?view=mMatchup&view=mMatchupScore',
                     params={'scoringPeriodId': week, 'matchupPeriodId': week},
                     cookies={"SWID": swid, "espn_s2": espn})

    d = r.json()

    '''
    Constructs week team slates with slotted position,
    position, and points (actual and ESPN projected),
    given full matchup info (`get_matchups`)
    '''
    slotcodes = {
        0: 'QB', 1: 'QB',
        2: 'RB', 3: 'Flex',
        4: 'WR', 5: 'Flex',
        6: 'TE',
        10: 'LB', 11: 'DL',
        14: 'DB',
        16: 'DST', 17: 'K',
        18: 'P', 19: 'HC',
        20: 'Bench', 21: 'IR',
        23: 'Flex'}

    slates = {}
    slots = []
    for team in d['teams']:
        slate = []
        for p in team['roster']['entries']:
            # get name
            name  = p['playerPoolEntry']['player']['fullName']

            # get actual lineup slot
            slotid = p['lineupSlotId']
            slot = slotcodes[slotid]
            slots.append(slotid)
            # get projected and actual scores
            act, proj = 0, 0
            for stat in p['playerPoolEntry']['player']['stats']:
                if stat['scoringPeriodId'] != week:
                    continue
                if stat['statSourceId'] == 0:
                    act = stat['appliedTotal']
                elif stat['statSourceId'] == 1:
                    proj = stat['appliedTotal']
                else:
                    print('Error')

            # get type of player
            pos = 'Unk'
            ess = p['playerPoolEntry']['player']['eligibleSlots']
            if 0 in ess: pos = 'QB'
            elif 2 in ess: pos = 'RB'
            elif 4 in ess: pos = 'WR'
            elif 6 in ess: pos = 'TE'
            elif 10 in ess: pos = 'LB'
            elif 11 in ess: pos = 'DL'
            elif 14 in ess: pos = 'DB'
            elif 16 in ess: pos = 'DST'
            elif 17 in ess: pos = 'K'
            elif 18 in ess: pos = 'P'
            elif 19 in ess: pos = 'HC'

            slate.append([name, slotid, slot, pos, act, proj])

        slate = pd.DataFrame(slate, columns=['Name', 'SlotID', 'Slot', 'Pos', 'Actual', 'Proj'])
        slates[team['id']] = slate

    slots = list(dict.fromkeys(slots))

    '''
    Given slates, compute total roster pts:
    actual, optimal, and using ESPN projections

    Parameters
    --------------
    slates : `dict` of `DataFrames`
        (from `get_slates`)
    posns : `list`
        roster positions, e.g. ['QB','RB', 'WR', 'TE']
    struc : `list`
        slots per position, e.g. [1,2,2,1]

    * This is not flexible enough to handle "weird" leagues
    like 6 Flex slots with constraints on # total RB/WR

    Returns
    --------------
    `dict` of `dict`s with actual, ESPN, optimal points
    '''

    get_posns = load_data(league_id, season, swid, espn)
    regular_season_end = get_params(get_posns)["regular_season_end"]
    lineup_slots_df = get_params(get_posns)["lineup_slots_df"]
    pos_df = lineup_slots_df[(lineup_slots_df.posID != 20) & (lineup_slots_df.posID != 21)]
    posns = pos_df.pos.to_list()
    struc = pos_df.limit.to_list()

    data = {}

    for tmid, slate in slates.items():
        slate = slates[tmid]
        # go through each team roster
        pts = {'opts':0, 'apts':0,
               'QB.opts':0, 'QB.apts':0,
               'RB.opts':0, 'RB.apts':0,
               'WR.opts':0, 'WR.apts':0,
               'TE.opts':0, 'TE.apts':0,
               'Flex.opts':0, 'Flex.apts':0,
               'DST.opts':0, 'DST.apts':0,
               'DL.opts':0, 'DL.apts':0,
               'LB.opts':0, 'LB.apts':0,
               'DB.opts':0, 'DB.apts':0,
               'HC.opts':0, 'HC.apts':0,
               'P.opts':0, 'P.apts':0,
               'K.opts':0, 'K.apts':0}

        # Total actual points - starters
        pts['apts'] = slate.query('Slot not in ["Bench", "IR"]').filter(['Actual']).sum().values[0]

        # get starters and remove 3rd RB/WR or 2nd TE
        starters = slate.query('Slot != "Bench"')
        flpos = ["RB", "WR", "TE"]
        flnum = [2, 2, 1]
        f_st = []
        for pos, num in zip(flpos, flnum):
            player = starters.query('Pos == @pos').sort_values(by="Actual", ascending=False).groupby("Pos").nth(num).Name.tolist()
            f_st.append(player)
        f_st = [e for e in f_st if e != []]
        f_st = [i for s in f_st for i in s]

        starters = starters[~starters.Name.isin(f_st)]
        starters = starters.Name.tolist()

        # Total actual points - by position
        for pos in posns:
            pts[pos+'.apts'] += slate.query('Slot == @pos').filter(['Actual']).sum().values[0]

        # get flex position IDs
        #flex_pos = [i for i in [3,5,23] if i in slots]
        flex_pl = pd.DataFrame()
        # Optimal points
        #actflex = -100  # actual pts scored by flex

        for pos, num in zip(posns, struc):
            # actual points, sorted by actual outcome
            #pos = "WR"
            #num = 2
            t = slate.query('Pos == @pos').sort_values(by='Actual', ascending=False).filter(['Actual']).values[:,0]

            # sum up points
            pts['opts'] += t[:num].sum()  # total optimal points
            pts[pos+'.opts'] = t[:num].sum()  # position optimal points

            # get best flex plays
            if 'Flex' in posns:
                fnum = posns.count('Flex')
                if pos in ['RB', 'WR', 'TE'] and len(t) > num:
                    flex = slate.query('Pos == @pos').sort_values(by='Actual', ascending=False)[fnum:]
                    flex_pl = flex_pl.append(flex)
        flex_pl = flex_pl[~flex_pl.Name.isin(starters)]
        flex_opt = flex_pl.sort_values(by='Actual', ascending=False).filter(['Actual']).values[:fnum].sum()
        pts['Flex.opts'] = flex_opt

        # Add flex points to total optimal and position optimal
        pts['opts'] += flex_opt

        data[tmid] = pts

    team_map = get_params(get_posns)["team_map"]
    df = pd.DataFrame.from_dict(data, orient='index')
    df['week'] = week
    df['team'] = df.index
    df = df.replace({'team':team_map})

    return df

def get_optimal(d, league_id, season, swid, espn):
    df_scores = pd.DataFrame()
    #regular_season_end = get_params(d)["regular_season_end"]+1
    matchup_week = get_params(d)["matchup_week"]
    for week in range(1, matchup_week):
        df = opt_points(league_id, season, week, swid, espn)
        df_scores = df_scores.append(df)

    return df_scores


from user import User
from collections import defaultdict

class Bank(User):
    def __init__(self, first_name, last_name, city, account):
        super().__init__(first_name, last_name, city, account)
        self.account = account
        self.balance = 0
        self.user_accounts = defaultdict(lambda: None)

    def account_balance(self):
        return f' <<< Your current balance is ${self.balance} >>>'

    def withdrawal(self, amount):
        if self.balance < amount:
            print(f'Insufficient funds!')
            print(self.account_balance())
            print('*******************************')
            return
        elif amount < 10:
            print('Minimum amount to withdraw is $10.')
            return
        self.balance -= amount
        print(f'{amount} was withdrawn from your account ending with ******{str(self.account)[-4:]}')
        print(self.account_balance())
        print('*******************************')

    def deposit(self, amount):
        print(f' - ${amount} was added to your account!')
        self.balance += amount
        print(self.account_balance())
        print('*******************************')

    def add_account(self):
        new_account = input('Please provide your account number: ')
        new_account_alias = input('Please provide a name for your account: ')
        if new_account_alias in self.user_accounts:
            print(f'Account {new_account} is already added.')
            return
        self.user_accounts[new_account_alias] = new_account


#!/usr/bin/env python3

import argparse, os, glob, math
import subprocess
import numpy as np

os.system('export GMX_MAXBACKUP=-1')

def parseArguments():
	parser = argparse.ArgumentParser()
	parser.add_argument("-gmx", "--gmx_path",  type=str,  default="gmx")

	args = parser.parse_args()
	return args

box_size_initial=35 #35		# in nm
box_size_step=2	# nm

droplet_thickness_initial=0.6 #0.6
droplet_shell_precision=2	# percentage as integer

crowder_total=13
biomolecular_fraction=0.30
ionic_strength=0.150	# M

make_droplets = False
make_box      = False
make_topology = True

path_mdp='mdp'
path_output='soup'

mdp_min='min.mdp'

# number of gro files for each crowder

crowders = {}
for c in range(1,crowder_total+1):
	crowders[c] = {}

crowders[1]['copies']  = 6
crowders[2]['copies']  = 7
crowders[3]['copies']  = 2
crowders[4]['copies']  = 1
crowders[5]['copies']  = 3
crowders[6]['copies']  = 1
crowders[7]['copies']  = 1
crowders[8]['copies']  = 1
crowders[9]['copies']  = 5
crowders[10]['copies'] = 1436
crowders[11]['copies'] = 144
crowders[12]['copies'] = 225
crowders[13]['copies'] = 255

def grp_list( gro ):
	# Creates a temporary index file to check what type of molecule is the crowder
#	print( 'q\n | gmx make_ndx -f crowder_n%s/npt.1.gro -o temporary.ndx >/dev/null 2>/dev/null ' %(crowder))
#	with open('q.txt', 'w') as outfile:
 #           outfile.write("q\n")
	os.system('echo "q""\\n" | %s make_ndx -f %s -o temporary.ndx >/dev/null 2>/dev/null' % (gmx_path, gro) )
	with open('temporary.ndx', 'r') as infile:
		lines = infile.readlines()
		grps = [x.split()[1] for x in lines if x[0]=='[']

	return grps
	os.remove('temporary.ndx')

def crowder_type( group_list ):
	if 'Protein' in group_list:
		return 'Protein', 'protein'
	elif 'RNA' in group_list:
		return 'RNA', 'nucleic acid'
	elif 'Other' in group_list:
		return 'Other', 'metabolite'
	else:
		return 'Unknown', 'unknown'

def crowder_mass( crowder_id ):
	with open('crowder_n%d/crowder_n%d.itp' % (crowder_id, crowder_id), 'r') as infile:
		mass = np.array([float(x.split()[-4]) for x in infile.readlines() if (('qtot' in x.split()) and (x.split()[0]!=';'))]).sum()
	return mass

def count_waters( gro_file ):
	waters = 0
	with open(gro_file, 'r') as infile:
		for line in infile.readlines():
			try:
				words = line.split()
				if (words[0][-3:]=='SOL') and (words[1][:2]=='OW'):
					waters += 1
			except:
				pass
	return waters

if __name__=='__main__':

	args  = parseArguments()
	gmx_path = args.gmx_path

	print('Gathering data about each crowder')

	for crowder_id in range(1, crowder_total+1):
		# Creates a temporary index file to check what type of molecule is the crowder
		grps = grp_list('crowder_n'+str(crowder_id)+'/npt.1.gro')
		crowders[crowder_id]['type'], crowders[crowder_id]['class'] = crowder_type(grps)
		crowders[crowder_id]['mass'] = crowder_mass(crowder_id)

		print('\tCrowder %d is a %s with mass = %.2f Da' % (crowder_id, crowders[crowder_id]['class'], crowders[crowder_id]['mass']) )

	biomolecular_mass=0
	for crowder_id in crowders:
		biomolecular_mass += crowders[crowder_id]['copies'] * crowders[crowder_id]['mass']

	print('')
	print('\tTotal biomolecular mass = %.2f Da' % (biomolecular_mass))
	print('\tCalculating the number of waters necessary to achieve the biomolecular fraction of %f %%' % (biomolecular_fraction*100))

	water_mass = (biomolecular_mass/biomolecular_fraction) - biomolecular_mass
	water_molecules = int(water_mass / 18)

	print('\t%d water molecules are necessary to reach the biomolecular fraction of %.1f %%' % (water_molecules, biomolecular_fraction*100))
	print('')
	print('')

	crowders_sorted = sorted( crowders.items() , key= lambda tup: tup[1]['mass'], reverse=True)
	crowders_order = [x[0] for x in crowders_sorted]

	if make_droplets:
		print('Making droplets for each crowder')
		print('\tFinding the optimum droplet shell thickness which comprises a total of %d water molecules for all crowders\n' % water_molecules)
		print('\tThe maximum difference that is allowed between the number of water molecules inside the droplets and the number of molecules we need is %d %%' % droplet_shell_precision)

		droplet_thickness_step=0.1	# nm
		droplet_thickness=droplet_thickness_initial		# nm
		droplet_thickness_prev=droplet_thickness

		while True:
			water_added_to_the_soup = 0

			for crowder_id in range(1,crowder_total+1):
				print('\t\tMaking droplet for crowder %d ... ' % crowder_id, end='')

				os.chdir('crowder_n%d' % crowder_id)

				os.system('echo "System" | %s trjconv -f npt.1.gro -s npt.1.tpr -pbc atom -ur compact -o eq_pbc.gro >/dev/null 2>/dev/null' % args.gmx_path)

				grps = grp_list('npt.1.gro')

				if 'MG' in grps:
					grp_mg = 0
					grp_count = 0
					for grp in grps:
						if (grp == 'MG') and (grp_mg == 0):
							grp_mg = grp_count
						grp_count += 1

					os.system('%s select -f eq_pbc.gro -on index_droplet.ndx -select "(group %s ) or (same residue as (group "SOL" and within %f of group %s) ) or (group %d and within %s of group %s )" -s npt.1.tpr >/dev/null 2>/dev/null' % (args.gmx_path, crowders[crowder_id]['type'], droplet_thickness, crowders[crowder_id]['type'], grp_mg, droplet_thickness, crowders[crowder_id]['type']))
				else:
					os.system('%s select -f eq_pbc.gro -on index_droplet.ndx -select "(group %s ) or (same residue as (group "SOL" and within %f of group %s) )" -s npt.1.tpr >/dev/null 2>/dev/null' % (args.gmx_path, crowders[crowder_id]['type'], droplet_thickness, crowders[crowder_id]['type']))

				os.system('%s trjconv -f eq_pbc.gro -s npt.1.tpr -o droplet.gro -n index_droplet.ndx >/dev/null 2>/dev/null' % args.gmx_path)

				water_added_by_one_droplet = count_waters('droplet.gro')

				water_added_by_this_crowder = water_added_by_one_droplet * crowders[crowder_id]['copies']

				water_added_to_the_soup += water_added_by_this_crowder

				os.chdir('..')
				print('ok')

			water_added_perc = float(water_added_to_the_soup/water_molecules)*100
			error_perc = np.absolute(water_added_perc - 100)

			current_biomolecular_fraction = biomolecular_mass / (biomolecular_mass + (water_added_to_the_soup*18.0))

			print('\t\t\tDroplet thickness = %f' % droplet_thickness)
			print('\t\t\tCurrent biomolecular fraction = %f' % current_biomolecular_fraction)
			print('\t\t\t%% of water added = %f' % water_added_perc)
			print('\t\t\t%% error = %f' % error_perc)

			if error_perc <= droplet_shell_precision:
				break
			else:
				if water_added_perc < 100 :
					droplet_thickness_prev = droplet_thickness
				else:
					droplet_thickness = droplet_thickness_prev
					droplet_thickness_step = droplet_thickness_step/10

				droplet_thickness = droplet_thickness + droplet_thickness_step

		print('')
		print('\tDroplet shell thickness converged to %.1f nm.' % droplet_thickness)
		print('\t%d water molecules will be added to the box.' % water_added_to_the_soup)
		print('\tThis is %.2f %% of the precise number required' % water_added_perc)
		print('\tThat\'s within the requested error margin of %.2f %%' % droplet_shell_precision)
		print('')
		print('')

	if make_box:
		print('Finding the smallest cubic box in which all components of the soup fit')

		print('\tSorting the crowders by mass')

		for crowder_id in crowders_order:
			print('\t\tCrowder %d, mass = %d' % (crowder_id, crowders[crowder_id]['mass']) )
		print('')

		try:
			os.mkdir(path_output)
		except:
			pass

		box_size = box_size_initial
		ok = False
		while True:
			with open('%s/box.gro' % (path_output), 'w') as outfile:
				outfile.write('Cytoplasm model\n')
				outfile.write('0\n')
				outfile.write('%.4f\t%.4f\t%.4f\n' % (box_size, box_size, box_size))
			print('\tSide lenght = %.4f nm' % box_size)

			crowder_count = 1
			for crowder_id in crowders_order:
				print('\tTrying to add %d copies of crowder number %d ... ' % (crowders[crowder_id]['copies'], crowder_id), end='')
				os.system('%s insert-molecules -f %s/box.gro -ci crowder_n%d/droplet.gro -o %s/box.gro -nmol %d -try 10 >out 2>err' % (gmx_path, path_output, crowder_id, path_output, crowders[crowder_id]['copies']))
				with open('err', 'r') as infile:
					for line in infile.readlines():
						try:
							if line.split()[0]=='Added':
								number_added = int(line.split()[1])
						except:
							pass
				os.remove("err")
				os.remove("out")
				print('added %d ...' % number_added, end='')

				if number_added == crowders[crowder_id]['copies']:
					print('ok')
					if crowder_count == crowder_total:
						ok = True
						break
					else:
						crowder_count += 1
				else:
					print('didn\'t fit')
					box_size = box_size + box_size_step
					break
			if ok:
				break

if make_topology:
	print('')
	print('Writing the .gro file for the soup ... ', end='')

	sections = ['title', 'atoms', 'biomol', 'sol', 'mg', 'box']
	gro = {}
	for grp in sections:
		gro[grp] = []

	with open('%s/box.gro' % path_output , 'r') as infile:
		lines = infile.readlines()

		gro['title'].append(lines[0])
		gro['atoms'].append(lines[1])
		gro['box'].append(lines[-1])

		del lines[-1]
		del lines[:2]

		for line in lines:
			words = line.split()
			if words[0][-3:] == 'SOL':
				gro['sol'].append(line)
			elif words[0][-2:] == 'MG' and words[1][:2]=='MG':
				gro['mg'].append(line)
			else:
				gro['biomol'].append(line)

		with open('%s/box_ordered.gro' % path_output, 'w') as outfile:
			for grp in sections:
				for line in gro[grp]:
					outfile.write(line)
	print('ok')

	print('Writing the topology file for the soup ... ', end='')
	num_sol = int(len(gro['sol'])/4)
	num_mg  = int(len(gro['mg']))
	with open('%s/topology.top' % path_output, 'w') as outfile:
		outfile.write('; Include forcefield parameters\n' )
		outfile.write('#include "../amber99sbws-dangK.ff/forcefield.itp"\n')
		outfile.write('#include "../gaff-types.itp"\n')
		outfile.write('\n')
		outfile.write('; Include chain topologies\n')
		for crowder_id in crowders_order:
			outfile.write('#include "crowder_n%d.itp"\n' % crowder_id)
			os.system('cp crowder_n%d/*.itp %s/' % (crowder_id, path_output) )
		outfile.write('\n')
		outfile.write('; Include water topology\n')
		outfile.write('#include "../amber99sbws-dangK.ff/tip4p2005s.itp"\n')
		outfile.write('\n')
		outfile.write('; Include topology for ions\n' )
		outfile.write('#include "../amber99sbws-dangK.ff/ions.itp"\n')
		outfile.write('\n')
		outfile.write('[ system ]\n')
		outfile.write('; Name\n')
		outfile.write('E.coli K12 cytoplasm model\n')
		outfile.write('\n')
		outfile.write('[ molecules ]\n' )
		outfile.write('; Compound        #mols\n' )
		for crowder_id in crowders_order:
			outfile.write('crowder_n%d\t%d\n' % (crowder_id, crowders[crowder_id]['copies']) )
		outfile.write('SOL\t%d\n' % num_sol )
		outfile.write('MG\t%d\n' % num_mg )
	print('ok')
	print('')
	print('')

	print('Neutralizing charges in the box with K+ or Cl- ... ', end='')
	os.chdir(path_output)
	os.system('%s grompp -f ../mdp/%s -p topology.top -c box_ordered.gro -o ion_neutralize.tpr -maxwarn 1 >/dev/null 2>/dev/null' % (args.gmx_path, mdp_min))
	os.system('echo SOL | %s genion -s ion_neutralize.tpr -p topology.top -o ion_neutralize.gro -pname K -nname CL -pq 1 -nq -1 -neutral >/dev/null 2>/dev/null' % args.gmx_path)
	os.remove('mdout.mdp')
	print('ok')

	print('Adding KCl to reach the ionic strength of %f mol/L ... ' % ionic_strength , end='' )
	num_kcl = int(ionic_strength * (num_sol/55.555))
	os.system('%s grompp -f ../mdp/%s -p topology.top -c ion_neutralize.gro -o ion_ionicstrength.tpr -maxwarn 1 >/dev/null 2>/dev/null' % (args.gmx_path, mdp_min))
	os.system('echo SOL | %s genion -s ion_ionicstrength.tpr -p topology.top -o ion_ionicstrength.gro -pname K -nname CL -pq 1 -nq -1 -np %d -nn %d >/dev/null 2>/dev/null' % (args.gmx_path, num_kcl, num_kcl))
	os.remove('mdout.mdp')
	print('ok')


import streamlit as st
import pandas as pd
import numpy as np
from sklearn.ensemble import RandomForestClassifier
def app():
    #app heading
    st.write("""
    # Target Viability Prediction App
    This app predicts the ***investment viability*** of the target type!
    """)
    #st.sidebar.header("IS YOUR TARGET HOT or NOT?")
    #creating sidebar for user input features
    #st.sidebar.header('User Input Parameters')

    def user_input_features():
        drug_desc = st.selectbox("Drug Descriptor"  "(0=Adoptive Cell Therapy, 1=Antiinfective Therapy, 2=Antiinflammatory Therapy, 3=Autologous therapy, 4=Chemotherapy, 5=DNA-based Targeted therapy, 6=Dendritic Cell Therapy, 7=Immunotherapy, 8=Multispecific Monoclonal Antibody, 9=Not Given, 10=Opiod/Non-Opiod Painkiller, 11=RNA-based Targeted therapy, 12=Radiopharmaceutical, 13=Recombinant, 14=Targeted Therapy)",["0","1","2","3","4","5","6","7","8","9","10","11","12","13","14"])
        target_type = st.selectbox("Target Type" "(0=CB-1, 1=CD3, 2=DNA, 3=DNA  Methyltransferase, 4=DNA Topoisomerase I, 5=DNA Topoisomerase II, 6=FCGR2B, 7=HSP, 8=IL11, 9=IL12R, 10=IL17RE, 11=IL4, 12=IL6, 13=Unknown, 14=kras, 15=magl, 16=neuropilin, 17=tgfb, 18=tigit, 19=tyk)",["0","1","2","3","4","5","6","7","8","9","10","11","12","13","14","15","16","17","18","19"])
        company_type = st.selectbox("Company Type" "(0=Institution, 1=Private, 2=Public)",["0","1","2"])
        status = st.selectbox("Status" "(0=Completed, 1=Discontinued, 2=Inactive, 3=Marketed, 4=Ongoing, 5=Phase 2, 6=Planned, 7=Pre-Clinical, 8=Unknown)",["0","1","2","3","4","5","6","7","8"])
        hds = st.selectbox( "Highest Development Stage" "(0=Discontinued, 1=Discovery, 2=IND/CTA filed, 3=Inactive, 4=Marketed, 5=Phase 1, 6=Phase 2, 7=Phase 3, 8=Phase 4, 9=Pre-Clinical)",["0","1","2","3","4","5","6","7","8","9"])
        therapy_area = st.selectbox("therapy area" "(0=CNS, 1=Cardiovascular, 2=Dermatology, 3=Fibrotic Diseases, 4=Immunology, 5=Infectious Disease, 6=Metabolic Disorders, 7=Musculoskeletal Disorders, 8=Oncology, 9=Oncology, 10=Respiratory, 11=Toxicology)",["0","1","2","3","4","5","6","7","8","9","10","11"])
        indication = st.selectbox( "Indication" "(0=Cancer, 1=Cancer, 2=Fibrosis, 3=Obesity, 4=Solid Tumor, 5=AutoImmune Disorders, 6=Cancer, 7=Digestive Disease, 8=Drug Addiction, 9=Fibrosis, 10=Immunology, 11=Lung Disease, 12=Neurodegenerative Diseases, 13=PTSD/Depression, 14=Pain, 15=Psoriasis, 16=Rheumatoid Arthritis, 17=Skin Infections, 18=Solid Tumor, 19=Solid Tumor/Cancer)",["0","1","2","3","4","5","6","7","8","9","10","11","12","13","14","15","16","17","18","19"])
        mol_type = st.selectbox("Molecular type" "(0=Oligonucleotide, 1=Polymer, 2=Protein, 3=Cell Therapy, 4=Gene Therapy, 5=Inhibitor, 6=Monoclonal Antibody, 7=Oncolytic Virus, 8=Small Molecule, 9=Unknown, 10=Vaccine)",["0","1","2","3","4","5","6","7","8","9","10"])
        trial_count = st.slider("Trial_count", 0, 100,1)
        public_news = st.slider("Count of public news", 1, 100,1)
        deals = st.slider("count of deals", 0, 50,1)
        deal_status = st.selectbox("Deal status" "(0=Announced, 1=Completed, 2=Completed/Announced, 3=Completed/Filing, 4=Completed/Pricing, 5=Terminated, 6=Unknown)",["0","1","2","3","4","5","6"])
        deal_amount = st.slider("Deal amount", 0, 160000,1)
        deal_type = st.selectbox("Deal Type" "(0=A, 1=CSA, 2=DO, 3=EO, 4=G, 5=LA, 6=ND, 7=P, 8=PE, 9=R, 10=VF)",["0","1","2","3","4","5","6","7","8","9","10"])
        data = {'drug_desc': drug_desc,
                'target_type': target_type ,
                'company_type': company_type ,
                'status': status ,
              'hds': hds,
              'therapy_area': therapy_area,
                'indication': indication,
                'mol_type': mol_type ,
                'trial_count': trial_count ,
                'public_news': public_news ,
              'deals': deals,
              'deal_status': deal_status,
                'deal_amount': deal_amount,
                'deal_type': deal_type
                }
        features = pd.DataFrame(data, index=[0])
        return features
    df = user_input_features()

    #st.subheader('User Input parameters')
    st.write(df)
    #reading csv file
    data=pd.read_csv("trial_data.csv")
    X = data.iloc[:,:-1].values
    Y = data.iloc[:,-1].values
    #random forest model
    rfc= RandomForestClassifier()
    rfc.fit(X, Y)
    #st.subheader('Wine quality labels and their corresponding index number')
    #st.write(pd.DataFrame({'wine quality': [3, 4, 5, 6, 7, 8 ]}))

    prediction = rfc.predict(df)
    #prediction_proba = rfc.predict_proba(df)
    st.subheader('Prediction')
    st.write(prediction)

    #st.subheader('Prediction Probability')
    #st.write(prediction_proba)


from easydict import EasyDict
import torch
import os

config = EasyDict()

# config.gpu = "0"
# os.environ['CUDA_VISIBLE_DEVICES'] = config.gpu

# dataloader jobs number
config.num_workers = 0

# batch_size
config.batch_size = 256

# training epoch number
config.max_epoch = 100

config.start_epoch = 0

# learning rate
config.lr = 1e-3
config.momentum = 0.9
config.weight_decay = 5e-6
config.optim = "Adam"

# using GPU
config.cuda = False
config.resume = True
config.display_freq = 10
config.save_freq = 10
config.print_freq = 10
config.random_seed = 0

config.save_dir = "./model"
config.exp_name = "sleep1"

config.pretrained_model = "./model/FC_best.pth"
# config.pretrained_model = "./model/sleep1/FC_1000.pth"

config.device = torch.device('cuda') if config.cuda else torch.device('cpu')

def print_config(config):
    print('==========Options============')
    for k, v in config.items():
        print('{}: {}'.format(k, v))
    print('=============End=============')

if __name__ == "__main__":
    import pdb; pdb.set_trace()


"""
We are extracting from VQA paired data
answers with type "Other" and their corresponding questions.
We will take a look at these, as well as their images, to make a new dataset.

We treat "validation" as "test" for now because we didn't obtain that data.
We can also split the "validation" into a "test" if we want.
"""

import json
from tqdm import tqdm
from collections import defaultdict

def load_annotations(root='./data/vqa/vqa2_raw/'):
    """Annotations come from a specific format of:
    {'question_type': first few words of the question, 'multiple_choice_answer': ground truth answer, 'answers': we can ignore this,
    'image_id': image associated, 'answer_type':  "yes/no", "number", and "other",
    'question_id': number}

    This file actually does not contain real questions

    Keyword Arguments:
        root {str} -- root path of the data folder (default: {'./data/vqa/vqa2_raw/'})

    Return:
        {question_id: {'question_type': 'none of the above',
            'multiple_choice_answer': 'down',
            'image_id': 262148,
            'answer_type': 'other',
            'question_id': 262148000}}
    """
    anno_train_path = "annotations/v2_mscoco_train2014_annotations.json"
    anno_val_path = "annotations/v2_mscoco_val2014_annotations.json"

    train_anno_data = {}
    val_anno_data = {}
    for item in json.load(open(root+anno_train_path))['annotations']:
        del item['answers']
        train_anno_data[item['question_id']] = item

    for item in json.load(open(root+anno_val_path))['annotations']:
        del item['answers']
        val_anno_data[item['question_id']] = item

    return train_anno_data, val_anno_data

def load_questions(root='./data/vqa/vqa2_raw/'):
    """This file contains questions but not answers (answers are from annotations)
    We are also only dealing with MSCOCO instead of the

    Keyword Arguments:
        root {str} -- root path (default: {'./data/vqa/vqa2_raw/'})

    Return:
        {question_id: {'image_id': 262148,
            'question': 'What is he on top of?',
            'question_id': 262148002}}
    """
    question_val_path = "questions/v2_OpenEnded_mscoco_val2014_questions.json"
    question_train_path = "questions/v2_OpenEnded_mscoco_train2014_questions.json"

    train_questions = {}
    val_questions = {}
    for item in json.load(open(root+question_train_path))['questions']:
        train_questions[item['question_id']] = item

    for item in json.load(open(root+question_val_path))['questions']:
        val_questions[item['question_id']] = item

    return train_questions, val_questions

def load_pairs(root='./data/vqa/vqa2_raw/'):
    """Load pairs of question_ids, where both point to the same question, but with different answer!

    Keyword Arguments:
        root {str} -- root path (default: {'./data/vqa/vqa2_raw/'})
    """
    comp_list_val_path = "comp_pair_list/v2_mscoco_val2014_complementary_pairs.json"
    comp_list_train_path = "comp_pair_list/v2_mscoco_train2014_complementary_pairs.json"

    comp_list_train = json.load(open(root+comp_list_train_path))
    comp_list_val = json.load(open(root+comp_list_val_path))

    return comp_list_train, comp_list_val

def construct_contrastive_dataset(train_tup, val_tup):
    """construct the dataset and store them

    Arguments:
        train_tup {[anno, question, comp_list]} -- A list of lists
        val_tup {[anno, question, comp_list]} -- same as above

    Return:
        [
          {'question': str, 'answer1': str, 'image_id_1': number, 'answer2': str, 'image_id_2': number,
           'question_type': str, 'answer_type': str}
        ]
    """
    # remeber to check question_type (assert)
    # check answer type (assert)

    train_data = []
    val_data = []
    train_stats = defaultdict(int)
    val_stats = defaultdict(int)
    for split in ['train', 'val']:
        anno, quests, comp_list = eval(split+'_tup')
        data = eval(split+'_data')
        for q1, q2 in tqdm(comp_list):
            q1_str = quests[q1]
            q2_str = quests[q2]
            assert q1_str['question'] == q2_str['question'], print(q1_str, q2_str)
            # retrieve two answers!
            ans1, ans2 = anno[q1], anno[q2] # ['multiple_choice_answer']
            # It's possible for two types to not be the same:
            # 'Which Zebra is lying down?'
            # answer1: 'middle', answer_type='other'
            # answer2: '0', answer_type='number'
            # QID: 84591002
            # assert ans1['answer_type'] == ans2['answer_type'], print(q1_str, ans1, ans2)
            assert ans1['image_id'] == q1_str['image_id']
            assert ans2['image_id'] == q2_str['image_id']
            if ans1['multiple_choice_answer'] == ans2['multiple_choice_answer']:
                continue

            if ans1['multiple_choice_answer'] == 'none' or ans2['multiple_choice_answer'] == 'none':
                continue

            if ans1['multiple_choice_answer'] == '0' or ans2['multiple_choice_answer'] == '0':
                continue

            data.append({
                'question': q1_str['question'],
                'answer1': ans1['multiple_choice_answer'],
                'image1': ans1['image_id'],
                'answer2': ans2['multiple_choice_answer'],
                'image2': ans2['image_id'],
                'answer_type1': ans1['answer_type'],
                'answer_type2': ans2['answer_type'],
                'question_type': ans1['question_type']
            })

            eval(split+"_stats")[ans1['answer_type']] += 1

    print("total number of train: {}".format(len(train_data)))
    print('total number of validation: {}'.format(len(val_data)))
    print(train_stats)
    return train_data, val_data

def extract_and_save_answer_type(data_list, answer_type='other'):
    processed_data = [[], []]
    for i, data in enumerate(data_list):
        for t in data:
            if t['answer_type1'] == t['answer_type2']:
                if t['answer_type1'] == answer_type:
                    processed_data[i].append(t)

    return processed_data

if __name__ == '__main__':
    STAGE = 'stage1'
    # Stage 1
    if STAGE == 'stage1':
        print('loading...')
        train_anno_data, val_anno_data = load_annotations()
        train_questions, val_questions = load_questions()
        comp_list_train, comp_list_val = load_pairs()
        print("merging...")
        train_data, val_data = construct_contrastive_dataset([train_anno_data, train_questions, comp_list_train],
                                                                [val_anno_data, val_questions, comp_list_val])
        print('saving...')
        json.dump(train_data, open('./data/vqa/pragmatic_train.json', 'w'))
        json.dump(val_data, open('./data/vqa/pragmatic_val.json', 'w'))
        STAGE = 'stage2'

    # Stage 2
    if STAGE == 'stage2':
        train_data = json.load(open('./data/vqa/pragmatic_train.json'))
        val_data = json.load(open('./data/vqa/pragmatic_val.json'))
        # 198951 out of 200394 in training have the same answer type for both answers
        train_data, val_data = extract_and_save_answer_type([train_data, val_data], 'other')
        json.dump(train_data, open('./data/vqa/pragmatic_other_train.json', 'w'))
        json.dump(val_data, open('./data/vqa/pragmatic_other_val.json', 'w'))


import scrapy
from crawler.items import CrawlerItem
import os
import time
import sys

from scrapy.spidermiddlewares.httperror import HttpError
from twisted.internet.error import DNSLookupError
from twisted.internet.error import TimeoutError

domain_file = "C:/Users/Lorwin/Desktop/crawler/crawler/new_domain_3.txt"
exception_file = "C:/Users/Lorwin/Desktop/crawler/crawler/exception.txt"
save_dir = "C:/Users/Lorwin/Desktop/crawler/crawler/saved_source/"

if not os.path.exists(save_dir):
    os.mkdir(save_dir)

class DmozSpider(scrapy.Spider):
    name = "kuan"
    allowed_domains = []

    def __init__(self, file=None,*args,**kwargs):
        super(DmozSpider, self).__init__(*args, **kwargs)
        self.crawl_file = file
        self.domain_list = list()

    def start_requests(self):
        self.load_domain_file()
        for index, url in enumerate(self.domain_list):
            url = "http://" + url
            try:
                # time.sleep(3)
                sys.stdout.write("{} / {} | url: {}\n".format(index, self.domain_list.__len__(), url))
                # print(url)
                # yield scrapy.Request(url=url,callback=self.parse_homepage,meta={'page':i},errback=self.errback_httpbin)
                yield scrapy.Request(url=url,callback=self.parse_homepage, meta={'url':url}, errback=self.errback_httpbin)
            except:
                print('!!!!!ERROR:{}\n'.format(url))
                with open(exception_file, mode="a") as f:
                    f.write("{}\n".format(url))

    def load_domain_file(self):
        with open(domain_file, mode="r") as f:
            for line in f:
                line = line.strip()
                self.domain_list.append(line)

    def parse_homepage(self,response):
        path_to_file = os.path.join(save_dir, response.meta['url'][7:]+".txt")
        # print(response.body)
        with open(path_to_file, mode="wb") as f:
            f.write(response.body)
        # for i in range(1,21):
        #     page = response.meta['page']
        #     # app_id = response.xpath('//*[@id="all-applist"]/li['+ str(i) +']/a/@href').extract_first()
        #     app_id = response.xpath('//*[@id="game_left"]/div/a['+ str(i) +']/@href').extract_first()
        #     domain = 'https://www.coolapk.com'
        #     link = domain + app_id
        #     info = 'Page:'+str(page)+' '+str(i)+'th '+app_id
        #     try:
        #         time.sleep(3)
        #         yield scrapy.Request(url=link,callback=self.parse_html,meta={'info':info},errback=self.errback_httpbin)
        #     except:
        #         print('Crawl',link,'error')

    # def parse_html(self,response):

    #     info = response.meta['info']

    #     print('-----',info,'-----')

    #     labels = response.xpath('/html/body/div/div[2]/div[2]/div[2]/div/div[5]/p[2]/a/@href').extract()

    #     name = response.xpath('/html/body/div/div[2]/div[2]/div[1]/div/div/div[1]/p[1]/text()').extract_first()

    #     down_link = response.xpath('/html/body/div/div[2]/div[2]/div[1]/div/div/div[1]/a/@href').extract_first()

    #     apk_name = response.xpath('/html/body/div/div[2]/div[2]/div[2]/div/div[6]/p[2]/text()').extract_first()

    #     item = CrawlerItem()

    #     item['label'] = labels
    #     item['name'] = name
    #     item['down_link'] = down_link
    #     item['apk_name'] = apk_name.replace('应用包名：','')

    #     yield item

    def errback_httpbin(self, failure):
        # log all errback failures,
        # in case you want to do something special for some errors,
        # you may need the failure's type
        self.logger.error(repr(failure))

        #if isinstance(failure.value, HttpError):
        if failure.check(HttpError):
            # you can get the response
            response = failure.value.response
            self.logger.error('HttpError on %s', response.url)

        #elif isinstance(failure.value, DNSLookupError):
        elif failure.check(DNSLookupError):
            # this is the original request
            request = failure.request
            self.logger.error('DNSLookupError on %s', request.url)

        #elif isinstance(failure.value, TimeoutError):
        elif failure.check(TimeoutError):
            request = failure.request
            self.logger.error('TimeoutError on %s', request.url)


from os import path, getcwd
import sys
THIS_DIR = getcwd()
MODBUS_API_DIR = path.abspath(path.join(THIS_DIR, 'modbus_api'))
DEVICE_API_DIR = path.abspath(path.join(THIS_DIR, 'device_api'))
sys.path.append(MODBUS_API_DIR)
sys.path.append(DEVICE_API_DIR)
from modbus_api.Modbus_Handler import Modbus_Handler_Serial_RTU,Modbus_Handler_TCPIP
from modbus_api.Modbus_Registers_Exceptions import ModbusRequestFailedException
from device_api.Device_Database import find_device
from device_api.Logger import Logger
from device_api.Argument_Parser import Web_Server_Access_Arg_Parser
from flask import Flask
import threading

arg_parser = Web_Server_Access_Arg_Parser()
arg_parser.parse(sys.argv[1:])
options = arg_parser.get_options()

mutex = threading.Lock()
api = Flask(__name__)
devDataRead = False
dl = None

def modbusReader():
    while(1):
        try:
            global dl
            global devDataRead
            global options
            if(options.ip_address):
                modbus = Modbus_Handler_TCPIP(slave_ids=[int(options.slave_address)],ip_address=options.ip_address,port=80)
            else:
                modbus = Modbus_Handler_Serial_RTU(slave_ids=[int(options.slave_address)],port=options.serial_port)
            dev = find_device(modbus,int(options.slave_address))
            dev.print_info()
            logger = Logger(dev.get_data_type())
            while(True):
                data = dev.read_data()
                if(devDataRead):
                    devDataRead = False
                mutex.acquire()
                logger.write_data(data)
                mutex.release()
        except KeyboardInterrupt:
            exit()
        except ModbusRequestFailedException:
            print('Connection Error, reconnecting...')

@api.route('/data', methods=['GET'])
def get_dev():
    global logger
    global devDataRead
    if(dl!=None):
        mutex.acquire()
        rslt = logger.get_data()
        devDataRead=True
        mutex.release()
        return rslt
    return {}

if __name__ == '__main__':
    dataReadThead = threading.Thread(target=modbusReader)
    dataReadThead.start()
    api.run(host=options.host,port=options.outputPort)


import math
import parse_data
import search

import tokenizer

talk_count = 4004
origin_data = parse_data.read_from_csv()
search_results = 20

# return a dictionary where key is term, value is frequency in the query text
def process_query(query, stemming=True):
    query_dict = {}
    words = tokenizer.tokenize(query, stemming)
    for w in words:
        count = 1
        if w in query_dict:
            count = query_dict[w] + 1
        query_dict[w] = count
    return query_dict

def get_documents(field, stemming):
    res = {}
    for doc_id in origin_data.keys():
        res[doc_id] = tokenizer.tokenize(origin_data[doc_id][field], stemming)
    return res

# return a dictionary where key is term, value is frequency in document and its id
def process_documents(query_dict, stemming, field):
    doc_dict = {}
    documents = get_documents(field, stemming)
    for key, value in documents.items():
        for term in value:
            if term in query_dict:
                if term in doc_dict:
                    count = 1
                    if key in doc_dict[term]:
                        count = doc_dict[term][key] + 1
                    doc_dict[term][key] = count
                else:
                    doc_dict[term] = {}
                    doc_dict[term][key] = 1
    return doc_dict

def get_df(query_dict, doc_dict):
    df_dict = {}
    for key in query_dict.keys():
        if key not in doc_dict:
            df_dict[key] = 0.0
        else:
            df_dict[key] = math.log10(talk_count / len(doc_dict[key]))
    return df_dict

def get_query_weight(query_dict, df_dict):
    query_weight_dict = {}
    for key in query_dict.keys():
        tf = 1 + math.log10(query_dict[key])
        query_weight_dict[key] = tf * df_dict[key]
    return query_weight_dict

def get_documents_weight(query_dict, doc_dict, df_dict):
    documents_weight_dict = {}
    for key in query_dict.keys():
        for doc_id in origin_data.keys():
            w = 0.0
            if key in doc_dict and doc_id in doc_dict[key]:
                tf = 1 + math.log10(doc_dict[key][doc_id])
                w = tf * df_dict[key]
            if doc_id not in documents_weight_dict:
                documents_weight_dict[doc_id] = {}
            documents_weight_dict[doc_id][key] = w
    return documents_weight_dict

def get_cosine_similarity(query_weight_dict, documents_weight_dict):
    sim_dict = {}
    q_length = 0.0
    for value in query_weight_dict.values():
        q_length += value ** 2
    q_length = math.sqrt(q_length)
    for doc_id in documents_weight_dict.keys():
        d_length = 0.0
        dot_product = 0.0
        for key, value in documents_weight_dict[doc_id].items():
            dot_product += value * query_weight_dict[key]
            d_length += value ** 2
        d_length = math.sqrt(d_length)
        if q_length == 0 or d_length == 0:
            sim_dict[doc_id] = 0.0
        else:
            sim_dict[doc_id] = dot_product / (q_length * d_length)
    return sim_dict

def rank_one(query, field):
    if field == "speaker":
        query_dict = process_query(query, False)
        doc_dict = process_documents(query_dict, False, field)
    else:
        query_dict = process_query(query)
        doc_dict = process_documents(query_dict, True, field)
    df_dict = get_df(query_dict, doc_dict)
    query_weight_dict = get_query_weight(query_dict, df_dict)
    documents_weight_dict = get_documents_weight(query_dict, doc_dict, df_dict)
    similarity = get_cosine_similarity(query_weight_dict, documents_weight_dict)
    candidates = {}
    for doc_id, sim in similarity.items():
        if sim > 0:
            candidates[doc_id] = sim
    return divide_relevant_levels(candidates)

def es_rank_one(query, field):
    candidates = search.general_search_query(query, field)
    return divide_relevant_levels(candidates)

def divide_relevant_levels(candidates):
    if not candidates:
        return [],[],[],[]
    max_sim = max(candidates.values())
    gap = max_sim / 4
    level1 = {}
    level2 = {}
    level3 = {}
    level4 = {}
    for tid, sim in candidates.items():
        doc_id = int(tid)
        views = origin_data[doc_id]['views']
        if sim >= 3 * gap:
            level1[doc_id] = views
        elif sim >= 2 * gap:
            level2[doc_id] = views
        elif sim >= gap:
            level3[doc_id] = views
        else:
            level4[doc_id] = views
    rank1 = sorted(level1, key=lambda k: level1[k], reverse=True)
    rank2 = sorted(level2, key=lambda k: level2[k], reverse=True)
    rank3 = sorted(level3, key=lambda k: level3[k], reverse=True)
    rank4 = sorted(level4, key=lambda k: level4[k], reverse=True)
    return rank1, rank2, rank3, rank4

def rank_all(query, method):
    if method == 'me':
        rank_title_tmp = rank_one(query, 'title')
        rank_speaker_tmp = rank_one(query, 'speaker')
        rank_description_tmp = rank_one(query, 'description')
        rank_transcript_tmp = rank_one(query, 'transcript')
    else:
        rank_title_tmp = es_rank_one(query, 'title')
        rank_speaker_tmp = es_rank_one(query, 'speaker')
        rank_description_tmp = es_rank_one(query, 'description')
        rank_transcript_tmp = es_rank_one(query, 'transcript')
    rank_title = [*rank_title_tmp[0], *rank_title_tmp[1], *rank_title_tmp[2]]
    rank_speaker = [*rank_speaker_tmp[0], *rank_speaker_tmp[1], *rank_speaker_tmp[2]]
    rank_description = [*rank_description_tmp[0], *rank_description_tmp[1]]
    rank_transcript = [*rank_transcript_tmp[0]]
    total_rank = rank_title.copy()
    tmp = total_rank.copy()
    for i in rank_speaker:
        if i not in tmp:
            total_rank.append(i)
    tmp = total_rank.copy()
    for i in rank_description:
        if i not in tmp:
            total_rank.append(i)
    tmp = total_rank.copy()
    for i in rank_transcript:
        if i not in tmp:
            total_rank.append(i)
    return total_rank

if __name__ == "__main__":
    # query = "New York Times"
    print(rank_one('best stats', 'title'))
    # print(origin_data[381]['description'])
    # print(origin_data[746]['title'])


import scrapy

rating_str_to_int = lambda x: {'one':1, 'two':2, 'three':3,'four':4,'five':5}.get(x.split(' ')[-1].lower())

class mySpider(scrapy.Spider):
    name = 'myBookStore'

    start_urls = ['http://books.toscrape.com']

    # parse function
    def parse(self, response):
        BASIC_URL = 'http://books.toscrape.com/'
        # parse the page that is returned by the url in
        # the start_urls

        books = response.xpath('//article[contains(@class,"product_pod")]')
        for indx, book in enumerate(books):
            book_name = book.xpath("./h3/a/@title").extract()[0]
            rating = rating_str_to_int(book.xpath("./p/@class").extract()[0])
            price = book.xpath("./div/p[@class='price_color']/text()").extract()[0]

            yield {
                'name':book_name,
                'price': price,
                'ratings': rating
            }

        # continue the parsing on the following pages
        abs_next_url = response.xpath('//ul[@class="pager"]/li[contains(@class,"next")]/a/@href').extract()[0]
        next_url = BASIC_URL + 'catalogue/{}'.format(abs_next_url) if 'catalogue' not in abs_next_url else \
                    BASIC_URL+ abs_next_url
        # recurse
        yield scrapy.Request(next_url,callback=self.parse)


import usb.core as core
import usb.util as util
import usb.control as control
import sys
import array
import os

currentdir = os.path.dirname(os.path.realpath(__file__))
parentdir = os.path.dirname(currentdir)
sys.path.append(parentdir)

import core.usb_util as usb_util

# find device
dev = core.find(idVendor=usb_util.VENDOR_ID, idProduct=usb_util.PRODUCT_ID)

if dev.is_kernel_driver_active(0):
	try:
		dev.detach_kernel_driver(0)
		print("Detached!")
	except core.USBError as e:
		sys.exit("Could not detach kernel driver: %s" % str(e))

# get config
cfg = dev.get_active_configuration()
# interface = cfg[(usb_util.CONFIGURATION_ID, usb_util.SETTING_ID)]
# out_ep = interface[usb_util.OUT_ENDPOINT_ID]
# in_ep = interface[usb_util.IN_ENDPOINT_ID]

print(cfg)

# read/write tests
sys.exit()

testStr = "Test2"
out_ep.write(testStr)
t = in_ep.read(len(testStr) * 8)

print(t)

t = usb_util.byteArrToString(t)

print(t)

print(in_ep.read(10000))

# print(endpoint)
# t = endpoint.read(100)
# print(t)
# endpoint.write("1")

# rc = dev.ctrl_transfer()

# ACM_CTRL_DTR = 0x01
# ACM_CTRL_RTS = 0x02

# bmRequestType = util.build_request_type(util.CTRL_IN, util.CTRL_TYPE_STANDARD, util.CTRL_RECIPIENT_ENDPOINT)
# c = dev.ctrl_transfer(bmRequestType, util.GET_STATUS, wIndex=0x81)
# print(c)

# rc = dev.ctrl_transfer(0x21, 0x22, )

# lid = util.get_langids(dev)[0]
# s = util.get_string(dev, 5, lid)

# t = endpoint.write("gg")
# print(t)

# encoding = array.array('B', [0x80, 0x25, 0x00, 0x00, 0x00, 0x00, 0x08])

# Set line state
# rc = dev.ctrl_transfer(0x21, 0x22, 0x01 | 0x02, 0, None)
# print(rc)

# if rc < 0:
# pass

# Set line encoding: 9600 (baudrate?)
# rc = dev.ctrl_transfer(0x21, 0x20, 0, 0, encoding, 0)
# print(rc)

# print(t)

# endpoint.write('00000000000000000000000')
# t = endpoint.read(1000)
# t = dev.read(0x85, 100)
# dev.write()
# print(t)
# returns: array('B', [161, 32, 0, 0, 0, 0, 2, 0, 0, 0])

# t = dev.write(0x2, "TestTest")
# print(t)