import json
from datetime import date, datetime, timedelta
from random import randint

import pandas as pd
import pkg_resources
import pytz
import requests
from lxml.html import fromstring
from unidecode import unidecode

from .data.funds_data import (
    fund_countries_as_list,
    funds_as_df,
    funds_as_dict,
    funds_as_list,
)
from .utils.data import Data
from .utils.extra import random_user_agent

def get_funds(country=None):
    """
    This function retrieves all the available `funds` from Investing.com and returns them as a :obj:`pandas.DataFrame`,
    which contains not just the fund names, but all the fields contained on the `funds.csv` file.
    All the available funds can be found at: https://www.investing.com/funds/

    Args:
        country (:obj:`str`, optional): name of the country to retrieve all its available funds from.

    Returns:
        :obj:`pandas.DataFrame` - funds_df:
            The resulting :obj:`pandas.DataFrame` contains all the funds basic information retrieved from Investing.com,
            some of which is not useful for the user, but for the inner package functions, such as the `id` field,
            for example.

            In case the information was successfully retrieved, the :obj:`pandas.DataFrame` will look like::

                country | name | symbol | issuer | isin | asset_class | currency | underlying
                --------|------|--------|--------|------|-------------|----------|------------
                xxxxxxx | xxxx | xxxxxx | xxxxxx | xxxx | xxxxxxxxxxx | xxxxxxxx | xxxxxxxxxx

    Raises:
        ValueError: raised whenever any of the introduced arguments is not valid or errored.
        FileNotFoundError: raised when the `funds.csv` file was not found.
        IOError: raised if the `funds.csv` file is missing or errored.

    """

    return funds_as_df(country=country)

def get_funds_list(country=None):
    """
    This function retrieves all the available funds and returns a list of each one of them.
    All the available funds can be found at: https://www.investing.com/funds/

    Args:
        country (:obj:`str`, optional): name of the country to retrieve all its available funds from.

    Returns:
        :obj:`list` - funds_list:
            The resulting list contains the retrieved data, which corresponds to the fund names of
            every fund listed on Investing.com.

            In case the information was successfully retrieved from the CSV file, the :obj:`list` will look like::

                funds = [
                    'Blackrock Global Funds - Global Allocation Fund E2',
                    'Quality Inversión Conservadora Fi',
                    'Nordea 1 - Stable Return Fund E Eur',
                    ...
                ]

    Raises:
        ValueError: raised whenever any of the introduced arguments is not valid or errored.
        FileNotFoundError: raised when the `funds.csv` file was not found.
        IOError: raised if the `funds.csv` file is missing or errored.

    """

    return funds_as_list(country=country)

def get_funds_dict(country=None, columns=None, as_json=False):
    """
    This function retrieves all the available funds on Investing.com and returns them as a :obj:`dict` containing
    the country, name, symbol, tag, id, issuer, isin, asset_class, currency and underlying data. All the available
    funds can be found at: https://www.investing.com/funds/

    Args:
        country (:obj:`str`, optional): name of the country to retrieve all its available funds from.
        columns (:obj:`list` of :obj:`str`, optional): description
            a :obj:`list` containing the column names from which the data is going to be retrieved.
        as_json (:obj:`bool`, optional): description
            value to determine the format of the output data (:obj:`dict` or :obj:`json`).

    Returns:
        :obj:`dict` or :obj:`json` - funds_dict:
            The resulting :obj:`dict` contains the retrieved data if found, if not, the corresponding
            fields are filled with `None` values.

            In case the information was successfully retrieved, the :obj:`dict` will look like::

                {
                    'country': country,
                    'name': name,
                    'symbol': symbol,
                    'issuer': issuer,
                    'isin': isin,
                    'asset_class': asset_class,
                    'currency': currency,
                    'underlying': underlying
                }

    Raises:
        ValueError: raised whenever any of the introduced arguments is not valid or errored.
        FileNotFoundError: raised when the `funds.csv` file was not found.
        IOError: raised if the `funds.csv` file is missing or errored.

    """

    return funds_as_dict(country=country, columns=columns, as_json=as_json)

def get_fund_countries():
    """
    This function retrieves all the country names indexed in Investing.com with available funds to retrieve data
    from, via reading the `fund_countries.csv` file from the resources directory. So on, this function will display a
    listing containing a set of countries, in order to let the user know which countries are taken into account and also
    the return listing from this function can be used for country param check if needed.

    Returns:
        :obj:`list` - countries:
            The resulting :obj:`list` contains all the available countries with funds as indexed in Investing.com

    Raises:
        FileNotFoundError: raised when the `fund_countries.csv` file was not found.
        IndexError: raised if `fund_countries.csv` file was unavailable or not found.

    """

    return fund_countries_as_list()

def get_fund_recent_data(
    fund, country, as_json=False, order="ascending", interval="Daily"
):
    """
    This function retrieves recent historical data from the introduced `fund` from Investing
    via Web Scraping. The resulting data can it either be stored in a :obj:`pandas.DataFrame` or in a
    :obj:`json` file, with `ascending` or `descending` order.

    Args:
        fund (:obj:`str`): name of the fund to retrieve recent historical data from.
        country (:obj:`str`): name of the country from where the introduced fund is.
        as_json (:obj:`bool`, optional):
            optional argument to determine the format of the output data (:obj:`pandas.DataFrame` or :obj:`json`).
        order (:obj:`str`, optional):
            optional argument to define the order of the retrieved data (`ascending`, `asc` or `descending`, `desc`).
        interval (:obj:`str`, optional):
            value to define the historical data interval to retrieve, by default `Daily`, but it can also be `Weekly` or `Monthly`.

    Returns:
        :obj:`pandas.DataFrame` or :obj:`json`:
            The function returns a either a :obj:`pandas.DataFrame` or a :obj:`json` file containing the retrieved
            recent data from the specified fund via argument. The dataset contains the open, high, low and close
            values for the selected fund on market days.

            The returned data is case we use default arguments will look like::

                Date || Open | High | Low | Close | Currency
                -----||------|------|-----|-------|----------
                xxxx || xxxx | xxxx | xxx | xxxxx | xxxxxxxx

            but if we define `as_json=True`, then the output will be::

                {
                    name: name,
                    recent: [
                        {
                            date: dd/mm/yyyy,
                            open: x,
                            high: x,
                            low: x,
                            close: x,
                            currency: x
                        },
                        ...
                    ]
                }

    Raises:
        ValueError: argument error.
        IOError: funds object/file not found or unable to retrieve.
        RuntimeError: introduced fund does not match any of the indexed ones.
        ConnectionError: if GET requests does not return 200 status code.
        IndexError: if fund information was unavailable or not found.

    Examples:
        >>> data = investpy.get_fund_recent_data(fund='bbva multiactivo conservador pp', country='spain')
        >>> data.head()
                     Open   High    Low  Close Currency
        Date
        2019-08-13  1.110  1.110  1.110  1.110      EUR
        2019-08-16  1.109  1.109  1.109  1.109      EUR
        2019-08-19  1.114  1.114  1.114  1.114      EUR
        2019-08-20  1.112  1.112  1.112  1.112      EUR
        2019-08-21  1.115  1.115  1.115  1.115      EUR

    """

    if not fund:
        raise ValueError(
            "ERR#0029: fund parameter is mandatory and must be a valid fund name."
        )

    if not isinstance(fund, str):
        raise ValueError("ERR#0028: fund argument needs to be a str.")

    if country is None:
        raise ValueError("ERR#0039: country can not be None, it should be a str.")

    if country is not None and not isinstance(country, str):
        raise ValueError("ERR#0025: specified country value not valid.")

    if not isinstance(as_json, bool):
        raise ValueError(
            "ERR#0002: as_json argument can just be True or False, bool type."
        )

    if order not in ["ascending", "asc", "descending", "desc"]:
        raise ValueError(
            "ERR#0003: order argument can just be ascending (asc) or descending (desc),"
            " str type."
        )

    if not interval:
        raise ValueError(
            "ERR#0073: interval value should be a str type and it can just be either"
            " 'Daily', 'Weekly' or 'Monthly'."
        )

    if not isinstance(interval, str):
        raise ValueError(
            "ERR#0073: interval value should be a str type and it can just be either"
            " 'Daily', 'Weekly' or 'Monthly'."
        )

    interval = interval.lower()

    if interval not in ["daily", "weekly", "monthly"]:
        raise ValueError(
            "ERR#0073: interval value should be a str type and it can just be either"
            " 'Daily', 'Weekly' or 'Monthly'."
        )

    resource_package = "investpy"
    resource_path = "/".join(("resources", "funds.csv"))
    if pkg_resources.resource_exists(resource_package, resource_path):
        funds = pd.read_csv(
            pkg_resources.resource_filename(resource_package, resource_path),
            keep_default_na=False,
        )
    else:
        raise FileNotFoundError("ERR#0057: funds file not found or errored.")

    if funds is None:
        raise IOError("ERR#0005: funds object not found or unable to retrieve.")

    country = unidecode(country.strip().lower())

    if country not in get_fund_countries():
        raise RuntimeError(
            "ERR#0034: country " + country + " not found, check if it is correct."
        )

    funds = funds[funds["country"].str.lower() == country]

    fund = unidecode(fund.strip().lower())

    if fund not in list(funds["name"].apply(unidecode).str.lower()):
        raise RuntimeError(
            "ERR#0019: fund " + fund + " not found, check if it is correct."
        )

    symbol = funds.loc[
        (funds["name"].apply(unidecode).str.lower() == fund).idxmax(), "symbol"
    ]
    id_ = funds.loc[(funds["name"].apply(unidecode).str.lower() == fund).idxmax(), "id"]
    name = funds.loc[
        (funds["name"].apply(unidecode).str.lower() == fund).idxmax(), "name"
    ]
    fund_currency = funds.loc[
        (funds["name"].apply(unidecode).str.lower() == fund).idxmax(), "currency"
    ]

    header = symbol + " Historical Data"

    params = {
        "curr_id": id_,
        "smlID": str(randint(1000000, 99999999)),
        "header": header,
        "interval_sec": interval.capitalize(),
        "sort_col": "date",
        "sort_ord": "DESC",
        "action": "historical_data",
    }

    head = {
        "User-Agent": random_user_agent(),
        "X-Requested-With": "XMLHttpRequest",
        "Accept": "text/html",
        "Accept-Encoding": "gzip, deflate",
        "Connection": "keep-alive",
    }

    url = "https://www.investing.com/instruments/HistoricalDataAjax"

    req = requests.post(url, headers=head, data=params)

    if req.status_code != 200:
        raise ConnectionError(
            "ERR#0015: error " + str(req.status_code) + ", try again later."
        )

    root_ = fromstring(req.text)
    path_ = root_.xpath(".//table[@id='curr_table']/tbody/tr")
    result = list()

    if path_:
        for elements_ in path_:
            if elements_.xpath(".//td")[0].text_content() == "No results found":
                raise IndexError("ERR#0008: fund information unavailable or not found.")

            info = []

            for nested_ in elements_.xpath(".//td"):
                info.append(nested_.get("data-real-value"))

            fund_date = datetime.strptime(
                str(
                    datetime.fromtimestamp(int(info[0]), tz=pytz.timezone("GMT")).date()
                ),
                "%Y-%m-%d",
            )

            fund_close = float(info[1].replace(",", ""))
            fund_open = float(info[2].replace(",", ""))
            fund_high = float(info[3].replace(",", ""))
            fund_low = float(info[4].replace(",", ""))

            result.insert(
                len(result),
                Data(
                    fund_date,
                    fund_open,
                    fund_high,
                    fund_low,
                    fund_close,
                    None,
                    fund_currency,
                    None,
                ),
            )

        if order in ["ascending", "asc"]:
            result = result[::-1]
        elif order in ["descending", "desc"]:
            result = result

        if as_json is True:
            json_ = {"name": name, "recent": [value.fund_as_json() for value in result]}

            return json.dumps(json_, sort_keys=False)
        elif as_json is False:
            df = pd.DataFrame.from_records([value.fund_to_dict() for value in result])
            df.set_index("Date", inplace=True)

            return df
    else:
        raise RuntimeError("ERR#0004: data retrieval error while scraping.")

def get_fund_historical_data(
    fund,
    country,
    from_date,
    to_date,
    as_json=False,
    order="ascending",
    interval="Daily",
):
    """
    This function retrieves historical data from the introduced `fund` from Investing
    via Web Scraping on the introduced date range. The resulting data can it either be
    stored in a :obj:`pandas.DataFrame` or in a :obj:`json` object with `ascending` or `descending` order.

    Args:
        fund (:obj:`str`): name of the fund to retrieve recent historical data from.
        country (:obj:`str`): name of the country from where the introduced fund is.
        from_date (:obj:`str`): date as `str` formatted as `dd/mm/yyyy`, from where data is going to be retrieved.
        to_date (:obj:`str`): date as `str` formatted as `dd/mm/yyyy`, until where data is going to be retrieved.
        as_json (:obj:`bool`, optional):
            to determine the format of the output data (:obj:`pandas.DataFrame` or :obj:`json`).
        order (:obj:`str`, optional):
            optional argument to define the order of the retrieved data (`ascending`, `asc` or `descending`, `desc`).
        interval (:obj:`str`, optional):
            value to define the historical data interval to retrieve, by default `Daily`, but it can also be `Weekly` or `Monthly`.

    Returns:
        :obj:`pandas.DataFrame` or :obj:`json`:
            The function returns a either a :obj:`pandas.DataFrame` or a :obj:`json` file containing the retrieved
            recent data from the specified fund via argument. The dataset contains the open, high, low and close
            values for the selected fund on market days.

            The returned data is case we use default arguments will look like::

                Date || Open | High | Low | Close | Currency
                -----||------|------|-----|-------|----------
                xxxx || xxxx | xxxx | xxx | xxxxx | xxxxxxxx

            but if we define `as_json=True`, then the output will be::

                {
                    name: name,
                    historical: [
                        {
                            date: dd/mm/yyyy,
                            open: x,
                            high: x,
                            low: x,
                            close: x,
                            currency: x
                        },
                        ...
                    ]
                }

    Raises:
        ValueError: argument error.
        IOError: funds object/file not found or unable to retrieve.
        RuntimeError: introduced fund does not match any of the indexed ones.
        ConnectionError: if GET requests does not return 200 status code.
        IndexError: if fund information was unavailable or not found.

    Examples:
        >>> data = investpy.get_fund_historical_data(fund='bbva multiactivo conservador pp', country='spain', from_date='01/01/2010', to_date='01/01/2019')
        >>> data.head()
                     Open   High    Low  Close Currency
        Date
        2018-02-15  1.105  1.105  1.105  1.105      EUR
        2018-02-16  1.113  1.113  1.113  1.113      EUR
        2018-02-17  1.113  1.113  1.113  1.113      EUR
        2018-02-18  1.113  1.113  1.113  1.113      EUR
        2018-02-19  1.111  1.111  1.111  1.111      EUR

    """

    if not fund:
        raise ValueError(
            "ERR#0029: fund parameter is mandatory and must be a valid fund name."
        )

    if not isinstance(fund, str):
        raise ValueError("ERR#0028: fund argument needs to be a str.")

    if country is None:
        raise ValueError("ERR#0039: country can not be None, it should be a str.")

    if country is not None and not isinstance(country, str):
        raise ValueError("ERR#0025: specified country value not valid.")

    if not isinstance(as_json, bool):
        raise ValueError(
            "ERR#0002: as_json argument can just be True or False, bool type."
        )

    if order not in ["ascending", "asc", "descending", "desc"]:
        raise ValueError(
            "ERR#0003: order argument can just be ascending (asc) or descending (desc),"
            " str type."
        )

    if not interval:
        raise ValueError(
            "ERR#0073: interval value should be a str type and it can just be either"
            " 'Daily', 'Weekly' or 'Monthly'."
        )

    if not isinstance(interval, str):
        raise ValueError(
            "ERR#0073: interval value should be a str type and it can just be either"
            " 'Daily', 'Weekly' or 'Monthly'."
        )

    interval = interval.lower()

    if interval not in ["daily", "weekly", "monthly"]:
        raise ValueError(
            "ERR#0073: interval value should be a str type and it can just be either"
            " 'Daily', 'Weekly' or 'Monthly'."
        )

    try:
        datetime.strptime(from_date, "%d/%m/%Y")
    except ValueError:
        raise ValueError(
            "ERR#0011: incorrect start date format, it should be 'dd/mm/yyyy'."
        )

    try:
        datetime.strptime(to_date, "%d/%m/%Y")
    except ValueError:
        raise ValueError(
            "ERR#0012: incorrect to_date format, it should be 'dd/mm/yyyy'."
        )

    start_date = datetime.strptime(from_date, "%d/%m/%Y")
    end_date = datetime.strptime(to_date, "%d/%m/%Y")

    if start_date >= end_date:
        raise ValueError(
            "ERR#0032: to_date should be greater than from_date, both formatted as"
            " 'dd/mm/yyyy'."
        )

    date_interval = {
        "intervals": [],
    }

    flag = True

    while flag is True:
        diff = end_date.year - start_date.year

        if diff > 19:
            obj = {
                "start": start_date.strftime("%m/%d/%Y"),
                "end": start_date.replace(year=start_date.year + 19).strftime(
                    "%m/%d/%Y"
                ),
            }

            date_interval["intervals"].append(obj)

            start_date = start_date.replace(year=start_date.year + 19) + timedelta(
                days=1
            )
        else:
            obj = {
                "start": start_date.strftime("%m/%d/%Y"),
                "end": end_date.strftime("%m/%d/%Y"),
            }

            date_interval["intervals"].append(obj)

            flag = False

    interval_limit = len(date_interval["intervals"])
    interval_counter = 0

    data_flag = False

    resource_package = "investpy"
    resource_path = "/".join(("resources", "funds.csv"))
    if pkg_resources.resource_exists(resource_package, resource_path):
        funds = pd.read_csv(
            pkg_resources.resource_filename(resource_package, resource_path),
            keep_default_na=False,
        )
    else:
        raise FileNotFoundError("ERR#0057: funds file not found or errored.")

    if funds is None:
        raise IOError("ERR#0005: funds object not found or unable to retrieve.")

    country = unidecode(country.strip().lower())

    if country not in get_fund_countries():
        raise RuntimeError(
            "ERR#0034: country " + country + " not found, check if it is correct."
        )

    funds = funds[funds["country"].str.lower() == country]

    fund = unidecode(fund.strip().lower())

    if fund not in list(funds["name"].apply(unidecode).str.lower()):
        raise RuntimeError(
            "ERR#0019: fund " + fund + " not found, check if it is correct."
        )

    symbol = funds.loc[
        (funds["name"].apply(unidecode).str.lower() == fund).idxmax(), "symbol"
    ]
    id_ = funds.loc[(funds["name"].apply(unidecode).str.lower() == fund).idxmax(), "id"]
    name = funds.loc[
        (funds["name"].apply(unidecode).str.lower() == fund).idxmax(), "name"
    ]
    fund_currency = funds.loc[
        (funds["name"].apply(unidecode).str.lower() == fund).idxmax(), "currency"
    ]

    final = list()

    header = symbol + " Historical Data"

    for index in range(len(date_interval["intervals"])):
        params = {
            "curr_id": id_,
            "smlID": str(randint(1000000, 99999999)),
            "header": header,
            "st_date": date_interval["intervals"][index]["start"],
            "end_date": date_interval["intervals"][index]["end"],
            "interval_sec": interval.capitalize(),
            "sort_col": "date",
            "sort_ord": "DESC",
            "action": "historical_data",
        }

        head = {
            "User-Agent": random_user_agent(),
            "X-Requested-With": "XMLHttpRequest",
            "Accept": "text/html",
            "Accept-Encoding": "gzip, deflate",
            "Connection": "keep-alive",
        }

        url = "https://www.investing.com/instruments/HistoricalDataAjax"

        req = requests.post(url, headers=head, data=params)

        if req.status_code != 200:
            raise ConnectionError(
                "ERR#0015: error " + str(req.status_code) + ", try again later."
            )

        if not req.text:
            continue

        root_ = fromstring(req.text)
        path_ = root_.xpath(".//table[@id='curr_table']/tbody/tr")

        result = list()

        if path_:
            for elements_ in path_:
                if elements_.xpath(".//td")[0].text_content() == "No results found":
                    if interval_counter < interval_limit:
                        data_flag = False
                    else:
                        raise IndexError(
                            "ERR#0008: fund information unavailable or not found."
                        )
                else:
                    data_flag = True

                info = []

                for nested_ in elements_.xpath(".//td"):
                    info.append(nested_.get("data-real-value"))

                if data_flag is True:
                    fund_date = datetime.strptime(
                        str(
                            datetime.fromtimestamp(
                                int(info[0]), tz=pytz.timezone("GMT")
                            ).date()
                        ),
                        "%Y-%m-%d",
                    )

                    fund_close = float(info[1].replace(",", ""))
                    fund_open = float(info[2].replace(",", ""))
                    fund_high = float(info[3].replace(",", ""))
                    fund_low = float(info[4].replace(",", ""))

                    result.insert(
                        len(result),
                        Data(
                            fund_date,
                            fund_open,
                            fund_high,
                            fund_low,
                            fund_close,
                            None,
                            fund_currency,
                            None,
                        ),
                    )

            if data_flag is True:
                if order in ["ascending", "asc"]:
                    result = result[::-1]
                elif order in ["descending", "desc"]:
                    result = result

                if as_json is True:
                    json_list = [value.fund_as_json() for value in result]

                    final.append(json_list)
                elif as_json is False:
                    df = pd.DataFrame.from_records(
                        [value.fund_to_dict() for value in result]
                    )
                    df.set_index("Date", inplace=True)

                    final.append(df)

        else:
            raise RuntimeError("ERR#0004: data retrieval error while scraping.")

    if order in ["descending", "desc"]:
        final.reverse()

    if as_json is True:
        json_ = {
            "name": name,
            "historical": [value for json_list in final for value in json_list],
        }
        return json.dumps(json_, sort_keys=False)
    elif as_json is False:
        return pd.concat(final)

def get_fund_information(fund, country, as_json=False):
    """
    This function retrieves basic financial information from the specified fund. Retrieved information
    from the fund can be valuable as it is additional information that can be used combined with OHLC
    values, so to determine financial insights from the company which holds the specified fund.

    Args:
        fund (:obj:`str`): name of the fund to retrieve the financial information from.
        country (:obj:`str`): name of the country from where the introduced fund is.
        as_json (:obj:`bool`, optional):
            optional argument to determine the format of the output data (:obj:`dict` or :obj:`json`).

    Returns:
        :obj:`pandas.DataFrame` or :obj:`dict`- fund_information:
            The resulting :obj:`pandas.DataFrame` contains the information fields retrieved from Investing.com
            from the specified fund; it can also be returned as a :obj:`dict`, if argument `as_json=True`.

            If any of the information fields could not be retrieved, that field/s will be filled with
            None values. If the retrieval process succeeded, the resulting :obj:`dict` will look like::

                fund_information = {
                    'Fund Name': fund_name,
                    'Rating': rating,
                    '1-Year Change': year_change,
                    'Previous Close': prev_close,
                    'Risk Rating': risk_rating,
                    'TTM Yield': ttm_yield,
                    'ROE': roe,
                    'Issuer': issuer,
                    'Turnover': turnover,
                    'ROA': row,
                    'Inception Date': inception_date,
                    'Total Assets': total_assets,
                    'Expenses': expenses,
                    'Min Investment': min_investment,
                    'Market Cap': market_cap,
                    'Category': category
                }

    """

    if not fund:
        raise ValueError(
            "ERR#0029: fund parameter is mandatory and must be a valid fund name."
        )

    if not isinstance(fund, str):
        raise ValueError("ERR#0028: fund argument needs to be a str.")

    if country is None:
        raise ValueError("ERR#0039: country can not be None, it should be a str.")

    if country is not None and not isinstance(country, str):
        raise ValueError("ERR#0025: specified country value not valid.")

    if not isinstance(as_json, bool):
        raise ValueError(
            "ERR#0002: as_json argument can just be True or False, bool type."
        )

    resource_package = "investpy"
    resource_path = "/".join(("resources", "funds.csv"))
    if pkg_resources.resource_exists(resource_package, resource_path):
        funds = pd.read_csv(
            pkg_resources.resource_filename(resource_package, resource_path),
            keep_default_na=False,
        )
    else:
        raise FileNotFoundError("ERR#0057: funds file not found or errored.")

    if funds is None:
        raise IOError("ERR#0005: funds object not found or unable to retrieve.")

    country = unidecode(country.strip().lower())

    if country not in get_fund_countries():
        raise RuntimeError(
            "ERR#0034: country " + country + " not found, check if it is correct."
        )

    funds = funds[funds["country"] == country]

    fund = unidecode(fund.strip().lower())

    if fund not in list(funds["name"].apply(unidecode).str.lower()):
        raise RuntimeError(
            "ERR#0019: fund " + fund + " not found, check if it is correct."
        )

    tag = funds.loc[
        (funds["name"].apply(unidecode).str.lower() == fund).idxmax(), "tag"
    ]

    url = "https://www.investing.com/funds/" + tag

    head = {
        "User-Agent": random_user_agent(),
        "X-Requested-With": "XMLHttpRequest",
        "Accept": "text/html",
        "Accept-Encoding": "gzip, deflate",
        "Connection": "keep-alive",
    }

    req = requests.get(url, headers=head)

    if req.status_code != 200:
        raise ConnectionError(
            "ERR#0015: error " + str(req.status_code) + ", try again later."
        )

    root_ = fromstring(req.text)
    path_ = root_.xpath("//div[contains(@class, 'overviewDataTable')]/div")

    result = pd.DataFrame(
        columns=[
            "Fund Name",
            "Rating",
            "1-Year Change",
            "Previous Close",
            "Risk Rating",
            "TTM Yield",
            "ROE",
            "Issuer",
            "Turnover",
            "ROA",
            "Inception Date",
            "Total Assets",
            "Expenses",
            "Min Investment",
            "Market Cap",
            "Category",
        ]
    )

    result.at[0, "Fund Name"] = fund

    if path_:
        for elements_ in path_:
            element = elements_.xpath(".//span[@class='float_lang_base_1']")[0]
            title_ = element.text_content()
            if title_ == "Day's Range":
                title_ = "Todays Range"
            if title_ in result.columns.tolist():
                try:
                    result.at[0, title_] = float(
                        element.getnext().text_content().replace(",", "")
                    )
                    continue
                except:
                    pass
                try:
                    text = element.getnext().text_content().strip()
                    result.at[0, title_] = datetime.strptime(
                        text, "%b %d, %Y"
                    ).strftime("%d/%m/%Y")
                    continue
                except:
                    pass
                try:
                    value = element.getnext().text_content().strip()
                    if value.__contains__("K"):
                        value = float(value.replace("K", "").replace(",", "")) * 1e3
                    elif value.__contains__("M"):
                        value = float(value.replace("M", "").replace(",", "")) * 1e6
                    elif value.__contains__("B"):
                        value = float(value.replace("B", "").replace(",", "")) * 1e9
                    elif value.__contains__("T"):
                        value = float(value.replace("T", "").replace(",", "")) * 1e12
                    result.at[0, title_] = value
                    continue
                except:
                    pass

        result.replace({"N/A": None}, inplace=True)

        if as_json is True:
            json_ = result.iloc[0].to_dict()
            return json_
        elif as_json is False:
            return result
    else:
        raise RuntimeError("ERR#0004: data retrieval error while scraping.")

def get_funds_overview(country, as_json=False, n_results=100):
    """
    This function retrieves an overview containing all the real time data available for the main funds from a country,
    such as the names, symbols, current value, etc. as indexed in Investing.com. So on, the main usage of this
    function is to get an overview on the main funds from a country, so to get a general view. Note that since
    this function is retrieving a lot of information at once, by default just the overview of the Top 100 funds
    is being retrieved, but an additional parameter called n_results can be specified so to retrieve N results.

    Args:
        country (:obj:`str`): name of the country to retrieve the funds overview from.
        as_json (:obj:`bool`, optional):
            optional argument to determine the format of the output data (:obj:`pandas.DataFrame` or :obj:`json`).
        n_results (:obj:`int`, optional): number of results to be displayed on the overview table (0-1000).

    Returns:
        :obj:`pandas.DataFrame` - funds_overview:
            The resulting :obj:`pandas.DataFrame` contains all the data available in Investing.com of the main ETFs
            from a country in order to get an overview of it.

            If the retrieval process succeeded, the resulting :obj:`pandas.DataFrame` should look like::

                country | name | symbol | last | change | total_assets
                --------|------|--------|------|--------|--------------
                xxxxxxx | xxxx | xxxxxx | xxxx | xxxxxx | xxxxxxxxxxxx

    Raises:
        ValueError: raised if there was any argument error.
        FileNotFoundError: raised when `funds.csv` file is missing.
        IOError: raised if data could not be retrieved due to file error.
        RuntimeError:
            raised either if the introduced country does not match any of the listed ones or if no overview results could be
            retrieved from Investing.com.
        ConnectionError: raised if GET requests does not return 200 status code.

    """

    if country is None:
        raise ValueError("ERR#0039: country can not be None, it should be a str.")

    if country is not None and not isinstance(country, str):
        raise ValueError("ERR#0025: specified country value not valid.")

    if not isinstance(as_json, bool):
        raise ValueError(
            "ERR#0002: as_json argument can just be True or False, bool type."
        )

    if not isinstance(n_results, int):
        raise ValueError(
            "ERR#0089: n_results argument should be an integer between 1 and 1000."
        )

    if 1 > n_results or n_results > 1000:
        raise ValueError(
            "ERR#0089: n_results argument should be an integer between 1 and 1000."
        )

    resource_package = "investpy"
    resource_path = "/".join(("resources", "funds.csv"))
    if pkg_resources.resource_exists(resource_package, resource_path):
        funds = pd.read_csv(
            pkg_resources.resource_filename(resource_package, resource_path),
            keep_default_na=False,
        )
    else:
        raise FileNotFoundError("ERR#0057: funds file not found or errored.")

    if funds is None:
        raise IOError("ERR#0005: funds object not found or unable to retrieve.")

    country = unidecode(country.strip().lower())

    if country not in get_fund_countries():
        raise RuntimeError("ERR#0025: specified country value is not valid.")

    funds = funds[funds["country"] == country]

    if country.lower() == "united states":
        country = "usa"
    elif country.lower() == "united kingdom":
        country = "uk"

    head = {
        "User-Agent": random_user_agent(),
        "X-Requested-With": "XMLHttpRequest",
        "Accept": "text/html",
        "Accept-Encoding": "gzip, deflate",
        "Connection": "keep-alive",
    }

    url = (
        "https://www.investing.com/funds/"
        + country.replace(" ", "-")
        + "-funds?&issuer_filter=0"
    )

    req = requests.get(url, headers=head)

    if req.status_code != 200:
        raise ConnectionError(
            "ERR#0015: error " + str(req.status_code) + ", try again later."
        )

    root_ = fromstring(req.text)
    table = root_.xpath(".//table[@id='etfs']/tbody/tr")

    results = list()

    if len(table) > 0:
        for row in table[:n_results]:
            id_ = row.get("id").replace("pair_", "")
            symbol = row.xpath(".//td[contains(@class, 'symbol')]")[0].get("title")

            nested = row.xpath(".//a")[0]
            name = nested.get("title").rstrip()
            full_name = nested.text.strip()

            country_flag = row.xpath(".//td[@class='flag']/span")[0].get("title")
            country_flag = unidecode(country_flag.lower())

            last_path = ".//td[@class='" + "pid-" + str(id_) + "-last" + "']"
            last = row.xpath(last_path)[0].text_content()

            if last == "":
                continue

            change_path = (
                ".//td[contains(@class, '" + "pid-" + str(id_) + "-pcp" + "')]"
            )
            change = row.xpath(change_path)[0].text_content()

            total_assets_path = change_path
            total_assets = row.xpath(total_assets_path)[0].getnext().text_content()

            if total_assets == "":
                total_assets = 0
            else:
                if total_assets.__contains__("K"):
                    total_assets = (
                        float(total_assets.replace("K", "").replace(",", "")) * 1e3
                    )
                elif total_assets.__contains__("M"):
                    total_assets = (
                        float(total_assets.replace("M", "").replace(",", "")) * 1e6
                    )
                elif total_assets.__contains__("B"):
                    total_assets = (
                        float(total_assets.replace("B", "").replace(",", "")) * 1e9
                    )
                else:
                    total_assets = float(total_assets.replace(",", ""))

            data = {
                "country": country_flag,
                "name": name,
                "full_name": full_name,
                "symbol": symbol,
                "last": float(last.replace(",", "")),
                "change": change,
                "total_assets": int(total_assets),
                "currency": funds.loc[(funds["name"] == name).idxmax(), "currency"],
            }

            results.append(data)
    else:
        raise RuntimeError(
            "ERR#0092: no data found while retrieving the overview from Investing.com"
        )

    df = pd.DataFrame(results)

    if as_json:
        return json.loads(df.to_json(orient="records"))
    else:
        return df

def search_funds(by, value):
    """
    This function searches funds by the introduced value for the specified field. This means that this function
    is going to search if there is a value that matches the introduced value for the specified field which is the
    `funds.csv` column name to search in. Available fields to search funds are 'name', 'symbol', 'issuer' and 'isin'.

    Args:
        by (:obj:`str`):
            name of the field to search for, which is the column name ('name', 'symbol', 'issuer' or 'isin').
        value (:obj:`str`): value of the field to search for, which is the str that is going to be searched.

    Returns:
        :obj:`pandas.DataFrame` - search_result:
            The resulting `pandas.DataFrame` contains the search results from the given query (the specified value
            in the specified field). If there are no results and error will be raised, but otherwise this
            `pandas.DataFrame` will contain all the available field values that match the introduced query.

    Raises:
        ValueError: raised if any of the introduced params is not valid or errored.
        FileNotFoundError: raised if `funds.csv` file is missing.
        IOError: raised if data could not be retrieved due to file error.
        RuntimeError: raised if no results were found for the introduced value in the introduced field.

    """

    if not by:
        raise ValueError(
            "ERR#0006: the introduced field to search is mandatory and should be a str."
        )

    if not isinstance(by, str):
        raise ValueError(
            "ERR#0006: the introduced field to search is mandatory and should be a str."
        )

    if not value:
        raise ValueError(
            "ERR#0017: the introduced value to search is mandatory and should be a str."
        )

    if not isinstance(value, str):
        raise ValueError(
            "ERR#0017: the introduced value to search is mandatory and should be a str."
        )

    resource_package = "investpy"
    resource_path = "/".join(("resources", "funds.csv"))
    if pkg_resources.resource_exists(resource_package, resource_path):
        funds = pd.read_csv(
            pkg_resources.resource_filename(resource_package, resource_path),
            keep_default_na=False,
        )
    else:
        raise FileNotFoundError("ERR#0057: funds file not found or errored.")

    if funds is None:
        raise IOError("ERR#0005: funds object not found or unable to retrieve.")

    funds.drop(columns=["tag", "id"], inplace=True)

    available_search_fields = funds.columns.tolist()

    if isinstance(by, str) and by not in available_search_fields:
        raise ValueError(
            "ERR#0026: the introduced field to search can either just be "
            + " or ".join(available_search_fields)
        )

    funds["matches"] = funds[by].str.contains(value, case=False)

    search_result = funds.loc[funds["matches"] == True].copy()

    if len(search_result) == 0:
        raise RuntimeError(
            "ERR#0043: no results were found for the introduced " + str(by) + "."
        )

    search_result.drop(columns=["matches"], inplace=True)
    search_result.reset_index(drop=True, inplace=True)

    return search_result


import webapp2
from google.appengine.ext.webapp.util import run_wsgi_app
from google.appengine.ext import db
import time

class HtmlPage():
    """
    Class containing basic html page layout.
    """
    header = '<title>%s v2</title>'

    footer = ''

class SpeciesInfo():
    """
    Class containing species info lookup tables.
    """
    common_names = ['Alder',
                    'Aspen',
                    'Starthistle',
                    'Juniper',
                    'Serviceberry',
                    'Sagebrush',
                    'Sumac',
                    'Wildrose',
                    'Fern',
                    'Maple',
                    'Elderberry',
                    'Pine',
                    'Cottonwood',
                    'Willow']

    latin_binomials = [ 'Alnus tenuifolia',
                        'Populus tremuloides',
                        'Centaurea solstitialis',
                        'Juniperus scopulorum',
                        'Amelanchier utahensis',
                        'Artemesia tridentata',
                        'Rhus glabra',
                        'Rosa woodsii',
                        'Pteridium aquilinum',
                        'Acer grandidentatum',
                        'Sambucus nigra',
                        'Pinus ponderosa',
                        'Populus trichocarpa',
                        'Salix fragilis']

    #These levels all need to correspond to the values used in the Unity module.
    #The values shown here are only used for display purposes.
    water_levels = ['High',
                    'Normal',
                    'Normal',
                    'Low',
                    'Normal',
                    'Low',
                    'Normal',
                    'Normal',
                    'High',
                    'Normal',
                    'Normal',
                    'Low',
                    'Normal',
                    'High']

    light_levels = ['Normal',
                    'Normal',
                    'Normal',
                    'Normal',
                    'Low',
                    'High',
                    'High',
                    'Low',
                    'Normal',
                    'Normal',
                    'Normal',
                    'Normal',
                    'Normal',
                    'Normal']

    temperature_levels = ['Low',
                          'Normal',
                          'Normal',
                          'Normal',
                          'Normal',
                          'Normal',
                          'Low',
                          'Normal',
                          'Normal',
                          'Normal',
                          'Normal',
                          'Normal',
                          'Normal',
                          'Normal']

    elevations = [   'Low',
                    'Mid',
                    'Mid',
                    'Mid',
                    'High',
                    'Mid',
                    'Mid',
                    'Mid',
                    'Mid',
                    'Mid',
                    'High',
                    'Mid',
                    'Low',
                    'Mid']

    lifespans = [   'Long (25 years)',
                    'Long (25 years)',
                    'Very Short (3 years)',
                    'Very Long (100 years)',
                    'Short (10 years)',
                    'Very Long (100 years)',
                    'Short (10 years)',
                    'Very Short (3 years)',
                    'Very Short (3 years)',
                    'Long (25 years)',
                    'Short (10 years)',
                    'Very Long (100 years)',
                    'Long (25 years)',
                    'Short (10 years)']

    colonizing_levels = [   'Medium',
                            'Medium',
                            'High',
                            'Low',
                            'Medium',
                            'Low',
                            'Medium',
                            'High',
                            'High',
                            'Medium',
                            'Medium',
                            'Low',
                            'Medium',
                            'Medium']

    replaces_lists = [  '',
                        '',
                        '',
                        '',
                        '',
                        '',
                        '',
                        '',
                        '',
                        '',
                        '',
                        '',
                        '',
                        '']

    replaced_by_lists = ['',
                         '',
                         '',
                         '',
                         '',
                         '',
                         '',
                         '',
                         '',
                         '',
                         '',
                         '',
                         '',
                         '']

    other_notes = [ '',
                    '',
                    'Invasive species',
                    '',
                    '',
                    '',
                    '',
                    '',
                    '',
                    '',
                    '',
                    '',
                    '',
                    '']

class MeadowRecordObject(db.Model):
    """
    Record class representing all the parameters to run a community simulation.
    """
    # Timestamp id for this record
    id = db.StringProperty()

    # CSV integers represeting the Unity3D Tree types for each of the (up to) 5 species in the community
    plant_types = db.StringProperty()

    #Water, light, temperature, and disturbance levels
    water_level = db.IntegerProperty()
    light_level = db.IntegerProperty()
    temperature_level = db.IntegerProperty()
    disturbance_level = db.IntegerProperty()

    #50x50 matrix representing the starting values for each position in the matrix
    #(R=random, N=permanent disturbance, 0=gap, 1-5=plant types)
    starting_matrix = db.TextProperty()

class ParametersFormPageOne(webapp2.RequestHandler):
    """
    First page of the two page community parameters form.
    Accessed by the user by url or hyperlink. Controls environment
    parameters, disturbance level, and species choices.
    """
    def get(self):
        page = HtmlPage()
        info = SpeciesInfo()
        self.response.out.write(page.header % 'Simulation parameters form')
        self.response.out.write(self.form_environment_settings)
        self.response.out.write(self.form_plant_choices_header)
        for i in range(1,6):
            default_selection = ['', '', '', '', '']
            default_selection[i - 1] = 'selected="selected"'
            link_option = ''
            none_option = self.form_none_option
            if (i == 1):
                link_option = self.form_plant_examples_link
                none_option = ''
            self.response.out.write(self.form_plant_choices %
                                    (i, i, none_option, info.common_names[0], default_selection[0],
                                     info.common_names[1], info.common_names[2], info.common_names[3],
                                     info.common_names[4], info.common_names[5], info.common_names[6],
                                     info.common_names[7], default_selection[1], info.common_names[8],
                                     default_selection[2], info.common_names[9], info.common_names[10],
                                     default_selection[3], info.common_names[11], info.common_names[12],
                                     default_selection[4], info.common_names[13], link_option))
        self.response.out.write(self.form_starting_distribution)
        self.response.out.write(self.form_submit_button)
        self.response.out.write(page.footer)

    form_environment_settings = """
        <form enctype="multipart/form-data" action="/parametersform2" method="post">

 Environment Settings:

                Water/moisture/precipitation level:
                <select name="water_level">
                    <option value="4">Highest</option>
                    <option value="3">Higher</option>
                    <option value="2" selected="selected">Normal</option>
                    <option value="1">Lower</option>
                    <option value="0">Lowest</option>
                </select>

                Light level:
                <select name="light_level">
                    <option value="4">Highest</option>
                    <option value="3">Higher</option>
                    <option value="2" selected="selected">Normal</option>
                    <option value="1">Lower</option>
                    <option value="0">Lowest</option>
                </select>

                Temperature level:
                <select name="temperature_level">
                    <option value="4">Highest</option>
                    <option value="3">Higher</option>
                    <option value="2" selected="selected">Normal</option>
                    <option value="1">Lower</option>
                    <option value="0">Lowest</option>
                </select>

                Ongoing disturbance level:
                <select name="disturbance_level">
                    <option value= "0">None</option>
                    <option value= "1">Very Low</option>
                    <option value= "2">Low</option>
                    <option value= "3">High</option>
                    <option value= "4">Very High</option>
                </select>

    """

    form_plant_choices_header = """

Species Settings:

            Select plant species to include in the community.

        """

    form_plant_choices = """

            Species %s
            <select name="plant_code_%s">
                %s
                <option value = "0">%s</option>
                <option value = "1" %s>%s</option>
                <option value = "2">%s</option>
                <option value = "3">%s</option>
                <option value = "4">%s</option>
                <option value = "5">%s</option>
                <option value = "6">%s</option>
                <option value = "7">%s</option>
                <option value = "8" %s>%s</option>
                <option value = "9" %s>%s</option>
                <option value = "10">%s</option>
                <option value = "11" %s>%s</option>
                <option value = "12">%s</option>
                <option value = "13" %s>%s</option>
            </select>
            %s
        """

    form_none_option = '<option value = "-1">**None**</option>'

    form_plant_examples_link = 'View examples'

    form_starting_distribution = """

            Select how the plants should be distributed in Year 0.

            <select name="starting_distribution">
                <option value = "0">Place plants randomly</option>
                <option value = "1">Let me choose</option>
            </select>

        """

    form_submit_button = '<input type="submit" name="next" value="Continue..."></form>'

class ParametersFormPageTwo(webapp2.RequestHandler):
    """
    Second page of the two page community parameters form.
    Controls the starting community.
    """
    def post(self):
        is_nonrandom_distribution = self.request.get('starting_distribution')
        if (is_nonrandom_distribution == '1'):
            #They want to choose the starting plant distribution
            self.response.out.write(self.header % ('Simulation parameters form', self.javascript))
            self.draw_form()
            self.response.out.write(self.footer)
        else:
            #They want a random distribution
            #This is poorly coded.  I'm just duplicating most of the StoreNewParametersClass!
            page = HtmlPage()
            self.response.out.write(page.header % 'Simulation parameters form')
            self.id = str(int(time.time() * 1000.0))
            self.store_record()
            self.response.out.write(self.success_output_all_parameters % self.id)
            self.response.out.write(page.footer)

    def draw_form(self):
        #Set up the default starting matrix with all Rs
        info = SpeciesInfo()
        self.response.out.write(self.form_instructions)
        self.response.out.write(self.form_header)
        self.response.out.write(self.form_table_header)
        for j in range(50):
            for i in range(50):
                index = (j * 50) + i
                self.response.out.write(self.form_button % index)
            if (j != 49):
                self.response.out.write('
')
        self.response.out.write(self.form_table_footer)
        plant_codes_list = []
        for i in range(1,6):
            plant_code = self.request.get('plant_code_%s' % i)
            if (plant_code != '-1'):
                plant_codes_list.append(plant_code)
        plant_codes_count = len(plant_codes_list)
        self.response.out.write(self.form_cell_value_selector_header)
        for i in range(0,plant_codes_count):
            self.response.out.write(self.form_cell_value_selector_option %
                                    (i+1, info.common_names[int(plant_codes_list[i])]))
        self.response.out.write(self.form_cell_value_selector_footer)
        self.response.out.write(self.form_assign_cells_button)
        self.response.out.write(self.form_assign_area_button)
        self.response.out.write(self.form_submit_button)
        self.response.out.write(self.form_passive_hidden_fields %
                                (self.request.get('water_level'),
                                 self.request.get('light_level'),
                                 self.request.get('temperature_level'),
                                 self.request.get('disturbance_level')))
        self.response.out.write(self.form_active_hidden_fields)
        for x in range(1, 6):
            self.response.out.write(self.form_plant_code_hidden_field %
                                    (x, self.request.get('plant_code_%s' % x)))
        self.response.out.write(self.form_footer)
        self.response.out.write(self.footer)

    def store_record(self):
        # Get a db record instance to hold the form data
        record = MeadowRecordObject()
        # Store a timestamp as the record id
        record.id = self.id
        # Store the water_level, light_level, and temperature_level,
        # disturbance_level and list of plant_species.
        record.water_level = int(self.request.get('water_level'))
        record.light_level = int(self.request.get('light_level'))
        record.temperature_level = int(self.request.get('temperature_level'))
        record.disturbance_level = int(self.request.get('disturbance_level'))
        plant_codes_list = []
        for i in range(1,6):
            plant_code = self.request.get('plant_code_%s' % i)
            if (plant_code != '-1'):
                plant_codes_list.append(plant_code)
        record.plant_types = ''
        if (len(plant_codes_list) != 0):
            record.plant_types = ','.join(plant_codes_list)
        # Store a community matrix of all 'R's
        record.starting_matrix = ''
        for i in range(2500):
            record.starting_matrix += 'R'
        record.put()

    success_output_all_parameters = """

                The simulation parameters are ready to load.

            To run the simulation use the following simulation code.

            Write it down - you will need it when you report your results.

%s

        """

    javascript = """

        """

    header = '<title>%s</title>%s'

    form_instructions = """

Year 0 plant distribution:

        Need help?
        """

    form_header = '<form enctype="multipart/form-data" action="/newparameters" method="post">'

    form_table_header = '

'

    form_button = ''

    form_table_footer = '

'

    form_cell_value_selector_header = """
        Cell value:
        <select id="cell_value">
            <option value = "R">Random plant type</option>
            <option value = "N">Permanent disturbance</option>
            <option value = "0">Gap (temporary)</option>
        """

    form_cell_value_selector_option = """
            <option value = "%s">%s</option>
        """

    form_cell_value_selector_footer = """
        </select>
        """

    form_assign_cells_button = """
        <input type="button" id="assign_cells" disabled="disabled" onclick="ApplyValueToCells(); return false;" value="Apply cell value to cells">
        """

    form_assign_area_button = """
        <input type="button" id="assign_area" disabled="disabled" onclick="ApplyValueToArea(); return false;" value="Apply cell value to area">
        """

    form_submit_button = """

<input type="submit" name="submit_value" value="Continue...">
        """

    form_passive_hidden_fields = """
        <input type="hidden" name="water_level" value="%s">
        <input type="hidden" name="light_level" value="%s">
        <input type="hidden" name="temperature_level" value="%s">
        <input type="hidden" name="disturbance_level" value="%s">
        """

    form_active_hidden_fields = """
        <input type="hidden" id="starting_matrix" name="starting_matrix" value="">
        """

    form_plant_code_hidden_field = """
        <input type="hidden" name="plant_code_%s" value="%s">
        """

    form_footer = '</form>'

    footer = ''

class StoreNewParameters(webapp2.RequestHandler):
    def post(self):
        page = HtmlPage()
        self.response.out.write(page.header % 'Simulation parameters form')
        self.id = str(int(time.time() * 1000.0))
        self.store_record()
        self.response.out.write(self.success_output_all_parameters % self.id)
        self.response.out.write(page.footer)

    def store_record(self):
        # Get a db record instance to hold the form data
        record = MeadowRecordObject()
        # Store a timestamp as the record id
        record.id = self.id
        # Store the water_level, light_level, and temperature_level,
        # disturbance_level and list of plant_species.
        record.water_level = int(self.request.get('water_level'))
        record.light_level = int(self.request.get('light_level'))
        record.temperature_level = int(self.request.get('temperature_level'))
        record.disturbance_level = int(self.request.get('disturbance_level'))
        plant_codes_list = []
        for i in range(1,6):
            plant_code = self.request.get('plant_code_%s' % i)
            if (plant_code != '-1'):
                plant_codes_list.append(plant_code)
        record.plant_types = ''
        if (len(plant_codes_list) != 0):
            record.plant_types = ','.join(plant_codes_list)
        # Store the community matrix
        # This matrix starts with 0 in the NW corner and I need 0 in the SW corner
        temp_starting_matrix = self.request.get('starting_matrix')
        upside_down_matrix = []
        for y in range(50):
            row = ''
            for x in range(50):
                row += temp_starting_matrix[y * 50 + x]
            upside_down_matrix.append(row)
        record.starting_matrix = ''
        for y in range(50):
            record.starting_matrix += upside_down_matrix[49 - y]
        record.put()

    success_output_all_parameters = """

                The simulation parameters are ready to load.

            To run the simulation use the following simulation code.

            Write it down - you will need it when you report your results.

%s

        """

class PlantPicturesPage(webapp2.RequestHandler):
    """
    Displays a page with photos of the different plant types in a new browser window.
    Accessed through links on the SetupMatrix page form.
    """
    def get(self):
        page = HtmlPage()
        info = SpeciesInfo()
        self.response.out.write(page.header % 'Species info')
        for i in range(14):
            self.response.out.write(self.plant_info_display %
                                    (info.common_names[i], info.latin_binomials[i],
                                     info.common_names[i], info.lifespans[i],
                                     info.water_levels[i], info.light_levels[i],
                                     info.temperature_levels[i], info.elevations[i],
                                     info.colonizing_levels[i], info.other_notes[i]))
        self.response.out.write(page.footer)

    plant_info_display = """

            <big>%s</big> (%s)

                    Maximum Lifespan: %s

                    Optimal Environment:

                        Water: %s

                        Light: %s

                        Temp: %s

                        Elevation: %s

                    Colonizing ability: %s

                    Other notes: %s

        """

class ShowParametersPage(webapp2.RequestHandler):
    """
    Accepts a simulation ID and returns a summary of the parameters for that ID.
    """
    def get(self):
        page = HtmlPage()
        info = SpeciesInfo()
        self.response.out.write(page.header % 'Simulation parameters')
        simulation_id = self.request.get('id')
        water_level = 2
        light_level = 2
        temperature_level = 2
        disturbance_level = 0
        #This should match the default plant types in the Unity Module
        plant_types = [1, 8, 9, 11, 13]
        plant_types_count = 5
        is_valid_id = True
        if (simulation_id != 'default'):
            data = db.GqlQuery("SELECT * FROM MeadowRecordObject WHERE id=:1",
                            simulation_id)
            if (data.count() == 1):
                # The simulation ID is valid, display the parameters
                data = data[0]
                is_valid_id = True
                water_level = data.water_level
                light_level = data.light_level
                temperature_level = data.temperature_level
                plant_types = data.plant_types.split(',')
                #remove the -1's (none's) from the list of plant types
                plant_types[:] = (value for value in plant_types if value != -1)
                plant_types_count = len(plant_types)
                disturbance_level = data.disturbance_level
                temp_starting_matrix = data.starting_matrix
                upside_down_matrix = []
                for y in range(50):
                    row = ''
                    for x in range(50):
                        row += temp_starting_matrix[y * 50 + x]
                    upside_down_matrix.append(row)
                starting_matrix = ''
                for y in range(50):
                    starting_matrix += upside_down_matrix[49 - y]
            else:
                # There is no such simulation ID (or there is more than one with that ID?)
                is_valid_id = False
                self.response.out.write(self.display_error % simulation_id)
        if (is_valid_id):
            self.response.out.write(self.display_environment_parameters %
                                    (simulation_id,
                                     self.levels[water_level],
                                     self.levels[light_level],
                                     self.levels[temperature_level],
                                     self.disturbance_levels[disturbance_level]))
            self.response.out.write(self.display_species_header)

            for i in range(0, plant_types_count):
                self.response.out.write(self.display_species %
                                        (i + 1,
                                         info.common_names[int(plant_types[i])],
                                         info.common_names[int(plant_types[i])]))
            self.response.out.write(self.display_species_footer)
            self.response.out.write(self.form_table_header)
            for j in range(50):
                for i in range(50):
                    index = (j * 50) + i
                    if (simulation_id != 'default'):
                        image = starting_matrix[index]
                    else:
                        image = 'R'
                    self.response.out.write(self.form_button % image)
                if (j != 49):
                    self.response.out.write('
')
            self.response.out.write(self.form_table_footer)
            self.response.out.write(self.form_legend)
        self.response.out.write(page.footer)

    #Conversions from the stored level integer values to human readable values
    levels = ["Lowest", "Lower", "Normal", "Higher", "Highest"]
    disturbance_levels = ["None", "Very Low","Low", "High", "Very High"]

    display_environment_parameters = """

Simulation Parameters

            Simulation ID: %s

            Water level: %s

            Light level: %s

            Temperature level: %s

            Ongoing disturbance level: %s

        """

    display_species_header = """

        """

    display_species = """

                    Species %s: %s

        """

    display_species_footer = """

            View plant details

        """

    display_error = """

Simulation ID %s does not exist!

        """

    form_table_header = 'Starting Community:

'

    form_button = ''

    form_table_footer = '

'

    form_legend = """
         = Random plant

         = Permanent disturbance

         = Gap (temporary)

         = Species 1

         = Species 2

         = Species 3

         = Species 4

         = Species 5

        """

class GetParameters(webapp2.RequestHandler):
    """
    Returns the community record with a particular timestamp as XML.
    Accessed by the vMeadow opensim module.
    """
    def get(self):
        data = db.GqlQuery("SELECT * FROM MeadowRecordObject WHERE id=:1",
                            self.request.get('id'))
        if (data.count() == 1):
            #if there are no records or more than one, something has gone wrong and
            #we are better off NOT sending anything.
            self.response.out.write(data[0].to_xml())

class RequestPlot(webapp2.RequestHandler):
    """
    Accepts data for plotting and returns an interactive dygraph-based plot.
    """
    def get(self):
        #This should only ever get called with post data but this helps with testing
        self.post()

    def post(self):
        plot_type = self.request.get('plot_type')
        simulation_id = self.request.get('simulation_id')
        if (plot_type == 'counts'):
            data_string = self.request.get('data_string')
            self.request_counts_plot(simulation_id, data_string)
        elif (plot_type == 'age'):
            data_string = self.request.get('data_string')
            self.request_ages_plot(simulation_id, data_string)
        elif (plot_type == 'biomass'):
            data_string = self.request.get('data_string')
            self.request_biomass_plot(simulation_id, data_string)
        else:
            self.response.out.write("Invalid plot type request")

    plot_creation_code = """

                <title>%s Plot</title>

Simulation ID: %s

        """

    # Debugging this can be nasty.  Keep this example of a good plot string for testing purposes.
    plot_data_string_TEST = """
        "year,Gaps,Alder,Fern,Cottonwood,Sagebrush,Pine\\n" +
        "0,135,75,10,15,45,120\\n" + "1,163,25,45,34,23,110\\n" +
        "2,174,26,67,45,65,23\\n" + "3,120,18,75,25,64,98\\n" +
        "4,58,76,100,25,48,93\\n" + "5,138,70,97,34,35,26\\n" +
        "6,154,86,25,35,45,55\\n" + "7,220,32,22,51,43,32\\n" +
        "8,167,35,43,45,54,56\\n" + "9,120,38,64,46,75,57\\n" +
        "10,118,40,73,37,84,48\\n" + "11,55,65,10,70,110,90\\n" +
        "12,180,80,34,36,38,32\\n"
        """

    def request_counts_plot(self, simulation_id, plot_data_string):
        self.response.out.write(self.plot_creation_code % (
                                'Count',
                                simulation_id,
                                plot_data_string,
                                'Counts by Species',
                                '# of individuals'))

    def request_ages_plot(self, simulation_id, plot_data_string):
        self.response.out.write(self.plot_creation_code % (
                                'Age',
                                simulation_id,
                                plot_data_string,
                                'Average Age by Species',
                                'average age'))

    def request_biomass_plot(self, simulation_id, plot_data_string):
         self.response.out.write(self.plot_creation_code % (
                                'Biomass',
                                simulation_id,
                                plot_data_string,
                                'Biomass by Species',
                                '% of total biomass'))

class SendCrossDomain(webapp2.RequestHandler):
    """
    Responds to crossdomain permission requests.
    """

    def get(self):
        crossdomain = '<?xml version="1.0"?>'
        crossdomain += '<cross-domain-policy>'
        crossdomain += '<allow-access-from domain="*"/>'
        crossdomain += '</cross-domain-policy>'
        self.response.headers['Content-Type'] = 'text/xml'
        self.response.out.write(crossdomain)

class ShowMapHelpPage(webapp2.RequestHandler):
    """
    Displays instructions for setting the starting plant distribution.
    """

    def get(self):
        page = HtmlPage()
        self.response.out.write(page.header % 'Map Instructions')
        self.response.out.write(self.instructions)
        self.response.out.write(page.footer)

    instructions = """

Using the map to set the starting plant distribution

        To put a specific plant (or gap) at specific locations:

Click on one or more locations on the map.

Select the plant type (or gap) from the drop-down menu.

Click the button to Apply cell value to cells.

Repeat 1-3 to put other plant types (or gaps) in other locations.

        To put a specific plant (or gap) in a rectangular area:

Click on exactly two locations on the map to define the corners of a rectangular area.

Select the plant type (or gap) from the drop-down menu.

Click the button to Apply cell value to area.

Repeat 1-3 to put other plant types (or gaps) in other areas.

        Note: By default, any locations you leave blank will be planted at random.
        """

# url to class mapping
application = webapp2.WSGIApplication([
    ('/', ParametersFormPageOne),
    ('/parametersform2', ParametersFormPageTwo),
    ('/newparameters', StoreNewParameters),
    ('/data', GetParameters),
    ('/requestplot', RequestPlot),
    ('/plants', PlantPicturesPage),
    ('/show', ShowParametersPage),
    ('/map-help', ShowMapHelpPage),
    ('/crossdomain.xml', SendCrossDomain)], debug=True)

def main():
    run_wsgi_app(application)

if __name__ == "__main__":
    main()


__author__ = 'Sotaya'
'''
Overpopulation: If a living cell is surrounded by more than three living cells it dies.
Stasis: If a living cell is surrounded by two or three living cells it survives.
Underpopulation: if a living cell is surrounded by fewer than two living cells, it dies.
Reproduction: if a dead cell is surrounded by exactly three cells, it becomes a live cell.
'''

import pygame
import sys
from pygame.locals import *
import random

class life:
    FPS = 5
    pygame.init()
    WIDTH = 640
    HEIGHT = 480
    CELLSIZE = 10

    assert WIDTH % CELLSIZE == 0
    assert HEIGHT % CELLSIZE == 0

    CELLWIDTH = int(WIDTH / CELLSIZE)  # number of horizontal cells
    CELLHEIGT = int(HEIGHT / CELLSIZE)  # number of vertical cells

    BLACK = (0, 0, 0)  # line colors
    WHITE = (255, 255, 255)  # background color
    DARKGRAY = (40, 40, 40)
    GREEN = (0, 255, 0)  # color for living cells

    def __init__(self):
        pass

    #The grid method draws the grid system, vertical and horizontal lines
    def grid(self):
        for x in range(0, self.WIDTH, self.CELLSIZE):
            pygame.draw.line(DISPLAY, self.DARKGRAY, (x, 0), (x, self.HEIGHT))

        for y in range(0, self.HEIGHT, self.CELLSIZE):
            pygame.draw.line(DISPLAY, self.DARKGRAY, (0, y), (self.WIDTH, y))

    #This initializes the grid system to 0 (empty at beginning)
    def empty_grid(self):
        grid_dict = {}
        for yy in range(self.CELLHEIGT):
            for xx in range(self.CELLWIDTH):
                grid_dict[xx, yy] = 0
        return grid_dict

    #this fills in the grid with live and dead cells, 0 or 1 randomly
    def begin_grid_random(self, life_dict):
        for item in life_dict:
            life_dict[item] = random.randint(0, 1)
        return life_dict

    #After filling the grid with 0's and 1's we color each cell according
    #to if its alive or dead.
    def color_grid(self, item, life_dict):
        x = item[0]
        y = item[1]
        y = y * self.CELLSIZE
        x = x * self.CELLSIZE

        if life_dict[item] == 0:
            pygame.draw.rect(DISPLAY, self.WHITE, (x, y, self.CELLSIZE, self.CELLSIZE))

        if life_dict[item] == 1:
            pygame.draw.rect(DISPLAY, self.GREEN, (x, y, self.CELLSIZE, self.CELLSIZE))
        return None

    #With this method we go through the each cell counting its neighbors
    def get_neighbors(self, item, life_dict):
        neighbors = 0
        for x in range(-1, 2):
            for y in range(-1, 2):
                check_cell = (item[0] + x, item[1] + y)
                if self.CELLWIDTH > check_cell[0] >= 0:
                    if self.CELLHEIGT > check_cell[1] >= 0:
                        if life_dict[check_cell] == 1:
                            if x == 0 and y == 0:
                                neighbors += 0
                            else:
                                neighbors += 1
        return neighbors

    #According to the number of neighbors found around a cell, we
    #either kill the cell or bring it to life
    def tick(self, life_dict):
        new_tick = {}
        for item in life_dict:
            number_neighbors = self.get_neighbors(item, life_dict)
            if life_dict[item] == 1:
                if number_neighbors < 2:
                    new_tick[item] = 0
                elif number_neighbors > 3:
                    new_tick[item] = 0
                else:
                    new_tick[item] = 1
            elif life_dict[item] == 0:
                if number_neighbors == 3:
                    new_tick[item] = 1
                else:
                    new_tick[item] = 0
        return new_tick

    #main method of the program, it  contains the games loop and
    #necessary initializations of data.
    def main(self):
        pygame.init()
        global DISPLAY
        FPSCLOCK = pygame.time.Clock()
        DISPLAY = pygame.display.set_mode((self.WIDTH, self.HEIGHT))
        pygame.display.set_caption("Yo Bitches - Game of Life")
        DISPLAY.fill(self.WHITE)  # background color
        life_dict = self.empty_grid()
        life_dict = self.begin_grid_random(life_dict)

        for item in life_dict:
            self.color_grid(item, life_dict)

        self.grid()
        pygame.display.update()

        #game loop
        while True:
            for event in pygame.event.get():
                if event.type == QUIT:
                    pygame.quit()
                    sys.exit()
            life_dict = self.tick(life_dict)

            for item in life_dict:
                self.color_grid(item, life_dict)

            self.grid()
            pygame.display.update()
            FPSCLOCK.tick(self.FPS)

if __name__ == '__main__':
    main = life()
    main.main()


#!/usr/bin/env python2
# -*- coding: utf-8 -*-
"""
Created on Fri Sep  1 06:52:02 2017

@author: mjb
"""
import torch
from torch.distributions import Categorical
import numpy as np
import Settings.game_settings as game_settings
import Game.card_to_string as card_to_string

from Game.card_tools import card_tools
card_tools = card_tools()

import Settings.arguments as arguments
import Settings.constants as constants
from Game.bet_sizing import BetSizing
from Game.Evaluation.evaluator import Evaluator
from Tree.tree_builder import PokerTreeBuilder
from Tree.strategy_filling import StrategyFilling

import random_card_generator
from nn.state import GameState

import random

filling = StrategyFilling()
builder = PokerTreeBuilder()
al_nn = []
rl_nn = []
evaluator = Evaluator()

class Env:

    def __init__(self):
        params = {}
        params['root_node'] = {}
        params['root_node']['board'] = card_to_string.string_to_board('')
        params['root_node']['street'] = 0
        params['root_node']['current_player'] = constants.players.P1
        params['root_node']['bets'] = arguments.Tensor([100, 100])
        params['limit_to_street'] = False
        builder = PokerTreeBuilder()
        self.root_node = builder.build_tree(params)
#        print(self.builder.node_id_acc)
        filling.fill_uniform(self.root_node)
        self.state = GameState()
        self._cached_terminal_equities = {}

    def reset(self):
        self.state = GameState()
        pri_card = random_card_generator.generate_cards(game_settings.private_count * 2)
        self.state.private.append(pri_card[0:game_settings.private_count])
        self.state.private.append(pri_card[game_settings.private_count:])
        self.state.node = self.root_node

#    def _chance_next(self, this_node, state):
#        rannum = random.random()
#        hand_id = int(state.private[this_node.current_player][0])
#        chance_strategy = this_node.strategy[:,hand_id]
#        for i in range(len(chance_strategy)):
#            if rannum <= sum(chance_strategy[0: i+1]):
#                return this_node.children[i]

    def _chance_next(self, this_node, state):
        possible_board = [1] * len(this_node.children)
        for p in state.private:
            possible_board[p[0]] = 0
        m = Categorical(torch.Tensor(possible_board))
        child_id = m.sample().item()
#        print(child_id)
        return this_node.children[child_id]

    # @return next_node, reward, terminal
    def step(self, agent, state, action):
        parent_node = state.node

        # TODO grasp if action if invaild
        if action[0][0] >= len(state.node.children):
            action[0][0] = len(state.node.children) - 1
        # fold in first round is invaild
#        if action[0][0] == 0 and parent_node.bets[0] == 100 and parent_node.bets[1] == 100:
#            action[0][0] = 1

#        assert (action < 4)
        next_node = state.node.children[action[0][0]]
        if next_node.current_player == constants.players.chance:
            next_node = self._chance_next(next_node, state)

    #    next_state reward
        next_state = GameState()
        next_state.node = next_node
        next_state.private = state.private
        reward = parent_node.bets[parent_node.current_player] - next_node.bets[parent_node.current_player]
        # if we choose to reach the terminal we should observe the ternimal value
        terminal = False

        #if chance node and the acting player is player 0 (who is act first at every round)
        if next_node.current_player == parent_node.current_player:
            return next_state, next_state, reward - arguments.blind, terminal
        if next_node.terminal:
            reward = reward + self._get_terminal_value(next_state, parent_node.current_player)
            terminal = True
            next_state = None
            real_next_state = next_state

        else:
            next_state_tensor = builder.statenode_to_tensor(next_state)
#            oppo_action = int((agent.sl.select_action(next_state) if random.random() > arguments.eta \
#                          else agent.rl.select_action(next_state_tensor))[0][0])
            oppo_action = int(agent.sl.select_action(next_state_tensor)[0][0])
            if oppo_action >= len(next_node.children):
                oppo_action = 1
            real_next_node = next_node.children[oppo_action]

            if real_next_node.current_player == constants.players.chance:
                real_next_node = self._chance_next(real_next_node, state)

            assert(real_next_node != None)
            real_next_state = GameState()
            real_next_state.node = real_next_node
            real_next_state.private = state.private
            if real_next_node.terminal:
                reward = reward + self._get_terminal_value(real_next_state, parent_node.current_player)
                real_next_state = None

#        print(parent_node.node_id)
#        print(next_node.node_id)
#        if real_next_node:
#            print(real_next_node.node_id)
#        else:
#            print('None')
#        print(reward)
#        self.process_log(state, real_next_node, action, reward)
        return next_state, real_next_state, reward - arguments.blind, terminal

        #[0,1,1,1] means the second action

#    def _get_ternimal_equity(self, node):
#        if len(self._cached_terminal_equities == 0):
#            cached = TerminalEquity()
#            cached.set_board(node.board)
#            self._cached_terminal_equities[node.board_string] = cached
#        cached = self._cached_terminal_equities[node.board_string]
#        return cached

#    # return vaule FloatTensor(2)
#    def _get_terminal_value(self, state):
#        node = state.node
#        assert(node.terminal)
#
#        if node.type == constants.node_types.terminal_fold:
#            #ternimal fold
#            value = - node.bets[node.current_player]
#        elif node.type == constants.node_types.terminal_call:
#            # show down
#            player_hand = arguments.Tensor(state.private[node.current_player].tolist() + node.board.tolist())
#            player_strength = evaluator.evaluate(player_hand, -1)
#            oppo_hand = arguments.Tensor(state.private[1 - node.current_player].tolist() + node.board.tolist())
#            oppo_strength = evaluator.evaluate(oppo_hand, -1)
#
#            if player_strength < oppo_strength:
#                value = node.bets[1 - node.current_player]
#            else:
#                value = -node.bets[node.current_player]
#        else:
#            assert(False)# not a vaild terminal node
#
#        return value

        # return vaule FloatTensor(2)
    def _get_terminal_value(self, state, player):
        # oppo take the action and lead to this terminal node

        node = state.node
        assert(node.terminal)
        value = arguments.Tensor([0,0])

        if node.type == constants.node_types.terminal_fold:
        #ternimal fold
            value[node.current_player] = node.bets.sum()

        elif node.type == constants.node_types.terminal_call:
            # show down
            player_hand = arguments.Tensor(state.private[node.current_player].tolist() + node.board.tolist())
            player_strength = evaluator.evaluate(player_hand, -1)
            oppo_hand = arguments.Tensor(state.private[1 - node.current_player].tolist() + node.board.tolist())
            oppo_strength = evaluator.evaluate(oppo_hand, -1)

            # the one take call lose
            if player_strength < oppo_strength:
                value[node.current_player] = node.bets.sum()
            elif player_strength > oppo_strength:
                value[1-node.current_player] = node.bets.sum()
            else:
                value = node.bets.clone()
        else:
            assert(False)# not a vaild terminal node

        return value[player]

    def _al_action(self, state):

        # get possible bets in the node
        possible_bets = get_possible_actions()
        actions_count = possible_bets.size(0)

        # get the strategy

        assert(math.abs(1 - hand_strategy.sum()) < 0.001)
        # sample the action
        action = strategy.cumsum(0).gt(random.random())

        return action

    def process_log(self, state, real_next_node, action, reward):
        node = state.node
        with open('state_action.log','a') as fout:
            fout.write('player: ' + str(node.current_player) + '\n' +
                       'private, public cards: ' + str(state.private[0][0]) +','+str(state.private[1][0])+ node.board_string + '\n' +
                       'bets: ' + str(node.bets[0]) + ',' + str(node.bets[1]) + '\n'
                       'actions: ' + str(action[0][0]) + '\n'
                       'rewards:' + str(reward) + '\n'+
                       'terminal: ' + str(real_next_node.terminal) + '\n\n')


from django.urls import path,re_path
from . import views

urlpatterns = [
    path('neoLabel/', views.labelAll),
    path('neoType/', views.typeAll),
    path('neoMatch/<str:label>/<str:entity>/', views.matchEntity),
    # path('neoRel/<str:label>/<str:entity>/<str:rel>/', views.matchRel),
    path('neoTypeEntity/', views.matchRel),
    # path('neomerge/', views.neoMerge.as_view()),
    path('neoEntity/', views.entityAll),
    # path('neoEntityType/<str:label1>/<str:entity>/<str:label2>/', views.entityTypeAll),
    path('neoEntityType/', views.entityTypeAll),
    path('neoLevel/', views.levelAll),
    path('neoEntity1Entity2/', views.entity1Entity2),
    # re_path(r'^neoRel/<str:label>/<str:entity>/(?P<rel>\S*)/$', views.matchRel),
]


from django.urls import path
from django.contrib.auth import views as auth_views
from . import views_employee as employee
from shift.views import ShiftList, ShiftAdd, ShiftUpdate, ShiftDelete
from . import views_chart as chart
from .views import Index, NomineeSalaryAverage, NomineeSalaryDetails, Chart

urlpatterns = [

    path('', Index.as_view(), name='index'),
        # empleado
    path('listar_empleados/', employee.EmployeeList.as_view(), name='listar_empleados'),
    path('empleado_agregar/', employee.EmployeeAdd.as_view(), name='empleado_agregar'),
    path('empleado_actualizar/<int:employee_id>/', employee.EmployeeUpdate.as_view(), name='empleado_actualizar'),
    path('empleado_eliminar/<int:employee_id>', employee.EmployeeDelete.as_view(), name='empleado_eliminar'),

       # horarios
    path('listar_horarios/', ShiftList.as_view(), name='listar_horarios'),
    path('horario_agregar/', ShiftAdd.as_view(), name='horario_agregar'),
    path('horario_actualizar/<int:id>', ShiftUpdate.as_view(), name='horario_actualizar'),
    path('horario_eliminar/<int:id>', ShiftDelete.as_view(), name='horario_eliminar'),

      # reporte
    path('chart/', chart.Chart.as_view(), name='chart'),
    path('nominee_salary_details/', NomineeSalaryDetails.as_view(), name='nominee_salary_details'),

      # login
    path('login/', auth_views.LoginView.as_view(), name='login'),
    path('logout/', auth_views.LogoutView.as_view(), name='logout'),
 ]


from django.urls import path
from module.views import views_show_module, view_alter_module

urlpatterns = [
    # Base vu par tous le monde
    path('', views_show_module.listModules, name='modules'),

    #Vue partie render
    path('<int:pk>', views_show_module.module, name='module'),
    path('AnalyseQuestionnaire/<int:pk>', views_show_module.questionnaireAnalyse, name='questionnaire'),

    ##### Vue des partie par Ajax
    path('sections/<int:module>/<int:ordre>', views_show_module.Sectiondetail.as_view()),
    #####Reception des reponses par Ajax
    path('questionReceve/<int:pk>/<int:ordre>', views_show_module.ReceveQuestion.as_view()),

    #######################################################################################################################
    ##########################################Creation####################################################################
    #######################################################################################################################
    path('AlterModules', view_alter_module.alterModules, name='AlterModules'),
    path('AlterPart', view_alter_module.alterActivy, name='AlterPart'),
    path('AlterPart/<str:creation>/<str:typeActivity>/<int:pkActivity>/<int:ordreIn>', view_alter_module.alterActivy, name='AlterPart'),
    #### Envoie et modification form
    path('showCreateForm/<str:formList>', view_alter_module.EnvoieDifferentForm, name='callDiffFormForQuestion'),
    path('showCreateForm/<str:formList>/<int:pkModif>', view_alter_module.EnvoieDifferentForm, name='callDiffFormForQuestion'),

              ]


from tkinter import *
from PIL import Image, ImageTk
from tkinter import ttk,messagebox #messagebox for pop up
import sqlite3
import os                          #for working with files and directories

class salesClass:
    def __init__(self, root):  # default constructor
        self.root = root
        self.root.geometry("1000x450+220+120")
        self.root.title("Sales")
        self.root.config(bg="white")
        self.root.focus_force() # to highlight the new window

##=============title===========
        lbl_title = Label(self.root, text="View Customer Bills", font=("goudy old style", 30), bg="#0f4d7d", fg="white", bd=3,relief=RIDGE)
        lbl_title.pack(side=TOP,fill=X,padx=20,pady=5)

##=============contents=====================
    #======content variables================
        self.var_invoice=StringVar()
        self.bill_list=[]

    #======content labels and entries=======
        #===row1===
        lbl_invoice=Label(self.root, text="Invoice No.", font=("goudy old style", 15), bg="white")
        lbl_invoice.place(x=20,y=70)
        txt_invoice = Entry(self.root, textvariable=self.var_invoice,font=("goudy old style", 15), bg="lightyellow")
        txt_invoice.place(x=150, y=70,width=180,height=30)
        btn_search = Button(self.root, text="Search", command=self.search,font=("goudy old style", 15,"bold"), bg="#2196f3",fg="white",cursor="hand2")
        btn_search.place(x=350, y=70, width=150, height=30)
        btn_clear = Button(self.root, text="Clear", command=self.clear,font=("goudy old style", 15,"bold"), bg="lightgrey",cursor="hand2")
        btn_clear.place(x=520, y=70, width=150, height=30)
        #===sales bill frame===
        sales_Frame=Frame(self.root,bd=3,relief=RIDGE)
        sales_Frame.place(x=20,y=120,width=220,relheight=0.7)
            #===Listbox to show files===
                #===scrollbar in listbox===
        scrolly1 = Scrollbar(sales_Frame, orient=VERTICAL)
        scrolly1.pack(side=RIGHT, fill=Y)
        scrollx1 = Scrollbar(sales_Frame, orient=HORIZONTAL)
        scrollx1.pack(side=BOTTOM, fill=X)
                #===Listbox===
        self.sales_list=Listbox(sales_Frame,font=("goudy old style",15),bg="white",yscrollcommand=scrolly1.set, xscrollcommand=scrollx1.set)
        self.sales_list.pack(fill=BOTH,expand=1)       #fill=BOTH to fill the whole frame, expand=1 means expand property is True
        scrolly1.config(command=self.sales_list.yview)
        scrollx1.config(command=self.sales_list.xview)  # to make the scroll bar work

        #to show the content of bill file in the "Bill Area Frame"
        self.sales_list.bind("<ButtonRelease-1>",self.get_bill_data)        #This is an event

        #to show the list of bills in the "Sales bill frame"
        self.show_bill()

        #===bill area frame===
        bill_Frame = Frame(self.root, bd=3, relief=RIDGE)
        bill_Frame.place(x=280, y=120, width=410, relheight=0.7)
            #===Text area to show files===
                # ===Bill Area title===
        lbl_billarea_title = Label(bill_Frame, text="Customer Bill Area", font=("goudy old style", 15), bg="orange")
        lbl_billarea_title.pack(side=TOP, fill=X)
                #===scrollbar in bill area===
        scrolly2 = Scrollbar(bill_Frame, orient=VERTICAL)
        scrolly2.pack(side=RIGHT, fill=Y)
        scrollx2 = Scrollbar(bill_Frame, orient=HORIZONTAL)
        scrollx2.pack(side=BOTTOM, fill=X)
                #===Bill Area text===
        self.bill_area = Text(bill_Frame, font=("goudy old style", 15), bg="lightyellow", yscrollcommand=scrolly2.set,xscrollcommand=scrollx2.set)
        self.bill_area.pack(fill=BOTH,expand=1)         #fill=BOTH to fill the whole frame, expand=1 means expand property is True
        scrolly2.config(command=self.bill_area.yview)
        scrollx2.config(command=self.bill_area.xview)  #to make the scroll bar work

        #=====Image====
            #===accessing the image and resizing===
        self.bill_Photo = Image.open("images/im2.png")
        self.bill_Photo = self.bill_Photo.resize((250, 300), Image.ANTIALIAS)  #ANTIALIAS doesn't change picture quality
        self.bill_Photo = ImageTk.PhotoImage(self.bill_Photo)                  #Image is dynamically placed
            #===placing the image===
        lbl_bill_Photo = Label(self.root, image=self.bill_Photo,bd=0)
        lbl_bill_Photo.place(x=730,y=130)

##============ Functionality =============
    #to show the list of bills in the bills directory
    def show_bill(self):
        del self.bill_list[:]                             #to clear the list
        self.sales_list.delete(0,END)
        #print(os.listdir("../dwivediPnTbilling/bills"))  #the path can also be given as "../dwivediPnTbilling/"
        for i in os.listdir("bills"):
            if i.split(".")[-1]=="txt": #to get and compare the file extension
                self.sales_list.insert(END,i)
                self.bill_list.append(i.split('.')[0])

    #to show the contents of the bill file in the bill area
    def get_bill_data(self,ev):                      #For event we have to pass a parameter
        file_index=self.sales_list.curselection() #curselection helps to show the index of the selected item from the list
        bill_file_name=self.sales_list.get(file_index)
        #print(bill_file_name)
        self.bill_area.delete('1.0',END)        #string indexing starts from 1.0 because it is text entry field
        fp=open(f"bills/{bill_file_name}",'r')
        for i in fp:
            self.bill_area.insert(END,i)
        fp.close()

    #to show the search results
    def search(self):
        if self.var_invoice.get()=="":
            messagebox.showerror("Error","Invoice no. should be required",parent=self.root)
        else:
            #print(self.bill_list,self.var_invoice.get()) #debugging
            if self.var_invoice.get() in self.bill_list:
                #print("Yes find the invoice")            #debugging
                fp = open(f"bills/{self.var_invoice.get()}.txt", 'r')
                self.bill_area.delete('1.0',END)
                for i in fp:
                    self.bill_area.insert(END, i)
                fp.close()
            else:
                messagebox.showerror("Error", "Invalid Invoice no.", parent=self.root)

    #to clear the list
    def clear(self):
        self.show_bill()                     #so that the list of bills doesn't vanish
        self.bill_area.delete("1.0",END)
        self.var_invoice.set("")

if __name__=="__main__": #while dealing with multiple files "main" means this is the main file
    root=Tk()
    obj= salesClass(root) #creating object of Billing Class
    root.mainloop()


import numpy as np
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegression
import matplotlib.pyplot as plt
import seaborn as sns

train = pd.read_csv('train.csv')
train.head()

missing_values = train.isnull()
missing_values
sns.heatmap(data=missing_values, yticklabels=False, cbar=False, cmap='viridis')
sns.countplot(x='Survived', data=train)
sns.countplot(x='Survived', data=train, hue='Pclass')

def impute_age(cols):
    Age = cols[0]
    Pclass = cols[1]

    if pd.isnull(Age):

        if Pclass == 1:
            return 37
        elif Pclass == 2:
            return 29
        else:
            return 24
    else:
        return Age

    train['Age'] = train[['Age', 'Pclass']].apply(impute_age, axis=1)

sex = pd.get_dummies(train['Sex'], drop_first=True)
embark = pd.get_dummies(train['Embarked'], drop_first=True)

train = pd.concat([train, sex, embark], axis=1)
train.drop(['Sex', 'Embarked', 'Ticket', 'Name', 'PassengerId'], axis=1, inplace=True)
X = train[['Pclass', 'Age', 'SibSp', 'Parch', 'Fare', 'male', 'Q', 'S']]
y = train['Survived']
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=0)

logmodel = LogisticRegression()
logmodel.fit(X_train, y_train)