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ãoowner=request.userdiretamente.effective_owneré uma property emaccounts.User(self.viewing or self) que resolve o dono correto tanto para contas normais quanto para contas visualizadoras (somente-leitura, verPRD.mdseçã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 pormeasured_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.