csv-mean-filter 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
analise.py ADDED
@@ -0,0 +1,90 @@
1
+ """Calcula a média de uma coluna numérica de um arquivo CSV."""
2
+
3
+ import argparse
4
+ import csv
5
+ import os
6
+ import sys
7
+
8
+
9
+ def interpretar_filtro(texto: str) -> tuple[str, str]:
10
+ """Converte "coluna=valor" em (coluna, valor), dividindo no primeiro "="."""
11
+ if "=" not in texto:
12
+ raise ValueError(f"Filtro sem '=': {texto!r}")
13
+ nome, valor = (parte.strip() for parte in texto.split("=", 1))
14
+ if not nome:
15
+ raise ValueError(f"Filtro sem nome de coluna: {texto!r}")
16
+ return nome, valor
17
+
18
+
19
+ def _filtro_argparse(texto: str) -> tuple[str, str]:
20
+ """Adapta interpretar_filtro para o type= do argparse."""
21
+ try:
22
+ return interpretar_filtro(texto)
23
+ except ValueError:
24
+ raise argparse.ArgumentTypeError("use o formato coluna=valor") from None
25
+
26
+
27
+ def calcular_media(
28
+ caminho_csv: str, coluna: str, filtro: tuple[str, str] | None = None
29
+ ) -> float:
30
+ """Retorna a média dos valores numéricos da coluna informada.
31
+
32
+ Células vazias são ignoradas. Com filtro (coluna, valor), considera só as
33
+ linhas cuja coluna do filtro é igual ao valor (após strip). Levanta
34
+ FileNotFoundError se o arquivo não existir e ValueError para coluna
35
+ ausente, valor não numérico ou ausência de valores numéricos.
36
+ """
37
+ if not os.path.isfile(caminho_csv):
38
+ raise FileNotFoundError(f"Arquivo não encontrado: {caminho_csv}")
39
+
40
+ with open(caminho_csv, encoding="utf-8-sig", newline="") as arquivo:
41
+ leitor = csv.DictReader(arquivo)
42
+ cabecalho = leitor.fieldnames or []
43
+ for nome in [coluna] + ([filtro[0]] if filtro else []):
44
+ if nome not in cabecalho:
45
+ raise ValueError(f"Coluna '{nome}' não encontrada no cabeçalho")
46
+
47
+ soma = 0.0
48
+ quantidade = 0
49
+ for linha in leitor:
50
+ if filtro and (linha.get(filtro[0]) or "").strip() != filtro[1].strip():
51
+ continue
52
+ bruto = (linha.get(coluna) or "").strip()
53
+ if not bruto:
54
+ continue
55
+ try:
56
+ soma += float(bruto)
57
+ except ValueError:
58
+ raise ValueError(
59
+ f"Valor não numérico '{bruto}' na linha {leitor.line_num}"
60
+ ) from None
61
+ quantidade += 1
62
+
63
+ if quantidade == 0:
64
+ sufixo = f" com o filtro {filtro[0]}={filtro[1]}" if filtro else ""
65
+ raise ValueError(f"Nenhum valor numérico na coluna '{coluna}'{sufixo}")
66
+ return soma / quantidade
67
+
68
+
69
+ def main(argv=None) -> int:
70
+ """Ponto de entrada da CLI; retorna o código de saída."""
71
+ parser = argparse.ArgumentParser(description="Média de uma coluna de um CSV.")
72
+ parser.add_argument("arquivo", help="caminho do arquivo CSV")
73
+ parser.add_argument("coluna", help="nome da coluna")
74
+ parser.add_argument(
75
+ "--filtrar-coluna", type=_filtro_argparse, metavar="COLUNA=VALOR",
76
+ help="considera só as linhas em que COLUNA é igual a VALOR",
77
+ )
78
+ args = parser.parse_args(argv)
79
+
80
+ try:
81
+ media = calcular_media(args.arquivo, args.coluna, args.filtrar_coluna)
82
+ except (FileNotFoundError, ValueError) as erro:
83
+ print(f"Erro: {erro}", file=sys.stderr)
84
+ return 1
85
+ print(media)
86
+ return 0
87
+
88
+
89
+ if __name__ == "__main__":
90
+ sys.exit(main())
@@ -0,0 +1,137 @@
1
+ Metadata-Version: 2.5
2
+ Name: csv-mean-filter
3
+ Version: 0.1.0
4
+ Summary: CLI em Python para calcular a média de colunas CSV com filtros dinâmicos, zero dependências e 36 testes unitários.
5
+ Author: jjuffagtotal-source
6
+ License-Expression: MIT
7
+ License-File: LICENSE
8
+ Classifier: Operating System :: OS Independent
9
+ Classifier: Programming Language :: Python :: 3
10
+ Requires-Python: >=3.10
11
+ Description-Content-Type: text/markdown
12
+
13
+ # analise
14
+
15
+ Script de linha de comando em Python que lê um arquivo CSV e calcula a média de uma coluna numérica, com filtro opcional por valor de outra coluna.
16
+
17
+ ## Funcionalidades
18
+
19
+ - Calcula a média de qualquer coluna numérica de um CSV com cabeçalho.
20
+ - Filtro opcional `--filtrar-coluna COLUNA=VALOR` para considerar só as linhas que batem com o valor.
21
+ - Ignora células vazias.
22
+ - Lê CSVs gravados pelo Excel (UTF-8 com BOM).
23
+ - Mensagens de erro claras e códigos de saída previsíveis.
24
+ - Usa apenas a biblioteca padrão: nenhuma dependência externa.
25
+
26
+ ## Requisitos
27
+
28
+ - Python 3.10 ou superior.
29
+
30
+ ## Instalação
31
+
32
+ Não há pacotes para instalar. Basta clonar o repositório:
33
+
34
+ ```bash
35
+ git clone <url-do-repositorio>
36
+ cd <pasta-do-repositorio>
37
+ ```
38
+
39
+ ## Instalação via pip
40
+
41
+ O projeto também pode ser instalado como pacote, o que disponibiliza o comando `csv-mean` no terminal. Na raiz do repositório:
42
+
43
+ ```bash
44
+ pip install .
45
+ ```
46
+
47
+ Futuramente, quando o pacote estiver publicado no PyPI:
48
+
49
+ ```bash
50
+ pip install csv-mean-filter
51
+ ```
52
+
53
+ Depois de instalado, use `csv-mean` no lugar de `python src/analise.py`:
54
+
55
+ ```bash
56
+ $ csv-mean dados.csv salario --filtrar-coluna idade=30
57
+ 1500.0
58
+ ```
59
+
60
+ Recomenda-se instalar dentro de um ambiente virtual (`python -m venv .venv`).
61
+
62
+ ## Uso
63
+
64
+ ```bash
65
+ python src/analise.py ARQUIVO COLUNA [--filtrar-coluna COLUNA=VALOR]
66
+ ```
67
+
68
+ ### Exemplos
69
+
70
+ Dado o arquivo `dados.csv`:
71
+
72
+ ```csv
73
+ nome,idade,salario
74
+ Ana,30,1000
75
+ Bia,25,3000
76
+ Caio,30,2000
77
+ ```
78
+
79
+ Média de uma coluna:
80
+
81
+ ```bash
82
+ $ python src/analise.py dados.csv salario
83
+ 2000.0
84
+ ```
85
+
86
+ Média só das linhas em que `idade` é `30`:
87
+
88
+ ```bash
89
+ $ python src/analise.py dados.csv salario --filtrar-coluna idade=30
90
+ 1500.0
91
+ ```
92
+
93
+ ### Regras do filtro
94
+
95
+ - O formato é `coluna=valor`. A divisão acontece no primeiro `=`, então `obs=a=b` filtra a coluna `obs` pelo valor `a=b`.
96
+ - A comparação é exata e diferencia maiúsculas de minúsculas, após remover espaços nas pontas. `30` não bate com `30.0`.
97
+ - `coluna=` (valor vazio) seleciona as linhas em que a coluna está vazia.
98
+ - Valores não numéricos em linhas que não batem com o filtro são ignorados.
99
+
100
+ ### Códigos de saída
101
+
102
+ | Código | Situação |
103
+ |--------|----------|
104
+ | `0` | Sucesso: a média é impressa na saída padrão. |
105
+ | `1` | Arquivo inexistente, coluna inexistente, valor não numérico ou nenhum valor numérico (mensagem `Erro: ...` na saída de erro). |
106
+ | `2` | Argumentos inválidos, como um filtro sem `=` (`use o formato coluna=valor`). |
107
+
108
+ ### Uso como módulo
109
+
110
+ Com a pasta `src/` no `PYTHONPATH` (ou executando a partir dela):
111
+
112
+ ```python
113
+ from analise import calcular_media, interpretar_filtro
114
+
115
+ calcular_media("dados.csv", "salario") # 2000.0
116
+ calcular_media("dados.csv", "salario", ("idade", "30")) # 1500.0
117
+ calcular_media("dados.csv", "salario", interpretar_filtro("idade=30"))
118
+ ```
119
+
120
+ ## Testes
121
+
122
+ Os testes usam apenas o `unittest` da biblioteca padrão. Na raiz do projeto:
123
+
124
+ ```bash
125
+ python -m unittest discover -s tests -v
126
+ ```
127
+
128
+ ## Estrutura
129
+
130
+ ```
131
+ src/analise.py # script e funções
132
+ tests/test_analise.py # testes unitários
133
+ ```
134
+
135
+ ## Licença
136
+
137
+ Distribuído sob a licença MIT. Veja o arquivo [LICENSE](LICENSE).
@@ -0,0 +1,6 @@
1
+ analise.py,sha256=Huof9wOXzfQEB-N7c-lSUpoiIbPwqq3ximBQZHXzEN8,3271
2
+ csv_mean_filter-0.1.0.dist-info/METADATA,sha256=Uk7pOPoZxJb6bWbh5kjqCPEQxnlJ_KHbLJ-ggIhlCMI,3600
3
+ csv_mean_filter-0.1.0.dist-info/WHEEL,sha256=2iZrJkDIkeDh0k9SikKMqFuRP-ua6mb4Ir1AsER4BEw,87
4
+ csv_mean_filter-0.1.0.dist-info/entry_points.txt,sha256=mVVBoJuoCaRX3PLd6G-sMPh-hApDrulU246cNaLpizo,42
5
+ csv_mean_filter-0.1.0.dist-info/licenses/LICENSE,sha256=CKKPTH-S0Hc0AldBPYfyAwkCmSSebN63KtzgWiep_7k,1076
6
+ csv_mean_filter-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.33.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ csv-mean = analise:main
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 jjuffagtotal-source
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.