#!/usr/bin/env python3 """ Script para gerar 500 dados mockados de rotas para o ERP SAAS PraFrota Baseado nas especificações da documentação técnica """ import json import random from datetime import datetime, timedelta from typing import List, Dict, Any # Dados reais extraídos do CSV REAL_PLATES = [ "TAS4J92", "MSO5821", "TAS2F98", "RJZ7H79", "TAO3J98", "TAN6I73", "SGD4H03", "NGF2A53", "TAS2F32", "RTT1B46", "EZQ2E60", "TDZ4J93", "SGL8D98", "TAS2F83", "RVC0J58", "EYP4H76", "FVV7660", "RUN2B51", "RUQ9D16", "TAS5A49", "RUN2B49", "SHX0J21", "FHT5D54", "SVG0I32", "RUN2B50", "FYU9G72", "TAS4J93", "SRZ9B83", "TAQ4G32", "RUP2B50", "SRG6H41", "SQX8J75", "TAS4J96", "RTT1B44", "RTM9F10", "FLE2F99", "RUN2B63", "RVC0J65", "RUN2B52", "TUE1A37", "RUP4H86", "RUP4H94", "RUN2B48", "SVF4I52", "STL5A43", "TAS2J46", "TAO3I97", "TAS5A46", "SUT1B94", "LUJ7E05", "SST4C72", "SRH6C66", "TAO6E76", "RUN2B55", "RVC8B13", "SVF2E84", "SRO2J16", "RVT2J97", "RUN2B58", "SHB4B37", "IWB9C17", "FJE7I82", "TAQ4G22", "SGJ9F81", "SVP9H73", "OVM5B05", "TAO3J94", "RUP2B56", "TAO4F04", "RUN2B64", "GGL2J42", "SRN7H36", "SFM8D30", "TAO6E80", "SVK8G96", "SIA7J06", "TAR3E11", "RVC0J64", "RJW6G71", "SSV6C52", "RUN2B54", "TAN6I66", "SPA0001", "SVH9G53", "RUN2B62", "RVC0J85", "TAR3D02", "RVC4G70", "RUP4H92", "RUN2B56", "SGL8F08", "TAO3J93", "LUC4H25", "TAN6H93", "TAQ4G30", "RUP4H87", "SHB4B36", "SGC2B17", "RVC0J70", "SVL1G82", "RVC0J63", "RVT2J98", "SPA0001", "RVT4F18", "TAR3C45", "TAO4E80", "TAN6I62", "SHB4B38", "RTO9B22", "RJE8B51", "TAO4F02", "SGJ9G23", "SRU2H94", "RTT1B48", "TAN6I69", "RUP2B49", "RUW9C02", "RUP4H91", "RVC0J74", "TAN6H99", "FZG8F72", "RUP4H88", "TAS2E35", "RUN2B60", "RTO9B84", "GHM7A76", "RTM9F11", "TAN6H97", "SQX9G04", "RVU9160", "SGL8E65", "RTT1B43", "TAO4F05", "TOG3H62", "TAS5A47", "TAQ6J50", "SRH4E56", "NSZ5318", "RUN2B53", "TAO3J97", "SGL8E73", "SHX0J22", "SFP6G82", "SRZ9C22", "RTT1B45", "TAN6163", "LTO7G84", "SGL8D26", "TAN6I59", "TAO4E89", "TAO4E90", "TAS2J51", "SGL8F81", "RTM9F14", "FKP9A34", "TAS2J45", "QUS3C30", "GDM8I81", "TAQ4G36", "RVC0J59", "TAS5A44", "RUN2B61", "RVC4G71", "TAS4J95", "TAQ4G37", "SPA0001", "RTB7E19", "TAS2E31", "RUP4H81", "SGD9A92", "RJF7I82", "EVU9280", "SPA0001", "SSC1E94", "TAR3E21", "TAN6I71", "TAS4J92", "TAN6I57", "TAO4F90", "SGJ2F13", "SGJ2D96", "SGJ2G40", "TAR3E14", "KRQ9A48", "RUP2B53", "SRN5C38", "SGJ2G98", "SRA7J03", "RIU1G19", "EUQ4159", "SRH5C60", "SSB6H85", "SRN6F73", "SRY4B65", "SGL8C62", "STU7F45", "SGJ9G45", "RVT4F19" ] PRODUCT_TYPES = [ "Medicamentos", "Eletrônicos", "Alimentos Perecíveis", "Alimentos Não Perecíveis", "Roupas e Acessórios", "Livros e Papelaria", "Casa e Decoração", "Cosméticos", "Automotive", "Brinquedos" ] MARKETPLACES = ["Mercado Livre", "Shopee", "Amazon"] # Coordenadas das regiões REGIONS = { "rioDeJaneiro": { "center": {"lat": -22.9068, "lng": -43.1729}, "bounds": {"north": -22.7000, "south": -23.1000, "east": -43.0000, "west": -43.8000}, "addresses": [ "Rua das Flores, 123 - Copacabana, Rio de Janeiro - RJ", "Av. Atlântica, 456 - Ipanema, Rio de Janeiro - RJ", "Rua Barata Ribeiro, 789 - Copacabana, Rio de Janeiro - RJ", "Av. Nossa Senhora de Copacabana, 321 - Copacabana, Rio de Janeiro - RJ", "Rua Visconde de Pirajá, 654 - Ipanema, Rio de Janeiro - RJ", "Av. Rio Branco, 987 - Centro, Rio de Janeiro - RJ", "Rua da Carioca, 147 - Centro, Rio de Janeiro - RJ", "Av. Presidente Vargas, 258 - Centro, Rio de Janeiro - RJ", "Rua Santa Clara, 369 - Copacabana, Rio de Janeiro - RJ", "Av. Princesa Isabel, 741 - Copacabana, Rio de Janeiro - RJ" ] }, "saoPaulo": { "center": {"lat": -23.5505, "lng": -46.6333}, "bounds": {"north": -23.3000, "south": -23.8000, "east": -46.3000, "west": -47.0000}, "addresses": [ "Av. Paulista, 1578 - Bela Vista, São Paulo - SP", "Rua Augusta, 1000 - Consolação, São Paulo - SP", "Av. Faria Lima, 2000 - Pinheiros, São Paulo - SP", "Rua Oscar Freire, 500 - Jardins, São Paulo - SP", "Av. Rebouças, 3000 - Pinheiros, São Paulo - SP", "Rua da Consolação, 1200 - Consolação, São Paulo - SP", "Av. Brigadeiro Faria Lima, 1500 - Jardim Paulistano, São Paulo - SP", "Rua Haddock Lobo, 800 - Cerqueira César, São Paulo - SP", "Av. Nove de Julho, 2500 - Jardim Paulista, São Paulo - SP", "Rua Estados Unidos, 600 - Jardim América, São Paulo - SP" ] }, "minasGerais": { "center": {"lat": -19.9167, "lng": -43.9345}, "bounds": {"north": -19.7000, "south": -20.2000, "east": -43.7000, "west": -44.2000}, "addresses": [ "Av. Afonso Pena, 1000 - Centro, Belo Horizonte - MG", "Rua da Bahia, 500 - Centro, Belo Horizonte - MG", "Av. do Contorno, 2000 - Santa Efigênia, Belo Horizonte - MG", "Rua Rio de Janeiro, 800 - Centro, Belo Horizonte - MG", "Av. Amazonas, 1500 - Centro, Belo Horizonte - MG", "Rua Curitiba, 300 - Centro, Belo Horizonte - MG", "Av. Brasil, 2500 - Santa Efigênia, Belo Horizonte - MG", "Rua Tupis, 600 - Centro, Belo Horizonte - MG", "Av. Francisco Sales, 1200 - Santa Efigênia, Belo Horizonte - MG", "Rua Espírito Santo, 400 - Centro, Belo Horizonte - MG" ] }, "vitoria": { "center": {"lat": -20.2976, "lng": -40.2958}, "bounds": {"north": -20.1000, "south": -20.5000, "east": -40.1000, "west": -40.5000}, "addresses": [ "Av. Princesa Isabel, 500 - Centro, Vitória - ES", "Rua Sete de Setembro, 200 - Centro, Vitória - ES", "Av. Jerônimo Monteiro, 800 - Centro, Vitória - ES", "Rua do Comércio, 300 - Centro, Vitória - ES", "Av. Marechal Mascarenhas de Moraes, 1000 - Bento Ferreira, Vitória - ES", "Rua General Osório, 150 - Centro, Vitória - ES", "Av. Nossa Senhora da Penha, 600 - Santa Lúcia, Vitória - ES", "Rua Chapot Presvot, 400 - Praia do Canto, Vitória - ES", "Av. Saturnino de Brito, 900 - Praia do Canto, Vitória - ES", "Rua Joaquim Lírio, 250 - Praia do Canto, Vitória - ES" ] } } # Nomes fictícios FIRST_NAMES = [ "João", "Maria", "José", "Ana", "Carlos", "Fernanda", "Pedro", "Juliana", "Roberto", "Mariana", "Ricardo", "Camila", "Marcos", "Patrícia", "André", "Luciana", "Fernando", "Carla", "Rafael", "Daniela", "Paulo", "Renata", "Gustavo", "Vanessa", "Bruno", "Cristina", "Diego", "Tatiana", "Felipe", "Amanda", "Rodrigo", "Priscila", "Thiago", "Natália", "Leonardo", "Bianca" ] LAST_NAMES = [ "Silva", "Santos", "Oliveira", "Souza", "Rodrigues", "Ferreira", "Alves", "Pereira", "Lima", "Gomes", "Costa", "Ribeiro", "Martins", "Carvalho", "Almeida", "Lopes", "Soares", "Fernandes", "Vieira", "Barbosa", "Rocha", "Dias", "Monteiro", "Cardoso", "Reis", "Araújo", "Moreira", "Freitas", "Mendes", "Ramos", "Castro", "Pinto", "Teixeira", "Correia", "Machado" ] def generate_random_name(): """Gera um nome aleatório""" return f"{random.choice(FIRST_NAMES)} {random.choice(LAST_NAMES)}" def generate_phone(area_code): """Gera um telefone aleatório""" return f"+55 {area_code} {random.randint(90000, 99999)}-{random.randint(1000, 9999)}" def generate_coordinates_in_region(region_name): """Gera coordenadas aleatórias dentro de uma região""" region = REGIONS[region_name] bounds = region["bounds"] lat = random.uniform(bounds["south"], bounds["north"]) lng = random.uniform(bounds["west"], bounds["east"]) return {"lat": round(lat, 6), "lng": round(lng, 6)} def generate_route_data(route_id: int, route_type: str, region: str) -> Dict[str, Any]: """Gera dados de uma rota específica""" # Definir códigos de área por região area_codes = { "rioDeJaneiro": "21", "saoPaulo": "11", "minasGerais": "31", "vitoria": "27" } area_code = area_codes[region] # Gerar coordenadas origin_coords = generate_coordinates_in_region(region) dest_coords = generate_coordinates_in_region(region) # Selecionar endereços addresses = REGIONS[region]["addresses"] origin_address = random.choice(addresses) dest_address = random.choice(addresses) # Para lastMile, usar endereços residenciais if route_type == "lastMile": marketplace = random.choice(MARKETPLACES) origin_address = f"Hub {marketplace} - {region.title()}" dest_address = random.choice(addresses) # Endereço residencial # Definir modal baseado no tipo if route_type == "lineHaul": modal_choices = ["rodoviario", "aereo", "aquaviario"] modal_weights = [0.8, 0.15, 0.05] else: modal_choices = ["rodoviario"] modal_weights = [1.0] modal = random.choices(modal_choices, weights=modal_weights)[0] # Definir prioridade priority = random.choices( ["normal", "express", "urgent"], weights=[0.7, 0.2, 0.1] )[0] # Definir status status = random.choices( ["pending", "inProgress", "completed", "delayed", "cancelled"], weights=[0.1, 0.4, 0.35, 0.1, 0.05] )[0] # Definir valores baseados no tipo if route_type == "lastMile": total_value = round(random.uniform(25.0, 150.0), 2) total_weight = round(random.uniform(0.5, 15.0), 1) estimated_cost = round(total_value * 0.4, 2) elif route_type == "lineHaul": total_value = round(random.uniform(1500.0, 5000.0), 2) total_weight = round(random.uniform(5000.0, 15000.0), 1) estimated_cost = round(total_value * 0.35, 2) else: # firstMile total_value = round(random.uniform(300.0, 2000.0), 2) total_weight = round(random.uniform(500.0, 5000.0), 1) estimated_cost = round(total_value * 0.45, 2) # Datas base_date = datetime.now() - timedelta(days=random.randint(0, 30)) scheduled_departure = base_date # Definir datas baseadas no status actual_departure = None estimated_arrival = scheduled_departure + timedelta(hours=random.randint(2, 12)) actual_arrival = None current_location = None actual_cost = None if status in ["inProgress", "completed", "delayed"]: actual_departure = scheduled_departure + timedelta(minutes=random.randint(-30, 60)) if status == "completed": actual_arrival = estimated_arrival + timedelta(minutes=random.randint(-60, 120)) current_location = dest_coords actual_cost = round(estimated_cost * random.uniform(0.8, 1.3), 2) elif status == "inProgress": # Posição entre origem e destino progress = random.uniform(0.2, 0.8) current_location = { "lat": round(origin_coords["lat"] + (dest_coords["lat"] - origin_coords["lat"]) * progress, 6), "lng": round(origin_coords["lng"] + (dest_coords["lng"] - origin_coords["lng"]) * progress, 6) } # Produto tipo product_type = random.choice(PRODUCT_TYPES) # Placa do veículo vehicle_plate = random.choice(REAL_PLATES) return { "id": f"rt_{route_id:03d}", "routeNumber": f"RT-2024-{route_id:06d}", "type": route_type, "modal": modal, "priority": priority, "driverId": f"drv_{route_id:03d}", "vehicleId": f"veh_{route_id:03d}", "companyId": "comp_001", "customerId": f"cust_{route_id:03d}", "origin": { "address": origin_address, "coordinates": origin_coords, "contact": generate_random_name(), "phone": generate_phone(area_code) }, "destination": { "address": dest_address, "coordinates": dest_coords, "contact": generate_random_name(), "phone": generate_phone(area_code) }, "scheduledDeparture": scheduled_departure.isoformat() + "Z", "actualDeparture": actual_departure.isoformat() + "Z" if actual_departure else None, "estimatedArrival": estimated_arrival.isoformat() + "Z", "actualArrival": actual_arrival.isoformat() + "Z" if actual_arrival else None, "status": status, "currentLocation": current_location, "contractId": f"cont_{route_id:03d}", "tablePricesId": f"tbl_{route_id:03d}", "totalValue": total_value, "totalWeight": total_weight, "estimatedCost": estimated_cost, "actualCost": actual_cost, "productType": product_type, "createdAt": (base_date - timedelta(hours=random.randint(1, 48))).isoformat() + "Z", "updatedAt": (base_date + timedelta(minutes=random.randint(0, 300))).isoformat() + "Z", "createdBy": f"user_{random.randint(1, 10):03d}", "vehiclePlate": vehicle_plate } def generate_all_routes(): """Gera todas as 500 rotas""" routes = [] route_id = 1 # Distribuição por tipo (conforme especificação) type_distribution = [ ("firstMile", 300), ("lineHaul", 125), ("lastMile", 75) ] # Distribuição por região region_distribution = [ ("saoPaulo", 175), ("rioDeJaneiro", 150), ("minasGerais", 125), ("vitoria", 50) ] # Calcular rotas por tipo e região total_routes_per_region = {region: count for region, count in region_distribution} for route_type, type_count in type_distribution: # Distribuir este tipo pelas regiões proporcionalmente for region, region_total in region_distribution: region_proportion = region_total / 500 routes_for_this_type_region = int(type_count * region_proportion) for _ in range(routes_for_this_type_region): if route_id <= 500: route = generate_route_data(route_id, route_type, region) routes.append(route) route_id += 1 # Completar até 500 se necessário while len(routes) < 500: remaining_type = random.choice(["firstMile", "lineHaul", "lastMile"]) remaining_region = random.choice(["saoPaulo", "rioDeJaneiro", "minasGerais", "vitoria"]) route = generate_route_data(route_id, remaining_type, remaining_region) routes.append(route) route_id += 1 return routes[:500] # Garantir exatamente 500 def main(): """Função principal""" print("Gerando 500 rotas mockadas...") routes = generate_all_routes() # Calcular estatísticas reais type_stats = {} status_stats = {} region_stats = {} for route in routes: # Tipo route_type = route["type"] type_stats[route_type] = type_stats.get(route_type, 0) + 1 # Status status = route["status"] status_stats[status] = status_stats.get(status, 0) + 1 # Região (baseado no telefone) phone = route["origin"]["phone"] if "11" in phone: region = "saoPaulo" elif "21" in phone: region = "rioDeJaneiro" elif "31" in phone: region = "minasGerais" else: region = "vitoria" region_stats[region] = region_stats.get(region, 0) + 1 # Estrutura final data = { "routes": routes, "metadata": { "totalRoutes": len(routes), "generatedAt": datetime.now().isoformat() + "Z", "version": "1.0", "description": "Dados mockados para o módulo de Rotas do ERP SAAS PraFrota", "actualDistributions": { "byType": type_stats, "byStatus": status_stats, "byRegion": region_stats }, "specifications": { "byType": { "firstMile": "60% (300 rotas) - Coleta em centros de distribuição", "lineHaul": "25% (125 rotas) - Transporte entre cidades", "lastMile": "15% (75 rotas) - Entrega final (Mercado Livre, Shopee, Amazon)" }, "byModal": { "rodoviario": "95% (475 rotas)", "aereo": "3% (15 rotas)", "aquaviario": "2% (10 rotas)" }, "regions": { "rioDeJaneiro": "30% (150 rotas)", "saoPaulo": "35% (175 rotas)", "minasGerais": "25% (125 rotas)", "vitoria": "10% (50 rotas)" } }, "realVehiclePlates": REAL_PLATES, "productTypes": PRODUCT_TYPES, "lastMileMarketplaces": MARKETPLACES, "coordinates": REGIONS } } # Salvar arquivo output_file = "ROUTES_MOCK_DATA_COMPLETE.json" with open(output_file, 'w', encoding='utf-8') as f: json.dump(data, f, ensure_ascii=False, indent=2) print(f"✅ Arquivo {output_file} gerado com sucesso!") print(f"📊 Estatísticas:") print(f" Total de rotas: {len(routes)}") print(f" Por tipo: {type_stats}") print(f" Por status: {status_stats}") print(f" Por região: {region_stats}") if __name__ == "__main__": main()