#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# @Abstract: 风险模型类文件
# @Filename: RIskModel
# @Date:   : 2018-06-21 20:03
# @Author  : YuJun
# @Email   : yujun_mail@163.com

from src.util.utils import Utils, SecuTradingStatus
from src.riskmodel.riskfactors.Size import Size
from src.riskmodel.riskfactors.Beta import Beta
from src.riskmodel.riskfactors.Momentum import Momentum
from src.riskmodel.riskfactors.ResVolatility import ResVolatility
from src.riskmodel.riskfactors.NonlinearSize import NonlinearSize
from src.riskmodel.riskfactors.Value import Value
from src.riskmodel.riskfactors.Liquidity import Liquidity
from src.riskmodel.riskfactors.EarningsYield import EarningsYield
from src.riskmodel.riskfactors.Growth import Growth
from src.riskmodel.riskfactors.Leverage import Leverage
import pandas as pd
import numpy as np
import src.settings as SETTINGS
import src.riskmodel.cons as riskmodel_ct
import src.riskmodel.riskfactors.cons as riskfactor_ct
import os
import logging
import cvxpy as cvx
from src.util.algo import Algo
import datetime
import math
from src.portfolio.portfolio import CWeightHolding, CPortHolding, load_holding_data

logging.basicConfig(level=logging.INFO,
                    format='%(asctime)s - %(filename)s[line:%(lineno)d] - %(levelname)s: %(message)s')

class Barra(object):
    """Barra风险模型基类"""

    def calc_factorloading(self, start_date, end_date=None, multi_prc=False):
        """
        计算风险因子的因子载荷
        Parameters:
        --------
        :param start_date: datetime-like, str
            计算开始日期, 格式: YYYY-MM-DD
        :param end_date: datetime-like, str
            计算结束日期, 格式: YYYY-MM-DD
        :param multi_prc: bool
            是否并行计算, 默认为False
        :return: None
        """
        # 读取交易日序列
        start_date = Utils.to_date(start_date)
        if end_date is not None:
            end_date = Utils.to_date(end_date)
            trading_days_series = Utils.get_trading_days(start=start_date, end=end_date)
        else:
            trading_days_series = Utils.get_trading_days(start=start_date, ndays=1)
        # 遍历交易日序列, 计算风险因子的因子载荷
        for calc_date in trading_days_series:
            # Size.calc_factor_loading(start_date=calc_date, end_date=None, month_end=False, save=True, multi_proc=multi_prc)
            # Beta.calc_factor_loading(start_date=calc_date, end_date=None, month_end=False, save=True, multi_proc=multi_prc)
            # Momentum.calc_factor_loading(start_date=calc_date, end_date=None, month_end=False, save=True, multi_proc=multi_prc)
            # ResVolatility.calc_factor_loading(start_date=calc_date, end_date=None, month_end=False, save=True, multi_proc=multi_prc)
            # NonlinearSize.calc_factor_loading(start_date=calc_date, end_date=None, month_end=False, save=True, multi_proc=multi_prc)
            # Value.calc_factor_loading(start_date=calc_date, end_date=None, month_end=False, save=True, multi_proc=multi_prc)
            # Liquidity.calc_factor_loading(start_date=calc_date, end_date=None, month_end=False, save=True, multi_proc=multi_prc)
            # EarningsYield.calc_factor_loading(start_date=calc_date, end_date=None, month_end=False, save=True, multi_proc=multi_prc)
            # Growth.calc_factor_loading(start_date=calc_date, end_date=None, month_end=False, save=True, multi_proc=multi_prc)
            # Leverage.calc_factor_loading(start_date=calc_date, end_date=None, month_end=False, save=True, multi_proc=multi_prc)
            #
            # self._calc_secu_dailyret(calc_date)
            self._calc_IndFactorloading(calc_date)
            self._calc_StyleFactorloading(calc_date)

    def estimate_factor_ret(self, start_date, end_date=None):
        """
        估计风险因子的因子报酬
        Parameters:
        --------
        :param start_date: datetime-like, str
            开始日期, e.g: YYYY-MM-DD, YYYYMMDD
        :param end_date: datetime-like, str
            结束日期, e.g: YYYY-MM-DD, YYYYMMDD
        :return: pd.series
        --------
            风险因子的因子报酬
            0. date: 日期
            1. market: country factor
            2...31. industry factor
            32...41. style factor
        """
        start_date = Utils.to_date(start_date)
        if end_date is not None:
            end_date = Utils.to_date(end_date)
            trading_days_series = Utils.get_trading_days(start=start_date, end=end_date)
        else:
            trading_days_series = Utils.get_trading_days(end=start_date, ndays=1)

        df_residual_rets = pd.DataFrame()
        for trading_day in trading_days_series:
            logging.info('Estimate risk factor return of {}.'.format(Utils.datetimelike_to_str(trading_day)))
            df_estimator_matrix = pd.DataFrame()
            # 读取个股流通市值权重
            cap_weight = self._get_cap_weight(trading_day)
            cap_weight = cap_weight.set_index('code')
            df_estimator_matrix = cap_weight
            # 读取个股下一个交易日的日收益率序列
            next_trading_day = Utils.get_next_n_day(start=trading_day, ndays=1)
            daily_ret = self._get_secu_dailyret(next_trading_day)
            daily_ret = daily_ret.set_index('code')
            df_estimator_matrix = pd.merge(left=df_estimator_matrix, right=daily_ret, how='inner', left_index=True, right_index=True)
            # 读取行业因子载荷矩阵
            ind_factorloading = self._get_IndFactorloading_matrix(trading_day)
            ind_factorloading = ind_factorloading.set_index('code')
            df_estimator_matrix = pd.merge(left=df_estimator_matrix, right=ind_factorloading, how='inner', left_index=True, right_index=True)
            # 读取风格因子载荷矩阵
            style_factorloading = self._get_StyleFactorloading_matrix(trading_day)
            style_factorloading = style_factorloading.set_index('code')
            df_estimator_matrix = pd.merge(left=df_estimator_matrix, right=style_factorloading, how='inner', left_index=True, right_index=True)

            # 优化计算因子报酬
            ind_factor_labels = ind_factorloading.columns.tolist()
            style_factor_labels = style_factorloading.columns.tolist()
            cap_weight = np.asarray(df_estimator_matrix[cap_weight.columns.tolist()])
            daily_ret = np.asarray(df_estimator_matrix[daily_ret.columns.tolist()])
            ind_factorloading = np.asarray(df_estimator_matrix[ind_factor_labels])
            style_factorloading = np.asarray(df_estimator_matrix[style_factor_labels])

            I = ind_factorloading.shape[1]      # number of industry factors
            S = style_factorloading.shape[1]    # number of style factors
            f_c = cvx.Variable()                # return of country factor
            f_i = cvx.Variable((I, 1))          # return of industry factors
            f_s = cvx.Variable((S, 1))          # return of style factors
            objective = cvx.Minimize(cap_weight.T * (daily_ret - f_c - ind_factorloading * f_i - style_factorloading * f_s) ** 2)
            constraints = [cvx.matmul(cap_weight.T, ind_factorloading) * f_i == 0]
            prob = cvx.Problem(objective, constraints)
            prob.solve()
            # print('status: ', prob.status)
            # print('optimal value: ', prob.value)
            # print('country factor ret: ', f_c.value)
            # print('industry factor ret:')
            # print(f_i.value)
            # print('stryle factor ret: ')
            # print(f_s.value)
            # 保存风险因子报酬
            if prob.status == cvx.OPTIMAL:
                factor_ret = np.concatenate((f_i.value, f_s.value), axis=0)
                factor_ret = np.insert(factor_ret, 0, f_c.value)
                # df_factor_ret = pd.DataFrame(factor_ret.reshape(1, len(factor_ret)), columns=['market'] + ind_factor_labels + style_factor_labels)
                ser_factor_ret = pd.Series(factor_ret, index=['market'] + ind_factor_labels + style_factor_labels)
                self._save_factor_ret(trading_day, ser_factor_ret, 'a')
                # print(ser_factor_ret)

                # 计算残差值
                arr_daily_ret = np.asarray(df_estimator_matrix['ret']).reshape((len(df_estimator_matrix), 1))   # 个股下一交易日收益率序列

                df_estimator_matrix.drop(columns=['weight', 'ret'], inplace=True)
                df_estimator_matrix.insert(loc=0, column='market', value=np.ones(len(df_estimator_matrix)))
                arr_factor_loading = np.asarray(df_estimator_matrix)    # 个股风险因子载荷矩阵

                arr_factor_ret = np.asarray(ser_factor_ret).reshape((len(ser_factor_ret), 1))   # 风险因子报酬向量

                arr_residual = np.around(arr_daily_ret - np.dot(arr_factor_loading, arr_factor_ret),8)

                # ser_residual = pd.Series(arr_residual.flatten(), index=df_estimator_matrix.index,name='residual')
                # ser_residual['date'] = datetime.datetime(trading_day.year, trading_day.month, trading_day.day)
                df_residual = pd.DataFrame(arr_residual.reshape((1, len(arr_residual))), index=[trading_day], columns=df_estimator_matrix.index)
                df_residual_rets = df_residual_rets.append(df_residual)
                # self._save_residual_ret(trading_day, ser_residual, 'a')

            else:
                logging.info('\033[1;31;40m{}优化计算风险因子报酬无解.\033[0m'.format(Utils.datetimelike_to_str(trading_day, dash=True)))

        # df_residual_rets.index.name = 'date'
        self._save_residual_ret(start_date, df_residual_rets, 'a')

    def calc_factor_covmat(self, start_date, end_date=None, calc_mode='cov'):
        """
        计算风险因子协方差矩阵
        Parameters:
        --------
        :param start_date: datetime-like, str
            开始日期, e.g: YYYY-MM-DD, YYYYMMDD
        :param end_date: datetime-like, str
            结束日期, e.g: YYYY-MM-DD, YYYYMMDD, 默认为None
        :param calc_mode: str
            计算模式,
            'cov'=计算因子朴素协方差矩阵和最终协方差矩阵, 并保存
            'naive'=只计算因子朴素协方差矩阵, 并保存
        :return:
        """
        # 读取交易日序列
        start_date = Utils.to_date(start_date)
        if end_date is None:
            trading_days_series = Utils.get_trading_days(end=start_date, ndays=1)
        else:
            end_date = Utils.to_date(end_date)
            trading_days_series = Utils.get_trading_days(start=start_date, end=end_date)

        for calc_date in trading_days_series:
            logging.info('Calc factor covmat of {}.'.format(Utils.datetimelike_to_str(calc_date, dash=True)))
            # 计算风险因子朴素协方差矩阵, 并保存
            factor_names, naive_covmat = self._naive_factor_covmat(calc_date)
            self._save_factor_covmat(calc_date, naive_covmat, factor_names, 'naive')
            # print('factor names:')
            # print(factor_names)
            # print('naive covmat :')
            # print(naive_covmat)
            if calc_mode == 'cov':
                # 对风险因子朴素协方差矩阵进行Newey_West调整
                nw_adj_covmat = self._covmat_NeweyWest_adj(naive_covmat, calc_date)
                # print('Newey_West adjusted covmat:')
                # print(nw_adj_covmat)
                # 进行波动率偏误调整
                lambda_F = self._vol_RegimeAdj_multiplier(calc_date)
                riskfactor_covmat = lambda_F ** 2 * nw_adj_covmat
                # 保存风险因子协方差矩阵
                self._save_factor_covmat(calc_date, riskfactor_covmat, factor_names, 'cov')

    def calc_spec_varmat(self, start_date, end_date=None, calc_mode='var'):
        """
        计算特质收益率方差矩阵
        Parameters:
        --------
        :param start_date: datetime-like, str
            开始日期, e.g: YYYY-MM-DD, YYYYMMDD
        :param end_date: datetime-like, str
            结束日期, e.g: YYYY-MM-DD, YYYYMMDD
        :param calc_mode: str
            计算模式
            'var'=计算特质收益率朴素方差及最终特质收益率方差矩阵数据, 并保存
            'naive'=只计算特质收益率朴素方差矩阵数据, 并保存
        :return:
        """
        # 读取交易日序列
        start_date = Utils.to_date(start_date)
        if end_date is None:
            trading_days_series = Utils.get_trading_days(end=start_date, ndays=1)
        else:
            end_date = Utils.to_date(end_date)
            trading_days_series = Utils.get_trading_days(start=start_date, end=end_date)

        for calc_date in trading_days_series:
            logging.info('Calc specific risk variance matrix of {}.'.format(Utils.datetimelike_to_str(calc_date, dash=True)))
            # 计算特质收益率朴素方差数据, 并保存
            naive_specvar = self._naive_spec_var(calc_date)
            self._save_spec_var(calc_date, naive_specvar, 'naive')
            if calc_mode == 'var':
                # 对特质收益率朴素方差矩阵进行Newey_West调整
                nw_spec_var = self._specvar_NeweyWest_adj(naive_specvar, calc_date)
                # 进行波动率偏误调整

                # 保存特质波动率矩阵数据
                self._save_spec_var(calc_date, nw_spec_var, 'var')

    def calc_stocks_covmat(self, calc_date):
        """
        计算个股协方差矩阵
        Parameters:
        --------
        :param calc_date: datetime-like, str
            计算日期, e.g: YYYY-MM-DD, YYYYMMDD
        :return: tuple(list, np.array)
        --------
            返回一个元组, 第一个元素为个股代码list, 第二个元素为个股协方差矩阵
        """
        calc_date = Utils.to_date(calc_date)
        # 取得个股风险因子载荷数据
        df_factorloading = self._get_factorloading_matrix(calc_date)
        # 取得个股特质波动率方差数据
        df_specvar = self._get_spec_var('var', calc_date)[Utils.datetimelike_to_str(calc_date, dash=False)]

        tmp = pd.merge(left=df_factorloading, right=df_specvar, how='inner', on='code')
        arr_factorloading = np.array(tmp.loc[:, riskfactor_ct.RISK_FACTORS])    # 个股因子载荷矩阵, 记为X
        arr_specvar = np.diagflat(tmp['spec_var'].tolist())                     # 个股特质波动率方差矩阵, 记为sigma

        # 取得风险因子协方差矩阵数据, 记为F
        arr_factor_covmat = self._get_factor_covmat('cov', calc_date, factors=riskfactor_ct.RISK_FACTORS)[Utils.datetimelike_to_str(calc_date, dash=False)]

        # 个股协方差矩阵V = XFX'+sigma
        V = np.linalg.multi_dot([arr_factorloading, arr_factor_covmat, arr_factorloading.T]) + arr_specvar

        return tmp['code'].tolist(), V

    def _save_factor_ret(self, date, data, save_type='a'):
        """
        保存风险因子报酬数据
        Parameters:
        --------
        :param date: datetime-like, str
            日期, e.g: YYYY-MM-DD, YYYYMMDD
        :param data: pd.Series
            因子报酬数据
        :param save_type: str
            保存方式, 'a'=新增, 'w'=覆盖, 默认为'a'
        :return:
        """
        date = Utils.to_date(date)
        factor_ret_path = os.path.join(SETTINGS.FACTOR_DB_PATH, riskmodel_ct.FACTOR_RETURN_PATH)

        data.name = date
        if save_type == 'a':
            df_factor_ret = self._get_factor_ret()
            if df_factor_ret is None:
                df_factor_ret = pd.DataFrame().append(data, ignore_index=True)
                df_factor_ret.index = [date]
                df_factor_ret.index.name = 'date'
                df_factor_ret.to_csv(factor_ret_path)
            else:
                df_factor_ret.loc[date] = data
                df_factor_ret.sort_index(inplace=True)
                df_factor_ret.to_csv(factor_ret_path)
        elif save_type == 'w':
            data.to_csv(factor_ret_path, index=True)

    def _save_residual_ret(self, date, data, save_type='a'):
        """
        保存个股风险模型中的残差收益率(特质收益率)数据
        Parameters:
        --------
        :param date: datetime-like, str
            日期, e.g: YYYY-MM-DD, YYYYMMDD
        :param data: pd.Series, pd.DataFrame
            个股残差收益率数据
        :param save_type: str
            保存方式, 'a'=新增, 'w'=覆盖, 默认为'a'
        :return:
        """
        date = Utils.to_date(date)
        residual_ret_path =os.path.join(SETTINGS.FACTOR_DB_PATH, riskmodel_ct.RISKMODEL_RESIDUAL_PATH)

        data.name = date
        if save_type == 'a':
            df_residual_ret = self._get_residual_ret()
            if isinstance(data, pd.Series):
                if df_residual_ret is None:
                    df_residual_ret = pd.DataFrame().append(data, ignore_index=True)
                    df_residual_ret.index = [date]
                    df_residual_ret.index.name = 'date'
                    df_residual_ret.to_csv(residual_ret_path)
                else:
                    data.name = date
                    if date in df_residual_ret.index:
                        df_residual_ret.drop(index=date, inplace=True)
                    df_residual_ret = df_residual_ret.append(data, ignore_index=False)
                    df_residual_ret.sort_index(inplace=True)
                    df_residual_ret.to_csv(residual_ret_path)
            elif isinstance(data, pd.DataFrame):
                if df_residual_ret is None:
                    df_residual_ret = data
                    df_residual_ret.to_csv(residual_ret_path)
                else:
                    dates = [d for d in data.index if d in df_residual_ret.index]
                    if len(dates) > 0:
                        df_residual_ret.drop(index=dates, inplace=True)
                    df_residual_ret = df_residual_ret.append(data)
                    df_residual_ret.sort_index(inplace=True)
                    if df_residual_ret.index.name != 'date':
                        df_residual_ret.index.name = 'date'
                    df_residual_ret.to_csv(residual_ret_path)
            else:
                raise TypeError("参数data类型必须为pd.Series或pd.DataFrame.")
        elif save_type == 'w':
            data.to_csv(residual_ret_path, index=True)

    def _get_factor_ret(self, start=None, end=None, ndays=None):
        """
        读取风险因子报酬数据
        Parameters:
        --------
        :param start: datetime-like, str
            开始日期, e.g: YYYY-MM-DD or YYYYMMDD
        :param end: datetime-like, str
            结束日期, e.g: YYYY-MM-DD or YYYYMMDD
        :param ndays: int
            天数
        :return: pd.DataFrame
        --------
            返回风险因子报酬数据
            0. date: 日期(index)
            1. market: country factor
            2...31. industry factor
            32...41. style factor

            根据提供的参数不同, 返回不同数据:
            1. start和end都不为None: 返回开始、结束日期区间内数据
            2. start为None, end和ndays不为None: 返回截止结束日期(含)的前ndays天数据
            3. end为None, start和ndays不为None: 返回自从开始日期(含)的后ndays天数据
            4. start和ndays为None, end不为None: 返回截止结束日期(含)的所有数据
            5. end和ndays为None, start不为None: 返回自从开始日期(含)开始的所有数据
            6. start和end都为None, 返回所有数据
        """
        factor_ret_path = os.path.join(SETTINGS.FACTOR_DB_PATH, riskmodel_ct.FACTOR_RETURN_PATH)
        if not os.path.isfile(factor_ret_path):
            return None
        df_factorret = pd.read_csv(factor_ret_path, header=0, index_col=0, parse_dates=[0])
        if df_factorret.empty:
            return None
        if (start is not None) and (end is not None):
            start = Utils.to_date(start)
            end = Utils.to_date(end)
            return df_factorret.loc[start: end]
        elif end is not None:
            end = Utils.to_date(end)
            if ndays is not None:
                return df_factorret.loc[: end].tail(ndays)
            else:
                return df_factorret.loc[: end]
        elif start is not None:
            start = Utils.to_date(start)
            if ndays is not None:
                return df_factorret.loc[start:].head(ndays)
            else:
                return df_factorret.loc[start:]
        else:
            return df_factorret

    def _get_residual_ret(self, start=None, end=None, ndays=None):
        """
        读取风险模型的残差收益率数据
        Parameters:
        --------
        :param start: datetime-like, str
            开始日期, e.g: YYYY-MM-DD, YYYYMMDD
        :param end: datetime-like, str
            结束日期, e.g: YYYY-MM-DD, YYYYMMDD
        :param ndays: int
            天数
        :return: pd.DataFrame
        --------
            返回风险模型的残差收益率数据

            根据提供的参数不同, 返回不同数据:
            1. start和end都不为None: 返回开始、结束日期区间内数据
            2. start为None, end和ndays不为None: 返回截止结束日期(含)的前ndays天数据
            3. end为None, start和ndays不为None: 返回自从开始日期(含)的后ndays天数据
            4. start和ndays为None, end不为None: 返回截止结束日期(含)的所有数据
            5. end和ndays为None, start不为None: 返回自从开始日期(含)开始的所有数据
            6. start和end都为None, 返回所有数据
        """
        residual_ret_path = os.path.join(SETTINGS.FACTOR_DB_PATH, riskmodel_ct.RISKMODEL_RESIDUAL_PATH)
        if not os.path.isfile(residual_ret_path):
            return None
        df_residual_ret = pd.read_csv(residual_ret_path, header=0, index_col=0, parse_dates=[0])
        if df_residual_ret.empty:
            return None
        if (start is not None) and (end is not None):
            start = Utils.to_date(start)
            end = Utils.to_date(end)
            df_residual_ret = df_residual_ret[start: end]
        elif end is not None:
            end = Utils.to_date(end)
            if ndays is not None:
                df_residual_ret = df_residual_ret.loc[: end].tail(ndays)
            else:
                df_residual_ret = df_residual_ret.loc[: end]
        elif start is not None:
            start = Utils.to_date(start)
            if ndays is not None:
                df_residual_ret = df_residual_ret.loc[start:].head(ndays)
            else:
                df_residual_ret = df_residual_ret.loc[start:]
        else:
            return df_residual_ret

        if df_residual_ret.empty:
            return None
        else:
            return df_residual_ret

    def _calc_secu_dailyret(self, start_date, end_date=None):
        """
        计算个股日收益率数据
        Parameters:
        --------
        :param start_date: datetime-like, str
            计算开始日期, e.g: YYYY-MM-DD, YYYYMMDD
        :param end_date: datetime-like, str
            计算结束日期
        :return:
            计算全体个股日收益率数据, 保存至数据库
        """
        # 读取交易日序列
        start_date = Utils.to_date(start_date)
        if end_date is not None:
            end_date = Utils.to_date(end_date)
            trading_days_series = Utils.get_trading_days(start=start_date, end=end_date)
        else:
            trading_days_series = Utils.get_trading_days(end=start_date, ndays=1)
        # 遍历交易日序列, 计算每个交易日的所有上市交易个股的日收益率数据
        for calc_date in trading_days_series:
            logging.info('[{}] Calc daily return data.'.format(Utils.datetimelike_to_str(calc_date)))
            df_dailyret = pd.DataFrame()
            # 读取在calc_date上市交易的A股代码
            stock_basics = Utils.get_stock_basics(calc_date)
            # 遍历个股, 计算日收益率（剔除停牌的个股）
            for _, stock_info in stock_basics.iterrows():
                logging.debug('[{}] Calc daily ret of {}.'.format(Utils.datetimelike_to_str(calc_date), stock_info.symbol))
                # 剔除停牌个股
                trading_status = Utils.trading_status(stock_info['symbol'], calc_date)
                if trading_status == SecuTradingStatus.Suspend:
                    continue
                # 计算日收益率
                daily_ret = Utils.calc_interval_ret(stock_info['symbol'], calc_date, calc_date)
                if daily_ret is None:
                    continue
                df_dailyret = df_dailyret.append(pd.Series([stock_info.symbol, calc_date, round(daily_ret, 6)], index=['code', 'date', 'ret']), ignore_index=True)
            # 保存每个交易日的收益率数据
            # dailyret_path = os.path.join(SETTINGS.FACTOR_DB_PATH, riskmodel_ct.DAILY_RET_PATH, '{}.csv'.format(Utils.datetimelike_to_str(calc_date, dash=False)))
            # df_dailyret.to_csv(dailyret_path, index=False, encoding='utf-8')

            dailyret_path = os.path.join(SETTINGS.FACTOR_DB_PATH, riskmodel_ct.DAILY_RET_PATH)
            Utils.factor_loading_persistent(dailyret_path, Utils.datetimelike_to_str(calc_date, dash=False), df_dailyret)

    def _calc_IndFactorloading(self, date):
        """
        计算风险模型行业因子载荷矩阵, 保存至数据库
        Parameters:
        --------
        :param date: datetime-like, str
            计算日期
        :return:
        """
        logging.info('[{}] Calc industry factor loading matrix.'.format(Utils.datetimelike_to_str(date)))
        # 读取指定日期的行业分类数据
        df_IndClassify_data = Utils.get_industry_classify(date)
        # 构造行业因子载荷矩阵, 并保存至数据库
        if df_IndClassify_data is not None:
            df_IndClassify_data = df_IndClassify_data.set_index('id')
            df_IndClassify_data.index.name = 'code'
            df_IndFactorloading = pd.get_dummies(df_IndClassify_data['ind_code'])
            df_IndFactorloading.reset_index(inplace=True)
            indfactorloading_path = os.path.join(SETTINGS.FACTOR_DB_PATH, riskmodel_ct.INDUSTRY_FACTORLOADING_PATH)
            Utils.factor_loading_persistent(indfactorloading_path, Utils.datetimelike_to_str(date, dash=False), df_IndFactorloading)
            # df_IndFactorloading.to_csv(os.path.join(SETTINGS.FACTOR_DB_PATH, riskmodel_ct.INDUSTRY_FACTORLOADING_PATH, 'ind_factorloading_{}.csv'.format(Utils.datetimelike_to_str(date, dash=False))))

    def _calc_StyleFactorloading(self, date):
        """
        计算风险模型中风格因子载荷矩阵, 保存至数据库
        Parameters:
        --------
        :param date: datetime-like, str
            计算日期
        :return:
        """
        logging.info('[{}] Calc style factor loading matrix.'.format(Utils.datetimelike_to_str(date)))
        df_stylefactorloading_matrix = pd.DataFrame()
        for risk_factor in riskfactor_ct.STYLE_RISK_FACTORS:
            factorloading_path = os.path.join(SETTINGS.FACTOR_DB_PATH, eval('riskfactor_ct.%s_CT' % risk_factor.upper())['db_file'])
            df_factor_loading = Utils.read_factor_loading(factorloading_path, Utils.datetimelike_to_str(date, dash=False))
            df_factor_loading.drop(columns='date', inplace=True)
            df_factor_loading.rename(index=str, columns={'factorvalue': risk_factor}, inplace=True)
            if df_stylefactorloading_matrix.empty:
                df_stylefactorloading_matrix = df_factor_loading
            else:
                df_stylefactorloading_matrix = pd.merge(left=df_stylefactorloading_matrix, right=df_factor_loading, how='inner', on='id')
        if not df_stylefactorloading_matrix.empty:
            df_stylefactorloading_matrix.rename(index=str, columns={'id': 'code'}, inplace=True)

        stylefactorloading_path = os.path.join(SETTINGS.FACTOR_DB_PATH, riskmodel_ct.STYLE_FACTORLOADING_PATH)
        Utils.factor_loading_persistent(stylefactorloading_path, Utils.datetimelike_to_str(date, dash=False), df_stylefactorloading_matrix)

    def _get_cap_weight(self, date):
        """
        读取指定日期上市个股的流通市值权重, 剔除停牌个股
        Parameters:
        --------
        :param date: datetime-like, str
            计算日期, e.g: YYYY-MM-DD, YYYYMMDD
        :return: pd.DataFrame, 个股流通市值权重数据
        --------
            0. code: 个股代码
            1. weight: 流通市值权重
            计算失败, 返回None
        """
        date = Utils.to_date(date)
        # 读取个股流通市值数据
        cap_data_path = os.path.join(SETTINGS.FACTOR_DB_PATH, riskfactor_ct.LNCAP_CT.liquidcap_dbfile)
        df_cap_data = Utils.read_factor_loading(cap_data_path, Utils.datetimelike_to_str(date, dash=False))
        # 计算个股流通市值权重
        sum_cap = df_cap_data['factorvalue'].sum()
        df_cap_data['weight'] = df_cap_data['factorvalue'] / sum_cap
        # 读取个股停牌信息
        df_suspension_info = Utils.get_suspension_info(date)
        if df_suspension_info is None:
            return None
        # 个股流通市值数据剔除停牌个股
        df_cap_data = df_cap_data[~df_cap_data['id'].isin(df_suspension_info['symbol'])]

        df_cap_data.drop(columns=['date', 'factorvalue'], inplace=True)
        df_cap_data.rename(columns={'id': 'code'}, inplace=True)
        df_cap_data.reset_index(drop=True, inplace=True)
        return df_cap_data

    def _get_secu_dailyret(self, date):
        """
        读取个股日收益率数据向量
        Parameters:
        --------
        :param date: datetime-like, str
            读取日期
        :return: pd.DataFrame
        --------
            0. code: 个股代码
            1. ret: 个股日收益率
            读取失败, 返回None
        """
        secu_dailyret_path = os.path.join(SETTINGS.FACTOR_DB_PATH, riskmodel_ct.DAILY_RET_PATH)
        df_secudailyret = Utils.read_factor_loading(secu_dailyret_path, Utils.datetimelike_to_str(date, dash=False))
        if df_secudailyret.empty:
            return None
        else:
            df_secudailyret = df_secudailyret.drop(columns='date')
            return df_secudailyret

    def _get_IndFactorloading_matrix(self, date):
        """
        读取行业因子载荷矩阵
        Parameters:
        --------
        :param date: datetime-like, str
            读取日期
        :return: pd.DataFrame
        --------
            0. code: 个股代码
            1...30: 行业因子载荷
            读取失败, 返回None
        """
        indfactorloading_path = os.path.join(SETTINGS.FACTOR_DB_PATH, riskmodel_ct.INDUSTRY_FACTORLOADING_PATH)
        df_IndFactorloading = Utils.read_factor_loading(indfactorloading_path, Utils.datetimelike_to_str(date, dash=False))
        if df_IndFactorloading.empty:
            return None
        else:
            return df_IndFactorloading

    def _get_StyleFactorloading_matrix(self, date):
        """
        读取风格因子载荷矩阵
        Parameters:
        --------
        :param date: datetime-like, str
            读取日期
        :return: pd.DataFrame
        --------
            0. code: 个股代码
            1...10: 风格因子载荷
            读取失败, 返回None
        """
        stylefactorloading_path = os.path.join(SETTINGS.FACTOR_DB_PATH, riskmodel_ct.STYLE_FACTORLOADING_PATH)
        df_StyleFactorloading = Utils.read_factor_loading(stylefactorloading_path, Utils.datetimelike_to_str(date, dash=False))
        if df_StyleFactorloading.empty:
            return None
        else:
            return df_StyleFactorloading

    def get_StyleFactorloading_matrix(self, date):
        """读取风格因子载荷矩阵"""
        return self._get_StyleFactorloading_matrix(date)

    def _get_factorloading_matrix(self, date):
        """
        读取风险因子载荷矩阵数据, 含市场因子f_c
        Parameters:
        --------
        :param date: datetime-like, str
            读取日期, e.g: YYYY-MM-DD, YYYYMMDD
        :return: pd.DataFrame
        """
        date = Utils.to_date(date)
        ind_factorloading = self._get_IndFactorloading_matrix(date)
        style_factorloading = self._get_StyleFactorloading_matrix(date)
        factorloading_matrix = pd.merge(left=ind_factorloading, right=style_factorloading, how='inner', on='code')
        for mf in riskfactor_ct.MARKET_FACTORS:
            factorloading_matrix[mf] = 1.0
        return factorloading_matrix

    def _naive_factor_covmat(self, date):
        """
        计算风险模型因子朴素协方差矩阵, 采用EWMA算法计算
        Parameters:
        --------
        :param date: datetime-lie, str
            计算日期, e.g: YYYY-MM-DD, YYYYMMDD
        :return: tuple(str, np.array)
        --------
            返回一个元组, 第一个元素是因子名称list, 第二个元素是因子朴素协方差矩阵
        """
        # 读取风险因子报酬序列数据
        ewma_param = riskmodel_ct.FACTOR_COVMAT_PARAMS['EWMA']
        date = Utils.to_date(date)
        df_factor_ret = self._get_factor_ret(end=date, ndays=ewma_param['trailing'])
        arr_factor_ret = np.array(df_factor_ret)

        # 采用协方差参数计算因子协方差矩阵
        cov_weight = Algo.ewma_weight(len(arr_factor_ret), ewma_param['cov_half_life'])
        cov_mat = np.cov(arr_factor_ret, rowvar=False, bias=True, aweights=cov_weight)

        # 采用方差参数计算因子方差向量, 并替换协方差矩阵的对角线数据
        vol_weight = Algo.ewma_weight(len(arr_factor_ret), ewma_param['vol_half_life'])
        avg = np.average(arr_factor_ret, axis=0, weights=vol_weight)
        X = arr_factor_ret - avg[None, :]
        arr_vol = np.sum(vol_weight[:, None] * X ** 2, axis=0)
        for k in np.arange(len(arr_vol)):
            cov_mat[k, k] = arr_vol[k]

        return df_factor_ret.columns.tolist(), cov_mat

    def _save_factor_covmat(self, date, cov_mat, factor_names, cov_type):
        """
        保存协方差矩阵数据
        Parameters:
        --------
        :param date: datetime-like, str
             日期, e.g: YYYY-MM-Dd, YYYYMMDD
        :param cov_mat: np.array
            风险因子协方差矩阵数据
        :param factor_names: list of str
            风险因子名称list, 协方差矩阵的header
        :param cov_type: str
            协方差矩阵类型, 'naive'=朴素协方差矩阵, 'cov'=最终协方差矩阵
        :return:
        """
        date = Utils.to_date(date)
        if cov_type == 'naive':
            cov_path = os.path.join(SETTINGS.FACTOR_DB_PATH, riskmodel_ct.FACTOR_NAIVE_COVMAT_PATH, 'cov_{}.csv'.format(Utils.datetimelike_to_str(date, dash=False)))
        elif cov_type == 'cov':
            cov_path = os.path.join(SETTINGS.FACTOR_DB_PATH, riskmodel_ct.FACTOR_COVMAT_PATH, 'cov_{}.csv'.format(Utils.datetimelike_to_str(date, dash=False)))
        else:
            raise ValueError("协方差矩阵类型错误.")

        df_covmat = pd.DataFrame(cov_mat, index=factor_names, columns=factor_names)
        df_covmat.to_csv(cov_path, index_label='factor')

    def _get_factor_covmat(self, cov_type, end, ndays=None, factors=None):
        """
        读取风险因子协方差矩阵数据
        Parameters:
        --------
        :param end: datetime-like, str
            结束日期, e.g: YYYY-MM-DD, YYYYMMDD
        :param ndays: int
            天数, 默认为None
        :param cov_type: str
            协方差矩阵类型, 'naive'=朴素协方差矩阵, 'cov'=最终协方差矩阵
        :param factors: list of str
            指定需要返回的因子列表
        :return: dict{str, np.array}
        --------
            风险因子协方差因子矩阵数据时间序列, dict{'YYYYMMDD': cov_mat}
        """
        if cov_type == 'naive':
            cov_path = os.path.join(SETTINGS.FACTOR_DB_PATH, riskmodel_ct.FACTOR_NAIVE_COVMAT_PATH)
        elif cov_type == 'cov':
            cov_path = os.path.join(SETTINGS.FACTOR_DB_PATH, riskmodel_ct.FACTOR_COVMAT_PATH)
        else:
            raise ValueError("协方差矩阵类型错误.")

        end = Utils.to_date(end)
        if ndays is None:
            trading_days_series = Utils.get_trading_days(end=end, ndays=1)
        else:
            trading_days_series = Utils.get_trading_days(end=end, ndays=ndays)

        factor_covmat = {}
        for calc_date in trading_days_series:
            covmat_path = os.path.join(cov_path, 'cov_{}.csv'.format(Utils.datetimelike_to_str(calc_date, dash=False)))
            df_factor_covmat = pd.read_csv(covmat_path, header=0, index_col=0)
            if df_factor_covmat is not None:
                df_factor_covmat = df_factor_covmat.loc[factors, factors]
            arr_factor_covmat = np.array(df_factor_covmat)
            factor_covmat[Utils.datetimelike_to_str(calc_date, dash=False)] = arr_factor_covmat

        if len(factor_covmat) == 0:
            return None
        else:
            return factor_covmat

    def _covmat_NeweyWest_adj(self, naive_covmat, date):
        """
        计算经newey_west调整后的风险因子协方差矩阵
        Parameters:
        --------
        :param naive_covmat: np.array
            风险因子朴素协方差矩阵,矩阵大小为: N×N
        :param date: datetime-like, str
            计算日期, e.g: YYYY-MM-DD, YYYYMMDD
        :return: np.array
        --------
            newey_west调整后的协方差矩阵
        """
        # 读取风险因子报酬序列数据
        nw_param = riskmodel_ct.FACTOR_COVMAT_PARAMS['Newey_West']
        date = Utils.to_date(date)
        df_factor_ret = self._get_factor_ret(end=date, ndays=nw_param['trailing'])
        arr_factor_ret = np.array(df_factor_ret)

        if naive_covmat.shape[0] != naive_covmat.shape[1]:
            raise ValueError("风险因子朴素协方差矩阵(naive_covmat)行和列的长度不一致")
        if naive_covmat.shape[0] != arr_factor_ret.shape[1]:
            raise ValueError("风险因子朴素协方差矩阵(naive_covmat)的长度与风险因子报酬矩阵(arr_factor_ret)的列长度不一致")

        m, n = arr_factor_ret.shape
        covmat_NW_adj = np.zeros((n, n))
        D = nw_param['cov_lags']
        if D < 1:
            raise ValueError("参数cov_lags必须大于0")
        for delta in range(1, D+1):
            weight = Algo.ewma_weight(len(arr_factor_ret) - delta, nw_param['cov_half_life'])

            arr_factor_ret1 = arr_factor_ret[: -delta, :]
            avg1 = np.average(arr_factor_ret1, axis=0, weights=weight)
            arr_factor_ret1 = arr_factor_ret1 - avg1

            arr_factor_ret2 = arr_factor_ret[delta: , :]
            avg2 = np.average(arr_factor_ret2, axis=0, weights=weight)
            arr_factor_ret2 = arr_factor_ret2 - avg2

            # m, n = weight.shape
            weight = np.reshape(weight, (len(weight), 1))
            arr_factor_ret1 = arr_factor_ret1 * weight
            cov1 = np.dot(arr_factor_ret1.T, arr_factor_ret2)
            cov2 = np.dot(arr_factor_ret2.T, arr_factor_ret1)

            alpha = 1.0 - delta / (1.0 + D)
            covmat_NW_adj += alpha * (cov1 + cov2)

        covmat_NW_adj = 21.0 * (naive_covmat + covmat_NW_adj)
        return covmat_NW_adj

    def _vol_RegimeAdj_multiplier(self, date):
        """
        计算波动率偏误乘数
        Parameters:
        --------
        :param date: datetime-like, str
            计算日期
        :return:
        """
        regime_adj_param = riskmodel_ct.FACTOR_COVMAT_PARAMS['Vol_Regime_Adj']
        date = Utils.to_date(date)
        # 读取风险因子报酬数据序列
        df_factorret = self._get_factor_ret(end=date, ndays=regime_adj_param['trailing'])
        if df_factorret.shape[0] != regime_adj_param['trailing']:
            raise ValueError("风险因子报酬数据序列长度不等于%d." % regime_adj_param['trailing'])
        if df_factorret.shape[1] != len(riskfactor_ct.STYLE_RISK_FACTORS) + len(riskfactor_ct.INDUSTRY_FACTORS) + 1:
            raise ValueError("风险因子报酬数据序列的列数与风险因子数量不一致.")
        arr_factorret = np.array(df_factorret)
        # 读取风险因子朴素协方差矩阵数据序列, 并转换为风险因子方差序列数据
        factor_covmat_series = self._get_factor_covmat(cov_type='naive', end=date-datetime.timedelta(days=1), ndays=regime_adj_param['trailing'])
        arr_varmat = np.zeros(arr_factorret.shape)
        k = 0
        for str_date, cov_mat in factor_covmat_series.items():
            n = len(cov_mat)
            for i in range(n):
                arr_varmat[k][i] = cov_mat[i][i]
            k += 1
        if arr_varmat.shape[0] != regime_adj_param['trailing']:
            raise ValueError("风险因子方差序列长度不等于%d." % regime_adj_param['trailing'])
        if arr_factorret.shape != arr_varmat.shape:
            raise ValueError("风险因子报酬数据序列与风险因子方差序列的大小不一致.")
        arr_factorret_square = arr_factorret ** 2
        b_stat = arr_factorret_square / arr_varmat
        b_stat = np.sum(b_stat, axis=1) / arr_factorret_square.shape[1]

        weight = Algo.ewma_weight(regime_adj_param['trailing'], regime_adj_param['half_life'])
        multiplier = np.sum(weight * b_stat)
        return math.sqrt(multiplier)

    def _naive_spec_var(self, date):
        """
        计算特质收益率朴素方差向量, 采用EWMA算法计算
        Parameters:
        --------
        :param date: datetime-like, str
            计算日期, e.g: YYYY-MM-DD, YYYYMMDD
        :return: pd.Series
        --------
            返回个股特质收益率方差数据, pd.Series的index为个股代码
        """
        date = Utils.to_date(date)
        # 读取个股基本信息数据
        stock_basics = Utils.get_stock_basics(date)
        # 读取截止date日期的个股特质收益率数据
        ewma_param = riskmodel_ct.SPECIFICRISK_VARMAT_PARAMS['EWMA']
        df_residual_ret = self._get_residual_ret(end=date)
        # 遍历个股基本信息, 采用EWMA算法计算特质收益率方差
        symbols = [symbol for symbol in stock_basics['symbol'] if symbol in df_residual_ret.columns.tolist()]
        df_residual_ret = df_residual_ret[symbols]
        weight = Algo.ewma_weight(ewma_param['trailing'], ewma_param['half_life'])

        ser_spec_var = pd.Series(name='spec_var')
        for symbol in df_residual_ret.columns:
            arr_residual_ret = np.asarray(df_residual_ret[symbol].dropna().tail(ewma_param['trailing']))
            if len(arr_residual_ret) < ewma_param['trailing']/2:
                continue
            if len(arr_residual_ret) == ewma_param['trailing']:
                avg = np.average(arr_residual_ret, weights=weight)
                arr_residual_ret -= avg
                naive_var = np.sum(weight * arr_residual_ret ** 2)
            else:
                w = Algo.ewma_weight(len(arr_residual_ret), ewma_param['half_life'])
                avg = np.average(arr_residual_ret, weights=w)
                arr_residual_ret -= avg
                naive_var = np.sum(w * arr_residual_ret ** 2)
            ser_spec_var[symbol] = naive_var

        ser_spec_var.index.name = 'code'
        return ser_spec_var

    def _save_spec_var(self, date, spec_var, var_type):
        """
        保存个股特质收益率方差数据
        Parameters:
        --------
        :param date: datetime-like, str
            日期, e.g: YYYY-MM-DD, YYYYMMDD
        :param spec_var: pd.Series
            个股特质收益率方差数据
        :param var_type: str
            方差数据类型, 'naive'=朴素方差数据, 'var'=最终特质收益率方差数据
        :return:
        """
        date = Utils.to_date(date)
        if var_type == 'naive':
            var_path = os.path.join(SETTINGS.FACTOR_DB_PATH, riskmodel_ct.SPECIFICRISK_NAIVE_VARMAT_PATH, 'specvar_{}.csv'.format(Utils.datetimelike_to_str(date, dash=False)))
        elif var_type == 'var':
            var_path = os.path.join(SETTINGS.FACTOR_DB_PATH, riskmodel_ct.SPECIFICRISK_VARMAT_PATH, 'specvar_{}.csv'.format(Utils.datetimelike_to_str(date, dash=False)))
        else:
            raise ValueError("特质收益率方差类型错误.")

        spec_var.to_csv(var_path, index=True, header=True)

    def _get_spec_var(self, var_type, end, ndays=None):
        """
        读取个股特质波动率方差数据
        Parameters:
        --------
        :param var_type: str
            特质博定律方差的数据类型, 'naive'=朴素方差数据, 'var'=最终特质波动率方差数据
        :param end: datetime-like, str
            结束日期, e.g: YYYY-MM-DD, YYYYMMDD
        :param ndays: int
            天数, 默认为None
        :return: dict{str, pd.DataFrame}
        --------
            个股特质波动率方差数据时间序列, dict{'YYYYMMDD': spec_var}
        """
        if var_type == 'naive':
            specvar_basepath = os.path.join(SETTINGS.FACTOR_DB_PATH, riskmodel_ct.SPECIFICRISK_NAIVE_VARMAT_PATH)
        elif var_type == 'var':
            specvar_basepath = os.path.join(SETTINGS.FACTOR_DB_PATH, riskmodel_ct.SPECIFICRISK_VARMAT_PATH)
        else:
            raise ValueError("特质波动率数据类型错误.")

        end = Utils.to_date(end)
        if ndays is None:
            trading_days_series = Utils.get_trading_days(end=end, ndays=1)
        else:
            trading_days_series = Utils.get_trading_days(end=end, ndays=ndays)

        spec_var = {}
        for calc_date in trading_days_series:
            specvar_path = os.path.join(specvar_basepath, 'specvar_{}.csv'.format(Utils.datetimelike_to_str(calc_date, dash=False)))
            if not os.path.isfile(specvar_path):
                raise FileExistsError("文件%s不存在." % specvar_path)
            df_specvar = pd.read_csv(specvar_path, header=0)
            spec_var[Utils.datetimelike_to_str(calc_date, dash=False)] = df_specvar

        if len(spec_var) == 0:
            return None
        else:
            return spec_var

    def _specvar_NeweyWest_adj(self, naive_specvar, date):
        """
        计算经newey_west调整后的特质波动率方差数据
        Parameters:
        --------
        :param naive_specvar: pd.Series
            特质波动率朴素方差向量数据
        :param date: datetime-like, str
            计算日期, e.g: YYYY-MM-DD, YYYYMMDD
        :return: pd.Series
        --------
            newey_west调整后的特质波动率方差向量数据
        """
        nw_param = riskmodel_ct.SPECIFICRISK_VARMAT_PARAMS['Newey_West']
        date = Utils.to_date(date)
        # 读取特质收益率序列数据
        df_spec_ret = self._get_residual_ret(end=date)
        # 读取股票基本信息
        stock_basics = Utils.get_stock_basics(date)
        # 遍历特质收益率序列数据, 计算个股经newey_west调整后的特质波动率方差数据
        symbols = [symbol for symbol in stock_basics['symbol'] if symbol in df_spec_ret.columns.tolist()]
        df_spec_ret = df_spec_ret[symbols]
        codes = df_spec_ret.columns.tolist()
        D = nw_param['lags']    # 滞后时间长度
        if D < 1:
            raise ValueError("滞后时间长度参数lags的值必须大于0")
        # 计算权重向量组
        weights = []
        for delta in range(1, D+1):
            weights.append(Algo.ewma_weight(nw_param['trailing']-delta, nw_param['half_life']))

        nw_specvar = pd.Series(name='spec_var')     # newey_west调整后的特质收益率方差向量
        for code in codes:
            if code not in naive_specvar:
                continue

            arr_spec_ret = np.array(df_spec_ret[code].dropna())
            if len(arr_spec_ret) < int(nw_param['trailing']/2):
                continue
            if len(arr_spec_ret) > nw_param['trailing']:
                arr_spec_ret = arr_spec_ret[: nw_param['trailing']]

            nw_adj = 0
            for delta in range(1, D+1):
                if len(arr_spec_ret) < nw_param['trailing']:
                    w = Algo.ewma_weight(len(arr_spec_ret)-delta, nw_param['half_life'])
                else:
                    w = weights[delta-1]

                arr_spec_ret1 = arr_spec_ret[: -delta]
                avg1 = np.average(arr_spec_ret1, weights=w)
                arr_spec_ret1 = arr_spec_ret1 - avg1

                arr_spec_ret2 = arr_spec_ret[delta:]
                avg2 = np.average(arr_spec_ret2, weights=w)
                arr_spec_ret2 = arr_spec_ret2 - avg2

                cov = np.sum(w * arr_spec_ret1 * arr_spec_ret2)
                alpha = 1.0 - delta / (1.0 + D)
                nw_adj += alpha * cov * 2
            nw_specvar[code] = 21.0 * (naive_specvar[code] + nw_adj)

        nw_specvar.index.name = 'code'
        return nw_specvar

    def risk_contribution(self, holding, date=datetime.date.today(), benchmark=None):
        """
        对给定的持仓数据进行风险归因
        Parameters:
        --------
        :param holding: WeightHolding类, 或PortHolding类
            持仓数据
        :param date: datetime-like, str
            计算日期
        :param benchmark: str
            比较基准的代码, e.g: SH000300
        :return: pd.Series
        --------
            持仓数据在风险因子上的风险归因值(index为风险因子代码)
        """
        if (not isinstance(holding, CWeightHolding)) and (not isinstance(holding, CPortHolding)):
            raise ValueError("风险归因应提供WeightHolding类或PortHolding类的持仓数据.")
        date = Utils.to_date(date)
        if holding.count < 1:
            raise ValueError("持仓数据不能为空.")
        codes = holding.holding['code'].tolist()
        # 取得持仓数据
        df_holding = holding.holding
        # # 取得持仓个股权重列向量
        # w = np.array(holding.holding['weight']).reshape((holding.count, 1))
        # 取得持仓的风险因子载荷数据
        df_factorloading = self._get_factorloading_matrix(date)
        df_factorloading = pd.merge(left=df_holding, right=df_factorloading, how='inner', on='code')
        # 取得个股特质波动率方差矩阵数据
        df_specvar = self._get_spec_var('var', date)[Utils.datetimelike_to_str(date, dash=False)]
        df_factorloading = pd.merge(left=df_specvar, right=df_factorloading, how='inner', on='code')

        # df_factorloading.set_index('code', inplace=True)
        arr_factorloading = np.array(df_factorloading.loc[:, riskfactor_ct.RISK_FACTORS])               # 因子暴露矩阵
        arr_weight = np.array(df_factorloading.loc[:, 'weight']).reshape((len(df_factorloading), 1))    # 个股权重向量
        arr_specvar =np.diagflat(df_factorloading['spec_var'].tolist())                                 # 特质波动率方差矩阵
        # 取得风险因子协方差矩阵
        arr_factor_covmat = self._get_factor_covmat('cov', date, factors=riskfactor_ct.RISK_FACTORS)[Utils.datetimelike_to_str(date, dash=False)]

        # 计算组合预期波动率
        fsigma = np.sqrt(np.linalg.multi_dot([arr_weight.T, arr_factorloading, arr_factor_covmat, arr_factorloading.T, arr_weight]) + np.linalg.multi_dot([arr_weight.T, arr_specvar, arr_weight]))
        fsigma = float(fsigma)

        # 计算风险归因值
        Psi = np.dot(arr_weight.T, arr_factorloading).transpose()
        risk_contribution = 1.0 / fsigma * Psi * np.dot(arr_factor_covmat, Psi)
        risk_contribution = pd.Series(risk_contribution.flatten(), index=riskfactor_ct.RISK_FACTORS)    # 风险因子的风险贡献数据
        fselection = fsigma - risk_contribution.sum()
        fallocation = risk_contribution.sum() - risk_contribution['market']
        risk_contribution['sigma'] = fsigma                                                         # 组合预期波动率
        risk_contribution['selection'] = fselection                                                 # 选股带来的风险贡献
        risk_contribution['allocation'] = fallocation                                               # 配置带来的风险贡献
        risk_contribution['industry'] = risk_contribution[riskfactor_ct.INDUSTRY_FACTORS].sum()     # 行业因子风险贡献
        risk_contribution['style'] = risk_contribution[riskfactor_ct.STYLE_RISK_FACTORS].sum()      # 风格因子风险贡献

        return risk_contribution

if __name__ == '__main__':
    BarraModel = Barra()
    # BarraModel.calc_factorloading('2014-01-01', '2014-12-31')
    # BarraModel._calc_secu_dailyret('2018-07-02')
    # BarraModel._get_cap_weight('2017-12-29')
    # BarraModel._calc_IndFactorloading('2017-12-29')
    # BarraModel._calc_StyleFactorloading('2017-12-29')
    # print(BarraModel._get_IndFactorloading_matrix('2017-12-29').head())
    # print(BarraModel._get_StyleFactorloading_matrix('2017-12-29').head())
    # print(BarraModel._get_secu_dailyret('2018-01-02').head())

    # BarraModel.estimate_factor_ret(start_date='2018-01-01', end_date='2018-06-30')
    # print(BarraModel._naive_factor_covmat('2018-06-29'))
    # BarraModel.calc_factor_covmat(start_date='2017-02-01', end_date='2018-06-29', calc_mode='all')
    # BarraModel.calc_spec_varmat(start_date='2018-01-01', end_date='2018-06-30', calc_mode='var')

    holding_data = load_holding_data('tmp', 'sh50')
    risk_contribution = BarraModel.risk_contribution(holding_data, '2018-06-29')
    print(risk_contribution)


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

from django import forms
from django.contrib.auth.models import User
from django.core.exceptions import ValidationError

from dashboard.models import Task, TaskLog

class TaskForm(forms.ModelForm):
    def __init__(self, *args, **kwargs):
        super(TaskForm, self).__init__(*args, **kwargs)
        for field in self.fields:
            self.fields[field].widget.attrs['class'] = 'form-control'

    def save(self, commit=True, author=False, user=False):
        data = None
        action = TaskLog.UPDATE_TASK

        task = super(TaskForm, self).save(commit=False)

        if not user:
            raise ValidationError('User was lost')

        if not task.author:  # Create task
            if author:
                action = TaskLog.CREATE_TASK
                task.author = author
            else:
                raise ValidationError('Author already exists')

        else:  # Update task
            data = {}
            origin = self.Meta.model.objects.get(pk=self.instance.pk)
            check_username = lambda x: x.username if isinstance(x, User) else x

            for field in self.cleaned_data:
                old_value = getattr(origin, field, None)
                new_value = self.cleaned_data.get(field)

                if old_value != new_value:
                    if field == 'assigned_to':
                        data[field] = [
                            check_username(old_value),
                            check_username(new_value)
                        ]
                    else:
                        data[field] = [old_value, new_value]

        if commit:
            task.save()
            TaskLog.objects.create(
                action=action,
                task=task,
                data=data or None,
                user=user,
            )
        return task

    class Meta(object):
        model = Task
        fields = (
            'subject', 'status', 'assigned_to', 'time_estimate', 'body_text',
        )


# example of itertools possibilities
from itertools import count, cycle

# create and print powers of two
for el in count(1):
    if el < 5:
        print(el)
    else:
        print('Finish first cycle')
        break

sample_list = ['1', '2', '3']
total_sum = 0
for el in cycle(sample_list):
    total_sum += int(el)
    print(f"Current el: {el}. Current sum: {total_sum}")
    if total_sum > 10:
        break


mat_name = 'CubeMat'
tex_name = 'CubeTex'

# use yt to input an image? or link to an image sequence
use_yt = False

#filevoxout = '/Users/jillnaiman1/Desktop/test1_ytl4_l1en27.bvox'

def translate(value, leftMin, leftMax, rightMin, rightMax):
    # Figure out how 'wide' each range is
    leftSpan = leftMax - leftMin
    rightSpan = rightMax - rightMin
    # Convert the left range into a 0-1 range (float)
    valueScaled = float(value - leftMin) / float(leftSpan)
    # Convert the 0-1 range into a value in the right range.
    return rightMin + (valueScaled * rightSpan)

import yt
import numpy as np
datafile = '~/data/IsolatedGalaxy/galaxy0030/galaxy0030'
ds = yt.load(datafile)
dd = ds.all_data()
level = 4
all_data = ds.covering_grid(level=level, left_edge=[0,0.0,0.0],dims=ds.domain_dimensions*2**level)
#pointdata = dd['density']/dd['density'].max()
pointdata = np.log10(all_data['density']).flatten()
pointdata_save = np.array(pointdata)
# rescale from 0->1
minp = pointdata.min()
maxp = pointdata.max()
for i in range(0,len(pointdata)):
    pointdata[i] = translate(pointdata[i], minp, maxp, 0, 1.)
# set low bounds = 0
pointdata[pointdata_save < -27] = 0.0
#pointdata /= pointdata.max()

# format the data following: http://pythology.blogspot.com/2014/08/you-can-do-cool-stuff-with-manual.html

nx, ny, nz, nframes = all_data['density'].shape[0], all_data['density'].shape[1], all_data['density'].shape[2],1
header = np.array([nx,ny,nz,nframes])
#binfile = open(filevoxout,'wb')
#header.astype('<i4').tofile(binfile)
#pointdata.astype('<f4').tofile(binfile)

# now, we'll make a "fake" bvox file
#from yt.extern.six.moves import StringIO # because we need something that works in py3 & 2

# example
#output = StringIO.StringIO()
#output.write('First line.\n')
#print >>output, 'Second line.'
#
## Retrieve file contents -- this will be
## 'First line.\nSecond line.\n'
#contents = output.getvalue()
#
## Close object and discard memory buffer --
## .getvalue() will now raise an exception.
#output.close()

hp = np.array([header,pointdata])
#outfile = StringIO()
from io import BytesIO
outfile = BytesIO(hp)
#outfile.write(header.tostring())# + pointdata.tostring())

#---------------------------------

def setMaterial(ob, mat):
    me = ob.data
    me.materials.append(mat)

# first create but if it does exist
flagg = True
# do we have cube?
for ob in bpy.data.objects:
    if ob.name == 'Cube':
        flagg = False

if flagg:
    bpy.ops.mesh.primitive_cube_add(radius=1.0)

# put it in the center
bpy.data.objects['Cube'].location = (0,0,0.)

# make a material
mat = bpy.data.materials.new(mat_name)
mat.type = 'VOLUME'
mat.volume.density = 0.0 # lower?
mat.volume.density_scale = 2.0 # upper in the slice plot
# more scattering = less the light penatrates into volume
mat.volume.scattering = 1.4
# make particles emissive?  I assume?... looks like it... we might have to
#  make different materials with different emissivities for different
#  emissivity of the gas
mat.volume.emission = 0.0
mat.transparency_method = 'Z_TRANSPARENCY'

setMaterial(bpy.data.objects['Cube'], mat)

# Create texture from image sequence
mtex = mat.texture_slots.add()
mat.active_texture_index = 0

# this should work but it doesnt right now...
tex = bpy.data.textures.new(tex_name, type = 'VOXEL_DATA')
#bpy.ops.texture.new()
#tex = bpy.data.textures['Texture']
#tex.name = tex_name
mat.active_texture = tex

tex.use_color_ramp = True

tex.voxel_data.domain_object = bpy.data.objects['Cube']

#tex.voxel_data.file_format = 'IMAGE_SEQUENCE'
tex.voxel_data.file_format = 'BLENDER_VOXEL'

#if not use_yt: # generate from image seq
#bpy.ops.image.open(filepath=image_seq_dir+image_seq)
#bpy.ops.image.open(filepath=image_seq_dir+image_seq)#, directory = image_seq_dir, files = [{"name":image_seq, "name":image_seq}], relative_path=True)

#img = bpy.data.images[image_seq]
#tex.voxel_data.filepath = image_seq_dir + image_seq
#tex.image = img

#img.source = 'SEQUENCE'

tex.voxel_data.filepath = filevoxout

mat.texture_slots[0].texture_coords = 'ORCO' # generated coords
mat.texture_slots[0].mapping = 'CUBE' # map to a cube

# NO idea
ts = mat.texture_slots[0]
ts.use_map_density = True
ts.density_factor = 1.0
ts.use_map_emission = True
ts.emission_factor = 1.0
ts.use_map_color_emission = True
ts.emission_color_factor = 1.0


import sys
import json
import requests

from secrets import api_key

class outfit():

    #initialize and take in zip code
    def __init__(self):
        self.zip = str(sys.argv[1])
        self.faren = 0
        self.shirt = ''
        self.pants = ''
        self.accessories = ''

    #get temperature data
    def get_temp_data(self):
        req_url = 'http://api.openweathermap.org/data/2.5/weather?zip={}&appid={}'.format(self.zip, api_key)
        response = requests.get(req_url)
        data_dictionary = response.json()
        self.kelvin = data_dictionary['main']['feels_like']

    #convert temperature to farenheit
    def kelvin_to_farenheit(self):
        self.get_temp_data()
        self.farenheit = (1.8 * (self.kelvin - 273)) + 32

    #recommend shirt
    def get_shirt(self):
        f = int(self.farenheit)
        if f > 72:
            self.shirt = ('tank top')
        elif f > 55 and f <= 72:
            self.shirt = ('short sleeves')
        elif f >= 40 and f <= 55:
            self.shirt = ('long sleeves')
        else:
            self.shirt = ('sweater')

    #recommend pants
    def get_pants(self):
        if int(self.farenheit) > 58:
            self.pants = ('shorts')
        else:
            self.pants = ('long pants')

    #recommend accessories
    def get_accessories(self):
        f = int(self.farenheit)
        if f < 40 and f > 32:
            self.accessories = ('hat')
        elif f <= 32:
            self.accessories = ('hat and gloves')
        else:
            self.accessories = ''

    #consolidate and print out recommendations
    def assemble_outfit(self):
        self.kelvin_to_farenheit()
        self.get_shirt()
        self.get_pants()
        self.get_accessories()
        print('')
        print('temperature feels like ' + str(self.farenheit) + ' F')
        print(self.shirt)
        print(self.pants)
        print(self.accessories)

my_outfit = outfit()
my_outfit.assemble_outfit()


import os

import pandas as pd
from uuid import uuid4
from django.utils import timezone
from tacos import settings

def create_row(figma_url, access_token, refresh_token):
    url = str(uuid4())
    return ({
                'figma_url': figma_url,
                'created_at': timezone.now(),
                'access_token': access_token,
                'refresh_token': refresh_token
            }, url)

def get_local_lottie_file_name(url, filename):
    return "{}_{}".format(url, filename)

def get_config_file_path():
    return f'{settings.LOCAL_FOLDER_LOTTIE_FILES}/{settings.LOCAL_FILE_CONFIG}'

def get_config_df():
    # Check if csv file already exists
    config_file_path = get_config_file_path()
    if os.path.isfile(config_file_path):
        with open(config_file_path, 'r') as f:
            df = pd.read_csv(f, index_col='url')
    else:
        df = pd.DataFrame(
            columns=['url', 'figma_url', 'lottie_files', 'created_at', 'access_token', 'refresh_token'])
        df = df.set_index('url')
    return df


#!/usr/bin/env python

from __future__ import print_function
import sqlite3
import csv
import sys

db_schema = """
DROP TABLE IF EXISTS `block`;
CREATE TABLE `block` (
    `start_ip` INT default NULL,    -- Starting IP number of this block
    `end_ip` INT default NULL,      -- Ending IP number of this block
    `loc_id` INT default NULL       -- Corresponding location ID
);
CREATE INDEX start_ip_loc_ind ON `block` (`start_ip`);
CREATE INDEX end_ip_loc_ind ON `block` (`end_ip`);

DROP TABLE IF EXISTS `location`;
CREATE TABLE `location` (
    `loc_id` INT default NULL,                  -- ID of this location
    `latitude` FLOAT default NULL,              -- Latitude
    `longitude` FLOAT default NULL,             -- Longitude
    PRIMARY KEY (`loc_id`)
);
"""

def wipe_database(db_name):
    conn = sqlite3.connect(db_name)
    conn.executescript(db_schema)
    conn.commit()
    conn.close()

def read_location_csv_file(db_name, loc_file):
    conn = sqlite3.connect(db_name)
    # Read the location details (currently only store lat/lon)
    if sys.version > '3':
        location_file = open(loc_file, 'r', encoding='iso-8859-1')
    else:
        location_file = open(loc_file, 'r')
    location_file.readline()
    location_file.readline()
    location_reader = csv.reader(location_file, delimiter=',', quotechar="\"")

    num_rows = 0
    curs = conn.cursor()
    for row in location_reader:
        num_rows += 1
        loc_id = int(row[0])
        lat = float(row[5])
        lon = float(row[6])
        curs.execute("INSERT INTO location (loc_id, latitude, longitude) VALUES (?, ?, ?);", (loc_id, lat, lon,))

    conn.commit()
    location_file.close()

    # Count the number of adds as a sanity check
    curs.execute("SELECT COUNT(*) FROM location;")
    num_locs = curs.fetchone()[0]
    print("Read", num_rows, "entries from file and added", num_locs, "entries to location table.")

    conn.close()

def read_block_csv_file(db_name, block_file):
    conn = sqlite3.connect(db_name)
    # Read the IP blocks to location mapping
    if sys.version > '3':
        ip_blocks_file = open(block_file, 'r', encoding='iso-8859-1')
    else:
        ip_blocks_file = open(block_file, 'r')
    ip_blocks_file.readline()
    ip_blocks_file.readline()
    blocks_reader = csv.reader(ip_blocks_file, delimiter=',', quotechar="\"")

    num_rows = 0
    curs = conn.cursor()
    for row in blocks_reader:
        num_rows += 1
        start_ip = int(row[0])
        end_ip = int(row[1])
        loc_id = int(row[2])
        curs.execute("INSERT INTO block (start_ip, end_ip, loc_id) VALUES (?, ?, ?);", (start_ip, end_ip, loc_id,))

    conn.commit()
    ip_blocks_file.close()

    # Count the number of adds as a sanity check
    curs.execute("SELECT COUNT(*) FROM block;")
    num_blocks = curs.fetchone()[0]
    print("Read", num_rows, "entries from file and added", num_blocks, "entries to block table.")

    conn.close()

def main():
    if len(sys.argv) != 4:
        print("syntax:", sys.argv[0], "DB_NAME LOCATION_CSV_FILE BLOCK_CSV_FILE")
        exit(1)

    print("*******************************************************")
    print("WARNING: THIS WILL COMPLETELY DESTROY THE IP LOOKUP")
    print("DATABASE. ONLY RUN IT IF YOU KNOW WHAT YOU ARE DOING.")
    print("*******************************************************")
    # Minor fix for Python 2
    if sys.version > '3':
        wipe_db = input("ARE YOU SURE YOU WANT TO CONTINUE? (y/n) ")
    else:
        wipe_db = raw_input("ARE YOU SURE YOU WANT TO CONTINUE? (y/n) ")

    if wipe_db == "y":
        DB_NAME = sys.argv[1]
        LOCATION_CSV_FILE = sys.argv[2]
        BLOCK_CSV_FILE = sys.argv[3]
        wipe_database(DB_NAME)
        read_location_csv_file(DB_NAME, LOCATION_CSV_FILE)
        read_block_csv_file(DB_NAME, BLOCK_CSV_FILE)
    else:
        print("CANCELLED")

if __name__ == "__main__":
    main()


"""This file is a SuccessFactorsXML spider created on top of the XMLSpider
scrapy crawl successfactorsxml -a url="https://career8.successfactors.com/career?company=TJX" -a extract=1

sample urls:
    https://career8.successfactors.com/career?company=TJX
"""
import json
import urllib
import urlparse

from scrapy.selector import Selector

from brightcorp.base.xmlspider import XMLSpider
from brightcorp.items import BrightcorpItemLoader
from brightcorp.processors import ConvertDateString, NormalizedJoin, Prefix

class SuccessFactorsXML(XMLSpider):

    name = 'successfactorsxml'
    tag = 'Job'
    follow_job_url = False

    mappings = {
        'city': ['Location'],
        'state': ['State/Province'],
        'country': ['Country', 'Kraj', 'Pays', 'Land'],
        'region': ['Province/State', 'dztwo/Stan', 'Province/', 'County',
                   'Bundesland/Kanton'],
        'jobcategory': ['Functional Area', 'Domaine fonctionnel',
                        'Obszar dzia', 'tigkeitsbereich', 'Area of Interest'],
        'jobtype': ['Vollzeit/Teilzeit', 'Full-time/Part-time',
                    'Temps plein/temps partiel', ' etat (FT)', 'Requisition Type']
    }

    refno_seen = set()

    def get_field(self, field_type, sel):
        for mapping in self.mappings.get(field_type, []):
            location = sel.xpath('//*[contains(label/text(), "%s")]/value/text()' % mapping)
            if location:
                return location.extract()[0]
        return ''

    @staticmethod
    def add_url_params(url, params):
        url_parts = list(urlparse.urlparse(url))
        query = dict(urlparse.parse_qsl(url_parts[4]))
        query.update(params)
        url_parts[4] = urllib.urlencode(query)
        return urlparse.urlunparse(url_parts)

    def start_requests(self):
        # load index xml through scrapy and then push those to our xml queue
        self.url_queue = []
        for url in self.start_urls:
            yield self.make_requests_from_url(url)

    def parse(self, response):
        sel = Selector(response)

        locales = [x['value'] for x in json.loads(sel.xpath('//input[@id="rcmlocales"]/@value').extract()[0])]

        # push all the urls to our queue
        for locale in locales:
            self.url_queue.append({'url': self.add_url_params(response.url,
                                                              {'career_ns': 'job_listing_summary',
                                                               'lang': locale,
                                                               'resultType': 'XML'}),
                                   'meta': {'locale': locale }
                                   })

        # load them as streaming files one by one
        for job in self.stream_from_url_queue():
            yield job

    def parse_job(self, response):
        sel = Selector(response)
        loader = BrightcorpItemLoader(selector=sel)
        refno = sel.xpath('//ReqId/text()').extract()[0]
        if refno in self.refno_seen:
            raise StopIteration()

        self.refno_seen.add(refno)

        loader.add_xpath('title', '//JobTitle/text()')
        loader.add_xpath('description', '//Job-Description/text()')
        loader.add_value(
            'referencenumber',
            refno,
            Prefix('%s-' % self.name)
        )
        loader.add_value(
            'url',
            self.add_url_params(
                self.start_urls[0],
                {'career_ns': 'job_listing', 'career_job_req_id': refno}
            )
        )

        loader.add_value(
            'location',
            [
                self.get_field('city', sel),
                self.get_field('state', sel),
                self.get_field('country', sel)
            ],
            NormalizedJoin(', ')
        )
        loader.add_xpath(
            'date', '//Posted-Date/text()', ConvertDateString('%m/%d/%Y')
        )
        loader.add_value('language', response.meta['locale'])
        loader.add_value('jobcategory', self.get_field('jobcategory', sel))
        loader.add_value('jobtype', self.get_field('jobtype', sel))

        yield loader.load_item()


from flask import request
from app import app
from basic.database import db
from basic.models import *
from basic.util import token_required, create_response

@app.route('/products/<int:product_id>/reviews/', methods=['GET'])
def get_product_reviews(product_id):
    product = Product.query.filter_by(id=product_id).first()
    if not product:
        return create_response({'message': 'Product not found!'}, 404, '*')
    return create_response({'reviews': [review.data() for review in product.reviews]}, 200, '*')

@app.route('/products/<int:product_id>/reviews/<int:review_id>', methods=['GET'])
def get_product_review(product_id, review_id):
    product = Product.query.filter_by(id=product_id).first()
    if not product:
        return create_response({'message': 'Product not found!'}, 404, '*')

    reviews = [review for review in product.reviews if review.id == review_id]
    if len(reviews) < 1:
        return create_response({'message': 'Review not found!'}, 404, '*')

    return create_response(reviews[0].data(), 200, '*')

@app.route('/products/<int:product_id>/reviews/', methods=['POST'])
@token_required
def create_product_review(current_user, product_id):

    if not current_user.admin:
        return create_response({'message': 'Permission denied!'}, 403, '*')

    product = Product.query.filter_by(id=product_id).first()
    if not product:
        return create_response({'message': 'Product not found!'}, 404, '*')

    data = request.get_json()

    try:
        date = data['date']
    except:
        date = None

    product_id = db.Column(db.Integer, db.ForeignKey('product.id'))
    title = db.Column(db.String(50), default='Review')
    stars = db.Column(db.Float(), default=2.5)
    text = db.Column(db.String(16000000), default='<Product Review Text>')

    review = Review(product_id=product.id,
                    title=data.get('title', None),
                    stars=data.get('stars', None),
                    text=data.get('text', None))

    db.session.add(review)
    product.reviews.append(review)
    db.session.commit()

    return create_response({'message': 'Review added!', 'id': review.id}, 200, '*')

@app.route('/products/<int:product_id>/reviews/<int:review_id>', methods=['PUT'])
@token_required
def modify_product_review(current_user, product_id, review_id):

    if not current_user.admin:
        return create_response({'message': 'Permission denied!'}, 403, '*')

    product = Product.query.filter_by(id=product_id).first()
    if not product:
        return create_response({'message': 'Product not found!'}, 404, '*')

    reviews = [review for review in product.reviews if review.id == review_id]
    if len(reviews) < 1:
        return create_response({'message': 'Review not found!'}, 404, '*')

    data = request.get_json()

    review = reviews[0]

    for key in data.keys():
        if key == 'title':
            review.title = data['title']
        elif key == 'stars':
            review.stars = data['stars']
        elif key == 'text':
            review.text = data['text']

    db.session.commit()

    return create_response({'message': 'Review updated!'}, 200, '*')

@app.route('/products/<int:product_id>/reviews/<int:review_id>', methods=['DELETE'])
@token_required
def delete_product_review(current_user, product_id, review_id):

    if not current_user.admin:
        return create_response({'message': 'Permission denied!'}, 403, '*')

    product = Product.query.filter_by(id=product_id).first()
    if not product:
        return create_response({'message': 'Product not found!'}, 404, '*')

    product.reviews = [
        review for review in product.reviews if review.id != review_id]
    review = Product.query.filter_by(id=review_id).first()
    if review:
        db.session.delete(review)
    db.session.commit()

    return create_response({'message': 'Review deleted!'}, 200, '*')


#import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
from numpy import random as rd
from sklearn.neighbors import KNeighborsRegressor as nnr
import random
from sklearn.preprocessing import MinMaxScaler
from sklearn.metrics import mean_squared_error
import copy
from sklearn.metrics import max_error

EPSILON = 1e-10

def mape(y,yp):
    #z=np.array([v!=0 and v or vt for v,vt in zip(y,yp)])
    r=np.abs((y-yp)/(y))
    r=r/len(r)
    return r.sum()

def smape(y,yp):
    c=(np.abs(y)+np.abs(yp))/2
    r= np.abs(y-yp)/c
    #print(y.shape,yp.shape)
    return np.mean(r)

def rmse(y,yp):
    r= mean_squared_error(y,yp)
    return np.sqrt(r)

def wsplit(data, m, tau):
    l,i=len(data),0
    n=l-m*tau
    x,y=[[] for k in range(n)],[0 for k in range(n)]
    while i+m*tau<l:
        ind=[0 for k in range(m)]
        for j in range(m):
            val= i+j*tau
            ind[j]=val
        x[i]=data[ind]
        y[i]=data[ind[-1]+tau]
        i+=1
    data=np.array(x)
    target=np.array(y)
    return data, target

class KNNR:
    def __init__(self, m=3, tau=1, k=5, prediction_size=24, w='uniform',
                 d='minkowski'):
        self.m=m
        self.tau=tau
        self.w=w
        self.k=k
        self.w=w
        self.d=d
        self.prediction_size=prediction_size

    def fit(self,data,target):
        self.data,self.target=data,target
        #a = self.d in ['cosine' and 'brute' or 'auto'
        self.regressor=nnr(n_neighbors=self.k, weights=self.w, algorithm='auto', metric=self.d)
        self.regressor.fit(self.data,self.target)
        return self

    def _forecast(self,n,s):
        pred=[]
        m,tau,nn,d,w=self.m,self.tau,self.k,self.d,self.w
        ind=[-i for i in range(m*tau,0,-tau)]
        while len(pred)<n:
            window=list(np.nan_to_num(s[ind],np.mean(s[ind])))
            val=self.regressor.predict([window])
            pred.append(val[0])
            s=np.append(s,val[0])
        return np.array(pred)

    def predict(self,n=None):
        if n is None:
            n=self.prediction_size
        return self._forecast(n,self.target)


import subprocess,os,sys,resource

def setlimits():
    #print("Setting resource limit in child (pid {})".format(os.getpid()))
    resource.setrlimit(resource.RLIMIT_AS, (2**30, 2**30))
    resource.setrlimit(resource.RLIMIT_CPU, (5,5))

def main():
    args = sys.argv[1:]
    with open(args[1],'r') as fin :
        with open(args[2],'w') as fout :

            p = subprocess.Popen([args[0]],stdin=fin,stdout=fout, preexec_fn = setlimits)
            #p = subprocess.Popen(args)
            p.wait()
            print(p.returncode)
            tmp = resource.getrusage(resource.RUSAGE_CHILDREN)
            total_time = tmp.ru_utime
            total_memory = tmp.ru_maxrss
            print(total_time*1000)
            print(total_memory)

if __name__ == "__main__":
    main()


import os
import sys
import glob
import numpy as np
import cv2
from matplotlib import pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
from detection_pipeline import curveFit

def adjust_gamma(image, gamma=1.0):
    invGamma = 1.0 / gamma
    table = np.array([((i / 255.0) ** invGamma) * 255
        for i in np.arange(0, 256)]).astype("uint8")
    return cv2.LUT(image, table)

def hist_equalize(img):
    hist,bins = np.histogram(img.flatten(),256,[0,256])
    cdf = hist.cumsum()
    cdf_m = np.ma.masked_equal(cdf,0)
    N = img.shape[0]*img.shape[1]
    cdf_m = cdf_m*255/N
    cdf = np.ma.filled(cdf_m,0).astype('uint8')
    img = cdf[img]

    return img

def hough_tf(img, img_):

    lower_color_bound = (0, 150, 180)
    upper_color_bound = (200, 180, 190)

    img_thresh = cv2.inRange(img, lower_color_bound, upper_color_bound) ## Does a yellow filter and keeps the white lanes as well.

    edges = cv2.Canny(img_thresh, 20, 20, apertureSize=3) ## finds lines on a thresholded image using canny. output is edges in random directions

    edges = cv2.GaussianBlur(edges,(35,35),0) # Blurred the random direction edges to no longer see them as edges but as spots on lanes

    if np.median(img_) < 100: # when entering the shaded area, enhance the edges and then do the operation on the image

        img_sharped = cv2.addWeighted(img, 8, cv2.blur(img, (35, 35)), -8, 128) # sharpen

        lower_color_bound = (140, 150, 135) ## use a filter to find points on the lanes
        upper_color_bound = (200, 250, 220) ## use a filter to find points on the lanes , this was very tricky and a narrow range.
        img_thresh = cv2.inRange(img_sharped, lower_color_bound, upper_color_bound) # thresholding

        edges = cv2.Canny(img_thresh, 20, 20, apertureSize=3)

        edges = cv2.GaussianBlur(edges,(35,35),0)

    elif np.median(img) > 158: # when exiting the shaded area,

        lower_color_bound = (120, 180, 200) ## use a filter to find points on the lanes
        upper_color_bound = (200, 250, 250) ## use a filter to find points on the lanes , this was very tricky and a narrow range.
        img_thresh = cv2.inRange(img, lower_color_bound, upper_color_bound) # thresholding

        edges = cv2.Canny(img_thresh, 20, 20, apertureSize=3)
        edges = cv2.GaussianBlur(edges,(35,35),0)

    lines = cv2.HoughLinesP(edges, 1, np.pi/180, 100, 10, 10)

    left_pts = []
    right_pts = []
    for x1, y1, x2, y2, in lines[:,0,:]:

        if np.abs((y2 - y1)/(x2 - x1)) < 5: ## allow lines which are only > ~65degs
            continue

        if (x1 < 120 or x2 < 130) or (x1 > 300 or x2 > 350) and (x1 < img.shape[1]-300 or x2 < img.shape[1]-350):
            continue

        if x1 < img.shape[1]/2:

            left_pts.append(([x1,y1]))
        elif x1 > img.shape[1]/2:

            right_pts.append(([x1,y1]))
        if x2 < img.shape[1]/2:

            left_pts.append(([x1,y1]))
        elif x2 > img.shape[1]/2:
            right_pts.append(([x1,y1]))

        cv2.line(img, (x1,y1), (x2, y2), (0,255,0), 2)
    left_pts = np.asarray(left_pts)
    right_pts = np.asarray(right_pts)
    if left_pts.shape == (0,):
        if right_pts.shape == (0,):
            left_pts = [[0,0], [0,0]]
            right_pts = [[0,0], [0,0]]
        else:
            left_pts = np.zeros_like(right_pts)
    elif right_pts.shape == (0,):
        if left_pts.shape == (0,):
            left_pts = [[0,0], [0,0]]
            right_pts = [[0,0], [0,0]]
        else:
            right_pts = np.zeros_like(left_pts)

    cv2.imshow(" s", img)
    #cv2.waitKey(0)
    return left_pts, right_pts, img

def hough_tf_image(img, img_):

    _, img_thresh = cv2.threshold(img_, 220, 255, cv2.THRESH_BINARY) # thresholding image to get a high intensity

    edges = cv2.GaussianBlur(img_thresh,(49,49),0) # Blur the edges to so that random direction lines are just seen as edge patches rather than edges

    if np.median(img_) < 100:

        _, img_thresh2 = cv2.threshold(img_, 80, 255, cv2.THRESH_BINARY) # threshold the histogram equalised img

        edges2 = cv2.Canny(img_thresh2, 3, 3, apertureSize=3) #

        edges2 = cv2.GaussianBlur(edges2,(5,5),0)

        edges = edges + edges2
        edges = adjust_gamma(edges, 1.4)

    lines = cv2.HoughLinesP(edges, 1, np.pi/180, 30, 10, 10)
    left_pts = []
    right_pts = []
    for x1, y1, x2, y2, in lines[:,0,:]:
        if np.abs((y2 - y1)/(x2 - x1)) < 2:
            continue

        if (x1 < 20 or x2 < 30) or (x1 > img.shape[1]-30 or x2 > img.shape[1]-35):
            continue

        if x1 < img.shape[1]/2:
            left_pts.append(([x1,y1]))
        elif x1 > img.shape[1]/2:
            right_pts.append(([x1,y1]))
        if x2 < img.shape[1]/2:
            left_pts.append(([x1,y1]))
        elif x2 > img.shape[1]/2:
            right_pts.append(([x1,y1]))

        cv2.line(img, (x1,y1), (x2, y2), (0,255,0), 2)
    left_pts = np.asarray(left_pts)
    right_pts = np.asarray(right_pts)
    if left_pts.shape == (0,):
        left_pts = np.zeros_like(right_pts)
    elif right_pts.shape == (0,):
        right_pts = np.zeros_like(left_pts)
    cv2.imshow(" s", img)
    return left_pts, right_pts, img

def hist_count(img, img_):
    fig1 = plt.figure()
    ax1 = fig1.gca(projection='3d')
    graph = np.zeros([img.shape[0], img.shape[1], 2])
    graph[:,:,0] = img[:,:,0]
    graph[:,:,1] = (img[:,:,1] + img[:,:,2])/(abs(img[:,:,1] - img[:,:,2]) + 1)
    X = np.arange(0,graph.shape[0])
    Y = np.arange(0,graph.shape[1])

    X, Y = np.meshgrid(X, Y)
    Z = (img[X, Y,1] + img[X,Y,2])/(abs(img[X,Y,1] - img[X,Y,2]) + 1)
    surf = ax1.plot_surface(X, Y, Z)
    fig2 = plt.figure()
    ax2 = fig2.gca(projection='3d')
    Z2 = img[X,Y,0]
    surf = ax2.plot_surface(X, Y, Z2)
    plt.show()
    exit(-1)

    cv2.imshow("lines",img)
    #cv2.waitKey(0)

if __name__ == '__main__':

    if sys.argv[2] == 'mp4':
        video_file = sys.argv[1]
        cap = cv2.VideoCapture(video_file)
        vidWriter = cv2.VideoWriter("./video_output.mp4",cv2.VideoWriter_fourcc(*'mp4v'), 24, (1280,720))

        arrl_store = []
        arrr_store = []
        while (cap.isOpened()):

            ret, img = cap.read()
            if ret == False:
                break

            src_pt = np.array([[407, 615], [886, 615],[312, 677],[962, 677] ])
            dst_pt = np.array([[150, 630], [1080, 630], \
                    [150, 701], [1080, 701]])

            H, _ = cv2.findHomography(src_pt, dst_pt)
            warped = cv2.warpPerspective(img, H, (1280, 720))
            warped_ = cv2.cvtColor(warped, cv2.COLOR_BGR2GRAY)

            left_pts, right_pts, lines = hough_tf(warped, warped_)
            # hist_count(warped, warped_)

            left_pts = np.asarray(left_pts)
            right_pts = np.asarray(right_pts)

            if len(arrl_store)<3:
                arrl_store.append(left_pts)
                arrr_store.append(right_pts)
            elif right_pts[right_pts.shape[0]-2,0]!=0 and left_pts[left_pts.shape[0]-2,0]!=0:
                arrr_store.pop()
                arrr_store.append(right_pts)
                arrl_store.pop()
                arrl_store.append(left_pts)
            arr_templ = np.asarray(arrl_store)
            arr_tempr = np.asarray(arrr_store)
            lefty = arr_templ[arr_templ.shape[0]-1]
            righty = arr_tempr[arr_tempr.shape[0]-1]
            flag =1
            lines,prediction = curveFit(lefty,righty,lines,flag)
            Hinv = np.linalg.inv(H)
            Hinv = Hinv/Hinv[2,2]
            blank_white = 255*np.ones([720, 1280, 3])
            blank_backProj = cv2.warpPerspective(blank_white, Hinv, (1280, 720))
            _, thresh1 = cv2.threshold(blank_backProj, 127, 255, cv2.THRESH_BINARY_INV)

            patch = np.logical_and(img, thresh1)
            img_dummy = np.zeros_like(img)
            img_dummy[patch] = img[patch]
            backProjected = cv2.warpPerspective(lines, Hinv, (1280, 720))

            img_dummy = img_dummy + backProjected

            cv2.putText(img_dummy, prediction, (200, 50),cv2.FONT_HERSHEY_SIMPLEX, 1.5,(0,255,0),2,\
            cv2.LINE_AA)
            cv2.imshow("test", img_dummy)

            vidWriter.write(img_dummy.astype(np.uint8))

    elif sys.argv[2] == 'png':

        files = np.sort(glob.glob(sys.argv[1] + '/*', recursive=True))
        vidWriter = cv2.VideoWriter("video_output.mp4",cv2.VideoWriter_fourcc(*'mp4v'), 24, (1392,512))
        arrl_store = []
        arrr_store = []
        for file in files:
            img = cv2.imread(file)

            src_pt = np.array([[526, 322], [767, 328],[354, 428],[866, 434] ])
            dst_pt = np.array([[200, img.shape[0]-450], [img.shape[1]-100, img.shape[0]-450], \
                    [200, img.shape[0] - 50], [img.shape[1]-100, img.shape[0] - 50]])

            H, _ = cv2.findHomography(src_pt, dst_pt)
            warped = cv2.warpPerspective(img, H, (img.shape[1], img.shape[0]))
            warped_ = cv2.cvtColor(warped, cv2.COLOR_BGR2GRAY)
            left_pts, right_pts, lines = hough_tf_image(warped, warped_)
            if len(arrl_store)<3:
                arrl_store.append(left_pts)
                arrr_store.append(right_pts)
            elif right_pts[right_pts.shape[0]-2,0]!=0 and left_pts[left_pts.shape[0]-2,0]!=0:
                arrr_store.pop()
                arrr_store.append(right_pts)
                arrl_store.pop()
                arrl_store.append(left_pts)
            arr_templ = np.asarray(arrl_store)
            arr_tempr = np.asarray(arrr_store)
            lefty = arr_templ[arr_templ.shape[0]-1]
            righty = arr_tempr[arr_tempr.shape[0]-1]
            flag = 0
            lines,prediction = curveFit(lefty,righty,lines,flag)
            Hinv = np.linalg.inv(H)
            Hinv = Hinv/Hinv[2,2]
            blank_white = 255*np.ones([img.shape[0], img.shape[1], 3])
            blank_backProj = cv2.warpPerspective(blank_white, Hinv, (img.shape[1], img.shape[0]))
            _, thresh1 = cv2.threshold(blank_backProj, 127, 255, cv2.THRESH_BINARY_INV)
            patch = np.logical_and(img, thresh1)
            img_dummy = np.zeros_like(img)
            img_dummy[patch] = img[patch]
            backProjected = cv2.warpPerspective(lines, Hinv, (img.shape[1], img.shape[0]))
            img_dummy = img_dummy + backProjected
            cv2.putText(img_dummy, prediction, (200, 50),cv2.FONT_HERSHEY_SIMPLEX, 1.5,(0,255,0),2,\
            cv2.LINE_AA)

            vidWriter.write(img_dummy.astype(np.uint8))

    vidWriter.release()


import json
import pytest
from pprint import pprint

from .conftest import wrapped

class TestActivities():
    """
    Tests for /activities/ endpoint
    """

    ACTIVITY_GOOD_DATA = {
        "description": "test activity",
        "items": [
            {"id": 0, "content_type": 'text/plain'}
        ]
    }

    def test_get_activity_success(self, testresult, request, client):
        """
        Simplest GET /activities/ invocation
        """
        path = "/api/activities/"

        response = client.get(path)
        wrapped(response, testresult, request)

        status_code = response.status_code
        assert status_code == 200
        assert json.loads(response.data)

    def test_create_activity_get_single_activity(self, testresult, request, client):
        """
        POST a new activity, then ensure we can get it (and the content is right)
        """
        data = self.ACTIVITY_GOOD_DATA

        path = "/api/activities/"
        # Post our JSON
        response = client.post(path, data=json.dumps(
            data), content_type='application/json')

        activity_id = json.loads(response.data)['activity']['id']

        # GET the activity we just posted
        path = "/api/activities/{}".format(activity_id)
        response = client.get(path)

        retrieved_activity = json.loads(response.data)['activity']

        wrapped(response, testresult, request)

        status_code = response.status_code
        assert status_code == 200

        # Verify the contents are the same, spare for a few keys
        # NOTE: we're skipping the 'items' entirely
        ignore_keys = ('id', 'created_at', 'items')
        d1 = self.ACTIVITY_GOOD_DATA.copy()
        d1 = dict((k, d1[k]) for k in d1 if not k in ignore_keys)

        d2 = retrieved_activity.copy()
        d2 = dict((k, d1[k]) for k in d1 if not k in ignore_keys)

        assert d1 == d2

    def test_create_activity_success(self, testresult, request, client):
        """
        Creating an activity should succeed
        """
        path = "/api/activities/"
        data = self.ACTIVITY_GOOD_DATA

        response = client.post(path, data=json.dumps(
            data), content_type='application/json')
        wrapped(response, testresult, request)

        status_code = response.status_code
        assert status_code == 201

    def test_create_activity_empty(self, testresult, request, client):
        """
        Creating an activity with no data should fail
        """
        path = "/api/activities/"
        data = {}

        response = client.post(
            path, data=data, content_type='application/json')
        wrapped(response, testresult, request)

        status_code = response.status_code
        assert response.status_code == 400

    def test_create_activity_empty_items(self, testresult, request, client):
        """
        Creating an activity should succeed
        """
        path = "/api/activities/"
        data = {
            "description": "test activity",
            "items": []
        }

        response = client.post(path, data=json.dumps(
            data), content_type='application/json')
        wrapped(response, testresult, request)

        status_code = response.status_code
        assert status_code == 400

    def test_activity_bad_json(self, testresult, request, client):
        """
        Create an activity with bad JSON
        """
        path = "/api/activities/"
        data = "abcabcd"

        response = client.post(
            path, data=data, content_type='application/json')
        wrapped(response, testresult, request)

        status_code = response.status_code
        assert response.status_code == 400

    def test_create_activity_no_items(self, testresult, request, client):
        """
        Creating an activity with a description, but no items should fail
        """
        path = "/api/activities/"
        data = {"description": "test activity"}

        response = client.post(
            path, data=data, content_type='application/json')
        wrapped(response, testresult, request)

        status_code = response.status_code
        assert response.status_code == 400


from flask import render_template, redirect, Response, request

from app import app
from pymongo import MongoClient
from textblob import TextBlob
import facebook
import credentials
import pygal
from bson.son import SON

from .models import Graph

@app.route('/')
@app.route('/index')
@app.route('/fbstat')
def fbstat():
    client = MongoClient('localhost',27017)
    db = client.fbstat2.posts
    fps = db.find().distinct("fp_name")
    data = []
    for fp in fps:
        tpl = (fp, db.find({"fp_name":fp}).count())
        data.append(tpl)

    return render_template("fbstat.html",
                            title='fbstat',
                            data=data)
@app.route('/getChart/<chartDest>', methods=['GET'])
def getChart(chartDest):
    client = MongoClient('localhost',27017)
    db = client.fbstat2.posts
    if chartDest == 'main' and 'chartId' in request.args:
        if request.args.get('chartId') == 'bar':

            fps = db.find().distinct("fp_name")
            chartData = []
            for fp in fps:
                tmpDct = {}
                tmpDct['name'] = fp
                tmpDct['postCount'] = db.find({"fp_name":fp}).count()
                chartData.append(tmpDct)

            graph = Graph()
            grapObj = graph.BarChart(chartData,'Fanpages by posts number', width=500)
            return Response(response=grapObj.render(), content_type='image/svg+xml')

        elif request.args.get('chartId') == 'perc':
            fps = db.find().distinct("fp_name")
            chartData = []
            for fp in fps:
                data = list(db.find({'fp_name':fp}))

                tmpDct = {}
                tmpDct['name'] = fp
                tmpDct['posCount'] = sum(item['sentiment'] > 0 for item in data)
                tmpDct['negCount'] = sum(item['sentiment'] < 0 for item in data)
                tmpDct['neuCount'] = sum(item['sentiment'] == 0 for item in data)
                tmpDct['allCount'] = tmpDct['posCount'] + tmpDct['negCount'] +  tmpDct['neuCount']
                chartData.append(tmpDct)

            graph = Graph()
            grapObj = graph.StackedGraph(chartData)
            return Response(response=grapObj.render(), content_type='image/svg+xml')

        else:
            pass
    elif chartDest == 'single' and ('chartId' and 'fpname') in request.args:
        fpname = request.args.get('fpname')
        if request.args.get('chartId') == 'daySent':
            pipeline = [
                {"$match": {"fp_name": fpname}},
                {"$group" : {
                    "_id":"$weekday",
                    "posCount": {
                        "$sum": {
                            "$cond": [
                                {"$gt": ["$sentiment", 0]},
                                1,
                                0
                            ]
                        }
                    },
                    "negCount": {
                        "$sum": {
                            "$cond": [
                                {"$lt": ["$sentiment", 0]},
                                1,
                                0
                            ]
                        }

                    },
                    "neuCount": {
                        "$sum": {
                            "$cond": [
                                {"$eq": ["$sentiment", 0]},
                                1,
                                0
                            ]
                        }
                    }
                }},
                {"$sort": SON([("_id", 1)])}

            ]
            chartData = list(db.aggregate(pipeline))

            graph = Graph()
            graphObj = graph.DaySent(chartData)
            return Response(response=graphObj.render(), content_type='image/svg+xml')
        else:
            pass
    else:
        pass

@app.route('/chart', methods=['GET'])
def single_chart():
    fpname = request.args.get('fpname')
    client = MongoClient('localhost',27017)
    db = client.fbstat2.posts
    data = list(db.find({"fp_name":fpname}))
    sent = {}
    sent['posNum'] = sum(item['sentiment'] > 0 for item in data)
    sent['negNum'] = sum(item['sentiment'] < 0 for item in data)
    sent['neuNum'] = sum(item['sentiment'] == 0 for item in data)
    sent['all'] = sent['posNum'] + sent['negNum'] + sent['neuNum']

    pie_chart = pygal.Pie()
    pie_chart.title = fpname

    pie_chart.add('pos', sent['posNum'] / float(sent['all']))
    pie_chart.add('neg', sent['negNum'] / float(sent['all']))
    pie_chart.add('neutral', sent['neuNum'] / float(sent['all']))
    return Response(response=pie_chart.render(), content_type='image/svg+xml')
@app.route('/fbstat/single', methods=['GET'])
def fbstat_single():
    fpname = request.args.get('fpname')
    client = MongoClient('localhost',27017)
    db = client.fbstat2.posts

    if fpname != None and fpname in db.find().distinct("fp_name"):

        data = list(db.find({"fp_name":fpname}))

        dateData = {}
        wkDays = db.find().distinct('weekday')

        for day in wkDays:
            dateData[day] = len(list(db.find({'fp_name':fpname, 'weekday':day})))

        return render_template("single.html",
                            title=fpname,
                            data=data,
                            dateData=dateData)
    else:
        return redirect("/fbstat", code=302)


import requests
import datetime
from flight_data import FlightData

class FlightSearch:
    # This class is responsible for talking to the Flight Search API.
    def __init__(self, config: dict):
        """Initialize class with configuration data (e.g. tokens, URLs, etc)"""
        self.config = config

    def get_iata_code(self, city_name):
        """Get the IATA Code for a city"""
        query = {
            "term": city_name,
            "locale": "en-US",
            "location_types": "city",
            "limit": 1
        }
        security = {"apikey": self.config["TEQUILA_API_KEY"]}
        url = f"{self.config['TEQUILA_URL']}/locations/query"
        return requests.get(url=url, params=query, headers=security).json()["locations"][0]["code"]

    def get_flight_search(self, from_city, price_ccy, sheet_data):
        """Get the data for a cheap flight out of 'from_city', to the data in 'sheet_data',
        with prices in 'price_ccy'"""
        query = {
            "fly_from": from_city,
            "fly_to": sheet_data["iataCode"],
            "date_from": (datetime.date.today() + datetime.timedelta(days=1)).strftime("%d/%m/%Y"),
            "date_to": (datetime.date.today() + datetime.timedelta(days=180)).strftime("%d/%m/%Y"),
            "nights_in_dst_from": 7,
            "nights_in_dst_to": 28,
            "flight_type": "round",
            "one_for_city": 1,
            "curr": price_ccy,
            "max_stopovers": 0
        }
        security = {"apikey": self.config["TEQUILA_API_KEY"]}
        url = f"{self.config['TEQUILA_URL']}/v2/search"
        response = requests.get(url=url, params=query, headers=security)

        # It is possible that there is no flight. In that case, return None
        try:
            data = response.json()["data"][0]
        except IndexError:
            print(f"No flights found for {query['fly_to']}.")
            return None

        # Store the returned flight data as an object of a flight data class,
        # for easier access
        flight_data = FlightData(from_city=data["route"][0]["cityFrom"],
                                 to_city=data["route"][0]["cityTo"],
                                 from_airport=data["route"][0]["flyFrom"],
                                 to_airport=data["route"][0]["flyTo"],
                                 price=data["price"],
                                 departure_date=data["route"][0]["local_departure"].split("T")[0],
                                 return_date=data["route"][1]["local_departure"].split("T")[0])
        print(f"{flight_data.to_city}: £{flight_data.price}")
        return flight_data


from ortools.graph import pywrapgraph

def solution():
    start_nodes = [0, 0, 1,2, 2, 3,3,4,5]  #başlangıç düğümleri
    end_nodes =   [1, 2, 3,3, 4, 5,6,5,6]  #bitiş düğümleri
    capacities =  [6, 7, 3, 5, 4, 4,2,5,8] #düğüm kapasiteleri

    max_flow = pywrapgraph.SimpleMaxFlow()
    for i in range(0, len(start_nodes)):
        max_flow.AddArcWithCapacity(start_nodes[i], end_nodes[i], capacities[i])

    #0 düğümü ile 6 düğümü arasındaki maksimum akışı bul
    if max_flow.Solve(0, 6) == max_flow.OPTIMAL:
        print('Max flow:', max_flow.OptimalFlow())
        print('------------------------')
        print('  Arc    Flow / Capacity')
        for i in range(max_flow.NumArcs()):
            print('%1s -> %1s   %3s  / %3s' % (
            max_flow.Tail(i),
            max_flow.Head(i),
            max_flow.Flow(i),
            max_flow.Capacity(i)))
    else:
        print('There was an issue with the max flow input.')

if __name__ == '__main__':
    solution()