init: scripts diversos (crawlers, conversores, scrapers)
This commit is contained in:
368
scraper/consolidate_knowledge_final.py
Executable file
368
scraper/consolidate_knowledge_final.py
Executable file
@@ -0,0 +1,368 @@
|
||||
"""
|
||||
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()
|
||||
Reference in New Issue
Block a user