CSE 111: Programação com Funções

S05 - Tarefa: Proposta de Projeto Final

Finalidade

Prove que você pode criar um programa Python significativo que resolva um problema do mundo real e tenha funções organizadas.

Tarefa

Durante as semanas restantes, você desenvolverá um programa Python de sua escolha. Seu programa deve incluir diversas funções, cada uma executando uma única tarefa. Você deve escrever funções de teste para pelo menos duas das funções do seu programa e executar suas funções de teste com pytest.

O ideal é que seu programa resolva um problema do mundo real ou se torne uma ferramenta em uma área de seu interesse, como química, física, geografia, moda, história da família, linguística, serviço social, ciência dos alimentos, carpintaria, máquinas, engenharia, matemática, computação etc. Antes de começar a escrever seu programa, você deve propô-lo ao seu instrutor, de forma a garantir que ele não seja nem muito fácil nem muito difícil para o tempo restante do curso. Se você terminar seu programa mais rápido do que você ou seu instrutor esperavam e ainda houver tempo restante no curso, então escolha e escreva outro programa.

Para receber o crédito total pelo projeto, você precisará dedicar pelo menos 12 horas a ele nas próximas duas semanas. Organize sua agenda adequadamente. Mantenha um registro do seu tempo, isso será necessário para a etapa da semana 6 e para a entrega final.

Proposta

Para escrever uma proposta de programa, baixe e salve este arquivo: proposta.txt. Abra o arquivo baixado no VS Code. Responda a cada uma das perguntas e salve seu arquivo.

Projetos Possíveis

Se tiver dificuldade em escolher um programa, você pode escolher um dos seguintes programas:

  1. Um programa que realiza cálculos úteis para sua área de estudo ou hobby.
  2. Um programa com uma interface gráfica de usuário (GUI) completa, incluindo janelas, quadros, rótulos, campos de texto e botões. Use o módulo tkinter ou kivy. A tarefa em grupo na próxima aula mostrará como escrever código para uma GUI simples.
  3. Um programa que lê um arquivo de texto e conta com que frequência cada palavra ocorre no arquivo.
  4. Crie um programa que leia um texto em português de um arquivo .txt e identifique palavras com grafia incorreta (ou comumente erradas por estudantes), substituindo-as pela forma correta. O objetivo é reforçar a norma ortográfica da língua portuguesa e conscientizar os alunos sobre erros comuns de grafia, como confusões entre “s” e “z”, “x” e “ch”, “g” e “j”, uso de “ss”, “ç”, etc. Consulte o artigo Reformas ortográficas da língua portuguesa na Wikipédia.
  5. Um programa para reorganizar ou renomear todos os arquivos em uma coleção de arquivos, como uma coleção de documentos do Microsoft Word, planilhas do Excel, arquivos de música (mp3), arquivos de imagens (jpg). Se você escolher desenvolver este programa, é altamente recomendável que você faça uma cópia dos arquivos que seu programa vai reorganizar ou renomear e execute seu programa na cópia. Isso porque é muito provável que você cometa erros ao escrever este programa, e tais erros podem fazer com que os arquivos sejam excluídos. Acredite.
  6. Um programa que simula graficamente a propagação de uma doença, como as simulações neste site. Seu programa deve incluir variáveis que podem ser alteradas, como taxa de movimento, probabilidade de transmissão, duração do contágio, duração da doença e probabilidade de morte.
  7. Um programa que recupera dados de um servidor na internet. O módulo requests Python contém funções que recuperam dados de um servidor.
  8. Um programa que usa o módulo pandas (conteúdo em inglês) para processar um conjunto de dados e produz gráficos que ajudam as pessoas a visualizar informações sobre o conjunto de dados.
  9. Um programa que recupera dados de um banco de dados relacional. O conector MySQL Python é um módulo Python que permite que programas Python leiam e gravem em um banco de dados relacional MySQL. Ele foi escrito pelos mesmos desenvolvedores que escreveram o servidor MySQL. Você pode baixá-lo da página de downloads do MySQL (conteúdo em inglês) e instalá-lo no seu computador Windows, Mac OS ou Linux.
  10. Um programa que recupera dados de um banco de dados Firebase.

Estes três sites listam outras ideias para projetos Python:

Ideias de projetos Python para iniciantes

Se você não entender a descrição de um desses possíveis projetos, peça aos membros do grupo do CSE 111 ou ao seu instrutor para lhe explicar o projeto.

Organização

Ao desenvolver seu programa, certifique-se de dividi-lo em funções. Cada função deve executar uma única tarefa e ser nomeada apropriadamente. Lembre-se de que as funções mais reutilizáveis e fáceis de testar não recebem entrada do usuário e não exibem resultados, mas têm parâmetros e retornam um resultado. Funções que recebem entrada do usuário e exibem resultados são importantes e fazem um trabalho útil, mas não é fácil testá-las e reutilizá-las. É normal que algumas das funções do seu programa não tenham parâmetros, não retornem nada, recebam a entrada do usuário e exibam os resultados, mas não será bom se todas as funções do seu programa forem assim.

Procedimento de Teste

Seguir boas práticas de programação inclui escrever funções de teste para grande parte das funções do seu programa. Use o pytest para executá-las e verificar se tudo está funcionando como esperado.

Alguns estudantes acreditam que certas funções de seus programas não podem ser testadas com pytest, mas isso não é verdade. Abaixo estão alguns dos motivos mais comuns dessa crença e um exemplo de código que demonstra como essas funções podem, sim, ser testadas. Você pode expandir ou recolher o exemplo clicando nos ícones de triângulo (Triângulo para a direita: conteúdo recolhido, pode ser expandido ao clicar. ou Triângulo para baixo: conteúdo expandido, pode ser recolhido ao clicar.).

Minha função não pode ser testada porque:

Calcula números
def pagamento(amt, ar, y, ppy):
    """Calcula e retorna o valor do pagamento
    para um empréstimo com uma taxa de juros anual fixa.
    """
    r = ar / ppy
    n = y * ppy
    p = amt * r / (1 - (1 + r) ** -n)
    return round(p, 2)
def test_pagamento():
    """Verifique se a função de pagamento funciona corretamente."""
    assert pagamento(10_000, 0.07, 4, 12) == 239.46
    assert pagamento(80_000, 0.04, 15, 4) == 1779.56
Processa texto em vez de números
def prefixo(s1, s2):
    """Retorne o prefixo, se houver, que aparece em
    s1 e s2. Em outras palavras, retorne uma string dos
    caracteres que aparecem no início em s1
    e s2. Por exemplo, se s1 é "inconcebível" e s2
    é "inconveniente", esta função retornará "incon".
    """
    s1 = s1.lower()
    s2 = s2.lower()
    i = 0
    limite = min(len(s1), len(s2))
    while i < limite:
        if s1[i] != s2[i]:
            break
        i += 1
    return s1[0:i]
def test_prefixo():
    """Verifique se a função de prefixo funciona corretamente."""
    assert prefixo("", "") == ""
    assert prefixo("", "correto") == ""
    assert prefixo("claro", "") == ""
    assert prefixo("feliz", "engraçado") == ""
    assert prefixo("catedral", "catálogo") == "cat"
    assert prefixo("dogmático", "dogma") == "dogm"
    assert prefixo("jovial", "juventude") == "j"
    assert prefixo("insensato", "ingrato") == "in"
    assert prefixo("desdenhoso", "desagradavel") == "des"
Obtém a entrada do usuário de um terminal
def get_int(prompt, min, max):
    """Solicite ao usuário um número inteiro, verifique se é
    entre min e max, inclusive, e retorne o inteiro.
    """
    feito = False
    while not feito:
        try:
            texto = input(prompt)
            numero = int(texto)
            if numero < min:
                print("Entrada inválida: "
                    f"o número deve ser {min} ou maior.")
            elif numero > max:
                print("Entrada inválida: "
                    f"o número deve ser {max} ou menor.")
            else:
                feito = True
        except ValueError as err_de_valor:
            print(f"Inteiro inválido: {texto}. Por favor, tente novamente.")
    return numero
from io import StringIO
import re
import sys
def test_get_int(monkeypatch):
    """Verifique se a função get_int funciona corretamente."""
    stdin = StringIO("1r\n-3\n12\n8")
    stdout = StringIO()
    monkeypatch.setattr(sys, "stdin", stdin)
    monkeypatch.setattr(sys, "stdout", stdout)
    min = 1
    max = 10
    prompt = f"Digite um inteiro entre {min} e {max}: "
    num = get_int(prompt, min, max)
    monkeypatch.undo()
    pattern = prompt + "\\s*" \
        + "Inteiro inválido: 1r\\. Por favor, tente novamente\\.\\s*" \
        + prompt + "\\s*" \
        + f"Entrada inválida: o número deve ser {min} ou maior\\.\\s*" \
        + prompt + "\\s*" \
        + f"Entrada inválida: o número deve ser {max} ou menor\\.\\s*" \
        + prompt + "\\s*"
    saida = stdout.getvalue()
    assert re.compile(pattern).match(saida) != None
    assert num == 8
Obtém a entrada do usuário de uma GUI
"""
Para testar um programa que recebe a entrada do usuário de uma
interface gráfica do usuário, separe as funções de eventos
(aqueles que são executados quando um usuário digita um valor em um campo
de texto) em duas funções. Mova os cálculos para fora da
função de evento e coloque-os em uma função de cálculo que leva
parâmetros, executa um cálculo e retorna um resultado.
Em seguida, escreva uma função de teste para a nova função de cálculo.
"""
# Esta função é chamada toda
# vez que o usuário solta uma tecla.
def calc(evento):
    try:
        # Obtenha a entrada do usuário.
        l = txtLargura.get()
        r = txtRazao.get()
        d = txtDiam.get()
        # Calcule o volume do pneu.
        v = volume_pneu(l, r, d)
        # Exiba os resultados para o usuário.
        lblResult.config(text=f"{v:.1}")
    except ValueError:
        # Quando o usuário apaga todos os dígitos de uma só vez
        # dos campos de texto, remova os rótulos dos resultados.
            lblResult.config(text="")
def volume_pneu(largura, razao, diametro):
    """Calcule e retorne o volume aproximado de um pneu."""
    vol = (math.pi * largura * largura * razao *
            (largura * razao + 2540 * diametro)) / 10_000_000
    return vol
def test_volume_pneu():
    """Verifique se a função volume_pneu funciona corretamente."""
    assert volume_pneu(185, 60, 14) == approx(30101,58, 0,01)
    assert volume_pneu(205, 60, 15) == approx(39924.49, 0.01)
Escreve em um arquivo
def escrever_arquivo(nomedoarquivo, linhas):
    """Crie um arquivo e escreva linhas de texto nele.
    Parametro
        linhas: uma lista de strings.
    Retorno: nada
    """
    with open(nomedoarquivo, "wt", encoding="utf-8") as arquivodesaida:
        for texto in linhas:
            print(texto, file=arquivodesaida)
import os
def test_escrever_arquivo():
    """Verifique se a função escrever_arquivo funciona corretamente."""
    linhas = [
    "Acautelai-vos, porém, dos falsos profetas, que vêm a vós vestidos como ovelhas",
    "mas interiormente são lobos devoradores. Por",
    "seus frutos os conhecereis. Porventura colhem-se uvas",
    "dos espinheiros ou figos dos abrolhos? Assim, toda árvore boa produz",
    "bons frutos, e toda árvore má produz",
    "frutos maus. Não pode a árvore boa dar maus",
    "frutos, nem a árvore má dar frutos bons.",
    "Toda árvore que não dá bom fruto corta-se",
    "e lança-se no fogo. E assim,",
    "pelos seus frutos os conhecereis."
    ]
    nomedoarquivo = "frutas.txt"
    # Chame a função escrever_arquivo para
    # escrever um arquivo chamado frutas.txt.
    escrever_arquivo(nomedoarquivo, linhas)
    # Leia o conteúdo do arquivo frutas.txt.
    with open(nomedoarquivo, "rt", encoding="utf-8") as arquivodeentrada:
        # Leia todos os caracteres do arquivo em uma string.
        string = arquivodeentrada.read()
    # Divida a string em uma lista de strings chamada
    # escritos. Cada linha de texto do arquivo de texto
    # será armazenada em seu próprio elemento na lista.
    escritos = string.splitlines()
    # Exclua o arquivo frutas.txt.
    os.remove(nomedoarquivo)
    # Verifique se gravar_arquivo está correto
    # corretamente o arquivo frutas.txt.
    assert linhas == escritos
Lê de um arquivo
def ler_arquivo(nomedoarquivo):
    """Lê e retorna o conteúdo
    de um arquivo de texto como uma lista de strings.
    """
    linhas = None
    with open(nomedoarquivo, "rt", encoding="utf-8") as arquivodeentrada:
        # Leia todos os caracteres do arquivo em uma string.
        string = arquivodeentrada.read()
    # Divida a string em uma lista de strings chamada
    # linhas. Cada linha de texto do arquivo de texto
    # será armazenada em seu próprio elemento na lista.
    linhas = string.splitlines()
    return linhas
import os
def test_ler_arquivo():
    """Verifique se a função ler_arquivo funciona corretamente."""
    # Escreva um arquivo de exemplo com três linhas
    nomedoarquivo = "linhas.txt"
    linhas = ["primeira", "segunda", "terceira"]
    with open(nomedoarquivo, "wt", encoding="utf-8") as arquivodesaida:
        print(*linhas, sep="\n", file=arquivodesaida)
    # Chame ler_arquivo para ler o arquivo de exemplo.
    ler = ler_arquivo(nomedoarquivo)
    # Exclua o arquivo linhas.txt.
    os.remove(nomedoarquivo)
    # Verifique se ler_arquivo leu o arquivo corretamente.
    assert ler == linhas
Usa o módulo pandas
def filtro_de_medidor(df, numero_de_medidor):
    """Retorne um novo DataFrame que contenha apenas as linhas
    onde a coluna numeroDoMedidor é igual ao parâmetro
    numero_de_medidor.
    Parâmetros
        df: O DataFrame que esta função filtrará.
        numero_de_medidor: df será filtrado para que todas as linhas
            no novo DataFrame tenham esse número de medidor.
    Retorno: Um novo DataFrame.
    """
    filtro = (df["numeroDoMedidor"] == numero_de_medidor)
    df_filtrado = df[filtro]
    return df_filtrado
import pandas as pd
def test_filtro_de_medidor():
    """Verifique se a função filtro_de_medidor
    funciona corretamente.
    """
    # Leia o arquivo agua.csv e converta a
    # coluna lerData de uma string para datetime64.
    df = pd.read_csv(NOMEDOARQUIVO, parse_dates=["lerData"])
    # Chame a função filtro_de_medidor.
    num_medidor = "M4103"
    df_filtrado = filtro_de_medidor(df, num_medidor)
    # Verifique se o data frame
    # filtrado contém pelo menos uma linha.
    assert len(df_filtrado.index) > 0
    # Verifique se todos os números dos medidores no
    # data frame filtrado são o número correto.
    for valor in df_filtrado["numeroDoMedidor"]:
        assert valor == num_medidor
A importância de testar seu código

Mesmo programadores experientes podem cometer erros. Por isso, é importante garantir a qualidade do código escrevendo e executando funções de teste. Dessa forma, você identifica problemas cedo e evita que eles cheguem até os usuários do seu software.

Envio

Este projeto terá 3 tarefas de envio.

  1. Proposta de Projeto: Final da Semana 05
  2. Etapa de Progresso do Projeto: Final da Semana 06
  3. Envio do Projeto: Semana 07

Envie seu documento de proposta concluído no Canvas.

Não espere o feedback do seu instrutor antes de começar a desenvolver seu programa. Suponha que esteja tudo bem com seu projeto, seu instrutor poderá fornecer orientações ou sugestões de modificação de escopo conforme achar apropriado.

Semana 6 Rubrica do Etapa do Projeto

Na próxima semana, você enviará um relatório de progresso para te ajudar a planejar sua semana. Aqui está a rubrica desse relatório.

  1. Tempo—50%: Você levou pelo menos 8 horas para criar seu programa Python ou funções de teste durante a aula atual?
  2. Organização—20%:
    1. Seu programa está organizado em múltiplas funções?
    2. Cada função no seu programa executa apenas uma tarefa?
  3. Progresso—20%: Você concluiu alguma parte significativa do seu programa durante a semana atual?
  4. Descrição—10%: A descrição do seu trabalho para esta aula está completa e é fácil de entender? Sua descrição deve incluir:
    1. Uma lista de nomes de funções no seu programa.
    2. Uma lista de nomes de funções de teste no seu código de teste.
    3. Uma lista da documentação que você leu, dos vídeos que assistiu e dos testes de código que você realizou.
    4. Uma descrição ou lista do trabalho que você concluiu no seu programa.

Links Úteis: