#@toggleInputDefault
from IPython.display import HTML
#Methode zum togglen(show/hide) markierter Inputs/Outputs mit jQuery(html,css)
HTML('''<script>
function toggler(){
if(window.already_toggling){
// Don't add multiple buttons. Needed for in notebokk use.
return 0;
}
/*quick way if all inputs/outputs get a button
let btnInput = $('.cell').prepend('<button>👁Toggle this input</button>').children('button');
let btnOutput = $('.output_wrapper').prepend('<button>Toggle this output</button>').children('button');
*/
//add buttons to inputs/outputs with @toggleInput/@toggleOutput #comments
let btnInput;
let btnOutput;
if ($('ipython-main-app') == true) {
//#comment is class .cm-comment in notebook
btnInput = $('..cm-comment:contains(@toggleInput)').closest('.cell')
.prepend('<button>Hide this input</button>').children('button');
btnOutput = $('..cm-comment:contains(@toggleOutput)').closest('.cell').children('.output_wrapper')
.prepend('<button>Hide this output</button>').children('button');
} else {
//#comment is class .c1 in html!
btnInput = $('.c1:contains(@toggleInput)').closest('.cell')
.prepend('<button>Hide this input</button>').children('button');
btnOutput = $('.c1:contains(@toggleOutput)').closest('.cell').children('.output_wrapper')
.prepend('<button>Hide this output</button>').children('button');
}
//store alternative text to change/swap on toggle
btnInput.data("altText", "Show this input");
btnOutput.data("altText", "Show this output");
//style buttons
btnInput.css({
"background-color": "transparent",
"color": "#303F9F",
"border": "1px solid #303F9F",
"margin-bottom": "5px"
});
btnOutput.css({
"background-color": "transparent",
"color": "#D84315",
"border": "1px solid #D84315",
"margin-top": "5px"
});
//add button hover functionality/style
btnInput.hover(function(){
$(this).css({"opacity": "0.5"});
}, function(){
$(this).css({"opacity": "1"});
});
btnOutput.hover(function(){
$(this).css({"opacity": "0.5"});
}, function(){
$(this).css({"opacity": "1"});
});
//add button click functionality (toggle + textchange)
btnInput.on('click', function(){
$(this).siblings('.input').slideToggle();
let oldText = $(this).text();
$(this).text($(this).data("altText"));
$(this).data("altText", oldText);
})
btnOutput.on('click', function(){
$(this).siblings('.output').slideToggle();
let oldText = $(this).text();
$(this).text($(this).data("altText"));
$(this).data("altText", oldText);
})
//hide inputs/outputs with #comments @toggleInputDefault/@toggleOutputDefault
//#comment is class .cm-comment in notebook and .c1 in html!
//#@toggleInputDefault
$('.cm-comment:contains(@toggleInputDefault)').closest('.cell')
.children('button').trigger('click');
$('.c1:contains(@toggleInputDefault)').closest('.cell')
.children('button').trigger('click');
//#@toggleOutputDefault
$('.cm-comment:contains(@toggleOutputDefault)').closest('.cell').children('.output_wrapper')
.children('button').trigger('click');
$('.c1:contains(@toggleOutputDefault)').closest('.cell').children('.output_wrapper')
.children('button').trigger('click');
window.already_toggling = true;
}
$( document ).ready(toggler);
</script>''')
#@toggleInputDefault
from tqdm import tqdm
import requests
from bs4 import BeautifulSoup
from imdb import IMDb
from collections import OrderedDict
#@toggleInputDefault
r = requests.get('http://www.imdb.com/chart/tvmeter?ref_=nv_tvv_mptv_4')
c = r.content
soup = BeautifulSoup(c, 'html.parser')
#@toggleInputDefault
#Tabelle mit den Serien finden
table = soup.find('tbody', 'lister-list')
#alle Einträge erfassen
titleColumns = table.findAll('td', 'titleColumn')
#@toggleInputDefault
#@toggleOutputDefault
series_data = {}
imdb = IMDb()
rank = 1
#Einträge durchgehen und Dict mit den IDs füllen
for titleColumn in tqdm(titleColumns):
#<a>-Tag im Eintrag finden
a = titleColumn.find('a')
#in der dort zu findenden URL die Stelle der ID herausfinden
i = a['href'].find('tt')
#ID herauskopieren
iid = a['href'][i+2:i+9]
m = imdb.get_movie(iid)
#den Titel über die IMDb-Biblio nehmen, damit Original Titel und nicht deutscher von der Webseite
title = m['title']
#Serie mit Titel und ID in der gewünschten Struktur im Dict abspeichern
dichelp = {}
dichelp['id'] = iid.encode('ascii','ignore') #ID braucht kein U-Code, simpler String also
dichelp['rank'] = rank
#movieObject auch gleich abspeichern, um später nicht erneut aufrufen zu müssen!
dichelp['movieObject'] = m
rank += 1
if title in series_data:#falls Title schon gehabt
for name, series in list(series_data.iteritems()):
if title == name:#Serie mit gleichem Title raussuchen
if iid != series['id']:#prüfen ob das die gleiche Serie ist oder nicht
series_data[name+' ('+str(series['movieObject']['year'])+')'] = series_data.pop(name)
series_data[title+' ('+str(m['year'])+')'] = dichelp #falls nicht Namensvariationen benutzen ansonsten(gleiche Serie) ignorieren
else:
series_data[title] = dichelp
#@toggleInputDefault
#@toggleOutputDefault
series_data
#@toggleInputDefault
#@toggleOutputDefault
for series_name,series in tqdm(list(series_data.iteritems())):
if not 'movieObject' in series:
series['movieObject'] = imdb.get_movie(series['id'])
if not 'episodes_total' in series:
#mehr Informationen einholen
m = series['movieObject']
#versuche Rating zu finden
try:
series['rating_series'] = m['rating']
except KeyError:
print(m, 'hat kein Rating?, update vote details!')
imdb.update(m, 'vote details')
try:
series['rating_series'] = m['rating']
except KeyError:
print(m, 'keine Hoffnung...')
series['rating_series'] = 0.5
imdb.update(m, 'episodes')
#speicher die Staffelanzahl, falls vorhanden
try:
series['seasons'] = m['number of seasons']
except KeyError:
series['seasons'] = 0
seasons_episodes_structure = {}
episodes_total_counter = 0
for season in m['episodes']:
episodes_season_counter = 0
for j in m['episodes'][season]:
episodes_season_counter += 1
#print counter, "episoden in Staffel ",season
#speicher Episodenanzahl dieser Staffel
seasons_episodes_structure[season] = episodes_season_counter
episodes_total_counter += episodes_season_counter
#speicher die Episodenanzahl der ganzen Serie
series['episodes_total'] = episodes_total_counter #evtl. besser m['number of episodes']
series['seasons_episodes_structure'] = seasons_episodes_structure
#jetzt Aufbau ist = {'series_name': series{'id', 'rating_series', 'episodes_total', 'seasons',
#'seasons_episodes_structure': {staffelnummer: episodenanzahl, staffelX-mal}}}
#@toggleInputDefault
#@toggleOutputDefault
from bokeh.plotting import figure, output_file, show, ColumnDataSource
from bokeh.io import output_notebook
output_notebook() #damit figure im notebokk angezeigt wird
from bokeh.models import CustomJS, HoverTool, LinearColorMapper, ColorBar, Renderer, Label, LabelSet, FixedTicker, Range1d, Span, BoxAnnotation
from bokeh.models.glyphs import HBar
from bokeh.charts import Bar
from bokeh.layouts import layout
from bokeh.palettes import viridis, inferno
from bokeh.models.widgets import Toggle
import ipywidgets as widgets
from IPython.display import display
#@toggleInputDefault
def get_data_serie_plot(iid):
print 'Get IMDbs Episodes Information for Serie',iid
plot_serie = {}
#finde die Serie und ihre bisherigen Daten in dem Serien-Dict
for name, serie in list(series_data.iteritems()):
if serie['id'] == iid:
plot_serie[name] = serie
break
#füge der Serie die Episoden-Daten hinzu
for name, serie in list(plot_serie.iteritems()):
serie['episodes'] = {}
m = serie['movieObject']
average_episode_rating = 0
rated_episodes = 0
for season in tqdm(m['episodes']):
serie['episodes'][season] = {}
for episode in m['episodes'][season]:
serie['episodes'][season][episode] = {}
e = m['episodes'][season][episode]
imdb.update(e, 'main')
#MovieObject der Episode abspeichern
serie['episodes'][season][episode]['episode_object'] = e
imdb.update(e, 'vote details')
#Rating der Episode
try:
serie['episodes'][season][episode]['episode_rating'] = e.get('rating')
except KeyError:
serie['episodes'][season][episode]['episode_rating'] = None #0
#print season, episode, serie['episodes'][season][episode]['episode_rating']
#Fälle wo Episoden noch kein Rating (da sie nur angekündigt, aber noch nicht erschienen sind) abfangen
if serie['episodes'][season][episode]['episode_rating'] != None:
rated_episodes += 1
average_episode_rating += serie['episodes'][season][episode]['episode_rating']
#Titel der Episode
serie['episodes'][season][episode]['episode_title'] = e.get('title')
#Durchschnitts-Rating aller gerateden Episoden
if rated_episodes != 0:
serie['average_episode_rating'] = average_episode_rating/rated_episodes
else:
serie['average_episode_rating'] = 0.5
#Differenz zwischen des Serien- und des Episodendurschnittsratings
serie['difference_average_episode_to_serie_rating'] = round(serie['average_episode_rating']-serie['rating_series'], 2)
#Anzahl der gerateden Episoden
serie['rated_episodes'] = rated_episodes
#füge die Daten auch gleich in das große Serien Dict
for na, se in list(series_data.iteritems()):
if se['id'] == iid:
se['episodes'] = serie['episodes']
#gib das Dict, das nur die gewünschte Serien mit ihren Daten erhält zurück
return plot_serie
print 'definiert'
#@toggleInputDefault
#Methode für Serien-Episode-Plot
def serie_episodes_figure(requestedSerie_id):
#gewünschte Serie erhalten
serie_detailed = get_data_serie_plot(requestedSerie_id)
#Struktur der Daten für Chart definieren
source = ColumnDataSource(data=dict(
episodes_ratings = [],
episodes_titles = [],
episodes_numbers = [],
episodes_seasonic = [],
season = [],
season_episode_numeration = []
))
#passende/benötigte Daten aus Serien-Dict an Source für Chart übergeben
for name, serie in list(serie_detailed.iteritems()):
average_episode_rating = 0
episode_counter = 0
min_rating = 10
max_rating = 0
serie_title = name
serie_rating = serie['rating_series']
serie_structure = serie['seasons_episodes_structure']
season_tracker = 0
episodes_in_seasonic = []
in_season_episode_numeration = []
for season in serie['episodes']:
season_tracker = season
for episodes in serie['episodes'][season]:
ep_rat = serie['episodes'][season][episodes]['episode_rating']
source.data['episodes_ratings'].append(ep_rat)
#SEpisoden ohne Rating keinen Einfluss auf min und max für Layout natürlich
if ep_rat != None:
if ep_rat < min_rating:
min_rating = ep_rat
if ep_rat > max_rating:
max_rating = ep_rat
source.data['episodes_titles'].append(serie['episodes'][season][episodes]['episode_title'])
episode_counter +=1
source.data['episodes_numbers'].append(episode_counter)
source.data['episodes_seasonic'].append(episodes)
episodes_in_seasonic.append(str(episodes))
source.data['season'].append(season_tracker)
source.data['season_episode_numeration'].append(str(season)+'.'+str(episodes))
in_season_episode_numeration.append(str(season)+'.'+str(episodes))
tot_episodes = serie['episodes_total']
average_episode_rating = round(serie['average_episode_rating'], 2)
#definieren der Ranges des Plots mit Grenzen, damit man nicht zuweit oder zunah zoomt und scrollt
if episode_counter <= 100:
x_start = episode_counter
else:
x_start = 100
x_range = Range1d(0, x_start, bounds = (0, episode_counter+2), min_interval = 10, max_interval = episode_counter+2)
y_range = Range1d(min_rating-1, max_rating+1, bounds = (0, 11), min_interval = 1, max_interval = 10)
p = figure(x_range = x_range, y_range = y_range,
tools = ['hover', 'tap', 'xwheel_pan', 'xwheel_zoom', 'wheel_zoom', 'pan', 'resize', 'reset'],
active_scroll = 'xwheel_zoom', title = serie_title, x_axis_location = 'below', plot_width = 900)
p.title.text_font_size = '18pt'
hover = p.select_one(HoverTool)
#Definieren der Tooltips beim Hovern
hover.tooltips = [('Episode Title', '@episodes_titles'), ('Episode Rating', '@episodes_ratings{1.1}'),
('Season.Episode', '@season'+'.'+'@episodes_seasonic')]
#Line- und Kreis-Chart aus den Daten generieren
p.line('episodes_numbers', 'episodes_ratings', line_width = 2, source = source)
p.circle('episodes_numbers', 'episodes_ratings', size = 7, source = source)
#Grids anpassen
p.ygrid.band_fill_alpha = 0.1
p.ygrid.band_fill_color = 'green'
p.xgrid.grid_line_color = None
p.ygrid.grid_line_color = None
p.ygrid.ticker = FixedTicker(ticks = range(0, 11, 1))
#Achsen benennen und deren Nummerierung anpassen
p.xaxis.axis_label = 'Episodes'
p.yaxis.axis_label = 'IMDb Rating'
p.yaxis.ticker = FixedTicker(ticks = range(0, 11, 1))
p.yaxis.major_tick_line_color = None
p.xaxis.axis_line_color = None
p.yaxis.axis_line_color = None
#unötige/störende Outline weg
p.outline_line_color = None
#Annotations für die Staffel
helper = 1
for season, episodes in list(serie_structure.iteritems()):
left = helper
right = left + episodes -1
helper = right + 1
box = BoxAnnotation(left = left, right = right, fill_alpha = 0.1, fill_color = 'red', line_color = None)
p.add_layout(box)
label = Label(x = left+((right-left)/2), y = min_rating-0.3, text = 'Season '+str(season),
text_baseline = 'hanging', text_align = 'center')
p.add_layout(label)
#Annotations für Episode-Average-Rating- und Serien-Rating-Linie
ep_av_ra_line = Span(location = average_episode_rating, dimension = 'width', visible = False,
line_color = 'green', line_dash = 'dashed', line_width = 2)
p.add_layout(ep_av_ra_line)
se_ra_line = Span(location = serie_rating, dimension = 'width', visible = False,
line_color = 'red', line_dash='dashed', line_width = 2)
p.add_layout(se_ra_line)
#simple Toggle-Knöpfe zum Steuern der Anzeigen der Episode-Average-Rating- und der Serien-Rating-Linie
code = '''object.visible = toggle.active'''
callback1 = CustomJS.from_coffeescript(code = code, args = {})
toggle_ep_av_ra = Toggle(label = 'Average Episode Rating ('+str(average_episode_rating)+')', button_type = 'success',
callback = callback1, active = False)
callback1.args = {'toggle': toggle_ep_av_ra, 'object': ep_av_ra_line}
callback2 = CustomJS.from_coffeescript(code = code, args = {})
toggle_se_ra = Toggle(label = ('Serie Rating ('+str(serie_rating)+')'), button_type = 'success',
callback = callback2, active = False)
callback2.args = {'toggle': toggle_se_ra, 'object': se_ra_line}
print 'requested figure completed'
return layout([p], [toggle_ep_av_ra, toggle_se_ra])
print 'definiert'
#@toggleInputDefault
#Methode für 2 Charts zum Vergleich der Rating-Differenzen
def rating_difference_comparison_figure():
#color_mapper = LinearColorMapper(low = -10, high = 10, palette = viridis(4))
color_mapper_diff = LinearColorMapper(low = -10, high = 10, palette = ['red', 'green'])
#Struktur der Daten für Chart definieren
source = ColumnDataSource(data=dict(
rank = [],
rating = [],
seasons = [],
episodes = [],
title = [],
iid = [],
rating_diff = [],
episodes_rating = []
))
bar_dict = {
'title' : [],
'ratings' : [],
'groups' : []
}
titles = []
#passende/benötigte Daten aus Serien-Dict an Source für Chart übergeben
#print 'Gathering Figure-Source-Data for Ranking-Chart'
for name, serie in list(series_data.iteritems()):
if 'difference_average_episode_to_serie_rating' in serie:
source.data['rank'].append(serie['rank'])
source.data['rating'].append(serie['rating_series'])
source.data['seasons'].append(serie['seasons'])
source.data['episodes'].append(serie['episodes_total'])
source.data['title'].append(name)
source.data['iid'].append(serie['id'])
source.data['rating_diff'].append(serie['difference_average_episode_to_serie_rating'])
source.data['episodes_rating'].append(serie['average_episode_rating'])
titles.append(name)
bar_dict['title'].append(name)
bar_dict['title'].append(name)
bar_dict['title'].append(name)
bar_dict['ratings'].append(serie['rating_series'])
bar_dict['ratings'].append(serie['average_episode_rating'])
bar_dict['ratings'].append(serie['difference_average_episode_to_serie_rating'])
bar_dict['groups'].append('Serie Rating')
bar_dict['groups'].append('Episodes Rating')
bar_dict['groups'].append('Rating Difference (Episodes-Serie)')
#
###Bar-Chart mit Balken für Serien-, Episodes-Rating und Differnez
bar_chart = Bar(bar_dict, values = 'ratings', label = 'title', group = 'groups', title = 'Rating Difference (Bar-Chart)',
xlabel = 'Serie Title', ylabel = 'Rating', tools = ['hover', 'xwheel_pan', 'reset'],
active_scroll = 'xwheel_pan', plot_width = 900)
hover_bar = bar_chart.select_one(HoverTool)
hover_bar.tooltips = [('Title', '@title'), ('Rating', '@height{1.11}'), ('What', '@groups')]
#kucken ob sinnvoll bei bar-Chart
#Labels definieren und hinzufügen
labels = LabelSet(x = 'title', y = 0, text = 'title', y_offset = -25,
text_font_size = "16pt", text_color = 'grey', text_baseline = 'hanging',
source = source, text_align = 'center')
bar_chart.add_layout(labels)
bar_chart.xaxis.major_label_text_font_size = '0pt' #da Serien-Titel im Chart, können die ander Achse weg
#unötige/störende Outline weg
bar_chart.outline_line_color = None
#Achsen anpassen
bar_chart.xaxis.axis_line_color = None
bar_chart.yaxis.axis_line_color = None
bar_chart.xaxis.major_tick_line_color = None
#
###Figure mit Linie und Kreis für Rating-Differenz
p = figure(y_range = titles, x_range = (-3,3), tools = ['hover', 'tap', 'pan', 'ywheel_pan', 'xwheel_zoom', 'reset'],
active_scroll = 'ywheel_pan',
title = 'Difference between episodes-average- and series-rating', x_axis_location = 'above', plot_width = 900)
p.title.text_font_size = '18pt'
hover = p.select_one(HoverTool)
p.segment(0, 'title', 'rating_diff', 'title', line_width = 3,
line_color = {'field': 'rating_diff', 'transform': color_mapper_diff},
source = source)
p.circle('rating_diff', 'title', size = 25, fill_color = {'field': 'rating_diff', 'transform': color_mapper_diff},
line_color = {'field': 'rating_diff', 'transform': color_mapper_diff}, line_width=3, source = source)
#Definieren der Tooltips beim Hovern
hover.tooltips = [('Title', '@title'), ('Serie Rating', '@rating{1.11}'),
('Episodes Rating', '@episodes_rating{1.11}'), ('Rating Difference', '@rating_diff{1.11}')]
#Labels bei Differenz-only-Chart für Serien Titel
labels = LabelSet(x = 0, y = 'title', text = 'title', text_align = 'center', text_color = 'black',
level = 'glyph', y_offset = 20, source = source)
p.add_layout(labels)
p.yaxis.major_label_text_font_size = '0pt' #da Serien-Titel im Chart, können die ander Achse weg
#Grids anpassen
p.xgrid.band_fill_alpha = 0.2
p.xgrid.band_fill_color = 'black'
p.ygrid.grid_line_color = None
p.xgrid.grid_line_color = 'grey'
p.xgrid.ticker = FixedTicker(ticks = range(-10, 11, 10))
#Achsen benennen und deren Nummerierung anpassen
p.xaxis.axis_label = 'Rating Difference (Episodes - Serie)'
p.yaxis.axis_label = 'Serie Title'
p.yaxis.major_tick_line_color = None
p.xaxis.axis_line_color = None
p.yaxis.axis_line_color = None
#unötige/störende Outline weg
p.outline_line_color = None
#Chart anzeigen
#show(p)
#show(bar_chart)
l = layout([[p], [bar_chart]])
#show(l)
print 'requested figure completed'
return l
print 'definiert'
#@toggleInputDefault
#@toggleOutput
plot = figure(x_range = (0, 10), y_range = (10, 0),tools = ['hover', 'tap', 'ywheel_pan', 'ypan'], active_scroll = 'ywheel_pan',
title = 'Popular Series on IMDb', x_axis_location = 'above', plot_width = 900)
plot.title.text_font_size = '18pt'
color_mapper = LinearColorMapper(low = 0.0, high = 10.0, palette = viridis(256))
hover = plot.select_one(HoverTool)
#Struktur der Daten für Chart definieren
source = ColumnDataSource(data=dict(
rank = [],
rating = [],
seasons = [],
episodes = [],
title = [],
))
#passende/benötigte Daten aus Serien-Dict an Source für Chart übergeben
for name, serie in list(series_data.iteritems()):
source.data['rank'].append(serie['rank'])
source.data['rating'].append(serie['rating_series'])
source.data['seasons'].append(serie['seasons'])
source.data['episodes'].append(serie['episodes_total'])
source.data['title'].append(name)
#Horizontale-Balken-Chart aus den Daten generieren
plot.hbar(y = 'rank', height = 0.9, left = 0, right = 'rating', fill_color = {'field': 'rating', 'transform': color_mapper},
line_color = None, source = source)
#Definieren der Tooltips beim Hovern
hover.tooltips = [("Title", "@title"), ("Number of seasons", "@seasons"), ("Number of episodes", "@episodes"),
("Rating", "@rating{1.1}")]
#Defineren des Selektierenverhaltens
#kann auch gleich in p.hbar() definiert werden
#evtl. sinnlos und sollte weg?
selected_bar = HBar(fill_alpha = 1)
nonselected_bar = HBar(fill_alpha = 0)
Renderer.selection_glyph = selected_bar
Renderer.nonselection_glyph = nonselected_bar
#Labels mit den Serien-Titeln definieren und hinzufügen
labels = LabelSet(x = 0.1, y = 'rank', text = 'title',
text_font_size = "16pt", text_color = 'black',
source = source, text_align = 'left', text_baseline = 'middle')
plot.add_layout(labels)
#Grids anpassen
plot.xgrid.band_fill_alpha = 0.1
plot.xgrid.band_fill_color = 'green'
plot.xgrid.grid_line_color = None
plot.ygrid.grid_line_color = None
plot.ygrid.ticker = FixedTicker(ticks = range(0, 11, 1))
#Achsen benennen und deren Nummerierung anpassen
plot.xaxis.axis_label = 'IMDb Rating'
plot.yaxis.axis_label = 'IMDb Popularity Rank'
plot.xaxis.ticker = FixedTicker(ticks = range(0, 11, 1))
plot.yaxis.ticker = FixedTicker(ticks = range(1, 101, 1))
plot.xaxis.major_tick_line_color = None
plot.yaxis.major_tick_line_color = None
plot.xaxis.axis_line_color = None
plot.yaxis.axis_line_color = None
#unötige/störende Outline weg
plot.outline_line_color = None
#ColorBar zur Erklärung der Balkenfarben hinzufügen
color_bar = ColorBar(color_mapper = color_mapper, #ticker = ContinuousTicker(),
label_standoff = 12, border_line_color = None, location = (0,0), orientation = 'horizontal')
plot.add_layout(color_bar, 'below')
#Chart zeigen
show(plot)
###
#Widgets UI und Funktionen zur Generierung zusätzlicher Serie-/Episoden-Plots durch den Nutzer
###
def btn_clk_episodes(widget):
#print dropdownWidget.value
show(serie_episodes_figure(dropdownWidget.value))
#notebook Ausgabe aktualisieren
#push_notebook(handle = handle)
def btn_clk_comparison(widget):
show(rating_difference_comparison_figure())
#simplerer Serie-Dropdown-Liste aus Serien Dict erstellen
dropdown = OrderedDict()
first_value = 0
#print 'Generating DropdownWidget (rankordered)'
for name, serie in sorted(series_data.items(), key=lambda x: x[1]['rank']):
dropdown[str(serie['rank'])+' : '+name] = serie['id']
if first_value == 0:
first_value = serie['id']
dropdownWidget = widgets.Dropdown(options = dropdown, value = first_value, description = 'Select Serie:',
layout = widgets.Layout(flex = '1 1 auto'), width = 'auto')
buttonWidget = widgets.Button(description = 'update Serie/Episode Chart',
layout = widgets.Layout(flex = '1 1 auto'), width = 'auto')
buttonWidget.on_click(btn_clk_episodes)
buttonWidgetDiffComp = widgets.Button(description = 'Comparison chart: Rating Difference (of updated series)',
layout = widgets.Layout(flex = '1 1 auto'), width = 'auto')
buttonWidgetDiffComp.on_click(btn_clk_comparison)
#Widgets layouten
box_layout = widgets.Layout(display = 'flex', flex_flow = 'row', align_items = 'stretch', width = '75%',
justify_content = 'center')
box = widgets.Box(children = [dropdownWidget, buttonWidget], layout = box_layout)
box_layout2 = widgets.Layout(display='flex', flex_flow='column', align_items='stretch', width='75%')
box2 = widgets.Box(children = [box, buttonWidgetDiffComp], layout = box_layout2)
display(box2)
#display(dropdownWidget, buttonWidget)