{"cells":[{"cell_type":"markdown","source":["**Script localiza_ODS.ipynb:** realiza busca automatizada de referências aos Objetivos de Desenvolvimento Sustentável (ODS/SDG) em registros JSON extraídos via XOAI, identificando ocorrências tanto nos nomes dos campos de metadados quanto nos valores armazenados nesses campos.\n","\n","\n","---\n","\n","\n","Ordem de processamento:\n","\n","**1. Ler arquivos JSON coletados dos Repositórios Institucionais**\n","\n","O script percorre:\n","\n","* todas as pastas de repositórios\n","* todos os arquivos `.json` previamente coletados\n","\n","Cada arquivo contém um registro individual exportado a partir do formato `xoai`.\n","\n","\n","**2. Normalizar nomes dos campos XOAI**\n","\n","Os nomes dos campos são normalizados para remover:\n","\n","* qualificadores linguísticos\n","* sufixos automáticos do XOAI\n","* marcações de idioma\n","\n","Exemplos removidos:\n","\n","* `.none`\n","* `.pt_BR`\n","* `.pt-BR`\n","* `.pt`\n","* `.por`\n","* `.eng`\n","* `.en`\n","* `.es`\n","* `.spa`\n","* `.value`\n","\n","Exemplo:\n","\n","```python\n","dc.subject.pt_BR.value\n","```\n","\n","é convertido para:\n","\n","```python\n","dc.subject\n","```\n","\n","Objetivo:\n","\n","* reduzir variações artificiais entre metadados equivalentes\n","* facilitar buscas semânticas mais consistentes\n","\n","\n","---\n","\n","\n","**🧠 Estratégia de normalização usando Regex**\n","\n","Regex utilizada:\n","\n","```python\n","r\"\\.(none|pt_BR|pt-BR|pt|por|eng|en|es|spa)\\.value$|\\.value$\"\n","```\n","\n","Funcionamento detalhado:\n","\n","### Parte 1\n","\n","```regex\n","\\.\n","```\n","\n","Representa um ponto literal (`.`).\n","\n","O caractere `.` possui significado especial em regex, então precisa ser escapado com `\\`.\n","\n","\n","### Parte 2\n","\n","```regex\n","(none|pt_BR|pt-BR|pt|por|eng|en|es|spa)\n","```\n","\n","Grupo alternativo (`|`) contendo os sufixos linguísticos aceitos.\n","\n","Exemplos compatíveis:\n","\n","* `.none`\n","* `.pt_BR`\n","* `.eng`\n","* `.es`\n","\n","\n","### Parte 3\n","\n","```regex\n","\\.value$\n","```\n","\n","Procura finalizações contendo:\n","\n","* `.value`\n","* apenas no final da string (`$`)\n","\n","Exemplos:\n","\n","* `dc.title.none.value`\n","* `dc.subject.pt_BR.value`\n","\n","\n","### Parte 4\n","\n","```regex\n","|\\.value$\n","```\n","\n","Alternativa adicional para capturar campos que terminam somente em:\n","\n","* `.value`\n","\n","Exemplo:\n","\n","```python\n","dc.title.value\n","```\n","\n","\n","### Resultado da normalização\n","\n","Exemplo original:\n","\n","```python\n","dc.subject.pt_BR.value\n","```\n","\n","Resultado após regex:\n","\n","```python\n","dc.subject\n","```\n","\n","Outro exemplo:\n","\n","```python\n","dc.description.abstract.eng.value\n","```\n","\n","Resultado:\n","\n","```python\n","dc.description.abstract\n","```\n","\n","\n","---\n","\n","\n","**3. Compilar expressões regex dos termos ODS**\n","\n","O script converte cada termo previamente definido na lista de palavras-chave ODS em uma expressão regular (`regex`) compilada para busca textual.\n","\n","**🧠 Estratégia de busca usando Regex**\n","\n","Cada termo é convertido em regex usando:\n","\n","```python\n","rf\"\\b{re.escape(termo.lower())}\\b\"\n","```\n","\n","Funcionamento detalhado:\n","\n","### `re.escape()`\n","\n","Escapa caracteres especiais automaticamente.\n","\n","Exemplo:\n","\n","```python\n","ação contra a mudança global do clima\n","```\n","\n","vira uma regex segura.\n","\n","\n","### `\\b`\n","\n","Representa limite de palavra (`word boundary`).\n","\n","Objetivo:\n","\n","* evitar falsos positivos\n","\n","Exemplo:\n","\n","```python\n","ods\n","```\n","\n","não será encontrado dentro de:\n","\n","```python\n","metodos\n","```\n","\n","mas será encontrado em:\n","\n","```python\n","ODS 13\n","```\n","\n","\n","### `lower()`\n","A busca é feita em minúsculas para evitar problemas de capitalização.\n","\n","\n","---\n","\n","\n","**4. Buscar ocorrências nos campos e valores**\n","\n","O script procura termos ODS em:\n","\n","* nomes dos campos XOAI normalizados\n","* valores armazenados nos campos\n","\n","Exemplos:\n","\n","Campo:\n","\n","```python\n","dc.subject\n","```\n","\n","Valor:\n","\n","```python\n","Objetivos de Desenvolvimento Sustentável\n","```\n","\n","\n","---\n","\n","\n","**5. Classificar tipo de ocorrência**\n","\n","Cada ocorrência recebe uma classificação:\n","\n","* `campo`\n","  → termo encontrado apenas no nome do metadado\n","\n","* `valor`\n","  → termo encontrado apenas no conteúdo\n","\n","* `campo e valor`\n","  → termo encontrado em ambos\n","\n","\n","---\n","\n","\n","**6. Remover duplicações**\n","\n","* prioriza ocorrências classificadas como `campo e valor`\n","\n","\n","---\n","\n","\n","**7. Gerar CSV consolidado**\n","\n","Campos exportados:\n","\n","* repositório\n","* arquivo JSON\n","* termo encontrado\n","* tipo de ocorrência\n","* campo XOAI original (`campo_xoai`)\n","* campo normalizado (`campo_limpo`)\n","* valor encontrado\n","\n","\n","---\n","\n","\n","**⚠️ Observações importantes:**\n","\n","* A detecção é baseada exclusivamente em correspondência textual (`keyword matching`), sem interpretação semântica ou análise de contexto\n","* Termos relacionados aos ODS podem aparecer em alguns registros, sem indicar necessariamente alinhamento temático com os Objetivos de Desenvolvimento Sustentável\n","* O uso de regex com limites de palavra reduz falsos positivos em buscas textuais\n","* A normalização dos campos evita fragmentação causada por qualificadores linguísticos do XOAI"],"metadata":{"id":"0SBY4tunJ7cf"}},{"cell_type":"code","execution_count":null,"metadata":{"colab":{"base_uri":"https://localhost:8080/"},"executionInfo":{"elapsed":2106,"status":"ok","timestamp":1779124187052,"user":{"displayName":"Alessandra2 Belézia","userId":"05649342417093026006"},"user_tz":180},"id":"ilhUeLG_zfj2","outputId":"3be3dcac-2a56-4c71-b82e-b7db7d3e8243"},"outputs":[{"output_type":"stream","name":"stdout","text":["Drive already mounted at /content/drive; to attempt to forcibly remount, call drive.mount(\"/content/drive\", force_remount=True).\n"]}],"source":["from google.colab import drive\n","drive.mount('/content/drive')"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"M1MRUM7w4RP7"},"outputs":[],"source":["import os\n","import json\n","import pandas as pd\n","import re"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"6TatCaWZ4R5G"},"outputs":[],"source":["# CAMINHO BASE\n","BASE_DIR = \"/content/drive/MyDrive/Repositorios-e-ODS/amostra_dcTypes\""]},{"cell_type":"code","execution_count":null,"metadata":{"id":"4Rw0DLLv4Tkb"},"outputs":[],"source":["termos = [\n","    \"ods\", \"sdg\",\n","\n","    \"objetivos de desenvolvimento sustentável\",\n","    \"objetivo de desenvolvimento sustentável\",\n","    \"sustainable development goals\",\n","    \"sustainable development goal\",\n","\n","    *[f\"objetivo {i}\" for i in range(1, 18)],\n","    *[f\"objetivo {i:02d}\" for i in range(1, 18)],\n","    *[f\"goal {i}\" for i in range(1, 18)],\n","    *[f\"goal {i:02d}\" for i in range(1, 18)],\n","\n","    \"no poverty\", \"zero hunger\", \"good health and well-being\",\n","    \"quality education\", \"gender equality\",\n","    \"clean water and sanitation\",\n","    \"affordable and clean energy\",\n","    \"decent work and economic growth\",\n","    \"industry, innovation and infrastructure\",\n","    \"reduced inequalities\",\n","    \"sustainable cities and communities\",\n","    \"responsible consumption and production\",\n","    \"climate action\",\n","    \"life below water\",\n","    \"life on land\",\n","    \"peace, justice and strong institutions\",\n","    \"partnerships for the goals\",\n","\n","    \"erradicação da pobreza\",\n","    \"saúde e bem-estar\",\n","    \"educação de qualidade\",\n","    \"igualdade de gênero\",\n","    \"água potável e saneamento\",\n","    \"energia limpa e acessível\",\n","    \"trabalho decente e crescimento econômico\",\n","    \"indústria, inovação e infraestrutura\",\n","    \"redução das desigualdades\",\n","    \"cidades e comunidades sustentáveis\",\n","    \"consumo e produção responsáveis\",\n","    \"ação contra a mudança global do clima\",\n","    \"vida na água\",\n","    \"vida terrestre\",\n","    \"paz, justiça e instituições eficazes\",\n","    \"parcerias e meios de implementação\"\n","]"]},{"cell_type":"code","source":["# FUNÇÃO PARA NORMALIZAR CAMPOS XOAI\n","def normalizar_campo(campo):\n","\n","    padrao_final = re.compile(\n","        r\"\\.(none|pt_BR|pt-BR|pt|por|eng|en|es|spa)\\.value$|\\.value$\",\n","        re.IGNORECASE\n","    )\n","\n","    return padrao_final.sub(\"\", campo)"],"metadata":{"id":"oRjwptwOD0vO"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":["# COMPILA REGEX DOS TERMOS\n","regex_termos = {\n","    termo: re.compile(rf\"\\b{re.escape(termo.lower())}\\b\")\n","    for termo in termos\n","}"],"metadata":{"id":"chylfa7JwAvS"},"execution_count":null,"outputs":[]},{"cell_type":"code","execution_count":null,"metadata":{"id":"sET58suB4dp0"},"outputs":[],"source":["# FUNÇÃO DE BUSCA\n","def buscar_termos_no_json(caminho_json):\n","\n","    resultados_dict = {}\n","\n","    with open(caminho_json, \"r\", encoding=\"utf-8\") as f:\n","        dados = json.load(f)\n","\n","    for chave_xoai, valores in dados.items():\n","\n","        campo_limpo = normalizar_campo(chave_xoai)\n","\n","        chave_lower = campo_limpo.lower()\n","\n","        for valor in valores:\n","\n","            valor_lower = valor.lower()\n","\n","            for termo, regex in regex_termos.items():\n","\n","                match_campo = regex.search(chave_lower)\n","                match_valor = regex.search(valor_lower)\n","\n","                if not (match_campo or match_valor):\n","                    continue\n","\n","                # CLASSIFICA MATCH\n","                if match_campo and match_valor:\n","                    tipo_match = \"campo e valor\"\n","                elif match_campo:\n","                    tipo_match = \"campo\"\n","                else:\n","                    tipo_match = \"valor\"\n","\n","                # CHAVE ÚNICA\n","                chave_resultado = (\n","                    termo,\n","                    chave_xoai,\n","                    valor\n","                )\n","\n","                # EVITA DUPLICAÇÃO\n","                if chave_resultado in resultados_dict:\n","\n","                    tipo_existente = resultados_dict[chave_resultado][1]\n","\n","                    if (\n","                        tipo_existente != \"campo e valor\"\n","                        and tipo_match == \"campo e valor\"\n","                    ):\n","                        resultados_dict[chave_resultado] = (\n","                            termo,\n","                            tipo_match,\n","                            chave_xoai,\n","                            campo_limpo,\n","                            valor\n","                        )\n","\n","                else:\n","                    resultados_dict[chave_resultado] = (\n","                        termo,\n","                        tipo_match,\n","                        chave_xoai,\n","                        campo_limpo,\n","                        valor\n","                    )\n","\n","    return list(resultados_dict.values())"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"DQwXfE854wT3"},"outputs":[],"source":["# EXECUÇÃO\n","linhas = []\n","\n","for repo in sorted(os.listdir(BASE_DIR)):\n","    repo_path = os.path.join(BASE_DIR, repo)\n","\n","    print (repo)\n","\n","    if not os.path.isdir(repo_path):\n","        continue\n","\n","    for arquivo in os.listdir(repo_path):\n","        if not arquivo.endswith(\".json\"):\n","            continue\n","\n","        caminho_json = os.path.join(repo_path, arquivo)\n","\n","        achados = buscar_termos_no_json(caminho_json)\n","\n","        for termo, tipo, campo_xoai, campo_limpo, valor in achados:\n","\n","            linhas.append({\n","                \"repositorio\": repo,\n","                \"arquivo\": arquivo,\n","                \"termo_encontrado\": termo,\n","                \"tipo_match\": tipo,\n","                \"campo_xoai\": campo_xoai,\n","                \"campo_limpo\": campo_limpo,\n","                \"valor\": valor\n","            })\n","            print(repo, arquivo, termo, tipo, campo_xoai, campo_limpo, valor)\n","\n","# SALVAR RESULTADO\n","df = pd.DataFrame(linhas)\n","\n","saida = os.path.join(\"/content/drive/MyDrive/Repositorios-e-ODS/localiza_ODS-RESULTADO.csv\")\n","df.to_csv(saida, index=False)\n","\n","print(f\"✅ Finalizado! {len(df)} ocorrências encontradas.\")"]},{"cell_type":"markdown","source":[],"metadata":{"id":"8h2-UbIBJd_p"}}],"metadata":{"colab":{"provenance":[]},"kernelspec":{"display_name":"Python 3","name":"python3"},"language_info":{"name":"python"}},"nbformat":4,"nbformat_minor":0}