import os import plotly.express as px import plotly.graph_objects as go import pandas as pd from dash import Dash, html, dcc, Input, Output, callback, dash_table import plotly.express as px import numpy as np from plotly.subplots import make_subplots import core df = pd.read_csv('https://raw.githubusercontent.com/plotly/datasets/master/gapminder_unfiltered.csv') debug = False external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css'] app = Dash(__name__, external_stylesheets=external_stylesheets) app.layout = html.Div([ dcc.Location(id='url', refresh=False), html.Div(id='page-content') ]) server = app.server wine_similarity_df = pd.read_csv('data/wine_similarity.csv') wine_list = wine_similarity_df['NAME'].unique() # wine_list.sort() geo = pd.read_csv('data/processed_wineyards.csv') wine_regions=list(geo['Region'].unique()) + ['all'] wine_country = list(geo['Country'].unique()) + ['all'] # local_type = list(geo['LOCAL TYPE'].unique()) + ['all'] user_rating_df = pd.read_csv('data/simulated_ratings.csv') user_rating_df.set_index('user', inplace=True) user_rating_df['user'] = user_rating_df.index user_ids = user_rating_df['user'] raw_ratings = pd.read_csv('data/raw_simulated_ratings.csv') # crop to 5 rows and 7 columns raw_ratings = raw_ratings.iloc[:5, :7] TEMPORARY_WINE_RECOMMENDATION_FORM_INFO = {} ## Layout ## dashboard_layout = html.Div([ # dcc.Link('About this project', href='/wiki'), html.H1(children='Wineyards around the world', style={'textAlign':'center'}), html.H1(children='Country', style={'textAlign':'center'}), dcc.Dropdown(wine_country, wine_country[-1], id='dropdown-wc'), html.H1(children='Region', style={'textAlign':'center'}), dcc.Dropdown(['all'],'all', id='dropdown-wr'), dcc.Graph(id='world-map-fig'), html.Div( [ html.Div( [ html.H1(children='Wine Recommender', style={'textAlign':'center'}), dcc.Dropdown(wine_list, wine_list[0], id='dropdown-selection'), html.H1(id='wine-recommendation', style={'textAlign':'center'}), ], className='six columns', ), html.Div( [ html.H1(children='something here', style={'textAlign':'center'}), ], className='six columns', ) ], className='row'), html.H1(children="Wine Recommender based on users' feedback", style={'textAlign':'center'}), html.Div( [ html.Div( [ dcc.Dropdown(user_ids, user_ids[0], id='dropdown-selection-user'), html.H4(id='wine-recommendation-from-user', style={'textAlign':'center'}), ], className='six columns', ), html.Div( [ html.H4(children='Data Example', style={'textAlign':'left'}), dash_table.DataTable( data=raw_ratings.to_dict('records'), columns=[{'id': c, 'name': c} for c in raw_ratings.columns] ) ], className='six columns', ), ], className='row'), html.H1(children="Wine Preferences Form", style={'textAlign':'center'}), html.Div( [ html.Div( [ #Dropdown for wine name dcc.Dropdown(wine_list, wine_list[0], id='dropdown-selection'), dcc.Input(id='input-wine-rating', type='number', placeholder='Enter wine rating', min=1, max=5), ], className='six columns', ), html.Div( [ html.Button('Submit', id='submit-button', n_clicks=0), ], className='six columns', ), ], className='row'), html.Div(id='recommended-wine-rating-info', style={'textAlign':'center'}), html.Button('Reset', id='recommend-wine-from-form-reset', n_clicks=0), # Button to submit the form html.Button('Recommend Wine', id='recommend-wine-from-form', n_clicks=0), html.Div(id='recommended-wine-form-output', style={'textAlign':'center'}), ], style={'background-color': '#333', 'font-family': 'Fantasy', 'color': '#999', 'padding': '10px'}) @app.callback( Output('recommend-wine-from-form-reset', 'n_clicks'), Input('recommend-wine-from-form-reset', 'n_clicks'), ) def reset_form(n_clicks): print(n_clicks) if n_clicks > 0: TEMPORARY_WINE_RECOMMENDATION_FORM_INFO.clear() return 0 @app.callback( Output('recommended-wine-rating-info', 'children'), Output('submit-button', 'n_clicks'), Input('submit-button', 'n_clicks'), Input('dropdown-selection', 'value'), Input('input-wine-rating', 'value'), ) def update_output(n_clicks, wine_name, rating): if n_clicks > 0: TEMPORARY_WINE_RECOMMENDATION_FORM_INFO[wine_name] = rating return f'You rated {wine_name} with a score of {rating}', 0 return '', 0 @app.callback( Output('recommended-wine-form-output', 'children'), Output('recommend-wine-from-form', 'n_clicks'), Input('recommend-wine-from-form', 'n_clicks'), ) def recommend_wine_from_form(n_clicks): if n_clicks > 0: user = "temporary_user" user_rating_df.loc[user] = TEMPORARY_WINE_RECOMMENDATION_FORM_INFO user_rating_df.fillna(0, inplace=True) user_rating_df['user'] = user_rating_df.index user_rating_df['cluster'] = core.get_most_similar_user_clust(user_rating_df, user) wine_recommendation_from_user = core.recommend_wine_from_users(user_rating_df, user, 3) wine_recommendation_from_user = f"Based on user form, we recommend: "+"; ".join(wine_recommendation_from_user) return wine_recommendation_from_user, 0 return '', 0 @app.callback( Output('dropdown-wr', 'options'), Input('dropdown-wc', 'value') ) def set_wine_type_options(selected_wine_type): # Return the options based on the selected wine country pattern = r'.*' if selected_wine_type == 'all' else rf'{selected_wine_type}' return list(geo[geo['Country'].str.match(pattern, na=False)]['Region'].unique()) + ['all'] wiki_layout = html.Div([ dcc.Link('Dashboard', href='/'), html.H1('About this project'), html.Div([ html.Div([ html.H3('What is this project about?'), html.P('We are a group of 4 Computer Science Engineering Students with a solid Artificial Intelligence background.'), html.P('This project aims to showcase AI applications for improving Wine Tourism for SOGRAPE.'), html.H3('\'Bout us'), html.Img(src='/assets/tourdevino_logo.webp', style={'width': '40%', 'height': 'auto', 'display': 'block', 'margin-left': 'auto', 'margin-right': 'auto'}), html.P('This project was developed by a team of 4, in the context of the SOGRAPE 2024 hackathon.'), html.P('The team members are:'), html.H4('Rui Melo'), html.H4('André Catarino'), html.H4('Dinis Costa'), html.H4('Paulo Fidalgo'), ], className='six columns'),], className='row'), ], style={'background-color': '#333', 'font-family': 'Fantasy', 'color': '#999', 'padding': '10px'} ) # Update the index @callback(Output('page-content', 'children'), Input('url', 'pathname')) def display_page(pathname): if pathname == '/': return dashboard_layout elif pathname == '/wiki': return wiki_layout else: return '404' # You could also return a 404 "URL not found" page here @app.callback( Output('wine-recommendation', 'children'), Output('world-map-fig', 'figure'), Output('wine-recommendation-from-user', 'children'), Input('dropdown-selection', 'value'), Input('dropdown-wr', 'value'), Input('dropdown-wc', 'value'), Input('dropdown-selection-user', 'value') ) def update_graph(value,wr,wc, user_value): ### Wine Recommendation ### recommended_wines = None if value: recommended_wines = core.get_top_5_similar_wines(value, wine_similarity_df)[1:] recommended_wines = f"Based on ´{value}´, we recommend: "+"; ".join(recommended_wines) ## Wine Recommendation from users feedback wine_recommendation_from_user = core.recommend_wine_from_users(user_rating_df, user_value, 3) wine_recommendation_from_user = f"Based on user information, we recommend: "+"; ".join(wine_recommendation_from_user) ### World Map of wineyards ### geo_df = pd.read_csv('data/processed_wineyards.csv') wr = r'.*' if wr == 'all' else rf'{wr}' wc = r'.*' if wc == 'all' else rf'{wc}' geo_df = geo_df[geo_df['Region'].str.contains(wr, case=False, na=False, regex=True) & geo_df['Country'].str.contains(wc, case=False, na=False, regex=True)] world_map_fig = px.scatter_map(geo_df, lat=geo_df['coord_x'], lon=geo_df['coord_y'], hover_name=geo_df['name'], zoom=4, hover_data={ 'IsTouristic': True, 'Wine Type': True, 'Country': True, 'Region': True, 'Address': True, }, title='Wineyards around the world', ) world_map_fig.update_layout(height=800, width=1200) return recommended_wines, world_map_fig, wine_recommendation_from_user if __name__ == "__main__": app.run_server(host="0.0.0.0", port="8050", debug=debug)