# -*- coding: utf-8 -*-

import datetime
from collections import deque

import numpy as np
import pandas as pd

from portfolios import Asset
from portfolios.security.security import Security
from portfolios.stats.basics import returns_column
from portfolios.utils.helpers import standard_date_format, todays_date

class Portfolio(Asset):
    """
       Class that holds several securities
    """

    def __init__(self, name):
        super().__init__(name)
        self.securities = {}
        self.securities_archive = {}
        self.tickers = []
        self.tickers_archive = []
        self.dividends = pd.DataFrame(columns=["Date", "Ticker", "Amount"])
        self.payments = pd.DataFrame(columns=["Date", "In", "Out"])
        self.wallet = pd.DataFrame(columns=["Date", "Change"])
        self.total_portfolio_value = 0.0
        self.total_security_value = 0.0
        self.cash = 0.0
        self.return_value = 0.0
        self.return_rate = 0.0
        self.prices = {}
        self.prices_fifo = {}
        self.prices_lifo = {}
        self.index = 0
        self.benchmark_ticker = "sp500"
        self.benchmark = None
        self.transactions = pd.DataFrame(
            columns=["Date", "Transaction", "Ticker", "Currency", "Price", "Quantity"]
        )
        self.date = standard_date_format(todays_date())

    def __iter__(self):
        return self

    def __next__(self):
        if len(self.tickers) == 0:
            self.index = 0
            raise StopIteration
        elif self.index >= len(self.tickers):
            self.index = 0
            raise StopIteration
        else:
            self.index = self.index + 1
            return self.tickers[self.index - 1]

    def get_cash(self, date="2100-01-01"):
        date = standard_date_format(date)

        return self.wallet.loc[(self.wallet.Date <= date), "Change"].sum()

    def deposit_cash(self, date, currency="USD", price=1.0, quantity=0):
        """
           Adds an amount of quantity*price to the wallet
           Price acts as exchange rate if currency is not USD
        """
        self.wallet = self.wallet.append(
            {"Date": date, "Change": 1.0 * price * quantity}, ignore_index=True
        ).sort_values("Date")

        # store transaction in df
        self.transactions = self.transactions.append(
            {
                "Date": date,
                "Transaction": "deposit",
                "Ticker": np.nan,
                "Currency": currency,
                "Price": 1.0 * price,
                "Quantity": 1.0 * quantity,
                "TradeValue": 1.0 * price * quantity,
            },
            ignore_index=True,
            sort=False,
        ).sort_values("Date")

        # store payment in df
        self.payments = self.payments.append(
            {"Date": date, "In": 1.0 * price * quantity, "Out": 0.0}, ignore_index=True
        ).sort_values("Date")

        self.cash = self.get_cash(date=todays_date())

        print(
            "depositing {0:.2f} {2} (new balance: {1:.2f} {2})".format(
                quantity * price, self.cash, currency
            )
        )

    def withdraw_cash(self, date, currency="USD", price=1.0, quantity=0):
        """
           Takes amount of quantity*price out of wallet
        """
        self.wallet = self.wallet.append(
            {"Date": date, "Change": -1.0 * price * quantity}, ignore_index=True
        ).sort_values("Date")

        # store transaction in df
        self.transactions = self.transactions.append(
            {
                "Date": date,
                "Transaction": "withdraw",
                "Ticker": np.nan,
                "Currency": currency,
                "Price": 1.0 * price,
                "Quantity": 1.0 * quantity,
                "TradeValue": 1.0 * price * quantity,
            },
            ignore_index=True,
            sort=False,
        ).sort_values("Date")

        # store payments in df
        self.payments = self.payments.append(
            {"Date": date, "In": 0.0, "Out": 1.0 * price * quantity}, ignore_index=True
        ).sort_values("Date")

        self.cash = self.get_cash(date=todays_date())

        if self.cash < 0.0:
            print(
                "Warning, cash balance negative: {0:.2f} {1}".format(
                    self.cash, currency
                )
            )
        else:
            print(
                "withdrawing {0:.2f} {2} (new balance: {1:.2f} {2})".format(
                    quantity * price, self.cash, currency
                )
            )  #

    def dividend(self, date, ticker="", currency="USD", price=1.0, quantity=0):
        """

        """
        self.wallet = self.wallet.append(
            {"Date": date, "Change": 1.0 * price * quantity}, ignore_index=True
        ).sort_values("Date")

        # store transaction in df
        self.transactions = self.transactions.append(
            {
                "Date": date,
                "Transaction": "dividend",
                "Ticker": ticker,
                "Currency": currency,
                "Price": 1.0 * price,
                "Quantity": 1.0 * quantity,
                "TradeValue": 1.0 * price * quantity,
            },
            ignore_index=True,
            sort=False,
        ).sort_values("Date")

        # store dividends in df
        self.dividends = self.dividends.append(
            {"Date": date, "Ticker": ticker, "Amount": 1.0 * price * quantity},
            ignore_index=True,
        ).sort_values("Date")

        self.cash = self.get_cash(date=todays_date())

        if ticker == "" or ticker != ticker:
            print(
                "interest {0:.2f} {2} (new balance: {1:.2f} {2})".format(
                    quantity * price, self.cash, currency
                )
            )
        else:
            print(
                "dividend {0} {1:.2f} {3} (new balance: {2:.2f} {3})".format(
                    ticker, quantity * price, self.cash, currency
                )
            )

    def add_security(self, ticker, min_date="2000-01-01"):
        if ticker in self.securities_archive:
            self.securities[ticker] = self.securities_archive[ticker]
            self.tickers.append(ticker)
        else:
            _security = Security(ticker, start=min_date, end=self.date)
            self.securities[_security.ticker] = _security
            self.tickers.append(ticker)
            self.prices[ticker] = deque()
            self.prices_lifo[ticker] = deque()
            self.prices_fifo[ticker] = deque()

    def add_security_archive(self, ticker, min_date="2000-01-01"):
        if ticker in self.securities:
            self.securities_archive[ticker] = self.securities[ticker]
            self.tickers_archive.append(ticker)
        else:
            _security = Security(ticker, start=min_date, end=self.date)
            self.securities_archive[_security.ticker] = _security
            self.tickers_archive.append(ticker)
            self.prices[ticker] = deque()
            self.prices_lifo[ticker] = deque()
            self.prices_fifo[ticker] = deque()

    def remove_security(self, ticker):
        # copy state of security to archive
        self.securities_archive[ticker] = self.securities[ticker]
        # delete from current list of securities
        del self.securities[ticker]
        self.tickers.remove(ticker)

    def buy_security(self, date, ticker, currency="USD", price=None, quantity=0):

        # potentially add ticker to list
        if ticker not in self.tickers:
            self.add_security(ticker)
            # print('adding', ticker)

        # and to archive
        if ticker not in self.tickers_archive:
            self.add_security_archive(ticker)

        # modify quantity for subsequent stock splits
        quantity, modifier = self.securities[ticker].modify_quantity(date, quantity)

        # get closing price of security for transaction date if price not provided
        # modify price according to stock splits
        try:
            if np.isnan(price):
                price = self.securities[ticker].get_price_at(date)
            else:
                price = price / modifier
        except:
            if price is None:
                price = self.securities[ticker].get_price_at(date)
            else:
                price = price / modifier

        # add number of securities to price deques
        # multiply by 1000 to capture fractional shares
        for i in range(np.int(np.ceil(quantity*1000))):
            self.prices[ticker].append(price)
            self.prices_lifo[ticker].append(price)
            self.prices_fifo[ticker].append(price)

        # store point in time value in wallet
        self.wallet = self.wallet.append(
            {"Date": date, "Change": -1.0 * price * quantity}, ignore_index=True
        ).sort_values("Date")

        # store transaction in df
        self.transactions = self.transactions.append(
            {
                "Date": date,
                "Transaction": "buy",
                "Ticker": ticker,
                "Currency": currency,
                "Price": 1.0 * price,
                "Quantity": 1.0 * quantity,
                "TradeValue": 1.0 * price * quantity,
            },
            ignore_index=True,
            sort=False,
        ).sort_values("Date")

        self.cash = self.get_cash(date=todays_date())

        print(
            "buying {0:.2f} {1} (new balance: {2:.2f} {3})".format(
                quantity, ticker, self.cash, currency
            )
        )

    def sell_security(self, date, ticker, currency="USD", price=None, quantity=0):

        # modify quantity for subsequent stock splits
        quantity, modifier = self.securities[ticker].modify_quantity(date, quantity)

        # get closing price of security for transaction date if price not provided
        # modify price according to stock splits
        try:
            if np.isnan(price):
                price = self.securities[ticker].get_price_at(date)
            else:
                price = price / modifier
        except:
            if price is None:
                price = self.securities[ticker].get_price_at(date)
            else:
                price = price / modifier

        # remove number of securities from price deques
        # multiply by 1000 to capture fractional shares
        for i in range(np.int(np.floor(quantity*1000))):
            _ = self.prices_fifo[ticker].popleft()
            _ = self.prices_lifo[ticker].pop()

        # make sure security is fully removed, correct for rounding errors
        _df = self.transactions.loc[
            self.transactions.Transaction.isin(("buy", "sell")), :
        ].copy()
        _df.loc[_df.Transaction == "sell", "Quantity"] = _df.loc[
            _df.Transaction == "sell", "Quantity"
        ].apply(lambda x: -x)

        # set quantity to exactly match remaining quantity in portfolio
        if _df.groupby(by=["Ticker"])["Quantity"].sum()[ticker] <= quantity * 1.0001:
            quantity = _df.groupby(by=["Ticker"])["Quantity"].sum()[ticker]

        # store point in time value in wallet
        self.wallet = self.wallet.append(
            {"Date": date, "Change": 1.0 * price * quantity}, ignore_index=True
        ).sort_values("Date")

        # store transaction in df
        self.transactions = self.transactions.append(
            {
                "Date": date,
                "Transaction": "sell",
                "Ticker": ticker,
                "Currency": currency,
                "Price": 1.0 * price,
                "Quantity": 1.0 * quantity,
                "TradeValue": 1.0 * price * quantity,
            },
            ignore_index=True,
            sort=False,
        ).sort_values("Date")

        self.cash = self.get_cash(date=todays_date())

        print(
            "selling {0:.2f} {1} (new balance: {2:.2f} {3})".format(
                quantity, ticker, self.cash, currency
            )
        )

        # potentially remove ticker from list
        _df = self.transactions.loc[
            self.transactions.Transaction.isin(("buy", "sell")), :
        ].copy()
        _df.loc[_df.Transaction == "sell", "Quantity"] = _df.loc[
            _df.Transaction == "sell", "Quantity"
        ].apply(lambda x: -x)
        if _df.groupby(by=["Ticker"])["Quantity"].sum()[ticker] <= 0.0001:
            self.remove_security(ticker)
            # print('removing', ticker)

    def overview(self):

        # you have to have one security in the portfolio for meaningful output
        if len(self.securities) > 0:

            # sum up by ticker
            _df = self.transactions.loc[
                self.transactions.Transaction.isin(("buy", "sell", "dividend")), :
            ].copy()
            _df.loc[_df.Transaction == "sell", "Quantity"] = _df.loc[
                _df.Transaction == "sell", "Quantity"
            ].apply(lambda x: -x)
            _df.loc[_df.Transaction == "sell", "TradeValue"] = _df.loc[
                _df.Transaction == "sell", "TradeValue"
            ].apply(lambda x: -x)
            _df.loc[_df.Transaction == "dividend", "TradeValue"] = _df.loc[
                _df.Transaction == "dividend", "TradeValue"
            ].apply(lambda x: -x)
            _df.loc[_df.Transaction == "dividend", "Quantity"] = 0
            self.overview_df = _df.groupby(by=["Ticker"])[
                "Quantity", "TradeValue"
            ].sum()

            # check if sum over volume of a security is < 0
            for index, row in self.overview_df.iterrows():
                if row["Quantity"] < -0.0001:
                    print(
                        "Negative volume encountered: {0:5}\t{1}".format(
                            index, row["Quantity"]
                        )
                    )

            # restrict to securities with volume > 0
            self.overview_df = self.overview_df.loc[self.overview_df.Quantity > 0.0001]

            for ticker in self.tickers:
                self.overview_df.loc[
                    self.overview_df.index == ticker, "LastPrice"
                ] = self.securities[ticker].get_last_price()
                # calculate average security prices from deque of prices
                self.overview_df.loc[
                    self.overview_df.index == ticker, "AvgPriceAll"
                ] = np.mean(self.prices[ticker])
                # calculate average security prices from deque of prices
                self.overview_df.loc[
                    self.overview_df.index == ticker, "AvgPriceFiFo"
                ] = np.mean(self.prices_fifo[ticker])
                # calculate average security prices from deque of prices (last in first out)
                self.overview_df.loc[
                    self.overview_df.index == ticker, "AvgPriceLiFo"
                ] = np.mean(self.prices_lifo[ticker])

            self.overview_df["CurrentValue"] = (
                self.overview_df["LastPrice"] * self.overview_df["Quantity"]
            )

            # sum up dividends by ticker
            self.overview_df["Dividends"] = self.dividends.groupby(by="Ticker")[
                "Amount"
            ].sum()

            self.overview_df["AvgPrice"] = self.overview_df["TradeValue"] / (
                1.0 * self.overview_df["Quantity"]
            )
            self.overview_df.fillna(0.0, inplace=True)
            self.overview_df["Return"] = (
                self.overview_df["CurrentValue"]
                - self.overview_df["TradeValue"]
                + self.overview_df["Dividends"]
            )

            # average price to value ratio
            self.overview_df["AvgPriceToValue"] = (
                self.overview_df["TradeValue"] / (self.overview_df["CurrentValue"] + self.overview_df["Dividends"])
            )

            # join in names of securities
            for ticker in self.tickers:
                self.overview_df.loc[
                    self.overview_df.index == ticker, "Description"
                ] = self.securities[ticker].name

        # make dummy df when no (more) securities in portfolio
        else:
            _d = {
                "Quantity": [0],
                "AvgPriceAll": [0],
                "AvgPriceFiFo": [0],
                "AvgPriceLiFo": [0],
                "AvgPrice": [0],
                "TradeValue": [0],
                "LastPrice": [0],
                "CurrentValue": [0],
                "Dividends": [0],
                "Return": [0],
                "AvgPriceToValue": [0],
                "Description": [np.nan],
            }
            self.overview_df = pd.DataFrame(data=_d, index=[""])

        # update portfolio stats
        self.total_security_value = self.overview_df[["CurrentValue"]].sum().values[0]
        self.total_portfolio_value = self.total_security_value + self.cash
        self.return_value = (
            self.total_security_value
            + self.cash
            - self.payments["In"].sum()
            + self.payments["Out"].sum()
        )
        if self.total_portfolio_value:
            try:
                self.return_rate = self.return_value / (self.payments["In"].sum() - self.payments["Out"].sum())
            except:
                self.return_rate = 0.0
        else:
            self.return_rate = 0.0

        print(
            self.overview_df[
                [
                    "Quantity",
                    "AvgPriceAll",
                    "AvgPriceFiFo",
                    "AvgPriceLiFo",
                    "AvgPrice",
                    "LastPrice",
                    "TradeValue",
                    "CurrentValue",
                    "Dividends",
                    "Return",
                    "AvgPriceToValue",
                    "Description",
                ]
            ]
        )
        print()
        print(
            "Total portfolio value:\t{0:8.2f} USD\nTotal security value:\t{1:8.2f} USD\nCash in wallet:\t\t{2:8.2f} USD\nTotal return:\t\t{3:8.2f} USD\t({4:.2f}%)".format(
                self.total_portfolio_value,
                self.total_security_value,
                self.cash,
                self.return_value,
                self.return_rate * 100.0,
            )
        )

    def overview_archive(self):

        # you have to have one security historically in the portfolio for meaningful output
        if len(self.securities_archive) > 0:

            # sum up by ticker
            _df = self.transactions.loc[
                self.transactions.Transaction.isin(("buy", "sell")), :
            ].copy()
            _df.loc[_df.Transaction == "sell", "Quantity"] = _df.loc[
                _df.Transaction == "sell", "Quantity"
            ].apply(lambda x: -x)
            _df.loc[_df.Transaction == "sell", "TradeValue"] = _df.loc[
                _df.Transaction == "sell", "TradeValue"
            ].apply(lambda x: -x)
            self.overview_archive_df = _df.groupby(by=["Ticker"])[
                "Quantity", "TradeValue"
            ].sum()

            # check if sum over volume of a security is < 0
            for index, row in self.overview_archive_df.iterrows():
                if row["Quantity"] < 0.0:
                    print(
                        "Negative volume encountered: {0:5}\t{1}".format(
                            index, row["Quantity"]
                        )
                    )

            # restrict to securities with volume == 0 (if in portfolio)
            if len(
                self.overview_archive_df.loc[self.overview_archive_df.Quantity == 0]
            ):
                self.overview_archive_df = self.overview_archive_df.loc[
                    self.overview_archive_df.Quantity == 0
                ]

                for ticker in self.tickers_archive:
                    self.overview_archive_df.loc[
                        self.overview_archive_df.index == ticker, "LastPrice"
                    ] = self.securities_archive[ticker].get_last_price()
                    # calculate average security prices from deque of prices
                    self.overview_archive_df.loc[
                        self.overview_archive_df.index == ticker, "AvgPriceAll"
                    ] = np.mean(self.prices[ticker])

                # sum up dividends by ticker
                self.overview_archive_df["Dividends"] = self.dividends.groupby(
                    by="Ticker"
                )["Amount"].sum()
                self.overview_archive_df.fillna(0.0, inplace=True)
                self.overview_archive_df["Return"] = (
                    -self.overview_archive_df["TradeValue"]
                    + self.overview_archive_df["Dividends"]
                )

                # join in names of securities
                for ticker in self.tickers_archive:
                    self.overview_archive_df.loc[
                        self.overview_archive_df.index == ticker, "Description"
                    ] = self.securities_archive[ticker].name

            # make dummy df when no (more) securities are in portfolio archive that are not in portfolio
            else:
                _d = {
                    "Quantity": [0],
                    "AvgPriceAll": [0],
                    "TradeValue": [0],
                    "LastPrice": [0],
                    "Dividends": [0],
                    "Return": [0],
                    "Description": [np.nan],
                }
                self.overview_archive_df = pd.DataFrame(data=_d, index=[""])

        # make dummy df when no (more) securities are in portfolio archive that are not in portfolio
        else:
            _d = {
                "Quantity": [0],
                "AvgPriceAll": [0],
                "TradeValue": [0],
                "LastPrice": [0],
                "Dividends": [0],
                "Return": [0],
                "Description": [np.nan],
            }
            self.overview_archive_df = pd.DataFrame(data=_d, index=[""])

        print(
            self.overview_archive_df[
                [
                    "Quantity",
                    "AvgPriceAll",
                    "LastPrice",
                    "TradeValue",
                    "Dividends",
                    "Return",
                    "Description",
                ]
            ]
        )

    def positions(self):
        # you have to have one security in the portfolio for meaningful output
        if len(self.securities) > 0:
            # sum up by ticker

            _df = self.transactions.loc[
                self.transactions.Transaction.isin(("buy", "sell")), :
            ].copy()
            _df.loc[_df.Transaction == "sell", "Quantity"] = _df.loc[
                _df.Transaction == "sell", "Quantity"
            ].apply(lambda x: -x)
            _df.loc[_df.Transaction == "sell", "TradeValue"] = _df.loc[
                _df.Transaction == "sell", "TradeValue"
            ].apply(lambda x: -x)
            self.positions_df = _df.groupby(by=["Ticker"])[
                "Quantity", "TradeValue"
            ].sum()

            # sum up purchases
            self.positions_df["Bought"] = (
                _df.loc[_df.Transaction == "buy", :]
                .groupby(by=["Ticker"])["Quantity"]
                .sum()
            )
            self.positions_df["Invested"] = (
                _df.loc[_df.Transaction == "buy", :]
                .groupby(by=["Ticker"])["TradeValue"]
                .sum()
            )

            # sum up sales
            self.positions_df["Sold"] = (
                _df.loc[_df.Transaction == "sell", :]
                .groupby(by=["Ticker"])["Quantity"]
                .sum()
            )
            self.positions_df["Devested"] = (
                _df.loc[_df.Transaction == "sell", :]
                .groupby(by=["Ticker"])["TradeValue"]
                .sum()
            )

            # check if sum over volume of a security is < 0
            for index, row in self.positions_df.iterrows():
                if row["Quantity"] < 0.0:
                    print(
                        "Negative volume encountered: {0:5}\t{1}".format(
                            index, row["Quantity"]
                        )
                    )

            # join in last price and names of securities
            for ticker in set(self.positions_df.index):
                if ticker in self.tickers:
                    self.positions_df.loc[
                        self.positions_df.index == ticker, "LastPrice"
                    ] = self.securities[ticker].get_last_price()
                    self.positions_df.loc[
                        self.positions_df.index == ticker, "Description"
                    ] = self.securities[ticker].name
                elif ticker in self.tickers_archive:
                    self.positions_df.loc[
                        self.positions_df.index == ticker, "LastPrice"
                    ] = self.securities_archive[ticker].get_last_price()
                    self.positions_df.loc[
                        self.positions_df.index == ticker, "Description"
                    ] = self.securities_archive[ticker].name
                else:
                    _security = Security(ticker)
                    self.positions_df.loc[
                        self.positions_df.index == ticker, "LastPrice"
                    ] = _security.get_last_price()
                    self.positions_df.loc[
                        self.positions_df.index == ticker, "Description"
                    ] = _security.name

            self.positions_df["CurrentValue"] = (
                self.positions_df["LastPrice"] * self.positions_df["Quantity"]
            )

            # sum up dividends by ticker
            self.positions_df["Dividends"] = self.dividends.groupby(by="Ticker")[
                "Amount"
            ].sum()

            # self.positions_df['AvgPrice'] = self.positions_df['TradeValue'] / (1.0*self.positions_df['Quantity'])
            self.positions_df.fillna(0.0, inplace=True)
            self.positions_df["Return"] = (
                self.positions_df["CurrentValue"]
                - self.positions_df["TradeValue"]
                + self.positions_df["Dividends"]
            )
            self.positions_df["PercentGrowth"] = (
                100.0 * self.positions_df["Return"] / self.positions_df["Invested"]
            )
            self.positions_df.PercentGrowth.replace(
                [np.inf, -np.inf], np.nan, inplace=True
            )

            print(
                self.positions_df[
                    [
                        "Quantity",
                        "Bought",
                        "Sold",
                        "CurrentValue",
                        "Invested",
                        "Devested",
                        "Dividends",
                        "Return",
                        "PercentGrowth",
                        "Description",
                    ]
                ].sort_values(by=["CurrentValue", "Invested"], ascending=False)
            )

        else:
            print("No positions in portfolio.")

    def get_timeseries(self):

        # get date range from the transaction list
        self.min_date = min(self.transactions.Date.min(), self.payments.Date.min())
        self.max_date = datetime.datetime.now()

        # make a list of days between min and max date as index for timeseries df
        date_index = pd.date_range(self.min_date, self.max_date, freq="D")
        _ts = pd.Series(range(len(date_index)), index=date_index)

        # create timeseries df from date index and wallet entries
        _df_ts = (
            _ts.to_frame("Day")
            .join(self.wallet[["Date", "Change"]].groupby(by="Date").sum(), how="left")
            .fillna(method="ffill")
        )

        # join in daily price data for each security
        for security in self.tickers_archive:
            _series = self.securities_archive[security].data["Close"]
            _series = _series.rename(str(_series.name) + "_" + security)
            _df_ts = _df_ts.join(_series, how="left", rsuffix="").fillna(method="ffill")

        for security in self.tickers_archive:
            _df = self.transactions.loc[
                (self.transactions.Ticker == self.securities_archive[security].ticker)
                & (self.transactions.Transaction.isin(("buy", "sell"))),
                :,
            ].copy()
            _df.loc[_df.Transaction == "sell", "Quantity"] = _df.loc[
                _df.Transaction == "sell", "Quantity"
            ].apply(lambda x: -x)
            _df = _df[["Date", "Quantity"]].groupby(by=["Date"]).sum().cumsum()
            _df.columns = _df.columns.map(lambda x: str(x) + "_" + security)
            _df_ts = _df_ts.join(_df, how="left", rsuffix="").fillna(method="ffill")

        # fill all values forward then calculate value in portfolio
        _df_ts = _df_ts.fillna(method="ffill")
        for security in self.tickers_archive:
            _df_ts[security] = (
                _df_ts["Close_" + security] * _df_ts["Quantity_" + security]
            )

        # calculate portfolio value
        _df_ts = _df_ts.join(
            self.wallet[["Date", "Change"]].groupby(by="Date").sum().cumsum(),
            how="left",
            rsuffix="_tot",
        ).fillna(method="ffill")
        _df_ts["Cash"] = _df_ts["Change_tot"]
        _df_ts["Total"] = _df_ts["Change_tot"]
        for security in self.tickers_archive:
            _df_ts["Total"] = _df_ts["Total"] + _df_ts[security].fillna(0)

        # calculate portfolio value
        _df = self.payments.groupby(by=["Date"]).sum().cumsum()
        _df["Total_deposited"] = _df["In"] - _df["Out"]
        _df_ts = _df_ts.join(_df, how="left", rsuffix="").fillna(method="ffill")
        _df_ts["Growth"] = _df_ts["Total"] / _df_ts["Total_deposited"]

        self.data = _df_ts[self.tickers_archive + ["Cash", "Total"]]
        self.data_growth = _df_ts[["Total", "Total_deposited", "Growth"]]

    def get_returns(self, column="Total"):
        try:
            self.data
        except AttributeError:
            self.get_timeseries()
        else:
            pass

        self.data_growth, _ = returns_column(df=self.data_growth, column=column)

        # get date range from the transaction list
        self.min_date = min(self.transactions.Date.min(), self.payments.Date.min())
        self.max_date = datetime.datetime.now()

        # make a list of days between min and max date as index for timeseries df
        date_index = pd.date_range(self.min_date, self.max_date, freq="D")
        _ts = pd.Series(range(len(date_index)), index=date_index)

        # create timeseries df from date index and wallet entries
        _df_ts = _ts.to_frame("Day")
        #

        # join in daily price change data for each security
        cols = []
        for security in self.tickers:
            _series = self.securities[security].data["Close"]
            _col_name = str(_series.name) + "_" + security
            _series = _series.rename(_col_name)
            _df_ts = _df_ts.join(_series, how="left", rsuffix="").fillna(method="ffill")
            _df_ts, _ = returns_column(df=_df_ts, column=_col_name, outname=security)
            cols.append(_col_name)

        self.returns = _df_ts.drop(cols, axis=1)

    def get_benchmark(self, benchmark_ticker="^GSPC"):

        if benchmark_ticker == "sp500" or benchmark_ticker == "^GSPC":
            _ticker = "^GSPC"
        else:
            _ticker = "^GSPC"  # S&P 500 as default

        # create security class object for benchmark ticker
        _benchmark = Security(_ticker, start=self.min_date, end=self.max_date)

        # calculate returns for the benchmark
        _benchmark.data, _ = returns_column(df=_benchmark.data, column="Close")

        self.benchmark = _benchmark

    def get_performance(self):
        try:
            self.data
        except AttributeError:
            self.get_timeseries()
        else:
            pass

        years = []
        growths = []
        dollar_returns = []
        for year in self.data_growth.index.year.unique():
            # get first and last day of year
            min_date = self.data_growth.loc[
                self.data_growth.index.year == year, :
            ].index.min()
            max_date = self.data_growth.loc[
                self.data_growth.index.year == year, :
            ].index.max()

            # compare growth (percentage and dollars) between end of year and beginning of year
            growth = 100 * (
                self.data_growth.loc[
                    self.data_growth.index == max_date, "Growth"
                ].values[0]
                - self.data_growth.loc[
                    self.data_growth.index == min_date, "Growth"
                ].values[0]
            )
            dollar_return = (
                self.data_growth.loc[
                    self.data_growth.index == max_date, "Total"
                ].values[0]
                - self.data_growth.loc[
                    self.data_growth.index == max_date, "Total_deposited"
                ].values[0]
            ) - (
                self.data_growth.loc[
                    self.data_growth.index == min_date, "Total"
                ].values[0]
                - self.data_growth.loc[
                    self.data_growth.index == min_date, "Total_deposited"
                ].values[0]
            )
            years.append(year)
            growths.append(growth)
            dollar_returns.append(dollar_return)

        _df = pd.DataFrame({"Year": years, "Growth": growths, "Return": dollar_returns})

        return _df

class TotalPortfolioValue(object):
    """
       Class that turns the portfolio total into a security-like object
    """

    def __init__(self, name, data):
        self.ticker = name
        self.data = data

    def set_name(self, name):
        self.name = name

    def update_data(self, data):
        self.data = data

    def get_returns(self, column="Total"):
        self.data, _ = returns_column(df=self.data, column=column)

    def get_benchmark(self, benchmark_ticker="^GSPC"):

        self.min_date = self.data.index.min()
        self.max_date = min(self.data.index.max(), datetime.datetime.now())

        if benchmark_ticker == "sp500" or benchmark_ticker == "^GSPC":
            _ticker = "^GSPC"
        else:
            _ticker = "^GSPC"  # S&P 500 as default

        # create security class object for benchmark ticker
        _benchmark = Security(_ticker, start=self.min_date, end=self.max_date)

        # calculate returns for the benchmark
        _benchmark.data, _ = returns_column(df=_benchmark.data, column="Close")

        self.benchmark = _benchmark


# -*- coding: utf-8 -*-
# referred: https://blog.csdn.net/Crafts_Neo/article/details/90356621

import numpy as np
import pandas as pd
from sklearn.preprocessing import StandardScaler

path = "./data/"

train = pd.read_csv(path + 'train.csv',engine = 'python', encoding = 'utf-8')
test  = pd.read_csv(path + 'test.csv' ,engine = 'python', encoding = 'gbk')

train = train[train['observation'] == 'PM2.5']
# print(train)
test = test[test['AMB_TEMP'] == 'PM2.5']
# print(test)
train = train.drop(['Date','stations','observation'],axis=1)
# print(train)
test_x = test.iloc[:,2:]
#print(test_x)

train_x = []
train_y = []

for i in range(15):
        x = train.iloc[:,i:i+9]
        x.columns = np.array(range(9))
        y=train.iloc[:,i+9]
        y.columns = np.array(range(1))
        train_x.append(x)
        train_y.append(y)

train_x = pd.concat(train_x)
train_y = pd.concat(train_y)

train_y = np.array(train_y,float)
test_x  = np.array(test_x,float)
#print(test_x)
#print(train_y)
ss = StandardScaler()

#print(ss)
print("-----")
ss.fit(train_x)
#print(ss)
train_x = ss.transform(train_x)

ss.fit(test_x) # what's the meaning?
test_x = ss.transform(test_x)

def r2_score(y_true,y_predict):
    MSE = np.sum((y_true-y_predict)**2)/len(y_true)
    return 1-MSE/np.var(y_true)

class LinearRegression:
    def __init__(self):
        self.coef_ = None
        self.intercept_ = None
        self._theta = None

    def fit_normal(self,X_train,y_train):
        assert X_train.shape[0] == y_train.shape[0], \
                "the size of X_train must be equal to the size of y_train"

        X_b = np.hstack([np.ones((len(X_train),1)),X_train])
        self._theta = np.linalg.inv(X_b.T.dot(X_b)).dot(X_b.T).dot(y_train)

        self.intercept_ = self._theta[0]
        self.coef_ = self._theta[1:]

        return self

    def fit_gd(self,X_train,y_train,eta = 0.01, n_iters = 1e4):
        assert X_train.shape[0] == y_train.shape[0], \
                "the size of X_train must be equal to the size of y_train"

        def J(theta,X_b,y):
            try:
                return np.sum((y-X_b.dot(theta))**2)/len(y)
            except:
                return float("inf")

        def dJ(theta,X_b,y):
            return X_b.T.dot(X_b.dot(theta)-y)*2./len(y)

        def gradient_descent(X_b,y,initial_theta,eta,n_iters = 1e4,epsilon=1e-8):
            theta = initial_theta
            cur_iter = 0
            while cur_iter < n_iters:
                gradient = dJ(theta,X_b,y)
                last_theta = theta
                theta = theta-eta*gradient
                if (abs(J(theta,X_b,y)-J(last_theta,X_b,y)) < epsilon):
                    break
                cur_iter += 1
            return theta

        X_b = np.hstack([np.ones((len(X_train),1)),X_train])
        initial_theta = np.zeros(X_b.shape[1])
        self._theta = gradient_descent(X_b,y_train,initial_theta,eta,n_iters)
        self.intercept_ = self._theta[0]
        self.coef_ = self._theta[1:]
        return self

    def predict(self,X_predict):
        assert self.intercept_ is not None and self.coef_ is not None,\
                "must fit before predict!"
        assert X_predict.shape[1] == len(self.coef_), \
                "the feature number of X_predict must be equal to X_train"
        X_b = np.hstack([np.ones((len(X_predict),1)),X_predict])
        return X_b.dot(self._theta)

    def score(self,X_test,y_test):
        y_predict = self.predict(X_test)
        return r2_score(y_test,y_predict)

    def __repr__(self):
        return

LR = LinearRegression().fit_gd(train_x,train_y)
LR.score(train_x,train_y)
result = LR.predict(test_x)

sampleSubmission = pd.read_csv(path+'sampleSubmission.csv',engine='python',encoding='gbk')
sampleSubmission['value'] = result
sampleSubmission.to_csv(path+'result.csv')


import os
import time
import logging
import json
import requests
import tempfile
import shutil
from collections import defaultdict
from repoze.lru import lru_cache

SLAVES_JSON_URL = "http://slavealloc.pvt.build.mozilla.org/api/slaves"
CACHE_FILE = "slaves.json"
CACHE_TTL = 10 * 60

log = logging.getLogger(__name__)

@lru_cache(10)
def get_classified_slaves(is_spot=True):
    js = get_slaves_json(SLAVES_JSON_URL, CACHE_FILE)
    slaves = [s for s in js if is_spot_slave(s) is is_spot and is_enabled(s)]
    # 2D dict: x[moz_type][region] = ["slave1", "slave2"]
    classified_slaves = defaultdict(lambda: defaultdict(set))
    for s in slaves:
        moz_type = slave_moz_type(s)
        region = slave_region(s)
        name = s.get("name")
        if all([moz_type, region, name]):
            classified_slaves[moz_type][region].add(name)
    return classified_slaves

def slave_region(slave):
    return slave.get("datacenter")

def is_spot_slave(slave):
    return "-spot-" in slave.get("name", "")

def is_enabled(slave):
    return slave.get("enabled")

def slave_moz_type(slave):
    # Separate golden slaves
    if slave.get("name") and "golden" in slave.get("name"):
        return "golden"

    # av-linux64
    if slave.get("name", "").startswith("av-linux64-") and \
       slave.get("bitlength") == "64" and \
       slave.get("environment") == "prod" and \
       slave.get("distro") == "centos6-mock" and \
       slave.get("purpose") == "build" and \
       slave.get("trustlevel") == "core":
        return "av-linux64"

    # bld-linux64
    if slave.get("bitlength") == "64" and \
       slave.get("environment") == "prod" and \
       slave.get("distro") == "centos6-mock" and \
       slave.get("purpose") == "build" and \
       slave.get("trustlevel") == "core":
        return "bld-linux64"

    # try-linux64
    if slave.get("bitlength") == "64" and \
       slave.get("environment") == "prod" and \
       slave.get("distro") == "centos6-mock" and \
       slave.get("purpose") == "build" and \
       slave.get("trustlevel") == "try":
        return "try-linux64"

    # tst-linux32
    if slave.get("bitlength") == "32" and \
       slave.get("environment") == "prod" and \
       slave.get("distro") == "ubuntu32" and \
       slave.get("purpose") == "tests" and \
       slave.get("trustlevel") == "try":
        return "tst-linux32"

    # tst-linux64
    if slave.get("bitlength") == "64" and \
       slave.get("environment") == "prod" and \
       slave.get("distro") == "ubuntu64" and \
       slave.get("purpose") == "tests" and \
       slave.get("speed") == "m1.medium" and \
       slave.get("trustlevel") == "try":
        return "tst-linux64"

    # tst-emulator64
    if slave.get("bitlength") == "64" and \
       slave.get("environment") == "prod" and \
       slave.get("distro") == "ubuntu64" and \
       slave.get("purpose") == "tests" and \
       slave.get("speed") == "c3.xlarge" and \
       slave.get("trustlevel") == "try":
        return "tst-emulator64"

    # b-2008
    if slave.get("bitlength") == "64" and \
       slave.get("environment") == "prod" and \
       slave.get("distro") == "win2k8" and \
       slave.get("purpose") == "build" and \
       slave.get("trustlevel") == "core":
        return "b-2008"

    # y-2008 (try-2008)
    if slave.get("bitlength") == "64" and \
       slave.get("environment") == "prod" and \
       slave.get("distro") == "win2k8" and \
       slave.get("purpose") == "build" and \
       slave.get("trustlevel") == "try":
        return "y-2008"

    # t-w732
    if slave.get("bitlength") == "32" and \
       slave.get("environment") == "prod" and \
       slave.get("distro") == "win7" and \
       slave.get("purpose") == "tests" and \
       slave.get("speed") == "c3.2xlarge" and \
       slave.get("trustlevel") == "try":
        return "t-w732"

    # g-w732
    if slave.get("bitlength") == "32" and \
       slave.get("environment") == "prod" and \
       slave.get("distro") == "win7" and \
       slave.get("purpose") == "tests" and \
       slave.get("speed") == "g2.2xlarge" and \
       slave.get("trustlevel") == "try":
        return "g-w732"

    return None

def get_slaves_json(url, cache):
    try:
        mtime = os.stat(cache).st_mtime
        now = time.time()
        if now - mtime < CACHE_TTL:
            log.debug("Using cached slaves.json")
            return read_slaves_json(cache)
        else:
            log.debug("File expired, fetching a new one")
    except (OSError, IOError, KeyError):
        log.warn("Error reading cache file, trying to fetch", exc_info=True)

    try:
        download_file(url, cache)
    except:  # noqa: E722
        log.warn("Cannot fetch slaves.json, reusing the existing file",
                 exc_info=True)
    return read_slaves_json(cache)

def read_slaves_json(filename):
    return json.load(open(filename))

def download_file(url, dest):
    req = requests.get(url, timeout=30)
    req.raise_for_status()
    _, tmp_fname = tempfile.mkstemp()
    with open(tmp_fname, "wb") as f:
        f.write(req.content)
    log.debug("moving %s to %s", tmp_fname, dest)
    shutil.move(tmp_fname, dest)


from time import sleep

from django.core.management.base import BaseCommand

from books.googlebooks import get_book_info_from_api
from books.models import Book, Category

class Command(BaseCommand):
    help = 'Add categories to existing books'

    def handle(self, *args, **options):
        books = Book.objects.all()
        for book in books:
            self.stdout.write(f"Adding categories for book {book.bookid} ({book.title})")
            if book.categories.count() > 0:
                self.stderr.write("book already has categories, skipping")
                continue
            try:
                get_book_info_from_api(book.bookid)
            except KeyError:
                self.stderr.write("Cannot get book info from Google Books API, skipping")
            sleep(0.5)  # not sure about API rates


import os
from batchspawner import SlurmSpawner
from oauthenticator.cilogon import CILogonSpawnerMixin
from traitlets import Unicode

class CometSpawner(CILogonSpawnerMixin, SlurmSpawner):

    req_gres = Unicode('', config=True, \
        help="Additional resources requested"
        )

    def _options_form_default(self):
        """Create a form for the user to choose the configuration for the SLURM job"""
        return """
        Comet node type
        <select name="queue">
          <option value="compute">standard node</option>
          <option value="shared">shared node</option>
          <option value="gpu">full GPU node</option>
          <option value="gpu-shared">shared GPU node</option>
        </select>
        Number of GPUs (only for shared GPU node)
        <select name="gpus">
          <option value="1">1</option>
          <option value="2">2</option>
          <option value="3">3</option>
          <option value="4">4</option>
        </select>
        Number of cores (only for shared node)
        <select name="cores">
          <option value="1">1</option>
          <option value="2">2</option>
          <option value="6">6</option>
          <option value="12">12</option>
        </select>
        Job duration
        <select name="runtime">
          <option value="1:00:00">1 hour</option>
          <option value="2:00:00">2 hours</option>
          <option value="5:00:00">5 hours</option>
          <option value="8:00:00">8 hours</option>
          <option value="12:00:00">12 hours</option>
          <option value="24:00:00">24 hours</option>
        </select>
        Account (leave empty for default)
         <input name="account"></input>
        </select>
        """

    def options_from_form(self, formdata):
        """Parse the form and add options to the SLURM job script"""
        options = {}
        options['queue'] = formdata.get('queue', [''])[0].strip()
        options['runtime'] = formdata.get('runtime', [''])[0].strip()
        options['other'] = ''
        account = formdata.get('account', [''])[0].strip()
        if account:
            options['other'] += "#SBATCH --account={}".format(account)
        if options['queue'].startswith('gpu'):
            options['other'] += "\n#SBATCH --gres='gpu:{}'".format(formdata.get("gpus")[0])
        if options['queue'] == "shared":
            options['other'] += "\n#SBATCH --ntasks-per-node={}".format(formdata.get("cores")[0])
        return options

    def stage_cert_file(self):
        """Stage the CILogon user cert for the spawner.

        Override for Spawners not on the local filesystem.
        """
        if not self.cert:
            self.log.info("No cert found for %s", self.user.name)

        dst = "/tmp/cert." + self.user.name
        self.log.info("Staging cert for %s: %s", self.user.name, dst)

        with open(dst, 'w') as f:
            fd = f.fileno()
            os.fchmod(fd, 0o600) # make private before writing content
            cert = self.cert
            if not cert:
                error_message = "Empty cert for {}, check TPM is running".format(self.user.name)
                self.log.error(error_message)
                raise RuntimeError(error_message)
            f.write(cert)
            with open("/srv/oauth-privkey.pem") as privkey: #FIXME get path from env
                f.write(privkey.read())
            # set user as owner
            # os.fchown(fd, uinfo['uid'], uinfo['gid'])

    def unstage_cert_file(self):
        """Unstage user cert

        called after stopping
        """
        dst = "/tmp/cert." + self.user.name
        if not os.path.exists(dst):
            self.log.debug("No cert for %s: %s", self.user.name, dst)
            return
        self.log.info("Unstaging cert for %s: %s", self.user.name, dst)
        try:
            os.remove(dst)
        except OSError as e:
            if e.errno == errno.EEXIST:
                pass
            self.log.error("Failed to unstage cert for %s (%s): %s",
                self.user.name, dst, e)


# -*- coding: utf-8 -*-

# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.

from __future__ import print_function

import argparse
import json
import logging
import sys

try:
    import pygments
    import pygments.formatters
    import pygments.lexers
    import pygments.util
except ImportError:
    pygments = None

from keystoneauth1 import exceptions
from keystoneauth1 import session
import os_client_config

from os_http import version

LOG = logging.getLogger(__name__)

_occ_version = os_client_config.__version__

formatter_name = 'console' if sys.stdout.isatty() else 'text'

class ErrorExit(Exception):

    def __init__(self, message, exit_code=1):
        self.message = message
        self.exit_code = exit_code

def format_resp(resp):
    # I can see no way to get the HTTP version
    headers = ["HTTP/1.1 %d %s" % (resp.status_code, resp.reason or '')]
    headers.extend('%s: %s' % k for k in resp.headers.items())
    headers = '\n'.join(headers)

    if 'json' in resp.headers.get('Content-Type', '').lower():
        body = json.dumps(resp.json(), sort_keys=True, indent=4)
    else:
        body = resp.content

    if pygments:
        mime = resp.headers.get('Content-Type')
        http_lexer = pygments.lexers.get_lexer_by_name('http')
        formatter = pygments.formatters.get_formatter_by_name(formatter_name)

        try:
            body_lexer = pygments.lexers.get_lexer_for_mimetype(mime)
        except pygments.util.ClassNotFound:
            body_lexer = pygments.lexers.get_lexer_by_name('text')

        headers = pygments.highlight(headers, http_lexer, formatter)
        body = pygments.highlight(body, body_lexer, formatter)

    return '\n'.join([headers, '', body])

def run(argv):
    parser = argparse.ArgumentParser(
        description='Simple HTTP testing for Openstack')

    parser.add_argument('--debug',
                        action='store_true',
                        help='Enable debug output')

    config = os_client_config.OpenStackConfig()
    config.register_argparse_arguments(parser, argv)

    positional = parser.add_argument_group('Positional Arguments')
    positional.add_argument('method',
                            metavar='METHOD',
                            help='The HTTP method to make the request with')
    positional.add_argument('url',
                            metavar='URL',
                            help='The URL or path to request')
    positional.add_argument('items',
                            metavar='ITEM',
                            nargs='*',
                            help='Additional items')

    opts = parser.parse_args(argv)

    if opts.debug:
        logging.basicConfig(level=logging.DEBUG)
    else:
        logging.basicConfig(level=logging.WARN)

    cloud = config.get_one_cloud(argparse=opts)

    adap = cloud.get_session_client(None)

    # FIXME(jamielennox): These things should be handled by os-client-config
    adap.logger = LOG
    adap.user_agent = 'os-http/%s os-client-config/%s %s' % (
        version.version_string,
        _occ_version,
        session.DEFAULT_USER_AGENT)
    adap.version = opts.os_api_version

    headers = {'Accept': 'application/json'}

    for item in opts.items:
        if ':' in item:
            key, val = item.split(':', 1)
            headers[key] = val
        else:
            raise ErrorExit("Unknown item: %s" % item)

    try:
        resp = adap.request(opts.url,
                            opts.method.upper(),
                            headers=headers,
                            raise_exc=False)

    except exceptions.EmptyCatalog:
        message = ("Failed to find an endpoint because the returned service "
                   "catalog is empty.")

        try:
            get_access = adap.session.auth.get_access
        except AttributeError:
            # not an identity plugin - see if it specifies project_id
            scoped = bool(adap.get_project_id())
        else:
            scoped = get_access(adap.session).scoped

        if not scoped:
            message += (" This seems to be because the credentials provided "
                        "result in an unscoped token. Please check your "
                        "authentication credentials.")

        raise ErrorExit(message)

    except exceptions.EndpointNotFound:
        service_params = ('service_type',
                          'service_name',
                          'interface',
                          'region_name',
                          'version')

        query = ", ".join("%s=%s" % (p, getattr(adap, p))
                          for p in service_params if getattr(adap, p))
        raise ErrorExit("Failed to find an endpoint in the service catalog "
                        "that matches your query: %s" % query)

    return format_resp(resp)

def main(argv=sys.argv[1:]):
    try:
        output = run(argv)
    except ErrorExit as e:
        LOG.error(e.message)
        sys.exit(e.exit_code)
    else:
        print(output)


from rest_framework import serializers, fields
from ..models.insurance import Insurance, TYPE_INSURANCE
from datetime import date
from rest_framework import fields
from ..models.car import Car

class InsuranceSerializer(serializers.ModelSerializer):
    type_of_insurance = fields.MultipleChoiceField(choices=TYPE_INSURANCE)
    end_date = fields.DateField(input_formats=['%Y-%m-%d', 'iso-8601',])

    class Meta:
        model = Insurance

        fields = ['insurance_status', 'insurance_company', 'end_date', 'type_of_insurance',
                  'worth_of_insurance', 'insurance_number', 'car']
        read_only_fields = ['end_date']

    def validate_type_of_insurance(self, value):

        """
        Validation of type of insurance.
        Checks if the car has compulsory OC insurance.
        """

        if 'OC' not in value:
            raise serializers.ValidationError("The car must have OC")
        return value

    def validate(self, data):

        """
        Validation of car data and insurance data relation.
        Checks that if the car is leased, the car has a full insurance too.
        """

        car_id = data['car'].id
        leasing = 'L'
        insurance = data['type_of_insurance']
        car_qs = Car.objects.filter(id=car_id)
        if not car_qs.exists():
            raise serializers.ValidationError('This car not exist...!')

        car = car_qs[0]
        car.status = False
        if car.way_of_purchase == leasing and insurance != {'AC', 'OC', 'NWW'}:
            raise serializers.ValidationError("If you bought a car in leasing, you must have full insurance.")

        if data['end_date'] < date.today() and data['insurance_status'] == True:
            raise serializers.ValidationError("Are you sure this car has insurance?")

        return data


import constraint

problem = constraint.Problem()

# mora izmedju 2500 i 3500 kalorija
# minimum 60g proteina
# kalcijum izmedju 500mg i 1000mg
# min para da ispuni sve ove uslove

problem.addVariable('ovseno', range(5))
problem.addVariable('piletina', range(4))
problem.addVariable('jaje', range(4))
problem.addVariable('mleko', range(9))
problem.addVariable('tiramisu', range(3))

def kalorije_ogranicenje(o,p,j,m,t):
    if(o*110 + p*205 + j*160 + m*160 + t*420 <= 3500 and o*110 + p*205 + j*160 + m*160 + t*420 >= 2500):
        return True
def proteini_minimum(o,p,j,m,t):
    if(o*4+p*32+j*13+m*8+t*4) >= 60:
        return True
def kalcijum_ogranicenje(o,p,j,m,t):
    if(o*2+p*12+j*285+m*22+t*22 >= 500 and o*2+p*12+j*285+m*22+t*22 <= 1000):
        return True
problem.addConstraint(kalorije_ogranicenje,['ovseno', 'piletina', 'jaje', 'mleko', 'tiramisu'])
problem.addConstraint(proteini_minimum, ['ovseno', 'piletina', 'jaje', 'mleko', 'tiramisu'])
problem.addConstraint(kalcijum_ogranicenje, ['ovseno', 'piletina', 'jaje', 'mleko', 'tiramisu'])

resenja = problem.getSolutions()
## 80 200 20 90 190
min_cena = 99999
kalorije = 0
proteini = 0
kalcijum = 0

for resenje in resenja:
    if(resenje['ovseno']*80 + resenje['piletina']*200+ resenje['jaje']*20 + resenje['mleko']*90 + resenje['tiramisu']*190) < min_cena:
        min_cena = resenje['ovseno']*80 + resenje['piletina']*200 + resenje['jaje']*20+resenje['mleko']*90 + resenje['tiramisu']*190
        kalorije = resenje['ovseno']*110 + resenje['piletina']*205 + resenje['jaje']*160+resenje['mleko']*160 + resenje['tiramisu']*420
        proteini = resenje['ovseno']*4 + resenje['piletina']*32 + resenje['jaje']*13+resenje['mleko']*8 + resenje['tiramisu']*4
        kalcijum = resenje['ovseno']*2 + resenje['piletina']*12 + resenje['jaje']*285+resenje['mleko']*22 + resenje['tiramisu']*22

print("Najbolja kombinacija: ", min_cena, " dinara, ",kalorije," kalorija")
print("Proteina: ", proteini, ", kalcijuma: ", kalcijum)


import scrapy

class QuotesSpider(scrapy.Spider):
    name = "quotes4"
    start_urls = [
        'http://quotes.toscrape.com/page/1/',
        'http://quotes.toscrape.com/page/2/',
    ]

    def parse(self, response):
        for quote in response.css('div.quote'):
            yield {
                'text': quote.css('span.text::text').extract_first(),
                'author': quote.css('span small::text').extract_first(),
                'tags': quote.css('div.tags a.tag::text').extract(),
            }


from django.urls import path
from . import views
app_name='owner'

urlpatterns = [
    path('master',views.master_page,name='master-page'),
    path('login',views.login,name='login-page'),
    path('add_mobile',views.add_mobile,name='add-mobile'),
    path('manage_products',views.manage_products,name='manage-products'),
    path('purchase_req',views.purchase_req,name='purchase-req'),
    path('registred_cus',views.registred_cus,name='registred-cus'),
    # path('update/<int:mobileid>',views.mobile_update,name='m-update'),
    path('delete/<int:mobileid>',views.mobile_delete,name='m-delete')

]


from django.contrib import admin
from django.urls import path, include
from django.conf import settings
from django.conf.urls.static import static

from rest_framework.routers import DefaultRouter

from authentication.views import UserViewSet
from product.views import ProductViewSet

from rest_framework import permissions
from drf_yasg.views import get_schema_view
from drf_yasg import openapi

...

schema_view = get_schema_view(
   openapi.Info(
      title="Ecommerce API",
      default_version='v1',
      description="This server is for selling cloths that some other wears",
      contact=openapi.Contact(email="adedijiabdulquadri@gmail.com"),
   ),
   public=True,
   permission_classes=(permissions.AllowAny,),
)

router = DefaultRouter()
router.register("api/auth/users", UserViewSet)
router.register("api/product", ProductViewSet)

urlpatterns = [
   path('swagger<format>/', schema_view.without_ui(cache_timeout=0), name='schema-json'),
   path('swagger/', schema_view.with_ui('swagger', cache_timeout=0), name='schema-swagger-ui'),
   path('redoc/', schema_view.with_ui('redoc', cache_timeout=0), name='schema-redoc'),
    path('api/auth/users/', include('authentication.urls')),
    path('api/product/', include('product.urls')),
    path('api/cart/', include('cart.urls')),
    path('admin/', admin.site.urls),

]

urlpatterns += static(settings.MEDIA_URL,
                          document_root=settings.MEDIA_ROOT)
urlpatterns += static(settings.STATIC_URL,
                          document_root=settings.STATIC_ROOT)

urlpatterns += router.urls


import torch
import numpy as np
import cv2
from sklearn.cluster import MeanShift
import imageio
import os.path as osp
import matplotlib.pyplot as plt

def meanshift(model, result_path, dataloader, S): #S is the shape of picture
    for iter, (x, _ )in enumerate(dataloader):
        y_exp = []
        x = x.float()
        y = model(x)
        y = torch.squeeze(y).detach().numpy()
        y = y.astype(np.float32)
        y = cv2.resize(y, (S, S), interpolation=cv2.INTER_NEAREST)
        for i in range(y.shape[0]):
            for j in range(y.shape[0]):
                if y[i, j] >= 0.5:
                    y_exp.append([i, j])
        y_exp = np.array(y_exp)
        print(iter, y_exp.shape[0]) #第几张图片以及图片大小
        y = y.astype(np.uint8)
        R = 22.0 #带宽
        #R = 52.0 #dataset2使用
        cluster_model = MeanShift(bandwidth=R, bin_seeding=True)
        cluster_model.fit(y_exp)
        labels = cluster_model.labels_
        labels = [la + 1 for la in labels]  # 每个标签都加1，因为label从0开始算
        for i in range(len(y_exp)):
            xi = y_exp[i, 0]
            yi = y_exp[i, 1]
            y[xi, yi] = labels[i]
        imageio.imwrite(osp.join(result_path, 'mask{:0>3d}.tif'.format(iter)), y.astype(np.uint16))
        # 以下可视化聚类结果
        '''
        height, width = y.shape[:2]
        label = np.unique(y)
        visual_img = np.zeros((height, width, 3))
        for lab in label:
            if lab == 0:
                continue
            color = np.random.randint(low=0, high=255, size=3)
            visual_img[y == lab, :] = color
        visual_img = visual_img.astype(np.uint8)
        plt.imshow(visual_img)
        plt.pause(0.01)
        plt.show()
        '''


import aiohttp
from bs4 import BeautifulSoup
from typing import List, Union, Tuple
from datetime import datetime
from dateutil import parser
import re

async def get(url, headers=None, image=None, params=None):
    async with aiohttp.ClientSession(connector=aiohttp.TCPConnector(verify_ssl=False)) as session:
        async with session.get(url, headers=headers, params=params, timeout=60.0) as response:
            headers = response.headers
            if image:
                img = await response.read()
                return headers, img
            text = await response.text()
            return text, headers

async def get_golden_keys():
    url = 'https://owwya.com/borderlands-3-shift-codes/'
    text, _ = await get(url)
    bs = BeautifulSoup(text, 'html.parser')
    inner_content = bs.find('div', {'class': 'content-inner'})
    codes_list = inner_content.find_all('li')
    return codes_parser([code.text for code in codes_list])

def codes_parser(codes_list: List[str]) -> List[str]:
    pattern = r'((\w{5}-){4}\w{5}).+(\d+).+\(\w+\s\w+\s(.+,.*?\d+).+[)]'
    pattern_permanent = r'((\w{5}-){4}\w{5}).+(\d+).+\((Permanent)'
    parsed_code_list = []
    for code in codes_list:
        look = re.search(pattern, code)
        look_permanent = re.search(pattern_permanent, code)
        if look:
            groups = look.groups()
            if len(groups) > 2 and date_validator(groups[3])[1]:
                parsed_code_list.append(f'Актуальный Shift код: {groups[0]}\n'
                                        f'Валиден до {date_validator(groups[3])[0]}\n'
                                        f'Кол-во золотых ключей: {groups[2]}')
        elif look_permanent:
            groups = look_permanent.groups()
            if len(groups) > 2:
                parsed_code_list.append(f'Постоянный Shift код: {groups[0]}\n'
                                        f'Кол-во золотых ключей: {groups[2]}')
    return parsed_code_list

def date_validator(eng_date: str) -> Tuple[str, bool]:
    month_dict = {
        1: 'Января',
        2: 'Февраля',
        3: 'Марта',
        4: 'Апреля',
        5: 'Мая',
        6: 'Июня',
        7: 'Июля',
        8: 'Августа',
        9: 'Сентября',
        10: 'Октбяря',
        11: 'Ноября',
        12: 'Декабря',
    }
    code_time = parser.parse(eng_date)
    today_time = datetime.now()
    if today_time <= code_time:
        return f'{code_time.day} {month_dict[code_time.month]} {code_time.year}', True
    else:
        return 'Код неактивен', False

def test_sum():
    isinstance()


from typing import Dict, Any, Optional

from ..abc import Space
from ..model import Interval

from lib_satprob.problem import Problem
from typings.searchable import Vector
from lib_satprob.variables import Indexes

class IntervalSet(Space):
    slug = 'space:interval_set'

    def __init__(
            self,
            indexes: Indexes,
            by_vector: Optional[Vector] = None
    ):
        super().__init__(by_vector)
        self.indexes = indexes

    # noinspection PyProtectedMember
    def get_initial(self) -> Interval:
        interval = self._get_searchable()
        if self.by_vector is not None:
            interval._set_vector(self.by_vector)
        return interval

    def _get_searchable(self) -> Interval:
        return Interval(indexes=self.indexes)

    def __config__(self) -> Dict[str, Any]:
        return {
            'slug': self.slug,
            'indexes': self.indexes.__config__(),
            'by_vector': self.by_vector,
        }

__all__ = [
    'IntervalSet'
]


# -*- coding: utf-8 -*-

import json

from django.utils.translation import ugettext_lazy as _
from django.shortcuts import get_object_or_404

# from rest_framework import permissions, serializers

from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.authtoken.models import Token
from rest_framework.authtoken.views import ObtainAuthToken
from rest_framework.authentication import TokenAuthentication
from rest_framework.permissions import IsAuthenticated

from src.api.serializers import TaskSerializer
from src.todo.models import Task

class LoginAPI(ObtainAuthToken):
    def post(self, request, *args, **kwargs):
        serializer = self.serializer_class(
            data=request.data, context={"request": request}
        )
        if serializer.is_valid():
            user = serializer.validated_data["user"]
            token, created = Token.objects.get_or_create(user=user)
            return Response(
                {
                    "status": "success",
                    "token": token.key,
                    "user_id": user.pk,
                    "email": user.email,
                }
            )
        else:
            return Response(
                {"status": "error", "message": "Username or password is incorrect"}
            )

class TaskListAPI(APIView):
    authentication_classes = (TokenAuthentication,)
    permission_classes = (IsAuthenticated,)

    def get(self, request, *args, **kwargs):
        datas = [
            {"id": task.id, "name": task.name, "complete": task.complete}
            for task in Task.objects.filter(user=request.user)
        ]
        return Response({"status": "success", "datas": datas})

class TaskCreateAPI(APIView):
    authentication_classes = (TokenAuthentication,)
    permission_classes = (IsAuthenticated,)

    def post(self, request, *args, **kwargs):
        serializer = TaskSerializer(data=request.data)
        if serializer.is_valid():
            print("Serializer is valid")
            task = serializer.save(user=request.user)
            return Response(
                {
                    "status": "success",
                    "data": {
                        "id": task.id,
                        "name": task.name,
                        "date": task.date,
                        "complete": task.complete,
                    },
                }
            )
        return Response({"status": "error", "message": "Can't create task"})

class TaskDeleteAPI(APIView):
    authentication_classes = (TokenAuthentication,)
    permission_classes = (IsAuthenticated,)

    def post(self, request, *args, **kwargs):
        data = request.data
        task = get_object_or_404(Task, id=data["id"])
        task.delete()
        return Response({"status": "success"})


# -*- coding: utf-8 -*-
import json
import os

class ProcessingStep:
    instances = []
    dummy_processing_step = None  # Used for orders that have no processing step left

    def __init__(self, task_config: dict, hidden=False):
        self.id = task_config["id"]
        self.name = task_config["title"].encode()
        self.base_duration = task_config["base_duration"]

        self.hidden = hidden
        if hidden:
            self.__class__.dummy_processing_step = self

        self.__class__.instances.append(self)

def load_processing_steps():
    """Load possible processing steps from json file and create an object for each"""

    path = os.path.realpath(__file__)
    directory = os.path.dirname(path).replace("objects", "configs")
    os.chdir(directory)

    processing_steps = json.load(open("processing_steps_paper_3.json", encoding="UTF-8"))

    # Hidden dummy processing step for finished orders
    ProcessingStep({"id": -1, "title": "Order finished", "base_duration": 0}, hidden=True)

    for step in processing_steps['tasks']:
        ProcessingStep(step)


# -*- coding: utf-8 -*-
"""
Created on Mon Aug 20 10:49:10 2018

@author: Ahmad

This program numerically solves the 1D Heat Transfer of a metallic rod under
the Dirichlet Boundary Conditions
"""
import numpy as np
from scipy.integrate import odeint

def dydt(y,t,T0,alpha,Nx,x):
    dydt = np.zeros(Nx)
    dydt[1:-1] = alpha * (y[2:] - 2*y[1:-1] + y[:-2]) / (np.diff(x)[1:])**2

    return dydt

def AnalyticalSol(x,t,kupp,alpha,T0,Ti,L):
    xx,tt = np.meshgrid(x,t)
    res = np.zeros(xx.shape)
    for k in np.arange(0,kupp+1,):
        res += np.sin((2*k + 1)*np.pi*xx/L)*np.exp(-alpha*(2*k + 1)**2*np.pi**2*tt/L**2)/(2*k + 1)
    res = T0 + 4*(Ti - T0)*res/np.pi
    return res

def Dirichlet():
    # Physical Properties (Aluminum)
    kappa = 273 # [W/m-K] Thermal conductivity
    rho = 2702 # [kg/m^3] Density of the metal
    Cp = 903 # [J/kg-K] Heat capacity of the metal

    alpha = kappa/(rho * Cp)

    # Boundary and initial conditions
    Ti = 273 # [k]; initial temperature
    T0 = 300 # [k]; boundary temperatures

    # Dimensions for discretizations
    L = 10 # [m]
    Nx = 30
    x = np.linspace(0,L,Nx)

    # initial condition vector
    y0 = np.ones(Nx) * Ti
    y0[0] = T0; y0[-1] = T0

    # solution time
    hr = 1 # [h]
    Nt = hr*3600 # view evolution every second
    timeArr = np.linspace(0,hr*3600,Nt) # [s]

    sol = odeint(dydt,y0,timeArr,
                 args=(T0,alpha,Nx,x))

    ansol = AnalyticalSol(x,timeArr,1000,alpha,T0,Ti,L)
    return sol,ansol

sol,ansol = Dirichlet()
print(sol[-1])
print(ansol[-1])


import glob
import numpy as np
import pandas as pd
import xarray as xr
import pathlib

from common import xarray_extras as xrx
from common import utils
import fpsim
import softio.core
import softio.gfas
import softio.maccity

# prepare mean air density
vertical_profile_of_air_density = pd.read_csv('/home/wolp/data/vertical_profile_of_air_density.csv', index_col='height_in_m', dtype=np.float32)['density_in_kg_per_m3']
vertical_profile_of_air_density = xr.DataArray.from_series(vertical_profile_of_air_density).rename({'height_in_m': 'height'})
vertical_profile_of_air_density['height'] = vertical_profile_of_air_density.height.astype('f4')
vertical_profile_of_air_density.name = 'air_density'
vertical_profile_of_air_density.attrs = dict(long_name='air_density', units='kg m-3')

def rt_transform_by_air_density(rt, oro):
    height_ds = softio.core.get_height_ds(rt['height'])
    top_height = height_ds[softio.core.TOP_HEIGHT]
    bottom_height = height_ds[softio.core.BOTTOM_HEIGHT]
    mean_height = 0.5 * (top_height + bottom_height)
    density = vertical_profile_of_air_density.interp(coords={'height': mean_height + oro}).astype(np.float32)
    rt = rt / density
    rt.attrs['units'] = 's m3 kg-1'
    return rt

def get_co_contrib(emission_inventory, fpsim_ds=None, fpsim_dir=None, time_granularity=None):
    """
    Main routine which calculates SOFT-IO CO contribution data for a given emission inventory and
    based on FLEXPART simulation result (given as fpsim_ds or fpsim_dir). The time resolution of CO contribution
    result is gridded according to a given time granularity
    :param emission_inventory: str; possible values: 'gfas', 'maccity', 'ceds2'
    :param fpsim_ds: xarray.Dataset containing FLEXPART simulation result as provided by the routing fpsim.open_fp_dataset
    :param fpsim_dir: str or path-like; a path to
    :param time_granularity: None or any object convertible to pandas.Timedelta;
    if None then the time granularity of FLEXPART simulation result will be applied
    :return: xarray.Dataset with CO contribution data
    """
    if fpsim_ds is None and fpsim_dir is None or fpsim_ds is not None and fpsim_dir is not None:
        raise ValueError('either fpsim_ds or fpsim_dir must be set')
    if fpsim_ds is None:
        output_dir = pathlib.PurePath(fpsim_dir, 'output')
        # grid_time_url = utils.unique(glob.glob(str(pathlib.PurePath(output_dir, 'grid_time_*.nc'))))
        grid_time_url = utils.unique(glob.glob(str(pathlib.PurePath(output_dir, 'grid_time_*'))))  # accepting .zarr too
        with fpsim.open_fp_dataset(str(grid_time_url)) as fpsim_ds:
            return get_co_contrib(emission_inventory, fpsim_ds=fpsim_ds, time_granularity=time_granularity)
    else:
        fpsim_ds = xrx.normalize_longitude(fpsim_ds)
        rt = fpsim_ds['spec001_mr']
        rt_units = rt.attrs['units']
        ind_source = int(fpsim_ds.attrs['ind_source'])
        ind_receptor = int(fpsim_ds.attrs['ind_receptor'])

        if ind_source == 2 and ind_receptor == 2:
            # must change residence time units from 's' to 's m3 kg-1' by dividing by air density at output grid cell
            rt = rt_transform_by_air_density(rt, fpsim_ds['ORO'])
        elif not (ind_source == 1 and ind_receptor == 2):
            raise ValueError(f'FLEXPART output with ind_source={ind_source}, ind_receptor={ind_receptor} '
                             f'and residence time units={rt_units} cannot be handled')

        emission_inventory = emission_inventory.lower()
        if emission_inventory == 'gfas':
            return softio.gfas.get_softio_gfas_results(rt, time_granularity=time_granularity)
        if emission_inventory == 'macc':
            return softio.maccity.get_softio_maccity_results(rt, time_granularity=time_granularity)
        if emission_inventory == 'ceds2':
            return softio.ceds2.get_softio_ceds2_results(rt, time_granularity=time_granularity)
        else:
            raise ValueError(f'unknown emission inventory: {emission_inventory}')


from compiler import *
import threading
import time
import logging

class Project:
	"""Representation of a TeXView project"""
	def __init__(self, directory, compile_wait_time=5000):
		"""Initialize a new representation of a project. The project will wait
		for compile_wait_time ms of no changes before compiling"""
		self.directory         = directory
		self.compiler          = Compiler(directory)
		self.compile_requests  = []
		self.already_compiling = False

		self.thread = ProjectObserver(self, compile_wait_time)
		self.thread.start()

	def queue_request(self):
		"""Queue a new compile request based on a change in the project"""
		logging.info("[REQUEST] Compile request for project \"%s\""%self.directory)
		self.compile_requests.append(time.time())

	def compile(self):
		"""Compiles the project"""
		if not self.already_compiling:
			self.already_compiling = True
			self.compile_requests = []

			self.compiler.compile()

			self.already_compiling = False
		else:
			self.queue_request()

class ProjectObserver(threading.Thread):
	"""An observer which checks whether a compile of the project is required"""
	def __init__(self, project, wait_time):
		threading.Thread.__init__(self)
		self.project   = project
		self.wait_time = wait_time

	def run(self):
		while True:
			requests = self.project.compile_requests

			if len(requests) > 0:
				current_time = time.time()
				last_request = requests[-1]
				delta        = (current_time - last_request) * 1000

				if delta >= self.wait_time:
					self.project.compile()

			time.sleep(1)


# LEGO sorter project
# Image loading and processing functions
# (c) lego-sorter team, 2022-2023

import os
import numpy as np
import logging
import tensorflow as tf
import tensorflow_addons as tfa
import matplotlib.pyplot as plt
from absl import flags
from keras.utils import image_dataset_from_directory
from dataclasses import dataclass
from functools import partial
from itertools import zip_longest
from typing import Tuple, Iterable, Union

from lib.globals import IMAGE_DIR, BATCH_SIZE, IMAGE_SIZE
from lib.model import preprocess_input, restore_processed_image

_logger = logging.getLogger('lego-sorter')

FLAGS = flags.FLAGS
flags.DEFINE_bool('gray', False, short_name='g',
    help='Convert images to grayscale')
flags.DEFINE_bool('edges', False, short_name='e',
    help='Convert images to wireframe (edges-only) images')
flags.DEFINE_bool('emboss', False, short_name='x',
    help='Mix wireframe with actual image, can be combined with --gray')
flags.DEFINE_boolean('zoom', False, short_name='z',
    help='Apply zoom augmentation (slows down the training by x5)')
flags.DEFINE_float('zoom_factor', 0.3,
    help='Maximum zoom level for image augmentation (for --zoom model only)')
flags.DEFINE_float('brightness_factor', 0.3,
    help='Maximum brightness level for image augmentation')
flags.DEFINE_float('rotation_factor', 0.45,
    help='Maximum rotation in image augmentation')
flags.DEFINE_float('crop_factor', 0.0, upper_bound=1.0,
    help='Crop image factor (from center)')
flags.DEFINE_float('edge_emboss', 0.5, lower_bound=0.0,
    help='Edge embossing factor (for --emboss model only)')
flags.DEFINE_float('shuffle_buffer_size', 0.25,
    help='Shuffle buffer, either ratio to number of files (<1) or actual size')

tf.get_logger().setLevel('ERROR')

@dataclass
class ImageDataset:
    """ Dataset with extra info resulting from `load_dataset()` call """
    tfds: tf.data.Dataset
    """ actual tensorflow dataset """

    num_files: int
    """ number of image files in a dataset """

    class_names: Iterable[str]
    """ class names """

def _sobel_edges(images):
    """ Internal - applies edges filter on gray image """
    shape = images.shape
    if len(shape) == 3: images = tf.expand_dims(images, 0)
    grad_components = tf.image.sobel_edges(images)
    grad_mag_components = grad_components**2
    grad_mag_square = tf.math.reduce_sum(grad_mag_components,axis=-1)
    images = tf.sqrt(grad_mag_square)
    if len(shape) == 3: images = images[0]
    return images

def _wireframe(images):
    """ Internal - generates wireframe image """
    shape = images.shape
    if len(shape) == 3: images = tf.expand_dims(images, 0)

    gray_images = tf.image.rgb_to_grayscale(images)
    sobel_images = _sobel_edges(gray_images)

    mask = tf.where(sobel_images < 127.5, 0.0, 255.0)   # VGG-16 operates on [0...255] range
    if FLAGS.edge_emboss > 0:
        kernel = tf.zeros((3, 3, mask.get_shape()[3]))
        dilation = (1.0, FLAGS.edge_emboss, FLAGS.edge_emboss, 1.0)
        mask = tf.nn.dilation2d(mask, kernel, (1,1,1,1), 'SAME', 'NHWC', dilation, 'dilation')

    if len(shape) == 3: mask = mask[0]
    return mask

def _preprocess(images, labels=None, seed=None):
    """ Image preprocessing function """
    if FLAGS.crop_factor:
        images = tf.image.central_crop(images, FLAGS.crop_factor)
        images = tf.image.resize(images, IMAGE_SIZE, preserve_aspect_ratio=False)

    images = preprocess_input(images)

    if FLAGS.emboss:
        mask = _wireframe(images)
        images = tf.where(tf.equal(mask, 0.0), images, tf.constant((0.0, 0.0, 255.0), images.dtype))
        if FLAGS.gray:
            images = tf.image.rgb_to_grayscale(images)
    elif FLAGS.gray:
        images = tf.image.rgb_to_grayscale(images)
    elif FLAGS.edges:
        images = _wireframe(images)

    return images, labels

# https://towardsdatascience.com/image-augmentations-in-tensorflow-62967f59239d
@tf.function
def _augment_images(image_and_label, seed):
    image, label = image_and_label
    new_seed = tf.random.experimental.stateless_split(seed, num=1)[0, :]
    image = tf.image.stateless_random_brightness(image, max_delta=FLAGS.brightness_factor, seed=new_seed)
    image = tf.image.stateless_random_flip_left_right(image, seed=new_seed)
    image = tf.image.stateless_random_flip_up_down(image, seed=new_seed)
    return image, label, seed

@tf.function
def _rotate_images(feature, label, seed):
    num_samples = int(tf.shape(feature)[0])
    degrees = tf.random.stateless_uniform(
        shape=(num_samples,), seed=seed, minval=FLAGS.rotation_factor*100.0, maxval=FLAGS.rotation_factor*100.0
    )
    degrees = degrees * 0.017453292519943295  # convert the angle in degree to radians
    rotated_images = tfa.image.rotate(feature, degrees, fill_mode='reflect')
    return rotated_images, label, seed

@tf.function
def _zoom_images(feature, label, seed):
    def _func(images, seed):
        return tf.keras.layers.RandomZoom(
                    (-FLAGS.zoom_factor, FLAGS.zoom_factor),
                    (-FLAGS.zoom_factor, FLAGS.zoom_factor),
                    fill_mode='nearest', interpolation='nearest', seed=seed)(images)
    zoomed_images = tf.py_function(func=_func, inp=[feature, seed], Tout=tf.float32)
    return zoomed_images, label, seed

@tf.function
def _drop_seed(feature, label, seed):
    return feature, label

def load_dataset() -> ImageDataset:
    """ Load images into a dataset """

    _flags = []
    if FLAGS.gray: _flags.append('gray')
    if FLAGS.edges: _flags.append('edges')
    if FLAGS.emboss: _flags.append('emboss')
    if FLAGS.zoom: _flags.append('zoom')
    _logger.debug(f'Model type flags: {", ".join(_flags)}')

    seed = np.random.randint(1e6)
    ds = image_dataset_from_directory(
        IMAGE_DIR,
        label_mode='categorical',
        color_mode='rgb',
        batch_size=None,
        image_size=IMAGE_SIZE,
        shuffle=False,
        crop_to_aspect_ratio=True,
        seed=seed,
    )
    num_files = len(ds.file_paths)
    buf_size = int(FLAGS.shuffle_buffer_size * num_files) if FLAGS.shuffle_buffer_size < 1 \
        else int(FLAGS.shuffle_buffer_size)
    return ImageDataset(
        ds.shuffle(buffer_size=buf_size, reshuffle_each_iteration=False, seed=seed) \
            .map(partial(_preprocess, seed=seed)) \
            .batch(BATCH_SIZE) \
            .prefetch(tf.data.AUTOTUNE),
        num_files,
        ds.class_names.copy()
    )

def split_dataset(ids: ImageDataset, split_by: float=0.8) -> Tuple[tf.data.Dataset, tf.data.Dataset]:
    """ Split dataset into train/test TF subsets """
    train_size = int(ids.num_files * split_by / BATCH_SIZE)
    train_ds = ids.tfds.take(train_size)
    test_ds = ids.tfds.skip(train_size)
    return (train_ds, test_ds)

def augment_dataset(tfds: tf.data.Dataset) -> tf.data.Dataset:
    """ Applies multiple augmentatiions to the TF dataset """
    counter = tf.data.experimental.Counter()
    tfds_with_seed = tf.data.Dataset.zip((tfds, (counter, counter)))
    augmented_dataset = tfds_with_seed.map(_augment_images)
    augmented_dataset = augmented_dataset.map(_rotate_images)
    if FLAGS.zoom:
        augmented_dataset = augmented_dataset.map(_zoom_images)
    augmented_dataset = augmented_dataset.map(_drop_seed)
    return augmented_dataset

def get_dataset_samples(tfds: tf.data.Dataset, num_batches: int = 1) -> tf.data.Dataset:
    """ Generate given number of samples from dataset """
    shape = tfds.cardinality().numpy()
    if shape <= num_batches:
        return tfds

    sample_idx = np.random.randint(shape-num_batches)
    return tfds.take(sample_idx).skip(sample_idx-num_batches)

def show_samples(tfds: tf.data.Dataset, class_names: Iterable[str], num_samples: int = 9):
    """ Show random image samples from TF dataset """
    plt.figure(figsize=(8, 8))
    cmap = 'gray' if FLAGS.gray or FLAGS.edges else None
    for images, labels in get_dataset_samples(tfds):
        for i in range(num_samples):
            _ = plt.subplot(int(num_samples/3), int(num_samples/3), i + 1)
            plt.imshow(restore_processed_image(images[i].numpy()), cmap=cmap)
            label = np.argmax(labels[i])
            plt.title(class_names[label])
            plt.axis('off')
    plt.show()

def predict_image(
    model: tf.keras.Model,
    image: np.ndarray,
    class_names: Iterable[str],
    return_image: bool = False) -> Tuple[str, float]:
    """ Run a pretrained model prediction on an image """

    resized_image = tf.keras.preprocessing.image.smart_resize(image, IMAGE_SIZE)
    prepared_image, _ = _preprocess(tf.expand_dims(resized_image, 0))

    prediction = model.predict(prepared_image, verbose=0)
    most_likely = np.argmax(prediction)
    label = class_names[most_likely]
    prob = prediction[0][most_likely]

    if not return_image:
        return label, prob

    return label, prob, prepared_image[0]

def predict_image_probs(
    model: tf.keras.Model,
    image: np.ndarray,
    class_names: Iterable[str]) -> Iterable[Tuple[str, float]]:
    """ Run a pretrained model prediction on an image returning all detections and probabilities """

    resized_image = tf.keras.preprocessing.image.smart_resize(image, IMAGE_SIZE)
    prepared_image, _ = _preprocess(tf.expand_dims(resized_image, 0))

    prediction = model.predict(prepared_image, verbose=0)
    probs = []
    for prob, pred in sorted(zip(prediction[0], range(len(prediction[0]))), key=lambda x: x[0], reverse=True):
        label = class_names[pred]
        probs.append((label, prob))
    return probs

def predict_dataset(model: tf.keras.Model, tfds: tf.data.Dataset) -> Tuple[tf.Tensor, tf.Tensor]:
    """ Get predictions for TF dataset """

    true_labels = [tf.argmax(labels) for _, labels in tfds.unbatch()]
    true_labels = tf.stack(true_labels, axis=0)

    predicted_labels = model.predict(tfds)
    predicted_labels = tf.concat(predicted_labels, axis=0)
    predicted_labels = tf.argmax(predicted_labels, axis=1)

    return true_labels, predicted_labels

def _pad_to_bounding_box_color(image, offset_height, offset_width,
                                 target_height, target_width, pad_color):
  """ Internal - TF `pad_to_bounding_box()` function adjusted to accept padding color """
  from tensorflow.python.framework import ops
  from tensorflow.python.ops import array_ops
  from tensorflow.python.ops.image_ops_impl import _ImageDimensions, _is_tensor

  with ops.name_scope(None, 'pad_to_bounding_box_white', [image]):
    image = ops.convert_to_tensor(image, name='image')

    is_batch = True
    image_shape = image.get_shape()
    if image_shape.ndims == 3:
      is_batch = False
      image = array_ops.expand_dims(image, 0)
    elif image_shape.ndims is None:
      is_batch = False
      image = array_ops.expand_dims(image, 0)
      image.set_shape([None] * 4)
    elif image_shape.ndims != 4:
      raise ValueError(
          '\'image\' (shape %s) must have either 3 or 4 dimensions.' %
          image_shape)

    batch, height, width, depth = _ImageDimensions(image, rank=4)
    after_padding_width = target_width - offset_width - width
    after_padding_height = target_height - offset_height - height

    # Do not pad on the depth dimensions.
    paddings = array_ops.reshape(
        array_ops.stack([
            0, 0, offset_height, after_padding_height, offset_width,
            after_padding_width, 0, 0
        ]), [4, 2])
    padded = array_ops.pad(image, paddings, constant_values=pad_color)

    padded_shape = [
        None if _is_tensor(i) else i
        for i in [batch, target_height, target_width, depth]
    ]
    padded.set_shape(padded_shape)

    if not is_batch:
      padded = array_ops.squeeze(padded, axis=[0])

    return padded

def zoom_image(image: Union[np.ndarray, tf.Tensor], zoom: float,
               pad_color: int = 0) -> Union[np.ndarray, tf.Tensor]:
    """ Image zoom with tensorflow functions.

    Arguments:
        image:  Either OpenCV (`np.ndarray`) or Tensorflow (`tf.Tensor`) image array.
            The image must be of supported `dtype` (`uint8` or `float`).
        zoom:   Zoom factor, float value greater than 0.
            If it is less than 1, then image is zoomed in (become larger),
            and if greater - zoomed out (become smaller) (this is oppozite to OpenCV's zoom behaviour).
        pad_color:  padding color (0 - black, 255 - white)

    Returns:
        zoomed image of the same type and size as original
    """

    if zoom < 0.0:
        raise ValueError(f'Zoom must be greater than zero')

    if zoom == 1.0:
        # No zoom
        return image

    is_tensor = isinstance(image, tf.Tensor)
    if not is_tensor:
        shape = image.shape
        image_size = np.array(shape[:2])
    else:
        shape = image.get_shape()
        image_size = np.array(shape[:2]) if shape.ndims < 4 else np.array(shape[1:3])

    if zoom < 1.0:
        # Zoom in (result is larger)
        crop_image = tf.image.central_crop(image, zoom)
        zoomed_image = tf.image.resize(crop_image, image_size, preserve_aspect_ratio=False)
    else:
        # Zoom out (result is smaller)
        zoom_size = (zoom * image_size).astype('int')
        offset_height, offset_width = (zoom_size[0]-image_size[0]) // 2, (zoom_size[1]-image_size[1]) // 2
        target_height, target_width = zoom_size[0], zoom_size[1]
        resized_image = _pad_to_bounding_box_color(image, offset_height, offset_width, target_height, target_width, pad_color)
        zoomed_image = tf.image.resize(resized_image, image_size, preserve_aspect_ratio=False)

    zoomed_image = tf.cast(zoomed_image, image.dtype)
    return zoomed_image.numpy() if not is_tensor and isinstance(zoomed_image, tf.Tensor) else zoomed_image

def predict_image_files_zoom(
    model: tf.keras.Model,
    file_names: Iterable[str],
    class_names: Iterable[str],
    true_labels: Iterable[str] = None,
    zoom_levels: Iterable[float] = None,
    show_processed_image: bool = False):
    """ Run a pretrained model prediction on multiple image files with optional zooming and display the result """

    cmap = 'gray' if FLAGS.gray or FLAGS.edges else None
    for file_name, true_label in zip_longest(file_names, true_labels or []):
        image = tf.image.decode_image(tf.io.read_file(file_name), channels=3)

        for zoom in zoom_levels or [1.0]:
            zoomed_image = zoom_image(image, zoom, 255)
            zoomed_image = tf.cast(zoomed_image, tf.uint8)

            predicted_label, predicted_prob, processed_image = predict_image(model, zoomed_image, class_names, return_image=True)

            plt.figure(f'{file_name} @ {zoom}')
            plt.title(f'{true_label or "?"} <- {predicted_label} ({predicted_prob:.2%}) @ {zoom}')
            img_to_show = restore_processed_image(processed_image.numpy()) if show_processed_image else zoomed_image.numpy()
            plt.imshow(img_to_show, cmap=cmap)
            plt.axis('off')

    plt.show()

def predict_image_file(
    model: tf.keras.Model,
    file_name: str,
    class_names: Iterable[str],
    true_label: str = None,
    show_processed_image: bool = False):
    """ Run a pretrained model prediction on an image file and display the result """

    predict_image_files_zoom(model, [file_name], class_names, [true_label], show_processed_image=show_processed_image)

def predict_image_files(
    model: tf.keras.Model,
    file_names: Iterable[str],
    class_names: Iterable[str],
    true_labels: Iterable[str] = None,
    show_processed_image: bool = False):
    """ Run a pretrained model prediction on multiple image files and display the result """

    predict_image_files_zoom(model, file_names, class_names, true_labels, show_processed_image=show_processed_image)

def show_prediction_samples(
    model: tf.keras.Model,
    tfds: tf.data.Dataset,
    class_names: Iterable[str],
    num_samples: int = 9):
    """ Show prediction samples from test dataset """

    plt.figure(figsize=(8, 8))
    cmap = 'gray' if FLAGS.gray or FLAGS.edges else None
    for images, labels in get_dataset_samples(tfds):
        for i in range(min(num_samples, labels.shape[0])):
            _ = plt.subplot(int(num_samples/3), int(num_samples/3), i + 1)
            label = class_names[np.argmax(labels[i])]
            image = images[i].numpy()
            prediction = model.predict(np.array([image]))
            most_likely = np.argmax(prediction)
            predicted_label = class_names[most_likely]
            predicted_prob = prediction[0][most_likely]

            plt.title(f'{label} <- {predicted_label} ({predicted_prob:.2%})')
            plt.imshow(restore_processed_image(image), cmap=cmap)
            plt.axis('off')
    plt.show()

def filter_dataset_by_label(tfds: tf.data.Dataset, class_names: Iterable[str], label: str) -> tf.data.Dataset:
    """ Return a dataset subset containing images of given label only """
    label_index = class_names.index(label)
    return tfds.unbatch().filter(lambda _, label: tf.equal(tf.math.argmax(label), label_index)).batch(BATCH_SIZE)

def fast_get_class_names() -> Iterable[str]:
    """ Faster way of getting image class names """
    return [f for f in os.listdir(IMAGE_DIR) if os.path.isdir(os.path.join(IMAGE_DIR, f))]


import asyncio
import unittest
from unittest.mock import Mock

from Crypto.PublicKey import RSA
from google.protobuf import text_format

from bfcp.messages import TrafficManager
from protos.bfcp_pb2 import BouncyMessage, NodeTableEntry

class TestMessages(unittest.TestCase):
    def test_end_to_end(self):
        loop = asyncio.get_event_loop()
        trust_table_manager = Mock().return_value

        # Set up the nodes in the network
        node1_port = 53251
        node1_rsa_key = RSA.generate(2048)
        node1_manager = TrafficManager(Mock(trust_table_manager=trust_table_manager), node1_rsa_key, loop,
                                       ('127.0.0.1', node1_port))

        node2_port = 53252
        node2_rsa_key = RSA.generate(2048)
        node2_manager = TrafficManager(Mock(trust_table_manager=trust_table_manager), node2_rsa_key, loop,
                                       ('127.0.0.1', node2_port))

        node3_port = 53253
        node3_rsa_key = RSA.generate(2048)
        node3_manager = TrafficManager(Mock(trust_table_manager=trust_table_manager), node3_rsa_key, loop,
                                       ('127.0.0.1', node3_port))

        # Prepare messages for sending
        msg1 = BouncyMessage()
        text_format.Parse(
            '''
            connection_request {
                target_server_address: "test_message_1"
            }
            ''',
            msg1
        )
        msg2 = BouncyMessage()
        text_format.Parse(
            '''
            connection_request {
                target_server_address: "test_message_2"
            }
            ''',
            msg2
        )
        msg3 = BouncyMessage()
        text_format.Parse(
            '''
            connection_request {
                target_server_address: "test_message_3"
            }
            ''',
            msg3
        )
        msg4 = BouncyMessage()
        text_format.Parse(
            '''
            connection_request {
                target_server_address: "test_message_4"
            }
            ''',
            msg4
        )
        msg5 = BouncyMessage()
        text_format.Parse(
            '''
            connection_request {
                target_server_address: "test_message_5"
            }
            ''',
            msg5
        )

        # Prepare the trust manager
        def pub_key_to_address(pub_key):
            node = NodeTableEntry()

            if pub_key == node1_rsa_key.publickey():
                text_format.Parse(
                    '''
                    trust_score: 1.0
                    node {
                        last_known_address: "127.0.0.1",
                        last_port: ''' + str(node1_port) + '''
                    }
                    ''',
                    node
                )
            if pub_key == node2_rsa_key.publickey():
                text_format.Parse(
                    '''
                    trust_score: 1.0
                    node {
                        last_known_address: "127.0.0.1",
                        last_port: ''' + str(node2_port) + '''
                    }
                    ''',
                    node
                )
            if pub_key == node3_rsa_key.publickey():
                text_format.Parse(
                    '''
                    trust_score: 1.0
                    node {
                        last_known_address: "127.0.0.1",
                        last_port: ''' + str(node3_port) + '''
                    }
                    ''',
                    node
                )
            return node

        trust_table_manager.get_node_by_pubkey.side_effect = pub_key_to_address

        # We will hold the received messages here
        node1_messages = []
        node2_messages = []
        node3_messages = []

        async def scenario():
            nonlocal node1_messages
            nonlocal node2_messages
            nonlocal node3_messages

            await node1_manager.send(msg1, node2_rsa_key.publickey())

            await node2_manager.send(msg2, node3_rsa_key.publickey())
            node3_messages.extend(await node3_manager.new_messages())

            await node3_manager.send(msg3, node1_rsa_key.publickey())
            node1_messages.extend(await node1_manager.new_messages())

            await node1_manager.send(msg4, node3_rsa_key.publickey())
            await node2_manager.send(msg5, node1_rsa_key.publickey())

            node1_messages.extend(await node1_manager.new_messages())
            node2_messages.extend(await node2_manager.new_messages())
            node3_messages.extend(await node3_manager.new_messages())

        loop.run_until_complete(scenario())

        self.assertCountEqual(
            node1_messages,
            [(node3_rsa_key.publickey(), msg3), (node2_rsa_key.publickey(), msg5)]
        )
        self.assertCountEqual(
            node2_messages,
            [(node1_rsa_key.publickey(), msg1)]
        )
        self.assertCountEqual(
            node3_messages,
            [(node2_rsa_key.publickey(), msg2), (node1_rsa_key.publickey(), msg4)]
        )

        node1_manager.close()
        node2_manager.close()
        node3_manager.close()

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