# Build AppViewer
# from jupyterlab_dash import AppViewer
# viewer = AppViewer()

from utils import get_state_codes, get_state_name, daily_increase, moving_average
from utils import all_states, state_code_dict, state_map_dict, fip_to_county, fip_to_state

import dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output

import json
import numpy as np
import pandas as pd
from functools import reduce
from datetime import datetime
from urllib.request import urlopen

import plotly.graph_objects as go
import plotly.express as px
from plotly.subplots import make_subplots

from database import fetch_all_db_as_df

# Definitions of constants. This projects uses extra CSS stylesheet at `./assets/style.css`
COLORS = ['rgb(67,67,67)', 'rgb(115,115,115)', 'rgb(49,130,189)', 'rgb(189,189,189)']
external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css', '/assets/style.css']
colors = {
"cases": 'rgba(80, 26, 80, 0.2)',
"deaths": 'rgba(16, 112, 2, 0.2)'
}
colors_bar = {
"cases": 'mediumturquoise',
"deaths": 'tomato'
}
colors_line = {
    "cases": "mediumturquoise",
    "deaths": "tomato"
}
colors_text = {
    "cases": "purple",
    "deaths": "olivedrab"
}

with urlopen('https://raw.githubusercontent.com/plotly/datasets/master/geojson-counties-fips.json') as response:
    county_json = json.load(response)

# Define the dash app first
app = dash.Dash(__name__, external_stylesheets=external_stylesheets)

df_dict = fetch_all_db_as_df()

# Define component functions

def page_header():
    """
    Returns the page header as a dash `html.Div`
    """
    return html.Div(id='header', children=[
        html.Div([html.H3('DATA1050 Final Project')],
                 className="ten columns"),
        html.A([html.Img(id='logo', src=app.get_asset_url('github.png'),
                         style={'height': '35px', 'paddingTop': '7%'}),
                html.Span('Old boys', style={'fontSize': '2rem', 'height': '35px', 'bottom': 0,
                                                'paddingLeft': '4px', 'color': '#a3a7b0',
                                                'textDecoration': 'none'})],
               className="two columns row",
               href='https://github.com/cengc13/data1050-final-project'),
    ], className="row")

def project_description():
    """
    Returns overall project description in markdown
    """
    return html.Div(children=[dcc.Markdown('''
        # US COVID-19 Tracker

        The coronavirus pandemic has caused more than one and half million deaths over the world.
        The COVID-19 has exhausted the United States, and it seems a dark and deadly winter is waiting ahead.
        Therefore, it is of crucial importance to understand and project the trend of COVID-19 cases in US
        so that policy-makers can come up with short-term and long-term strategies to limit the spread and
        mitigate the effect of another outbreak in the near future.

        **US COVID-19 tracker is also a tool to assist making strategies.**
        It can be used to understand what factors might affect the spread of the pandemic in US
        and project the trend if more precautions and restrictions are imposed.

        ## Data Source
        Covid-19 tracker mainly utilizes historical and live covid-19 data from
        [New York Times github repository](https://github.com/nytimes/covid-19-data).
        The hirarchical case and death [data](https://raw.githubusercontent.com/nytimes/covid-19-data/master/us.csv)
        **is regularly updated every day**.

        Also, the data for state and county population is merged to obtain the positive rate over population at different
        geographical levels.

        Additionally, the [survey data](https://raw.githubusercontent.com/nytimes/covid-19-data/master/mask-use/mask-use-by-county.csv)
        by New York time on maks use by county is investigated to see if there exists a
        correlation between the outbreak and mask use frequency in each state. All data sets in this project are well-structured.
        ''', className='eleven columns', style={'paddingLeft': '5%'})], className="row")

def visualization_description():
    """
    Returns the text and plots of EDA and interactive visualization of this project.
    """
    return html.Div(children=[
      dcc.Markdown('''
            ## EDA & Interactive Visualization
            This project uses `Dash` and `Plotly` for visualization. We use the
            high-level components/tools in Dash to provide compact figures which allow user
            to choose what to display. For example, we utilized radio items to select targets
            (case or death number), dropdown layout to select the state, and slider to select
            time points.

            Curve plots are used to show the time variation of cumulative and daily reported
            cases and deaths for the  national-level and state-level covid-19 data. Heat maps are used
            to track the outbreak geographically.

        ''', className='row eleven columns', style={'paddingLeft': '5%'}),
    ]
    )

def enhancement_description():
    """
    Returns the text and plots of Enhancements of this project.
    """
    return html.Div(children=[
      dcc.Markdown('''
      ## Enhancement
      Public health experts suggest that face coverings can substantially slow the transmission
      of covid. In this section, we firstly use a heatmap to show the propensity of people to wear
      masks in each county. This heat map is based on the survey data from a large number of interviews
      conducted by the global data and survey firm Dynata at the request of The New York Times.

      Next, we attempt to understand what factors might affect the spread of the pandamic in US states.
      For this analysis, we select two responsive variables including case fatality rate and infection rate,
      and two predictors, namely average wear-mask probability and population density. A simple and intuitive
      linear correlation analysis is conducted. For Covid data, we use the latest state-level data for demonstration.
      In order to obtain the state-level mask-use data, the county-level data is aggregated over states and we take
      the average of features in each state to get the state-level features.

        ''', className='row eleven columns', style={'paddingLeft': '5%'}),
    ]
    )

# Defines the dependencies of interactive components
@app.callback(Output('time-series-total', 'figure'),
             Input('target-label', 'value'))
def time_series_cumulative(label):
    df = df_dict['covid-us']
    x = df['date']
    trace = go.Scatter(x=x, y=df[label], mode='lines', name=label, fill='tozeroy',
                       fillcolor=colors[label],
                       line={'width': 2, 'color': colors[label]},
                       hovertemplate='%{x|%b %d, %Y}
 %{y:-.0f}'
                      )

    title = f'Cumulative Covid {label.lower()} in U.S. over time'
    layout = dict(title=title,
                  yaxis_title=f'# of {label}',
                  xaxis_title='Date/Time',
                  font=dict(family="Courier New, monospace",
                            size=16))
    data = [trace]
    fig = dict(data=data, layout=layout)
    return fig

@app.callback(Output('time-series-daily', 'figure'),
             Input('daily-label', 'value'))
def time_series_daily(label, window_size=7):
    df = df_dict['covid-us']
    x = df['date']
    daily = daily_increase(df[label])
    moving_avg = moving_average(daily, window_size)

    trace1 = go.Bar(x=x, y=daily, name=f'Daily new {label}',
                    marker = dict(color = colors_bar[label],
                                  line=dict(color=colors_bar[label],width=1.5),
                                  opacity=0.2),
                    hovertemplate='%{x|%b %d, %Y}
Daily: %{y:-.0f}'
                   )
    trace2 = go.Scatter(x=x, y=moving_avg,
                        name=f'Moving average in {window_size} days',
                        line={'width':3, 'color': colors_line[label]},
                        hovertemplate='Moving average: %{y:-.0f}'
                        )

    title = f'Daily reported new Covid {label.lower()} in U.S. over time'
    layout = dict(title=title,
                  yaxis_title=f'# of {label} per day',
                  xaxis_title='Date/Time',
                  font=dict(family="Courier New, monospace",
                            size=16),
                  hoverlabel=dict(
                            bgcolor="white",
                            font_size=16,
                            font_family="Rockwell"),
                  hovermode='x Unified',
                  legend=dict(
                        yanchor="top",
                        y=0.99,
                        xanchor="left",
                        x=0.01)
                 )
    data = [trace1, trace2]
    fig = dict(data=data, layout=layout)
    return fig

@app.callback(Output('time-series-state', 'figure'),
            Input('plot-type', 'value'),
            Input('state-name', 'value'),
            Input('label-by-state', 'value'),
             )
def time_series_state(plot_type='daily', state_name='Rhode Island', label='cases',):
#     print(label, plot_type, state_name)
    df = df_dict['covid-us-state']
    df['state_code'] = df['state'].apply(lambda x: state_code_dict[x])
    state_code = state_code_dict[state_name]
    df_state = df[df.state_code == state_code]
    state = state_name
    df_state = df_state.sort_values(by='date')
    df_state = pd.DataFrame(df_state, columns=df_state.columns)
    x = df_state.date
    y = df_state[label].values
    if plot_type == 'daily':
        window_size = 7
        daily_cases = daily_increase(y)
        moving_avg = moving_average(daily_cases, window_size)
        trace_bar = go.Bar(x=x, y=daily_cases, name=f'Daily new {label}',
                    marker = dict(color = colors_bar[label],
                                  line=dict(color=colors_bar[label],width=1.5),
                                  opacity=0.2),
                    hovertemplate='Date: %{x|%A, %b %d, %Y}
 Daily increase : %{y:.0f}'
                   )
        trace_line = go.Scatter(
            x=x,
            y=moving_avg,
            name=f'Moving average in {window_size} days',
            line={'width':1.5, 'color': colors_line[label]},
            hovertemplate='7 Day Avg. : %{y:.0f}')
        title = f'Daily reported new Covid {label.lower()} in {state}'
        layout = dict(title=title,
              yaxis_title=f'# of {label} per day',
              xaxis_title='Date/Time',
              font=dict(family="Courier New, monospace",
                        size=16),
              hoverlabel=dict(
                bgcolor="white",
                font_size=16,
                font_family="Rockwell"),
              hovermode='x Unified',
              legend=dict(
                        yanchor="top",
                        y=0.99,
                        xanchor="left",
                        x=0.01))
        fig = dict(data=[trace_bar, trace_line], layout=layout)
        return fig
    elif plot_type == 'cumulative':
        trace = go.Scatter(x=x, y=y, mode='lines', name=label, fill='tozeroy',
                       fillcolor=colors[label],
                       line={'width': 2, 'color': colors[label]},
                       hovertemplate='%{x|%b %d, %Y}
 %{y:-.0f}'
                      )

        title = f'Cumulative Covid {label.lower()} in {state}'
        layout = dict(title=title,
                      yaxis_title=f'Confirmed # of {label}',
                      xaxis_title='Date/Time',
                      font=dict(family="Courier New, monospace",
                                size=16))
        data = [trace]
        fig = dict(data=data, layout=layout)
        return fig

@app.callback(Output('heat-map-by-state', 'figure'),
              Input('label-radioitems', 'value'))
def heat_map(label):
    """Create the heap map of given label in US at the beginning of given month"""
    df = df_dict['covid-us-state']
    df['month'] = df.date.dt.month_name()
    df['state_code'] = df['state'].apply(lambda x: get_state_codes(x))
    df_month = df[((df.date.dt.day == 1) | (df.date == max(df.date)))]
    fig = px.choropleth(df_month,
                    locations='state_code',
                    locationmode="USA-states",
                    scope="usa",
                    color=label, # a column in the dataset
                    hover_name='state', # column to add to hover information
                    hover_data = {'cases': ':.0f', 'deaths': ':.0f', 'state_code': False, 'month': False},
                    color_continuous_scale=px.colors.sequential.Sunsetdark if \
                        label == 'cases' else px.colors.sequential.Greys,
                    animation_group='state',
                    animation_frame='month'
                   )
    fig.update_layout(title_text=f"Heat Map - Total {label.title()} in US States"),
    fig.update_layout(margin={"r":0,"l":0,"b":0})
    fig.update_layout(transition_duration=500)

    last_frame_num = len(fig.frames) -1

    fig.layout['sliders'][0]['active'] = last_frame_num

    fig = go.Figure(data=fig['frames'][-1]['data'], frames=fig['frames'], layout=fig.layout)
    fig.update_coloraxes(colorbar_title=f"Color
Confirmed {label.title()}")
    fig.layout.pop('updatemenus')
    return fig

def heat_map_mask_use():
    df = df_dict['mask-use-by-county']
    df['countyfp'] = df['countyfp'].apply(lambda x: str(int(x)).zfill(5))
    df['wear_mask_prob'] = 0.25 * df['rarely'] + 0.5 * df['sometimes'] + \
                0.75 * df['frequently'] + 1.0 * df['always']
    df['county'] = df.apply(lambda x: fip_to_county(x.countyfp), axis=1)
    df['state_code'] = df.apply(lambda x: fip_to_state(x.countyfp), axis=1)
    df = df.drop(df[df['state_code'] == 'N/A'].index).reset_index(drop=True)
    df['state'] = df['state_code'].apply(lambda x: state_map_dict[x])
    fig = px.choropleth(df,
                        locations='countyfp',
                        geojson=county_json,
                        scope="usa",
                        color='wear_mask_prob', # a column in the dataset
                        hover_name='state', # column to add to hover information
                        hover_data = {'county': True, 'countyfp': False, 'wear_mask_prob': ':.3f'},
                        color_continuous_scale=px.colors.sequential.Reds,
                       )
    fig.update_layout(title_text="Heat Map - Who is Wearing Masks in US Counties"),
    fig.update_coloraxes(colorbar_title="Color
Wear Mask Prob")
    #fig.update(layout_coloraxis_showscale=False)
    fig.update_layout(margin={"r":0,"l":0,"b":0})
    return fig

def scatter_matrix():
    df = df_dict['covid-us-state']
    df.fips = df.fips.apply(lambda x: str(x).zfill(2))
    df = df[df.date == max(df.date)]
    df = df.drop(columns='date', axis=1).reset_index(drop=True)
    state_pop = df_dict['state-population']
    state_area =  df_dict['state-area']

    mask_use = df_dict['mask-use-by-county']
    mask_use.countyfp = mask_use.countyfp.apply(lambda x: str(x).zfill(5))
    mask_use['wear_mask_prob'] = 0.25 * mask_use['rarely'] + 0.5 * mask_use['sometimes'] + \
                    0.75 * mask_use['frequently'] + 1.0 * mask_use['always']
    mask_use['state_code'] = mask_use.apply(lambda x: fip_to_state(x.countyfp), axis=1)
    mask_use['county'] = mask_use.apply(lambda x: fip_to_county(x.countyfp), axis=1)
    df_agg = mask_use.groupby('state_code').agg(['mean'])
    df_agg.columns = ["_".join(x) for x in np.ravel(df_agg.columns)]
    df_agg.reset_index(inplace=True)
    df_agg.rename(columns={'wear_mask_prob_mean' : 'wear_mask_prob'}, inplace=True)
    df_agg = df_agg[['state_code', 'wear_mask_prob']]
    df_agg.drop(df_agg[df_agg['state_code'] == 'N/A'].index, inplace = True)
    df_agg.drop(df_agg[df_agg['state_code'] == 'DC'].index, inplace = True)
    df_agg['state'] = df_agg['state_code'].apply(lambda x: state_map_dict[x])
    df_agg = df_agg[['state', 'wear_mask_prob']]
    data_frames = [df, state_pop, state_area, df_agg]
    df_merged = reduce(lambda left, right: pd.merge(left,right,on=['state'],
                                                how='inner'), data_frames)

    df_merged['CFR'] = df_merged['deaths'] / df_merged['cases']
    df_merged['IR'] = df_merged['cases'] / df_merged['total']
    df_merged['PD'] = df_merged['total'] / df_merged['area']
    df_merged['WMP'] = df_merged['wear_mask_prob']
    df_ana = df_merged.loc[:, ['state', 'CFR', 'IR', 'PD', 'WMP']]
    df_ana[['CFR', 'IR', 'PD', 'WMP']] = np.round(df_ana[['CFR', 'IR', 'PD', 'WMP']], 3)

    fig = go.Figure(data=go.Splom(
                dimensions=[dict(label='CFR', # 'Fatality rate',
                                 values=df_ana['CFR']),
                            dict(label='IR', #'Infection rate',
                                 values=df_ana['IR']),
                            dict(label='PD', #'Population density',
                                 values=df_ana['PD']),
                            dict(label='WMP', #'Wear mask prob.',
                                 values=df_ana['WMP'])],
                text=df_ana['state'],
#                 hovertemplate="%{x}, %{y}",
                marker=dict(showscale=False, # colors encode categorical variables
                            line_color='white', line_width=0.5),
                showupperhalf=False,
                ))

    fig.update_layout(
    title='Scatter Matrix',
    dragmode='select',
    width=600,
    height=600,
    hovermode='closest',
    )
    return fig

def correlation_matrix():
    df = df_dict['covid-us-state']
    df.fips = df.fips.apply(lambda x: str(x).zfill(2))
    df = df[df.date == max(df.date)]
    df = df.drop(columns='date', axis=1).reset_index(drop=True)
    state_pop = df_dict['state-population']
    state_area =  df_dict['state-area']

    mask_use = df_dict['mask-use-by-county']
    mask_use.countyfp = mask_use.countyfp.apply(lambda x: str(x).zfill(5))
    mask_use['wear_mask_prob'] = 0.25 * mask_use['rarely'] + 0.5 * mask_use['sometimes'] + \
                    0.75 * mask_use['frequently'] + 1.0 * mask_use['always']
    mask_use['state_code'] = mask_use.apply(lambda x: fip_to_state(x.countyfp), axis=1)
    mask_use['county'] = mask_use.apply(lambda x: fip_to_county(x.countyfp), axis=1)
    df_agg = mask_use.groupby('state_code').agg(['mean'])
    df_agg.columns = ["_".join(x) for x in np.ravel(df_agg.columns)]
    df_agg.reset_index(inplace=True)
    df_agg.rename(columns={'wear_mask_prob_mean' : 'wear_mask_prob'}, inplace=True)
    df_agg = df_agg[['state_code', 'wear_mask_prob']]
    df_agg.drop(df_agg[df_agg['state_code'] == 'N/A'].index, inplace = True)
    df_agg.drop(df_agg[df_agg['state_code'] == 'DC'].index, inplace = True)
    df_agg['state'] = df_agg['state_code'].apply(lambda x: state_map_dict[x])
    df_agg = df_agg[['state', 'wear_mask_prob']]
    data_frames = [df, state_pop, state_area, df_agg]
    df_merged = reduce(lambda left, right: pd.merge(left,right,on=['state'],
                                                how='inner'), data_frames)

    df_merged['CFR'] = df_merged['deaths'] / df_merged['cases']
    df_merged['IR'] = df_merged['cases'] / df_merged['total']
    df_merged['PD'] = df_merged['total'] / df_merged['area']
    df_merged['WMP'] = df_merged['wear_mask_prob']
    df_ana = df_merged.loc[:, ['state', 'CFR', 'IR', 'PD', 'WMP']]
    df_ana[['CFR', 'IR', 'PD', 'WMP']] = np.round(df_ana[['CFR', 'IR', 'PD', 'WMP']], 3)
    df_corr = df_ana[['CFR', 'IR', 'PD', 'WMP']].corr()

    fig = go.Figure(data=go.Heatmap(z=df_corr,
                                    x=['CFR', 'IR', 'PD', 'WMP'],
                                    y=['CFR', 'IR', 'PD', 'WMP'],
                                    colorscale='Blues',
                                   hovertemplate=" Corr(%{x}, %{y}) = %{z:.2f}"),
                   )
    fig.update_layout(
        title='Correlation Matrix',
        height=600,
        width=600,
        )
    return fig

def architecture_summary():
    """
    Returns the text and image of architecture summary of the project.
    """
    return html.Div(children=[
        dcc.Markdown('''
            ## Project Architecture
            This project uses MongoDB as the database. All data acquired are stored in raw form to the
            database (with de-duplication). An abstract layer is built in `database.py` so all queries
            can be done via function call. For a more complicated app, the layer will also be
            responsible for schema consistency. A `plot.ly` & `dash` app is serving this web page
            through. Actions on responsive components on the page is redirected to `app.py` which will
            then update certain components on the page.
        ''', className='row eleven columns', style={'paddingLeft': '5%'}),

        html.Div(children=[
            html.Img(src="https://docs.google.com/drawings/d/e/2PACX-1vQNerIIsLZU2zMdRhIl3ZZkDMIt7jhE_fjZ6ZxhnJ9bKe1emPcjI92lT5L7aZRYVhJgPZ7EURN0AqRh/pub?w=670&h=457",
                     className='row'),
        ], className='row', style={'textAlign': 'center'}),

        dcc.Markdown('''

        ''')
    ], className='row')

def visualization_summary():
    """
    All EDA figures should be arranged in this function.
    """
    return html.Div(children=[
        dcc.Markdown('''
        ### US Case and Death Count
        ''', className='row eleven columns', style={'paddingLeft': '5%'}),

            # Time series curves for cumulative cases and deaths in US
            dcc.Markdown('''
            #### Time-series cumulative cases and deaths
            ''', className='row eleven columns', style={'paddingLeft': '5%'}),

            html.Div([
                html.Div([
                    html.Label( ['Label:'],
                        style={'font-weight': 'bold', 'float': 'left',
                               'color': 'white', 'display': 'inline-block',
                               },
                        ),
                    dcc.RadioItems(
                        id='target-label',
                        options=[{'label': i.title(), 'value': i} for i in ['cases', 'deaths']],
                        value='cases',
                        labelStyle={
                        'display': 'inline-block',
                        },
                        style={
                        'width': '20%',
                        'float': 'left',
                        'font-weight': 'bold',
                        'color': 'white',
                        }),],  style={'width': '98%', 'display': 'inline-block'}),
                dcc.Graph(id='time-series-total', style={'height': 500, 'width': 1100})
                ],
                style={'width': '98%', 'float': 'right', 'display': 'inline-block'}),

            # Time series curves for daily cases and deaths in US
                    dcc.Markdown('''
            #### Time-series daily reported cases and deaths
            ''', className='row eleven columns', style={'paddingLeft': '5%'}),
            html.Div([
                html.Div([
                    html.Label( ['Label:'],
                        style={'font-weight': 'bold', 'float': 'left',
                               'color': 'white', 'display': 'inline-block',
                               },
                        ),
                    dcc.RadioItems(
                        id='daily-label',
                        options=[{'label': i.title(), 'value': i} for i in ['cases', 'deaths']],
                        value='cases',
                        labelStyle={
                        'display': 'inline-block',
                        },
                        style={
                        'width': '20%',
                        'float': 'left',
                        'font-weight': 'bold',
                        'color': 'white',
                        }),],  style={'width': '98%', 'display': 'inline-block'}),
                dcc.Graph(id='time-series-daily', style={'height': 500, 'width': 1100})
            ],
                style={'width': '98%', 'float': 'right', 'display': 'inline-block'}),

        dcc.Markdown('''
        ### Case and Death Count by State
        ''', className='row eleven columns', style={'paddingLeft': '0%'}),

             # Time series curves for cases and deaths by state
            dcc.Markdown('''
            #### Time-series cases and deaths by state
            ''', className='row eleven columns', style={'paddingLeft': '5%'}),

            html.Div([
                html.Div([
                    html.Label( ['Label:'],
                        style={'font-weight': 'bold', 'float': 'left',
                               'color': 'white', 'display': 'inline-block',
                               },
                        ),
                    dcc.RadioItems(
                        id='label-by-state',
                        options=[{'label': i.title(), 'value': i} for i in ['cases', 'deaths']],
                        value='cases',
                        labelStyle={
                        'display': 'inline-block',
                        },
                        style={
                        'width': '20%',
                        'float': 'left',
                        'font-weight': 'bold',
                        'color': 'white',
                        }),
                    html.Label( ['Plot type:'],
                        style={'font-weight': 'bold', 'float': 'left',
                               'color': 'white', 'display': 'inline-block',
                               },
                        ),
                    dcc.RadioItems(
                        id='plot-type',
                        options=[{'label': i.title(), 'value': i} for i in ['cumulative', 'daily']],
                        value='daily',
                        labelStyle={
                        'display': 'inline-block',
                        },
                        style={
                        'width': '20%',
                        'float': 'left',
                        'font-weight': 'bold',
                        'color': 'white',
                        }),
                    html.Label( ['State:'],
                        style={'font-weight': 'bold', 'float': 'left',
                               'color': 'white', 'display': 'inline-block',
                               'margin-right': '10px'
                               },
                        ),
                    dcc.Dropdown(
                        id='state-name',
                        options=[{'label': i, 'value': i} for i in list(all_states)],
                        value='Rhode Island',
                        style={'width': '40%', 'float':'left', 'display': 'inline-block'}
                    ),],  style={'width': '98%', 'display': 'inline-block'}),
                dcc.Graph(id='time-series-state', style={'height': 500, 'width': 1100})
                ],
                style={'width': '98%', 'float': 'right', 'display': 'inline-block'}),

            # Heat map by month
            dcc.Markdown('''
            #### Heat Map - Covid in US states
            We use the state-level COVID-19 data to power the heat map and track the outbreak
            over all states of US.
            ''', className='row eleven columns', style={'paddingLeft': '0%'}),

            html.Div([
                html.Div([
                    html.Label( ['Label:'],
                        style={'font-weight': 'bold', 'float': 'left',
                               'color': 'white', 'display': 'inline-block',
                               },
                        ),
                    dcc.RadioItems(
                        id='label-radioitems',
                        options=[{'label': i.title(), 'value': i} for i in ['cases', 'deaths']],
                        value='cases',
                        labelStyle={
                        'display': 'inline-block',
                        },
                        style={
                        'width': '20%',
                        'float': 'left',
                        'font-weight': 'bold',
                        'color': 'white',
                        }),],  style={'width': '98%', 'display': 'inline-block'}),
                dcc.Graph(id='heat-map-by-state', style={'height': 800, 'width': 1000})
            ],
                style={'width': '100%', 'float':'right', 'display': 'inline-block'}),

    ])

def enhancement_summary():
    """
    All Enhancement details should be arranged here.
    """
    return html.Div(children=[
         dcc.Markdown('''
          ### Who is Wearing Masks in US Counties？
         ''', className='row eleven columns', style={'paddingLeft': '6%'}),
         dcc.Graph(id='mask-use-by-county', figure=heat_map_mask_use(),
                   style={'height': 800, 'width': 1000, 'display': 'inline-block'}),

         dcc.Markdown('''
          ### Whether Population Density and Propensity of Wearing Masks Affect the Spread?
         ''', className='row eleven columns', style={'paddingLeft': '0%'}),
        html.Div([
             html.Label( ['CFR: Case Fatality Rate'],
                        style={'font-weight': 'bold', 'float': 'left',
                               'color': 'white', 'display': 'inline-block',
                               },
                        ),
             html.Label( ['IR: Infeaction Rate',],
                        style={'font-weight': 'bold', 'float': 'left','margin-left': '100px',
                               'color': 'white', 'display': 'inline-block',
                               },
                        ),
             html.Label( ['PD: Population Density',],
                        style={'font-weight': 'bold', 'float': 'left', 'margin-left': '100px',
                               'color': 'white', 'display': 'inline-block',
                               },
                        ),
             html.Label( ['WMP: Wear Mask Probability'],
                style={'font-weight': 'bold', 'float': 'left','margin-left': '100px',
                       'color': 'white', 'display': 'inline-block',
                       },
                ),
             dcc.Graph(id='scatter-matrix', figure=scatter_matrix(),
                       style={'width': '48%',  'display': 'inline-block'}),
             dcc.Graph(id='correlation-matrix', figure=correlation_matrix(),
                       style={'width': '48%', 'float':'right', 'display': 'inline-block'}),
         ], style={'width': '100%',  'display': 'inline-block'}),
    ]
                   )

# Sequentially add page components to the app's layout
def dynamic_layout():
    return html.Div([
        page_header(),
        html.Hr(),
        project_description(),
        visualization_description(),
        visualization_summary(),
        enhancement_description(),
        enhancement_summary(),
        # architecture_summary(),
    ], className='row', id='content')

# set layout to a function which updates upon reloading
app.layout = dynamic_layout

if __name__ == '__main__':
    app.run_server(debug=True, port=8888, host='0.0.0.0')


import numpy as np
import cv2

def extract_frames(video_path, n_frames=15):
    """
    Extract frames from a video. You can use either provided method here or implement your own method.

    params:
        - video_local_path (str): the path of video.
    return:
        - frames (list): a list containing frames extracted from the video.
    """
    ########################################################################################################
    # You can change the lines below to implement your own frame extracting method (and possibly other preprocessing),
    # or just use the provided codes.
    vid = cv2.VideoCapture(video_path)
    frames = []

    # while True:
    #     success, frame = vid.read()
    #     if not success:
    #         break
    #     if frame is not None:
    #         frames.append(frame)
    #     # Here, we extract one frame only without other preprocessing
    #     if len(frames) >= n_frames:
    #         break

    v_len = int(vid.get(cv2.CAP_PROP_FRAME_COUNT))
    sample = np.linspace(0, v_len - 1, n_frames).astype(int)
    for j in range(v_len):
        success = vid.grab()
        if j in sample:
            # Load frame
            success, frame = vid.retrieve()
            if not success:
                continue
            if frame is not None:
                frames.append(frame)

    vid.release()
    return frames
    ########################################################################################################


#!/usr/bin/env python
# coding: utf-8

# # [Warming Up!](https://academy.dqlab.id/main/livecode/287/531/2651)

# In[1]:

bersatulawancovid = ['cuci tangan', 'pakai masker', 'jaga jarak']
print(bersatulawancovid)

# # [Are You Ready?](https://academy.dqlab.id/main/livecode/287/531/2652)

# In[2]:

True

# # [Mengakses API covid19.go.id](https://academy.dqlab.id/main/livecode/287/532/2653)

# In[3]:

import requests
resp = requests.get('https://data.covid19.go.id/public/api/update.json')

# # [Status Code](https://academy.dqlab.id/main/livecode/287/532/2656)

# In[4]:

print(resp)

# # [Headers API](https://academy.dqlab.id/main/livecode/287/532/2657)

# In[5]:

print(resp.headers)

# # [Mengekstrak Isi Respon](https://academy.dqlab.id/main/livecode/287/532/2658)

# In[6]:

cov_id_raw  = resp.json()

# # [Mengekstrak isi Respon - 2](https://academy.dqlab.id/main/livecode/287/532/2659)

# In[7]:

print('Length of cov_id_raw : %d.' %len(cov_id_raw))
print('Komponen cov_id_raw  : %s.' %cov_id_raw.keys())
cov_id_update = cov_id_raw['update']

# # [Analisa Data](https://academy.dqlab.id/main/livecode/287/532/2660)

# In[8]:

print('Tanggal pembaharuan data penambahan kasus :', cov_id_update['penambahan']['tanggal'])
print('Jumlah penambahan kasus sembuh :', cov_id_update['penambahan']['jumlah_sembuh'])
print('Jumlah penambahan kasus meninggal :', cov_id_update['penambahan']['jumlah_meninggal'])
print('Jumlah total kasus positif hingga saat ini :', cov_id_update['total']['jumlah_positif'])
print('Jumlah total kasus meninggal hingga saat ini:', cov_id_update['total']['jumlah_meninggal'])

# # [Apa Kabar Jawa Barat?](https://academy.dqlab.id/main/livecode/287/533/2662)

# In[9]:

import requests
resp_jabar = requests.get('https://data.covid19.go.id/public/api/prov_detail_JAWA_BARAT.json')
cov_jabar_raw = resp_jabar.json()

# # [Memahami Kasus COVID-19 di Jawa Barat](https://academy.dqlab.id/main/livecode/287/533/2663)

# In[10]:

print('Nama-nama elemen utama:\n', cov_jabar_raw.keys())
print('\nJumlah total kasus COVID-19 di Jawa Barat : %d' %cov_jabar_raw['kasus_total'])
print('Persentase kematian akibat COVID-19 di Jawa Barat : %f.2%%' %cov_jabar_raw['meninggal_persen'])
print('Persentase tingkat kesembuhan dari COVID-19 di Jawa Barat : %f.2%%' %cov_jabar_raw['sembuh_persen'])

# # [Memperoleh Informasi yang Lebih Lengkap](https://academy.dqlab.id/main/livecode/287/533/2664)

# In[11]:

import numpy as np
import pandas as pd
cov_jabar = pd.DataFrame(cov_jabar_raw['list_perkembangan'])
print('Info cov_jabar:\n', cov_jabar.info())
print('\nLima data teratas cov_jabar:\n', cov_jabar.head())

# # [Menjinakkan Data](https://academy.dqlab.id/main/livecode/287/533/2665)

# In[12]:

cov_jabar_tidy = (cov_jabar.drop(columns=[item for item in cov_jabar.columns
																if item.startswith('AKUMULASI')
																or item.startswith('DIRAWAT')])
												.rename(columns=str.lower)
												.rename(columns={'kasus': 'kasus_baru'})
				  )
cov_jabar_tidy['tanggal'] = pd.to_datetime(cov_jabar_tidy['tanggal']*1e6, unit='ns')
print('Lima data teratas:\n', cov_jabar_tidy.head())

# # [Menunjukkan Melalui Gambar](https://academy.dqlab.id/main/livecode/287/533/2666)

# In[13]:

import matplotlib.pyplot as plt

# # [Menunjukkan Melalui Gambar - 2](https://academy.dqlab.id/main/livecode/287/533/2667)

# In[14]:

import matplotlib.pyplot as plt

plt.clf()
fig, ax = plt.subplots(figsize=(10,5))
ax.bar(data=cov_jabar_tidy, x='tanggal', height='kasus_baru')
plt.show()

# # [Informasi pada Grafik](https://academy.dqlab.id/main/livecode/287/533/2668)

# In[15]:

import matplotlib.pyplot as plt
import matplotlib.dates as mdates

plt.clf()
fig, ax = plt.subplots(figsize=(10,5))
ax.bar(data=cov_jabar_tidy, x='tanggal', height='kasus_baru', color='salmon')
fig.suptitle('Kasus Harian Positif COVID-19 di Jawa Barat',
y=1.00, fontsize=16, fontweight='bold', ha='center')
ax.set_title('Terjadi pelonjakan kasus di awal bulan Juli akibat klaster Secapa AD Bandung',
fontsize=10)
ax.set_xlabel('')
ax.set_ylabel('Jumlah kasus')
ax.text(1, -0.1, 'Sumber data: covid.19.go.id', color='blue',
		ha='right', transform=ax.transAxes)

ax.xaxis.set_major_locator(mdates.MonthLocator())
ax.xaxis.set_major_formatter(mdates.DateFormatter('%b %Y'))

plt.grid(axis='y')
plt.tight_layout()
plt.show()

# # [Grafik untuk Kasus Sembuh](https://academy.dqlab.id/main/livecode/287/533/2669)

# In[16]:

import matplotlib.pyplot as plt
import matplotlib.dates as mdates

plt.clf()
fig, ax = plt.subplots(figsize=(10,5))
ax.bar(data=cov_jabar_tidy, x='tanggal', height='sembuh', color='olivedrab')
ax.set_title('Kasus Harian Sembuh Dari COVID-19 di Jawa Barat',
fontsize=22)
ax.set_xlabel('')
ax.set_ylabel('Jumlah kasus')
ax.text(1, -0.1, 'Sumber data: covid.19.go.id', color='blue',
ha='right', transform=ax.transAxes)

ax.xaxis.set_major_locator(mdates.MonthLocator())
ax.xaxis.set_major_formatter(mdates.DateFormatter('%b %Y'))

plt.grid(axis='y')
plt.tight_layout()
plt.show()

# # [Grafik untuk Kasus Meninggal](https://academy.dqlab.id/main/livecode/287/533/2670)

# In[17]:

import matplotlib.pyplot as plt
import matplotlib.dates as mdates

plt.clf()
fig, ax = plt.subplots(figsize=(10,5))
ax.bar(data=cov_jabar_tidy, x='tanggal', height='meninggal', color='slategrey')
ax.set_title('Kasus Harian Meninggal Dari COVID-19 di Jawa Barat',
fontsize=22)
ax.set_xlabel('')
ax.set_ylabel('Jumlah kasus')
ax.text(1, -0.1, 'Sumber data: covid.19.go.id', color='blue',
ha='right', transform=ax.transAxes)

ax.xaxis.set_major_locator(mdates.MonthLocator())
ax.xaxis.set_major_formatter(mdates.DateFormatter('%b %Y'))

plt.grid(axis='y')
plt.tight_layout()
plt.show()

# # [Apakah Pekan ini Lebih Baik?](https://academy.dqlab.id/main/livecode/287/535/2671)

# In[18]:

cov_jabar_pekanan = (cov_jabar_tidy.set_index('tanggal')['kasus_baru']
					  .resample('W')
					  .sum()
					  .reset_index()
					  .rename(columns={'kasus_baru': 'jumlah'})
)
cov_jabar_pekanan['tahun'] = cov_jabar_pekanan['tanggal'].apply(lambda x: x.year)
cov_jabar_pekanan['pekan_ke'] = cov_jabar_pekanan['tanggal'].apply(lambda x: x.weekofyear)
cov_jabar_pekanan = cov_jabar_pekanan[['tahun', 'pekan_ke', 'jumlah']]

print('Info cov_jabar_pekanan:')
print(cov_jabar_pekanan.info())
print('\nLima data teratas cov_jabar_pekanan:\n', cov_jabar_pekanan.head())

# # [Menjawab Pertanyaan](https://academy.dqlab.id/main/livecode/287/535/2672)

# In[19]:

cov_jabar_pekanan['jumlah_pekanlalu'] = cov_jabar_pekanan['jumlah'].shift().replace(np.nan, 0).astype(np.int)
cov_jabar_pekanan['lebih_baik'] = cov_jabar_pekanan['jumlah'] < cov_jabar_pekanan['jumlah_pekanlalu']

print('Sepuluh data teratas:\n', cov_jabar_pekanan.head(10))

# # [Membuat Bar Chart](https://academy.dqlab.id/main/livecode/287/535/2673)

# In[20]:

import matplotlib.pyplot as plt

plt.clf()
fig, ax = plt.subplots(figsize=(10,5))
ax.bar(data=cov_jabar_pekanan, x='pekan_ke', height='jumlah',
color=['mediumseagreen' if x is True else 'salmon' for x in cov_jabar_pekanan['lebih_baik']])
fig.suptitle('Kasus Pekanan Positif COVID-19 di Jawa Barat',
y=1.00, fontsize=16, fontweight='bold', ha='center')
ax.set_title('Kolom hijau menunjukan penambahan kasus baru lebih sedikit dibandingkan satu pekan sebelumnya',
fontsize=12)
ax.set_xlabel('')
ax.set_ylabel('Jumlah kasus')
ax.text(1, -0.1, 'Sumber data: covid.19.go.id', color='blue',
ha='right', transform=ax.transAxes)

plt.grid(axis='y')
plt.tight_layout()
plt.show()

# # [Pola dan Dinamika](https://academy.dqlab.id/main/livecode/287/535/2674)

# In[21]:

cov_jabar_akumulasi = cov_jabar_tidy[['tanggal']].copy()
cov_jabar_akumulasi['akumulasi_aktif'] = (cov_jabar_tidy['kasus_baru'] - cov_jabar_tidy['sembuh'] - cov_jabar_tidy['meninggal']).cumsum()
cov_jabar_akumulasi['akumulasi_sembuh'] = cov_jabar_tidy['sembuh'].cumsum()
cov_jabar_akumulasi['akumulasi_meninggal'] = cov_jabar_tidy['meninggal'].cumsum()
cov_jabar_akumulasi.tail()

# # [Membuat Line Chart](https://academy.dqlab.id/main/livecode/287/535/2675)

# In[22]:

import matplotlib.pyplot as plt
import matplotlib.dates as mdates

plt.clf()
fig, ax = plt.subplots(figsize=(10,5))
ax.plot('tanggal', 'akumulasi_aktif', data=cov_jabar_akumulasi, lw=2)

ax.set_title('Akumulasi aktif COVID-19 di Jawa Barat',
fontsize=22)
ax.set_xlabel('')
ax.set_ylabel('Akumulasi aktif')
ax.text(1, -0.1, 'Sumber data: covid.19.go.id', color='blue',
ha='right', transform=ax.transAxes)

ax.xaxis.set_major_locator(mdates.MonthLocator())
ax.xaxis.set_major_formatter(mdates.DateFormatter('%b %Y'))

plt.grid()
plt.tight_layout()
plt.show()

# # [Kabar Buruk dan Kabar Baik](https://academy.dqlab.id/main/livecode/287/535/2676)

# In[23]:

True

# # [Tahap Terakhir](https://academy.dqlab.id/main/livecode/287/535/2677)

# In[24]:

import matplotlib.pyplot as plt
import matplotlib.dates as mdates

plt.clf()
fig, ax = plt.subplots(figsize=(10,5))
cov_jabar_akumulasi.plot(x='tanggal', kind='line', ax=ax, lw=3,
color=['salmon', 'slategrey', 'olivedrab'])

ax.set_title('Dinamika Kasus COVID-19 di Jawa Barat',
fontsize=22)
ax.set_xlabel('')
ax.set_ylabel('Akumulasi aktif')
ax.text(1, -0.1, 'Sumber data: covid.19.go.id', color='blue',
ha='right', transform=ax.transAxes)

ax.xaxis.set_major_locator(mdates.MonthLocator())
ax.xaxis.set_major_formatter(mdates.DateFormatter('%b'))

plt.grid()
plt.tight_layout()
plt.show()

Learn/Python/Applied Data Science/Eksplorasi dan Analisis Data COVID-19 Indonesia using Python/Eksplorasi dan Analisis Data COVID-19 Indonesia using Python.py


from typing import Optional, Sequence, Union

import numpy as np

from mygrad.tensor_base import Tensor, _resolve_constant, implements_numpy_override
from mygrad.typing import ArrayLike, DTypeLikeReals, Real

Shape = Union[Sequence[int], int]

def _anything_but_tensor(x):
    if isinstance(x, Tensor):
        x = x.data
    return x

__all__ = [
    "arange",
    "empty",
    "empty_like",
    "eye",
    "geomspace",
    "identity",
    "linspace",
    "logspace",
    "ones",
    "ones_like",
    "full",
    "full_like",
    "zeros",
    "zeros_like",
]

def empty(
    shape: Shape, dtype: DTypeLikeReals = np.float32, *, constant: Optional[bool] = None
) -> Tensor:
    """Return a new Tensor of the given shape and type, without initializing entries.

    This docstring was adapted from ``numpy.empty`` [1]_

    Parameters
    ----------
    shape : Union[int, Tuple[int]]
        The shape of the empty array.

    dtype : data-type, optional (default=numpy.float32)
        The data type of the output Tensor.

    constant : Optional[bool]
        If ``True``, this tensor is a constant, and thus does not facilitate
        back propagation.

        Defaults to ``False`` for float-type data.
        Defaults to ``True`` for integer-type data.

        Integer-type tensors must be constant.

    Returns
    -------
    Tensor
        A tensor of uninitialized data of the given shape and dtype.

    References
    ----------
    .. [1] Retrieved from https://numpy.org/doc/stable/reference/generated/numpy.empty.html

    See Also
    --------
    empty_like : Return an empty tensor with shape and type of input.
    ones : Return a new tensor setting values to one.
    zeros : Return a new tensor setting values to zero.
    full : Return a new tensor of given shape filled with value.

    Notes
    -----
    `empty`, unlike `zeros`, does not set the array values to zero,
    and may therefore be marginally faster.  On the other hand, it requires
    the user to manually set all the values in the array, and should be
    used with caution.

    Examples
    --------
    >>> import mygrad as mg
    >>> mg.empty([2, 2], constant=True)
    Tensor([[ -9.74499359e+001,   6.69583040e-309],
            [  2.13182611e-314,   3.06959433e-309]])         #random

    >>> mg.empty([2, 2], dtype=int)
    Tensor([[-1073741821, -1067949133],
            [  496041986,    19249760]])                     #random
    """
    return Tensor(np.empty(shape=shape, dtype=dtype), constant=constant, copy=False)

@implements_numpy_override()
def empty_like(
    other: ArrayLike,
    dtype: Optional[DTypeLikeReals] = None,
    shape: Optional[Union[int, Sequence[int]]] = None,
    *,
    constant: Optional[bool] = None,
) -> Tensor:
    """Return a new Tensor of the same shape and type as the given array.

    This docstring was adapted from ``numpy.empty_like`` [1]_

    Parameters
    ----------
    other : ArrayLike
        The Tensor or array whose shape and datatype should be mirrored.

    dtype : Optional[DTypeLikeReals]
        Override the data type of the returned Tensor with this value, or None to not override.

    shape : Optional[Union[int, Sequence[int]]]
        If specified, overrides the shape of the result

    constant : Optional[bool]
        If ``True``, this tensor is a constant, and thus does not facilitate
        back propagation. If ``None`` then:

        Inferred from ``other``, if other is a tensor
        Defaults to ``False`` for float-type data.
        Defaults to ``True`` for integer-type data.

    Returns
    -------
    Tensor
        A tensor of uninitialized data whose shape and type match `other`.

    References
    ----------
    .. [1] Retrieved from https://numpy.org/doc/stable/reference/generated/numpy.empty_like.html

    See Also
    --------
    empty : Return a new Tensor of the given shape and type, without initializing entries.
    ones : Return a new tensor setting values to one.
    zeros : Return a new tensor setting values to zero.
    full : Return a new tensor of given shape filled with value.

    Examples
    --------
    >>> import mygrad as mg
    >>> x = mg.arange(4).reshape(2, 2)
    >>> mg.empty_like(x, constant=True)
    Tensor([[ -9.74499359e+001,   6.69583040e-309],
            [  2.13182611e-314,   3.06959433e-309]])         #random

    >>> mg.empty_like(x, dtype=int)
    Tensor([[-1073741821, -1067949133],
            [  496041986,    19249760]])                     #random
    """
    constant = _resolve_constant(other, constant=constant)
    return Tensor(
        np.empty_like(_anything_but_tensor(other), dtype=dtype, shape=shape),
        constant=constant,
        copy=False,
    )

def eye(
    N: int,
    M: Optional[int] = None,
    k: int = 0,
    dtype: DTypeLikeReals = float,
    *,
    constant: Optional[bool] = None,
) -> Tensor:
    """Return a 2D Tensor with ones on the diagonal and zeros elsewhere.

    This docstring was adapted from ``numpy.eye`` [1]_

    Parameters
    ----------
    N : int
        The number of rows in the output Tensor.

    M : int, optional (default=None)
        The number of columns in the output, or None to match `rows`.

    k : int, optional (default=0)
        The index of the diagonal. 0 is the main diagonal; a positive value is the upper
        diagonal, while a negative value refers to the lower diagonal.

    dtype : data-type, optional (default=numpy.float32)
        The data type of the output Tensor.

    constant : Optional[bool]
        If ``True``, this tensor is a constant, and thus does not facilitate
        back propagation.

        Defaults to ``False`` for float-type data.
        Defaults to ``True`` for integer-type data.

        Integer-type tensors must be constant.

    References
    ----------
    .. [1] Retrieved from https://numpy.org/doc/stable/reference/generated/numpy.eye.html

    Returns
    -------
    Tensor
        A tensor whose elements are 0, except for the :math:`k`-th diagonal, whose values are 1.

    Examples
    --------
    >>> import mygrad as mg
    >>> mg.eye(2, dtype=int)
    Tensor([[1, 0],
            [0, 1]])
    >>> mg.eye(3, k=1)
    Tensor([[ 0.,  1.,  0.],
            [ 0.,  0.,  1.],
            [ 0.,  0.,  0.]])
    """
    return Tensor(
        np.eye(N, M=M, k=k, dtype=dtype),
        constant=constant,
        copy=False,
    )

def identity(
    n: int, dtype: DTypeLikeReals = float, *, constant: Optional[bool] = None
) -> Tensor:
    """Return the identity Tensor; a square Tensor with 1s on the main diagonal and 0s elsewhere.

    This docstring was adapted from ``numpy.identity`` [1]_

    Parameters
    ----------
    n : int
        The number of rows and columns in the output Tensor.

    dtype : data-type, optional (default=numpy.float32)
        The data type of the output Tensor.

    constant : Optional[bool]
        If ``True``, this tensor is a constant, and thus does not facilitate
        back propagation.

        Defaults to ``False`` for float-type data.
        Defaults to ``True`` for integer-type data.

        Integer-type tensors must be constant.

    Returns
    -------
    Tensor
        A square Tensor whose main diagonal is 1 and all other elements are 0.

    References
    ----------
    .. [1] Retrieved from https://numpy.org/doc/stable/reference/generated/numpy.identity.html

    Examples
    --------
    >>> import mygrad as mg
    >>> mg.identity(3)
    Tensor([[ 1.,  0.,  0.],
            [ 0.,  1.,  0.],
            [ 0.,  0.,  1.]])
    """
    return Tensor(np.identity(n, dtype=dtype), constant=constant, copy=False)

def ones(
    shape: Shape, dtype: DTypeLikeReals = np.float32, *, constant: Optional[bool] = None
) -> Tensor:
    """
    Return a Tensor of the given shape and type, filled with ones.

    This docstring was adapted from ``numpy.ones`` [1]_

    Parameters
    ----------
    shape : Union[int, Tuple[int]]
        The shape of the output Tensor.

    dtype : data-type, optional (default=numpy.float32)
        The data type of the output Tensor.

    constant : Optional[bool]
        If ``True``, this tensor is a constant, and thus does not facilitate
        back propagation.

        Defaults to ``False`` for float-type data.
        Defaults to ``True`` for integer-type data.

        Integer-type tensors must be constant.

    Returns
    -------
    Tensor
        A Tensor of ones with the given shape and data type.

    References
    ----------
    .. [1] Retrieved from https://numpy.org/doc/stable/reference/generated/numpy.ones.html

    See Also
    --------
    ones_like : Return an tensor of ones with shape and type of input.
    empty : Return a new uninitialized tensor.
    zeros : Return a new tensor setting values to zero.
    full : Return a new tensor of given shape filled with value.

    Examples
    --------
    >>> import mygrad as mg
    >>> mg.ones(5)
    Tensor([ 1.,  1.,  1.,  1.,  1.])

    >>> mg.ones((5,), dtype=int)
    Tensor([1, 1, 1, 1, 1])

    >>> mg.ones((2, 1))
    Tensor([[ 1.],
           [ 1.]])

    >>> mg.ones((2, 2))
    Tensor([[ 1.,  1.],
            [ 1.,  1.]])
    """
    return Tensor(np.ones(shape, dtype=dtype), constant=constant, copy=False)

@implements_numpy_override()
def ones_like(
    other: ArrayLike,
    dtype: Optional[DTypeLikeReals] = None,
    shape: Optional[Union[int, Sequence[int]]] = None,
    *,
    constant: Optional[bool] = None,
) -> Tensor:
    """
    Return a Tensor of the same shape and type as the given, filled with ones.

    This docstring was adapted from ``numpy.ones_like`` [1]_

    Parameters
    ----------
    other : array_like
        The Tensor or array whose shape and datatype should be mirrored.

    dtype : Optional[DTypeLikeReals]
        Override the data type of the returned Tensor with this value, or None to not override.

    shape : Optional[Union[int, Sequence[int]]]
        If specified, overrides the shape of the result

    constant : Optional[bool]
        If ``True``, this tensor is a constant, and thus does not facilitate
        back propagation. If ``None`` then:

        Inferred from ``other``, if other is a tensor
        Defaults to ``False`` for float-type data.
        Defaults to ``True`` for integer-type data.

    Returns
    -------
    Tensor
        A Tensor of ones whose shape and data type match `other`.

    References
    ----------
    .. [1] Retrieved from https://numpy.org/doc/stable/reference/generated/numpy.ones_like.html

    Examples
    --------
    >>> import mygrad as mg
    >>> x = mg.arange(6).reshape((2, 3))
    >>> x
    Tensor([[0, 1, 2],
            [3, 4, 5]])

    >>> mg.ones_like(x)
    Tensor([[1, 1, 1],
            [1, 1, 1]])

    >>> y = mg.arange(3, dtype=float)
    >>> y
    Tensor([ 0.,  1.,  2.])

    >>> mg.ones_like(y)
    Tensor([ 1.,  1.,  1.])
    """
    constant = _resolve_constant(other, constant=constant)

    return Tensor(
        np.ones_like(_anything_but_tensor(other), dtype=dtype, shape=shape),
        constant=constant,
        copy=False,
    )

def zeros(
    shape: Shape, dtype: DTypeLikeReals = np.float32, *, constant: Optional[bool] = None
) -> Tensor:
    """
    Return a Tensor of the given shape and type, filled with zeros.

    This docstring was adapted from ``numpy.zeros`` [1]_

    Parameters
    ----------
    shape : Union[int, Tuple[int]]
        The shape of the output Tensor.

    dtype : data-type, optional (default=numpy.float32)
        The data type of the output Tensor.

    constant : Optional[bool]
        If ``True``, this tensor is a constant, and thus does not facilitate
        back propagation.

        Defaults to ``False`` for float-type data.
        Defaults to ``True`` for integer-type data.

        Integer-type tensors must be constant.

    Returns
    -------
    Tensor
        A Tensor of zeros with the given shape and data type.

    References
    ----------
    .. [1] Retrieved from https://numpy.org/doc/stable/reference/generated/numpy.zeros.html

    See Also
    --------
    ones_like : Return an tensor of ones with shape and type of input.
    empty : Return a new uninitialized tensor.
    ones : Return a new tensor setting values to one.
    full : Return a new tensor of given shape filled with value.

    Examples
    --------
    >>> import mygrad as mg
    >>> mg.zeros(5)
    Tensor([ 0.,  0.,  0.,  0.,  0.])

    >>> mg.zeros((5,), dtype=int, constant=True) # tensor will not back-propagate a gradient
    Tensor([0, 0, 0, 0, 0])

    >>> mg.zeros((2, 1))
    Tensor([[ 0.],
            [ 0.]])

    >>> mg.zeros((2, 2))
    Tensor([[ 0.,  0.],
            [ 0.,  0.]])
    """
    return Tensor(np.zeros(shape, dtype), constant=constant, copy=False)

@implements_numpy_override()
def zeros_like(
    other: ArrayLike,
    dtype: Optional[DTypeLikeReals] = None,
    shape: Optional[Union[int, Shape]] = None,
    *,
    constant: Optional[bool] = None,
) -> Tensor:
    """
    Return a Tensor of the same shape and type as the given, filled with zeros.

    This docstring was adapted from ``numpy.zeros_like`` [1]_

    Parameters
    ----------
    other : ArrayLike
        The Tensor or array whose shape and datatype should be mirrored.

    dtype : Optional[DTypeLikeReals]
        Override the data type of the returned Tensor with this value, or None to not override.

    shape : Optional[int, Sequence[int]]
        If specified, overrides the shape of the result

    constant : Optional[bool]
        If ``True``, this tensor is a constant, and thus does not facilitate
        back propagation. If ``None`` then:

        Inferred from ``other``, if other is a tensor
        Defaults to ``False`` for float-type data.
        Defaults to ``True`` for integer-type data.

        Integer-type tensors must be constant.

    Returns
    -------
    Tensor
        A Tensor of zeros whose shape and data type match `other`.

    References
    ----------
    .. [1] Retrieved from https://numpy.org/doc/stable/reference/generated/numpy.zeros_like.html

    See Also
    --------
    empty_like : Return an empty tensor with shape and type of input.
    ones_like : Return an tensor of ones with shape and type of input.
    full_like : Return a new tensor with shape of input filled with value.
    zeros : Return a new tensor setting values to zero.

    Examples
    --------
    >>> import mygrad as mg
    >>> x = mg.arange(6).reshape((2, 3))
    >>> x
    Tensor([[0, 1, 2],
            [3, 4, 5]])

    >>> mg.zeros_like(x, constant=True)  # tensor will not back-propagate a gradient
    Tensor([[0, 0, 0],
            [0, 0, 0]])

    >>> y = mg.arange(3, dtype=float)
    >>> y
    Tensor([ 0.,  1.,  2.])

    >>> mg.zeros_like(y)
    Tensor([ 0.,  0.,  0.])
    """
    constant = _resolve_constant(other, constant=constant)
    return Tensor(
        np.zeros_like(_anything_but_tensor(other), dtype=dtype, shape=shape),
        constant=constant,
        copy=False,
    )

def full(
    shape: Shape,
    fill_value: ArrayLike,
    dtype: Optional[DTypeLikeReals] = None,
    *,
    constant: Optional[bool] = None,
) -> Tensor:
    """
    Return a Tensor of the given shape and type, filled with `fill_value`.

    This docstring was adapted from ``numpy.full`` [1]_

    Parameters
    ----------
    shape : Union[int, Iterable[int]]
        The shape of the output Tensor.

    fill_value : ArrayLike
        The value with which to fill the output Tensor. Note that this function
        is not differentiable – the resulting tensor will not backprop through
        `fill_value`.

        The value with which to fill the output Tensor.

    dtype : Optional[DTypeLikeReals]
        The data type of the output Tensor, or None to match `fill_value`..

    constant : Optional[bool]
        If ``True``, this tensor is a constant, and thus does not facilitate
        back propagation.

        Defaults to ``False`` for float-type data.
        Defaults to ``True`` for integer-type data.

        Integer-type tensors must be constant.

    Returns
    -------
    Tensor
        A Tensor of `fill_value` with the given shape and dtype.

    References
    ----------
    .. [1] Retrieved from https://numpy.org/doc/stable/reference/generated/numpy.full.html

    Examples
    --------
    >>> import mygrad as mg
    >>> mg.full((2, 2), 33)
    Tensor([[ 33,  33],
            [ 33,  33]])

    >>> mg.full((2, 2), 10)
    Tensor([[10, 10],
            [10, 10]])
    """
    return Tensor(
        np.full(shape, fill_value=fill_value, dtype=dtype),
        constant=constant,
        copy=False,
    )

@implements_numpy_override()
def full_like(
    other: ArrayLike,
    fill_value: Real,
    dtype: Optional[DTypeLikeReals] = None,
    shape: Optional[Union[int, Shape]] = None,
    constant: Optional[bool] = None,
) -> Tensor:
    """Return a Tensor of the same shape and type as the given, filled with `fill_value`.

    This docstring was adapted from ``numpy.full_like`` [1]_

    Parameters
    ----------
    other : ArrayLike
        The tensor or array whose shape and datatype should be mirrored.

    fill_value : Real
        The value with which to fill the output Tensor.

    dtype : Optional[DTypeLikeReals]
        Override the data type of the returned Tensor with this value, or None to not override.

    shape : Optional[int, Sequence[int]]
        If specified, overrides the shape of the result

    constant : Optional[bool]
        If ``True``, this tensor is a constant, and thus does not facilitate
        back propagation. If ``None`` then:

        Inferred from ``other``, if other is a tensor
        Defaults to ``False`` for float-type data.
        Defaults to ``True`` for integer-type data.

    Returns
    -------
    Tensor
        A Tensor of `fill_value` whose shape and data type match `other`.

    References
    ----------
    .. [1] Retrieved from https://numpy.org/doc/stable/reference/generated/numpy.full_like.html

    Examples
    --------
    >>> import mygrad as mg
    >>> x = mg.arange(6, dtype=int)
    >>> mg.full_like(x, 1)
    Tensor([1, 1, 1, 1, 1, 1])
    >>> mg.full_like(x, 0.1)
    Tensor([0, 0, 0, 0, 0, 0])
    >>> mg.full_like(x, 0.1, dtype=np.double)
    Tensor([ 0.1,  0.1,  0.1,  0.1,  0.1,  0.1])
    >>> mg.full_like(x, np.nan, dtype=np.double)
    Tensor([ nan,  nan,  nan,  nan,  nan,  nan])

    >>> y = mg.arange(6, dtype=np.double)
    >>> mg.full_like(y, 0.1)
    Tensor([ 0.1,  0.1,  0.1,  0.1,  0.1,  0.1])
    """
    constant = _resolve_constant(other, constant=constant)

    return Tensor(
        np.full_like(
            _anything_but_tensor(other),
            fill_value=_anything_but_tensor(fill_value),
            dtype=dtype,
            shape=shape,
        ),
        constant=constant,
        copy=False,
    )

def arange(
    *args,
    constant: Optional[bool] = None,
    **kwargs,
) -> Tensor:
    """
    arange([start,] stop[, step,], dtype=None, *, constant=None)

    Return a Tensor with evenly-spaced values within a given interval.

    Values are generated within [start, stop). Note that for non-integer steps, results may be
    inconsistent; you are better off using `linspace` instead.

    This docstring was adapted from ``numpy.arange`` [1]_

    Parameters
    ----------
    start : Real, optional, default=0
        The start of the interval, inclusive.

    stop : Real
        The end of the interval, exclusive.

    step : int, optional (default=1)
        The spacing between successive values.

    dtype : Optional[DTypeLikeReals]
        The data type of the output Tensor, or None to infer from the inputs.

    constant : Optional[bool]
        If ``True``, this tensor is a constant, and thus does not facilitate
        back propagation.

        Defaults to ``False`` for float-type data.
        Defaults to ``True`` for integer-type data.

        Integer-type tensors must be constant.

    Returns
    -------
    Tensor
        A Tensor of evenly-spaced values in [start, end).

    References
    ----------
    .. [1] Retrieved from https://numpy.org/doc/stable/reference/generated/numpy.arange.html

    Examples
    --------
    >>> import mygrad as mg
    >>> mg.arange(3)
    Tensor([0, 1, 2])
    >>> mg.arange(3.0, constant=True)  # resulting tensor will not back-propagate a gradient
    Tensor([ 0.,  1.,  2.])
    >>> mg.arange(3,7)
    Tensor([3, 4, 5, 6])
    >>> mg.arange(3,7,2)
    Tensor([3, 5])
    """
    return Tensor(np.arange(*args, **kwargs), constant=constant, copy=False)

def linspace(
    start: ArrayLike,
    stop: ArrayLike,
    num: int = 50,
    endpoint: bool = True,
    dtype: Optional[DTypeLikeReals] = None,
    axis: int = 0,
    *,
    constant: Optional[bool] = None,
) -> Tensor:
    """Return a Tensor with evenly-spaced numbers over a specified interval.

    Values are generated within [start, stop], with the endpoint optionally excluded.

    This docstring was adapted from ``numpy.linspace`` [1]_

    Parameters
    ----------
    start : ArrayLike
        The starting value of the sequence, inclusive.

    stop : ArrayLike
        The ending value of the sequence, inclusive unless `include_endpoint` is False.

    num : int, optional (default=50)
        The number of values to generate. Must be non-negative.

    endpoint : bool, optional (default=True)
        Whether to include the endpoint in the Tensor. Note that if False, the step size changes
        to accommodate the sequence excluding the endpoint.

    dtype : Optional[DTypeLikeReals]
        The data type of the output Tensor, or None to infer from the inputs.

    axis : int, optional (default=0)
        The axis in the result to store the samples - for array-like start/stop.

    constant : Optional[bool]
        If ``True``, this tensor is a constant, and thus does not facilitate
        back propagation.

        Defaults to ``False`` for float-type data.
        Defaults to ``True`` for integer-type data.

        Integer-type tensors must be constant.

    Returns
    -------
    Tensor

    References
    ----------
    .. [1] Retrieved from https://numpy.org/doc/stable/reference/generated/numpy.linspace.html

    See Also
    --------
    arange : Similar to `linspace`, but uses a step size (instead of the
             number of samples).
    logspace : Samples uniformly distributed in log space.

    Examples
    --------
    >>> import mygrad as mg
    >>> mg.linspace(2.0, 3.0, num=5)
    Tensor([ 2.  ,  2.25,  2.5 ,  2.75,  3.  ])
    >>> mg.linspace(2.0, 3.0, num=5, endpoint=False)
    Tensor([ 2. ,  2.2,  2.4,  2.6,  2.8])
    """
    return Tensor(
        np.linspace(
            start,
            stop,
            num,
            endpoint=endpoint,
            dtype=dtype,
            axis=axis,
        ),
        constant=constant,
        copy=False,
    )

def logspace(
    start: ArrayLike,
    stop: ArrayLike,
    num: int = 50,
    endpoint: bool = True,
    base: Real = 10,
    dtype: Optional[DTypeLikeReals] = None,
    axis: int = 0,
    *,
    constant: Optional[bool] = None,
) -> Tensor:
    """Return a Tensor with evenly-spaced numbers over a specified interval on a log scale.
    This is not a differentiable function - it does not propagate gradients to its inputs.

    In linear space, values are generated within [base**start, base**stop], with the endpoint
    optionally excluded.

    This docstring was adapted from ``numpy.logspace`` [1]_

    Parameters
    ----------
    start : ArrayLike
        The starting value of the sequence, inclusive; start at `base ** start`.

    stop : ArrayLike
        The ending value of the sequence, inclusive unless `include_endpoint` is False; end at
        `base ** stop`.

    num : int, optional (default=50)
        The number of values to generate. Must be non-negative.

    endpoint : bool, optional (default=True)
        Whether to include the endpoint in the Tensor. Note that if False, the step size changes
        to accommodate the sequence excluding the endpoint.

    base : Real, optional (default=10)
        The base of the log space.

    dtype : Optional[DTypeLikeReals]
        The data type of the output Tensor, or None to infer from the inputs.

    axis : int, optional (default=0)
        The axis in the result to store the samples - for array-like start/stop.

    constant : Optional[bool]
        If ``True``, this tensor is a constant, and thus does not facilitate
        back propagation.

        Defaults to ``False`` for float-type data.
        Defaults to ``True`` for integer-type data.

        Integer-type tensors must be constant.

    Returns
    -------
    Tensor

    See Also
    --------
    arange : Similar to linspace, with the step size specified instead of the
             number of samples. Note that, when used with a float endpoint, the
             endpoint may or may not be included.
    linspace : Similar to logspace, but with the samples uniformly distributed
               in linear space, instead of log space.
    geomspace : Similar to logspace, but with endpoints specified directly.

    References
    ----------
    .. [1] Retrieved from https://numpy.org/doc/stable/reference/generated/numpy.logspace.html

    Examples
    --------
    >>> import mygrad as mg
    >>> mg.logspace(2.0, 3.0, num=4)
    Tensor([  100.        ,   215.443469  ,   464.15888336,  1000.        ])
    >>> mg.logspace(2.0, 3.0, num=4, endpoint=False)
    Tensor([ 100.        ,  177.827941  ,  316.22776602,  562.34132519])
    >>> mg.logspace(2.0, 3.0, num=4, base=2.0)
    Tensor([ 4.        ,  5.0396842 ,  6.34960421,  8.        ])

    """
    return Tensor(
        np.logspace(
            start=start,
            stop=stop,
            num=num,
            endpoint=endpoint,
            base=base,
            dtype=dtype,
            axis=axis,
        ),
        constant=constant,
        copy=False,
    )

def geomspace(
    start: ArrayLike,
    stop: ArrayLike,
    num=50,
    endpoint=True,
    dtype=None,
    axis=0,
    *,
    constant: Optional[bool] = None,
) -> Tensor:
    """Return a Tensor with evenly-spaced values in a geometric progression.

    Each output sample is a constant multiple of the previous output.

    This docstring was adapted from ``numpy.geomspace`` [1]_

    Parameters
    ----------
    start : ArrayLike
        The starting value of the output.

    stop : ArrayLike
        The ending value of the sequence, inclusive unless `endpoint` is false.

    num : int, optional (default=50)
        The number of values to generate. Must be non-negative.

    endpoint : bool, optional (default=True)
        Whether to include the endpoint in the Tensor. Note that if False, the step size changes
        to accommodate the sequence excluding the endpoint.

    dtype : Optional[DTypeLikeReals]
        The data type of the output Tensor, or None to infer from the inputs.

    axis : int, optional (default=0)
        The axis in the result to store the samples - for array-like start/stop.

    constant : Optional[bool]
        If ``True``, this tensor is a constant, and thus does not facilitate
        back propagation.

        Defaults to ``False`` for float-type data.
        Defaults to ``True`` for integer-type data.

        Integer-type tensors must be constant.

    Returns
    -------
    Tensor

    References
    ----------
    .. [1] Retrieved from https://numpy.org/doc/stable/reference/generated/numpy.geomspace.html

    See Also
    --------
    logspace : Similar to geomspace, but with endpoints specified using log
               and base.
    linspace : Similar to geomspace, but with arithmetic instead of geometric
               progression.
    arange : Similar to linspace, with the step size specified instead of the
             number of samples.

    Examples
    --------
    >>> import mygrad as mg
    >>> mg.geomspace(1, 1000, num=4)
    Tensor([    1.,    10.,   100.,  1000.])
    >>> mg.geomspace(1, 1000, num=3, endpoint=False)
    Tensor([   1.,   10.,  100.])
    >>> mg.geomspace(1, 1000, num=4, endpoint=False)
    Tensor([   1.        ,    5.62341325,   31.6227766 ,  177.827941  ])
    >>> mg.geomspace(1, 256, num=9)
    Tensor([   1.,    2.,    4.,    8.,   16.,   32.,   64.,  128.,  256.])

    Note that the above may not produce exact integers:

    >>> mg.geomspace(1, 256, num=9, dtype=int)
    Tensor([  1,   2,   4,   7,  16,  32,  63, 127, 256])
    >>> np.around(mg.geomspace(1, 256, num=9).data).astype(int)
    array([  1,   2,   4,   8,  16,  32,  64, 128, 256])

    Negative, and decreasing inputs are allowed:

    >>> mg.geomspace(1000, 1, num=4)
    Tensor([ 1000.,   100.,    10.,     1.])
    >>> mg.geomspace(-1000, -1, num=4)
    Tensor([-1000.,  -100.,   -10.,    -1.])
    """
    return Tensor(
        np.geomspace(
            start=start,
            stop=stop,
            num=num,
            endpoint=endpoint,
            dtype=dtype,
            axis=axis,
        ),
        constant=constant,
        copy=False,
    )


#!/usr/bin/env python3

"""
@author: Marcelo Fialho Jacinto
@email: marcelo.jacinto@tecnico.ulisboa.pt
@date: 30/03/2022
@licence: MIT
"""
import rospy
import numpy as np
from std_msgs.msg import Float64
from auv_msgs.msg import BodyForceRequest

class OpenLoopNode:
    """
    Remote controller ROS node class. Receives from a joystick (using pygame) the desired controls for
    the inner-loops and publishes these periodically (at a pre-defined frequency) to the inner-loops of the
    vehicle
    """

    def __init__(self):
        """
        Class constructor. Initializes the ros node, loads the parameters from the ros parameter server, creates the inner-loops
        publishers and initializes the timer that publishes the desired inputs to the inner-loops
        """

        # ---Initialize the ROS NODE---
        rospy.init_node('open_loop_controller')

        # --- Load parameters from the ROS parameter server
        self.load_params()

        # --- Initialize subscribers and publishers
        self.initializeSubscribers()
        self.initializePublishers()

        self.h_timerActivate = False

        # --- Desired variables to apply to the vehicle (NOTE: this is not speeds but rather forces and torques to applyt to the vehicle)
        self.surge_desired = 0.0       # Fx (force on x-axis)
        self.sway_desired = 0.0        # Fy (force on y-axis)
        self.heave_desired = 0.0       # Fz (force on z-axis)
        self.yaw_rate_desired = 0.0    # Tz (torque about the z-axis)

        # Last time instant where a message with desired speeds was received
        self.last_update = rospy.Time.now()

        # ---Start the ROS NODE callback---
        self.initializeTimer()

    def load_params(self):

        # Read the node frequency
        self.node_frequency = rospy.get_param('~node_frequency', 10)

        # Read the configurations from the ros parameter server
        self.gain_Fx = rospy.get_param('~gain_Fx')
        self.gain_Fy = rospy.get_param('~gain_Fy')
        self.gain_Fz = rospy.get_param('~gain_Fz')
        self.gain_Tz = rospy.get_param('~gain_Tz')

    def initializeSubscribers(self):
        self.surge_sub = rospy.Subscriber(rospy.get_param('~topics/subscribers/surge_ref'), Float64, self.surgeCallback)
        self.sway_sub = rospy.Subscriber(rospy.get_param('~topics/subscribers/sway_ref'), Float64, self.swayCallback)
        self.heave_sub = rospy.Subscriber(rospy.get_param('~topics/subscribers/heave_ref'), Float64, self.heaveCallback)
        self.yaw_rate_sub = rospy.Subscriber(rospy.get_param('~topics/subscribers/yaw_rate_ref'), Float64, self.yawRateCallback)

    def initializePublishers(self):
        self.force_pub = rospy.Publisher(rospy.get_param('~topics/publishers/body_force_request'), BodyForceRequest, queue_size=1)

    def timerCallback(self, event):
        """
        Callback used to publish to the inner-loops the desired control inputs
        :param event: A timer event - unused but required by the ROS API
        """

        # Scale the desired input (surge, sway, heave and yaw-rate to some force and torque)
        output_msg = BodyForceRequest()
        output_msg.header.stamp = rospy.Time.now()

        output_msg.wrench.force.x = self.gain_Fx * self.surge_desired
        output_msg.wrench.force.y = self.gain_Fy * self.sway_desired
        output_msg.wrench.force.z = self.gain_Fz * self.heave_desired

        output_msg.wrench.torque.x = 0.0
        output_msg.wrench.torque.y = 0.0
        output_msg.wrench.torque.z = self.gain_Tz * self.yaw_rate_desired

        self.force_pub.publish(output_msg)

    # ---- Callbacks section ----

    def surgeCallback(self, msg: Float64):
        self.surge_desired = float(msg.data)
        self.last_update = rospy.Time.now()

    def swayCallback(self, msg: Float64):
        self.sway_desired = float(msg.data)
        self.last_update = rospy.Time.now()

    def heaveCallback(self, msg: Float64):
        self.heave_desired = float(msg.data)
        self.last_update = rospy.Time.now()

    def yawRateCallback(self, msg: Float64):
        self.yaw_rate_desired = float(msg.data)
        self.last_update = rospy.Time.now()

    def initializeTimer(self):
        """
        Method that starts the system timer that periodically calls a callback
        """
        self.timer = rospy.Timer(rospy.Duration(1.0 / self.node_frequency), self.timerCallback)

def main():
    """
    Initialize the RemoteControllerNode and let the timer callback do all the work
    """
    open_loop_controller = OpenLoopNode()
    rospy.spin()

if __name__ == '__main__':
    main()


"""Custom type aliases.

This module defines commonly used types in the library. These are separated into two
different kinds, API types and argument types.

API types are aliases which define custom types used throughout the library. Objects of
this type may be supplied as arguments or returned by a method.

Argument types are aliases which define commonly used method arguments. These should
only ever be used in the signature of a method and then be converted internally, e.g.
in a class instantiation or an interface. They enable the user to conveniently
specify a variety of object types for the same argument, while ensuring a unified
internal representation of those same objects.
"""

from __future__ import annotations

import numbers
from typing import Iterable, Tuple, Union

import numpy as np
import scipy.sparse
from numpy.typing import ArrayLike as _NumPyArrayLike
from numpy.typing import DTypeLike as _NumPyDTypeLike

########################################################################################
# API Types
########################################################################################

# Array Utilities
ShapeType = Tuple[int, ...]

# Scalars, Arrays and Matrices
ScalarType = np.ndarray
MatrixType = Union[np.ndarray, "probnum.linops.LinearOperator"]

########################################################################################
# Argument Types
########################################################################################

# Python Numbers
IntLike = Union[int, numbers.Integral, np.integer]
"""Type of a public API argument for supplying an integer.

Values of this type should always be converted into :class:`int`\\ s before further
internal processing."""

FloatLike = Union[float, numbers.Real, np.floating]
"""Type of a public API argument for supplying a float.

Values of this type should always be converteg into :class:`float`\\ s before further
internal processing."""

# Array Utilities
ShapeLike = Union[IntLike, Iterable[IntLike]]
"""Type of a public API argument for supplying a shape.

Values of this type should always be converted into :class:`ShapeType` using the
function :func:`probnum.utils.as_shape` before further internal processing."""

DTypeLike = _NumPyDTypeLike
"""Type of a public API argument for supplying an array's dtype.

Values of this type should always be converted into :class:`np.dtype`\\ s before further
internal processing."""

_ArrayIndexLike = Union[
    int,
    slice,
    type(Ellipsis),
    None,
    np.newaxis,
    np.ndarray,
]
ArrayIndicesLike = Union[_ArrayIndexLike, Tuple[_ArrayIndexLike, ...]]
"""Type of the argument to the :meth:`__getitem__` method of a NumPy-like array type
such as :class:`np.ndarray`, :class:`probnum.linops.LinearOperator` or
:class:`probnum.randvars.RandomVariable`."""

# Scalars, Arrays and Matrices
ScalarLike = Union[int, float, complex, numbers.Number, np.number]
"""Type of a public API argument for supplying a scalar value.

Values of this type should always be converted into :class:`np.number`\\ s using the
function :func:`probnum.utils.as_scalar` before further internal processing."""

ArrayLike = _NumPyArrayLike
"""Type of a public API argument for supplying an array.

Values of this type should always be converted into :class:`np.ndarray`\\ s using
the function :func:`np.asarray` before further internal processing."""

LinearOperatorLike = Union[
    ArrayLike,
    scipy.sparse.spmatrix,
    "probnum.linops.LinearOperator",
]
"""Type of a public API argument for supplying a finite-dimensional linear operator.

Values of this type should always be converted into :class:`probnum.linops.\\
LinearOperator`\\ s using the function :func:`probnum.linops.aslinop` before further
internal processing."""

########################################################################################
# Other Types
########################################################################################

NotImplementedType = type(NotImplemented)


"""
Retour sur la discrétisation des variables quantitatives
----------------------------------------------------------------
Les méthodes de discrétisation les plus courantes sont disponibles sous Geopandas (quantile, intervalle égal, Jenks). La discrétisation dite de Jenks qui recherche à construire des classes les plus homogènes possibles est en générale la plus pertinente. Néanmoins, ce n'est pas toujours le cas, comme en témoigne les cartes suivantes de densité de population départementale.
"""
# sphinx_gallery_thumbnail_number = 3
import pandas as pd
import geopandas as gpd
import numpy as np

import matplotlib.pyplot as plt
###############################################################################
# Lecture des données
# ================================
#
# On ouvre le fond du bâti de la ville de Caen.


#!/usr/bin/env python3

import matplotlib.pyplot as plt
import numpy as np

plt.title('Un primo plot con Python')
plt.xlabel('Tc')
plt.ylabel('Tf')
x, y = np.loadtxt('temp.dat', unpack=True)
plt.plot(x, y, 'x', label='Temperature caricate da file')
plt.xlim(-10, 50)
plt.ylim(10, 125)
plt.show()


# -*- coding: utf-8 -*-
"""
Created on Wed Jul 11 19:41:15 2018
@author: Moseli Motsoehli
Title: Satander starter
"""
###########################libraries################################
####################################################################
import pandas as pd
import numpy as np
import seaborn as sb

from sklearn.model_selection import train_test_split as tts

####################################################################
###########################configs##################################
data_location = "E:/ML Personal/setander/"
val_size = 0.2

####################################################################
#########################Data Exploration###########################
data=pd.read_csv(data_location+"train.csv")

data.head(2)

features=[k for k in data]
print('Missing Values in each feature')
for feat in features:
    print('%s: %s'%(feat,data[feat].isnull().sum()))
    #print('%s: %s'%(feat,sum(data[feat])))

####################################################################
#########################Healper Functions###########################


import pytest
from pytest import warns
import mock
import numpy as np
import gilp
from gilp.simplex import (InvalidBasis, Infeasible, InfeasibleBasicSolution,
                          UnboundedLinearProgram, _invertible, _phase_one,
                          _simplex_iteration, branch_and_bound_iteration, BFS)

class TestLP:

    def test_init_exceptions(self):
        with pytest.raises(ValueError, match='.*b should have one of .*'):
            A = np.array([[1,0],[0,1]])
            b = np.array([[1],[2],[3]])
            c = np.array([[1],[2]])
            gilp.LP(A,b,c)
        with pytest.raises(ValueError, match='.*c should have one of .*'):
            A = np.array([[1,0],[0,1]])
            b = np.array([[1],[2]])
            c = np.array([[1],[2],[3]])
            gilp.LP(A,b,c)

    @pytest.mark.parametrize("lp,n,m,A,b,c,equality",[
        (gilp.LP(np.array([[1,2],[3,0]]),
                 np.array([3,4]),
                 np.array([1,2])),
         2,2,
         np.array([[1,2],[3,0]]),
         np.array([[3],[4]]),
         np.array([[1],[2]]),
         False),
        (gilp.LP(np.array([[1,2,3],[3,0,1]]),
                 np.array([[3],[4]]),
                 np.array([[1],[2],[3]]),
                 equality=True),
         3,2,
         np.array([[1,2,3],[3,0,1]]),
         np.array([[3],[4]]),
         np.array([[1],[2],[3]]),
         True)])
    def test_init(self,lp,n,m,A,b,c,equality):
        actual = lp.get_coefficients(equality=equality)
        assert n == actual[0]
        assert m == actual[1]
        assert (A == actual[2]).all()
        assert (b == actual[3]).all()
        assert (c == actual[4]).all()
        assert (equality == lp.equality)

    def test_get_bfs(self, degenerate_lp):
        lp = degenerate_lp
        bfs = np.array([[2],[4],[0],[0],[4],[1],[0]])
        assert (bfs == lp.get_basic_feasible_sol([0,1,4,5,6]).x).all()
        assert (bfs == lp.get_basic_feasible_sol([0,1,2,4,5]).x).all()
        assert (bfs == lp.get_basic_feasible_sol([0,1,3,4,5]).x).all()
        with pytest.raises(InvalidBasis):
            lp.get_basic_feasible_sol([1,2,3,4])
        with pytest.raises(InvalidBasis):
            lp.get_basic_feasible_sol([0,1,2,4,5,6])
        with pytest.raises(InfeasibleBasicSolution):
            lp.get_basic_feasible_sol([0,1,2,3,5])

    def test_get_all_bfs(self):
        A = np.array([[1,1],[-2,1]])
        b = np.array([[6],[0]])
        c = np.array([[1],[1]])
        lp = gilp.LP(A,b,c)
        bfs = [np.array([[2],[4],[0],[0]]),
               np.array([[0],[0],[6],[0]]),
               np.array([[6],[0],[0],[12]]),
               np.array([[0],[0],[6],[0]]),
               np.array([[0],[0],[6],[0]])]
        bases = [[0,1],[0,2],[0,3],
                 [1,2],[2,3]]
        values = [6,0,6,0,0]
        optimal = [False]*5
        actual = lp.get_basic_feasible_solns()
        actual_bfs = [x.x for x in actual]
        actual_bases = [x.B for x in actual]
        actual_values = [x.obj_val for x in actual]
        actual_optimal = [x.optimal for x in actual]

        assert all(np.allclose(x,y,atol=1e-7) for x,y in zip(bfs, actual_bfs))
        assert bases == actual_bases
        assert values == actual_values
        assert optimal == actual_optimal

    def test_tableau(self, degenerate_lp):
        T = np.array([[1,0,0,1,1,0,0,0,10],
                      [0,1,0,1,-1,0,0,0,2],
                      [0,0,1,0,1,0,0,0,4],
                      [0,0,0,-1,2,1,0,0,4],
                      [0,0,0,-1,1,0,1,0,1],
                      [0,0,0,2,-3,0,0,1,0]])
        assert (T == degenerate_lp.get_tableau([0,1,4,5,6])).all()
        with pytest.raises(InvalidBasis):
            degenerate_lp.get_tableau([1,2,3])

class TestSimplexIteration:

    def test_bad_inputs(self, klee_minty_3d_lp):
        bfs = BFS(x=np.array([[5],[5],[0],[0],[0],[65]]),
                  B=[0,1,5],
                  obj_val=95,
                  optimal=False)
        with pytest.raises(ValueError,match='Invalid pivot rule.*'):
            _simplex_iteration(lp=klee_minty_3d_lp,
                               bfs=bfs,
                               pivot_rule='invalid')
        with pytest.raises(ValueError,match='x should have shape.*'):
            bfs = BFS(x=np.array([[5],[5],[0],[0],[0]]),
                      B=[0,1,5],
                      obj_val=95,
                      optimal=False)
            _simplex_iteration(lp=klee_minty_3d_lp,
                               bfs=bfs,
                               pivot_rule='bland')

    def test_bland(self, klee_minty_3d_lp):
        bfs = BFS(x=np.array([[5],[5],[0],[0],[0],[65]]),
                  B=[0,1,5],
                  obj_val=95,
                  optimal=False)
        actual = _simplex_iteration(lp=klee_minty_3d_lp,
                                    bfs=bfs,
                                    pivot_rule='bland')
        assert (np.array([[5],[5],[65],[0],[0],[0]]) == actual[0]).all()
        actual[1].sort()
        assert [0,1,2] == actual[1]
        assert 95 == actual[2]
        assert not actual[3]

    def test_min_index(self, klee_minty_3d_lp):
        bfs = BFS(x=np.array([[5],[5],[0],[0],[0],[65]]),
                  B=[0,1,5],
                  obj_val=95,
                  optimal=False)
        actual = _simplex_iteration(lp=klee_minty_3d_lp,
                                    bfs=bfs,
                                    pivot_rule='min_index')
        assert (np.array([[5],[5],[65],[0],[0],[0]]) == actual[0]).all()
        actual[1].sort()
        assert [0,1,2] == actual[1]
        assert 95 == actual[2]
        assert not actual[3]

    def test_dantzig(self, klee_minty_3d_lp):
        bfs = BFS(x=np.array([[0],[0],[0],[5],[25],[125]]),
                  B=[3,4,5],
                  obj_val=0,
                  optimal=False)
        actual = _simplex_iteration(lp=klee_minty_3d_lp,
                                    bfs=bfs,
                                    pivot_rule='dantzig')
        assert (np.array([[5],[0],[0],[0],[5],[85]]) == actual[0]).all()
        actual[1].sort()
        assert [0,4,5] == actual[1]
        assert 20 == actual[2]
        assert not actual[3]

    def test_max_reduced_cost(self, klee_minty_3d_lp):
        bfs = BFS(x=np.array([[0],[0],[0],[5],[25],[125]]),
                  B=[3,4,5],
                  obj_val=0,
                  optimal=False)
        actual = _simplex_iteration(lp=klee_minty_3d_lp,
                                    bfs=bfs,
                                    pivot_rule='max_reduced_cost')
        assert (np.array([[5],[0],[0],[0],[5],[85]]) == actual[0]).all()
        actual[1].sort()
        assert [0,4,5] == actual[1]
        assert 20 == actual[2]
        assert not actual[3]

    def test_greatest_ascent1(self, klee_minty_3d_lp):
        bfs = BFS(x=np.array([[0],[0],[0],[5],[25],[125]]),
                  B=[3,4,5],
                  obj_val=0,
                  optimal=False)
        actual = _simplex_iteration(lp=klee_minty_3d_lp,
                                    bfs=bfs,
                                    pivot_rule='greatest_ascent')
        assert (np.array([[0],[0],[125],[5],[25],[0]]) == actual[0]).all()
        actual[1].sort()
        assert [2,3,4] == actual[1]
        assert 125 == actual[2]
        assert not actual[3]

    def test_greatest_ascent2(self, klee_minty_3d_lp):
        bfs = BFS(x=np.array([[0],[0],[125],[5],[25],[0]]),
                  B=[2,3,4],
                  obj_val=125,
                  optimal=False)
        actual = _simplex_iteration(lp=klee_minty_3d_lp,
                                    bfs=bfs,
                                    pivot_rule='greatest_ascent')
        assert (np.array([[0],[0],[125],[5],[25],[0]]) == actual[0]).all()
        actual[1].sort()
        assert [2,3,4] == actual[1]
        assert 125 == actual[2]
        assert actual[3]

    def test_manual_select(self, klee_minty_3d_lp):
        with mock.patch('builtins.input', return_value="2"):
            bfs = BFS(x=np.array([[0],[0],[0],[5],[25],[125]]),
                      B=[3,4,5],
                      obj_val=0,
                      optimal=False)
            actual = _simplex_iteration(lp=klee_minty_3d_lp,
                                        bfs=bfs,
                                        pivot_rule='manual_select')
            assert (np.array([[0],[25],[0],[5],[0],[25]]) == actual[0]).all()
            actual[1].sort()
            assert [1,3,5] == actual[1]
            assert 50 == actual[2]
            assert not actual[3]
        with mock.patch('builtins.input', return_value="2"):
            bfs = BFS(x=np.array([[0],[0],[0],[5],[25],[125]]),
                      B=[3,4,5],
                      obj_val=0,
                      optimal=False)
            actual = _simplex_iteration(lp=klee_minty_3d_lp,
                                        bfs=bfs,
                                        pivot_rule='manual')
            assert (np.array([[0],[25],[0],[5],[0],[25]]) == actual[0]).all()
            actual[1].sort()
            assert [1,3,5] == actual[1]
            assert 50 == actual[2]
            assert not actual[3]

class TestSimplex():

    def test_bad_inputs(self, klee_minty_3d_lp, unbounded_lp):
        with pytest.raises(ValueError,match='Invalid pivot rule.*'):
            gilp.simplex(lp=klee_minty_3d_lp,
                         pivot_rule='invalid')
        with pytest.raises(ValueError,match='.* following shapes: .*'):
            gilp.simplex(lp=klee_minty_3d_lp,
                         initial_solution=np.array([[5],[5],[0],[0]]))
        with pytest.raises(ValueError,match='.* should have one of .*'):
            gilp.simplex(lp=gilp.LP(np.array([[1]]),
                                    np.array([[1]]),
                                    np.array([[1]]),
                                    equality=True),
                         initial_solution=np.array([[1],[1]]))
        with pytest.raises(ValueError,match='Iteration limit*'):
            gilp.simplex(lp=klee_minty_3d_lp,
                         iteration_limit=-1)
        with warns(UserWarning, match='.*was not a basic feasible solution.*'):
            gilp.simplex(klee_minty_3d_lp,
                         initial_solution=np.array([[2],[2],[2]]))
        with pytest.raises(UnboundedLinearProgram):
            gilp.simplex(unbounded_lp,'greatest_ascent')

    def test_simplex(self, klee_minty_3d_lp):
        actual = gilp.simplex(klee_minty_3d_lp,
                              pivot_rule='dantzig',
                              initial_solution=np.array([[0],[0],[0],
                                                         [5],[25],[125]]))
        bfs = np.array([[0],[0],[125],[5],[25],[0]])
        bases = [2,3,4]
        assert np.allclose(bfs,actual[0],atol=1e-7)
        assert np.allclose(bases,actual[1],atol=1e-7)
        assert 125 == actual[2]
        assert actual[3]

    def test_initial_solution(self, klee_minty_3d_lp):
        actual = gilp.simplex(klee_minty_3d_lp,
                              initial_solution=[5, 5, 65],
                              pivot_rule='dantzig')
        bfs = np.array([[0],[0],[125],[5],[25],[0]])
        bases = [2,3,4]
        assert np.allclose(bfs,actual[0],atol=1e-7)
        assert np.allclose(bases,actual[1],atol=1e-7)
        assert 125 == actual[2]
        assert actual[3]

    def test_degenerate_init_sol(self):
        A = np.array([[1,1],[0,1],[1,-1],[1,0],[-2,1]])
        b = np.array([[6],[4],[2],[3],[0]])
        c = np.array([[1],[0]])
        lp = gilp.LP(A,b,c)
        gilp.simplex(lp,initial_solution=np.array([[2],[4]]))

    def test_iteration_limit(self, klee_minty_3d_lp):
        actual = gilp.simplex(klee_minty_3d_lp,
                              pivot_rule='dantzig',
                              iteration_limit=3,
                              initial_solution=[[0],[0],[0],[5],[25],[125]])
        bfs = np.array([[0],[25],[0],[5],[0],[25]])
        bases = [1,5,3]
        assert np.allclose(bfs,actual[0],atol=1e-7)
        assert np.allclose(bases,actual[1],atol=1e-7)
        assert 50 == actual[2]
        assert not actual[3]

class TestPhaseOne():

    @pytest.mark.parametrize("lp,bfs",[
        (gilp.LP([[1,1,0],[-1,1,-1]],
                 [3, 1],
                 [2, 1, 0],
                 equality=True), (np.array([[1],[2],[0]]),[0,1]))])
    def test_phase_one(self,lp,bfs):
        x,B = _phase_one(lp)[:2]
        assert all(x == bfs[0])
        assert B == bfs[1]

    @pytest.mark.parametrize("lp",[
        (gilp.LP(np.array([[1],[-1]]),
                 np.array([2,-3]),
                 np.array([1]))),
        (gilp.LP(np.array([[1,0],[0,-1],[-1,0]]),
                 np.array([2,-3,-3]),
                 np.array([1,1]))),
        (gilp.LP(np.array([[0,1],[0,-1],[-1,0]]),
                 np.array([2,-3,-3]),
                 np.array([1,2])))])
    def test_infeasible(self,lp):
        with pytest.raises(Infeasible):
            _phase_one(lp)

    @pytest.mark.parametrize("lp,bfs",[
        (gilp.LP(np.array([[1],[1]]),
                 np.array([[0],[0]]),
                 np.array([[1]]),
                 equality=True), (np.array([[0]]),[0])),
        (gilp.LP(np.array([[1],[1]]),
                 np.array([[3],[3]]),
                 np.array([[1]]),
                 equality=True), (np.array([[3]]),[0])),
        (gilp.LP(np.array([[1,1],[1,1]]),
                 np.array([[1],[1]]),
                 np.array([[2],[1]]),
                 equality=True), (np.array([[1],[0]]),[0])),
        (gilp.LP(np.array([[1,1],[1,0]]),
                 np.array([[1],[1]]),
                 np.array([[1],[1]]),
                 equality=True), (np.array([[1],[0]]),[0,1]))])
    def test_degenerate(self,lp,bfs):
        x,B = _phase_one(lp)[:2]
        assert all(x == bfs[0])
        assert B == bfs[1]

@pytest.mark.parametrize("A,t",[
    (np.array([[1,0],[0,1]]), True),
    (np.array([[0,1],[1,0]]), True),
    (np.array([[1,0,0],[0,1,0]]), False),
    (np.array([[2,0,0],[0,0,3],[0,1,0]]), True)])
def test_invertible(A,t):
    assert _invertible(A) == t

@pytest.mark.parametrize("lp, expected",[
    (gilp.examples.ALL_INTEGER_2D_LP,
     np.array([[0.0, 0.0],
               [7.0, 0.0],
               [-0.0, 16.0],
               [7.0, 6.0],
               [4.0, 12.0]])),
    (gilp.examples.ALL_INTEGER_3D_LP,
     np.array([[0.0, 0.0, 0.0],
               [-0.0, 8.0, -0.0],
               [6.0, 0.0, 2.0],
               [6.0, 0.0, 0.0],
               [6.0, 6.0, 2.0],
               [6.0, 8.0, -0.0],
               [3.0, 0.0, 5.0],
               [0.0, 0.0, 5.0],
               [3.0, 3.0, 5.0],
               [0.0, 3.0, 5.0]]))])
def test_get_vertices(lp, expected):
    result = lp.get_vertices()
    result = np.array([list(x[:,0]) for x in result])
    assert (result == expected).all()

def test_get_vertices_equality_lp():
    with pytest.raises(ValueError, match='.*be in standard inequality .*'):
        lp = gilp.LP([[1,1]], [1], [1,2], equality=True)
        lp.get_vertices()

def test_branch_and_bound_manual():
    lp = gilp.LP(np.array([[1,1],[5,9]]),
                 np.array([[6],[45]]),
                 np.array([[5],[8]]))
    with mock.patch('builtins.input', return_value="0"):
        with pytest.raises(ValueError,match='index can not be branched on.'):
            iteration = branch_and_bound_iteration(lp, None, None, True)
    with mock.patch('builtins.input', return_value="2"):
        iteration = branch_and_bound_iteration(lp, None, None, True)
        assert not iteration.fathomed
        assert iteration.incumbent is None
        assert iteration.best_bound is None
        assert all(gilp.simplex(iteration.right_LP).x[:2]
                   == np.array([[1.8],[4]]))
        assert all(gilp.simplex(iteration.left_LP).x[:2]
                   == np.array([[3],[3]]))

@pytest.mark.parametrize("lp,x,val",[
    (gilp.LP(np.array([[-2,2],[2,2]]),
             np.array([[1],[7]]),
             np.array([[1],[2]])),
     np.array([[2],[1]]),
     4.0),
    (gilp.LP(np.array([[1,1],[5,9]]),
             np.array([[6],[45]]),
             np.array([[5],[8]])),
     np.array([[0],[5]]),
     40.0),
    (gilp.LP(np.array([[1,10],[1,0]]),
             np.array([[20],[2]]),
             np.array([[0],[5]])),
     np.array([[0],[2]]),
     10.0),
    (gilp.LP(np.array([[-2,2,1,0],[2,2,0,1]]),
             np.array([[1],[7]]),
             np.array([[1],[2],[0],[0]]),equality=True),
     np.array([[2],[1],[3],[1]]),
     4.0),
    (gilp.LP(np.array([[1,1,1,0],[5,9,0,1]]),
             np.array([[6],[45]]),
             np.array([[5],[8],[0],[0]]),equality=True),
     np.array([[0],[5],[1],[0]]),
     40.0),
    (gilp.LP(np.array([[1,10,1,0],[1,0,0,1]]),
             np.array([[20],[2]]),
             np.array([[0],[5],[0],[0]]),equality=True),
     np.array([[0],[2],[0],[2]]),
     10.0)])
def test_branch_and_bound(lp,x,val):
    ans = gilp.branch_and_bound(lp)
    assert all(x == ans[0])
    assert val == ans[1]