Repository: prefeitura-rio/pipelines_rj_smtr
Pull request #522: [HOTFIX] Captura SERPRO
Author: login: akaBotelho
Created: 2025-04-07T15:42:13Z | Merged: 2025-04-07T15:56:25Z



<!-- This is an auto-generated comment: release notes by coderabbit.ai -->
## Summary by CodeRabbit

- **Refatoração**
  - Atualizada a rotina de extração de dados para aceitar um parâmetro de data, com conversão aprimorada dos valores temporais.
  - Reestruturado o processamento para trabalhar com partições menores, ajustar o volume de dados processados e melhorar o monitoramento interno do fluxo.
  - Aprimorada a manipulação de arquivos temporários e a lógica de tratamento de erros.

- **Chores**
  - Removido um comentário redundante, aprimorando a clareza do fluxo interno.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->


Diff:
diff --git a/pipelines/capture/serpro/flows.py b/pipelines/capture/serpro/flows.py
index f65c58049..1e09ff83d 100644
--- a/pipelines/capture/serpro/flows.py
+++ b/pipelines/capture/serpro/flows.py
@@ -30,5 +30,3 @@
 ]
 
 CAPTURA_SERPRO.schedule = create_serpro_schedule()
-
-# triger CD
diff --git a/pipelines/capture/serpro/tasks.py b/pipelines/capture/serpro/tasks.py
index 6674a7983..efd93d126 100644
--- a/pipelines/capture/serpro/tasks.py
+++ b/pipelines/capture/serpro/tasks.py
@@ -32,10 +32,12 @@ def create_serpro_extractor(
         Callable: Função para extração dos dados
     """
 
-    def extract_data():
+    def extract_data(timestamp):
+        """
+        Extrai dados do SERPRO
+        """
         ts = datetime.fromisoformat(timestamp) if isinstance(timestamp, str) else timestamp
-
-        start_date = ts.date().strftime("%Y-%m-%d")
+        start_date = ts.date()
 
         if ts.month == 12:
             next_month = ts.replace(year=ts.year + 1, month=1, day=1)
@@ -43,45 +45,61 @@ def extract_data():
             next_month = ts.replace(month=ts.month + 1, day=1)
 
         last_day = next_month - timedelta(days=1)
-        end_date = last_day.date().strftime("%Y-%m-%d")
+        end_date = last_day.date()
+
+        with tempfile.NamedTemporaryFile(mode="w+", delete=False, suffix=".csv") as temp_file:
+            csv_writer = csv.writer(temp_file)
+            header = False
+            total_rows = 0
 
-        jdbc = JDBC(db_params_secret_path="radar_serpro", environment="dev")
-        try:
-            query = constants.SERPRO_CAPTURE_PARAMS.value["query"].format(
-                start_date=start_date, end_date=end_date
-            )
+            current_date = start_date
+            partition_size = timedelta(days=5)
 
-            jdbc.execute_query(query)
-            columns = jdbc.get_columns()
+            try:
+                jdbc = JDBC(db_params_secret_path="radar_serpro", environment="dev")
 
-            temp_file = tempfile.NamedTemporaryFile(mode="w+", delete=False, suffix=".csv")
-            csv_writer = csv.writer(temp_file)
+                while current_date <= end_date:
+                    partition_end = min(current_date + partition_size, end_date)
 
-            csv_writer.writerow(columns)
+                    log(f"Processando partição: {current_date} até {partition_end}")
 
-            batch_size = 50000
-            total_rows = 0
+                    query = constants.SERPRO_CAPTURE_PARAMS.value["query"].format(
+                        start_date=current_date.strftime("%Y-%m-%d"),
+                        end_date=partition_end.strftime("%Y-%m-%d"),
+                    )
+
+                    jdbc.execute_query(query)
+
+                    if not header:
+                        columns = jdbc.get_columns()
+                        csv_writer.writerow(columns)
+                        header = True
+
+                    batch_size = 5000
+                    while True:
+                        rows = jdbc.fetch_batch(batch_size=batch_size)
+                        if not rows:
+                            break
+                        csv_writer.writerows(rows)
+                        total_rows += len(rows)
+
+                    current_date = partition_end + timedelta(days=1)
+
+                log(f"Total de registros encontrados: {total_rows}")
 
-            while True:
-                rows = jdbc.fetch_batch(batch_size=batch_size)
-                if not rows:
-                    break
+                temp_file_name = temp_file.name
 
-                for row in rows:
-                    csv_writer.writerow(row)
-                    total_rows += 1
+            except Exception as e:
+                log(f"Erro ao extrair dados do SERPRO: {str(e)}", level="error")
+                raise
+            finally:
+                jdbc.close()
 
-            log(f"Total de registros encontrados: {total_rows}")
+        with open(temp_file_name, "r") as f:
+            result = f.read()
 
-            with open(temp_file.name, "r") as f:
-                result = f.read()
+        os.unlink(temp_file_name)
 
-            os.unlink(temp_file.name)
-            return result
-        except Exception as e:
-            log(f"Erro ao extrair dados do SERPRO: {str(e)}", level="error")
-            raise
-        finally:
-            jdbc.close()
+        return result
 
     return extract_data


Reviews:
user: login: coderabbitai[bot]
body: **Actionable comments posted: 2**

<details>
<summary>🧹 Nitpick comments (1)</summary><blockquote>

<details>
<summary>pipelines/capture/serpro/tasks.py (1)</summary><blockquote>

`90-99`: **Tratamento adequado de arquivos temporários**

O arquivo temporário é fechado corretamente e depois removido com `os.unlink()`. No entanto, há uma possível melhoria de segurança:


```diff
-        with open(temp_file_name, "r") as f:
-            result = f.read()
-
-        os.unlink(temp_file_name)
+        try:
+            with open(temp_file_name, "r") as f:
+                result = f.read()
+        finally:
+            os.unlink(temp_file_name)
```

Esta alteração garante que o arquivo temporário seja sempre excluído, mesmo que ocorra uma exceção durante a leitura.

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: CHILL**
**Plan: Pro**

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between 4e144f986e902022bb4ccc6fab978f070a78d321 and 4c26fc9ec983fe00adf9864f5fb4e86d8c5f6add.

</details>

<details>
<summary>📒 Files selected for processing (2)</summary>

* `pipelines/capture/serpro/flows.py` (0 hunks)
* `pipelines/capture/serpro/tasks.py` (1 hunks)

</details>

<details>
<summary>💤 Files with no reviewable changes (1)</summary>

* pipelines/capture/serpro/flows.py

</details>

<details>
<summary>🧰 Additional context used</summary>

<details>
<summary>🧬 Code Definitions (1)</summary>

<details>
<summary>pipelines/capture/serpro/tasks.py (2)</summary><blockquote>

<details>
<summary>pipelines/utils/jdbc.py (5)</summary>

* `JDBC` (9-79)
* `execute_query` (45-52)
* `get_columns` (54-58)
* `fetch_batch` (60-64)
* `close` (72-79)

</details>
<details>
<summary>pipelines/capture/serpro/constants.py (1)</summary>

* `constants` (13-52)

</details>

</blockquote></details>

</details>

</details>

<details>
<summary>⏰ Context from checks skipped due to timeout of 90000ms (4)</summary>

* GitHub Check: Build Fedora Image
* GitHub Check: Build and publish docs
* GitHub Check: Build Docker image
* GitHub Check: Summary

</details>

<details>
<summary>🔇 Additional comments (5)</summary><blockquote>

<details>
<summary>pipelines/capture/serpro/tasks.py (5)</summary>

`35-41`: **Mudança na assinatura da função e manipulação de datas**

A função agora recebe um parâmetro `timestamp` explícito, o que melhora a clareza da API. A conversão de `timestamp` para objeto date é uma abordagem melhor do que usar strings formatadas.

---

`50-54`: **Melhoria no gerenciamento de arquivos temporários**

A utilização do gerenciador de contexto (`with`) para o arquivo temporário é uma boa prática, garantindo que recursos sejam liberados adequadamente. No entanto, observe que você está definindo `delete=False`, o que requer a remoção manual do arquivo mais tarde.

---

`55-57`: **Implementação de particionamento eficiente**

A introdução do processamento em partições de 5 dias é uma excelente otimização para lidar com grandes volumes de dados, reduzindo a pressão de memória e melhorando a estabilidade.

---

`78-86`: **Redução do tamanho do lote para melhor desempenho**

A redução do tamanho do lote de 50.000 para 5.000 pode ajudar a equilibrar o uso de memória e desempenho. Esta é uma boa prática para evitar problemas de memória ao processar grandes conjuntos de dados.

---

`35-100`: <details>
<summary>❓ Verification inconclusive</summary>

**Verificar o impacto da mudança na assinatura da função**

A função `extract_data` agora requer um parâmetro `timestamp`, o que pode causar problemas se for chamada de outros lugares sem este parâmetro. Certifique-se de que todos os chamadores foram atualizados para fornecer este parâmetro.

---

🏁 Script executed:

```shell
#!/bin/bash
# Verifica se há chamadas para extract_data sem o parâmetro timestamp
rg -p "extract_data\(\s*\)" --type py
```

Length of output: 39

---



</details>

**Atenção: Impacto na assinatura da função extract_data**  
A função `extract_data` agora exige o parâmetro `timestamp`. Nossa busca automatizada utilizando o comando regex não encontrou chamadas com a assinatura antiga (sem argumentos). Contudo, é importante confirmar manualmente se não existem invocações dinâmicas ou indiretas que possam estar omitindo o parâmetro. Verifique se:  
- Todos os pontos de chamada da função foram atualizados para fornecer o parâmetro `timestamp`.  
- Chamadas via importação dinâmica ou via frameworks que possam construir a assinatura de forma implícita não existam.

</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-07T15:45:11Z
user: login: adrianomsn
state: DISMISSED
author_association: CONTRIBUTOR
submitted_at: 2025-04-07T15:47:07Z
user: login: adrianomsn
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-04-07T15:53:14Z
user: login: coderabbitai[bot]
body: **Actionable comments posted: 1**

<details>
<summary>♻️ Duplicate comments (2)</summary><blockquote>

<details>
<summary>pipelines/capture/serpro/tasks.py (2)</summary><blockquote>

`58-59`: **Tratamento de erro na instanciação do JDBC**

A instanciação do JDBC dentro do bloco `try` é uma melhoria em relação à versão anterior, garantindo que qualquer erro na conexão seja capturado e tratado adequadamente.

---

`92-94`: **Tratamento de exceções específicas**

O tratamento de exceções foi melhorado com o registro detalhado do erro antes de relançar a exceção. Isso facilitará a identificação de problemas durante a execução.

</blockquote></details>

</blockquote></details>

<details>
<summary>🧹 Nitpick comments (3)</summary><blockquote>

<details>
<summary>pipelines/capture/serpro/tasks.py (3)</summary><blockquote>

`35-38`: **Melhoria na documentação da função extract_data**

A documentação da função `extract_data` poderia ser melhorada para explicar como o parâmetro `timestamp` é utilizado para determinar o período de extração de dados. Também é importante mencionar que a função retorna os dados em formato CSV como string.

```diff
    def extract_data(timestamp):
        """
        Extrai dados do SERPRO
+       
+       Args:
+           timestamp (Union[str, datetime]): Data de referência para extração
+           
+       Returns:
+           str: Dados extraídos em formato CSV
        """
```

---

`55-57`: **Parametrização do tamanho da partição**

Considere tornar o tamanho da partição configurável através de uma constante em vez de valor fixo.

```diff
-            partition_size = timedelta(days=5)
+            partition_size = timedelta(days=constants.SERPRO_CAPTURE_PARAMS.value.get("partition_days", 5))
```

Isso tornaria o código mais flexível para ajustes futuros sem necessidade de alterar o código-fonte.

---

`78-78`: **Redução do tamanho do lote**

A redução do tamanho do lote para 5000 registros é uma boa prática para gerenciar melhor o uso de memória. Considere tornar esse valor configurável:

```diff
-                    batch_size = 5000
+                    batch_size = constants.SERPRO_CAPTURE_PARAMS.value.get("batch_size", 5000)
```

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: CHILL**
**Plan: Pro**

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between 4c26fc9ec983fe00adf9864f5fb4e86d8c5f6add and 5a3d4522648bb41bdc7a46da272941048a739016.

</details>

<details>
<summary>📒 Files selected for processing (1)</summary>

* `pipelines/capture/serpro/tasks.py` (1 hunks)

</details>

<details>
<summary>🧰 Additional context used</summary>

<details>
<summary>🧬 Code Definitions (1)</summary>

<details>
<summary>pipelines/capture/serpro/tasks.py (2)</summary><blockquote>

<details>
<summary>pipelines/utils/jdbc.py (5)</summary>

* `JDBC` (9-79)
* `execute_query` (45-52)
* `get_columns` (54-58)
* `fetch_batch` (60-64)
* `close` (72-79)

</details>
<details>
<summary>pipelines/capture/serpro/constants.py (1)</summary>

* `constants` (13-52)

</details>

</blockquote></details>

</details>

</details>

<details>
<summary>⏰ Context from checks skipped due to timeout of 90000ms (4)</summary>

* GitHub Check: Build Fedora Image
* GitHub Check: Build and publish docs
* GitHub Check: Build Docker image
* GitHub Check: Summary

</details>

<details>
<summary>🔇 Additional comments (6)</summary><blockquote>

<details>
<summary>pipelines/capture/serpro/tasks.py (6)</summary>

`39-41`: **Aprovação da conversão de timestamp**

A conversão do timestamp para objeto datetime e a extração da data estão corretas. Isso garante flexibilidade ao aceitar tanto string ISO quanto objeto datetime como entrada.

---

`48-49`: **Aprovação do cálculo de data final**

A conversão de `last_day` para objeto `date` está correta e melhora a consistência ao trabalhar com os mesmos tipos de dados para as datas de início e fim.

---

`50-53`: **Boa prática no gerenciamento de arquivo temporário**

O uso do contexto `with` para o arquivo temporário é uma boa prática. A configuração `delete=False` permite que o arquivo seja acessado posteriormente, fora do bloco.

---

`64-64`: **Melhoria na observabilidade com logs**

A adição do log informando a partição sendo processada melhora significativamente a observabilidade do processo, facilitando o monitoramento e a depuração.

---

`66-70`: **Formatação de datas para a consulta SQL**

A formatação de datas para a consulta SQL está correta. A consulta utiliza as datas de início e fim da partição atual para filtrar os dados.

---

`35-104`: <details>
<summary>❓ Verification inconclusive</summary>

**Verificar o teste da função após alteração da assinatura**

A alteração da assinatura da função `extract_data` para aceitar um parâmetro `timestamp` pode exigir atualizações nos testes existentes.

---

🏁 Script executed:

```shell
#!/bin/bash
# Verificar se existem testes que chamam a função extract_data

echo "Procurando por testes que usam a função extract_data..."
rg -A 2 -B 2 "extract_data\(" --type py --glob "test_*.py" ./pipelines/capture/serpro/
```

Length of output: 209

---



</details>

**Atenção: Verificar manualmente os testes de `extract_data`**

Apesar da busca automatizada não ter retornado chamadas à função em arquivos de teste na pasta `pipelines/capture/serpro/`, é essencial confirmar se a alteração da assinatura para aceitar um parâmetro `timestamp` foi refletida em todos os testes existentes. Caso os testes não tenham sido atualizados ou não existam, recomenda-se:

- Revisar outras áreas do repositório onde `extract_data` é utilizada.
- Atualizar ou adicionar casos de teste para cobrir a nova assinatura da função e evitar regressões.

</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-07T15:55:49Z

Review comments:
diff_hunk: @@ -32,56 +32,70 @@ def create_serpro_extractor(
         Callable: Função para extração dos dados
     """
 
-    def extract_data():
+    def extract_data(timestamp):
+        """
+        Extrai dados do SERPRO
+        """
         ts = datetime.fromisoformat(timestamp) if isinstance(timestamp, str) else timestamp
-
-        start_date = ts.date().strftime("%Y-%m-%d")
+        start_date = ts.date()
 
         if ts.month == 12:
             next_month = ts.replace(year=ts.year + 1, month=1, day=1)
         else:
             next_month = ts.replace(month=ts.month + 1, day=1)
 
         last_day = next_month - timedelta(days=1)
-        end_date = last_day.date().strftime("%Y-%m-%d")
+        end_date = last_day.date()
 
-        jdbc = JDBC(db_params_secret_path="radar_serpro", environment="dev")
-        try:
-            query = constants.SERPRO_CAPTURE_PARAMS.value["query"].format(
-                start_date=start_date, end_date=end_date
-            )
+        with tempfile.NamedTemporaryFile(mode="w+", delete=False, suffix=".csv") as temp_file:
+            csv_writer = csv.writer(temp_file)
+            header = False
+            total_rows = 0
 
-            jdbc.execute_query(query)
-            columns = jdbc.get_columns()
+            current_date = start_date
+            partition_size = timedelta(days=5)
 
-            temp_file = tempfile.NamedTemporaryFile(mode="w+", delete=False, suffix=".csv")
-            csv_writer = csv.writer(temp_file)
+            jdbc = JDBC(db_params_secret_path="radar_serpro", environment="dev")
 
-            csv_writer.writerow(columns)
+            try:
+                while current_date <= end_date:
+                    partition_end = min(current_date + partition_size, end_date)
 
-            batch_size = 50000
-            total_rows = 0
+                    log(f"Processando partição: {current_date} até {partition_end}")
+
+                    query = constants.SERPRO_CAPTURE_PARAMS.value["query"].format(
+                        start_date=current_date.strftime("%Y-%m-%d"),
+                        end_date=partition_end.strftime("%Y-%m-%d"),
+                    )
+
+                    jdbc.execute_query(query)
+
+                    if not header:
+                        columns = jdbc.get_columns()
+                        csv_writer.writerow(columns)
+                        header = True
+
+                    batch_size = 5000
+                    while True:
+                        rows = jdbc.fetch_batch(batch_size=batch_size)
+                        if not rows:
+                            break
+                        csv_writer.writerows(rows)
+                        total_rows += len(rows)
+
+                    current_date = partition_end + timedelta(days=1)
 
path: pipelines/capture/serpro/tasks.py
user: login: coderabbitai[bot]
body: _🛠️ Refactor suggestion_

**Adicionar tratamento de exceções específicas**

O código atual tem um bloco `try-finally` sem captura de exceções específicas. Você pode melhorar o tratamento de erros para fornecer mensagens mais informativas:


```diff
             try:
                 while current_date <= end_date:
                     partition_end = min(current_date + partition_size, end_date)

                     log(f"Processando partição: {current_date} até {partition_end}")
 
                     query = constants.SERPRO_CAPTURE_PARAMS.value["query"].format(
                         start_date=current_date.strftime("%Y-%m-%d"),
                         end_date=partition_end.strftime("%Y-%m-%d"),
                     )
 
                     jdbc.execute_query(query)
 
                     if not header:
                         columns = jdbc.get_columns()
                         csv_writer.writerow(columns)
                         header = True
 
                     batch_size = 5000
                     while True:
                         rows = jdbc.fetch_batch(batch_size=batch_size)
                         if not rows:
                             break
                         csv_writer.writerows(rows)
                         total_rows += len(rows)
 
                     current_date = partition_end + timedelta(days=1)

                 log(f"Total de registros encontrados: {total_rows}")

                 temp_file_name = temp_file.name
+            except Exception as e:
+                log(f"Erro durante a extração dos dados: {str(e)}", level="error")
+                raise
             finally:
                 jdbc.close()
```

Esta alteração fornece uma mensagem de erro mais informativa e mantém o comportamento de propagação da exceção.


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
            try:
                while current_date <= end_date:
                    partition_end = min(current_date + partition_size, end_date)

                    log(f"Processando partição: {current_date} até {partition_end}")

                    query = constants.SERPRO_CAPTURE_PARAMS.value["query"].format(
                        start_date=current_date.strftime("%Y-%m-%d"),
                        end_date=partition_end.strftime("%Y-%m-%d"),
                    )

                    jdbc.execute_query(query)

                    if not header:
                        columns = jdbc.get_columns()
                        csv_writer.writerow(columns)
                        header = True

                    batch_size = 5000
                    while True:
                        rows = jdbc.fetch_batch(batch_size=batch_size)
                        if not rows:
                            break
                        csv_writer.writerows(rows)
                        total_rows += len(rows)

                    current_date = partition_end + timedelta(days=1)

                log(f"Total de registros encontrados: {total_rows}")

                temp_file_name = temp_file.name
            except Exception as e:
                log(f"Erro durante a extração dos dados: {str(e)}", level="error")
                raise
            finally:
                jdbc.close()
`````

</details>

<!-- suggestion_end -->

<!-- This is an auto-generated comment by CodeRabbit -->

✅ Addressed in commit 5a3d452
created_at: 2025-04-07T15:45:11Z
author_association: CONTRIBUTOR
diff_hunk: @@ -32,56 +32,70 @@
         Callable: Função para extração dos dados
     """
 
-    def extract_data():
+    def extract_data(timestamp):
+        """
+        Extrai dados do SERPRO
+        """
         ts = datetime.fromisoformat(timestamp) if isinstance(timestamp, str) else timestamp
-
-        start_date = ts.date().strftime("%Y-%m-%d")
+        start_date = ts.date()
 
         if ts.month == 12:
             next_month = ts.replace(year=ts.year + 1, month=1, day=1)
         else:
             next_month = ts.replace(month=ts.month + 1, day=1)
 
         last_day = next_month - timedelta(days=1)
-        end_date = last_day.date().strftime("%Y-%m-%d")
+        end_date = last_day.date()
 
-        jdbc = JDBC(db_params_secret_path="radar_serpro", environment="dev")
-        try:
-            query = constants.SERPRO_CAPTURE_PARAMS.value["query"].format(
-                start_date=start_date, end_date=end_date
-            )
+        with tempfile.NamedTemporaryFile(mode="w+", delete=False, suffix=".csv") as temp_file:
+            csv_writer = csv.writer(temp_file)
+            header = False
+            total_rows = 0
 
-            jdbc.execute_query(query)
-            columns = jdbc.get_columns()
+            current_date = start_date
+            partition_size = timedelta(days=5)
 
-            temp_file = tempfile.NamedTemporaryFile(mode="w+", delete=False, suffix=".csv")
-            csv_writer = csv.writer(temp_file)
+            jdbc = JDBC(db_params_secret_path="radar_serpro", environment="dev")
 
-            csv_writer.writerow(columns)
+            try:
+                while current_date <= end_date:
+                    partition_end = min(current_date + partition_size, end_date)
 
-            batch_size = 50000
-            total_rows = 0
+                    log(f"Processando partição: {current_date} até {partition_end}")
+
+                    query = constants.SERPRO_CAPTURE_PARAMS.value["query"].format(
+                        start_date=current_date.strftime("%Y-%m-%d"),
+                        end_date=partition_end.strftime("%Y-%m-%d"),
+                    )
+
+                    jdbc.execute_query(query)
+
+                    if not header:
+                        columns = jdbc.get_columns()
+                        csv_writer.writerow(columns)
+                        header = True
+
path: pipelines/capture/serpro/tasks.py
user: login: coderabbitai[bot]
body: _⚠️ Potential issue_

**Verificar o tratamento de erros na conexão JDBC**

A conexão JDBC agora é instanciada após a criação do arquivo temporário, o que é bom. No entanto, o bloco `try` começa apenas na linha 60, deixando a linha 58 (criação da conexão) fora do tratamento de exceções.


```diff
-            jdbc = JDBC(db_params_secret_path="radar_serpro", environment="dev")
-
-            try:
+            try:
+                jdbc = JDBC(db_params_secret_path="radar_serpro", environment="dev")
```

Isso garante que a instanciação da conexão JDBC também seja protegida pelo tratamento de exceções.


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
            try:
                jdbc = JDBC(db_params_secret_path="radar_serpro", environment="dev")

                while current_date <= end_date:
                    partition_end = min(current_date + partition_size, end_date)

                    log(f"Processando partição: {current_date} até {partition_end}")

                    query = constants.SERPRO_CAPTURE_PARAMS.value["query"].format(
                        start_date=current_date.strftime("%Y-%m-%d"),
                        end_date=partition_end.strftime("%Y-%m-%d"),
                    )

                    jdbc.execute_query(query)

                    if not header:
                        columns = jdbc.get_columns()
                        csv_writer.writerow(columns)
                        header = True
`````

</details>

<!-- suggestion_end -->

<!-- This is an auto-generated comment by CodeRabbit -->

✅ Addressed in commit 5a3d452
created_at: 2025-04-07T15:45:11Z
author_association: CONTRIBUTOR
diff_hunk: @@ -32,56 +32,74 @@ def create_serpro_extractor(
         Callable: Função para extração dos dados
     """
 
-    def extract_data():
+    def extract_data(timestamp):
+        """
+        Extrai dados do SERPRO
+        """
         ts = datetime.fromisoformat(timestamp) if isinstance(timestamp, str) else timestamp
-
-        start_date = ts.date().strftime("%Y-%m-%d")
+        start_date = ts.date()
 
         if ts.month == 12:
             next_month = ts.replace(year=ts.year + 1, month=1, day=1)
         else:
             next_month = ts.replace(month=ts.month + 1, day=1)
 
         last_day = next_month - timedelta(days=1)
-        end_date = last_day.date().strftime("%Y-%m-%d")
+        end_date = last_day.date()
+
+        with tempfile.NamedTemporaryFile(mode="w+", delete=False, suffix=".csv") as temp_file:
+            csv_writer = csv.writer(temp_file)
+            header = False
+            total_rows = 0
 
-        jdbc = JDBC(db_params_secret_path="radar_serpro", environment="dev")
-        try:
-            query = constants.SERPRO_CAPTURE_PARAMS.value["query"].format(
-                start_date=start_date, end_date=end_date
-            )
+            current_date = start_date
+            partition_size = timedelta(days=5)
 
-            jdbc.execute_query(query)
-            columns = jdbc.get_columns()
+            try:
+                jdbc = JDBC(db_params_secret_path="radar_serpro", environment="dev")
 
-            temp_file = tempfile.NamedTemporaryFile(mode="w+", delete=False, suffix=".csv")
-            csv_writer = csv.writer(temp_file)
+                while current_date <= end_date:
+                    partition_end = min(current_date + partition_size, end_date)
 
-            csv_writer.writerow(columns)
+                    log(f"Processando partição: {current_date} até {partition_end}")
 
-            batch_size = 50000
-            total_rows = 0
+                    query = constants.SERPRO_CAPTURE_PARAMS.value["query"].format(
+                        start_date=current_date.strftime("%Y-%m-%d"),
+                        end_date=partition_end.strftime("%Y-%m-%d"),
+                    )
+
+                    jdbc.execute_query(query)
+
+                    if not header:
+                        columns = jdbc.get_columns()
+                        csv_writer.writerow(columns)
+                        header = True
+
+                    batch_size = 5000
+                    while True:
+                        rows = jdbc.fetch_batch(batch_size=batch_size)
+                        if not rows:
+                            break
+                        csv_writer.writerows(rows)
+                        total_rows += len(rows)
+
+                    current_date = partition_end + timedelta(days=1)
+
+                log(f"Total de registros encontrados: {total_rows}")
 
-            while True:
-                rows = jdbc.fetch_batch(batch_size=batch_size)
-                if not rows:
-                    break
+                temp_file_name = temp_file.name
 
-                for row in rows:
-                    csv_writer.writerow(row)
-                    total_rows += 1
+            except Exception as e:
+                log(f"Erro ao extrair dados do SERPRO: {str(e)}", level="error")
+                raise
+            finally:
+                jdbc.close()
 
-            log(f"Total de registros encontrados: {total_rows}")
+        with open(temp_file_name, "r") as f:
+            result = f.read()
 
-            with open(temp_file.name, "r") as f:
-                result = f.read()
+        os.unlink(temp_file_name)
path: pipelines/capture/serpro/tasks.py
user: login: coderabbitai[bot]
body: _🛠️ Refactor suggestion_

**Uso de contexto para leitura do arquivo temporário**

Recomendo usar um contexto `with` também para a leitura do arquivo temporário, garantindo que ele seja fechado corretamente:

```diff
-        with open(temp_file_name, "r") as f:
-            result = f.read()
-
-        os.unlink(temp_file_name)
+        try:
+            with open(temp_file_name, "r") as f:
+                result = f.read()
+            return result
+        finally:
+            os.unlink(temp_file_name)
```

Isso garante que o arquivo seja removido mesmo em caso de exceção durante a leitura.



<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
        try:
            with open(temp_file_name, "r") as f:
                result = f.read()
            return result
        finally:
            os.unlink(temp_file_name)
`````

</details>

<!-- suggestion_end -->

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-04-07T15:55:48Z
author_association: CONTRIBUTOR

PR comments:
user: login: coderabbitai[bot]
created_at: 2025-04-07T15:42:21Z
body: <!-- This is an auto-generated comment: summarize by coderabbit.ai -->
<!-- walkthrough_start -->

## Walkthrough
Esta mudança remove linhas de comentário em um dos arquivos e modifica a função de extração de dados em outro. No arquivo `flows.py`, foram removidas duas linhas de comentário sem impacto funcional. Em `tasks.py`, a função `extract_data` foi alterada para aceitar um parâmetro `timestamp` e ajustou a lógica interna, incluindo o processamento em partições de cinco dias, redução do tamanho do lote e mudanças na ordem de instanciamento da conexão JDBC, além da remoção do tratamento explícito de erros.

## Changes

| Arquivo                                      | Resumo das mudanças                                                                                                                                                             |
|----------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| pipelines/capture/serpro/flows.py              | Remoção de duas linhas de comentário (ex.: "# triger CD"); nenhuma alteração na lógica funcional.                                                                              |
| pipelines/capture/serpro/tasks.py              | Modificação da assinatura da função `extract_data` para incluir o parâmetro `timestamp`; ajustes na criação e uso de datas (agora como `datetime.date`); mudança na ordem de criação do arquivo temporário e JDBC; execução de consultas divididas em partições de cinco dias; redução no tamanho do lote de 50.000 para 5.000; remoção de tratamento de erros (try-except-finally) e adição de logs para partições processadas. |

## Sequence Diagram(s)
```mermaid
sequenceDiagram
    participant Usuário
    participant Extractor
    participant TempFile
    participant JDBC

    Usuário->>Extractor: Chama extract_data(timestamp)
    Extractor->>TempFile: Cria arquivo temporário
    TempFile-->>Extractor: Retorna handle do arquivo
    Extractor->>JDBC: Instancia conexão JDBC
    Extractor->>JDBC: Executa consulta em partições (5 dias)
    JDBC-->>Extractor: Retorna dados da partição
    Extractor->>Extractor: Log de partição processada
    Extractor->>TempFile: Fecha e deleta o arquivo temporário
```

## Suggested reviewers
- pixuimpou

## Poem
> Sou um coelho saltitante,  
> Com bytes de alegria a cantar,  
> Nos fluxos e nas tarefas,  
> Novas trilhas a traçar,  
> Saltando no código, vou celebrar!  
> 🐰✨

<!-- walkthrough_end -->
<!-- internal state start -->


<!-- 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 -->

<!-- internal state end -->
<!-- finishing_touch_checkbox_start -->

<details>
<summary>✨ Finishing Touches</summary>

- [ ] <!-- {"checkboxId": "7962f53c-55bc-4827-bfbf-6a18da830691"} --> 📝 Generate Docstrings

</details>

<!-- finishing_touch_checkbox_end -->
<!-- tips_start -->

---



<details>
<summary>🪧 Tips</summary>

### Chat

There are 3 ways to chat with [CodeRabbit](https://coderabbit.ai?utm_source=oss&utm_medium=github&utm_campaign=prefeitura-rio/pipelines_rj_smtr&utm_content=522):

- Review comments: Directly reply to a review comment made by CodeRabbit. Example:
  - `I pushed a fix in commit <commit_id>, please review it.`
  - `Generate unit testing code for this file.`
  - `Open a follow-up GitHub issue for this discussion.`
- Files and specific lines of code (under the "Files changed" tab): Tag `@coderabbitai` in a new review comment at the desired location with your query. Examples:
  - `@coderabbitai generate unit testing code for this file.`
  -	`@coderabbitai modularize this function.`
- PR comments: Tag `@coderabbitai` in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
  - `@coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.`
  - `@coderabbitai read src/utils.ts and generate unit testing code.`
  - `@coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.`
  - `@coderabbitai help me debug CodeRabbit configuration file.`

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

### CodeRabbit Commands (Invoked using PR comments)

- `@coderabbitai pause` to pause the reviews on a PR.
- `@coderabbitai resume` to resume the paused reviews.
- `@coderabbitai review` to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
- `@coderabbitai full review` to do a full review from scratch and review all the files again.
- `@coderabbitai summary` to regenerate the summary of the PR.
- `@coderabbitai generate docstrings` to [generate docstrings](https://docs.coderabbit.ai/finishing-touches/docstrings) for this PR.
- `@coderabbitai resolve` resolve all the CodeRabbit review comments.
- `@coderabbitai plan` to trigger planning for file edits and PR creation.
- `@coderabbitai configuration` to show the current CodeRabbit configuration for the repository.
- `@coderabbitai help` to get help.

### Other keywords and placeholders

- Add `@coderabbitai ignore` anywhere in the PR description to prevent this PR from being reviewed.
- Add `@coderabbitai summary` to generate the high-level summary at a specific location in the PR description.
- Add `@coderabbitai` anywhere in the PR title to generate the title automatically.

### CodeRabbit Configuration File (`.coderabbit.yaml`)

- You can programmatically configure CodeRabbit by adding a `.coderabbit.yaml` file to the root of your repository.
- Please see the [configuration documentation](https://docs.coderabbit.ai/guides/configure-coderabbit) for more information.
- If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: `# yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json`

### Documentation and Community

- Visit our [Documentation](https://docs.coderabbit.ai) for detailed information on how to use CodeRabbit.
- Join our [Discord Community](http://discord.gg/coderabbit) to get help, request features, and share feedback.
- Follow us on [X/Twitter](https://twitter.com/coderabbitai) for updates and announcements.

</details>

<!-- tips_end -->
author_association: CONTRIBUTOR
user: login: github-actions[bot]
created_at: 2025-04-07T15:53:28Z
body: ### Análise da árvore de código

**Os seguintes arquivos são afetados diretamente por alterações realizadas nesse pull request:**
	- `pipelines/capture/serpro/flows.py`
	- `pipelines/capture/serpro/tasks.py`
author_association: NONE