Giter Site home page Giter Site logo

dadosabertosdefeira / maria-quiteria Goto Github PK

View Code? Open in Web Editor NEW
165.0 9.0 70.0 1.01 MB

Backend para coleta e disponibilização dos dados 📜

Home Page: https://mq.dadosabertosdefeira.com.br/painel

License: MIT License

Python 96.19% Shell 0.04% JavaScript 0.10% CSS 0.41% HTML 2.95% Dockerfile 0.10% Makefile 0.17% Procfile 0.04%
dados-abertos feira-de-santana civic-tech scrapy hacktoberfest django django-rest-framework

maria-quiteria's People

Contributors

anapaulagomes avatar anaschwendler avatar andrewsmedina avatar beatrizuezu avatar cuducos avatar dependabot-preview[bot] avatar dependabot[bot] avatar edsoncelio avatar elicaciocdefarias avatar ellalves avatar exageraldo avatar felipefrizzo avatar francilioaraujo avatar gabrielcjr avatar gomex avatar jroquejr avatar ladeia avatar luandadantas avatar marcusaldrey avatar matheuscas avatar mayaradg avatar rafavillalta avatar raulbsantos avatar rennerocha avatar rislamiranda avatar rudaporto avatar sheory avatar snifbr avatar soslaio avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

maria-quiteria's Issues

Salvar natureza das despesas

Cada despesa tem uma natureza, determinada pelo TCM-BA. O código da natureza é esse grande número no início do campo natureza abaixo:

natureza

Esses códigos de natureza da despesa estão na especificação de despesa. Em CSV aqui.

Para facilitar a identificação das despesas, filtro e futuro gastos, temos que adicionar essa informação às despesas do nosso banco também.

Importante: existem zeros extras nas informações das despesas da Câmara.

Criar script para validação de amostra dos dados

Algumas páginas oferecem a opção de relatório. Seria interessante ter um script para verificar a presença dos dados do relatório nos dados coletados e mostrar a % de dados encontrados e, caso exista dados que não existem de um lado ou de outro, quais são.

Fazer download e extrair conteúdo de todos os spiders

  • Adicionar suporte ao FilePipelines em todos os spiders (mudando do campo document_url para file_urls, dessa forma o Scrapy pode baixar os arquivos)
  • Adicionar itens aos spiders (items.py)
  • Utilizar o ExtractPDFContentPipeline para extrair o conteúdo dos PDFs (#14)

Spiders:

  • BidsSpider (cityhall.py)
  • ContractsSpider (cityhall.py)
  • LegacyGazetteSpider (gazette.py)

Lidar com arquivos compactados em licitações

Alguns arquivos de licitações são comprimidos, geralmente com o formato .rar ou .zip. O ideal é descompactar esses arquivos e acessar o conteúdo de seus PDFs.

Exemplo:

Vá para a página do Diário Oficial e busque por Palavra:

Contratação de pessoa jurídica para implantação da iluminação e decoração provisória de natal e festas de final de ano (natal encantado 2019), abrangendo instalação, manutenção e retirada, com fornecimento de material, mão de obra e equipamentos necessários.

natal-encantado-rar

Link original: https://www.diariooficial.feiradesantana.ba.gov.br/abrir.asp?edi=1152&p=1
Link do arquivo: http://www.feiradesantana.ba.gov.br/licitacoes/4831pmfstp0482019.rar

Essa issue só poderá ser desenvolvida quando o conteúdo dos PDFs for salvo junto com as licitações no banco de dados.

ATENÇÃO: aparentemente o Tika já reconhece alguns arquivos compactados e extrai o conteúdo deles. Para validar a necessidade dessa issue, esses casos devem ser testados. O ideal seria ter os arquivos e seus conteúdos separados. Atualmente todos os conteúdos do arquivo compactado são colocados juntos.

Importar dados fornecidos pela Câmara

A Câmara disponibilizou arquivos CSV de contratos, despesas, licitações, receitas, arquivos de licitações e arquivos de contratos. Precisamos importar esses dados em models na MQ.

Criar uma página inicial

Essa página inicial seria apenas para ter uma página inicial. 😅 Necessitaria de:

  • Uma página de apresentação do projeto (o que é, como entrar em contato)
  • Link para o dashboard (#65)
  • Links para redes sociais: instagram, facebook, blog, github

Outros links estão disponíveis também no linktree.

Além dos links fixos, um espaço para links temporários, como o relatório do COVID19 em Feira (https://datastudio.google.com/u/0/reporting/cd9d3ac4-299b-46b4-bdab-8e9c081f5071?s=s8-Q-jNGhZg) é necessário.

Suporte ao português no dashboard

Por enquanto o django-admin ainda está em inglês - tanto os nomes das colunas quanto modelos, etc. Seria interessante ter suporte ao português em todo o dashboard.

  • Adicionar valor verboso para os atributos dos modelos
  • Suporte ao português no django-admin (setar LANGUAGE_CODE = "pt-br" no settings.py)
  • Mudar formato de datas para o brasileiro
    USE_L10N = False
    DATE_FORMAT = "d/m/Y"
    DATETIME_FORMAT = "d/m/Y H:i"

Uma outra coisa para se pensar é: atualmente os itens são exportados com as colunas em inglês (por causa do código); valeria a pena traduzi-los também?

Fazer backup dos arquivos

Atualmente baixamos os arquivos localmente, extraímos as informações e depois deletamos (caso a variável KEEP_FILES não esteja configurada). Seria interessante jogar esses arquivos para um S3 ou algo assim.

Tenta baixar arquivo mesmo quando botão de edital não existe

Erro: NotSupported: Unsupported URL scheme 'tel': no handler available for that scheme

Esse erro acontece quando um telefone é colocado como um <a> e o spider entende que é um arquivo (o código assume que os links no texto serão o edital). Para evitar esse erro, podemos: 1) verificar se é uma URL válida; 2) verificar se a imagem com o título "Baixar Edital" está presente.

Spider: cityhall_bids (licitações)

document_url = raw_description.xpath(".//@href").extract_first()

Botão esperado:
Baixar Edital

Exemplo: LICITAÇÃO Nº002-2020 PREGÃO ELETRÔNICO Nº001-2020 – FHFS.
http://www.feiradesantana.ba.gov.br/seadm/licitacoes_pm.asp?cat=FHFS&dt=02-2020#links

Os registros sem data devem ser mostrados por último

Assumindo que as datas são extremamente relevantes para os usuários que consultam os nossos dados, precisamos colocar os registros sem data por último, para evitar algo como:

last-true

Atualmente, na página de diários oficiais os diários sem data são exibidos primeiro, ao invés dos mais recentes.

O modelo de diários já tem um parâmetro para resolver esse problema mas não funciona:

ordering = [F("date").desc(nulls_last=True)]

Esse problema também atinge as licitações - talvez devamos extender a solução para esse problema como padrão a todos os modelos.

Suporte a coleta de dados por data

Atualmente os spiders coletam todos os dados disponíveis. Eles devem ser adaptados para receber uma data para coleta. O objetivo é ter a opção de coletar tudo e também coletar apenas dados do dia anterior.

Spider Estrutura PR
Licitações Uma página com todos links por ano de licitações. #32
Contratos Varre todas as páginas. Tem um campo POST_DATA que pode ser útil. #29
Pagamentos Já utiliza o campo POST_DATA. #27
Agenda Por mês e ano. #30
Diário Oficial Uma linha na tabela por dia. Paginação só serve para vários dias. #31

Coletar parcerias com 3° Setor da prefeitura

Fonte: http://www.transparencia.feiradesantana.ba.gov.br/index.php?view=parcerias

Essa issue consiste em:

  1. Criar um spider para coletar as rparcerias com 3° Setor da prefeitura no módulo cityhall, onde os coletores da prefeitura de Feira de Santana estão.
  2. Salvar os itens coletados (adicionar um model em datasets e salvar os itens no management command crawl.py)

Os itens dessa página possuem um botão de Download (exemplo: http://www.transparencia.feiradesantana.ba.gov.br/parcerias/2019/117/117Termos10110.pdf). Para salvar o conteúdo desses arquivos é necessário apenas preencher o campo file_urls (deve ser uma lista) e ter os campos file_urls e file_content declarados no item (alguns exemplos: https://github.com/DadosAbertosDeFeira/maria-quiteria/blob/master/scraper/items.py).

Salvar pagamentos no banco de dados

Precisamos salvar esses pagamentos no banco de dados. Isso consiste em:

Aqui você pode ver como um pagamento se parece:

class CityHallPaymentsItem(BaseModel):

Alguns campos precisarão ser adaptados, como value. Ao invés de salvarmos como texto, por exemplo, temos que salvar em formato decimal, dessa forma fica mais fácil pra quem for fazer análises no futuro.

Salvar contratos da prefeitura no banco de dados

Precisamos salvar contratos no banco de dados. Isso consiste em:

Aqui você pode ver como um contrato se parece:

class CityHallContractItem(BaseModel):
contract_id = StringType(required=True)
starts_at = DateType(formats=("%d/%m/%Y", "%d/%m/%y"))
summary = StringType()
contractor_document = StringType()
contractor_name = StringType()
value = StringType()
ends_at = DateType(formats=("%d/%m/%Y", "%d/%m/%y"))
file_urls = ListType(StringType)
file_content = StringType()

Alguns campos precisarão ser adaptados, como value. Ao invés de salvarmos como texto, por exemplo, temos que salvar em formato decimal, dessa forma fica mais fácil pra quem for fazer análises no futuro.

Investigar uso de memória na extração de documentos

Ao extrair vários documentos ou documentos grandes o Tika extrapola o uso de memória na coleta de diários oficiais. Essa issue visa:

  • Investigar o quanto de memória é aceitável
  • Como monitorar o vazamento de memórias
  • Medidas para tunar o Tika para melhor uso dos recursos

Atualmente o projeto roda no Heroku com plano básico (512MB de memória). É importante manter nesse plano para barater os custos do projeto.

Fazer upload dos artefatos na nuvem

Todos os arquivos disponíveis para download devem ser arquivados na nuvem (sugestão: Amazon S3). As URLs dos arquivos devem ser salvas no banco de dados para acesso posterior.

Criar um django-admin público

Por enquanto não temos previsão para ter um frontend e seria legal já disponibilizar os dados que temos e deixá-los disponíveis para as pessoas buscarem, assim como o Serenata fez com o Jarbas.

Coletar dados do diário oficial

Um spider para as informações do diário já existe no projeto Diário Oficial (OKFN Brasil) e pode ser visto aqui.

O diário oficial, assim como diversas páginas de sites dos municípios de Feira de Santana, tem dificuldades em manter o padrão. Por isso, tanto o conteúdo dos PDFs quando as informações crawleadas devem ser testadas extensamente. Contribuidores do projeto do diário começaram um trabalho para parsear essas informações: okfn-brasil/querido-diario#106.

A minha ideia aqui é contribuir para o parser e somente depois adicioná-lo ao Maria Quitéria. Embora eu tenha contribuído na adição do spider ao projeto Diário Oficial, qualquer código deles adicionado a esse projeto deve ter os crédios adicionados.

Adicionar histórico de alterações aos models

Em alguns casos, pode haver atualizações dos dados. Por exemplo: um vereador que estava ausente um dia pode ter sua falta justificada no outro. Para acompanhar essas situações, precisamos que os models salvem o histórico das atualizações.

É importante verificar também como fica o histórico em casos de criação ou atualizações em massa (bulk_create/bulk_update).

Alguns projetos que cumprem a tarefa:

Extrair arquivos para um único model

Muitos models tem arquivos (suas URLs e conteúdos). Alguns deles passarão a ter uma lista de arquivo (como licitações). Estava pensando em eliminar a repetição e deixar os arquivos em um único model conectado por objetos genéricos. Algo mais ou menos assim:

class File(models.Model):
    file_url = models.URLField(null=True, blank=True)
    file_content = models.TextField(null=True, blank=True)
    content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
    object_id = models.PositiveIntegerField()
    content_object = GenericForeignKey('content_type', 'object_id')

Essa mudança facilitaria também em uma busca global de arquivos (#23). Uma outra vantagem seria a criação dos índices para a FTS em um lugar só (#80).

O que acham?

Melhorar a busca no conteúdo dos arquivos

Atualmente as bases de dados do painel tem usado a busca simples, que é mais lenta e cara em termos computacionais. Já temos uma busca com full-text search mas pode ser mais rápida e apresentar resultados mais satisfatórios ao buscar no conteúdo dos arquivos.

Busca lenta no django-admin

Antes de a gente botar o bloco na rua com #65, precisamos resolver um problema importante de performance no django-admin. O número de registros não é um problema. O problema é que a busca checa arquivos muito grandes e ainda não temos uma busca, tipo full text search, pra aliviar a carga. A busca está lenta, especialmente, na página dos diários. Alguns diários, sozinhos, tem mais de 200 páginas.

Para reproduzir esse problema, você precisa:

  • Rodar o comando python manage.py crawl com a variável de ambiente FEATURE_FLAG__SAVE_GAZETTE=1
  • Rodar o servidor e, na página de diários, efetuar algumas buscas

Dica do @cuducos: https://github.com/okfn-brasil/serenata-de-amor/blob/master/jarbas/dashboard/admin/paginators.py

Critério para filtrar itens a serem coletados por data é falho

Atualmente o critério utilizado é o mesmo para todas as bases de dados:

return cls.objects.latest("crawled_at").crawled_at.date()

Porém esse critério não funciona para todas as bases. Exemplo:

Base de dados Data Última raspagem Próxima raspagem
Diário Oficial 29/03/2020 31/03/2020 01/04/2020
Contratos 01/08/2017 31/03/2020 01/04/2020

Atualmente vamos checar se os itens novos tem data maior ou igual a data da última raspagem e não a última data do item. Em nenhum dos dois casos vamos conseguir coletar os novos itens, caso eles tenham data menor que a data da última raspagem. No caso dos contratos da prefeitura, por exemplo, os contratos estão desatualizados desde 2017. Se os contratos dos últimos três anos forem atualizados, vamos acabar perdendo-os.
Um outro problema é que nem todos os registros têm, necessariamente, uma data.

Minha ideia no momento: cada model especificar o campo de data a ser utilizado. Caso não tenha um, usamos o crawled_at. O que acham?

Transformar parte Django em um módulo único

Hoje a aplicação Django está espalhada no Maria Quitéria: tanto os módulos datasest quanto core são parte do Django. Podemos ter um módulo web ou backend encapsulando isso tudo, como o Henrique descreve aqui. Me candidato a fazer esse refactor, por sinal ; ) O que acham?

Ideias para melhorar o orquestrador das coletas

Talvez seja só eu sendo chata mesmo mas lá vai: acho que o crawl está ficando bem grandinho e requer alguns passos entre escrever um spider e salvar coisas no banco:

  • escrever o spider
  • criar um item
  • criar um validator
  • criar um model
  • criar método pra salvar no model (com a lógica de atualização)
  • adicionar spider ao crawl
  • adicionar checagem no item e chamar método para salvar no model
  • adicionar deleção na opção de deletar todos

É bastante coisa pra lembrar e torna a entrada de pessoas que não estão familiarizadas com o código mais difícil. E a tendência é que esse comando cresça ainda mais (imagino que triplique).

Ainda não tenho uma ideia formada mas imaginei um módulo com um contrato tendo tudo o que o crawl precisa: o spider, item e model de cada coisa. No crawl só faríamos o match desse contrato pra cada coisa (pra deletar tudo, salvar item, pegar a data de atualização, etc). Faz sentido? Talvez eu precise montar um exemplo pequeno pra explicar melhor... 🤔

Alternativa para CI

Gitlab não roda merge requests de repositórios externos:

Pipelines for merge requests are incompatible with CI/CD for external repositories.

https://docs.gitlab.com/ee/ci/merge_request_pipelines/

Algumas alternativas:

  • GitHub Actions
  • Azure pipelines

Travis seria a Minha escolha número um mas, infelizmente, passou a limitar o número de builds gratuitos.

Adicionar validação aos spiders

Status atual:

Spider Tem validação? PR
Agenda ✔️ #17
Contratos ✔️ #18
Diário Oficial ✔️ ✔️
Diário Oficial (legado - antes de 2015) ✔️ ✔️
Licitações ✔️ #24
Pagamentos ✔️ #26

A validação de itens é feita pelo spidermon. Para adicionar uma nova validação os seguintes passos são necessários:

  • Adicionar um item (em items.py)
  • Adotar item novo no retorno dos spiders desejados
  • Adicionar um validator item, com o mesmo nome do item (em validators.py)
  • Adicionar novo validator nas configurações (em settings.py)

Salvar dados crawleados em um banco de dados

Salvar dados dos atuais spiders:

  • Agenda da Câmara Municipal.
  • Contratos da Prefeitura (#63)
  • Diário Oficial (incluindo o legado) da Prefeitura/Câmara de Vereadores
  • Licitações da Prefeitura (#69)
  • Pagamentos da Prefeitura (#70)

Monitorar execução dos spiders

Algumas ideias:

  • Utilizar monitors do spidermon (#42) ✔️
  • Criar uma página de status com Scrapy stats 🔜
  • Enviar um email com um relatório da execução dos spiders (e estatísticas do spidermon)
  • Utilizar Sentry ✔️

Migrar e adaptar contratos

Um crawler para os contratos existem atualmente aqui. Durante a migração

  • Raspar dados além do PDF como objeto, contratada, valor, número do contrato
  • Pegar o número total de páginas dinamicamente
  • Utilizar a API do Scrapy ao invés da rows

Coletar lista de presença dos vereadores

Após pedido via Lei de Acesso a Informação e cobrança pela imprensa local, a Câmara de Vereadores passou a disponibilizar a lista de presença nas notícias do site e no portal da transparência (https://www.feiradesantana.ba.leg.br/lista-presenca).

A página parece bastante similar ao crawler da agenda dos vereadores que consta no site.

Devemos coletar essas informações diariamente. Essa issue consiste em:

  1. Criar um spider para coleta (no módulo https://github.com/DadosAbertosDeFeira/maria-quiteria/blob/master/scraper/spiders/citycouncil.py)
  2. Salvar os itens coletados (adicionar um model em datasets e salvar os itens no management command crawl.py)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.