from basketball_reference_scraper.teams import get_roster, get_team_stats, get_opp_stats, get_roster_stats, get_team_misc
from basketball_reference_scraper.seasons import get_schedule
from basketball_reference_scraper.box_scores import get_box_scores
from basketball_reference_scraper.injury_report import get_injury_report
from utils import *
import sys
import pandas as pd
import numpy as np
import datetime
pd.options.mode.chained_assignment = None

# TODO:
#   - Get injury report
#   - Check if away team has anyone on the report
#   - Check if home team has anyone on the report
#   - If so, get the player and return his win shares
#   - Add up total missing win shares and create a column displaying that
#   - Standardize it

def convert_team_names(entire_schedule):
    team_abbreviations = {'Atlanta Hawks' : 'ATL',
                        'Brooklyn Nets' : 'BRK',
                        'Boston Celtics' : 'BOS',
                        'Charlotte Hornets' : 'CHO',
                        'Chicago Bulls' : 'CHI',
                        'Cleveland Cavaliers' : 'CLE',
                        'Dallas Mavericks' : 'DAL',
                        'Denver Nuggets' : 'DEN',
                        'Detroit Pistons' : 'DET',
                        'Golden State Warriors' : 'GSW',
                        'Houston Rockets' : 'HOU',
                        'Indiana Pacers' : 'IND',
                        'Los Angeles Clippers' : 'LAC',
                        'Los Angeles Lakers' : 'LAL',
                        'Memphis Grizzlies' : 'MEM',
                        'Miami Heat' : 'MIA',
                        'Milwaukee Bucks' : 'MIL',
                        'Minnesota Timberwolves' : 'MIN',
                        'New Orleans Pelicans' : 'NOP',
                        'New York Knicks' : 'NYK',
                        'Oklahoma City Thunder' : 'OKC',
                        'Orlando Magic' : 'ORL',
                        'Philadelphia 76ers' : 'PHI',
                        'Phoenix Suns' : 'PHO',
                        'Portland Trail Blazers' : 'POR',
                        'Sacramento Kings' : 'SAC',
                        'San Antonio Spurs' : 'SAS',
                        'Toronto Raptors' : 'TOR',
                        'Utah Jazz' : 'UTA',
                        'Washington Wizards' : 'WAS'}

    team_abbreviations_keys = list(team_abbreviations.keys())

    row = 0
    for home_team in entire_schedule['HOME']:
        for i in range(0, 30):
            if team_abbreviations_keys[i] in home_team:
                entire_schedule.at[row, 'HOME'] = team_abbreviations[team_abbreviations_keys[i]]
        row += 1
    row = 0
    for away_team in entire_schedule['VISITOR']:
        for i in range(0, 30):
            if team_abbreviations_keys[i] in away_team:
                entire_schedule.at[row, 'VISITOR'] = team_abbreviations[team_abbreviations_keys[i]]
        row += 1

    a = datetime.datetime(2020, 7, 30)
    b = datetime.datetime(2020, 7, 31)
    c = datetime.datetime(2020, 8, 1)
    d = datetime.datetime(2020, 8, 2)
    e = datetime.datetime(2020, 8, 3)
    f = datetime.datetime(2020, 8, 4)
    g = datetime.datetime(2020, 8, 5)
    h = datetime.datetime(2020, 8, 6)
    i = datetime.datetime(2020, 8, 7)
    j = datetime.datetime(2020, 8, 8)
    k = datetime.datetime(2020, 8, 9)
    l = datetime.datetime(2020, 8, 10)
    m = datetime.datetime(2020, 8, 11)
    n = datetime.datetime(2020, 8, 12)
    o = datetime.datetime(2020, 8, 13)
    p = datetime.datetime(2020, 8, 14)

    date = [a,a,b,b,b,b,b,b,c,c,c,c,c,d,d,d,d,d,d,e,e,e,e,e,e,f,f,f,f,f,f,g,g,g,g,g,g,h,h,h,h,h,h,i,i,i,i,i,i,j,j,j,j,j,k,k,k,k,k,k,k,l,l,l,l,l,m,m,m,m,m,m,m,n,n,n,n,o,o,o,o,o,o,o,p,p,p,p]
    #date = ['7/30/2020', '7/30/2020', '7/31/2020', '7/31/2020', '7/31/2020', '7/31/2020', '7/31/2020', '7/31/2020', '8/1/2020', '8/1/2020', '8/1/2020', '8/1/2020', '8/1/2020', '8/2/2020', '8/2/2020', '8/2/2020', '8/2/2020', '8/2/2020', '8/2/2020', '8/3/2020', '8/3/2020', '8/3/2020', '8/3/2020', '8/3/2020', '8/3/2020', '8/4/2020', '8/4/2020', '8/4/2020', '8/4/2020', '8/4/2020', '8/4/2020', '8/5/2020', '8/5/2020', '8/5/2020', '8/5/2020', '8/5/2020', '8/5/2020', '8/6/2020', '8/6/2020', '8/6/2020', '8/6/2020', '8/6/2020', '8/6/2020', '8/7/2020', '8/7/2020', '8/7/2020', '8/7/2020', '8/7/2020', '8/7/2020', '8/8/2020', '8/8/2020', '8/8/2020', '8/8/2020', '8/8/2020',
    #                            '8/9/2020', '8/9/2020', '8/9/2020', '8/9/2020', '8/9/2020', '8/9/2020', '8/9/2020', '8/10/2020', '8/10/2020', '8/10/2020', '8/10/2020', '8/10/2020', '8/11/2020', '8/11/2020', '8/11/2020', '8/11/2020', '8/11/2020', '8/11/2020', '8/11/2020', '8/12/2020', '8/12/2020', '8/12/2020', '8/12/2020', '8/13/2020', '8/13/2020', '8/13/2020', '8/13/2020', '8/13/2020', '8/13/2020', '8/13/2020', '8/14/2020', '8/14/2020', '8/14/2020', '8/14/2020']

    empty = [" "] * len(date)

    df1 = pd.DataFrame({'VISITOR' : ['UTA', 'LAC', 'ORL', 'MEM', 'PHO', 'BOS', 'SAC', 'HOU', 'MIA', 'UTA', 'NOP', 'PHI', 'LAL', 'WAS', 'POR', 'SAS', 'SAC', 'MIL', 'DAL', 'TOR', 'DEN', 'IND', 'MEM', 'SAS', 'LAL', 'BRK', 'DAL', 'PHO', 'ORL', 'BOS', 'HOU', 'MEM', 'PHI', 'DEN', 'OKC', 'TOR', 'BRK', 'NOP', 'MIA', 'IND', 'LAC', 'POR', 'LAL', 'UTA', 'OKC', 'SAC', 'ORL', 'WAS', 'BOS', 'LAC', 'UTA', 'LAL', 'PHO', 'MIL', 'WAS', 'MEM', 'SAS', 'ORL', 'PHI', 'HOU', 'BRK', 'OKC', 'DAL', 'TOR', 'IND', 'DEN', 'BRK', 'HOU', 'PHO', 'POR', 'BOS', 'NOP', 'MIL', 'IND', 'TOR', 'MIA', 'LAC', 'WAS', 'POR', 'SAC', 'MIL', 'NOP', 'DAL', 'SAS', 'PHI', 'MIA', 'OKC', 'DEN'],
                       'VISITOR_PTS': empty,
                       'HOME' : ['NOP', 'LAL', 'BRK', 'POR', 'WAS', 'MIL', 'SAS', 'DAL', 'DEN', 'OKC', 'LAC', 'IND', 'TOR', 'BRK', 'BOS', 'MEM', 'ORL', 'HOU', 'PHO', 'MIA', 'OKC', 'WAS', 'NOP', 'PHI', 'UTA', 'MIL', 'SAC', 'LAC', 'IND', 'MIA', 'POR', 'UTA', 'WAS', 'SAS', 'LAL', 'ORL', 'BOS', 'SAC', 'MIL', 'PHO', 'DAL', 'DEN', 'HOU', 'SAS', 'MEM', 'BRK', 'PHI', 'NOP', 'TOR', 'POR', 'DEN', 'IND', 'MIA', 'DAL', 'OKC', 'TOR', 'NOP', 'BOS', 'POR', 'SAC', 'LAC', 'PHO', 'UTA', 'MIL', 'MIA', 'LAL', 'ORL', 'SAS', 'PHI', 'DAL', 'MEM', 'SAC', 'WAS', 'HOU', 'PHI', 'OKC', 'DEN', 'BOS', 'BRK', 'LAL', 'MEM', 'ORL', 'PHO', 'UTA', 'HOU', 'IND', 'LAC', 'TOR'],
                       'HOME_PTS': empty,
                       'DATE' : date})

    entire_schedule = pd.concat([entire_schedule, df1], sort=False)

    return entire_schedule

def add_winner_column(entire_schedule):
    winner = []
    for index, row in entire_schedule.iterrows():
        if pd.isna(row['HOME_PTS']) or pd.isna(row['VISITOR_PTS']):
            winner.insert(index, float('Nan'))
        elif row['HOME_PTS'] > row['VISITOR_PTS']:
            winner.insert(index, 1)
        else:
            winner.insert(index, 0)
    entire_schedule["WINNER"] = winner
    return entire_schedule

def add_conference_game(entire_schedule):
    west_teams = [
        'DAL','DEN','GSW','HOU','LAC','LAL','MEM','MIN',
        'NOP','OKC','PHO','POR','SAC','SAS','UTA']
    east_teams = [
        'ATL','BRK','BOS','CHO','CHI','CLE','DET','IND',
        'MIA','MIL','NYK','ORL','PHI','TOR','WAS']
    conference_game = []
    for index, row in entire_schedule.iterrows():
        if (row['HOME'] in west_teams and row['VISITOR'] in west_teams) or (row['HOME'] in east_teams and row['VISITOR'] in east_teams):
            conference_game.insert(index, 1)
        else:
            conference_game.insert(index, 0)
    entire_schedule["CON_GAME"] = conference_game
    return entire_schedule

def add_team_stats(entire_schedule):
    team_misc_2020 = getTeamMisc(2020)
    team_misc_2019 = getTeamMisc(2019)
    last_year_win_percentage_home = []
    last_year_win_percentage_visitor = []
    count = 1
    for index, row in entire_schedule.iterrows():
        home = team_misc_2019.loc[team_misc_2019['TEAM'] == row['HOME'], 'W'].values[0]
        visitor = team_misc_2019.loc[team_misc_2019['TEAM'] == row['VISITOR'], 'W'].values[0]

        last_year_win_percentage_home.insert(index, home/82)
        last_year_win_percentage_visitor.insert(index, visitor/82)

        home_team_season_stats = team_misc_2020[team_misc_2020['TEAM'] == row['HOME']].add_prefix("HOME_")
        visitor_team_season_stats = team_misc_2020[team_misc_2020['TEAM'] == row['VISITOR']].add_prefix("VISITOR_")

        home_team_season_stats = home_team_season_stats[['HOME_NRtg', 'HOME_DRtg', 'HOME_ORtg', 'HOME_DRB%', 'HOME_SRS', 'HOME_MOV',
            'HOME_PACE']]
        visitor_team_season_stats = visitor_team_season_stats[['VISITOR_NRtg', 'VISITOR_DRtg', 'VISITOR_ORtg', 'VISITOR_DRB%', 'VISITOR_SRS', 'VISITOR_MOV',
            'VISITOR_PACE']]

        if count == 1:
            home_team_stats = home_team_season_stats
            visitor_team_stats = visitor_team_season_stats
        else:
            home_team_stats = pd.concat([home_team_stats, home_team_season_stats])
            visitor_team_stats = pd.concat([visitor_team_stats, visitor_team_season_stats])
        count += 1

    entire_schedule = entire_schedule.reset_index(drop=True)
    home_team_stats = home_team_stats.reset_index(drop=True)
    visitor_team_stats = visitor_team_stats.reset_index(drop=True)
    entire_schedule = pd.concat([entire_schedule, home_team_stats, visitor_team_stats], axis=1, sort=True)
    entire_schedule["HOME_LAST_SEASON_W%"] = last_year_win_percentage_home
    entire_schedule["VISITOR_LAST_SEASON_W%"] = last_year_win_percentage_visitor
    entire_schedule["HOME_GAME"] = [1] * len(entire_schedule)
    entire_schedule["AWAY GAME"] = [0] * len(entire_schedule)
    return entire_schedule

def add_win_percentage(entire_schedule):
    teams = [
        'ATL','BRK','BOS','CHO','CHI','CLE','DAL','DEN',
        'DET','GSW','HOU','IND','LAC','LAL','MEM','MIA',
        'MIL','MIN','NOP','NYK','OKC','ORL','PHI','PHO',
        'POR','SAC','SAS','TOR','UTA','WAS']
    init = [0] * 30
    data = {'wins' : init, 'losses' : init}

    injury_report = get_injury_report()
    win_loss = pd.DataFrame(data, index=teams)
    home_win_percentage = []
    visitor_win_percentage = []
    for index, row in entire_schedule.iterrows():

        if (win_loss.loc[row['HOME'], 'wins'] + win_loss.loc[row['HOME'], 'losses']) == 0:
            if win_loss.loc[row['HOME'], 'wins'] > 0:
                home_win_per = 1
            else:
                home_win_per = 0
        else:
            home_wins = win_loss.loc[row['HOME'], 'wins']
            if get_first_no_result_date(entire_schedule) == row['DATE']:
                injury_account = get_missing_win_shares(injury_report, row['HOME'])
                home_wins += injury_account
            home_win_per = home_wins / (win_loss.loc[row['HOME'], 'wins'] + win_loss.loc[row['HOME'], 'losses'])

        if (win_loss.loc[row['VISITOR'], 'wins'] + win_loss.loc[row['VISITOR'], 'losses']) == 0:
            if win_loss.loc[row['VISITOR'], 'wins'] > 0:
                visitor_win_per = 1
            else:
                visitor_win_per = 0
        else:
            visitor_wins = win_loss.loc[row['VISITOR'], 'wins']
            if get_first_no_result_date(entire_schedule) == row['DATE']:
                injury_account = get_missing_win_shares(injury_report, row['VISITOR'])
                visitor_wins += injury_account
            visitor_win_per = visitor_wins / (win_loss.loc[row['VISITOR'], 'wins'] + win_loss.loc[row['VISITOR'], 'losses'])

        home_win_percentage.insert(index, home_win_per)
        visitor_win_percentage.insert(index, visitor_win_per)

        if row['WINNER'] == 1:
            win_loss.at[row['HOME'],'wins'] = win_loss.loc[row['HOME'],'wins'] + 1
            win_loss.at[row['VISITOR'],'losses'] = win_loss.loc[row['VISITOR'],'losses'] + 1
        elif row['WINNER'] == 0:
            win_loss.at[row['VISITOR'],'wins'] = win_loss.loc[row['VISITOR'],'wins'] + 1
            win_loss.at[row['HOME'],'losses'] = win_loss.loc[row['HOME'],'losses'] + 1

    entire_schedule["HOME_W%"] = home_win_percentage
    entire_schedule["VISITOR_W%"] = visitor_win_percentage
    return entire_schedule

def add_home_away_splits(entire_schedule):
    teams = [
        'ATL','BRK','BOS','CHO','CHI','CLE','DAL','DEN',
        'DET','GSW','HOU','IND','LAC','LAL','MEM','MIA',
        'MIL','MIN','NOP','NYK','OKC','ORL','PHI','PHO',
        'POR','SAC','SAS','TOR','UTA','WAS']
    init = [0] * 30
    data = {'home_wins' : init, 'home_losses' : init, 'road_wins': init, 'road_losses' : init}
    home_road_split = pd.DataFrame(data, index=teams)
    home_win_at_home = []
    visitor_win_on_road = []
    for index, row in entire_schedule.iterrows():
        if (home_road_split.loc[row['HOME'], 'home_wins'] + home_road_split.loc[row['HOME'], 'home_losses']) == 0:
            if home_road_split.loc[row['HOME'], 'home_wins'] > 0:
                home_win_per = 1
            else:
                home_win_per = 0
        else:
            home_wins = home_road_split.loc[row['HOME'], 'home_wins']
            home_win_per = home_wins / (home_road_split.loc[row['HOME'], 'home_wins'] + home_road_split.loc[row['HOME'], 'home_losses'])

        if (home_road_split.loc[row['VISITOR'], 'road_wins'] + home_road_split.loc[row['VISITOR'], 'road_losses']) == 0:
            if home_road_split.loc[row['VISITOR'], 'road_wins'] > 0:
                visitor_win_per = 1
            else:
                visitor_win_per = 0
        else:
            visitor_wins = home_road_split.loc[row['VISITOR'], 'road_wins']
            visitor_win_per = visitor_wins / (home_road_split.loc[row['VISITOR'], 'road_wins'] + home_road_split.loc[row['VISITOR'], 'road_losses'])

        home_win_at_home.insert(index, home_win_per)
        visitor_win_on_road.insert(index, visitor_win_per)

        if row['WINNER'] == 1:
            home_road_split.at[row['HOME'],'home_wins'] = home_road_split.loc[row['HOME'],'home_wins'] + 1
            home_road_split.at[row['VISITOR'],'road_losses'] = home_road_split.loc[row['VISITOR'],'road_losses'] + 1
        elif row['WINNER'] == 0:
            home_road_split.at[row['VISITOR'],'road_wins'] = home_road_split.loc[row['VISITOR'],'road_wins'] + 1
            home_road_split.at[row['HOME'],'home_losses'] = home_road_split.loc[row['HOME'],'home_losses'] + 1

    entire_schedule['HOME_W%_AT_HOME'] = home_win_at_home
    entire_schedule['VISITOR_W%_ON_ROAD'] = visitor_win_on_road
    return entire_schedule

def add_second_of_b2b(entire_schedule):
    teams_that_played_last_night = []
    teams_that_played_today = []
    home_team_b2b = []
    visitor_team_b2b = []
    last_night_day = "Monday"

    for index, row in entire_schedule.iterrows():
        day = pd.Timestamp.day_name(row['DATE'])

        if get_next_day(last_night_day) != day:
            teams_that_played_last_night = teams_that_played_today
            teams_that_played_today = []
            last_night_day = get_day_before(day)

        if get_next_day(last_night_day) == day:
            if row['HOME'] in teams_that_played_last_night:
                home_team_b2b.insert(index, 1)
            else:
                home_team_b2b.insert(index, 0)

            if row['VISITOR'] in teams_that_played_last_night:
                visitor_team_b2b.insert(index, 1)
            else:
                visitor_team_b2b.insert(index, 0)

        teams_that_played_today.append(row['HOME'])
        teams_that_played_today.append(row['VISITOR'])

    entire_schedule["HOME_B2B"] = home_team_b2b
    entire_schedule["VISITOR_B2B"] = visitor_team_b2b
    return entire_schedule

def add_last_twenty(entire_schedule):
    team_last_twenty = get_teams_last_twenty_init()
    home_last_twenty_percent = []
    visitor_last_twenty_percent = []
    for index, row in entire_schedule.iterrows():

        home_last_twenty_percent.append(get_win_loss_percentage_last_twenty(row['HOME'], team_last_twenty))
        visitor_last_twenty_percent.append(get_win_loss_percentage_last_twenty(row['VISITOR'], team_last_twenty))

        if not pd.isna(row['WINNER']):
            temp_home = team_last_twenty[row['HOME']]
            temp_home.pop(0)
            temp_visitor = team_last_twenty[row['VISITOR']]
            temp_visitor.pop(0)

            if row['WINNER'] == 1:
                temp_home.append('W')
                temp_visitor.append('L')
                team_last_twenty.update({row['HOME'] : temp_home})
                team_last_twenty.update({row['VISITOR'] : temp_visitor})
            elif row['WINNER'] == 0:
                temp_home.append('L')
                temp_visitor.append('W')
                team_last_twenty.update({row['HOME'] : temp_home})
                team_last_twenty.update({row['VISITOR'] : temp_visitor})

    entire_schedule["HOME_LAST_20"] = home_last_twenty_percent
    entire_schedule["VISITOR_LAST_20"] = visitor_last_twenty_percent
    return entire_schedule

def finalize_csv(name, year):
    entire_schedule = get_schedule(year, playoffs=False)
    entire_schedule = convert_team_names(entire_schedule)
    add_winner_column(entire_schedule)
    entire_schedule = add_win_percentage(add_team_stats(entire_schedule))
    entire_schedule = add_second_of_b2b(entire_schedule)
    entire_schedule = add_home_away_splits(entire_schedule)
    entire_schedule = add_last_twenty(entire_schedule)
    entire_schedule.to_csv(name, index=False)

    sys.stderr.write("[PROGRESS] CSV file creation completed!\n")
    sys.stderr.flush()

    return name


import sql
import asyncio

HEADER = 8
HOST = "0.0.0.0"
PORT = 12345
FORMAT = "utf-8"
ADDR = (HOST, PORT)
DISCONNECT_MESSAGE = "!DISCONNECT"

async def handle(reader:asyncio.StreamReader, writer:asyncio.StreamWriter):
    while True:
        try:
            print(writer.get_extra_info('peername'))
            data = await reader.read(100)
            message = data.decode()
            addr = writer.get_extra_info('peername')
            print(f"Received {message!r} from {addr!r}")
            print(f"Send: {message!r}")
            writer.write(data)
            await writer.drain()
            if message == DISCONNECT_MESSAGE:
                print("Close the connection")
                writer.close()
                await writer.wait_closed()
                break
        except:
            print("Close the connection")
            writer.close()
            await writer.wait_closed()
            break
async def main():
    server = await asyncio.start_server(handle, '127.0.0.1', 8888)
    addrs = ', '.join(str(sock.getsockname()) for sock in server.sockets)
    print(f'Serving on {addrs}')
    async with server:
        await server.serve_forever()
asyncio.run(main())


from enum import Enum

class warriorRank(Enum):
    Pushover= 1
    Novice= 2
    Fighter= 3
    Warrior= 4
    Veteran= 5
    Sage= 6
    Elite= 7
    Conqueror= 8
    Champion= 9
    Master= 10
    Greatest= 11

class Warrior():

    def __init__(self) -> None:
        self.level: int = 1
        self.rank_counter = 1
        self.rank: str = warriorRank(self.rank_counter).name
        self.experience = 100
        self.achievements = []

        self.MAX_EXPERIENCE = 10000
        self.MAX_LEVEL = self.MAX_EXPERIENCE // 100

    def manage_experience_and_rank(self, experience: int) -> None:

        """Manages the whole experience and rank calculation"""

        if self.experience + experience >= self.MAX_EXPERIENCE:
            self.experience = self.MAX_EXPERIENCE
            self.level = self.MAX_LEVEL
            self.set_rank_counter()
            self.set_rank()
        else:
            self.experience += experience
            if self.validate_level(self.level):
                self.set_level()
            self.set_rank_counter()
            self.set_rank()

    def set_achievement(self, achievement: str) -> None:

        """Tracks the training achievements"""

        self.achievements.append(achievement)

    def set_level(self) -> None:

        """Calculates and sets the current level of the warrior"""

        self.level = self.experience // 100

    def calculate_rank(self, level: int) -> int:

        """Calculates the current rank of the warrior based on his level"""

        if self.validate_level(level):
            return level // 10 + 1
        return 11

    def set_rank_counter(self) -> None:

        """Sets the rank counter based on the current level of the warrior"""

        self.rank_counter = self.calculate_rank(self.level)

    def set_rank(self) -> None:

        """Sets the current rank of the warrior based on rank counter and rank enum"""

        self.rank = warriorRank(self.rank_counter).name

    def calculate_battle_experience(self, opponent_level: int) -> int:

        """Calculates the whole experience gained from battles based on the level difference between both warriors"""

        if self.level == opponent_level:
            self.manage_experience_and_rank(10)

        elif self.level - 1 == opponent_level:
            self.manage_experience_and_rank(5)

        elif self.level - 2 >= opponent_level:
            self.manage_experience_and_rank(0)

        else:
            level_diff = opponent_level - self.level

            self.manage_experience_and_rank(20 * level_diff * level_diff)

    def check_defeat(self, opponent_level: int) -> bool:

        """Function to evaluate if fighter can compete with opponent or
        if opponent is to strong"""

        return (self.level <= opponent_level - 5) and (warriorRank(self.rank_counter).value < self.calculate_rank(opponent_level))

    def comment_fight(self, opponent_level: int) -> str:

        """Function to manage fight commenting after succesful fight"""

        if self.level >= opponent_level + 2:
            return "Easy fight"
        elif self.level >= opponent_level:
            return "A good fight"
        else:
            return "An intense fight"

    def validate_level(self, level: int) -> bool:

        """Validates the current level"""

        return 0 < level <= self.MAX_LEVEL

    def training(self, training_data: list[str, int, int]) -> str:

        """Function to manage the training abilities of a fighter"""

        description, experience, minumum_level = training_data

        if self.level >= minumum_level:
            self.set_achievement(description)
            self.manage_experience_and_rank(experience)
            return description

        else:
            return "Not strong enough"

    def battle(self, opponent_level: int) -> str:

        """Manages the battle of two warriors inclusive experience management etc."""

        if not self.validate_level(opponent_level):
            return "Invalid level"

        elif self.check_defeat(opponent_level):
            return "You've been defeated"

        else:
            fight_comment = self.comment_fight(opponent_level)
            self.calculate_battle_experience(opponent_level)
            return fight_comment


from collections import namedtuple
from google.cloud import bigquery
import json
import logging
import os

logger = logging.getLogger('SQLBuilder')

class Field(object):
    REPRESENTATION = "{}-{}-{}"
    STRING = "{} ({})"
    def __init__(self, name, index, alias, modifier, order, limit):
        self._alias = None
        self._index = None
        self._name = None
        self._modifier = None
        self._order = None
        self._limit = None
        self._setup(name, index, alias, modifier, order, limit)

    def _setup(self, name, index, alias, modifier, order, limit):
        self.alias = alias
        self.index = index
        self.limit = limit
        self.modifier = modifier
        self.name = name
        self.order = order

    @property
    def alias(self):
        return self._alias
    @alias.setter
    def alias(self, alias):
        alias = str(alias)
        self._alias = alias

    @property
    def index(self):
        return self._index
    @index.setter
    def index(self, index):
        self._index = index

    @property
    def limit(self):
        return self._limit
    @limit.setter
    def limit(self, limit):
        self._limit = limit

    @property
    def modifier(self):
        return self._modifier
    @modifier.setter
    def modifier(self, modifier):
        self._modifier = modifier

    @property
    def name(self):
        return self._name
    @name.setter
    def name(self, name):
        self._name = name

    @property
    def order(self):
        return self._order
    @order.setter
    def order(self, order):
        self._order = order

    def __str__(self):
        return str(self.alias)

    def __repr__(self):
        return self.REPRESENTATION.format(str(self.index), str(self.name), str(self.alias))

class FieldCollection(object):
    OPERATION_ALIAS = '{}({}) AS {}'
    OPERATION = '{}({})'
    FIELD_ALIAS = '{} AS {}'
    HANDLE = '{}_{}'

    def __init__(self):
        self._fieldsCount = -1
        self._fieldsByIndex = {}

    def __len__(self):
        return self._fieldsCount + 1

    def addField(self, fieldName, alias=None, operation=None, modifier=None, order=None, limit=None):
        self._fieldsCount += 1
        if operation is None:
            if alias is None:
                handle = fieldName
            else:
                handle = self.FIELD_ALIAS.format(fieldName, alias)
        else:
            if alias is None:
                handle = self.OPERATION.format(operation, fieldName)
            else:
                handle = self.OPERATION_ALIAS.format(operation, fieldName, alias)
        field = Field(fieldName, self._fieldsCount, handle, modifier, order, limit)
        self._fieldsByIndex[self._fieldsCount] = field
        return self._fieldsCount

    def getByIndex(self, index):
        if index in self._fieldsByIndex.keys():
            return self._fieldsByIndex(index)
        else:
            return None

    def getAll(self):
        return list(self._fieldsByIndex.values())

class StandardSqlFunction(object):
    PATTERN = "{}({})"
    NOT_SUPPORTED = "Standard function '{}' not supported"

    def __init__(self, functional, expression):
        self._functional = None
        self._expression = None
        self._setup(functional, expression)

    def _setup(self, functional, expression):
        self.functional = str(functional)
        self.expression = str(expression)

    @property
    def functional(self):
        return self._functional
    @functional.setter
    def functional(self, functional):
        functional = getattr(self, functional, None)
        if functional is None:
            logger.error(self.NOT_SUPPORTED.format(functional))
            raise Exception(self.NOT_SUPPORTED.format(functional))
        self._functional = functional

    @property
    def expression(self):
        return self._expression
    @expression.setter
    def expression(self, expression):
        self._expression = expression
    @expression.deleter
    def expression(self):
        del self._expression

    def apply(self):
        return self.PATTERN.format(self.functional, self.expression)

class NumberingFunction(StandardSqlFunction):
    DENSE_RANK      = "DENSE_RANK"
    RANK            = "RANK"
    ROW_NUMBER      = "ROW_NUMBER"

    NOT_SUPPORTED   = "Numbering function '{}' not supported"

class AggregationFunction(StandardSqlFunction):
    ANY         = "ANY_VALUE"
    ARRAY_AGG   = "ARRAY_AGG"
    AVG         = "AVG"
    COUNT       = "COUNT"
    MIN         = "MIN"
    MAX         = "MAX"
    SUM         = "SUM"

    NOT_SUPPORTED = "Aggregation function '{}' not supported"

class QueryClause(object):

    def __init__(self, config):
        self._table = None
        self._limit = None
        self._values = None
        self._groupby = None
        self._config = None
        self._setup(config)

    @property
    def table(self):
        return self._table
    @table.setter
    def table(self, table):
        self._table = table

    @property
    def limit(self):
        return self._limit
    @limit.setter
    def limit(self, limit):
        self._limit = limit

    @property
    def values(self):
        return self._values
    @values.setter
    def values(self, values):
        self._values = values

    @property
    def groupby(self):
        return self._groupby
    @groupby.setter
    def groupby(self, groupby):
        self._groupby = groupby

    @property
    def config(self):
        return self._config
    @config.setter
    def config(self, config):
        self._config = config

    def _setup(self, config):
        self.config = config
        self._parse()

    def _parse(self):
        self.table = self.config.table
        self.limit = self.config.limit
        self.values = self.config.values
        self.groupby = self.config.groupby

class GroupByClauses(object):
    NOT_VALID_STRUCTURE = "GroupBy '{}' not a valid structure"

    def __init__(self, config):
        self._clauses = None
        self._config = None
        self._setup(config)

    @property
    def config(self):
        return self._config
    @config.setter
    def config(self, config):
        self._config = config
        self._parse()

    @property
    def clauses(self):
        return self._clauses
    @clauses.setter
    def clauses(self, clauses):
        self._clauses = clauses

    def _parse(self):
        if self.config.groupby is None:
            self.clauses = None
        else:
            for position, groupby in enumerate(self.config.groupby):
                try:
                    handler = ConfigHandlerGroupBy(groupby)
                    self.clauses[position] = handler()
                except:
                    logger.info(self.NOT_VALID_STRUCTURE.format(position))

    def _setup(self, config):
        self.clauses = {}
        self.config = config

class GroupByClause(object):
    def __init__(self, config):
        self._aggregation = None
        self._config = None
        self._direction = None
        self._field = None
        self._limit = None
        self._sort = None
        self._setup(config)

    @property
    def aggregation(self):
        return self._aggregation
    @aggregation.setter
    def aggregation(self, aggregation):
        self._aggregation = aggregation

    @property
    def config(self):
        return self._config
    @config.setter
    def config(self, config):
        self._config = config
        self._parse()

    @property
    def direction(self):
        return self._direction
    @direction.setter
    def direction(self, direction):
        self._direction = direction

    @property
    def field(self):
        return self._field
    @field.setter
    def field(self, field):
        self._field = field

    @property
    def sort(self):
        return self._sort
    @sort.setter
    def sort(self, sort):
        self._sort = sort

    def _parse(self):
        self.aggregation = self.config.aggregation
        self.direction = self.config.direction
        self.field = self.config.field
        self.limit = self.config.limit
        self.sort = self.config.sort

    def _setup(self, config):
        self.config = config

class ValueClauses(object):
    NOT_VALID_STRUCTURE = "Value '{}' not a valid structure"

    def __init__(self, config):
        self._clauses = None
        self._config = None
        self._setup(config)

    @property
    def config(self):
        return self._config
    @config.setter
    def config(self, config):
        self._config = config
        self._parse()

    @property
    def clauses(self):
        return self._clauses
    @clauses.setter
    def clauses(self, clauses):
        self._clauses = clauses

    def _parse(self):
        if self.config.values is None:
            self.clauses = None
        else:
            for position, value in enumerate(self.config.values):
                try:
                    handler = ConfigHandlerValue(value)
                    self.clauses[position] = handler()
                except:
                    logger.info(self.NOT_VALID_STRUCTURE.format(position))

    def _setup(self, config):
        self.clauses = {}
        self.config = config

class ValueClause(object):
    def __init__(self, config):
        self._arrayLimit = None
        self._config = None
        self._dateAggregation = None
        self._field = None
        self._operation = None
        self._setup(config)

    @property
    def dateAggregation(self):
        return self._dateAggregation
    @dateAggregation.setter
    def dateAggregation(self, dateAggregation):
        self._dateAggregation = dateAggregation

    @property
    def config(self):
        return self._config
    @config.setter
    def config(self, config):
        self._config = config

    @property
    def field(self):
        return self._field
    @field.setter
    def field(self, field):
        self._field = field

    @property
    def arrayLimit(self):
        return self._arrayLimit
    @arrayLimit.setter
    def arrayLimit(self, arrayLimit):
        self._arrayLimit = arrayLimit

    @property
    def operation(self):
        return self._operation
    @operation.setter
    def operation(self, operation):
        self._operation = operation

    def _parse(self):
        self.dateAggregation = self.config.dateAggregation
        self.field = self.config.field
        self.arrayLimit = self.config.arrayLimit
        self.operation = self.config.operation

    def _setup(self, config):
        self.config = config

class Composer(object):

    EXPRESSION = "{} {}"
    STATEMENT_FROM = "FROM"
    STATEMENT_GROUPBY = "GROUP BY"
    STATEMENT_LIMIT = "LIMIT"
    STATEMENT_SELECT = "SELECT"
    STATEMENT_ORDERBY = "ORDER BY"
    SEPARATOR_CLAUSES = " "
    SEPARATOR_FIELDS = ", "
    TABLE_NAME_ESC = "`{}`"
    QUALIFIER_DISTINCT = "DISTINCT"

    def __init__(self, path):
        self._aliases = None
        self._fields = None
        self._groupByClauses = None
        self._groupByFields = None
        self._path = None
        self._sql = None
        self._table = None
        self._query = None
        self._setup(path)

    def buildQuery(self):
        self.sql = []
        self._with()
        self._select()
        self._from()
        self._where()
        self._groupby()
        self._limit()
        self._offset()
        return self.SEPARATOR_CLAUSES.join(
            [
                partial for partial in self.sql if len(partial)
            ]
        )

    def _with(self):
        pass

    def _select(self):
        self.sql.append(
            self.EXPRESSION.format(
                self.STATEMENT_SELECT,
                self.SEPARATOR_FIELDS.join(
                    [str(field) for field in self.fields.getAll()]
                )
            )
        )

    def _from(self):
        self.sql.append(
            self.EXPRESSION.format(
                self.STATEMENT_FROM,
                self.TABLE_NAME_ESC.format(self.table)
            )
        )

    def _where(self):
        pass

    def _groupby(self):
        if self.groupByFields:
            self.sql.append(self.EXPRESSION.format(
                self.STATEMENT_GROUPBY,
                self.SEPARATOR_FIELDS.join(
                    [str(field) for field in self.groupByFields.getAll()]
                )
            )
        )

    def _limit(self):
        if self.limit:
            self.sql.append(
                self.EXPRESSION.format(
                    self.STATEMENT_LIMIT,
                    self.limit
                )
            )

    def _offset(self):
        if self.offset:
            self.sql.append(
                self.EXPRESSION.format(
                    self.STATEMENT_OFFSET,
                    self.offset
                )
            )

    def _setup(self, path):
        self.path = path
        self.config = Configuration(self.path)
        self._parseConfig()

    @property
    def groupByClauses(self):
        return self._groupByClauses
    @groupByClauses.setter
    def groupByClauses(self, groupByClauses):
        self._groupByClauses = groupByClauses

    @property
    def fields(self):
        return self._fields
    @fields.setter
    def fields(self, fields):
        self._fields = fields

    @property
    def groupByFields(self):
        return self._groupByFields
    @groupByFields.setter
    def groupByFields(self, groupByFields):
        self._groupByFields = groupByFields

    @property
    def path(self):
        return self._path
    @path.setter
    def path(self, path):
        self._path = path

    @property
    def sql(self):
        return self._sql
    @sql.setter
    def sql(self, sql):
        self._sql = sql

    @property
    def table(self):
        return self._table
    @table.setter
    def table(self, table):
        self._table = table

    @property
    def valueClauses(self):
        return self._valueClauses
    @valueClauses.setter
    def valueClauses(self, valueClauses):
        self._valueClauses = valueClauses

    def _groupbyClauses(self, groupByClauses):
        self.groupByClauses = groupByClauses

    def _values(self, valueClauses):
        self.valueClauses = valueClauses

    def addTable(self, tableName):
        self.table = tableName

    def _parseConfig(self):
        self._parseTable()
        self._parseGroupBy()
        self._parseValues()
        self._parseLimit()
        self._parseOffset()
        self._collectFields()

    def _parseTable(self):
        self.table = self.config.config[ConfigHandlerQuery.TABLE]

    def _parseGroupBy(self):
        configHandlerQuery = ConfigHandlerQuery(self.config.config)
        configInstance = configHandlerQuery()
        self.groupByClauses = GroupByClauses(configInstance)

    def _parseValues(self):
        configHandlerQuery = ConfigHandlerQuery(self.config.config)
        configuration = configHandlerQuery()
        self.valueClauses = ValueClauses(configuration)

    def _parseLimit(self):
        if ConfigHandlerQuery.LIMIT in self.config.config.keys():
            self.limit = self.config.config[ConfigHandlerQuery.LIMIT]
        else:
            self.limit = None

    def _parseOffset(self):
        if ConfigHandlerQuery.OFFSET in self.config.config.keys():
            self.offset = self.config.config[ConfigHandlerQuery.OFFSET]
        else:
            self.offset = None

    def _collectFields(self):
        self.fields = FieldCollection()
        self.groupByFields = FieldCollection()
        for index, groupByClause in self.groupByClauses.clauses.items():
            if groupByClause.alias:
                self.fields.addField(
                    groupByClause.field,
                    alias=groupByClause.alias
                )
            else:
                self.fields.addField(groupByClause.field)
            self.groupByFields.addField(groupByClause.field)
        for index, valueClause in self.valueClauses.clauses.items():
            # @TODO: Add support for date aggregation
            expression = valueClause.field
            if valueClause.modifier is not None:
                expression = self.EXPRESSION.format(valueClause.modifier, expression)
            if valueClause.order is not None:
                order = self.EXPRESSION.format(self.STATEMENT_ORDERBY, valueClause.field)
                direction = self.EXPRESSION.format(order, valueClause.direction)
                expression = self.EXPRESSION.format(expression, direction)
            if valueClause.operation == AggregationFunction.ARRAY_AGG and valueClause.arrayLimit:
                limit = self.EXPRESSION.format(self.STATEMENT_LIMIT, valueClause.arrayLimit)
                expression = self.EXPRESSION.format(expression, limit)
            expression = AggregationFunction(
                valueClause.operation,
                expression,
            ).apply()
            self.fields.addField(expression, alias=valueClause.alias)

class Configuration():

    def __init__(self, path):
        self._path = None
        self._config = None
        self._setup(path)

    @property
    def path(self):
        return self._path
    @path.setter
    def path(self, path):
        self._path = str(path)

    @property
    def config(self):
        return self._config
    @config.setter
    def config(self, config):
        self._config = config

    def _setup(self, path):
        self.path = path
        self._load()

    def _load(self):
        try:
            with open(self.path) as handle:
                data = handle.read()
        except:
            raise Exception("Can not open file: {}".format(self.path))
        try:
            self.config = json.loads(data)
        except:
            raise Exception("Not a valid JSON format: {}".format(self.path))

    def __call__(self):
        return self.config

    def __repr__(self):
        return self.path

    def __str__(self):
        return self.path

class ConfigurationHandler(object):
    MISSING_FIELD = "Missing mandatory configuration field: {}"

    def __init__(self, config):
        self._config = config
        self._setup(config)

    def _setup(self, config):
        self.config = config
        self._parse()

    @property
    def config(self):
        return self._config
    @config.setter
    def config(self, config):
        self._config = config

    def __call__(self):
        return self.config

class ConfigHandlerQuery(ConfigurationHandler):
    GROUPBY = 'GROUP_BY'
    LIMIT   = 'TOTAL_LIMIT'
    OFFSET  = 'OFFSET'
    TABLE   = 'TABLE_NAME'
    VALUES  = 'VALUES'

    def _parse(self):
        try:
            table = self.config[self.TABLE]
            limit = None
            if self.LIMIT in self.config.keys():
                limit = self.config[self.LIMIT]
            values = None
            if self.VALUES in self.config.keys():
                values = self.config[self.VALUES]
            groupby = None
            if self.GROUPBY in self.config.keys():
                groupby = self.config[self.GROUPBY]
        except:
            logger.error(self.MISSING_TABLE.format(self.TABLE))
            raise Exception(self.MISSING_TABLE.format(self.TABLE))
        Configuration = namedtuple(
            'Configuration',
            'table limit values groupby'
        )
        self.config = Configuration(table, limit, values, groupby)

class ConfigHandlerValue(ConfigurationHandler):
    ARRAY_LIMIT = 'ArrayLimit'
    DATE_AGGREGATION = 'DateAggregation'
    FIELD = 'Field'
    OPERATION = 'Operation'
    MODIFIER = 'Modifier'
    ORDER = 'Order'
    DIRECTION = 'Direction'
    ALIAS = 'Alias'

    def _parse(self):
        try:
            _field = self.config[self.FIELD]
            _arrayLimit = None
            if self.ARRAY_LIMIT in self.config.keys():
                _arrayLimit = self.config[self.ARRAY_LIMIT]
            _alias = None
            if self.ALIAS in self.config.keys():
                _alias = self.config[self.ALIAS]
            _dateAggregation = None
            if self.DATE_AGGREGATION in self.config.keys():
                _dateAggregation = self.config[self.DATE_AGGREGATION]
            _direction = None
            if self.DIRECTION in self.config.keys():
                _direction = self.config[self.DIRECTION]
            _operation = None
            if self.OPERATION in self.config.keys():
                _operation = self.config[self.OPERATION]
            _modifier = None
            if self.MODIFIER in self.config.keys():
                _modifier = self.config[self.MODIFIER]
            _order = None
            if self.ORDER in self.config.keys():
                _order = self.config[self.ORDER]
        except:
            logger.error(self.MISSING_FIELD.format(self.FIELD))
            raise Exception(self.MISSING_FIELD.format(self.FIELD))
        Configuration = namedtuple(
            'Configuration',
            'arrayLimit alias dateAggregation field modifier operation order direction'
        )
        self.config = Configuration(
            _arrayLimit,
            _alias,
            _dateAggregation,
            _field,
            _modifier,
            _operation,
            _order,
            _direction
        )

class ConfigHandlerGroupBy(ConfigurationHandler):
    DATE_AGGREGATION    = 'DateAggregation'
    DIRECTION           = 'SortDirection'
    FIELD               = 'Field'
    LIMIT               = 'Limit'
    SORT                = 'Sort'
    ALIAS               = 'Alias'

    def _parse(self):
        try:
            field = self.config[self.FIELD]
            aggregation = None
            if self.DATE_AGGREGATION in self.config.keys():
                aggregation = self.config[self.DATE_AGGREGATION]
            alias = None
            if self.ALIAS in self.config.keys():
                alias = self.config[self.ALIAS]
            direction = None
            if self.DIRECTION in self.config.keys():
                direction = self.config[self.DIRECTION]
            limit = None
            if self.LIMIT in self.config.keys():
                limit = self.config[self.LIMIT]
            sort = None
            if self.SORT in self.config.keys():
                sort = self.config[self.SORT]
        except:
            logger.error(self.MISSING_FIELD.format(self.FIELD))
            raise Exception(self.MISSING_FIELD.format(self.FIELD))
        Configuration = namedtuple(
            'Configuration',
            'aggregation alias direction field limit sort'
        )
        self.config = Configuration(
            aggregation,
            alias,
            direction,
            field,
            limit,
            sort
        )

if "__main__" == __name__:
    print("SQLBuilder is a package file, execution has no effects.\nTo execute tests suite run testsqlbuilder.py")


import streamlit as st
from streamlit_card import card
from streamlit_extras.switch_page_button import switch_page
import pandas as pd
import mysql.connector
conn1 = mysql.connector.connect(host="localhost", user="root", password="<PASSWORD>",database='streamlit')
CSR1=conn1.cursor()

User1 = st.session_state["username"]

st.set_page_config(
    page_title="EXAMS",
    initial_sidebar_state="collapsed",
)

st.header("Hello : ",User1)
st.header("exams to be cleared")

student_card1 = card(
    title="JEE",
    text="Click to view JEE details",
    image="https://via.placeholder.com/150",
    on_click=lambda: print("Clicked!")
)

student_card2 = card(
    title="KCET",
    text="Click to view KECET details",
    image="https://via.placeholder.com/150",
    on_click=lambda: print("Clicked!")
    )

student_card3 = card(
        title="NEET",
        text="Click to view NEET details",
        image="https://via.placeholder.com/150",
        on_click=lambda: print("Clicked!")
)


import requests
from typing import Literal, List, Union
import time
import functools

WIKIPEDIA_API_URL = 'https://en.wikipedia.org/w/api.php'

def params_info(
    titles: Union[str, List[str]] = None,
    pageids: Union[int, List[int]] = None,
    inprop: Literal['protection', 'talkid', 'watched', 'watchers', 'visitingwatchers', 'notificationtimestamp', 'subjectid', 'url', 'readable', 'preload', 'displaytitle'] = None,
):
    """
    Example URL: https://en.wikipedia.org/w/api.php?action=query&format=json&prop=info&titles=Algebra
    """
    assert (titles is None) ^ (pageids is None), 'Either titles or pageids must be specified.'
    params = {
        'action': 'query',
        'format': 'json',
        'prop': 'info',
        'titles': titles,
        'pageids': pageids,
        'inprop': inprop
    }
    params = {k: v for k, v in params.items() if v is not None}
    return params

def params_categorymembers(
    cmtitle: str = None,
    cmpageid: int = None,
    cmtype: Literal['page', 'subcat', 'file'] = None,
    cmlimit: int = 'max',
):
    """
    API Reference: https://www.mediawiki.org/wiki/API:Categorymembers

    Example URL: https://en.wikipedia.org/w/api.php?action=query&format=json&list=categorymembers&cmtype=subcat&cmtitle=Category:Algebra&cmlimit=max
    """

    assert (cmtitle is None) ^ (cmpageid is None), 'Either cmtitle or cmpageid must be specified.'
    assert cmtype in ['page', 'subcat', 'file'], 'cmtype must be one of "page", "subcat", "file".'

    params = {
        'action': 'query',
        'format': 'json',
        'list': 'categorymembers',
        'cmtitle': cmtitle,
        'cmpageid': cmpageid,
        'cmtype': cmtype,
        'cmlimit': cmlimit
    }
    params = {k: v for k, v in params.items() if v is not None}
    return params

def params_categories(
    titles: Union[str, List[str]] = None,
    pageids: Union[int, List[int]] = None,
    cllimit: int = 'max',
):
    """
    List all categories the pages belong to.

    Example https://en.wikipedia.org/w/api.php?action=query&prop=categories&titles=Albert%20Einstein&format=json
    """
    assert (titles is None) ^ (pageids is None), 'Either titles or pageids must be specified.'
    params = {
        'action': 'query',
        'format': 'json',
        'prop': 'categories',
        'titles': titles,
        'pageids': pageids,
        'cllimit': cllimit
    }
    params = {k: v for k, v in params.items() if v is not None}
    return params

def params_iwlinks(
        titles: Union[str, List[str]] = None,
        pageids: Union[int, List[int]] = None,
        iwlimit: int = 'max',
    ):
    """
    Returns all interwiki links from the given pages.

    Example https://en.wikipedia.org/w/api.php?action=query&format=json&prop=iwlinks&titles=Albert%20Einstein
    """
    assert (titles is None) ^ (pageids is None), 'Either titles or pageids must be specified.'
    params = {
        'action': 'query',
        'format': 'json',
        'prop': 'iwlinks',
        'titles': titles,
        'pageids': pageids,
        'iwlimit': iwlimit
    }
    params = {k: v for k, v in params.items() if v is not None}
    return params

def params_links(
    titles: Union[str, List[str]] = None,
    pageids: Union[int, List[int]] = None,
    plnamespace: int = None,
    pllimit: int = 'max',
):
    """
    Returns all links from the given pages.

    Example https://en.wikipedia.org/w/api.php?action=query&format=json&prop=links&titles=Algebra&
    """
    assert (titles is None) ^ (pageids is None), 'Either titles or pageids must be specified.'
    params = {
        'action': 'query',
        'format': 'json',
        'prop': 'links',
        'titles': titles,
        'pageids': pageids,
        'plnamespace': plnamespace,
        'pllimit': pllimit
    }
    params = {k: v for k, v in params.items() if v is not None}
    return params

def params_extlinks(
    titles: Union[str, List[str]] = None,
    pageids: Union[int, List[int]] = None,
    elprotocol: Literal['http', 'https', 'ftp', 'ftps'] = None,
    ellimit: int = 'max',
):
    """
    Returns all external links from the given pages.

    Example https://en.wikipedia.org/w/api.php?action=query&format=json&prop=extlinks&titles=Algebra
    """
    assert (titles is None) ^ (pageids is None), 'Either titles or pageids must be specified.'
    params = {
        'action': 'query',
        'format': 'json',
        'prop': 'extlinks',
        'titles': titles,
        'pageids': pageids,
        'elprotocol': elprotocol,
        'ellimit': ellimit
    }
    params = {k: v for k, v in params.items() if v is not None}
    return params

def params_linkshere(
    titles: Union[str, List[str]] = None,
    pageids: Union[int, List[int]] = None,
    lhnamespace: int = None,
    lhshow: Literal['redirect', 'nonredirect'] = None,
    lhlimit: int = 'max',
):
    """
    Returns all pages that link to the given pages.

    Example https://en.wikipedia.org/w/api.php?action=query&format=json&list=backlinks&bltitle=Algebra&bllimit=max
    """
    assert (titles is None) ^ (pageids is None), 'Either titles or pageids must be specified.'
    params = {
        'action': 'query',
        'format': 'json',
        'list': 'backlinks',
        'titles': titles,
        'pageids': pageids,
        'lhnamespace': lhnamespace,
        'lhshow': lhshow,
        'lhlimit': lhlimit
    }
    params = {k: v for k, v in params.items() if v is not None}
    return params

def params_images(
    titles: Union[str, List[str]] = None,
    pageids: Union[int, List[int]] = None,
    imlimit: int = 'max',
):
    """
    Returns all images from the given pages.

    Example https://en.wikipedia.org/w/api.php?action=query&prop=images&titles=Astronomy&format=json
    """
    assert (titles is None) ^ (pageids is None), 'Either titles or pageids must be specified.'
    params = {
        'action': 'query',
        'format': 'json',
        'prop': 'images',
        'titles': titles,
        'pageids': pageids,
        'imlimit': imlimit
    }
    params = {k: v for k, v in params.items() if v is not None}
    return params

def params_pageviews(
    titles: Union[str, List[str]] = None,
    pageids: Union[int, List[int]] = None,
    pvipdays: int = 30,
):
    """
    Returns the number of pageviews for the given pages.

    Example https://en.wikipedia.org/w/api.php?action=query&format=json&titles=Mathematics&prop=pageviews&pvipdays=30
    """
    assert (titles is None) ^ (pageids is None), 'Either titles or pageids must be specified.'
    params = {
        'action': 'query',
        'format': 'json',
        'prop': 'pageviews',
        'titles': titles,
        'pageids': pageids,
        'pvipdays': pvipdays
    }
    params = {k: v for k, v in params.items() if v is not None}
    return params

def params_siteviews():
    """
    Returns the number of views of wiki site.

    Example https://en.wikipedia.org/w/api.php?action=query&format=json&meta=siteviews
    """
    params = {
        'action': 'query',
        'format': 'json',
        'meta': 'siteviews',
    }
    return params

def params_mostviewed():
    """
    Returns the most viewed pages. (based on last day's pageview count)
    Example https://en.wikipedia.org/w/api.php?action=query&format=json&list=mostviewed
    """
    params = {
        'action': 'query',
        'format': 'json',
        'list': 'mostviewed',
    }
    return params

def params_content_parse(
    page: str = None,
    prop: Literal['text', 'wikitext'] = None
):
    """
    Returns content of given pages, either as HTML or wikitext.

    Example https://en.wikipedia.org/w/api.php?action=parse&format=json&page=Algebra&prop=text&formatversion=2
    """
    params = {
        'action': 'query',
        'format': 'json',
        'prop': prop,
        'page': page,
    }
    params = {k: v for k, v in params.items() if v is not None}
    return params

def params_content_extracts(
    titles: Union[str, List[str]] = None,
    pageids: Union[int, List[int]] = None,
    exchars: int = None,
    exsentences: int = 'max',
    exlimit: int = 'max',
):
    """
    Returns content of given pages, as extracts.

    Example https://en.wikipedia.org/w/api.php?action=query&prop=extracts&exsentences=10&exlimit=1&titles=Pet_door&explaintext=1&formatversion=2
    """
    assert (titles is None) ^ (pageids is None), 'Either titles or pageids must be specified.'
    assert (exchars is None) ^ (exsentences is None), 'Either exchars or exsentences must be specified.'

    params = {
        'action': 'query',
        'format': 'json',
        'prop': 'extracts',
        'titles': titles,
        'pageids': pageids,
        'exchars': exchars,
        'exsentences': exsentences,
        'exintro': None if isinstance(titles, str) or isinstance(pageids, int) else "",
        'exlimit': exlimit,
        'explaintext': 1,
        'formatversion': 2,
    }
    params = {k: v for k, v in params.items() if v is not None}
    return params


import os
import sys
from random import Random, choices
import numpy as np
import configparser
from rtinspector import RealtimeInspector, RTIResult
from mario_constant import *

############################################################################
# Dependency Matrix Converter
############################################################################
class DMatConverter():
    """
    numpy array to string converter for key storing in hashtable
    """
    def Decode(sequence, bsize):
        res = np.array([ord(c) for c in sequence], dtype=int)
        return res.reshape(bsize)

    def Encode(mat):
        res = ''.join([chr(ele) for ele in mat.flatten().tolist()])
        return res

class DMat_11:
    """ Dependency Matrix [1 1] """
    def PrevState(matrix, unit):
        return DMatConverter.Encode(matrix[0:unit,0:unit])

    def NextState(matrix, unit):
        return DMatConverter.Encode(matrix[0:unit,unit:unit*2])

    @classmethod
    def Parse(cls, matrix, unit):
        return (cls.PrevState(matrix, unit), cls.NextState(matrix, unit))

class DMat_1101:
    """ Dependency Matrix [1 1; 0 1] """
    def PrevState(matrix, unit):
        return DMatConverter.Encode( np.concatenate((matrix[0:unit, 0:unit], matrix[unit:unit*2, unit:unit*2]),axis=1) )

    def NextState(matrix, unit):
        return DMatConverter.Encode(matrix[0:unit,unit:unit*2])

    @classmethod
    def Parse(cls, matrix, unit):
        return (cls.PrevState(matrix, unit), cls.NextState(matrix, unit))

class DMat_1111:
    """ Dependency Matrix [1 1; 1 1] """
    def PrevState(matrix, unit):
        return DMatConverter.Encode( np.concatenate((matrix[0:unit, 0:unit], matrix[unit:unit*2, 0:unit], matrix[unit:unit*2, unit:unit*2]),axis=1))

    def NextState(matrix, unit):
        return DMatConverter.Encode(matrix[0:unit,unit:unit*2])

    @classmethod
    def Parse(cls, matrix, unit):
        return (cls.PrevState(matrix, unit), cls.NextState(matrix, unit))

############################################################################
# Mario Level Object
############################################################################
class MarioLevel:
    """
    Load level from file and convert into numpy array.
    """
    def __init__(self, path):
        self.data = None
        self.load_level(path)
        pass

    def load_level(self, path):
        f = open(path, 'r')
        # load the each lines of level
        lines = []
        for line in f.readlines():
            lines.append(line.strip())

        h = len(lines)
        w = len(lines[0])
        self.data = np.zeros((h, w), dtype=int)
        self.data.fill(MarioSprite.EMPTY.value)

        i = 0
        for line in lines:
            # convert into np.array and remove the unnecessary sprites
            self.data[i,:] = np.array([MarioSprite.EMPTY.value if ord(c) in ALL_SPRITES_TO_BE_REMOVED else ord(c) for c in line.strip()][0:w])
            i += 1

        f.close()

    def get_data(self):
        return self.data

    def EncodeLevel(data):
        lines = data.tolist()
        out = []
        for line in lines:
            out.append(''.join([chr(c) for c in line]))
        return '\n'.join(out)

############################################################################
# Dependency Table
############################################################################
class DependencyTable:
    """
    Stores the markov states(t), states(t+1) and frequency
    """
    def __init__(self):
        self._table= {}
        pass

    def train(self, states):
        # train
        prev_state = states[0]
        next_state = states[1]
        if not self._table.get(prev_state):
            self._table[prev_state] = {}
        count = self._table[prev_state].get(next_state)
        if count == None: self._table[prev_state][next_state] = 0
        self._table[prev_state][next_state] +=1
        pass

    def generate_state(self, state):
        # generate
        data = None
        table = self._table.get(state)
        if not table: return data
        data = choices([k for k in table.keys()], [v for v in table.values()])
        if type(data) == list: data = data[0]
        return data

############################################################################
# Mario Core Markov Chains training and generator
############################################################################
# * use decode, encode only in this class
class MarioCoreMC:
    """
    Mario Core Markov Chain object
    """
    def __init__(self, unitsize):
        self._unit = BLOCK_UNIT if not unitsize else unitsize

        # dependency tables
        self._dt_wall = DependencyTable()
        self._dt_bottom = DependencyTable() # same with dt_11

        self._dt_11 = DependencyTable()
        self._dt_1101 = DependencyTable()
        self._dt_1111 = DependencyTable()
        pass

    def train(self, level):
        """ train the level """
        ldata = level.get_data()
        nrows = ldata.shape[0]/self._unit

        ######
        # 1. save the wallet information
        self._dt_wall.train((DEFAULT_STATE, DMatConverter.Encode( ldata[:, 0:self._unit])))

        ######
        # 2. train the bottom lines
        n = int((nrows-1)*self._unit)
        bottoms = ldata[n:n+self._unit, :]

        length = bottoms.shape[1]
        if length % self._unit is not 0: length -= (length % self._unit)

        for xoff in range(self._unit,length,self._unit):
            nstate = bottoms[:,xoff:xoff+self._unit]
            mat = bottoms[:,xoff-self._unit:xoff+self._unit]
            self._dt_bottom.train(DMat_11.Parse(mat, self._unit))

        ######
        # 3. train from bottom to top and left to right : to 3 dependency table
        for xoff in range(self._unit, length, self._unit):
            for yoff in range(ldata.shape[0] - 2*self._unit, -1, -self._unit):
                # train to D1101 D1111
                mat = ldata[yoff:yoff+self._unit*2, xoff-self._unit:xoff+self._unit]
                self._dt_1101.train(DMat_1101.Parse(mat, self._unit))
                self._dt_1111.train(DMat_1111.Parse(mat, self._unit))

                # train to D11
                self._dt_11.train(DMat_11.Parse(ldata[yoff:yoff+self._unit, xoff-self._unit:xoff+self._unit], self._unit))
        pass

    def generate(self, output, rti):
        """ generate new level """
        (h, w) = output.shape
        rti.reset(output, self._unit)

        ######
        # 1. generate the left wall
        wall = self._dt_wall.generate_state(DEFAULT_STATE)
        wall = DMatConverter.Decode(wall, (h, self._unit))
        output[:, 0:self._unit] = wall[:,:]

        ######
        # 2. generate the bottom
        #output[h-self._unit:h, 0:self._unit] = self._start_block[:,:]
        retry = True
        if w%self._unit != 0:
            w -= w%self._unit
        while retry:
            cur = output[h-self._unit:h, 0:self._unit]
            for xoff in range(self._unit, w, self._unit):
                generated = self._dt_bottom.generate_state(DMatConverter.Encode(cur))
                if not generated: break
                cur = DMatConverter.Decode(generated, (self._unit, self._unit))
                output[h-self._unit:h, xoff:xoff+self._unit] = cur[:,:]

            retry = not rti.check_bottom()

        #####
        # 3. generate whole tiles
        for xoff in range(self._unit, w, self._unit):
            for yoff in range(h - 2*self._unit, -1, -self._unit):
                res = rti.inspect((xoff, yoff)) ## inspect whenever changed new column

                # check current position
                generated = None

                # dependency matrix methods
                while res is not RTIResult.TRUE:
                    #
                    if res is RTIResult.DMAT_1111:
                        cur = output[yoff:yoff+self._unit*2, xoff-self._unit:xoff+self._unit]
                        generated = self._dt_1111.generate_state(DMat_1111.PrevState(cur, self._unit))
                    #
                    elif res is RTIResult.DMAT_1101:
                        cur = output[yoff:yoff+self._unit*2, xoff-self._unit:xoff+self._unit]
                        generated = self._dt_1101.generate_state(DMat_1101.PrevState(cur, self._unit))
                    #
                    elif res is RTIResult.DMAT_11:
                        cur = output[yoff:yoff+self._unit, xoff-self._unit:xoff+self._unit]
                        generated = self._dt_11.generate_state(DMat_11.PrevState(cur, self._unit))
                    #
                    elif res is RTIResult.ERROR:
                        print("retry.... this happens when blocksize is too big or training data is not enouch.")
                        break
                        # sys.exit(1)

                    res = rti.check_block(generated)
                    if res is RTIResult.TRUE:
                        output[yoff:yoff+self._unit, xoff:xoff+self._unit] = DMatConverter.Decode(generated, (self._unit, self._unit))[:,:]
                ######################## end while

        # placing MARIO_START MARIO_EXIT
        startpos = rti.mario_start_pos()
        exitpos = rti.mario_exit_pos()

        if startpos:
            output[startpos[1], startpos[0]] = MarioSprite.MARIO_START.value
        output[exitpos[1], exitpos[0]] = MarioSprite.MARIO_EXIT.value

        # TODO self._rt_inspector.place enemy / coin / items
        pass #generate()

############################################################################
# Group Generator
############################################################################
class GroupQLevelGenerator:
    """
    Mario Level Generator of GroupQ
    """

    def __init__(self, config_path):
        self._config = None
        self.load_config(config_path)

        # load config
        self._unitsize = self._config[CONFIG_PARAMS].get('block_unit_size')
        if not self._unitsize: self._unitsize = DEFAULT_BLOCK_UNIT
        else: self._unitsize = int(self._unitsize)

        self._rt_inspector = RealtimeInspector(self._config)
        self._markovchain = MarioCoreMC(self._unitsize)
        # generating output
        self._output = np.zeros(DEST_SIZE, dtype=int)

    def load_config(self, config_path):
        # load config
        arg_load = config_path
        self._config = configparser.ConfigParser()
        self._config.read(arg_load)

    def train_levels(self):
        """ training the  mario levels from input paths in config file """
        te = TimeEstimate().start()

        # looping input paths
        for key in self._config[CONFIG_INPUTPATH]:
            base_path = self._config[CONFIG_INPUTPATH][key]
            # load levels
            flist = os.listdir(base_path)
            files = []
            [files.append(f) if f.endswith('txt') else None for f in flist]
            for filename in files:
                level = MarioLevel(os.path.join(base_path, filename))
                # train level
                self._markovchain.train( level )
        return te.stop()

    def generate_level(self):
        """generate the mario level"""
        te = TimeEstimate().start()

        # reset output and call genrate method of markov chain object
        self._output.fill(MarioSprite.EMPTY.value)
        self._markovchain.generate(self._output, self._rt_inspector)

        return te.stop()

    def save_file(self, idx):
        """ save the generated file to lvl_{idx}.txt """
        # ready for save
        filename = os.path.join(self._config[CONFIG_OUTPUTPATH]["path"], "lvl-{}.txt".format(idx))
        f = open(filename, 'w')
        f.write(MarioLevel.EncodeLevel(self._output))
        f.close()
        return filename

    def generate_levels(self):
        """ generate the levels and save to path"""
        nlevels = self._config[CONFIG_PARAMS].get('num_levels')
        nlevels = int(nlevels) if nlevels else DEFAULT_NUM_OF_LEVELS

        for i in range(nlevels):
            t = self.generate_level()
            fn = self.save_file(i+1)
            print("level generating time : {:.3f} secs, saved to : '{}'".format(t, fn))


#!/usr/bin/python3
"""Handles communications with the evaluation server"""
import sys
import socket
import base64
import time
import argparse
import threading
import errno
import queue
import traceback
import os
from Crypto.Cipher import AES
from Crypto import Random

class PowerState:
    def __init__(self):
        self.lock = threading.Lock()
        self.voltage = 0.0  # voltage (in V)
        self.current = 0.0  # current (in A)
        self.cumpower = 0

class EvalClient:
    def __init__(self, address, secret_key):
        if len(secret_key) not in (16, 24, 32):
            raise TypeError('AES key must be either 16, 24, or 32 bytes long')
        rndfile = Random.new()
        self.iv = rndfile.read(16)
        self.address = address
        self.secret_key = secret_key
        self.sock = None

    @property
    def is_connected(self):
        return self.sock is not None

    def connect(self):
        """Attempts to connect to the server.

        Returns True if the connection was successful, False otherwise.
        """
        if self.sock:
            return True
        try:
            self.sock = socket.create_connection(self.address)
        except OSError as e:
            print(e, file=sys.stderr)
            return False
        return True

    def send(self, action, voltage, current, power, cumpower):
        assert self.is_connected
        cipher = AES.new(self.secret_key, AES.MODE_CBC, self.iv)
        msg = '#{action}|{voltage}|{current}|{power}|{cumpower}'.format(
                action=action, voltage=voltage, current=current,
                power=power, cumpower=cumpower)
        msg = msg.encode()
        padding = b' ' * (AES.block_size - len(msg) % AES.block_size)
        msg = padding + msg
        msg = self.iv + cipher.encrypt(msg)
        msg = base64.b64encode(msg)
        try:
            self.sock.sendall(msg)
        except OSError:
            self.sock = None
            raise

class EvalServerThread(threading.Thread):
    """Eval server communication thread"""

    def __init__(self, client, pow_state, action_queue, interval=10):
        super().__init__(name='EvalServerThread', daemon=True)
        self.client = client
        self.pow_state = pow_state
        self.action_queue = action_queue
        self.interval = interval  # Connection interval (in s)

    def run(self):
        try:
            self._run()
        except:
            traceback.print_exc()
            os._exit(1)

    def _run(self):
        while True:
            while not self.client.is_connected:
                print('connecting to', self.client.address, file=sys.stderr)
                if not self.client.connect():
                    time.sleep(self.interval)
            # Receive item
            action = self.action_queue.get()
            # Read voltage/current
            with self.pow_state.lock:
                voltage = self.pow_state.voltage
                current = self.pow_state.current
            # Try to send it to the evaluation server
            try:
                self.client.send(action=action, voltage=voltage,
                                 current=current, power=voltage*current,
                                 cumpower=self.pow_state.cumpower)
            except OSError as e:
                print(e, file=sys.stderr)
            finally:
                self.action_queue.task_done()

class MlThread(threading.Thread):
    """Thread that reads from the machine-learning stream"""

    def __init__(self, filepath, action_queue):
        super().__init__(name='MlThread', daemon=True)
        self.filepath = filepath
        self.action_queue = action_queue

    def run(self):
        try:
            while True:
                self.run_one()
        except:
            traceback.print_exc()
            os._exit(1)

    def run_one(self):
        print('opening', self.filepath, '...', file=sys.stderr)
        with open(self.filepath, 'r') as f:
            print('opened', self.filepath)
            for line in f:
                try:
                    self.action_queue.put_nowait(line.strip())
                except queue.Full:
                    pass

class PowThread(threading.Thread):
    """Thread that reads from the power-measurement stream"""

    def __init__(self, filepath, pow_state):
        super().__init__(name='PowThread', daemon=True)
        self.filepath = filepath
        self.pow_state = pow_state

    def to_current(self, sensor_value):
        "Converts the raw sensor value to current (in A)"

        VOLTAGE_REF = 5.1
        RS = 0.1  # Shunt resistor value (in ohms)

        # Remap the ADC value into a voltage number
        sensor_value = (sensor_value * VOLTAGE_REF) / 1023

        # Follow the equation given by the INA169 datasheet to determine
        # the current flowing through RS. Assume RL = 10k
        # Is = (Vout * 1k) / (RS * RL)
        return sensor_value / (10 * RS)

    def to_voltage(self, sensor_value):
        "Convert the raw potential sensor value to voltage (in V)"

        VOLTAGE_REF = 5.1
        return min(2 * (sensor_value * VOLTAGE_REF) / 1023, VOLTAGE_REF)

    def run(self):
        try:
            while True:
                self.run_one()
        except:
            traceback.print_exc()
            os._exit(1)

    def run_one(self):
        print('opening', self.filepath, '...', file=sys.stderr)
        with open(self.filepath, 'r') as f:
            print('opened', self.filepath, file=sys.stderr)
            for line in f:
                raw_voltage, raw_current = [x.strip() for x in line.split(',')]
                raw_voltage = int(raw_voltage)
                raw_current = int(raw_current)
                with self.pow_state.lock:
                    self.pow_state.voltage = self.to_voltage(raw_voltage)
                    self.pow_state.current = self.to_current(raw_current)

class CumpowerThread(threading.Thread):
    """Thread that updates cumpower"""

    def __init__(self, pow_state):
        super().__init__(name='CumpowerThread', daemon=True)
        self.pow_state = pow_state

    def run(self):
        try:
            self._run()
        except:
            traceback.print_exc()
            os._exit(1)

    def _run(self):
        while True:
            with self.pow_state.lock:
                self.pow_state.cumpower += (self.pow_state.voltage *
                        self.pow_state.current)
            time.sleep(1)

def main(args, prog=None):
    p = argparse.ArgumentParser(prog=prog)
    p.add_argument('--secret', default='hihihihihihihihi')
    p.add_argument('mlstream')
    p.add_argument('powstream')
    p.add_argument('server')
    p.add_argument('port', type=int)
    args = p.parse_args()
    wifi_client = EvalClient((args.server, args.port), args.secret)
    pow_state = PowerState()
    action_queue = queue.Queue(1)
    wifi_thread = EvalServerThread(wifi_client, pow_state, action_queue)
    ml_thread = MlThread(args.mlstream, action_queue)
    pow_thread = PowThread(args.powstream, pow_state)
    cumpower_thread = CumpowerThread(pow_state)
    wifi_thread.start()
    ml_thread.start()
    pow_thread.start()
    cumpower_thread.start()
    wifi_thread.join()
    ml_thread.join()
    pow_thread.join()
    cumpower_thread.join()

if __name__ == '__main__':
    main(sys.argv[1:], sys.argv[0])


import great_expectations as ge
from pydantic import BaseModel

class Chapter(BaseModel):
    id: int
    work_id: str
    section_number: int
    chapter_number: int
    description: str

# url: str = f"postgresql://user:secret@localhost:5432/gxshakezz?options=-csearch_path=shakespeare"
url: str = f"postgresql://user:secret@localhost:5432/gxshakezz"

_context: ge.DataContext = ge.get_context()

# _context.sources.add_postgres(name='gxshakezz', connection_string=str(url))

_datasource = _context.sources.add_sql(name="my_gx", connection_string=url)
# _datasource = _context.get_datasource("my_gx")
_table_asset = _datasource.add_table_asset(name="my_gx_asset", table_name="chapter")
#
_br = _table_asset.build_batch_request()
#
_batches = _datasource.get_batch_list_from_batch_request(_br)
#

_b = _batches[0]
#
_b.head()

# play with expectations
_validator = _context.get_validator(
    datasource_name="my_gx", data_asset_name="my_gx_asset"
)
_validator.expect_column_values_to_not_be_null("work_id")

_validator.list_available_expectation_types()


import json
from multiprocessing import Process
from multiprocessing.pool import ThreadPool
from pathlib import Path

import urllib3

from models.downloader import Downloader

urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)

def check_metadata(metadata):
    for course in metadata:
        if len(course.get("lectures")) == 0:
            metadata.remove(course)
    Path('metadata.json').write_text(json.dumps(metadata, indent=4))
    print("Cleaning metadata on './metadata.json'")

def main():

    metadata = []
    loader = Downloader()

    if Path('metadata.json').exists():
        metadata = json.loads(Path('metadata.json').read_text())
        if len(metadata) > 0:
            for course in metadata:
                if len(course.get('lectures')) > 0:
                    print(f'#{course.get("course_id")} - {course.get("course_title")} is already loading, skipping...')
                    continue
                else:
                    check_metadata(metadata)
                    metadata = json.loads(Path('metadata.json').read_text())
                    loader.download_metadata()
    else:
        loader.download_metadata()

    for course in metadata:
        p = Process(target=loader.download_course, args=(course,))
        p.start()
        p.join()

if __name__ == "__main__":
    main()


# 创建Excel表格
# 1、安装openpyxl
# `pip install openpyxl`
# 官方文档 [](https://openpyxl.readthedocs.io/en/stable/)
# 官方中文文档 https://openpyxl-chinese-docs.readthedocs.io/zh_CN/latest/tutorial.html
# 参考文档 [最详细的Excel模块Openpyxl教程]https://zhuanlan.zhihu.com/p/342422919

from openpyxl.styles import Alignment
from openpyxl import Workbook
from openpyxl.styles import Font

from copy import copy

def createExcel(excelName, titles, posts):
    wb = Workbook()

    # grab the active worksheet
    ws = wb.active

    # 1、添加数据
    # for title in titles:
    # 1.1 添加第一行
    ws.append(titles)
    # 1.2 添加后续行
    for post in posts:
        ws.append([post.nickname, post.create_time, post.link])
    # 2、设置样式
    # 设置单个列宽
    ws.column_dimensions['A'].width = 15.0
    ws.column_dimensions['B'].width = 30.0
    ws.column_dimensions['C'].width = 15.0
    ws.column_dimensions['D'].width = 15.0

    for col in ws.columns:
        for cell in col:
            # openpyxl styles aren't mutable,
            # so you have to create a copy of the style, modify the copy, then set it back
            if cell.column == 3:
                if cell.row > 1:
                    # 拿到指定的cell,设置样式
                    cell.hyperlink = cell.value # 设置为超链接
                    cell.font = Font(color='003366FF') # 设置字体颜色
            # 设置为居中
            alignment_obj = copy(cell.alignment)
            alignment_obj.horizontal = 'justify'
            alignment_obj.vertical = 'center'
            alignment_obj.wrap_text = True
            cell.alignment = alignment_obj

    # Save the file
    wb.save(excelName)

class Post:
    nickname = ""
    create_time = ""
    link = "http://www.baidu.com"

post1 = Post()
post1.nickname = "张三"
post1.create_time = "2022-10-29 19:00:00"

post2 = Post()
post2.nickname = "李四"
post2.create_time = "2022-10-29 19:00:00"

createExcel("table.xlsx", ["姓名","发布时间","链接"], [post1, post2])


from thop import profile
import torch
from archs.BSRN_arch import BSRN as net

model = net()
input = torch.randn(1, 3, 320, 180)

macs, params = profile(model, inputs=(input, ))

print("Multi-adds[G] ")
print(macs/1e9)
print("Parameters [K]")
print(params/1e3)


"""
demo04_load.py  模型读取
"""
import numpy as np
import sklearn.linear_model as lm
import matplotlib.pyplot as mp
import sklearn.metrics as sm
import pickle

x ,y = np.loadtxt('../ml_data/single.txt',
	delimiter=',', usecols=(0,1),
	unpack=True)
# 把x改为n行1列  这样才可以作为输入交给模型训练
x = x.reshape(-1, 1)
# 从文件中加载模型对象
with open('../ml_data/linear.pkl', 'rb') as f:
	model = pickle.load(f)
	print('load success.')

pred_y = model.predict(x)

# 评估回归模型的误差
# 平均绝对值误差  1/m∑|预测输出-真实输出|
print(sm.mean_absolute_error(y, pred_y))
# 平均平方误差  sqrt(1/m∑(预测输出-真实输出)^2)
print(sm.mean_squared_error(y, pred_y))
# 中位数绝对值误差  median(|预测输出-真实输出|)
print(sm.median_absolute_error(y, pred_y))
# r2得分 (0,1]的一个分值,分数越高,误差越小
print(sm.r2_score(y, pred_y))

mp.figure('Linear Regression', facecolor='lightgray')
mp.title('Linear Regression', fontsize=18)
mp.xlabel('X', fontsize=16)
mp.ylabel('Y', fontsize=16)
mp.tick_params(labelsize=12)
mp.grid(linestyle=':')
mp.scatter(x, y, s=60, c='dodgerblue',
	label='Points')
mp.plot(x, pred_y, c='orangered', linewidth=2,
	label='Regression Line')
mp.legend()
mp.show()


import time
from flask import Blueprint, request, current_app
from pydat.api.controller.exceptions import ClientError, ServerError
from pydat.api import elasticsearch_handler as es_handler
from pydat.api import flask_cache
from pydat.core.elastic.exceptions import (
    ESConnectionError,
    ESQueryError,
    ESNotFoundError,
)
from urllib import parse
import socket
from pydat.api.controller import whois_shared

whoisv2_bp = Blueprint("whoisv2", __name__)

def valid_size_offset(chunk_size, offset):
    """Validates chunk size and offset

    Args:
        chunk_size (int): Size of data chunk to split total data into
        offset (int): Number of chunks to offset start by

    Raises:
        ClientError: chunk_size and/or offset are not integers
        ClientError: chunk_size and/or offset are not valid integers
    """
    if not isinstance(chunk_size, int):
        raise ClientError(f"Chunk size {chunk_size} is not an integer")
    if not isinstance(offset, int):
        raise ClientError(f"Offset {offset} is not an integer")

    error = None
    if chunk_size < 1:
        error = f"Invalid chunk size {chunk_size}"
    elif offset < 0:
        error = f"Invalid offset {offset}"
    if error is not None:
        raise ClientError(error)

# Metadata
@whoisv2_bp.route("/metadata")
@whoisv2_bp.route("/metadata/<version>")
@flask_cache.cached()
def metadata(version=None):
    """Retrieves metadata for all or a specific versions

    Args:
        version (float, optional): Specific version to find metadata for.
                                    Defaults to None.

    Returns:
        dict: Details for application metadata
    """
    try:
        results = whois_shared.metadata(version)
    except RuntimeError as e:
        raise ServerError(e)
    except ESQueryError:
        raise ServerError("Unable to query Elasticsearch backend")
    except ESNotFoundError:
        raise ClientError(
            "Unable to find metadata with that version", status_code=404
        )

    return {'metadata': results}

# Resolve
@whoisv2_bp.route("/resolve/<domain>")
def resolve(domain):
    """Attempts to resolve provided domain name

    Args:
        domain (str): Specific domain name to resolve

    Raises:
        ClientError: Domain name could not be resolved
        ServerError: Failed to communicate with socket

    Returns:
        dict: Hostnames and IPs for domain
    """
    domain = parse.unquote(domain)

    if current_app.config.get("DISABLERESOLVE", False):
        raise ClientError("Active resolution disabled", status_code=400)

    try:
        hostname, aliaslist, iplist = socket.gethostbyname_ex(domain)
    except (socket.herror, socket.gaierror):
        raise ClientError(f"Domain name {domain} couldn't be resolved")
    except OSError as e:
        raise ServerError(str(e), 504)

    hostnames = [hostname]
    for alias in aliaslist:
        hostnames.append(alias)

    return {"hostnames": hostnames, "ips": iplist}

# Domains
@whoisv2_bp.route("/domains/diff", methods=["POST"])
def domains_diff():
    """Compares the keys and values between the two versions

    Args:
        domainName (str): Specific domain name to search
        v1 (float): A valid version of domainName
        v2 (float): Another valid version to compare v1 to

    Raises:
        ClientError: Input body must be JSON
        ClientError: Domain name must be provided
        ClientError: Two versions must be provided

    Returns:
        dict: Diff results from comparing keys and values of v1 and v2
    """
    if not request.is_json:
        raise ClientError("Wrong format, JSON required")

    json_data = request.get_json()
    try:
        domain = json_data["domain"]
    except KeyError:
        raise ClientError("A domain name must be provided")
    try:
        version1 = json_data["version1"]
        version2 = json_data["version2"]
    except KeyError:
        raise ClientError("Two versions must be provided")

    return whois_shared.diff(domain, version1, version2)

@whoisv2_bp.route("/domain", methods=["POST"])
def domain():
    """Specific search on a valid search field. Requires a value

    Raises:
        ClientError: Input provided in not in JSOn format
        ClientError: Value must be provided
        ClientError: Version is not a int
        ClientError: Invalid search was provided
        ServerError: Unable to connect to search engine
        ServerError: Unexpected issue when requesting results
        ServerError: Failed to process results
        ClientError: Offset exceeded total results

    Returns:
        dict: Domain hits that match search following the offset and size
    """
    if not request.is_json:
        raise ClientError("Wrong format, JSON required")

    json_data = request.get_json()
    try:
        value = json_data["value"]
    except KeyError:
        raise ClientError("Value is required")

    version = json_data.get("version", None)
    try:
        if version:
            version = int(version)
    except ValueError:
        raise ClientError(f"Version {version} is not an integer")
    chunk_size = json_data.get("chunk_size", 50)
    offset = json_data.get("offset", 0)
    valid_size_offset(chunk_size, offset)

    value = parse.unquote(value)

    try:
        search_results = es_handler.search(
            'domainName', value, filt=None, low=version, versionSort=True
        )
    except ValueError:
        raise ClientError(f"Invalid domain name {value}")
    except ESConnectionError:
        raise ServerError("Unable to connect to search engine")
    except ESQueryError:
        raise ServerError("Unexpected issue when requesting search")
    except RuntimeError:
        raise ServerError("Failed to process results")

    # Return results based on chunk size and offset
    start = offset * chunk_size
    if start > 0 and start >= search_results["total"]:
        raise ClientError(
            f"Offset {offset} is too high for {search_results['total']} hits"
        )
    end = start + chunk_size
    if end > search_results["total"]:
        end = search_results["total"]
    return {
        "total": search_results["total"],
        "chunk_size": chunk_size,
        "offset": offset,
        "results": search_results["data"][start:end],
    }

@whoisv2_bp.route("/query", methods=["POST"])
def query():
    """Advanced search allowing for flexible domain searches.
        Allows sorting of data

    Raises:
        ClientError: Wrong format, JSON required
        ClientError: Query is required
        ClientError: Invalid sort key provided
        ClientError: Invalid sort direction provided
        ClientError: Invalid search query
        ServerError: Unable to connect to search engine
        ServerError: Unexpected issue when requesting results
        ServerError: Failed to process results
        ClientError: Offset exceeded total results

    Returns:
        [type]: [description]
    """
    if not request.is_json:
        raise ClientError("Wrong format, JSON required")

    json_data = request.get_json()
    try:
        query = json_data["query"]
    except KeyError:
        raise ClientError("Query is required")

    # TODO FIXME Allow sorting based on any field
    allowed_sort_keys = [
        "domainName",
        "registrant_name",
        "contactEmail",
        "standardRegCreatedDate",
        "registrant_telephone",
        "dataVersion",
        "_score",
    ]

    chunk_size = json_data.get("chunk_size", 50)
    offset = json_data.get("offset", 0)
    valid_size_offset(chunk_size, offset)
    unique = json_data.get("unique", False)
    sort_keys = json_data.get("sort_keys", [])

    skip = offset * chunk_size
    # handle sort_key
    sort = []
    for sort_key in sort_keys:
        if 'name' not in sort_key:
            raise ClientError(
                "Unable to find required 'name' field in sort_key")
        elif sort_key['name'] not in allowed_sort_keys:
            raise ClientError(f"Invalid sort key {sort_key['name']} provided")

        if 'dir' not in sort_key:
            raise ClientError(
                "Unable to find required 'dir' field in sort_key")
        elif sort_key['dir'] not in ['asc', 'desc']:
            raise ClientError(
                "Sort key 'dir' field must be 'asc' or desc'")

        sort.append((sort_key['name'], sort_key['dir']))
    if not sort:
        sort = None

    try:
        search_results = es_handler.advanced_search(
            query, skip, chunk_size, unique, sort=sort
        )
    except ValueError:
        raise ClientError(f"Invalid search query {query}")
    except ESConnectionError:
        current_app.logger.exception("ESConnectionError")
        raise ServerError("Unable to connect to search engine")
    except ESQueryError:
        current_app.logger.exception("ESQueryError")
        raise ServerError("Unexpected issue when requesting search")
    except RuntimeError:
        current_app.logger.exception("RuntimeError")
        raise ServerError("Failed to process results")
    except Exception:
        current_app.exception("Unhandled Exception")
        raise ServerError("Unexpected issue handling query")

    if skip > 0 and skip >= search_results["total"]:
        raise ClientError(f"Offset {offset} is too high")

    return {
        "total": search_results["total"],
        "results": search_results["data"],
        "chunk_size": chunk_size,
        "offset": offset,
    }

@whoisv2_bp.route("/stats", methods=["GET"])
@flask_cache.cached(timeout=3600)
def stats():
    """Get cluster stats
    """

    try:
        cstats = es_handler.cluster_stats()
    except ESConnectionError:
        raise ServerError("Unable to connect to search engine")
    except ESQueryError:
        raise ServerError("Unexpected issue when requesting search")

    return {
        "stats": cstats,
        "cache_time": time.time()
    }

@whoisv2_bp.route('/info', methods=["GET"])
@flask_cache.cached(timeout=60)
def info():
    """Get cluster info
    """

    cluster_info = {}

    try:
        cluster_info['records'] = es_handler.record_count()
        cluster_info['health'] = es_handler.cluster_health()
        cluster_info['last'] = es_handler.last_version()
        cluster_info['last_update'] = es_handler.last_update()
    except ESConnectionError:
        raise ServerError("Unable to connect to search engine")
    except ESQueryError:
        raise ServerError("Unexpected issue when requesting search")
    except RuntimeError:
        raise ServerError("Failed to process results")

    return cluster_info


import random
from argparse import ArgumentParser
from copy import deepcopy
from os.path import join

import numpy as np
import torch
from sklearn.metrics import accuracy_score, precision_recall_fscore_support
from transformers import EvalPrediction, BertModel, BertConfig, BertTokenizerFast
from transformers.trainer_utils import set_seed

from configs import ModelConfig, mnist_config
from models import Classifier
from models.linear_bert import LinBertSelfAttention
from models.orig_bert import PosBiasBertSelfAttention

def set_seed_(seed: int):
    random.seed(seed)
    np.random.seed(seed)
    set_seed(seed)

def compute_metrics(pred: EvalPrediction):
    labels = pred.label_ids
    preds = pred.predictions.argmax(-1)
    precision, recall, f1, _ = precision_recall_fscore_support(labels, preds, average='macro')
    accuracy = accuracy_score(labels, preds)
    return {
        "f1": f1,
        "recall": recall,
        "accuracy": accuracy,
        "precision": precision,
    }

def parse_model_config(arg_parser: ArgumentParser) -> ModelConfig:
    arg_parser.add_argument("--is_linear", action='store_true')
    arg_parser.add_argument("--feature_map", choices=["elu", "exp", "dpfp", "favor"], default="elu")
    arg_parser.add_argument("--pos_bias_type", choices=["fft", "naive", "fft_2d", "naive_2d"], default=None)
    arg_parser.add_argument("--bias_base_type", choices=["full", "symmetric"], default=None)
    args = arg_parser.parse_args()

    return ModelConfig(
        is_linear=args.is_linear,
        feature_map=args.feature_map,
        pos_bias_type=args.pos_bias_type,
        bias_base_type=args.bias_base_type
    )

def _copy_weights(input_, target):
    input_.weight.data = target.weight.data
    input_.bias.data = target.bias.data

def _copy_self_attn(self_attn, target_self_attn):
    _copy_weights(
        input_=self_attn.query,
        target=target_self_attn.query
    )
    _copy_weights(
        input_=self_attn.value,
        target=target_self_attn.value
    )
    _copy_weights(
        input_=self_attn.key,
        target=target_self_attn.key
    )

def make_attn_linear(model: BertModel, config: BertConfig):
    tmp_model = deepcopy(model)

    for i, _ in enumerate(tmp_model.bert.encoder.layer):
        model.bert.encoder.layer[i].attention.self = LinBertSelfAttention(config)

        _copy_self_attn(
            self_attn=model.bert.encoder.layer[i].attention.self,
            target_self_attn=tmp_model.bert.encoder.layer[i].attention.self
        )
    return model

def add_pos_bias(model: BertModel, config: BertConfig):
    tmp_model = deepcopy(model)

    for i, _ in enumerate(tmp_model.bert.encoder.layer):
        model.bert.encoder.layer[i].attention.self = PosBiasBertSelfAttention(config)

        _copy_self_attn(
            self_attn=model.bert.encoder.layer[i].attention.self,
            target_self_attn=tmp_model.bert.encoder.layer[i].attention.self
        )
    return model

def freeze_weights(model: BertModel):
    for param in model.parameters():
        param.requires_grad = False
    for i, _ in enumerate(model.bert.encoder.layer):
        for param in model.bert.encoder.layer[i].attention.parameters():
            param.requires_grad = True
    return model

def construct_model(
    is_linear: bool = False,
    feature_map: str = "elu",
    pos_bias_type: str = None,
    shape: int = None
):
    if pos_bias_type is not None:
        model_config = ModelConfig(
            is_linear=is_linear,
            feature_map=feature_map,
            pos_bias_type=pos_bias_type,
            bias_base_type="full"
        )
    else:
        model_config = ModelConfig(
            is_linear=is_linear,
            feature_map=feature_map,
        )
    data_path = "data"
    dataset_name = "mnist"
    seed = 9
    set_seed_(seed)

    output_path = join(data_path, dataset_name)
    vocab_path = join(data_path, dataset_name)
    tokenizer = BertTokenizerFast.from_pretrained(vocab_path)
    vocab_size = tokenizer.vocab_size
    config, args = mnist_config(
        dataset_name=dataset_name,
        output_path=output_path,
        seed=seed,
        is_test=False,
        x_shape=shape,
        y_shape=shape,
        n_channels=1,
        vocab_size=vocab_size,
        model_config=model_config
    )

    config.hidden_size = 256
    config.num_layers = 6
    model = Classifier(config=config)
    inputs = torch.LongTensor(6, config.max_position_embeddings).random_(0, vocab_size)
    return model, inputs


import re  # expressões regulares
import requests  # permitirá enviar solicitações http
from bs4 import BeautifulSoup  # extrai dados de arquivos html e xml
from configparser import ConfigParser  # para manipulação de arquivos INI

# Adicionei manualmente uma chave ('dados') dentro do arquivo .ini para que o interpretador localizar as informações

# Aqui ele usa o ConfigParser para leitura do arquivo solicitado
config = ConfigParser()
config.read('desafio.ini')

# Captura a informação na seção (criada) 'dados' e chave 'url', 'inicio', 'fim'
url = (config.get('dados', 'url'))
inicio = (config.get('dados', 'inicio'))
fim = (config.get('dados', 'fim'))

# Concatena a url com o nome do candidato
if '=' in url:
    new_url = url + 'alison'
    url = new_url

# Conteúdo da página do URL do site
page = requests.get(url)

# Função para remover as tags
def remove_tags(html):
    # analisa o conteúdo html
    soup = BeautifulSoup(html, "html.parser")  # cria um objeto chamado soup que está interpretando o documento HTML

    for data in soup(['script']):
        # Remove tags
        data.decompose()

    # Retorna dados removendo as tags e recuperando o conteúdo
    return ' '.join(soup.stripped_strings)

# Imprime os dados extraídos sem tags
# usa a função 'remove_tags' para remover as tags
sem_tags = remove_tags(page.content)

# localiza todas as palavras que iniciam com o valor de "inicio" (con)
start = re.findall(r'\b' + inicio + '\w+', sem_tags, flags=re.I)

print("HTML TRATADO: ")
print(sem_tags)

palavras = []  # lista para receber as palavras buscadas

# localiza todas as palavras terminam com o conteúdo de 'fim' (e)
for i in start:  # separa o conteúdo em uma lista para realizar a busca
    if i.endswith(fim):  # condição de busca do conteúdo das variáveis "fim"
        palavras.append(i)

print(f'\nLISTA DE PALAVRAS: {palavras}')  # imprime a lista das palavras encontradas
print(f'A LISTA CONTÉM: {len(palavras)} PALAVRAS')  # imprime a quantidade de letras encontradas