Preciso de um auxilio sobre um codigo que eu fiz

20 views
Skip to first unread message

Ti Gama

unread,
Aug 28, 2024, 8:14:52 AM8/28/24
to Grupy-AM
Olá, bom dia 

Eu fiz uma automação no python , e ele gera um dash conectado em um BD que eu tenho , so que o problema que ele so atualiza o dash , quando eu paro o codigo e executo novamente , teria algum meio de fazer isso sem ter que parar o processo e executar novamente??

Segue o codigo:

import pandas as pd
import dash
from dash import dcc, html, dash_table
from dash.dependencies import Input, Output, State
from sqlalchemy import create_engine, text

# Criação da instância do Dash
app = dash.Dash(__name__, suppress_callback_exceptions=True)


# Configuração da conexão com o banco de dados Protheus12 usando SQLAlchemy
server = 'xxxxxxxxx'
database = 'xxxxxxxxxxx'
username = 'xxxxxxxxxxxx'
password = 'xxxxxxxxxxxxxx'
driver = 'ODBC Driver 18 for SQL Server'

# Conectando ao banco de dados usando SQLAlchemy
connection_string = (
    f'mssql+pyodbc://{username}:{password}@{server}/{database}?'
    f'driver={driver}&Encrypt=yes&TrustServerCertificate=yes'
)
engine = create_engine(connection_string)

# Definindo a consulta SQL
query = '''
SELECT
    CASE
        WHEN (SELECT TOP 1 [Aprovador] FROM VW_MN_PEDIDOS_COMPRA_EM_APROVACAO
                WHERE [Pedido] = PD.[Num.PC]) IS NOT NULL
        THEN (SELECT TOP 1 [Aprovador] FROM VW_MN_PEDIDOS_COMPRA_EM_APROVACAO
                WHERE [Pedido] = PD.[Num.PC])
        WHEN (SELECT TOP 1 [Aprovador] FROM VW_MN_PEDIDOS_COMPRA_EM_APROVACAO
                WHERE [Pedido] = PD.[Num.PC]) IS NULL AND PD.[Num.PC] IS NOT NULL THEN 'Aprovado'
        ELSE 'Em Cotação'
    END [Status],
    PD.[Num.PC], SC.[NÚM SC] AS [Numero da SC],
    ISNULL(PD.[Solicitante], SC.[SOLICITANTE]) AS [Solicitante],
    ISNULL(PD.[Observacoes], SC.[OBSERVAÇÃO]) AS [Observacoes],
    PD.[Fornecedor], PD.[Loja], PD.[Razao Social],
    ISNULL(PD.[Produto], SC.[PRODUTO]) AS [Produto],
    ISNULL(PD.[Descricao], SC.[DESCRIÇÃO]) AS [Descricao],
    CONVERT(VARCHAR, CONVERT(DATE, ISNULL(PD.[Emissao], SC.[EMISSÃO])), 103) AS [Emissao],
    [Entrega],
    ISNULL(PD.[UM], SC.[UOM]) AS [UM],
    ISNULL(PD.[Quantidade], SC.[QUANT ORIGINAL]) AS [Qtd. Original],
    [Prc Unitario], [Vlr.Total], [Qtd.Entregue],
    ISNULL(PD.[Quant.Receber], SC.[SALDO COMPRAR]) AS [Saldo],
    [Saldo Receber],
    ISNULL(PD.[Tipo], SC.[TIPO]) AS [TIPO],
    ISNULL(PD.[C Custo], SC.[C Custo]) AS [C Custo],
    ISNULL(PD.[DEPARTAMENTO], SC.[DEPARTAMENTO]) AS [Departamento],
    [Cta Contabil], [Moeda], [Tx Dolar]
FROM VW_MN_PEDIDOS_COMPRA_EM_ABERTO AS PD
FULL JOIN VW_MN_SC_COMPRA_EM_ABERTO AS SC
    ON PD.[Item SC] = SC.[ITEM SC] AND PD.[Numero da SC] = SC.[NÚM SC]
    AND SC.[EMISSÃO] >= '20230101'
'''
# Executando a consulta e armazenando o resultado em um DataFrame do pandas
with engine.connect() as connection:
    df = pd.read_sql(text(query), connection)

df['Emissao'] = pd.to_datetime(df['Emissao'])

# Limpar a coluna 'C Custo' antes de converter para inteiros
df['C Custo'] = df['C Custo'].astype(str).str.strip()  # Remove espaços em branco
df['C Custo'] = pd.to_numeric(df['C Custo'], errors='coerce')  # Converte para numérico, transformando valores inválidos em NaN
df.dropna(subset=['C Custo'], inplace=True)  # Remove linhas com NaN na coluna 'C Custo'
df['C Custo'] = df['C Custo'].astype(int)  # Converte para inteiros
df = df.sort_values(by=['Emissao'], ascending=[False])
df['Emissao'] = df["Emissao"].dt.strftime('%d/%m/%Y')

# Mapeamento de C Custo para seus nomes ou descrições
ccusto_mapping = {
'1011': 'PRESIDENCIA',
'1012': 'CORPORATIVO',
'1021': 'PLANEJAMENTO',
'1022': 'GERENCIA - PLANEJAMENTO',
'1023': 'PLANEJAMENTO',
'1031': 'T.I.',
'1032': 'GERENCIA - TI',
'1033': 'TI AM',
'1041': 'RECRUTAMENTO E SELECAO',
'1042': 'MANUTENCAO',
'1043': 'SERVICOS GERAIS',
'1044': 'DP. JURIDICO',
'1045': 'ADMINISTRACAO DE PESSOAL AM',
'1046': 'ADMINISTRACAO DE PESSOAL',
'1047': 'APRENDIZ',
'1048': 'LOJA GAMA',
'1049': 'GERENCIA - RH',
'1051': 'ADM - LOGISTICA',
'1052': 'ESTOQUE',
'1053': 'IMPORTACAO',
'1054': 'POS VENDA - SAC',
'1055': 'POS VENDAS - POSTOS',
'1056': 'ASSIST TECNICA',
'1057': 'GERENCIA - LOGISTICA',
'1058': 'RETRABALHO - LOGISTICA',
'1059': 'LOGISTICA - SC',
'1061': 'COMPRAS',
'1071': 'TRIBUTOS',
'1072': 'REC/DESP FINANCEIRA',
'1081': 'CONTAS A PAGAR',
'1082': 'CREDITO E COBRANCA',
'1083': 'GERENCIA - FINANCEIRO',
'1091': 'CONTROLADORIA',
'1092': 'APOIO ADM E LEGAL',
'1093': 'CONSULTOR EXTERNO',
'1094': 'GERENCIA - CONTROLADORIA',
'1095': 'CONTROLADORIA AM',
'1111': 'DIRETORIA COMERCIAL',
'1112': 'NORTE/ NORDESTE',
'1113': 'ELETRO',
'1114': 'BELEZA',
'1115': 'HUMMER',
'1116': 'DIRETORIA COMERCIAL',
'1117': 'TELEVENDAS',
'1118': 'GERENCIA - ADM DE VENDAS',
'1119': 'ADM. VENDAS',
'1121': 'MARKETING',
'1122': 'IGI',
'1123': 'EDUCACAO PROFISSIONAL',
'1124': 'TRADE MARKETING (EXTERNO)',
'1125': 'MKT - PROFISSIONAL',
'1126': 'LOJA GAMA',
'1127': 'CERTIFICACOES',
'1128': 'TRADE MARKETING INTERNO',
'1129': 'RETRABALHO MKT',
'1131': 'CONTAS ESPECIAIS',
'1132': 'GERENCIA - CONT ESPECIAIS',
'1133': 'NOVOS NEGOCIOS',
'1134': 'INTELIGENCIA DE MERCADO',
'1135': 'GERENCIA COMERCIAL',
'1136': 'E-COMMERCE',
'1141': 'REGIONAIS',
'1142': 'TRADE MARKETING (INTERNO)',
'1143': 'COMERCIAL - PROFISSIONAL',
'1144': 'CONTAS-CHAVE 4',
'1145': 'REGIONAL BA+SE+NORTE',
'1146': 'REGIONAL NORDESTE',
'1147': 'REGIONAL RJ+ES',
'1148': 'REGIONAL SP INTERIOR',
'1149': 'REGIONAL SP CAPITAL',
'1150': 'REGIONAL MG+TO',
'1151': 'BRAUN',
'1152': 'REGIONAL SUL',
'1153': 'CANAL BELEZA',
'1161': 'TRANSITORIA AM',
'1200': 'CONTROLADORIA',
'1201': 'ALMOXARIFADO',
'1202': 'QUALIDADE',
'1203': 'ENGENHARIA DE PROCESSOS',
'1204': 'MANUTENCAO',
'1205': 'COMPRAS',
'1206': 'FERRAMENTARIA',
'1207': 'TECNOLOGIA DA INFORMACAO',
'1208': 'ENGENHARIA DE PRODUTOS',
'1209': 'GERENCIA INDUSTRIAL',
'1210': 'RECURSOS HUMANOS',
'1211': 'PCPM',
'1212': 'SESMT',
'1213': 'FACILITIES',
'1214': 'MOD IQ PERFETTO',
'1215': 'MOI IQ PERFETTO',
'1216': 'PCP',
'1217': 'FERRAMENTARIA',
'1218': 'PRODUCAO INJ PLASTICA',
'1219': 'PRODUTO ACABADO PRANCHA',
'1220': 'SALA DE PINTURA',
'1221': 'PRODUTO ACABADO SECADOR',
'1222': 'SETOR TAMPOGRAFIA',
'1223': 'MOI  PRANCHA',
'1224': 'MOI SECADOR',
'1225': 'SETOR CALEFATOR',
'1226': 'PLACA SECADOR',
'1227': 'SUBCONJUNTO PATIM',
'1228': 'SETOR ESTAMPARIA',
'1229': 'SETOR PINTURA',
'1230': 'MOI INJ PLASTICA',
'1231': 'MOI TAMPOGRAFIA',
'1232': 'MOI PINTURA',
'1233': 'TECNOLOGIA DA INFORMACAO',
'1234': 'JOVEM APRENDIZ',
'1300': 'PRODUCAO INJ PLASTICA',
'1301': 'PRODUTO ACABADO PRANCHA',
'1302': 'PRODUTO ACABADO SECADOR',
'1303': 'TAMPOGRAFIA',
'1304': 'MOI PRANCHA',
'1305': 'MOI SECADOR',
'1306': 'ESTAMPARIA',
'1307': 'PINTURA',
'1308': 'MOI INJ PLASTICA',
'1309': 'MOI TAMPOGRAFIA',
'1310': 'MOI PINTURA',
'1311': 'CALEFATOR',
'1312': 'MOI CALEFATOR',
'1313': 'DESP COM CONDOMINIO',
'1314': 'MOI IQ PERFETTO',
'1511': 'MDS - MEN DRY SAVING (BARBEADORES)',
'1512': 'FEHR-FEMALE ELETRIC HAIR REMOVAL -DEPILD',
'1513': 'ETB-ELETRIC TOOTH BRUSHES-ESCV DENTE ELE',
'1514': 'IRONS (PRANCHAS)',
'1515': 'HAIR DRYERS (SECADORES)',
'1516': 'HOUSE HOLD APPLIANCES (ELETRODOMESTICOS)',
'1521': 'OTHER EXPENSES (OUTRAS DESPESAS)',
'1601': 'ISSUE - OUTRAS DESPESAS',
'2000': 'AUTO SERVICOS',
'2001': 'CATALOGOS PREMIOS',
'2002': 'DEPARTAMENTOS',
'2003': 'ESPECIALIDADE',
'2004': 'FARMACIA',
'2005': 'INDIRETO PROFISSIONAL',
'2006': 'GAMA ITALY DO BRASIL',
'2007': 'GM DUNA INVERSIONES',
'2008': 'DUNA ENTERPRISES S.L',
'2009': 'SAFERY S.A',
'2010': 'DISTRI BEAUTY S.A',
'2011': 'ARIMEX IMPORTADORA S.A',
'2012': 'GAMA CHILE S.A',
'2013': 'ITALIAN BEAUTY S.A.C',
'2014': 'ITALIAN GROUP S.A DE C.V',
'2015': 'GA.MA COLOMBIA LTDA',
'7902': 'GIF PA PRANCHA',
'7903': 'GIF PA SECADOR',
'7904': 'GIF CALEFATOR',
'7907': 'GIF ESTAMPARIA',
'7910': 'GIF PLACA SECADOR',
'7911': 'GIF SALA DE PINTURA',
'7912': 'GIF TAMPOGRAFIA',
'7913': 'GIF INJ PLASTICA',
'7914': 'GIF CALEFATOR',
'7915': 'GIF MOLA',
'8902': 'MOI PA PRANCHA',
'8903': 'MOI PA SECADOR',
'8904': 'MOI CALEFATOR',
'8907': 'MOI ESTAMPARIA',
'8910': 'MOI PLACA SECADOR',
'8911': 'MOI SALA DE PINTURA',
'8912': 'MOI TAMPOGRAFIA',
'8913': 'MOI INJ PLASTICA',
'8914': 'MOI CALEFATOR',
'8915': 'MOI MOLA',
'9901': 'PROD. S.A. PCS INJETADAS',
'9902': 'PRODUTO ACABADO PRANCHA',
'9903': 'PRODUTO ACABADOR SECADOR',
'9904': 'MONTAGEM CALEFATOR',
'9905': 'INJECAO PLASTICA',
'9906': 'PATIN',
'9907': 'ESTAMPARIA',
'9908': 'PROD. S.A. PATIN',
'9909': 'PROD. S.A. PLACA',
'9910': 'MONTAGEM PLACA',
'9911': 'MOD SALA DE PINTURA',
'9912': 'MOD TAMPOGRAFIA',
'9913': 'INJECAO PLASTICA',
'9914': 'MOD CALEFATOR',
'9915': 'MOD MOLA',
}

# Dicionário de mapeamento de C Custo para senhas
ccusto_passwords = {
    '1012': '9584', '1031': '6934', '1044': '9546', '1046': '9271',
    '1051': '9164', '1082': '3964', '1091': '2591', '1125': '3964',
    '1129': '5612', '1133': '7894', '1136': '5294', '1141': '1265',
    '1153': '9856', '1200': '7486', '1201': '9632', '1202': '7412',
    '1203': '8521', '1204': '6412', '1206': '5973', '1207': '8462',
    '1208': '5971', '1210': '3519', '1212': '2587', '1213': '2589',
    '1300': '9651', '1301': '7541', '1302': '4567', '1307': '7823',
    '1311': '0235'
}

# Função para criar o dropdown de "C Custo" com nome e número
def create_ccusto_dropdown(ccusto_list):
    return dcc.Dropdown(
        id='ccusto-dropdown',
        options=[{'label': f'{c} - {ccusto_mapping.get(str(c), "Desconhecido")}', 'value': c} for c in ccusto_list],
        placeholder='Selecione o C Custo',
        style={'width': '50%', 'marginBottom': '20px'}
    )

# Layout do login
login_layout = html.Div(style={'display': 'flex', 'alignItems': 'center', 'justifyContent': 'center', 'height': '100vh', 'backgroundColor': '#f0f0f0'}, children=[
    html.Div(style={'width': '300px', 'padding': '20px', 'borderRadius': '10px', 'boxShadow': '0px 4px 8px rgba(0, 0, 0, 0.1)', 'backgroundColor': '#ffffff'}, children=[
        html.H2('Login', style={'textAlign': 'center', 'color': '#003f7f'}),
        dcc.Input(id='username', type='text', placeholder='C Custo', style={'width': '95%', 'padding': '10px', 'marginTop': '10px', 'marginBottom': '10px', 'borderRadius': '5px', 'border': '1px solid #ddd'}),
        dcc.Input(id='password', type='password', placeholder='Senha', style={'width': '95%', 'padding': '10px', 'marginBottom': '10px', 'borderRadius': '5px', 'border': '1px solid #ddd'}),
        html.Button('Entrar', id='login-button', style={'width': '100%', 'padding': '10px', 'borderRadius': '5px', 'backgroundColor': '#003f7f', 'color': 'white', 'border': 'none', 'cursor': 'pointer'})
    ])
])

# Layout do dashboard
app.layout = html.Div([
    dcc.Location(id='url', refresh=False),
    html.Div(id='page-content', children=login_layout)
])

# Função para renderizar o dashboard
def render_dashboard(filtered_df, ccusto_options):
    return html.Div([
        html.H1('Pedidos de Compra em Aberto', style={'fontSize': '18px', 'color': '#003f7f', 'textAlign': 'center'}),
        create_ccusto_dropdown(ccusto_options),
        dash_table.DataTable(
            id='table',
            columns=[
                {"name": "Num.PC", "id": "Num.PC"},
                {"name": "Numero da SC", "id": "Numero da SC"},
                {"name": "Status", "id": "Status"},
                {"name": "Solicitante", "id": "Solicitante"},
                {"name": "Fornecedor", "id": "Fornecedor"},
                {"name": "Loja", "id": "Loja"},
                {"name": "Razao Social", "id": "Razao Social"}
            ],
            data=filtered_df[['Num.PC', 'Numero da SC', 'Status', 'Solicitante', 'Fornecedor', 'Loja', 'Razao Social']].drop_duplicates().to_dict('records'),
            page_size=10,
            style_table={'overflowX': 'auto', 'border': '1px solid #ddd'},
            style_cell={'height': 'auto', 'minWidth': '100px', 'width': '100px', 'maxWidth': '100px',
                        'whiteSpace': 'normal', 'textAlign': 'left', 'border': '1px solid #ddd', 'fontSize': '10px', 'backgroundColor': '#ffffff'},
            style_header={'border': '1px solid #ddd', 'backgroundColor': '#003f7f', 'color': 'white', 'fontSize': '12px'},
            row_selectable='single', selected_rows=[],
            style_data_conditional=[
                {
                    'if': {'column_id': 'Status', 'filter_query': '{Status} = "Aprovado"'},
                    'backgroundColor': '#d4edda',
                    'color': 'black',
                },
                {
                    'if': {'column_id': 'Status', 'filter_query': '{Status} != "Aprovado"'},
                    'backgroundColor': '#fff3cd',
                    'color': 'black',
                },
            ]
        ),
        html.Div(id='details-section', style={'marginTop': '10px', 'border': '1px solid #ddd', 'padding': '5px',
                                              'borderRadius': '5px', 'boxShadow': '0px 4px 8px rgba(0, 0, 0, 0.1)',
                                              'fontSize': '12px', 'backgroundColor': '#ffffff'})
    ])

# Callback para autenticação e redirecionamento
@app.callback(
    Output('page-content', 'children'),
    [Input('login-button', 'n_clicks')],
    [State('username', 'value'), State('password', 'value')]
)
def login(n_clicks, username, password):
    if n_clicks is None:
        return login_layout

    if ((username == 'Master' and password == '1234') or
    (username == '1206' and password == '5973')):
        ccusto_options = [int(c) for c in df['C Custo'].astype(str).unique()]
        return render_dashboard(df, ccusto_options)

    if username in ccusto_passwords and password == ccusto_passwords[username]:
        ccusto = int(username)
        filtered_df = df[df['C Custo'].astype(int) == ccusto]
        ccusto_options = [ccusto]
        return render_dashboard(filtered_df, ccusto_options)

    return html.Div("Credenciais inválidas. Tente novamente.", style={'textAlign': 'center', 'color': 'red'})

# Callback para atualizar a tabela com base no dropdown de C Custo
@app.callback(
    Output('table', 'data'),
    [Input('ccusto-dropdown', 'value')]
)
def update_table(ccusto_selected):
    if ccusto_selected is None:
        return []

    if ccusto_selected == 'Master':
        return df[['Num.PC', 'Status', 'Numero da SC', 'Solicitante', 'Fornecedor', 'Loja', 'Razao Social']].drop_duplicates().to_dict('records')
   
    return df[df['C Custo'] == ccusto_selected][['Num.PC', 'Status', 'Numero da SC', 'Solicitante', 'Fornecedor', 'Loja', 'Razao Social']].drop_duplicates().to_dict('records')

# Callback para atualizar os detalhes do pedido selecionado
@app.callback(
    Output('details-section', 'children'),
    [Input('table', 'selected_rows')],
    [State('table', 'data')]
)
def update_details(selected_rows, data):
    if not selected_rows:
        return html.Div("Selecione um pedido para ver os detalhes.")
   
    selected_row_index = selected_rows[0]
    selected_num_pc = data[selected_row_index]['Num.PC']

    pedido_detalhado = df[df['Num.PC'] == selected_num_pc]

    if pedido_detalhado.empty:
        selected_sc = data[selected_row_index]['Numero da SC']
        pedido_detalhado = df[df['Numero da SC'] == selected_sc]
   
    if pedido_detalhado.empty:
        return html.Div(f"Nenhum detalhe encontrado para Num.PC: {selected_num_pc}.")
   
    columns_to_show = [col for col in pedido_detalhado.columns if col not in ['Status', 'Solicitante', 'Fornecedor', 'Loja', 'Razao Social']]
   
    if 'Status' not in columns_to_show:
        columns_to_show.append('Status')
   
    return html.Table(style={'width': '100%', 'borderCollapse': 'collapse', 'border': '1px solid #ddd'}, children=[
        html.Thead(html.Tr([html.Th(col, style={'border': '1px solid #ddd', 'padding': '8px', 'backgroundColor': '#003f7f', 'color': 'white', 'fontSize': '10px'})
                            for col in columns_to_show])),
        html.Tbody([html.Tr([html.Td(pedido_detalhado.iloc[0][col], style={'border': '1px solid #ddd', 'padding': '8px', 'fontSize': '10px'})
                             for col in columns_to_show])])
    ])

# Configuração do servidor para tornar o aplicativo acessível em outras máquinas
if __name__ == '__main__':
    app.run_server(host='127.0.0.1', port=8050, debug=False)
Reply all
Reply to author
Forward
0 new messages