# ---
# jupyter:
#   jupytext:
#     text_representation:
#       extension: .py
#       format_name: light
#       format_version: '1.5'
#       jupytext_version: 1.14.4
#   kernelspec:
#     display_name: Python 3
#     language: python
#     name: python3
# ---

# +
import pandas as pd
import numpy as np
from datetime import datetime
from sklearn.ensemble import IsolationForest
from sklearn.decomposition import PCA
import matplotlib.pyplot as plt

from plotly import plotly as py
from plotly.offline import init_notebook_mode, iplot
import plotly.graph_objs as go
init_notebook_mode(connected=True)

from sqlalchemy import create_engine
import warnings
warnings.filterwarnings('ignore')

# +
from pricing.service.scoring.lscore import LScoring
from sqlalchemy import create_engine
import numpy as np
import pandas as pd
import requests
from time import sleep
from datetime import datetime
from conector.mysql import mysql_engine, CaptalysDBContext
from dateutil.relativedelta import relativedelta

class DScoring(object):
    def __init__(self, cnpj, produto, socios=False, baseline_type = 'lscore'):
        self.cnpj = cnpj
        self.doctype = 'cpf' if len(self.cnpj)<12 else 'cnpj'
        self.baseline_type = baseline_type
        self.score_socios = socios
        self.produto = produto
        self.lscore = None
        self.baseline = 1000
        self.fator_elegibilidade = 2
        self.faturamento_medio = None
        self.calibracao_segmento = None
        self.consulta = None
        self.estados_dividas = None
        self.dispersao_divida = None
        self.idade_empresa = None
        self.metricas = None
        
    def score_mestre(self):
        ls = LScoring(cnpj=self.cnpj, produto=self.produto)
        
        df = pd.DataFrame(ls.params)
        periodo = len(df)

        datas = pd.date_range(end=datetime.now().date().replace(day=1) - relativedelta(months=1), periods=periodo, freq='MS')
        datas = [el.date() for el in datas]
        df['data'] = datas

        params = df.to_dict("records")
        ls.params = params
        lscore = ls.calcula().get('score')
        fat_medio = ls.faturamentos['valor'].mean()
        self.lscore = lscore
        self.faturamento_medio = fat_medio
        return
    
    
    @property
    def campos_divida(self):
        return {
                "restricoes" : ["data_ocorrencia", "modalidade_natureza", "natureza", "valor"],
                "protestos" : ["data_anotacao", "natureza", "sub_judice_descricao", "valor"],
                "pendencias" : ["data_ocorrencia", "modalidade", "natureza", "valor"],
                "processos" : ["data_ocorrencia", "descricao_natureza", "natureza", "valor"],
                "restricoes_financeiras" : ["data_ocorrencia", "modalidade_natureza", "natureza", "valor"]
               }
    @property
    def campos_rename(self):
        return {
                "processos" : {"descricao_natureza" : "modalidade_natureza"},
                "pendencias" : {"modalidade" : "modalidade_natureza"},
                "protestos" : {'sub_judice_descricao' : "modalidade_natureza", "data_anotacao" : "data_ocorrencia"}
                }
    
    @property
    def segmentos(self):
        return {"credito" : ['EMPRESCONTA', 'EMPRESTIMO', 'CREDCARTAO', 'FINANCIAMENT', 
                             'CREDITOEFINANCIAMENTO-FINANC'],
                "processos" : ['EXCJUDTRAB', 'FISCALESTADUAL', 'EXECUCAO', 'FISCALFEDERAL', 
                               'FISCALMUNICIPAL','EXECUCAO-JE', 'BUSCAEAPREENSAO'],
                "infra" : ['FATAGUA', 'TELEFFX', 'TELEFFIXA', 'TELEFMOVEL', 'CONDOMINIO', 
                           'ENERGIAELET', 'ALUGUEL', 'SERVTELEFON'] 
               }
    

    @staticmethod
    def get_numero_consulta(doc):
        engine = create_engine("mysql+pymysql://capMaster:#jackpot123#@<EMAIL>:23306/varejo")
        con = engine.connect()
        query = "select data_ref, numero_consulta from consultas_idwall_operacoes where cnpj_cpf='{}'".format(doc)
        df = pd.read_sql(query, con)
        numero = df[df['data_ref']==df['data_ref'].max()]["numero_consulta"].iloc[0]
        con.close()
#         self.numero_consulta = numero
        return numero
    
    @staticmethod
    def get_details(numero):
        URL = "https://api-v2.idwall.co/relatorios"
        authorization = "b3<PASSWORD>-<PASSWORD>-4<PASSWORD>-ade8-78a<PASSWORD>"
        url_details = URL + "/{}".format(numero) + "/dados"
        while True:
            dets = requests.get(url_details, headers={"authorization": authorization})
            djson = dets.json()
            sleep(1)
            if djson['result']['status'] == "CONCLUIDO":
                break

        return dets.json()
    
    @staticmethod
    def formata_dados(df):
        df['modalidade_natureza'] = df.apply(lambda x : x['modalidade_natureza'].replace(" ", "") if isinstance(x['modalidade_natureza'], str) else "OUTROS", axis=1)
        df['valor'] = df.apply(lambda x : x['valor'].split("R$ ")[1].replace(",", "."), axis=1)
        df["valor"] = df.apply(lambda x : float(x["valor"]), axis=1)
        return df
    
    def get_infos_dividas(self, js, tp_pendencia):
        res = js.get("result").get(tp_pendencia)
        if not res is None:
            df = pd.DataFrame(res.get('itens'))
            cols = self.campos_divida.get(tp_pendencia)
            if "uf" in list(df.columns):
                cols = cols + ["uf"]
                df = df[cols].copy()
            else:
                df = df[cols]
                df["uf"] = None
            rename = self.campos_rename.get(tp_pendencia)
            if not rename is None:
                df.rename(columns = rename, inplace=True)
            df["tipo"] = tp_pendencia
            return df
        return None
    
    
    def gera_dados(self, doc):
        numero = self.get_numero_consulta(doc)
        js = self.get_details(numero)
        if len(doc) > 11:
            self.consulta = js
        fr = []
        lista_pendencias = ["restricoes", "processos", "protestos", "pendencias", "restricoes_financeiras"]
        for el in lista_pendencias:
            res = self.get_infos_dividas(js, el)
            if not res is None:
                fr.append(res)
        if len(fr) == 0:
            return pd.DataFrame()
        df = pd.concat(fr)
        df = self.formata_dados(df)
        if len(doc) > 11:
            self.estados_dividas = df["uf"].unique().tolist()
        return df
    
    def calcula_dispersao_divida(self):
        uf_cnpj = self.consulta.get("result").get("cnpj").get("localizacao").get("estado")
        lista_dispersao = [el for el in self.estados_dividas if el!= uf_cnpj]
        print(lista_dispersao)
        dispersao = len(lista_dispersao)/4
        self.dispersao_divida = dispersao
        return
    
    def get_idade(self):
        data_abertura = self.consulta.get("result").get("cnpj").get("data_abertura")
        data_abertura = data_abertura.replace("/", "-")
        data = datetime.strptime(data_abertura, "%d-%m-%Y").date()
        idade = ((datetime.now().date() - data).days/366)
        self.idade_empresa = np.around(idade, 2)
        return 

    
    def atribui_segmento(self, df):
        df['segmento'] = df.apply(lambda x : 'processos' if x['tipo']=='processos'
                              else('credito' if x['modalidade_natureza'] in self.segmentos.get("credito")
                                  else ('infra' if x['modalidade_natureza'] in self.segmentos.get("infra") else "outros")), axis=1)
        return df
    
    @staticmethod
    def calcula_probabilidade(df):
        dt = df.groupby("segmento").count().reset_index()[["segmento", "valor"]]
        dt.columns = ["segmento", "ocorrencias"]
        dt["probabilidade"] = dt["ocorrencias"]/dt["ocorrencias"].sum()
        return dt
    
    @staticmethod
    def calcula_composicao(df):
        dt = df.groupby("segmento").sum().reset_index()
        dt.columns = ["segmento", "valor_divida"]
        dt["composicao"] = dt["valor_divida"]/dt["valor_divida"].sum()
        return dt
    
    
    def calcula_pi(self, dfcalc):
        dfcalc['pi'] = dfcalc['valor_divida']/dfcalc['fat_medio']
        dfcalc['pi'] = (1/self.fator_elegibilidade)*dfcalc['pi']
        return dfcalc

    @property
    def peso_segmento(self):
        return {
            "credito" : 4,
            "processos" : 3,
            "infra" : 2,
            "outros" : 1
        }
    
    @property
    def fator_segmento(self):
        return {"credito" : 1, "processos" : 0.8, "infra" : 0.6, "outros" : 0.4}
    
    def lambda_(self, c, p, segmento):
        f = self.fator_segmento.get(segmento)
        return c*p*f
    
    def calcula_lambda(self, dfcalc):
        dfcalc["lambda"] = dfcalc.apply(lambda x : self.lambda_(x["composicao"], x["pi"], x["segmento"]), axis=1)
        return dfcalc
    
    @staticmethod
    def calcula_risco(dfcalc):
        dfcalc["risco"] = dfcalc["probabilidade"]*dfcalc["lambda"]
        return dfcalc
    
    @staticmethod
    def d_score(risco_, score_limite):
        return -score_limite*risco_ + score_limite


    def calcula_dscore(self, dfcalc):
        if self.baseline_type == 'lscore':
            score_limite = 1*self.lscore
        else:
            score_limite = self.baseline
        dfcalc["dscore"] = dfcalc.apply(lambda x : self.d_score(x["risco"], score_limite) if x["pi"] <=1 else 0, axis=1)
        return dfcalc
    
    def get_metricas(self, dfcalc):
        segmentos = ["credito", "processos", "infra", "outros"]
        final = {}
        for el in segmentos:
            dt = dfcalc[dfcalc["segmento"]==el]
            res = {}
            if dt.empty:
                res["num_ocorr"] = 0
                res["comp"] = 0
                res["risco"] = 0
                final[el] = res
            else:
                res["num_ocorr"] = dt["ocorrencias"].iloc[0]
                res["comp"] = dt['composicao'].iloc[0]
                res["risco"] = dt["risco"].iloc[0]
                final[el] = res
        self.metricas = final
        return
    
    def update_dataset(self):
        df_metricas = pd.DataFrame()
        df_metricas["cnpj"] = [self.cnpj]
        df_metricas["produto"] = [self.produto]
        df_metricas["data_metricas"] = [datetime.now().date()]
        df_metricas["data_modelo"] = [None]
        df_metricas["num_ocorr_cr"] = [self.metricas.get('credito').get('num_ocorr')]
        df_metricas["num_ocorr_proc"] = [self.metricas.get('processos').get('num_ocorr')]
        df_metricas["num_ocorr_infra"] =  [self.metricas.get('infra').get('num_ocorr')]
        df_metricas["num_ocorr_out"] = [self.metricas.get('outros').get('num_ocorr')]
        df_metricas["comp_cr"] = [self.metricas.get('credito').get('comp')]
        df_metricas["comp_proc"] = [self.metricas.get('processos').get('comp')]
        df_metricas["comp_infra"] = [self.metricas.get('infra').get('comp')]
        df_metricas["comp_out"] = [self.metricas.get('outros').get('comp')]
        df_metricas["risco_cr"] = [self.metricas.get('credito').get('risco')]
        df_metricas["risco_proc"] = [self.metricas.get('processos').get('risco')]
        df_metricas["risco_infra"] = [self.metricas.get('infra').get('risco')]
        df_metricas["risco_out"] = [self.metricas.get('outros').get('risco')]
        df_metricas["idade_empresa"] = [self.idade_empresa]
        df_metricas["dispersao_divida"] = [self.dispersao_divida]
        df_metricas["outlier"] = [None]
        
        
        engine = create_engine("mysql+pymysql://capMaster:#jackpot123#@captalys.cmrbivuuu7sv.sa-east-1.rds.amazonaws.com:23306/credit_model")
        con = engine.connect()
        
        con.execute("delete from outlier_detection where cnpj='{}'".format(self.cnpj))
        df_metricas.to_sql('outlier_detection', schema='credit_model', con=con, if_exists='append', index=False)
        
        con.close()
        
        return
    
    def get_socios(self):
        schema = 'credito-digital' if self.produto != 'moip' else self.produto
        engine = mysql_engine(schema)
        query = "select cpf from tb_Socio where cnpj='{}'".format(self.cnpj)
        with CaptalysDBContext(engine) as db:
            res = db.session.execute(query).fetchall()

        lista_socios = [el[0] for el in res]
        self.lista_socios = lista_socios
        return lista_socios
    
    def calcula_socios(self):
        lista_socios = self.get_socios()
        resp = []
        for el in lista_socios:
            _df = self.gera_dados(el)
            if not _df.empty:
                resp.append(_df)
        if len(resp) == 0:
            return np.nan
        
        df = pd.concat(resp)
        df = self.atribui_segmento(df)
        dfp = self.calcula_probabilidade(df)
        dfc = self.calcula_composicao(df)
        dfcalc = dfp.merge(dfc, left_on="segmento", right_on="segmento", how='left')
        dfcalc['fat_medio'] = self.faturamento_medio
        dfcalc = self.calcula_pi(dfcalc)
        dfcalc = self.calcula_lambda(dfcalc)
        
        dfcalc = self.calcula_risco(dfcalc)
        dfcalc = self.calcula_dscore(dfcalc)
        self.get_metricas(dfcalc)
        dscore = dfcalc['dscore'].mean()
        
        lista_segmentos = dfcalc["segmento"].tolist()
        lista_dscore = dfcalc["dscore"].tolist()
        lista_dscore = [int(el) for el in lista_dscore]
        res = dict(zip(lista_segmentos, lista_dscore))
        res["lscore"] = int(self.lscore)
        res['dscore'] = int(dscore)
        res['score'] = int((self.lscore + dscore)/2)
        return res, dfcalc
    
            
    def calcula(self, update=True):
        self.score_mestre()
        doc = self.cnpj
        df = self.gera_dados(doc)
        if df.empty:
            return {}, None
        self.calcula_dispersao_divida()
        self.get_idade()
        df = self.atribui_segmento(df)
        dfp = self.calcula_probabilidade(df)
        dfc = self.calcula_composicao(df)
        dfcalc = dfp.merge(dfc, left_on="segmento", right_on="segmento", how='left')

        dfcalc['fat_medio'] = self.faturamento_medio
        dfcalc = self.calcula_pi(dfcalc)
        dfcalc = self.calcula_lambda(dfcalc)
        dfcalc = self.calcula_risco(dfcalc)
        dfcalc = self.calcula_dscore(dfcalc)
        self.get_metricas(dfcalc)
        if update:
            self.update_dataset()
        
        dscore = dfcalc['dscore'].mean()
        
        lista_segmentos = dfcalc["segmento"].tolist()
        lista_dscore = dfcalc["dscore"].tolist()
        lista_dscore = [int(el) for el in lista_dscore]
        res = dict(zip(lista_segmentos, lista_dscore))
        res["lscore"] = int(self.lscore)
        res['dscore'] = int(dscore)
        res['score'] = int((self.lscore + dscore)/2)
        
        dfcalc["baseline_type"] = self.baseline_type
        dfcalc["baseline"] = self.lscore if self.baseline_type == 'lscore' else 1000
        dfcalc["fator_elegibilidade"] = self.fator_elegibilidade
        dfcalc["cnpj"] = self.cnpj
        dfcalc["produto"] = self.produto
        dfcalc["data_ref"] = datetime.now().date()
        return res, dfcalc

# if __name__ == '__main__':
#     ds = DScoring(cnpj='14534748000189', produto='tomatico')
#     res, dfcalc = ds.calcula(update=False)
#     print(dfcalc.columns)
#     print(res)
#     print(dfcalc)

# -

#escarlate
cnpj_exemplo = '14534748000189'
ds = DScoring(cnpj=cnpj_exemplo, produto='tomatico')
res, dfcalc = ds.calcula()

res

dfcalc

total_dividas = dfcalc['valor_divida'].sum()

total_dividas/47309.444167

df_modelo[df_modelo["cnpj"]=='21666147000195']

lista_cobranca = ["14534748000189",
"21666147000195",
"22861508000117",
"13339876000109",
"25401132000156",
"26998230000185",
"14550173000198",
"04015113000111",
"20694842000106"]



dfcobranca = df_modelo[df_modelo['cnpj'].isin(lista_cobranca)]

dfcobranca

engine = create_engine("mysql+pymysql://capMaster:#jackpot123#@captalys.cmrbivuuu7sv.sa-east-1.rds.amazonaws.com:23306/credit_model")
con = engine.connect()
dfscore = pd.read_sql("select * from debt_score")


def getData():
    engine = create_engine("mysql+pymysql://capMaster:#jackpot123#@captalys.cmrbivuuu7sv.sa-east-1.rds.amazonaws.com:23306/credit_model")
    con = engine.connect()
    df = pd.read_sql("select * from outlier_detection", con)
    con.close()

    df_modelo = df[["cnpj", "num_ocorr_cr", "num_ocorr_proc", "num_ocorr_infra", "num_ocorr_out", "comp_cr", "comp_proc",
       "comp_infra", "comp_out", "risco_cr", "risco_proc", "risco_infra", "risco_out", "idade_empresa", "dispersao_divida"]]

    df_modelo.dropna(inplace=True)
#     df_modelo = df_modelo[df_modelo['cnpj'].isin[(['00000000000191', '60701190000104'])]]

    df_modelo["dispersao"] = df_modelo["dispersao_divida"]*(4/27)

    df_modelo.drop(columns=['dispersao_divida'], axis=1, inplace=True)
    df_modelo.index = df_modelo.cnpj
    df_modelo = df_modelo.drop("cnpj", 1)
    df_modelo.fillna(0, inplace=True)
    return df_modelo, df[["cnpj", "produto"]]


df_modelo, df = getData()

df_modelo.shape

df.head()

df_modelo.head()

df_modelo["dispersao"].max()

# +
# outlier detection

# +
X = df_modelo.copy()

outlier_detect =  IsolationForest(n_estimators=100,
                                 max_samples='auto', contamination=.5, max_features=X.shape[1], random_state=1)

outlier_detect.fit(X)

outliers_predicted = outlier_detect.predict(X)

df_modelo["outlier"] = outliers_predicted

df_modelo[df_modelo['outlier']==-1].head()
# -

df_modelo.reset_index(inplace=True)

df_modelo.shape


res = df_modelo.merge(df, left_on=['cnpj'], right_on='cnpj', how='left')

res = df_modelo.merge(df[["cnpj", "produto"]], left_on=['cnpj'], right_on='cnpj', how='left')

res[res['cnpj']=='93379337000132']

res[res['outlier']==1].describe()

res[res['outlier']==1]['comp_cr'].unique().tolist()

res[res['outlier']==1]['comp_proc'].unique().tolist()

res[res['outlier']==1]["comp_infra"].unique().tolist()



res[res['outlier']==-1].describe()

res[res['cnpj']=='14534748000189']

dfout = res[res['outlier']==-1]

dfout[dfout['cnpj']=='00226038000187']

dfout[dfout['comp_proc']==1]

dfout[dfout['num_ocorr_proc']==99]

dfout['num_ocorr_proc'].max()



import matplotlib.pyplot as plt

plt.hist(x=df_modelo["num_ocorr_proc"], bins=20)

plt.hist(x=df_modelo["num_ocorr_cr"], bins=20)

plt.hist(x=df_modelo["num_ocorr_infra"], bins=20)

plt.hist(x=df_modelo["num_ocorr_out"], bins=20)

plt.hist(x=df_modelo["comp_cr"], bins=20)

plt.hist(x=df_modelo["comp_proc"], bins=20)

plt.hist(x=df_modelo["comp_infra"], bins=20)

plt.hist(x=dfout["comp_infra"], bins=20)

plt.hist(x=df_modelo["comp_out"], bins=20)

plt.hist(x=df_modelo["risco_cr"], bins=20)

plt.hist(x=dfout["risco_cr"], bins=20)

plt.hist(x=df_modelo["risco_proc"], bins=20)

plt.hist(x=df_modelo["risco_infra"], bins=20)

plt.hist(x=dfout["risco_infra"], bins=20)

plt.hist(x=df_modelo["risco_out"], bins=20)

plt.hist(df_modelo['idade_empresa'])

plt.hist(df_modelo["dispersao"])

dfout.to_excel("modelo_outlier2.xlsx")







dt = df.reset_index()

dt.head()

dt.rename(columns={"dispersao_dividas" : "dispersao_divida"}, inplace=True)

# salva base de outliers
engine = create_engine("mysql+pymysql://capMaster:#jackpot123#@captalys.<EMAIL>.amazonaws.com:23306/varejo")
con = engine.connect()
dt.to_sql("outlier_detection", schema="varejo", con=con, if_exists='append', index=False)
con.close()

from sqlalchemy import create_engine

# +
# outliers visualization using PCA
# -

from sklearn.preprocessing import StandardScaler

X.head()

features = list(X.columns)

x = X.loc[:, features].values

x

x = StandardScaler().fit_transform(x)

x

y = df.loc[:, ["outlier"]].values

df_pca = X.copy()

df_pca.iloc[:, :] = x

# +
# df_pca["outlier"] = y
# -

df_pca.head()

# +
pca = PCA(n_components=2)

principalComponents = pca.fit_transform(df_pca)

df_pca = pd.DataFrame(data = principalComponents
             , columns = ['pc1', 'pc2'])

df_pca["outlier"] = outliers_predicted

fig = plt.figure(figsize = (8,8))
ax = fig.add_subplot(1,1,1) 
ax.set_xlabel('Principal Component 1', fontsize = 15)
ax.set_ylabel('Principal Component 2', fontsize = 15)
ax.set_title('2 component PCA', fontsize = 20)
targets = ["outlier", "inlier"]
colors = ['r', 'b']
for target, color in zip(targets,colors):
    indicesToKeep = df_pca['outlier'] == -1 if target == "outlier" else df_pca['outlier'] == 1
    ax.scatter(df_pca.loc[indicesToKeep, 'pc1']
               , df_pca.loc[indicesToKeep, 'pc2']
               , c = color
               , s = 50)
ax.legend(targets)
ax.grid()
# -

df.head()

df_out = df[df['outlier']==-1]

df_out = df_out.reset_index()

df_out.sort_values("idade").head(10)

df.sort_values('num_ocorr_infra', ascending=False)



df.sort_values("num_ocorr_proc", ascending=False).head()





StarcoderdataJupyterScriptsDedupFiltered