G-Scripts/draft_mar.py
daniel.rodrigues 736016ea99 att
2025-10-31 20:40:59 -03:00

549 lines
22 KiB
Python

# -*- coding: utf-8 -*-
import requests
import json
import socket
import pyodbc
import time
from requests.exceptions import RequestException
from datetime import datetime, timezone, timedelta
# URL da API
url = "https://mar-orders-bff-api.demanda-abastecimento.grupoboticario.digital/api/orderdraft/order-building-data?draftType=SEM"
# Cabeçalhos da requisição
headers = {
"accept": "*/*",
"accept-language": "pt-BR,pt;q=0.9,en-US;q=0.8,en;q=0.7",
"authorization": "Basic b2NVc2VySW50ZXJuYWw6Nk5RV0BOU2M1anpEUy1oeg==",
"content-type": "application/json",
"origin": "https://extranet.grupoboticario.com.br",
"priority": "u=1, i",
"referer": "https://extranet.grupoboticario.com.br/",
"sec-ch-ua": '"Not)A;Brand";v="99", "Google Chrome";v="127", "Chromium";v="127"',
"sec-ch-ua-mobile": "?0",
"sec-ch-ua-platform": '"Windows"',
"sec-fetch-dest": "empty",
"sec-fetch-mode": "cors",
"sec-fetch-site": "cross-site",
"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36"
}
# Lista de códigos das lojas
store_codes = [
"12522", "12817", "12818", "12820", "12823", "12824", "12826", "12828", "12829",
"12830", "12838", "13427", "14617", "19103", "20005", "20006", "20009", "20056",
"20057", "20441", "20858", "20968", "20969", "20970", "20986", "20988", "20989",
"20991", "20992", "20993", "20994", "20995", "20996", "20997", "20998", "20999",
"21000", "21001", "21068", "21277", "21278", "21296", "21375", "21381", "21383",
"21495", "21624", "21647", "22541", "3546", "4560", "5699", "910173", "910291",
"21007", "23665", "23712", "23705", "23711", "23704", "23707", "23702", "23703",
"23713", "23708", "23706", "23701", "23709","23475","23156","14668", "24253",
"24254", "24255", "24258", "24257", "24268", "24269", "24293", "23813"
]
# Mapeamento de códigos para nomes de categorias
category_map = {
"10040": "CUIDADOS COM A PELE",
"10090": "HOME CARE",
"10060": "DESODORANTES",
"10110": "MAQUIAGEM",
"10080": "GIFTS",
"10120": "OLEOS",
"10050": "CUIDADOS FACIAIS",
"10170": "UNHAS",
"10150": "SOLAR",
"10190": "CUIDADOS PETS",
"10160": "SUPORTE A VENDA",
"10030": "CUIDADOS COM A BARBA",
"10130": "PERFUMARIA",
"10100": "INFANTIL",
"10020": "CABELOS",
"10070": "EMBALAGENS",
"10140": "SABONETE CORPO",
"10010": "ACESSORIOS"
}
# Mapeamento de códigos para nomes de subcategorias
subcategory_map = {
"1004020125": "CUIDADOS COM O CORPO",
"1006020150": "DESODORANTE FEMININO",
"1013020285": "PERFUMARIA FEMININA",
"1006020155": "DESODORANTE MASCULINO",
"1013020290": "PERFUMARIA MASCULINA",
"1001020055": "ORGANIZADOR COSTURAVEL",
"1014020315": "MULTIFUNCIONAL",
"1019020430": "PETS",
"1016020385": "MATERIAL DE APOIO",
"1001020030": "ELETRONICOS",
"1001020045": "FUNCIONAIS MAQUIAGEM",
"1010020225": "PERFUMARIA",
"1010020200": "CABES",
"1010020205": "CUIDADOS COM O CORPO",
"1010020235": "SABONETES",
"1016020395": "PRM",
"1009020195": "AROMATIZACAO",
"1001020025": "CORPO E BANHO",
"1015020335": "POS-SOL",
"1015020360": "PROTETOR FACIAL",
"1015020355": "PROTETOR CORPO",
"1014020300": "CORPO",
"1005020135": "HIDRATANTES E TRATAMENTOS",
"1005020140": "LIMPEZA",
"1005020145": "MASCARA FACIAL",
"1001020015": "CAPINHA",
"1009020140": "LIMPEZA",
"1004020120": "CUIDADOS COM AS MAOS",
"1001020060": "ORGANIZADOR NAO COSTURAVEL",
"1002020085": "CONDICIONADOR",
"1004020130": "CUIDADOS COM OS PES",
"1002020090": "FINALIZADOR",
"1006020160": "DESODORANTE UNISSEX",
"1002020105": "TRATAMENTO CABELOS",
"1012020275": "CUIDADOS COM O CORPO",
"1014020305": "CUIDADOS INTIMOS",
"1002020095": "SHAMPOO/PRE-SHAMPOO",
"1003020110": "POS-BARBEAR",
"1003020115": "PRE-BARBEAR",
"1001020005": "BRINQUEDO",
"1008020190": "ESTOJO REGULAR",
"1011020255": "MAQUIAGEM MULTIFUNCIONAL",
"1011020260": "MAQUIAGEM OLHOS",
"1011020245": "MAQUIAGEM BOCA",
"1001020070": "PINCEIS",
"1007020170": "EMBALAGENS VENDAVEIS",
"1001020035": "EMBALAGEM PRESENTEAVEL",
"1011020265": "MAQUIAGEM ROSTO",
"1001020040": "EMBALAGEM PRODUTO",
"1011020270": "MAQUIAGEM SOBRANCELHAS",
"1013020295": "PERFUMARIA UNISSEX",
"1014020310": "MAOS",
"1002020100": "STYLING",
"1001020010": "CABES",
"1002020075": "2 EM 1",
"1015020365": "PROTETOR FACIAL COM COR",
"1012020280": "MULTIFUNCIONAL",
"1010020220": "MAQUIAGEM",
"1010020210": "DESODORANTE"
}
# Configurações de conexão com o banco de dados
conn = pyodbc.connect(
'DRIVER={ODBC Driver 18 for SQL Server};'
'SERVER=10.77.77.10;'
'DATABASE=GINSENG;'
'UID=supginseng;'
'PWD=Iphone2513@;'
'PORT=1433;'
'TrustServerCertificate=yes'
)
def check_internet_connection():
"""Verifica se há conexão com a internet"""
try:
socket.gethostbyname("google.com")
return True
except socket.error:
return False
def create_draft_temp_table(cursor):
"""Cria a tabela draft_temp se ela não existir"""
try:
# Verifica se a tabela já existe
cursor.execute("""
IF NOT EXISTS (SELECT * FROM sys.tables WHERE name = 'draft_temp' AND schema_id = SCHEMA_ID('dbo'))
BEGIN
CREATE TABLE dbo.draft_temp (
id INT IDENTITY(1,1) PRIMARY KEY,
date DATE DEFAULT CAST(GETDATE() AS DATE),
loja_id NVARCHAR(50),
code NVARCHAR(50),
description NVARCHAR(255),
launch NVARCHAR(50),
deactivation NVARCHAR(50),
thirdToLastCycleSales INT,
secondToLastCycleSales INT,
lastCycleSales INT,
currentCycleSales INT,
nextCycleProjection INT,
secondToNextCycleProjection INT,
stock_actual INT,
stock_inTransit INT,
purchaseSuggestion INT,
smartPurchase_purchaseSuggestionCycle INT,
smartPurchase_nextCyclePurchaseSuggestion INT,
pendingOrder INT,
salesCurve NVARCHAR(50),
promotions_description NVARCHAR(MAX),
promotions_discountPercent NVARCHAR(MAX),
priceSellin FLOAT,
businessUnit NVARCHAR(50),
codCategory NVARCHAR(255),
criticalItem_dtProvidedRegularization NVARCHAR(50),
criticalItem_blockedWallet BIT,
criticalItem_isCritical BIT,
codSubCategory NVARCHAR(255),
isProductDeactivated BIT,
brandGroupCode NVARCHAR(50),
daysWithoutSales INT,
coverageDays INT,
hasCoverage BIT
)
END
""")
conn.commit()
print("Tabela draft_temp verificada/criada com sucesso.")
except Exception as e:
print(f"Erro ao criar tabela draft_temp: {e}")
conn.rollback()
raise
def process_and_insert_data(store_code, response_data, cursor):
"""Processa os dados da API e insere na tabela draft_temp"""
try:
# Limpar dados da loja no banco antes de inserir novos dados
delete_sql = "DELETE FROM draft_temp WHERE loja_id = ?"
cursor.execute(delete_sql, (store_code,))
products = response_data.get('data', {}).get('products', [])
current_cycle = response_data.get('data', {}).get('currentCycle', '')
registros = []
for product in products:
promotions = product.get('promotions', [])
filtered_promotions = [promo for promo in promotions if promo.get('cycle') == current_cycle]
if filtered_promotions:
promotion_description = " | ".join(promo.get('description', '') for promo in filtered_promotions)
promotion_discount = " | ".join(str(promo.get('discountPercent', '')) for promo in filtered_promotions)
else:
promotion_description = ''
promotion_discount = ''
# Busca o nome da categoria no dicionário
cod_category = product.get('codCategory', '')
category_name = category_map.get(str(cod_category), '')
# Busca o nome da subcategoria no dicionário
cod_subcategory = product.get('codSubCategory', '')
subcategory_name = subcategory_map.get(str(cod_subcategory), '')
# Corrigir o campo criticalItem_dtProvidedRegularization
dt_provided_regularization = product.get('criticalItem', {}).get('dtProvidedRegularization', '')
if isinstance(dt_provided_regularization, list):
if not dt_provided_regularization or dt_provided_regularization[0] is None:
dt_provided_regularization = ''
elif dt_provided_regularization is None:
dt_provided_regularization = ''
# Corrigindo o campo daysWithoutSales e coverageDays
days_without_sales = product.get('daysWithoutSales', 0)
coverage_days = product.get('coverageDays', 0)
if days_without_sales is None:
days_without_sales = 0
if coverage_days is None:
coverage_days = 0
registro = (
store_code,
product.get('code'),
product.get('description'),
product.get('launch'),
product.get('deactivation'),
product.get('sales', {}).get('thirdToLastCycleSales', 0),
product.get('sales', {}).get('secondToLastCycleSales', 0),
product.get('sales', {}).get('lastCycleSales', 0),
product.get('sales', {}).get('currentCycleSales', 0),
product.get('sales', {}).get('nextCycleProjection', 0),
product.get('sales', {}).get('secondToNextCycleProjection', 0),
product.get('stock', {}).get('actual', 0),
product.get('stock', {}).get('inTransit', 0),
product.get('purchaseSuggestion', 0),
product.get('smartPurchase', {}).get('purchaseSuggestionCycle', 0),
product.get('smartPurchase', {}).get('nextCyclePurchaseSuggestion', 0),
product.get('pendingOrder', 0),
product.get('salesCurve', ''),
promotion_description,
promotion_discount,
product.get('priceSellin', 0.0),
product.get('businessUnit', ''),
category_name,
dt_provided_regularization,
product.get('criticalItem', {}).get('blockedWallet', False),
product.get('criticalItem', {}).get('isCritical', False),
subcategory_name,
product.get('isProductDeactivated', False),
product.get('brandGroupCode', ''),
days_without_sales,
coverage_days,
product.get('hasCoverage', False)
)
registros.append(registro)
if registros:
sql = """
INSERT INTO draft_temp (
loja_id, code, description, launch, deactivation,
thirdToLastCycleSales, secondToLastCycleSales, lastCycleSales, currentCycleSales, nextCycleProjection, secondToNextCycleProjection,
stock_actual, stock_inTransit, purchaseSuggestion,
smartPurchase_purchaseSuggestionCycle, smartPurchase_nextCyclePurchaseSuggestion,
pendingOrder, salesCurve,
promotions_description, promotions_discountPercent,
priceSellin, businessUnit, codCategory,
criticalItem_dtProvidedRegularization, criticalItem_blockedWallet, criticalItem_isCritical,
codSubCategory, isProductDeactivated, brandGroupCode, daysWithoutSales, coverageDays, hasCoverage
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
"""
cursor.executemany(sql, registros)
conn.commit()
return len(registros)
return 0
except Exception as e:
print(f"Erro ao processar dados da loja {store_code}: {e}")
conn.rollback()
raise
def fetch_and_insert_data(store_code, index, total, cursor, retry_count=1):
"""Baixa os dados da API e insere diretamente no banco"""
attempt = 0
while attempt < retry_count:
try:
# Definindo storeType com base no código da loja
if store_code == "21007":
store_type = "TQT"
elif store_code in ["910173", "910291"]:
store_type = "QDB"
else:
store_type = "BOT"
# Dados da requisição com o código da loja
data = {
"storeCode": store_code,
"useId": 163165,
"storeType": store_type,
"generateNew": False
}
# Fazendo a requisição POST
response = requests.post(url, headers=headers, json=data)
response.raise_for_status()
# Requisição bem-sucedida
response_data = response.json()
# Processa e insere os dados diretamente no banco
num_products = process_and_insert_data(store_code, response_data, cursor)
print(f"{index}/{total}: Loja {store_code} processada com sucesso ({num_products} produtos inseridos).")
return True
except RequestException as e:
print(f"{index}/{total}: Erro na requisição para a loja {store_code}: {e}")
except Exception as e:
print(f"{index}/{total}: Erro inesperado para a loja {store_code}: {e}")
attempt += 1
print(f"{index}/{total}: Tentativa {attempt} de {retry_count} para a loja {store_code}.")
time.sleep(5)
print(f"{index}/{total}: Falha ao processar a loja {store_code} após {retry_count} tentativas.")
return False
def finalize_tables(cursor):
"""
Finaliza o processo:
1. Exclui a tabela dbo.draft
2. Renomeia dbo.draft_temp para dbo.draft
3. Atualiza dbo.draft_historico com os dados do dia
"""
try:
print("\n=== Iniciando finalização das tabelas ===")
# Passo 1: Excluir a tabela draft se existir
print("Passo 1: Excluindo tabela dbo.draft...")
cursor.execute("""
IF EXISTS (SELECT * FROM sys.tables WHERE name = 'draft' AND schema_id = SCHEMA_ID('dbo'))
BEGIN
DROP TABLE dbo.draft
END
""")
conn.commit()
print("Tabela dbo.draft excluída com sucesso.")
# Passo 2: Renomear draft_temp para draft
print("Passo 2: Renomeando dbo.draft_temp para dbo.draft...")
cursor.execute("EXEC sp_rename 'dbo.draft_temp', 'draft'")
conn.commit()
print("Tabela renomeada com sucesso: dbo.draft_temp -> dbo.draft")
# Passo 3: Atualizar draft_historico
print("Passo 3: Atualizando dbo.draft_historico...")
# Obter a data de hoje
today = datetime.now().strftime("%Y-%m-%d")
print(f"Data de hoje: {today}")
# Verificar se já existem dados para hoje
cursor.execute("SELECT COUNT(*) FROM dbo.draft_historico WHERE CAST(data AS DATE) = ?", (today,))
count = cursor.fetchone()[0]
if count > 0:
print(f"Encontrados {count} registros para a data {today}. Excluindo...")
cursor.execute("DELETE FROM dbo.draft_historico WHERE CAST(data AS DATE) = ?", (today,))
conn.commit()
print(f"Registros da data {today} excluídos com sucesso.")
else:
print(f"Nenhum registro encontrado para a data {today}.")
# Inserir dados da tabela draft na draft_historico
print("Inserindo dados da dbo.draft na dbo.draft_historico...")
cursor.execute("""
INSERT INTO dbo.draft_historico (
loja_id, code, description, launch, deactivation,
thirdtolastcyclesales, secondtolastcyclesales, lastcyclesales,
currentcyclesales, nextcycleprojection, secondtonextcycleprojection,
stock_actual, stock_intransit, purchasesuggestion,
smartpurchase_purchasesuggestioncycle, smartpurchase_nextcyclepurchasesuggestion,
pendingorder, salescurve,
promotions_description, promotions_discountpercent,
pricesellin, businessunit, codcategory,
criticalitem_dtprovidedregularization, criticalitem_blockedwallet, criticalitem_iscritical,
codsubcategory, isproductdeactivated, brandgroupcode,
dayswithoutsales, coveragedays, hascoverage, data
)
SELECT
loja_id, code, description, launch, deactivation,
thirdToLastCycleSales, secondToLastCycleSales, lastCycleSales,
currentCycleSales, nextCycleProjection, secondToNextCycleProjection,
stock_actual, stock_inTransit, purchaseSuggestion,
smartPurchase_purchaseSuggestionCycle, smartPurchase_nextCyclePurchaseSuggestion,
pendingOrder, salesCurve,
promotions_description, promotions_discountPercent,
priceSellin, businessUnit, codCategory,
criticalItem_dtProvidedRegularization, criticalItem_blockedWallet, criticalItem_isCritical,
codSubCategory, isProductDeactivated, brandGroupCode,
daysWithoutSales, coverageDays, hasCoverage, date
FROM dbo.draft
""")
conn.commit()
# Verificar quantos registros foram inseridos
cursor.execute("SELECT @@ROWCOUNT")
inserted_count = cursor.fetchone()[0]
print(f"{inserted_count} registros inseridos na dbo.draft_historico com sucesso.")
print("=== Finalização das tabelas concluída com sucesso ===\n")
return True
except Exception as e:
print(f"Erro ao finalizar tabelas: {e}")
conn.rollback()
return False
def update_api_status(status):
"""Atualiza o status da API"""
try:
url_status = "https://api.grupoginseng.com.br/api/status/1"
# Definir fuso horário de São Paulo (UTC-3)
sao_paulo_offset = timedelta(hours=-3)
# Gerar data/hora atual no formato YYYY-MM-DD HH:MM:SS com UTC-3
current_datetime = datetime.now(timezone(sao_paulo_offset)).strftime("%Y-%m-%d %H:%M:%S")
# Montar o payload
payload = {
"STATUS": status,
"DATA": current_datetime
}
# Cabeçalhos
headers_status = {
"Content-Type": "application/json"
}
# Enviar PUT request
response = requests.put(url_status, json=payload, headers=headers_status)
print("Hora enviada:", current_datetime)
print("Status Code:", response.status_code)
print("Response Body:", response.text)
except Exception as e:
print(f"Erro ao atualizar status da API: {e}")
def main():
"""Função principal"""
if not check_internet_connection():
print("Sem conexão com a internet. Verifique sua conexão e tente novamente.")
update_api_status("FAIL")
return
cursor = conn.cursor()
try:
# Criar a tabela draft_temp se não existir
create_draft_temp_table(cursor)
failed_stores = []
total = len(store_codes)
# Processa todas as lojas
for index, store_code in enumerate(store_codes, start=1):
success = fetch_and_insert_data(store_code, index, total, cursor)
if not success:
failed_stores.append(store_code)
# Tenta novamente as lojas que falharam
if failed_stores:
print("\nTentando novamente as lojas que falharam.")
retry_count = 1
for attempt in range(1, retry_count + 1):
retry_failed_stores = []
for store_code in failed_stores:
index = store_codes.index(store_code) + 1
success = fetch_and_insert_data(store_code, index, total, cursor)
if not success:
retry_failed_stores.append(store_code)
failed_stores = retry_failed_stores
if not failed_stores:
print("\nTodos os arquivos foram processados com sucesso.")
# Finalizar tabelas antes de atualizar status
if finalize_tables(cursor):
update_api_status("OK")
else:
update_api_status("FAIL")
break
if failed_stores:
print(f"Tentativa {attempt} de {retry_count} para as lojas que falharam.")
time.sleep(10)
# Se ainda houver lojas com falha após todas as tentativas
if failed_stores:
print(f"\nNão foi possível processar algumas lojas: {failed_stores}")
update_api_status("FAIL")
else:
print("\nTodos os arquivos foram processados com sucesso.")
# Finalizar tabelas antes de atualizar status
if finalize_tables(cursor):
update_api_status("OK")
else:
update_api_status("FAIL")
except Exception as e:
print(f"Erro durante a execução: {e}")
update_api_status("FAIL")
finally:
cursor.close()
conn.close()
print("\nProcesso finalizado.")
if __name__ == "__main__":
main()