O script import_data.pl
é responsável por importar dados de um arquivo CSV para o banco de dados do sistema OMLPI.
Ele processa informações sobre indicadores, subindicadores e seus respectivos valores para diferentes localidades e anos.
-
Verificação de Checksum: O script calcula o checksum MD5 do arquivo de dados e compara com o último checksum armazenado no banco de dados para evitar importações desnecessárias.
-
Descompressão de Arquivos: Utiliza a biblioteca Archive::Zip para descomprimir o arquivo de dados.
-
Importação de Indicadores: Carrega informações básicas dos indicadores, como descrição, área, base de dados e ODSs relacionados.
-
Importação de Subindicadores: Processa os desagregadores (subindicadores) associados a cada indicador.
-
Importação de Dados: Carrega os valores dos indicadores e subindicadores para cada localidade e ano.
-
Tratamento de Dados: Realiza conversões e formatações nos valores numéricos para garantir consistência no banco de dados.
-
Atualização de Visões Materializadas: Atualiza as visões materializadas utilizadas para consultas rápidas de indicadores aleatórios.
-
Inicialização:
- Configura o logger para registro de atividades.
- Conecta-se ao banco de dados PostgreSQL.
-
Verificação de Atualização:
- Calcula o checksum MD5 do arquivo de dados.
- Compara com o último checksum armazenado no banco de dados.
- Se forem iguais, encerra o script (não há atualizações).
-
Processamento de Indicadores:
- Lê o arquivo "indicadores.csv".
- Insere ou atualiza informações dos indicadores no banco de dados.
-
Processamento de Subindicadores:
- Lê o arquivo "desagregadores.csv".
- Insere ou atualiza informações dos subindicadores no banco de dados.
-
Processamento de Dados:
- Lê o arquivo "dados.csv".
- Para cada linha:
- Verifica e formata os valores numéricos.
- Insere os dados dos indicadores na tabela temporária
indicator_locale_bulk
. - Insere os dados dos subindicadores na tabela temporária
subindicator_locale_bulk
.
- Após o processamento, copia os dados das tabelas temporárias para as tabelas principais.
-
Pós-processamento:
- Atualiza valores nulos (wildcard) para NULL no banco de dados.
- Atualiza as visões materializadas
random_locale_indicator
erandom_indicator_cache
. - Atualiza o checksum do dataset no banco de dados.
- Define uma flag para gerar o arquivo de dados completo.
- O script utiliza blocos
eval
para capturar e registrar erros durante o processo de importação. - Em caso de erro fatal, o script é encerrado com código de saída 255.
- O script utiliza tabelas temporárias para otimizar a inserção em lote dos dados, porém ainda leva certa de 1h até 3h para rodar, dependendo do hardware.
Embora o script faça esse processamento, se ele fosse re-feito hoje, poderia ser otimizado utilizando
duckdb
para importar boa parte dos dados e tratamentos, depois só exportar novamente para o disco o que mudou, e importar no postgres.
- O script lida com diferentes formatos numéricos, realizando conversões necessárias.
Há várias regexp's para tentar lidar com os números dos CSV's que as vezes estão em pt-br e outras vezes em formato americano. Isso é um pouco arriscado, o ideal é que os CSV's já chegassem sempre no mesmo formato, assim apenas o script poderia sempre criar uma exception caso aparecesse uma linha fora do padrão, e não ter que tentar corrigir o número, podendo gerar um número diferente do correto.
- Há verificações para evitar duplicação de dados durante a importação.
- Utiliza-se um sistema de deduplicação baseado em chaves para evitar entradas duplicadas.
Para reduzir o uso de memoria durante o script, pode-se reverter o commit
0458ec4
que utilizava o DB_File com memory-map em disco para este hash do dedup.
Pode ser chamado manualmente ou agendado para execução periódica, dependendo da frequência de atualização dos dados. Como o arquivo que ele está olhando está dentro do próprio repositório, então a executadão esta sendo feita manualmente.
Os arquivos geralmente são recebidos em varios arquivos separados em UTF-8 com BOM, então para remover o BOM e juntar em um arquivo só:
$ file *
1.csv: Unicode text, UTF-8 (with BOM) text, with very long lines (3099), with CRLF line terminators
2.csv: Unicode text, UTF-8 (with BOM) text, with very long lines (3099), with CRLF line terminators
3.csv: Unicode text, UTF-8 (with BOM) text, with very long lines (3099), with CRLF line terminators
4.csv: Unicode text, UTF-8 (with BOM) text, with very long lines (3099), with CRLF line terminators
Desagregadores.csv: Unicode text, UTF-8 (with BOM) text, with CRLF line terminators
Indicadores.csv: Unicode text, UTF-8 (with BOM) text, with very long lines (563), with CRLF, LF line terminators
Rodar o comando sed
sed -i '1s/^\xEF\xBB\xBF//' *.csv
Após executar, os arquivos devem ficar:
$ file *
1.csv: ASCII text, with very long lines (3099), with CRLF line terminators
2.csv: ASCII text, with very long lines (3099), with CRLF line terminators
3.csv: ASCII text, with very long lines (3099), with CRLF line terminators
4.csv: ASCII text, with very long lines (3099), with CRLF line terminators
Desagregadores.csv: Unicode text, UTF-8 text, with CRLF line terminators
Indicadores.csv: Unicode text, UTF-8 text, with very long lines (563), with CRLF, LF line terminators
E então juntar o arquivo de dados:
cat 1.csv 2.csv 3.csv 4.csv > dados.csv
rm 1.csv 2.csv 3.csv 4.csv
mv Desagregadores.csv desagregadores.csv
mv Indicadores.csv indicadores.csv
zip -7 -m -r versao-XXX.zip *
Mover o zip gerado para @omlpi-api / resources/dataset/
e apontar o link simbólico latest
para o arquivo novo
mv $path-da-versao.zip $path-para-o-repo/resources/dataset/
cd $path-para-o-repo/resources/dataset/
rm latest; ln -s v15.zip latest
git add .; git commit -m 'Nova versão';
Já no servidor, para importação:
O Dockerfile está configurado com o usuário app
, considerando o arquivo de exemplo, e o nome do container omlpi_api
docker exec -it -u [nome do usuário] [nome do container] [comando]
# docker exec -it -u app omlpi_api sh # adicione `sudo` se não estiver de root
$ bash # carrega o bash, isso serve para carregar o arquivo de ~/.bashrc
# que então irá carregar as envs para os scripts perl's
# via perlbrew/local::lib, equivalente ao "nvm" no caso de node
[app@container:/$] cd src/ # troca o diretório para onde o código da app está montando
[app@container:/$] . envfile_local.sh # carrega as variáveis de ambiente, ou por outros meios
[app@container:/$] perl script/import_data.pl # executa o script de importação
-- saida esperada do script:
[dd/mm/yyyy hh:mm:hh] [pid] [INFO] Starting data import...
[dd/mm/yyyy hh:mm:hh] [pid] [INFO] Getting the file checksum
[dd/mm/yyyy hh:mm:hh] [pid] [INFO] Dataset checksum: ac03371e254460f2bc96a5f2a6ddf830
[dd/mm/yyyy hh:mm:hh] [pid] [INFO] Last checksum: a5fdc2186feeb0ccfbd94bddb83442ea
[dd/mm/yyyy hh:mm:hh] [pid] [INFO] New dataset! Let's import data!
...
Caso o script de rollback, ele poderá ser executado novamente, pois as alterações não são persistidas no banco de dados em caso de erro.
https://docs.google.com/document/d/1V3ZUVqFHRzb9yKoXX13r2SdmNZihArqgiZCmiMhTRe0/edit?usp=sharing