diff --git a/Extrair_draft.py b/Extrair_draft.py deleted file mode 100644 index 02c8459..0000000 --- a/Extrair_draft.py +++ /dev/null @@ -1,198 +0,0 @@ -# -*- coding: utf-8 -*- - -import requests -import json -import socket -import os -import shutil -from requests.exceptions import RequestException -import subprocess -import time - -# 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", "910291", "910173", - "23813" -] - -def check_internet_connection(): - try: - # Tenta resolver o endereço para verificar a conectividade - socket.gethostbyname("google.com") - return True - except socket.error: - return False - -def fetch_data_from_api(store_code, index, total, retry_count=1): - 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() # Levanta um erro para códigos de status HTTP 4xx/5xx - - # Requisição bem-sucedida - response_data = response.json() - - # Nome do arquivo com base no código da loja - json_filename = f"{store_code}.json" - - # Salva os dados em um arquivo JSON com o nome do código da loja - with open(json_filename, "w") as file: - json.dump(response_data, file, indent=4) - - print(f"{index}/{total}: Dados recebidos da API para a loja {store_code} e salvos em '{json_filename}'.") - - # Caminho da nova pasta de destino - destination_folder = "/home/danielrodrigues/Scripts/Draft/Arquivos/" - - # Cria a pasta de destino se não existir - if not os.path.exists(destination_folder): - os.makedirs(destination_folder) - - # Caminho completo do novo local do arquivo - new_file_path = os.path.join(destination_folder, json_filename) - - # Move o arquivo JSON para a pasta de destino, substituindo se necessário - if os.path.exists(new_file_path): - os.remove(new_file_path) - - shutil.move(json_filename, new_file_path) - print(f"{index}/{total}: Arquivo movido para '{new_file_path}'.") - return True # Retorna True para indicar sucesso - - 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) # Espera antes de tentar novamente - - print(f"{index}/{total}: Falha ao processar a loja {store_code} após {retry_count} tentativas.") - return False # Retorna False para indicar falha - -def update_api_status(status): - import requests - from datetime import datetime, timezone, timedelta - - # URL do endpoint - url = "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 = { - "Content-Type": "application/json" - } - - # Enviar PUT request - response = requests.put(url, json=payload, headers=headers) - - # Mostrar resposta - print("Hora enviada:", current_datetime) - print("Status Code:", response.status_code) - print("Response Body:", response.text) - -def main(): - if check_internet_connection(): - failed_stores = [] - total = len(store_codes) - - # Processa todas as lojas - for index, store_code in enumerate(store_codes, start=1): - success = fetch_data_from_api(store_code, index, total) - if not success: - failed_stores.append(store_code) - - # Tenta novamente as lojas que falharam - if failed_stores: - print("Tentando 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_data_from_api(store_code, index, total) - if not success: - retry_failed_stores.append(store_code) - - failed_stores = retry_failed_stores - if not failed_stores: - print("Todos os arquivos foram processados com sucesso.") - update_api_status("OK") - break - - if failed_stores: - print(f"Tentativa {attempt} de {retry_count} para as lojas que falharam.") - time.sleep(10) # Espera antes de tentar novamente - - # Se ainda houver lojas com falha após todas as tentativas - if failed_stores: - print(f"Não foi possível processar algumas lojas: {failed_stores}") - update_api_status("FAIL") - else: - print("Todos os arquivos foram processados com sucesso.") - update_api_status("OK") - else: - print("Sem conexão com a internet. Verifique sua conexão e tente novamente.") - update_api_status("FAIL") - -if __name__ == "__main__": - main() diff --git a/draft_mar.py b/draft_mar.py new file mode 100644 index 0000000..f0a4f38 --- /dev/null +++ b/draft_mar.py @@ -0,0 +1,448 @@ +# -*- 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", "910291", "910173", + "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 ( + 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 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.") + update_api_status("OK") + 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.") + update_api_status("OK") + + 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() +