Pular para conteúdo

Convenções do Projeto

Idioma

  • Código: inglês (nomes de variáveis, funções, classes, comentários, commits).
  • Interface com o usuário: Português Brasileiro (labels, mensagens, textos de templates).

Estilo de código

  • Aderente à PEP-8.
  • Aspas simples em todo o código Python.
  • Ferramentas de linting aplicadas na sprint final: black, flake8, isort.

Django — padrões gerais

Class-Based Views (CBVs)

Usar CBVs nativas do Django sempre que possível. Function-based views somente quando a CBV equivalente tornaria o código mais complexo.

Bloqueio de escrita para contas visualizadoras

Views só-escrita (create/update/delete/ações) recebem core.mixins.ViewerForbiddenMixin na declaração da classe (class MinhaView(LoginRequiredMixin, ViewerForbiddenMixin, View):), que retorna 403 no dispatch() para contas visualizadoras.

Views que atendem GET (leitura, deve ficar aberto ao visualizador) e POST (escrita, deve ser bloqueado) na mesma classe não podem usar esse mixin — bloquearia o GET também. Nesses casos, o bloqueio é feito manualmente no início do método post():

def post(self, request, *args, **kwargs):
    if request.user.viewing_id:
        return HttpResponseForbidden('Contas visualizadoras não podem realizar esta ação.')
    return super().post(request, *args, **kwargs)

Padrão usado em accounts.ProfileView, analytics.ForecastView e analytics.DisaggregationView.

Apps com responsabilidade única

Cada app Django tem uma responsabilidade isolada. Não há lógica de negócio cruzando apps via import direto; a comunicação entre apps é feita via signals ou pela service layer.

Signals

Se signals forem necessários, ficam em signals.py dentro da app correspondente e são registrados no método ready() do apps.py da mesma app.

Service layer

Lógica de negócio (cálculos tarifários, classificação de desconexão, parsing MQTT) fica em services.py dentro da app correspondente, não nas views nem nos models.

BaseModel

Todo model herda de core.models.BaseModel, que adiciona created_at e updated_at automáticos.

from core.models import BaseModel

class Meter(BaseModel):
    ...

Credenciais e segredos

Nenhum segredo no código-fonte. Usar python-decouple para ler do arquivo .env.

from decouple import config

SECRET_KEY = config('SECRET_KEY')
DEBUG = config('DEBUG', default=False, cast=bool)

O arquivo .env está no .gitignore e nunca é commitado.


Segurança de dados

  • A REST API usa autenticação por token (DRF TokenAuthentication).
  • Todo queryset de dados do usuário (API e views web) é filtrado por owner=request.user.effective_owner — não owner=request.user diretamente. effective_owner é uma property em accounts.User (self.viewing or self) que resolve o dono correto tanto para contas normais quanto para contas visualizadoras (somente-leitura, ver PRD.md seção 6.11). Nenhum endpoint retorna dados de outros usuários.
  • A comunicação MQTT usa TLS (MQTTS, porta 8883) com autenticação por usuário e senha.

Banco de dados

  • PostgreSQL 16 com a extensão TimescaleDB.
  • A tabela TelemetryReading é uma hypertable particionada por measured_at.
  • As demais tabelas são relacionais comuns no mesmo banco.
  • Nenhuma migração é feita manualmente; toda alteração de schema passa pelo sistema de migrações do Django.

Escopo

O projeto segue o princípio de não over-engineering: nenhuma funcionalidade além do especificado no PRD é implementada. Três linhas parecidas são melhores que uma abstração prematura.


Referências na documentação

python scripts/check_doc_refs.py valida as referências cruzadas dos documentos e roda no CI (job "Referências da documentação"). Ele reprova quando um link local aponta para arquivo inexistente, quando uma citação de código por número de linha aponta para arquivo que não existe ou para linha além do fim do arquivo, e quando um documento em docs/ fica fora do nav do mkdocs.yml (exceções ficam em NAV_EXEMPT, no próprio script).

Não cite markdown por número de linha, ou seja, o nome de um .md seguido de dois-pontos e um número: esse número envelhece silenciosamente a cada edição do arquivo alvo, e foi assim que três referências deste próprio documento derivaram. Use referência de seção, no formato [`hardware.md`](hardware.md), seção 4. Citar código por linha continua permitido, porque nesse caso o checker consegue validar o alvo a cada execução.

O script só usa a biblioteca padrão do Python, então roda sem instalar dependências e sem banco.


Configurações do Django

Configuração Valor correto
LANGUAGE_CODE 'pt-br'
TIME_ZONE 'America/Sao_Paulo'
USE_I18N True
USE_TZ True

Responsividade

O layout autenticado é mobile-first:

Breakpoint Sidebar Conteúdo
< lg (< 1024 px) Oculta (fixed, fora da tela). Ativa via botão hamburger. Largura total da viewport. Padding top pt-14 compensa o botão hamburger fixo.
lg+ (≥ 1024 px) Visível e estática (w-64 shrink-0), parte do flex-row. flex-1 min-w-0, usa o espaço restante.

Regra geral: qualquer componente novo deve declarar classes para ambos os modos. Testar sempre nos viewports: iPhone SE (375 px), iPad Mini (768 px) e Full HD (1920 px).


Estrutura de templates

Templates ficam em templates/ na raiz do projeto, compartilhados entre as apps. Cada app pode ter um subdiretório próprio dentro de templates/.

templates/
├── base.html          ← layout autenticado (sidebar + navbar)
├── public_base.html   ← layout público (landing page)
├── accounts/
├── meters/
├── dashboard/
└── ...

App Flet

O app Flet (flet_app/) é um projeto Python separado, não uma app Django. Ele consome exclusivamente a REST API exposta pela app api. Toda regra de negócio fica no backend Django; o Flet é apenas cliente.