print('Loading...')

import matplotlib.pyplot as plt
from matplotlib.ticker import MaxNLocator, MultipleLocator, AutoMinorLocator
from matplotlib.axes import Axes
import pandas as pd
import numpy as np
import time
import math
import seaborn as sns
from IPython.display import clear_output, display, IFrame
from chord import Chord
# UI
import ipywidgets as widgets
from tqdm import tnrange, tqdm_notebook

# other fuctions
from functions import get_discogs
from functions import get_lyrics
from functions import plot_wordcloud
from functions import sentiment_analysis
from functions import advanced_analytics

# word processing
import re
import os
import string
import nltk
nltk.download('punkt')
from nltk.corpus import stopwords
nltk.download('stopwords')
stop_words = stopwords.words('english')
clear_output()

import warnings
warnings.filterwarnings('ignore')

#-------------------------------------------------------------------------------
# GLOBAL VARIABLES

# default selected tab in the UI
selected_section = 0
selection_tab_of_section_1 = 0
selection_tab_of_section_2 = 0
selection_tab_of_section_3 = 0
selection_tab_of_section_4 = 0
selection_tab_of_section_5 = 0

# global var for discog
discog = ''
discog_store = []

#global var for artist
artist = ''

colour_palette = {'blue': '#4878D0',
 'orange': '#EE854A',
 'purple': '#956CB4',
 'red': '#D9363E',
 'green': '#6ACC64',
 'purple': '#956CB4',
 'brown': '#8C613C',
 'pink': '#DC7EC0',
 'light grey': '#c4bba5',
 'grey': '#797979',
 'tan': '#D5BB67'}

descriptions_colour = colour_palette.get('grey')

#plt.gca()
plt.style.use('seaborn')

def no_selections_warning():
    # clear previous output
    clear_output()
    display(
            widgets.HTML(
                value=f'''

                      No artist/discography selected. Make sure selections
                      are made before running visualisations.
                      ''',
                layout=widgets.Layout(width="100%"))
    )
    global selected_section
    selected_section = 0
    # set selected  tab
    global selection_tab_of_section_1
    selection_tab_of_section_1 = 0 if artist == '' else 1
    # display UI
    UI()

#-------------------------------------------------------------------------------
# UI SECTION 1 variables and functions
#-------------------------------------------------------------------------------
#-------------------------------------------------------------------------------
# SECTION 1 | TAB 1 variables and functions

#global widget to type artist name
artist_input = widgets.Text()

# define function to overwrite artist from user input
def set_artist(x):
    global artist
    artist = x

# function to adapt long texts for display in charts

def adapt_title(text):
    adapted_text = []
    while len(text) > 0:
        text_slice = text[:21]
        last_char = 0 if text_slice == text else min(
            text_slice[::-1].find(' '), len(text_slice)
        )
        adapted_text.append(
            text_slice[:None if last_char == 0 else -last_char]
        )
        text = text[21 - last_char:]
    return '\n'.join(adapted_text)

# function to run at click of the button
def get_discography(x):
    #clear previous output and print message
    clear_output()
    print("Looking for best match for '" + str(artist_input.value) +"'")
    time.sleep(2)
    # if already exists, load data from discog_store.csv
    global discog_store
    try:
        discog_store = pd.read_csv('discog_store.csv')
    except:
        pass
    # try overwrite artist with artist found as best match from DISCOGS
    global artist
    try:
        set_artist(
            get_discogs.getArtistID(artist_input.value)[1]
        )
    # if no match found from discogs, stop and print a message
    except:
        display(
            widgets.HTML(
                value=f'''
                          No match found,\
                          please try again
                          ''',
                layout=widgets.Layout(width="100%"))
        )
        #print(widgets"No match found, please try again")
        return UI()
    # if match found, print the corrected artist name
    print("Retrieving discography for '" + artist + "'")
    time.sleep(1)
    # get discogs - check if in csv, else use DISCOGS API
    global discog
    if len(discog_store) == 0:
        discog = get_discogs.getArtistData(artist)
    elif artist in discog_store['ARTIST_NAME'].unique():
        discog = discog_store[discog_store['ARTIST_NAME'] == artist].copy()
    else:
        discog = get_discogs.getArtistData(artist)
#-------------------------------------------------------------------------------
    discog['YEAR_ALBUM'] = "[" + discog['YEAR'].astype(str) + "] " \
                            + discog['ALBUM']

    # look for duplicates and overwrite their names (issue #51)
    for album in discog['YEAR_ALBUM']:
        unique_album_ids = discog[discog['YEAR_ALBUM'] == album]\
                                               ['DISCOGS_ALBUM_ID'].unique().tolist()
        if len(unique_album_ids) > 1:
            counter = 1
            for album_id in unique_album_ids:
                discog.loc[discog['DISCOGS_ALBUM_ID'] == album_id, 'YEAR_ALBUM'] = \
                discog[discog['DISCOGS_ALBUM_ID'] == album_id]\
                                      ['YEAR_ALBUM'] + " (" + str(counter) + ")"
                counter += 1

    # make version for displaying in charts (with line breaks)
    discog['YEAR_ALBUM_DISPLAY'] = discog['YEAR_ALBUM'].astype(str).apply(adapt_title)
    # clear previous output
    clear_output()
    # set selected section
    global selected_section
    selected_section = 0
    # set selected  tab
    global selection_tab_of_section_1
    selection_tab_of_section_1 = 1
    # set album selecor content
    # overwrite album_filter with current selection
    global album_filter
    set_album_filter(
        discog[discog['EXCLUDE_ALBUM'] != True]['YEAR_ALBUM'].unique().tolist()
    )
    global album_selector
    set_album_selector(
        discog['YEAR_ALBUM'].unique().tolist(),
        album_filter
    )

    # display UI
    UI()

#-------------------------------------------------------------------------------
# SECTION 1 | TAB 2 variables and functions

#create album_filter
album_filter = []

# func to set album filter
def set_album_filter(x):
    global album_filter
    album_filter = x

discog_filtered = []

options = []
def multi_checkbox_widget(albums, albums_filter):
    options_dict = {album: widgets.Checkbox(description=album,
                                            value=False,
                                            layout={'margin' : '-2px',
                                                    'width' : 'initial'})
                    for album in albums}
    options = [options_dict[album] for album in albums]
    for option in options:
        if option.description in albums_filter:
            option.value = True
    options_widget = widgets.VBox(options,
                                  layout = {'overflow': 'scroll',
                                            'max_height': '300px',
                                            'width' : 'initial'})
    multi_select = widgets.VBox([options_widget],
                                layout=widgets.Layout(padding = "20px"))
    return multi_select

def set_album_selector(options, options_filter):
    global album_selector
    album_selector = multi_checkbox_widget(options,options_filter)

album_selector = multi_checkbox_widget([],[])
import time

def select_deselect_all(x):
    #clear previous output
    clear_output()
    global album_selector
    if album_selector.children[0].children[0].value == True:
        set_album_selector(discog['YEAR_ALBUM'].unique().tolist(), [])
    else:
        set_album_selector(discog['YEAR_ALBUM'].unique().tolist(),
                           discog['YEAR_ALBUM'].unique().tolist())
    # select this tab
    global selected_section
    selected_section = 0
    # select sub tab
    global selection_tab_of_section_1
    selection_tab_of_section_1 = 1

    # display UI
    UI()

def apply_selection(x):
    global discog
    global discog_store
    try:
        discog_store = pd.read_csv('discog_store.csv')
    except:
        pass
    #apply user selections, overwrite album_filter with current selection
    global album_selector
    global album_filter
    selected = []
    # read user input
    for album in album_selector.children[0].children:
        # overwrite EXCLUDE_ALBUM flag
        discog.loc[discog['YEAR_ALBUM'] == album.description,
                   'EXCLUDE_ALBUM'] = not album.value
        #make a list of selected albums
        if album.value == True:
            selected.append(album.description)
    # set filter = list of selected albums
    set_album_filter(selected)
    # reset selector to keep the actual selections
    set_album_selector(discog['YEAR_ALBUM'].unique().tolist(), selected)

    #clear previous output
    clear_output()

    # overwrite .CSV and update flags
    if len(discog_store) == 0:
        discog_store = discog.copy(deep=True)
    else:
        if artist not in discog_store['ARTIST_NAME'].unique():
            discog_store = discog_store.append(discog,
                                               ignore_index=True,
                                               sort=False)
        else:
            for i,r in discog.iterrows():
                # overwrite EXCLUDE_ALBUM flag
                discog_store.loc[discog_store['YEAR_ALBUM'] == r['YEAR_ALBUM'],
                                 'EXCLUDE_ALBUM'] = r['EXCLUDE_ALBUM']

    try:
        discog_store.to_csv('discog_store.csv', index = False)
    except PermissionError:
        clear_output()
        display(
            widgets.HTML(
                value=f'''
                      Permission denied: \
                      make sure 'discog_store.csv' is closed and try again
                      ''',
                layout=widgets.Layout(width="100%")
            )
        )
        return UI()

    #get lyrics
    print('Getting the lyrics, please hold on')
    # if there are any albums and songs to be included
    if len(discog_store[(discog_store['ARTIST_NAME'] == artist)
                    & (discog_store["EXCLUDE_ALBUM"] == False)
                    & (discog_store["EXCLUDE_SONG"] == False)]) > 0:
        # and LYRICS colum does not exist yet
        if "LYRICS" not in discog_store.columns:
            # get lyrics for all records of the artist (flags considered)
            lyrics_data = get_lyrics.getLyrics(
                discog_store[discog_store['ARTIST_NAME'] == artist])
        # otherwise if there are any lyrics in current selection that are empty
        elif len(discog_store[(discog_store['ARTIST_NAME'] == artist)
                              & (discog_store["EXCLUDE_ALBUM"] == False)
                              & (discog_store["EXCLUDE_SONG"] == False)
                              &(~discog_store['LYRICS'].notnull())]) > 0:
            # look only for lyrics that have not yet been collected
            lyrics_data = get_lyrics.getLyrics(
                discog_store[(discog_store['ARTIST_NAME'] == artist) \
                             &(~discog_store['LYRICS'].notnull())])
        else:
            lyrics_data = []
    else:
        lyrics_data = []

    # if any new lyrics were retrieved
    if len(lyrics_data) != 0:
        # do sentiment analysis
        print('Analyzing lyric sentiment...')
        sentiment_data = sentiment_analysis.sentimentAnalyser(
            lyrics_data, artist
        )

        print('Processing and cleaning, please hold on')
        # iterate through results and populate the columns in main discog store
        # add lyrics
        for i,r in lyrics_data.iterrows():
            discog_store.loc[
                (discog_store['ARTIST_NAME'] == r['ARTIST_NAME']) &
                (discog_store['TRACK_TITLE'] == r['TRACK_TITLE']),
                "LYRICS"] = r['LYRICS']
        # add sentiment data
        for i,r in sentiment_data.iterrows():
            discog_store.loc[
                (discog_store['ARTIST_NAME'] == r['ARTIST_NAME']) &
                (discog_store['TRACK_TITLE'] == r['TRACK_TITLE']),
                "SENTIMENT_PCT_NEGATIVE"] = r['SENTIMENT_PCT_NEGATIVE']
            discog_store.loc[
                (discog_store['ARTIST_NAME'] == r['ARTIST_NAME']) &
                (discog_store['TRACK_TITLE'] == r['TRACK_TITLE']),
                "SENTIMENT_PCT_NEUTRAL"] = r['SENTIMENT_PCT_NEUTRAL']
            discog_store.loc[
                (discog_store['ARTIST_NAME'] == r['ARTIST_NAME']) &
                (discog_store['TRACK_TITLE'] == r['TRACK_TITLE']),
                "SENTIMENT_PCT_POSITIVE"] = r['SENTIMENT_PCT_POSITIVE']
            discog_store.loc[
                (discog_store['ARTIST_NAME'] == r['ARTIST_NAME']) &
                (discog_store['TRACK_TITLE'] == r['TRACK_TITLE']),
                "SENTIMENT_COMPOUND_SCORE"] = r['SENTIMENT_COMPOUND_SCORE']
            discog_store.loc[
                (discog_store['ARTIST_NAME'] == r['ARTIST_NAME']) &
                (discog_store['TRACK_TITLE'] == r['TRACK_TITLE']),
                "SENTIMENT_GROUP"] = r['SENTIMENT_GROUP']

        # add column with lyrics with removed stopwords
        discog_store["LYRICS_CLEAN"] = discog_store['LYRICS'].astype(str).apply(
            lambda x: ' '.join(list(word for word in re.findall(
                r"[\w]+|[^\s\w]",x.lower()
            ) if word not in stop_words
                                   )))
        # remove punctuation
        discog_store["LYRICS_CLEAN"] = discog_store[
            'LYRICS_CLEAN'
        ].str.replace('[^\w\s] ','')

        #list unique clean words
        discog_store["LYRICS_CLEAN_UNIQUE"] = discog_store[
            'LYRICS_CLEAN'
        ].astype(str).apply(lambda x: list(set(x.split())))
        #count unique clean words
        discog_store["LYRICS_CLEAN_UNIQUE_COUNT"] = discog_store[
            'LYRICS_CLEAN_UNIQUE'
        ].apply(lambda x: len(x))
#-------------------------------------------------------------------------------

    # write the updated content
    try:
        discog_store.to_csv('discog_store.csv', index = False)
    except PermissionError:
        clear_output()
        display(
            widgets.HTML(
                value=f'''
                Permission denied: \
                make sure 'discog_store.csv' is closed and try again
                ''',
                layout=widgets.Layout(width="100%")
            )
        )
        return UI()

    clear_output()
    # select tab
    global selected_section
    selected_section = 0
    # select sub tab
    global selection_tab_of_section_1
    selection_tab_of_section_1 = 2

    #update discog_filtered variable

    ### read latest saved discog_store (reformats datatypes)
    discog_store = pd.read_csv('discog_store.csv')
    ### overwrite discog_filtered
    global discog_filtered
    discog_filtered = discog_store[
        (discog_store['ARTIST_NAME']==artist)\
        &(discog_store['YEAR_ALBUM'].isin(album_filter))
    ].copy()
    if len(discog_filtered) == 0:
        no_selections_warning()
    else:
        # populate sentiment chart dropdown with selected albums
        global sentiment_dropdown2
        sentiment_dropdown2 = widgets.Dropdown(
            options=discog_filtered['YEAR_ALBUM'].unique(),
            value=discog_filtered['YEAR_ALBUM'].unique()[0],
            description='Select Album:',
            disabled=False,)

        # display UI
        UI()

#-------------------------------------------------------------------------------
# SECTION 1 | TAB 3 variables and functions

# set bin_size var with default 10
bin_size = 10

#defining a slider for bin_size selector
period_selection_slider = widgets.IntSlider(
        value=bin_size,
        min=1,
        max=10,
        step=1,
        description='Period Size (# of years)',
        style = {'description_width': 'initial'},
        disabled=False,
        continuous_update=True,
        orientation='horizontal',
        readout=True,
        readout_format='d'
    )

# define function to overwrite bin_size from slider input
def set_bin_size(x):
    global bin_size
    bin_size = period_selection_slider.value
    #clear previous output
    clear_output()
    # select next section
    global selected_section
    selected_section = 1
    # select tab
    global selection_tab_of_section_2
    selection_tab_of_section_2 = 0
    # restart UI
    UI()

#-------------------------------------------------------------------------------
# UI SECTION 2 variables and functions
#-------------------------------------------------------------------------------
#-------------------------------------------------------------------------------
# SECTION 2 | TAB 1 variables and functions

def generate_period_bins(discog, bin_size):
    '''returns list of period bins starting 0th year of the decade
    of first release'''
    bins = []
    if bin_size == 1:
        start_year = int(discog['YEAR'].min())
        last_year = int(discog['YEAR'].max())
        year = start_year
        while year <= last_year:
            bins.append(str(year))
            year += bin_size
    else:
        start_year = int(str(discog['YEAR'].min())[:3]+'0')
        last_year = int(discog['YEAR'].max()) \
                    + int(str(100 - int(str(discog['YEAR'].max())[2:]))[-1])
        year = start_year
        while year <= last_year:
            bins.append(str(year) + '-' + str(year + bin_size-1))
            year += bin_size
    return bins

def unique_per_period(discog, column, bin_size):
    '''returns a dataframe with count of unique values within a column
    by defined bin size'''
    data = {'period' : generate_period_bins(discog, bin_size),
            column : []}
    if bin_size == 1:
        for period in data['period']:
            data[column].append(
                len(
                    discog[
                        discog['YEAR'].astype(int) == int(period)
                    ][column].unique()
                )
            )
    else:
        for period in data['period']:
            data[column].append(
                len(
                    discog[
                        (discog['YEAR'].astype(int) >= int(period[:4])) \
                        & (discog['YEAR'].astype(int) <= int(period[5:]))
                    ][column].unique()
                )
            )
        # filter out all but one period empty before first and after last record
    output = pd.DataFrame.from_dict(data)
    # flag record with and without data
    output.loc[output[column] == 0, 'data_flag'] = 0
    output.loc[output[column] > 0, 'data_flag'] = 1
    # chronological counter
    counter1=[]
    counter = 0
    for i in range(output.shape[0]):
        if i == output.shape[0]-1:
            counter = counter + output.iloc[i]['data_flag']
            counter1.append(counter)
        elif i == 0:
            counter = counter \
                      + output.iloc[i]['data_flag'] \
                      + output.iloc[i+1]['data_flag']
            counter1.append(counter)
        else:
            counter = counter + output.iloc[i+1]['data_flag']
            counter1.append(counter)
        output['counter1'] = pd.Series(counter1)
    # reverse - anti chronological counter
    output = output.iloc[::-1].reset_index(drop=True)
    counter2=[]
    counter = 0
    for i in range(output.shape[0]):
        if i == output.shape[0]-1:
            counter = counter + output.iloc[i]['data_flag']
            counter2.append(counter)
        elif i == 0:
            counter = counter \
                      + output.iloc[i]['data_flag'] \
                      + output.iloc[i+1]['data_flag']
            counter2.append(counter)
        else:
            counter = counter + output.iloc[i+1]['data_flag']
            counter2.append(counter)
        output['counter2'] = pd.Series(counter2)
    # reverse back
    output = output.iloc[::-1].reset_index(drop=True)
    # return filtered data
    return output[(output.counter1 > 0) & (output.counter2 > 0)]

def album_song_count_per_period(discog, bin_size):
    '''returns a merged dataframe by period
    with counts of albums and songs per period'''
    data_albums = unique_per_period(discog, 'YEAR_ALBUM', bin_size)
    data_songs = unique_per_period(discog, 'TRACK_TITLE', bin_size)
    return data_albums.merge(data_songs, on = 'period')

def add_period_column(discog, bin_size):
    '''adds column with period info without any other modifications'''
    period_col_data = []
    bins = generate_period_bins(discog, bin_size)
    if bin_size == 1:
        for i, r in discog.iterrows():
            period_col_data.append(r['YEAR'])
    else:
        for i, r in discog.iterrows():
            for period in bins:
                if int(r['YEAR']) >= int(period[:4]) \
                and int(r['YEAR']) <= int(period[5:]):
                    period_col_data.append(period)
    discog['period'] = period_col_data
    return discog

def plot_albums_songs_per_period_bar(discog, bin_size):
    '''plots the number of albums and songs per period'''

    width = 0.2
    data = album_song_count_per_period(
        discog,
        bin_size
    ).set_index('period')
    fig, ax1 = plt.subplots(figsize=(8,5))
    ax2 = ax1.twinx()  # instantiate a second axes sharing the same x-axis
    ax2.set_axisbelow(True)
    color = colour_palette.get('blue')

    ax1.set_ylabel('Number of songs', color=color)
    #ax1.plot(data.period, data.album, color=color)

    data['TRACK_TITLE'].plot(kind='bar',
                             color=color,
                             ax=ax1,
                             width=width,
                             position=1)

    ax1.tick_params(axis='y', labelcolor=color)
    ax1.tick_params(axis='x', labelrotation=45 if len(data.index) > 5 else 0)
    ax1.set_xlabel('Year' if bin_size == 1 else str(bin_size) + '-year period')

    # ax2 = ax1.twinx()  # instantiate a second axes that shares the same x-axis

    color = colour_palette.get('orange')
    ax2.grid(False)
    ax2.set_ylabel('Number of albums', color=color)  # x-label handled with ax1
    #ax2.plot(data.index.tolist(), data.track_title, color=color)
    ax2.tick_params(axis='y', labelcolor=color)

    fig.tight_layout()  # otherwise the right y-label may be slightly clipped
    data['YEAR_ALBUM'].plot(kind='bar',
                        color=color,
                        ax=ax2,
                        width=width,
                        position=0
                       )
    ax1.yaxis.set_major_locator(MaxNLocator(integer=True))
    ax2.yaxis.set_major_locator(MaxNLocator(integer=True))
    plt.title('Albums and Song Count by Period', fontsize=18)
    ax2.grid(False, axis='both')
    ax1.grid(False, axis='x')
    plt.show()

# function to run at click of the button_3
def show_basic_charts(x):
    if len(discog_filtered) == 0:
        no_selections_warning()
    else:
        global bin_size
        #clear previous output
        clear_output()
        # select current tab
        global selected_section
        selected_section = 1
        # select sub tab
        global selection_tab_of_section_2
        selection_tab_of_section_2 = 0

        # display UI
        UI()

        #display chart using the bin_size
        #plot_albums_songs_per_period(discog_filtered, bin_size)
        plot_albums_songs_per_period_bar(discog_filtered, bin_size)

#-------------------------------------------------------------------------------
# SECTION 2 | TAB 2 variables and functions

def violin_plot(discog, bin_size):
    data = add_period_column(discog, bin_size)
    # Draw Plot
    plt.figure(figsize=(8,5), dpi= 80)
    violinplot = sns.violinplot(x='period',
                   y='LYRICS_CLEAN_UNIQUE_COUNT',
                   data=data,
                   scale='width',
                   inner='quartile',
                   linewidth = 1.5,
                   cut=0,
                   palette=colour_palette.values())
    # rotate x axis labels
    if len(data['period'].unique()) > 5:
        for item in violinplot.get_xticklabels():
            item.set_rotation(45)
    # Decoration
    plt.title('Lexical Diversity', fontsize=18)
    plt.ylabel('Number of unique words (excl. stopwords)')
    plt.xlabel('Year' if bin_size == 1 else str(bin_size) + '-year period')
    plt.show()

def show_lexical_diversity(x):
    if len(discog_filtered) == 0:
        no_selections_warning()
    else:
        global bin_size
        #clear previous output
        clear_output()
        # select current tab
        global selected_section
        selected_section = 1
        # select sub tab
        global selection_tab_of_section_2
        selection_tab_of_section_2 = 1
        # display UI
        UI()
        #pirate_plot(discog_filtered, bin_size)
        violin_plot(discog_filtered, bin_size)

#-------------------------------------------------------------------------------
# SECTION 2 | TAB 3 variables and functions

# function to run at click of the button_show_wordclouds
def show_wordclouds(x):
    if len(discog_filtered) == 0:
        no_selections_warning()
    else:

        #clear previous output
        clear_output()
        print("WordClouds are rolling in...")
        global bin_size
        # select current tab
        global selected_section
        selected_section = 1
        # select sub tab
        global selection_tab_of_section_2
        selection_tab_of_section_2 = 2

        data = add_period_column(discog_filtered, bin_size)

        clear_output()
        # display UI
        UI()
        if wordcloud_by_selection_dropdown.value == 'period':
            plot_wordcloud.createWordCloud(
                data[~data['LYRICS_CLEAN'].isnull()], 'period'
            )
        elif wordcloud_by_selection_dropdown.value ==  'album':
            plot_wordcloud.createWordCloud(
                data[~data['LYRICS_CLEAN'].isnull()], 'YEAR_ALBUM'
            )

wordcloud_by_selection_dropdown = widgets.Dropdown(options=['period', 'album',],
                                                   value='period',
                                                   description='Display by:',
                                                   disabled=False,)

#-------------------------------------------------------------------------------
# UI SECTION 3 variables and functions
#-------------------------------------------------------------------------------
#-------------------------------------------------------------------------------
# SECTION 3 | TAB 1 variables and functions

def plot_albums_discogs_popularity(discog):
    '''plots Discogs registered owners and average ratings'''
    width = 0.2
    data = pd.pivot_table(discog,
                          index = 'YEAR_ALBUM_DISPLAY',
                          values = ['DISCOGS_PPL_HAVING', 'DISCOGS_AVG_RATING'],
                          aggfunc = 'max')

    fig, ax1 = plt.subplots(figsize=(max(8,min(15,len(data)+2)),5))

    color = colour_palette.get('orange')
    ax1.set_axisbelow(True)
    #ax1.yaxis.grid(True, which = 'major', linestyle = '--', color = '#d3d3d3')
    ax1.yaxis.grid(True)
    ax1.set_ylabel('Discogs owners', color=color)
    #ax1.plot(data.period, data.album, color=color)
    data['DISCOGS_PPL_HAVING'].plot(kind='bar',
                                   color=color,
                                   ax=ax1,
                                   width=width,
                                   position=1)
    ax1.tick_params(axis='y', labelcolor=color)
    ax1.tick_params(axis='x', labelrotation=45)
    ax1.set_xlabel('') # no label but adds spacing between charts
    xlabels = data.index.tolist()
    ax1.set_xticklabels(xlabels, ha='right')

    ax2 = ax1.twinx()  # instantiate a second axes that shares the same x-axis
    color = colour_palette.get('blue')
    ax2.set_ylabel('Discogs average rating', color=color)
    #ax2.plot(data.index.tolist(), data.track_title, color=color)
    ax2.tick_params(axis='y', labelcolor=color)

    fig.tight_layout()  # otherwise the right y-label may be slightly clipped
    data['DISCOGS_AVG_RATING'].plot(kind='bar',
                                   color=colour_palette.get('blue'),
                                   ax=ax2,
                                   width=width,
                                   position=0)
    #ax1.yaxis.set_major_locator(MaxNLocator(integer=True))
    #ax2.yaxis.set_major_locator(MaxNLocator(integer=True))
    #ax1.xticks(ha='right')
    ax2.grid(None)
    plt.title('Discogs Users - Owners and Average Ratings\n', fontsize=18)
    plt.show()
    print('\n')

def plot_albums_ratings(discog):
    threshold = discog['DISCOGS_AVG_RATING'].mean()
    data = pd.pivot_table(discog,index='YEAR_ALBUM_DISPLAY',
                          values=['DISCOGS_AVG_RATING'],
                          aggfunc = 'max')
    values = np.array(data['DISCOGS_AVG_RATING'].tolist())

    # plot it
    fig, ax = plt.subplots(figsize=(max(8,min(15,len(data)+2)),5))

    data['DISCOGS_AVG_RATING'].plot(kind='bar',
                                   color=colour_palette.get('purple'),
                                   ax=ax,
                                   width=0.5,
                                   position=1)
    ax.set_ylabel('Avg. Discogs user rating')
    ax.set_xlabel('') # no label but adds spacing between charts

    ax.tick_params(axis='x', labelrotation=45)
    xlabels = data.index.tolist()
    ax.set_xticklabels(xlabels, ha='right')

    # horizontal line indicating the threshold
    ax.plot([-1, len(data)], [threshold, threshold], "k--") #, color = 'b')

    plt.title('Average Discogs Users Rating by Album vs Index\n', fontsize=18)
    plt.show()
    print('\n')

def plot_albums_ratings_indexing(discog):
    threshold = discog['DISCOGS_AVG_RATING'].mean()
    data = pd.pivot_table(discog,index='YEAR_ALBUM_DISPLAY',
                          values=['DISCOGS_AVG_RATING'],
                          aggfunc = 'max')
    values = (np.array(data['DISCOGS_AVG_RATING'])-threshold).tolist()
    data['indexing'] = values

    # plot it
    fig, ax = plt.subplots(figsize=(max(8,min(15,len(data)+2)),5))
    data['positive'] = data['indexing'] > 0
    data['indexing'].plot(kind='bar',
                          color=data.positive.map(
                              {True: colour_palette.get('green'),
                               False: colour_palette.get('red')}
                          ),
                          ax=ax, width=0.5, position=1)

    ax.set_ylabel('Album Discogs rating vs. average')
    ax.set_xlabel('') # no label but adds spacing between charts

    ax.tick_params(axis='x', labelrotation=45)
    xlabels = data.index.tolist()
    ax.set_xticklabels(xlabels, ha='right')

    # horizontal line indicating the threshold
    ax.plot([-1, len(data)], [0, 0], "k--") #, color = 'b')

    plt.title('Average Discogs Users Rating by Album vs Index\n',
              fontsize=18)
    plt.show()

# function to run at click of the button
def show_discogs_users_charts(x):
    if len(discog_filtered) == 0:
        no_selections_warning()
    else:
        global bin_size

        # select current tab
        global selected_section
        selected_section = 2
        # select sub tab
        global selection_tab_of_section_3
        selection_tab_of_section_3 = 0

        #clear previous output
        clear_output()
        # display UI
        UI()

        plot_albums_discogs_popularity(discog_filtered)
        plot_albums_ratings(discog_filtered)
        plot_albums_ratings_indexing(discog_filtered)

#-------------------------------------------------------------------------------
# SECTION 3 | TAB 2 variables and functions

def create_chord_diag(df, column1, column2):
    '''
    Creating Chord diagram.
    @param - Dataframe
    @param - bin_size -> int [by default 10]

    @return - Chord_diagram using Chord Library
    column1 - refers charts, either album or track
              (BILLBOARD_TRACK_RANK, BILLBOARD_ALBUM_RANK)
    column2 - period or sentiment (sentiment buckets tracks,
              sentiment buckets for albums)
    '''
    # Creating period column
    global bin_size
    data = add_period_column(df, bin_size)
    artist_nam = df['ARTIST_NAME'].iloc[0]

    # Flag Charted/Uncharted tracks/albums
    data.loc[data[column1] >= 1.0
                   , "Charted_Uncharted"
                  ] = 'Charted'

    data.loc[~(data[column1] >= 1.0)
                   , "Charted_Uncharted"
                  ] = 'Uncharted'
    #Group By period and Chart_Unchart
    df_groupby = pd.pivot_table(
        data,
        index = [column2, 'Charted_Uncharted'],
        values = 'TRACK_TITLE' if column1 == 'BILLBOARD_TRACK_RANK' else 'ALBUM',
        aggfunc = lambda x: len(x.unique())
    ).reset_index()

    # Converting period to string to solve issue when using bin size 1
    df_groupby[column2] = df_groupby[column2].astype(str)

#     df_groupby = data[[column2
#                               , column1
#                               , "Charted_Uncharted"]
#                             ].groupby([column2, 'Charted_Uncharted']).nunique().reset_index()

    # Creating two pivot data
    pivot_data = df_groupby.pivot(
        index = column2,
        columns = 'Charted_Uncharted',
        values = 'TRACK_TITLE' if column1 == 'BILLBOARD_TRACK_RANK' else 'ALBUM'
    )
    pivot_data2 = df_groupby.pivot(
        index = 'Charted_Uncharted',
        columns = column2,
        values = 'TRACK_TITLE' if column1 == 'BILLBOARD_TRACK_RANK' else 'ALBUM'
    )

    # Appending two dataFrame
    df_final = pd.concat(
        [pivot_data, pivot_data2],
        sort = True).fillna(0).astype(int).sort_index()
    matrix = df_final.values.tolist()
    ls_col_nam = [col for col in df_final.columns]
#     {artist}

    # calculate whether to wrap labels or not
    # anticipate all albums/tracks may be charted or uncharted
    wrap_index = True
    if 'Uncharted' in pivot_data.columns and 'Charted'in pivot_data.columns:
        wrap_index = False if (
        (pivot_data['Uncharted'].sum() / pivot_data['Charted'].sum()) <= 0.2 or
        (pivot_data['Charted'].sum() / pivot_data['Uncharted'].sum()) <= 0.2
        ) else True
    if len(data[column2].unique()) > 5:
        wrap_index = False

    #if len(data[column2].unique()) < 5

    plot = Chord(matrix
                 , ls_col_nam
                 , padding=0.05
                 , width = 600 if wrap_index == True else 500
                 , margin= 10 if wrap_index == True else 80
                 , wrap_labels= wrap_index
                ).to_html()

    display(
        widgets.HTML(
            value=f'''

            Artist: {artist_nam}

''',
            layout=widgets.Layout(width="100%")
        )
    )
    display(
        IFrame(
            src="./out.html",
            width=1000,
            height=700
        )
    )

#------------------------------------------------------------------------------------------

# function to run at click of the button
def show_billboard_100_charts(x):
    if len(discog_filtered) == 0:
        no_selections_warning()
    else:

        global bin_size

        # select current tab
        global selected_section
        selected_section = 2
        # select sub tab
        global selection_tab_of_section_3
        selection_tab_of_section_3 = 1

        #clear previous output
        clear_output()
        # display UI
        UI()

        display(widgets.HTML(value=f'''

        Songs placement in Billboard 100 charts
''',
                                  layout=widgets.Layout(width="100%")))
        create_chord_diag(discog_filtered, column1 = 'BILLBOARD_TRACK_RANK', column2 ='period')

#-------------------------------------------------------------------------------
# SECTION 3 | TAB 3 variables and functions

#TODO make chord diagram for albums

# function to run at click of the button
def show_billboard_album_charts(x):
    if len(discog_filtered) == 0:
        no_selections_warning()
    else:

        global bin_size

        # select current tab
        global selected_section
        selected_section = 2
        # select sub tab
        global selection_tab_of_section_3
        selection_tab_of_section_3 = 2

        #clear previous output
        clear_output()
        # display UI
        UI()

        display(
            widgets.HTML(
                value=f'''

                      Album placement in Billboard Albums charts

''',
                layout=widgets.Layout(width="100%")
            )
        )
        create_chord_diag(
            discog_filtered,
            column1 = 'BILLBOARD_ALBUM_RANK',
            column2 ='period')

#-------------------------------------------------------------------------------
# # SECTION 4 | TAB 1 variables and functions

sentiment_dropdown1 = widgets.Dropdown(options=['albums', 'tracks by album',],
                                                value='albums',
                                                description='Display by:',
                                                disabled=False,)
sentiment_dropdown2 = widgets.Dropdown(options=[''],
                                                value='',
                                                description='',
                                                disabled=False,)

# function to run at click of the button_show_wordclouds
def show_sentiment_graphs(x):
    if len(discog_filtered) == 0:
        no_selections_warning()
    else:

        #clear previous output
        clear_output()
        print("Analysing the sentiment of the lyrics...")
        # select current tab
        global selected_section
        selected_section = 3
        # select sub tab
        global selection_tab_of_section_4
        selection_tab_of_section_4 = 0

        clear_output()
        UI()

        if sentiment_dropdown1.value == 'albums':
            # display label
            display(
                widgets.HTML(
                    value=f'''

                              Sentiment Analysis - diverging bars

                              Artist: {artist}
''',
                    layout=widgets.Layout(width="100%"))
            )
            # display plot of scores by album, sorted chronologically
            advanced_analytics.plotDivergingBars(
                discog_filtered.reset_index(),
                'SENTIMENT_COMPOUND_SCORE',
                'YEAR_ALBUM',
                green=colour_palette.get('green'),
                red=colour_palette.get('red'),
                sort_by_values = False)
        else:
            # display more specific label
            display(
                widgets.HTML(
                    value=f'''

                              Sentiment Analysis - diverging bars

                              Artist: {artist} |
                              Album: {sentiment_dropdown2.value[7:]} |
                              Year: {sentiment_dropdown2.value[1:5]}

''',
                    layout=widgets.Layout(width="100%"))
            )
            # display plot by song in a selected album, sorted by sentiment score
            advanced_analytics.plotDivergingBars(
                discog_filtered[
                    discog_filtered.YEAR_ALBUM == sentiment_dropdown2.value
                ].reset_index(),
                'SENTIMENT_COMPOUND_SCORE',
                'TRACK_TITLE',
                green=colour_palette.get('green'),
                red=colour_palette.get('red')
            )

# inner function to be triggered with a change of dropdown1 value
def adapt_UI(x):
    global discog_filtered, sentiment_dropdown2
    global selected_section
    selected_section = 3
    # select sub tab
    global selection_tab_of_section_4
    selection_tab_of_section_4 = 0
    clear_output()
    sentiment_dropdown2 = widgets.Dropdown(options=discog_filtered['YEAR_ALBUM'].unique(),
                                            value=discog_filtered['YEAR_ALBUM'].unique()[0],
                                            description='Select Album:',
                                            disabled=False,)
    UI()
#-------------------------------------------------------------------------------
# SECTION 4 | TAB 2

def show_sentiment_vs_charts_song(x):
    if len(discog_filtered) == 0:
        no_selections_warning()
    else:
        # select current tab
        global selected_section
        selected_section = 3
        # select sub tab
        global selection_tab_of_section_4
        selection_tab_of_section_4 = 1

        #clear previous output
        clear_output()
        # display UI
        UI()
        display(
            widgets.HTML(
                value=f'''

                Tracks sentiment vs placement in Billboard 100 charts

''',
                layout=widgets.Layout(width="100%")
            )
        )
        create_chord_diag(
            discog_filtered,
            column1 = 'BILLBOARD_TRACK_RANK',
            column2 ='SENTIMENT_GROUP'
        )

#-------------------------------------------------------------------------------
# SECTION 4 | TAB 3 Sentiment score over time

def sntm_scr_ovr_time(data):
    import matplotlib.gridspec as gridspec
    # Decide Colors
    mycolors = ['tab:red', 'tab:blue', 'tab:green'
                , 'tab:orange', 'tab:brown', 'tab:grey'
                , 'tab:pink', 'tab:olive'
               ]

    mycolors1 = ['tab:grey'
                 , 'tab:green'
                ]
    mycolors2 = ['tab:grey'
                 , 'tab:red'
                ]

    expect_cols = ['Negative', 'Neutral', 'Positive']
    ##-----------------------------------------------------------------------------------##
    fig= plt.figure(figsize=(16, 9)) # Creating a figure
    gs = gridspec.GridSpec(2,2) # Creating 2 by 2 Grid in the figure
    # Subplot on each axis
    ax1 = fig.add_subplot(gs[0,:]) # ploting whole first row of grid
    ax2 = fig.add_subplot(gs[1,0])
    ax3 = fig.add_subplot(gs[1,1])

    ##-----------------------------------------------------------------------------------##
    df = pd.pivot_table(data,
                        index = 'YEAR',
                        columns = 'SENTIMENT_GROUP',
                        values = 'TRACK_TITLE',
                        aggfunc = 'count').fillna(0)
    for col in expect_cols:
        if col not in df.columns:
            df[col] = 0
    # Prepare data
    x  = df.index
    y_neg = [0 if math.isnan(x) else x for x in [x*-1 for x in df['Negative'].values.tolist()]]
    y_ntr1 = [0 if math.isnan(x) else x for x in [math.floor(x/2) *-1 for x in df['Neutral'].values.tolist()]]
    y_ntr2 = [0 if math.isnan(x) else x for x in [math.ceil(x/2)  for x in df['Neutral'].values.tolist()]]
    y_pos = [0 if math.isnan(x) else x for x in df['Positive'].values.tolist()]
    #y_pos = df['Positive'].values.tolist()
    y_1 = np.vstack([y_ntr2, y_pos])
    y_2 = np.vstack([y_ntr1, y_neg,])

    # Plot for each column
#     ax = plt.gca()

    ax1.bar(x, y_ntr2, width = 0.9, color = colour_palette.get('light grey'))
    ax1.bar(x, y_pos, width = 0.9, bottom=y_ntr2, color = colour_palette.get('green'))
    ax1.bar(x, y_ntr1, width = 0.9, color = colour_palette.get('light grey'))
    ax1.bar(x, y_neg, width = 0.9, bottom=y_ntr1, color = colour_palette.get('red'))

    # add gridline
    ax1.set_axisbelow(True)
    ax1.yaxis.set_major_locator(MultipleLocator(5))
    ax1.yaxis.set_minor_locator(AutoMinorLocator(5)) # minor line every 1
    ax1.yaxis.grid(True, which = 'major', linestyle = '--', color = '#d3d3d3')
    ax1.yaxis.grid(True, which = 'minor', linestyle = ':', color = '#d1d1d1')

    # Decorations
    ax1.set_title('Song lyrics by Sentiment over time', fontsize=14)

    #set axes limits
    # make sure the chart always displays at least 12 years on x axis
    min_ticks = 12
    x_vals = df.index
    x_min = int(x_vals.min()) - 1
    x_max = int(x_vals.max()) + 1
    diff = x_max - x_min
    if diff < min_ticks:
        padding = (min_ticks - diff) / 2
        x_max = math.ceil(x_max + padding)
        x_min = math.floor(x_min - padding)

    # set the actual limits for each axis
    ylimit = round(max(max(y_pos) + max(y_ntr2), abs(min(y_neg) + min(y_ntr1))) *1.1)
    ylimit_final = [-ylimit, ylimit]
    xlimit_final = [x_min, x_max]
    ax1.set(ylim = ylimit_final, xlim = xlimit_final)

    # remove y axis tick labels
    ax1.yaxis.set_ticklabels([])
    # force x axis tics to be integers
    ax1.xaxis.set_major_locator(MaxNLocator(integer=True))

    ##-----------------------------------------------------------------------------------##
    # Ploting for charted and uncharted Track Rank

    columns = data.columns[1:]
    labs = columns.values.tolist()

    data.loc[~data['BILLBOARD_TRACK_RANK'].isnull(), 'charted_uncharted'] = 'charted'
    data.loc[data['BILLBOARD_TRACK_RANK'].isnull(), 'charted_uncharted'] = 'uncharted'

    # charted
    df_charted = pd.pivot_table(data[data.charted_uncharted == 'charted'],
                                index = 'YEAR',
                                columns = 'SENTIMENT_GROUP',
                                values = 'TRACK_TITLE',
                                aggfunc = 'count').fillna(0)
    x  = df_charted.index
    for col in expect_cols:
        if col not in df_charted.columns:
            df_charted[col] = 0

    y_neg = [0 if math.isnan(x) else x for x in [x*-1 for x in df_charted['Negative'].values.tolist()]]
    y_ntr1 = [0 if math.isnan(x) else x for x in [math.floor(x/2) *-1 for x in df_charted['Neutral'].values.tolist()]]
    y_ntr2 = [0 if math.isnan(x) else x for x in [math.ceil(x/2)  for x in df_charted['Neutral'].values.tolist()]]
    y_pos = [0 if math.isnan(x) else x for x in df_charted['Positive'].values.tolist()]
    y_1 = np.vstack([y_ntr2, y_pos])
    y_2 = np.vstack([y_ntr1, y_neg,])

    # plot bars
    ax2.bar(x, y_ntr2, width = 0.9, color = colour_palette.get('light grey'))
    ax2.bar(x, y_pos, width = 0.9, bottom=y_ntr2, color = colour_palette.get('green'))
    ax2.bar(x, y_ntr1, width = 0.9, color = colour_palette.get('light grey'))
    ax2.bar(x, y_neg, width = 0.9, bottom=y_ntr1, color = colour_palette.get('red'))

    # add gridline
    ax2.set_axisbelow(True)
    ax2.yaxis.set_major_locator(MultipleLocator(5))
    ax2.yaxis.set_minor_locator(AutoMinorLocator(5)) # minor line every 1
    ax2.yaxis.grid(True,
                   which = 'major',
                   linestyle = '--',
                   color = '#d3d3d3')
    ax2.yaxis.grid(True,
                   which = 'minor',
                   linestyle = ':',
                   color = '#d1d1d1')

    # Setting up x and y axis limit
    ax2.set(ylim = ylimit_final, xlim = xlimit_final)
    # remove y axis tick labels
    ax2.yaxis.set_ticklabels([])
    # force x axis tics to be integers
    ax2.xaxis.set_major_locator(MaxNLocator(integer=True))

    # set title
    ax2.title.set_text('Charted Tracks')
    ##-----------------------------------------------------------------------------------##
    # uncharted
    df_uncharted = pd.pivot_table(data[data.charted_uncharted == 'uncharted'],
                                index = 'YEAR',
                                columns = 'SENTIMENT_GROUP',
                                values = 'TRACK_TITLE',
                                aggfunc = 'count').fillna(0)
    x  = df_uncharted.index
    for col in expect_cols:
        if col not in df_uncharted.columns:
            df_uncharted[col] = 0

    y_neg = [
        0 if math.isnan(x) else x for x in [
            x*-1 for x in df_uncharted['Negative'].values.tolist()
        ]
    ]
    y_ntr1 = [
        0 if math.isnan(x) else x for x in [
            math.floor(x/2) *-1 for x in df_uncharted['Neutral'].values.tolist()
        ]
    ]
    y_ntr2 = [
        0 if math.isnan(x) else x for x in [
            math.ceil(x/2)  for x in df_uncharted['Neutral'].values.tolist()
        ]
    ]
    y_pos = [
        0 if math.isnan(x) else x for x in df_uncharted['Positive'].values.tolist()
    ]
    y_1 = np.vstack([y_ntr2, y_pos])
    y_2 = np.vstack([y_ntr1, y_neg,])

    # plot bars
    ax3.bar(x,
            y_ntr2,
            width = 0.9,
            color = colour_palette.get('light grey')
           )
    ax3.bar(x,
            y_pos,
            width = 0.9,
            bottom=y_ntr2,
            color = colour_palette.get('green')
           )
    ax3.bar(x,
            y_ntr1,
            width = 0.9,
            color = colour_palette.get('light grey')
           )
    ax3.bar(x,
            y_neg,
            width = 0.9,
            bottom=y_ntr1,
            color = colour_palette.get('red')
           )

    # add gridline
    ax3.set_axisbelow(True)
    ax3.yaxis.set_major_locator(MultipleLocator(5))  # line every 5
    ax3.yaxis.set_minor_locator(AutoMinorLocator(5)) # minor line every 1
    ax3.yaxis.grid(True,
                   which = 'major',
                   linestyle = '--',
                   color = '#d3d3d3')
    ax3.yaxis.grid(True,
                   which = 'minor',
                   linestyle = ':',
                   color = '#d1d1d1')

    # Setting up x and y axis limit
    ax3.set(ylim = ylimit_final, xlim = xlimit_final)
    # remove y axis tick labels
    ax3.yaxis.set_ticklabels([])
    # force x axis tics to be integers
    ax3.xaxis.set_major_locator(MaxNLocator(integer=True))
    ax3.title.set_text('Uncharted Tracks')

    plt.show()

#-------------------------------------------------------------------------------
# SECTION 4 | TAB 3 Sentiment score over time Charted Vs Uncharted Tracks
## Not in use ###
# def sntm_scr_ovr_cht_unchta(data):
#     mycolors1 = ['tab:grey', 'tab:green']
#     mycolors2 = ['tab:grey', 'tab:red']

#     f, (ax1, ax2) = plt.subplots(1, 2, figsize=(16, 4.5))
#     f.suptitle('Sentiment score over time, charted vs uncharted tracks', fontsize=14)

#     columns = data.columns[1:]
#     labs = columns.values.tolist()

#     data.loc[~data['BILLBOARD_TRACK_RANK'].isnull(), 'charted_uncharted'] = 'charted'
#     data.loc[data['BILLBOARD_TRACK_RANK'].isnull(), 'charted_uncharted'] = 'uncharted'

#     # charted
#     df_charted = pd.pivot_table(data[data.charted_uncharted == 'charted'],
#                                 index = 'YEAR',
#                                 columns = 'SENTIMENT_GROUP',
#                                 values = 'TRACK_TITLE',
#                                 aggfunc = 'count')
#     x  = df_charted.index
#     y_neg = [0 if math.isnan(x) else x for x in [x*-1 for x in df_charted['Negative'].values.tolist()]]
#     y_ntr1 = [0 if math.isnan(x) else x for x in [x/2 *-1 for x in df_charted['Neutral'].values.tolist()]]
#     y_ntr2 = [0 if math.isnan(x) else x for x in [x/2  for x in df_charted['Neutral'].values.tolist()]]
#     y_pos = [0 if math.isnan(x) else x for x in df_charted['Positive'].values.tolist()]
#     y_1 = np.vstack([y_ntr2, y_pos])
#     y_2 = np.vstack([y_ntr1, y_neg,])

#     # plot bars
#     ax1.bar(x, y_ntr2, width = 0.9, color = '#b2b2b2')
#     ax1.bar(x, y_pos, width = 0.9, bottom=y_ntr2, color = '#198c19')
#     ax1.bar(x, y_ntr1, width = 0.9, color = '#b2b2b2')
#     ax1.bar(x, y_neg, width = 0.9, bottom=y_ntr1, color = '#ff4c4c')

#     # add gridline
#     ax1.set_axisbelow(True)
#     ax1.yaxis.grid(True, which = 'major', linestyle = '--', color = '#d3d3d3')

#     # calculate axis limits
#     xlimit_1 = [df_charted.index.min()-1, df_charted.index.max()+1]
#     ylimit_1 = round(max(max(y_pos) + max(y_ntr2), abs(min(y_neg) + min(y_ntr1))) *1.1)
#     # labels
#     # ax1.set_yticklabels([abs(x) for x in ax1.get_yticks()])

#     # set title
#     ax1.title.set_text('Charted Tracks')

#     # uncharted
#     df_uncharted = pd.pivot_table(data[data.charted_uncharted == 'uncharted'],
#                                 index = 'YEAR',
#                                 columns = 'SENTIMENT_GROUP',
#                                 values = 'TRACK_TITLE',
#                                 aggfunc = 'count')
#     x  = df_uncharted.index
#     y_neg = [0 if math.isnan(x) else x for x in [x*-1 for x in df_uncharted['Negative'].values.tolist()]]
#     y_ntr1 = [0 if math.isnan(x) else x for x in [x/2 *-1 for x in df_uncharted['Neutral'].values.tolist()]]
#     y_ntr2 = [0 if math.isnan(x) else x for x in [x/2  for x in df_uncharted['Neutral'].values.tolist()]]
#     y_pos = [0 if math.isnan(x) else x for x in df_uncharted['Positive'].values.tolist()]
#     y_1 = np.vstack([y_ntr2, y_pos])
#     y_2 = np.vstack([y_ntr1, y_neg,])

#     # plot bars
#     ax2.bar(x, y_ntr2, width = 0.9, color = '#b2b2b2')
#     ax2.bar(x, y_pos, width = 0.9, bottom=y_ntr2, color = '#198c19')
#     ax2.bar(x, y_ntr1, width = 0.9, color = '#b2b2b2')
#     ax2.bar(x, y_neg, width = 0.9, bottom=y_ntr1, color = '#ff4c4c')
#     # add gridline
#     ax2.set_axisbelow(True)
#     ax2.yaxis.grid(True, which = 'major', linestyle = '--', color = '#d3d3d3')
#     # calculate axis limits
#     xlimit_2 = [df_uncharted.index.min()-1, df_uncharted.index.max()+1]
#     ylimit_2 = round(max(max(y_pos) + max(y_ntr2), abs(min(y_neg) + min(y_ntr1))) *1.1)
#     # labels
#     # ax2.set_yticklabels([abs(x) for x in ax2.get_yticks()])
#     # set title
#     ax2.title.set_text('Uncharted Tracks')

#     # set final limtits for both subplots
#     xlimit_final = [min(xlimit_1[0], xlimit_2[0]), max(xlimit_1[1], xlimit_2[1])]
#     ylimit_final = max(ylimit_1, ylimit_2)
#     ax1.set(ylim=[-ylimit_final, ylimit_final], xlim=xlimit_final)
#     ax2.set(ylim=[-ylimit_final, ylimit_final], xlim=xlimit_final)

#     plt.show()

def show_sentiment_score_ovr_time(x):
    if len(discog_filtered) == 0:
        no_selections_warning()
    else:
        # select current tab
        global selected_section
        selected_section = 3
        # select sub tab
        global selection_tab_of_section_4
        selection_tab_of_section_4 = 2

        #clear previous output
        clear_output()
        # display UI
        UI()

        sntm_scr_ovr_time(discog_filtered)
#     sntm_scr_ovr_cht_unchta(discog_filtered)
#-------------------------------------------------------------------------------

# def oldUI():
#     #---------------------------------------------------------------------------
#     # SECTION 1          "Get Data" (Get discography, select albums, get lyrics)
#     #---------------------------------------------------------------------------
#     # SECTION 1 | TAB 1    "Artist" (Get discography)
#     # global variable (input box)
#     global artist_input
#     # button = get artist discography
#     button_get_discography = widgets.Button(description="Get discography")
#     button_get_discography.on_click(get_discography)
#     # wrap tab
#     SECTION_1_TAB_1 = widgets.VBox([artist_input, button_get_discography,])
#     #---------------------------------------------------------------------------
#     # SECTION 1 | TAB 2     "Albums" (select albums to include in the analysis)
#     # selector to include/exclude albums
#     global album_selector
#     # show current artist
#     label_current_artist = widgets.HTML(value=f'''Selected\
#     discography for {artist}''',
#                            layout=widgets.Layout(width="100%"))
#     # button to select/deselect
#     text_select_deselect_all = widgets.Label(
#         'Use checkboxes to toggle selection:',
#         layout=widgets.Layout(width="80%"))
#     button_select_deselect_all = widgets.Button(
#         description="Select/deselect all")
#     button_select_deselect_all.on_click(select_deselect_all)
#     # button = confirm
#     button_apply_selection = widgets.Button(description="Apply")
#     button_apply_selection.on_click(apply_selection)
#     # wrap elements
#     SECTION_1_TAB_2 = widgets.VBox([label_current_artist,
#                               text_select_deselect_all,
#                               button_select_deselect_all,
#                               album_selector,
#                               button_apply_selection,],
#                              layout=widgets.Layout(width="80%",
#                                                    padding = "10px"))
#     #---------------------------------------------------------------------------
#     # SECTION 1 | TAB 3     "Time period" ( select time intervals for charts)
#     # period size selection slider
#     global period_selection_slider
#     # button to update the period selection
#     button_update_period_selection = widgets.Button(description="Confirm")
#     button_update_period_selection.on_click(set_bin_size)
#     # vertical block
#     SECTION_1_TAB_3 = widgets.VBox([period_selection_slider,
#                                     button_update_period_selection,])

#     #---------------------------------------------------------------------------
#     # SECTION 1 build
#     section_1_children = [SECTION_1_TAB_1, SECTION_1_TAB_2,SECTION_1_TAB_3]
#     section_1 = widgets.Tab(children=section_1_children)
#     section_1.set_title(0, 'Artist')
#     section_1.set_title(1, 'Albums')
#     section_1.set_title(2, 'Time periods')
#     section_1.selected_index = selection_tab_of_section_1
#     #---------------------------------------------------------------------------

#     #---------------------------------------------------------------------------
#     # SECTION 2          "Visualisations - single artist"
#     #---------------------------------------------------------------------------
#     # SECTION 2 | TAB 1   "Basic Charts"
#     # button to update chart
#     button_show_basic_charts = widgets.Button(description="Show/refresh charts")
#     button_show_basic_charts.on_click(show_basic_charts)
#     # vertical block
#     SECTION_2_TAB_1 = widgets.VBox([button_show_basic_charts,])
#     #---------------------------------------------------------------------------
#     # SECTION 2 | TAB 2   "Wordclouds"
#     # dropdown
#     global wordcloud_by_selection_dropdown
#     # button to update chart
#     button_show_wordclouds = widgets.Button(description="Show")
#     button_show_wordclouds.on_click(show_wordclouds)
#     # vertical block
#     SECTION_2_TAB_2 = widgets.VBox([wordcloud_by_selection_dropdown,
#                                     button_show_wordclouds,])
#     #---------------------------------------------------------------------------
#     # SECTION 2 | TAB 3   "Users and Ratings"
#     # button to show charts
#     button_users_ratings_charts = widgets.Button(description="Show")
#     button_users_ratings_charts.on_click(show_users_ratings_charts)
#     # vertical block
#     SECTION_2_TAB_3 = widgets.VBox([button_users_ratings_charts,])
#     #---------------------------------------------------------------------------
#      # SECTION 2 | TAB 4   "Sentiment Analysis"
#     global sentiment_dropdown1, sentiment_dropdown2
#     # button to show charts
#     button_sentiment_analysis = widgets.Button(description="Show")
#     button_sentiment_analysis.on_click(show_sentiment_graphs)
#     # vertical block
#     # define tab depending on the value of the global variable
#     if sentiment_dropdown1.value == 'tracks by album':
#         SECTION_2_TAB_4 = widgets.VBox([sentiment_dropdown1,sentiment_dropdown2,button_sentiment_analysis, ])
#     else:
#         SECTION_2_TAB_4 = widgets.VBox([sentiment_dropdown1,button_sentiment_analysis])

#     # trigger inner function when value of the dropdown1 changes
#     sentiment_dropdown1.observe(adapt_UI, names='value')
#     #---------------------------------------------------------------------------
#     # SECTION 2 build
#     section_2_children = [SECTION_2_TAB_1,
#                           SECTION_2_TAB_2,
#                           SECTION_2_TAB_3,
#                           SECTION_2_TAB_4,]
#     section_2 = widgets.Tab()
#     section_2.children = section_2_children
#     section_2.set_title(0, 'Basic Charts')
#     section_2.set_title(1, 'Wordclouds')
#     section_2.set_title(2, 'Users and ratings')
#     section_2.set_title(3, 'Sentiment analysis')
#     section_2.selected_index = selection_tab_of_section_2
#     section_2_wrapper_label = widgets.HTML(
#         value=f'''Current artist: {artist}''',
#         layout=widgets.Layout(width="100%"))
#     section_2_wrapper = widgets.VBox([section_2_wrapper_label,
#                                       section_2,])
#     #---------------------------------------------------------------------------

#     #---------------------------------------------------------------------------
#     # SECTION 3          "Visualisations - compare artists"
#     #---------------------------------------------------------------------------
#     # SECTION 3 | TAB 1   "Select artists"
#     SECTION_3_TAB_1 = widgets.VBox()
#     #---------------------------------------------------------------------------
#     # SECTION 3 | TAB 1   "Basic charts"
#     SECTION_3_TAB_2 = widgets.VBox()
#     #---------------------------------------------------------------------------
#     # SECTION 3 build
#     section_3_children = [SECTION_3_TAB_1,
#                           SECTION_3_TAB_2,]
#     section_3 = widgets.Tab()
#     section_3.children = section_3_children
#     section_3.set_title(0, 'Select Artists')
#     section_3.set_title(1, 'Basic Charts')
#     #---------------------------------------------------------------------------

#     #---------------------------------------------------------------------------
#     # FINAL UI compiler
#     #---------------------------------------------------------------------------
#     UI = widgets.Accordion(children=[section_1,
#                                      section_2_wrapper,
#                                      section_3])
#     UI.set_title(0, 'Configuration')
#     UI.set_title(1, 'Visualisations - single artist')
#     UI.set_title(2, 'Visualisations - compare artists')
#     UI.selected_index = selected_section
#     display(UI)
#     #---------------------------------------------------------------------------

def UI():
    # select font for charts
    plt.rcParams["font.family"] = "DejaVu Sans"
    #---------------------------------------------------------------------------
    # SECTION 1            "Configuration"
    #---------------------------------------------------------------------------
    # SECTION 1 | TAB 1    "Artist" (Get discography)
    # global variable (input box)
    global artist_input
    # button = get artist discography
    button_get_discography = widgets.Button(description="Get discography")
    button_get_discography.on_click(get_discography)
    # wrap tab
    SECTION_1_TAB_1 = widgets.VBox([artist_input, button_get_discography,])
    #---------------------------------------------------------------------------
    # SECTION 1 | TAB 2     "Albums" (select albums to include in the analysis)
    # selector to include/exclude albums
    global album_selector
    # show current artist
    label_current_artist = widgets.HTML(value=f'''

    Selected discography for {artist}

    Source link

''',
                           layout=widgets.Layout(width="100%"))
    # button to select/deselect
    text_select_deselect_all = widgets.Label(
        'Use checkboxes to toggle selection:',
        layout=widgets.Layout(width="80%"))
    button_select_deselect_all = widgets.Button(
        description="Select/deselect all")
    button_select_deselect_all.on_click(select_deselect_all)
    # button = confirm
    button_apply_selection = widgets.Button(description="Apply")
    button_apply_selection.on_click(apply_selection)
    # wrap elements
    SECTION_1_TAB_2 = widgets.VBox([label_current_artist,
                              text_select_deselect_all,
                              button_select_deselect_all,
                              album_selector,
                              button_apply_selection,],
                             layout=widgets.Layout(width="80%",
                                                   padding = "10px"))
    #---------------------------------------------------------------------------
    # SECTION 1 | TAB 3     "Time period" ( select time intervals for charts)
    # period size selection slider
    global period_selection_slider
    # button to update the period selection
    button_update_period_selection = widgets.Button(description="Confirm")
    button_update_period_selection.on_click(set_bin_size)
    # vertical block
    SECTION_1_TAB_3 = widgets.VBox([period_selection_slider,
                                    button_update_period_selection,])

    #---------------------------------------------------------------------------
    # SECTION 1 build
    section_1_children = [SECTION_1_TAB_1,
                          SECTION_1_TAB_2,
                          SECTION_1_TAB_3]
    section_1 = widgets.Tab(children=section_1_children)
    section_1.set_title(0, 'Artist')
    section_1.set_title(1, 'Albums')
    section_1.set_title(2, 'Time periods')
    section_1.selected_index = selection_tab_of_section_1
    section_1_wrapper_label = widgets.HTML(
        value=f'''
        Choose the ARTIST to load the discography, select ALBUMS to be
           included in the analysis and choose the TIME PERIOD length for time
           based visualisations.

''',
        layout=widgets.Layout(width="100%"))
    section_1_wrapper = widgets.VBox([section_1_wrapper_label,
                                      section_1,])
    #---------------------------------------------------------------------------

    #---------------------------------------------------------------------------
    # SECTION 2          "Visualisations - Overview and Lexical Diversity"
    #---------------------------------------------------------------------------

    #---------------------------------------------------------------------------
    # SECTION 2 | TAB 1   "Overview"
    # button to update chart
    s2t1_desc = widgets.HTML(
        value=f'''
        Displays Number of Albums and Tracks in the selected Artist's
        discography over time, arranged in periods of chosen size.

''',
        layout=widgets.Layout(width="80%"))
    button_show_basic_charts = widgets.Button(description="Show/refresh charts")
    button_show_basic_charts.on_click(show_basic_charts)
    # vertical block
    SECTION_2_TAB_1 = widgets.VBox([s2t1_desc,
                                    button_show_basic_charts,])
    #---------------------------------------------------------------------------
    # SECTION 2 | TAB 2   "Lexical Diversity"
    # button to update chart
    s2t2_desc = widgets.HTML(
        value=f'''
        Displays the lexical diversity of the Artist's lyrics over time,
        arranged in periods of chosen size.

''',
        layout=widgets.Layout(width="80%"))
    button_show_lexical_diversity = widgets.Button(description="Show/refresh charts")
    button_show_lexical_diversity.on_click(show_lexical_diversity)
    # vertical block
    SECTION_2_TAB_2 = widgets.VBox([s2t2_desc,
                                    button_show_lexical_diversity,])
    #---------------------------------------------------------------------------
    # SECTION 2 | TAB 3   "Wordclouds"
    s2t3_desc = widgets.HTML(
        value=f'''
        Displays Artist's lyrics in Wordclouds, either by selected period
        size or by individual Album.

''',
        layout=widgets.Layout(width="80%"))
    # dropdown
    global wordcloud_by_selection_dropdown
    # button to update chart
    button_show_wordclouds = widgets.Button(description="Show")
    button_show_wordclouds.on_click(show_wordclouds)
    # vertical block
    SECTION_2_TAB_3 = widgets.VBox([s2t3_desc,
                                    wordcloud_by_selection_dropdown,
                                    button_show_wordclouds,])
    #---------------------------------------------------------------------------
    # SECTION 2 build
    section_2_children = [SECTION_2_TAB_1,
                          SECTION_2_TAB_2,
                          SECTION_2_TAB_3,]
    section_2 = widgets.Tab()
    section_2.children = section_2_children
    section_2.set_title(0, 'Basic Charts')
    section_2.set_title(1, 'Lexical Diversity')
    section_2.set_title(2, 'Wordclouds')
    section_2.selected_index = selection_tab_of_section_2
    section_2_wrapper_label = widgets.HTML(
        value=f'''Current artist: {artist}''',
        layout=widgets.Layout(width="100%"))
    section_2_wrapper = widgets.VBox([section_2_wrapper_label,
                                      section_2,])
    #---------------------------------------------------------------------------

    #---------------------------------------------------------------------------
    # SECTION 3          "Visualisations - Ratings and Success"
    #---------------------------------------------------------------------------
    #---------------------------------------------------------------------------
    # SECTION 3 | TAB 1   "Discogs Ratings"
    # button to show charts
    s3t1_desc = widgets.HTML(
        value=f'''
        Visualises data retrieved from DISCOGS.COM, such as number of
        registered record owners or average album ratings.
         ''',
        layout=widgets.Layout(width="80%"))
    button_discogs_users_charts = widgets.Button(description="Show")
    button_discogs_users_charts.on_click(show_discogs_users_charts)
    # vertical block
    SECTION_3_TAB_1 = widgets.VBox([s3t1_desc,
                                    button_discogs_users_charts,])
    #---------------------------------------------------------------------------
    # SECTION 3 | TAB 2   "Billboard 100"
    s3t2_desc = widgets.HTML(
        value=f'''
        Visualises the proportions of tracks in the selected Artist's
        discography that reached a position in the official Billboard 100 music
        charts vs those that didn't, by period.

''',
        layout=widgets.Layout(width="80%"))
    # button to show charts
    button_billboard_100_charts = widgets.Button(description="Show")
    button_billboard_100_charts.on_click(show_billboard_100_charts)
    # vertical block
    SECTION_3_TAB_2 = widgets.VBox([s3t2_desc,
                                    button_billboard_100_charts,])
    #---------------------------------------------------------------------------
    # SECTION 3 | TAB 3   "Billboard Albums"
    s3t3_desc = widgets.HTML(
        value=f'''
        Visualises the proportions of Albums in the selected Artist's
        discography that reached a position in the official Billboard  music
        Album charts vs those that didn't, by period.

''',
        layout=widgets.Layout(width="80%"))
    # button to show charts
    button_billboard_albums_charts = widgets.Button(description="Show")
    button_billboard_albums_charts.on_click(show_billboard_album_charts)
    # vertical block
    SECTION_3_TAB_3 = widgets.VBox([s3t3_desc,
                                    button_billboard_albums_charts,])
    #---------------------------------------------------------------------------
    # SECTION 3 build
    section_3_children = [SECTION_3_TAB_1,
                          SECTION_3_TAB_2,
                          SECTION_3_TAB_3,]
    section_3 = widgets.Tab()
    section_3.children = section_3_children
    section_3.set_title(0, 'Discogs Ratings')
    section_3.set_title(1, 'Billboard 100')
    section_3.set_title(2, 'Billboard Albums')
    section_3.selected_index = selection_tab_of_section_3
    section_3_wrapper_label = widgets.HTML(
        value=f'''Current artist: {artist}''',
        layout=widgets.Layout(width="100%"))
    section_3_wrapper = widgets.VBox([section_3_wrapper_label,
                                      section_3,])
    #---------------------------------------------------------------------------

    #---------------------------------------------------------------------------
    # SECTION 4          "Visualisations - Sentiment Analysis"
    #---------------------------------------------------------------------------
    #---------------------------------------------------------------------------
    # SECTION 4 | TAB 1   "Albums and Songs Sentiment"
    s4t1_desc = widgets.HTML(
        value=f'''
        Visualises the sentiment score of individual lyrics. Choose to
        display by Album or individually by Tracks of a selected Album.

''',
        layout=widgets.Layout(width="80%"))
    global sentiment_dropdown1, sentiment_dropdown2
    # button to show charts
    button_sentiment_analysis = widgets.Button(description="Show")
    button_sentiment_analysis.on_click(show_sentiment_graphs)
    # vertical block
    # define tab depending on the value of the global variable
    if sentiment_dropdown1.value == 'tracks by album':
        SECTION_4_TAB_1 = widgets.VBox([s4t1_desc,
                                        sentiment_dropdown1,
                                        sentiment_dropdown2,
                                        button_sentiment_analysis, ])
    else:
        SECTION_4_TAB_1 = widgets.VBox([s4t1_desc,
                                        sentiment_dropdown1,
                                        button_sentiment_analysis])

    # trigger inner function when value of the dropdown1 changes
    sentiment_dropdown1.observe(adapt_UI, names='value')

    #---------------------------------------------------------------------------
    # SECTION 4 | TAB 2   "Sentiment and Charts"
    s4t2_desc = widgets.HTML(
        value=f'''
        Shows a chord diagram comparing the sentiment of the lyrics with
        whether the track reached a position in the official Billboard 100 music
        charts or not.

''',
        layout=widgets.Layout(width="80%"))
    # button to show Sentiment Vs Charts
    button_sentiment_vs_charts_song = widgets.Button(description="Show")
    button_sentiment_vs_charts_song.on_click(show_sentiment_vs_charts_song)
    # vertical block
    SECTION_4_TAB_2 = widgets.VBox([s4t2_desc,
                                    button_sentiment_vs_charts_song,])

    # SECTION 4 | TAB 3   "Sentiment Score over time"
    s4t3_desc = widgets.HTML(
        value=f'''
        Displays tracks grouped by sentiment score over time. Shown as all
        tracks together as well as charted and uncharted tracks individually.

        GREEN
         represents tracks with Positive,
        GREY
         with Neutral, and
        RED
         with Negative sentiment score. Each
        gridline represents a single track.

''',
        layout=widgets.Layout(width="80%"))
    # button to show Sentiment score over time
    button_sentiment_over_time = widgets.Button(description="Show")
    button_sentiment_over_time.on_click(show_sentiment_score_ovr_time)
    # vertical block
    SECTION_4_TAB_3 = widgets.VBox([s4t3_desc,
                                    button_sentiment_over_time,])

    #---------------------------------------------------------------------------
    # SECTION 4 build
    section_4_children = [SECTION_4_TAB_1,
                          SECTION_4_TAB_2,
                          SECTION_4_TAB_3,]
    section_4 = widgets.Tab()
    section_4.children = section_4_children
    section_4.set_title(0, 'Lyrics Sentiment')
    section_4.set_title(1, 'Sentiment vs Charts')
    section_4.set_title(2, 'Sentiment Over Time')

    section_4.selected_index = selection_tab_of_section_4
    section_4_wrapper_label = widgets.HTML(
        value=f'''Current artist: {artist}''',
        layout=widgets.Layout(width="100%"))
    section_4_wrapper = widgets.VBox([section_4_wrapper_label,
                                      section_4,])

    #---------------------------------------------------------------------------
    # FINAL UI compiler
    #---------------------------------------------------------------------------
    UI = widgets.Accordion(children=[section_1_wrapper,
                                     section_2_wrapper,
                                     section_3_wrapper,
                                     section_4_wrapper])
    UI.set_title(0, 'Configuration')
    UI.set_title(1, 'Visualisations - Overview and Lexical Diversity')
    UI.set_title(2, 'Visualisations - Ratings and Success')
    UI.set_title(3, 'Visualisations - Sentiment Analysis')
    UI.selected_index = selected_section
    display(UI)
    #---------------------------------------------------------------------------


# -*- coding: utf-8 -*-
from __future__ import unicode_literals

from django.db import migrations, models

class Migration(migrations.Migration):

    dependencies = [
        ('events', '0001_initial'),
    ]

    operations = [
        migrations.CreateModel(
            name='EventCategory',
            fields=[
                ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
                ('name', models.CharField(max_length=50)),
                ('created', models.DateTimeField(auto_now_add=True)),
                ('modified', models.DateTimeField(auto_now=True)),
            ],
        ),
        migrations.AddField(
            model_name='event',
            name='categories',
            field=models.ManyToManyField(to='events.EventCategory'),
        ),
    ]


def read_simple_graph(vertex_number, edge_number):
    graph = [[0] * vertex_number for i in range(vertex_number)]
    for j in range(edge_number):
        l1, l2 = list(map(int, input().split()))
        graph[l1][l2] = 1
    return graph

v, e = list(map(int, input().split()))
my_graph = read_simple_graph(v, e)
for i in range(v):
    out_power = sum(my_graph[i])
    in_power = 0
    for j in range(v):
        in_power += my_graph[j][i]
    print(i, in_power, out_power)


import math

import numpy as np

# класс подсчета интеграла
class Integral(object):
    # конструктор принимает на вход пределы интегрирования, функцию или таблицу xi
    def __init__(self, a, b, func=None, x_data=None, y_data=None):
        self.a = a
        self.b = b
        if func is not None:
            self.func = func
        else:
            self.func = self.newton_polynomial
        self.x_ = x_data
        self.y_ = y_data

    # коэффиценты полинома ньютона
    def _poly_newton_coefficient(self, x, y):

        m = len(x)

        x = np.copy(x)
        a = np.copy(y)
        for k in range(1, m):
            a[k:m] = (a[k:m] - a[k - 1]) / (x[k:m] - x[k - 1])
        return a

    # функция полинома ньютона
    def newton_polynomial(self, x):

        a = self._poly_newton_coefficient(self.x_, self.y_)
        n = len(self.x_) - 1  # Degree of polynomial
        p = a[n]

        for k in range(1, n + 1):
            p = a[n - k] + (x - self.x_[n - k]) * p

        return p

    # правило прямоугольников
    def rectangle_rule(self, n):
        h = abs(self.a - self.b) / n
        sum = 0
        for i in range(0, n+1):
            sum += h * self.func(self.a + h * i)
        return sum

    # правило трапеций
    def trapezoid_rule(self, n):
        h = abs(self.a - self.b) / n
        sum = 0
        for i in range(0, n+1):
            sum += h * (self.func(self.a + h * i) + self.func(self.a + h * (i + 1))) * 0.5
        return sum

    # правило Симпсона
    def simpsone_rule(self, n):
        h = abs(self.a - self.b) / n
        sum = self.func(self.a) + self.func(self.b)
        for i in range(2, n, 2):
            sum += 2 * self.func(self.a + i * h)
        for i in range(1, n, 2):
            sum += 4 * self.func(self.a + i * h)
        sum *= h / 3
        return sum

    # определение количества шагов методом Рунге
    def runge_rule(self, rule, n0, o, e):
        I1 = 0
        I2 = 100 * e
        while o * (abs(I2 - I1)) > e:
            I2 = rule(n0)
            I1 = rule(2 * n0)
            n0 *= 2
        return I1, I2
def func(x):
    return x * math.acos(x)

x = [0.079, 0.637, 1.345, 2.095, 2.782]
y = [-4.308, -0.739, 1.697, 4.208, 6.203]
a = x[0]
b = x[len(x) - 1]
e = 0.001
n0 = 10
integral = Integral(-0.5, 0.5, func)
print(integral.runge_rule(integral.rectangle_rule, n0, float(1 / 3), e))
print("\n")
print(integral.runge_rule(integral.trapezoid_rule, n0, float(1 / 3), e))
print("\n")
print(integral.runge_rule(integral.simpsone_rule, n0, float(1 / 15), e))
print("\n")
newton_integral = Integral(a, b, None, x, y)
print(newton_integral.runge_rule(newton_integral.rectangle_rule, n0, float(1 / 3), e))
print("\n")
print(newton_integral.runge_rule(newton_integral.trapezoid_rule, n0, float(1 / 3), e))
print("\n")
print(newton_integral.runge_rule(newton_integral.simpsone_rule, n0, float(1 / 15), e))
print("\n")
# По проверке на онлайн-калькуляторе метод симпсона - самый точный


import sqlite3
from sqlite3 import Error
from datetime import date

class PatientDataStore():
    """
    Stores patient information in sqlite3 database
    """
    __instance = None

    @staticmethod
    def getInstance():
        """
        This is a static method to create class as a singleton
        """
        if PatientDataStore.__instance == None:
            PatientDataStore()
        return PatientDataStore.__instance

    def __init__(self):
        """
        Initialize connection to sqlite datbase
        """
        if PatientDataStore.__instance != None:
            raise Exception("This class is a singleton")
        else:
            PatientDataStore.__instance = self
            self.__open_connection()
            patients_table = "CREATE TABLE IF NOT EXISTS patients(id integer PRIMARY KEY, name text, temperature integer, home_parish text, diagnosis integer, created_at text)"
            cursorObj = self.con.cursor()
            cursorObj.execute(patients_table)
            self.con.commit()

    def store_patient(self, name, parish, temp, diagnosis):
        """
        Initialize connection to sqlite datbase
        Parameters:
            name(str): patients name
            parish(str): patients home parish
            temp(int): patients temperature
            diagnosis(int): patients diagnosis percentage chance patient has covid
        """
        created_at = date.today().strftime("%d-%m-%Y")
        new_patient = f"INSERT INTO patients(name, temperature, home_parish, diagnosis, created_at) VALUES ('{name}', {temp}, '{parish}', {diagnosis}, '{created_at}')"
        cursorObj = self.con.cursor()
        cursorObj.execute(new_patient)
        self.con.commit()

    def get_patients(self):
        """
        Return all patients stored
        """
        cursorObj = self.con.cursor()
        cursorObj.execute("SELECT * FROM patients")
        rows = list(cursorObj.fetchall())
        print(rows)
        self.con.commit()

    def __open_connection(self):
        try:
            self.con = sqlite3.connect("covidAi.db")
            print("[Connection established]")
        except Error:
            print(Error)

    def __close_connection(self):
        self.con.close()

if __name__ == "__main__":
    pateintdb = PatientDataStore()


import codecs
import hashlib
import base58

from bitcoinaddress.util import checksum
from bitcoinaddress import Wallet

from bitcoinutils.keys import PrivateKey
from bitcoinutils.setup import setup

import ecdsa
from ecdsa.curves import SECP256k1
from ecdsa.util import sigencode_der_canonize

from .base import BaseKey
from ..hash import sha256, sha3
from ..common import parseType
from ..hint import KEY_PRIVATE, KEY_PUBLIC

class Keypair(object):
    def __init__(self, priv, useSeed):
        if not useSeed:
            raw, _ = parseType(priv)
            self.priv = BaseKey(KEY_PRIVATE, raw)
            self.seed = None
        else:
            self.priv = None
            self.seed = priv
        self.generatePrivateKey()
        self.generatePublicKey()

    def generatePrivateKey(self):
        if self.priv != None:
            assert isinstance(
                self.priv, BaseKey), 'Wrong private key or seed; Keypair.generatePrivateKey'
        elif self.seed != None:
            assert len(
                self.seed) >= 36, 'Seed is too short to create Keypair; Keypair.generatePrivateKey'
            sh = sha3(self.seed.encode())
            if len(sh.digest) < 44:
                shb = base58.b58encode(sh.digest)[:-3]
            else:
                shb = base58.b58encode(sh.digest)[:-4]
            k = str(hex((int.from_bytes(shb, "big") % (SECP256k1.order - 1)) + 1))[2:]
            self.priv = BaseKey(KEY_PRIVATE, encodeKey(bytes.fromhex(k)))

    def generatePublicKey(self):
        wif = base58.b58encode_check(
            base58.b58decode_check(self.priv.key)[:-1]).decode()
        wallet = Wallet(wif)
        self.pub = BaseKey(KEY_PUBLIC, base58.b58encode(codecs.decode(
            wallet.address.pubkeyc, "hex")).decode())

    @property
    def privateKey(self):
        return self.priv.typed

    @property
    def publicKey(self):
        return self.pub.typed

    def sign(self, b):
        assert isinstance(b, bytes), 'Input must be bytes object; Keypair.sign'
        setup('mainnet')

        hs = sha256(b).digest
        wif = self.priv.key

        pk = PrivateKey(wif=wif)
        sk = ecdsa.SigningKey.from_string(pk.key.to_string(), curve=SECP256k1)

        return sk.sign(hs, hashfunc=hashlib.sha256, sigencode=sigencode_der_canonize)

def encodeKey(key):
    pk = b'\x80' + key + b'\x01'
    return base58.b58encode(pk + checksum(pk)).decode()

def getNewKeypair():
    return getKeypairFromPrivateKey(encodeKey(Wallet().key.digest) + KEY_PRIVATE)

def getKeypairFromPrivateKey(priv):
    _, type = parseType(priv)
    assert type == KEY_PRIVATE, 'Not private key; getKeypairFromPrivateKey'
    return Keypair(priv, False)

def getKeypairFromSeed(seed):
    assert len(
        seed) >= 36, 'Seed is too short to create Keypair; getKeypairFromseed'
    return Keypair(seed, True)


from dagster_graphql.test.utils import execute_dagster_graphql

from dagster.core.instance import DagsterInstance

from .utils import define_test_context, sync_execute_get_run_log_data

COMPUTE_LOGS_QUERY = '''
  query ComputeLogsQuery($runId: ID!, $stepKey: String!) {
    pipelineRunOrError(runId: $runId) {
      ... on PipelineRun {
        runId
        computeLogs(stepKey: $stepKey) {
          stdout {
            data
          }
        }
      }
    }
  }
'''
COMPUTE_LOGS_SUBSCRIPTION = '''
  subscription ComputeLogsSubscription($runId: ID!, $stepKey: String!, $ioType: ComputeIOType!, $cursor: String!) {
    computeLogs(runId: $runId, stepKey: $stepKey, ioType: $ioType, cursor: $cursor) {
      data
    }
  }
'''

def test_get_compute_logs_over_graphql(snapshot):
    payload = sync_execute_get_run_log_data(
        {'executionParams': {'selector': {'name': 'spew_pipeline'}, 'mode': 'default'}}
    )
    run_id = payload['run']['runId']

    result = execute_dagster_graphql(
        define_test_context(instance=DagsterInstance.local_temp()),
        COMPUTE_LOGS_QUERY,
        variables={'runId': run_id, 'stepKey': 'spew.compute'},
    )
    compute_logs = result.data['pipelineRunOrError']['computeLogs']
    snapshot.assert_match(compute_logs)

def test_compute_logs_subscription_graphql(snapshot):
    payload = sync_execute_get_run_log_data(
        {'executionParams': {'selector': {'name': 'spew_pipeline'}, 'mode': 'default'}}
    )
    run_id = payload['run']['runId']

    subscription = execute_dagster_graphql(
        define_test_context(instance=DagsterInstance.local_temp()),
        COMPUTE_LOGS_SUBSCRIPTION,
        variables={'runId': run_id, 'stepKey': 'spew.compute', 'ioType': 'STDOUT', 'cursor': '0'},
    )
    results = []
    subscription.subscribe(lambda x: results.append(x.data))
    assert len(results) == 1
    result = results[0]
    assert result['computeLogs']['data'] == 'HELLO WORLD\n'
    snapshot.assert_match(results)


# -*- coding: utf-8 -*-
import pandas as pd
from sklearn.preprocessing import LabelEncoder
from sklearn.model_selection import train_test_split
from sklearn.tree import DecisionTreeClassifier
from sklearn.externals.six import StringIO
from IPython.display import Image
from sklearn.tree import export_graphviz
import pydotplus
from math import sqrt
dataset = pd.read_csv("bank_customer.csv")
df = pd.DataFrame(dataset)
bank_data = df.copy()

def combine_job_poutcome(bank_data):
    bank_data['job'] = bank_data['job'].replace(['management', 'admin.'], 'white-collar')
    bank_data['job'] = bank_data['job'].replace(['services', 'housemaid'], 'pink-collar')
    bank_data['job'] = bank_data['job'].replace(['retired', 'student','unemployed','unknown'], 'other')

    bank_data['poutcome'] = bank_data['poutcome'].replace(['other','unknown'], 'unknown')

combine_job_poutcome(bank_data)

def convert_categorical_values(bank_data):

    le = LabelEncoder()
    bank_data['job'] = le.fit_transform(bank_data['job'])
    bank_data['marital'] = le.fit_transform(bank_data['marital'])
    bank_data['education'] = le.fit_transform(bank_data['education'])
    bank_data['default'] = le.fit_transform(bank_data['default'])
    bank_data['housing'] = le.fit_transform(bank_data['housing'])
    bank_data['loan'] = le.fit_transform(bank_data['loan'])
    bank_data['contact'] = le.fit_transform(bank_data['contact'])
    bank_data['month'] = le.fit_transform(bank_data['month'])
    bank_data['poutcome'] = le.fit_transform(bank_data['poutcome'])
    bank_data['deposit'] = le.fit_transform(bank_data['deposit'])

convert_categorical_values(bank_data)

data_1 = bank_data[['age','job','marital','education','balance','housing','duration','poutcome']]
data_2 = bank_data[['job','marital','education','housing']]

def train_and_test(dataset):
    print("Accuracy values of the dataset :")
    X = dataset
    y = bank_data['deposit'].to_frame()
    X_train,X_test,y_train,y_test = train_test_split(X,y,test_size=0.3,random_state=100)
    return X_train,X_test,y_train,y_test

X_train,X_test,y_train,y_test = train_and_test(data_1)

def with_entropy(depth):

    tree_with_entropy = DecisionTreeClassifier(criterion = "entropy", random_state = 100, max_depth = depth, min_samples_leaf = 5)
    tree_with_entropy.fit(X_train, y_train)
    return tree_with_entropy

def with_gini_index(depth):

    tree_with_gini = DecisionTreeClassifier(criterion = "gini", random_state = 100, max_depth = depth, min_samples_leaf = 5)
    tree_with_gini.fit(X_train, y_train)
    return tree_with_gini

def prediction(X_test, tree):

    prediction = tree.predict(X_test)
    print(prediction)
    return prediction

def calculate_test_accuracy(tree):
    tree_score_test = tree.score(X_test, y_test)
    print("Test score: ",tree_score_test)
    return tree_score_test

def calculate_train_accuracy(tree):
    tree_score_train = tree.score(X_train, y_train)
    print("Training score: ",tree_score_train)
    return tree_score_train

def calculate_confidence_interval(accuracy,dataset):
    z = 1.96
    interval = z * sqrt((accuracy * (1-accuracy)) / 100)
    return interval

def upper_lower_p():
    alpha = 0.5 #because %95
    lower_p = alpha / 2
    upper_p = upper_p = (100 - alpha) + (alpha / 2.0)
    print("lower_p :", lower_p,"upper_p :", upper_p)

def plot_tree(tree_name, file_name):

    dot_data = StringIO()
    export_graphviz(tree_name, out_file=dot_data,
                    feature_names=X_train.columns,
                    filled=True, rounded=True,
                    special_characters=True)
    graph = pydotplus.graph_from_dot_data(dot_data.getvalue())
    Image(graph.create_png())

    graph.write_png(file_name)

def display(dataset):
    print("--------------","DEPTH 3","--------------------")
    gini_tree_depth_3 = with_gini_index(3)

    entropy_tree_depth_3 = with_entropy(3)

    gini_prediction = prediction(X_test, gini_tree_depth_3)
    print("Gini Accuracy is :")
    calculate_test_accuracy(gini_tree_depth_3)
    calculate_train_accuracy(gini_tree_depth_3)

    entropy_prediction = prediction(X_test, entropy_tree_depth_3)
    print("Entropy Accuracy is :")
    calculate_test_accuracy(entropy_tree_depth_3)
    calculate_train_accuracy(entropy_tree_depth_3)

    print("Interval of accuracy: ")
    interval = calculate_confidence_interval(calculate_test_accuracy(gini_tree_depth_3),data_1)
    print(interval)

    upper_lower_p()

    print("--------------","DEPTH 7","--------------------")
    gini_tree_depth_7 = with_gini_index(7)

    entropy_tree_depth_7 = with_entropy(7)

    gini_prediction = prediction(X_test, gini_tree_depth_7)
    print("Gini Accuracy is :")
    calculate_test_accuracy(gini_tree_depth_7)
    calculate_train_accuracy(gini_tree_depth_7)

    entropy_prediction = prediction(X_test, entropy_tree_depth_7)
    print("Entropy Accuracy is :")
    calculate_test_accuracy(entropy_tree_depth_7)
    calculate_train_accuracy(entropy_tree_depth_7)

    print("Interval of accuracy: ")
    interval = calculate_confidence_interval(calculate_test_accuracy(gini_tree_depth_7),data_1)
    print(interval)

    upper_lower_p()

    plot_tree(gini_tree_depth_7, "gini_data1_7.png")
    plot_tree(entropy_tree_depth_7, "entropy_data1_7.png")

    plot_tree(gini_tree_depth_3, "gini_data1_3.png")
    plot_tree(entropy_tree_depth_3, "entropy_data1_3.png")


#!/usr/bin/env python3
from sys import stderr, exit

from TALinputs import TALinput
from multilanguage import Env, Lang, TALcolors

from hanoi_lib import ConfigGenerator, HanoiTowerProblem
from utils_lang import get_formatted_move

# METADATA OF THIS TAL_SERVICE:
args_list = [
    ('v',str),
    ('start',str),
    ('final',str),
    ('n',int),
    ('answ',int),
    ('ok_if_congruent_modulus',int),
    ('silent',bool),
    ('feedback',str),
    ('with_certificate',bool),
]

ENV =Env(args_list)
TAc =TALcolors(ENV)
LANG=Lang(ENV, TAc, lambda fstring: eval(f"f'{fstring}'"))

# START CODING YOUR SERVICE:
# Get configurations
gen = ConfigGenerator()
start, final, error = gen.getConfigs(ENV['start'], ENV['final'], ENV['n'])

# Check errors
if error == 'n_not_valid':
    TAc.print(LANG.render_feedback("n_not_valid", f"If you use the all_* form for start and final, you must use a N >= 0."), "red", ["bold"])
    exit(0)
elif error == 'different_len':
    TAc.print(LANG.render_feedback("different_len", f'If you use a custom configuration for start and final, the length of start must be equal to the length of final'), "red", ["bold"])
    exit(0)

# Init Hanoi Tower
hanoi = HanoiTowerProblem(ENV['v'])

# Get the correct answer
modulus = ENV['ok_if_congruent_modulus']
user_answ = ENV['answ']
opt_answ = hanoi.getMinMoves(start, final)
if modulus != 0:
    overflow = (opt_answ >= modulus)
    mod_answ = opt_answ % modulus

# check the user answer
if modulus == 0 or not overflow: #case: not modulus or modulus irrilevant
    if user_answ == opt_answ:
        if not ENV['silent']:
            TAc.print(LANG.render_feedback("answ-equal", f'Nice! Your answer is equal to the optimal minimum number.'), "green", ["bold"])

    else:
        TAc.print(LANG.render_feedback("answ-wrong", f'Oh no! Your answer is not equal to the optimal minimum number.'), "red", ["bold"])

        # Provide feedback
        if ENV["feedback"] == "true_val":
            TAc.print(LANG.render_feedback("get-answ", f'The optimal minimum number of moves is {opt_answ}.'), "red", ["reverse"])

        elif ENV["feedback"] == "smaller_or_bigger":
            if opt_answ < user_answ:
                TAc.print(LANG.render_feedback("answ-less", f'The optimal minimum number of moves is smaller then your answer.'), "red", ["reverse"])
            else:
                TAc.print(LANG.render_feedback("answ-more", f'The optimal minimum number of moves is bigger then your answer.'), "red", ["reverse"])

        # Provide certificate
        if ENV["with_certificate"] == 1:
            if user_answ < opt_answ:
                TAc.print(LANG.render_feedback("use-check_lower_bounds", f'Use check_lower_bounds service for check it.'), "red", ["reverse"])
            else:
                TAc.print(LANG.render_feedback("certificate", f'This is a certificate of a solution with less moves:'), "red", ["reverse"])
                for e in hanoi.getNotOptimalMovesList(start, final, desired_size=(user_answ-1)):
                    TAc.print(LANG.render_feedback("certificate-line", f'{get_formatted_move(e, "extended", ENV["lang"])}'), "yellow", ["reverse"])

else: # case: modulus
    if user_answ == mod_answ:
        if not ENV['silent']:
            TAc.print(LANG.render_feedback("answ-equal-mod", f'Oh no! Your answer is equal to the optimal minimum number in modulo={modulus}.'), "red", ["bold"])

    else:
        TAc.print(LANG.render_feedback("answ-wrong-mod", f'Oh no! Your answer is not equal to the optimal minimum number in modulo={modulus}.'), "red", ["reverse"])

        # Provide feedback
        if ENV["feedback"] == "true_val":
            TAc.print(LANG.render_feedback("get-answ-mode", f'The optimal minimum number in modulo={modulus} of moves is {mod_answ} = {opt_answ} % {modulus}.'), "red", ["reverse"])

        elif ENV["feedback"] == "smaller_or_bigger":
            if mod_answ < user_answ:
                TAc.print(LANG.render_feedback("answ-less-mod", f'The optimal minimum number in modulo={modulus} of moves is smaller then your answer.'), "red", ["reverse"])
            else:
                TAc.print(LANG.render_feedback("answ-more-mod", f'The optimal minimum number in modulo={modulus} of moves is bigger then your answer.'), "red", ["reverse"])

        # Provide certificate
        if ENV["with_certificate"] == 1:
            if user_answ < opt_answ:
                TAc.print(LANG.render_feedback("use-check_lower_bounds", f'Use check_lower_bounds service for check it.'), "red", ["reverse"])
            else:
                TAc.print(LANG.render_feedback("certificate", f'This is a certificate of a solution with less moves:'), "red", ["reverse"])
                for e in hanoi.getNotOptimalMovesList(start, final, desired_size=len(user_answ) -1):
                    TAc.print(LANG.render_feedback("certificate-line", f'{get_formatted_move(e, "extended", ENV["lang"])}'), "yellow", ["reverse"])

exit(0)


# https://www.hackerrank.com/challenges/merge-the-tools/problem

if __name__ == '__main__':
    s = 'AABCAAADA'
    k = 3

    assert len(s) % k == 0, "The length of s must me dividable by k"

    substrings = [set(s[i*k:k*(i+1)]) for i in range(0, k)]
    for substr in substrings:
        print("".join(list(substr)))