369 lines
13 KiB
Python
Executable File
369 lines
13 KiB
Python
Executable File
"""
|
|
consolidate_knowledge_final.py - Consolidação FINAL CTF Knowledge Base
|
|
|
|
Consolida:
|
|
- Batch 1 + Batch 2: 268 casos (já consolidados anteriormente)
|
|
- Batch 3: Casos extraídos de triumphexp
|
|
- Reddit: Casos extraídos de r/Autoupholstery + r/upholstery
|
|
|
|
Gera:
|
|
- CTF_Knowledge_Base_FINAL_Consolidated.json (todos os casos)
|
|
- CTF_Knowledge_Base_FINAL_Statistics.json (estatísticas detalhadas)
|
|
- CTF_Knowledge_Base_FINAL_Report.md (relatório executivo)
|
|
|
|
Author: Descomplicar® Crescimento Digital
|
|
Link: https://descomplicar.pt
|
|
Copyright: 2025 Descomplicar®
|
|
"""
|
|
|
|
import json
|
|
import os
|
|
from pathlib import Path
|
|
from collections import defaultdict, Counter
|
|
from datetime import datetime
|
|
import re
|
|
|
|
# Configuração
|
|
INPUT_DIR = "/media/ealmeida/Dados/GDrive/Cloud/Clientes_360/CTF_Carstuff/KB/Scrapper/sites/knowledge_base_final"
|
|
OUTPUT_DIR = "/media/ealmeida/Dados/GDrive/Cloud/Clientes_360/CTF_Carstuff/KB/Scrapper/sites"
|
|
OUTPUT_FILE = "CTF_Knowledge_Base_FINAL_Consolidated.json"
|
|
STATS_FILE = "CTF_Knowledge_Base_FINAL_Statistics.json"
|
|
REPORT_FILE = "CTF_Knowledge_Base_FINAL_Report.md"
|
|
|
|
def extract_site_name(filename):
|
|
"""Extrair nome do site do nome do ficheiro."""
|
|
match = re.match(r'knowledge_(.+?)[\._]\d+\.json', filename)
|
|
if match:
|
|
return match.group(1)
|
|
return "unknown"
|
|
|
|
def detect_batch(site_name, filename):
|
|
"""Detectar batch baseado no nome do site/ficheiro."""
|
|
if 'triumphexp' in site_name.lower():
|
|
return 'batch3'
|
|
elif 'reddit' in filename.lower():
|
|
return 'reddit'
|
|
else:
|
|
# Batch 1 + 2 (sites conhecidos)
|
|
batch1_sites = ['thehogring', 'mgexp', 'relicate', 'vansairforce']
|
|
if any(site in site_name.lower() for site in batch1_sites):
|
|
return 'batch1'
|
|
else:
|
|
return 'batch2'
|
|
|
|
def load_all_cases():
|
|
"""Carregar todos os casos de todos os ficheiros JSON."""
|
|
input_path = Path(INPUT_DIR)
|
|
all_cases = []
|
|
batch_stats = defaultdict(lambda: {"cases": 0, "files": 0})
|
|
site_stats = defaultdict(lambda: {"cases": 0, "files": 0})
|
|
category_stats = Counter()
|
|
material_stats = Counter()
|
|
keyword_stats = Counter()
|
|
severidade_stats = Counter()
|
|
|
|
print(f"📂 A ler ficheiros de: {INPUT_DIR}")
|
|
|
|
json_files = sorted(input_path.glob("knowledge_*.json"))
|
|
total_files = len(json_files)
|
|
|
|
print(f"📊 Encontrados {total_files} ficheiros JSON\n")
|
|
|
|
for idx, json_file in enumerate(json_files, 1):
|
|
try:
|
|
with open(json_file, 'r', encoding='utf-8') as f:
|
|
data = json.load(f)
|
|
|
|
site_name = extract_site_name(json_file.name)
|
|
batch = detect_batch(site_name, json_file.name)
|
|
|
|
site_stats[site_name]["files"] += 1
|
|
batch_stats[batch]["files"] += 1
|
|
|
|
# Extrair casos completos
|
|
casos = data.get("casos_completos", [])
|
|
|
|
for caso in casos:
|
|
# Adicionar metadados do ficheiro ao caso
|
|
enhanced_caso = {
|
|
**caso,
|
|
"metadata": {
|
|
"batch": batch,
|
|
"site_origem": site_name,
|
|
"ficheiro_origem": json_file.name,
|
|
"categoria_aplicacao": data.get("categoria_aplicacao", ""),
|
|
"tipo_conteudo": data.get("tipo_conteudo", ""),
|
|
"nivel_expertise": data.get("nivel_expertise", ""),
|
|
"keywords_tecnicas": data.get("keywords_tecnicas", []),
|
|
"materiais_principais": data.get("materiais_discutidos", {}).get("principais", [])
|
|
}
|
|
}
|
|
|
|
all_cases.append(enhanced_caso)
|
|
site_stats[site_name]["cases"] += 1
|
|
batch_stats[batch]["cases"] += 1
|
|
|
|
# Estatísticas agregadas
|
|
if data.get("categoria_aplicacao"):
|
|
category_stats[data["categoria_aplicacao"]] += 1
|
|
|
|
for material in data.get("materiais_discutidos", {}).get("principais", []):
|
|
material_stats[material] += 1
|
|
|
|
for keyword in data.get("keywords_tecnicas", []):
|
|
keyword_stats[keyword] += 1
|
|
|
|
# Severidade
|
|
sev = caso.get("problema", {}).get("severidade", "unknown")
|
|
severidade_stats[sev] += 1
|
|
|
|
if (idx % 50 == 0) or (idx == total_files):
|
|
print(f" Progresso: {idx}/{total_files} ({idx/total_files*100:.1f}%) - {len(all_cases)} casos extraídos")
|
|
|
|
except Exception as e:
|
|
print(f"⚠️ Erro ao processar {json_file.name}: {e}")
|
|
continue
|
|
|
|
return all_cases, dict(batch_stats), dict(site_stats), dict(category_stats), dict(material_stats), dict(keyword_stats), dict(severidade_stats)
|
|
|
|
def generate_statistics(cases, batch_stats, site_stats, category_stats, material_stats, keyword_stats, severidade_stats):
|
|
"""Gerar estatísticas detalhadas."""
|
|
|
|
# Top materiais e keywords
|
|
top_materiais = dict(Counter(material_stats).most_common(20))
|
|
top_keywords = dict(Counter(keyword_stats).most_common(30))
|
|
|
|
# Casos por site (ordenado)
|
|
site_stats_sorted = dict(sorted(
|
|
site_stats.items(),
|
|
key=lambda x: x[1]["cases"],
|
|
reverse=True
|
|
))
|
|
|
|
# Casos por batch (ordenado)
|
|
batch_stats_sorted = dict(sorted(
|
|
batch_stats.items(),
|
|
key=lambda x: x[1]["cases"],
|
|
reverse=True
|
|
))
|
|
|
|
statistics = {
|
|
"metadata": {
|
|
"generated_at": datetime.now().isoformat(),
|
|
"description": "Consolidação FINAL - Batches 1+2+3 + Reddit",
|
|
"total_files_processed": sum(s["files"] for s in site_stats.values()),
|
|
"total_cases_extracted": len(cases)
|
|
},
|
|
"distribution": {
|
|
"by_batch": batch_stats_sorted,
|
|
"by_site": site_stats_sorted,
|
|
"by_category": dict(category_stats),
|
|
"by_severidade": dict(severidade_stats)
|
|
},
|
|
"top_elements": {
|
|
"materiais": top_materiais,
|
|
"keywords_tecnicas": top_keywords
|
|
}
|
|
}
|
|
|
|
return statistics
|
|
|
|
def generate_markdown_report(stats, cases):
|
|
"""Gerar relatório em Markdown."""
|
|
|
|
total_cases = stats["metadata"]["total_cases_extracted"]
|
|
total_files = stats["metadata"]["total_files_processed"]
|
|
|
|
report = f"""# 🎯 CTF KNOWLEDGE BASE - CONSOLIDAÇÃO FINAL
|
|
|
|
**Generated**: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}
|
|
**Description**: Consolidação COMPLETA - Batches 1+2+3 + Reddit
|
|
**Author**: Descomplicar® Crescimento Digital
|
|
|
|
---
|
|
|
|
## 📊 RESUMO EXECUTIVO
|
|
|
|
- **Total Ficheiros Processados**: {total_files}
|
|
- **Total Casos Extraídos**: {total_cases}
|
|
- **Taxa Média**: {total_cases/total_files:.2f} casos por ficheiro
|
|
|
|
---
|
|
|
|
## 📈 DISTRIBUIÇÃO POR BATCH
|
|
|
|
| Batch | Ficheiros | Casos | Casos/Ficheiro |
|
|
|-------|-----------|-------|----------------|
|
|
"""
|
|
|
|
for batch, data in stats["distribution"]["by_batch"].items():
|
|
files = data["files"]
|
|
cases_count = data["cases"]
|
|
avg = cases_count / files if files > 0 else 0
|
|
report += f"| **{batch}** | {files} | {cases_count} | {avg:.2f} |\n"
|
|
|
|
report += f"\n**TOTAL** | {total_files} | {total_cases} | {total_cases/total_files:.2f} |\n"
|
|
|
|
report += """
|
|
|
|
---
|
|
|
|
## 🏷️ DISTRIBUIÇÃO POR CATEGORIA
|
|
|
|
| Categoria | Casos | % |
|
|
|-----------|-------|---|
|
|
"""
|
|
|
|
for cat, count in sorted(stats["distribution"]["by_category"].items(), key=lambda x: x[1], reverse=True):
|
|
pct = (count / total_cases) * 100
|
|
report += f"| {cat} | {count} | {pct:.1f}% |\n"
|
|
|
|
report += """
|
|
|
|
---
|
|
|
|
## ⚠️ DISTRIBUIÇÃO POR SEVERIDADE
|
|
|
|
| Severidade | Casos | % |
|
|
|------------|-------|---|
|
|
"""
|
|
|
|
for sev, count in sorted(stats["distribution"]["by_severidade"].items(), key=lambda x: x[1], reverse=True):
|
|
pct = (count / total_cases) * 100
|
|
report += f"| {sev} | {count} | {pct:.1f}% |\n"
|
|
|
|
report += """
|
|
|
|
---
|
|
|
|
## 🧰 TOP 20 MATERIAIS MAIS MENCIONADOS
|
|
|
|
| Material | Menções |
|
|
|----------|---------|
|
|
"""
|
|
|
|
for mat, count in list(stats["top_elements"]["materiais"].items())[:20]:
|
|
report += f"| {mat} | {count} |\n"
|
|
|
|
report += """
|
|
|
|
---
|
|
|
|
## 🔍 TOP 30 KEYWORDS TÉCNICAS
|
|
|
|
| Keyword | Ocorrências |
|
|
|---------|-------------|
|
|
"""
|
|
|
|
for kw, count in list(stats["top_elements"]["keywords_tecnicas"].items())[:30]:
|
|
report += f"| {kw} | {count} |\n"
|
|
|
|
report += """
|
|
|
|
---
|
|
|
|
## 📈 TOP 15 SITES POR VOLUME
|
|
|
|
| Site | Ficheiros | Casos | Média |
|
|
|------|-----------|-------|-------|
|
|
"""
|
|
|
|
for site, data in list(stats["distribution"]["by_site"].items())[:15]:
|
|
files = data["files"]
|
|
cases_count = data["cases"]
|
|
avg = cases_count / files if files > 0 else 0
|
|
report += f"| `{site}` | {files} | {cases_count} | {avg:.2f} |\n"
|
|
|
|
report += """
|
|
|
|
---
|
|
|
|
## 🎯 PRÓXIMAS AÇÕES
|
|
|
|
1. ✅ **Consolidação FINAL Concluída**: {total_cases} casos totais
|
|
2. 🚀 **Deploy para Sistema**: Importar para CTF Knowledge Base system
|
|
3. 📊 **Análise Qualidade**: Review manual de casos de alta prioridade
|
|
4. 🔄 **Manutenção**: Atualizações periódicas com novos casos
|
|
|
|
---
|
|
|
|
**© 2025 Descomplicar® - Crescimento Digital**
|
|
**Link**: https://descomplicar.pt
|
|
""".replace("{total_cases}", str(total_cases))
|
|
|
|
return report
|
|
|
|
def main():
|
|
"""Função principal."""
|
|
print("═══════════════════════════════════════════════════════════")
|
|
print(" CTF KNOWLEDGE BASE - CONSOLIDAÇÃO FINAL")
|
|
print(" Batches 1+2+3 + Reddit")
|
|
print(" Descomplicar® Crescimento Digital")
|
|
print("═══════════════════════════════════════════════════════════")
|
|
print()
|
|
|
|
# 1. Carregar todos os casos
|
|
print("📖 Passo 1: A carregar casos de TODOS os ficheiros...")
|
|
cases, batch_stats, site_stats, category_stats, material_stats, keyword_stats, severidade_stats = load_all_cases()
|
|
print(f"✅ Carregados {len(cases)} casos de {sum(s['files'] for s in site_stats.values())} ficheiros\n")
|
|
|
|
# 2. Gerar estatísticas
|
|
print("📊 Passo 2: A gerar estatísticas...")
|
|
statistics = generate_statistics(cases, batch_stats, site_stats, category_stats, material_stats, keyword_stats, severidade_stats)
|
|
print("✅ Estatísticas geradas\n")
|
|
|
|
# 3. Criar ficheiro consolidado
|
|
print("💾 Passo 3: A guardar ficheiro consolidado...")
|
|
consolidated = {
|
|
"metadata": {
|
|
"generated_at": datetime.now().isoformat(),
|
|
"description": "Knowledge Base FINAL - Batches 1+2+3 + Reddit Completos",
|
|
"version": "FINAL-1.0",
|
|
"author": "Descomplicar® Crescimento Digital",
|
|
"total_cases": len(cases),
|
|
"total_files": statistics["metadata"]["total_files_processed"]
|
|
},
|
|
"statistics": statistics,
|
|
"cases": cases
|
|
}
|
|
|
|
output_path = Path(OUTPUT_DIR) / OUTPUT_FILE
|
|
with open(output_path, 'w', encoding='utf-8') as f:
|
|
json.dump(consolidated, f, ensure_ascii=False, indent=2)
|
|
print(f"✅ Guardado: {output_path}\n")
|
|
|
|
# 4. Guardar estatísticas separadas
|
|
print("📊 Passo 4: A guardar ficheiro de estatísticas...")
|
|
stats_path = Path(OUTPUT_DIR) / STATS_FILE
|
|
with open(stats_path, 'w', encoding='utf-8') as f:
|
|
json.dump(statistics, f, ensure_ascii=False, indent=2)
|
|
print(f"✅ Guardado: {stats_path}\n")
|
|
|
|
# 5. Gerar relatório Markdown
|
|
print("📝 Passo 5: A gerar relatório Markdown...")
|
|
report = generate_markdown_report(statistics, cases)
|
|
report_path = Path(OUTPUT_DIR) / REPORT_FILE
|
|
with open(report_path, 'w', encoding='utf-8') as f:
|
|
f.write(report)
|
|
print(f"✅ Guardado: {report_path}\n")
|
|
|
|
# Resumo final
|
|
print("═══════════════════════════════════════════════════════════")
|
|
print(" CONSOLIDAÇÃO FINAL CONCLUÍDA COM SUCESSO")
|
|
print("═══════════════════════════════════════════════════════════")
|
|
print()
|
|
print(f"🎯 Casos TOTAIS consolidados: {len(cases)}")
|
|
print()
|
|
print("📊 Distribuição por Batch:")
|
|
for batch, data in sorted(statistics["distribution"]["by_batch"].items(), key=lambda x: x[1]["cases"], reverse=True):
|
|
print(f" - {batch}: {data['cases']} casos ({data['files']} ficheiros)")
|
|
print()
|
|
print("📄 Ficheiros gerados:")
|
|
print(f" 1. {OUTPUT_FILE} - Knowledge Base FINAL completa")
|
|
print(f" 2. {STATS_FILE} - Estatísticas detalhadas")
|
|
print(f" 3. {REPORT_FILE} - Relatório executivo")
|
|
print()
|
|
|
|
if __name__ == "__main__":
|
|
main()
|