diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..734fb31 --- /dev/null +++ b/.env.example @@ -0,0 +1,62 @@ +# ============================================================================= +# ACL (KernelBot) — variáveis de ambiente +# Copie para .env e preencha os valores reais: +# copy .env.example .env (Windows PowerShell: Copy-Item .env.example .env) +# O ficheiro .env não deve ir para o Git (.gitignore). +# ============================================================================= + +# --- OpenRouter (obrigatório) --- +# Chave da API em https://openrouter.ai/keys — usada em todas as chamadas ao LLM. +OPENROUTER_API_KEY= + +# --- MySQL (obrigatório para o índice BM25 e ingestão) --- +# Host do servidor MySQL (ex.: 127.0.0.1 ou localhost). Nao use 127.0.0.0 — costuma dar timeout. +DB_HOST= +# Porta TCP (padrão MySQL: 3306). +DB_PORT=3306 +# Nome da base de dados onde está a tabela `knowledge`. +DB_NAME= +# Utilizador com permissão de leitura (e escrita se correres ingest). +DB_USER= +DB_PASSWORD= + +# --- Contexto global (/content sem disciplina) --- +# geral = BM25 em todos os silos e merge por score. +# all = mesmo modo de nome alternativo aceite pelo código. +ACL_GLOBAL_CONTEXT=geral + +# --- Contexto fixado (pin na sessão) --- +# Quantos turnos manter chunks fixados antes de voltar a buscar forte. +ACL_PINNED_MAX_TURNS=5 +# Tamanho máximo em caracteres do texto fixado no pin. +ACL_PINNED_MAX_CHARS=24000 +# Score normalizado abaixo do qual o pin é considerado "fraco" (legado / heurísticas de pin). +ACL_PINNED_WEAK_SCORE=0.4 + +# --- Política de retrieval (modo strict — ver engine/retrieval.py) --- +# Score BM25 bruto mínimo do melhor candidato; abaixo → hard stop (contexto insuficiente). +ACL_RETRIEVAL_MIN_SCORE=1.5 +# Margem mínima entre 1.º e 2.º score; evita ambos muito próximos (ambiguidade). +ACL_RETRIEVAL_MIN_SCORE_MARGIN=0.15 +# Cobertura mínima (0–1) dos termos informativos da query nos chunks escolhidos. +ACL_RETRIEVAL_MIN_COVERAGE=0.34 +# Igual, mas termos "centrais" da query contam com peso 2× na métrica. +ACL_RETRIEVAL_MIN_COVERAGE_WEIGHTED=0.34 +# Número mínimo de termos informativos na pergunta (strict). +ACL_RETRIEVAL_MIN_TERMS=2 +# Quantos candidatos o SearchEngine devolve antes de build_decision (1–50). +ACL_RETRIEVAL_CANDIDATE_K=8 +# Quantos chunks entram no prompt após passar os gates (1–20). +ACL_RETRIEVAL_TOP_K=4 +# Máximo de chunks por fonte (slug) entre os selecionados — diversidade (1–10). +ACL_RETRIEVAL_MAX_CHUNKS_PER_SOURCE=2 + +# --- Logging --- +# text = linha legivel; json = uma linha JSON por evento ACL (grep/agregadores). +# ACL_LOG_FORMAT=text +# ACL_LOG_LEVEL=INFO +# ACL_LOG_LEVEL=DEBUG # inclui evento retrieval_gates_inputs (metricas antes dos cortes) + +# --- Não configurável por .env (definido em core/config.py) --- +# Lista de modelos OpenRouter, URL base, timeout HTTP, textos de system/sticky vêm de ficheiros +# em core/systemPrompt/ — ajusta lá ou no código se precisares de outro stack de modelos. diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml deleted file mode 100644 index 80b4663..0000000 --- a/.github/workflows/main.yml +++ /dev/null @@ -1,46 +0,0 @@ -name: KernelBot Metrics -on: - schedule: [{cron: "0 0 * * *"}] - workflow_dispatch: - push: {branches: ["master", "main"]} - -permissions: - contents: write - -jobs: - github-metrics: - runs-on: ubuntu-latest - steps: - - uses: lowlighter/metrics@latest - with: - token: ${{ secrets.METRICS_TOKEN }} - committer_token: ${{ secrets.METRICS_TOKEN }} - user: GaabDevWeb # <--- Usei o user que apareceu no seu log - template: classic - base: "" # Agora vai funcionar porque o plugin de linguagens dará o conteúdo - - config_timezone: America/Sao_Paulo - config_display: large - - # PLUGIN: IDIOMAS (Focado no que você codou) - plugin_languages: yes - plugin_languages_details: lines, percentage - plugin_languages_indepth: yes - plugin_languages_analysis_timeout: 15 - - # PLUGIN: ATIVIDADE (Linhas de código) - plugin_lines: yes - - # PLUGIN: WORKFLOW (Issues e PRs) - plugin_followup: yes - plugin_followup_sections: repositories - - # PLUGIN: HÁBITOS (O visual brutalista de barras) - plugin_habits: yes - plugin_habits_charts: yes - plugin_habits_trim: yes - - # OUTPUT - filename: kernel-status.svg - output_action: commit - committer_branch: main diff --git a/.gitignore b/.gitignore index 3dd5e9e..b8ef60e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,40 @@ +<<<<<<< HEAD .env +.pytest_cache/ + **/__pycache__/ *.py[cod] *$py.class *.so -cursor/ +content/ +.venv/ +.venv_readme_check/ +.ruff_cache/ +.mypy_cache/ +dist/ +build/ +*.egg-info/ +scripts/ +======= +.env + +.pytest_cache/ + +**/__pycache__/ +*.py[cod] +*$py.class +*.so + +content/ +.venv/ +.venv_readme_check/ +.ruff_cache/ +.mypy_cache/ +dist/ +build/ +*.egg-info/ +scripts/ + +>>>>>>> 124ae17972dd35902cec901ff1819bd5be55891f diff --git a/LICENSE b/LICENSE deleted file mode 100644 index f96d4c5..0000000 --- a/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2026 Gaab - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/README.md b/README.md index da191f7..55c58b5 100644 --- a/README.md +++ b/README.md @@ -1,55 +1,20 @@ -# KernelBots (ACL) - -Agente de contexto local com RAG (BM25) sobre Markdown em `content/`, interface em `templates/` e respostas em streaming via OpenRouter. - -## Requisitos - -- Python 3.10+ -- Chave `OPENROUTER_API_KEY` no arquivo `.env` na raiz do repositório - -## Instalação - -```bash -pip install -r requirements.txt -``` - -## Executar - -```bash -python main.py -``` - -Ou com Uvicorn: - -```bash -uvicorn main:app --host 127.0.0.1 --port 8000 -``` - -Abra `http://127.0.0.1:8000`. - -## Estrutura - -| Caminho | Função | -|--------|--------| -| `main.py` | Orquestração: logging, `SearchEngine`, watchdog, `create_app` | -| `core/` | Config (`Settings`), logging centralizado | -| `engine/` | BM25 (`SearchEngine`), `ContentWatcher`, `ContextManager`, `ChatProvider` | -| `api/` | Rotas FastAPI (`GET /`, `POST /chat`) | -| `app/` | `create_app()`, estado injetado em `app.state` | -| `content/` | Arquivos `.md` indexados | -| `templates/` | UI (Jinja2) | - -## Testes - -```bash -python -m pytest tests/ -v -``` - -## Logging - -O projeto usa `logging` da biblioteca padrão com loggers prefixados `kernelbots.*` (ex.: `kernelbots.engine.search`, `kernelbots.api.chat`). Para logs estruturados em JSON no stdout, é possível estender `core/logging_config.py` com algo como `structlog` no mesmo ponto de configuração. - -## Comandos no chat - -- `/content …` — força uso da base local (com fallback para os primeiros chunks se não houver hit BM25). -- `/doc …` — injeta o conteúdo de `documentation.md` quando disponível no índice. +
+ Banner +
+ +
+ +
+ Work in Progress + +

Em obras (ou quase isso)

+ +

O código já está performando mais que muito sênior por aí, mas a documentação ainda está sendo "indexada" pela minha produtividade.

+ +

+ Se você é um recrutador: O código fala mais que mil palavras. Olhe a pasta engine/.
+ Se você é um curioso: Volte em breve. Ou dê um python main.py e descubra. + + +
+

diff --git a/SQL/instructions_for_creation.md b/SQL/instructions_for_creation.md deleted file mode 100644 index 1110b26..0000000 --- a/SQL/instructions_for_creation.md +++ /dev/null @@ -1,2 +0,0 @@ -# Para criar o banco: -mysql -u root -p -e "CREATE DATABASE IF NOT EXISTS pybot CHARACTER SET utf8mb4;" \ No newline at end of file diff --git a/SQL/schema.sql b/SQL/schema.sql deleted file mode 100644 index 1c73b4b..0000000 --- a/SQL/schema.sql +++ /dev/null @@ -1,10 +0,0 @@ -CREATE TABLE IF NOT EXISTS knowledge ( - id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY, - title VARCHAR(255) NOT NULL, - content TEXT NOT NULL, - category VARCHAR(100) NOT NULL DEFAULT 'geral', - active TINYINT(1) NOT NULL DEFAULT 1, - created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, - updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - INDEX idx_active_category (active, category) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; \ No newline at end of file diff --git a/api/routes.py b/api/routes.py index cb24c0f..b6c43ad 100644 --- a/api/routes.py +++ b/api/routes.py @@ -81,11 +81,10 @@ async def chat(request: Request) -> StreamingResponse: log.info("🔄 Comando /reload recebido — reconstruindo índice BM25...") services.search_engine.rebuild() chunk_count = len(services.search_engine.chunks) - db_count = sum(1 for c in services.search_engine.chunks if c.get("source", "").startswith("db:")) - md_count = chunk_count - db_count + silo_count = len(services.search_engine.discipline_ids) status = ( f"Índice reconstruído: {chunk_count} chunk(s) total " - f"({md_count} de arquivos .md + {db_count} do MySQL)." + f"({silo_count} silo(s) do MySQL)." ) log.info("✅ /reload concluído — %s", status) @@ -106,7 +105,11 @@ async def _reload_stream() -> AsyncGenerator[str, None]: ) return StreamingResponse( - services.chat_provider.stream_response(built.messages, trace=built.trace), + services.chat_provider.stream_response( + built.messages, + trace=built.trace, + decision=built.decision, + ), media_type="text/event-stream", headers={ "Cache-Control": "no-cache", diff --git a/app/factory.py b/app/factory.py index 86b031a..241c91f 100644 --- a/app/factory.py +++ b/app/factory.py @@ -24,9 +24,7 @@ def create_app(services: AppServices) -> FastAPI: async def lifespan(app: FastAPI): log.info("🚀 ACL iniciado e pronto para receber requisições.") yield - services.observer.stop() - services.observer.join() - log.info("🛑 Watchdog encerrado. Servidor finalizado.") + log.info("🛑 Servidor finalizado.") app = FastAPI(title="ACL — Agente de Contexto Local", lifespan=lifespan) app.state.services = services @@ -41,4 +39,5 @@ async def lifespan(app: FastAPI): app.mount("/src", StaticFiles(directory=str(src_dir)), name="src") app.include_router(router) + return app diff --git a/app/state.py b/app/state.py index 82f2304..a6b2ed9 100644 --- a/app/state.py +++ b/app/state.py @@ -4,8 +4,6 @@ from dataclasses import dataclass -from watchdog.observers import Observer - from engine.chat_provider import ChatProvider from engine.context import ContextManager from engine.pinned_store import PinnedSessionStore @@ -17,5 +15,4 @@ class AppServices: search_engine: SearchEngine context_manager: ContextManager chat_provider: ChatProvider - observer: Observer pinned_store: PinnedSessionStore diff --git a/content/doc/acl-overview.md b/content/doc/acl-overview.md deleted file mode 100644 index dce32a0..0000000 --- a/content/doc/acl-overview.md +++ /dev/null @@ -1,39 +0,0 @@ -# ACL — Agente de Contexto Local - -## O que é o ACL? - -O ACL (Agente de Contexto Local) é um chatbot RAG de alta performance desenvolvido com FastAPI. Ele indexa arquivos Markdown desta pasta (`/content`) em memória usando BM25 e injeta o contexto relevante no prompt antes de cada chamada à LLM. - -## Como funciona o BM25? - -BM25 (Best Match 25) é um algoritmo de ranking probabilístico que calcula a relevância de um documento para uma query. Diferente da busca vetorial, o BM25 não precisa de GPU nem de embeddings — é puramente estatístico e extremamente rápido. - -A fórmula considera: -- **TF (Term Frequency)**: quantas vezes o termo aparece no documento -- **IDF (Inverse Document Frequency)**: quão raro o termo é na coleção -- **Normalização por tamanho**: documentos longos não penalizam artificialmente - -## Como usar o /content - -Prefixe sua mensagem com `/content` para forçar a injeção do contexto da base de conhecimento, independente do score BM25. Exemplo: - -``` -/content quais são os arquivos disponíveis? -/content explique o que é BM25 -``` - -Sem o prefixo, o ACL só injeta contexto se o score de relevância for > 0.7. - -## Streaming SSE - -O backend retorna respostas via Server-Sent Events (SSE). Cada token gerado pela LLM é enviado imediatamente para o frontend, que renderiza o Markdown parcial em tempo real. - -## Watchdog - -O sistema monitora a pasta `/content` em tempo real. Ao salvar ou criar um arquivo `.md`, o índice BM25 é reconstruído automaticamente em background sem reiniciar o servidor. - -## Adicionando conhecimento - -1. Crie um arquivo `.md` nesta pasta (`/content/`) -2. O índice será atualizado automaticamente em ~1.5 segundos -3. Faça uma pergunta — o ACL usará o novo conteúdo como contexto diff --git a/content/doc/documentation.md b/content/doc/documentation.md deleted file mode 100644 index cd5e289..0000000 --- a/content/doc/documentation.md +++ /dev/null @@ -1,323 +0,0 @@ -# ACL — Agente de Contexto Local -> Chatbot RAG de alta performance com indexação BM25, file-watching reativo e streaming SSE via OpenRouter. - ---- - -## 1. Visão Geral - -O ACL é uma aplicação monolítica de **propósito único**: transformar um diretório local de arquivos Markdown em uma base de conhecimento consultável via chat, usando um LLM externo como motor de respostas. A arquitetura é deliberadamente enxuta — sem banco de dados, sem fila assíncrona, sem cache distribuído. Toda a inteligência fica na memória do processo. - -**Stack:** -| Camada | Tecnologia | -|---|---| -| Servidor HTTP | FastAPI + Uvicorn | -| Índice de busca | BM25Okapi (`rank-bm25`) | -| File watching | Watchdog (thread background) | -| LLM gateway | OpenRouter API (via `httpx` async) | -| Frontend | HTML/CSS/JS puro (Jinja2 template) | -| Streaming | Server-Sent Events (SSE) | - ---- - -## 2. Estrutura de Arquivos - -``` -KernelBots/ -├── main.py # Orquestração: logging, SearchEngine, watchdog, create_app -├── core/ # Settings (env), logging_config -├── engine/ # SearchEngine (BM25), ContentWatcher, ContextManager, ChatProvider -├── api/ # Rotas FastAPI (GET /, POST /chat) -├── app/ # create_app(), estado injetado (AppServices) -├── requirements.txt -├── .env # Segredo: OPENROUTER_API_KEY -├── content/ # Base de conhecimento — arquivos .md indexados pelo BM25 -│ ├── documentation.md -│ ├── acl-overview.md -│ └── aula-*.md -├── tests/ # Smoke tests (pytest) -└── templates/ - └── index.html # Frontend: UI, CSS e JS em um único arquivo -``` - -**Organização em pacotes:** separa config (`core`), motor RAG + LLM (`engine`), HTTP (`api`) e fábrica FastAPI (`app`). O ponto de entrada continua sendo um único comando (`python main.py` ou `uvicorn main:app`). - ---- - -## 3. Arquitetura - -```mermaid -flowchart TD - subgraph Frontend["Frontend (index.html)"] - UI["Chat UI\n(HTML/CSS/JS)"] - SSE["EventSource\nSSE Reader"] - end - - subgraph Backend["Backend (main.py + FastAPI)"] - EP_HOME["GET /\nServe index.html"] - EP_CHAT["POST /chat\nStreamingResponse"] - ROUTER["ContextManager.build_messages()"] - BM25["SearchEngine\n(instância in-memory)"] - WATCHER["ContentWatcher\n(Thread Watchdog)"] - STREAM["ChatProvider.stream_response()\nSSE Generator"] - end - - subgraph External["Externo"] - OR["OpenRouter API\n3 modelos com fallback"] - end - - subgraph FS["Filesystem"] - MD["content/*.md\nBase de conhecimento"] - end - - UI -->|POST /chat JSON| EP_CHAT - EP_HOME -->|HTMLResponse| UI - EP_CHAT --> ROUTER - ROUTER -->|query| BM25 - BM25 -->|chunks + scores| ROUTER - ROUTER -->|messages list| STREAM - STREAM -->|SSE chunks| SSE - SSE -->|tokens| UI - - WATCHER -->|detecta mudança| BM25 - MD -->|leitura inicial| BM25 - MD -->|evento inotify| WATCHER - STREAM -->|HTTP streaming| OR -``` - ---- - -## 4. Módulos e Responsabilidades - -### 4.1 `SearchEngine` — Índice de Recuperação (BM25) - -**Responsabilidade:** Manter em memória um índice BM25 de todos os chunks extraídos dos arquivos `.md` em `/content`. É o coração do sistema RAG (evolução do antigo `BM25Index`). - -**Por que BM25 e não embedding vetorial?** -BM25 é determinístico, não requer GPU, não tem custo de API, e para domínios de texto técnico estruturado (como aulas em Markdown) a precisão é comparável a embeddings de modelos pequenos. A escolha é conscientemente pragmática para um sistema portável. - -**Fluxo de indexação:** -1. `__init__` chama `rebuild()` imediatamente na inicialização. -2. `_chunk_markdown(path)` divide cada `.md` em seções por headers `#`, `##`, `###`. Cada seção vira um `dict { text, source }`. -3. Todos os chunks de todos os arquivos são tokenizados com regex `\w+` (lowercase) e alimentados ao `BM25Okapi`. -4. O índice e os chunks ficam protegidos por `threading.Lock` para acesso seguro do Watchdog. - -**Busca:** -```python -def search(self, query: str, top_k: int = 3) -> list[dict]: -``` -- Tokeniza a query da mesma forma que os documentos. -- Obtém scores BM25 brutos e **normaliza pelo score máximo** (escala 0–1). -- Filtra por `Settings.bm25_score_threshold` (0.7) — chunks abaixo disso são descartados. -- Retorna os `top_k` hits com score acima do threshold. - -> [!IMPORTANT] -> O threshold de 0.7 é agressivo. Se o usuário perguntar algo cujos termos não colidem bem com os tokens do índice, o sistema degrada para modo "assistente geral" sem contexto. Isso é intencional para evitar injeção de contexto irrelevante, mas pode surpreender. - ---- - -### 4.2 `ContentWatcher` — File Watching Reativo - -**Responsabilidade:** Monitorar `/content` em uma thread de background e acionar rebuild do BM25 quando arquivos `.md` forem criados, modificados ou deletados. - -**Decisão de design crítica:** debounce de **1.5 segundos** via `threading.Timer`. Se múltiplos eventos chegarem em sequência (ex: editor salvando com escrita atômica em dois estágios), apenas o último dispara o rebuild. Cancela o timer anterior antes de criar um novo. - -```python -def _debounced_rebuild(self) -> None: - if self._timer and self._timer.is_alive(): - self._timer.cancel() - self._timer = threading.Timer(1.5, self._search_engine.rebuild) - self._timer.start() -``` - -O `Observer` é iniciado em **`main.py`** após instanciar o `SearchEngine`. O encerramento (`stop()` / `join()`) ocorre no **`lifespan`** de `create_app()` em `app/factory.py`. - ---- - -### 4.3 `ContextManager.build_messages()` — Roteador de Contexto - -**Responsabilidade:** Decidir se a requisição precisa de contexto RAG ou não, e montar a lista de mensagens para o LLM. - -**Lógica de decisão:** - -```mermaid -flowchart TD - A[user_message] --> B{começa com /content?} - B -->|sim| C[force_rag = True\nremove prefix] - B -->|não| D[force_rag = False] - C --> E[BM25 search query] - D --> E - E --> F{hits encontrados?} - F -->|sim| G[Injeta chunks no system prompt] - F -->|não e force_rag| H[Injeta top-5 chunks raw] - F -->|não e não force_rag| I[System prompt geral\nsem contexto] - G --> J[mode: RAG] - H --> J - I --> K[mode: assistente geral] - J --> L[retorna messages list] - K --> L -``` - -O comando `/content` é um override manual: força busca no índice mesmo que o score seja zero, injetando os 5 primeiros chunks como contexto de último recurso. - ---- - -### 4.4 `ChatProvider.stream_response()` — Gerador SSE com Fallback - -**Responsabilidade:** Fazer requisição streaming ao OpenRouter, parsear os chunks SSE da API, e re-emitir para o cliente como SSE. - -**Modelos com fallback (em ordem de tentativa):** -| Prioridade | Modelo | -|---|---| -| 1 | `arcee-ai/trinity-large-preview:free` | -| 2 | `google/gemini-2.5-flash:free` | -| 3 | `meta-llama/llama-3.3-70b-instruct:free` | - -**Condições de fallback:** -- HTTP 429 (rate limit) → tenta próximo imediatamente. -- HTTP >= 400 (qualquer outro erro) → loga o body e tenta próximo. -- `httpx.TimeoutException` (limite 60s) → tenta próximo. -- Qualquer exceção inesperada → loga com traceback e tenta próximo. - -**Protocolo de escape SSE:** -O backend não pode emitir `\n` literal dentro de um campo `data:` SSE (quebraria o protocolo). A solução é escapar: -```python -safe = token.replace("\n", "\\n") -yield f"data: {safe}\n\n" -``` -E o frontend desfaz o escape: -```js -fullText += payload.replace(/\\n/g, '\n'); -``` - -**Metadado `ACL_META` (rastreabilidade):** antes dos tokens do modelo, o servidor pode enviar `data: [ACL_META]` + JSON (`v`, `label`, `sources`) derivado de `ContextManager.build_messages` → `trace`, para breadcrumbs e feedback “Analisando resumos de …” no cliente modular (`frontend/src/api.js`). - ---- - -### 4.5 `index.html` — Frontend Monolítico - -O frontend é **autocontido** em um único arquivo Jinja2, sem bundler, sem framework JS. - -**Dependências externas (CDN):** -- `marked.js v12` — parse de Markdown para HTML. -- `highlight.js v11.9` — syntax highlighting em blocos de código. -- `Inter` e `JetBrains Mono` — Google Fonts. - -**Design tokens (CSS custom properties):** -| Token | Valor | Uso | -|---|---|---| -| `--bg` | `#1E1E1E` | Background geral | -| `--surface` | `#27272A` | Header/Footer | -| `--accent` | `#5B88B2` | Botões, links, foco | -| `--error` | `#F87171` | Mensagens de erro | -| `--success` | `#4ADE80` | Badge "Online" | - -**Gerenciamento de estado:** -- Histórico de conversa é persistido em `sessionStorage` com chave `acl_history`. -- Limite de 10 mensagens (`MAX_HISTORY = 10`) — truncagem pela cauda mais antiga. -- Na recarga da página, o histórico é re-renderizado com `renderSavedHistory()`. - -**Fluxo de streaming no frontend:** -1. `fetch('/chat', { method: 'POST', body: JSON.stringify({ message }) })` -2. `res.body.getReader()` — leitura manual do stream. -3. Buffer de texto parcial para lidar com chunks SSE fragmentados entre `read()` calls. -4. Re-render Markdown incremental a cada token recebido (`bubble.innerHTML = renderMarkdown(fullText)`). -5. Cursor piscante (`.cursor-blink::after { content: "▋" }`) removido ao final do stream. - ---- - -## 5. Fluxo Completo de uma Requisição - -``` -Usuário digita mensagem → Enter - │ - ▼ -POST /chat { message: "..." } - │ - ▼ -ContextManager.build_messages(user_message) - ├── SearchEngine.search(query) → [chunk1, chunk2, ...] - └── compõe system prompt (RAG ou geral) - │ - ▼ -StreamingResponse(ChatProvider.stream_response(messages)) - │ - ▼ -httpx.AsyncClient → OpenRouter API (stream=True) - │ - ├── modelo 1 falha? → modelo 2 → modelo 3 - └── stream de tokens SSE - │ - ▼ -yield "data: \n\n" (por token) - │ - ▼ -Frontend: reader.read() → buffer → parse SSE → marked.parse() → innerHTML -``` - ---- - -## 6. Configuração e Operação - -### Variáveis de Ambiente (`.env`) - -| Variável | Obrigatório | Descrição | -|---|---|---| -| `OPENROUTER_API_KEY` | **Sim** | Chave de API do OpenRouter. Falha fatal se ausente. | - -### Dependências (`requirements.txt`) - -``` -fastapi -uvicorn -httpx -python-dotenv -jinja2 -rank-bm25 -watchdog -pytest -``` - -> [!WARNING] -> Nenhuma versão está fixada. Em produção, isso é uma bomba-relógio. Use `pip freeze > requirements.txt` após validação e commite as versões exatas. - -### Inicialização - -```bash -# Método 1 — direto -python main.py - -# Método 2 — uvicorn com reload (desenvolvimento) -uvicorn main:app --host 127.0.0.1 --port 8000 --reload -``` - -Servidor sobe em `http://127.0.0.1:8000`. - -### Adicionando conteúdo à base de conhecimento - -Basta copiar arquivos `.md` para `/content/`. O Watchdog detecta em até 1.5s e reconstrói o índice automaticamente. Nenhum restart necessário. - ---- - -## 7. Limitações e Pontos de Atenção - -| # | Problema | Impacto | Mitigação atual | -|---|---|---|---| -| 1 | **Sem persistência de histórico no servidor** | Cada requisição é stateless. O histórico é só local (sessionStorage) | Intencional para simplicidade | -| 2 | **BM25 threshold rígido (0.7)** | Queries com vocabulário diferente do corpus não ativam RAG | Usar `/content` para forçar | -| 3 | **Modelos gratuitos com rate limit** | Fallback entre 3 modelos pode esgotar e retornar erro | Monitorar logs; adicionar modelos pagos | -| 4 | **API key exposta no `.env` sem rotação** | Risco de vazamento se o repo for publicado | `.env` deve estar no `.gitignore` | -| 5 | **Sem autenticação na interface** | Qualquer um na rede local acessa o chat | Deploy apenas em `127.0.0.1` | -| 6 | **Versões não fixadas nas dependências** | Quebra silenciosa em novas instalações | Fixar com `pip freeze` | -| 7 | **Re-render Markdown a cada token** | CPU despendida em parse repetido durante stream | Aceitável para tráfego baixo | - ---- - -## 8. Extensões Naturais - -Se o projeto crescer, estes são os próximos passos arquiteturais em ordem de impacto: - -1. **Fixar versões** em `requirements.txt` → imediato, zero custo. -2. **Rate limiter no endpoint `/chat`** → `slowapi` + Redis para proteger em rede local. -3. **Multi-turno real** → passar histórico de mensagens no payload ao LLM (atualmente cada request é stateless no servidor). -4. **Embeddings híbridos** → combinar BM25 com um modelo de embedding local (ex: `sentence-transformers`) para recall semântico. -5. **Autenticação básica** → `fastapi.security.HTTPBasic` se exposto além de localhost. diff --git a/content/planejamento-curso-carreira/aula-01-bloco-entrada-estagio-atividades-complementares.md b/content/planejamento-curso-carreira/aula-01-bloco-entrada-estagio-atividades-complementares.md deleted file mode 100644 index 742f24a..0000000 --- a/content/planejamento-curso-carreira/aula-01-bloco-entrada-estagio-atividades-complementares.md +++ /dev/null @@ -1,421 +0,0 @@ ---- -title: "Como funciona sua graduação: blocos, estágio e atividades complementares" -slug: "bloco-entrada-estagio-atividades-complementares" -discipline: "planejamento-curso-carreira" -order: 1 -description: "Entenda como os blocos, projetos, estágio obrigatório e atividades complementares estruturam sua graduação no Infinity e impactam seu planejamento de carreira." -reading_time: 20 -difficulty: "easy" -concepts: - - blocos de disciplinas - - projetos de bloco - - estágio obrigatório - - atividades complementares - - presença e frequência - - planejamento de carreira durante o curso -prerequisites: [] -learning_objectives: - - "Compreender a lógica de blocos, projetos e a ausência de TCC na graduação do Infinity." - - "Planejar como cumprir as horas de estágio obrigatório aproveitando experiências profissionais e projetos reais." - - "Planejar como cumprir atividades complementares ao longo da graduação sem deixar tudo para o final." - - "Entender regras básicas de presença, avaliação por competências e uso de requerimentos." -exercises: - - question: "Explique, com suas palavras, o que é um 'bloco' na graduação do Infinity e por que ele é organizado em torno de um projeto." - answer: "Um bloco é um conjunto de disciplinas (normalmente cinco) que rodam em paralelo por cerca de seis meses e são planejadas de forma integrada para que o aluno, ao final, consiga entregar um produto/projeto que junta conhecimentos de todas elas." - hint: "Pense na ideia de disciplinas que 'giram juntas' e levam a um produto no final." - - question: "Por que o curso pode não ter TCC tradicional e ainda assim cumprir o que o MEC exige?" - answer: "Porque o MEC exige carga horária mínima e objetivos formativos, não um TCC em si; se o plano pedagógico aprovado prevê projetos de bloco sucessivos em vez de um TCC único, esses projetos podem cumprir o papel de integração de conhecimentos na forma de produtos finais." - hint: "Lembre que o que é obrigatório é o que está no plano pedagógico apresentado ao MEC." - - question: "Qual a diferença entre estágio obrigatório e atividades complementares na graduação?" - answer: "O estágio obrigatório é uma carga de horas de trabalho em atividades de TI/dados/tecnologia, formalmente vinculadas ao curso, que fazem parte da carga horária mínima da graduação. Atividades complementares são horas em ações formativas adicionais (palestras, cursos, idiomas, trabalho voluntário etc.) que ampliam a formação, mas não substituem o estágio." - hint: "Pense em estágio como 'trabalho na área' e em atividades complementares como 'formação extra comprovada'." -review_after_days: - - 3 - - 14 ---- - -## Visão Geral do Conceito - -Esta lição explica **como sua graduação é estruturada** no Infinity e o que isso significa para o seu planejamento de carreira desde o primeiro bloco. Você vai entender o papel dos `blocos de disciplinas`, por que não existe `TCC tradicional`, como funcionam o `estágio obrigatório` e as `atividades complementares`. -Tudo isso impacta diretamente **quando** e **como** você poderá entrar no mercado (ou se reposicionar) e qual será sua carga de estudos e projetos ao longo do curso. - -Ao finalizar, você terá uma visão clara do “tabuleiro” da graduação e poderá começar a tomar decisões conscientes sobre **onde investir energia** em cada fase. - -## Modelo Mental - -Um bom modelo mental para esta disciplina é enxergar a graduação como um **projeto de longo prazo de desenvolvimento profissional**, não apenas como um conjunto de aulas isoladas. - -Você pode imaginar o curso como um **pipeline de formação**: - -- Entrada: você, com sua história, experiência prévia e objetivos. -- Estrutura: blocos, disciplinas, projetos, estágio e atividades complementares. -- Saída: um profissional que já entregou vários produtos, tem horas comprovadas de atuação e um portfólio coerente com o mercado. - -Em vez de um grande “chefe final” chamado `TCC`, a graduação se organiza como **vários mini-chefes**: projetos de bloco que vão construindo, passo a passo, sua experiência prática. - -Do ponto de vista de carreira, o modelo mental importante é: - -- Cada bloco é uma **sprint longa** em que você aumenta seu nível técnico e entrega um produto. -- O `estágio obrigatório` é a **tradução disso em horas formais de trabalho na área**. -- As `atividades complementares` são os **“buffs”** que você adiciona ao seu perfil: idiomas, eventos, cursos, voluntariado. - -## Mecânica Central - -### Estrutura em blocos - -Na graduação do Infinity: - -- Um `bloco` é um conjunto de disciplinas (tipicamente cinco) que **rodam simultaneamente** por cerca de seis meses. -- Essas disciplinas são planejadas de forma a **convergir para um produto final**: um projeto que integra o que foi aprendido em todas elas. -- Em vez de um `TCC` único, você terá **projetos de bloco** ao longo do curso, cada um servindo como evidência de competências desenvolvidas. - -### Avaliação por competências - -O modelo é **baseado em competências e projetos**, não em provas tradicionais: - -- Não há provas de múltipla escolha com nota de 0 a 10 como foco principal. -- A avaliação acontece por meio de **trabalhos e entregas práticas**, a cada duas semanas e em projetos finais de bloco (como as avaliações `TP` e `AT` mencionadas na aula). -- A regra de presença segue a legislação: cerca de **75% de frequência mínima** nas aulas síncronas para você ser considerado presente naquela disciplina. - -### Estágio obrigatório - -O `estágio obrigatório`: - -- Faz parte da **carga horária mínima exigida** para você se formar. -- Normalmente exige **cerca de 400 horas** de atuação em atividades ligadas à área do curso (TI, dados, desenvolvimento, suporte, produto digital, etc.). -- Pode ser cumprido de formas diferentes: - - Estágio formal com contrato entre empresa, aluno e faculdade. - - Trabalho CLT ou como `PJ`, desde que haja comprovação de que você atua na área. - - Atuação como servidor público em funções ligadas à TI/dados, mediante declaração. -- Só contam horas **a partir do início da graduação**; trabalhos anteriores ao início do curso não valem para estágio. - -### Atividades complementares - -As `atividades complementares`: - -- São um conjunto de horas que você precisa acumular com ações formativas adicionais, como: - - Participação em palestras, congressos, hackathons. - - Cursos externos (por exemplo, na `Udemy`, `Coursera` etc., desde que sejam comprováveis e não pirateados). - - Cursos de idiomas. - - Trabalho voluntário em entidades formais. -- A quantidade de horas varia conforme o curso (por exemplo, cerca de 80h em cursos mais curtos e até 300h em engenharias). -- Cada categoria tem **limites máximos** de horas que podem ser aproveitadas, descritos no manual da graduação. - -### Requerimentos e comprovação - -Quase tudo que envolve **registro formal** (declarações, estágio, atividades complementares) passa por: - -- Abertura de um `requerimento` no sistema oficial (por exemplo, `requerimento.infinit.edu.br`). -- Anexar comprovações: declaração de empresa, carteira de trabalho, certificados de cursos, crachá de eventos, carta de ONG etc. -- Acompanhamento do andamento do pedido pelo **número de protocolo** gerado pelo sistema. - -### Diagrama do fluxo da graduação - -O diagrama abaixo resume o fluxo principal da sua jornada na graduação, do ponto de vista de blocos, estágios e formação: - -```mermaid -flowchart TD - A[Ingresso na graduação] --> B[Bloco de entrada
nivelamento e método] - B --> C[Blocos seguintes
disciplinas técnicas + projetos] - C --> D[Projetos de bloco
produtos integrando disciplinas] - D --> E[Horas de estágio obrigatório
estágio, CLT, PJ, serviço público] - D --> F[Atividades complementares
palestras, cursos, idiomas, voluntariado] - E --> G[Requisitos formais cumpridos
carga horária + documentação] - F --> G - G --> H[Conclusão do curso
diploma + portfólio] -``` - -## Uso Prático - -Nesta seção, a aplicação é **de planejamento de curso e carreira**, não de código. A ideia é transformar regras abstratas em decisões concretas para os próximos semestres. - -### Exemplo 1 — Aproveitar trabalho atual como estágio - -Imagine que você já trabalha como analista de suporte, analista de dados, desenvolvedor ou em função de TI em uma empresa. - -Passo a passo para transformar isso em horas de estágio obrigatório: - -1. Confirmar se suas atividades são **diretamente relacionadas** à área da graduação (TI, dados, desenvolvimento de produtos digitais etc.). -2. Ler, no manual da graduação, o capítulo de `estágio obrigatório` e o conceito de `termo substitutivo de estágio`. -3. Solicitar à empresa: - - Uma carta em papel timbrado (ou PDF oficial) descrevendo suas funções. - - Carga horária semanal. - - Tempo de atuação na função. -4. Abrir um `requerimento` específico de estágio no sistema da faculdade. -5. Anexar a documentação e aguardar a análise do coordenador do curso. - -O resultado prático é que você **não precisa abandonar seu trabalho atual** para cumprir o estágio, desde que ele seja compatível com a área. - -### Exemplo 2 — Planejar estágio sendo iniciante - -Se você está começando do zero na área, sem experiência prévia, uma estratégia típica é: - -- Focar no **primeiro bloco** em: - - Entender o modelo de ensino, blocos, avaliações e prazos. - - Construir base técnica suficiente para ser competitivo em vagas de estágio (a partir de ~1 ano de curso, como muitas empresas pedem). -- A partir do segundo bloco: - - Usar projetos de bloco como **portfólio** em processos seletivos. - - Buscar oportunidades de estágio remoto ou presencial que permitam conciliar com o trabalho atual, se houver. - - Fazer pequenos freelas ou projetos colaborativos com colegas para aumentar experiência prática. - -### Exemplo 3 — Planejar atividades complementares desde cedo - -Para evitar acumular centenas de horas de atividades complementares no final do curso: - -- Use cada semestre para: - - Assistir palestras, meetups online, lives técnicas recomendadas pela faculdade. - - Completar ao menos **um curso externo** relevante para sua trilha (por exemplo, fundamentos de Git, lógica de programação, fundamentos de nuvem). - - Investir em um curso de idioma (inglês, espanhol), se isso fizer sentido para sua carreira. -- Guarde **todos os comprovantes** em uma pasta organizada (digital), já pensando em anexar depois nos requerimentos. - -## Erros Comuns - -- **Deixar estágio e atividades complementares para o final** - Resultado: acúmulo de pressão justamente quando as disciplinas técnicas estão mais avançadas, aumentando risco de atraso na formatura. - -- **Achar que qualquer trabalho conta como estágio** - Estágio precisa estar vinculado **à área da graduação**; funções sem relação com TI/dados não são aceitas como estágio obrigatório. - -- **Não guardar comprovantes** - Fazer cursos, participar de eventos e voluntariado sem guardar certificados, crachás ou declarações impede que essas horas sejam validadas. - -- **Confundir estágio com atividade complementar** - Alguns alunos tentam usar atividades complementares para substituir a carga horária de estágio, o que não é permitido: são componentes diferentes da matriz curricular. - -- **Ignorar o manual da graduação** - Deixar de ler o manual faz com que as regras de presença, estágio e atividades complementares pareçam “surpresas desagradáveis” perto da formatura. - -- **Subestimar a importância de projetos de bloco** - Tratar o projeto de bloco como “só mais um trabalho” reduz o potencial de gerar portfólio forte para processos seletivos. - -## Visão Geral de Debugging - -Aqui, “debugging” significa **corrigir o curso da sua jornada acadêmica e de carreira** quando algo está saindo do planejado. - -Perguntas para diagnosticar problemas: - -- Você sabe **quantas horas de estágio** precisa cumprir e quantas já foram validadas? -- Você sabe **quantas horas de atividades complementares** são exigidas no seu curso? -- Você tem uma **lista clara** dos projetos de bloco que já entregou e quais competências eles mostram? -- Você conhece os **canais oficiais** para tirar dúvidas (gerência acadêmica, equipe de estágio, secretaria, Infinity Online)? - -Passos básicos de debugging: - -1. **Leitura de documentação** - Revisar o manual do aluno e o manual da graduação, especialmente seções de estágio, atividades complementares e presença. - -2. **Mapeamento de status atual** - Fazer uma tabela com: - - Blocos cursados e projetos entregues. - - Horas de estágio validadas e pendentes. - - Horas de atividades complementares acumuladas. - -3. **Abertura de requerimentos** - Quando houver dúvidas específicas (por exemplo, se determinado emprego vale como estágio), abrir requerimento para a equipe de estágio/coordenador e **documentar a resposta**. - -4. **Ajuste de plano** - Com base nas respostas: - - Antecipar ou postergar início de estágio. - - Intensificar participação em atividades complementares em períodos mais leves do curso. - -
-Checklist rápido de correção de rota - -- [ ] Sei quantas horas de estágio preciso cumprir e quantas já tenho. -- [ ] Sei quantas horas de atividades complementares preciso cumprir e quantas já tenho. -- [ ] Tenho registros organizados (PDFs, imagens) de todas as atividades que podem ser aproveitadas. -- [ ] Sei como abrir requerimento para dúvidas ou validações. -- [ ] Tenho um plano de quais blocos são melhores para focar em estágio e em atividades complementares. -
- -## Principais Pontos - -- **Blocos** organizam disciplinas em torno de **projetos práticos**, substituindo o modelo de TCC único por uma sequência de produtos. -- **Estágio obrigatório** é parte da carga horária total do curso e precisa ser **comprovado** com atividades na área de TI/dados/tecnologia. -- **Atividades complementares** são horas extras de formação (palestras, cursos, voluntariado, idiomas) que precisam de **comprovantes formais**. -- As regras de **presença, estágio e atividades complementares** estão descritas no **manual da graduação** e devem ser conhecidas desde o início. -- Um bom planejamento distribui estágio e atividades complementares ao longo dos blocos, evitando acumular tudo no final do curso. - -## Preparação para Prática - -Ao terminar esta lição, você deve ser capaz de: - -- Explicar para outra pessoa como funciona a estrutura de blocos, projetos e ausência de TCC na sua graduação. -- Desenhar um plano preliminar de **quando** pretende iniciar estágio e **como** pretende comprovar essas horas. -- Levantar quais atividades que você já faz (ou pretende fazer) podem contar como **atividades complementares**. -- Identificar, no manual e nos sistemas oficiais, **onde tirar dúvidas** e **como abrir requerimentos** para situações específicas. - -Na próxima seção, você vai transformar essa compreensão em um **plano concreto de ação** para os próximos semestres. - -## Laboratório de Prática - -Nesta disciplina, o Laboratório de Prática não envolve código, mas sim **planejamento estruturado**. Use Markdown ou texto estruturado para responder, de preferência dentro do ambiente do curso. - -### Exercício Easy — Mapa do seu bloco de entrada - -**Objetivo:** conectar o que você ouviu na aula com sua situação atual. - -Enunciado: - -Crie um pequeno documento em Markdown respondendo às perguntas abaixo: - -1. Qual é a sua graduação (por exemplo, Análise e Desenvolvimento de Sistemas, Ciência de Dados, etc.)? -2. Quais disciplinas compõem o seu **bloco de entrada**? -3. Qual é o **produto/projeto final** esperado para este bloco (se já foi mencionado)? -4. Quais competências você espera desenvolver neste bloco que podem ajudar em um futuro estágio? - -Boilerplate sugerido (para você copiar e completar): - -```markdown -# Mapa do meu bloco de entrada - -## Minha graduação -- Curso: - -## Disciplinas do bloco de entrada -- Disciplina 1: -- Disciplina 2: -- Disciplina 3: -- Disciplina 4: -- Disciplina 5: - -## Projeto ou produto deste bloco -- Descrição (se já foi informada): - -## Competências que quero desenvolver neste bloco -- Competência 1: -- Competência 2: -- Competência 3: -``` - -### Exercício Medium — Plano preliminar de estágio - -**Objetivo:** desenhar um plano realista de como cumprir as horas de estágio obrigatório. - -Enunciado: - -Monte um plano preliminar para cumprir suas horas de estágio, cobrindo: - -- Se você já trabalha na área ou não. -- Formas possíveis de comprovar horas (CLT, PJ, serviço público, estágio formal, freelas). -- Em qual período do curso você pretende iniciar o estágio (por exemplo, após 1 ano). -- Uma estimativa de quantas horas por semana você poderia dedicar ao estágio. - -Boilerplate sugerido: - -```markdown -# Plano preliminar de estágio - -## Minha situação atual -- Trabalho na área? (sim/não) -- Tipo de vínculo (CLT, PJ, serviço público, outro): - -## Formas possíveis de comprovar horas -- Opção 1: -- Opção 2: - -## Quando pretendo iniciar o estágio -- Bloco/semestre alvo: -- Motivo dessa escolha: - -## Estimativa de carga horária semanal -- Horas por semana disponíveis para estágio: -- Previsão de meses necessários para atingir ~400 horas: -``` - -### Exercício Hard — Roteiro de atividades complementares para o curso inteiro - -**Objetivo:** distribuir atividades complementares ao longo da graduação para evitar acúmulo. - -Enunciado: - -Assumindo que seu curso exige um total de **X horas** de atividades complementares (consulte o manual ou use uma estimativa inicial), crie um plano distribuindo essas horas ao longo dos blocos/semestres. Para cada período, liste: - -- Tipos de atividades que você pretende fazer (palestras, cursos online, hackathons, voluntariado, idiomas). -- Quantidade estimada de horas. -- Como você pretende **comprovar** cada atividade. - -Boilerplate sugerido: - -```markdown -# Plano de atividades complementares - -## Total estimado de horas necessárias -- Total de horas de atividades complementares: X - -## Distribuição por bloco/semestre - -### Bloco 1 -- Atividade 1 (tipo, carga horária estimada, forma de comprovação): -- Atividade 2: - -### Bloco 2 -- Atividade 1: -- Atividade 2: - -### Bloco 3 -- Atividade 1: -- Atividade 2: - -### Bloco 4 -- Atividade 1: -- Atividade 2: - -## Riscos e plano B -- O que faço se não conseguir cumprir o planejado em um bloco? -``` - ---- - - - - - diff --git a/content/planejamento-curso-carreira/aula-02-planejamento-carreira-swot.md b/content/planejamento-curso-carreira/aula-02-planejamento-carreira-swot.md deleted file mode 100644 index b32320b..0000000 --- a/content/planejamento-curso-carreira/aula-02-planejamento-carreira-swot.md +++ /dev/null @@ -1,414 +0,0 @@ ---- -title: "Planejamento de carreira com SWOT: das estrelas ao caminho concreto" -slug: "planejamento-carreira-swot" -discipline: "planejamento-curso-carreira" -order: 2 -description: "Como transformar um universo de possibilidades em um plano de carreira concreto usando SWOT, metas e ações alinhadas ao seu tempo de vida." -reading_time: 25 -difficulty: "easy" -concepts: - - singularidade da trajetória profissional - - tempo de vida e tempo de carreira - - planejamento de carreira - - matriz SWOT aplicada à pessoa - - definição de objetivo profissional - - ações para mitigar ameaças e reforçar forças -prerequisites: - - bloco-entrada-estagio-atividades-complementares -learning_objectives: - - "Entender por que comparações lineares de tempo de carreira entre pessoas diferentes são enganadoras." - - "Definir um objetivo profissional claro de curto ou médio prazo." - - "Aplicar a matriz SWOT a si mesmo para mapear forças, fraquezas, oportunidades e ameaças em relação a esse objetivo." - - "Derivar ações práticas a partir da análise SWOT para avançar na direção escolhida." -exercises: - - question: "Por que não faz sentido comparar sua linha do tempo de carreira com a de colegas da mesma idade como se todos tivessem o mesmo 'relógio'?" - answer: "Porque cada pessoa tem contexto, oportunidades, atrasos e escolhas diferentes; a carreira é marcada por singularidades (mudanças, pausas, reviravoltas) e não por uma linha reta única que sirva para todos." - hint: "Lembre-se dos exemplos de gente que começou tarde e ainda assim teve sucesso." - - question: "Explique com suas palavras a diferença entre 'força' e 'oportunidade' na matriz SWOT aplicada à carreira." - answer: "Forças são características internas suas (competências, atitudes, experiências) que ajudam a chegar no objetivo; oportunidades são condições externas, do mercado ou do contexto, que você pode aproveitar para usar essas forças e avançar." - hint: "Pense em 'dentro de mim' versus 'fora de mim'." - - question: "Dê um exemplo de fraqueza que pode se tornar ameaça em um processo seletivo de estágio ou júnior em TI." - answer: "Timidez extrema e dificuldade de se apresentar pode ser uma fraqueza que se torna ameaça porque prejudica o desempenho em entrevistas e dinâmicas de grupo, reduzindo suas chances de conseguir a vaga." - hint: "Lembre da discussão sobre oratória e dinâmica de grupo." -review_after_days: - - 7 - - 21 ---- - -## Visão Geral do Conceito - -Esta lição mostra como sair do **céu cheio de possibilidades** (cursos, linguagens, trilhas, países, formatos de trabalho) e chegar a um **caminho concreto de carreira**, usando a matriz `SWOT` aplicada à sua vida. -O foco é tirar você do modo “boiando no mar, deixando a maré levar” e colocar no modo “navegação intencional”: clareza de onde está, para onde quer ir e que ajustes precisa fazer. - -Você vai entender por que **comparações rígidas de tempo** (idade, ano de formatura, tempo até o primeiro emprego) são perigosas para a saúde mental e para o planejamento, e como substituir isso por um processo mais inteligente: **definir objetivo, mapear forças/fraquezas e construir ações**. - -## Modelo Mental - -O modelo mental central desta aula combina três ideias: - -1. **Universo de pontos vs constelações** - - Olhar para a carreira sem planejamento é como olhar um céu cheio de estrelas sem conexões: muitos pontos bonitos, mas sem forma. - - Quando você traça linhas entre alguns pontos (decisões, competências, experiências), surgem **constelações**: narrativas coerentes sobre quem você é profissionalmente e aonde quer chegar. - -2. **Maré vs navegação ativa** - - Ficar sem plano é como entrar no mar e ficar apenas boiando: a corrente decide aonde você vai parar. - - Ter um plano não garante um mar sem ondas, mas permite **ajustar a rota** sempre que surgir um desvio (demissão, mudança de país, crise econômica). - -3. **Relógios diferentes** - - Cada pessoa tem um `tempo de vida` e um `tempo de carreira` diferentes. - - Histórias reais de gente que começou tardiamente (ou muito cedo) e chegou longe mostram que **não existe idade mágica** para “dar certo”, mas sim **decisões consistentes ao longo do tempo**. - -Pense na matriz `SWOT` como um **painel de instrumentos**: ela não decide o destino, mas mostra condições para que você ajuste a rota conscientemente. - -## Mecânica Central - -### Conceito de SWOT aplicado à carreira - -A matriz `SWOT` (Strengths, Weaknesses, Opportunities, Threats) vem do planejamento estratégico, mas aqui é aplicada **à pessoa**: - -- `Strengths (Forças)` — características internas que te ajudam: - - Competências técnicas que você já tem (por exemplo, lógica de programação, Excel avançado, contato prévio com Python). - - Competências comportamentais (organização, disciplina, facilidade de comunicação, curiosidade). - - Experiências relevantes (trabalhos anteriores, projetos, voluntariado). - -- `Weaknesses (Fraquezas)` — características internas que atrapalham: - - Gaps técnicos importantes (não saber inglês, zero experiência com ferramentas básicas da área). - - Gaps comportamentais (procrastinação forte, timidez extrema, dificuldade de trabalhar em grupo). - -- `Opportunities (Oportunidades)` — fatores externos que ajudam: - - Mercado aquecido em determinadas áreas (dados, desenvolvimento web, nuvem). - - Bolsas, eventos, hackathons, projetos colaborativos dentro da faculdade. - - Rede de contatos, comunidade da turma, empresas da região, possibilidade de trabalho remoto. - -- `Threats (Ameaças)` — fatores internos ou externos que podem te travar: - - Fraquezas que se tornam críticas (por exemplo, medo de falar em público atrapalhando todas as entrevistas). - - Contexto de vida (falta de tempo, necessidade de trabalhar em horário pesado, responsabilidades familiares). - - Condições de mercado (crises, alta concorrência em determinadas posições iniciais). - -### Fluxo do planejamento de carreira com SWOT - -O diagrama abaixo mostra o fluxo típico sugerido na aula: - -```mermaid -flowchart TD - A[Universo de possibilidades
cursos, áreas, trilhas] --> B[Definir objetivo profissional
cargo ou área desejada] - B --> C[Analisar forças e fraquezas
SWOT interna] - C --> D[Mapear oportunidades e ameaças
mercado e contexto de vida] - D --> E[Escolher ações concretas
reforçar forças, reduzir ameaças] - E --> F[Executar e revisar rota
TPs, projetos, estágio, atividades] - F --> B - - style A fill:#111,stroke:#555 - style B fill:#151515,stroke:#888 - style C fill:#151515,stroke:#888 - style D fill:#151515,stroke:#888 - style E fill:#151515,stroke:#888 - style F fill:#111,stroke:#555 -``` - -Note que há um **ciclo**: você revisita o objetivo e a matriz SWOT sempre que algo relevante muda (por exemplo, um curso novo, uma promoção, uma mudança de país). - -### Ligação com o TP1 da disciplina - -O exercício mencionado na aula (TP1) segue exatamente esse fluxo: - -1. Definir **a área, profissão ou cargo** que você almeja (imediato ou de médio prazo). -2. Listar as **características desse profissional**: - - Competências técnicas. - - Competências comportamentais. -3. Fazer sua **SWOT pessoal**: - - Pelo menos três forças e três fraquezas. - - Oportunidades e ameaças conectadas ao objetivo. -4. Definir **ações concretas** para: - - Reforçar as forças relevantes. - - Reduzir ou mitigar ameaças baseadas em fraquezas. - -## Uso Prático - -Nesta seção, o uso prático é construir um esboço do seu **plano de carreira** com base nas orientações da aula. - -### Exemplo 1 — Estudante de ADS mirando vaga de estágio em desenvolvimento back-end - -Suposição: - -- Curso: Análise e Desenvolvimento de Sistemas. -- Objetivo de curto/médio prazo: vaga de estágio ou júnior em desenvolvimento back-end. - -Trecho de SWOT possível: - -- Forças: - - Já fez cursos introdutórios de lógica e de Python. - - Boa disciplina para estudar diariamente. - - Facilidade em explicar conceitos para colegas. -- Fraquezas: - - Inglês básico, dificuldade de ler documentação. - - Tende a procrastinar projetos longos. - - Insegurança em entrevistas. -- Oportunidades: - - Alta demanda por back-end com APIs. - - Projetos de bloco que obrigam a construir produtos reais. - - Comunidade da turma disposta a montar projetos paralelos. -- Ameaças: - - Perder vagas com exigência forte de leitura em inglês. - - Falhar em dinâmicas de grupo por timidez. - -Possíveis ações: - -- Matricular-se em um curso de inglês focado em leitura técnica. -- Participar de um hackathon ou projeto colaborativo para treinar trabalho em grupo. -- Fazer um mini-curso de **oratória/apresentação** ou ensaiar entrevistas com colegas usando ferramentas como `ChatGPT` como apoio. - -### Exemplo 2 — Profissional em transição de área - -Suposição: - -- Pessoa já trabalha como assistente administrativo, mas quer migrar para a área de dados. - -Aplicação: - -- Objetivo: “Atuar como analista júnior de dados em até X anos”. -- Forças: - - Forte conhecimento de processos internos da empresa. - - Hábito de trabalhar com planilhas e relatórios. - - Rede de contatos na organização atual. -- Fraquezas: - - Pouco domínio de estatística. - - Zero experiência com SQL e ferramentas de visualização. -- Oportunidades: - - Empresa iniciando área de BI. - - Possibilidade de trabalhar em projetos internos de dados ainda como administrativo. -- Ameaças: - - Ficar preso em atividades administrativas por falta de portfólio de dados. - -Possíveis ações: - -- Usar projetos de bloco e atividades complementares para construir **cases de dados**. -- Buscar, dentro da empresa, tarefas que envolvam coleta e análise de dados para ir documentando resultados. -- Planejar, com antecedência, quando iniciar o estágio formal ou a transição de função. - -## Erros Comuns - -- **Querer um plano perfeito e imutável** - A carreira é cheia de reviravoltas; tentar desenhar um roteiro rígido demais gera frustração. O objetivo é ter **direção e capacidade de ajuste**, não um mapa fixo. - -- **Fazer SWOT genérica demais** - Listar forças e fraquezas sem conectar ao objetivo específico (“ser dev back-end”, “ser analista de dados”, “ser product manager”) torna a ferramenta pouco útil. - -- **Ignorar fraquezas que viram ameaças reais** - Saber que tem dificuldade extrema de se comunicar e ainda assim não tomar nenhuma ação concreta é abrir mão de oportunidades que dependem justamente dessa competência. - -- **Copiar objetivos dos outros** - Escolher trilha de carreira apenas porque “é o que todo mundo está fazendo” ou “paga mais” desconsidera o seu perfil, interesses e contexto de vida. - -- **Não transformar o plano em ações com prazo** - Sem prazos e tarefas pequenas (cursos, projetos, exercícios de exposição), o planejamento vira só um texto bonito sem impacto na realidade. - -## Visão Geral de Debugging - -Se o seu plano de carreira “não estiver rodando bem”, estas são algumas perguntas de debugging: - -- Você **de fato definiu um objetivo** (cargo/área) ou só escreveu termos vagos como “trabalhar com tecnologia”? -- Sua matriz `SWOT` foi pensada **em função desse objetivo** ou é apenas uma lista genérica de qualidades e defeitos? -- Você tem **ações com prazo** para lidar com pelo menos uma fraqueza crítica? -- Está se comparando demais com linhas do tempo alheias (idade, ano de graduação, primeira vaga) e ignorando sua própria trajetória? - -Passos para corrigir rota: - -1. **Reescrever o objetivo** - Torná-lo mais específico (ex.: “vaga de estágio em suporte técnico em até 12 meses”, “meu primeiro projeto pago de front-end em até 18 meses”). - -2. **Refazer a SWOT focada no objetivo** - Revisar forças/fraquezas, oportunidades/ameaças com a pergunta: “isso impacta ou não este objetivo específico?”. - -3. **Escolher poucas ações críticas** - Em vez de tentar fazer tudo, focar nas 2–3 ações que mais reduzem ameaças ou mais alavancam forças nesse momento. - -4. **Marcar revisão periódica** - Definir momentos (por exemplo, a cada bloco) para revisar o plano, ajustando à luz de novas informações. - -
-Checklist de saúde do seu plano de carreira - -- [ ] Objetivo definido de forma específica e em tempo aproximado. -- [ ] SWOT pessoal feita com o objetivo em mente. -- [ ] Pelo menos uma ação concreta para cada fraqueza crítica listada. -- [ ] Lista de oportunidades monitoradas (eventos, cursos, vagas, projetos internos). -- [ ] Data marcada para revisar a matriz SWOT e o objetivo. -
- -## Principais Pontos - -- **Cada pessoa tem seu próprio tempo**; comparar carreiras em linha reta é enganoso e desmotivador. -- A matriz `SWOT` aplicada à carreira ajuda a transformar um universo de possibilidades em um plano de ação conectado ao seu objetivo. -- Um **bom plano** começa com objetivo claro, passa por mapeamento honesto de forças e fraquezas e chega em ações com prazo. -- Revisitando o plano a cada bloco, você ajusta a rota sem se perder, mesmo quando surgem imprevistos. - -## Preparação para Prática - -Depois desta lição, você deve ser capaz de: - -- Explicar por que **seguir o seu próprio tempo** de carreira é mais saudável e eficaz do que copiar roteiros alheios. -- Escolher um **objetivo profissional inicial** coerente com sua realidade atual. -- Construir uma matriz `SWOT` pessoal conectada a esse objetivo. -- Derivar, a partir da SWOT, **pelo menos três ações específicas** para os próximos meses. - -No Laboratório de Prática, você vai produzir versões estruturadas desse plano, que podem servir de base para o TP1 e para conversas com mentores, professores e colegas. - -## Laboratório de Prática - -### Exercício Easy — Descrevendo sua constelação de carreira - -**Objetivo:** sair do “céu de pontos” e começar a desenhar a constelação. - -Enunciado: - -Em um pequeno texto estruturado, responda: - -1. Qual é o **objetivo profissional** que você tem em mente para os próximos 2–3 anos? -2. Quais são **3 experiências ou características** que já fazem parte da sua história e que se conectam com esse objetivo? -3. O que mais te motiva nessa direção (interesse intelectual, impacto, salário, estilo de vida etc.)? - -Boilerplate sugerido: - -```markdown -# Minha constelação de carreira - -## Objetivo para os próximos 2–3 anos -- Quero: - -## Pontos que já se conectam com esse objetivo -- Ponto 1: -- Ponto 2: -- Ponto 3: - -## O que me motiva nessa direção -- Motivo 1: -- Motivo 2: -``` - -### Exercício Medium — Sua matriz SWOT pessoal - -**Objetivo:** aplicar SWOT à sua carreira, com base no objetivo definido. - -Enunciado: - -Monte uma matriz SWOT pessoal em texto ou tabela simples, considerando **o objetivo profissional que você definiu no exercício anterior**. - -Boilerplate sugerido: - -```markdown -# Minha SWOT de carreira - -## Objetivo -- Objetivo profissional: - -## Forças (Strengths) -- Força 1: -- Força 2: -- Força 3: - -## Fraquezas (Weaknesses) -- Fraqueza 1: -- Fraqueza 2: -- Fraqueza 3: - -## Oportunidades (Opportunities) -- Oportunidade 1: -- Oportunidade 2: - -## Ameaças (Threats) -- Ameaça 1: -- Ameaça 2: -``` - -### Exercício Hard — Plano de ações para 6–12 meses - -**Objetivo:** transformar a SWOT em um plano prático com ações, prazos e critérios de sucesso. - -Enunciado: - -Com base na sua matriz SWOT, crie um **plano de ações** para os próximos 6–12 meses. Para cada ação, detalhe: - -- Qual fator da SWOT ela atinge (força, fraqueza, oportunidade ou ameaça). -- O que exatamente você fará. -- Prazo estimado. -- Como saberá que a ação foi concluída com sucesso. - -Boilerplate sugerido: - -```markdown -# Plano de ações (6–12 meses) - -## Ação 1 -- Fator da SWOT relacionado: -- Descrição da ação: -- Prazo: -- Critério de sucesso: - -## Ação 2 -- Fator da SWOT relacionado: -- Descrição da ação: -- Prazo: -- Critério de sucesso: - -## Ação 3 -- Fator da SWOT relacionado: -- Descrição da ação: -- Prazo: -- Critério de sucesso: -``` - ---- - - - - - diff --git a/content/planejamento-curso-carreira/aula-03-curriculo-ats-experiencias.md b/content/planejamento-curso-carreira/aula-03-curriculo-ats-experiencias.md deleted file mode 100644 index 1d640c9..0000000 --- a/content/planejamento-curso-carreira/aula-03-curriculo-ats-experiencias.md +++ /dev/null @@ -1,441 +0,0 @@ ---- -title: "Currículo para tecnologia: função, estrutura, ATS e experiências que contam" -slug: "curriculo-ats-experiencias" -discipline: "planejamento-curso-carreira" -order: 3 -description: "Como construir um currículo claro, seguro e compatível com ATS para vagas em tecnologia, mesmo em transição de carreira ou começando do zero." -reading_time: 25 -difficulty: "easy" -concepts: - - papel do currículo em processos seletivos - - currículo como isca e não como garantia de vaga - - segurança de dados pessoais em currículos - - sistemas ATS e palavras-chave - - estrutura clássica de currículo para tecnologia - - descrição de experiências com método SOAR/STAR -prerequisites: - - bloco-entrada-estagio-atividades-complementares - - planejamento-carreira-swot -learning_objectives: - - "Explicar qual é o papel real do currículo em um processo seletivo de tecnologia." - - "Construir um currículo simples, limpo e compatível com ATS, evitando erros de formato e conteúdo." - - "Selecionar e organizar experiências, inclusive de transição de carreira e empreendedorismo, para dialogar com vagas em TI/dados." - - "Escrever descrições de experiência usando estrutura de situação, ação e resultado." -exercises: - - question: "Por que o currículo é descrito como 'isca' e não como o que de fato garante a vaga?" - answer: "Porque a função principal do currículo é chamar a atenção do recrutador o suficiente para que ele queira te conhecer em uma entrevista; quem conquista a vaga é a pessoa na interação, não o documento." - hint: "Lembre-se da analogia da pescaria feita na aula." - - question: "Cite três informações pessoais que você NÃO deve colocar no currículo por questões de segurança." - answer: "Endereço completo, documentos (como CPF e RG) e dados sensíveis como nome completo de pais e data exata de nascimento; isso reduz risco de uso indevido e engenharia social." - hint: "Pense no exemplo do currículo que quase permitia clonar a pessoa." - - question: "O que é um sistema ATS e por que ele afeta o formato do seu currículo?" - answer: "ATS é um sistema de triagem automática de currículos que lê texto e busca palavras-chave; se o currículo tem formato ruim (muitas colunas, gráficos, imagens) ou não repete termos importantes da vaga, o ATS pode não identificar bem o perfil e eliminar o candidato antes do recrutador humano." - hint: "Lembre da crítica a currículos gráficos com duas colunas e fotos." -review_after_days: - - 7 - - 21 ---- - -## Visão Geral do Conceito - -Esta lição reconstrói o papel do currículo na sua carreira em tecnologia: ele é **a isca, não o peixe**. -Você vai entender que o currículo não “ganha a vaga” sozinho; o que ele faz é **chamar a atenção** do recrutador ou da triagem automática para que você seja convidado a uma entrevista, onde de fato poderá se apresentar. - -Ao mesmo tempo, o currículo é um documento sensível: ele precisa equilibrar **clareza técnica, compatibilidade com sistemas ATS e segurança de dados pessoais**, além de traduzir bem experiências de transição de carreira, empreendedorismo, trabalhos anteriores e estágio. - -## Modelo Mental - -Alguns modelos mentais importantes desta aula: - -1. **Currículo como isca de pesca** - - O processo seletivo é como uma pescaria: várias “iscas” (currículos) são lançadas para atrair o interesse do recrutador. - - Sua função é **ser visto e gerar convite para conversa**; depois disso, o currículo já cumpriu seu papel. - -2. **Currículo como API entre você e o recrutador** - - Pense no currículo como uma API de baixo ruído: ele expõe **endpoints claros** (formação, experiência, skills, links) que o recrutador ou o ATS consegue “chamar” rapidamente. - - Campos mal definidos, texto denso ou layout confuso são como documentações ruins: aumentam o atrito e fazem o recrutador pular para o próximo “endpoint” (= outro candidato). - -3. **Currículo como log estruturado, não como redação** - - Em vez de um texto corrido, o currículo funciona melhor como um **log estruturado de eventos relevantes** da sua trajetória (formações, experiências, projetos, atividades extras). - - O método `SOAR` ou `STAR` ajuda a transformar “fiz suporte” em uma história de situação, ação e resultado que comunica valor real. - -## Mecânica Central - -### Papel do currículo no funil de seleção - -O currículo atua nas primeiras etapas do funil: - -- **Triagem automática (ATS)**: - - Ferramentas que leem texto, buscam `palavras-chave` e eliminam currículos que não parecem aderentes. - - Formatos com duas colunas, muitos elementos gráficos e PDFs pesados podem atrapalhar a leitura por essas ferramentas. - -- **Leitura rápida por um recrutador**: - - Um analista de RH ou gestor técnico normalmente passa poucos segundos na primeira leitura. - - Ele procura **palavras e blocos específicos**: curso, tecnologias, experiências relevantes, contexto de negócio, idiomas, links (Linkedin, GitHub). - -- **Função principal**: - - **Despertar interesse suficiente** para que o recrutador pense: “Quero ouvir essa pessoa”. - - Uma vez marcada a entrevista, o currículo cumpriu sua missão. - -### Segurança de dados e o que NÃO colocar - -Por circular digitalmente fora do seu controle, o currículo não deve expor: - -- Endereço completo (rua, número, CEP); use algo como “Cidade / Estado”. -- Documentos: `CPF`, `RG` etc. -- Dados familiares (nome completo de pais). -- Dados que facilitem clonagem de identidade (data de nascimento + documentos + endereço). -- Foto: além de não ajudar o ATS, pode enviar sinais visuais irrelevantes e introduzir vieses. - -Informações que devem aparecer: - -- Nome completo. -- E-mail profissional. -- Telefone/WhatsApp. -- Cidade/Estado. -- Link para `LinkedIn`. -- Link para `GitHub` (essencial em tecnologia). -- Dupla cidadania, quando existir e for relevante (por exemplo, “Cidadania brasileira e italiana”). - -### Estrutura recomendada de currículo para tecnologia - -Uma estrutura simples e amigável para ATS e humanos: - -1. **Cabeçalho de contato** - - Nome. - - Contatos (e-mail, telefone). - - Cidade/Estado. - - Links (LinkedIn, GitHub, portfólio). - -2. **Título e resumo/objetivo** - - Título que já indique sua área: por exemplo, “Estudante de ADS em busca de estágio em desenvolvimento back-end”. - - Um pequeno parágrafo ou lista curta dizendo: - - Em que ponto da formação você está. - - Que tipo de oportunidade busca (estágio, júnior, dados, suporte, produto). - - 2–3 competências-chave aderentes às vagas que vai mirar. - - Este campo é onde você encaixa muitas **palavras-chave** da vaga-alvo. - -3. **Formação acadêmica** - - Graduação (curso, instituição, data de conclusão ou “em andamento — previsão: AAAA”). - - Formações técnicas relevantes (técnico em informática etc.). - - Não incluir ensino médio quando já se está no nível superior. - -4. **Experiência profissional** - - Ordem cronológica reversa (do mais recente ao mais antigo). - - Para cada experiência: - - Nome da empresa. - - Cargo/função. - - Período (mês/ano início — mês/ano fim ou “atual”). - - 3–5 bullets descrevendo resultados usando `SOAR/STAR`. - -5. **Cursos e certificações** - - Cursos relevantes (Fundação Bradesco, Udemy, Coursera, trilhas específicas etc.). - - Não misturar com graduação; manter em seção separada. - -6. **Outras atividades e interesses** - - Trabalho voluntário. - - Participação em palestras, congressos, hackathons. - - Atividades que mostram disciplina, trabalho em equipe, resiliência (esportes, música, empreendedorismo). - -### Descrição de experiências com SOAR/STAR - -Em vez de apenas listar funções, descreva **situações concretas**: - -- `S` — Situação: qual era o contexto? -- `O/T` — Oportunidade/Task: qual era o desafio ou a tarefa? -- `A` — Ação: o que você fez? -- `R` — Resultado: qual foi o efeito concreto? - -Exemplo simplificado inspirado na aula: - -- “Responsável pela montagem e suporte de infraestrutura de TI em stand de feira de óleo e gás, garantindo o funcionamento contínuo de demonstrações técnicas para dezenas de clientes sem incidentes durante todo o evento.” - -### Diagrama do fluxo currículo ↔ ATS ↔ recrutador - -```mermaid -flowchart LR - A[Você
experiências, cursos, projetos] --> B[Currículo estruturado
texto claro + palavras-chave] - B --> C[ATS / filtros automáticos
busca de termos e formato] - C -->|aprovado| D[Recrutador humano
leitura rápida] - D -->|interesse| E[Convite para entrevista] - E --> F[Entrevista e avaliações
técnicas/comportamentais] - - B -.->|formato ruim
texto confuso| G[Descartado cedo] -``` - -## Uso Prático - -### Exemplo 1 — Primeiro currículo para estágio em TI - -Você ainda não trabalhou formalmente, mas: - -- Tem curso técnico em informática. -- Está cursando ADS. -- Fez alguns projetos de bloco e cursos livres. - -Como preencher: - -- No resumo: - - “Estudante de Análise e Desenvolvimento de Sistemas, com curso técnico em informática, em busca de estágio em suporte técnico ou desenvolvimento back-end. Familiaridade com `Windows`, `Linux` básico, lógica de programação em `Python` e uso de `GitHub` para versionamento.” -- Em experiência: - - Criar uma subseção “Projetos acadêmicos relevantes” com bullets usando SOAR/STAR (por exemplo, projetos de bloco). -- Em cursos: - - Listar cursos livres de lógica, Git, introdução a banco de dados etc. - -### Exemplo 2 — Transição de carreira para dados - -Você vem de engenharia, logística, comércio exterior, design ou outra área: - -- Transforme experiências “de fora de TI” em evidências úteis: - - Trabalho com dashboards. - - Análise de dados de clientes. - - Experiência com relatórios, métricas, prazos, contato com stakeholders. -- Mostre isso nas descrições: - - “Desenvolvimento de dashboards em ferramenta X para acompanhar indicadores de logística, reduzindo tempo de tomada de decisão.” - - “Atuação em laboratório de geoprocessamento em parceria com Defesa Civil, realizando análises de dados espaciais para suporte a decisões.” - -### Exemplo 3 — Empreendedorismo e dupla cidadania - -Se você foi empresária(o) ou franqueada(o): - -- Destaque: - - Gestão financeira. - - Relacionamento com clientes. - - Gestão de fornecedores. - - Definição e acompanhamento de metas. - - Uso de ferramentas digitais (por exemplo, marketing digital, sites, automações). - -Se tem dupla cidadania: - -- Inclua isso de forma simples: - - “Cidadania brasileira e italiana.” -- Isso pode ser relevante para vagas remotas, empresas globais ou oportunidades internacionais. - -## Erros Comuns - -- **Usar currículo altamente gráfico, com duas colunas e muitos elementos visuais** - Pode ficar bonito, mas dificulta leitura por ATS e até por recrutadores que querem texto copiável. - -- **Copiar e colar apenas o job description** - Descrever só “responsável por X, Y, Z” não mostra quem você foi na prática, nem que resultados ajudou a gerar. - -- **Incluir dados sensíveis demais** - Endereço completo, documentos e informações familiares aumentam o risco de uso indevido e engenharia social. - -- **Misturar ensino médio, cursos livres e graduação em um bloco só** - Confunde a leitura e dilui a importância da formação principal. - -- **Ter parágrafos enormes, sem bullets** - Dificulta a leitura rápida; o recrutador tende a deixar “para depois” — e quase nunca volta. - -## Visão Geral de Debugging - -Se o seu currículo não está gerando entrevistas, pergunte: - -- Ele está **alinhado a vagas reais** (com palavras-chave copiadas corretamente do anúncio)? -- O layout é simples o suficiente para **ATS e humanos**? -- Há **projetos, cursos e atividades recentes** que você ainda não incluiu? -- Você está **adaptando o resumo/objetivo** para cada tipo de vaga ou usando sempre o mesmo texto genérico? - -Passos de correção: - -1. **Rever layout** - Transformar templates gráficos em um formato simples, linear, com seções bem marcadas. - -2. **Mapear palavras-chave das vagas-alvo** - Ler anúncios e destacar termos técnicos e de contexto, replicando-os no currículo (quando verdadeiros). - -3. **Reescrever descrições de experiência com SOAR/STAR** - Substituir frases vagas por bullets que indiquem situação, ação e resultado. - -4. **Checar dados pessoais e segurança** - Remover documentos, endereço completo e outros itens desnecessários. - -
-Checklist rápido de saúde do currículo - -- [ ] Layout simples, legível e compatível com ATS (sem colunas múltiplas e fotos). -- [ ] Dados pessoais sem informações sensíveis desnecessárias. -- [ ] Resumo/objetivo alinhado à área e às vagas que você está buscando. -- [ ] Experiências descritas com foco em resultados (SOAR/STAR). -- [ ] Sessões claras para formação, experiência, cursos e outras atividades. -
- -## Principais Pontos - -- O currículo é uma **isca** para gerar entrevistas, não o elemento que sozinho garante a vaga. -- **Segurança de dados** importa: não exponha mais do que o necessário em um documento que circula livremente. -- Um bom currículo para tecnologia é **simples, limpo, cheio de palavras-chave relevantes e rico em exemplos de experiências e projetos**. -- Métodos como `SOAR`/`STAR` ajudam a transformar qualquer trajetória — inclusive transição de carreira e empreendedorismo — em histórias que comunicam valor. - -## Preparação para Prática - -Depois desta lição, você deve ser capaz de: - -- Montar um currículo em formato compatível com ATS, focado em tecnologia. -- Selecionar quais informações pessoais incluir e quais omitir por segurança. -- Reescrever descrições de experiência para refletir melhor seu perfil e objetivos. -- Conectar este currículo ao que você já planejou em termos de carreira (SWOT, blocos, estágio). - -No Laboratório de Prática, você vai estruturar (ou revisar) o seu currículo real, usando os modelos e critérios desta lição. - -## Laboratório de Prática - -### Exercício Easy — Mapa das seções do seu currículo - -**Objetivo:** garantir que seu currículo tem as seções essenciais, bem organizadas. - -Enunciado: - -Crie um esboço textual (não precisa ser o currículo completo ainda) listando as seções que o seu currículo terá e o que entra em cada uma. - -Boilerplate sugerido: - -```markdown -# Estrutura do meu currículo - -## Cabeçalho -- Nome: -- E-mail: -- Telefone: -- Cidade/Estado: -- LinkedIn: -- GitHub: - -## Título e resumo -- Título: -- Resumo (2–3 frases): - -## Formação -- Curso: -- Instituição: -- Situação (concluído / em andamento + previsão): - -## Experiência profissional -- Experiência 1: -- Experiência 2: - -## Cursos e certificações -- Curso 1: -- Curso 2: - -## Outras atividades e interesses -- Atividade 1: -- Atividade 2: -``` - -### Exercício Medium — Reescrevendo uma experiência com SOAR/STAR - -**Objetivo:** transformar uma descrição vaga em uma descrição orientada a resultados. - -Enunciado: - -Escolha uma experiência (formal, projeto, empreendedorismo ou voluntariado) e escreva **pelo menos três bullets** usando a estrutura SOAR/STAR. - -Boilerplate sugerido: - -```markdown -# Experiência: [nome da empresa ou projeto] - -- Situação / contexto: -- Oportunidade / tarefa: -- Ação: -- Resultado: - -## Bullets para o currículo -- Bullet 1: -- Bullet 2: -- Bullet 3: -``` - -### Exercício Hard — Revisão completa do seu currículo atual - -**Objetivo:** aplicar todos os conceitos para gerar uma versão pronta para enviar a vagas reais. - -Enunciado: - -Pegue seu currículo atual e: - -1. Adapte o layout para o formato recomendado. -2. Ajuste dados pessoais (removendo informações sensíveis). -3. Atualize experiências usando SOAR/STAR. -4. Inclua cursos, projetos e atividades complementares recentes. - -Crie uma nova versão e escreva um pequeno comentário explicando as principais mudanças. - -Boilerplate sugerido: - -```markdown -# Revisão do meu currículo - -## Mudanças de layout -- Antes: -- Depois: - -## Mudanças em dados pessoais -- Removido: -- Adicionado: - -## Mudanças em experiências -- Experiência 1 (antes/depois): -- Experiência 2 (antes/depois): - -## Novos cursos / projetos incluídos -- Item 1: -- Item 2: -``` - ---- - - - - - diff --git a/content/planejamento-curso-carreira/aula-04-linkedin-carreira-oportunidades.md b/content/planejamento-curso-carreira/aula-04-linkedin-carreira-oportunidades.md deleted file mode 100644 index 074728f..0000000 --- a/content/planejamento-curso-carreira/aula-04-linkedin-carreira-oportunidades.md +++ /dev/null @@ -1,431 +0,0 @@ ---- -title: "LinkedIn para carreira em tecnologia: visibilidade, networking e busca de vagas" -slug: "linkedin-carreira-oportunidades" -discipline: "planejamento-curso-carreira" -order: 4 -description: "Como usar o LinkedIn de forma estratégica para ser encontrado por recrutadores, buscar vagas, manter networking ativo e conectar isso ao seu plano de carreira em tecnologia." -reading_time: 25 -difficulty: "easy" -concepts: - - linkedin como rede profissional - - niveis de conexão (1º, 2º, 3º grau) - - busca e candidatura a vagas no linkedin - - funcionamento básico de perfis premium - - estrutura de perfil (banner, foto, título, sobre, experiências, competências) - - networking ativo e grupos temáticos -prerequisites: - - bloco-entrada-estagio-atividades-complementares - - planejamento-carreira-swot - - curriculo-ats-experiencias -learning_objectives: - - "Diferenciar o LinkedIn de outras redes sociais e usá-lo com foco profissional." - - "Configurar e estruturar um perfil de LinkedIn alinhado à área de tecnologia." - - "Utilizar recursos de vagas, alertas, mensagens e grupos para aumentar oportunidades." - - "Manter um networking ativo e saudável conectado ao seu plano de carreira." -exercises: - - question: "Por que o LinkedIn é descrito como uma rede profissional e não apenas mais uma rede social?" - answer: "Porque o foco do LinkedIn é a vida profissional: experiências, competências, vagas, networking e conteúdo ligado ao trabalho, recrutamento e negócios, não compartilhamento de aspectos pessoais como nas redes sociais tradicionais." - hint: "Lembre da ênfase em evitar conteúdo pessoal e tratar o LinkedIn como ambiente profissional." - - question: "Explique o que significam conexões de 1º, 2º e 3º grau e por que isso impacta sua visibilidade nas buscas de recrutadores." - answer: "Conexões de 1º grau são pessoas ligadas diretamente a você; as de 2º grau são conexões das suas conexões; as de 3º grau são conexões do 2º grau. O LinkedIn costuma mostrar resultados até o 3º grau nas buscas; quanto mais conexões relevantes você tem, maior o alcance da sua rede e a chance de aparecer nas pesquisas de recrutadores." - hint: "Lembre do exemplo da professora criando conexões com diretores médicos para expandir a rede." - - question: "Por que é importante preencher e organizar o campo de competências no LinkedIn?" - answer: "Porque o LinkedIn usa as competências como palavras-chave nas buscas e nos matches com vagas; ter competências relevantes listadas e bem ordenadas aumenta a chance de ser encontrado e de ter um match alto ao se candidatar." - hint: "Pense no momento em que o LinkedIn compara vagas com seu perfil e aponta quais skills batem." -review_after_days: - - 7 - - 21 ---- - -## Visão Geral do Conceito - -Esta lição mostra como usar o `LinkedIn` como **ferramenta central** do seu planejamento de curso e carreira em tecnologia, e não apenas como mais uma rede social. -Você vai entender o papel das **conexões**, como os recrutadores de fato usam a plataforma, como funcionam as **vagas**, alertas e mensagens e como o seu perfil pode ser configurado para aumentar sua **visibilidade** e **credibilidade profissional**. - -Além disso, a lição conecta o LinkedIn ao que você já trabalhou em currículo, SWOT e objetivos: o perfil passa a ser a sua **vitrine dinâmica**, alinhada ao plano de carreira que você está construindo. - -## Modelo Mental - -Alguns modelos mentais importantes: - -1. **LinkedIn como mapa de grafos de pessoas** - - Em vez de pensar em “timeline de posts”, pense em **nós (perfis)** e **arestas (conexões)**. - - Você enxerga (e é enxergado) até conexões de **3º grau**; a partir do 4º, desaparece da sua visão e das buscas. - -2. **LinkedIn como motor de busca de talentos** - - Recrutadores usam o LinkedIn como um “Google de pessoas” com filtros por cargo, localização, competências, setor etc. - - Um perfil bem preenchido e alinhado age como uma **página bem indexada**: aparece mais em buscas relevantes. - -3. **LinkedIn como feed de oportunidades e reputação** - - Postagens, grupos, recomendações e interações funcionam como **sinais públicos** da sua atuação e interesses. - - Pequenas ações consistentes (comentar, parabenizar, participar de grupos) mantêm seu nome em circulação (“networking ativo”). - -## Mecânica Central - -### Rede de conexões e visibilidade (1º, 2º, 3º grau) - -No LinkedIn: - -- **1º grau**: pessoas com quem você se conectou diretamente. -- **2º grau**: conexões das suas conexões. -- **3º grau**: conexões do 2º grau. - -Impacto disso: - -- Buscas de recrutadores normalmente retornam perfis **até 3º grau**. -- Quando você se conecta a alguém de um setor ou empresa, você passa a ter acesso (e ser acessível) à rede dessa pessoa. -- Exemplo da aula: - - A professora precisava recrutar um diretor médico. - - No início, não tinha diretores médicos na rede → buscas fracas. - - Depois que se conectou a alguns, novas buscas passaram a trazer muitos outros diretores (2º e 3º grau). - -Conclusão: **conexões relevantes aumentam seu alcance** nas buscas de recrutadores e nas sugestões de contatos. - -### Vagas, alertas e candidaturas - -Na aba de vagas, você pode: - -- Pesquisar por: - - Cargo (ex.: “analista desenvolvedor”, “estagiário de dados”). - - Local (cidade, remoto, raio em km). - - Nível de experiência, tipo de contrato etc. -- Ver dois tipos principais de candidatura: - - **Candidatura simplificada**: usa o próprio perfil do LinkedIn; você se candidata em poucos cliques. - - **Candidatura externa**: redireciona para outro sistema (ex.: Gupy, Kenoby, site da empresa), onde você preencherá mais dados. -- **Configurar alertas**: - - Ao ativar um alerta, o LinkedIn passa a avisar novas vagas relacionadas ao termo e filtros escolhidos. - -Quando você tem perfil premium: - -- Pode ver como suas competências se comparam com as skills pedidas na vaga. -- Pode aparecer mais frequentemente nas primeiras posições da lista de candidatos. -- Pode enviar mais mensagens diretas (InMails) para recrutadores fora da sua rede de 1º grau. - -### Perfil: estrutura essencial - -Um bom perfil para a área de tecnologia inclui: - -- **Banner**: - - Imagem de fundo com tema profissional (tecnologia, dados, código, cidade, algo neutro). -- **Foto**: - - Foto profissional ou ao menos neutra (não usar fotos de praia, festas, nem de anime). - - Apenas você, não fotos com outras pessoas. -- **Nome** e **Título**: - - Título descrevendo **quem você é profissionalmente**, não só o nome da empresa: - - Ex.: “Estudante de ADS | Foco em desenvolvimento back-end em Python”. - - Ex.: “Analista de dados em transição da engenharia para tecnologia”. -- **Localização**: - - Cidade/Estado. -- **Sobre (About)**: - - Um resumo rico, com: - - Quem você é. - - Em que ponto está (formação, transição de carreira). - - Que tipo de oportunidade procura. - - Principais competências técnicas e comportamentais. - - Links úteis (por exemplo, `GitHub`). - - Use muitas **palavras-chave relevantes** para a sua área. - -- **Experiências**: - - Parecido com o currículo, mas aqui você pode ser um pouco mais detalhado. - - Usar SOAR/STAR para descrever projetos, resultados, responsabilidades. - -- **Formação acadêmica**: - - Graduação atual (ex.: “Bacharelado em Ciência de Dados — em andamento, previsão 2029”). - - Cursos anteriores relevantes (como engenharia parcialmente concluída, técnico etc.). - -- **Licenças e certificados**: - - Certificações técnicas (quando houver), com códigos de verificação quando disponíveis. - -- **Competências (Skills)**: - - Lista extensa de tecnologias, ferramentas e competências comportamentais. - - Até dezenas de skills, se forem relevantes. - - Reorganizar para que as **três primeiras** sejam as mais importantes (elas aparecem com destaque). - -- **Idiomas**: - - Níveis de proficiência claros (básico, intermediário, avançado, fluente). - -- **Recomendações**: - - Feedbacks escritos de colegas, líderes ou professores, visíveis no seu perfil. - - São sempre aprovadas por você antes de aparecerem. - -### LinkedIn Learning e grupos - -- **Grupos**: - - Espaços temáticos (por ex.: Python, dados, front-end, vagas de TI). - - Usados para: - - Trocar experiências. - - Ver vagas específicas. - - Participar de discussões técnicas. - - Entrar em grupos alinhados ao seu foco ajuda a: - - Aumentar sua rede relevante. - - Ser visto em contextos profissionais adequados. - -- **LinkedIn Learning**: - - Biblioteca de cursos, vídeos e trilhas. - - Para perfis premium, grande parte do conteúdo é liberado. - - Pode complementar seus estudos formais e virar item de “cursos” no currículo e no próprio LinkedIn. - -### Configurações de privacidade e notificações - -- Você controla: - - O que é visível do seu perfil. - - Se atualizações (como mudanças de cargo, nova formação) são divulgadas para sua rede. - - Quem vê suas conexões, e até se mostra que você visitou o perfil de alguém. -- Estratégia comum: - - **Desativar** a divulgação automática enquanto faz uma grande reforma no perfil. - - Ajustar tudo com calma. - - Reativar, e então fazer uma última mudança para sinalizar sua nova fase de carreira (se fizer sentido). - -## Uso Prático - -### Exemplo 1 — Montando um LinkedIn mínimo viável para estágio - -Para quem está começando: - -- Preencher: - - Foto neutra. - - Título: “Estudante de ADS | Em busca de estágio em desenvolvimento ou suporte”. - - Sobre: resumo curto, mencionando bloco atual, linguagens/ferramentas que já está estudando e interesse por estágio. - - Formação: graduação em andamento + qualquer curso técnico relevante. - - Competências: lógica de programação, Python, SQL básico, Git, trabalho em equipe, comunicação etc. -- Criar alertas para: - - “Estágio em desenvolvimento”. - - “Estágio em dados”. - -### Exemplo 2 — Transição de carreira usando LinkedIn - -Para quem vem de outra área: - -- No título e no sobre: - - Deixar clara a transição: “Profissional de logística em transição para análise de dados”. - - Explicar brevemente como as experiências anteriores ajudam na nova área (ex.: dashboards, análise de indicadores, contato com clientes). -- Nas experiências: - - Descrever atividades que já tocam dados, tecnologia ou projetos. -- Nas competências: - - Misturar skills da área antiga que são úteis (gestão de processos, relacionamento) com as novas (SQL, Python, BI). - -### Exemplo 3 — Networking ativo e conteúdo - -- Acompanhar: - - Professores, coordenadores, RH de empresas-alvo, engenheiros e analistas da sua área. -- Ações simples: - - Parabenizar mudanças de cargo ou conquistas. - - Curtir e comentar posts técnicos com observações relevantes. - - Compartilhar, de vez em quando, experiências de aprendizado (por exemplo, um projeto que você concluiu), focando nas lições e no processo. - -## Erros Comuns - -- **Tratar o LinkedIn como Instagram/Facebook** - Postar conteúdos estritamente pessoais ou polêmicos, que nada têm a ver com trabalho ou aprendizado, prejudica sua imagem profissional. - -- **Perfil vazio ou com pouquíssimas informações** - Ter LinkedIn só “por ter” mas sem foto, título, sobre, formação e competências, faz com que você não apareça em buscas relevantes. - -- **Título focado apenas no cargo atual ou no nome da empresa** - Exemplo: “Analista na Empresa X” em vez de “Analista de dados | Foco em BI e SQL”. O título deve falar de **você**, não só da empresa. - -- **Poucas competências cadastradas** - Ignorar o campo de competências reduz as chances de match com vagas e com buscas de recrutadores. - -- **Não revisar configurações de privacidade** - Deixar tudo público sem querer ou avisar a rede a cada pequena edição pode causar ruído, especialmente em contextos sensíveis (transição de carreira, mudanças de emprego). - -## Visão Geral de Debugging - -Se o seu LinkedIn não está gerando visualizações de perfil, conexões relevantes ou convites: - -- Seu **título e sobre** comunicam claramente sua área-alvo e competências? -- Seu perfil está **preenchido** (foto, formação, experiências, competências, idiomas)? -- Você está **participando ativamente** (conexões, grupos, interações) ou só criou a conta e deixou parada? -- Você revisou as **configurações de privacidade** para garantir que as pessoas certas conseguem ver o que precisa ser visto? - -Passos de correção: - -1. **Reescrever título e sobre** para alinhar com sua trilha em tecnologia. -2. **Completar seções faltantes** (formação, experiências, cursos, competências, idiomas). -3. **Entrar em grupos relevantes** e seguir pessoas-chave da área. -4. **Adicionar conexões** de forma intencional (colegas de curso, professores, profissionais da área). - -
-Checklist de saúde do seu LinkedIn - -- [ ] Foto e banner profissionais ou neutros. -- [ ] Título descrevendo claramente sua atuação/objetivo em tecnologia. -- [ ] Seção “Sobre” preenchida com resumo rico e palavras-chave relevantes. -- [ ] Formação, experiências, cursos e competências atualizados. -- [ ] Alertas de vagas configurados para cargos/áreas de interesse. -- [ ] Participação mínima em grupos e interações com a rede. -
- -## Principais Pontos - -- O LinkedIn é uma **rede profissional** e um dos principais motores de busca de talentos para tecnologia. -- Seu alcance depende muito da **qualidade das conexões** e da completude do perfil. -- Usar recursos como **vagas, alertas, grupos, competências e recomendações** aumenta sua visibilidade e abre oportunidades. -- O LinkedIn deve refletir e apoiar o **plano de carreira** que você desenhou (objetivos, SWOT, currículo). - -## Preparação para Prática - -Depois desta lição, você deve ser capaz de: - -- Configurar um perfil de LinkedIn alinhado à sua trilha em tecnologia. -- Usar a área de vagas, alertas e mensagens para buscar oportunidades. -- Construir conexões relevantes e participar de grupos técnicos. -- Conectar atualizações de perfil com as evoluções do seu currículo e plano de carreira. - -No Laboratório de Prática, você vai construir ou revisar o seu perfil de LinkedIn de ponta a ponta. - -## Laboratório de Prática - -### Exercício Easy — Checklist do seu perfil atual - -**Objetivo:** mapear o que já está pronto e o que falta no seu LinkedIn. - -Enunciado: - -Crie um checklist simples avaliando o estado atual do seu perfil em cada seção (foto, título, sobre, formação, experiências, competências, idiomas, links). - -Boilerplate sugerido: - -```markdown -# Checklist do meu LinkedIn - -## Foto e banner -- Foto profissional/neutra: [ ] -- Banner configurado: [ ] - -## Título -- Título descreve minha área/objetivo em tecnologia: [ ] - -## Sobre -- Tenho um resumo preenchido: [ ] -- O resumo menciona minhas competências e objetivos: [ ] - -## Formação -- Graduação atual cadastrada: [ ] -- Previsão de conclusão informada: [ ] - -## Experiências -- Experiências anteriores cadastradas: [ ] - -## Competências -- Competências técnicas e comportamentais relevantes adicionadas: [ ] -- Três principais competências organizadas: [ ] - -## Idiomas -- Idiomas e níveis cadastrados: [ ] - -## Links -- GitHub/portfólio incluídos (quando existirem): [ ] -``` - -### Exercício Medium — Reescrevendo título e sobre - -**Objetivo:** alinhar título e resumo ao seu plano de carreira. - -Enunciado: - -Baseando-se na sua SWOT e nos objetivos definidos em lições anteriores, escreva uma nova versão do seu **título** e do seu **sobre** para usar no LinkedIn. - -Boilerplate sugerido: - -```markdown -# Novo título -- Versão atual: -- Nova versão: - -# Novo "Sobre" -- Versão atual (se existir): - -- Nova versão proposta: -``` - -### Exercício Hard — Plano de ação LinkedIn para 30 dias - -**Objetivo:** transformar a teoria em uso consistente da plataforma. - -Enunciado: - -Monte um plano de ações para os próximos 30 dias, incluindo: - -- Atualizações de perfil (título, sobre, competências, experiências). -- Número mínimo de conexões novas por semana. -- Grupos que você pretende entrar. -- Número mínimo de interações (comentários, compartilhamentos) em conteúdos relevantes. - -Boilerplate sugerido: - -```markdown -# Plano de ação LinkedIn — 30 dias - -## Atualizações de perfil -- Ação 1: -- Ação 2: - -## Networking -- Novas conexões por semana: -- Tipos de profissionais alvo (ex.: devs, analistas de dados, RH de empresas-alvo): - -## Grupos -- Grupo 1: -- Grupo 2: - -## Interações -- Comentários em posts técnicos por semana: -- Publicações próprias (se houver): -``` - ---- - - - - - diff --git a/content/planejamento-curso-carreira/aula-05-habitos-mudanca-equipes-proatividade.md b/content/planejamento-curso-carreira/aula-05-habitos-mudanca-equipes-proatividade.md deleted file mode 100644 index 7bdf564..0000000 --- a/content/planejamento-curso-carreira/aula-05-habitos-mudanca-equipes-proatividade.md +++ /dev/null @@ -1,434 +0,0 @@ ---- -title: "Hábitos, mudança e trabalho em equipe: proatividade na carreira em tecnologia" -slug: "habitos-mudanca-equipes-proatividade" -discipline: "planejamento-curso-carreira" -order: 5 -description: "Como hábitos, resistência à mudança, trabalho em equipe e proatividade influenciam sua carreira em tecnologia e a implantação de projetos de TI." -reading_time: 25 -difficulty: "easy" -concepts: - - hábitos e resistência à mudança - - impacto de mudanças tecnológicas em pessoas e processos - - gestão de mudança organizacional - - trabalho em equipe e papéis - - comunicação, colaboração e confiança - - proatividade vs reatividade -prerequisites: - - bloco-entrada-estagio-atividades-complementares - - planejamento-carreira-swot - - curriculo-ats-experiencias - - linkedin-carreira-oportunidades -learning_objectives: - - "Reconhecer como hábitos e medos pessoais alimentam resistência à mudança em projetos de tecnologia." - - "Identificar elementos básicos de gestão de mudanças em implantações de sistemas e processos." - - "Descrever habilidades centrais para trabalho em equipe eficaz em TI." - - "Diferenciar atitudes reativas e proativas em situações de estudo, trabalho e carreira." -exercises: - - question: "Por que mudanças tecnológicas (como implantação de um novo sistema) costumam gerar resistência nas pessoas?" - answer: "Porque mexem com hábitos consolidados, colocam em dúvida a sensação de competência e segurança do profissional, geram medo de não conseguir manter o mesmo nível de qualidade e, em muitos casos, despertam um temor inconsciente de perder o emprego ou o reconhecimento." - hint: "Lembre dos exemplos da automação de escritório e da implantação de sistemas integrados como o SAP." - - question: "Quais são dois elementos essenciais de um bom processo de gestão de mudança ao implantar um novo sistema numa empresa?" - answer: "Envolver e ouvir as pessoas impactadas desde o início (entender processos, receios, formas de trabalho) e planejar treinamento e apoio contínuo (manuais, suporte, equipes de apoio) para que elas se sintam seguras na transição." - hint: "Pense na terceira tentativa bem-sucedida de implantação que a professora citou." - - question: "Em termos de atitude, qual é a diferença entre ser reativo e ser proativo diante de um problema na faculdade ou no trabalho?" - answer: "Ser reativo é culpar fatores externos (professor, prova, trânsito, chefia) e concluir que não há o que fazer; ser proativo é assumir responsabilidade e perguntar 'o que eu posso fazer dentro do que está ao meu alcance para melhorar a situação?', buscando ações concretas de mudança." - hint: "Lembre do vídeo sobre o aluno que mudou sua atitude em relação à matéria." -review_after_days: - - 7 - - 21 ---- - -## Visão Geral do Conceito - -Esta lição trata de como **hábitos**, **mudança** e **trabalho em equipe** influenciam diretamente sua vida acadêmica e sua carreira em tecnologia. -Projetos de TI não mexem só com código e sistemas: mexem com **pessoas**, suas rotinas, seus medos e sua forma de trabalhar. Entender resistência à mudança, colaboração em equipe e o hábito da **proatividade** é essencial para implantar soluções com sucesso e para crescer na carreira. - -Você verá exemplos de grandes implantações de sistemas, de equipes que funcionam (e que não funcionam) e o contraste entre atitudes reativas e proativas, conectando tudo isso ao seu dia a dia como estudante e futuro profissional de TI/dados. - -## Modelo Mental - -Alguns modelos mentais-chave: - -1. **Hábito como loop de recompensa** - - Mesmo hábitos ruins (como fumar, procrastinar ou resistir a um novo sistema) entregam algum tipo de “recompensa” (alívio, conforto, sensação de controle), o que torna difícil abandoná-los. - - Mudar hábitos exige **substituir** essa recompensa, não apenas “forçar força de vontade”. - -2. **Mudança tecnológica como onda que atinge pessoas e processos** - - Implantar um sistema novo (ERP, prontuário eletrônico, app) não é só “instalar software”: é mudar rotinas, fluxos, responsabilidades e identidade profissional de muita gente. - - Sem gestão de mudança, a tendência é as pessoas **continuarem fazendo do jeito antigo**, mesmo com sistema novo no ar. - -3. **Equipe como organismo, não como soma de indivíduos** - - Cada pessoa é um indivíduo com seus hábitos, mas, diante de uma ameaça ou desafio, uma equipe forte se organiza, coordena e age em conjunto (como as formigas do vídeo). - - O líder é parte da equipe, não alguém de fora: ele entra na “bola de defesa” junto com todos. - -4. **Proatividade como escolha entre estímulo e resposta** - - Entre o que acontece (estímulo) e a forma como você reage (resposta), existe um espaço de **liberdade de escolha**. - - Gente reativa culpa; gente proativa pergunta: **“O que eu posso fazer dentro desse cenário?”**. - -## Mecânica Central - -### Hábitos e resistência à mudança - -Quando um projeto de tecnologia altera a forma de trabalhar: - -- As pessoas se perguntam (conscientemente ou não): - - “Vou continuar entregando bem?” - - “Vão achar que sou incompetente se eu tiver dificuldade?” - - “Posso perder o emprego se não me adaptar?” -- Isso gera: - - **Medo**, ansiedade, desconfiança. - - Tendência a se agarrar ao jeito antigo de fazer as coisas. - - Resistência passiva: “deixa que eu continuo anotando no papel e depois alguém digita”. - -Sem gestão de mudança, essa resistência faz projetos falharem, mesmo quando a tecnologia em si funciona. - -### Gestão de mudança em projetos de TI - -Elementos que apareceram nos exemplos da aula: - -- **Mapear processos e atores**: - - Entender quem faz o quê, como faz hoje e o que mudará com o novo sistema. - - Identificar todas as áreas impactadas (não só TI). - -- **Envolver pessoas desde cedo**: - - Conversar com usuários-chave. - - Ouvir receios e sugestões. - - Criar senso de participação (“fui ouvido, ajudei a construir”). - -- **Treinamento e apoio**: - - Treinamentos práticos. - - Manuais, vídeos, FAQs. - - Equipes de apoio de plantão na virada (go-live). - -- **Acompanhamento e ajustes**: - - Monitorar problemas reais após a implantação. - - Ajustar fluxos e telas se necessário. - -Histórias como a terceira tentativa de implantação de um sistema integrado ou a transformação de um grande plano de saúde mostram que gestão de mudança é **tão importante** quanto a tecnologia escolhida. - -### Trabalho em equipe: lições das formigas e do caranguejo - -Das metáforas usadas na aula: - -- **Formigas**: - - Cada uma tem sua tarefa individual, mas diante de uma ameaça, elas se reorganizam como equipe. - - Surge um líder que coordena, mas ele se **junta** à equipe na ação (não fica só olhando). - -- **Caranguejo menor**: - - Em equipes reais, o ritmo é determinado pelo **mais lento**, não pelo mais rápido. - - Se você só se preocupa em “chegar primeiro” e abandona quem tem mais dificuldade, a entrega da equipe como um todo sofre. - - Uma equipe madura identifica quem está com mais dificuldade e ajuda a elevá-lo. - -### Habilidades centrais para trabalho em equipe - -A aula destaca várias: - -- **Comunicação**: - - Não é só falar; é garantir que a mensagem foi entendida. - - Considerar filtros pessoais, crenças, experiências e gatilhos emocionais. - - Checar entendimento (“foi isso que você quis dizer?”). - -- **Autoconhecimento**: - - Saber seus gatilhos (o que te irrita, te trava, te desmotiva). - - Perceber quando está alterado e evitar resolver conflitos nesse estado. - -- **Gestão de conflitos**: - - Muitos conflitos nascem de **comunicação falha**. - - Resolver cedo, com conversa franca, reduz atrito e desgaste. - -- **Flexibilidade**: - - Aceitar que erros pontuais acontecem, especialmente em coisas novas. - - Distinguir entre erro pontual (parte do aprendizado) e comportamento displicente recorrente. - -- **Resiliência**: - - Capacidade de lidar com frustrações, imprevistos, dificuldades e seguir em frente. - - Fundamental em ambientes complexos como TI/dados. - -- **Colaboração**: - - Estender a mão para quem está com mais dificuldade. - - Compartilhar conhecimento e insights. - -- **Confiança**: - - Evitar fofocas e intrigas. - - Construir um ambiente onde as pessoas sabem que podem contar umas com as outras. - -### Proatividade vs reatividade - -Com base no vídeo inspirado em Stephen Covey e Viktor Frankl: - -- **Pessoa reativa**: - - Culpa professor, prova, trânsito, empresa, avó, política, “sorte”. - - Conclui que não há nada que possa fazer. - - Seu cérebro “desliga” a busca por soluções. - -- **Pessoa proativa**: - - Assume responsabilidade por sua parte da situação. - - Pergunta: “O que está ao meu alcance fazer diferente?”. - - Adota ações concretas (estudar mais, buscar ajuda, reorganizar tempo, mudar abordagem). - -Aplicação prática: - -- Em vez de dizer “não entendo essa matéria porque o professor é ruim”, pensar: - - “Que outras fontes posso usar?”. - - “Como posso estudar de um jeito diferente?”. - - “Com quem posso tirar dúvidas?”. - -## Uso Prático - -### Exemplo 1 — Estudo reativo vs proativo - -Situação: - -- Prova difícil, professor confuso, nota baixa. - -Reativo: - -- Reclama do professor e da prova. -- Não muda seu método de estudo. -- Repete o ciclo no próximo semestre. - -Proativo: - -- Analisa o que entendeu e o que não entendeu. -- Busca materiais alternativos (vídeos, docs, grupos de estudo). -- Ajusta rotina (mais prática, menos só leitura). -- Pede ajuda a colegas/professores. - -### Exemplo 2 — Implantação de sistema com e sem gestão de mudança - -Sem gestão de mudança: - -- Sistema é colocado no ar. -- Pessoas não foram envolvidas, nem treinadas. -- Usuários continuam usando planilhas e papel. -- Dados não entram no sistema → o sistema “não existe” na prática. - -Com gestão de mudança: - -- Mapeamento de processos e atores. -- Treinamentos e manuais. -- Equipes de apoio no go-live. -- Comunicação clara de benefícios e do porquê da mudança. -- Gradualmente, as pessoas adotam o novo sistema e o antigo é desativado. - -### Exemplo 3 — Equipe de projeto ajudando o “caranguejo mais lento” - -Projeto em grupo na faculdade: - -- Um colega tem mais dificuldade técnica ou de organização. -- Reativo: - - Reclamar dele, deixá-lo isolado, sobrecarregar outros. -- Proativo: - - Dividir tarefas de forma estratégica. - - Parear programação, revisar juntos, ensinar. - - Ajudá-lo a alcançar o ritmo mínimo para a entrega. - -## Erros Comuns - -- **Ignorar o fator humano em projetos de TI** - Focar só em tecnologia e esquecer que usuários têm hábitos, medos e formas próprias de trabalhar. - -- **Tratar qualquer erro como fracasso inaceitável** - Isso mata inovação e aprendizado; em novas situações, algum nível de erro é inevitável. - -- **Evitar conversas difíceis em equipe** - Deixar conflitos crescerem em vez de esclarecer mal-entendidos logo. - -- **Culpar sempre fatores externos** - Reagir como se nunca houvesse algo sob seu controle, mesmo quando há espaço para pequenas mudanças pessoais. - -## Visão Geral de Debugging - -Quando você sente que está travado em estudo, trabalho ou projeto: - -- Está adotando postura mais **reativa ou proativa**? -- Que parte da situação **está realmente sob seu controle**? -- Está reconhecendo e lidando com seus **gatilhos emocionais**? -- A equipe está de fato atuando como equipe ou cada um por si? - -Passos de correção: - -1. **Nomear o problema** - - Escrever qual é exatamente o desafio (nota baixa, prazo apertado, dificuldade com ferramenta, conflito em equipe). - -2. **Separar o que você controla do que não controla** - - Focar em ações no que você controla (estudo, comunicação, pedido de ajuda, organização). - -3. **Conversar com a equipe/pessoas envolvidas** - - Esclarecer expectativas, combinar formas de trabalho, alinhar entregas. - -4. **Ajustar hábitos** - - Pequenas mudanças consistentes (por exemplo, blocos de estudo diário, check-ins rápidos com o grupo, registrar decisões). - -
-Checklist de atitude proativa em projetos - -- [ ] Eu sei claramente qual é o meu papel neste projeto. -- [ ] Identifiquei quais partes da situação estão sob meu controle. -- [ ] Tomei pelo menos uma ação concreta para melhorar o cenário. -- [ ] Conversei com a equipe sobre dificuldades e possíveis ajustes. -- [ ] Estou observando meus hábitos (de estudo/trabalho) e ajustando o que preciso. -
- -## Principais Pontos - -- Mudanças tecnológicas afetam **pessoas, hábitos e medos**; sem gestão de mudança, projetos falham mesmo com boa tecnologia. -- Trabalho em equipe eficaz exige **comunicação, colaboração, confiança, flexibilidade, resiliência e autoconhecimento**. -- A **proatividade** é um hábito central de profissionais que crescem: eles assumem responsabilidade e buscam ações dentro do que podem fazer, em vez de culpar tudo ao redor. -- Esses princípios valem tanto para **projetos de TI** quanto para sua própria jornada de estudos e carreira. - -## Preparação para Prática - -Depois desta lição, você deve ser capaz de: - -- Identificar fontes de resistência à mudança em cenários de tecnologia. -- Listar ações básicas de gestão de mudança que uma equipe de projeto pode tomar. -- Refletir sobre seus hábitos e postura (reativa vs proativa) em estudo e trabalho. -- Observar e fortalecer habilidades de trabalho em equipe em seus projetos acadêmicos e profissionais. - -No Laboratório de Prática, você vai aplicar essas ideias a situações concretas da sua realidade. - -## Laboratório de Prática - -### Exercício Easy — Analisando um hábito seu - -**Objetivo:** conectar o conceito de hábito à sua rotina de estudos/carreira. - -Enunciado: - -Escolha um hábito seu que impacta diretamente seus estudos (positivo ou negativo) e responda: - -- Qual é o hábito? -- Que recompensa ele te dá (alívio, diversão, sensação de controle etc.)? -- Ele te aproxima ou te afasta dos seus objetivos em tecnologia? - -Boilerplate sugerido: - -```markdown -# Meu hábito e sua influência - -## Hábito escolhido -- Descrição: - -## Recompensa que esse hábito me dá -- Recompensa: - -## Impacto nos meus objetivos em tecnologia -- Me aproxima porque: -- Me afasta porque: -``` - -### Exercício Medium — Mapa de resistência em uma mudança de TI - -**Objetivo:** praticar o olhar de gestão de mudança. - -Enunciado: - -Imagine que sua empresa (ou uma empresa fictícia) vai implantar um novo sistema importante (por exemplo, ERP, prontuário eletrônico, plataforma de dados). Liste: - -- Quem serão as principais pessoas/grupos impactados. -- Que receios eles podem ter. -- Três ações que a equipe de projeto pode tomar para reduzir essa resistência. - -Boilerplate sugerido: - -```markdown -# Mapa de resistência em uma mudança de TI - -## Grupos impactados -- Grupo 1: -- Grupo 2: - -## Possíveis receios -- Receio 1: -- Receio 2: - -## Ações da equipe de projeto para reduzir resistência -- Ação 1: -- Ação 2: -- Ação 3: -``` - -### Exercício Hard — Diário de proatividade por 7 dias - -**Objetivo:** treinar o hábito de escolher respostas proativas. - -Enunciado: - -Durante 7 dias, registre ao menos uma situação por dia em que você: - -- Percebeu uma reação inicial reativa (culpa externa, desânimo, reclamação). -- Parou, pensou, e escolheu uma ação proativa dentro do que estava sob seu controle. - -Boilerplate sugerido: - -```markdown -# Diário de proatividade — 7 dias - -## Dia 1 -- Situação: -- Reação inicial: -- Ação proativa escolhida: - -## Dia 2 -- Situação: -- Reação inicial: -- Ação proativa escolhida: - -... (até o Dia 7) -``` - ---- - - - - - diff --git a/content/planejamento-curso-carreira/aula-06-privilegios-etica-diversidade-gatilhos.md b/content/planejamento-curso-carreira/aula-06-privilegios-etica-diversidade-gatilhos.md deleted file mode 100644 index bcd0a19..0000000 --- a/content/planejamento-curso-carreira/aula-06-privilegios-etica-diversidade-gatilhos.md +++ /dev/null @@ -1,427 +0,0 @@ ---- -title: "Privilégios, ética, diversidade e gatilhos emocionais na carreira em tecnologia" -slug: "privilegios-etica-diversidade-gatilhos" -discipline: "planejamento-curso-carreira" -order: 6 -description: "Reflexões sobre privilégios de partida, ética, diversidade e gatilhos emocionais e como tudo isso impacta sua trajetória e atuação profissional em tecnologia." -reading_time: 25 -difficulty: "easy" -concepts: - - privilégios e ponto de partida diferentes - - comparação injusta de trajetórias - - ética, moral e responsabilidade individual - - diversidade como fonte de aprendizado e solução - - gatilhos emocionais e autoconhecimento - - postura profissional em contextos de conflito e diferença -prerequisites: - - bloco-entrada-estagio-atividades-complementares - - planejamento-carreira-swot - - curriculo-ats-experiencias - - linkedin-carreira-oportunidades - - habitos-mudanca-equipes-proatividade -learning_objectives: - - "Reconhecer que pessoas têm pontos de partida diferentes e por que isso torna comparações lineares injustas." - - "Compreender a distinção entre ética e moral e sua relevância no contexto de TI." - - "Valorizar a diversidade de origens, sotaques e experiências como fonte de aprendizado e inovação." - - "Identificar gatilhos emocionais pessoais e estratégias básicas para não reagir de forma destrutiva em situações profissionais." -exercises: - - question: "Por que o vídeo da 'corrida dos 100 dólares' mostra que não é justo comparar todas as pessoas como se começassem do mesmo ponto?" - answer: "Porque ele evidencia que alguns participantes têm vantagens de partida (família estruturada, segurança financeira, acesso a recursos) que os colocam à frente antes mesmo da corrida começar, enquanto outros precisam superar mais obstáculos; comparar apenas quem 'chegou primeiro' ignora essas diferenças estruturais." - hint: "Lembre das perguntas feitas antes dos participantes avançarem alguns passos." - - question: "Na definição apresentada pelo Cortella, qual a diferença entre ética e moral?" - answer: "Ética é o conjunto de valores e princípios que usamos para decidir entre 'quero', 'devo' e 'posso'; moral é a prática concreta desses princípios no dia a dia, isto é, como o comportamento expressa ou não a ética (por exemplo, ter como princípio não pegar o que não é seu vs. efetivamente não roubar)." - hint: "Pense em ética como concepção e moral como conduta." - - question: "O que significa dizer que pessoas são 'diferentes, mas não desiguais' no contexto de diversidade?" - answer: "Significa reconhecer que grupos e indivíduos têm culturas, sotaques, jeitos e histórias distintas (diferenças), mas que isso não os torna 'menos' em dignidade ou direitos; igualdade é um princípio ético, enquanto diferença é uma característica sociocultural." - hint: "Lembre da frase sobre homens e mulheres, nordestinos e sudestinos." -review_after_days: - - 7 - - 21 ---- - -## Visão Geral do Conceito - -Esta lição convida você a refletir sobre **pontos de partida diferentes**, **ética**, **diversidade** e **gatilhos emocionais**, conectando esses temas à sua trajetória em tecnologia. -Nem todo mundo começa do mesmo lugar; entender isso ajuda a parar de se comparar de maneira injusta e a olhar sua própria caminhada com mais respeito. Ao mesmo tempo, a forma como você lida com poder, diferenças e emoções no dia a dia molda sua reputação profissional. - -Os vídeos e exemplos da aula mostram como privilégios, arrogância, respeito, diferenças culturais e autocontrole aparecem em situações concretas — de uma corrida simbólica a negociações, lutas e interações no ambiente de trabalho. - -## Modelo Mental - -Alguns modelos mentais importantes: - -1. **Corrida com largadas diferentes** - - A vida não é uma corrida em que todos saem da mesma linha: algumas pessoas começam alguns passos à frente; outras, muito atrás. - - Ignorar isso e comparar apenas “quem chegou primeiro” apaga desigualdades reais de contexto. - -2. **Ética como bússola, moral como caminhada** - - Ética responde às perguntas “quero?”, “devo?”, “posso?” com base em valores e princípios. - - Moral é como essa bússola se traduz nas ações concretas: o que você realmente faz quando ninguém está olhando. - -3. **Diversidade como mudança de ponto de vista** - - Um “ponto de vista” é a vista a partir de um ponto: mudar o ponto (cultura, região, sotaque, história) muda a forma como o mundo é percebido. - - Conviver com diferenças tira você do centro do mundo e amplia repertórios. - -4. **Gatilho emocional como evento + reação automática** - - Certas situações ou falas apertam “botões” sensíveis (gatilhos) que levam a reações intensas. - - Autoconhecimento permite reconhecer esses gatilhos e criar um espaço entre estímulo e resposta para escolher melhor como agir. - -## Mecânica Central - -### Privilégios e comparação de trajetórias - -O vídeo da corrida simboliza: - -- Pessoas respondendo a perguntas sobre: - - Família. - - Segurança financeira. - - Oportunidades educacionais. - - Situações de risco e violência. -- Cada resposta positiva faz alguns avançarem antes da largada. - -Lições: - -- Alguns têm **“head start”**: mais apoio, mais segurança, mais recursos. -+- Outros precisam correr mais, com mais obstáculos, para chegar no mesmo lugar. -- Comparar apenas resultados finais (“quem ganhou?”) sem olhar **de onde cada um saiu** é injusto e desumanizador. - -Para você, isso significa: - -- Evitar comparar sua trajetória de forma rasa com colegas que tiveram contextos muito diferentes. -- Valorizar suas conquistas à luz das dificuldades que você precisou enfrentar. - -### Ética, moral e TI - -Com base na explicação do Cortella: - -- **Ética**: - - Conjunto de valores e princípios para decidir: - - `Quero` — desejo. - - `Devo` — é certo fazer? - - `Posso` — é permitido / viável? - -- **Moral**: - - Prática dessa ética: comportamento concreto. - - Exemplo: - - Princípio ético: “não pegar o que não é meu”. - - Comportamento moral: roubar ou não roubar. - -No contexto de TI: - -- Profissionais têm acesso a: - - Dados sensíveis (saúde, finanças, documentos). - - Lógicas de negócios, segredos de empresa. -- Por isso, é crítico: - - Ser visto como alguém que **não usa esse acesso para fins indevidos**. - - Agir com ética mesmo quando ninguém está olhando. - -### Diversidade: diferenças que enriquecem, não desigualdades - -Pontos trazidos pelo Cortella: - -- Pessoas de regiões, culturas, religiões, sotaques e histórias diferentes: - - Têm formas diversas de se relacionar, trabalhar, brincar, sentir o mundo. -- “Diferente” ≠ “desigual”: - - Igualdade é princípio ético (mesma dignidade de direitos). - - Diferença é fato sociocultural (modos distintos de viver e ver). - -No ambiente de trabalho e de estudo: - -- Tratar todos como “idênticos” apaga realidades diferentes. -- Ao mesmo tempo, tratar diferença como se justificasse desigualdade de respeito ou direitos é antiético. -- A diversidade, quando acolhida, amplia: - - Possibilidades de solução. - - Criatividade. - - Capacidade de atender públicos diferentes. - -### Gatilhos emocionais, arrogância e autoconhecimento - -Do vídeo da luta e da discussão sobre gatilhos: - -- A atleta arrogante: - - Usa humilhação, provocação, soberba para tentar enfraquecer o psicológico da adversária. - - Essa tática existe em muitos contextos (negociações, feedbacks, reuniões). - -- A atleta centrada: - - Trabalhou psicológico e autoconfiança com o treinador. - - Não internaliza os ataques. - - Foca na luta, não nas provocações. - -Gatilhos emocionais: - -- São pontos sensíveis (por exemplo: críticas à competência, aparência, passado, origem). -- Quando alguém toca num deles, a reação pode ser: - - Desproporcional. - - Automática. - - Atrapalhar decisões e relações. - -Estratégias mencionadas: - -- Perceber o momento em que o gatilho foi acionado. -- Criar uma pausa: - - Respirar. - - Beber água (literalmente, segurar água na boca). - - Pedir licença e sair brevemente da situação, se necessário. -- Voltar quando estiver em estado emocional mais estável. -- Exercícios de reflexão: - - Perguntar a si mesmo, em terceira pessoa: “por que [seu nome] está tão irritado agora?”. - - Mapear situações que se repetem e o que as desencadeia. - -## Uso Prático - -### Exemplo 1 — Comparando sua jornada acadêmica - -Situação: - -- Você olha um colega que parece “voar” nas disciplinas, com tudo em dia, e se desanima. - -Reflexão: - -- Qual é o contexto dele? - - Apoio familiar. - - Menos responsabilidades paralelas. - - Experiência prévia. -- Qual é o seu contexto? - - Trabalho, família, dificuldades financeiras, menos base anterior etc. - -Aplicação: - -- Em vez de concluir “sou ruim”, perguntar: - - “Dado o meu ponto de partida, o que eu já conquistei?” - - “Qual é o próximo passo possível para mim agora?” - -### Exemplo 2 — Ética em acesso a dados - -Situação: - -- Você trabalha (ou fará estágio) em uma empresa de saúde, banco ou e-commerce, com acesso a dados sensíveis. - -Decisões éticas: - -- Mesmo podendo tecnicamente: - - Espiar dados de pessoas conhecidas. - - Copiar bases. - - Vazar informações por interesse próprio. -- A ética diz: - - “Quero?” — curiosidade não é justificativa. - - “Devo?” — não, é antiético e muitas vezes ilegal. - - “Posso?” — mesmo podendo tecnicamente, escolho não fazer. - -### Exemplo 3 — Diversidade em equipes de tecnologia - -Equipe: - -- Pessoas de diferentes regiões (sotaques, backgrounds). -- Gente em transição de carreira. -- Gêneros, orientações, etnias variadas. - -Abordagem enriquecedora: - -- Ver a diferença como oportunidade: - - Um enxerga gargalos que outro não vê. - - Um traz experiências de outro setor/país. - - Isso tudo soma na criação de produtos mais inclusivos e robustos. - -## Erros Comuns - -- **Comparar trajetórias sem considerar contexto** - Olhar apenas resultados finais (“quem passou em tal processo”, “quem conseguiu tal vaga”) sem levar em conta privilégios e obstáculos iniciais. - -- **Tratar ética como algo opcional em TI** - Achar que, por saber “como tudo funciona por dentro”, pode-se brincar com dados, acessos e segredos sem consequências. - -- **Confundir diferença com desigualdade** - Usar diferenças culturais, regionais ou pessoais para justificar falta de respeito ou exclusão. - -- **Ignorar gatilhos emocionais** - Reagir sempre no automático e culpar o mundo sem olhar para padrões próprios de reação. - -## Visão Geral de Debugging - -Quando sentir injustiça, conflito ou forte reação emocional: - -- Você está se comparando com alguém **do mesmo ponto de partida** que o seu? -- Está avaliando se seu comportamento está alinhado com seus próprios valores éticos? -- Está reconhecendo e respeitando diferenças de contexto, cultura e história? -- Consegue identificar se algum **gatilho emocional** foi apertado? - -Passos de correção: - -1. **Recontextualizar comparações** - - Olhar menos para “quem está na frente” e mais para “de onde eu vim e para onde estou indo”. - -2. **Revisar decisões sob a lente ética** - - Perguntar: “quero?”, “devo?”, “posso?” antes de agir em situações sensíveis. - -3. **Abraçar a diversidade como fonte de aprendizado** - - Ouvir histórias diferentes das suas. - - Perguntar em vez de julgar de imediato. - -4. **Trabalhar seus gatilhos** - - Mapear situações que se repetem. - - Criar rotinas de pausa e reflexão antes de responder. - -
-Checklist de reflexão pessoal - -- [ ] Reconheço que não começo do mesmo ponto que todo mundo e ainda assim valorizo minhas conquistas. -- [ ] Tenho clareza de alguns princípios éticos que guiam minhas decisões (quero/devo/posso). -- [ ] Consigo citar pelo menos dois exemplos em que aprendi algo importante com alguém diferente de mim. -- [ ] Identifiquei pelo menos um gatilho emocional e estou experimentando estratégias para não reagir no automático. -
- -## Principais Pontos - -- As pessoas têm **privilégios e obstáculos diferentes**; comparar apenas resultados finais sem olhar o ponto de partida é injusto. -- **Ética** é a base de decisões responsáveis em TI, especialmente quando se lida com dados e informações sensíveis. -- **Diversidade** não é ameaça, é fonte de expansão de repertório e melhores soluções, desde que acompanhada de respeito e igualdade de dignidade. -- **Gatilhos emocionais e autoconhecimento** ajudam você a reagir com maturidade em conflitos, negociações e desafios do dia a dia. - -## Preparação para Prática - -Depois desta lição, você deve ser capaz de: - -- Refletir sobre seu próprio contexto de privilégios e desafios. -- Aplicar conceitos básicos de ética e moral em situações típicas de TI. -- Enxergar a diversidade como aliada em vez de inimiga. -- Identificar gatilhos emocionais e experimentar formas de resposta mais conscientes. - -No Laboratório de Prática, você vai registrar essas reflexões de forma estruturada. - -## Laboratório de Prática - -### Exercício Easy — Meu ponto de partida - -**Objetivo:** tomar consciência do próprio contexto. - -Enunciado: - -Escreva um pequeno texto sobre como você enxerga seu ponto de partida em relação à sua carreira em tecnologia, mencionando privilégios e desafios que reconhece. - -Boilerplate sugerido: - -```markdown -# Meu ponto de partida em tecnologia - -## Privilégios que eu reconheço -- Item 1: -- Item 2: - -## Desafios que eu enfrento -- Item 1: -- Item 2: - -## Como isso influencia minhas comparações com outras pessoas -- Reflexão: -``` - -### Exercício Medium — Ética em uma situação de dados sensíveis - -**Objetivo:** aplicar a lente ética (quero/devo/posso) a um caso de TI. - -Enunciado: - -Descreva uma situação hipotética em que você tenha acesso a dados sensíveis em um sistema (por exemplo, informações de saúde, finanças ou cadastros) e explique, usando “quero/devo/posso”, qual seria a decisão ética a tomar. - -Boilerplate sugerido: - -```markdown -# Situação de dados sensíveis - -## Contexto -- Descrição: - -## Análise ética -- Quero: -- Devo: -- Posso: - -## Decisão que eu tomaria e por quê -- Decisão: -- Justificativa: -``` - -### Exercício Hard — Diário de diferenças e aprendizados por 5 dias - -**Objetivo:** treinar olhar para a diversidade como fonte de aprendizado. - -Enunciado: - -Por 5 dias, registre situações em que você interagiu (ou observou) alguém diferente de você (origem, sotaque, idade, área, visão de mundo) e anote: - -- Qual era a diferença. -- O que inicialmente te causou estranhamento, desconforto ou curiosidade. -- O que você aprendeu ou poderia aprender daquela diferença. - -Boilerplate sugerido: - -```markdown -# Diário de diversidade — 5 dias - -## Dia 1 -- Diferença observada: -- Minha reação inicial: -- Aprendizado real ou potencial: - -## Dia 2 -- Diferença observada: -- Minha reação inicial: -- Aprendizado real ou potencial: - -... (até o Dia 5) -``` - ---- - - - - - diff --git a/content/planejamento-curso-carreira/aula-07-at-apresentacao-oratoria.md b/content/planejamento-curso-carreira/aula-07-at-apresentacao-oratoria.md deleted file mode 100644 index 2a7684a..0000000 --- a/content/planejamento-curso-carreira/aula-07-at-apresentacao-oratoria.md +++ /dev/null @@ -1,350 +0,0 @@ ---- -title: "Trabalho final (AT), apresentações e oratória: medo, preparo e voz" -slug: "at-apresentacao-oratoria" -discipline: "planejamento-curso-carreira" -order: 7 -description: "Como montar o AT a partir dos TPs, o que entregar na apresentação sobre você, como reduzir medo de falar em público e preparar slides e voz com método." -reading_time: 28 -difficulty: "medium" -concepts: - - consolidação do AT (TP1, TP2, TP3) e item de apresentação - - medo de apresentar e medo de rejeição - - medo real versus medo construído - - respiração e regulação emocional antes de falar - - roteiro (espinha dorsal) antes dos slides - - slides como apoio ao palestrante - - ensaio, tempo e teste técnico - - voz, entonação e presença em apresentação presencial - - HAIL e hábitos tóxicos de fala (pecados da fala) -prerequisites: - - bloco-entrada-estagio-atividades-complementares - - planejamento-carreira-swot - - curriculo-ats-experiencias - - linkedin-carreira-oportunidades - - habitos-mudanca-equipes-proatividade - - privilegios-etica-diversidade-gatilhos -learning_objectives: - - "Organizar o AT como um único documento ordenado com o conteúdo dos três TPs e o item extra de apresentação." - - "Reconhecer o vínculo entre desconforto com apresentações, julgamento percebido e medo de rejeição." - - "Aplicar preparação prática: roteiro, ensaio cronometrado, teste de mídia e desenho visual enxuto de slides." - - "Usar respiração lenta e consciente e cuidados com voz (volume, ritmo, entonação) em contexto de apresentação ou entrevista." -exercises: - - question: "Por que a professora destaca que apresentar 'sobre você mesmo' reduz um tipo clássico de medo em relação a apresentações técnicas?" - answer: "Porque o conteúdo está na sua zona de domínio: você fala da própria trajetória, interesses e percepções, sem a pressão de ser questionado como especialista absoluto em um tema externo que você domina menos. Isso não elimina nervosismo social, mas remove o medo de 'não saber o assunto' no sentido técnico." - hint: "Pense na diferença entre expor um tema que você estuda pouco e narrar sua própria história." - - question: "Qual é a função principal dos slides em uma boa apresentação, segundo a lógica da aula?" - answer: "Apoiar quem fala com palavras-chave e estrutura, mantendo o foco da audiência na pessoa que apresenta; quando o slide compete por atenção (excesso de texto, animação constante, desalinhamento com a fala), ele prejudica a comunicação em vez de ajudar." - hint: "Lembre do caso em que o palestrante projetou o arquivo errado." - - question: "O que significa separar medo 'real' de medo 'construído' no contexto do vídeo sobre falar em público?" - answer: "Medo real costuma estar ligado a risco objetivo à integridade (situações que justificam alerta fisiológico). Medo construído nasce de interpretações e antecipações — por exemplo, catastrofar julgamentos ou consequências futuras — e pode ser trabalhado com reenquadramento, prática e técnicas como respiração, pois a situação de falar não é, por si, equivalente a um perigo físico imediato." - hint: "Compare o exemplo do penhasco com o exemplo da apresentação." -review_after_days: - - 7 - - 21 ---- - -## Visão Geral do Conceito - -Esta lição amarra três frentes: **entrega do trabalho final (AT)**, **uma apresentação breve sobre você** (como prática de comunicação) e **ferramentas de oratória e preparo** (medo, respiração, estrutura de slides, voz e postura). O objetivo não é “decorar dicas motivacionais”, e sim **operacionalizar**: o que juntar no arquivo, o que enviar no ambiente virtual, como ensaiar e como desenhar apoio visual que não brigue com sua fala. - -No contexto do curso, a apresentação também prepara terreno para **apresentações recorrentes** ao longo da graduação e para situações profissionais em que você precisa expor trabalho, ideias ou resultados. - -## Modelo Mental - -1. **AT como integração, não como três entregas soltas** - As nove primeiras questões correspondem ao que você já produziu nos TPs; o AT pede um **documento único**, na **ordem do enunciado**, incorporando ajustes se houver retorno da correção. A nota relevante está nesse conjunto consolidado. - -2. **Medo de palco como medo social** - O desconforto comum não é “ódio ao PowerPoint”, e sim **exposição + julgamento percebido**. Muitas pessoas associam errar visivelmente a **perda de aceitação** do grupo. Nomear esse padrão (“o que exatamente eu temo que aconteça?”) reduz o tamanho vago do medo. - -3. **Slides = legendas do que você vai dizer, não um segundo palestrante** - Quem carrega a narrativa é **você**; o slide segura o fio condutor (palavras-chave, imagem, estrutura). Se o público precisa ler parágrafos ou decifrar animações enquanto você fala, a carga cognitiva **compete** com sua mensagem. - -4. **Voz e corpo como canais de credibilidade** - Antes de “performar”, o básico é ser **ouvido e compreendido**: volume adequado, ritmo, variação de entonação, pausas. Isso vale para sala de aula, reuniões híbridas e entrevistas. - -## Mecânica Central - -### O que é o AT e o item 10 - -- **Itens 1 a 9:** correspondem ao **TP1, TP2 e TP3**; a expectativa é **reunir** o que você já fez em **um documento completo**, seguindo a ordem do AT, em vez de anexar os TPs separadamente como entrega principal. -- **Item 10:** pequena **apresentação sobre você** (nome, curso, origem/moradia, caminho até TI, competências que ajudam na área, interesses de carreira — com flexibilidade no que você prefere compartilhar). -- **Entrega no ambiente do curso:** enviar o texto consolidado e os **slides** (por exemplo `PDF` exportado de `PPT` ou outra ferramenta). **Não** é obrigatório gravar vídeo da apresentação para entrega; a roda de apresentações ocorre **em aula**. -- **Arquivos:** pode ser **um único arquivo** ou **mais de um** (por exemplo texto e slides separados), conforme organização que fizer sentido para você. Incluir links no material (por exemplo portfólio ou `LinkedIn`) foi autorizado quando pertinente. - -> **Regra operacional:** trate prazos do `Moodle` como **fonte oficial** junto ao que a professora anuncia em aula; em caso de divergência, **confirme no ambiente** e nas orientações atualizadas da disciplina. - -### Medo, julgamento e rejeição - -A linha de raciocínio apresentada na aula liga: - -- erro ou gafe percebida → **medo de julgamento** → fantasia de **perda de lugar** no grupo (trabalho, turma, liderança); -- em muitos casos, o núcleo afetivo é **medo de rejeição**, não o slide em si. - -**Estratégia sugerida:** “desenhar” o medo — dar **nome** ao que você acha que vai acontecer se errar — e confrontar com evidências (uma apresentação acadêmica raramente tem o peso catastrófico que o cérebro antecipa no auge do nervosismo). - -### Medo “real” versus medo “construído” (vídeo da aula) - -- **Real:** associado a ameaça objetiva; o desconforto protege. -- **Construído:** alimentado por narrativas e antecipações; pode ser **retrabalhado** com prática, reenquadramento e técnicas corporais. - -Para apresentações, a ideia é **não tratar** o palco como penhasco: o desconforto existe, mas a **curva de aprendizado** melhora com repetição. - -### Respiração e “branco” mental - -Sob forte ansiedade, o relato da aula conecta **estresse** a dificuldade de **raciocínio fluido** (o famoso branco). A respiração **lenta e profunda pelo diafragma**, com **boca fechada** na inspiração, aparece como forma de reduzir a sensação de pânico e recuperar foco. - -> **Atenção:** evite **hiperventilação** (respiração muito rápida), que pode piorar o estado físico. - -### Estrutura de trabalho: espinha dorsal antes do software - -Ordem recomendada: - -1. **Roteiro no papel** — lista de tópicos e ordem lógica (o que precisa vir antes para a história fazer sentido). -2. **Decisão de recursos** — fotos, linha do tempo, infográfico, quantidade de slides compatível com o **tempo**. -3. **Montagem na ferramenta** — `PowerPoint`, `Google Slides`, `Prezi`, etc. - -Ferramentas com **muito movimento espacial** (caso do `Prezi` citado) podem gerar resultado visual forte, mas exigem **mais domínio** para não se perder durante a fala. - -### Pecados da fala e o acrônimo em inglês **HAIL** - -No trecho de oratória compartilhado na aula aparecem **hábitos a evitar** (fofoca, julgar, negatividade, reclamação, desculpas, exagero, dogmatismo/confundir fato com opinião) e quatro bases desejáveis que formam a palavra **HAIL**: - -- **Honesty** — honestidade. -- **Authenticity** — autenticidade. -- **Integrity** — integridade (coerência entre discurso e prática). -- **Love** — no sentido de **desejar o bem** ao interlocutor, não romanticismo; isso também modera a honestidade brutal desnecessária. - -### Caixa de ferramentas da voz (resumo) - -- **Registro** — onde a voz ressoa (nariz, garganta, peito); associações culturais ligam graves a autoridade, mas o ponto prático é **projetar sem gritar**. -- **Timbre** — “cor” da voz; treinável com orientação profissional, postura e respiração. -- **Entonação** — variação de melodia; falar em **uma nota só** cansa e obscurece ênfases. -- **Ritmo** — acelerar para energia, desacelerar para peso; monotonia de ritmo + pós-almoço derruba atenção. -- **Silêncio** — pausas curtas são legítimas e marcam ideias importantes. -- **Volume** — ajustar para o fundo da sala; **microfone** pode proteger suas cordas vocais e evitar que quem está na frente sinta **agressividade** de um grito contínuo. - -```mermaid -flowchart TD - A[Definir tempo e público] --> B[Escrever roteiro / tópicos] - B --> C[Estimar nº de slides compatível] - C --> D[Montar slides enxutos] - D --> E[Testar mídia e navegação] - E --> F[Ensaiar em voz alta com cronômetro] - F --> G{Ajustar conteúdo e ordem} - G -->|cortar ou acrescentar| B - G -->|ok| H[Respiração + voz aquecida] - H --> I[Apresentar] -``` - -## Uso Prático - -### Checklist antes de apresentar em sala - -1. **Tempo:** ensaio **real** com cronômetro; ajuste o volume de conteúdo (não confie em “vou falar rápido”). -2. **Tecnologia:** abrir arquivo no equipamento que será usado; testar vídeo, animações e links. -3. **Alinhamento:** confirmar que o arquivo projetado é **o deck certo** (a aula trouxe o exemplo do palestrante que exibia **outra** apresentação). -4. **Legibilidade:** fonte grande o suficiente para quem está longe; poucas palavras por slide. -5. **Visual:** poucas cores por slide; evitar animação constante que roube foco. -6. **Corpo e voz:** respiração lenta antes de entrar; no presencial, **olhar** para pessoas diferentes, mover-se com naturalidade; verificar se **quem está atrás ouve**. - -### Quando o tema é você mesmo - -Use o item 10 como **treino de exposição** com **baixo risco de conteúdo externo**: você não precisa “passar por especialista” em um assunto novo, precisa **estruturar** sua narrativa com clareza e respeito ao tempo. - -### Apresentações presenciais e microfone - -Se a sala for grande, **gritar** para chegar ao fundo pode: - -- cansar ou lesionar a voz; -- soar desproporcional para quem está perto. - -A alternativa citada é **amplificar** com microfone quando disponível, mantendo tom **firme** sem confundir volume com hostilidade. - -## Erros Comuns - -- **Confundir entrega com ensaio:** montar 20–30 slides para **cinco minutos** sem ensaiar — a aula usa o exemplo de apresentação interrompida por **descompasso** entre tempo e material. -- **Slide parecendo artigo:** blocos de texto que o público tenta ler enquanto você fala outra coisa. -- **Excesso de estímulo visual:** muitas cores, fontes e transições; o cérebro da audiência **não** prioriza sua voz automaticamente. -- **Deck errado ou desatualizado:** perda de atenção mesmo depois de corrigir o arquivo. -- **Hiperventilar** antes de entrar no lugar de **respirar devagar**. -- **Monotonia total de voz e ritmo** — especialmente após refeição, quando a audiência já está com menor energia. -- **Depender de ferramentas complexas** sem treinar navegação (`Prezi` e similares). - -## Visão Geral de Debugging - -Quando algo “sai ruim” em apresentação, separe **camadas**: - -1. **Conteúdo/tempo** — faltou ensaio ou o roteiro estava acima da capacidade do slot. -2. **Mídia** — arquivo, resolução, modo apresentador, cabo, permissões. -3. **Estado fisiológico** — taquicardia, branco; volte a **respiração lenta** e a **primeira frase** memorizada para destravar. -4. **Alinhamento com slides** — você está falando **A** e mostrando **B**? Pare, confirme o arquivo, realinhe ou ignore o slide problemático com honestidade (“vamos ao próximo ponto”). - -## Principais Pontos - -- AT: **um documento** com itens 1–9 na ordem; **corrigir** o que a correção dos TPs apontou; item 10 é **apresentação sobre você**. -- Entregar **slides** (`PDF` ou equivalente) no `Moodle` junto do texto; apresentação oral em **aula**. -- Medo de palco costuma carregar **medo de julgamento/rejeição**; nomear ajuda; **domínio do tema** aumenta segurança — no item 10, o tema é você. -- **Roteiro antes do slide**; slides **enxutos**; **ensaio cronometrado**; **teste técnico** antes. -- Respiração **lenta e profunda** para reduzir pico de ansiedade; evitar hiperventilação. -- Voz: **variação**, **ritmo**, **pausas**, volume adequado; microfone como alternativa ao grito. -- Evitar hábitos tóxicos de fala; buscar base **HAIL** na comunicação profissional. - -## Preparação para Prática - -Você deve sair desta lição capaz de: - -- Montar a **lista de entregáveis** do AT e o **roteiro** do item 10 sem depender só do improviso. -- Explicar **por que** slides minimalistas costumam funcionar melhor que murais de texto. -- Aplicar **um** ensaio completo com cronômetro e anotar cortes objetivos. -- Usar **três ciclos** de respiração diafragmática antes de uma fala ou entrevista. - -No Laboratório, você formaliza roteiro, checklist e autoavaliação de voz. - -## Laboratório de Prática - -### Exercício Easy — Roteiro em espinha dorsal do item 10 - -**Objetivo:** fixar a ordem do que você vai dizer antes de abrir qualquer software de slide. - -**Enunciado:** escreva, em tópicos curtos, **dois minutos** de fala sobre você seguindo a lógica da aula (quem é, de onde veio, por que TI, competências, rumo desejado). Marque explicitamente **qual frase abre** e **qual frase fecha**. - -**Boilerplate sugerido:** - -```markdown -# Roteiro — Apresentação sobre mim (ensaio ~2 min) - -## Abertura (primeira frase exata) -- TODO: escrever a primeira frase que você falará ao microfone - -## Corpo (tópicos na ordem) -1. TODO: nome e curso -2. TODO: origem / onde mora (o que quiser compartilhar) -3. TODO: como chegou em TI -4. TODO: competências que ajudam -5. TODO: interesse atual de carreira (área ou tipo de problema) - -## Fechamento (última frase exata) -- TODO: frase de encerramento (agradecimento ou convite a perguntas) - -## Tempo medido no ensaio -- TODO: minutos:segundos após ler em voz alta uma vez -``` - -### Exercício Medium — Checklist técnico e de slides - -**Objetivo:** reduzir falhas de projetor, arquivo errado e slides ilegíveis. - -**Enunciado:** monte um checklist pessoal com **pelo menos oito itens** cobrindo: arquivo correto, fonte mínima, número de slides vs tempo, animações, cores, backup (`PDF`), teste no equipamento e plano B se a mídia falhar. - -**Boilerplate sugerido:** - -```markdown -# Checklist — Antes de apresentar - -## Arquivo e equipamento -- [ ] TODO: confirmei que o arquivo aberto é o deck certo (nome + última versão) -- [ ] TODO: testei no projetor / modo apresentador -- [ ] TODO: plano B se a mídia falhar (ex.: versão PDF só com imagens-chave) - -## Design e legibilidade -- [ ] TODO: tamanho mínimo de fonte definido para o fundo da sala -- [ ] TODO: limitei cores e animações (critério: não competir com minha voz) - -## Conteúdo e tempo -- [ ] TODO: número de slides coerente com o tempo (ensaio cronometrado) -- [ ] TODO: cada slide tem só palavras-chave / uma ideia central - -## Outros -- [ ] TODO: item extra 1 -- [ ] TODO: item extra 2 -``` - -### Exercício Hard — Autoescuta e ajuste de voz (três minutos) - -**Objetivo:** perceber monotonia, velocidade e volume e planejar um ajuste concreto. - -**Enunciado:** grave **até 3 minutos** de você lendo seu roteiro ou improvisando sobre ele. Ouça com fones e responda: Onde fui **rápido demais**? Onde **monótono**? Onde o volume **cai**? Escolha **uma** mudança comportamental (por exemplo: “pausar 1 s após cada tópico” ou “subir volume nas duas primeiras frases”) e regrave **só o primeiro minuto** aplicando essa mudança. - -**Boilerplate sugerido:** - -```markdown -# Autoescuta — voz e ritmo - -## Links ou nomes dos arquivos de áudio -- Gravação 1 (antes): TODO -- Gravação 2 (1 min com ajuste): TODO - -## O que ouvi de problema na gravação 1 -- Ritmo: TODO -- Entonação: TODO -- Volume: TODO - -## Ajuste escolhido (uma única mudança clara) -- TODO: descrever o ajuste em uma frase objetiva - -## Resultado na gravação 2 (1 min) -- O que melhorou: TODO -- O que ainda precisaria de treino: TODO -``` - ---- - - - - diff --git a/content/projeto-bloco/aula-01-introducao-projeto-bloco-formacao.md b/content/projeto-bloco/aula-01-introducao-projeto-bloco-formacao.md deleted file mode 100644 index e87e51b..0000000 --- a/content/projeto-bloco/aula-01-introducao-projeto-bloco-formacao.md +++ /dev/null @@ -1,291 +0,0 @@ ---- -title: "Introdução ao Projeto de Bloco — Formação em Dados" -slug: "introducao-projeto-bloco-formacao" -discipline: "projeto-bloco" -order: 1 -description: "Visão geral do projeto de bloco que integra Python, SQL e visualização de dados em um pipeline único de dados." -reading_time: 18 -difficulty: "easy" -concepts: - - projeto de bloco - - integração de disciplinas - - pipeline de dados - - ferramentas de dados -prerequisites: [] -learning_objectives: - - "Entender o papel do projeto de bloco como eixo integrador das disciplinas de Python, SQL e visualização de dados." - - "Enxergar o fluxo completo de um pipeline de dados simples, da coleta até o dashboard." - - "Reconhecer as principais ferramentas utilizadas no projeto de bloco e quando cada uma entra no fluxo." -exercises: - - question: "Por que o projeto de bloco foca em integrar várias disciplinas em vez de aprofundar apenas em uma tecnologia isolada?" - answer: "Porque o objetivo é aproximar o aluno de cenários de projeto reais, em que problemas de dados exigem combinar Python, SQL e visualização de forma coordenada, e não apenas conhecer a sintaxe de uma ferramenta específica." - hint: "Pense em como os dados percorrem o caminho completo até virar valor de negócio." - - question: "Qual é a diferença entre aprender `Python` em uma disciplina isolada e usar `Python` dentro do projeto de bloco?" - answer: "Na disciplina isolada o foco está na linguagem em si (sintaxe, estruturas, funções); no projeto de bloco o foco é usar Python como ferramenta dentro de um fluxo de projeto, conectando leitura de arquivos, tratamento de dados e integração com banco ou dashboard." - hint: "Compare 'aprender a ferramenta' com 'usar a ferramenta em um projeto'." - - question: "Em um pipeline de dados simples, em que parte faz mais sentido usar `SQL` e em que parte faz mais sentido usar uma ferramenta de visualização como `Looker Studio`?" - answer: "SQL é mais adequado na etapa em que os dados já estão no banco e precisamos filtrar, juntar e agregar informações; ferramentas de visualização entram depois disso, para montar dashboards, gráficos e relatórios a partir das consultas prontas." - hint: "Pense em 'preparar dados' versus 'apresentar dados'." ---- - -## Visão Geral do Conceito - -O projeto de bloco é a trilha prática que conecta, em um único cenário, o que você aprende em `Python`, `SQL` e visualização de dados com ferramentas como `Looker Studio`. -Em vez de estudar cada disciplina de forma isolada, você aplica esses conhecimentos em um mini‑projeto de dados que se aproxima mais de um ambiente real de trabalho. - -O objetivo desta lição é alinhar expectativas sobre o projeto de bloco de **Fundamentos de Processamento de Dados**, mostrar como as peças se conectam e preparar você para aproveitar melhor as aulas práticas e os testes de performance. - -## Modelo Mental - -Pense no projeto de bloco como um **simulador leve de projeto de dados real**. -Você não está apenas “fazendo exercícios de linguagem”, mas treinando a forma de pensar de alguém que trabalha com dados em empresa: entender o problema, desenhar um fluxo, escolher ferramentas, escrever consultas e scripts simples e, por fim, entregar um resultado visual. - -Um bom modelo mental é enxergar o projeto como um **pipeline**: - -- **Entrada**: dados vindos de planilhas, sistemas ou arquivos. -- **Processamento**: limpeza, transformação e combinação desses dados com `Python` e `SQL`. -- **Saída**: dashboards e relatórios em `Looker Studio` ou ferramenta similar. - -Você é avaliado não só pela sintaxe, mas por conseguir **contar uma história de dados** coerente: onde os dados estão, o que precisa ser calculado e como isso aparece para o usuário final. - -## Mecânica Central - -Na prática, o projeto de bloco deste semestre funciona em torno de algumas ideias principais: - -- **Etapas graduais em vez de um único grande entregável**: em vez de construir um único projeto longo desde o primeiro dia, você passa por etapas menores, muitas vezes em sessões de *live coding* avaliadas (testes de performance). -- **Integração entre disciplinas**: cada disciplina aprofunda sua parte (por exemplo, sintaxe de `Python`), mas o projeto de bloco obriga a “ligar os pontos” entre elas. -- **Foco em fundamentos de dados**: entender **como dados fluem** entre arquivos, banco e dashboards é mais importante do que usar recursos avançados de qualquer ferramenta. - -Um fluxo típico que você irá construir pode ser representado assim: - -```mermaid -flowchart LR - A[Fontes de dados
planilha, CSV, sistema] --> B[Tratamento com Python
limpeza e transformação] - B --> C[Carregamento em banco
tabelas relacionais com SQL] - C --> D[Consultas SQL
filtros, junções, agregações] - D --> E[Dashboard
Looker Studio ou similar] -``` - -Esse diagrama resume o tipo de raciocínio que o projeto de bloco quer desenvolver: sempre saber **onde** o dado está, **o que** é feito com ele e **como** isso vira um resultado visual ou de negócio. - -## Uso Prático - -Ao longo do semestre, você deve usar o projeto de bloco para aproximar o conteúdo das disciplinas do seu contexto profissional: - -- Se você já trabalha em empresa, pode propor **cenários reais simplificados** (por exemplo, relatórios de vendas, atendimento ou estoque) como base para o seu mini‑projeto. -- Se ainda não trabalha, pode usar dados públicos ou exemplos próximos do dia a dia (por exemplo, acompanhamento de estudos, controle financeiro simples, registros de chamados). - -Alguns exemplos concretos de uso do projeto de bloco: - -- Carregar uma planilha de vendas com `Python`, limpar campos vazios e salvar em um arquivo pronto para importar em um banco de dados. -- Escrever consultas em `SQL` que calculam faturamento mensal, ticket médio e produtos mais vendidos. -- Montar um dashboard com gráfico de linha (eixo tempo) e tabela detalhada usando `Looker Studio` ou ferramenta similar, conectando diretamente às consultas. - -Ao usar o projeto de bloco dessa forma, você transforma cada aula teórica em um **passo a mais** em um projeto que poderia, com ajustes, ser mostrado em uma entrevista ou no seu portfólio. - -## Erros Comuns - -- **Tratar o projeto como um conjunto de exercícios desconectados** - Ficar apenas “resolvendo questões” sem amarrar tudo em um cenário único de dados faz você perder o principal ganho do projeto de bloco, que é pensar de ponta a ponta. - -- **Começar pela ferramenta em vez do problema** - Abrir o `Looker Studio` antes de entender quais métricas e filtros fazem sentido leva a dashboards bonitos mas pouco úteis. - -- **Ignorar a qualidade dos dados** - Escrever consultas e gráficos em cima de dados sujos (valores faltantes, tipos errados, datas inconsistentes) gera resultados enganosos. A etapa de limpeza com `Python` é parte essencial do projeto, não “detalhe”. - -- **Querer usar recursos avançados cedo demais** - Antes de pensar em *joins* muito complexos ou visualizações sofisticadas, garanta que o pipeline básico (carregar, limpar, salvar, consultar, visualizar) está funcionando. - -## Visão Geral de Debugging - -Quando algo der errado no seu projeto de bloco, pense em **debugging de fluxo**, não só em debugging de código: - -1. **Verifique a entrada** - Os arquivos ou tabelas têm os campos esperados? Os tipos de dados estão corretos (datas, números, textos)? -2. **Verifique o processamento intermediário** - O script em `Python` está realmente produzindo o arquivo ou tabela com as colunas que a próxima etapa espera? -3. **Verifique as consultas** - A consulta em `SQL` está filtrando demais ou de menos? Os *joins* estão corretos? -4. **Verifique a visualização** - O gráfico está usando a dimensão e a métrica certas? O filtro de data não está escondendo dados importantes? - -Uma boa prática é manter **amostras pequenas de dados** para testes rápidos e só depois aplicar o fluxo completo em volumes maiores. - -## Principais Pontos - -- O projeto de bloco é um **exercício guiado de projeto de dados**, não apenas um conjunto de exercícios de sintaxe. -- Você usa `Python`, `SQL` e visualização para construir um **pipeline de dados completo**, ainda que simples. -- A avaliação considera sua capacidade de **integrar disciplinas**, entender o problema e entregar algo que faria sentido em um contexto profissional. -- Debuggar o projeto significa seguir o caminho dos dados, da entrada até o dashboard, identificando em qual etapa o problema aparece. - -## Preparação para Prática - -Depois desta lição, você deve ser capaz de: - -- Descrever em poucas linhas o objetivo do projeto de bloco de fundamentos de dados. -- Rascunhar um pipeline simples que envolva arquivos, banco de dados e dashboard. -- Escolher um pequeno cenário de dados (real ou fictício) que possa servir de base para o seu mini‑projeto ao longo do semestre. - -No Laboratório de Prática a seguir, você irá começar a **desenhar e documentar** esse pipeline, preparando o terreno para as próximas lições mais técnicas de `Python` e `SQL`. - -## Laboratório de Prática - -### Exercício Easy — Desenhando seu primeiro pipeline - -Escolha um cenário simples de dados (por exemplo, vendas em uma loja pequena, controle de estudos, controle financeiro pessoal) e descreva, em código, as etapas principais do pipeline do seu projeto de bloco. - -Use o esqueleto abaixo e complete os trechos marcados com `TODO`: - -```python -SCENARIO_DESCRIPTION = "Controle simples de vendas em uma loja de bairro" - - -def describe_pipeline() -> list[str]: - """ - Retorna uma lista de etapas do pipeline de dados do seu projeto de bloco. - A lista deve seguir a ordem em que os dados passam pelo sistema. - """ - steps: list[str] = [] - - # TODO: adicionar pelo menos 4 etapas, por exemplo: - # - "Coletar dados de vendas em uma planilha CSV" - # - "Limpar e padronizar os dados com Python" - # - "Carregar dados em uma tabela no banco" - # - "Criar consultas SQL para métricas principais" - # - "Montar dashboard com gráficos e filtros" - - return steps - - -if __name__ == "__main__": - for step in describe_pipeline(): - print(f"- {step}") -``` - -O arquivo deve executar sem erro mesmo antes de você preencher os `TODO` (a saída pode ser vazia inicialmente). - -### Exercício Medium — Rascunhando consultas para o dashboard - -Imagine que os dados do seu cenário foram carregados em uma tabela `vendas`. -Escreva consultas em `SQL` que poderiam alimentar gráficos e tabelas do seu dashboard. - -Complete os trechos marcados como `TODO`: - -```sql --- TODO: completar a consulta para faturamento total por mês -SELECT - /* TODO: escolher coluna de data ou mês */ AS mes, - SUM(/* TODO: coluna de valor da venda */) AS faturamento_total -FROM vendas -/* TODO: se necessário, ajustar agrupamento ou filtros */ -GROUP BY mes; - --- TODO: consulta para produtos mais vendidos -SELECT - /* TODO: coluna de identificação do produto */ AS produto, - COUNT(*) AS quantidade_vendida -FROM vendas -GROUP BY produto -ORDER BY quantidade_vendida DESC; -``` - -O foco aqui é pensar **quais perguntas de negócio** seu dashboard precisa responder, traduzindo isso em consultas. - -### Exercício Hard — Conectando tudo em um mini‑roteiro de projeto - -Agora, crie um pequeno roteiro em `Python` que documente, de forma executável, as responsabilidades de cada etapa do seu projeto de bloco. - -Use o esqueleto abaixo: - -```python -from dataclasses import dataclass -from typing import Callable, List - - -@dataclass -class Step: - name: str - description: str - owner: str # ex.: "Python", "SQL", "Dashboard" - - -def build_project_steps() -> List[Step]: - steps: List[Step] = [] - - # TODO: adicionar pelo menos 5 etapas reais do seu projeto de bloco, - # distribuindo responsabilidades entre Python, SQL e dashboard. - # Exemplo: - # steps.append(Step( - # name="Importar planilha de vendas", - # description="Ler arquivo CSV bruto enviado pelo time comercial.", - # owner="Python", - # )) - - return steps - - -def summarize_pipeline(steps: List[Step]) -> None: - print("Resumo do pipeline do projeto de bloco:") - for i, step in enumerate(steps, start=1): - print(f"{i}. [{step.owner}] {step.name} - {step.description}") - - -if __name__ == "__main__": - pipeline_steps = build_project_steps() - summarize_pipeline(pipeline_steps) -``` - -Esse roteiro pode servir como documentação inicial do seu projeto para ser compartilhado com colegas, professores ou em uma futura apresentação. - - - - - diff --git a/content/projeto-bloco/aula-02-metodologias-projeto-de-dados.md b/content/projeto-bloco/aula-02-metodologias-projeto-de-dados.md deleted file mode 100644 index 28dd097..0000000 --- a/content/projeto-bloco/aula-02-metodologias-projeto-de-dados.md +++ /dev/null @@ -1,380 +0,0 @@ ---- -title: "Metodologias de Projeto para Dados — Tradicional vs Ágil" -slug: "metodologias-projeto-de-dados" -discipline: "projeto-bloco" -order: 2 -description: "Como metodologias tradicional e ágil organizam projetos de dados e o que isso muda no seu projeto de bloco." -reading_time: 22 -difficulty: "easy" -concepts: - - metodologia tradicional - - metodologia ágil - - manifesto ágil - - projeto de dados -prerequisites: - - introducao-projeto-bloco-formacao -learning_objectives: - - "Distinguir metodologia tradicional e metodologia ágil, com foco em projetos de dados." - - "Relacionar valores do manifesto ágil com situações reais do projeto de bloco." - - "Organizar um mini-projeto de dados em etapas ou sprints, com entregas parciais claras." -exercises: - - question: "Por que metodologias ágeis ganharam espaço em projetos de software e dados em relação ao modelo tradicional em cascata?" - answer: "Porque o modelo tradicional entrega valor só no final do projeto e reage mal a mudanças; já metodologias ágeis permitem entregas menores e frequentes, maior feedback do cliente e adaptação contínua, o que é crucial em ambientes de tecnologia e dados que mudam rápido." - hint: "Compare 'um grande pacote no fim' com 'várias entregas pequenas ao longo do caminho'." - - question: "Dê um exemplo de situação em um projeto de dados em que seguir estritamente o plano inicial pode ser pior do que adaptar o escopo." - answer: "Se, durante o projeto, percebe-se que uma métrica importante para o negócio foi esquecida, insistir no plano original pode gerar um dashboard que ninguém usa; adaptar o escopo para incluir essa métrica torna o projeto mais útil mesmo que isso exija revisar parte do plano." - hint: "Pense em mudanças de necessidade do cliente ou descoberta de novas fontes de dados." - - question: "Explique com suas palavras o valor 'software em funcionamento mais que documentação abrangente' do manifesto ágil aplicado a um dashboard de dados." - answer: "Significa priorizar ter um dashboard simples mas funcional, que já responda perguntas reais, em vez de gastar muito tempo em documentação extensa sobre algo que ainda não foi validado pelo usuário." - hint: "O que é mais útil para o cliente no dia a dia?" ---- - -## Visão Geral do Conceito - -Todo projeto profissional — inclusive projetos de dados — precisa de uma forma organizada de sair da ideia até a entrega de valor para o cliente. -As **metodologias de projeto** são justamente esse conjunto de práticas que definem como o time planeja, executa, acompanha e entrega o trabalho. - -Nesta lição, vamos comparar a **metodologia tradicional** (modelo em cascata) com a **metodologia ágil** e o `Manifesto Ágil`, ligando esses conceitos ao seu **projeto de bloco** e a projetos de dados no mercado. - -## Modelo Mental - -Um bom modelo mental para metodologias de projeto é pensar em **como o valor aparece para o cliente ao longo do tempo**: - -- Na **metodologia tradicional**, o projeto é como construir uma ponte: define-se tudo no início, segue-se uma sequência rígida de fases e só no fim o cliente “usa” a solução. -- Na **metodologia ágil**, o projeto é como manter e evoluir um aplicativo: o cliente recebe versões menores, frequentes, e o time ajusta o rumo a cada iteração. - -Em projetos de dados isso significa escolher entre: - -- Ter um **grande dashboard pronto só no final**, após meses de trabalho; ou -- Ter **relatórios menores e úteis desde cedo**, que vão ficando melhores a cada sprint. - -O projeto de bloco usa essa visão para que você aprenda a **trabalhar em ciclos curtos**, sempre entregando algo que funciona, em vez de acumular tudo para o último dia. - -## Mecânica Central - -### 1. Metodologia Tradicional (cascata) - -Na metodologia tradicional, o projeto passa por **fases sequenciais**, em geral com pouca volta atrás: - -- **Iniciação**: justificativa do projeto, objetivo, escopo alto nível. -- **Planejamento detalhado**: requisitos, cronograma, orçamento, riscos. -- **Execução**: desenvolvimento ou construção da solução. -- **Monitoramento e controle**: acompanhar prazos, custos, qualidade. -- **Encerramento**: entrega final, aceite do cliente, documentação. - -Em um projeto de dados isso pode significar: - -- Meses definindo requisitos de todos os relatórios. -- Um grande esforço único para modelar o banco, construir ETL e dashboards. -- Usuário só interagindo com a solução quando tudo “termina”. - -Esse modelo funciona melhor quando: - -- O problema é muito bem conhecido e **muda pouco**. -- As regras regulatórias exigem documentação rígida. -- O custo de refazer é altíssimo (por exemplo, infraestrutura física). - -### 2. Metodologia Ágil - -Metodologias ágeis (como `Scrum` e `Kanban`) nasceram da necessidade de entregar software mais rápido e com maior capacidade de adaptação. - -Algumas características centrais: - -- Trabalho organizado em **iterações curtas** (sprints) ou fluxo contínuo. -- Lista priorizada de itens de trabalho (`backlog`). -- Entregas frequentes de **incrementos funcionais** (por exemplo, um conjunto de relatórios já utilizáveis). -- Reuniões rápidas para alinhamento diário (`daily`). -- Revisões e retrospectivas para ajustar processo e prioridades. - -Em projetos de dados, isso se traduz, por exemplo, em: - -- Primeira sprint focada em conectar a fonte de dados e entregar um relatório simples. -- Sprints seguintes adicionando métricas novas, segmentações, telas extras. -- Ajustes de escopo baseados em feedback: “esse gráfico não ajuda; precisamos enxergar por região, não por produto”. - -### 3. Manifesto Ágil - -O `Manifesto Ágil` resume os valores que orientam as metodologias ágeis. -Ele não é um método em si, mas uma **bússola de princípios**. - -Os quatro valores principais podem ser lidos em linguagem de projetos de dados assim: - -- **Indivíduos e interações mais que processos e ferramentas** - Ter uma boa conversa com o time de negócio para entender a métrica correta é mais importante do que seguir cegamente um template de documentação. - -- **Software em funcionamento mais que documentação abrangente** - Um dashboard simples, mas que o time de vendas já usa, vale mais do que um documento extenso definindo requisitos de um dashboard que ainda não existe. - -- **Colaboração com o cliente mais que negociação de contratos** - É melhor ajustar o backlog junto com o cliente ao perceber que uma métrica não faz sentido do que insistir no escopo só porque “estava no contrato”. - -- **Responder a mudanças mais que seguir um plano** - Se o negócio muda (nova política comercial, novo produto), o pipeline de dados e os relatórios devem ser ajustados, mesmo que isso quebre o plano original. - -O manifesto não joga fora planos nem documentação, mas lembra que, em ambientes de tecnologia e dados, **flexibilidade** costuma gerar mais valor do que rigidez. - -### 4. Comparando os modelos em um projeto de dados - -O diagrama a seguir contrasta a visão de entrega de valor em cada abordagem: - -```mermaid -flowchart TD - subgraph Tradicional - T1[Levantamento completo\n de requisitos] --> T2[Projeto detalhado\n de dados e relatórios] - T2 --> T3[Implementação única\n ETL + banco + dashboards] - T3 --> T4[Entrega final\n grande pacote] - end - - subgraph Agil - A1[Backlog inicial\n de métricas e relatórios] --> A2[ Sprint 1\n primeiros relatórios funcionais ] - A2 --> A3[Revisão + ajuste\n de prioridades] - A3 --> A4[ Sprint 2\n novas métricas e melhorias ] - A4 --> A5[ Ciclo contínuo\n feedback e evolução ] - end -``` - -No projeto de bloco, mesmo em escala reduzida, a ideia é se aproximar do lado **ágil**: -ter entregas frequentes (por exemplo, versões do pipeline e do dashboard) em vez de um único “trabalho final” no fim do semestre. - -## Uso Prático - -### Aplicando no projeto de bloco - -No contexto do seu projeto de bloco: - -- Você pode tratar cada **etapa importante** (como conexão às fontes, construção de consultas, montagem do dashboard) como um **mini‑entregável**. -- Pode organizar o trabalho em **sprints de estudo**, por exemplo: - - Sprint 1: consolidar fontes de dados e limpar arquivos. - - Sprint 2: criar tabelas e consultas em `SQL`. - - Sprint 3: montar a primeira versão do dashboard. - - Sprint 4: refinar visualizações e adicionar filtros. - -Em um cenário de empresa, um time de dados poderia: - -- Rodar uma sprint de duas semanas cujo objetivo é **colocar em produção** um painel mínimo para acompanhar vendas diárias. -- Nas sprints seguintes, melhorar a modelagem, a performance das consultas e a qualidade visual, usando feedback real de quem usa o painel. - -### Exemplo concreto de backlog para projeto de dados - -Um backlog simplificado de projeto de dados poderia incluir itens como: - -- “Conectar ao banco transacional e extrair tabela de pedidos”. -- “Criar tabela de fatos de vendas agregadas por dia”. -- “Implementar métrica de ticket médio”. -- “Criar gráfico de linha de vendas por dia”. -- “Adicionar filtro por região no dashboard”. - -Cada item gera uma pequena entrega que já muda algo para o usuário final. -O seu projeto de bloco pode (e deve) ser pensado assim, mesmo que a avaliação venha em momentos específicos. - -## Erros Comuns - -- **Confundir agilidade com falta de planejamento** - Metodologias ágeis não significam “fazer tudo de qualquer jeito”. Ainda há planejamento, mas em ciclos menores, com espaço para revisão. - -- **Tentar aplicar 100% de um framework formal sem adaptação** - Forçar todas as cerimônias de `Scrum` em um projeto pequeno de estudo pode gerar mais burocracia do que aprendizado. É melhor aplicar os **princípios** (entregas frequentes, feedback, priorização) de forma leve. - -- **Não definir o que é “valor” para cada entrega** - Abrir o editor de dashboards, adicionar gráficos aleatórios e chamar isso de sprint é um erro. Cada entrega precisa responder: *qual nova pergunta de negócio este artefato ajuda a responder?* - -- **Usar metodologia apenas como etiqueta** - Dizer que “usa ágil” mas continuar trabalhando como cascata (planejamento fixo, nenhuma adaptação, entregas só no final) é muito comum e pouco efetivo. - -## Visão Geral de Debugging - -Debuggar problemas de projeto (não só de código) significa observar **onde o fluxo de trabalho está travando**: - -1. **Problemas de comunicação** - - O time não entende o que o cliente realmente precisa? - - As histórias de usuário ou itens de backlog estão mal escritos? -2. **Problemas de fatiamento de entregas** - - As tarefas são grandes demais e nunca “terminam”? - - É possível quebrar “Dashboard completo de vendas” em entregas menores? -3. **Problemas de priorização** - - O time está gastando tempo em detalhes de visual, enquanto faltam métricas essenciais? - - O que realmente muda o dia a dia do usuário? -4. **Problemas de adaptação** - - Mudanças de contexto (nova regra de negócio, fonte de dados indisponível) estão sendo reconhecidas e refletidas no plano? - - Ou o time continua seguindo o plano original por inércia? - -Ao perceber um “bug de processo”, use a mentalidade do manifesto ágil: -volte a conversar com as pessoas certas, simplifique a próxima entrega e ajuste o plano em vez de culpar somente o código. - -## Principais Pontos - -- Metodologias de projeto definem **como** o trabalho é organizado, não só **o que** será feito. -- O modelo **tradicional** foca em fases sequenciais e entrega grande ao final; o modelo **ágil** foca em iterações curtas com entregas frequentes. -- O `Manifesto Ágil` prioriza pessoas, software funcionando, colaboração e adaptação a mudanças. -- Em projetos de dados (e no projeto de bloco), trabalhar de forma ágil significa **entregar valor parcial cedo**, colher feedback e melhorar continuamente. - -## Preparação para Prática - -Depois desta lição, você deve ser capaz de: - -- Explicar para um colega as diferenças entre metodologia tradicional e ágil com exemplos de projetos de dados. -- Esboçar um backlog ou plano de sprints para o seu projeto de bloco, indicando o que será entregue em cada ciclo. -- Identificar sinais de que um projeto está “travado” por seguir um modelo inadequado e sugerir ajustes inspirados em princípios ágeis. - -No Laboratório de Prática a seguir, você vai praticar a **tradução de um projeto de dados em backlog e sprints**, e refletir sobre como isso se aplicaria a um time real de engenharia de dados. - -## Laboratório de Prática - -### Exercício Easy — Backlog mínimo de projeto de dados - -Crie uma lista de itens de backlog para um pequeno projeto de dashboard de vendas. -Use o esqueleto abaixo e complete os `TODO`: - -```python -from dataclasses import dataclass -from typing import List - - -@dataclass -class BacklogItem: - id: int - title: str - description: str - business_value: int # 1 a 5 - - -def build_minimum_backlog() -> List[BacklogItem]: - items: List[BacklogItem] = [] - - # TODO: adicionar pelo menos 4 itens reais de projeto de dados, - # com títulos e descrições claras. Exemplo: - # items.append(BacklogItem( - # id=1, - # title="Conectar ao banco de vendas", - # description="Configurar conexão de leitura ao banco transacional de pedidos.", - # business_value=5, - # )) - - return items - - -if __name__ == "__main__": - for item in build_minimum_backlog(): - print(f"[{item.business_value}] {item.title} - {item.description}") -``` - -O arquivo deve rodar sem erros antes de você preencher os `TODO` (a lista pode começar vazia). - -### Exercício Medium — Planejando sprints para o projeto de bloco - -Agora, distribua os itens de backlog em sprints curtas, planejando entregas funcionais a cada ciclo. - -```python -from dataclasses import dataclass -from typing import List - - -@dataclass -class Sprint: - name: str - goal: str - backlog_ids: List[int] - - -def plan_sprints() -> List[Sprint]: - sprints: List[Sprint] = [] - - # TODO: criar pelo menos 3 sprints (ex.: "Sprint 1 - Conectar e limpar dados"), - # cada uma com um objetivo claro e lista de IDs de backlog relacionados - # ao seu projeto de bloco. - - return sprints - - -if __name__ == "__main__": - for sprint in plan_sprints(): - print(f"{sprint.name}: {sprint.goal} -> itens {sprint.backlog_ids}") -``` - -O foco aqui é garantir que **cada sprint tenha um objetivo concreto e alcançável**, que produza algo utilizável ou testável. - -### Exercício Hard — Comparando fluxo tradicional e ágil no seu cenário - -Para consolidar, escreva um pequeno script que descreve como seria o **fluxo tradicional** e o **fluxo ágil** para o mesmo projeto de dados, destacando vantagens e riscos de cada abordagem. - -```python -from dataclasses import dataclass -from typing import List - - -@dataclass -class ApproachStep: - model: str # "tradicional" ou "agil" - order: int - description: str - risk: str - - -def compare_approaches() -> List[ApproachStep]: - steps: List[ApproachStep] = [] - - # TODO: descrever pelo menos 4 passos para cada modelo (tradicional e ágil) - # aplicados ao MESMO projeto de dados. - # Para cada passo, registrar também um risco típico daquele modelo. - - return steps - - -if __name__ == "__main__": - for step in compare_approaches(): - print(f"[{step.model}] {step.order} - {step.description} (risco: {step.risk})") -``` - -Esse exercício ajuda a treinar sua capacidade de **escolher a abordagem adequada** e explicar os impactos para pessoas não técnicas. - - - - - diff --git a/content/projeto-bloco/aula-03-pipeline-ferramentas-bancos-dados.md b/content/projeto-bloco/aula-03-pipeline-ferramentas-bancos-dados.md deleted file mode 100644 index 2e30685..0000000 --- a/content/projeto-bloco/aula-03-pipeline-ferramentas-bancos-dados.md +++ /dev/null @@ -1,386 +0,0 @@ ---- -title: "Pipeline de Dados, Ferramentas e Escolha de Bancos — do ETL ao Dashboard" -slug: "pipeline-ferramentas-bancos-dados" -discipline: "projeto-bloco" -order: 3 -description: "Como organizar o fluxo origem → processamento → destino, escolher bancos relacionais ou não-relacionais e usar ferramentas do dia a dia de projetos de dados." -reading_time: 24 -difficulty: "easy" -concepts: - - pipeline de dados - - ETL - - bancos relacionais - - bancos não relacionais - - ferramentas de banco e visualização -prerequisites: - - introducao-projeto-bloco-formacao - - metodologias-projeto-de-dados -learning_objectives: - - "Entender o ciclo origem → processamento → destino em um pipeline de dados simples." - - "Distinguir bancos de dados relacionais e não relacionais e quando usar cada um." - - "Reconhecer o papel de ferramentas como DBeaver, SQL Server Management Studio e Looker Studio dentro do fluxo de dados." -exercises: - - question: "Qual a diferença entre um banco de dados e a ferramenta que você usa para acessá-lo?" - answer: "O banco de dados é o sistema que armazena e gerencia os dados; a ferramenta (como DBeaver ou SQL Server Management Studio) é apenas o cliente que se conecta a esse sistema para criar consultas, visualizar e administrar os dados." - hint: "Pense na analogia do Instagram acessado pelo celular ou pelo navegador." - - question: "Em que situações faz mais sentido escolher um banco relacional para um projeto de dados?" - answer: "Quando os dados são bem estruturados, com entidades e relacionamentos definidos (por exemplo, sistemas de CRM ou ERP), e quando regras de integridade e consistência são importantes." - hint: "Repare se o domínio já tem 'tabelas' claras como clientes, pedidos, produtos." - - question: "Por que ferramentas de visualização como Looker Studio atuam mais no 'destino' do pipeline de dados e não no processamento pesado?" - answer: "Porque elas são otimizadas para consumir dados já preparados, montar dashboards e permitir exploração visual; transformações complexas e limpeza de dados são mais eficientes em camadas anteriores do pipeline (ETL, banco, scripts)." - hint: "Onde você prefere resolver problemas de tipo, nulos e junções: no gráfico ou antes dele?" ---- - -## Visão Geral do Conceito - -Projetos de dados profissionais raramente envolvem apenas “rodar uma query” ou “montar um gráfico”. -Na prática, você trabalha dentro de um **pipeline de dados**, que começa na **origem**, passa por **processamento / transformação** e termina em um **destino** onde alguém consome a informação — geralmente dashboards, relatórios ou APIs. - -Esta lição conecta a visão de projeto de bloco com o ciclo `origem → processamento → destino`, apresentando os principais tipos de bancos de dados e ferramentas que você verá no mercado (e que o professor comenta em aula): `SQL Server`, `PostgreSQL`, `MySQL`, `MongoDB`, `DBeaver`, `SQL Server Management Studio`, `pgAdmin`, `Looker Studio`, `Power BI` e outras. - -## Modelo Mental - -Use o seguinte modelo mental para qualquer projeto de dados, inclusive o seu projeto de bloco: - -- **Origem**: onde os dados nascem ou são coletados (sistemas transacionais, planilhas, arquivos CSV, APIs). -- **Processamento / ETL**: scripts em `Python`, processos de `ETL` (Extract–Transform–Load) e consultas em `SQL` que limpam, transformam e organizam os dados. -- **Destino**: onde as pessoas de negócio realmente enxergam valor — dashboards em `Looker Studio` ou `Power BI`, relatórios, exports para outros sistemas. - -Ferramentas são **meios** para navegar nesse pipeline, não o objetivo final. -Saber “clicar no Looker” ou “abrir o DBeaver” é pouco; o que importa é entender **em qual parte do fluxo você está** e qual problema de dados está resolvendo ali. - -## Mecânica Central - -### 1. Pipeline de dados e ETL - -O professor descreve o pipeline como um **ciclo de vida dos dados**: - -1. **Origem** — dados crus em sistemas, arquivos ou bancos. -2. **Processamento** — extração, transformação e carga (`ETL`). -3. **Destino** — camada de consumo (dashboards, relatórios, consultas analíticas). - -Visualmente: - -```mermaid -flowchart LR - O[Origem de dados
CSV, APIs, sistemas] --> E[Extract
copiar e ler dados] - E --> T[Transform
limpar, padronizar, juntar] - T --> L[Load
gravar em tabelas ou camadas analíticas] - L --> D[Destino
dashboards, relatórios, exports] -``` - -No projeto de bloco, você vai trabalhar principalmente entre **origem e processamento** (Python + SQL), preparando os dados para que ferramentas de visualização possam consumi-los com segurança. - -### 2. Bancos de dados relacionais - -Quando o professor fala de `SQL Server`, `PostgreSQL`, `MySQL`, `Oracle` e outros, ele está falando de **bancos relacionais**. - -Características principais: - -- Dados organizados em **tabelas** com linhas (registros) e colunas (campos). -- Relações explícitas entre tabelas (por exemplo, cliente–pedido–itens). -- Forte suporte a **integridade referencial** e **regras de negócio** no próprio banco. -- Linguagem padrão de consulta: `SQL`. - -Isso significa que, em geral, a mesma consulta SQL pode ser adaptada com poucas mudanças para diferentes bancos relacionais — e é exatamente isso que acontece quando você troca, por exemplo, de `PostgreSQL` para `MySQL`. - -### 3. Bancos de dados não relacionais - -Já sistemas como `MongoDB` são exemplos de bancos **não relacionais** (NoSQL), muitas vezes orientados a documentos: - -- Dados armazenados como **documentos** (geralmente em formato `JSON` ou similar). -- Estrutura de campos **mais flexível**: documentos da mesma coleção podem ter campos diferentes. -- Linguagem de consulta própria, em vez de SQL tradicional. - -Isso dá mais liberdade quando você não conhece de antemão todos os campos que podem aparecer, mas exige **disciplina de modelagem** para não virar caos. - -Um ponto importante que o professor enfatiza: -não existe um “meio termo mágico”; o que há é **analisar requisitos do projeto** e escolher a tecnologia que mais se encaixa. - -### 4. Critérios para escolher entre relacional e não relacional - -Algumas perguntas práticas: - -- Os dados são altamente estruturados e estáveis? - → Tendência a usar bancos relacionais. -- Você precisa de **joins complexos** e integridade forte entre entidades? - → Relacional costuma ser melhor. -- O esquema dos dados muda com frequência ou é imprevisível? - → Bancos não relacionais ganham espaço. -- O volume e a velocidade de escrita são extremos, com variedade grande de formatos? - → Muitas arquiteturas combinam camadas NoSQL com relacionais. - -No seu projeto de bloco inicial, faz sentido **começar com um banco relacional**. -Você aprende a pensar em tabelas, chaves e consultas SQL — a base para quase todos os projetos de dados. - -### 5. Ferramentas de banco de dados e visualização - -O professor também lista diversas **ferramentas de trabalho**: - -- Ferramentas de banco: - - `SQL Server Management Studio (SSMS)` para `SQL Server`. - - `pgAdmin` para `PostgreSQL`. - - `MySQL Workbench` para `MySQL`. - - Ferramentas multi-banco como `DBeaver`, que conseguem se conectar a vários SGBDs diferentes pela mesma interface. -- Ferramentas de visualização: - - `Looker Studio` (Google). - - `Power BI` (Microsoft). - - Outras como `Qlik`. - -Um ponto chave é **não confundir ferramenta com banco**. -Na analogia que surgiu em aula: pense no **Instagram** como se fosse o banco de dados, e no **aplicativo mobile** e no **navegador web** como ferramentas diferentes que acessam o mesmo conteúdo. - -## Uso Prático - -### No seu projeto de bloco - -Você pode estruturar o ambiente de trabalho assim: - -- Escolher **um banco relacional** local (por exemplo, `PostgreSQL` ou `MySQL`). -- Usar uma **ferramenta cliente** (como `DBeaver` ou a ferramenta nativa do banco) para criar tabelas e testar consultas. -- Escrever scripts em `Python` que: - - lêem dados de arquivos CSV; - - realizam limpeza e transformação básica; - - inserem ou atualizam dados no banco. -- Conectar uma ferramenta de visualização (como `Looker Studio`) a esse banco ou a *views* SQL para montar os dashboards. - -### Exemplo de mini pipeline para estudo - -Um pipeline de estudo alinhado ao projeto de bloco poderia ser: - -1. Baixar um CSV de vendas (por exemplo, do contexto de outra disciplina). -2. Escrever um script em `Python` que: - - converte datas para formato padrão; - - padroniza nomes de colunas; - - salva um novo CSV “limpo”. -3. Criar uma tabela `vendas` em um banco relacional e carregar o CSV limpo. -4. Escrever consultas em `SQL` para faturamento diário, ticket médio, produtos mais vendidos. -5. Conectar um dashboard em `Looker Studio` a essas consultas e montar gráficos simples. - -Esse tipo de prática é muito próximo de atividades reais em times de engenharia / análise de dados. - -## Erros Comuns - -- **Confundir banco com ferramenta de acesso** - Achar que “usa DBeaver” como se fosse usar um banco específico, quando na verdade ele é só o cliente. Isso atrapalha na hora de discutir arquitetura com outras pessoas. - -- **Confiar demais na ferramenta de visualização para processar dados crus** - Subir arquivos mal estruturados direto no dashboard e tentar fazer toda a limpeza ali costuma gerar relatórios lentos, difíceis de manter e cheios de inconsistências. - -- **Ignorar requisitos do projeto ao escolher o banco** - Escolher uma tecnologia só porque “é a que eu conheço” ou “é o que está na moda”, sem avaliar estrutura dos dados, volume, necessidade de relacionamentos e flexibilidade de esquema. - -- **Não pensar no pipeline de ponta a ponta** - Ficar preso só na query ou só no gráfico, sem considerar como aquela etapa conversa com a anterior e a seguinte. - -## Visão Geral de Debugging - -Quando o pipeline dá problema, pergunte-se: - -1. **Origem** - - O arquivo chegou com colunas diferentes das esperadas? - - A API mudou o formato do JSON? -2. **Processamento** - - O script em `Python` está tratando valores nulos, tipos incorretos e datas? - - A carga para o banco está falhando por causa de chave duplicada ou violação de constraint? -3. **Banco de dados** - - A modelagem está adequada? Tabelas e índices fazem sentido para as consultas que o dashboard roda? - - Há diferenças de dialeto SQL entre o que você escreveu e o SGBD que está usando? -4. **Destino (dashboard)** - - Os gráficos estão apontando para as tabelas/views corretas? - - Filtros e agregações do dashboard não estão contradizendo a lógica das consultas SQL? - -Debuggar bem é **seguir o caminho do dado**, verificando em que ponto ele deixa de fazer sentido. - -## Principais Pontos - -- Todo projeto de dados passa por um pipeline **origem → processamento → destino**, frequentemente implementado como um processo `ETL`. -- Bancos relacionais (SQL Server, PostgreSQL, MySQL, Oracle) e não relacionais (MongoDB, etc.) atendem necessidades diferentes, a serem escolhidas pelos **requisitos do projeto**. -- Ferramentas como `DBeaver` e `pgAdmin` são clientes; dashboards como `Looker Studio` e `Power BI` ficam na camada de **destino**. -- No projeto de bloco, você treina a construir esse fluxo de ponta a ponta em escala reduzida, pensando como alguém que trabalha diariamente com dados. - -## Preparação para Prática - -Após esta lição, você deve ser capaz de: - -- Desenhar o pipeline do seu projeto de bloco identificando origem, processamento e destino dos dados. -- Justificar a escolha de um banco relacional específico para esse projeto, com base nos requisitos. -- Selecionar um conjunto mínimo de ferramentas (cliente de banco + linguagem + visualização) e explicar o papel de cada uma. - -No Laboratório de Prática a seguir, você irá **documentar e simular esse pipeline em código**, reforçando a visão integrada entre Python, SQL, bancos e dashboards. - -## Laboratório de Prática - -### Exercício Easy — Mapeando origem, processamento e destino - -Descreva, em código, o pipeline do seu projeto de bloco indicando tecnologias usadas em cada etapa. - -```python -from dataclasses import dataclass -from typing import List - - -@dataclass -class PipelineStage: - name: str - role: str - technology: str - - -def build_pipeline() -> List[PipelineStage]: - stages: List[PipelineStage] = [] - - # TODO: adicionar pelo menos 4 etapas reais do seu projeto de bloco. - # Exemplo de ideias: - # - "Origem CSV de vendas" / "entrada" / "arquivo .csv" - # - "Limpeza inicial" / "processamento" / "Python" - # - "Carga em tabela relacional" / "armazenamento" / "PostgreSQL" - # - "Dashboard de vendas" / "consumo" / "Looker Studio" - - return stages - - -if __name__ == "__main__": - for stage in build_pipeline(): - print(f"{stage.name} -> ({stage.role}) via {stage.technology}") -``` - -### Exercício Medium — Comparando bancos e ferramentas para um cenário - -Escolha um cenário simples (por exemplo, monitorar tickets de suporte) e compare opções de bancos e ferramentas de acesso. - -```python -from dataclasses import dataclass -from typing import List - - -@dataclass -class TechOption: - kind: str # "banco" ou "ferramenta" - name: str # ex.: "PostgreSQL", "DBeaver" - strengths: str - risks: str - - -def evaluate_options() -> List[TechOption]: - options: List[TechOption] = [] - - # TODO: registrar pelo menos 3 bancos e 2 ferramentas de acesso, - # explicando pontos fortes e riscos em relação ao seu cenário. - - return options - - -if __name__ == "__main__": - for option in evaluate_options(): - print(f"[{option.kind}] {option.name} -> + {option.strengths} / - {option.risks}") -``` - -### Exercício Hard — Simulando um mini ETL em código - -Implemente um esqueleto de ETL em `Python` que documente claramente as etapas de extração, transformação e carga, mesmo que a implementação completa ainda seja `TODO`. - -```python -from dataclasses import dataclass -from typing import Any, List - - -@dataclass -class ETLStep: - name: str - description: str - - -def extract() -> List[Any]: - """Extrai dados da origem (simulado).""" - # TODO: simular leitura de dados (ex.: lista de dicionários representando linhas de um CSV) - return [] - - -def transform(rows: List[Any]) -> List[Any]: - """Transforma dados brutos em dados prontos para carga.""" - # TODO: aplicar pelo menos uma transformação simples (ex.: normalizar campos, remover nulos) - return rows - - -def load(rows: List[Any]) -> None: - """Carrega dados transformados em um destino (simulado).""" - # TODO: simular inserção em uma tabela (por exemplo, apenas imprimir as linhas transformadas) - for row in rows: - print(row) - - -def run_etl() -> None: - steps = [ - ETLStep("extract", "Ler dados da origem"), - ETLStep("transform", "Limpar e padronizar dados"), - ETLStep("load", "Carregar dados no destino"), - ] - print("Rodando ETL com etapas:") - for step in steps: - print(f"- {step.name}: {step.description}") - - raw = extract() - ready = transform(raw) - load(ready) - - -if __name__ == "__main__": - run_etl() -``` - -Esse exercício prepara você para, mais à frente, conectar esse esqueleto a dados reais do seu projeto de bloco. - - - - - diff --git a/content/projeto-bloco/aula-04-laboratorio-dados-python-sql.md b/content/projeto-bloco/aula-04-laboratorio-dados-python-sql.md deleted file mode 100644 index 918b127..0000000 --- a/content/projeto-bloco/aula-04-laboratorio-dados-python-sql.md +++ /dev/null @@ -1,378 +0,0 @@ ---- -title: "Montando seu Laboratório de Dados com Python e SQL" -slug: "laboratorio-dados-python-sql" -discipline: "projeto-bloco" -order: 4 -description: "Como configurar um ambiente mínimo de trabalho com bancos de dados, Python e IDEs para desenvolver o projeto de bloco." -reading_time: 22 -difficulty: "easy" -concepts: - - laboratorio de dados - - ambiente de desenvolvimento - - instalacao de python - - ferramentas para bancos de dados - - ides para python -prerequisites: - - introducao-projeto-bloco-formacao - - metodologias-projeto-de-dados - - pipeline-ferramentas-bancos-dados -learning_objectives: - - "Planejar um laboratório local de dados alinhado ao projeto de bloco." - - "Instalar e validar um ambiente mínimo com banco relacional, Python e ferramenta cliente." - - "Escolher e configurar uma IDE para desenvolvimento em Python integrada ao pipeline de dados." -exercises: - - question: "Por que é importante ter um laboratório próprio de dados, em vez de depender apenas de ambientes prontos da faculdade?" - answer: "Porque o laboratório próprio é onde você pratica sem restrições, simula cenários próximos do mercado, testa ferramentas diferentes e desenvolve autonomia para configurar ambientes — algo essencial no dia a dia de projetos reais." - hint: "Pense em como você aprenderia menos se só tivesse acesso ao computador de outra pessoa." - - question: "Qual a diferença entre instalar um banco de dados e instalar apenas uma ferramenta cliente como o DBeaver?" - answer: "Instalar o banco de dados significa ter o servidor que armazena e processa dados; instalar o DBeaver significa ter apenas uma interface que se conecta a um banco já existente — ele não é, por si só, um banco de dados." - hint: "Lembre da analogia Instagram (serviço) vs app/navegador (ferramenta)." - - question: "Por que é preciso instalar extensões específicas de Python no VS Code para trabalhar bem com a linguagem?" - answer: "Porque o VS Code é um editor genérico; sem extensões ele não sabe interpretar, executar nem oferecer recursos de linguagem para Python. As extensões adicionam suporte a execução, depuração, linting e IntelliSense para Python." - hint: "Pense em 'ensinar' o editor qual linguagem você vai usar." ---- - -## Visão Geral do Conceito - -Um **laboratório de dados** é o conjunto de ferramentas e configurações que você usa diariamente para estudar, testar ideias e desenvolver projetos com dados. -Ele inclui banco(s) de dados, linguagens de programação, IDEs, utilitários de linha de comando e pequenos projetos de teste. - -Nesta lição, vamos transformar as orientações do professor em um **roteiro concreto** para você montar seu próprio laboratório de dados com `Python` e `SQL`, alinhado ao projeto de bloco. - -## Modelo Mental - -Pense no laboratório como um **ambiente de treino profissional**: - -- É onde você erra à vontade, instala ferramentas, testa versões diferentes e quebra coisas sem medo. -- É também onde você começa a ter a mesma rotina de quem trabalha com dados: abrir IDE, conectar em banco, rodar scripts, ajustar queries, visualizar resultados. - -Um bom modelo mental é enxergar o laboratório em **camadas**: - -- **Sistema operacional**: Windows, Linux ou macOS. -- **Serviços de dados**: servidores de banco (por exemplo, `PostgreSQL`, `MySQL`, `SQL Server`). -- **Ferramentas cliente**: `DBeaver`, `SQL Server Management Studio`, `pgAdmin`, `MongoDB Compass`. -- **Linguagem de programação**: runtime de `Python`. -- **IDEs e editores**: `VS Code`, `PyCharm`, `Jupyter Notebook`. - -Essas camadas trabalham juntas para você conseguir testar o pipeline de dados visto nas lições anteriores. - -## Mecânica Central - -### 1. Arquitetura geral do laboratório - -Podemos representar o laboratório com o diagrama abaixo: - -```mermaid -flowchart TD - OS[Sistema operacional
Windows / Linux / macOS] - DB[Servidores de banco
PostgreSQL / MySQL / SQL Server] - CLIENT[Ferramentas cliente
DBeaver / SSMS / pgAdmin / Compass] - PY[Runtime Python
python.org] - IDE[IDEs / Editores
VS Code / PyCharm / Jupyter] - PROJECT[Projetos de estudo
projeto de bloco, scripts de teste] - - OS --> DB - OS --> PY - DB --> CLIENT - PY --> IDE - CLIENT --> PROJECT - IDE --> PROJECT -``` - -Seu projeto de bloco vive na camada `PROJECT`, mas depende de todas as outras para funcionar bem. - -### 2. Bancos de dados e ferramentas cliente - -Para o laboratório mínimo de dados, você precisa de **pelo menos um banco relacional** e **uma ferramenta de acesso**: - -- Exemplos de servidores: - - `PostgreSQL` - - `MySQL` - - `SQL Server` -- Exemplos de ferramentas cliente: - - `SQL Server Management Studio` (para SQL Server). - - `pgAdmin` (para PostgreSQL). - - `MySQL Workbench` (para MySQL). - - Ferramentas multi-banco como `DBeaver`. - -Lembre-se: - -- O **servidor** é o que guarda os dados. -- A **ferramenta cliente** é só a interface para visualizar e manipular esses dados. - -### 3. Instalação do runtime Python - -Para usar `Python` como ferramenta de dados, você precisa primeiro instalar o **runtime oficial**: - -1. Acessar o site oficial (`python.org`) e baixar a versão recomendada para o seu sistema. -2. Durante a instalação: - - Marcar a opção para adicionar Python ao `PATH` (quando disponível). - - Aceitar a instalação padrão (*Install Now*). -3. Testar em um terminal: - -```bash -python --version -``` - -Se aparecer uma versão (por exemplo, `Python 3.13.x`), o runtime está disponível para scripts e IDEs. - -### 4. IDEs e editores para Python - -O professor enfatiza três ferramentas principais: - -- `VS Code` - Editor leve e extensível, suporta muitas linguagens. Para funcionar bem com Python você precisa: - - instalar a extensão oficial de `Python`; - - selecionar o interpretador correto (por exemplo, `Python 3.13` instalado no sistema). - -- `PyCharm` - IDE focada em Python, com muitos recursos para projetos maiores (refatoração, testes, depuração avançada). Possui versão gratuita adequada para estudos. - -- `Jupyter Notebook` - Ambiente interativo baseado em blocos de código, ótimo para experimentos rápidos com dados e visualizações. - -Cada uma tem vantagens diferentes, mas todas precisam ser apontadas para o **mesmo runtime Python**, para que seus scripts funcionem de forma consistente. - -### 5. Organização mínima do laboratório - -Uma organização simples e funcional para o semestre pode ser: - -- Pasta `laboratorio-dados/` com: - - subpasta `bancos/` para dumps, scripts de criação de tabelas e anotações sobre conexões; - - subpasta `python/` para scripts e notebooks de teste; - - subpasta `projeto-bloco/` para o mini-projeto integrado do semestre. - -Dentro de `python/`, ter pelo menos: - -- Um script de teste de conexão ao banco. -- Um script simples de leitura de CSV e inserção em tabela. - -Isso garante que o laboratório não seja apenas “ferramentas instaladas”, mas um **espaço de prática contínua**. - -## Uso Prático - -### Checklist de laboratório mínimo para o projeto de bloco - -Para acompanhar bem as próximas aulas, é recomendável ter: - -- **1 banco relacional instalado** (por exemplo, PostgreSQL ou MySQL). -- **1 ferramenta cliente** para esse banco (pgAdmin, MySQL Workbench ou DBeaver). -- **Python 3.x instalado** via site oficial. -- **Pelo menos uma IDE** configurada com suporte a Python (VS Code com extensão de Python, PyCharm ou equivalente). -- **1 pasta de projeto** com scripts de teste, notebooks e anotações. - -### Exemplo de fluxos do dia a dia no laboratório - -Algumas atividades típicas que você pode praticar: - -- Criar uma tabela `clientes` no banco via ferramenta cliente e depois escrever um script em Python que insere registros de teste. -- Exportar dados de um sistema ou planilha para CSV e usar Python para limpar e carregar no banco. -- Ler dados do banco com Python, aplicar alguma transformação e salvar o resultado em outro formato (por exemplo, outro CSV ou JSON). - -Essas rotinas constroem o “músculo” que você vai usar tanto nas avaliações quanto em projetos reais. - -## Erros Comuns - -- **Instalar apenas a ferramenta cliente e achar que instalou o banco** - Abrir o DBeaver sem ter nenhum servidor de banco rodando leva à frustração: você precisa primeiro de um banco de dados para se conectar. - -- **Confiar apenas em ambientes online** - Praticar somente em notebooks de nuvem ou ambientes da faculdade impede que você aprenda a lidar com problemas reais de instalação, caminhos, permissões e versões. - -- **Misturar muitos bancos e ferramentas logo no início** - Instalar tudo (PostgreSQL, MySQL, SQL Server, MongoDB, etc.) de uma vez pode deixar o ambiente pesado e confuso. É melhor começar com **uma combinação simples e estável**. - -- **Não validar o ambiente com testes pequenos** - Achar que “está tudo ok” porque instalou não basta; é essencial rodar scripts mínimos para verificar se Python, banco e IDE estão conversando entre si. - -## Visão Geral de Debugging - -Quando algo não funciona no laboratório, siga esta ordem: - -1. **Verificar instalação** - - Comandos como `python --version` e a tela de conexão do banco funcionam? - - O serviço do banco está realmente em execução? -2. **Verificar configuração de IDE / cliente** - - O VS Code está usando o mesmo interpretador Python que você testou no terminal? - - As credenciais de conexão (host, porta, usuário, senha, banco) estão corretas? -3. **Verificar código de teste** - - O script de conexão está usando a biblioteca correta (`psycopg2`, `mysql-connector`, etc.)? - - Mensagens de erro estão sendo lidas com atenção? -4. **Verificar conflito de versões ou portas** - - Existe outro serviço usando a mesma porta do banco? - - As versões de drivers e clientes são compatíveis com a versão do servidor? - -Tratar o laboratório como um **projeto vivo** facilita muito sua vida quando, no futuro, você precisar subir ambientes mais complexos. - -## Principais Pontos - -- Um laboratório de dados bem montado é o **fundamento prático** do seu aprendizado em Python, SQL e projeto de bloco. -- É essencial entender a diferença entre **servidores de banco**, **ferramentas cliente** e **IDEs de linguagem**. -- Começar simples (um banco, uma ferramenta cliente, uma IDE) e validar com scripts pequenos é melhor do que tentar abraçar todas as tecnologias de uma vez. -- Cuidar do laboratório é parte do seu desenvolvimento profissional, não apenas um detalhe técnico. - -## Preparação para Prática - -Depois desta lição, você deve ser capaz de: - -- Descrever o laboratório mínimo necessário para o seu projeto de bloco. -- Escolher um conjunto pequeno mas completo de ferramentas para banco e Python. -- Validar o ambiente com testes simples de conexão e execução de código. - -No Laboratório de Prática a seguir, você vai **documentar e testar** o seu laboratório em código, criando uma base sólida para todas as atividades do semestre. - -## Laboratório de Prática - -### Exercício Easy — Inventário do laboratório - -Crie um pequeno inventário programático do seu laboratório atual. - -```python -from dataclasses import dataclass -from typing import List - - -@dataclass -class Tool: - category: str # "banco", "cliente", "ide", "outro" - name: str - version: str - status: str # "instalado", "planejado", "nao_instalado" - - -def list_lab_tools() -> List[Tool]: - tools: List[Tool] = [] - - # TODO: preencher com as ferramentas reais do seu ambiente, - # incluindo pelo menos um banco, uma ferramenta cliente e uma IDE. - - return tools - - -if __name__ == "__main__": - for tool in list_lab_tools(): - print(f"[{tool.category}] {tool.name} ({tool.version}) -> {tool.status}") -``` - -### Exercício Medium — Checklist automatizado de ambiente - -Implemente um script que faça pequenas verificações automáticas do seu laboratório (mesmo que de forma simulada). - -```python -import shutil -from typing import Dict - - -def check_python() -> bool: - """Verifica se o comando 'python' está disponível no PATH.""" - # TODO: usar shutil.which("python") para checar presença do executável - return False - - -def check_client_installed(executable_name: str) -> bool: - """Verifica se uma ferramenta cliente parece estar instalada.""" - # TODO: reutilizar shutil.which para nomes como "psql", "mysql" ou outros - return False - - -def run_checks() -> Dict[str, bool]: - results = {} - results["python"] = check_python() - # TODO: adicionar mais verificações relevantes ao seu laboratório - return results - - -if __name__ == "__main__": - for name, ok in run_checks().items(): - print(f"{name}: {'OK' if ok else 'FALHOU'}") -``` - -O objetivo é se acostumar a **verificar o ambiente de forma sistemática**, em vez de descobrir problemas só durante uma entrega importante. - -### Exercício Hard — Plano de evolução do laboratório - -Descreva, em código, um plano de evolução do seu laboratório ao longo do curso. - -```python -from dataclasses import dataclass -from typing import List - - -@dataclass -class LabMilestone: - semester: int - goal: str - required_tools: List[str] - - -def build_lab_roadmap() -> List[LabMilestone]: - roadmap: List[LabMilestone] = [] - - # TODO: definir pelo menos 3 marcos de evolução do laboratório, - # por exemplo: - # - semestre 1: banco relacional + Python + IDE - # - semestre 2: introdução a NoSQL e pipelines ETL mais complexos - # - semestre 3: orquestração, nuvem, etc. - - return roadmap - - -if __name__ == "__main__": - for milestone in build_lab_roadmap(): - print(f"Semestre {milestone.semester}: {milestone.goal} -> {', '.join(milestone.required_tools)}") -``` - -Esse exercício ajuda você a enxergar o laboratório como **algo que cresce junto com a sua carreira**, não apenas como uma configuração pontual. - - - - - diff --git a/content/projeto-bloco/aula-05-variaveis-python-projeto-dados.md b/content/projeto-bloco/aula-05-variaveis-python-projeto-dados.md deleted file mode 100644 index 7cddba6..0000000 --- a/content/projeto-bloco/aula-05-variaveis-python-projeto-dados.md +++ /dev/null @@ -1,388 +0,0 @@ ---- -title: "Variáveis em Python dentro de Projetos de Dados" -slug: "variaveis-python-projeto-dados" -discipline: "projeto-bloco" -order: 5 -description: "Como pensar e usar variáveis em Python em conjunto com tipos de dados de bancos relacionais dentro de um projeto de dados." -reading_time: 24 -difficulty: "easy" -concepts: - - variaveis - - tipos de dados - - atribuicao - - entrada-e-saida - - conversao-de-tipos -prerequisites: - - introducao-projeto-bloco-formacao - - metodologias-projeto-de-dados - - pipeline-ferramentas-bancos-dados - - laboratorio-dados-python-sql -learning_objectives: - - "Explicar o que é uma variável em Python e como ela se relaciona com tipos de dados." - - "Usar atribuição, entrada e conversão de tipos para construir pequenos fluxos de cálculo em Python." - - "Conectar variáveis em Python com tipos de campos em bancos relacionais em um cenário de projeto de dados." -exercises: - - question: "O que significa dizer que uma variável em Python é 'apenas um nome para um valor em memória'?" - answer: "Significa que a variável não é o valor em si, mas uma etiqueta que aponta para um espaço de memória onde o valor está armazenado; você pode mudar essa etiqueta para apontar para outro valor ao longo do programa." - hint: "Pense em etiquetas coladas em caixas diferentes ao longo do tempo." - - question: "Por que Python dispensa declaração explícita de tipo de variável, enquanto muitas outras linguagens exigem isso?" - answer: "Porque Python faz inferência de tipo em tempo de execução; ele analisa o valor atribuído e decide internamente qual tipo usar, enquanto linguagens mais formais precisam da informação de tipo antecipadamente para compilar ou validar o código." - hint: "Compare 'diga antes o tipo' com 'eu descubro a partir do valor'." - - question: "Qual a importância de entender que `input()` sempre retorna `str` quando se está lidando com números?" - answer: "Porque, se você não converter o retorno de `input()` para um tipo numérico (`int`, `float`), operações matemáticas vão falhar ou produzir resultados incorretos; é necessário converter explicitamente a string para o tipo numérico adequado." - hint: "Repare no que acontece se você tentar somar dois `input()` sem conversão." ---- - -## Visão Geral do Conceito - -Variáveis são um dos blocos fundamentais de qualquer código de dados. -Em Python — linguagem central do seu projeto de bloco — variáveis são **nomes** que apontam para valores em memória e podem mudar ao longo da execução do programa. - -Nesta lição, vamos organizar o que o professor trabalha em aula sobre **variáveis, tipos de dados, atribuição, entrada/saída e conversão** em Python, sempre conectando com a realidade de **projetos de dados** e com os tipos de campos em bancos relacionais. - -## Modelo Mental - -Um modelo mental simples é pensar em variáveis como **etiquetas em caixas de dados**: - -- Cada etiqueta (nome da variável) aponta para uma caixa (valor e tipo em memória). -- Você pode colar a mesma etiqueta em outra caixa (reatribuição), mudando o valor que ela representa. -- Em Python, você não precisa dizer de antemão que tipo de coisa vai dentro da caixa; o interpretador descobre isso a partir do valor colocado. - -Já em bancos relacionais, os **campos** das tabelas são mais rígidos: cada coluna tem um tipo fixo (inteiro, texto, data, etc.) que não muda linha a linha. - -Entender essas duas visões — flexibilidade de Python e rigidez do banco — é crucial para integrar código e dados com segurança. - -## Mecânica Central - -### 1. O que é uma variável - -Em termos práticos: - -- Uma variável é um **identificador** (nome) associado a um valor armazenado em memória. -- Em Python, você cria variáveis por **atribuição**: - -```python -contador = 0 -nome_cliente = "Ana" -taxa_juros = 0.035 -ativo = True -``` - -- Não há declaração de tipo explícita; o tipo é inferido a partir do valor. - -Em outras linguagens (e até em scripts SQL), muitas vezes é obrigatório declarar tipo antes de usar — por exemplo, `DECLARE @contador INT;`. - -### 2. Tipos básicos em Python - -Os tipos mais comuns discutidos na aula: - -- Numéricos: - - `int` — números inteiros. - - `float` — números com casas decimais. -- Textuais: - - `str` — cadeias de caracteres. -- Lógicos: - - `bool` — `True` ou `False`. - -Você pode usar a função `type()` para inspecionar o tipo: - -```python -valor = 10.5 -print(type(valor)) # -``` - -### 3. Atribuição e `case sensitive` - -Python usa o símbolo `=` para atribuição: - -```python -contador = 0 -contador = contador + 1 -``` - -E é **case sensitive**: - -- `contador`, `Contador` e `CONTADOR` são **variáveis diferentes**. -- Usar nomes coerentes e consistentes evita bugs difíceis de encontrar. - -### 4. Entrada e saída de dados - -Na aula, o professor destaca duas funções básicas: - -- `print()` — saída (mostra algo na tela). -- `input()` — entrada (lê do teclado). - -Ponto crítico: - -- `input()` **sempre retorna uma string**, mesmo que você digite apenas números. -- Para fazer cálculos, é preciso **converter**: - -```python -texto = input("Digite um número: ") -numero = float(texto) # ou int(texto) -resultado = numero * 2 -print(resultado) -``` - -### 5. Conversão de tipos - -As funções de conversão são usadas para transformar o valor de um tipo em outro: - -- `int()` — converte para inteiro (quando possível). -- `float()` — converte para número com casas decimais. -- `str()` — converte para texto. -- `bool()` — converte para verdadeiro/falso de acordo com regras específicas. - -Exemplo alinhado ao que aparece na aula: - -```python -valor_digitado = input("Digite o valor de uma compra: ") -valor = float(valor_digitado) # converte str -> float -desconto = 0.1 -valor_final = valor * (1 - desconto) -print(f"Valor final com desconto: {valor_final}") -``` - -### 6. Variáveis em Python vs campos em bancos relacionais - -No contexto de projeto de dados: - -- Em Python, variáveis podem mudar de tipo ao longo da execução (não é uma boa prática, mas é possível). -- No banco relacional, **cada coluna tem um tipo fixo**, escolhido no momento em que a tabela é criada (por exemplo, `INTEGER`, `DECIMAL(10,2)`, `VARCHAR(100)`, `DATE`). - -Para evitar problemas: - -- Garanta que os valores que você insere com Python **respeitam o tipo da coluna**. -- Use conversão explícita antes de enviar dados ao banco. - -Isso conecta a aula de variáveis à realidade dos pipelines de dados que você irá montar. - -### 7. Estruturas de controle e variáveis - -Variáveis não existem no vácuo; elas aparecem dentro de: - -- **Estruturas de decisão** (`if`, `elif`, `else`). -- **Laços de repetição** (`while`, `for`). - -O professor mostra menus, loops e comparações usando variáveis como: - -```python -opcao = 0 - -while opcao != 4: - print("1 - Cadastrar cliente") - print("2 - Listar clientes") - print("3 - Gerar relatório") - print("4 - Sair") - opcao = int(input("Escolha uma opção: ")) -``` - -Esses mesmos padrões são comuns em scripts de automação de dados e ferramentas internas. - -## Uso Prático - -### No projeto de bloco - -Você pode aplicar os conceitos de variáveis em Python para: - -- Ler parâmetros de filtros (por exemplo, período de datas) digitados pelo usuário e gerar consultas ou arquivos a partir disso. -- Controlar laços que percorrem linhas lidas de um CSV e preparam registros para inserção no banco. -- Modelar pequenos menus de linha de comando que disparam partes diferentes do pipeline de dados (carregar, limpar, gerar relatório). - -### Exemplo: script simples de cálculo de imposto - -Inspirando-se no exemplo real de reforma fiscal citado na aula, um script mínimo poderia: - -```python -aliquota = float(input("Digite a alíquota (%): ")) -base_calculo = float(input("Digite a base de cálculo: ")) - -imposto = base_calculo * (aliquota / 100) - -print(f"Imposto devido: {imposto}") -``` - -Aqui você usa: - -- entrada com `input()`; -- conversão com `float()`; -- variáveis nomeadas de forma clara; -- uma expressão simples que pode depois ser levada para um contexto mais amplo de projeto. - -## Erros Comuns - -- **Esquecer que `input()` retorna string** - Tentar fazer `resultado = input() * 2` e se surpreender com concatenação de texto ou erro. - -- **Misturar maiúsculas e minúsculas nos nomes** - Escrever `totalVendas` em um lugar e `totalvendas` em outro e achar que é a mesma variável, gerando erros de nome não definido. - -- **Reaproveitar a mesma variável para tipos muito diferentes** - Guardar uma string em `valor`, depois um número, depois um dicionário pode tornar o código confuso e difícil de depurar. - -- **Confiar demais na flexibilidade de Python** - Flexibilidade é útil, mas em projetos reais é importante manter disciplina de nomes, tipos esperados e conversões explícitas. - -## Visão Geral de Debugging - -Ao depurar problemas envolvendo variáveis: - -1. **Inspecione valores intermediários com `print()`** - - Verifique o que está sendo lido por `input()`. - - Confira o resultado de cada conversão. -2. **Verifique o tipo com `type()`** - - Se a operação falhar, pergunte-se “qual é o tipo desse valor agora?”. -3. **Reveja nomes de variáveis** - - Erros como `NameError` geralmente indicam digitação incorreta ou uso fora de escopo. -4. **Separe responsabilidades** - - Use variáveis diferentes para texto bruto, valores convertidos e resultados de cálculo. - -Esse estilo de debugging é o mesmo que você usará mais tarde ao lidar com dados reais de APIs, arquivos e bancos. - -## Principais Pontos - -- Variáveis em Python são **nomes para valores em memória**, com tipos inferidos a partir dos valores atribuídos. -- `input()` sempre retorna string; para cálculos, use conversão explícita (`int`, `float`, etc.). -- Em bancos relacionais, tipos de campos são fixos por coluna, exigindo que Python envie dados coerentes com esses tipos. -- Boas práticas com variáveis (nomes claros, atenção a maiúsculas/minúsculas, conversões visíveis) simplificam muito o debugging e a integração com bancos. - -## Preparação para Prática - -Depois desta lição, você deve ser capaz de: - -- Implementar pequenos scripts em Python que usem variáveis, tipos básicos, entrada/saída e conversão. -- Ler e entender exemplos de código com menus, laços e condições que usam variáveis de forma organizada. -- Planejar como essas variáveis vão alimentar consultas SQL e campos de tabelas em um projeto de dados. - -No Laboratório de Prática a seguir, você vai consolidar esse conhecimento em exercícios focados em **variáveis, tipos e conversões** simulando partes de um projeto de dados real. - -## Laboratório de Prática - -### Exercício Easy — Declarando e inspecionando variáveis - -Crie um pequeno script que declara variáveis representando partes de um registro de vendas e imprime seus tipos. - -```python -def describe_sale() -> None: - # TODO: definir variáveis como cliente, data_venda, valor_bruto, pago - # e imprimir o tipo de cada uma usando type(). - pass - - -if __name__ == "__main__": - describe_sale() -``` - -### Exercício Medium — Menu simples com entrada e conversão - -Implemente um menu de linha de comando que permita ao usuário escolher operações básicas sobre um valor numérico. - -```python -def show_menu() -> None: - print("1 - Dobrar valor") - print("2 - Calcular 10% de imposto") - print("3 - Converter para string formatada") - print("4 - Sair") - - -def main() -> None: - opcao = 0 - while opcao != 4: - show_menu() - # TODO: ler a opção com input(), converter para int - # TODO: se a opção for 1, 2 ou 3, pedir um valor numérico (input -> float) - # e aplicar a operação correspondente, usando variáveis claras. - # TODO: tratar opções inválidas com uma mensagem amigável. - break # remover depois de implementar o loop corretamente - - -if __name__ == "__main__": - main() -``` - -### Exercício Hard — Mapeando variáveis Python para campos de banco - -Desenhe, em código, o mapeamento entre variáveis Python e tipos de campos em uma tabela de banco de dados para um cenário simples (por exemplo, tabela `transacoes`). - -```python -from dataclasses import dataclass -from typing import List - - -@dataclass -class FieldMapping: - python_var: str - python_type: str - db_column: str - db_type: str - - -def build_mappings() -> List[FieldMapping]: - mappings: List[FieldMapping] = [] - - # TODO: adicionar pelo menos 4 mapeamentos coerentes, - # por exemplo: valor_transacao (float) -> VALOR DECIMAL(10,2) - - return mappings - - -if __name__ == "__main__": - for m in build_mappings(): - print(f"{m.python_var} ({m.python_type}) -> {m.db_column} ({m.db_type})") -``` - -Esse exercício fortalece a ponte entre código Python e modelagem de dados relacional. - - - - - diff --git a/content/projeto-bloco/aula-06-sql-consultas-integracao-python-excel.md b/content/projeto-bloco/aula-06-sql-consultas-integracao-python-excel.md deleted file mode 100644 index 46df06b..0000000 --- a/content/projeto-bloco/aula-06-sql-consultas-integracao-python-excel.md +++ /dev/null @@ -1,384 +0,0 @@ ---- -title: "Consultas SQL na Prática e Integração Python–Excel–SQL Server" -slug: "sql-consultas-integracao-python-excel" -discipline: "projeto-bloco" -order: 6 -description: "Como usar SQL para consultar dados em bancos relacionais e integrar Python, arquivos Excel e SQL Server em um pipeline simples." -reading_time: 26 -difficulty: "easy" -concepts: - - consultas-sql - - bancos-relacionais - - integracao-python-sql - - importacao-de-excel - - drivers-de-conexao -prerequisites: - - introducao-projeto-bloco-formacao - - metodologias-projeto-de-dados - - pipeline-ferramentas-bancos-dados - - laboratorio-dados-python-sql - - variaveis-python-projeto-dados -learning_objectives: - - "Escrever consultas SQL simples para ler e inspecionar dados em um banco relacional." - - "Entender o fluxo de importação de dados de Excel para SQL Server como parte de um pipeline de dados." - - "Configurar um script em Python que se conecta a um banco via driver apropriado e executa consultas básicas." -exercises: - - question: "Por que SQL continua sendo central em projetos de dados, mesmo com linguagens como Python fazendo muita lógica de negócio?" - answer: "Porque SQL é a linguagem padrão para trabalhar com bancos relacionais, onde grande parte dos dados de negócio está armazenada; ele é otimizado para filtrar, juntar e agregar grandes volumes de dados de forma declarativa e eficiente, algo que seria bem mais trabalhoso de reproduzir apenas em Python." - hint: "Pense em onde os dados 'moram' e em quem faz melhor filtros e agregações grandes." - - question: "Qual é o papel de um driver (como ODBC) na integração entre Python e SQL Server?" - answer: "O driver é a camada que sabe falar o protocolo do banco de dados; Python usa o driver para abrir conexões, enviar comandos SQL e receber resultados em um formato que o código consiga manipular." - hint: "Veja o driver como um 'intérprete' entre duas linguagens." - - question: "Por que faz sentido usar Excel como origem mas mover os dados para um banco SQL em projetos maiores?" - answer: "Porque Excel é conveniente para edição manual e compartilhamento, mas não é feito para consultas complexas, múltiplos usuários simultâneos ou grandes volumes; ao mover para um banco SQL, você ganha segurança, performance e capacidade de consulta estruturada." - hint: "Compare colaboração e escala de um arquivo .xlsx com as de um servidor de banco." ---- - -## Visão Geral do Conceito - -Depois de entender variáveis em Python, pipeline de dados e ferramentas de laboratório, o próximo passo natural é olhar com mais cuidado para a **linguagem SQL** e para a **integração Python–Excel–SQL Server** apresentada na aula. - -Esta lição mostra como: - -- usar SQL para consultar dados em bancos relacionais; -- pensar em Excel como **origem** de dados e SQL Server como **destino**; -- escrever um esqueleto de script em Python que conecta esses pontos usando um driver adequado. - -## Modelo Mental - -Use este modelo mental: - -- **Excel**: ponto de partida — planilhas enviadas por times de negócio, sistemas legados ou relatórios manuais. -- **Python**: cola do pipeline — lê arquivos, faz limpezas e transformações, chama bibliotecas e drivers. -- **Banco relacional (SQL Server, PostgreSQL, etc.)**: destino estruturado — onde consultas SQL rodam, relatórios são alimentados e visualizações se conectam. - -Você não precisa “escolher um lado”: a força está em **combinar** essas ferramentas na ordem certa. - -## Mecânica Central - -### 1. SQL em bancos relacionais - -SQL (Structured Query Language) é a linguagem usada para: - -- **criar** estrutura de dados (tabelas, índices); -- **consultar** dados (`SELECT`); -- **manipular** dados (`INSERT`, `UPDATE`, `DELETE`). - -Para a parte de consultas, uma estrutura típica é: - -```sql -SELECT - coluna1, - coluna2 -FROM nome_da_tabela -WHERE condicao -ORDER BY coluna1; -``` - -No contexto do projeto de bloco, o professor usa SQL Server com uma ferramenta como o **SQL Server Management Studio (SSMS)** para: - -- inspecionar tabelas; -- executar `SELECT` de teste (por exemplo, “TOP 1000 linhas”); -- validar se os dados que vieram do Excel chegaram corretamente. - -### 2. Importação de Excel para SQL Server - -O fluxo que a aula descreve pode ser resumido assim: - -1. **Origem**: planilha Excel com dados (por exemplo, vendas, clientes, produtos). -2. **Validação visual**: conferir se as colunas e tipos fazem sentido (datas, números, textos). -3. **Modelagem no banco**: - - criar uma tabela com colunas correspondentes (`INTEGER`, `DECIMAL`, `VARCHAR`, etc.); - - definir chaves primárias e índices se necessário. -4. **Carga**: - - via ferramentas do próprio SQL Server (assistentes de importação) ou - - via scripts em Python (o foco desta lição). - -Em projetos reais, essa etapa é parte de um **ETL**: extrair do Excel, transformar (limpar, converter) e carregar no banco. - -### 3. Integração Python–SQL Server com driver - -Para conectar Python a um banco SQL Server, você precisa de: - -- um **driver** instalado (por exemplo, ODBC do SQL Server); -- uma biblioteca Python que saiba usar esse driver (por exemplo, `pyodbc` ou similar); -- informações de conexão: servidor, banco, usuário, senha, driver. - -Um esqueleto simplificado baseado na ideia da aula: - -```python -import pyodbc - - -def get_connection() -> pyodbc.Connection: - connection_string = ( - "DRIVER={SQL Server};" - "SERVER=localhost;" - "DATABASE=projeto_bloco;" - "UID=projeto_bloco;" - "PWD=projeto123;" - ) - return pyodbc.connect(connection_string) -``` - -Depois da conexão, você pode executar consultas: - -```python -def listar_clientes(): - with get_connection() as conn: - with conn.cursor() as cur: - cur.execute("SELECT TOP 10 * FROM clientes;") - for row in cur.fetchall(): - print(row) -``` - -### 4. Fluxo Excel → Python → SQL - -O fluxo completo mostrado na aula pode ser visto assim: - -```mermaid -flowchart LR - EXCEL[Planilha Excel
.xlsx de origem] --> PY[Script Python
pandas / openpyxl] - PY --> CLEAN[Transformação
limpeza e padronização] - CLEAN --> DB[SQL Server
tabela destino] - DB --> SQL[Consultas SQL
SELECT, filtros, joins] -``` - -Passos típicos em Python: - -1. Ler o Excel (por exemplo, com `pandas.read_excel`). -2. Padronizar nomes de colunas, tipos, datas. -3. Abrir conexão com SQL Server via driver. -4. Inserir linhas na tabela destino (por `INSERT` em loop ou via recursos em lote). - -### 5. Vantagens de mover dados para SQL - -Alguns motivos práticos comentados e implícitos na aula: - -- **Escalabilidade**: bancos lidam melhor com grandes volumes do que planilhas. -- **Confiabilidade**: integridade referencial, tipos fortes, transações. -- **Consumo por múltiplos sistemas**: aplicações, relatórios e dashboards acessam dados centralizados. -- **Histórico e auditoria**: mais controle sobre alterações e acesso. - -Excel continua útil como ferramenta de entrada/edição, mas SQL é onde o dado “vive” no longo prazo. - -## Uso Prático - -### No seu laboratório - -Você pode reproduzir em escala reduzida: - -- Criar uma tabela `clientes` ou `vendas` em SQL Server (ou outro banco relacional). -- Montar uma planilha Excel com algumas linhas de teste. -- Escrever um script Python que: - - lê o Excel; - - converte campos com `int`, `float`, datas; - - insere dados na tabela com uma instrução `INSERT` parametrizada. - -### Exemplo simplificado de carga em Python - -```python -import pyodbc -import pandas as pd - - -def carregar_excel_para_sql(caminho_excel: str) -> None: - df = pd.read_excel(caminho_excel) - - with get_connection() as conn: - with conn.cursor() as cur: - for _, row in df.iterrows(): - cur.execute( - """ - INSERT INTO vendas (data, cliente, valor) - VALUES (?, ?, ?) - """, - row["data"], - row["cliente"], - float(row["valor"]), - ) - conn.commit() -``` - -Este tipo de código é exatamente o que muitos times usam para conectar planilhas a bancos em projetos reais. - -## Erros Comuns - -- **Ignorar tipos de coluna na criação da tabela** - Criar tudo como `VARCHAR` e só depois descobrir que filtros numéricos e de data estão lentos ou incorretos. - -- **Confiar que o Excel está sempre limpo** - Células vazias, formatos mistos (texto vs número) e datas inconsistentes podem quebrar a carga. É importante prever conversões e validações em Python. - -- **Montar strings SQL concatenando valores diretamente** - Isso causa problemas de segurança e bugs com aspas e caracteres especiais; prefira comandos parametrizados, como no exemplo com `?`. - -- **Esquecer de fechar conexão ou commitar transações** - Pode deixar dados parcialmente gravados ou travar recursos no servidor. - -## Visão Geral de Debugging - -Quando uma integração Python–Excel–SQL dá problema, siga esta ordem: - -1. **Excel** - - As colunas estão com nomes e formatos consistentes? - - Existem linhas em branco ou valores estranhos? -2. **Python (leitura)** - - O `DataFrame` ou lista de linhas está como esperado (use `print(df.head())`)? - - Conversões de tipo estão funcionando sem exceção? -3. **Conexão / driver** - - A string de conexão está correta (servidor, banco, usuário, senha, driver)? - - O driver está instalado no sistema? -4. **SQL** - - A tabela existe com os nomes de colunas corretos? - - Inserções funcionam se testadas manualmente no SSMS? - -Essa abordagem baseada no pipeline reduz o tempo gasto tentando adivinhar onde está o erro. - -## Principais Pontos - -- SQL é a linguagem de consulta central em bancos relacionais e se integra naturalmente com Python em projetos de dados. -- Excel é uma boa origem de dados, mas mover informações para um banco SQL traz ganhos de escalabilidade, segurança e capacidade de consulta. -- A integração Python–Excel–SQL Server depende de um driver e de uma string de conexão bem configurada. -- Entender o pipeline Excel → Python → SQL ajuda a depurar problemas e a projetar soluções mais robustas no projeto de bloco. - -## Preparação para Prática - -Depois desta lição, você deve ser capaz de: - -- Escrever consultas SQL simples para inspecionar tabelas. -- Descrever o fluxo de importação de dados de Excel para um banco relacional. -- Esboçar um script Python que conecta em um banco e executa comandos SQL. - -No Laboratório de Prática a seguir, você colocará isso em prática em cenários próximos do que aparece na aula. - -## Laboratório de Prática - -### Exercício Easy — Primeiras consultas em uma tabela de vendas - -Escreva algumas consultas SQL básicas para uma tabela `vendas`. - -```sql --- TODO: selecionar todas as colunas de vendas -SELECT - * -FROM - vendas; - --- TODO: selecionar apenas data, cliente e valor, ordenando da venda mais recente para a mais antiga - --- TODO: selecionar vendas acima de um certo valor (por exemplo, 100.00) -``` - -### Exercício Medium — Esqueleto de integração Excel → SQL com Python - -Implemente um esqueleto de função em Python que leia um arquivo Excel e prepare os dados para inserção em SQL. - -```python -from typing import Any, Iterable, List - - -def ler_excel(caminho: str) -> List[dict[str, Any]]: - """Lê um arquivo Excel e retorna linhas como dicionários.""" - # TODO: usar pandas ou outra biblioteca para ler o Excel - return [] - - -def inserir_em_sql(linhas: Iterable[dict[str, Any]]) -> None: - """Insere linhas em uma tabela SQL.""" - # TODO: abrir conexão com o banco e executar INSERT parametrizado - pass - - -def main() -> None: - caminho = "dados.xlsx" # TODO: ajustar para o caminho real - linhas = ler_excel(caminho) - inserir_em_sql(linhas) - - -if __name__ == "__main__": - main() -``` - -### Exercício Hard — Teste de conexão e consulta em Python - -Crie um script que testa a conexão com o banco e executa uma consulta de validação. - -```python -from typing import Any - - -def testar_conexao() -> bool: - """Tenta abrir uma conexão e retorna True se tiver sucesso.""" - # TODO: implementar usando a biblioteca de conexão escolhida (por exemplo, pyodbc) - return False - - -def consultar_amostra() -> list[tuple[Any, ...]]: - """Retorna algumas linhas de uma tabela para inspeção.""" - # TODO: abrir conexão, executar SELECT limitado e retornar as linhas - return [] - - -if __name__ == "__main__": - if testar_conexao(): - linhas = consultar_amostra() - for linha in linhas: - print(linha) - else: - print("Falha ao conectar ao banco.") -``` - -Esse exercício treina tanto a configuração do driver quanto o uso de SQL dentro de scripts Python. - - - - - diff --git a/content/projeto-bloco/aula-07-perfis-profissionais-case-consumidores.md b/content/projeto-bloco/aula-07-perfis-profissionais-case-consumidores.md deleted file mode 100644 index 4789610..0000000 --- a/content/projeto-bloco/aula-07-perfis-profissionais-case-consumidores.md +++ /dev/null @@ -1,301 +0,0 @@ ---- -title: "Perfis Profissionais em Dados e Desenvolvimento — e um Case Real de Classificação de Consumidores" -slug: "perfis-profissionais-case-consumidores" -discipline: "projeto-bloco" -order: 7 -description: "Visão dos perfis de mercado (DBA, engenheiro de dados, analista, arquiteto) e case real de projeto com CREATE TABLE, regras e views em SQL Server." -reading_time: 28 -difficulty: "easy" -concepts: - - perfis-profissionais-dados - - DBA - - engenheiro-de-dados - - analista-de-dados - - arquiteto-vs-engenheiro - - case-real-sql - - views-e-tabelas -prerequisites: - - introducao-projeto-bloco-formacao - - metodologias-projeto-de-dados - - pipeline-ferramentas-bancos-dados - - sql-consultas-integracao-python-excel -learning_objectives: - - "Distinguir perfis como DBA, engenheiro de dados, analista de dados, analista de sistemas e arquiteto de soluções." - - "Entender a diferença entre o papel do arquiteto (define estrutura e tecnologias) e do engenheiro (constrói e mantém)." - - "Relacionar o que se aprende em aula (CREATE TABLE, SELECT, views) com projetos reais de classificação e armazenamento de dados." -exercises: - - question: "Qual a diferença principal entre o papel de um Arquiteto de Dados/Soluções e o de um Engenheiro de Dados no contexto de um projeto?" - answer: "O arquiteto define como a solução será estruturada: quais tecnologias, como os dados fluem, quais componentes; o engenheiro implementa, desenvolve e mantém essa estrutura no dia a dia (pipelines, tabelas, testes, infraestrutura)." - hint: "Pense em quem desenha o plano e quem executa o plano." - - question: "Por que em um projeto real de classificação de consumidores em perfis faz sentido criar uma tabela por perfil em vez de uma única tabela com uma coluna 'perfil'?" - answer: "Depende do caso: tabelas separadas por perfil podem facilitar regras específicas, cargas independentes e consultas focadas; uma única tabela com coluna de perfil simplifica mudança de perfil e consultas globais. O case da aula usou tabelas por perfil para armazenar o resultado de regras distintas por tipo de consumidor." - hint: "Pense em volume, regras de negócio e quem consome os dados." - - question: "O professor diz que 'o diferencial no mercado são vocês', não só saber Python ou SQL. O que isso significa na prática?" - answer: "Significa que muitas pessoas aprendem as mesmas ferramentas; o que diferencia é a capacidade de entender o problema de negócio, traduzir regras em código, depurar e entregar valor — ou seja, compreensão do problema e da solução, não só sintaxe." - hint: "Compare 'saber escrever SELECT' com 'saber qual SELECT escrever e por quê'." ---- - -## Visão Geral do Conceito - -Esta lição faz um **review** do que foi visto no projeto de bloco e introduz dois eixos centrais: **perfis profissionais** em dados e desenvolvimento (DBA, engenheiro de dados, analista, arquiteto, etc.) e um **case real** de projeto em uma grande empresa — classificação de milhões de consumidores em perfis de consumo, com estrutura de tabelas, regras em SQL e views para consumo. - -O objetivo é alinhar expectativas: o que você estuda (CREATE TABLE, SELECT, variáveis, pipelines) é o mesmo tipo de atividade que se faz em projetos reais; o diferencial está em **como** você aplica esse conhecimento ao problema de negócio. - -## Modelo Mental - -- **Perfis em dados**: pense em uma **cadeia de valor dos dados**. Alguém desenha a arquitetura (arquiteto), alguém constrói e mantém o pipeline e o armazenamento (engenheiro de dados / DBA), alguém analisa e extrai insight (analista de dados). Cada perfil usa ferramentas e entregas diferentes. -- **Arquiteto vs Engenheiro**: o **arquiteto** decide “o quê” e “como em alto nível” (tecnologias, fluxos, padrões); o **engenheiro** implementa, testa, opera e mantém. Os dois trabalham juntos; um não substitui o outro. -- **Case de perfis de consumidor**: dados de consumidores entram → regras de negócio classificam cada um em um perfil → resultados são gravados em tabelas (uma por perfil ou estruturas equivalentes) → views e relatórios consomem esses dados. O mesmo CREATE TABLE e SELECT que você vê na disciplina aparecem nesse contexto. - -## Mecânica Central - -### 1. Perfis profissionais em foco - -O professor apresenta perfis típicos do mercado, com foco em **dados** e **desenvolvimento**: - -- **DBA (Database Administrator)** - Focado em **bancos de dados**: instalação, configuração, segurança, performance, backup. Não define a aplicação inteira; recebe demandas de projeto e garante que o banco atenda. Em empresas grandes, o servidor (máquina e SO) pode ser entregue por outra área (sustentação); o DBA instala e configura apenas o software de banco. - -- **Engenheiro de Dados** - Desenvolve e mantém o **pipeline de dados**: origem → processamento → destino. Preocupa-se com integração, ETL/ELT, infraestrutura onde os dados trafegam, testes e otimização. Tem visão de arquitetura mas atua na construção e operação. - -- **Arquiteto de Dados / Arquiteto de Soluções (nuvem)** - Define **como** os dados e a solução serão estruturados: quais bancos, quais ferramentas, como os componentes se conectam. Em projetos em nuvem, pode definir que um projeto usará, por exemplo, PostgreSQL em um serviço gerenciado; a equipe de nuvem configura o ambiente e o DBA/engenheiro cuida do banco em si. - -- **Analista de Dados** - **Analisa** os dados já disponíveis: dashboards, relatórios, Power BI, Excel. Encontra padrões, responde perguntas de negócio e entrega insight. Não cuida da infraestrutura do pipeline; consome o que arquitetos e engenheiros deixaram pronto. - -- **Analista de Sistemas** - Faz a **ponte entre negócio e tecnologia**: entende requisitos e processos e os traduz para a equipe de desenvolvimento. Pode atuar também em desenvolvimento quando conhece o negócio. - -- **Engenheiro de Software** - Projeta, desenvolve e mantém **sistemas e aplicações**: design de software, qualidade de código, testes, decisões de arquitetura junto com arquitetos. - -Esses perfis coexistem; projetos reais reúnem vários deles. ETL, pipeline e governança de dados aparecem como responsabilidades de perfis específicos (como na visita de consultores da IBM citada na aula: um focado em dados, outro em ETL, outros em governança). - -### 2. Arquiteto vs Engenheiro - -- **Arquiteto**: define a **estrutura** da solução — ex.: “vamos usar microserviços”, “os dados ficam aqui e ali”, “esta tecnologia para isso”. Não implementa o código do dia a dia. -- **Engenheiro**: **implementa** o que o arquiteto desenhou — cria os microserviços, os pipelines, as tabelas, os testes — e mantém tudo funcionando. - -Em dados: o arquiteto pode definir “vamos ter um data lake + camada analítica em SQL Server”; o engenheiro de dados constrói os jobs de ingestão, as tabelas e as views. - -### 3. Metodologia: quando ágil e quando tradicional - -- **Ágil**: entregas curtas (ex.: a cada 2–3 semanas), backlog, ajuste de rumo rápido. Comum em desenvolvimento de sistemas e em muitos projetos de dados. -- **Tradicional**: fases bem definidas, documentação formal, mais rigidez. Usado quando há **regulação ou criticidade** (segurança, área aeronáutica, certificações) que exigem rastreabilidade e processo metódico. - -Os dois coexistem no mercado; a escolha depende do tipo de projeto e da empresa. - -### 4. Case real: classificação de consumidores em perfis - -O professor descreve um projeto em uma **grande empresa de cosméticos** (maior do Brasil, com atuação internacional): - -- **Objetivo**: classificar **milhões de consumidores** em **perfis de consumo** (régua de perfis — na aula, 17 perfis). -- **Fluxo**: - 1. Processo (ex.: diário) lê dados de consumidores de uma base de origem. - 2. Regras de negócio classificam cada consumidor em um dos perfis. - 3. Resultados são gravados em **tabelas no SQL Server** — no case, uma tabela por perfil (perfil_1, perfil_2, …). - 4. **Views** reúnem dados de várias tabelas para facilitar consulta e visualização pelo usuário. - 5. Dados podem ser consumidos por relatórios, Excel ou ferramentas de BI. - -**Mensagem didática**: os comandos usados são os mesmos da disciplina — `CREATE TABLE`, `SELECT`, variáveis em blocos SQL, filtros e regras. A diferença está na escala, nas regras de negócio e na organização do projeto (prazo de meses, várias tabelas e views). Ou seja: **o que você aprende em aula é o que se usa no projeto**. - -### 5. Diagrama do case - -```mermaid -flowchart LR - ORIGEM[Base de consumidores
origem] --> PROC[Processo de
classificação] - PROC --> T1[Perfil 1] - PROC --> T2[Perfil 2] - PROC --> TN[Perfil N...] - T1 --> V[Views] - T2 --> V - TN --> V - V --> CONS[Consumo: relatórios,
Excel, BI] -``` - -### 6. Views e tabelas - -- **Tabelas**: armazenam os dados brutos ou resultado do processamento (ex.: cada perfil em sua tabela). -- **Views**: são “consultas nomeadas” que juntam ou filtram tabelas para o usuário ou para ferramentas. Para quem consome, pouco importa se veio de uma tabela ou de uma view; importa o **resultado** que atende ao negócio. - -### 7. Erro e aprendizado - -O professor reforça: **errar faz parte** do processo. Ajustar código, testar, corrigir e refinar é o dia a dia de qualquer perfil técnico. O importante é entender a **lógica** do problema; a ferramenta (Python, SQL, etc.) é um meio. O **diferencial no mercado** é a capacidade de compreender o problema e entregar solução, não só decorar sintaxe. - -## Uso Prático - -- Use esta lição para **se situar** nos perfis: com qual você mais se identifica (dados vs desenvolvimento, mais arquitetura vs mais mão na massa)? -- Ao estudar `CREATE TABLE` e `SELECT`, pense em cenários como o do case: “e se eu tivesse que armazenar o resultado de uma classificação em várias tabelas ou em uma tabela com coluna de perfil?”. -- Treine **traduzir regras de negócio** em estruturas de dados (tabelas, colunas) e em consultas (views, SELECTs). - -## Erros Comuns - -- **Confundir arquiteto com engenheiro** e achar que um “faz tudo” sozinho; na prática, os papéis se complementam. -- **Achar que projeto real é “outra língua”** — na base estão os mesmos conceitos (tabelas, consultas, tipos, pipelines). -- **Ter medo de errar** ou achar que só “experts” podem mexer em código; erro e iteração fazem parte do trabalho. - -## Visão Geral de Debugging - -No case de perfis: se um consumidor não aparece no perfil esperado, verificar (1) regras do processo de classificação, (2) dados de entrada, (3) destino (tabela correta?) e (4) view que agrega os dados. O mesmo raciocínio de “seguir o dado” das lições anteriores se aplica. - -## Principais Pontos - -- Existem vários **perfis** em dados e desenvolvimento (DBA, engenheiro de dados, analista de dados, arquiteto, analista de sistemas, engenheiro de software); cada um com foco e ferramentas diferentes. -- **Arquiteto** define estrutura e tecnologias; **engenheiro** implementa e mantém. -- **Case real**: classificação de consumidores em perfis → tabelas por perfil no SQL Server, regras em código, views para consumo — usando os mesmos conceitos (CREATE TABLE, SELECT, variáveis) vistos em aula. -- O **diferencial** é a compreensão do problema e da solução; ferramentas são meio, não fim. - -## Preparação para Prática - -Ao final desta lição, você deve conseguir: descrever brevemente cada perfil citado; explicar a diferença entre arquiteto e engenheiro; e relacionar um projeto hipotético de “classificação em categorias” com tabelas, regras e views (como no case). - -## Laboratório de Prática - -### Exercício Easy — Mapeando perfis e responsabilidades - -Modele em código (estrutura de dados) os perfis vistos na aula e uma linha de responsabilidade de cada um. - -```python -from dataclasses import dataclass -from typing import List - - -@dataclass -class Perfil: - nome: str - foco: str # "dados", "desenvolvimento", "negócio" - responsabilidade_principal: str - - -def listar_perfis() -> List[Perfil]: - perfis: List[Perfil] = [] - # TODO: adicionar pelo menos 4 perfis (ex.: DBA, Engenheiro de Dados, Analista de Dados, Arquiteto) - return perfis - - -if __name__ == "__main__": - for p in listar_perfis(): - print(f"{p.nome} ({p.foco}): {p.responsabilidade_principal}") -``` - -### Exercício Medium — Esqueleto de tabelas para perfis de consumidor - -Imagine um cenário com 3 perfis de consumidor (ex.: “alto valor”, “médio”, “baixo”). Defina em código (nomes de tabelas e colunas mínimas) a estrutura que você criaria no banco. - -```python -from dataclasses import dataclass -from typing import List - - -@dataclass -class TabelaPerfil: - nome_tabela: str - colunas: List[str] - - -def estruturas_perfis() -> List[TabelaPerfil]: - estruturas: List[TabelaPerfil] = [] - # TODO: definir 3 tabelas (ex.: perfil_alto_valor, perfil_medio, perfil_baixo) com colunas como id, consumidor_id, data_classificacao, etc. - return estruturas - - -if __name__ == "__main__": - for t in estruturas_perfis(): - print(f"{t.nome_tabela}: {', '.join(t.colunas)}") -``` - -### Exercício Hard — Arquiteto vs Engenheiro em um pipeline - -Descreva, em texto estruturado (dataclass ou dict), o que seria **decisão de arquiteto** e o que seria **tarefa de engenheiro** em um projeto de pipeline “Excel → processamento → SQL Server → dashboard”. - -```python -from dataclasses import dataclass -from typing import List - - -@dataclass -class DecisaoArquiteto: - decisao: str - justificativa: str - - -@dataclass -class TarefaEngenheiro: - tarefa: str - artefato_entregue: str - - -def decisoes_arquiteto() -> List[DecisaoArquiteto]: - # TODO: listar 3 decisões que o arquiteto tomaria (ex.: usar SQL Server como destino, ter uma camada de staging) - return [] - - -def tarefas_engenheiro() -> List[TarefaEngenheiro]: - # TODO: listar 3 tarefas que o engenheiro executaria (ex.: criar tabelas de staging, script de carga Python) - return [] - - -if __name__ == "__main__": - print("Arquiteto:") - for d in decisoes_arquiteto(): - print(f" - {d.decisao}: {d.justificativa}") - print("Engenheiro:") - for t in tarefas_engenheiro(): - print(f" - {t.tarefa} -> {t.artefato_entregue}") -``` - - - - diff --git a/content/projeto-bloco/aula-08-python-jupyter-crud-bancos-relacionais.md b/content/projeto-bloco/aula-08-python-jupyter-crud-bancos-relacionais.md deleted file mode 100644 index 8c064d1..0000000 --- a/content/projeto-bloco/aula-08-python-jupyter-crud-bancos-relacionais.md +++ /dev/null @@ -1,376 +0,0 @@ ---- -title: "Python, Jupyter e CRUD em PostgreSQL, MySQL e SQL Server" -slug: "python-jupyter-crud-bancos-relacionais" -discipline: "projeto-bloco" -order: 8 -description: "Montagem de ambiente, requisitos de projeto, conectores pip, Jupyter Notebook e CRUD completo via Python em três bancos relacionais." -reading_time: 32 -difficulty: "medium" -concepts: - - requisitos-de-projeto - - CRUD - - pip - - jupyter-notebook - - drivers-e-conectores - - pyodbc - - mysql-connector-python - - psycopg2 - - transacao-commit - - versoes-estaveis -prerequisites: - - laboratorio-dados-python-sql - - perfis-profissionais-case-consumidores -learning_objectives: - - "Definir requisitos mínimos de um mini-projeto de integração Python–banco de dados." - - "Instalar e fixar versões estáveis de Python, bibliotecas e servidores de banco para laboratório." - - "Explicar por que cada SGBD exige driver no sistema e biblioteca no Python." - - "Executar CRUD (CREATE TABLE, INSERT, SELECT, UPDATE, DELETE) a partir de um notebook com confirmação de escrita (commit)." -exercises: - - question: "Por que o Python, sozinho, não 'enxerga' um banco PostgreSQL ou SQL Server instalado na mesma máquina?" - answer: "O interpretador Python precisa de uma biblioteca cliente (e, em geral, drivers ODBC/JDBC nativos) que saiba falar o protocolo do banco. Instalar só o servidor não instala o conector no seu código." - hint: "Separe 'servidor de banco' de 'biblioteca no ambiente Python'." - - question: "O que significa CRUD e como ele se relaciona com comandos SQL típicos?" - answer: "CRUD é Create, Read, Update, Delete — em SQL costuma mapear para INSERT/CREATE, SELECT, UPDATE e DELETE (às vezes CREATE TABLE entra na parte de estrutura, não só de linha)." - hint: "Associe cada letra de CRUD a um verbo SQL." - - question: "Por que a aula insiste em não atualizar à toa Python e bibliotecas no meio de um projeto?" - answer: "Atualizar pode mudar comportamento, deprecar funções e quebrar dependências; em time e em produção costuma-se fixar versões e só subir mudança após testes em dev/homologação." - hint: "Pense em reprodutibilidade e pipelines de deploy." ---- - -## Visão Geral do Conceito - -Esta aula fecha o ciclo **“entender o projeto → montar o laboratório → integrar Python a dados”**: a partir de **requisitos** explícitos (integração com bancos, interface de execução passo a passo, CRUD), você configura **Python estável**, **servidores de banco** (PostgreSQL, MySQL, SQL Server), **Jupyter Notebook** e **bibliotecas de conexão** via `pip`. O objetivo não é decorar cada linha de SQL, e sim ter um **mapa repetível**: instalar driver/conector, abrir conexão, executar comandos, **confirmar escrita** com `commit` quando necessário, validar no cliente SQL (pgAdmin, DBeaver, SSMS). - -O fio condutor do **Projeto de Bloco — Formação** (consultas SQL, visualização, Python introdutório e avançado) encontra aqui a prática de **ligar linguagem de aplicação a SGBD relacional**, como em times de dados e backend. - -## Modelo Mental - -- **Projeto = requisitos + ambiente + entrega**: antes do código, define-se o que deve existir (quais bancos, que tipo de interface, quais operações). Isso espelha o **Projeto Bloco Metodologias** (temporário, objetivo claro, recursos definidos) e pode seguir fluxo mais **tradicional** (fases e documentos) ou **ágil** (iterações), conforme o contexto citado nas aulas anteriores. -- **Duas pontes, não uma**: (1) o **servidor** do banco escuta em uma porta; (2) o **driver** (no SO) e a **biblioteca** (no Python) traduzem chamadas da sua aplicação para o protocolo do banco. Sem a biblioteca no Python, a integração não existe. -- **CRUD como contrato**: para uma entidade (ex.: registro de versão de job, produto, cliente de teste), você precisa **criar estrutura/linha**, **ler**, **atualizar** e **apagar** — o mesmo roteiro que uma tela de sistema ou um script de manutenção faria. -- **Notebook = laboratório**: células executam **passo a passo**, o que ajuda a depurar conexão, SQL e ordem de operações (criar antes de inserir, etc.). - -```mermaid -flowchart TD - REQ[Requisitos do mini-projeto] --> ENV[Python + SGBDs + Jupyter] - ENV --> PIP["pip install conectores"] - PIP --> NB[Notebook: import + conexão] - NB --> CRUD[CREATE / INSERT / SELECT / UPDATE / DELETE] - CRUD --> VAL{Escrita ok?} - VAL -->|sim| COMMIT[commit / fechar cursor] - VAL -->|não| DBG[Rever SQL, permissões, transação] - COMM --> GUI[Conferir no cliente: pgAdmin / DBeaver / SSMS] -``` - -## Mecânica Central - -### 1. Recapitulação do case e do papel do analista-desenvolvedor - -A aula retoma o **projeto real** (SQL Server, regras vindas de planilha, semanas entendendo negócio antes de codar): você **traduz requisitos** em estrutura e SQL. No dia a dia, costuma-se receber **usuário e senha** e **hosts/portas** para ambientes autorizados — o mesmo padrão usado no laboratório com banco `Python` e login dedicado. - -### 2. Requisitos do laboratório da aula - -Em linha com o roteiro discutido na transcrição, o conjunto mínimo inclui: - -- Integração **Python ↔ banco relacional**. -- Três sabores: **PostgreSQL**, **MySQL**, **SQL Server** (Oracle foi deixado de fora por custo de configuração no tempo da aula). -- Interface **web** simples para edição/execução: **Jupyter Notebook** (alternativas citadas: VS Code com suporte a notebooks, ambientes online). -- Execução **passo a passo** (células). -- Implementar **CRUD** em cada banco. - -> **Regra:** CRUD — **C**reate (inclui criar tabela/registro), **R**ead (`SELECT`), **U**pdate (`UPDATE`), **D**elete (`DELETE`). - -### 3. Versões estáveis e pinagem - -A aula alerta: **não usar pré-release** no ambiente de trabalho; usar **versão estável** do Python e **não atualizar no meio do projeto** sem necessidade — mudanças podem **deprecar APIs** (ex.: bibliotecas como Pandas) e quebrar scripts. Em equipes, isso vira requisito explícito: **manter versões fixas** dos componentes até passar por **dev → homologação → produção**. - -*Não coberto no material em vídeo:* números exatos de versão mudam com o tempo; siga o site oficial do Python e do fornecedor do SGBD para a série estável atual. - -### 4. Instalação dos SGBDs (visão prática) - -- **PostgreSQL**: instalador Windows, versão de laboratório; após instalar, **pgAdmin** costuma acompanhar. Instalação “mínima” no desktop consome menos RAM que um servidor ajustado para produção — **o mesmo binário** pode ser configurado de forma mais pesada em máquina dedicada. -- **MySQL**: **MySQL Community Server**; na instalação, escolher perfil de **desenvolvimento/desktop** quando disponível, para não reservar recursos como um servidor dedicado. Download: a dica da turma foi usar opção de **baixar sem login** quando a página pede conta Oracle. -- **SQL Server**: opções **Developer** (recursos completos para dev) ou **Express** (limitada, adequada a muitos laboratórios). Instalação típica no Windows. - -Links úteis estão consolidados em `Aulas/projetoBloco/Contexto/ProjetoBL-Links-Ferramentas.txt` (Python, PostgreSQL, MySQL, SQL Server, DBeaver, VS Code, etc.). - -### 5. Ferramenta cliente e “plano B” - -Em incidente real citado na aula, o cliente Oracle **travou** durante investigação de bloqueio no banco; a saída foi usar **DBeaver** para concluir a análise. Moral: domine **mais de um cliente** e saiba reconectar com o mesmo servidor. - -### 6. Python, pip e Jupyter - -- Verificar instalação: no terminal, `python` / `python --version`. -- Instalar Jupyter (pacote clássico): - `pip install notebook` - Abrir interface web: - `python -m notebook` -- `pip` é o instalador padrão de **bibliotecas** Python; IDE costuma delegar a mesma instalação. - -### 7. Bibliotecas de conexão (o que a aula instalou) - -| SGBD | Biblioteca Python (exemplo na aula) | Observação | -|------|-------------------------------------|------------| -| SQL Server | `pyodbc` | Depende de driver ODBC adequado no Windows | -| MySQL | `mysql-connector-python` | Pacote com nome explícito no PyPI | -| PostgreSQL | driver comunitário tipo `psycopg2` / `psycopg2-binary` | Instalação via `pip` | - -Comandos típicos: - -```bash -pip install notebook pyodbc mysql-connector-python psycopg2-binary -``` - -Se o banco estivesse **só em outro servidor**, ainda assim seriam necessários **drivers** no cliente para o stack Python escolhido. - -### 8. Fluxo de CRUD no notebook - -1. `import` da biblioteca. -2. Abrir **conexão** com host, porta, usuário, senha, nome do banco (parâmetros em variáveis — alinhado ao **mapa mental de variáveis** do projeto: nomes claros, tipos conscientes). -3. Criar **cursor**, executar `CREATE TABLE` se necessário. -4. `INSERT` com valores; em muitos drivers, **confirmar** com `commit()`. -5. `SELECT` e leitura dos resultados (ex.: laço sobre linhas). -6. `UPDATE` e `DELETE` com `commit` conforme o padrão da API. -7. Conferir no cliente gráfico com **refresh** na tabela. - -No SQL Server, a aula menciona também **Pandas** (`pip install pandas`) como ecossistema de manipulação de dados — útil para análise e integração, não só para SQL bruto. - -```mermaid -sequenceDiagram - participant NB as Jupyter (Python) - participant LIB as Biblioteca (pyodbc / mysql / psycopg2) - participant DB as SGBD - - NB->>LIB: connect(host, port, user, password, database) - LIB->>DB: protocolo nativo / ODBC - NB->>LIB: execute(SQL) - LIB->>DB: comando - DB-->>LIB: resultado / erro - NB->>LIB: commit() para DML -``` - -### 9. Ambientes dev, homologação e produção - -A aula antecipa conversa futura: em empresas existem **bancos de desenvolvimento**, **homologação** e **produção**, cada um com **permissões** e credenciais próprias — o mesmo tipo de **usuário escopado** que você preparou no PostgreSQL para o banco `Python` no laboratório. - -## Uso Prático - -Exemplo mínimo (padrão lógico; ajuste driver DSN/usuário ao seu ambiente): - -```python -# Ilustração: PostgreSQL com psycopg2 — padrão de conexão + INSERT + commit -import psycopg2 - -conn = psycopg2.connect( - host="localhost", - port=5432, - user="seu_usuario", - password="sua_senha", - dbname="python", -) -cur = conn.cursor() -cur.execute( - """ - CREATE TABLE IF NOT EXISTS controle_job ( - id SERIAL PRIMARY KEY, - nome_job VARCHAR(80) NOT NULL, - versao VARCHAR(20) NOT NULL - ); - """ -) -conn.commit() -cur.close() -conn.close() -``` - -No **MySQL**, a forma costuma ser análoga com `mysql.connector`; no **SQL Server**, `pyodbc` com string de conexão apontando para o driver ODBC instalado. - -Modelo mínimo da tabela de exemplo (para relacionar com o mapa **Projeto BL SQL**: tabela, colunas, chave): - -```mermaid -erDiagram - CONTROLE_JOB { - int id PK - varchar nome_job - varchar versao - } -``` - -## Erros Comuns - -- **Esquecer o `commit`**: `INSERT`/`UPDATE`/`DELETE` não persistem se a conexão estiver em transação implícita e você não confirmar. -- **Instalar só o SGBD**: Python continua sem biblioteca — erro ao importar ou ao conectar. -- **Misturar pré-release e estável**: comportamento inesperado e avisos de depreciação (como no relato do Pandas no chat). -- **Atualizar pacote no meio do semestre** sem testar: exemplos do curso e notebooks podem parar de rodar. -- **Porta/host incorretos**: especialmente com múltiplos SGBDs na mesma máquina (`5432` PostgreSQL, `3306` MySQL típicos, SQL Server costuma usar instância/porta própria). -- **Permissão de usuário**: usuário só com `SELECT` não cria tabela; erros de **permission denied** vêm daí. - -## Visão Geral de Debugging - -1. **Ping semântico**: o Python importa a biblioteca? Se não, problema de `pip` ou ambiente virtual errado. -2. **Teste de conexão mínima**: string de conexão só com `SELECT 1` (ou equivalente) reduz variáveis. -3. **Cliente gráfico**: se DBeaver conecta e o notebook não, compare **driver**, **porta** e **usuário** byte a byte. -4. **Transação**: verifique se há `commit` e se outra sessão não está **lockando** (como no caso da aula com ferramenta travando). -5. **Logs do SGBD**: mensagens de erro do servidor costumam indicar sintaxe vs permissão. - -## Principais Pontos - -- Requisitos claros (**integração**, **três SGBDs**, **notebook**, **CRUD**) guiam o laboratório como um **mini-projeto**. -- **CRUD** é o conjunto mínimo de operações sobre dados de uma entidade. -- **pip** instala bibliotecas; **drivers** no SO completam a ponte até o banco. -- **Jupyter** viabiliza execução passo a passo e documentação viva. -- **Versões fixas** e evitar pré-releases reduzem surpresas — padrão profissional. -- **Validar no cliente SQL** fecha o ciclo com o mapa mental de **tabelas, linhas e comandos SQL** do projeto. - -## Preparação para Prática - -Você deve ser capaz de: listar os requisitos do laboratório da aula; instalar Python estável, Jupyter e conectores; criar banco e usuário de teste; executar um CRUD completo em pelo menos um SGBD e repetir o padrão nos outros dois; explicar o papel de `commit` e de credenciais. - -## Laboratório de Prática - -### Exercício Easy — Dicionário de conexão - -Monte um **template** de configuração (sem senhas reais no repositório) para um job de ETL que grava em PostgreSQL. - -```python -from typing import TypedDict - - -class PostgresConn(TypedDict): - host: str - port: int - dbname: str - user: str - password: str - - -def load_connection_from_env() -> PostgresConn: - # TODO: substituir este placeholder por leitura de variáveis de ambiente ou arquivo ignorado pelo git - # (ex.: POSTGRES_HOST, POSTGRES_PORT, POSTGRES_DB, POSTGRES_USER, POSTGRES_PASSWORD). - return { - "host": "localhost", - "port": 5432, - "dbname": "python", - "user": "TODO_USUARIO", - "password": "TODO_SENHA", - } - - -if __name__ == "__main__": - cfg = load_connection_from_env() - print(cfg["host"], cfg["port"], cfg["dbname"]) -``` - -### Exercício Medium — Função CRUD mínima (PostgreSQL) - -Implemente funções que executam **uma** operação cada, com `commit` explícito após escrita. - -```python -from __future__ import annotations - -from typing import Any, List, Tuple - - -def get_conn() -> Any: - """Retorna uma conexão psycopg2 (Any até você tipar com o módulo instalado).""" - # TODO: import psycopg2 e retornar psycopg2.connect(...) com credenciais seguras (env / secrets) - return None - - -def crud_ensure_table(conn: Any) -> None: - # TODO: CREATE TABLE IF NOT EXISTS para tabela de log de importação (id, origem, linhas_ok, criado_em) - pass - - -def crud_insert_log(conn: Any, origem: str, linhas_ok: int) -> None: - # TODO: INSERT + commit - pass - - -def crud_select_last_logs(conn: Any, limit: int = 5) -> List[Tuple[Any, ...]]: - # TODO: SELECT ordenado por criado_em DESC LIMIT %s - return [] -``` - -### Exercício Hard — Checklist de versões pinadas - -Simule um **arquivo de requisitos** usado em time para evitar drift de versões. - -```python -REQUIRED = { - "python": "3.x.x", # TODO: fixar série estável usada no seu laboratório - "psycopg2-binary": "x.x.x", - "mysql-connector-python": "x.x.x", - "pyodbc": "x.x.x", - "notebook": "x.x.x", -} - - -def assert_versions_match_installed() -> list[str]: - """Retorna lista de divergências entre REQUIRED e pacotes instalados.""" - # TODO: usar importlib.metadata.version ou pkg_resources para comparar - return [] - - -if __name__ == "__main__": - for issue in assert_versions_match_installed(): - print("PROBLEMA:", issue) -``` - - - - diff --git a/content/projeto-bloco/aula-09-etapas-projeto-python-recap-mercado.md b/content/projeto-bloco/aula-09-etapas-projeto-python-recap-mercado.md deleted file mode 100644 index 9e06cc1..0000000 --- a/content/projeto-bloco/aula-09-etapas-projeto-python-recap-mercado.md +++ /dev/null @@ -1,295 +0,0 @@ ---- -title: "Etapas do ciclo de projeto, estruturas Python e mercado de trabalho" -slug: "etapas-projeto-python-recap-mercado" -discipline: "projeto-bloco" -order: 9 -description: "Mapa das etapas do Projeto Bloco, revisão de listas/tuplas/dicionários e laços, pipeline origem–transformação–destino e dicas práticas de busca de oportunidades." -reading_time: 28 -difficulty: "medium" -concepts: - - ciclo-de-vida-de-projeto - - listas-tuplas-dicionarios - - for-while-range - - pipeline-de-dados - - perfis-em-projeto - - curriculo-e-mercado -prerequisites: - - metodologias-projeto-de-dados - - python-jupyter-crud-bancos-relacionais -learning_objectives: - - "Relacionar cada etapa do diagrama Projeto Bloco Etapas ao tipo de entrega e documentação típica." - - "Escolher entre lista, tupla e dicionário conforme mutabilidade e modelo chave–valor." - - "Decidir entre for e while com base em repetição contada versus condição." - - "Descrever origem, transformação e destino em um fluxo de dados e o papel de cada perfil." -exercises: - - question: "Por que a tupla é adequada quando você quer impedir alteração acidental dos dados carregados no meio do processamento?" - answer: "A tupla é imutável: após criada, não permite atribuir novos valores aos elementos; isso reduz efeitos colaterais quando o mesmo conjunto deve ser tratado como 'somente leitura' naquele trecho do código." - hint: "Compare com lista, que é mutável." - - question: "Em projetos grandes, por que um mesmo profissional raramente cobre todas as etapas do ciclo de vida?" - answer: "Projetos grandes dividem papéis (gestão, análise, desenvolvimento, dados, testes). O escopo e a profundidade técnica exigem especialização; cada etapa usa ferramentas e competências distintas." - hint: "Pense em empresa grande versus time único." - - question: "O que é transformação de dados entre origem e destino, além de copiar colunas?" - answer: "Inclui limpeza (nulos, duplicados), validação de formatos, normalização, remoção de caracteres que quebram ingestão e regras de negócio que garantem qualidade antes do consumo em BI ou ciência de dados." - hint: "Lembre qualidade e regras, não só cópia." ---- - -## Visão Geral do Conceito - -Esta aula articula três eixos que aparecem juntos na prática: **onde o projeto “começa e termina”** (etapas do **Projeto Bloco**), **como o Python expressa dados e repetições** (estruturas e laços que você já usou em integração com banco e planilhas) e **como se posicionar no mercado** (currículo, redes, busca consistente de oportunidades). O diagrama **Projeto Bloco Etapas** não é burocracia vazia: ele organiza **o que documentar**, **quem costuma participar** em cada fase e **como seu perfil** (desenvolvimento, dados, empreendedor) encaixa em uma ou várias etapas. - -A recapitulação técnica reforça que **origem → transformação → destino** é o mesmo raciocínio da aula anterior (Python + SQL + Excel), agora ligado a **qualidade de dados** e a **papéis** (engenheiro de dados prepara; analista/cientista consome; visualização depende de dados já tratados quando possível). - -## Modelo Mental - -- **Projeto como sequência de compromissos**: da ideia e convencimento (**Apresentação**, **Proposta**, **Abertura**) até o que entra em produção e evolui (**Testes** … **Evolução**). Nem todo projeto formaliza todas as caixas; o mapa serve para você **saber o que está faltando** quando algo dá errado. -- **Seu papel é um recorte**: em empresa grande, você pode ser só **modelagem** ou só **infra de banco**; em empresa pequena ou como empreendedor, você **empilha papéis** e toca várias etapas. O mapa explica por que “saber tudo” não é o padrão — é normal **aprofundar em algumas etapas** e colaborar nas demais. -- **Python como cola**: listas, tuplas e dicionários são formas de **segurar e endereçar dados** em memória; `for` e `while` repetem trabalho (por exemplo, linhas de planilha ou resultados de consulta). **Ferramenta** (IDLE, Jupyter, Anaconda, VS Code) é meio; **fundamento** é entender o fluxo. -- **Mercado**: currículo e perfis profissionais online são **pontos de entrada**; entrevistas ruins também ensinam. O objetivo é **iterar** com calma, não esperar acerto na primeira candidatura. - -```mermaid -flowchart LR - subgraph origem["Origem"] - A1[Arquivos CSV / Excel] - A2[API / pasta monitorada] - A3[Banco legado] - end - subgraph tx["Transformação"] - T1[Limpeza e validação] - T2[Regras de negócio] - T3[Qualidade] - end - subgraph dest["Destino"] - D1[DW / lake / banco alvo] - D2[Camada para BI] - D3[Ciência de dados] - end - origem --> tx --> dest -``` - -## Mecânica Central - -### 1. Diagrama Projeto Bloco Etapas (referência da aula) - -As etapas foram apresentadas como um fluxo completo, da concepção à evolução. Em ordem lógica de leitura: - -```mermaid -flowchart TD - P1[Apresentação — o que é o projeto] - P2[Proposta — convencimento e solução] - P3[Abertura — início e autorização] - P4[Requisitos — especificação e funcionalidades] - P5[Planejamento — cronograma, etapas, equipe] - P6[Análise — entendimento, detalhamento, avaliação] - P7[Modelagem — entidades e relacionamentos] - P8[Construção — desenvolvimento] - P9[Testes — verificação, cenários, avaliação] - P10[Implantação — instalação, liberação, cliente] - P11[Validação — comprovação com cliente] - P12[Aceitação — aprovação] - P13[Evolução — melhoria contínua e incremental] - P1 --> P2 --> P3 --> P4 --> P5 --> P6 --> P7 --> P8 --> P9 --> P10 --> P11 --> P12 --> P13 -``` - -**Gestores de projeto** precisam enxergar **todas** as etapas relevantes para planejar entregas e formalidades. **Especialistas** costumam **profundar** em subconjuntos (por exemplo, requisitos + modelagem + construção para quem desenvolve; modelagem + construção + testes para parte de dados). **Empreendedores** frequentemente atravessam **quase todas** as caixas com menos divisão de papéis. - -### 2. Recapitulação: integração Python e banco - -O objetivo da semana anterior permanece: entender **conexão** de aplicação a SGBD, **transporte** de dados (por exemplo, planilha → banco), uso de **bibliotecas** via `pip` e drivers adequados (**PostgreSQL**, **MySQL**, **SQL Server** — cada um com seu conector). Em nuvem, muitos serviços expõem scripts ou configurações em **Python** e **JSON**; o importante é o **padrão mental** de origem, transformação e destino, não decorar um único provedor. - -### 3. Ferramentas leves x ambientes completos - -- **IDLE**: vem com o Python, **pouco uso de recursos**, bom para **iniciantes** e para testar scripts sem configurar projeto em IDE pesada. -- **Jupyter Notebook**: células executáveis, ótimo para **exploração** e integração passo a passo (como nas aulas de banco). -- **Anaconda**: distribuição que agrupa **Python**, **Jupyter** e muitas ferramentas para **ciência e análise de dados**; ocupa mais disco, mas centraliza ambientes. - -Quando a máquina é limitada, **focar no que o curso exige agora** e preferir SGBDs mais leves em laboratório **não invalida** o aprendizado: o mesmo software de servidor pode ser configurado de forma mais pesada em produção. - -### 4. Listas, tuplas e dicionários - -| Estrutura | Sintaxe típica | Mutabilidade | Uso mental | -|-----------|------------------|--------------|------------| -| Lista | `[ ]` | Mutável | Sequência ordenada; alterar itens, adicionar, remover | -| Tupla | `( )` | Imutável | “Congelar” um conjunto de valores após leitura; evita alteração acidental | -| Dicionário | `{ chave: valor }` | Mutável (valores/chaves conforme operações) | Modelo **chave → valor**; acesso por chave, como colunas nomeadas | - -Índices em listas e tuplas começam em **0**; índice negativo percorre **do fim para o início**. `len()` retorna quantidade de elementos. - -### 5. Repetição: `for` versus `while` - -- **for**: quando há **contagem definida** ou iteração direta sobre coleção (ex.: `range(n)` — lembra que vai de **0** a **n-1**). -- **while**: quando a repetição depende de uma **condição** que pode não ter tamanho fixo (ex.: ler até encontrar marcador). - -> **Regra:** Em `while`, **atualize** a variável que controla a condição; caso contrário, você cria **loop infinito** (risco de travar o processo ou a máquina). - -### 6. Perfis em projetos de dados (exemplo discutido) - -- **Engenheiros de dados**: desenham **fluxo** da origem ao destino (transformações, ambientes sem acesso direto a servidor podem depender de DBA para **volume de tabelas** e **metadados**). -- **Cientistas de dados / analistas de BI**: consomem dados **já preparados** no destino; conectam **Power BI**, **Tableau**, **Qlik** e similares para análise e painéis. -- **DBA / infra**: em alguns cenários, só **preparam servidor e instância**; o **modelo lógico** já veio de outras equipes. - -### 7. Performance de dashboards e consultas (debate da aula) - -Travamentos em ferramentas de visualização raramente têm **uma** causa: entram **volume de dados**, **largura da tabela** (muitas colunas, campos grandes), **período** consultado (histórico longo força mais processamento), **refresh** e **infraestrutura** (rede, servidor, configuração). **Pré-agregar** e **reduzir colunas** expostas ao painel costuma aliviar carga; o ideal é que o usuário veja **dados já processados** na camada de consumo. - -## Uso Prático - -### Exemplo: dicionário como “linha” de registro - -```python -registro = { - "pedido_id": "P-1002", - "valor": 199.90, - "status": "pendente", -} -# Acesso por chave (falha clara se chave não existir com []) -print(registro["pedido_id"]) -``` - -### Exemplo: tupla para coordenadas fixas de um pipeline - -```python -# Ordem fixa: origem, destino, periodicidade_horas -config_etl = ("pasta/inbox", "postgresql://...", 1) -origem, destino, periodicidade = config_etl -``` - -### Exemplo: `for` com `range` para amostrar índices - -```python -for i in range(3): - print(f"processando lote índice {i}") -``` - -*Não coberto no vídeo:* implementação completa de um CRUD em terminal; o instrutor sugeriu como possibilidade futura para consolidar integração com banco. - -## Erros Comuns - -- **Confundir mutabilidade**: tentar “alterar” uma tupla como se fosse lista → gera erro ou leva a recriar objetos sem perceber o impacto em referências compartilhadas. -- **`while` sem progresso**: esquecer de incrementar contador ou atualizar condição → **loop infinito**. -- **`range` e off-by-one**: assumir que `range(5)` inclui o **5** — na verdade são os índices **0..4**. -- **Achar que a ferramenta de BI resolve modelo ruim**: painel lento com milhões de linhas e centenas de colunas sem camada de preparação → sintoma de **origem/transformação** mal desenhadas, não só “culpa do clique”. -- **Currículo estático**: não atualizar conquistas e palavras-chave alinhadas ao que você **realmente** fez → desalinhamento com o mercado. - -## Visão Geral de Debugging - -- **Python não faz o esperado com listas/tuplas**: confira **imutabilidade**, **tipos** dos elementos e se você está usando **índice** ou **chave** corretos. -- **Loop não termina**: inspecione a **condição** do `while` e cada caminho que deveria **sair** do laço. -- **Dashboard lento**: reduza **período**, **colunas** e **granularidade**; verifique se o gargalo é **consulta**, **rede** ou **capacidade** do servidor — documente hipóteses antes de trocar de ferramenta. - -## Principais Pontos - -- O diagrama **Projeto Bloco Etapas** cobre da apresentação à evolução; **formalize** o que o seu contexto exige. -- **Lista** mutável, **tupla** imutável, **dicionário** chave–valor — escolha pelo **comportamento** dos dados. -- **`for`** para repetições **contadas** ou iteráveis; **`while`** para **condição**; cuide do **loop infinito**. -- **Pipeline** de dados: origem → transformação (qualidade) → destino; perfis diferentes atuam em **pontos** distintos. -- **Mercado**: currículo atualizado, presença em **redes profissionais**, busca **regular** de vagas e **entrevistas como aprendizado**. - -## Preparação para Prática - -Você deve ser capaz de: **explicar** uma etapa do diagrama em uma frase; **escolher** lista/tupla/dicionário para um caso; **escrever** um laço que percorra índices ou chaves sem erro de lógica; **relacionar** lentidão de painel a **dados** e **infra**, não só à ferramenta de visualização. - -## Laboratório de Prática - -### Easy — Classificar estrutura de dados - -Um script recebe uma string `"lista"`, `"tupla"` ou `"dict"` e deve retornar um exemplo **vazio** da estrutura correspondente (lista vazia, tupla vazia, dicionário vazio). Complete a função. - -```python -def estrutura_vazia(tipo: str): - # TODO: retornar [], () ou {} conforme tipo; para entrada inválida, retornar None - return None # placeholder: substitua pela lógica; após implementar, valide os casos abaixo - - -if __name__ == "__main__": - # Esperado após o TODO: lista -> [], tupla -> (), dict -> {}, outro -> None - print(estrutura_vazia("lista"), estrutura_vazia("tupla"), estrutura_vazia("dict"), estrutura_vazia("outro")) -``` - -### Medium — Contagem segura com `while` - -Processar uma fila simulada de IDs de jobs (lista). Remova e imprima cada ID com **while** até a fila esvaziar. Não use `for` sobre a lista original sem cópia — evite mutar a coleção enquanto itera de forma incorreta. - -```python -def esvaziar_fila(jobs): - # TODO: enquanto houver jobs, retire um (pop) e acumule em processados - processados = [] - return processados - - -if __name__ == "__main__": - fila = ["j1", "j2", "j3"] - print(esvaziar_fila(list(fila))) # esperado após TODO: ["j1","j2","j3"] e fila de entrada esvaziada na cópia trabalhada -``` - -### Hard — Mini-checklist de qualidade em registros - -Dada uma lista de dicionários representando linhas de um CSV já carregado (`pedido_id`, `valor`, `email`), retorne quantos registros falham em: `valor` nulo ou negativo; `email` sem `"@"`; `pedido_id` duplicado (considerar primeira ocorrência válida, duplicatas seguintes contam como falha). - -```python -def auditar_registros(linhas): - # TODO: retornar dict com chaves "falhas_valor", "falhas_email", "duplicatas_id" - return {"falhas_valor": 0, "falhas_email": 0, "duplicatas_id": 0} - - -if __name__ == "__main__": - amostra = [ - {"pedido_id": "A", "valor": 10.0, "email": "a@x.com"}, - {"pedido_id": "A", "valor": 5.0, "email": "b@x.com"}, - {"pedido_id": "B", "valor": -1, "email": "invalido"}, - ] - print(auditar_registros(amostra)) # após TODO: duplicatas_id >= 1 para esta amostra -``` - - - - diff --git a/content/python/aula-01-por-que-programar-python.md b/content/python/aula-01-por-que-programar-python.md deleted file mode 100644 index a7bca47..0000000 --- a/content/python/aula-01-por-que-programar-python.md +++ /dev/null @@ -1,415 +0,0 @@ ---- -title: "Por que programar e por que Python?" -slug: "por-que-programar-python" -discipline: "python" -order: 1 -description: "Visão geral da disciplina, da profissão e dos motivos para aprender programação usando Python." -reading_time: 25 -difficulty: "easy" -concepts: - - programação - - pensamento computacional - - Python - - curva de aprendizagem - - curva do esquecimento - - prática deliberada -prerequisites: [] -learning_objectives: - - "Entender por que programação é uma habilidade central para ADS e tecnologia." - - "Descrever as principais características da linguagem Python e seus usos." - - "Reconhecer o papel da prática, da revisão e da comunidade no aprendizado de programação." -exercises: - - question: "Por que assistir apenas às aulas (sem praticar) não é suficiente para aprender a programar?" - answer: "Porque, segundo modelos como a pirâmide de aprendizagem e a curva do esquecimento, só ouvir/assistir leva a baixa retenção; é preciso praticar, revisar, discutir e ensinar para consolidar o conhecimento." - hint: "Lembre-se dos exemplos de curva do aprendizado, curva do esquecimento e das recomendações de prática do professor." -review_after_days: [1, 3, 7, 30] ---- - -## Visão Geral do Conceito - -Esta lição apresenta **por que aprender a programar** e **por que a disciplina começa com `Python`** como primeira linguagem. -Vamos conectar os objetivos da disciplina, o modelo de avaliação e os princípios de estudo à realidade de quem está iniciando em Análise e Desenvolvimento de Sistemas (ADS) e processamento de dados. - -Ao final, você terá um mapa mental claro: o que é programação, qual o papel de `Python` nesse cenário e como organizar sua rotina para realmente aprender, e não só “assistir aula”. - -## Modelo Mental - -### Programar como ferramenta e como esporte - -Programação é, ao mesmo tempo: - -- Uma **ferramenta**: você usa código para automatizar tarefas, analisar dados, integrar sistemas, criar APIs, construir dashboards e resolver problemas reais de negócio. -- Um **esporte/arte**: escrever código é como resolver quebra‑cabeças. Existem várias soluções possíveis para o mesmo problema, com diferentes estilos, paradigmas e níveis de eficiência. - -Um bom modelo mental para programação é pensar em **dar instruções precisas para um agente extremamente obediente, porém literal**: o computador faz exatamente o que você manda, não o que você “quis dizer”. - -### Papel do aluno em EAD: você é o protagonista - -Na educação presencial tradicional, o professor costuma ser o ator principal; no modelo de educação a distância/híbrido apresentado pelo professor Gesiel, **o aluno passa a ser o protagonista**: - -- Você escolhe horários, organiza rotina e decide quantas vezes revisa o conteúdo. -- O professor e o material (trilhas, vídeos, livros) são **guias**, não a única fonte de conhecimento. -- Sem prática deliberada e auto‑gestão, a disciplina vira apenas mais um vídeo na sua fila de streaming. - -> **Regra:** nesta disciplina, assistir à aula é o ponto de partida — a aprendizagem acontece quando você pratica, revisa, discute e tenta explicar para outras pessoas. - -### Curva do aprendizado e curva do esquecimento - -Duas ideias centrais da aula de contexto explicam por que a disciplina insiste em prática contínua: - -- **Curva do aprendizado**: no começo a evolução é lenta (sensação de “não sei nada”), depois acelera e, com o tempo, chega a um platô de proficiência. Esse salto só acontece **com várias tentativas**: escrever código, errar, corrigir, refatorar. -- **Curva do esquecimento (Ebbinghaus)**: se você não revisa, a retenção cai rapidamente — em poucos dias você esquece quase tudo. Revisões espaçadas (1 dia, 3 dias, 1 semana, 1 mês) “empurram” a curva para cima. - -Esses modelos justificam: - -- A existência de TPs e AT ao longo de 11 semanas. -- A recomendação explícita de **digitar o código**, não apenas copiar. -- A ênfase do professor em comunidades, grupos de estudo e ensino entre pares. - -### Diagrama: ciclo de aprendizado em programação - -```mermaid -flowchart TD - A[Assistir aula / ler material] --> B[Anotar ideias principais] - B --> C[Praticar: digitar e adaptar o código] - C --> D[Revisar: reescrever e comentar o que entendeu] - D --> E[Discutir em grupo / comunidade] - E --> F[Ensinar ou explicar para alguém] - F --> C - - C -.-> G[Curva do aprendizado acelera] - D -.-> H[Curva do esquecimento desacelera] -``` - -## Mecânica Central - -### O que é programação? - -De forma operacional, **programar** é definir, passo a passo, o que o computador deve fazer para resolver um problema, usando uma linguagem formal como `Python`. -Para isso você combina: - -- **Dados** (números, textos, datas, listas, dicionários…). -- **Operações** (cálculos, comparações, acesso a estruturas de dados). -- **Controle de fluxo** (decisões, laços de repetição, chamadas de funções). - -Ao longo da disciplina, esses blocos aparecerão como: - -- Primeiros programas, operadores matemáticos e fundamentos de `strings`. -- Entrada de usuário e verificação de valores. -- Estruturas condicionais, repetições e listas. -- Primeiras funções e modularização de código. - -### O que é Python e por que começar por ele? - -Da aula de contexto, consolidamos as características mais relevantes de `Python` para um estudante de ADS: - -- **Linguagem de alto nível**: você escreve próximo da forma como pensa o problema, sem se preocupar com detalhes de memória e registradores. -- **Multiplataforma**: roda em `Unix`, `Windows` e outros sistemas sem mudar o código. -- **Multiparadigma**: suporta estilos procedurais, orientados a objetos e funcionais. -- **Tipagem dinâmica e forte**: - - Dinâmica: o tipo é decidido em tempo de execução. - - Forte: a linguagem não deixa você misturar tipos incompatíveis (por exemplo, somar número com texto sem conversão). -- **Interpretada (com opção de empacotamento)**: ideal para ciclos rápidos de teste. -- **Open Source**: nenhuma taxa ou royalty para uso. - -No contexto de processamento de dados: - -- A comunidade é enorme e ativa (ex.: Python Brasil, Pythons regionais, bibliotecas de dados e IA). -- A biblioteca padrão é extensa (arquivos, rede, datas, `json`, `sqlite3`, etc.). -- Ecosistema forte em dados e IA (apesar de esta disciplina focar nos fundamentos de programação). - -### Mecânica da disciplina e avaliação - -A organização da disciplina, apresentada em aula, reforça a ideia de aprendizado progressivo: - -- **Cronograma trimestral (11 semanas)**: - - Semanas 1–9: conteúdo e prática guiada. - - Semana 10: entrega do AT (trabalho avaliativo principal). - - Semana 11: reentrega/reintegra do AT, se necessário. -- **Avaliação por competência**: - - TPs (Testes de Performance) são pré‑requisitos e treinamento para o AT. - - AT é obrigatório e determina o conceito final (como `DNL`, `DL`, etc.). - - Entregas atrasadas perdem conceito máximo, mesmo que o conteúdo esteja correto. - -> **Regra:** trate cada TP como um treino valioso para o AT — eles não “dão nota”, mas constroem a proficiência exigida na avaliação final. - -## Uso Prático - -### Exemplos reais de onde Python aparece - -Alguns exemplos citados na aula (e amplamente conhecidos no mercado): - -- Motores de busca e serviços web (a primeira versão do buscador do Google usou fortemente `Python`). -- Sistemas bancários e modelos de `machine learning` para recomendação, antifraude, churn e ofertas. -- Pipelines de dados, ETLs, APIs de dados e dashboards. -- Ferramentas científicas, automação de tarefas de escritório, scripts para manutenção de servidores. - -Para você, estudante de fundamentos de processamento de dados, isso significa: - -- O mesmo conhecimento de base (variáveis, condicionais, laços, funções) é reaproveitado em: - - pequenos scripts para automatizar tarefas pessoais; - - ETLs e pipelines de dados; - - back‑ends de APIs; - - notebooks de análise exploratória. - -### Um exemplo simples inspirado na aula: relógio digital - -Na aula, o professor mostra um **relógio digital** feito com Python e a biblioteca gráfica `tkinter`. -O código ilustra vários aspectos importantes: - -- Importação de módulos (`import tkinter as tk`, `from time import strftime`). -- Criação de janelas e widgets. -- Uso de funções e atualização periódica com `after`. - -Versões futuras da disciplina vão retomar esse tipo de exemplo quando você já dominar os blocos básicos da linguagem. - -## Erros Comuns - -- **Acreditar que assistir aula é suficiente** - Ignorar a prática de digitar e adaptar o código faz com que a curva do esquecimento domine; poucas semanas depois, tudo parece “novo” de novo. - -- **Depender demais de modelos de linguagem (LLMs) no início** - Usar uma LLM para gerar todas as soluções impede que você desenvolva o **pensamento computacional**: decompor problemas, criar algoritmos, prever comportamentos. Use a IA como apoio para explicar erros e revisar código, não como substituto para pensar. - -- **Não ter rotina de estudo definida** - “Estudar quando der” quase sempre significa “estudar pouco e em blocos muito espaçados”. Sem consistência semanal, os TPs e o AT tornam‑se fontes de ansiedade em vez de consolidação do aprendizado. - -- **Subestimar o tempo de maturação da curva de aprendizado** - Desistir logo nas primeiras dificuldades porque “não levo jeito” é, muitas vezes, apenas sinal de que você ainda está no trecho inicial da curva, onde o progresso é pouco visível. - -## Visão Geral de Debugging - -Mesmo sem escrever muito código nesta primeira lição, já vale adotar uma mentalidade de debugging: - -- **Quando algo não faz sentido no conteúdo**: - - Identifique exatamente onde está a dúvida (termo, exemplo, diagrama, parte da aula). - - Volte ao trecho correspondente na trilha escrita ou na gravação. - - Escreva com suas próprias palavras o que você entendeu — muitas vezes a confusão fica clara nesse processo. - -- **Quando um conceito não “gruda”**: - - Verifique se você já praticou o suficiente (ex.: escrevendo exemplos adicionais, criando pequenas variações). - - Planeje revisões explícitas (1, 3, 7, 30 dias) no seu calendário, mesmo que sejam rápidas. - -- **Quando o código não funciona** (nas próximas lições): - - Leia a mensagem de erro completa e destaque termos técnicos. - - Reproduza o erro em um exemplo mínimo. - - Procure padrões: tipos incompatíveis, indentação, nomes de variáveis, uso incorreto de funções. - -## Principais Pontos - -- Programar é uma **ferramenta poderosa e uma forma de pensamento** que se aplica a praticamente qualquer área de tecnologia e dados. -- O modelo de EAD/híbrido da disciplina coloca **você** como protagonista: sem rotina, prática e revisão, o conteúdo se perde rapidamente. -- Curva do aprendizado e curva do esquecimento explicam por que é preciso **praticar, revisar, discutir e ensinar** para consolidar conhecimento. -- `Python` é uma primeira linguagem estratégica: simples de ler, extremamente versátil e muito usada em dados, web e automação. -- TPs e ATs estruturam o trimestre para que você construa proficiência de forma progressiva, não em uma maratona de última hora. - -## Preparação para Prática - -Antes de partir para código mais intenso nas próximas lições, você deve ser capaz de: - -- Explicar, em voz alta, **por que** está estudando programação e o que espera resolver com isso nos próximos anos. -- Apontar pelo menos **três usos concretos de Python** em dados ou sistemas que façam sentido para você. -- Desenhar (no papel ou em ferramenta digital) a **sua própria curva de aprendizado** esperada, anotando estratégias para mantê‑la em crescimento. -- Configurar um ambiente mínimo para praticar (IDE ou editor, versão de `Python`, local ou online) e saber onde salvar/organizar seus arquivos de exercícios. - -As atividades do Laboratório de Prática abaixo ajudam a transformar essas reflexões em ações concretas, já com um pouco de código. - -## Laboratório de Prática - -> **Importante:** os códigos abaixo são boilerplates **executáveis**, com lacunas marcadas como `TODO`. O objetivo é que você edite essas partes no Editor Integrado, testando e experimentando. - -### Exercício Easy — Plano mínimo de estudo semanal - -Crie um pequeno script que ajude você a planejar **quantas horas precisa estudar por semana** para minimizar a curva do esquecimento, considerando: - -- Quantas aulas síncronas tem na semana. -- Quantas horas livres por dia você consegue dedicar à disciplina. -- Uma recomendação mínima de 30 minutos extras de prática por dia em que houver aula. - -Use entradas simples de usuário e mostre um resumo no final. - -```python -def calcular_horas_semanais(aulas_por_semana: int, horas_livres_por_dia: float) -> float: - """ - Calcula uma estimativa de horas de estudo por semana. - - Recomendação básica: - - Para cada aula síncrona, reservar pelo menos 1 hora extra de prática. - - Em dias com aula, adicionar 0.5h (30 minutos) extras. - """ - # TODO: implementar a regra descrita na docstring - # Dica: pense na semana como tendo 7 dias e some somente os dias com aula. - return 0.0 - - -def main() -> None: - print("=== Planejador simples de horas de estudo ===") - try: - aulas = int(input("Quantas aulas síncronas de Python você tem na semana? ")) - horas_livres = float(input("Em média, quantas horas livres por dia você consegue dedicar à disciplina? ")) - except ValueError: - print("Entrada inválida. Use números, por favor.") - return - - horas_semanais = calcular_horas_semanais(aulas, horas_livres) - print(f"\nVocê deveria reservar pelo menos {horas_semanais:.1f} horas por semana para estudar Python.") - print("Ajuste esse valor conforme sua realidade e outras disciplinas.") - - -if __name__ == "__main__": - main() -``` - -### Exercício Medium — Simulador da curva do esquecimento - -Implemente um programa que, dado um **tópico de estudo** (por exemplo, “strings em Python”), sugira datas relativas para revisão, inspiradas na curva do esquecimento: - -- Revisão 1: 1 dia depois. -- Revisão 2: 3 dias depois. -- Revisão 3: 7 dias depois. -- Revisão 4: 30 dias depois. - -Não se preocupe ainda com datas reais do calendário; pense apenas em “+1 dia”, “+3 dias” etc. - -```python -REVIEW_OFFSETS_DIAS = [1, 3, 7, 30] - - -def gerar_plano_revisao(nome_topico: str) -> list[str]: - """ - Gera descrições de revisões baseadas em offsets fixos (em dias). - - Exemplo de saída para 'strings em Python': - - 'Revisar 'strings em Python' em +1 dia' - - 'Revisar 'strings em Python' em +3 dias' - ... - """ - # TODO: criar uma lista de mensagens de revisão usando REVIEW_OFFSETS_DIAS - # Use f-strings para montar as frases. - return [] - - -def main() -> None: - print("=== Plano de revisão por curva do esquecimento ===") - topico = input("Qual tópico você estudou hoje? ") - revisoes = gerar_plano_revisao(topico) - - if not revisoes: - print("Nenhuma revisão planejada ainda. Implemente a função gerar_plano_revisao().") - return - - print("\nSugestão de revisões:") - for linha in revisoes: - print("-", linha) - - -if __name__ == "__main__": - main() -``` - -### Exercício Hard — Diário de prática e motivação - -Escreva um programa interativo que registre **minissedimentos diários de prática** de Python em um formato simples de texto. -O programa deve: - -- Perguntar o **dia da semana** e **quanto tempo você praticou** (em minutos). -- Pedir uma breve descrição do que você praticou (por exemplo: “fiz os exercícios de strings”). -- Salvar um registro em um arquivo local chamado `diario_pratica.txt`. -- Relembrar a importância de comunidade: ao final, sugerir que você compartilhe o que aprendeu com alguém (grupo, fórum, colega). - -```python -from pathlib import Path -from datetime import datetime - - -ARQUIVO_DIARIO = Path("diario_pratica.txt") - - -def registrar_entrada(dia_semana: str, minutos: int, descricao: str) -> None: - """ - Registra uma linha no arquivo de diário com timestamp, dia, minutos e descrição. - O arquivo é criado automaticamente se não existir. - """ - # TODO: abrir o arquivo em modo append e escrever uma linha no formato: - # [YYYY-MM-DD HH:MM] (dia_semana) minutos min - descricao - # Dica: use datetime.now().strftime(...) para formar o timestamp. - pass - - -def main() -> None: - print("=== Diário de prática de Python ===") - dia = input("Dia da semana (ex.: segunda, terça): ").strip() - - try: - minutos = int(input("Quantos minutos você praticou hoje? ")) - except ValueError: - print("Valor inválido de minutos. Use apenas números inteiros.") - return - - descricao = input("Descreva em uma frase o que você praticou: ").strip() - - registrar_entrada(dia_semana=dia, minutos=minutos, descricao=descricao) - - print("\nEntrada registrada em", ARQUIVO_DIARIO.resolve()) - print("Lembrete: compartilhar o que você aprendeu com alguém aumenta muito a retenção :)") - - -if __name__ == "__main__": - main() -``` - - - - - diff --git a/content/python/aula-02-algoritmos-e-notebooks.md b/content/python/aula-02-algoritmos-e-notebooks.md deleted file mode 100644 index 62d3d66..0000000 --- a/content/python/aula-02-algoritmos-e-notebooks.md +++ /dev/null @@ -1,424 +0,0 @@ ---- -title: "Algoritmos, pensamento computacional e seu primeiro notebook Python" -slug: "algoritmos-e-notebooks" -discipline: "python" -order: 2 -description: "Do que é programar à escrita do primeiro algoritmo em Python dentro de um notebook no Deepnote." -reading_time: 30 -difficulty: "easy" -concepts: - - algoritmos - - pensamento computacional - - linguagem de programação - - notebooks - - IDE - - Deepnote -prerequisites: - - "por-que-programar-python" -learning_objectives: - - "Explicar o que é um algoritmo e por que ele é a base da programação." - - "Construir algoritmos simples em linguagem natural para problemas do dia a dia." - - " Entender o papel de uma IDE e de notebooks no desenvolvimento em Python." - - "Criar e executar o primeiro programa Python em um notebook no Deepnote." -exercises: - - question: "Quais são as três características essenciais de um algoritmo bem definido?" - answer: "Clareza (cada passo compreensível), finitude (termina em algum momento) e determinismo na maioria dos casos (para a mesma entrada, produz o mesmo resultado)." - hint: "Pense na parte da aula sobre 'O que são algoritmos?' e nas propriedades listadas logo depois." -review_after_days: [1, 3, 7, 30] ---- - -## Visão Geral do Conceito - -Nesta lição, você vai entender **o que é programar na prática**: escrever **algoritmos** que resolvem problemas reais e transformá‑los em código Python, usando um ambiente de notebooks (Deepnote) como **IDE** principal da disciplina. - -Vamos sair de exemplos do dia a dia (receita de bolo, montar móveis, rota no GPS) para: - -- definir formalmente o que é um algoritmo; -- construir algoritmos em linguagem natural; -- conhecer o Deepnote como ambiente de notebooks; -- escrever e executar o seu **primeiro programa em Python** (`print("Hello, world!")`). - -## Modelo Mental - -### Programar = resolver problemas com passos claros - -Em vez de pensar em programação como “escrever código”, pense como: - -- **Definir um objetivo claro** (ex.: “lavar a louça e levar o lixo para fora”). -- **Quebrar o objetivo em passos bem definidos**, em ordem. -- **Garantir que esses passos terminem** e levem ao resultado desejado. - -Esse conjunto de passos forma um **algoritmo**. O código Python é “apenas” a forma rígida, precisa e executável de registrar esse algoritmo para que o computador possa segui‑lo sem ambiguidade. - -### Pensamento computacional - -O professor destaca que programar envolve desenvolver um **conjunto de habilidades cognitivas**: - -- Decompor problemas complexos em partes menores. -- Identificar padrões e estruturas de repetição (laços). -- Definir regras de decisão (condicionais). -- Raciocinar sobre entradas, processamento e saídas. - -> **Modelo mental:** antes de abrir o Deepnote, imagine que você está ensinando uma pessoa extremamente literal a fazer uma tarefa — cada passo precisa ser explícito, na ordem certa e com um objetivo claro no final. - -### Notebooks como “cadernos executáveis” - -Um **notebook** (como o do Deepnote) é um “caderno” digital que mistura: - -- blocos de **texto explicativo** (Markdown); -- blocos de **código executável** (Python, SQL, etc.); -- blocos de **saída** (texto, tabelas, gráficos). - -Ele permite praticar programação de forma **iterativa e exploratória**: - -- escreve‑se um pequeno trecho de código; -- executa‑se; -- observa‑se a saída; -- ajusta‑se o algoritmo. - -Essa abordagem é ideal para quem está começando: você vê o efeito de cada mudança logo abaixo do código, sem perder o contexto. - -## Mecânica Central - -### O que é um algoritmo? - -Da definição apresentada em aula: - -> Um algoritmo é uma **sequência explícita, literal, limitada e sistemática de instruções e operações**, direcionadas à consecução de um objetivo pré‑definido. - -Em termos práticos, um bom algoritmo: - -- **Começa de um estado inicial bem definido**. -- **Descreve passos claros**, sem ambiguidade. -- **Termina** (não fica em loop infinito) e produz um **resultado esperado**. - -Exemplos cotidianos: - -- **Receita de bolo**: lista de ingredientes + passos numerados da preparação ao forno. -- **Manual de montagem de móveis**: sequência de instruções com número de peças, ordem de encaixe etc. -- **Direções de GPS**: siga em frente 500 m → vire à esquerda → mantenha à direita… até o destino. - -### Propriedades essenciais de algoritmos - -Um algoritmo bem formado deve ter: - -- **Clareza**: cada passo é compreensível e não deixa dúvida sobre o que fazer. -- **Finitude**: o processo termina em algum momento (computabilidade). -- **Determinismo (na maioria dos casos)**: mesmas entradas levam ao mesmo resultado. - (Há algoritmos probabilísticos, mas a disciplina começa com os determinísticos.) - -Essas características se aplicam **antes** da escolha da linguagem; são propriedades conceituais, independentes de Python, Java, SQL etc. - -### De algoritmo informal a código Python - -Exemplo da aula: **algoritmo para fazer café**. - -- Passos em linguagem natural: - 1. Ferva a água. - 2. Coloque o pó no filtro. - 3. Despeje a água. - 4. Sirva o café. - -- Representação em “pseudocódigo”: - -```text -Início - Ferver água - Colocar pó no filtro - Despejar água quente - Servir café -Fim -``` - -- Primeira versão em código Python: - -```python -def fazer_cafe(): - print("Ferva a água") - print("Coloque o pó no filtro") - print("Despeje a água") - print("Sirva o café") - - -fazer_cafe() -``` - -O código acima é propositalmente simples: ele mostra como um conjunto de instruções lineares pode ser “traduzido” para Python, mantendo os mesmos passos. - -### IDE e notebooks: onde esse código vive - -Para transformar algoritmos em programas executáveis, usamos ferramentas de desenvolvimento: - -- **IDE (Integrated Development Environment)**: ambiente de desenvolvimento integrado, com editor, execução, visualização de erros, etc. -- **Deepnote** é a IDE escolhida na disciplina, em formato de notebooks: - - **Workspace**: espaço de trabalho (pessoal ou de time), com projetos, permissões e integrações. - - **Projetos**: unidades de trabalho que agrupam notebooks, arquivos e variáveis de ambiente. - - **Blocos**: - - `Code blocks`: código Python (e outras linguagens, conforme ambiente). - - `SQL blocks`: consultas SQL para dados tabulares. - - `Text blocks`: documentação em Markdown. - - `Chart` e `Input blocks`: gráficos e interações (mais usados em dados). - -Na prática da disciplina, você vai: - -1. Acessar o Deepnote com seu e‑mail institucional. -2. Criar um **novo projeto** (por exemplo, `aula-02-algoritmos`). -3. Usar o notebook padrão criado para escrever e executar seus primeiros códigos Python. - -## Uso Prático - -### Exercício mental: algoritmos do seu dia a dia - -Alguns exemplos discutidos ou sugeridos em aula: - -- Planejar férias: - - decidir destino, orçamento, datas, hospedagem; - - reservar voos e hotéis na ordem correta; - - preparar lista de itens para viagem. -- Lavar louça e levar o lixo: - - verificar se há pratos sujos; - - lavar, enxaguar, colocar no escorredor até não restar mais nenhum; - - juntar o lixo, amarrar o saco, levar até o local de descarte. - -Você pode (e deve) desenhar esses algoritmos como: - -- passos numerados em texto; -- fluxogramas com losangos (decisão) e retângulos (ações); -- pseudocódigo parecido com o exemplo do café. - -### Deepnote na prática: seu primeiro notebook - -Passo a passo descrito pelo professor (adaptado): - -1. Acesse o site do Deepnote (`deepnote.com`) no navegador. -2. Clique em **Sign in** e entre com seu **e‑mail institucional**. -3. Verifique o e‑mail recebido (“Sign in now”) e clique no link. -4. Ao entrar, crie um **novo projeto** (por exemplo, `aula-02-algoritmos`). -5. No notebook criado automaticamente: - - certifique‑se de ter um bloco de código; - - escreva: - -```python -print("Hello, world!") -``` - - - execute o bloco (botão **Run** ou `Ctrl+Enter`) e observe a saída logo abaixo. - -Este é o seu **primeiro programa em Python** na disciplina, rodando em uma infraestrutura provisionada automaticamente pelo Deepnote (máquina virtual, ambiente Python, etc.), sem necessidade de instalar nada localmente. - -## Erros Comuns - -- **Confundir “usar LLM” com “saber programar”** - Pedir para um modelo de linguagem gerar todo o código impede a construção do seu próprio raciocínio algorítmico. Use IA como apoio para explicações e debugging, não como substituto para pensar o algoritmo. - -- **Pular a etapa do algoritmo e ir direto para código** - Começar a escrever Python sem ter clareza do problema e dos passos causa loops inesperados, condições incompletas e código confuso. - -- **Escrever algoritmos ambíguos ou infinitos** - Instruções vagas (“prepare o ambiente”, “arrume a cozinha”) não especificam ações concretas; esquecer uma condição de parada gera algoritmos que nunca terminam. - -- **Ignorar o papel do ambiente (IDE/notebook)** - Tentar aprender Python lutando com instalação e configuração de ambiente pode desmotivar quem está começando. A disciplina usa o Deepnote justamente para remover essa fricção inicial. - -## Visão Geral de Debugging - -Mesmo em nivel introdutório, vale adotar algumas práticas de debugging: - -- **Quando o notebook não executa**: - - verifique se há **mensagem de erro** logo abaixo do bloco (símbolos em vermelho, stack trace); - - leia a mensagem com calma, destacando termos como `SyntaxError`, `NameError`, etc.; - - confira se as aspas estão fechadas, parênteses balanceados e se os nomes estão escritos corretamente. - -- **Quando o algoritmo não produz o resultado esperado**: - - reescreva os passos em linguagem natural e verifique se estão claros e finitos; - - faça um “debug manual”: percorra mentalmente as instruções com um exemplo concreto de entrada. - -- **Quando o Deepnote se comporta de forma estranha**: - - confirme se está logado com o e‑mail institucional; - - recarregue o notebook ou crie um bloco de código novo; - - se necessário, desconecte e reconecte a máquina (kernel) nas opções do notebook. - -## Principais Pontos - -- Programar é, essencialmente, **resolver problemas** por meio de **algoritmos claros, finitos e (em geral) determinísticos**. -- Você já usa algoritmos no dia a dia (receitas, rotinas, planos) — a disciplina formaliza isso e o traduz para Python. -- Notebooks como os do **Deepnote** permitem combinar documentação, código e resultados em um único ambiente, ideal para aprendizado progressivo. -- O primeiro passo concreto é escrever e executar `print("Hello, world!")` em um bloco de código do notebook, garantindo que o ambiente esteja funcionando. - -## Preparação para Prática - -Após esta lição, você deve ser capaz de: - -- Escrever, em linguagem natural, algoritmos simples para tarefas comuns (cozinha, estudo, rotina diária). -- Converter um desses algoritmos em uma sequência de instruções em pseudocódigo ou fluxograma. -- Acessar o Deepnote com seu e‑mail institucional, criar um projeto e um notebook. -- Escrever e executar pequenos trechos de código Python (como `print(...)`) de forma confiável. - -Os exercícios do Laboratório de Prática a seguir vão ajud á-lo a fixar esses passos, ligando algoritmo → pseudocódigo → notebook Python. - -## Laboratório de Prática - -> Todos os códigos abaixo são boilerplates executáveis. Complete as partes marcadas com `TODO` no Editor Integrado. - -### Exercício Easy — Algoritmo do café em Python - -Transforme o algoritmo “fazer café” da aula em um programa Python que permita reutilizar os passos e imprimir o processo de forma clara. - -Requisitos: - -- Criar uma função `fazer_cafe()` que imprima cada passo do algoritmo. -- Permitir personalizar o tipo de café (por exemplo, “coado” ou “espresso”) apenas na mensagem final. - -```python -def fazer_cafe(tipo: str) -> None: - """ - Imprime os passos básicos para fazer café. - - Parâmetros: - tipo: descrição do tipo de café (ex.: "coado", "espresso"). - """ - # TODO: completar o corpo da função com prints para cada passo: - # - ferver a água - # - colocar o pó no filtro - # - despejar a água - # - servir o café, incluindo o tipo na mensagem final - pass - - -def main() -> None: - print("=== Algoritmo do café em Python ===") - tipo = input("Qual tipo de café você quer preparar? ") - fazer_cafe(tipo) - - -if __name__ == "__main__": - main() -``` - -### Exercício Medium — Algoritmo para lavar louça em pseudocódigo e código - -Baseado no exemplo discutido em aula (fluxograma de “lavar louça e levar o lixo”), escreva: - -1. O algoritmo em **pseudocódigo** (comentários estruturados). -2. Uma função Python que simule o processo, imprimindo o que está acontecendo. - -```python -# Pseudocódigo (em português): -# TODO: escreva aqui os passos de alto nível para: -# - verificar se há pratos sujos -# - enquanto houver pratos sujos, lavar e colocar no escorredor -# - ao final, indicar que a pia está limpa - - -def lavar_louca(quantidade_pratos: int) -> None: - """ - Simula o algoritmo de lavar louça, imprimindo os passos. - - Parâmetro: - quantidade_pratos: número inicial de pratos sujos. - """ - # TODO: implementar a lógica: - # enquanto ainda houver pratos (> 0), "lave" um por vez, decrementando o contador - # e imprimindo mensagens como "Lavando prato X..." - # No final, imprimir algo como "Pia limpa!" - pass - - -def main() -> None: - print("=== Algoritmo para lavar louça ===") - try: - pratos = int(input("Quantos pratos sujos há na pia? ")) - except ValueError: - print("Valor inválido. Use um número inteiro.") - return - - lavar_louca(pratos) - - -if __name__ == "__main__": - main() -``` - -### Exercício Hard — Registrar seu primeiro notebook no Deepnote - -Crie um **notebook no Deepnote** que combine: - -- uma seção de texto (Markdown) explicando, com suas palavras: - - o que é um algoritmo; - - um exemplo do seu dia a dia; - - por que notebooks ajudam no aprendizado; -- um bloco de código com pelo menos **dois exemplos**: - - o `Hello, world!` da aula; - - a chamada de uma das funções dos exercícios anteriores (por exemplo, `fazer_cafe`). - -Use o script abaixo como modelo de conteúdo mínimo de código. O texto em Markdown deve ser criado diretamente no notebook (não é avaliado via arquivo separado). - -```python -def hello() -> None: - print("Hello, world! Bem-vindo ao meu primeiro notebook Python no Deepnote.") - - -def main() -> None: - hello() - # TODO: aqui você deve chamar pelo menos UMA das funções - # que implementou em exercícios anteriores (por exemplo, fazer_cafe ou lavar_louca), - # com valores de exemplo. - - -if __name__ == "__main__": - main() -``` - - - - - diff --git a/content/python/aula-03-variaveis-tipos-e-estilo.md b/content/python/aula-03-variaveis-tipos-e-estilo.md deleted file mode 100644 index 75a2d0d..0000000 --- a/content/python/aula-03-variaveis-tipos-e-estilo.md +++ /dev/null @@ -1,480 +0,0 @@ ---- -title: "Variáveis, tipos de dados e estilo de código em Python" -slug: "variaveis-tipos-estilo-python" -discipline: "python" -order: 3 -description: "Como o Python armazena dados em variáveis, quais são os tipos básicos e como escrever código legível seguindo o Zen do Python e a PEP 8." -reading_time: 35 -difficulty: "easy" -concepts: - - variáveis - - tipos de dados - - int - - float - - bool - - string - - comentários - - case sensitive - - PEP 8 - - Zen do Python -prerequisites: - - "por-que-programar-python" - - "algoritmos-e-notebooks" -learning_objectives: - - "Explicar o que é uma variável e como ela se relaciona com memória em Python." - - "Identificar e utilizar corretamente os tipos básicos: int, float, bool e str." - - "Escrever nomes de variáveis claros, respeitando regras de sintaxe e convenções da PEP 8." - - "Usar comentários, docstrings e funções built‑in como type(), help() e dir() durante o desenvolvimento." -exercises: - - question: "Por que é importante dar nomes explícitos para variáveis em vez de usar nomes como `x` ou `v1`?" - answer: "Porque nomes explícitos tornam o código mais legível e autoexplicativo, alinhados ao Zen do Python ('explicit is better than implicit'), facilitando manutenção, debugging e colaboração." - hint: "Lembre do exemplo em que a variável é usada para armazenar algo com significado (idade, saldo, mensagem) e de como isso aparece nos prints." -review_after_days: [1, 3, 7, 30] ---- - -## Visão Geral do Conceito - -Esta lição apresenta a **base da manipulação de dados em Python**: variáveis, tipos de dados e estilo de código. - -Você vai ver: - -- como o Python usa **variáveis** como nomes para dados guardados em memória; -- quais são os **tipos básicos** (`int`, `float`, `bool`, `str`) e o que cada um representa; -- como o interpretador enxerga **nomes de variáveis** (case sensitive, palavras reservadas); -- como escrever código mais legível usando comentários, docstrings, o **Zen do Python** e a **PEP 8**. - -## Modelo Mental - -### Variáveis como “post‑its” apontando para a memória - -Uma boa forma de pensar em variáveis: - -- a memória RAM é um grande conjunto de “gavetas” numeradas; -- quando você escreve um comando de atribuição, por exemplo: - -```python -idade = 25 -``` - -o Python pede ao computador uma gaveta adequada para guardar o valor `25` e cola um “post‑it” com o nome `idade` apontando para aquela posição. - -> **Modelo mental:** o nome da variável não guarda o dado em si; ele aponta para um valor em memória. Ao reatribuir (`idade = 30`), você passa a apontar para outro valor. - -### Tipos de dados como significado do valor - -Cada valor em Python tem um **tipo**, que diz qual o significado e quais operações fazem sentido: - -- `int` (inteiro): números sem parte decimal (`1`, `0`, `-42`). -- `float` (ponto flutuante): números com casas decimais (`1.3`, `0.0`, `-2.5`). -- `bool` (booleano): valores lógicos `True` ou `False`. -- `str` (string): texto, ou seja, cadeias de caracteres como `"Introdução à programação com Python"`. - -O tipo orienta o interpretador sobre: - -- como armazenar o dado (representação interna); -- como operar com ele (soma, concatenação, comparações, etc.); -- que erros emitir quando uma combinação não faz sentido. - -### Estilo de código: Zen do Python e PEP 8 - -Para além de “funcionar”, código Python deve ser **pitônico** — legível, simples e explícito. -Do **Zen do Python** (obtido com `import this`), alguns princípios centrais: - -- “**Beautiful is better than ugly.**” -- “**Explicit is better than implicit.**” -- “**Simple is better than complex.**” - -A **PEP 8** (referência em `docAula.txt`) é o guia de estilo oficial que traduz esses princípios para regras concretas: - -- nomes de variáveis em minúsculas, com `snake_case`; -- espaçamento consistente; -- uso adequado de comentários e docstrings; -- organização do código em funções e módulos. - -## Mecânica Central - -### Atribuição e nomes de variáveis - -Uma atribuição simples em Python tem a forma: - -```python -nome_variavel = valor -``` - -Componentes: - -- **`nome_variavel`**: identificador que a partir de agora aponta para um valor em memória. -- **`=`**: operador de **atribuição**, não de igualdade matemática. -- **`valor`**: literal numérico, booleano, string, ou resultado de uma expressão. - -#### Regras de nomes de variáveis - -Em Python, nomes de variáveis: - -- **devem começar** com: - - uma letra (`a`–`z` ou `A`–`Z`), ou - - um sublinhado (`_`); -- podem continuar com letras, dígitos ou sublinhados; -- **não podem ser palavras reservadas** (keywords) da linguagem, como: - - `if`, `for`, `while`, `class`, `def`, `True`, `False`, etc. - -O módulo `keyword` permite listar todas as keywords: - -```python -import keyword - -print(keyword.kwlist) -``` - -#### Case sensitive - -Python é **case sensitive**, ou seja, diferencia maiúsculas de minúsculas: - -```python -variavel = 12 -Variavel = 25 - -print(variavel) # 12 -print(Variavel) # 25 -``` - -Nesse exemplo, `variavel` e `Variavel` são **variáveis diferentes**. -Por isso, a PEP 8 recomenda padronizar: nomes de variáveis em **minúsculas** e, quando compostos, usando `snake_case`: - -```python -idade_cliente = 30 -saldo_inicial = 1000.0 -``` - -### Tipos básicos e a função `type()` - -Você pode verificar o tipo de qualquer valor ou variável com a função built‑in `type()`: - -```python -print(type(1)) # -print(type(1.3)) # -print(type(True)) # -print(type("Python")) # -``` - -Com variáveis: - -```python -inteiro = 1 -flutuante = 1.3 -booleano = True -mensagem = "Introdução à programação com Python" - -print(type(inteiro)) -print(type(flutuante)) -print(type(booleano)) -print(type(mensagem)) -``` - -Python usa esses tipos para: - -- decidir como representar o valor internamente; -- validar operações (por exemplo, impedir que você some texto e número sem conversão); -- oferecer métodos específicos (por exemplo, `.upper()` para strings). - -### Comentários e docstrings - -Comentários servem para documentar **por que** o código faz algo, não apenas “o que faz”. Em Python: - -- **Comentário de linha única**: começa com `#` até o fim da linha. - -```python -# Esta variável guarda a idade mínima para acesso -idade_minima = 18 -``` - -- **Docstrings** (strings de documentação): geralmente usadas para documentar funções, classes e módulos, usando três aspas (`"""` ou `'''`): - -```python -def somar(a, b): - """ - Retorna a soma de dois números. - - Parâmetros: - a: primeiro número (int ou float) - b: segundo número (int ou float) - """ - return a + b -``` - -No início da aula, o professor também usa docstrings como **comentários de múltiplas linhas** em um notebook; isso funciona porque o valor da string é ignorado quando não é usado em nenhuma expressão. - -### Funções built‑in úteis: `help()` e `dir()` - -Python disponibiliza várias funções embutidas (**built‑ins**). Três muito úteis para estudo: - -- `print()`: imprime valores no console (você já está usando). -- `type()`: retorna o tipo de um objeto. -- `help()` e `dir()`: - -```python -print(dir(int)) # Lista atributos e métodos de int -help(int) # Mostra a documentação da classe int -``` - -Essas funções expõem a “auto‑documentação” da linguagem e são ferramentas importantes para entender melhor tipos e funções. - -## Uso Prático - -### Exemplo: definindo variáveis e inspecionando tipos - -```python -idade = 25 # int -altura = 1.73 # float -tem_carteira = True # bool -curso = "Introdução a Python" # str - -print(idade, type(idade)) -print(altura, type(altura)) -print(tem_carteira, type(tem_carteira)) -print(curso, type(curso)) -``` - -Saída esperada (aproximada): - -```text -25 -1.73 -True -Introdução a Python -``` - -### Exemplo: nomes ruins vs. bons nomes - -```python -# Nomes ruins (pouco informativos) -x = 25 -y = True -z = "ok" - -# Nomes melhores (explícitos) -idade_cliente = 25 -cliente_ativo = True -status_pedido = "aprovado" -``` - -Ambas as versões “funcionam”, mas apenas a segunda é legível para outra pessoa (ou para você daqui a algumas semanas). - -## Erros Comuns - -- **Esquecer o `#` no início de um comentário** - Escrever texto livre em um bloco de código sem comentar gera `SyntaxError: invalid syntax`. Sempre inicie comentários de linha com `#`. - -- **Começar nomes de variáveis com dígitos** - Nomes como `1var` ou `2resultado` são inválidos e geram erro de sintaxe. Use letras ou `_` no início. - -- **Reutilizar o mesmo nome sem perceber** - Atribuir um novo valor ao mesmo nome sobrescreve o anterior, sem aviso. Isso é esperado em Python, mas pode causar surpresas se você não lembrar onde a variável foi redefinida. - -- **Ignorar case sensitive** - Achar que `variavel` e `Variavel` são a mesma coisa leva a bugs difíceis de notar. Padronize para minúsculas. - -- **Misturar tipos sem conversão** - Tentar fazer algo como `"idade: " + 25` causa `TypeError`. É preciso converter (`str(25)`) ou usar f‑strings (`f"idade: {25}"`). - -## Visão Geral de Debugging - -Para depurar problemas relacionados a variáveis e tipos: - -- **Quando recebe `SyntaxError` em um comentário ou nome de variável**: - - confira se o comentário começa com `#`; - - verifique se o nome não começa com dígito e não contém espaços; - - revise se não usou palavra reservada (como `class`, `for`, `if`) como nome. - -- **Quando o valor não é o esperado**: - - adicione `print()` ao longo do notebook para inspecionar valores intermediários; - - use `type()` para conferir se o tipo está coerente com o que você imagina. - -- **Quando não lembra o que um tipo ou função faz**: - - use `help(objeto)` e `dir(objeto)` para explorar a interface; - - consulte a PEP 8 (`https://peps.python.org/pep-0008/`) para dúvidas de estilo. - -## Principais Pontos - -- Variáveis são **nomes** que apontam para valores em memória; a atribuição usa o operador `=`. -- Python é **case sensitive** e tem **palavras reservadas** que não podem ser usadas como nomes de variáveis. -- Os tipos básicos (`int`, `float`, `bool`, `str`) representam números inteiros, decimais, valores lógicos e texto; `type()` revela o tipo de qualquer valor. -- Comentários, docstrings, `help()` e `dir()` são aliados para escrever código legível e autoexplicativo, alinhado ao Zen do Python e à PEP 8. - -## Preparação para Prática - -Depois desta lição, você deve ser capaz de: - -- Criar variáveis com nomes claros, seguindo convenções de estilo. -- Identificar e usar corretamente `int`, `float`, `bool` e `str`. -- Ler e interpretar mensagens de erro simples ligadas a sintaxe e nomes. -- Usar `type()`, `help()` e `dir()` para entender melhor objetos do Python. - -Os exercícios do Laboratório de Prática abaixo vão consolidar esses conceitos em exemplos concretos ligados a dados do dia a dia. - -## Laboratório de Prática - -> Todos os códigos são boilerplates executáveis; complete onde houver `TODO`. - -### Exercício Easy — Cadastro simples de aluno - -Crie um pequeno script que guarda em variáveis os dados básicos de um aluno: - -- `nome` (string), -- `idade` (int), -- `media_nota` (float), -- `aprovado` (bool, baseado em média ≥ 7.0). - -Use `type()` para mostrar o tipo de cada variável. - -```python -def main() -> None: - # TODO: atribuir valores às variáveis abaixo, usando tipos adequados - nome = "" # str - idade = 0 # int - media_nota = 0.0 # float - - aprovado = media_nota >= 7.0 # bool derivado - - print("=== Cadastro de aluno ===") - print("Nome:", nome, "| tipo:", type(nome)) - print("Idade:", idade, "| tipo:", type(idade)) - print("Média:", media_nota, "| tipo:", type(media_nota)) - print("Aprovado:", aprovado, "| tipo:", type(aprovado)) - - -if __name__ == "__main__": - main() -``` - -### Exercício Medium — Verificador de nomes de variáveis - -Implemente uma função que receba uma string e verifique se ela **poderia** ser usada como nome de variável em Python, considerando: - -- não pode começar com dígito; -- não pode ser palavra reservada; -- pode conter apenas letras, dígitos e `_`. - -Use o módulo `keyword`. - -```python -import keyword - - -def eh_nome_valido(nome: str) -> bool: - """ - Retorna True se 'nome' pode ser usado como nome de variável em Python. - """ - # TODO: implementar as regras descritas acima - # 1. Verificar se nome está em keyword.kwlist. - # 2. Verificar primeiro caractere (letra ou '_'). - # 3. Verificar se todos os caracteres são letras, dígitos ou '_'. - return False - - -def main() -> None: - print("=== Verificador de nomes de variáveis ===") - candidatos = ["1var", "var", "for", "_oculta", "idade1", "Variavel", "True"] - - for c in candidatos: - print(f"{c!r}: {'válido' if eh_nome_valido(c) else 'inválido'}") - - -if __name__ == "__main__": - main() -``` - -### Exercício Hard — Diário de tipos com help() e dir() - -Escreva um script que: - -- Declara uma lista de exemplos de valores (`[0, 1.0, True, "python", [1, 2, 3]]`). -- Para cada valor: - - imprime o valor e o tipo; - - usa `dir()` para listar alguns métodos disponíveis; - - opcionalmente, mostra um trecho curto de `help()` para um dos métodos principais (por exemplo, `help(str.upper)`). - -Use comentários para explicar **o que você descobriu** sobre cada tipo. - -```python -def inspecionar_objeto(obj: object) -> None: - """ - Mostra informações básicas sobre um objeto usando type() e dir(). - """ - print("Valor:", repr(obj)) - print("Tipo:", type(obj)) - - atributos = dir(obj) - print("Alguns atributos/métodos:", atributos[:10]) # mostra só os 10 primeiros - - # TODO: para strings, chamar help(str.upper) uma vez e observar a documentação. - # Use um comentário abaixo para registrar o que você entendeu. - - -def main() -> None: - exemplos = [0, 1.0, True, "python", [1, 2, 3]] - - for objeto in exemplos: - print("\n---") - inspecionar_objeto(objeto) - - -if __name__ == "__main__": - main() -``` - - - - - diff --git a/content/python/aula-04-conversao-tipos-operadores-aritmeticos.md b/content/python/aula-04-conversao-tipos-operadores-aritmeticos.md deleted file mode 100644 index e98cced..0000000 --- a/content/python/aula-04-conversao-tipos-operadores-aritmeticos.md +++ /dev/null @@ -1,472 +0,0 @@ ---- -title: "Conversão de tipos e operadores aritméticos em Python" -slug: "conversao-tipos-operadores-aritmeticos" -discipline: "python" -order: 4 -description: "Como o Python converte valores entre tipos básicos e realiza operações aritméticas com segurança de tipos." -reading_time: 40 -difficulty: "easy" -concepts: - - conversão de tipos - - tipagem dinâmica - - tipagem forte - - int - - float - - bool - - string - - funções built-in - - operadores aritméticos - - precedência de operadores - - ValueError -prerequisites: - - "por-que-programar-python" - - "algoritmos-e-notebooks" - - "variaveis-tipos-estilo-python" -learning_objectives: - - "Entender o que significa o Python ter tipagem dinâmica e forte e como isso afeta conversões entre tipos." - - "Usar corretamente as funções de conversão `int()`, `float()`, `bool()` e `str()` em cenários reais." - - "Aplicar operadores aritméticos (+, -, *, /, //, %, **) respeitando a precedência padrão da linguagem." - - "Reconhecer e tratar erros comuns de conversão de tipos, como `ValueError` ao tentar converter textos não numéricos." -exercises: - - question: "Por que dizemos que o Python tem tipagem dinâmica, mas forte, e qual é o impacto prático disso na conversão de tipos?" - answer: "Tipagem dinâmica significa que o tipo de uma variável é inferido em tempo de execução a partir do valor atribuído (você não precisa declarar `int x` antes); tipagem forte significa que o Python não permite misturar tipos incompatíveis sem conversão explícita (por exemplo, não soma diretamente string com número nem converte um texto qualquer em `float`). Na prática, isso facilita a escrita do código (menos burocracia), mas exige cuidado ao converter e combinar valores de tipos diferentes." - hint: "Lembre dos exemplos em que `27` e `'python'` ocupam espaços de memória diferentes, das conversões bem-sucedidas de `'12.5'` para `float` e do erro ao tentar converter `'se aqui tiver um texto, o que acontece?'` para `float`." -review_after_days: [1, 3, 7, 30] ---- - -## Visão Geral do Conceito - -Nesta lição vamos aprofundar o que começamos em [[variaveis-tipos-estilo-python]]: além de declarar variáveis, precisamos **converter valores entre tipos** e **fazer contas** com eles. -Você vai ver como o Python usa tipagem dinâmica (ele descobre o tipo a partir do valor) e, ao mesmo tempo, tipagem forte (só combina tipos compatíveis), usando funções como `int()` e `float()` junto com operadores aritméticos. - -> **Ideia central:** entender **quando e como** converter valores entre tipos básicos e usar operadores aritméticos com segurança é o que torna seus notebooks capazes de lidar com entradas reais (texto, números de sensores, valores vindo de planilhas ou APIs) sem quebrar. - -## Modelo Mental - -Pense na memória do computador como uma **prateleira de caixas**. -Cada tipo básico do Python ocupa um número diferente de “caixas” nessa prateleira: um `int` precisa de menos espaço do que uma `str` longa, por exemplo. - -Quando você escreve: - -```python -valor_1 = 27 # int -valor_2 = "python" # str -``` - -o interpretador: - -- cria duas variáveis (nomes/literais) `valor_1` e `valor_2`; -- **infere** que `27` é um número inteiro e `"python"` é texto; -- reserva na memória blocos adequados para cada tipo; -- guarda o valor em cada bloco e associa o nome da variável a esse endereço. - -A **conversão de tipos** é como pedir ao Python: “pegue o que está nessa caixa e me entregue numa caixa de outro formato”. -Às vezes é fácil (converter `"12.5"` para número), às vezes é impossível (converter `"se aqui tiver um texto, o que acontece?"` para número). - -## Mecânica Central - -### Tipagem dinâmica e forte - -- **Tipagem dinâmica:** você não declara o tipo antes; o Python descobre observando o valor: - -```python -idade = 27 # inferido como int -preco = 13.9 # inferido como float -aprovado = True # inferido como bool -curso = "python" # inferido como str -``` - -- **Tipagem forte:** o Python **não mistura** automaticamente tipos incompatíveis. - - Exemplo: `27 + "3"` gera `TypeError`. - - Exemplo: `float("se aqui tiver um texto, o que acontece?")` gera `ValueError`. - -### Funções built-in de conversão - -As principais funções de conversão entre tipos básicos são: - -- `int(x)`: tenta converter `x` para inteiro; -- `float(x)`: tenta converter `x` para número de ponto flutuante; -- `bool(x)`: converte `x` para valor lógico (`True`/`False`); -- `str(x)`: converte `x` para string. - -Exemplo de conversão bem-sucedida e mal-sucedida: - -```python -primeira_variavel = "12.987645812330881" -variavel_convertida = float(primeira_variavel) # OK - -print(type(primeira_variavel), primeira_variavel) -print(type(variavel_convertida), variavel_convertida) - -primeira_variavel = "se aqui tiver um texto, o que acontece?" -variavel_convertida = float(primeira_variavel) # ValueError! -``` - -Nesse segundo caso, o Python não sabe transformar aquela frase em um número, então lança um `ValueError`. - -### Operadores aritméticos básicos - -Os operadores que usamos no dia a dia com números em Python são: - -- `+` — soma -- `-` — subtração -- `*` — multiplicação -- `/` — divisão com resultado `float` -- `//` — divisão inteira (piso) -- `%` — módulo (resto da divisão) -- `**` — exponenciação - -#### Precedência de operações - -A ordem padrão de avaliação, da maior prioridade para a menor, é: - -1. Parênteses `()`. -2. Exponenciação `**`. -3. Multiplicação, divisão, divisão inteira, módulo (`*`, `/`, `//`, `%`). -4. Adição e subtração (`+`, `-`). - -```python -nota_matematica = 6.8 -nota_programacao = 8.1 - -soma_notas = nota_matematica + nota_programacao -media = soma_notas / 2 - -print("Nota de Matemática:", nota_matematica) -print("Nota de Programação:", nota_programacao) -print("Soma das notas:", soma_notas) -print("Média:", media) -``` - -### Fluxo de conversão segura - -Um fluxo típico ao trabalhar com dados que chegam como texto (de inputs, CSVs ou APIs) é: - -```mermaid -flowchart TD - A[Texto de entrada
ex: '12.5'] --> B{Texto parece numérico?} - B -- não --> C[Registrar erro
ou pedir nova entrada] - B -- sim --> D[Escolher tipo alvo
int, float, bool] - D --> E[Chamar função de conversão
int(), float(), bool()] - E --> F{Conversão funcionou?} - F -- não (ValueError) --> C - F -- sim --> G[Usar valor convertido
em cálculos] -``` - -Esse diagrama resume o que você viu na aula: usar conversão **apenas quando faz sentido**, e tratar o caso em que o texto não é numérico. - -## Uso Prático - -### Exemplo 1: convertendo leituras de temperatura - -Imagine um arquivo CSV com a temperatura de um sensor em graus Celsius, mas tudo como texto: - -```python -leituras_celsius_brutas = ["22.5", "23.0", "21.8", "erro", "24.1"] - -temperaturas_validas = [] - -for leitura in leituras_celsius_brutas: - try: - temperatura = float(leitura) - temperaturas_validas.append(temperatura) - except ValueError: - print("Leitura inválida ignorada:", leitura) - -print("Temperaturas válidas:", temperaturas_validas) -``` - -Aqui combinamos: - -- `float()` para conversão, -- tratamento de `ValueError`, -- operadores aritméticos (que você pode usar depois para média, máximo, etc.). - -### Exemplo 2: dashboard simples de notas - -```python -nota_matematica = 6.8 -nota_programacao = 8.1 - -soma = nota_matematica + nota_programacao -media = soma / 2 - -print("Nota de Matemática:", nota_matematica) -print("Nota de Programação:", nota_programacao) -print("Soma das Notas:", soma) -print("Média:", media) -``` - -Você pode imaginar esse cálculo sendo feito antes de alimentar um dashboard de desempenho de alunos no seu projeto de dados. - -### Exemplo 3: divisão inteira, resto e exponenciação - -```python -valor1 = 17 -valor2 = 4 - -piso = valor1 // valor2 # divisão inteira -resto = valor1 % valor2 # resto da divisão -potencia = valor1 ** valor2 # exponenciação - -print("Piso:", piso) -print("Resto:", resto) -print("Potência:", potencia) -``` - -Esses operadores aparecem em diversas situações: particionar registros em lotes, calcular índices de páginas, gerar IDs, trabalhar com criptografia e mais. - -## Erros Comuns - -- **Tentar converter qualquer texto em número** - - Exemplo: `float("se aqui tiver um texto, o que acontece?")`. - - Resultado: `ValueError: could not convert string to float`. - -- **Esquecer que `.` é o separador decimal** - - Exemplo: escrever `13,9` em vez de `13.9` no código. - - O Python entende `13,9` como duas expressões separadas por vírgula (um *tuple*), não como número decimal. - -- **Confundir divisão normal e divisão inteira** - - `17 / 4` → `4.25` (resultado `float`); - - `17 // 4` → `4` (parte inteira). - -- **Misturar string com número em operações aritméticas** - - Exemplo: `"Total: " + 100` → `TypeError`. - - Correto: `"Total: " + str(100)` ou `f"Total: {100}"`. - -## Visão Geral de Debugging - -Quando surgir um erro relacionado a tipos (`TypeError`, `ValueError`), siga este roteiro: - -1. **Inspecione o valor e o tipo** com `print()` e `type()`: - - ```python - print(repr(valor), type(valor)) - ``` - -2. **Pergunte-se**: “Esse valor pode, de forma lógica, ser convertido para o tipo que estou pedindo?” - - `"27"` → pode virar número; - - `"vinte e sete"` → não pode virar `int` sem tratamento extra. - -3. **Localize a linha exata do erro** olhando o *stack trace* (a mensagem mostra o número da linha e o arquivo). - -4. **Simplifique a expressão**: quebre em etapas menores (primeiro converta, depois some, depois formate para texto). - -5. **Use `try`/`except` com parcimônia**: trate explicitamente o caso de conversão inválida, em vez de simplesmente “engolir” qualquer exceção. - -## Principais Pontos - -- O Python **infere** o tipo de uma variável a partir do valor atribuído (tipagem dinâmica), mas **não mistura tipos incompatíveis** (tipagem forte). -- As funções `int()`, `float()`, `bool()` e `str()` fazem conversão explícita entre tipos básicos e geram erros quando a conversão não faz sentido. -- Operadores aritméticos (`+`, `-`, `*`, `/`, `//`, `%`, `**`) seguem uma ordem de precedência que impacta o resultado das expressões. -- Tratar corretamente conversões e operadores é essencial para ler dados externos (inputs, CSVs, APIs) e alimentar pipelines e dashboards sem que o notebook quebre. - -## Preparação para Prática - -Depois desta lição, você deve ser capaz de: - -- Ler valores de entrada como texto e convertê-los explicitamente para o tipo certo antes de calcular. -- Escolher entre divisão normal (`/`), inteira (`//`) e módulo (`%`) dependendo do que o problema pede. -- Identificar rapidamente quando um erro foi causado por uma conversão impossível (como tentar transformar um texto qualquer em número) e corrigi-lo. - -No **Laboratório de Prática**, você vai aplicar essas ideias em mini-projetos de dados: conversão de temperaturas, cálculo de área/perímetro e um pequeno “analisador” numérico de três valores. - -## Laboratório de Prática - -### Desafio Easy — Converter leituras de temperatura - -Você está recebendo leituras de temperatura de sensores em um arquivo CSV, mas todas chegam como **texto**. -Implemente uma função que receba uma lista de strings com temperaturas em graus Celsius, **converta para `float`** e retorne uma lista com os valores em Fahrenheit. - -Regra de conversão: \( F = C \times \frac{9}{5} + 32 \). - -```python -from typing import Iterable, List - -def converter_celsius_para_fahrenheit(leituras_celsius: Iterable[str]) -> List[float]: - """ - Recebe uma coleção de strings representando temperaturas em graus Celsius - (por exemplo, ["22.5", "23.0", "erro"]) e retorna uma lista com as - temperaturas válidas convertidas para Fahrenheit. - - Leituras que não puderem ser convertidas para float devem ser ignoradas, - mas o programa não deve quebrar. - """ - resultados: List[float] = [] - - # TODO: para cada leitura: - # 1. Tentar converter a string para float usando float(). - # 2. Se a conversão funcionar, aplicar a fórmula de conversão para Fahrenheit. - # 3. Adicionar o resultado na lista `resultados`. - # 4. Se der ValueError, simplesmente ignore aquela leitura. - - return resultados - - -def main() -> None: - leituras = ["22.5", "23.0", "21.8", "erro", "24.1"] - temperaturas_f = converter_celsius_para_fahrenheit(leituras) - print("Leituras originais:", leituras) - print("Temperaturas em Fahrenheit:", temperaturas_f) - - -if __name__ == "__main__": - main() -``` - -### Desafio Medium — Área e perímetro de um retângulo a partir de strings - -Você está implementando uma pequena calculadora de layout de dashboards, em que a largura e altura dos “cards” chegam como texto (por exemplo, a partir de um formulário em uma aplicação web). -Implemente uma função que receba `largura` e `altura` como strings, converta para `float` e retorne área e perímetro. - -Se qualquer conversão falhar, a função deve retornar `None`. - -```python -from typing import Optional, Tuple - -def calcular_area_e_perimetro_retangulo(largura_str: str, altura_str: str) -> Optional[Tuple[float, float]]: - """ - Recebe largura e altura de um retângulo como strings (por exemplo "10.5" e "3") - e tenta converter para float. Se der certo, retorna (area, perimetro). - Se qualquer conversão falhar, retorna None. - """ - # TODO: - # 1. Tentar converter largura_str e altura_str para float. - # 2. Calcular área (largura * altura). - # 3. Calcular perímetro (2 * (largura + altura)). - # 4. Retornar (area, perimetro). - # 5. Se alguma conversão der ValueError, retornar None. - - return None - - -def main() -> None: - exemplos = [("10", "5"), ("7.5", "3.2"), ("largura", "4")] - for largura, altura in exemplos: - resultado = calcular_area_e_perimetro_retangulo(largura, altura) - print(f"Entrada: largura={largura!r}, altura={altura!r} => resultado: {resultado}") - - -if __name__ == "__main__": - main() -``` - -### Desafio Hard — Mini-analisador numérico de três valores - -Em um caderno de experimentos, você costuma registrar **três medições** (por exemplo, tempo de resposta de uma API, temperatura média de um servidor, número de acessos). -Implemente uma função que receba três valores como **strings**, converta para `float` e calcule: - -- média aritmética; -- média geométrica; -- desvio padrão (populacional) dos três valores; -- dobro da soma; -- triplo do produto; -- raiz quadrada da soma dos quadrados. - -Use apenas operadores aritméticos básicos e funções de conversão. - -```python -from math import sqrt -from typing import Dict, Optional - - -def analisar_tres_valores(a_str: str, b_str: str, c_str: str) -> Optional[Dict[str, float]]: - """ - Recebe três valores numéricos como strings, converte para float e retorna - um dicionário com várias estatísticas: - - - media_aritmetica - - media_geometrica - - desvio_padrao - - dobro_da_soma - - triplo_do_produto - - raiz_soma_quadrados - - Se qualquer conversão falhar, retorna None. - """ - # TODO: - # 1. Converter a_str, b_str e c_str para float. - # 2. Calcular todas as estatísticas descritas acima usando apenas - # operadores aritméticos (+, -, *, /, **, //, %) e sqrt(). - # 3. Montar e retornar um dicionário com esses resultados. - # 4. Se alguma conversão der ValueError, retornar None. - - return None - - -def main() -> None: - exemplos = [ - ("10", "20", "30"), - ("1.5", "3.0", "4.5"), - ("dez", "20", "30"), # deve falhar na conversão - ] - - for a, b, c in exemplos: - resultado = analisar_tres_valores(a, b, c) - print(f"Entradas: {a!r}, {b!r}, {c!r}") - print("Resultado:", resultado) - print("-" * 40) - - -if __name__ == "__main__": - main() -``` - - - - - diff --git a/content/python/aula-05-strings-literais-multilinhas.md b/content/python/aula-05-strings-literais-multilinhas.md deleted file mode 100644 index 1c9e96e..0000000 --- a/content/python/aula-05-strings-literais-multilinhas.md +++ /dev/null @@ -1,217 +0,0 @@ ---- -title: "Strings em Python: aspas, literais e textos multilinha" -slug: "strings-literais-multilinhas" -discipline: "python" -order: 5 -description: "Como o Python representa texto com strings, diferenças entre aspas simples e duplas, uso de strings multilinha e erros típicos com aspas." -reading_time: 40 -difficulty: "easy" -concepts: - - strings - - literais de string - - aspas simples - - aspas duplas - - strings multilinha - - docstrings - - SyntaxError - - representação de texto na memória -prerequisites: - - "por-que-programar-python" - - "algoritmos-e-notebooks" - - "variaveis-tipos-estilo-python" - - "conversao-tipos-operadores-aritmeticos" -learning_objectives: - - "Entender o que é uma string em Python e como ela é armazenada em memória." - - "Criar strings usando aspas simples, aspas duplas e literais multilinha com três aspas." - - "Identificar e corrigir erros comuns de aspas em literais de string (especialmente `SyntaxError: unterminated string literal`)." - - "Usar strings multilinha para textos longos, docstrings e templates em projetos de dados." -exercises: - - question: "Por que `poema = 'Que pode uma criatura senão,\\nentre criaturas, amar?` (com quebra de linha no meio) gera um `SyntaxError` em Python, e como corrigir isso mantendo o texto em múltiplas linhas?" - answer: "Porque literais de string definidos com aspas simples ou duplas precisam começar e terminar na mesma linha; ao quebrar a linha sem indicar continuação, o Python entende que a string terminou e encontra texto 'solto', gerando `SyntaxError: unterminated string literal`. Para manter o texto em múltiplas linhas, você deve usar três aspas simples ou duplas (`''' ... '''` ou \"\"\" ... \"\"\"), criando uma string multilinha (docstring)." - hint: "Lembre do exemplo com o poema de Carlos Drummond e da diferença entre `'texto'` e `'''texto\\nem\\nvarias\\nlinhas'''`." -review_after_days: [1, 3, 7, 30] ---- - -## Visão Geral do Conceito - -Strings (`str`) são o tipo básico para representar **texto** em Python: tudo que está entre aspas é guardado como uma sequência de caracteres na memória. -Nesta aula você aprende a definir strings com aspas simples, aspas duplas e três aspas, e a evitar o erro clássico de quebrar o texto no meio de um literal. - -## Modelo Mental - -Pense em uma string como uma **linha de caracteres em caixas consecutivas** na RAM. -O literal entre aspas diz ao Python “guarde exatamente estes caracteres, nesta ordem”; o nome da variável aponta para o início dessa sequência. -Quando há quebras de linha, elas são apenas **caracteres especiais** (`\n`) dentro da mesma sequência. - -## Mecânica Central - -- `aspas_simples = 'texto'` -- `aspas_duplas = "texto"` -- `multilinha = '''linha 1\nlinha 2\nlinha 3'''` (três aspas simples ou três aspas duplas). - -Com **uma** aspa de cada lado (`'...'` ou `"..."`), o Python exige que o literal esteja em **uma única linha**; se você apertar Enter no meio, obtém: - -```text -SyntaxError: unterminated string literal -``` - -Para textos em várias linhas (como o poema do Carlos Drummond da aula), use três aspas: - -```python -poema = '''Que pode uma criatura senão, -entre criaturas, amar?''' -``` - -E para colocar aspas dentro do texto: - -- use aspas duplas por fora quando o texto tem aspas simples: `"texto entre 'aspas'"`; -- use aspas simples por fora quando o texto tem aspas duplas: `'Lorem "Ipsum" vem de...'`. - -```mermaid -flowchart TD - A[Quero escrever texto] --> B{Cabe em 1 linha?} - B -- sim --> C[Usar '...' ou "..."] - B -- não --> D[Usar '''...''' ou """..."""] - C --> E{Texto contém aspas simples?} - E -- sim --> F[Usar aspas duplas por fora] - E -- não --> G[Escolher estilo preferido] -``` - -## Uso Prático - -- Documentar um caderno de experimentos com um **bloco de texto multilinha** em uma variável. -- Guardar um **e‑mail de notificação** ou um **modelo de mensagem** como string de três aspas. -- Representar corretamente frases com aspas internas, como o exemplo de *Lorem Ipsum* da aula. - -## Erros Comuns - -- Quebrar um literal `'...'` ou `"..."` no meio da linha → `SyntaxError: unterminated string literal`. -- Esquecer de alternar o tipo de aspas quando o texto já contém aspas internas. -- Confundir o texto exibido no notebook com o literal em si (achar que pode “colar do PDF” sem adaptar para uma string válida). - -## Visão Geral de Debugging - -Quando aparecer um `SyntaxError` relacionado a string: - -1. Veja **onde** o erro foi detectado (linha indicada pela mensagem). -2. Confira se o literal começou e terminou na mesma linha e se há o mesmo tipo de aspa abrindo e fechando. -3. Se o texto é longo ou possui quebras de linha, converta para três aspas. - -## Principais Pontos - -- Strings são sequências de caracteres entre aspas, todas do tipo `str`. -- Literais com uma aspa de cada lado precisam caber em uma linha; quebras “cruas” geram `SyntaxError`. -- Três aspas criam strings multilinha, ideais para poemas, e‑mails, docstrings e templates. -- Alternar entre aspas simples e duplas é a forma mais simples de incluir aspas dentro do texto. - -## Preparação para Prática - -Você deve ser capaz de: - -- reescrever textos de múltiplas linhas do mundo real em literais Python válidos; -- decidir rapidamente quando usar `'...'`, `"..."` ou `'''...'''`; -- corrigir erros de aspas em código próprio ou de colegas. - -## Laboratório de Prática - -### Desafio Easy — Guardar um fragmento de poema - -Implemente uma função que receba três linhas de texto e devolva uma **string multilinha** com o fragmento formatado, usando três aspas. - -```python -def montar_fragmento_poema(linha1: str, linha2: str, linha3: str) -> str: - """ - Monta um pequeno poema em três linhas, usando uma string multilinha. - """ - # TODO: retornar uma string criada com ''' ... ''' contendo - # as três linhas em linhas separadas. - poema = "" - return poema -``` - -### Desafio Medium — Mensagem com aspas internas - -Crie uma função que devolva uma mensagem de aviso contendo **aspas simples e duplas** no texto, sem gerar `SyntaxError`. - -```python -def mensagem_com_aspas() -> str: - """ - Retorna algo como: - Atenção: o campo "observação" não pode conter o caractere '|' . - """ - # TODO: escolher o tipo de aspas externas adequado - # para que as aspas internas apareçam corretamente. - mensagem = "" - return mensagem -``` - -### Desafio Hard — Normalizar texto colado do PDF - -Suponha que você recebeu um parágrafo colado de um PDF (com várias quebras de linha) e quer transformá‑lo em uma **string de uma linha só**, pronta para ser exibida em logs. - -```python -from typing import Iterable - -def juntar_linhas_em_uma(linhas: Iterable[str]) -> str: - """ - Recebe várias linhas (como lidas de um arquivo de texto) - e devolve uma única string, com espaços entre as linhas. - """ - # TODO: - # 1. Juntar as linhas removendo quebras de linha. - # 2. Garantir que não haja espaços duplos desnecessários. - texto = "" - return texto -``` - - - - - diff --git a/content/python/aula-06-strings-escape-concatenacao.md b/content/python/aula-06-strings-escape-concatenacao.md deleted file mode 100644 index fb5f121..0000000 --- a/content/python/aula-06-strings-escape-concatenacao.md +++ /dev/null @@ -1,500 +0,0 @@ ---- -title: "Strings em Python: escapes, concatenação e repetição" -slug: "strings-escape-concatenacao" -discipline: "python" -order: 6 -description: "Como controlar quebras de linha, tabulações, barras invertidas e concatenar ou repetir strings em Python com segurança de tipos." -reading_time: 45 -difficulty: "easy" -concepts: - - strings - - caracteres de escape - - barra invertida - - \n - - \t - - raw string - - concatenação - - repetição de strings - - tipagem forte - - TypeError -prerequisites: - - "por-que-programar-python" - - "algoritmos-e-notebooks" - - "variaveis-tipos-estilo-python" - - "conversao-tipos-operadores-aritmeticos" -learning_objectives: - - "Usar caracteres de escape como \\n e \\t para controlar quebras de linha e tabulações em strings." - - "Explicar a diferença entre strings normais e raw strings (prefixo r'...')." - - "Concatenar e repetir strings com os operadores + e * respeitando as regras de tipos do Python." - - "Identificar e corrigir erros típicos de mistura entre int e str em operações com +." -exercises: - - question: "Por que a expressão `123 + 'Aqui eh uma string'` em Python gera um `TypeError`, enquanto `str(123) + 'Aqui eh uma string'` funciona?" - answer: "Porque o Python tem tipagem forte e não permite somar diretamente um inteiro (`int`) com uma string (`str`); o operador + não sabe se deve somar números ou concatenar texto. Ao usar `str(123)`, você converte explicitamente o número para string e passa a ter dois operandos do mesmo tipo (`str`), permitindo que + seja interpretado como concatenação." - hint: "Lembre da explicação de tipagem dinâmica e forte, e dos exemplos em que o Python reclama de 'unsupported operand type(s) for +: int and str'." -review_after_days: [1, 3, 7, 30] ---- - -## Visão Geral do Conceito - -Depois de aprender a criar strings simples e multilinha, o próximo passo é **controlar como esse texto aparece**: em uma única linha ou em várias, alinhado em colunas, contendo barras invertidas literais, etc. -Esta lição mostra como o Python usa a barra invertida (`\`) para **caracteres de escape** (como `\n` e `\t`), o que são **raw strings** e como usar os operadores `+` e `*` para concatenar e repetir strings com segurança de tipos. - -> **Ideia central:** você precisa dominar a barra invertida e os operadores `+` e `*` para escrever mensagens legíveis, logs formatados e saídas de terminal organizadas sem cair em erros de aspas e tipos. - -## Modelo Mental - -Pense nas strings como **linhas invisíveis em uma tela**. - -- O interpretador lê os caracteres da esquerda para a direita. -- Quando encontra `\n`, ele entende “**pule para a próxima linha**”. -- Quando encontra `\t`, ele entende “**avance até a próxima coluna de tabulação**”. -- Quando encontra `\\`, ele entende “**mostre uma barra invertida normal**”. - -Em strings “normais”, a barra invertida **ativa esses poderes especiais**. -Em **raw strings** (prefixo `r`), a barra invertida deixa de ser especial e passa a ser apenas **mais um caractere** da string. - -Os operadores: - -- `+` age como **cola**: junta duas strings em uma só; -- `*` age como **repetidor**: pega uma string e a repete N vezes. - -Mas, por conta da tipagem forte, o Python **só permite** essas operações se os tipos forem compatíveis. - -## Mecânica Central - -### Quebras de linha com `\n` - -No terminal ou no notebook, `\n` significa **nova linha**. - -```python -texto = "Contrary to popular belief,\nLorem Ipsum is not simply random text." -print(texto) -print(type(texto)) -``` - -Saída: - -```text -Contrary to popular belief, -Lorem Ipsum is not simply random text. - -``` - -Note que a string é uma só (`str`), mas visualmente aparece em **duas linhas**. - -### Tabulações com `\t` - -O caractere de escape `\t` representa uma **tabulação horizontal** — um avanço até a próxima “coluna de tab” na linha. - -```python -print("Lorem Ipsum: 123") -print("Lorem:\t123") -print("Lorem Ipsum simply:\t123") -``` - -Uso típico em ADS: alinhar colunas simples de texto no terminal (nome, endereço, valor, etc.) antes de partir para bibliotecas mais sofisticadas ou dashboards. - -### Escapando a barra invertida `\\` - -Se você escrever: - -```python -text = 'aqui está um texto explicativo sobre a "barra invertida" - \' -print(text) -``` - -o Python gera: - -```text -SyntaxError: unterminated string literal -``` - -porque ele entende que a barra invertida está tentando **escapar** o próximo caractere (a aspa), “quebrando” o literal. - -Para mostrar **uma** barra invertida na saída, você precisa escapar a própria barra: - -```python -text = 'aqui está um texto explicativo sobre a "barra invertida" - \\' -print(text) -``` - -Saída: - -```text -aqui está um texto explicativo sobre a "barra invertida" - \ -``` - -### Raw strings (`r"..."`) - -Em muitos cenários (caminhos de arquivo no Windows, expressões regulares, textos com muitas barras), é cansativo escapar cada `\`. -Nesses casos, você pode prefixar a string com `r` para dizer ao Python: **trate todas as barras invertidas como texto bruto**. - -```python -texto = r"Lorem ipsum dolor sit amet,\nconsectetur adipiscing elit,\nsed do eiusmod tempor incididunt ut labore." -print(texto) -``` - -Saída (tudo em uma linha, com `\n` aparecendo literalmente): - -```text -Lorem ipsum dolor sit amet,\nconsectetur adipiscing elit,\nsed do eiusmod tempor incididunt ut labore. -``` - -Compare com a versão sem `r`, em que `\n` vira quebra de linha: - -```python -texto = "Lorem ipsum dolor sit amet,\nconsectetur adipiscing elit,\nsed do eiusmod tempor incididunt ut labore." -print(texto) -``` - -### Operador `+` para concatenação - -O operador `+` é um **operador coringa**: com números, ele soma; com strings, ele concatena. - -```python -nome = "Python" -sobrenome = "Programming" - -nome_completo = nome + " " + sobrenome -print(nome_completo) # Python Programming -``` - -Mas misturar `int` e `str` sem conversão gera erro: - -```python -numero = 123 -string = "Aqui eh uma string" - -concatenar_numero_string = numero + string -print(concatenar_numero_string) -``` - -Erro: - -```text -TypeError: unsupported operand type(s) for +: 'int' and 'str' -``` - -Para concatenar, você precisa converter explicitamente: - -```python -concatenar_numero_string = str(numero) + " " + string -print(concatenar_numero_string) # 123 Aqui eh uma string -``` - -Ou, se quiser somar valores numéricos, converter a string para número: - -```python -numero_em_string = "123" -resultado = numero + int(numero_em_string) -print(resultado) # 246 -``` - -### Operador `*` para repetição de strings - -O operador `*` com uma string e um inteiro **repete** a string: - -```python -nome = "Python" -resultado_multiplicacao_string = 5 * nome -print(resultado_multiplicacao_string) -``` - -Saída: - -```text -PythonPythonPythonPythonPython -``` - -Isso é muito útil para criar divisórias e “molduras” em saídas de terminal: - -```python -linha = "+" + 30 * "-" + "+" -print(linha) -print("Name:", "John Doe") -print("Addr:", "Avenue A") -print(linha) -``` - -### Diagrama: como o Python interpreta escapes em strings - -```mermaid -flowchart TD - A[Digite o literal de string] --> B{Tem prefixo r?} - B -- sim --> C[Tratar todas as barras invertidas
como texto normal] - B -- não --> D{Encontrei '\\'?} - D -- não --> E[Adicionar caractere normal
à string em memória] - D -- sim --> F{Sequência especial? \n, \t, \\...} - F -- sim --> G[Converter para caractere especial
(nova linha, tab, barra...)] - F -- não --> H[Erro de sintaxe ou escape inválido] -``` - -Esse fluxo resume o que acontece nos exemplos da aula: sem `r`, `\n` e `\t` são interpretados; com `r`, tudo vira texto literal. - -## Uso Prático - -### Logs de múltiplas linhas com `\n` - -```python -status = "OK" -mensagem_log = ( - "Processo de ingestão finalizado.\n" - f"Status: {status}\n" - "Origem: arquivo CSV 'clientes.csv'\n" -) - -print(mensagem_log) -``` - -### Tabelas simples com `\t` - -```python -print("Nome\t\tSaldo") -print("João\t\t1250.50") -print("Maria\t\t980.00") -print("Anderson\t300.00") -``` - -### Caminhos de arquivos com raw strings - -```python -caminho = r"C:\Users\aluno\Downloads\dados.csv" -print("Lendo arquivo em:", caminho) -``` - -Sem `r`, você teria de escrever `"C:\\Users\\aluno\\Downloads\\dados.csv"` para não quebrar a string. - -## Erros Comuns - -- **Misturar `int` e `str` em `+`** - - Causa: `123 + "abc"`. - - Sintoma: `TypeError: unsupported operand type(s) for +: 'int' and 'str'`. - - Correção: converter para o tipo adequado (`str()` ou `int()` / `float()`), dependendo se você quer concatenar ou somar. - -- **Esquecer de escapar barra invertida** - - Causa: caminho de arquivo `"C:\novo\arquivo.txt"` (sem escapar). - - Sintoma: `\n` vira quebra de linha, ou `SyntaxError` em alguns casos. - - Correção: usar `\\` ou prefixar com `r"..."`. - -- **Esperar que raw strings interpretem `\n`** - - Causa: usar `r"linha1\nlinha2"` achando que haverá quebra de linha. - - Sintoma: `\n` aparece literalmente no output. - - Correção: remover `r` quando quiser o efeito de quebra de linha. - -## Visão Geral de Debugging - -Quando algo estranho acontece com uma string (quebra de linha onde não devia, barra sumindo, erro de `TypeError`), siga estes passos: - -1. **Inspecione o valor cru** com `repr()`: - - ```python - print(repr(texto)) - ``` - - Assim você enxerga os `\n`, `\t` e `\\` explicitamente. - -2. **Cheque o tipo**: - - ```python - print(type(texto)) - ``` - - Verifique se o dado está como `str` ou como número. - -3. **Simplifique a expressão**: se o erro está em `numero + string`, teste só `numero`, só `string` e depois adicione `str()` ou `int()` aos poucos. - -4. **Suspeite da barra invertida** quando uma string “quebra” ou um erro de sintaxe aparece perto de `\`. - -## Principais Pontos - -- `\n` quebra a linha; `\t` insere uma tabulação; `\\` mostra uma barra invertida. -- Raw strings (`r"..."`) tratam **todas** as barras invertidas como texto literal, sem interpretar escapes. -- O operador `+` concatena strings, mas gera `TypeError` quando mistura `int` e `str` sem conversão explícita. -- O operador `*` permite repetir strings, sendo ótimo para criar separadores e molduras em saídas de terminal. - -## Preparação para Prática - -Depois desta lição, você deve ser capaz de: - -- gerar mensagens de log e relatórios simples com quebras de linha e colunas alinhadas; -- construir molduras de texto reutilizáveis com `*` e caracteres simples; -- decidir quando usar raw strings (por exemplo em caminhos de arquivo ou regex); -- evitar e corrigir erros de concatenação envolvendo strings e números. - -No **Laboratório de Prática**, você vai aplicar esses conceitos em contextos de ADS: registro de logs, formatação de uma mini-tabela de clientes e criação de um cabeçalho reutilizável para relatórios de terminal. - -## Laboratório de Prática - -### Desafio Easy — Log formatado com quebras de linha - -Você está construindo um script que lê arquivos CSV de uma pasta e quer registrar um **log legível** toda vez que um arquivo é processado. -Implemente uma função que receba o nome do arquivo e a quantidade de linhas lidas e retorne uma string de log com múltiplas linhas. - -```python -def montar_log_processamento(nome_arquivo: str, linhas_lidas: int) -> str: - """ - Monta uma mensagem de log em múltiplas linhas, por exemplo: - - Processamento concluído. - Arquivo: clientes.csv - Linhas lidas: 120 - """ - # TODO: usar \n para quebrar linhas e montar a mensagem de forma legível. - # Dica: você pode usar concatenação com + ou f-strings, se já se sentir confortável. - - mensagem = "" - return mensagem - - -def main() -> None: - exemplo = montar_log_processamento("clientes.csv", 120) - print(exemplo) - - -if __name__ == "__main__": - main() -``` - -### Desafio Medium — Mini-tabela de clientes com `\t` e `*` - -Você quer imprimir uma **mini-tabela** simples de clientes no terminal, com colunas para nome e endereço. -Use `\t` para alinhar as colunas e `*` para criar uma linha de separação reutilizável. - -```python -CLIENTES = [ - ("John Doe", "Avenue A"), - ("Mary Jones", "Avenue B"), - ("Anderson", "Avenue C"), -] - - -def imprimir_tabela_clientes(clientes: list[tuple[str, str]]) -> None: - """ - Imprime uma tabela simples no terminal, por exemplo: - - +------------------------------+ - Nome: John Doe - Ender: Avenue A - +------------------------------+ - Nome: Mary Jones - Ender: Avenue B - ... - """ - # TODO: - # 1. Criar uma linha de separação, por exemplo: "+" + "-" * 30 + "+". - # 2. Fazer um loop sobre a lista de clientes. - # 3. Para cada cliente, imprimir a linha de separação e depois - # as linhas com Nome e Ender usando \t para alinhar. - pass - - -def main() -> None: - imprimir_tabela_clientes(CLIENTES) - - -if __name__ == "__main__": - main() -``` - -### Desafio Hard — Normalizar caminhos de arquivo com raw strings - -Você está recebendo caminhos de arquivo vindos de um formulário Web e deseja gerar uma versão **“segura” para Python**, pronta para ser usada em scripts no Windows, onde barras invertidas precisam ser escapadas. - -Implemente uma função que receba um caminho como string normal (por exemplo, `C:\dados\clientes\2026\jan.csv`) e retorne: - -1. uma versão com barras escapadas (`C:\\dados\\clientes\\2026\\jan.csv`); -2. uma raw string equivalente (`r"C:\dados\clientes\2026\jan.csv"`). - -```python -def normalizar_caminho_windows(caminho: str) -> tuple[str, str]: - """ - Recebe um caminho de arquivo e retorna: - - caminho_escapado: com barras invertidas duplicadas - - caminho_raw: uma string começando com r"..." que poderia ser usada em código Python - - Exemplo de entrada: - "C:\\dados\\clientes\\2026\\jan.csv" - """ - # TODO: - # 1. Substituir cada "\" por "\\" na string. - # 2. Montar a raw string prefixando com r e envolvendo entre aspas duplas. - # (Dica: use concatenação de strings para montar algo como 'r"' + caminho_original + '"') - - caminho_escapado = "" - caminho_raw = "" - return caminho_escapado, caminho_raw - - -def main() -> None: - exemplos = [ - r"C:\dados\clientes\2026\jan.csv", - r"D:\projetos\ads\pipeline\input\data.csv", - ] - for caminho in exemplos: - escapado, raw = normalizar_caminho_windows(caminho) - print("Original: ", caminho) - print("Escapado:", escapado) - print("Raw: ", raw) - print("-" * 40) - - -if __name__ == "__main__": - main() -``` - - - - - diff --git a/content/python/aula-07-strings-indices-slice-metodos.md b/content/python/aula-07-strings-indices-slice-metodos.md deleted file mode 100644 index 2014823..0000000 --- a/content/python/aula-07-strings-indices-slice-metodos.md +++ /dev/null @@ -1,376 +0,0 @@ ---- -title: "Strings em Python: índices, slices e métodos úteis" -slug: "strings-indices-slice-metodos" -discipline: "python" -order: 7 -description: "Como enxergar strings como sequências indexadas de caracteres, usar o operador [] para indexação e slicing, e aplicar métodos úteis para análise e limpeza de texto." -reading_time: 50 -difficulty: "easy" -concepts: - - strings - - sequência de caracteres - - índice positivo - - índice negativo - - operador colchete [] - - slicing [inicio:fim:passo] - - substrings - - métodos de string - - len - - split - - join - - strip - - upper/lower/title -prerequisites: - - "por-que-programar-python" - - "algoritmos-e-notebooks" - - "variaveis-tipos-estilo-python" - - "conversao-tipos-operadores-aritmeticos" - - "strings-literais-multilinhas" - - "strings-escape-concatenacao" -learning_objectives: - - "Enxergar strings como sequências indexadas de caracteres, com índices positivos e negativos." - - "Usar o operador colchete [] para acessar um único caractere de uma string." - - "Aplicar slicing [inicio:fim:passo] para criar substrings e percorrer texto com passos diferentes, incluindo fatias invertidas." - - "Utilizar métodos de string (`upper`, `lower`, `title`, `replace`, `split`, `join`, `strip`, entre outros) para análise e limpeza de dados textuais." -exercises: - - question: "Qual a diferença entre acessar `hello[2]` e `hello[2:7]` em uma string como `hello = 'hello python'`, e por que dizemos que o índice final do slice é 'não inclusivo'?" - answer: "`hello[2]` acessa apenas um caractere na posição de índice 2 (no exemplo, o `'l'`), enquanto `hello[2:7]` cria uma nova string contendo todos os caracteres do índice 2 até o índice 6; o índice 7 funciona como 'fronteira de parada', não entra na fatia. Dizemos que o índice final é não inclusivo porque o caractere nessa posição não é incluído na substring: o Python pega de `inicio` até `fim - 1`." - hint: "Compare o diagrama de índices na lousa (0..11 e -12..-1) com a saída do exemplo `hello[2:7]` na aula, que resulta em `'llo p'`." -review_after_days: [1, 3, 7, 30] ---- - -## Visão Geral do Conceito - -Até aqui você aprendeu a **criar** strings (aspas, multilinhas, escapes) e a concatenar ou repetir texto. -Nesta aula você vai enxergar strings como **sequências indexadas de caracteres** e aprender a: - -- acessar um caractere específico com o operador colchete `[]`; -- recortar pedaços (substrings) com **slicing** `[inicio:fim:passo]`; -- aplicar métodos úteis para **analisar e limpar texto** (como `upper`, `lower`, `replace`, `split`, `join`, `strip`). - -> **Resumo em uma frase:** string em Python é uma sequência indexada; dominar índices, slices e métodos é a base para qualquer tipo de processamento de texto em projetos de dados. - -## Modelo Mental - -Use a metáfora da aula: pense na string como uma **lista unidimensional de caracteres**. - -```python -hello = "hello python" -``` - -Visualmente: - -- índice positivo (da esquerda para a direita): - `h e l l o p y t h o n` - `0 1 2 3 4 5 6 7 8 9 10 11` -- índice negativo (da direita para a esquerda): - `h e l l o p y t h o n` - `-12 -11 -10 -9 -8 -7 -6 -5 -4 -3 -2 -1` - -A variável `hello` aponta para essa “lista de 12 chars”. -O operador `[]` é o **acesso a uma posição** dessa lista; o slicing `[inicio:fim:passo]` é como pedir “um pedaço dessa sequência”. - -## Mecânica Central - -### Acessando um caractere com `[]` - -Com o operador colchete, você pega **um único caractere** pelo índice: - -```python -hello = "hello python" - -print(hello[2]) # índice positivo 2 -> 'l' -print(hello[5]) # índice 5 -> espaço ' ' -print(hello[-6]) # índice negativo -6 -> 'p' -``` - -- `hello[2]` lê “caractere armazenado na posição de índice 2”. -- `hello[-6]` lê “caractere na posição -6 contando da direita para a esquerda”. - -Se você usar essa notação em um notebook (Deepnote, Jupyter) como **última linha da célula**, o ambiente mostra o próprio valor da string, mesmo sem `print()`. - -### Slicing: `[inicio:fim:passo]` - -O slicing cria **substrings** a partir da string original: - -- `[inicio:fim]` → vai do índice `inicio` até `fim - 1` (fim é **não inclusivo**); -- `[inicio:fim:passo]` → percorre a sequência com o passo informado; -- qualquer um dos três parâmetros pode ser omitido. - -Exemplos equivalentes aos da aula: - -```python -hello = "hello python" - -print(hello[2:7]) # 'llo p' -print(hello[1:10:3]) # 'eoy' -print(hello[-8:]) # 'o python' -print(hello[:-3]) # 'hello pyt' -``` - -- `hello[2:7]`: pega índices 2,3,4,5,6 → `'llo p'`; -- `hello[1:10:3]`: começa no índice 1 (`'e'`), vai até 9 pulando de 3 em 3 → `'eoy'`; -- `hello[-8:]`: de `-8` até o fim (`'o python'`); -- `hello[:-3]`: do início até o índice `-3` (não incluso) → `'hello pyt'`. - -### Slices invertidos com passo negativo - -Quando o passo é negativo, o Python percorre a string de **trás para frente**. -O exemplo da aula para inverter a string inteira: - -```python -hello = "hello python" - -invertida = hello[::-1] -print(invertida) # 'nohtyp olleh' -``` - -E o desafio “como retornar `NOHTYP`?” é uma variação onde você escolhe bem o `inicio`, o `fim` e o `passo` para pegar só a parte `'python'` na ordem inversa. - -### Métodos úteis de `str` - -Alguns dos métodos demonstrados no notebook: - -- `upper()` / `lower()` / `swapcase()` / `capitalize()` / `title()` -- `replace(antigo, novo)` -- função built-in `len(obj)` -- `split(separador)` -- `join(iterável_de_strings)` -- `strip(chars)` -- `help(obj.metodo)` para ver a docstring. - -Exemplos: - -```python -hello = "hello python" -nome = "Gesiel Lopes" -curso = "INTRODUCAO A PROGRAMACAO COM PYTHON" - -print(hello.upper()) # 'HELLO PYTHON' -print(nome.lower()) # 'gesiel lopes' -print(nome.swapcase()) # 'gESIEL lOPES' -print(hello.capitalize()) # 'Hello python' -print(curso.title()) # 'Introducao A Programacao Com Python' - -print(hello.replace(" ", "--")) # 'hello--python' -print(len(hello)) # 12 -``` - -`split` e `join`: - -```python -texto = "hello python" -print(texto.split(" ")) # ['hello', 'python'] - -string_list = [hello, nome, curso] -using_join_in_string_list = " - ".join(string_list) -print(using_join_in_string_list) -``` - -`strip` (limpeza de espaços e caracteres nas bordas): - -```python -strip_exemplo = " exemplo de strip " -print(strip_exemplo.strip()) # 'exemplo de strip' -print("hello python".strip("hn")) # remove 'h'/'n' no início/fim se existirem -``` - -Você pode usar `help(str.strip)` ou `help(curso.strip)` para ver a documentação completa direto no notebook. - -### Diagrama: índices, slices e passo - -```mermaid -flowchart TD - A[String original 'hello python'] --> B[Escolher índice ou fatia] - B --> C{Acessar um único índice?} - C -- sim --> D[Usar hello[indice]
retorna 1 caractere] - C -- não --> E[Definir inicio, fim, passo] - E --> F[Slice hello[inicio:fim:passo]] - F --> G{passo > 0?} - G -- sim --> H[Percorrer da esquerda para a direita
de inicio até fim-1] - G -- não --> I[Percorrer da direita para a esquerda
de inicio até fim+1] - F --> J[Resultado é uma nova string (substring)] -``` - -## Uso Prático - -### Exemplos de ADS onde isso aparece o tempo todo - -- **Normalizar nomes e títulos**: usar `strip()`, `title()`, `upper()` e `lower()` para padronizar campos como nome de cliente, curso ou produto. -- **Validar formatos simples**: usar slices para extrair prefixos, sufixos e partes de identificadores (por exemplo, validar se um código começa com `"BR-"` ou se um CNPJ tem o número de dígitos correto). -- **Parsear arquivos de texto e logs**: usar `split()` para quebrar linhas em campos, `join()` para remontar textos, e slices para pegar “colunas” fixas em formatos legados. - -## Erros Comuns - -- **Confundir posição com índice**: lembrar que a “primeira letra” está no índice `0`, não em `1`. -- **Esquecer que o `fim` do slice é não inclusivo**: `texto[0:3]` pega índices `0,1,2`, não `0,1,2,3`. -- **Misturar índices positivos e negativos sem pensar na direção do passo**: `texto[2:-1]` é diferente de `texto[-1:2:-1]`. -- **Achar que `len(texto)` é um método de string** (`texto.len()`); na verdade, é uma função built-in que recebe o objeto como argumento. - -## Visão Geral de Debugging - -Quando algo estranho acontece com um índice ou slice: - -1. **Imprima o texto e os índices** que você acha que está usando, desenhando na lousa (ou em comentário) o mapeamento de 0..n-1 e -n..-1. -2. Teste **passos menores**: primeiro `texto[inicio:fim]`, depois adicione o `passo`. -3. Use `len(texto)` para checar se seu índice está dentro dos limites (de `-len` até `len-1`). -4. Para métodos, chame `help(str.metodo)` ou `help(texto.metodo)` e leia a docstring, como na aula com `help(curso.strip)`. - -## Principais Pontos - -- Strings são **sequências indexadas** de caracteres com índices positivos e negativos. -- O operador `[]` acessa um caractere; o slicing `[inicio:fim:passo]` cria novas strings a partir da original. -- O índice final do slice é **não inclusivo**; o passo pode ser positivo ou negativo. -- Métodos de string como `upper`, `lower`, `title`, `replace`, `split`, `join` e `strip` são ferramentas fundamentais para limpar e transformar texto em projetos de dados. - -## Preparação para Prática - -Depois desta lição, você deve conseguir: - -- navegar por uma string usando índices e slices para extrair pedaços específicos; -- limpar entradas textuais vindas de formulários, CSVs ou APIs usando métodos de `str`; -- combinar `split` e `join` para reestruturar textos (por exemplo, nomes completos, frases, logs). - -No **Laboratório de Prática** você vai aplicar esses conceitos em tarefas típicas de ADS: extrair partes de códigos/textos, padronizar nomes e limpar ruído de espaços. - -## Laboratório de Prática - -### Desafio Easy — Extrair caracteres por posição - -Você está recebendo códigos simples de produtos em um arquivo CSV, e precisa extrair algumas informações de posição fixa. - -```python -def extrair_caracteres_codigo(codigo: str) -> tuple[str, str, str]: - """ - Recebe um código de produto como string, por exemplo 'AB1234X', - e retorna uma tupla com: - - primeira_letra: caractere no índice 0 - - ultima_letra: caractere no índice -1 - - miolo: substring do índice 2 até o penúltimo caractere - - Exemplos: - 'AB1234X' -> ('A', 'X', '1234') - """ - # TODO: - # 1. Usar indexação simples para pegar primeira e última letra. - # 2. Usar slicing para pegar o "miolo" (sem as duas letras externas). - primeira_letra = "" - ultima_letra = "" - miolo = "" - return primeira_letra, ultima_letra, miolo -``` - -### Desafio Medium — Normalizar nome completo de cliente - -Em um cadastro de clientes, os nomes chegam com capitalização e espaços inconsistentes. -Você precisa padronizar esses nomes antes de salvar no banco de dados. - -```python -def normalizar_nome_cliente(nome_bruto: str) -> str: - """ - Recebe um nome de cliente potencialmente "sujo", por exemplo: - " gEsIeL lOpEs " - e retorna uma versão normalizada: - "Gesiel Lopes" - - Regras: - - Remover espaços extras no início e no fim. - - Substituir múltiplos espaços internos por um único espaço. - - Capitalizar cada parte do nome (estilo título). - """ - # TODO: - # 1. Usar strip() para remover espaços nas bordas. - # 2. Usar split() para quebrar nas partes do nome. - # 3. Normalizar cada parte com lower()/title(). - # 4. Rejuntar com " ".join(...). - nome_normalizado = "" - return nome_normalizado -``` - -### Desafio Hard — Fatiar e reformatar identificadores de registro - -Você recebe identificadores de registro no formato `"2026-03-ADS-00123"`, e precisa separar e remontar essas informações em diferentes formatos para relatórios. - -```python -from typing import Dict - -def analisar_identificador_registro(identificador: str) -> Dict[str, str]: - """ - Recebe um identificador no formato 'AAAA-MM-CURSO-NNNNN', por exemplo: - '2026-03-ADS-00123' - - Deve retornar um dicionário com: - - ano: '2026' - - mes: '03' - - curso: 'ADS' - - numero: '00123' - - formato_curto: '2026/03-00123' - - formato_legivel: 'Curso ADS - março/2026 - registro 00123' - - (Não se preocupe com nomes reais de meses; você pode apenas reutilizar o '03' - no formato_legivel, ou fazer um mapeamento simples se quiser.) - """ - # TODO: - # 1. Usar split('-') para quebrar o identificador em partes. - # 2. Extrair ano, mes, curso e numero. - # 3. Montar os formatos adicionais usando f-strings ou concatenação. - resultado: Dict[str, str] = {} - return resultado -``` - - - - - diff --git a/content/python/aula-08-strings-interpolacao-input.md b/content/python/aula-08-strings-interpolacao-input.md deleted file mode 100644 index d550543..0000000 --- a/content/python/aula-08-strings-interpolacao-input.md +++ /dev/null @@ -1,406 +0,0 @@ ---- -title: "Strings em Python: interpolação, f-strings e input" -slug: "strings-interpolacao-input" -discipline: "python" -order: 8 -description: "Como formatar mensagens com dados dinâmicos usando operadores de formatação, método format, f-strings e ler valores do usuário com input." -reading_time: 50 -difficulty: "easy" -concepts: - - strings - - interpolação de strings - - formatação com % - - método format - - f-strings - - input - - conversão de tipos com input - - ValueError - - TypeError - - casas decimais -prerequisites: - - "por-que-programar-python" - - "algoritmos-e-notebooks" - - "variaveis-tipos-estilo-python" - - "conversao-tipos-operadores-aritmeticos" - - "strings-literais-multilinhas" - - "strings-escape-concatenacao" - - "strings-indices-slice-metodos" -learning_objectives: - - "Comparar os estilos de interpolação de strings em Python: operador %, método .format() e f-strings." - - "Usar f-strings para montar mensagens legíveis combinando texto, variáveis e expressões Python." - - "Ler dados do usuário com input(), entender que sempre chegam como string e convertê-los para tipos numéricos quando necessário." - - "Formatar números com número controlado de casas decimais em saídas de texto." -exercises: - - question: "Por que a expressão `graus_c = (graus_f - 32) * 5/9` falha com `TypeError` se `graus_f` veio direto de `input()`, e quais são os passos corretos para corrigir esse código?" - answer: "Porque `input()` sempre retorna uma string; ao tentar fazer `graus_f - 32`, o Python está tentando subtrair um inteiro (`int`) de uma string (`str`), o que gera `TypeError: unsupported operand type(s) for -: 'str' and 'int'`. A correção é converter explicitamente a string para número (por exemplo, com `int(graus_f)` ou `float(graus_f)`) antes de usar em operações aritméticas, e então usar o valor numérico convertido na fórmula." - hint: "Lembre do exemplo da aula em que o primeiro código de conversão gera um `TypeError`, e depois o professor faz `graus_f = int(graus_f)` antes de calcular `graus_c`." -review_after_days: [1, 3, 7, 30] ---- - -## Visão Geral do Conceito - -Depois de entender como criar e manipular strings, o próximo passo é **usar texto para conversar com o usuário**: - -- montar mensagens com valores dinâmicos (nome, resultados de contas, medidas); -- ler entradas do usuário e usar esses valores em cálculos. - -Nesta lição você aprende três estilos de interpolação de strings em Python (`%`, `.format()` e **f-strings**) e a função **`input()`** para capturar dados digitados, inclusive o cuidado com conversão de tipos. - -> **Ideia central:** interpolação e `input()` conectam o seu algoritmo ao usuário — você gera mensagens explicativas e recebe valores reais para processar. - -## Modelo Mental - -Pense em uma mensagem na tela como um **template com lacunas**: - -```text -Hello World {nome}, python is fantastic! -``` - -As lacunas `{...}` são preenchidas em tempo de execução com: - -- **valores de variáveis** (`nome`, `graus_c`, `media`); -- **expressões Python** (`graus_celsius * 9/5 + 32`, `coisa.capitalize()`). - -Para receber o valor que vai preencher essas lacunas, você usa: - -- `input()` → mostra um prompt, espera o usuário digitar e apertar Enter, e devolve **sempre uma string**; -- depois você converte para o tipo que precisa (`int`, `float`) e usa em contas. - -## Mecânica Central - -### Estilo 1: operador `%` (formatação antiga) - -Este estilo vem do C e ainda aparece em código legado: - -```python -valor = 12.345678 - -print("Número como string: %s" % valor) # %s -> string -print("Número como float: %f" % valor) # %f -> float com 6 casas -print("Número com 2 casas: %.2f" % valor) # 2 casas decimais -print("Inteiro: %d" % 42) # %d -> inteiro -print("Percentual: %d%%" % 80) # %% -> literal % -``` - -Interpolando duas variáveis: - -```python -cat = "woodchuck" -weight = 3 - -template = "%s weighs %dkg" -print(template % (cat, weight)) -``` - -### Estilo 2: método `.format()` - -Usa **chaves `{}`** dentro da string e o método `.format()` para preencher: - -```python -thing = "woodchuck" -place = "lake" - -print("The {} is in the {}.".format(thing, place)) -print("The {1} is in the {0}.".format(place, thing)) # reordena pelos índices -print("The {thing} is in the {place}.".format(thing=thing, place=place)) -``` - -Você também pode usar `.format()` para controlar casas decimais: - -```python -valor = 113.3333333333 -print("Valor com 3 casas: {:.3f}".format(valor)) # 113.333 -``` - -### Estilo 3: f-strings (recomendado) - -As **f-strings** são o estilo moderno (Python 3.6+). Basta prefixar a string com `f` e usar `{}`: - -```python -thing = "woodchuck" -place = "werepond" - -print(f"The {thing} is in the {place}.") -print(f"The {thing.capitalize()} is in the {place.rjust(20)}") -``` - -Dentro de `{}` você pode colocar **qualquer expressão Python válida**: - -```python -nome = "gesiel lopes" -print(f"Hello World {nome.title()}, python is fantastic!") -``` - -Controle de casas decimais com f-strings: - -```python -graus_f = 236 -graus_c = (graus_f - 32) * 5/9 -print(f"Graus Fahrenheit: {graus_f} em Graus Celsius: {graus_c:.3f}") -``` - -Aqui `:.3f` significa: formate como float (`f`) com 3 casas decimais. - -### Lendo entradas com `input()` - -`input()` mostra um prompt e devolve **sempre uma string**: - -```python -texto = input() # usuário digita algo -print(texto, type(texto)) # sempre -``` - -Com mensagem amigável: - -```python -nome = input("Qual o seu nome: ") -print(f"Hello World {nome}, python is fantastic!") -``` - -### Cuidado: conversão de tipos com input - -Se você precisa fazer contas, deve **converter** o resultado de `input()`: - -```python -graus_f = input("Digite o valor em Graus Fahrenheit: ") -graus_c = (graus_f - 32) * 5/9 -``` - -Esse código gera: - -```text -TypeError: unsupported operand type(s) for -: 'str' and 'int' -``` - -Porque `graus_f` é `str`. A versão correta: - -```python -graus_f = input("Digite o valor em Graus Fahrenheit: ") -graus_f = int(graus_f) # ou float(graus_f) -graus_c = (graus_f - 32) * 5/9 -print(f"Graus Fahrenheit: {graus_f} em Graus Celsius: {graus_c:.3f}") -``` - -## Diagrama: fluxo input → conversão → processamento → saída formatada - -```mermaid -flowchart TD - A[Mostrar mensagem com input()] --> B[Usuário digita texto e aperta Enter] - B --> C[Python recebe uma string] - C --> D{Precisa fazer contas?} - D -- não --> E[Usar string direto em f-strings
ou prints] - D -- sim --> F[Converter com int()/float()] - F --> G[Calcular resultado numérico] - G --> H[Formatar com f-string
(por ex.: {valor:.2f})] - H --> I[Exibir mensagem final no terminal/notebook] -``` - -## Uso Prático - -- **Interfaces simples em notebook**: perguntar nome do aluno, parâmetros de um cálculo, caminho de arquivo, etc. -- **Scripts de conversão de unidades**: receber temperatura, moedas, distâncias, aplicar fórmulas e mostrar resultados legíveis ao usuário. -- **Logs amigáveis**: combinar dados numéricos com texto em mensagens estruturadas usando f-strings. - -## Erros Comuns - -- **Esquecer que `input()` devolve string** e tentar fazer contas direto → `TypeError`. -- **Misturar número e string na interpolação antiga com `%`** sem a chave correta (`%d` vs `%s`). -- **Não controlar casas decimais** ao mostrar resultados de divisão, produzindo saídas com muitas casas difíceis de ler. -- **Usar `+` para concatenar strings e números** em vez de interpolação ou conversão explícita. - -## Visão Geral de Debugging - -Quando algo dá errado em código com interpolação ou `input()`: - -1. Use `print(repr(valor), type(valor))` logo após o `input()` para ver o tipo real. -2. Verifique a **mensagem de erro**: - - `TypeError: ... 'str' and 'int'` → mistura de string com número sem conversão; - - `ValueError` ao converter (`int("abc")`) → entrada do usuário inválida. -3. Isole a parte de formatação: teste o cálculo primeiro, depois envolva numa f-string com `:.2f`, `:.3f`, etc. -4. Prefira f-strings em código novo; mantenha `%` ou `.format()` apenas quando necessário em código legado. - -## Principais Pontos - -- Python oferece três estilos de interpolação de strings: `%`, `.format()` e f-strings — **prefira f-strings** em código novo. -- `input()` sempre retorna string; para realizar contas, converta explicitamente com `int()` ou `float()`. -- Você pode controlar casas decimais com `:.2f`, `:.3f`, etc., tanto em `.format()` quanto em f-strings. -- Interpolação e `input()` juntos permitem construir scripts interativos e mensagens claras, fundamentais em projetos de dados. - -## Preparação para Prática - -Depois desta lição, você deve ser capaz de: - -- construir mensagens contextualizadas com dados vindos de variáveis ou expressões; -- receber entradas do usuário e tratar adequadamente a conversão de tipos; -- formatar resultados numéricos com o nível de precisão desejado. - -No **Laboratório de Prática**, você vai criar pequenos scripts de linha de comando que combinam leitura de dados, cálculos e saídas bem formatadas. - -## Laboratório de Prática - -### Desafio Easy — Boas-vindas personalizadas - -Implemente um pequeno script que pergunte o nome da pessoa e devolva uma saudação amigável usando f-strings. - -```python -def saudacao_boas_vindas() -> str: - """ - Pergunta o nome da pessoa e retorna uma mensagem - no formato: - "Hello World {nome}, python is fantastic!" - """ - # TODO: - # 1. Usar input() para ler o nome. - # 2. Montar a mensagem usando f-string. - mensagem = "" - return mensagem - - -def main() -> None: - print(saudacao_boas_vindas()) - - -if __name__ == "__main__": - main() -``` - -### Desafio Medium — Conversor interativo de temperaturas - -Crie um script que peça ao usuário uma temperatura em graus Fahrenheit, converta para Celsius e mostre o resultado com **3 casas decimais**. - -```python -def converter_fahrenheit_para_celsius_interativo() -> None: - """ - Lê uma temperatura em graus Fahrenheit via input(), - converte para Celsius e imprime o resultado formatado. - - Exemplo de saída: - Graus Fahrenheit: 236 em Graus Celsius: 113.333 - """ - # TODO: - # 1. Ler o valor com input(). - # 2. Converter para float. - # 3. Aplicar a fórmula: (F - 32) * 5/9. - # 4. Exibir com f-string usando 3 casas decimais. - pass - - -def main() -> None: - converter_fahrenheit_para_celsius_interativo() - - -if __name__ == "__main__": - main() -``` - -### Desafio Hard — Calculadora de média com formatação - -Implemente uma mini calculadora que leia três notas de um aluno via `input()`, calcule a média aritmética e exiba um relatório com as notas e a média formatada com 2 casas decimais. - -```python -from typing import Tuple - - -def ler_tres_notas() -> Tuple[float, float, float]: - """ - Lê três notas (como strings via input) e retorna - um tuple de floats (n1, n2, n3). - """ - # TODO: - # 1. Chamar input() três vezes. - # 2. Converter cada entrada para float. - n1 = 0.0 - n2 = 0.0 - n3 = 0.0 - return n1, n2, n3 - - -def calcular_media(n1: float, n2: float, n3: float) -> float: - """ - Calcula a média aritmética simples de três notas. - """ - # TODO: implementar a fórmula da média aritmética. - media = 0.0 - return media - - -def imprimir_relatorio_notas() -> None: - """ - Lê três notas, calcula a média e imprime algo como: - - Notas: 7.0, 8.5, 9.25 - Média: 8.25 - - Usando f-strings com 2 casas decimais para a média. - """ - # TODO: - # 1. Usar ler_tres_notas() para obter as notas. - # 2. Calcular a média com calcular_media(). - # 3. Imprimir o relatório formatado. - pass - - -def main() -> None: - imprimir_relatorio_notas() - - -if __name__ == "__main__": - main() -``` - - - - - diff --git a/content/python/aula-09-desvio-condicional.md b/content/python/aula-09-desvio-condicional.md deleted file mode 100644 index 7ae978b..0000000 --- a/content/python/aula-09-desvio-condicional.md +++ /dev/null @@ -1,381 +0,0 @@ ---- -title: "Desvios condicionais em Python: if, elif e else" -slug: "desvios-condicionais-if-elif-else" -discipline: "python" -order: 9 -description: "Como usar if, elif e else com operadores relacionais e booleanos para tomar decisões em programas Python." -reading_time: 55 -difficulty: "easy" -concepts: - - booleanos - - operadores relacionais - - if - - elif - - else - - bloco identado - - fluxo condicional - - operadores de comparação - - expressão booleana - - curto-circuito mental (sequência de testes) -prerequisites: - - "por-que-programar-python" - - "algoritmos-e-notebooks" - - "variaveis-tipos-estilo-python" - - "conversao-tipos-operadores-aritmeticos" - - "strings-literais-multilinhas" - - "strings-escape-concatenacao" - - "strings-indices-slice-metodos" - - "strings-interpolacao-input" -learning_objectives: - - "Entender o que é uma expressão booleana e como os operadores relacionais produzem valores True/False." - - "Usar if, elif e else para criar fluxos de decisão encadeados em programas Python." - - "Aplicar condições em problemas práticos como aprovação de alunos, classificação de IMC e faixas de desconto." - - "Evitar erros comuns de identação e de comparação ao escrever blocos condicionais." -exercises: - - question: "Qual é a diferença entre usar apenas if em sequência e usar if/elif/else para testar faixas mutuamente exclusivas (por exemplo, conceitos A, B, C para uma média)?" - answer: "Vários if independentes avaliam todas as condições, mesmo que uma já tenha sido verdadeira, o que pode gerar mais de um bloco verdadeiro ou lógicas inconsistentes; já uma cadeia if/elif/else garante exclusividade: quando uma condição é satisfeita, as demais são ignoradas. Em classificações por faixa (como conceito da média ou categoria de IMC), usamos if/elif/else para garantir que cada valor caia em exatamente um intervalo." - hint: "Lembre dos exemplos de média em que o mesmo valor poderia cair em mais de um if, e de como o elif 'trava' a cadeia após o primeiro True." -review_after_days: [1, 3, 7, 30] ---- - -### Visão Geral - -Nesta aula você aprende a tomar decisões em Python usando **desvios condicionais** com `if`, `elif` e `else`. A partir de **expressões booleanas** (comparações que resultam em `True` ou `False`), seu programa escolhe **um caminho de execução**: aprovar ou reprovar um aluno, classificar um IMC, decidir desconto, entre outros. - -### Modelo Mental - -Pense em um **fluxo de triagem** de pronto-socorro: cada paciente passa por perguntas em ordem, e cada resposta o envia para uma fila diferente (emergência, prioridade, rotina). O `if` em Python faz esse papel: - -- **Porta de entrada**: uma expressão booleana (`media >= 7`, `imc < 18.5`). -- **Seta de decisão**: se a condição é verdadeira, você entra no **bloco identado** logo abaixo. -- **Rotas alternativas**: `elif` e `else` representam outros caminhos possíveis quando a primeira condição não é satisfeita. - -O programa sempre segue **um único ramo** em uma cadeia `if / elif / else`, como um fluxograma de perguntas e respostas. - -### Mecânica Central - -- **Valores booleanos**: - -```python -aprovado = True -reprovado = False -``` - -- **Operadores relacionais (de comparação)**: - -```python -idade = 20 - -print(idade > 18) # maior que -print(idade >= 18) # maior ou igual -print(idade == 18) # igual -print(idade != 18) # diferente -print(idade < 18) # menor que -print(idade <= 18) # menor ou igual -``` - -- **If simples**: - -```python -media = 8.2 - -if media >= 7.0: - print("Aluno aprovado!") -``` - -- **If / else**: - -```python -media = 5.3 - -if media >= 7.0: - print("Aluno aprovado!") -else: - print("Aluno reprovado.") -``` - -- **If / elif / else (várias faixas)**: - -```python -media = 6.2 - -if media >= 9.0: - print("Conceito A") -elif media >= 7.0: - print("Conceito B") -elif media >= 5.0: - print("Conceito C") -else: - print("Conceito D") -``` - -Note a **identação obrigatória** (geralmente 4 espaços) em cada bloco interno. - -### Uso Prático - -Alguns cenários comuns em ADS: - -- **Aprovação com recuperação**: média final em relação a limiares (≥ 7 aprovado, entre 5 e 7 recuperação, < 5 reprovado). -- **Classificação de IMC**: faixas de IMC com textos explicativos para relatório de saúde. -- **Regras de desconto**: aplicar percentuais diferentes dependendo do valor total de compras. - -Exemplo de aprovação com recuperação: - -```python -media = float(input("Digite a média final do aluno: ")) - -if media >= 7.0: - status = "aprovado" -elif media >= 5.0: - status = "em recuperação" -else: - status = "reprovado" - -print(f"Status do aluno: {status}") -``` - -### Visual: Fluxo de decisão com if / elif / else - -```mermaid -flowchart TD - A[Início] --> B[Calcular média / métrica] - B --> C{Condição 1
media >= 7?} - C -- Sim --> D[Bloco if
Aprovado] - C -- Não --> E{Condição 2
media >= 5?} - E -- Sim --> F[Bloco elif
Recuperação] - E -- Não --> G[Bloco else
Reprovado] - D --> H[Fim] - F --> H - G --> H -``` - -Esse diagrama representa a cadeia completa de decisões. Cada losango é uma **expressão booleana** avaliada em sequência. - -### Erros Comuns - -- **Esquecer a identação**: - -```python -if media >= 7.0: -print("Aprovado") # Erro: o bloco precisa ser identado -``` - -- **Misturar comparação com atribuição**: - -```python -if media = 7: # Erro de sintaxe, '=' é atribuição - ... - -if media == 7: # Comparação correta - ... -``` - -- **Sobrepor intervalos ao usar apenas if**: - -```python -# Pode imprimir mais de uma mensagem para o mesmo valor -if media >= 5: - print("C") -if media >= 7: - print("B") -``` - -Aqui, se `media == 7.5`, as duas condições são verdadeiras. Use `if/elif/else` quando quiser **exclusividade**. - -### Visão Geral de Debugging - -Quando algo não funciona em um conjunto de condicionais: - -- **Imprima valores intermediários** (média, IMC, total) antes do `if` para conferir se a conta está correta. -- **Teste limites de faixa**: valores bem na borda (`4.9`, `5.0`, `6.99`, `7.0`) para ver em qual ramo caem. -- **Verifique a ordem das condições**: intervalos mais restritos (notas altas) costumam vir primeiro. -- Em caso de **erro de sintaxe**, observe cuidadosamente: - - Dois pontos `:` no final da linha do `if/elif/else`. - - Identação consistente usando o mesmo número de espaços. - -### Principais Pontos - -- **Operadores relacionais** produzem `True` ou `False` a partir de comparações. -- **`if`, `elif`, `else`** definem **ramificações exclusivas** de execução. -- A **identação** define quais linhas pertencem a cada bloco condicional. -- Em classificações por faixa (média, IMC, desconto), a **ordem dos testes** e o uso correto de `elif` são cruciais. - -### Preparação para Prática - -Antes de ir para o laboratório: - -- Releia mentalmente o modelo de **triagem**: uma pergunta por vez, em ordem. -- Tenha em mãos alguns **exemplos concretos** (médias de alunos, valores de IMC, valores de carrinho de compras). -- Anote, em português, as regras de negócio primeiro; depois, traduza essas regras para `if / elif / else`. - -### Laboratório de Prática - -#### 1. Classificador simples de aprovação (Easy) - -Implemente uma função para classificar o status de um aluno com base em sua média final. - -Regras: - -- Média **maior ou igual a 7.0** → `"aprovado"`. -- Média **entre 5.0 (inclusive) e 7.0 (exclusive)** → `"recuperacao"`. -- Média **menor que 5.0** → `"reprovado"`. - -```python -def classificar_aluno(media: float) -> str: - """ - Classifica o aluno de acordo com a média final. - - Regras: - - media >= 7.0 -> "aprovado" - - 5.0 <= media < 7.0 -> "recuperacao" - - media < 5.0 -> "reprovado" - """ - status = "" - - # TODO: implementar a lógica de classificação usando if/elif/else - # Dica: comece testando a condição mais "forte" (aprovado) e vá descendo. - - return status - - -if __name__ == "__main__": - exemplos = [4.3, 5.0, 6.9, 7.0, 9.5] - for m in exemplos: - print(m, "->", classificar_aluno(m)) -``` - -#### 2. Analise de IMC com faixas (Medium) - -Você recebeu uma tabela simplificada de classificação de **Índice de Massa Corporal (IMC)**. Implemente uma função que, dado um valor de IMC, retorne uma string com a categoria. - -Use faixas como: - -- `imc < 18.5` → `"abaixo do peso"` -- `18.5 <= imc < 25` → `"peso normal"` -- `25 <= imc < 30` → `"sobrepeso"` -- `imc >= 30` → `"obesidade"` - -```python -def classificar_imc(imc: float) -> str: - """ - Classifica o IMC em faixas de saúde. - """ - categoria = "" - - # TODO: implementar cadeia if/elif/else para cobrir todas as faixas - # Lembre de garantir que cada valor caia em apenas uma categoria. - - return categoria - - -if __name__ == "__main__": - imcs_teste = [17.9, 18.5, 23.7, 25.0, 29.9, 31.2] - for valor in imcs_teste: - print(valor, "->", classificar_imc(valor)) -``` - -#### 3. Regra de desconto progressivo em compras (Hard) - -Você está implementando a lógica de descontos de uma loja online. A regra é: - -- Total **abaixo de 100.00** → sem desconto (0%). -- Total **de 100.00 até 499.99** → 5% de desconto. -- Total **de 500.00 até 999.99** → 10% de desconto. -- Total **a partir de 1000.00** → 15% de desconto. - -Implemente uma função que receba o valor total da compra e retorne **o valor final com desconto aplicado**. - -```python -def aplicar_desconto(total: float) -> float: - """ - Calcula o valor final de uma compra após aplicar - o desconto progressivo definido pela loja. - """ - valor_final = total - - # TODO: usar if/elif/else para definir a porcentagem de desconto - # e atualizar o valor_final. - # - # Exemplo: - # - Para total = 120.00, deve aplicar 5% e retornar 114.00 - - return valor_final - - -if __name__ == "__main__": - carrinhos = [50.0, 150.0, 520.0, 1200.0] - for total in carrinhos: - print(f"Total: R$ {total:.2f} -> Com desconto: R$ {aplicar_desconto(total):.2f}") -``` - - - - - diff --git a/content/python/aula-10-operadores-logicos-match-case.md b/content/python/aula-10-operadores-logicos-match-case.md deleted file mode 100644 index ed54904..0000000 --- a/content/python/aula-10-operadores-logicos-match-case.md +++ /dev/null @@ -1,404 +0,0 @@ ---- -title: "Operadores lógicos, tabela-verdade e match/case em Python" -slug: "operadores-logicos-match-case" -discipline: "python" -order: 10 -description: "Como combinar condições com operadores lógicos AND/OR/negação, entender valores verdadeiros/falsos e usar match/case para substituir cadeias longas de if/elif." -reading_time: 60 -difficulty: "easy" -concepts: - - operadores lógicos - - and - - or - - negação - - tabela-verdade - - expressão booleana composta - - truthy e falsy - - match/case - - pattern matching estrutural -prerequisites: - - "por-que-programar-python" - - "algoritmos-e-notebooks" - - "variaveis-tipos-estilo-python" - - "conversao-tipos-operadores-aritmeticos" - - "strings-literais-multilinhas" - - "strings-escape-concatenacao" - - "strings-indices-slice-metodos" - - "strings-interpolacao-input" - - "desvios-condicionais-if-elif-else" -learning_objectives: - - "Interpretar e construir expressões booleanas compostas com os operadores lógicos and, or e not." - - "Ler e utilizar tabelas-verdade para prever o resultado de combinações lógicas." - - "Aplicar o conceito de valores truthy e falsy em testes condicionais em Python." - - "Usar a estrutura match/case (pattern matching) como alternativa legível a cadeias longas de if/elif." -exercises: - - question: "Qual é a diferença prática entre usar and e or em uma expressão condicional como `(media >= 7 and faltas <= 10)` versus `(media >= 7 or faltas <= 10)` para decidir se um aluno está aprovado?" - answer: "Com `and`, as duas condições precisam ser verdadeiras ao mesmo tempo: o aluno só é aprovado se tiver média suficiente e poucas faltas; se qualquer uma falhar, o resultado é falso. Com `or`, basta que uma delas seja verdadeira: o aluno seria aprovado mesmo com muitas faltas se a média for alta, ou mesmo com média baixa se tiver poucas faltas, o que normalmente não representa a regra de negócio desejada." - hint: "Pense na linha da tabela-verdade: no `and`, qualquer falso 'contamina' a expressão; no `or`, qualquer verdadeiro 'salva' a expressão." -review_after_days: [1, 3, 7, 30] ---- - -### Visão Geral - -Nesta aula você aprende a **combinar condições** em Python usando **operadores lógicos** (`and`, `or`, negação) e a enxergar essas combinações por meio de **tabelas-verdade**. Também vê como o Python interpreta certos valores como **truthy/falsy** e conhece a estrutura **`match/case`**, introduzida no Python 3.10, que substitui cadeias grandes de `if/elif` com código mais limpo. - -### Modelo Mental - -Imagine uma triagem de regras de negócio como um **painel de interruptores**: - -- Cada condição simples (ex.: `media >= 7`, `faltas <= 10`) é um **interruptor** que pode estar ligado (`True`) ou desligado (`False`). -- O operador **`and`** é como “ligar duas chaves em série”: se qualquer uma estiver desligada, a energia não passa. -- O operador **`or`** é como “chaves em paralelo”: se pelo menos uma estiver ligada, a energia passa. -- A **negação** (`not` na sintaxe do Python) inverte o estado da chave: ligado vira desligado e vice-versa. -- O **`match/case`** é um painel numerado: você escolhe um valor (ex.: dia da semana) e ele “casa” com um dos casos pré-definidos, executando apenas o bloco correspondente. - -A tabela-verdade é, literalmente, uma **tabela de todas as combinações possíveis** de chaves (`True`/`False`) e de qual resultado final elas produzem. - -### Mecânica Central - -- **Operadores lógicos básicos em Python**: - -```python -a = True -b = False - -print(a and b) # False (os dois precisam ser True) -print(a or b) # True (basta um ser True) -print(not a) # False (negação lógica) -``` - -- **Tabelas-verdade de `and` e `or`**: - -| A | B | A and B | A or B | -|-------|-------|---------|--------| -| True | True | True | True | -| True | False | False | True | -| False | True | False | True | -| False | False | False | False | - -- **Negação**: - -| A | not A | -|-------|-------| -| True | False | -| False | True | - -- **Combinação de comparações**: - -```python -media = 8.0 -faltas = 6 - -aprovado = (media >= 7.0) and (faltas <= 10) -print(aprovado) # True se as duas condições forem satisfeitas -``` - -- **Truthy e falsy** em Python (em contexto booleano): - - Falsy: `False`, `None`, `0`, `0.0`, `""` (string vazia), estruturas vazias (`[]`, `{}`, `set()`). - - Tudo o mais é tratado como **truthy**. - -```python -texto = "" -if texto: - print("Tem conteúdo") -else: - print("Está vazio") # executado, porque "" é falsy -``` - -- **Match/case (pattern matching estrutural)** – Python 3.10+: - -```python -dia = int(input("Digite o dia da semana (1-7): ")) - -match dia: - case 1: - print("Segunda-feira") - case 2: - print("Terça-feira") - case 3: - print("Quarta-feira") - case 4: - print("Quinta-feira") - case 5: - print("Sexta-feira") - case 6: - print("Sábado") - case 7: - print("Domingo") - case _: - print("Dia inválido") -``` - -### Uso Prático - -Alguns usos diretos em ADS: - -- **Regras de aprovação**: média mínima **e** limite máximo de faltas. -- **Filtros de clientes**: renda suficiente **e** sem dívidas, ou renda muito alta independentemente das dívidas. -- **Alertas em dashboards**: indicador muito baixo **ou** variação muito negativa. - -Exemplo de regra de aprovação com presença: - -```python -media = float(input("Média final: ")) -faltas = int(input("Número de faltas: ")) - -if media >= 7 and faltas <= 10: - print("Aprovado") -elif media >= 5 and faltas <= 10: - print("Recuperação") -else: - print("Reprovado") -``` - -### Visual: combinação lógica e match/case - -```mermaid -flowchart TD - A[Início] --> B[Calcular métricas
ex.: média, faltas, dia_semana] - B --> C{Cond1 AND Cond2?
ex.: media >= 7 AND faltas <= 10} - C -- True --> D[Bloco Aprovado] - C -- False --> E{Cond3 OR Cond4?
ex.: media >= 5 OR trabalho_extra == True} - E -- True --> F[Bloco Recuperação] - E -- False --> G[Outros casos] - B --> H{match dia_semana} - H --> I[case 1 → Segunda] - H --> J[case 2 → Terça] - H --> K[...] - H --> L[case _ → Inválido] - D --> M[Fim] - F --> M - G --> M - I --> M - J --> M - K --> M - L --> M -``` - -O diagrama mostra como expressões com `and`/`or` direcionam o fluxo e como o `match` faz um roteamento limpo por valor. - -### Erros Comuns - -- **Confundir `and` com `or`**: usar `or` em regras que exigem duas condições ao mesmo tempo gera aprovações indevidas. -- **Esquecer parênteses em expressões complexas**: confiar apenas na precedência pode deixar o código difícil de ler e interpretar errado. -- **Confiar demais em truthy/falsy sem clareza**: escrever `if valor:` quando na verdade você quer testar explicitamente `if valor is not None and valor != 0`. -- **Achar que `match/case` existe em qualquer versão**: usar `match` em Python < 3.10 resulta em `SyntaxError`. - -### Visão Geral de Debugging - -Para depurar expressões lógicas: - -- Imprima as **subexpressões** separadas: - -```python -print(media >= 7, faltas <= 10) -print((media >= 7) and (faltas <= 10)) -``` - -- Monte uma pequena **tabela-verdade de teste**, anotando combinações de entrada e resultado esperado (“teste de mesa”). -- Simplifique expressões grandes, extraindo pedaços para variáveis com nomes claros (`tem_media_suficiente`, `tem_poucas_faltas`). -- Ao usar `match/case`, sempre defina um `case _:` para cobrir valores inesperados. - -### Principais Pontos - -- `and` só resulta em `True` se **todas** as condições forem verdadeiras; `or` resulta em `True` se **pelo menos uma** for verdadeira; `not` inverte o valor lógico. -- **Tabelas-verdade** ajudam a prever o resultado de combinações de `True`/`False`. -- Python interpreta alguns valores como **falsy** (`False`, `None`, `0`, `""`, coleções vazias) e todo o resto como truthy. -- **`match/case`** oferece uma forma mais legível de escrever múltiplos caminhos de fluxo baseados em um único valor. - -### Preparação para Prática - -Antes do laboratório: - -- Reescreva, em português, **três regras de negócio** que você conhece e sublinhe onde entrariam `and`, `or` e `not`. -- Faça uma tabelinha para duas condições que aparecem no seu dia a dia (ex.: “tem dinheiro” / “tem tempo”) e preencha a tabela-verdade de `and` e `or`. -- Confirme qual é a versão do Python no ambiente que você usa (para saber se pode usar `match/case`). - -### Laboratório de Prática - -#### 1. Aprovado com média e frequência (Easy) - -Implemente uma função que determina o status de um aluno com base em **média** e **percentual de frequência**. - -Regras: - -- **Aprovado**: média ≥ 7.0 **e** frequência ≥ 75%. -- **Reprovado por nota**: média < 7.0 **e** frequência ≥ 75%. -- **Reprovado por frequência**: frequência < 75% (independentemente da média). - -```python -def avaliar_aluno(media: float, frequencia: float) -> str: - """ - Avalia o status do aluno com base em média e frequência. - - Retorna: - - "aprovado" - - "reprovado_nota" - - "reprovado_frequencia" - """ - status = "" - - # TODO: usar and / or para implementar as regras acima. - # Dica: trate primeiro o caso de frequência baixa (regra mais "forte"). - - return status - - -if __name__ == "__main__": - exemplos = [ - (8.0, 80.0), - (6.5, 80.0), - (9.0, 60.0), - ] - for media, freq in exemplos: - print(media, freq, "->", avaliar_aluno(media, freq)) -``` - -#### 2. Filtro de clientes para campanha (Medium) - -Você precisa selecionar clientes para uma **campanha de cartão de crédito** usando algumas variáveis: - -- `renda_mensal` (float, em reais). -- `tem_dividas_em_atraso` (bool). -- `score_credito` (int, de 0 a 1000). - -Regras: - -- Cliente está **aprovado** para a campanha se: - - `renda_mensal >= 3000` **e** `score_credito >= 600` **e** **não** tem dívidas em atraso, **ou** - - `renda_mensal >= 8000` **e** `score_credito >= 500` (alta renda compensa score médio, desde que `tem_dividas_em_atraso` seja `False`). - -```python -def cliente_aprovado_para_campanha( - renda_mensal: float, - tem_dividas_em_atraso: bool, - score_credito: int, -) -> bool: - """ - Retorna True se o cliente deve entrar na campanha, False caso contrário. - """ - aprovado = False - - # TODO: implementar a expressão booleana usando and, or e not - # de acordo com as regras acima. - - return aprovado - - -if __name__ == "__main__": - exemplos = [ - (2500.0, False, 700), - (3500.0, False, 650), - (9000.0, False, 520), - (9000.0, True, 750), - ] - for renda, em_atraso, score in exemplos: - print(renda, em_atraso, score, "->", - cliente_aprovado_para_campanha(renda, em_atraso, score)) -``` - -#### 3. Descrição de dia da semana com match/case (Hard) - -Implemente uma função que recebe um inteiro representando o **dia da semana** e retorna uma descrição mais rica, usando **`match/case`**. - -Regras: - -- `1` → `"Segunda-feira - início da semana de trabalho"` -- `2` → `"Terça-feira - dia produtivo"` -- `3` → `"Quarta-feira - meio da semana"` -- `4` → `"Quinta-feira - quase lá"` -- `5` → `"Sexta-feira - dia de breja (ou deploy!)"` -- `6` → `"Sábado - descanso ou estudos"` -- `7` → `"Domingo - planejamento da semana"` -- Qualquer outro valor → `"Dia inválido"` - -```python -def descrever_dia_semana(dia: int) -> str: - """ - Retorna uma descrição amigável para o dia da semana, - usando match/case (Python 3.10+). - """ - descricao = "" - - # TODO: implementar usando match dia: case 1: ... case 2: ... case _: - # Certifique-se de cobrir o caso padrão com case _. - - return descricao - - -if __name__ == "__main__": - for d in range(0, 9): - print(d, "->", descrever_dia_semana(d)) -``` - - - - - diff --git a/content/python/aula-11-loops-for-range-listas.md b/content/python/aula-11-loops-for-range-listas.md deleted file mode 100644 index 633a894..0000000 --- a/content/python/aula-11-loops-for-range-listas.md +++ /dev/null @@ -1,319 +0,0 @@ ---- -title: "Laços de repetição em Python: for, range e listas" -slug: "loops-for-range-listas" -discipline: "python" -order: 11 -description: "Como repetir blocos de código com o laço for, gerar sequências com range e percorrer listas e strings de forma segura e legível." -reading_time: 60 -difficulty: "easy" -concepts: - - laços de repetição - - loop for - - range - - listas - - sequência - - iteração - - objeto iterável - - índice - - off-by-one -prerequisites: - - "por-que-programar-python" - - "algoritmos-e-notebooks" - - "variaveis-tipos-estilo-python" - - "conversao-tipos-operadores-aritmeticos" - - "strings-literais-multilinhas" - - "strings-escape-concatenacao" - - "strings-indices-slice-metodos" - - "strings-interpolacao-input" - - "desvios-condicionais-if-elif-else" - - "operadores-logicos-match-case" -learning_objectives: - - "Entender o papel de laços de repetição para evitar código duplicado e controlar repetições em algoritmos." - - "Usar o laço for em Python para iterar sobre sequências como listas e strings." - - "Gerar sequências numéricas com a função range, compreendendo início, parada não inclusiva e passo." - - "Percorrer listas com for para processar coleções de dados de forma clara e segura." -exercises: - - question: "Por que dizemos que a função `range(10)` gera uma parada 'não inclusiva', e como isso ajuda a evitar erros de índice ao iterar sobre listas?" - answer: "Porque `range(10)` gera os inteiros de 0 até 9, ou seja, o valor de parada (10) não aparece na sequência; ele é apenas a fronteira de corte. Isso combina exatamente com os índices de uma lista de tamanho 10 (0 a 9), o que permite usar `for i in range(len(lista))` sem correr o risco de acessar um índice inválido." - hint: "Compare mentalmente: `len(lista) == 10`, índices válidos de 0 a 9, e a saída de `list(range(10))`." -review_after_days: [1, 3, 7, 30] ---- - -### Visão Geral - -Depois de aprender a **tomar decisões** com `if`, `elif`, `else` e operadores lógicos, o próximo passo é aprender a **repetir** trechos de código sem copiar e colar. Nesta aula você vê o laço **`for`**, a função **`range()`** para gerar sequências numéricas e a ideia de **listas** como coleções ordenadas que podem ser percorridas. - -### Modelo Mental - -Pense em um **carrossel de itens**: - -- Uma **lista** é uma fila ordenada de elementos (alunos, notas, produtos, caracteres de uma string). -*- Um laço `for` é o funcionário que, para **cada** item da fila, executa a mesma tarefa: “pega o próximo, processa, volta para o próximo...”. -- A função `range()` é uma **fábrica de números sequenciais**: você diz de onde começa, onde para (não-inclusivo) e de quantos em quantos quer avançar; ela devolve uma sequência que o `for` percorre. - -Esse modelo evita o “castigo do Bart Simpson”: escrever a mesma linha de código dez, cem ou mil vezes. - -### Mecânica Central - -- **Lista (array unidimensional)**: - -```python -vogais = ['a', 'e', 'i', 'o', 'u'] - -print(vogais[0]) # 'a' -print(vogais[2]) # 'i' -``` - -Cada posição é acessada por um **índice numérico** de `0` até `len(vogais) - 1`. - -- **Laço `for` sobre uma sequência**: - -```python -vogais = ['a', 'e', 'i', 'o', 'u'] - -for letra in vogais: - print(f"Letra: {letra}") -``` - -- **`for` sobre uma string (strings também são sequências)**: - -```python -frase = "Eu amo a linguagem Python" - -for caractere in frase: - print(caractere) # inclusive espaços em branco -``` - -- **Função `range()`** – gera sequência numérica imutável (não pode ser alterada depois de criada): - -```python -for i in range(10): - print(i) # 0, 1, 2, ..., 9 - -for i in range(2, 15): - print(i) # 2, 3, ..., 14 - -for i in range(2, 21, 2): - print(i) # 2, 4, 6, ..., 20 (números pares) -``` - -### Uso Prático - -Laços `for` aparecem em praticamente todo código de dados: - -- **Percorrer listas de valores**: notas de alunos, vendas diárias, temperaturas. -- **Iterar sobre caracteres** em uma string para análise de texto. -- **Gerar sequências numéricas** com `range()` para simulações, índices ou construções de tabelas. - -Exemplo prático: imprimir o índice e o valor de cada nota de um aluno: - -```python -notas = [7.5, 8.0, 6.3, 9.2] - -for i in range(len(notas)): - print(f"Prova {i + 1}: nota = {notas[i]}") -``` - -### Visual: laço `for` percorrendo uma sequência - -```mermaid -flowchart TD - A[Início] --> B[Definir sequência
lista, string ou range] - B --> C[Configurar laço for
for valor in sequência] - C --> D{Ainda há próximo
elemento na sequência?} - D -- Sim --> E[valor recebe próximo elemento] - E --> F[Executar bloco de código
com o valor atual] - F --> D - D -- Não --> G[Fim do laço] - G --> H[Fim do programa] -``` - -O `for` é responsável por pegar **cada elemento** da sequência, um por vez, e executar o mesmo bloco para todos. - -### Erros Comuns - -- **Copiar e colar prints** em vez de usar `for`, o que dificulta manutenção. -- **Confundir parada não inclusiva de `range()`**: esperar que `range(10)` gere o número 10. -- **Tentar iterar um tipo não iterável**: - -```python -frase = 123 -for caractere in frase: - print(caractere) # TypeError: 'int' object is not iterable -``` - -- **Esquecer da identação** dentro do laço: - -```python -for letra in vogais: -print(letra) # Erro: precisa estar identado -``` - -### Visão Geral de Debugging - -Para depurar laços: - -- Imprima **valores intermediários** dentro do laço (`print(i, letra)`), especialmente em `range()` com três parâmetros. -- Verifique manualmente as extremidades de `range(inicio, parada, passo)` e compare com a saída esperada. -- Use `len(lista)` para confirmar se o intervalo de `range()` está alinhado ao tamanho da sequência. -- Em casos de erros de tipo, confira se o objeto realmente é **iterável** (lista, string, `range`, etc.). - -### Principais Pontos - -- Listas são coleções ordenadas indexadas de `0` a `len(lista) - 1`. -- O laço `for` em Python itera sobre qualquer **objeto iterável** (listas, strings, `range`, etc.). -- `range()` gera sequências numéricas com parada **não inclusiva**, o que combina perfeitamente com índices de listas. -- Usar laços de repetição reduz duplicação de código e torna seus algoritmos mais claros e fáceis de manter. - -### Preparação para Prática - -Antes de ir para o laboratório: - -- Pegue um trecho de código em que você repetiu `print()` várias vezes e tente reescrever usando um laço `for`. -- Experimente no interpretador Python: `list(range(5))`, `list(range(2, 10))`, `list(range(2, 21, 2))` e veja as sequências geradas. -- Monte uma mini tabela que relacione `len(lista)` com os índices válidos e com `range(len(lista))`. - -### Laboratório de Prática - -#### 1. Castigo do Bart Simpson automatizado (Easy) - -Implemente uma função que recebe uma frase e um número de repetições, e imprime a frase esse número de vezes. - -```python -def escrever_castigo(frase: str, repeticoes: int) -> None: - """ - Imprime a frase repeticoes vezes, uma por linha. - """ - # TODO: usar um laço for com range(repeticoes) - # para evitar copiar e colar prints. - pass - - -if __name__ == "__main__": - escrever_castigo("Eu amo Python, eu vou aprender!", 10) -``` - -#### 2. Relatório de caracteres de uma frase (Medium) - -Implemente uma função que, dada uma frase, percorre cada caractere e monta uma **lista de linhas** descrevendo a posição e o próprio caractere. - -Exemplo (entrada `"ABC"`): - -- `"0: 'A'"` -- `"1: 'B'"` -- `"2: 'C'"` - -```python -from typing import List - - -def descrever_caracteres(frase: str) -> List[str]: - """ - Retorna uma lista de descrições 'indice: caractere' para cada - caractere da frase. - """ - descricoes: List[str] = [] - - # TODO: usar range(len(frase)) e frase[i] para montar as descrições - # e adicioná-las na lista 'descricoes'. - - return descricoes - - -if __name__ == "__main__": - for linha in descrever_caracteres("Eu amo Python"): - print(linha) -``` - -#### 3. Gerador de números pares com range (Hard) - -Implemente uma função que recebe dois inteiros `inicio` e `fim` e devolve uma lista com todos os **números pares** nesse intervalo, inclusive os limites se forem pares. - -Use **apenas `range()` e `for`**, sem `while`. - -```python -from typing import List - - -def gerar_pares(inicio: int, fim: int) -> List[int]: - """ - Gera todos os números pares entre inicio e fim (inclusive), - usando range() e for. - """ - pares: List[int] = [] - - # TODO: - # 1. Ajustar o primeiro valor para ser o primeiro par >= inicio. - # 2. Usar range(inicio_par, fim + 1, 2) para percorrer apenas pares. - # 3. Preencher a lista 'pares' com os valores gerados. - - return pares - - -if __name__ == "__main__": - print(gerar_pares(2, 20)) # esperado: [2, 4, 6, ..., 20] - print(gerar_pares(3, 15)) # esperado: [4, 6, 8, 10, 12, 14] - print(gerar_pares(10, 10)) # esperado: [10] -``` - - - - - diff --git a/content/python/aula-12-range-acumuladores-enumerate.md b/content/python/aula-12-range-acumuladores-enumerate.md deleted file mode 100644 index 02a46d3..0000000 --- a/content/python/aula-12-range-acumuladores-enumerate.md +++ /dev/null @@ -1,354 +0,0 @@ ---- -title: "Range avançado, acumuladores, enumerate e loops aninhados em Python" -slug: "range-acumuladores-enumerate-loops-aninhados" -discipline: "python" -order: 12 -description: "Como combinar range() com for para repetir ações, acumular valores, numerar elementos com enumerate e criar loops aninhados em estruturas 2D." -reading_time: 65 -difficulty: "easy" -concepts: - - range - - laços de repetição - - acumuladores - - operadores de atribuição composta - - tabuada - - enumerate - - loops aninhados - - matriz 2D (linha e coluna) -prerequisites: - - "por-que-programar-python" - - "algoritmos-e-notebooks" - - "variaveis-tipos-estilo-python" - - "conversao-tipos-operadores-aritmeticos" - - "strings-literais-multilinhas" - - "strings-escape-concatenacao" - - "strings-indices-slice-metodos" - - "strings-interpolacao-input" - - "desvios-condicionais-if-elif-else" - - "operadores-logicos-match-case" - - "loops-for-range-listas" -learning_objectives: - - "Aprofonfar o uso da função range() com diferentes assinaturas (stop, start/stop, start/stop/step) para controlar laços for." - - "Usar variáveis acumuladoras e operadores de atribuição composta (+=) para somar valores em laços." - - "Construir taboadas e relatórios numéricos com for e range()." - - "Aplicar enumerate e loops aninhados para percorrer coleções com índices e estruturas 2D." -exercises: - - question: "Por que é mais legível escrever `soma += numero` dentro de um laço do que `soma = soma + numero`, e o que essa sintaxe comunica para quem lê o código?" - answer: "`soma += numero` é uma forma abreviada e idiomática de expressar 'pegue o valor atual de soma e acrescente numero a ele', evitando repetição do nome da variável e focando na ideia de acumular. Para quem lê, essa sintaxe sinaliza rapidamente que se trata de um acumulador sendo atualizado em cada iteração, o que é um padrão muito comum em loops." - hint: "Compare quantas vezes o nome da variável aparece nas duas formas e pense em como isso ajuda a reconhecer padrões como 'soma', 'contador', 'total'." -review_after_days: [1, 3, 7, 30] ---- - -### Visão Geral - -Esta aula aprofunda o uso de **laços `for` com `range()`**, apresentando padrões clássicos de programação: **acumuladores** (como somas e médias), **tabuada**, numeração de elementos com **`enumerate()`** e **loops aninhados** para representar estruturas 2D como matrizes. Tudo isso reforça a ideia de que a maior parte da lógica em programas de dados combina **condições** e **repetição controlada**. - -### Modelo Mental - -Pense em três metáforas: - -- **Acumulador**: um “cofrinho” que começa em zero e, a cada iteração, recebe mais moedas (`soma += numero`). -- **Enumerate**: uma fila em que cada pessoa recebe um crachá com número; você enxerga ao mesmo tempo o **índice** e o **valor**. -- **Loops aninhados**: uma grade de assentos (linhas e colunas); o laço externo percorre as linhas, o interno percorre as colunas de cada linha. - -O `range()` é o “ritmo” que define quantas vezes o laço toca a mesma partitura; o acumulador registra o resultado ao final. - -### Mecânica Central - -- **`range()` recap (3 formas principais)**: - -```python -for i in range(5): # 0, 1, 2, 3, 4 - print(i) - -for i in range(2, 6): # 2, 3, 4, 5 - print(i) - -for i in range(1, 10, 2): # 1, 3, 5, 7, 9 - print(i) -``` - -- **Acumulador simples (soma e média)**: - -```python -soma = 0 -for i in range(5): - numero = int(input(f"Digite o {i + 1}º número: ")) - soma += numero # acumulador - -media = soma / 5 -print(f"Soma = {soma}") -print(f"Média = {media:.3f}") -``` - -- **Operador de atribuição composta**: - -```python -soma = 0 -soma = soma + 3 # forma longa -soma += 3 # forma abreviada equivalente -``` - -- **Tabuada com `for` e `range()`**: - -```python -numero = int(input("Digite um número: ")) -print(f"\nTabuada do número: {numero}\n") - -for i in range(11): # 0 a 10 - print(f"{numero} x {i} = {numero * i}") -``` - -- **`enumerate()` para índice + valor**: - -```python -frutas = ["maca", "uva", "laranja", "ata"] - -for i, fruta in enumerate(frutas): - print(f"Fruta {i}: {fruta}") -``` - -- **Loops aninhados para coordenadas (matriz 3x3)**: - -```python -for i in range(3): # linhas - for j in range(3): # colunas - print(f"({i},{j})", end=" ") - print() # quebra de linha ao final de cada linha -``` - -### Uso Prático - -Algumas aplicações diretas: - -- **Calculadoras simples**: somar vários valores digitados (notas, vendas diárias, medições). -- **Relatórios de tabuada** para debug de fórmulas e treinos de multiplicação. -- **Relatórios indexados** usando `enumerate()` (por exemplo, lista de produtos numerados ou logs com linha). -- **Estruturas 2D** (linhas x colunas) em matrizes, tabelas ou grids de pixels/dashboards. - -Exemplo: somar n leituras de sensor e calcular a média: - -```python -qtd = int(input("Quantas leituras? ")) -soma = 0.0 - -for i in range(qtd): - leitura = float(input(f"Leitura {i + 1}: ")) - soma += leitura - -media = soma / qtd if qtd > 0 else 0.0 -print(f"Média das leituras: {media:.2f}") -``` - -### Visual: acumulador, enumerate e loops aninhados - -```mermaid -flowchart TD - A[Início] --> B[Definir range()/coleção] - B --> C{for ... in range/coleção} - C --> D[Acumulador
total += valor] - C --> E[Enumerate
(indice, valor)] - C --> F[Loop interno
for j in range(...)] - D --> G[Atualizar total] - E --> H[Usar indice e valor
em impressões/relatórios] - F --> I[Processar par (i,j)
em matriz 2D] - G --> C - H --> C - I --> C - C --> J[Fim do laço] - J --> K[Fim do programa] -``` - -Esse diagrama destaca que a estrutura do laço é a mesma; o que muda é o que você faz **dentro** dele (acumular, numerar, aninhar). - -### Erros Comuns - -- **Esquecer de inicializar o acumulador** (usar `soma` sem ter definido `soma = 0` antes do laço). -- **Dividir pela quantidade errada** ao calcular a média (usar o último índice em vez do total de elementos). -- **Confundir `range(10)` com 1 a 10**: lembrar que a parada é **não inclusiva**. -- **Tentar usar `enumerate()` mas ignorar um dos valores**: - -```python -for par in enumerate(frutas): - print(par) # imprime tuplas (indice, valor), não só o valor; se quiser separado, desempacote em duas variáveis -``` - -- **Loops aninhados mal identados**, gerando saídas em formato inesperado. - -### Visão Geral de Debugging - -Para depurar esses padrões: - -- Faça **testes de mesa** (como na aula): anote manualmente os valores de `soma`, `numero`, `i` a cada iteração. -- Coloque `print()` temporários dentro do laço para ver: `print(i, numero, soma)`. -- Ao usar `enumerate()`, teste primeiro com `print(list(enumerate(frutas)))` para ver a estrutura (lista de tuplas). -- Em loops aninhados, teste com tamanhos pequenos (`range(2)`) até entender a ordem de visita dos pares `(i, j)`. - -### Principais Pontos - -- `range()` controla **quantas vezes** um laço é executado e quais valores de índice são usados. -- Variáveis **acumuladoras** e operadores `+=` são padrão para somas e contagens dentro de laços. -- `enumerate()` entrega **índice e valor** ao mesmo tempo, deixando o código mais limpo do que `range(len(lista))`. -- **Loops aninhados** permitem percorrer estruturas 2D (linhas x colunas), bastando ter um `for` dentro de outro. - -### Preparação para Prática - -Antes do laboratório: - -- Reescreva um trecho com `soma = soma + numero` usando `+=` e note a diferença visual. -- Pegue uma lista qualquer (`frutas`, `cidades`) e imprima tanto o índice quanto o valor usando `enumerate()`. -- Esboce em papel uma matriz 3x3 e, ao lado, liste na ordem em que `(i, j)` aparece no loop aninhado. - -### Laboratório de Prática - -#### 1. Somar e calcular média de n números (Easy) - -Implemente uma função que recebe um número `n` e, em seguida, lê `n` números do usuário, retornando a soma e a média. - -```python -from typing import Tuple - - -def somar_e_calcular_media(qtd: int) -> Tuple[float, float]: - """ - Lê 'qtd' números do usuário, acumulando a soma - e retornando (soma, media). - """ - soma = 0.0 - - # TODO: usar range(qtd) e um acumulador soma += numero - # para ler 'qtd' valores do usuário. - - media = soma / qtd if qtd > 0 else 0.0 - return soma, media - - -if __name__ == "__main__": - n = int(input("Quantos números deseja digitar? ")) - soma, media = somar_e_calcular_media(n) - print(f"Soma = {soma}") - print(f"Média = {media:.3f}") -``` - -#### 2. Tabuada formatada com múltiplas colunas (Medium) - -Implemente uma função que recebe um número inteiro `numero` e imprime a tabuada de 1 a 10, **duas colunas por linha**: - -Exemplo (para 5): - -`5 x 1 = 5 5 x 2 = 10` -`5 x 3 = 15 5 x 4 = 20` -... - -```python -def imprimir_tabuada_dupla(numero: int) -> None: - """ - Imprime a tabuada de 'numero' de 1 a 10, em duas colunas por linha: - 5 x 1 = 5 5 x 2 = 10 - 5 x 3 = 15 5 x 4 = 20 - ... - """ - # TODO: - # - Use range() e um laço for. - # - Em cada linha, imprima dois pares (i, i+1) se houver espaço (até 10). - # - Use f-strings e '\t' para alinhar as colunas. - pass - - -if __name__ == "__main__": - n = int(input("Digite um número para ver a tabuada dupla: ")) - imprimir_tabuada_dupla(n) -``` - -#### 3. Imprimir coordenadas de uma matriz N x M (Hard) - -Implemente uma função que, dados `linhas` e `colunas`, imprime todas as coordenadas `(i, j)` de uma matriz `linhas x colunas`, usando **loops aninhados**. Cada linha da matriz deve ser impressa em uma linha do console. - -```python -def imprimir_coordenadas_matriz(linhas: int, colunas: int) -> None: - """ - Imprime as coordenadas (i,j) de uma matriz de tamanho - linhas x colunas, linha por linha. - """ - # TODO: - # - Use um for externo para as linhas: for i in range(linhas) - # - Use um for interno para as colunas: for j in range(colunas) - # - Em cada iteração interna, imprima f"({i},{j}) " com end="". - # - Após o laço interno, faça um print() vazio para quebrar a linha. - pass - - -if __name__ == "__main__": - imprimir_coordenadas_matriz(3, 3) - print("---") - imprimir_coordenadas_matriz(2, 4) -``` - - - - - diff --git a/content/python/aula-13-pratica-teste-mesa-tabuada-enumerate-matriz.md b/content/python/aula-13-pratica-teste-mesa-tabuada-enumerate-matriz.md deleted file mode 100644 index ef5c219..0000000 --- a/content/python/aula-13-pratica-teste-mesa-tabuada-enumerate-matriz.md +++ /dev/null @@ -1,326 +0,0 @@ ---- -title: "Prática: teste de mesa, tabuada, enumerate e matriz (loops em Python)" -slug: "aula-13-pratica-teste-mesa-tabuada-enumerate-matriz" -discipline: "python" -order: 13 -description: "Aplicar range, acumuladores, enumerate e loops aninhados com teste de mesa, formatação de saída e duas formas de exibir matriz." -reading_time: 50 -difficulty: "easy" -concepts: - - teste de mesa - - acumulador (inicialização e +=) - - tabuada com range e formatação - - enumerate para índice e valor - - len() em coleções - - loops aninhados (linha/coluna) - - print(end=) e concatenação por linha -prerequisites: - - "range-acumuladores-enumerate-loops-aninhados" -learning_objectives: - - "Fazer teste de mesa para algoritmos com acumulador e prever saídas." - - "Implementar tabuada com range(11), f-string ou .format() e cabeçalho." - - "Iterar listas com enumerate() e com range(len()) para índice + valor." - - "Gerar coordenadas de matriz com loops aninhados e controlar quebra de linha (result ou end=)." -exercises: - - question: "Por que o teste de mesa é considerado uma forma de 'debug' manual e em que momento do desenvolvimento ele mais ajuda?" - answer: "O teste de mesa simula a execução passo a passo, anotando valores das variáveis a cada iteração, o que permite prever o resultado e encontrar erros de lógica antes de executar o código. Ajuda mais ao desenhar o algoritmo e ao suspeitar de off-by-one ou acumulador não inicializado." - hint: "Pense em quando você ainda não rodou o programa e quer validar a lógica." - - question: "Qual a vantagem de usar enumerate(frutas) em vez de for i in range(len(frutas)) com frutas[i]?" - answer: "enumerate() evita hardcode do tamanho, deixa o código mais legível e pythônico, e entrega índice e valor em uma única iteração sem risco de índice fora do intervalo." - hint: "Compare a quantidade de nomes (len, range, índice) e a intenção expressa no código." -review_after_days: [1, 3, 7] ---- - -## Visão Geral do Conceito - -Esta aula aplica os conceitos de **laços `for` com `range()`**, **acumuladores**, **`enumerate()`** e **loops aninhados** em problemas concretos: soma e média de vários números, tabuada, listagem indexada de uma coleção e representação de coordenadas em matriz. Além disso, introduz o **teste de mesa** como técnica de depuração manual e mostra duas formas de formatar saída em múltiplas “linhas” (acumulando string ou usando o parâmetro `end` de `print()`). O objetivo é consolidar o uso desses padrões em situações reais de processamento de dados e relatórios simples. - -## Modelo Mental - -- **Teste de mesa**: você é o “computador”; a cada linha do código, anota o valor atual das variáveis (por exemplo `soma`, `numero`, `i`). Ao final de cada iteração, sabe exatamente o estado do programa sem precisar executá-lo. -- **Acumulador**: um único “recipiente” (por exemplo `soma`) que começa com um valor neutro (0 para soma, 1 para produto) e, a cada volta do laço, é atualizado com o novo valor (por exemplo `soma += numero`). -- **Tabuada**: um único número fixo (digitado) multiplicado por uma sequência (0 a 10); o laço percorre a sequência e imprime uma linha por vez. -- **Enumerate**: em vez de você mesmo gerar índices com `range(len(lista))`, a função `enumerate()` entrega (índice, valor) a cada iteração. -- **Matriz (linhas × colunas)**: o laço externo percorre as linhas; o interno, as colunas. Para exibir “uma linha por vez” no console, ou você acumula os pares em uma string e dá um `print(result)` ao sair do laço interno, ou usa `print(..., end=' ')` dentro do interno e um `print()` vazio depois para quebrar a linha. - -## Mecânica Central - -### Teste de mesa para soma e média - -Algoritmo típico: ler 5 números, acumular em `soma`, exibir soma e média. O teste de mesa descreve, por iteração, os valores de `soma` e `numero` e o resultado da atribuição (por exemplo “soma = 0 + 1 → 1”, “soma = 1 + 2 → 3”, …). Com entradas 1, 2, 3, 4 e 5, o resultado esperado é soma 15 e média 3,000 (três casas decimais). - -### Inicialização do acumulador e += - -- O acumulador deve ser **inicializado** antes do laço. Para soma, use o elemento neutro: `soma = 0`. -- `soma += numero` é equivalente a `soma = soma + numero`. - -### Tabuada: range(11) e formatação - -- Ler um inteiro `numero`; exibir cabeçalho (ex.: “Tabuada do numero: 5”) e, para `i` em `range(11)` (0 a 10), imprimir `numero * i`. -- Formatação: f-string `f'{numero} x {i} = {numero*i}'` ou `'{} x {} = {}'.format(numero, i, numero*i)`; `\t` pode ser usado para alinhar colunas. - -### Enumerate e alternativa com range(len()) - -- `enumerate(coleção)` retorna pares (índice, valor). Uso típico: `for i, fruta in enumerate(frutas):`. -- Alternativa sem `enumerate`: `for i in range(len(frutas)):` e acessar `frutas[i]`. Usar `len(frutas)` deixa o código dinâmico em relação ao tamanho da lista. - -### Loops aninhados para matriz (saída por linha) - -- Objetivo: exibir coordenadas `(i, j)` de uma matriz 3×3, com uma linha de pares por linha do console. -- **Primeira forma**: dentro do laço externo (linhas), inicializar `result = ''`; no laço interno (colunas), fazer `result += f'({i},{j}) '`; ao sair do interno, `print(result)`. -- **Segunda forma**: dentro do interno, `print(f'({i},{j})', end=' ')`; após o interno, `print()` para quebrar a linha. O parâmetro `end` de `print()` substitui o caractere padrão de fim de linha (`\n`) pelo valor passado (por exemplo espaço ou string vazia). - -Fluxo dos padrões desta aula: - -```mermaid -flowchart TD - A[Início] --> B{Padrão} - B -->|Acumulador| C[soma = 0] - C --> D[for i in range N] - D --> E[numero = input / soma += numero] - E --> D - D --> F[print soma e média] - B -->|Tabuada| G[numero = input] - G --> H[for i in range 11] - H --> I[print numero x i = produto] - I --> H - B -->|Índice + valor| J[for i, valor in enumerate lista] - J --> K[print i, valor] - K --> J - B -->|Matriz| L[for i in range linhas] - L --> M[result = '' ou end=' '] - M --> N[for j in range colunas] - N --> O[acumular ou print end] - O --> N - N --> P[print result ou print vazio] - P --> L -``` - -## Uso Prático - -### Soma e média de cinco números (com teste de mesa) - -Valores a serem digitados: 1, 2, 3, 4 e 5. Resultado esperado: soma 15, média 3,000. - -```python -# Variável para acumular os valores digitados -soma = 0 - -for i in range(5): - numero = int(input(f'Digite o {i + 1}º numero: ')) - soma += numero # soma = soma + numero - -print(f'Soma dos numeros digitados eh: {soma}') -print(f'A media dos numeros digitados eh: {soma/5:.3f}') -``` - -Teste de mesa (resumo): 1ª iteração soma 0, numero 1 → soma = 1; 2ª soma 1, numero 2 → 3; 3ª soma 3, numero 3 → 6; 4ª soma 6, numero 4 → 10; 5ª soma 10, numero 5 → 15. Saída: soma 15, média 3,000. - -### Tabuada de um número (0 a 10) - -Exemplo: digitando 5, exibir “Tabuada do numero: 5” e as linhas 5×0=0 até 5×10=50. - -```python -numero = int(input('Digite um numero: ')) -print(f'Tabuada do numero: {numero}\n') - -for i in range(11): - print(f'{numero} x {i} = {numero * i}') -``` - -Alternativa com `.format()`: `print('{} x {} \t= {}'.format(numero, i, numero * i))`. - -### Lista com índice: enumerate e range(len) - -Dada a coleção `['maca', 'uva', 'laranja', 'ata']`, saída desejada: “fruta 0: maca”, “fruta 1: uva”, etc. - -**Com enumerate:** - -```python -frutas = ['maca', 'uva', 'laranja', 'ata'] - -for i, fruta in enumerate(frutas): - print(f'fruta {i}: {fruta}') -``` - -**Com range(len) (dinâmico):** - -```python -frutas = ['maca', 'uva', 'laranja', 'ata'] - -for i in range(len(frutas)): - print(f'fruta {i}: {frutas[i]}') -``` - -### Matriz 3×3: coordenadas (i, j) por linha - -Saída desejada: primeira linha “(0,0) (0,1) (0,2)”, segunda “(1,0) (1,1) (1,2)”, terceira “(2,0) (2,1) (2,2)”. - -**Primeira forma — acumulando string por linha:** - -```python -for i in range(3): - result = '' - for j in range(3): - result += f'({i}, {j}) ' - print(result) -``` - -**Segunda forma — print com end e quebra ao final da linha:** - -```python -for i in range(3): - for j in range(3): - print(f'({i},{j})', end=' ') - print() -``` - -## Erros Comuns - -- **Não inicializar o acumulador**: usar `soma += numero` sem antes definir `soma = 0` gera `NameError`. -- **Média com divisor errado**: usar o último índice (por exemplo 4) em vez da quantidade de elementos (5) na divisão da média. -- **range(10) quando se quer 0 a 10**: `range(11)` gera 0..10; `range(10)` para em 9. -- **.format() com %d**: ao usar `.format()`, os placeholders são `{}`, não `%d` (que é da formatação com `%`). -- **Esquecer o print() após o laço interno**: ao usar `print(..., end=' ')`, sem um `print()` vazio depois do interno todas as coordenadas saem na mesma linha. - -## Visão Geral de Debugging - -- **Teste de mesa**: anote em papel ou comentário os valores de `soma`, `numero` e `i` a cada iteração e confira o resultado final (soma 15, média 3,0 para 1,2,3,4,5). -- **Impressões temporárias**: dentro do laço, `print(i, numero, soma)` para inspecionar o estado. -- **Enumerate**: conferir a estrutura com `print(list(enumerate(frutas)))` (lista de tuplas (índice, valor)). -- **Loops aninhados**: reduzir para `range(2)` e observar a ordem dos pares (0,0), (0,1), (1,0), (1,1) para entender quem é linha e quem é coluna. - -## Principais Pontos - -- Teste de mesa é executar o algoritmo “à mão”, anotando variáveis a cada passo; ajuda a validar lógica e a encontrar erros de acumulador ou contagem. -- Acumulador deve ser inicializado (0 para soma) antes do laço; `soma += numero` é a forma idiomática de atualizar. -- Tabuada de 0 a 10 usa `range(11)`; saída pode ser f-string ou `.format()`. -- `enumerate(coleção)` fornece (índice, valor); alternativa é `range(len(coleção))` e acesso por índice. -- Em matriz, laço externo = linhas, interno = colunas; para uma linha por linha no console: acumular em string e `print(result)` ou `print(..., end=' ')` + `print()`. - -## Preparação para Prática - -Antes do laboratório: - -- Fazer um teste de mesa completo para o algoritmo de soma de 3 números (por exemplo 10, 20, 30) e conferir soma 60 e média 20,0. -- Escrever a tabuada de 7 usando `range(11)` e f-string. -- Percorrer uma lista de nomes com `enumerate()` e imprimir “posição N: nome”. -- Desenhar uma grade 2×2 e listar a ordem dos pares `(i, j)` como no loop aninhado. - -## Laboratório de Prática - -### 1. Teste de mesa e soma de N notas (Easy) - -Implemente um programa que lê um inteiro `n`, depois `n` notas (float), acumula a soma e exibe a média com duas casas decimais. No código, inclua um comentário com um mini teste de mesa para n=3 e notas 8.0, 7.5 e 9.0. - -```python -def ler_notas_e_media(qtd: int) -> float: - """ - Lê 'qtd' notas do usuário, acumula a soma e retorna a média. - """ - soma = 0.0 - # TODO: for i in range(qtd), ler float(input(...)), acumular em soma - # TODO: retornar soma / qtd (ou 0.0 se qtd == 0) - return 0.0 - - -if __name__ == "__main__": - n = int(input("Quantas notas? ")) - media = ler_notas_e_media(n) - print(f"Media: {media:.2f}") -``` - -### 2. Tabuada formatada com cabeçalho (Medium) - -Implemente uma função que recebe um inteiro `numero` e imprime a tabuada de 0 a 10 com um cabeçalho “Tabuada do numero: X” e uma linha em branco abaixo. Use f-string e `\t` para alinhar a coluna do resultado. - -```python -def imprimir_tabuada(numero: int) -> None: - """Imprime a tabuada de 'numero' de 0 a 10 com cabeçalho.""" - # TODO: print cabeçalho com \n - # TODO: for i in range(11): print numero x i = produto (com \t) - pass - - -if __name__ == "__main__": - n = int(input("Digite um numero para a tabuada: ")) - imprimir_tabuada(n) -``` - -### 3. Lista indexada e matriz M×N (Hard) - -Implemente duas funções: (1) `listar_com_indice(itens: list)` que imprime cada elemento no formato “indice: valor” usando `enumerate()`; (2) `imprimir_matriz_coordenadas(linhas: int, colunas: int)` que imprime as coordenadas `(i,j)` com uma linha do console por linha da matriz, usando `print(..., end=' ')` e `print()`. - -```python -def listar_com_indice(itens: list) -> None: - """Imprime cada item como 'indice: valor' usando enumerate.""" - # TODO: for i, valor in enumerate(itens): print(f'{i}: {valor}') - pass - - -def imprimir_matriz_coordenadas(linhas: int, colunas: int) -> None: - """Imprime coordenadas (i,j) com uma linha do console por linha da matriz.""" - # TODO: for i in range(linhas): for j in range(colunas): print (i,j) end=' '; depois print() - pass - - -if __name__ == "__main__": - listar_com_indice(["maca", "uva", "laranja"]) - imprimir_matriz_coordenadas(2, 3) -``` - - - - diff --git a/content/python/aula-14-funcoes-parametros-docstrings-builtins.md b/content/python/aula-14-funcoes-parametros-docstrings-builtins.md deleted file mode 100644 index c3c8dd7..0000000 --- a/content/python/aula-14-funcoes-parametros-docstrings-builtins.md +++ /dev/null @@ -1,309 +0,0 @@ ---- -title: "Funções em Python: definição, parâmetros, docstrings e builtins" -slug: "funcoes-parametros-docstrings-builtins" -discipline: "python" -order: 14 -description: "Definir funções com def, separar parâmetros e argumentos, documentar com docstrings, usar help() e revisar funções integradas do Python." -reading_time: 28 -difficulty: "medium" -concepts: - - blocos reutilizáveis e coesão - - def e chamada de função - - parâmetros formais vs argumentos - - argumentos posicionais e nomeados - - docstrings e help() - - funções integradas (built-in) - - PEP 8 para nomes de função -prerequisites: - - "aula-13-pratica-teste-mesa-tabuada-enumerate-matriz" -learning_objectives: - - "Definir funções com nomes em snake_case e responsabilidade única." - - "Distinguir parâmetro (na definição) de argumento (na chamada) e usar argumentos nomeados para evitar troca de ordem." - - "Escrever docstrings legíveis e inspecionar funções com help()." - - "Reconhecer builtins usuais (print, input, conversões, type, len) e quando consultar a documentação oficial." -exercises: - - question: "Qual a diferença prática entre parâmetro e argumento, e por que argumentos nomeados ajudam após ler dados com input()?" - answer: "Parâmetro é o nome na assinatura (o 'slot'); argumento é o valor passado na chamada. Com vários valores vindos do usuário, chamar com nome=valor garante que idade não vá parar no parâmetro que esperava nome, mesmo que a ordem na chamada seja diferente da assinatura." - hint: "Pense na chamada calcular_metricas(nome=..., idade=...) versus passar quatro valores só por posição." - - question: "Por que aparece NameError ao rodar só a célula que chama uma função definida em outra célula do notebook?" - answer: "O notebook só registra a definição no kernel depois que a célula da def foi executada. Sem essa execução, o nome da função não existe no namespace atual." - hint: "Ordem de execução ≠ ordem visual das células." -review_after_days: [3, 7] ---- - -## Visão Geral do Conceito - -Funções agrupam trechos de código reutilizáveis, com responsabilidades claras: você nomeia uma operação, define entradas (`parâmetros`) e pode devolver um resultado (`return`) ou só produzir efeitos (por exemplo `print`). Em projetos de dados e automação, funções aparecem em todo lugar: normalizar colunas, validar JSON, formatar logs, aplicar regras de negócio. - -Você já usa **funções integradas** (`built-ins`) como `print()`, `input()`, `int()`, `float()`, `type()` e `len()`. Esta lição mostra como definir as suas com `def`, documentar com **docstrings** e explorar APIs de bibliotecas com `help()`, alinhado ao [tutorial oficial sobre definição de funções](https://docs.python.org/3/tutorial/controlflow.html#defining-functions) e ao [PEP 8](https://peps.python.org/pep-0008/). - -## Modelo Mental - -- **Caixa com rótulos na tampa:** a assinatura `def processar(a, b)` é a “tampa” com os nomes dos slots. Na chamada, os valores que você encaixa nesses slots são os `argumentos`. Argumentos **posicionais** preenchem da esquerda para a direita; **nomeados** (`b=2, a=1`) dizem explicitamente qual slot recebe qual valor. -- **Docstring como manual interno:** a primeira string literal após o `def` vira documentação da função; `help(minha_funcao)` mostra esse texto no console. -- **Bibliotecas profissionais:** em pacotes como scikit-learn, docstrings seguem estilos estruturados (por exemplo seções *Parameters* e *Returns* no estilo NumPy). Você não precisa memorizar o formato; precisa saber **onde** ler (`help()`, site da lib). - -## Mecânica Central - -### Sintaxe básica - -```python -def nome_da_funcao(parametro1, parametro2): - """Uma linha que resume o efeito ou contrato da função.""" - # corpo - return resultado # opcional -``` - -- `def` associa o nome ao objeto função no namespace atual (módulo ou sessão do notebook). -- Sem `return` explícito, a função devolve `None`. - -### Parâmetros vs argumentos - -> **Regra:** na **definição**, lista-se `parâmetros` (nomes formais). Na **chamada**, passam-se `argumentos` (valores reais). - -Se a ordem posicional não bater com a intenção (por exemplo trocar altura e nome em um cálculo numérico), o Python ainda “aceita” enquanto os tipos permitirem operações — e o bug aparece tarde, às vezes como `TypeError` ao aplicar operador a tipos errados. - -### Docstrings e `help()` - -Docstrings próprias podem ser simples (como na aula). Em código de biblioteca, é comum ver blocos maiores com seções; o mecanismo é o mesmo: texto armazenado em `__doc__` e exibido por `help(objeto)`. - -Exemplo de uso pedagógico com API de terceiros: após `from sklearn.base import ClusterMixin`, `help(ClusterMixin.fit_predict)` mostra assinatura e documentação do método — útil quando o nome do parâmetro explica o contrato (por exemplo `X` como dados de entrada). - -### Builtins recorrentes neste módulo - -Consulte a [lista oficial de funções integradas](https://docs.python.org/3/library/functions.html) quando precisar. As que mais aparecem no início da trilha: - -| Função | Papel curto | -|--------|----------------| -| `print()` | Enviar texto ao fluxo de saída; parâmetros `sep`, `end` controlam separadores e fim de linha | -| `input()` | Ler uma linha como `str` | -| `int()` / `float()` | Converter strings numéricas para números | -| `type()` | Informar o tipo do objeto | -| `len()` | Tamanho de sequências (ex.: caracteres em uma string) | - -### Boas práticas (PEP 8 e aula) - -- Nomes de função em `snake_case`, preferindo **verbos** que descrevem a ação (`calcular_`, `formatar_`, `validar_`). -- Função **pequena e coesa**: uma responsabilidade clara; várias etapas podem virar várias funções chamadas em sequência. -- Incluir docstring no código que outras pessoas (ou você no mês seguinte) vão reutilizar. - -Fluxo: definição no ambiente → chamada → associação argumento–parâmetro → execução do corpo → valor de retorno (ou `None`). - -```mermaid -flowchart TD - A[def foi executada no kernel?] -->|não| B[NameError na chamada] - A -->|sim| C[Associar argumentos aos parâmetros] - C --> D[Executar corpo da função] - D --> E{TypeError em operação?} - E -->|sim| F[Rever ordem posicional e tipos] - E -->|não| G[return explícito ou None] -``` - -## Uso Prático - -### Função sem parâmetros e necessidade de “executar a definição” - -Em notebooks, só existe o que já foi executado nesta sessão do kernel. - -```python -def saudacao() -> None: - """Imprime uma saudação fixa.""" - print("Olá — função definida e carregada no kernel.") - - -saudacao() -``` - -Se você escrever a célula da `def` mas não rodá-la, a célula seguinte que chama o nome será uma `NameError`. - -### Parâmetros, docstring e `help` em função própria - -```python -def somar_dois_valores(x: float, y: float) -> None: - """Exibe a soma de dois números (exemplo didático).""" - soma = x + y - print(f"Entradas {x} e {y}; soma = {soma}") - - -somar_dois_valores(1, 2) -# help(somar_dois_valores) # descomente no notebook para ver a docstring -``` - -### Quando a ordem posicional quebra o cálculo - -Cenário: função com `nome`, `idade`, `altura_m` e `peso_kg`. Se você passar só por posição na ordem errada, um texto pode cair no parâmetro que deveria ser número e o erro surge em `**` ou na divisão. - -```python -def imc_preview(nome: str, idade: int, altura_m: float, peso_kg: float) -> float: - """Retorna IMC (peso / altura²); altura em metros.""" - return peso_kg / (altura_m**2) - - -nome, idade, altura_m, peso_kg = "Ana", 30, 1.70, 72.0 - -# Errado (não rode): imc_preview(idade, peso_kg, nome, altura_m) → TypeError em altura_m ** 2 - -# Certo: argumentos nomeados eliminam ambiguidade de ordem -print(round(imc_preview(nome=nome, idade=idade, altura_m=altura_m, peso_kg=peso_kg), 2)) -``` - -> **Regra:** depois de `input()`, converta com `int()` ou `float()` e chame funções de domínio com **argumentos nomeados** quando houver mais de dois parâmetros facilmente confundíveis. - -### Explorar documentação de builtin - -```python -# help(print) -``` - -Mostra assinatura completa (`*args`, `sep`, `end`, `file`, `flush`) — útil para logs formatados e saída sem quebra de linha. - -**Não coberto no material da aula 14 em profundidade:** valores padrão de parâmetro, `*args` / `**kwargs`, anotações de tipo avançadas. Para aprofundar, seguir a seção [“More on Defining Functions”](https://docs.python.org/3/tutorial/controlflow.html#more-on-defining-functions) do tutorial. - -## Erros Comuns - -- **`NameError: name 'f' is not defined`:** célula com `def f...` não foi executada, ou nome digitado diferente do definido. -- **Ordem posicional trocada com tipos “compatíveis” até certo ponto:** o programa imprime lixo (por exemplo nome na variável que era idade) e só falha na linha do cálculo — sintoma de desalinhamento semântico, não só de sintaxe. -- **`TypeError: unsupported operand type(s) for **` (ou similar):** operação numérica recebeu `str` ou outro tipo indevido; traceback aponta a expressão — rastreie qual argumento ficou em qual parâmetro. -- **Esquecer que `input()` devolve string:** usar o valor em conta sem `float()`/`int()` antes de funções que esperam número. - -## Visão Geral de Debugging - -1. Leia a **última linha** do traceback (tipo da exceção e mensagem). -2. Suba até a **primeira linha do seu código** (não da biblioteca padrão): é onde a hipótese mais provável está. -3. Para funções: confira **assinatura** com `help(minha_funcao)` ou o cabeçalho da `def`. -4. Insira `print(repr(...))` ou use o depurador no VS Code/Cursor para ver **tipo e valor** de cada parâmetro na entrada da função. -5. Em notebook: “Kernel não viu a def” é causa número um de `NameError` — rode de cima para baixo ou use “Run All”. - -## Principais Pontos - -- `def` define; **chamar** é usar o nome seguido de `()` com argumentos. -- Parâmetros na definição; argumentos na chamada; nomeados reduzem troca de ordem. -- Docstrings alimentam `help()`; bibliotecas reais têm docstrings longas e padronizadas. -- Builtins cobrem I/O básico, conversão, tipo e tamanho — sempre há documentação oficial. -- PEP 8: `snake_case`, verbos, funções coesas. - -## Preparação para Prática - -Você deve ser capaz de: criar funções com docstring; chamar com posicionais e nomeados; usar `help()` em função própria e em builtin; explicar um `NameError` em notebook por ordem de execução. - -## Laboratório de Prática - -### 1. Linha de log padronizada (Easy) - -Implemente o corpo de `formatar_linha_log` para retornar no formato exato `[NIVEL] modulo | mensagem` (espaço após o nível, pipe com um espaço de cada lado). Inclua docstring descrevendo parâmetros em uma linha. - -```python -def formatar_linha_log(nivel: str, modulo: str, mensagem: str) -> str: - """Retorna uma linha no formato '[NIVEL] modulo | mensagem'.""" - # TODO: retornar f-string no formato [NIVEL] modulo | mensagem - return "" - - -if __name__ == "__main__": - print(formatar_linha_log("INFO", "ingestao_csv", "arquivo recebido")) - print(formatar_linha_log("ERRO", "api_pedidos", "timeout após 30s")) -``` - -### 2. Classificação de promoção com argumentos nomeados (Medium) - -Complete a lógica: se `preco_atual <= 0.8 * preco_original` retorne `"promocao_forte"`; se `<= 0.95 * preco_original` retorne `"promocao_leve"`; senão `"preco_normal"`. O bloco `if __name__` já mostra chamada **só com argumentos nomeados** (ordem diferente da assinatura). - -```python -def classificar_faixa_preco(preco_original: float, preco_atual: float, sku: str) -> str: - """Classifica desconto relativo ao preço original de um SKU.""" - # TODO: implementar comparações com 0.8 e 0.95 - return "preco_normal" - - -if __name__ == "__main__": - rotulo = classificar_faixa_preco( - sku="SKU-9921", - preco_atual=77.0, - preco_original=100.0, - ) - print(rotulo) -``` - -### 3. Pedido JSON: função auxiliar + resumo (Hard) - -Separe responsabilidades: `total_unidades` soma o campo `quantidade` de cada item em `payload["itens"]`; `resumo_pedido` usa essa função e retorna a string `Pedido {numero}: {total} unidades`. - -```python -def total_unidades(payload: dict) -> int: - """Soma quantidade de todos os itens do pedido.""" - # TODO: iterar payload['itens'], acumular item['quantidade'] - return 0 - - -def resumo_pedido(numero: str, payload: dict) -> str: - """Texto resumido para painel operacional.""" - # TODO: usar total_unidades e f-string pedida - return f"Pedido {numero}: 0 unidades" - - -if __name__ == "__main__": - pedido_api = { - "itens": [ - {"sku": "A1", "quantidade": 2}, - {"sku": "B7", "quantidade": 5}, - ] - } - print(resumo_pedido("P-4401", pedido_api)) -``` - - - - diff --git a/content/python/aula-15-retorno-composicao-funcoes.md b/content/python/aula-15-retorno-composicao-funcoes.md deleted file mode 100644 index 730d0b0..0000000 --- a/content/python/aula-15-retorno-composicao-funcoes.md +++ /dev/null @@ -1,458 +0,0 @@ ---- -title: "Parâmetros avançados e retorno de funções em Python" -slug: "retorno-composicao-funcoes" -discipline: "python" -order: 15 -description: "Parâmetros posicionais, nomeados e padrão; return, None, composição de funções e múltiplos retornos com tuple unpacking." -reading_time: 18 -difficulty: "medium" -concepts: - - parâmetros posicionais - - parâmetros nomeados (keyword arguments) - - parâmetros com valor padrão - - return - - None - - composição de funções - - múltiplos retornos - - tuple unpacking -prerequisites: - - funcoes-parametros-docstrings-builtins -learning_objectives: - - "Distinguir parâmetros posicionais, nomeados e padrão e aplicar cada um corretamente na assinatura de uma função." - - "Usar return para extrair valores de funções e entender o que None representa quando não há retorno explícito." - - "Compor funções — passar o retorno de uma função como argumento de outra — sem armazenar variáveis intermediárias desnecessárias." - - "Usar múltiplos retornos com tuple unpacking para desestruturar resultados em variáveis separadas." -exercises: - - question: "Qual é a diferença prática entre um parâmetro posicional e um parâmetro nomeado ao chamar uma função?" - answer: "No posicional, a ordem em que os argumentos são passados determina qual parâmetro recebe qual valor. No nomeado, você especifica explicitamente nome=valor, tornando a ordem irrelevante." - hint: "Pense em def f(a, b): se você chamar f(y=2, x=1), o Python sabe exatamente onde cada valor vai." - - question: "Por que uma função sem return explícito retorna None e não um erro?" - answer: "Python retorna None implicitamente sempre que o fluxo de execução chega ao fim de uma função sem encontrar um return. None é o valor que representa 'ausência de valor' na linguagem." - hint: "Experimente atribuir o resultado de print() a uma variável e imprimir o que ela contém." - - question: "Quando faz sentido não armazenar o retorno de uma função em uma variável?" - answer: "Quando o retorno será usado apenas uma vez, diretamente como argumento de outra função ou condição de um if, e não precisa sobreviver além daquele ponto. Armazená-lo criaria uma variável em memória sem necessidade." - hint: "if é_par(calcular_quadrado(n)): — nenhum valor intermediário precisa de nome." ---- - -## Visão Geral do Conceito - -Funções em Python não são apenas blocos de código reutilizáveis: elas comunicam valores de volta para quem as chamou. O mecanismo de **retorno** (`return`) é o que transforma uma função de uma instrução isolada em um componente que pode ser encadeado, composto e integrado em fluxos maiores de processamento. - -Antes do retorno, a **assinatura da função** — como os parâmetros são declarados — determina o contrato que a função estabelece com quem a chama: o que ela exige, o que é opcional e em que ordem os valores devem chegar. - -Esta lição fecha o ciclo básico de funções em Python, cobrindo: - -- Os três tipos de parâmetros (posicionais, nomeados, padrão). -- O que `return` faz e o que acontece sem ele (`None`). -- Composição: usar o retorno de uma função como argumento de outra. -- Múltiplos retornos e como desestruturá-los com **tuple unpacking**. - ---- - -## Modelo Mental - -**Parâmetros** são variáveis declaradas na assinatura; **argumentos** são os valores passados na chamada. Essa distinção é operacional: - -``` -def somar(x, y): # x e y são PARÂMETROS - return x + y - -somar(18, 98) # 18 e 98 são ARGUMENTOS -``` - -Pense na função como uma máquina: os parâmetros são as entradas definidas na planta da máquina; os argumentos são os materiais que você insere quando a liga. - -**Return** é a saída da máquina. Uma função sem `return` realiza um efeito colateral (ex.: imprime algo) mas não entrega produto algum. Atribuir seu resultado a uma variável dará `None` — não um erro, apenas a confissão do Python de que não há valor a entregar. - -**Composição** é o ato de encadear máquinas: a saída de uma vira a entrada de outra. `is_par(calcular_quadrado(3))` encadeia duas funções sem criar variáveis intermediárias. - ---- - -## Mecânica Central - -### Parâmetros posicionais - -A posição na chamada mapeia diretamente para a posição na assinatura. A ordem importa. - -```python -def somar_dois_numeros(x, y): - return x + y - -somar_dois_numeros(18, 98) # x=18, y=98 → 116 -somar_dois_numeros(98, 18) # x=98, y=18 → 116 (comutativo aqui, mas a ordem foi diferente) -``` - -### Parâmetros nomeados (keyword arguments) - -Você pode nomear os argumentos na chamada. Quando faz isso, a **ordem deixa de importar**. - -```python -somar_dois_numeros(y=98, x=18) # Resultado idêntico: x=18, y=98 -``` - -Nomeados e posicionais podem coexistir, mas posicionais devem vir primeiro. - -### Parâmetros com valor padrão - -Defina um valor padrão com `=` na assinatura. O parâmetro torna-se **opcional**: se o argumento não for passado, o padrão é usado; se for passado, o padrão é sobrescrito. - -```python -def saudacao(nome, mensagem="Olá, seja bem-vindo!"): - return f"{mensagem} {nome}" - -saudacao("Ana") # usa o padrão -saudacao("Ana", mensagem="Boa noite!") # sobrescreve -``` - -> **Regra:** Parâmetros com valor padrão devem sempre vir **depois** dos parâmetros obrigatórios na assinatura. `def f(a="x", b)` é inválido. - -### Diagrama: tipos de parâmetros - -```mermaid -flowchart TD - A["Chamada da função"] --> B{Argumento nomeado?} - B -->|Sim| C["Associa pelo nome\n(ordem irrelevante)"] - B -->|Não| D["Associa pela posição\n(ordem obrigatória)"] - C --> E{Parâmetro tem\nvalor padrão?} - D --> E - E -->|Sim, arg não passado| F["Usa valor padrão"] - E -->|Arg passado| G["Usa argumento recebido"] - F --> H["Corpo da função executa"] - G --> H -``` - ---- - -## Uso Prático - -### return e its consequências - -```python -def formatar_status_pedido(pedido_id: str, status: str, prefixo: str = "PEDIDO") -> str: - return f"[{prefixo}#{pedido_id}] Status: {status.upper()}" - -# O retorno pode ser atribuído, usado em print, ou passado para outra função -linha_log = formatar_status_pedido("8821", "processando") -print(linha_log) -# [PEDIDO#8821] Status: PROCESSANDO -``` - -### None — o retorno implícito - -```python -def registrar_evento(evento: str) -> None: - print(f"LOG: {evento}") - # sem return - -resultado = registrar_evento("conexão estabelecida") -print(f"Valor retornado: {resultado}") -# LOG: conexão estabelecida -# Valor retornado: None -``` - -`None` não é um erro. É o valor explícito de "sem retorno". Atribuir o resultado de uma função sem retorno e usar esse valor em lógica condicional geralmente é um bug. - -### Composição de funções - -```python -def calcular_quadrado(numero: int) -> int: - return numero ** 2 - -def e_par(numero: int) -> bool: - return numero % 2 == 0 - -# Composição: retorno de calcular_quadrado vira argumento de e_par -numero = int(input("Digite um número: ")) -if e_par(calcular_quadrado(numero)): - print(f"O quadrado de {numero} é par.") -else: - print(f"O quadrado de {numero} é ímpar.") -``` - -**Por que não criar variável intermediária?** Se o valor só será usado uma vez como argumento ou condição, armazená-lo em variável ocupa memória desnecessariamente. O Python avalia o argumento interno primeiro, descarta o resultado intermediário da pilha assim que entrega ao argumento externo. - -### Composição com resultado armazenado (quando faz sentido) - -```python -# Quando o resultado intermediário é reutilizado em mais de um lugar -quadrado = calcular_quadrado(numero) -print(f"Quadrado: {quadrado}") -print(f"É par: {e_par(quadrado)}") -``` - -### Múltiplos retornos e tuple unpacking - -Uma função pode retornar mais de um valor separando-os por vírgula. O Python empacota tudo em uma **tupla** automaticamente. - -```python -def min_max(numeros: list[int]) -> tuple[int, int]: - return min(numeros), max(numeros) - -# Sem desestruturação: recebe tupla inteira -resultado = min_max([2, 3, 4, 1]) -print(resultado) # (1, 4) - -# Com tuple unpacking: variáveis individuais -menor, maior = min_max([2, 3, 4, 1]) -print(f"Menor: {menor}, Maior: {maior}") # Menor: 1, Maior: 4 -``` - -> **Regra:** Na desestruturação, o número de variáveis à esquerda do `=` deve bater com o número de valores retornados. Caso contrário, `ValueError`. - ---- - -## Erros Comuns - -### 1. Confundir `print` com `return` - -**Sintoma:** A função exibe o valor correto no terminal, mas ao atribuí-la a uma variável, a variável é `None`. - -```python -def calcular_total(preco, qtd): - print(preco * qtd) # ERRADO: efeito colateral, não retorno - -resultado = calcular_total(10.5, 3) -print(resultado * 2) # TypeError: unsupported operand type(s) for *: 'NoneType' and 'int' -``` - -**Correção:** substituir `print` por `return`. - ---- - -### 2. Parâmetro padrão com tipo mutável - -**Sintoma:** Resultados estranhos entre chamadas separadas da mesma função. - -```python -# NUNCA faça isso -def adicionar_item(item, lista=[]): - lista.append(item) - return lista - -print(adicionar_item("a")) # ['a'] -print(adicionar_item("b")) # ['a', 'b'] ← objeto padrão reutilizado! -``` - -**Correção:** use `None` como padrão e crie o objeto dentro da função: - -```python -def adicionar_item(item, lista=None): - if lista is None: - lista = [] - lista.append(item) - return lista -``` - ---- - -### 3. Desestruturação com contagem errada - -```python -menor, maior, media = min_max([1, 2, 3]) -# ValueError: not enough values to unpack (expected 3, got 2) -``` - -Sempre confira quantos valores a função retorna antes de desestruturar. - ---- - -### 4. Parâmetro obrigatório depois de padrão - -```python -def f(a="x", b): # SyntaxError - pass -``` - -Parâmetros sem padrão (obrigatórios) devem sempre vir antes dos que têm padrão. - ---- - -## Visão Geral de Debugging - -Quando uma função retorna um resultado inesperado, siga esta ordem: - -1. **Verifique o `return`**: tem algum? Está dentro do bloco correto (indentação)? Um `return` dentro de um `if` sem `else` pode deixar execuções sem retorno → `None`. -2. **Inspecione os parâmetros**: adicione um `print(f"DEBUG: a={a}, b={b}")` logo no início da função para confirmar o que chegou. -3. **Teste com valores fixos**: substitua temporariamente variáveis complexas por literais para isolar se o problema é na entrada ou na lógica interna. -4. **Desestruturação falha**: se houver `ValueError: too many values to unpack`, a função retorna mais do que o esperado — confirme o número de valores retornados com `print(type(resultado), resultado)`. - -
-Diagnóstico: função retornando None inesperadamente - -Checklist rápido: - -- A função tem `print` onde deveria ter `return`? -- O `return` está dentro de um bloco `if` e o fluxo nunca entra nele? -- Você está chamando `função()` e atribuindo, mas a função não tem `return` explícito? - -Experimente: `print(type(resultado))` — se mostrar ``, o problema está confirmado no ponto de retorno. - -
- ---- - -## Principais Pontos - -- **Parâmetros posicionais**: ordem importa; cada posição mapeia para um parâmetro. -- **Parâmetros nomeados**: nome importa; ordem irrelevante na chamada. -- **Parâmetros padrão**: opcionais; obrigatoriamente depois dos sem padrão na assinatura; nunca use tipos mutáveis como padrão. -- **`return`**: encerra a função e entrega o valor; sem ele, a função retorna `None`. -- **`None`**: valor explícito de ausência — não é erro, mas usá-lo inadvertidamente em cálculos é. -- **Composição**: `f(g(x))` — o retorno de `g` vira argumento de `f`; elimina variáveis intermediárias quando o valor não precisa persistir. -- **Múltiplos retornos**: Python empacota em tupla; use tuple unpacking para variáveis individuais. - ---- - -## Preparação para Prática - -Ao terminar esta lição, você deve ser capaz de: - -1. Escrever uma função com parâmetros mistos (obrigatório + padrão) e chamar corretamente com e sem o argumento opcional. -2. Diferenciar funções de efeito colateral (`print`) de funções que produzem valor (`return`) e escolher a abordagem certa para cada caso. -3. Compor duas funções em uma única expressão sem variáveis intermediárias. -4. Implementar uma função de múltiplos retornos e desestruturá-la com tuple unpacking. - ---- - -## Laboratório de Prática - -### 🟢 Easy — Formatar linha de log - -Você está construindo um sistema de log para uma API de pagamentos. Implemente a função `formatar_log` que recebe `nivel` (string, padrão `"INFO"`), `codigo` (string) e `mensagem` (string) e retorna uma string formatada. - -```python -def formatar_log(codigo: str, mensagem: str, nivel: str = "INFO") -> str: - # TODO: retornar string no formato "[NIVEL] COD mensagem" - # Exemplo: "[INFO] PAG-001 Pagamento aprovado" - return "" - - -# Teste esperado -print(formatar_log("PAG-001", "Pagamento aprovado")) -# [INFO] PAG-001 Pagamento aprovado - -print(formatar_log("PIX-404", "Chave não encontrada", nivel="ERROR")) -# [ERROR] PIX-404 Chave não encontrada -``` - ---- - -### 🟡 Medium — Calcular estatísticas de uma lista de preços - -Você recebe uma lista de preços de produtos (floats). Implemente `estatisticas_precos` que retorne **três valores**: o menor preço, o maior preço e a média (arredondada em 2 casas). Use tuple unpacking para exibir o resultado. - -```python -def estatisticas_precos(precos: list[float]) -> tuple[float, float, float]: - # TODO: retornar (menor, maior, media) - # Use as funções built-in min(), max() e sum() - menor = 0.0 - maior = 0.0 - media = 0.0 - return menor, maior, media - - -# Teste esperado -precos = [29.90, 15.00, 99.99, 45.50, 8.75] -minimo, maximo, media = estatisticas_precos(precos) -print(f"Menor: R${minimo:.2f} | Maior: R${maximo:.2f} | Média: R${media:.2f}") -# Menor: R$8.75 | Maior: R$99.99 | Média: R$39.83 -``` - ---- - -### 🔴 Hard — Pipeline de validação de transação - -Implemente um mini-pipeline de três funções compostas para validar uma transação financeira: - -1. `normalizar_valor(valor_str)` — recebe string como `"R$ 1.250,50"`, remove símbolo e formatação BR e retorna `float`. -2. `classificar_transacao(valor)` — recebe `float`, retorna `"baixo"` (< 100), `"medio"` (100–999.99) ou `"alto"` (≥ 1000). -3. `gerar_alerta(classificacao, limite_diario)` — recebe string de classificação e limite diário (`float`, padrão `5000.0`); retorna `True` se classificação for `"alto"` e limite > 3000, `False` caso contrário. - -Compose as três em uma única expressão: `gerar_alerta(classificar_transacao(normalizar_valor("R$ 1.250,50")))`. - -```python -def normalizar_valor(valor_str: str) -> float: - # TODO: remover "R$ ", trocar "." por "" e "," por "." e converter para float - return 0.0 - - -def classificar_transacao(valor: float) -> str: - # TODO: retornar "baixo", "medio" ou "alto" conforme os limites - return "" - - -def gerar_alerta(classificacao: str, limite_diario: float = 5000.0) -> bool: - # TODO: retornar True se classificacao == "alto" e limite_diario > 3000 - return False - - -# Teste esperado -alerta = gerar_alerta(classificar_transacao(normalizar_valor("R$ 1.250,50"))) -print(f"Alerta emitido: {alerta}") -# Alerta emitido: True - -alerta2 = gerar_alerta(classificar_transacao(normalizar_valor("R$ 45,00"))) -print(f"Alerta emitido: {alerta2}") -# Alerta emitido: False -``` - ---- - - - - diff --git a/content/python/aula-16-algoritmos-pratica-composicao-funcoes.md b/content/python/aula-16-algoritmos-pratica-composicao-funcoes.md deleted file mode 100644 index ee5b48a..0000000 --- a/content/python/aula-16-algoritmos-pratica-composicao-funcoes.md +++ /dev/null @@ -1,675 +0,0 @@ ---- -title: "Algoritmos na prática: ordenação, módulo random e composição de funções" -slug: "algoritmos-pratica-composicao-funcoes" -discipline: "python" -order: 16 -description: "Aula final da disciplina: implementação de Bubble Sort, jogo de adivinhação com random e caixa registradora com múltiplas funções compostas." -reading_time: 22 -difficulty: "medium" -concepts: - - bubble sort - - algoritmo de ordenação - - referência mutável e cópia de lista - - módulo random - - random.randint - - loop while com break - - acumuladores - - retorno múltiplo de funções - - composição de funções - - validação de entrada -prerequisites: - - retorno-composicao-funcoes - - loops-for-range-listas - - desvios-condicionais-if-elif-else -learning_objectives: - - "Implementar um algoritmo de ordenação simples (Bubble Sort) usando loops aninhados e troca com variável auxiliar." - - "Explicar por que listas em Python são passadas por referência e como usar .copy() para evitar mutação indesejada." - - "Usar o módulo random para gerar números inteiros aleatórios num intervalo." - - "Construir um programa interativo com while, break e validação de entrada do usuário." - - "Decompor um problema em múltiplas funções com responsabilidades distintas e compô-las numa chamada principal." -exercises: - - question: "Por que, sem usar .copy(), a lista original e a lista ordenada mostram o mesmo resultado após a chamada da função de ordenação?" - answer: "Porque listas em Python são objetos mutáveis passados por referência. Quando a função modifica os elementos da lista recebida, está alterando o mesmo objeto na memória. As duas variáveis apontam para o mesmo endereço." - hint: "Pense no que id(lista) retornaria antes e depois da chamada." - - question: "Qual é a diferença entre random.randint(1, 20) e range(1, 20) no contexto de geração de números?" - answer: "range(1, 20) cria uma sequência determinística de 1 a 19. random.randint(1, 20) retorna um único número inteiro aleatório no intervalo fechado [1, 20], incluindo o valor 20." - hint: "random.randint usa intervalo fechado; range usa intervalo semi-aberto." - - question: "Por que usar while True com break em vez de while meu_palpite != numero no jogo de adivinhação pode ser mais flexível?" - answer: "Com while True e break, é possível adicionar múltiplas condições de saída (ex.: desistência, número máximo de tentativas, acerto) sem reescrever a expressão da condição principal do loop." - hint: "Pense em quantas condições de saída existem no jogo: acerto e digitação de zero." ---- - -## Visão Geral do Conceito - -Esta aula é uma síntese de toda a disciplina de Introdução à Programação com Python. O conteúdo não traz novos vocabulários: traz **novos problemas reais** para resolver com os instrumentos já aprendidos — loops, funções, condicionais, listas e entrada do usuário. - -Os três desafios desta aula revelam três competências essenciais de um desenvolvedor: - -1. **Pensar em algoritmos** — construir lógica de ordenação do zero. -2. **Usar bibliotecas padrão** — importar e usar o módulo `random` para gerar dados imprevisíveis. -3. **Decompor problemas em funções compostas** — dividir um sistema em partes menores com responsabilidades claras. - -> **Regra:** A habilidade de programar cresce apenas com prática. Ler código não substitui escrever código — e errar faz parte do aprendizado. - ---- - -## Modelo Mental - -### Bubble Sort: bolhas subindo - -Imagine uma lista de números como uma coluna de bolhas subindo numa água viscosa. As bolhas mais pesadas (números maiores) afundam e as mais leves (números menores) sobem. O algoritmo percorre a lista repetidamente, comparando cada elemento com seu vizinho e trocando de posição quando necessário. - -Cada passagem empurra o maior elemento restante para o final — como uma "bolha" que sobe até a superfície. - -### Referência vs. cópia de lista - -Em Python, uma variável que aponta para uma lista **não guarda os dados** — ela guarda o **endereço de memória** onde os dados estão. Quando você passa essa variável para uma função, a função também recebe o mesmo endereço. Modificar os elementos dentro da função modifica a lista original. - -Para preservar a lista original, é necessário criar uma **cópia independente** com `.copy()` antes de modificar. - -### Composição de funções - -Em vez de um script monolítico com 60 linhas, um sistema pode ser dividido em funções com responsabilidade única: uma que coleta dados, outra que calcula, outra que exibe o resultado. Cada função é testável e reutilizável de forma independente. - ---- - -## Mecânica Central - -### 1. Bubble Sort com dois loops aninhados - -O algoritmo compara cada elemento com seu sucessor imediato. Se o elemento atual for maior que o próximo, eles trocam de lugar. Um loop externo percorre os índices `i` (elemento atual) e um loop interno percorre os índices `j` (elemento seguinte, `j = i + 1`). - -```mermaid -flowchart TD - A["Recebe lista_numerica"] --> B["Criar cópia: lista_ord = lista_numerica.copy()"] - B --> C["for i in range(len(lista_ord))"] - C --> D["for j in range(i+1, len(lista_ord))"] - D --> E{"lista_ord[i] > lista_ord[j]?"} - E -->|"Sim"| F["Trocar: aux = lista_ord[i]\nlista_ord[i] = lista_ord[j]\nlista_ord[j] = aux"] - E -->|"Não"| G["Continuar"] - F --> G - G --> D - D -->|"j esgotado"| C - C -->|"i esgotado"| H["return lista_ord"] -``` - -```python -def ordenar_lista_numerica(lista_numerica: list[int | float]) -> list[int | float]: - """Retorna uma nova lista ordenada sem modificar a original (Bubble Sort).""" - lista_ord = lista_numerica.copy() # cópia independente — não muta o original - - for i in range(len(lista_ord)): - for j in range(i + 1, len(lista_ord)): - if lista_ord[i] > lista_ord[j]: - aux = lista_ord[i] # guarda o valor atual - lista_ord[i] = lista_ord[j] # coloca o menor no lugar - lista_ord[j] = aux # coloca o maior no lugar do sucessor - - return lista_ord - - -# Teste -lista = [4, 5, 1, 2] -lista_ordenada = ordenar_lista_numerica(lista) -print("Original:", lista) # [4, 5, 1, 2] — intacta -print("Ordenada:", lista_ordenada) # [1, 2, 4, 5] -``` - -> **Atenção ao desempenho:** O Bubble Sort percorre toda a coleção a cada passagem, independentemente de ela já estar ordenada parcialmente. Para N elementos, faz O(N²) comparações no pior caso. É o primeiro algoritmo histórico de ordenação e o de pior performance — Python já oferece `.sort()` e `sorted()` que usam Timsort (O(N log N)). - ---- - -### 2. Módulo `random` — números aleatórios - -O módulo `random` faz parte da biblioteca padrão do Python, mas precisa ser **importado explicitamente** para ficar disponível. - -```python -import random - -numero = random.randint(1, 20) # inteiro aleatório no intervalo FECHADO [1, 20] -print(numero) # pode ser qualquer valor de 1 a 20 (inclusive) -``` - -Funções relevantes do módulo: - -| Função | Descrição | -|--------|-----------| -| `random.randint(a, b)` | Inteiro aleatório em `[a, b]` (fechado nos dois extremos) | -| `random.randrange(start, stop)` | Inteiro aleatório em `[start, stop)` (igual ao `range`) | -| `random.random()` | Float aleatório em `[0.0, 1.0)` | -| `random.choice(seq)` | Elemento aleatório de uma sequência | - ---- - -### 3. Jogo de adivinhação — `while` com múltiplas saídas - -```python -import random - - -def jogar_adivinhacao() -> None: - """Jogo interativo de adivinhação com dicas de 'acima/abaixo'.""" - numero = random.randint(1, 20) - palpites = 0 - meu_palpite = int(input("Adivinhe meu número entre 1 e 20 (0 para desistir): ")) - - while meu_palpite != numero: - if meu_palpite == 0: - print("Que pena, você desistiu do jogo 😢") - break - - palpites += 1 - - if meu_palpite > numero: - print(f"Seu palpite {meu_palpite} está ACIMA.") - else: - print(f"Seu palpite {meu_palpite} está ABAIXO.") - - meu_palpite = int(input("Tente novamente: ")) - - if meu_palpite != 0: - print(f"Ótimo, você acertou em {palpites + 1} tentativas!") - - -jogar_adivinhacao() -``` - -**Fluxo de controle:** - -```mermaid -flowchart TD - A["Gera número aleatório"] --> B["Lê primeiro palpite"] - B --> C{"palpite == numero?"} - C -->|"Sim"| H["Parabéns! Acertou!"] - C -->|"Não"| D{"palpite == 0?"} - D -->|"Sim"| E["Mensagem de desistência\nbreak"] - D -->|"Não"| F["palpites += 1\nDica: acima ou abaixo"] - F --> G["Lê novo palpite"] - G --> C -``` - ---- - -### 4. Caixa registradora — composição de três funções - -O sistema é dividido em três responsabilidades distintas: - -- `registrar_compra()` — coleta valores do usuário e retorna totalizadores. -- `calcular_desconto(total)` — aplica regras de desconto progressivo. -- `exibir_relatorio(...)` — exibe o resumo completo da compra. - -```python -def registrar_compra() -> tuple[int, float, int]: - """ - Coleta valores dos produtos via input até que o usuário digite 0. - Retorna: (qtd_produtos, total, qtd_produtos_caros) - """ - qtd_produtos = 0 - total = 0.0 - qtd_produtos_caros = 0 - - while True: - valor_produto = float(input("Digite o valor do produto em R$ (0 para encerrar): ")) - - if valor_produto == 0: - break - - if valor_produto < 0: - print("Valor inválido. Digite um valor positivo.") - continue # volta ao início do loop sem registrar - - qtd_produtos += 1 - total += valor_produto - - if valor_produto >= 100: - qtd_produtos_caros += 1 - - return qtd_produtos, total, qtd_produtos_caros - - -def calcular_desconto(total: float) -> float: - """ - Aplica desconto progressivo sobre o total da compra. - < R$ 100 → 0% - R$ 100–299,99 → 5% - R$ 300–499,99 → 10% - ≥ R$ 500 → 15% - """ - if total < 100: - return 0.0 - elif total < 300: - return total * 0.05 - elif total < 500: - return total * 0.10 - else: - return total * 0.15 - - -def exibir_relatorio( - qtd_produtos: int, - total: float, - qtd_produtos_caros: int -) -> None: - """Calcula e exibe o resumo completo da compra.""" - if qtd_produtos == 0: - print("Nenhum produto registrado.") - return - - media = total / qtd_produtos - desconto = calcular_desconto(total) - valor_final = total - desconto - - print("\n===== RESUMO DA COMPRA =====") - print(f"Quantidade de produtos: {qtd_produtos}") - print(f"Total da compra: R$ {total:.2f}") - print(f"Média por produto: R$ {media:.2f}") - print(f"Produtos acima de R$ 100,00: {qtd_produtos_caros}") - print(f"Desconto aplicado: R$ {desconto:.2f}") - print(f"Valor final a pagar: R$ {valor_final:.2f}") - print("============================") - - -# --- Ponto de entrada --- -qtd, total, caros = registrar_compra() -exibir_relatorio(qtd, total, caros) -``` - -**Tabela de descontos:** - -| Total da compra | Desconto | -|-----------------|----------| -| Abaixo de R$ 100 | 0% | -| R$ 100 a R$ 299,99 | 5% | -| R$ 300 a R$ 499,99 | 10% | -| R$ 500 ou mais | 15% | - ---- - -## Uso Prático - -### Cenário: processando notas fiscais em lote - -A estrutura da caixa registradora pode ser adaptada para processar listas de valores lidos de um CSV ou de uma API, substituindo o `input()` por iteração sobre dados reais: - -```python -import csv -from pathlib import Path - - -def processar_nf(caminho_csv: str) -> tuple[int, float, int]: - """Processa uma nota fiscal a partir de um CSV com coluna 'valor'.""" - qtd_produtos = 0 - total = 0.0 - qtd_caros = 0 - - with Path(caminho_csv).open(encoding="utf-8") as f: - reader = csv.DictReader(f) - for row in reader: - try: - valor = float(row["valor"]) - except ValueError: - print(f"Linha ignorada — valor inválido: {row}") - continue - - if valor <= 0: - continue - - qtd_produtos += 1 - total += valor - if valor >= 100: - qtd_caros += 1 - - return qtd_produtos, total, qtd_caros -``` - ---- - -## Erros Comuns - -### 1. Modificar a lista original sem perceber - -**Erro:** Passar a lista para a função de ordenação e imprimir a "original" depois: ambas mostram o mesmo resultado. - -```python -# ERRADO: a função muta a lista que recebeu -def ordenar_sem_copia(lista): - for i in range(len(lista)): - for j in range(i + 1, len(lista)): - if lista[i] > lista[j]: - lista[i], lista[j] = lista[j], lista[i] - return lista - -lista = [4, 2, 7, 1] -ordenada = ordenar_sem_copia(lista) -print(lista) # [1, 2, 4, 7] — a original foi modificada! -print(ordenada) # [1, 2, 4, 7] — apontam para o mesmo objeto -``` - -**Correção:** Crie uma cópia no início da função com `lista.copy()`. - ---- - -### 2. `random.randint` vs. `random.randrange` — intervalo fechado vs. aberto - -```python -import random - -random.randint(1, 20) # pode retornar 1, 2, ... ou 20 (fechado) -random.randrange(1, 20) # pode retornar 1, 2, ... ou 19 (20 excluído) -``` - -Usar `randrange` onde se quer incluir o limite máximo é um bug silencioso: o número 20 **nunca** será sorteado. - ---- - -### 3. `float(input())` sem tratamento de exceção - -Se o usuário digitar letras em vez de um número, ocorre `ValueError`: - -```python -# Frágil: -valor = float(input("Digite o valor: ")) - -# Robusto: -while True: - try: - valor = float(input("Digite o valor: ")) - break - except ValueError: - print("Entrada inválida. Digite um número.") -``` - ---- - -### 4. Loop infinito por esquecer de atualizar a variável de controle - -```python -# BUG: palpite nunca é atualizado dentro do loop -palpite = int(input("Palpite: ")) -while palpite != numero: - print("Errou!") - # palpite nunca muda → loop infinito -``` - -**Correção:** Leia o novo palpite ao final do bloco do `while`. - ---- - -## Visão Geral de Debugging - -**Bubble Sort não ordena corretamente:** -1. Verifique se o loop interno começa em `i + 1` e não em `0` (começar em `0` causa trocas erradas no início). -2. Confirme que o limite do `range` externo é `len(lista)` e não `len(lista) - 1`. -3. Use `print(lista_ord)` ao final de cada iteração do loop externo para rastrear o progresso. - -**Jogo de adivinhação entra em loop infinito:** -1. Verifique se `meu_palpite` é atualizado dentro do `while` (nova chamada a `input()`). -2. Confirme que o `break` está dentro do bloco `if meu_palpite == 0`. - -**Caixa registradora — total incorreto:** -1. Verifique se `total += valor_produto` está dentro do `else` (após a validação de zero e de valor negativo). -2. Imprima `total` e `qtd_produtos` antes do `return` para confirmar os acumuladores. - -
-Ver diagnóstico rápido de acumuladores - -```python -# Adicione temporariamente ao loop da caixa registradora: -print(f"[DEBUG] valor={valor_produto}, total acumulado={total}, qtd={qtd_produtos}") -``` - -Remova o print depois de confirmar o comportamento correto. -
- ---- - -## Principais Pontos - -- **Bubble Sort** usa dois `for` aninhados e troca elementos com variável auxiliar — é O(N²) e o pior algoritmo de ordenação, mas didaticamente essencial para entender lógica de comparação e troca. -- **Listas são mutáveis e passadas por referência** — use `.copy()` quando precisar preservar a lista original. -- **`import random`** é necessário para usar `random.randint(a, b)`, que gera inteiro no intervalo **fechado** `[a, b]`. -- **`while True` + `break`** é a forma idiomática de loops com múltiplas condições de saída. -- **Funções com responsabilidade única** tornam o código testável, legível e reutilizável. -- **Retorno múltiplo** (`return a, b, c`) retorna uma tupla — desempacote com `a, b, c = funcao()`. -- **`continue`** pula para a próxima iteração do loop sem executar o restante do bloco. - ---- - -## Preparação para Prática - -Após esta lição, o estudante deve conseguir: - -- Implementar um algoritmo de busca ou ordenação a partir de uma descrição textual do comportamento. -- Usar o módulo `random` para gerar dados de teste. -- Estruturar um programa interativo com entrada do usuário, validação, laço de repetição e saída formatada. -- Dividir um problema em funções distintas que se compõem para resolver o problema completo. -- Identificar quando uma lista está sendo mutada dentro de uma função e aplicar `.copy()` quando necessário. - ---- - -## Laboratório de Prática - -### 🟢 Easy — Buscador de Elemento em Lista - -**Contexto:** Um sistema de estoque recebe uma lista de SKUs (identificadores de produto) e precisa verificar se um determinado SKU está presente, retornando sua posição. - -Implemente `buscar_sku` sem usar o operador `in` ou o método `.index()` — use apenas um loop `for`. - -```python -def buscar_sku(lista_skus: list[str], sku_alvo: str) -> int: - """ - Percorre lista_skus e retorna o índice da primeira ocorrência de sku_alvo. - Retorna -1 se o SKU não for encontrado. - """ - for i in range(len(lista_skus)): - # TODO: comparar lista_skus[i] com sku_alvo e retornar i se for igual - pass - - return -1 # TODO: manter este retorno apenas se não encontrou - - -# Testes -skus = ["SKU-001", "SKU-007", "SKU-042", "SKU-007"] -print(buscar_sku(skus, "SKU-007")) # esperado: 1 -print(buscar_sku(skus, "SKU-999")) # esperado: -1 -``` - ---- - -### 🟡 Medium — Simulador de Sorteio com Histórico - -**Contexto:** Um sistema de sorteios de cupons fiscais sorteia números sem repetição a partir de um intervalo definido pelo usuário. O histórico de sorteios deve ser preservado entre rodadas. - -```python -import random - - -def sortear_cupom(historico: list[int], minimo: int, maximo: int) -> int | None: - """ - Sorteia um número inteiro aleatório no intervalo [minimo, maximo] - que ainda não tenha sido sorteado (não está em historico). - Retorna None se todos os números do intervalo já foram sorteados. - """ - # TODO: calcular todos os candidatos do intervalo que não estão em historico - candidatos: list[int] = [] # TODO: preencher com números disponíveis - - if not candidatos: - return None - - # TODO: sortear um elemento aleatório de candidatos usando random.choice - numero = None # TODO: substituir - - historico.append(numero) - return numero - - -# Uso esperado -historico: list[int] = [] -for _ in range(5): - resultado = sortear_cupom(historico, 1, 5) - print(f"Sorteado: {resultado} | Histórico: {historico}") - -# Todos esgotados: -resultado = sortear_cupom(historico, 1, 5) -print(f"Resultado quando esgotado: {resultado}") # esperado: None -``` - ---- - -### 🔴 Hard — Sistema de Desconto por Categoria com Relatório - -**Contexto:** Uma loja online aplica descontos diferentes por categoria de produto. Implemente as três funções abaixo e componha-as num pipeline que processa uma lista de itens e gera um relatório detalhado. - -**Regras de desconto por categoria:** - -| Categoria | Desconto | -|-----------|----------| -| `"eletronico"` | 10% | -| `"vestuario"` | 20% | -| `"alimento"` | 5% | -| Outras | 0% | - -```python -from typing import TypedDict - - -class Produto(TypedDict): - nome: str - categoria: str - preco: float - - -DESCONTOS: dict[str, float] = { - "eletronico": 0.10, - "vestuario": 0.20, - "alimento": 0.05, -} - - -def calcular_preco_final(produto: Produto) -> float: - """Retorna o preço com desconto aplicado conforme a categoria.""" - # TODO: buscar o percentual de desconto em DESCONTOS (.get com default 0.0) - # TODO: retornar preco * (1 - desconto) - return produto["preco"] # placeholder — remover - - -def gerar_relatorio(produtos: list[Produto]) -> dict[str, float]: - """ - Processa a lista de produtos e retorna um dicionário com: - - 'total_original': soma dos preços originais - - 'total_final': soma dos preços com desconto - - 'total_economia': diferença entre total_original e total_final - - 'media_final': média do preço final por produto - """ - # TODO: inicializar acumuladores - total_original = 0.0 - total_final = 0.0 - - for produto in produtos: - # TODO: acumular total_original com produto["preco"] - # TODO: calcular preco_final chamando calcular_preco_final(produto) - # TODO: acumular total_final com preco_final - pass - - qtd = len(produtos) if produtos else 1 # evitar divisão por zero - return { - "total_original": total_original, - "total_final": total_final, - "total_economia": total_original - total_final, - "media_final": total_final / qtd, - } - - -def exibir_relatorio_loja(produtos: list[Produto]) -> None: - """Imprime o relatório formatado da compra.""" - relatorio = gerar_relatorio(produtos) - print("\n===== RELATÓRIO DA LOJA =====") - for produto in produtos: - pf = calcular_preco_final(produto) - print(f" {produto['nome']:20s} | Original: R$ {produto['preco']:7.2f} | Final: R$ {pf:7.2f}") - print(f"\nTotal original: R$ {relatorio['total_original']:.2f}") - print(f"Total final: R$ {relatorio['total_final']:.2f}") - print(f"Economia: R$ {relatorio['total_economia']:.2f}") - print(f"Média por item: R$ {relatorio['media_final']:.2f}") - print("=============================") - - -# Teste -catalogo: list[Produto] = [ - {"nome": "Notebook Pro", "categoria": "eletronico", "preco": 3500.00}, - {"nome": "Camiseta Dry Fit", "categoria": "vestuario", "preco": 89.90}, - {"nome": "Arroz 5kg", "categoria": "alimento", "preco": 32.50}, - {"nome": "Cabo USB", "categoria": "eletronico", "preco": 45.00}, - {"nome": "Mochila", "categoria": "acessorio", "preco": 199.00}, -] - -exibir_relatorio_loja(catalogo) -# Economia esperada: 350 + 17,98 + 1,625 + 4,5 + 0 = R$ 374,105 -``` - ---- - - - - diff --git a/content/visualizacao-sql/aula-01-visualizar-dados-csv-looker-studio.md b/content/visualizacao-sql/aula-01-visualizar-dados-csv-looker-studio.md deleted file mode 100644 index 0b91087..0000000 --- a/content/visualizacao-sql/aula-01-visualizar-dados-csv-looker-studio.md +++ /dev/null @@ -1,396 +0,0 @@ ---- -title: "Visualizar dados de um CSV no Looker Studio" -slug: "visualizar-dados-csv-looker-studio" -discipline: "visualizacao-sql" -order: 1 -description: "Por que visualizamos dados, como usar arquivos CSV e dar os primeiros passos com dashboards no Looker Studio." -reading_time: 30 -difficulty: "easy" -concepts: - - visualização de dados - - dashboards - - arquivos CSV - - Looker Studio - - métricas e dimensões -prerequisites: [] -learning_objectives: - - "Entender o papel da visualização de dados na tomada de decisão em negócios." - - "Reconhecer o formato CSV como fonte de dados para dashboards." - - "Acessar o Looker Studio usando a conta acadêmica do Infnet." - - "Criar um primeiro relatório conectando um arquivo CSV simples." -exercises: - - question: "Por que a visualização de dados é importante para a tomada de decisão em empresas?" - answer: "Ela transforma tabelas e números em histórias visuais que permitem enxergar tendências, padrões e problemas rapidamente, apoiando decisões mais informadas." - hint: "Pense em como gráficos ajudam a explicar resultados para quem não é técnico." - - question: "O que é um arquivo CSV e por que ele é tão usado em ferramentas de visualização de dados?" - answer: "É um arquivo de texto em que os valores são separados por vírgula ou ponto e vírgula; é simples, portátil e fácil de importar em quase qualquer ferramenta de dados." - hint: "Lembre dos exemplos de `Ch1_ExampleCSV.csv` e da planilha do coffee shop." - - question: "Por que é obrigatório usar o e-mail institucional do Infnet para acessar o Looker Studio na disciplina?" - answer: "Porque o ambiente acadêmico e os recursos de ensino estão vinculados às contas institucionais, o que garante controle de acesso, integração com o Infinite Online e gestão acadêmica adequada." - hint: "Relembre a explicação sobre Infinite Online, alunos, professores e validações." -review_after_days: [3, 7] ---- - -## Visão Geral do Conceito - -A disciplina de **Introdução à Visualização de Dados e SQL** começa com uma pergunta simples: como transformar dados brutos em decisões melhores? Em muitas empresas, as pessoas precisam decidir entre confiar apenas em `achismo` ou apoiar-se em dados. A visualização de dados existe exatamente para isso: conectar **negócio** e **dados** por meio de gráficos, painéis e relatórios. - -Nesta primeira lição, o foco é entender **por que** visualizar dados é importante, **como** arquivos `CSV` entram nessa história e **como** dar os primeiros passos no `Looker Studio`. Usaremos um arquivo simples (como o `Ch1_ExampleCSV.csv` ou a planilha de um coffee shop) para construir nosso primeiro painel. - -Ao final, você terá uma visão clara do **cronograma da disciplina**, do modelo de avaliação com `TPs` e `AT`, e dos primeiros passos práticos com dashboards. - -## Modelo Mental - -Pense em visualização de dados como **contar histórias com dados**: - -- Os **dados brutos** (arquivos, tabelas, planilhas) são como anotações soltas. -- O **dashboard** é a história organizada que você conta para alguém entender o que está acontecendo. -- O **Looker Studio** é o palco onde essa história aparece: gráficos, filtros e indicadores. - -Outra forma de enxergar: - -- Sem visualização, uma empresa decide entre: - - Pessoa 1 — que depende dos dados (planilhas, relatórios, gráficos). - - Pessoa 2 — que decide só pelo instinto. -- Com visualização bem-feita, os dois mundos se aproximam: a experiência da pessoa é **alimentada** por evidências. - -O arquivo `CSV` entra como o **ponto de partida da jornada**: encontrar dados, entender seu formato e conectá-los ao Looker Studio é o primeiro passo para qualquer dashboard. - -Visualização de dados, ao longo da disciplina, sempre vai percorrer três palavras: - -- **História** — qual narrativa de negócio queremos contar? -- **Jornada** — qual caminho o usuário faz pelo relatório até chegar ao insight? -- **Entendimento e insights** — quais conclusões reais podemos tirar do painel? - -## Mecânica Central - -### 1. Fluxo básico de um dashboard com CSV - -O fluxo mínimo que você precisa dominar nesta lição é: - -1. Ter um arquivo `CSV` com colunas bem definidas (por exemplo, data da venda, produto, quantidade, receita). -2. Acessar o `Looker Studio` usando **a conta institucional do Infnet**. -3. Criar um **relatório** (no Looker Studio, o dashboard é chamado de relatório). -4. Adicionar uma **fonte de dados** via upload de arquivo `CSV`. -5. Escolher **métricas** (valores que somamos, contamos) e **dimensões** (categorias pelas quais analisamos essas métricas). -6. Construir pelo menos um gráfico simples (ex.: vendas por dia, ou quantidade por produto). - -Visualmente, o fluxo desta lição pode ser visto assim: - -```mermaid -flowchart LR - A[Arquivo CSV
Ex.: DS-Coffee-Shop.csv] --> B[Fonte de dados
Looker Studio] - B --> C[Campos
Métricas e Dimensões] - C --> D[Gráficos e Tabelas] - D --> E[Dashboard / Relatório] - E --> F[Insight para decisão
ex.: ajustar estoque, horários, promoções] -``` - -### 2. Formato CSV em prática - -Um arquivo `CSV` é um texto simples. Cada linha representa um registro; cada coluna é separada por vírgula ou ponto e vírgula. Exemplo simplificado, inspirado em uma cafeteria: - -```text -date,product,category,quantity,total_revenue -2026-03-01,Cappuccino,Drinks,3,21.00 -2026-03-01,Brownie,Food,2,18.00 -2026-03-02,Latte,Drinks,5,35.00 -``` - -Por ser um formato universal, quase toda ferramenta de dados (inclusive o Looker Studio) sabe **importar** esse tipo de arquivo sem esforço. - -### 3. Acesso acadêmico e Infinite Online - -No contexto do curso, o acesso ao Looker Studio e ao material didático é organizado assim: - -- Você entra no **Infinite Online** com a conta de aluno. -- Dentro do bloco de entrada, acessa a disciplina **Introdução à Visualização de Dados e SQL**. -- A partir da aula, dos TPs e da trilha de aprendizado, recebe os links e arquivos (como `Ch1_ExampleCSV.csv` e `DS-Coffee-Shop.xlsx`). -- Sempre que o material exigir login no Looker Studio, você deve usar **o e-mail institucional do Infnet**, não o Gmail pessoal. - -Esse cuidado garante que: - -- O acesso está vinculado ao **mundo acadêmico** (alunos, professores, coordenação). -- Professores conseguem acompanhar atividades e organizar avaliações com `TPs` e `AT`. - -### 4. Cronograma de 11 semanas - -O trimestre é organizado em **11 semanas (etapas)**: - -- Da **semana 1 à 9** — foco em conteúdo: - - Etapas 1 a 4: Looker Studio (visualização com CSV e Google Sheets, preparação de dados, controles e compartilhamento). - - Etapas 5 a 9: SQL (conceitos, ambiente de prática e consultas com operadores, `ORDER BY`, `GROUP BY`, `HAVING`, `DISTINCT`). -- **Semana 10** — entrega do `AT`. -- **Semana 11** — dúvidas finais e, se necessário, reentrega do `AT`. - -Essa estrutura garante tempo para: - -- Introdução conceitual e prática de visualização. -- Transição para SQL, que será a base dos dados em muitas situações reais. - -### 5. Avaliação por competências (TP e AT) - -A disciplina segue o modelo de **avaliação por competências**: - -- `TP` — Teste de Performance: - - São **3 TPs** nesta disciplina. - - **Todos** devem ser entregues e são **obrigatórios**. - - Não valem pontos diretamente, mas são **pré-requisitos** para poder fazer o `AT`. - - Servem como **aquecimento prático**, alinhado ao que é cobrado depois. -- `AT` — Assessment: - - É a **prova/trabalho principal** da disciplina. - - Precisa ser entregue dentro do prazo. - - **Vale pontos** e compõe a nota final. - -Na prática, quase tudo que aparece no `TP1` está conectado ao que você vê nas primeiras aulas: acesso ao Looker Studio, upload de CSV, montagem de um primeiro relatório e registro em `PDF` com prints e links. - -## Uso Prático - -### Exemplo 1 — Primeiro relatório com CSV de exemplo - -Suponha que você recebeu o arquivo `Ch1_ExampleCSV.csv` com dados de vendas diárias. O passo a passo mínimo para criar o primeiro relatório no Looker Studio é: - -1. Acessar `https://lookerstudio.google.com/navigation/reporting` usando o **e-mail institucional do Infnet**. -2. Clicar em **Criar** → **Relatório**. -3. Em **Adicionar dados**, escolher **Upload de arquivo CSV**. -4. Enviar o arquivo `Ch1_ExampleCSV.csv`. -5. Selecionar alguns campos: - - Dimensão: `date` - - Métrica: `total_revenue` -6. Inserir um gráfico de série temporal mostrando faturamento por dia. - -Isso já produz um primeiro **dashboard mínimo**: você consegue ver, em segundos, dias com maior e menor faturamento. - -### Exemplo 2 — Dashboard de cafeteria (DS-Coffee-Shop) - -Com um arquivo semelhante ao `DS-Coffee-Shop.xlsx` convertido para CSV: - -- Dimensões possíveis: - - `date` - - `product` - - `category` - - `store_location` -- Métricas possíveis: - - `quantity` - - `total_revenue` - -Você pode montar: - -- Um gráfico de barras com **receita por categoria** (`category` × `SUM(total_revenue)`). -- Uma tabela com **top 10 produtos por receita** (`product` ordenado por `SUM(total_revenue)`). -- Um filtro por `store_location` para comparar lojas. - -Mesmo sem SQL ainda, essa combinação já começa a responder perguntas de negócio: - -- Quais produtos mais vendem? -- Qual categoria traz mais receita? -- Qual loja merece reforço de estoque? - -## Erros Comuns - -- **Misturar contas de e-mail** - Tentar acessar o Looker Studio com o Gmail pessoal em vez do e-mail institucional do Infnet, perdendo acesso aos recursos configurados para o curso. - -- **Ignorar o formato do CSV** - Subir arquivos com separador errado (por exemplo, vírgula vs. ponto e vírgula) sem conferir visualmente se as colunas foram detectadas corretamente. - -- **Nomear mal o relatório** - Criar relatórios chamados apenas de “Relatório 1” ou “Teste”, o que atrapalha a organização, especialmente em TPs e projetos. - -- **Focar só na estética** - Investir tempo apenas em cores e fontes, sem se perguntar qual pergunta de negócio o gráfico responde. - -- **Deixar o TP para a última hora** - Esperar até o fim do prazo do TP para montar o dashboard, descobrindo tarde demais problemas de acesso, conta ou dados incompletos. - -## Visão Geral de Debugging - -Quando algo dá errado na construção do dashboard, siga uma linha de raciocínio sistemática: - -1. **Conferir acesso e conta** - - Estou logado com o **e-mail institucional** correto? - - Tenho acesso ao Infinite Online e aos links do TP? -2. **Validar dados na origem** - - O arquivo `CSV` abre corretamente em um editor de texto ou planilha? - - As colunas essenciais (data, valor, categoria) existem e estão no formato esperado? -3. **Checar mapeamento no Looker Studio** - - Campos numéricos estão marcados como **métricas** (ex.: `NUMBER`, `CURRENCY`)? - - Datas estão com tipo de data (`DATE`), não apenas `TEXT`? -4. **Revisar filtros e intervalos de data** - - Há algum filtro escondendo todos os dados? - - O intervalo de datas está correto (por exemplo, não está filtrando um período sem registros)? -5. **Registrar evidências** - - Tire prints do problema para facilitar perguntas a monitores, professores ou colegas. - -## Principais Pontos - -- **Visualização de dados** conecta negócio e dados, transformando números em histórias e decisões. -- O formato **CSV** é a forma mais simples e universal de levar dados para ferramentas como o Looker Studio. -- O **Looker Studio** organiza tudo em relatórios (dashboards) com **fontes de dados**, **métricas** e **dimensões**. -- A disciplina tem um **cronograma de 11 semanas**, com 9 de conteúdo e 2 voltadas ao AT e suas revisões. -- **TPs são obrigatórios** e preparam direto para o **AT**, mas não substituem a aula ao vivo nem a prática contínua. - -## Preparação para Prática - -Depois desta lição, você deve ser capaz de: - -- Explicar, em poucas frases, **por que** a visualização de dados é crucial para um cientista, engenheiro ou analista de dados. -- Abrir o Looker Studio com a conta correta e localizar o espaço de criação de relatórios. -- Conectar um arquivo `CSV` a um relatório. -- Construir pelo menos **um gráfico simples** (por exemplo, faturamento diário) e **um filtro** (por produto ou categoria). -- Ler o enunciado de um TP e identificar quais partes já são cobertas pelo que você viu na aula. - -Se algum desses pontos ainda parece nebuloso, volte nas seções de **Modelo Mental**, **Mecânica Central** e **Uso Prático** antes de ir para o Laboratório. - -## Laboratório de Prática - -### Desafio Easy — Primeiro gráfico a partir de um CSV - -Objetivo: criar um gráfico simples de faturamento diário a partir de um arquivo CSV. - -Enunciado: - -- Considere um arquivo `coffee_shop_sales.csv` com colunas `date`, `product`, `category`, `quantity`, `total_revenue`. -- No Looker Studio, crie um relatório chamado **"Meu Primeiro Relatório — Coffee Shop"**. -- Conecte o arquivo CSV como fonte de dados. -- Adicione um gráfico de série temporal mostrando `SUM(total_revenue)` por `date`. -- Adicione um filtro por `category`. - -Use o bloco abaixo apenas como referência textual dos campos esperados (não será executado no Looker Studio, mas ajuda a documentar o esquema de dados): - -```sql --- TODO: documentar os campos do CSV usado no dashboard --- Tabela lógica: coffee_shop_sales --- Colunas principais: --- date (DATE) --- product (TEXT) --- category (TEXT) --- quantity (NUMBER) --- total_revenue (NUMBER) -``` - -### Desafio Medium — Comparar categorias de produto - -Objetivo: enxergar quais categorias trazem mais receita usando o mesmo conjunto de dados. - -Enunciado: - -- Reutilize o relatório anterior ou crie outro chamado **"Coffee Shop — Categorias"**. -- Crie um gráfico de barras com: - - Dimensão: `category` - - Métrica: `SUM(total_revenue)` -- Ordene as barras da maior para a menor receita. -- Adicione um filtro de intervalo de datas para analisar apenas um mês específico. - -No editor de exercícios do ISS, use o bloco abaixo para esboçar uma query SQL que você **poderia** usar em um banco relacional que gerasse esses mesmos dados: - -```sql --- TODO: completar a query que calcula receita por categoria -SELECT - category, - SUM(total_revenue) AS total_revenue -FROM coffee_shop_sales --- TODO: adicionar filtro de datas adequado, por exemplo, apenas um mês -GROUP BY category -ORDER BY total_revenue DESC; -``` - -### Desafio Hard — Storytelling de negócio em uma única página - -Objetivo: produzir uma página de dashboard que conte uma história coerente sobre o desempenho da cafeteria. - -Enunciado: - -- Crie um relatório chamado **"Coffee Shop — Visão Geral"**. -- A partir do mesmo CSV: - - Mostre **faturamento total por mês**. - - Destaque as **3 categorias com maior receita total**. - - Exiba os **5 produtos mais vendidos** em quantidade. - - Inclua pelo menos **um controle/filtro** (por exemplo, por loja, se existir `store_location`). -- Pense em como organizar os gráficos na tela de forma que alguém não técnico entenda, em menos de um minuto, **o que está indo bem** e **o que merece atenção**. - -Use o bloco abaixo como ponto de partida para estruturar as consultas que alimentariam esse painel em um banco SQL: - -```sql --- TODO: montar consultas que dariam suporte ao storytelling do dashboard - --- 1) Receita por mês -SELECT - DATE_TRUNC('month', date) AS month, - SUM(total_revenue) AS monthly_revenue -FROM coffee_shop_sales -GROUP BY month -ORDER BY month; - --- 2) Top categorias por receita -SELECT - category, - SUM(total_revenue) AS total_revenue -FROM coffee_shop_sales -GROUP BY category -ORDER BY total_revenue DESC -LIMIT 3; - --- 3) Top produtos por quantidade vendida -SELECT - product, - SUM(quantity) AS total_quantity -FROM coffee_shop_sales -GROUP BY product -ORDER BY total_quantity DESC -LIMIT 5; -``` - - - - - diff --git a/content/visualizacao-sql/aula-02-conta-bancaria-google-sheets-looker.md b/content/visualizacao-sql/aula-02-conta-bancaria-google-sheets-looker.md deleted file mode 100644 index b8c4007..0000000 --- a/content/visualizacao-sql/aula-02-conta-bancaria-google-sheets-looker.md +++ /dev/null @@ -1,420 +0,0 @@ ---- -title: "Conta bancária no Looker Studio com Google Planilhas" -slug: "conta-bancaria-google-sheets-looker" -discipline: "visualizacao-sql" -order: 2 -description: "Preparar dados de conta corrente no Google Planilhas e criar um dashboard com scorecards, série temporal e tabela no Looker Studio." -reading_time: 40 -difficulty: "medium" -concepts: - - preparação de dados para visualização - - Google Planilhas como fonte de dados - - métricas e dimensões - - scorecards - - séries temporais - - tabelas e filtros interativos -prerequisites: - - "visualizar-dados-csv-looker-studio" -learning_objectives: - - "Importar um arquivo CSV de conta corrente para o Google Drive e convertê-lo em Google Planilhas." - - "Limpar e formatar colunas de data e moeda para uso em dashboards." - - "Conectar a planilha ao Looker Studio e configurar tipos e agregações de campos." - - "Construir um relatório com tabela detalhada, scorecards de resumo e série temporal do saldo." -exercises: - - question: "Por que é comum usar o Google Planilhas entre o arquivo CSV original e o Looker Studio?" - answer: "Porque o Google Planilhas facilita limpeza, formatação e pequenos ajustes nos dados, e se integra nativamente ao Looker Studio, reduzindo atrito na conexão e na atualização do dashboard." - hint: "Repare na etapa de formatação das colunas `Date`, `Transaction Amount` e `Balance`." - - question: "O que diferencia uma métrica de uma dimensão no contexto desta aula?" - answer: "Dimensões descrevem 'por onde quebramos' a análise (data, categoria, tipo de transação); métricas são valores numéricos que agregamos (soma, média, máximo, mínimo) como montantes e saldos." - hint: "Veja como `Transaction Amount` e `Balance` são configurados no Looker Studio." - - question: "Por que configurar corretamente o tipo de dado e a agregação padrão dos campos no Looker Studio é crítico para o dashboard?" - answer: "Porque tipos e agregações incorretos geram cálculos errados e gráficos enganosos, por exemplo tratando `Balance` como texto ou somando em vez de tirar média." - hint: "Compare `Balance` (média) com `Transaction Amount` (soma)." -review_after_days: [3, 10] ---- - -## Visão Geral do Conceito - -Na Aula 1 você viu como **visualizar dados de um CSV simples** diretamente no `Looker Studio`. Nesta aula, vamos um passo além: usar um conjunto realista de **transações bancárias** (`Chapter2-AccountData.csv`) para construir um painel de conta corrente com **tabela detalhada**, **scorecards** e **série temporal**. - -O foco aqui é aprender a **preparar dados no Google Planilhas** e configurá-los corretamente no Looker Studio. Isso envolve limpar datas e valores monetários, definir o tipo correto de cada campo, escolher agregações adequadas e montar uma primeira página de relatório legível para alguém de negócio. - -Ao final da lição, você terá um dashboard de conta corrente semelhante ao do livro de referência, adaptado para **Real brasileiro (R$)** e pronto para ser estendido nas próximas aulas (comparações de anos, filtros avançados e exploração interativa). - -## Modelo Mental - -Pense no fluxo desta aula como uma **cozinha de dados**: - -- O arquivo `Chapter2-AccountData.csv` é o **ingrediente cru**: transações de uma conta bancária com datas, descrições, categorias e valores. -- O `Google Drive` e o `Google Planilhas` são a **bancada de preparo**: onde você organiza, padroniza e formata os dados. -- O `Looker Studio` é o **fogão e o prato final**: onde os dados viram visualizações que contam a história do saldo, dos depósitos e dos saques. - -Uma conta bancária tem duas perspectivas complementares: - -- **Visão de detalhe** — cada transação com data, descrição, categoria e valor. -- **Visão de resumo** — indicadores como saldo médio, saldo mínimo/máximo e tendência ao longo do tempo. - -O dashboard desta aula é justamente a **ponte** entre essas duas perspectivas, usando: - -- Uma **tabela** para detalhar transações. -- **Scorecards** para destacar saldos médios/mínimos/máximos. -- Uma **série temporal** para enxergar o comportamento do saldo ao longo de meses. - -## Mecânica Central - -### 1. Fluxo de dados: do CSV ao dashboard - -O pipeline dessa aula pode ser resumido assim: - -```mermaid -flowchart TD - A[Arquivo CSV
Chapter2-AccountData.csv] --> B[Google Drive
Pasta Looker Studio Exemplos/Exemplo 1] - B --> C[Google Planilhas
Planilha Chapter2-AccountData] - C --> D[Looker Studio
Fonte de dados Google Planilhas] - D --> E[Relatório Conta Corrente
tabela, scorecards, série temporal] -``` - -Cada etapa tem um papel específico: - -- **CSV** — saída original do banco online, com campos como `Transaction Number`, `Date`, `Description`, `Memo`, `Category`, `Transaction Amount`, `Balance`, `Temp 1`. -- **Google Planilhas** — organiza e formata os dados, inclusive conversão de moeda e datas. -- **Fonte de dados no Looker Studio** — mapeia colunas para tipos (texto, moeda, data) e define agregações padrão. -- **Relatório** — monta os gráficos e componentes visuais para análise. - -### 2. Preparando a planilha no Google Drive - -Passos principais descritos no guia: - -1. Acesse `https://drive.google.com` com sua conta acadêmica. -2. Crie a pasta `Looker Studio Exemplos` e, dentro dela, a pasta `Exemplo 1`. -3. Envie o arquivo `Chapter2-AccountData.csv` para essa pasta. -4. Clique duas vezes no arquivo e escolha **Abrir com → Planilhas Google**. - -Na **Planilha Google**: - -- Verifique se cada coluna está corretamente alinhada: - - `Transaction Number` — identificador da transação. - - `Date` — data no padrão local. - - `Description` e `Memo` — textos descritivos. - - `Category` — categoria da despesa/receita. - - `Transaction Amount` — valor da transação (positivo para depósito, negativo para saque). - - `Balance` — saldo após a transação. - -### 3. Limpeza e formatação de colunas - -Para que o Looker Studio interprete corretamente **datas** e **valores monetários**, o guia orienta a: - -- Na coluna `Date`: - - Selecionar todas as células de data. - - Menu `Formatar → Número → Texto simples`. - - Em seguida, ajustar alinhamento em `Formatar → Alinhamento → Esquerda`. - -- Na coluna `Transaction Amount`: - - Selecionar todos os valores. - - Aplicar formato de moeda em `Formatar → Número → Moeda`. - - Usar `Editar → Localizar e substituir` para: - - Remover vírgulas `,` usadas como separadores de milhar. - - Trocar pontos `.` por vírgulas `,` quando necessário para o padrão brasileiro. - -- Repetir o mesmo processo para a coluna `Balance`. - -Esses ajustes garantem que, quando a planilha for usada como fonte de dados: - -- `Transaction Amount` e `Balance` sejam tratados como **números (moeda)**. -- O Looker possa calcular somas, médias, mínimos e máximos corretamente. - -### 4. Conectando a planilha ao Looker Studio - -No Looker Studio: - -1. Acesse `https://lookerstudio.google.com`. -2. Clique em **Relatório em branco**. -3. Na janela **Adicionar dados ao relatório**, escolha o conector **Google Planilhas**. -4. Em **Todos os itens**, selecione a planilha `Chapter2-AccountData`. -5. Confirme em **Adicionar ao relatório**. - -Na tela de configuração de campos: - -- Ajuste os tipos e agregações, seguindo o guia: - - `Balance` - - Tipo: **Moeda → BRL - Real brasileiro (R$)**. - - Agregação padrão: **Médio** (média do saldo). - - `Transaction Amount` - - Tipo: **Moeda → BRL - Real brasileiro (R$)**. - - Agregação padrão: **Soma** (total de transações). - - `Transaction Number` - - Tipo: **Texto** (não queremos somar ou fazer média deste campo). - -Renomeie o relatório para algo significativo, por exemplo: - -- `Conta corrente – Exemplo prático do Looker Studio 1.0`. - -### 5. Componentes visuais principais - -O relatório desta aula combina três tipos de visualizações: - -- **Tabela detalhada**: - - Dimensões: `Date`, `Description`, `Memo`. - - Métricas: `Transaction Amount`, `Balance`. - - Ordenação: `Date` (decrescente) e, em seguida, `Balance`. - - Estilo: ocultar números de linha, ajustar colunas aos dados. - -- **Scorecards (Visão Geral)**: - - Scorecard 1 — `Saldo Médio`: - - Métrica: `AVG(Balance)`. - - Cor de fundo: amarelo. - - Scorecard 2 — `Saldo Máximo`: - - Métrica: `MAX(Balance)`. - - Cor de fundo: verde clara. - - Scorecard 3 — `Saldo Mínimo`: - - Métrica: `MIN(Balance)`. - - Cor de fundo: vermelha clara. - -- **Gráfico de série temporal suavizada**: - - Dimensão: `Date`. - - Métrica: `AVG(Balance)` com nome de exibição `Saldo Médio`. - - Tratamento de dados ausentes com **Interpolação linear** para reduzir “quedas” artificiais abaixo de zero. - -Esses três blocos criam uma narrativa: - -1. Scorecards respondem: **“qual é o nível típico do saldo?”** -2. A série temporal mostra: **“como o saldo evolui ao longo do tempo?”** -3. A tabela explica: **“quais transações explicam esses movimentos?”** - -## Uso Prático - -### Exemplo 1 — Preparando a planilha de conta corrente - -Suponha que você acabou de baixar o `Chapter2-AccountData.csv` da página de recursos do livro. O fluxo mínimo para deixá-lo pronto no Google Planilhas é: - -1. Enviar o CSV para a pasta `Looker Studio Exemplos/Exemplo 1` no Google Drive. -2. Abrir como `Planilhas Google`. -3. Ajustar formatação das colunas: - - Garantir que `Date` use um formato de data coerente com o Brasil. - - Remover símbolos de moeda e separadores de milhar em `Transaction Amount` e `Balance` antes de aplicar o formato de moeda. -4. Salvar e fechar a guia da planilha. - -Depois disso, a planilha está pronta para se tornar uma **fonte de dados estável** para vários relatórios (1.0, 2.0, 3.0, etc.). - -### Exemplo 2 — Relatório 1.0: visão básica da conta corrente - -Objetivo: montar a primeira versão do relatório com tabela + scorecards + série temporal. - -Passos principais (resumidos do guia): - -1. Criar um relatório em branco e conectá-lo à planilha `Chapter2-AccountData`. -2. Ajustar tipos e agregações dos campos relevantes. -3. Inserir a **tabela** com: - - Dimensões: `Date`, `Description`, `Memo`. - - Métricas: `Transaction Amount`, `Balance`. - - Ordenações: `Date` (decrescente) e `Balance` (crescente). -4. Adicionar **scorecards** para `Saldo Médio`, `Saldo Máximo` e `Saldo Mínimo` com cores distintas. -5. Criar a **série temporal suavizada** de `Saldo Médio` ao longo do tempo. - -Esse relatório já permite responder perguntas como: - -- Em quais períodos o saldo ficou mais crítico? -- Qual foi o saldo máximo atingido? -- Quais transações ocorreram nas datas mais problemáticas? - -### Exemplo 3 — Cabeçalho, layout e seletor de período - -O guia também sugere melhorar a **apresentação visual**: - -- Adicionar um retângulo de cabeçalho com cor de fundo (por exemplo, azul) e texto com o nome da conta: - `Conta Corrente Chris Cooper`. -- Inserir o logo da conta/cliente (por exemplo, `ChrisCooperLogo.png`). -- Construir um **controle de período** no cabeçalho para filtrar um intervalo padrão (por exemplo, 2018–2019). - -Esses detalhes ajudam a transformar um conjunto de gráficos soltos em um **relatório profissional** com identidade visual e filtros intuitivos. - -## Erros Comuns - -- **Não limpar símbolos de moeda e separadores** - Deixar `Transaction Amount` e `Balance` com `$`, vírgulas de milhar e pontos inconsistentes impede que o Looker trate os campos como números, quebrando somas e médias. - -- **Tratar identificadores como números agregáveis** - Manter `Transaction Number` com tipo numérico e agregação padrão pode gerar totais sem sentido; esse campo deve ser tratado como texto. - -- **Misturar datas como texto sem padronização** - Datas com formatos mistos (diferentes padrões de dia/mês/ano) produzem gráficos de séries temporais confusos ou vazios. - -- **Esquecer de configurar agregações padrão** - Deixar `Balance` com agregação `Soma` distorce o conceito de saldo, que normalmente faz mais sentido como **média** ou valor no fim de um período. - -- **Layout poluído** - Encher a página com muitos gráficos pequenos, sem hierarquia visual, dificulta a leitura rápida por parte do usuário de negócio. - -## Visão Geral de Debugging - -Quando o relatório da conta corrente não se comportar como esperado, siga esta sequência: - -1. **Verifique a planilha** - - Abra o `Chapter2-AccountData` no Google Planilhas. - - Confira se não há colunas duplicadas, cabeçalhos repetidos ou linhas extras no topo/rodapé. - -2. **Cheque tipos e agregações na fonte de dados** - - Veja se `Transaction Amount` e `Balance` estão como moeda BRL. - - Confirme as agregações padrão (`SUM` para montantes, `AVG` para saldo). - -3. **Teste visualizações simples primeiro** - - Monte um gráfico de tabela com poucas colunas para validar se os dados aparecem. - - Depois, adicione scorecards e séries temporais. - -4. **Revise filtros de período e controles** - - Filtros de período muito restritos podem esconder todos os dados. - - Certifique-se de que o controle de período está vinculado à dimensão de data correta. - -5. **Compare com o guia** - - Volte aos passos do capítulo (2.1–2.4) e revise configurações críticas como nomes de exibição, tipos de dados e ordenações. - -## Principais Pontos - -- **Google Planilhas** é uma etapa fundamental para limpar e estruturar dados vindos de CSV antes de visualizá-los. -- Configurar **tipos de dados** e **agregações padrão** no Looker Studio é tão importante quanto montar os gráficos. -- Um bom dashboard de conta corrente combina **tabela detalhada**, **scorecards de resumo** e **série temporal**. -- Cabeçalho, cores e controles de período ajudam a transformar um conjunto de gráficos em um **relatório profissional**. -- A maior parte dos problemas vem da **qualidade dos dados de entrada**, não dos gráficos em si — por isso, preparação de dados é parte central da disciplina. - -## Preparação para Prática - -Depois desta lição, você deve ser capaz de: - -- Organizar um dataset de conta corrente no Google Drive/Planilhas e deixá-lo limpo para uso. -- Conectar a planilha ao Looker Studio e configurar campos-chave (`Date`, `Transaction Amount`, `Balance`). -- Construir um relatório simples, porém útil, com tabela, scorecards e série temporal. -- Identificar rapidamente se um problema vem dos dados, da fonte ou da visualização. - -Se algum desses itens ainda não estiver claro, volte às seções de **Mecânica Central** e **Uso Prático** antes de iniciar o Laboratório. - -## Laboratório de Prática - -### Desafio Easy — Limpar e formatar a planilha de conta corrente - -Objetivo: deixar o `Chapter2-AccountData.csv` pronto no Google Planilhas para ser usado por qualquer relatório. - -Enunciado: - -- Crie as pastas `Looker Studio Exemplos/Exemplo 1` no seu Google Drive acadêmico. -- Envie o arquivo `Chapter2-AccountData.csv` e abra-o como Planilhas Google. -- Limpe e formate as colunas `Date`, `Transaction Amount` e `Balance` conforme descrito na lição. -- Garanta que todos os valores monetários estejam corretamente reconhecidos como moeda em R$. - -Use o bloco abaixo apenas como *checklist codificado* das colunas esperadas: - -```sql --- TODO: conferir se a planilha tem exatamente estas colunas principais --- Transaction Number (TEXT) --- Date (DATE) --- Description (TEXT) --- Memo (TEXT) --- Category (TEXT) --- Transaction Amount (NUMBER / CURRENCY) --- Balance (NUMBER / CURRENCY) -``` - -### Desafio Medium — Montar o relatório Conta Corrente 1.0 - -Objetivo: construir o relatório básico com tabela, scorecards e série temporal. - -Enunciado: - -- No Looker Studio, crie um relatório em branco e conecte a planilha `Chapter2-AccountData`. -- Configure os campos: - - `Balance` como moeda BRL com agregação padrão `AVG`. - - `Transaction Amount` como moeda BRL com agregação padrão `SUM`. - - `Transaction Number` como texto. -- Monte: - - Uma **tabela** com `Date`, `Description`, `Memo`, `Transaction Amount`, `Balance`, ordenada por data (decrescente). - - Três **scorecards**: `Saldo Médio`, `Saldo Máximo`, `Saldo Mínimo`. - - Uma **série temporal suavizada** de `Saldo Médio`. - -No editor integrado do ISS, use este esboço de consulta para documentar como você **validaria** os mesmos dados em SQL: - -```sql --- TODO: ajustar nomes de tabela/colunas conforme seu ambiente SQL -SELECT - MIN(balance) AS saldo_minimo, - MAX(balance) AS saldo_maximo, - AVG(balance) AS saldo_medio, - SUM(amount) AS total_transacoes -FROM account_transactions; -``` - -### Desafio Hard — Melhorar layout e experiência de leitura - -Objetivo: transformar o relatório 1.0 em algo mais próximo de um dashboard usado por um gerente de conta. - -Enunciado: - -- Copie seu relatório 1.0 para uma nova versão chamada **"Conta Corrente – Exemplo prático 1.1"**. -- Ajuste: - - **Tamanho da página** (largura/altura) para algo confortável (por exemplo, 900×700). - - **Cabeçalho** com retângulo, título e, se quiser, um logo fictício. - - **Scorecards** com cores de fundo coerentes e nomes de exibição claros. - - **Controle de período** configurado para focar em um recorte de datas (por exemplo, apenas 2018). -- Certifique-se de que a página, ao ser aberta, conta uma história rápida: saldo atual, comportamento ao longo do tempo e lista recente de transações. - -Use o bloco abaixo como um lugar para rascunhar como você documentaria as decisões de design do dashboard: - -```markdown - -``` - - - - - diff --git a/content/visualizacao-sql/aula-03-estrutura-csv-e-dados-produtos.md b/content/visualizacao-sql/aula-03-estrutura-csv-e-dados-produtos.md deleted file mode 100644 index 7c64199..0000000 --- a/content/visualizacao-sql/aula-03-estrutura-csv-e-dados-produtos.md +++ /dev/null @@ -1,350 +0,0 @@ ---- -title: "Entendendo arquivos CSV e dados de produtos" -slug: "estrutura-csv-e-dados-produtos" -discipline: "visualizacao-sql" -order: 3 -description: "Como estruturar dados em arquivos CSV, conferir colunas em planilhas e preparar uma base de produtos para usar em dashboards no Looker Studio." -reading_time: 35 -difficulty: "easy" -concepts: - - estrutura de arquivos CSV - - cabeçalhos e colunas - - importação de CSV em planilhas - - validação de dados antes da visualização - - relação entre linhas de detalhe e métricas agregadas -prerequisites: - - "visualizar-dados-csv-looker-studio" -learning_objectives: - - "Reconhecer a estrutura básica de um arquivo CSV com cabeçalhos e linhas de dados." - - "Criar e editar um CSV simples a partir de um conteúdo de texto." - - "Importar um CSV em Excel ou Google Planilhas para visualizar colunas e linhas corretamente." - - "Identificar quais colunas podem virar dimensões e quais podem virar métricas em um dashboard." -exercises: - - question: "Qual é a função da primeira linha em um arquivo CSV bem formado?" - answer: "Ela contém os cabeçalhos (nomes das colunas), que serão usados por planilhas, bancos de dados e ferramentas de BI para identificar cada campo." - hint: "Relembre o exemplo com `Product Name`, `Description`, `Flavor`, `Product Type` e `Number Sold`." - - question: "Por que arquivos CSV são tão usados para integrar dados entre sistemas e ferramentas de visualização?" - answer: "Porque são arquivos de texto leves, simples de gerar e ler, e praticamente todas as ferramentas de planilha, bancos de dados e BI conseguem importá-los." - hint: "Pense na explicação sobre CSV como um dos formatos 'universais' de dados." - - question: "Como você decide se uma coluna deve ser usada como métrica ou como dimensão em um dashboard?" - answer: "Colunas numéricas que fazem sentido ser somadas, contadas ou agregadas viram métricas; colunas que categorizam ou descrevem os registros (nome do produto, tipo, sabor) viram dimensões." - hint: "Olhe para `Number Sold` versus `Product Name` e `Product Type`." -review_after_days: [2, 7] ---- - -## Visão Geral do Conceito - -Antes de montar dashboards no Looker Studio, você precisa de **dados bem estruturados**. Nesta lição, o foco é entender como arquivos `CSV` organizam informações em colunas e linhas, usando o exemplo de uma tabela de produtos (`Product Name`, `Description`, `Flavor`, `Product Type`, `Number Sold`). - -Você vai ver como criar um CSV a partir de um conteúdo de texto, abrir esse arquivo em um editor simples (como o bloco de notas) e importá-lo para uma planilha (Excel ou Google Planilhas) para enxergar os dados em forma tabular. A partir daí, já é possível pensar em **dimensões** e **métricas** que serão usadas em relatórios futuros. - -O objetivo é ganhar segurança em olhar para arquivos de dados e responder: **quais colunas existem?**, **o que cada uma significa?** e **como elas podem virar gráficos úteis depois?** - -## Modelo Mental - -Pense em um arquivo CSV como uma **tabela salva em texto**: - -- Cada **linha** representa um registro (por exemplo, um produto). -- Cada **vírgula** (ou ponto e vírgula) separa **colunas**. -- A **primeira linha** contém os **cabeçalhos** — os nomes das colunas. - -No exemplo da aula, temos algo como: - -- `Product Name` — nome comercial do produto (ex.: "Vanilla Sponge Cake"). -- `Description` — descrição mais longa. -- `Flavor` — sabor (ex.: "Vanilla", "Chocolate"). -- `Product Type` — tipo de produto (bolo, biscoito, etc.). -- `Number Sold` — quantidade vendida. - -O CSV é só um **meio de transporte**: para analisar de verdade, você vai quase sempre: - -1. Conferir e ajustar o arquivo em um editor de texto simples. -2. Carregar o arquivo em uma planilha para enxergar as colunas e linhas. -3. Depois, conectar essa planilha ou o próprio CSV a uma ferramenta de BI, como o Looker Studio. - -## Mecânica Central - -### 1. Estrutura básica de um CSV - -Um CSV típico de produtos poderia ter um trecho aproximado assim: - -```text -Product Name,Description,Flavor,Product Type,Number Sold -Vanilla Sponge Cake,"Light vanilla sponge cake",Vanilla,Cake,120 -Double Chocolate Brownie,"Rich chocolate brownie",Chocolate,Brownie,85 -Lemon Tart,"Tart with fresh lemon filling",Lemon,Tart,64 -``` - -Regras importantes: - -- A **primeira linha** define os **cabeçalhos**. -- Cada coluna é separada por uma **vírgula** (ou, em alguns casos, ponto e vírgula). -- Valores de texto que contêm vírgulas internas podem aparecer entre aspas (`"..."`). - -Se você abrir esse arquivo num editor de texto como o bloco de notas, tudo aparece em uma linha longa com vírgulas; ao importar na planilha, cada vírgula vira uma **coluna**. - -### 2. Criando um CSV a partir de um conteúdo de texto - -Na aula, o professor mostra um fluxo comum em ambientes de estudo: - -1. Você recebe um **link** que aponta para o conteúdo bruto de um CSV (por exemplo, `C1_ExampleCSV.csv`). -2. Abre o link no navegador e seleciona todo o conteúdo. -3. Cria um novo arquivo no sistema operacional: - - `Novo → Documento de texto`. -4. Renomeia o arquivo para o nome indicado (por exemplo, `C1_ExampleCSV.csv`), garantindo que a **extensão** seja realmente `.csv` — não `.txt`. -5. Abre o arquivo com um editor simples (bloco de notas) e cola o conteúdo copiado do navegador. -6. Salva o arquivo. - -Para garantir que você está vendo a extensão no Windows, é importante desativar a opção que **oculta extensões de arquivos conhecidos**, como foi demonstrado na aula. - -### 3. Visualizando o CSV em planilhas - -Ver o CSV em texto ajuda a entender a estrutura, mas é mais fácil enxergar colunas em uma planilha. Existem duas abordagens comuns: - -- **Excel**: - - Abrir o Excel. - - Usar a aba `Dados` → **Obter dados de arquivo de texto/CSV**. - - Selecionar o arquivo `C1_ExampleCSV.csv`. - - Confirmar o carregamento para ver cada coluna em sua própria célula. - -- **Google Planilhas**: - - Criar uma planilha em branco. - - Usar **Arquivo → Importar** ou **Dados → Importar** (dependendo da interface) para carregar o CSV. - - Conferir se cada coluna está no lugar certo (`Product Name`, `Description`, `Flavor`, `Product Type`, `Number Sold`). - -Depois disso, você passa a enxergar claramente: - -- Cabeçalhos na primeira linha. -- Registros nas linhas seguintes. -- Tipos de valores (texto vs números). - -### 4. Dimensões e métricas no contexto do CSV - -Mesmo sem abrir o Looker Studio ainda, você já pode decidir como esse conjunto de dados será usado em um dashboard: - -- **Dimensões** (descrevem, categorizam): - - `Product Name` - - `Flavor` - - `Product Type` -- **Métricas** (agregam, somam, contam): - - `Number Sold` — pode ser somado para obter o total de vendas por produto, sabor, tipo, etc. - -Esse mapeamento é fundamental quando você começar a montar gráficos: - -- Gráfico de barras por `Product Type` usando `SUM(Number Sold)`. -- Gráfico de pizza por `Flavor` usando `SUM(Number Sold)`. -- Tabela detalhada com `Product Name`, `Description`, `Number Sold`. - -## Uso Prático - -### Exemplo 1 — Criando e conferindo um CSV de produtos - -Passo a passo baseado na aula: - -1. Receba o link para o conteúdo de `C1_ExampleCSV.csv`. -2. Copie todo o conteúdo no navegador. -3. Crie um arquivo `C1_ExampleCSV.csv` no seu sistema (garantindo que a extensão é `.csv`). -4. Abra o arquivo no bloco de notas e cole o conteúdo. -5. Salve o arquivo. -6. Importe o arquivo em Excel ou Google Planilhas. - -Perguntas para se fazer ao olhar a planilha: - -- Os cabeçalhos estão na **linha 1**? -- Cada coluna contém apenas um tipo de informação? -- `Number Sold` está claramente numérico (sem símbolos estranhos)? - -### Exemplo 2 — Identificando métricas e dimensões - -Com a planilha aberta: - -- Marque mentalmente (ou em um caderno): - - Dimensões: nomes, sabores, tipos. - - Métrica principal: `Number Sold`. - -Isso já permite pensar em perguntas de negócio simples: - -- Qual tipo de produto vende mais? -- Qual sabor é mais popular? -- Quais produtos têm vendas muito baixas e talvez precisem de revisão? - -### Exemplo 3 — Conectando com o pensamento de BI - -Mesmo sem entrar no Looker Studio ainda, esse CSV de produtos ilustra duas ideias centrais em BI: - -- **Linhas de detalhe** — cada produto (ou cada combinação de atributos) em uma linha. -- **Agregações** — somar, contar ou calcular estatísticas sobre uma coluna numérica. - -Ao construir dashboards mais complexos (com Looker, Power BI ou outra ferramenta), você sempre volta a esse modelo mental: - -- TSV/CSV/planilha → tabelas de base. -- Dimensões (categorias) + métricas (números) → gráficos e indicadores. - -## Erros Comuns - -- **Esquecer cabeçalhos na primeira linha** - Colar dados sem uma linha de cabeçalho faz com que planilhas e ferramentas de BI tratem a primeira linha como dado, dificultando a configuração posterior. - -- **Salvar como `.txt` achando que é `.csv`** - Manter a extensão errada pode atrapalhar importações automáticas; muitas ferramentas esperam explicitamente `.csv`. - -- **Misturar formatos na mesma coluna** - Colocar texto em uma coluna que deveria ser numérica (como `Number Sold`) impede agregações e gráficos corretos. - -- **Não verificar colunas após a importação** - Assumir que o Excel ou o Google Planilhas “entendeu tudo” pode esconder problemas, como colunas deslocadas ou quebras de linha internas incorretas. - -## Visão Geral de Debugging - -Quando um CSV não se comportar como esperado ao importar: - -1. **Abra no editor de texto** - - Confira se a primeira linha tem todos os cabeçalhos. - - Verifique se cada linha tem o mesmo número de vírgulas. -2. **Cheque separadores** - - Se o arquivo usa ponto e vírgula em vez de vírgula, ajuste a opção de importação na planilha ou converta o arquivo. -3. **Procure por caracteres estranhos** - - Valores com quebras de linha internas ou aspas não fechadas podem quebrar o parse. -4. **Valide tipos de colunas na planilha** - - Veja se colunas numéricas realmente foram reconhecidas como números. -5. **Faça um pequeno subconjunto** - - Se o arquivo é grande, teste com poucas linhas primeiro, garantindo que o formato está correto antes de aplicar à versão completa. - -## Principais Pontos - -- Um **CSV bem formado** começa com cabeçalhos e mantém um padrão de separação em todas as linhas. -- Importar o CSV para uma **planilha** é a maneira mais rápida de conferir a estrutura real dos dados. -- Entender desde cedo quem é **dimensão** e quem é **métrica** facilita a construção de dashboards depois. -- Pequenos erros de formato (extensão, separadores, tipos mistos) geram grandes dores de cabeça em ferramentas de BI. -- Revisar dados **antes** de criar visualizações é parte essencial do trabalho em visualização de dados e SQL. - -## Preparação para Prática - -Depois desta lição, você deve ser capaz de: - -- Criar um CSV simples a partir de conteúdo de texto e salvá-lo com a extensão correta. -- Importar o arquivo em Excel ou Google Planilhas e verificar sua estrutura. -- Distinguir colunas que serão dimensões de colunas que serão métricas. -- Identificar problemas básicos de formato que atrapalhariam a construção de dashboards. - -Se ainda houver dúvidas, revise a parte de importação e o exemplo de planilha de produtos antes de seguir para dashboards mais avançados. - -## Laboratório de Prática - -### Desafio Easy — Construir seu primeiro CSV de produtos - -Objetivo: montar manualmente um arquivo CSV simples e conferi-lo em uma planilha. - -Enunciado: - -- Crie um arquivo `meus_produtos.csv` com as colunas: - - `Product Name`, `Flavor`, `Product Type`, `Number Sold`. -- Inclua pelo menos 5 produtos reais ou fictícios. -- Salve o arquivo com a extensão `.csv`. -- Importe o arquivo em Excel ou Google Planilhas e verifique se cada coluna está correta. - -Use este esqueleto como referência para o conteúdo do arquivo: - -```text -Product Name,Flavor,Product Type,Number Sold -TODO_Preencher,TODO_Preencher,TODO_Preencher,TODO_Preencher -``` - -Atualize as linhas `TODO_Preencher` com dados reais antes de importar. - -### Desafio Medium — Marcar dimensões e métricas na planilha - -Objetivo: identificar explicitamente dimensões e métricas no conjunto de produtos. - -Enunciado: - -- Na planilha onde o CSV foi importado: - - Crie uma nova aba chamada `metadata`. - - Nessa aba, crie uma mini tabela listando cada coluna (`Product Name`, `Flavor`, `Product Type`, `Number Sold`) e marque se ela é **Dimensão** ou **Métrica**. -- Use essa tabela para planejar pelo menos dois gráficos que você faria em um futuro dashboard (por exemplo, vendas por tipo de produto, vendas por sabor). - -No editor do ISS, você pode registrar esse planejamento assim: - -```markdown - -``` - -### Desafio Hard — Criar um CSV a partir de outro formato - -Objetivo: praticar a transformação de dados de um formato qualquer para CSV. - -Enunciado: - -- Imagine que você recebeu uma lista de produtos em formato diferente (por exemplo, colada de um documento de texto, de um PDF simples ou de um e-mail). -- Transforme essa lista em um CSV bem formado com: - - Cabeçalhos na primeira linha. - - Colunas coerentes (nome, tipo, categoria, vendas estimadas). -- Valide o resultado importando o arquivo na planilha e construindo uma tabela ordenada por `Number Sold`. - -Use o bloco abaixo como espaço para documentar quais decisões você tomou ao transformar o formato original em CSV: - -```markdown - -``` - - - - - diff --git a/content/visualizacao-sql/aula-04-dashboard-cafeteria-herman.md b/content/visualizacao-sql/aula-04-dashboard-cafeteria-herman.md deleted file mode 100644 index 478a3a6..0000000 --- a/content/visualizacao-sql/aula-04-dashboard-cafeteria-herman.md +++ /dev/null @@ -1,420 +0,0 @@ ---- -title: "Dashboard da cafeteria Herman: objetivos de negócio, métricas e layout" -slug: "dashboard-cafeteria-herman" -discipline: "visualizacao-sql" -order: 4 -description: "Como transformar o cenário da cafeteria Herman em um dashboard no Looker Studio, escolhendo métricas, dimensões e layout a partir do dataset DS-Coffee-Shop." -reading_time: 40 -difficulty: "medium" -concepts: - - definição de objetivo de negócio para dashboards - - métricas e dimensões em contexto de varejo - - dataset DS-Coffee-Shop - - layout e hierarquia visual de relatórios - - uso do Looker Studio para tomada de decisão -prerequisites: - - "visualizar-dados-csv-looker-studio" - - "estrutura-csv-e-dados-produtos" -learning_objectives: - - "Identificar objetivos de negócio claros para um dashboard de cafeteria." - - "Selecionar métricas e dimensões relevantes a partir do dataset DS-Coffee-Shop." - - "Planejar a estrutura de um relatório no Looker Studio (painel principal e possíveis desdobramentos)." - - "Relacionar o dashboard com decisões práticas de estoque e vendas." -exercises: - - question: "Qual é o principal objetivo de negócio do Herman ao pedir um dashboard?" - answer: "Aumentar o volume de vendas da cafeteria, entendendo melhor quais produtos vendem mais e quando, para tomar decisões melhores de estoque e operação." - hint: "Relembre a entrevista com o Herman descrita na lição." - - question: "Por que é importante conversar com a área de negócio antes de decidir quais gráficos criar?" - answer: "Porque quem conhece o negócio sabe quais perguntas precisam ser respondidas; sem isso, o dashboard pode ficar bonito, mas não útil para a tomada de decisão." - hint: "Veja as perguntas sobre objetivo do relatório, informações necessárias e nível de conhecimento de dados." - - question: "No cenário da cafeteria, cite duas dimensões e duas métricas importantes para o dashboard." - answer: "Dimensões: nome do produto, categoria; métricas: quantidade vendida, receita total." - hint: "Pense em colunas típicas do dataset DS-Coffee-Shop." -review_after_days: [3, 10] ---- - -## Visão Geral do Conceito - -Nesta lição, você vai pegar o contexto da **cafeteria Herman Cake & Coffee Shop** e traduzi-lo em um **dashboard de negócio** no Looker Studio. O objetivo declarado pelo Herman é simples: **aumentar o volume de vendas da cafeteria**. A partir desse objetivo, você precisa escolher **métricas**, **dimensões** e um **layout de relatório** que ajudem o dono a decidir melhor. - -O dataset `DS-Coffee-Shop` (disponível nos materiais da disciplina) traz informações de vendas de produtos da cafeteria. A partir dele, você construirá um painel capaz de responder perguntas como: quais produtos vendem mais, em que períodos, e quais itens devem ser priorizados em estoque e promoções. - -Mais do que arrastar gráficos, esta lição foca em **pensar como alguém de BI**: começar pelo objetivo de negócio, definir o que precisa ser medido e só depois montar visualizações no Looker Studio. - -## Modelo Mental - -Pense no processo em três camadas: - -1. **Negócio (o mundo do Herman)** - - Objetivo: vender mais e evitar faltar produto na vitrine. - - Dor atual: ele só recebe relatórios **semanais** do contador e reage atrasado. - -2. **Dados (o mundo das tabelas)** - - Dataset `DS-Coffee-Shop` com colunas como produto, categoria, data, quantidade, receita. - - Linhas representando vendas individuais ou agregadas. - -3. **Dashboard (ponte entre os dois mundos)** - - Painel principal com visão geral de vendas. - - Gráficos e tabelas que contam a “história” da cafeteria: o que vende melhor, quando e quanto. - -Podemos representar essa visão em alto nível assim: - -```mermaid -flowchart TD - A[Objetivo do Herman
Aumentar vendas] --> B[Perguntas de negócio
O que vende? Quando? Onde falta produto?] - B --> C[Dataset DS-Coffee-Shop
produtos, categorias, datas, vendas] - C --> D[Dashboard Looker Studio
painel principal + detalhes] - D --> E[Decisões
estoque, promoções, horário de produção] -``` - -O dashboard não é fim em si mesmo: ele é uma **ferramenta de decisão**. Toda escolha de métrica, filtro ou gráfico deve responder a perguntas que aproximem o Herman de seu objetivo. - -## Mecânica Central - -### 1. Entrevista com o Herman: objetivos e contexto - -Na transcrição da aula, o professor reconstrói uma pequena entrevista com o Herman. A partir dela, temos: - -- **Objetivo atual** - - “Quero aumentar o volume de vendas da cafeteria.” - -- **Como ele trabalha hoje** - - Registra receitas em planilhas ou sistemas simples. - - Envia dados ao contador. - - Recebe um relatório com produtos mais vendidos apenas **no fim da semana**. - - Usa esse relatório para fazer pedidos de ingredientes para a **semana seguinte**. - -- **Problema** - - A visão é atrasada: muitas decisões são tomadas “no escuro” até chegar o relatório semanal. - - Ele não tem visão diária (ou quase em tempo real) do que está acontecendo. - -- **Nível de conhecimento de dados** - - Herman conhece bem o **negócio** (o que vende, quais produtos são críticos). - - Mas não domina planilhas, métricas nem ferramentas de BI. - -Essa entrevista define o “briefing” do dashboard: precisamos dar ao Herman uma visão mais rápida, clara e visual das vendas, usando uma ferramenta que seja **acessível via navegador** e fácil de atualizar — o Looker Studio. - -### 2. Dataset DS-Coffee-Shop - -O arquivo `DS-Coffee-Shop` (fornecido nos materiais) representa as vendas de uma cafeteria. Embora os detalhes possam variar, um esquema típico inclui colunas como: - -- `date` — data da venda. -- `product_name` — nome do produto (ex.: cappuccino, brownie). -- `category` — categoria (bebida, comida, doce, salgado). -- `quantity` — quantidade vendida. -- `unit_price` — preço unitário. -- `total_revenue` — receita total (`quantity * unit_price`). -- (opcional) `store_location`, `time_of_day`, etc. - -Mapeando para o modelo mental de BI: - -- **Dimensões** (para “cortar” a análise): - - `date`, `product_name`, `category`, possivelmente `store_location`. -- **Métricas** (para agregar): - - `quantity`, `total_revenue`. - -É esse dataset que vamos conectar ao Looker Studio para construir o dashboard da cafeteria Herman. - -### 3. Perguntas de negócio que o dashboard deve responder - -Com base no objetivo do Herman, algumas perguntas essenciais são: - -- **Produtos e categorias** - - Quais produtos mais vendem? - - Quais categorias trazem mais receita? - - Existem produtos com vendas tão baixas que ocupam espaço de estoque à toa? - -- **Tempo** - - Em quais dias da semana as vendas são maiores? - - Há sazonalidade por mês (ex.: mais vendas em datas específicas)? - -- **Operação** - - Quais produtos deveriam ter produção reforçada em determinados horários/dias? - - Há produtos frequentemente em falta que prejudicam a experiência do cliente? - -Cada pergunta dessas deve inspirar **pelo menos um gráfico ou componente** no dashboard. - -### 4. Estrutura do relatório no Looker Studio - -Um desenho possível para o primeiro painel da cafeteria: - -- **Cabeçalho** - - Título: “Herman Cake & Coffee Shop — Visão Geral de Vendas”. - - Logo simples da cafeteria (se disponível). - - Controle de período (por exemplo, mês corrente, último mês). - -- **Faixa superior: Visão geral** - - Scorecards: - - Receita total no período. - - Quantidade total vendida. - - Número de produtos distintos vendidos. - -- **Área central: Tendência ao longo do tempo** - - Série temporal com `SUM(total_revenue)` por `date` ou por `month`. - -- **Área inferior: Detalhes por produto/categoria** - - Gráfico de barras com `SUM(total_revenue)` por `product_name` (top N). - - Gráfico de barras ou pizza por `category`. - - Tabela com colunas chave (`product_name`, `category`, `quantity`, `total_revenue`). - -Podemos representar a hierarquia de informação assim: - -```mermaid -flowchart TD - H[Dashboard Herman] - H --> G1[Visão geral
scorecards] - H --> G2[Tendência temporal
receita por data/mês] - H --> G3[Detalhes por produto
barras e tabela] - G3 --> G3a[Top produtos] - G3 --> G3b[Receita por categoria] -``` - -Esse layout permite que o Herman: - -- Veja rapidamente **se as vendas estão boas** (scorecards). -- Entenda **como variam no tempo** (série temporal). -- Descubra **o que está puxando ou segurando o resultado** (produtos e categorias). - -### 5. Painéis adicionais e desdobramentos - -Além do painel principal, você pode planejar: - -- **Painel de produtos** - - Foco em performance de cada item. - - Filtros por categoria. - - Tabelas com margens, se disponíveis no dataset. - -- **Painel de horários/dias da semana** - - Se houver coluna de hora/dia da semana, gráficos por faixa horária/dia para apoiar decisões de escala de equipe e produção. - -O importante é sempre seguir o fluxo: - -1. Objetivo de negócio. -2. Perguntas específicas. -3. Dados disponíveis. -4. Visualização adequada. - -## Uso Prático - -### Exemplo 1 — Escolhendo métricas para Herman - -Suponha que você já importou `DS-Coffee-Shop` para o Google Planilhas ou diretamente para o Looker Studio. Você precisa escolher **3–4 métricas principais** para a visão geral: - -- `SUM(total_revenue)` — receita total no período. -- `SUM(quantity)` — quantidade total de itens vendidos. -- `COUNT_DISTINCT(product_name)` — número de produtos distintos vendidos. -- (Opcional) `AVG(total_revenue)` por transação, se o dataset trouxer essa granularidade. - -Essas métricas permitem ao Herman perceber se: - -- A cafeteria está vendendo “forte” em termos de receita. -- A quantidade de itens vendidos faz sentido com a capacidade de produção. -- O mix de produtos é variado ou está concentrado em poucos itens. - -### Exemplo 2 — Dimensões e filtros relevantes - -Para que o dashboard seja explorável, você pode usar: - -- Filtro de **período** (controle de data). -- Filtro de **categoria** (`category`). -- Filtro de **produto** (`product_name`) via lista suspensa. - -Esses controles permitem que o Herman: - -- Veja rapidamente a performance apenas de bebidas ou apenas de doces. -- Compare períodos específicos (por exemplo, semana do Dia dos Namorados vs semana normal). - -### Exemplo 3 — Conectando com decisões de estoque - -Com o dashboard montado, algumas decisões típicas que o Herman pode tomar: - -- Aumentar o estoque de insumos para produtos com alta receita e alta venda recorrente. -- Descontinuar ou repensar produtos com venda muito baixa e margem pequena. -- Planejar promoções em dias/horários com movimento fraco. - -O dashboard passa a ser não apenas um relatório estático, mas uma **ferramenta de simulação mental**: ao mexer nos filtros, o Herman visualiza cenários e decide. - -## Erros Comuns - -- **Começar pelo gráfico, não pelo objetivo** - Sair arrastando componentes no Looker Studio sem saber o que a área de negócio precisa leva a dashboards bonitos, porém inúteis. - -- **Métricas descoladas da operação** - Escolher indicadores que não geram ações concretas (por exemplo, gráficos muito sofisticados, mas que não influenciam estoque, promoções ou escala de funcionários). - -- **Excesso de informação em uma única página** - Lotar o painel com muitos gráficos pequenos, sem hierarquia visual, dificulta a leitura para alguém como o Herman. - -- **Ignorar o nível de conhecimento da área de negócio** - Usar termos ou visualizações avançadas sem contexto (métricas estatísticas complexas, por exemplo) para um público que ainda está começando no tema. - -- **Desalinhamento com os dados disponíveis** - Planejar perguntas que o dataset atual ainda não responde (por exemplo, tentar analisar margem de lucro se só temos preço de venda). - -## Visão Geral de Debugging - -Quando o dashboard da cafeteria não estiver ajudando de verdade: - -1. **Revisite o objetivo de negócio** - - O painel responde diretamente a “como aumentar o volume de vendas?”. - - Existem métricas que ninguém está usando para decidir nada? - -2. **Observe como o usuário navega** - - O Herman encontra rapidamente o que precisa? - - Quais gráficos ele ignora sempre? - -3. **Simule decisões** - - Escolha uma pergunta prática (“devo comprar mais insumo de qual produto essa semana?”). - - Veja se o dashboard leva a uma resposta clara. - -4. **Simplifique quando necessário** - - Remova ou mova para painéis secundários gráficos que poluem a visão principal. - -5. **Considere lacunas de dados** - - Se decisões importantes dependem de dados que você não tem (como margem), considere incluir essas colunas no dataset em versões futuras. - -## Principais Pontos - -- Todo dashboard começa com um **objetivo de negócio claro**, não com um gráfico. -- O caso da **cafeteria Herman** é um exemplo de como traduzir dores reais (relatórios lentos, visão semanal) em um painel visual. -- O dataset **DS-Coffee-Shop** fornece dimensões e métricas suficientes para responder perguntas fundamentais de vendas. -- Um bom layout organiza a informação em **visão geral → tendência → detalhes**, com filtros bem escolhidos. -- O sucesso do dashboard se mede pela **qualidade das decisões** que ele viabiliza, não pela quantidade de gráficos. - -## Preparação para Prática - -Depois desta lição, você deve ser capaz de: - -- Conduzir uma mini-entrevista de requisitos com uma pessoa de negócio (como o Herman). -- Traduzir um objetivo de negócio em perguntas e, em seguida, em métricas/dimensões concretas. -- Esboçar no papel (ou em texto) a estrutura de um painel antes de abrir o Looker Studio. -- Avaliar se um dashboard realmente está ajudando a tomar decisões melhores. - -Se ainda estiver confuso sobre como sair “dos dados para as decisões”, volte às seções de **Modelo Mental** e **Mecânica Central** antes do Laboratório. - -## Laboratório de Prática - -### Desafio Easy — Mapear perguntas de negócio para métricas e dimensões - -Objetivo: praticar a tradução de objetivos em elementos de dashboard. - -Enunciado: - -- Liste, em uma tabela, pelo menos **5 perguntas** que o Herman poderia querer responder com o dashboard (por exemplo, “Quais são os 5 produtos com maior receita na última semana?”). -- Para cada pergunta, indique: - - Quais **dimensões** do `DS-Coffee-Shop` você precisa. - - Quais **métricas** precisa calcular. - -No editor do ISS, use o esqueleto abaixo: - -```markdown - -``` - -### Desafio Medium — Esboçar o layout do dashboard Herman - -Objetivo: planejar o layout de um painel de uma página para a cafeteria. - -Enunciado: - -- Crie um esboço textual (ou desenhado em outro lugar, descrito aqui) contendo: - - Cabeçalho (título, logo, controle de período). - - Bloco de visão geral (scorecards). - - Bloco de tendência temporal. - - Bloco de detalhes por produto/categoria. -- Para cada bloco, indique quais campos do `DS-Coffee-Shop` pretende usar. - -Você pode registrar esse esboço assim: - -```markdown - -``` - -### Desafio Hard — Propor um segundo painel complementar - -Objetivo: pensar além da visão geral e propor um painel complementar útil. - -Enunciado: - -- Proponha um **segundo painel** (ou aba) para o relatório da cafeteria, com foco em um tema específico, por exemplo: - - Horários/dias da semana. - - Análise de categorias. - - Produtos em risco de ruptura de estoque. -- Descreva: - - Objetivo específico desse segundo painel. - - Métricas e dimensões usadas. - - Como ele se conecta ao painel principal (por exemplo, link, botão, navegação por aba). - -Use o bloco abaixo para capturar o desenho desse segundo painel: - -```markdown - -``` - - - - - diff --git a/content/visualizacao-sql/aula-05-implementando-dashboard-herman-looker.md b/content/visualizacao-sql/aula-05-implementando-dashboard-herman-looker.md deleted file mode 100644 index 20f8e68..0000000 --- a/content/visualizacao-sql/aula-05-implementando-dashboard-herman-looker.md +++ /dev/null @@ -1,388 +0,0 @@ ---- -title: "Implementando o dashboard da cafeteria Herman no Looker Studio" -slug: "implementando-dashboard-herman-looker" -discipline: "visualizacao-sql" -order: 5 -description: "Passo a passo para importar o dataset DS-Coffee-Shop, conectar com o Looker Studio e montar o painel principal da cafeteria Herman." -reading_time: 45 -difficulty: "medium" -concepts: - - importação de dados no Google Planilhas - - conexão Google Planilhas → Looker Studio - - construção de scorecards, séries temporais e gráficos de barras - - filtros e controles em dashboards - - iteração prática sobre um dashboard já planejado -prerequisites: - - "dashboard-cafeteria-herman" - - "conta-bancaria-google-sheets-looker" -learning_objectives: - - "Importar o dataset DS-Coffee-Shop para o Google Drive/Planilhas." - - "Conectar a planilha ao Looker Studio usando o conector Google Planilhas." - - "Configurar campos, tipos e agregações para o dataset da cafeteria Herman." - - "Construir o painel principal planejado na aula anterior com scorecards, série temporal e gráficos de detalhes." -exercises: - - question: "Por que é recomendado usar o Google Planilhas como intermediário entre o arquivo DS-Coffee-Shop e o Looker Studio?" - answer: "Porque o Google Planilhas facilita organização, limpeza e ajustes finos de tipos/formatos, e se integra nativamente ao Looker Studio com o conector Google Planilhas." - hint: "Compare com o fluxo em que o arquivo é lido direto do sistema operacional." - - question: "Quais componentes principais compõem o painel principal da cafeteria Herman?" - answer: "Scorecards de visão geral (receita, quantidade vendida), uma série temporal de receita e gráficos/tabelas detalhando vendas por produto e por categoria." - hint: "Revise o layout planejado na aula anterior." - - question: "O que acontece se um campo numérico for importado como texto no Looker Studio?" - answer: "Ele não poderá ser somado ou agregado corretamente, prejudicando métricas e gráficos que dependem desse valor." - hint: "Lembre do cuidado com tipos e agregações na conexão de dados." -review_after_days: [3, 7] ---- - -## Visão Geral do Conceito - -Na aula anterior, você planejou o **dashboard da cafeteria Herman**: objetivo de negócio, perguntas, métricas, dimensões e layout. Nesta lição, você vai **tirar o plano do papel**: importar o dataset `DS-Coffee-Shop` para o Google Planilhas, conectá-lo ao Looker Studio e montar o painel principal passo a passo. - -O foco aqui é a **execução técnica** usando as ferramentas do ecossistema Google: Google Drive, Google Planilhas e Looker Studio. Ao final, você terá um relatório funcional que o Herman poderia usar no dia a dia para acompanhar vendas e tomar decisões de estoque. - -## Modelo Mental - -Veja o fluxo desta implementação como um pipeline bem definido: - -```mermaid -flowchart TD - A[Arquivo DS-Coffee-Shop
(.xlsx ou .csv)] --> B[Google Drive] - B --> C[Google Planilhas
planilha DS-Coffee-Shop] - C --> D[Looker Studio
fonte de dados Google Planilhas] - D --> E[Dashboard Herman
painel principal] -``` - -Em cada etapa você faz algo específico: - -- **A → B**: enviar o arquivo para a nuvem. -- **B → C**: abrir/convertê-lo em planilha e organizar as colunas. -- **C → D**: configurar os campos corretamente (tipos e agregações) ao criar a fonte de dados. -- **D → E**: colocar os gráficos no lugar certo, seguindo o layout pensado na aula anterior. - -Com esse modelo na cabeça, fica mais fácil depurar erros (se algo der errado, você sabe em qual etapa olhar). - -## Mecânica Central - -### 1. Importando o dataset DS-Coffee-Shop para o Google Planilhas - -1. Acesse `https://drive.google.com` com sua conta acadêmica. -2. Crie (se ainda não existir) uma pasta para a disciplina, por exemplo: - `Visualizacao-SQL` → `Herman-Coffee-Shop`. -3. Envie o arquivo `DS-Coffee-Shop.xlsx` ou `DS-Coffee-Shop.csv` para essa pasta. -4. Clique duas vezes no arquivo: - - Se for `.xlsx`, o Google já oferece abrir em **Planilhas Google**. - - Se for `.csv`, use **Abrir com → Planilhas Google**. - -Verifique na planilha: - -- Cabeçalhos na primeira linha (`date`, `product_name`, `category`, `quantity`, `unit_price`, `total_revenue`, etc.). -- Colunas de datas reconhecidas como data. -- Colunas numéricas (`quantity`, `unit_price`, `total_revenue`) reconhecidas como número/moeda (ajuste via **Formatar → Número** se necessário). - -### 2. Criando a planilha de trabalho da cafeteria - -Para manter tudo organizado: - -1. Se desejar, renomeie a planilha para algo como: - `DS-Coffee-Shop (Herman)`. -2. Crie uma nova aba chamada `dashboard_support` (opcional) para: - - Criar colunas auxiliares, se precisar (por exemplo, mês/ano a partir de `date`). - - Documentar brevemente o significado de cada coluna. - -Exemplo de fórmula para extrair mês/ano: - -```plaintext -=TEXT(A2; "yyyy-MM") -``` - -Onde `A2` é a célula de data; ajuste para o formato correto do seu idioma/local. - -### 3. Conectando a planilha ao Looker Studio - -No Looker Studio: - -1. Vá para `https://lookerstudio.google.com`. -2. Clique em **Relatório em branco**. -3. Na janela **Adicionar dados ao relatório**: - - Escolha o conector **Google Planilhas**. - - Em seguida, em **Todos os itens**, selecione a planilha `DS-Coffee-Shop (Herman)`. - - Escolha a aba apropriada (por exemplo, a aba original ou `dashboard_support`). -4. Confirme em **Adicionar ao relatório**. - -Na tela de configuração de campos (fonte de dados): - -- Ajuste **tipos e agregações**, por exemplo: - - `date`: - - Tipo: Data ou Data e hora. - - Agregação padrão: nenhuma (é dimensão). - - `product_name` e `category`: - - Tipo: Texto. - - `quantity`: - - Tipo: Número. - - Agregação padrão: Soma. - - `unit_price`: - - Tipo: Número ou Moeda. - - Agregação padrão: Média (ou nenhuma, conforme necessidade). - - `total_revenue`: - - Tipo: Moeda (idealmente BRL). - - Agregação padrão: Soma. - -Esses detalhes garantem que as métricas funcionem corretamente em gráficos e scorecards. - -### 4. Construindo o painel principal de Herman - -Com a fonte de dados configurada: - -1. **Cabeçalho** - - Insira uma forma retangular no topo. - - Adicione um texto como “Herman Cake & Coffee Shop — Visão Geral de Vendas”. - - Opcional: insira uma imagem de logo. - - Adicione um **controle de período** vinculado ao campo `date` com um intervalo padrão (por exemplo, último mês). - -2. **Scorecards de visão geral** - - Scorecard 1: `Receita total` - - Métrica: `SUM(total_revenue)`. - - Scorecard 2: `Quantidade total vendida` - - Métrica: `SUM(quantity)`. - - Scorecard 3 (opcional): `Número de produtos distintos` - - Métrica: `COUNT_DISTINCT(product_name)`. - -3. **Série temporal de receita** - - Adicione um **gráfico de série temporal**: - - Dimensão: `date` ou um campo derivado `month_year`. - - Métrica: `SUM(total_revenue)`. - - Ordene pela dimensão de data. - -4. **Gráfico de barras por produto/categoria** - - Gráfico de barras 1: `Top produtos por receita` - - Dimensão: `product_name`. - - Métrica: `SUM(total_revenue)`. - - Limite para Top N (por exemplo, 10). - - Gráfico de barras 2: `Receita por categoria` - - Dimensão: `category`. - - Métrica: `SUM(total_revenue)`. - -5. **Tabela detalhada** - - Campos sugeridos: - - Dimensões: `product_name`, `category`, possivelmente `date`. - - Métricas: `quantity`, `total_revenue`. - - Ordene por `total_revenue` (decrescente) ou por `date` dependendo do foco. - -### 5. Adicionando filtros e controles - -Para tornar o painel explorável: - -- Adicione um **controle de lista suspensa** para `category`. -- Adicione um controle de pesquisa/lista para `product_name`. -- Configure para que todos os gráficos da página respondam a esses controles (comportamento padrão, a menos que você o altere). - -Esses filtros permitem que o Herman: - -- Foque em uma categoria específica (por exemplo, apenas bebidas). -- Investigue produtos individuais. - -## Uso Prático - -### Exemplo 1 — Verificando se a fonte de dados está correta - -Depois de conectar a planilha: - -- Abra a aba de **Dados** no Looker Studio. -- Clique em alguns campos e verifique: - - Se `total_revenue` está com ícone de moeda. - - Se `quantity` está como número. - - Se `date` é reconhecido como data. - -Crie rapidamente uma tabela simples (`date`, `product_name`, `total_revenue`) para checar se os valores parecem coerentes com o que você vê na planilha. - -### Exemplo 2 — Testando o controle de período - -Com o controle de período no cabeçalho: - -- Ajuste o intervalo de datas para: - - Apenas um mês. - - Vários meses. -- Observe se: - - A série temporal se adapta. - - Scorecards mudam. - - Tabelas e barras se ajustam automaticamente. - -Se algum gráfico não responder, verifique se ele está configurado para usar a mesma dimensão de data da fonte. - -### Exemplo 3 — Explorando produtos top e cauda longa - -Use o gráfico de barras de produtos: - -- Ordene por `SUM(total_revenue)` (decrescente). -- Observe os 5–10 produtos mais fortes. -- Use a tabela detalhada para olhar produtos na “cauda longa” (baixa receita). - -Isso prepara o Herman para decisões como: - -- Investir em promoções dos produtos mais rentáveis. -- Reavaliar produtos de baixa performance. - -## Erros Comuns - -- **Não conferir tipos de dados na planilha antes de conectar** - Isso leva a campos importados como texto e métricas quebradas. - -- **Esquecer de definir agregações padrão** - `SUM` e `AVG` incorretos (ou ausentes) tornam scorecards e gráficos enganadores. - -- **Lotar o painel com muitos gráficos sem testar filtros** - Sem testar a interação entre controles e gráficos, o painel pode ficar confuso e difícil de usar. - -- **Ignorar o desempenho** - Embora o dataset da cafeteria seja pequeno, em cenários maiores, múltiplos gráficos pesados na mesma página podem prejudicar o tempo de carregamento. - -## Visão Geral de Debugging - -Quando algo não funciona no dashboard da cafeteria: - -1. **Planilha primeiro** - - Abra o `DS-Coffee-Shop (Herman)` e verifique se os dados estão corretos e completos. -2. **Fonte de dados no Looker Studio** - - Revise tipos e agregações. -3. **Gráfico específico** - - Verifique se a dimensão e a métrica estão corretas. - - Teste com um filtro de período amplo (por exemplo, “todos os dados”) para descartar filtros muito restritivos. -4. **Controles/filtros** - - Veja se o gráfico está incluído no escopo dos controles de filtro. - -## Principais Pontos - -- A implementação do dashboard de Herman segue um pipeline claro: arquivo → Drive → Planilhas → Looker Studio → painel. -- Configurar corretamente tipos e agregações na fonte de dados é tão importante quanto desenhar o layout. -- O painel principal combina **scorecards, série temporal, barras e tabelas** para responder às perguntas centrais do negócio. -- Filtros bem escolhidos (período, categoria, produto) transformam o painel em uma ferramenta de exploração, não apenas um relatório estático. - -## Preparação para Prática - -Depois desta lição, você deve ser capaz de: - -- Configurar uma fonte de dados no Looker Studio a partir de uma planilha Google. -- Reproduzir, do zero, o painel principal da cafeteria Herman. -- Ajustar e depurar campos problemáticos (tipos e agregações). -- Validar se o dashboard responde corretamente a filtros e períodos. - -Se ainda estiver inseguro, repita o processo com um novo relatório, até que os passos se tornem naturais. - -## Laboratório de Prática - -### Desafio Easy — Conectar o DS-Coffee-Shop ao Looker Studio - -Objetivo: garantir que a conexão de dados está correta. - -Enunciado: - -- Importe o arquivo `DS-Coffee-Shop` para o Google Planilhas. -- Crie um relatório em branco no Looker Studio e conecte a planilha via conector Google Planilhas. -- Monte uma tabela simples com `date`, `product_name`, `category`, `quantity`, `total_revenue` e confira se os valores batem com a planilha. - -Use o bloco abaixo apenas como checklist: - -```markdown - -``` - -### Desafio Medium — Montar o painel principal de Herman - -Objetivo: implementar o layout planejado na aula anterior. - -Enunciado: - -- No mesmo relatório, construa: - - Cabeçalho com título e controle de período. - - Três scorecards principais (receita total, quantidade total, produtos distintos). - - Série temporal de receita. - - Gráfico de barras para top produtos por receita. - - Gráfico de barras ou pizza por categoria. - - Tabela detalhada de vendas. - -No editor do ISS, você pode documentar o que foi implementado: - -```markdown - -``` - -### Desafio Hard — Refinar filtros e interação do dashboard - -Objetivo: melhorar a usabilidade do painel para o Herman. - -Enunciado: - -- Adicione e configure: - - Controle de período com intervalo padrão relevante (por exemplo, último mês). - - Filtro de `category`. - - Filtro de `product_name` com pesquisa habilitada. -- Teste combinações de filtros (por exemplo, uma categoria específica em um determinado período) e ajuste gráficos/tabelas para manter a legibilidade (limite de linhas, ordenação, etc.). - -Registre suas decisões de design: - -```markdown - -``` - - - - - diff --git a/content/visualizacao-sql/aula-06-refinando-relatorio-conta-bancaria-looker.md b/content/visualizacao-sql/aula-06-refinando-relatorio-conta-bancaria-looker.md deleted file mode 100644 index 3713569..0000000 --- a/content/visualizacao-sql/aula-06-refinando-relatorio-conta-bancaria-looker.md +++ /dev/null @@ -1,329 +0,0 @@ ---- -title: "Refinando o relatório de conta bancária no Looker Studio" -slug: "refinando-relatorio-conta-bancaria-looker" -discipline: "visualizacao-sql" -order: 6 -description: "Limpar e formatar dados bancários no Google Planilhas, configurar campos no Looker Studio e ajustar o relatório de conta corrente para análise eficiente." -reading_time: 40 -difficulty: "medium" -concepts: - - limpeza e formatação de dados em planilhas - - conversão de formatos de moeda e data - - configuração de tipos e agregações no Looker Studio - - campos calculados para relatórios bancários - - boas práticas de refinamento de dashboards -prerequisites: - - "conta-bancaria-google-sheets-looker" -learning_objectives: - - "Aplicar formatações corretas de data e moeda ao dataset de conta corrente no Google Planilhas." - - "Configurar tipos de dados e agregações adequadas para campos bancários no Looker Studio." - - "Criar e ajustar campos calculados relevantes (saldo médio, total de transações, etc.)." - - "Refinar o relatório de conta corrente para torná-lo mais legível e útil para análise." -exercises: - - question: "Por que é necessário remover separadores de milhar e ajustar pontos/vírgulas ao formatar valores monetários no Google Planilhas?" - answer: "Porque separadores de milhar e uso inconsistente de pontos/vírgulas impedem que o Planilhas e o Looker Studio reconheçam os valores como números, o que quebra somas, médias e outros cálculos." - hint: "Relembre os passos de localizar/substituir vírgula e ponto nas colunas Transaction Amount e Balance." - - question: "Qual a diferença entre tratar `Balance` como média e `Transaction Amount` como soma no relatório de conta corrente?" - answer: "`Balance` representa o saldo ao longo do tempo e faz mais sentido ser analisado via média (ou valores pontuais), enquanto `Transaction Amount` representa fluxos de dinheiro que devem ser somados para calcular o total movimentado." - hint: "Veja a configuração de agregação padrão no Looker Studio." - - question: "Por que `Transaction Number` deve ser configurado como texto no Looker Studio?" - answer: "Porque é apenas um identificador de transação, não faz sentido somar ou tirar média desse campo; tratá-lo como número pode gerar agregações sem significado." - hint: "Observe como os campos são configurados na tela de dados do Looker Studio." -review_after_days: [3, 10] ---- - -## Visão Geral do Conceito - -Esta lição aprofunda o exemplo de **conta bancária** iniciado nas aulas anteriores. Em vez de apenas conectar os dados, o foco agora é **refinar o relatório**: limpar e formatar corretamente datas e valores no Google Planilhas, configurar tipos e agregações no Looker Studio e criar ajustes que tornem o dashboard mais confiável e legível. - -Usaremos o arquivo `Chapter2-AccountData.csv` convertido para planilha Google e conectado ao Looker Studio. O objetivo é garantir que campos como `Date`, `Transaction Amount` e `Balance` estejam configurados de forma correta, para que scorecards, séries temporais e tabelas reflitam a realidade da conta corrente de forma robusta. - -## Modelo Mental - -Pense na preparação dos dados bancários como uma **receita em duas camadas**: - -- **Camada 1 — Planilha (cozinha de dados)** - - Garantir que datas sejam datas, moedas sejam moedas e identificadores sejam texto. - - Ajustar símbolos, separadores e formatos para o padrão local (Brasil). - -- **Camada 2 — Looker Studio (montagem do prato)** - - Escolher tipos e agregações adequadas para cada campo. - - Criar campos calculados (quando necessário) e usá-los em gráficos/tabelas. - -Se a camada 1 não estiver bem resolvida, a camada 2 será sempre frágil. Refinar o relatório é, essencialmente, **garantir que as duas camadas conversem bem**. - -## Mecânica Central - -### 1. Limpeza e formatação de datas no Google Planilhas - -Usando a planilha `Chapter2-AccountData`: - -- Selecione a coluna `Date` (a partir de `B2` até o fim). -- Aplique: - - **Formatar → Número → Texto simples**, se estiver convertendo de um formato estranho. - - Em seguida, ajuste o alinhamento em **Formatar → Alinhamento → Esquerda**. -- Se necessário, converta para um formato de data reconhecido pelo Brasil, revisando: - - Ordem dia/mês/ano. - - Zeros à esquerda. - -Essa padronização facilita o reconhecimento correto da dimensão de tempo tanto na planilha quanto no Looker Studio. - -### 2. Tratando valores monetários (Transaction Amount e Balance) - -Nas colunas `Transaction Amount` e `Balance`: - -1. Selecione todos os valores de cada coluna (por exemplo, `D2:D…` e `E2:E…`). -2. Aplique o formato de moeda: - - **Formatar → Número → Moeda** (verificando que está em Real Brasileiro). -3. Use **Editar → Localizar e substituir** com a seguinte sequência: - - **Passo 1:** localizar vírgula `,` e substituir por **nada** (campo vazio), para remover separadores de milhar do formato americano. - - **Passo 2:** localizar ponto `.` e substituir por vírgula `,`, para alinhar ao padrão brasileiro. - -Essa ordem é importante: primeiro remove-se os separadores de milhar, depois ajusta-se o separador decimal. No final, verifique visualmente se: - -- Os valores aparecem como moeda em R$. -- Não há valores “quebrados” (por exemplo, número grudado com símbolo estranho). - -### 3. Configurando campos no Looker Studio - -Com a planilha limpa, ao conectar ou editar a fonte de dados no Looker Studio: - -- Para `Balance`: - - Tipo: **Moeda → BRL - Real brasileiro (R$)**. - - Agregação padrão: **Médio**. - -- Para `Transaction Amount`: - - Tipo: **Moeda → BRL - Real brasileiro (R$)**. - - Agregação padrão: **Soma**. - -- Para `Transaction Number`: - - Tipo: **Texto**. - - Agregação padrão: nenhuma (como dimensão). - -Essas configurações garantem que: - -- Scorecards de saldo médio e gráficos de série temporal usem `Balance` de forma coerente. -- Totais de movimentação financeira sejam calculados via `SUM(Transaction Amount)`. -- `Transaction Number` sirva apenas para identificação, sem induzir cálculos sem sentido. - -### 4. Ajustando campos calculados e layout - -Com os tipos configurados, você pode revisar: - -- **Scorecards**: - - `Saldo Médio`: usa `AVG(Balance)`. - - `Saldo Máximo`: `MAX(Balance)`. - - `Saldo Mínimo`: `MIN(Balance)`. - -- **Série temporal**: - - Dimensão: `Date` (ou ano/mês derivado). - - Métrica: `AVG(Balance)` com nome de exibição “Saldo Médio”. - -- **Tabela de transações**: - - Dimensões: `Date`, `Description`, `Memo`. - - Métricas: `Transaction Amount`, `Balance`. - - Ordenações: por `Date` (decrescente) e/ou `Balance`. - -Reveja também: - -- Títulos de gráficos. -- Precisão decimal para valores monetários (por exemplo, 2 casas decimais). -- Alinhamento de colunas e uso de formatações condicionais, se necessário. - -## Uso Prático - -### Exemplo 1 — Verificando o efeito de formatação incorreta - -Antes de limpar: - -- `Transaction Amount` pode ser importado como texto (`$10,056.87`). -- No Looker Studio, tentar somar esse campo gera resultados incorretos ou falhas. - -Depois da limpeza e formatação: - -- O mesmo campo é reconhecido como número em R$. -- Scorecards e séries temporais mostram valores coerentes de movimentação. - -### Exemplo 2 — Ajustando agregações erradas - -Se `Balance` estiver com agregação padrão `Soma`: - -- Scorecards e gráficos verão “saldo total” como soma de todos os saldos, o que não faz sentido. - -Ao mudar para `Médio`: - -- Scorecards passam a mostrar valores de saldo médio. -- A interpretação fica alinhada com a ideia de **nível de saldo** ao longo do tempo. - -### Exemplo 3 — Tabela com ordenação útil - -Uma tabela desordenada com muitas colunas é difícil de ler. Ao: - -- Remover colunas irrelevantes para a análise. -- Manter apenas `Date`, `Description`, `Transaction Amount`, `Balance`. -- Ordenar por `Date` (decrescente). - -A tabela passa a funcionar como um **extrato visual**: as transações mais recentes aparecem no topo, com seus impactos no saldo. - -## Erros Comuns - -- **Pular a etapa de limpeza na planilha** - Confiar que o Looker Studio “vai arrumar tudo sozinho” normalmente resulta em campos numéricos tratados como texto. - -- **Configurar todas as métricas como soma** - Usar `SUM` indiscriminadamente para `Balance` e outros campos pode produzir gráficos enganadores. - -- **Deixar identificadores como números agregáveis** - Tratar `Transaction Number` como número permite somas e médias sem sentido, poluindo o relatório. - -- **Ignorar o padrão local de formatação** - Não ajustar pontos/vírgulas ao padrão brasileiro provoca relações erradas entre o que se vê na planilha e o que é calculado no Looker Studio. - -## Visão Geral de Debugging - -Quando o relatório de conta corrente apresentar valores estranhos: - -1. **Volte à planilha** - - Veja se datas e valores estão no formato esperado. -2. **Confirme tipos na fonte de dados** - - Garanta que cada campo está com tipo e agregação esperados. -3. **Teste gráficos simples** - - Monte uma tabela mínima para verificar se os números batem com a planilha. -4. **Reveja localizar/substituir** - - Verifique se os passos de remoção de vírgulas e troca de pontos por vírgulas foram aplicados na ordem correta. - -## Principais Pontos - -- Refinar o relatório de conta bancária exige **limpeza cuidadosa** de datas e moedas na planilha. -- A configuração de **tipos e agregações** no Looker Studio é crucial para que scorecards e gráficos façam sentido. -- Alguns campos (como identificadores) devem ser tratados como **texto**, não como números. -- Pequenos detalhes de formatação podem ter impactos grandes na **confiabilidade do dashboard**. - -## Preparação para Prática - -Depois desta lição, você deve ser capaz de: - -- Revisar e corrigir formatações problemáticas em datasets similares. -- Conferir se a fonte de dados do Looker Studio está coerente com a planilha. -- Ajustar/agregar métricas de acordo com o significado de cada campo. -- Tornar o relatório de conta corrente mais amigável para leitura e análise. - -Se ainda restarem dúvidas, volte às seções de **Mecânica Central** e repita os passos na sua própria planilha. - -## Laboratório de Prática - -### Desafio Easy — Garantir formatação correta de datas e moedas - -Objetivo: revisar e corrigir datas e valores monetários no dataset de conta corrente. - -Enunciado: - -- Na planilha `Chapter2-AccountData`, certifique-se de que: - - `Date` está formatado como texto simples/alinhado à esquerda e depois convertido apropriadamente para data. - - `Transaction Amount` e `Balance` estão formatados como moeda em R$, com separadores corretos. - -Registre o que foi ajustado: - -```markdown - -``` - -### Desafio Medium — Revisar configurações de campos no Looker Studio - -Objetivo: garantir que a fonte de dados está correta para o relatório de conta corrente. - -Enunciado: - -- Abra a fonte de dados usada no relatório de conta corrente. -- Revise os campos `Date`, `Transaction Amount`, `Balance`, `Transaction Number`. -- Ajuste tipos e agregações conforme descrito na lição. -- Teste scorecards e a série temporal após os ajustes. - -Use o bloco abaixo para anotar as configurações: - -```markdown - -``` - -### Desafio Hard — Melhorar a legibilidade do relatório de conta corrente - -Objetivo: refinar o layout e as configurações do relatório para facilitar a leitura. - -Enunciado: - -- Ajuste: - - Precisão decimal em scorecards e gráficos. - - Ordenações em tabelas (por data ou saldo). - - Títulos e rótulos de eixos. -- Opcional: adicione uma formatação condicional simples na tabela (por exemplo, destacar linhas com `Balance` muito baixo). - -Descreva as melhorias aplicadas: - -```markdown - -``` - - - - - diff --git a/content/visualizacao-sql/aula-07-dashboard-comparacao-anos-conta-bancaria-looker.md b/content/visualizacao-sql/aula-07-dashboard-comparacao-anos-conta-bancaria-looker.md deleted file mode 100644 index fbf6479..0000000 --- a/content/visualizacao-sql/aula-07-dashboard-comparacao-anos-conta-bancaria-looker.md +++ /dev/null @@ -1,402 +0,0 @@ ---- -title: "Dashboard de comparação de anos no Looker Studio" -slug: "dashboard-comparacao-anos-conta-bancaria-looker" -discipline: "visualizacao-sql" -order: 7 -description: "Construindo um dashboard estático que compara anos de uma conta bancária usando scorecards, séries temporais e tabelas no Looker Studio." -reading_time: 25 -difficulty: "medium" -concepts: - - scorecards - - series temporais - - tabelas-resumo - - comparacao-de-periodos - - filtros-de-data -prerequisites: - - refinando-relatorio-conta-bancaria-looker -learning_objectives: - - "Comparar o desempenho de uma conta bancária entre dois anos usando scorecards, séries temporais e tabelas." - - "Configurar um dashboard estático no Looker Studio baseado em um relatório existente." - - "Escolher dimensões e métricas adequadas para comparar períodos de tempo." -exercises: - - question: "Por que usar scorecards é mais eficiente do que ler uma tabela grande de transações quando queremos comparar rapidamente dois anos?" - answer: "Porque os scorecards já trazem métricas agregadas (como total de transações e saldo médio) para cada período, permitindo ver tendências e diferenças em segundos, sem ter que interpretar dezenas de linhas de tabela." - hint: "Pense em tempo de leitura e clareza visual." - - question: "Qual é a vantagem de usar uma série temporal com duas métricas (2017 e 2018) em vez de dois gráficos separados?" - answer: "Uma série temporal com as duas métricas na mesma escala permite comparar ponto a ponto os valores dos anos ao longo do tempo, identificando facilmente cruzamentos, tendências e sazonalidade." - hint: "Compare a facilidade de ver quando uma linha passa a ficar acima da outra." - - question: "O que muda quando você substitui uma dimensão de data completa (dia) por uma dimensão de mês em uma tabela de resumo?" - answer: "A tabela deixa de mostrar cada transação ou dia para mostrar apenas uma linha por mês, com métricas agregadas como quantidade de transações e saldo médio, o que simplifica a visualização do comportamento geral ao longo do ano." - hint: "Pense no número de linhas e no objetivo do relatório." -review_after_days: - - 3 - - 10 ---- - -## Visão Geral do Conceito - -Esta lição mostra como construir um **dashboard estático** no Looker Studio para comparar o comportamento de uma **conta bancária** entre dois anos (2017 e 2018) usando o dataset `Chapter2-AccountData.csv`. -Em vez de navegar por centenas de transações, você aprenderá a montar uma **história visual** com **scorecards**, **série temporal** e **tabela-resumo** que pode ser exportada como PDF e compartilhada com terceiros. - -O foco é partir de um relatório já existente (da aula anterior) e refiná-lo para responder a uma pergunta específica: **“O que mudou no saldo e nas transações da conta entre 2017 e 2018?”**. - -## Modelo Mental - -O modelo mental desta lição é pensar o dashboard como um **extrato anual narrado visualmente**: - -- Os **scorecards** são o “resumo executivo” – mostram rapidamente totais e médias de um período. -- A **série temporal** é a “linha do tempo da conta” – mostra como o saldo médio evolui ao longo dos meses em cada ano. -- A **tabela-resumo mensal** é o “detalhe compacto” – mostra, mês a mês, quantas transações aconteceram e qual foi o saldo médio. - -Em vez de enxergar o CSV como uma planilha caótica com colunas como `Transaction Amount`, `Balance` e `Category`, você passa a vê-lo como **fonte de respostas visuais**: “quanto gastei”, “como variou meu saldo”, “em que meses houve mais movimento”. - -## Mecânica Central - -### Estrutura do dataset de conta bancária - -O arquivo `Chapter2-AccountData.csv` contém colunas típicas de um extrato, como: - -- `Transaction Number`: identificador único da transação. -- `Date`: data da transação. -- `Description` e `Memo`: textos descritivos. -- `Category`: categoria da transação (ex.: Housing, Food & Dining, Insurance). -- `Transaction Amount`: valor da transação (crédito ou débito). -- `Balance`: saldo da conta após a transação. - -No Looker Studio, normalmente: - -- A coluna `Date` vira **dimensão de data**. -- As colunas `Transaction Amount` e `Balance` viram **métricas numéricas**. -- O campo `Category` pode ser usado como dimensão para detalhar os gastos por tipo. - -### Componentes do dashboard de comparação de anos - -O dashboard desta lição é composto, no mínimo, por três blocos principais: - -- **Cabeçalho estático** - - Nome da pessoa/empresa (ex.: “Chris Cooper”). - - Período analisado (ex.: “Visão geral 2018 – comparação com 2017”). - -- **Scorecards (Visão Geral / Scorecards)** - - Total de transações em 2018 comparado com 2017. - - Saldo médio em 2018 comparado com 2017. - - (Opcional) Total líquido (entradas − saídas) em 2018 comparado com 2017. - -- **Série temporal (linha do tempo do saldo médio)** - - Eixo X: meses. - - Série 1: saldo médio mensal em 2018. - - Série 2: saldo médio mensal em 2017. - -- **Tabela-resumo mensal** - - Dimensão: mês. - - Métricas: número de transações no mês e saldo médio no mês. - -### Fluxo lógico do dashboard - -O fluxo de informação que o dashboard implementa pode ser visto assim: - -```mermaid -flowchart TD - A[CSV AccountData
Chapter2-AccountData.csv] --> B[Conexão de dados
Looker Studio] - B --> C[Scorecards
Totais e saldos médios por ano] - B --> D[Série temporal
Saldo médio 2017 vs 2018] - B --> E[Tabela-resumo
Transações e saldo médio por mês] - C --> F[Exportar PDF
Resumo executivo] - D --> F - E --> F -``` - -O segredo é que **todos os componentes usam a mesma fonte de dados**, mas com **diferentes combinações de dimensões e métricas**: - -- Scorecards agregam **por ano**. -- Série temporal agrega **por mês**, mas plota duas séries (uma por ano). -- Tabela agrega **por mês**, exibindo contagem de transações e saldo médio. - -## Uso Prático - -### 1. Partindo de um relatório existente - -Na aula anterior, você já tinha um relatório no Looker Studio com o extrato da conta bancária. Em vez de recriar tudo do zero: - -1. Abra o relatório anterior (ex.: “Conta corrente – exemplo prático do Looker Studio 1.0”). -2. Clique nos **três pontinhos** no canto superior direito do relatório. -3. Use a opção de **fazer uma cópia** do relatório. -4. Renomeie o relatório copiado (ex.: “Conta corrente – comparação 2017 vs 2018”). - -Isso garante que: - -- As conexões com a fonte de dados (planilha ou CSV) já estejam configuradas. -- O estilo e o layout base sejam reaproveitados. - -### 2. Configurando scorecards para comparação de anos - -No relatório copiado: - -1. Insira três **scorecards** (um para cada métrica principal). -2. Para cada scorecard: - - Defina a **métrica principal** (ex.: total de transações ou saldo médio). - - Ative a **comparação com período anterior** (ex.: ano anterior). - - Formate o número (moeda, separador de milhar, casas decimais). - -Um exemplo de combinação possível: - -- Scorecard 1: “Total de transações em 2018” - - Métrica: contagem de transações (pode usar a métrica automática de contagem de linhas). - - Comparação com 2017 (diferença e percentual). - -- Scorecard 2: “Saldo médio em 2018” - - Métrica: média da coluna de saldo. - - Comparação com 2017. - -- Scorecard 3: “Total líquido em 2018” - - Métrica: soma de `Transaction Amount`. - - Comparação com 2017. - -### 3. Série temporal comparando 2017 e 2018 - -Para construir a série temporal: - -1. Insira um **gráfico de série temporal**. -2. Use como **dimensão de tempo** a coluna de data da transação. -3. Ajuste o **nível de detalhe** para meses (em vez de dias) para simplificar. -4. Crie duas séries (métricas) de saldo médio, uma para cada ano, ou configure um campo calculado que separe as séries por ano. - -O objetivo é chegar a algo próximo de: - -- Linha azul escura: saldo médio mês a mês em 2018. -- Linha azul clara: saldo médio mês a mês em 2017. - -Visualmente, você quer responder: - -- “Em que meses o saldo de 2018 ficou acima ou abaixo do de 2017?” -- “Há picos ou quedas atípicas em algum dos anos?” - -### 4. Tabela-resumo por mês - -Para a tabela: - -1. Insira um **gráfico de tabela**. -2. Use como dimensão um campo de **mês** (derivado da data). -3. Adicione as métricas: - - Número de transações no mês (contagem de linhas ou de `Transaction Number`). - - Saldo médio no mês (média da coluna de saldo). -4. Opcionalmente, adicione uma coluna com o total líquido do mês (soma de `Transaction Amount`). - -Essa tabela é o “check” da história contada pelos gráficos: - -- Se um mês aparece com muitas transações e saldo médio muito baixo, é um mês de **forte saída de caixa**. -- Se o saldo médio é muito alto com poucas transações, possivelmente houve **pouco movimento**, mas bons aportes. - -### 5. Exportando um relatório estático - -Ao final da montagem: - -1. Ajuste o **layout de página** para caber em uma folha A4 ou similar. -2. Revise títulos, legendas e textos do cabeçalho. -3. Use a opção do Looker Studio de **exportar como PDF**. - -O resultado é um **relatório estático**, pronto para ser enviado por e-mail, anexado a um relatório maior ou impresso para discussão em reunião. - -## Erros Comuns - -- **Misturar granularidades de tempo sem perceber** - - Problema: série temporal em nível de dia e tabela em nível de mês, sem deixar isso claro. - - Sintoma: números não batem entre o gráfico e a tabela. - - Correção: padronizar o nível de agregação (por exemplo, trabalhar tudo em nível de mês para comparação anual). - -- **Usar métricas erradas nos scorecards** - - Problema: usar soma do saldo em vez de saldo médio, gerando números gigantes e pouco interpretáveis. - - Sintoma: o valor do scorecard é muito maior do que qualquer valor de saldo observado. - - Correção: garantir que o scorecard use **média de saldo** e não soma. - -- **Deixar o controle de período aberto demais** - - Problema: o filtro de data inclui mais anos do que o esperado. - - Sintoma: as linhas da série temporal se sobrepõem de forma estranha, e os scorecards parecem “sem sentido”. - - Correção: travar o dashboard para comparar explicitamente **2017 vs 2018** ou configurar filtros claros para o usuário. - -- **Ignorar a consistência visual** - - Problema: cores diferentes para o mesmo ano em gráficos distintos. - - Sintoma: confusão ao ler o dashboard (ex.: 2018 é azul escuro em um gráfico e azul claro em outro). - - Correção: padronizar cores para cada ano e reutilizar essa paleta em todos os componentes. - -## Visão Geral de Debugging - -Quando algo parece errado no dashboard: - -- **1. Conferir o período filtrado** - - Verifique se os filtros de data estão realmente pegando apenas 2017 e 2018. - - Compare rapidamente com a tabela de dados bruta (no próprio Looker Studio ou na planilha). - -- **2. Validar a granularidade de tempo** - - Confirme se a série temporal e a tabela-resumo estão usando o mesmo nível (mês). - - Se necessário, duplique o gráfico e mude a granularidade para testar o impacto. - -- **3. Validar as métricas usadas** - - Confira se campos como saldo médio, total líquido e contagem de transações estão definidos corretamente. - - Compare os valores de um mês específico entre a tabela e a série temporal. - -- **4. Checar a fonte de dados** - - Abra o dataset de origem (CSV ou planilha) e confira algumas linhas específicas. - - Verifique se há linhas duplicadas, datas fora do intervalo esperado ou categorias estranhas. - -## Principais Pontos - -- **Dashboard estático**: serve para contar uma história fechada sobre um período, muitas vezes em PDF. -- **Scorecards**: trazem o resumo executivo da conta – totais e médios por ano. -- **Série temporal**: mostra a evolução do saldo médio ao longo dos meses, com duas linhas para comparação de anos. -- **Tabela-resumo mensal**: compacta o detalhe, exibindo número de transações e saldo médio por mês. -- **Consistência**: mesma fonte de dados, granularidade de tempo e paleta de cores coerente ao longo de todo o relatório. - -## Preparação para Prática - -Após esta lição, você deve ser capaz de: - -- Clonar um relatório existente no Looker Studio e adaptá-lo para comparar anos. -- Escolher dimensões e métricas adequadas para responder perguntas de negócio sobre uma conta bancária. -- Montar um layout de dashboard estático que combine scorecards, série temporal e tabela-resumo. -- Validar se os números apresentados fazem sentido, comparando com a fonte de dados. - -No Laboratório de Prática, você irá: - -- Traduzir o layout do dashboard em consultas SQL que calculam os mesmos números. -- Explorar variações de comparação (por categoria, por tipo de despesa, por mês). -- Pensar em novos indicadores que fariam sentido para o “cliente” da conta bancária. - -## Laboratório de Prática - -### Easy — Totais e saldo médio mensal (2017) - -Você recebeu uma cópia da tabela de transações da conta bancária em um banco de dados SQL, na tabela `account_transactions`, com colunas equivalentes ao CSV: data, valor da transação, saldo e categoria. -Seu primeiro objetivo é calcular, para o ano de 2017, **quantas transações ocorreram em cada mês** e **qual foi o saldo médio mensal**. - -Complete a query abaixo: - -```sql --- TODO: selecionar o ano e o mês da data --- TODO: contar o número de transações --- TODO: calcular o saldo médio no mês -SELECT - strftime('%Y', date) AS year, - strftime('%m', date) AS month, - COUNT(*) AS total_transactions, - AVG(balance) AS average_monthly_balance -FROM account_transactions -WHERE strftime('%Y', date) = '2017' -GROUP BY - year, - month -ORDER BY - year, - month; -``` - -Seu desafio é adaptar os campos e funções de data para o dialeto SQL que você estiver usando (SQLite, PostgreSQL, etc.) mantendo a mesma ideia. - -### Medium — Comparando totais líquidos por ano - -Agora você quer reproduzir, via SQL, um dos scorecards do dashboard: **total líquido (entradas − saídas) em 2017 e 2018**. - -Suponha que: - -- Créditos (entradas) são transações com valor positivo em `transaction_amount`. -- Débitos (saídas) são transações com valor negativo. - -Complete a query: - -```sql --- TODO: extrair o ano da data --- TODO: somar o valor das transações por ano -SELECT - strftime('%Y', date) AS year, - SUM(transaction_amount) AS net_total -FROM account_transactions -WHERE strftime('%Y', date) IN ('2017', '2018') -GROUP BY - year -ORDER BY - year; -``` - -Use o resultado para alimentar um scorecard por ano ou para criar um gráfico de barras simples comparando 2017 e 2018. - -### Hard — Série temporal de saldo médio comparando anos - -Por fim, você quer construir via SQL a base para a **série temporal** do dashboard: o saldo médio mensal de 2017 e 2018, pronto para ser consumido por um gráfico (em Looker Studio ou outra ferramenta). - -Complete a query: - -```sql --- TODO: extrair ano e mês da data --- TODO: calcular saldo médio por ano e mês -SELECT - strftime('%Y', date) AS year, - strftime('%m', date) AS month, - AVG(balance) AS average_monthly_balance -FROM account_transactions -WHERE strftime('%Y', date) IN ('2017', '2018') -GROUP BY - year, - month -ORDER BY - year, - month; -``` - -Depois, pense em como você poderia transformar esse resultado em: - -- Uma série temporal com duas linhas (uma para cada ano). -- Um heatmap de meses x anos mostrando a intensidade do saldo médio. - - - - - diff --git a/content/visualizacao-sql/aula-08-dashboard-pizza-barras-transacoes-looker.md b/content/visualizacao-sql/aula-08-dashboard-pizza-barras-transacoes-looker.md deleted file mode 100644 index a943e57..0000000 --- a/content/visualizacao-sql/aula-08-dashboard-pizza-barras-transacoes-looker.md +++ /dev/null @@ -1,367 +0,0 @@ ---- -title: "Dashboard de pizza e barras para tipos e categorias de transações no Looker Studio" -slug: "dashboard-pizza-barras-transacoes-looker" -discipline: "visualizacao-sql" -order: 8 -description: "Usando gráficos de pizza e barras no Looker Studio para analisar tipos e categorias de transações em um extrato bancário." -reading_time: 30 -difficulty: "medium" -concepts: - - graficos-de-pizza - - graficos-de-barras - - dimensoes-e-metricas - - agregacao-por-categoria - - uso-de-campos-calculados-no-looker-studio -prerequisites: - - dashboard-comparacao-anos-conta-bancaria-looker -learning_objectives: - - "Construir um gráfico de pizza que mostra a proporção de valores entre tipos de transação (depósitos vs saques)." - - "Construir um gráfico de barras que cruza categorias de gasto com tipos de transação, usando a mesma base de dados." - - "Escolher dimensões e métricas adequadas para responder perguntas sobre o comportamento da conta bancária." -exercises: - - question: "Por que é importante usar o valor absoluto (`Abs Amount`) em vez do valor original da transação quando se quer comparar o volume de movimentação em gráficos de pizza ou barras?" - answer: "Porque o valor original inclui sinais positivos e negativos (depósitos e saques), e a soma direta pode se anular; o valor absoluto representa o volume movimentado independentemente do sentido, permitindo comparar o peso de cada tipo ou categoria de forma honesta." - hint: "Pense em um mês com muitos saques e muitos depósitos de valores parecidos." - - question: "Qual a diferença entre usar `Transaction Type` como dimensão principal e usá-lo como dimensão detalhada em um gráfico de barras?" - answer: "Como dimensão principal, o gráfico mostra uma barra por tipo de transação; como dimensão detalhada, `Transaction Type` divide cada barra de outra dimensão (como `Category`) em segmentos, permitindo comparar a distribuição dentro de cada categoria." - hint: "Observe quantas barras aparecem e como elas são segmentadas." - - question: "Por que gráficos de pizza não são ideais para comparar muitos rótulos diferentes, como dezenas de categorias de gasto?" - answer: "Porque muitos rótulos geram fatias muito finas, difíceis de ler e comparar visualmente; gráficos de barras lidam melhor com muitas categorias, pois cada barra tem o próprio eixo de comprimento." - hint: "Compare a leitura de 2–3 fatias grandes com 15 fatias pequenas." -review_after_days: - - 3 - - 10 ---- - -## Visão Geral do Conceito - -Esta lição continua o trabalho com o extrato bancário usado nas aulas anteriores, mas agora o foco é **entender a composição das transações** com a ajuda de **gráficos de pizza e barras** no Looker Studio. -Em vez de apenas olhar para o saldo ao longo do tempo, queremos responder perguntas como: **“Quanto do volume movimentado é saque vs depósito?”** e **“Quais categorias consomem mais dinheiro?”**. - -Usaremos o mesmo dataset de conta bancária (`Chapter2-AccountData.csv`), já limpo e configurado nas lições anteriores, para montar um dashboard com: - -- Um **gráfico de pizza** comparando o volume de saques e depósitos. -- Um **gráfico de barras** cruzando categorias de gasto com tipos de transação. - -## Modelo Mental - -O modelo mental aqui é enxergar o dashboard como uma **radiografia da composição do extrato**: - -- O **gráfico de pizza** responde: “de tudo que movimentamos, qual parte é **saque** e qual parte é **depósito**?”. -- O **gráfico de barras** responde: “dentro de cada **categoria de gasto**, quanto foi movimentado e como se divide entre saques e depósitos?”. - -Você não está mais focado em “quando” aconteceu (tempo), mas em **“como” e “onde” o dinheiro foi movimentado**: - -- **Dimensões**: `Transaction Type` (saque vs depósito), `Category` (Housing, Food & Dining, Insurance etc.). -- **Métricas**: volume movimentado em valor absoluto (campo calculado como `Abs Amount`). - -## Mecânica Central - -### Campos chave para os gráficos - -No Looker Studio (a partir da fonte de dados da conta bancária), você precisa dos seguintes campos: - -- Dimensões: - - `Transaction Type` — tipo da transação (por exemplo, `Withdrawal` e `Deposit`). - - `Category` — categoria da transação (Housing, Food & Dining, Insurance etc.). - -- Métrica principal: - - `Abs Amount` — valor absoluto do campo de valor da transação (normalmente derivado de `Transaction Amount`). - -> **Regra:** para comparar “peso” de tipos e categorias, use sempre **valores absolutos**; caso contrário, depósitos e saques podem se anular. - -### Gráfico de pizza — proporção de saques e depósitos - -O gráfico de pizza tem: - -- **Dimensão:** `Transaction Type`. -- **Métrica:** `Abs Amount` (soma dos valores absolutos). - -Ele responde: - -- “Qual porcentagem do volume total movimentado é de saques?” -- “Qual porcentagem é de depósitos?” - -### Gráfico de barras — categorias x tipos de transação - -O gráfico de barras empilhadas (ou agrupadas) tem: - -- **Dimensão (eixo Y ou X)**: `Category`. -- **Dimensão detalhada**: `Transaction Type` (para empilhar ou agrupar por tipo dentro de cada categoria). -- **Métrica (eixo de valor)**: `Abs Amount`. - -Ele responde: - -- “Quais categorias concentram maior volume movimentado?” -- “Dentro de cada categoria, o peso maior é de saques ou de depósitos?” - -### Fluxo lógico do dashboard - -O fluxo de construção desses gráficos pode ser visto assim: - -```mermaid -flowchart TD - A[Extrato bancário
AccountData.csv] --> B[Conexão Looker Studio] - B --> C[Gráfico de Pizza
Dimensão: Transaction Type
Métrica: SUM(Abs Amount)] - B --> D[Gráfico de Barras empilhadas
Dimensão: Category
Detalhe: Transaction Type
Métrica: SUM(Abs Amount)] - C --> E[Insight 1:
peso de saques vs depósitos] - D --> F[Insight 2:
categorias que mais consomem dinheiro] -``` - -O dashboard mostrado na aula segue exatamente essa lógica: primeiro uma visão geral simples (pizza) e, em seguida, um detalhamento por categoria (barras). - -## Uso Prático - -### 1. Construindo o gráfico de pizza - -Partindo do relatório de conta corrente (por exemplo, “Conta corrente — exploração de dados 3.0”): - -1. Entre no modo de **edição** (botão **Editar** no canto superior direito). -2. Clique em **Adicionar um gráfico** e selecione **Pizza → Gráfico de pizza**. -3. Posicione o gráfico **abaixo do cabeçalho**, do lado esquerdo, como visto na aula. -4. Na guia de **Configuração**: - - Em **Dimensão**, substitua `Transaction Number` por `Transaction Type`. - - Em **Métrica**, substitua `Record Count` por `Abs Amount` (arrastando o campo para o slot de métrica). - -Com isso, cada fatia representará o **volume total** movimentado por tipo de transação, e não apenas a quantidade de linhas. - -### 2. Ajustando a métrica para valor absoluto - -Na aula, aparece o ajuste crucial: - -- Inicialmente, o gráfico de pizza poderia estar usando: - - `Record Count` — contando o número de transações. - - Ou o próprio `Transaction Amount` com sinal, o que pode distorcer a leitura. - -Ao trocar para `Abs Amount`: - -- Saques de −R$ 500,00 e depósitos de +R$ 500,00 deixam de se anular. -- O gráfico passa a refletir que houve **R$ 1.000,00 de volume movimentado**, metade de cada tipo. - -### 3. Construindo o gráfico de barras por categoria - -Em seguida, você adiciona o gráfico de barras: - -1. Ainda no modo de edição, clique em **Adicionar um gráfico** e escolha **Barras → Gráfico de barras**. -2. Posicione-o **ao lado do gráfico de pizza**, ligeiramente abaixo. -3. Na aba de **Configuração**: - - Em **Dimensão (eixo Y ou X, dependendo da orientação)**, escolha `Category`. - - Em **Dimensão detalhada**, use `Transaction Type`. - - Em **Métrica (eixo de valor)**, use `Abs Amount`. - -4. Ajuste: - - A orientação (vertical/horizontal). - - O tamanho do gráfico. - - A largura da área de rótulos, arrastando até que os nomes de categoria fiquem **legíveis** (passo mostrado na aula). - -O resultado é um gráfico que: - -- Tem **uma barra por categoria** (Housing, Food & Dining, etc.). -- Divide cada barra em segmentos (ou grupos) de `Withdrawal` e `Deposit`. -- Mostra visualmente quais categorias concentram mais volume movimentado. - -### 4. Ajustando rótulos e legibilidade - -Para garantir que o dashboard fique utilizável: - -- Passe o mouse sobre a borda onde aparecem os rótulos das categorias até o cursor virar uma **seta dupla** (esquerda/direita). -- Arraste essa borda para a direita até que os nomes das categorias fiquem completos. -- Revise: - - Títulos dos gráficos (por exemplo, “Volume movido por tipo de transação” e “Volume movido por categoria e tipo”). - - Cores associadas a `Withdrawal` e `Deposit` (mantenha consistência entre pizza e barras). - - Eventuais limites de dados, se quiser focar apenas em um ano específico. - -## Erros Comuns - -- **Usar `Record Count` em vez de valor monetário** - - Problema: o gráfico mostra apenas quantas transações existem, não o quanto de dinheiro foi movimentado. - - Sintoma: uma categoria com muitas transações pequenas parece mais “pesada” que outra com poucas transações muito grandes. - - Correção: trocar a métrica para soma de `Abs Amount`. - -- **Não usar valor absoluto** - - Problema: depósitos e saques se anulam na soma. - - Sintoma: gráficos que sugerem “pouco volume total” quando, na prática, houve muito dinheiro entrando e saindo. - - Correção: criar e usar um campo de valor absoluto (ex.: `Abs Amount`) como métrica. - -- **Rótulos ilegíveis no gráfico de barras** - - Problema: categorias aparecem cortadas ou sobrepostas. - - Sintoma: é difícil entender qual barra corresponde a qual categoria. - - Correção: aumentar a área dos rótulos, reduzir o número de categorias mostradas ou filtrar para as principais. - -- **Cores inconsistentes entre os gráficos** - - Problema: `Withdrawal` é azul claro no gráfico de pizza e azul escuro no de barras (ou vice-versa). - - Sintoma: confusão ao comparar rapidamente os gráficos. - - Correção: padronizar a paleta e salvar como tema, se possível. - -## Visão Geral de Debugging - -Quando os gráficos não parecem fazer sentido: - -- **1. Verifique a métrica** - - Confirme se o gráfico está usando `Abs Amount` e não `Record Count` ou o valor original com sinal. - - Teste rapidamente trocando a métrica e observando o efeito. - -- **2. Revise as dimensões** - - Garanta que `Transaction Type` e `Category` estão nos lugares certos (dimensão principal vs detalhada). - - Verifique se não há filtros inesperados escondendo tipos ou categorias. - -- **3. Compare com uma tabela simples** - - Monte uma tabela com `Transaction Type`, `Category` e `Abs Amount`. - - Veja se os totais por tipo e por categoria batem com o que aparece na pizza e nas barras. - -- **4. Cheque filtragem por período** - - Se o dashboard estiver filtrado para um ano ou intervalo de datas, confirme se isso está claro nos títulos e cabeçalhos. - -## Principais Pontos - -- Gráficos de pizza e barras ajudam a entender **a composição** do extrato, não só a sua evolução no tempo. -- Usar **valor absoluto** (`Abs Amount`) é fundamental para representar corretamente o volume movimentado. -- A escolha de **dimensão principal** e **dimensão detalhada** muda completamente a leitura de um gráfico de barras. -- Legibilidade (rótulos, cores, tamanhos) é parte essencial de um dashboard útil. - -## Preparação para Prática - -Depois desta lição, você deve ser capaz de: - -- Configurar gráficos de pizza e barras no Looker Studio usando dimensões e métricas adequadas. -- Justificar quando faz sentido usar valor absoluto para métricas financeiras. -- Ajustar rótulos e layout para que gráficos com muitas categorias continuem legíveis. -- Validar os números dos gráficos comparando com tabelas de apoio. - -No Laboratório de Prática, você irá: - -- Traduzir os gráficos em consultas SQL que calculam os mesmos totais. -- Explorar variações por categoria, tipo de transação e período. -- Pensar em outras visualizações que poderiam complementar esse painel (por exemplo, um gráfico de barras por mês e categoria). - -## Laboratório de Prática - -### Easy — Volume total por tipo de transação - -Suponha que você tenha uma tabela SQL chamada `account_transactions` com colunas: - -- `transaction_type` (texto, por exemplo, 'Withdrawal' ou 'Deposit'). -- `transaction_amount` (valor numérico, com sinal). - -Escreva uma query que calcule o **volume total movimentado por tipo de transação**, usando o valor absoluto. - -```sql --- TODO: agrupar por tipo de transação --- TODO: somar o valor absoluto do montante -SELECT - transaction_type, - SUM(ABS(transaction_amount)) AS total_volume -FROM account_transactions -GROUP BY - transaction_type -ORDER BY - total_volume DESC; -``` - -Use o resultado para alimentar um gráfico de pizza em qualquer ferramenta de visualização. - -### Medium — Volume por categoria e tipo de transação - -Agora, considere que a tabela também contém: - -- `category` (texto, categoria da transação). - -Escreva uma query que calcule, para cada categoria, o volume total movimentado separado por tipo de transação. - -```sql --- TODO: agrupar por categoria e tipo --- TODO: usar valor absoluto do montante -SELECT - category, - transaction_type, - SUM(ABS(transaction_amount)) AS total_volume -FROM account_transactions -GROUP BY - category, - transaction_type -ORDER BY - total_volume DESC; -``` - -Esse resultado pode ser usado para montar um gráfico de barras empilhadas: uma barra por categoria, com segmentos por tipo de transação. - -### Hard — Top categorias por volume movimentado - -Por fim, você quer criar uma visão focada apenas nas **categorias com maior volume movimentado**, independentemente de tipo. - -Escreva uma query que: - -1. Calcule o volume total (usando valor absoluto) por categoria. -2. Retorne apenas as **5 categorias com maior volume**. - -```sql --- TODO: calcular volume por categoria usando ABS --- TODO: limitar para as 5 maiores categorias -SELECT - category, - SUM(ABS(transaction_amount)) AS total_volume -FROM account_transactions -GROUP BY - category -ORDER BY - total_volume DESC -LIMIT 5; -``` - -Pense em como esse resultado poderia ser exibido: - -- Em um gráfico de barras horizontal com as 5 categorias. -- Em uma tabela-resumo destacando essas categorias e seus valores. - - - - - diff --git a/content/visualizacao-sql/aula-09-introducao-bancos-dados-tipos-dados-sql.md b/content/visualizacao-sql/aula-09-introducao-bancos-dados-tipos-dados-sql.md deleted file mode 100644 index 8384d0f..0000000 --- a/content/visualizacao-sql/aula-09-introducao-bancos-dados-tipos-dados-sql.md +++ /dev/null @@ -1,401 +0,0 @@ ---- -title: "Introdução a bancos de dados, tipos de dados e SQL" -slug: "introducao-bancos-dados-tipos-dados-sql" -discipline: "visualizacao-sql" -order: 9 -description: "Entendendo o que são bancos de dados, dados vs informação, tipos de dados e o papel do SQL na comunicação cliente-servidor." -reading_time: 30 -difficulty: "easy" -concepts: - - bancos-de-dados - - dados-vs-informacao - - tipos-de-dados - - cliente-servidor - - sql-como-linguagem-de-consulta -prerequisites: - - estrutura-csv-e-dados-produtos -learning_objectives: - - "Distinguir dado de informação e explicar por que isso importa em bancos de dados." - - "Descrever a arquitetura básica cliente-servidor em um sistema de banco de dados." - - "Reconhecer e dar exemplos de tipos de dados comuns usados em SQL (texto, numérico, data, timestamp)." - - "Entender o papel do SQL na consulta e manipulação de dados armazenados." -exercises: - - question: "Qual é a diferença entre um dado e uma informação no contexto de bancos de dados?" - answer: "Um dado é um valor bruto sem contexto (como o número 5 isolado); informação é o dado mais contexto, como \"tamanho do sapato 5\", que já indica o que o valor significa." - hint: "Relembre o exemplo do número 5 e do tamanho do sapato." - - question: "Por que é importante escolher o tipo de dado adequado (por exemplo, `DATE` vs `VARCHAR`) para uma coluna que armazena datas?" - answer: "Porque tipos como `DATE` permitem operações de data (comparar, somar dias, calcular diferenças) enquanto `VARCHAR` trata tudo como texto, dificultando ou impedindo essas operações." - hint: "Pense na situação em que datas armazenadas como texto precisaram ser convertidas para data para serem manipuladas." - - question: "Qual é o papel do SQL em um sistema de banco de dados cliente-servidor?" - answer: "O SQL é a linguagem usada pelo cliente para enviar comandos (consultas, inserções, atualizações) ao servidor de banco de dados, que interpreta esses comandos, acessa os dados e devolve os resultados ao cliente." - hint: "Lembre da explicação sobre o cliente pedindo serviço ao servidor." -review_after_days: - - 3 - - 10 ---- - -## Visão Geral do Conceito - -Esta lição dá um passo atrás em relação aos dashboards e volta para a base: **o que é um banco de dados**, qual a diferença entre **dado** e **informação**, como funcionam **tipos de dados** e qual é o papel do **SQL** em um cenário **cliente-servidor**. -Antes de escrever consultas complexas ou conectar ferramentas como Looker Studio, é essencial entender como os dados são armazenados, tipados e acessados. - -O foco aqui é construir o vocabulário fundamental de bancos de dados: database, tabela, dado, informação, tipos de dados (texto, número, data, timestamp) e a ideia de um cliente que conversa com um servidor usando SQL. - -## Modelo Mental - -Pense em um **banco de dados** como uma **biblioteca bem organizada de informações**: - -- Cada **livro** é como uma tabela. -- Cada **página** é como um registro (linha). -- Cada **linha da página** é como um campo (coluna). - -Os **dados** são as letras e números escritos nessas páginas; a **informação** é o significado que surge quando você sabe **a que esses dados se referem** (por exemplo, “5” como tamanho de sapato, idade, quantidade, etc.). -O **tipo de dado** é a “gramática” que define o que pode ser armazenado em cada campo (texto, número, data), e o **SQL** é o idioma formal que o “leitor” (cliente) usa para pedir livros, páginas e trechos específicos ao “bibliotecário” (servidor). - -## Mecânica Central - -### Bancos de dados e a arquitetura cliente-servidor - -Um `database` é uma coleção organizada de dados que: - -- Pode ser **armazenada eficientemente**. -- Pode ser **ordenada** (ex.: ordenar clientes pelo nome). -- Pode ser **pesquisada** com rapidez usando consultas. - -Na maioria dos sistemas reais, existe uma arquitetura **cliente-servidor**: - -- **Cliente**: programa que faz a solicitação (aplicação, ferramenta de BI, linha de comando, script). -- **Servidor**: software de banco de dados (como PostgreSQL, MySQL, SQL Server, SQLite em modo servidor) que recebe as solicitações, executa as consultas e devolve os resultados. - -O **cliente** envia comandos em SQL para o **servidor**, que: - -1. Interpreta a instrução. -2. Lê ou altera os dados nas tabelas. -3. Retorna os resultados (linhas, contagens, mensagens de erro). - -### Dado vs informação - -- **Dado**: valor isolado, sem contexto suficiente para interpretação. - - Exemplo: o número `5` sozinho, sem nome de coluna nem unidade. - -- **Informação**: dado + contexto, que permite entender o que o valor significa. - - Exemplo: “tamanho do sapato = `5`”. - -Em uma tabela de banco de dados: - -- O dado aparece como o valor armazenado em uma célula. -- A informação aparece quando você sabe **o nome da coluna**, **o tipo de dado** e o **significado de negócio** daquela coluna (por exemplo, “idade do cliente”, “preço do produto”, “data de cadastro”). - -> **Regra:** sem nome de coluna, tipo e contexto de negócio, o banco armazena apenas dados; a informação surge quando esses elementos se combinam. - -### Tipos de dados (datatypes) - -Em bancos de dados relacionais, cada coluna tem um **tipo de dado** (datatype) que define: - -- Que valores podem ser armazenados. -- Que operações são permitidas sobre esses valores. - -Alguns grupos comuns: - -- **Texto** - - Exemplos de tipos: `TEXT`, `CHAR`, `VARCHAR`, `STRING` (dependendo do banco). - - Usados para nomes, e-mails, códigos alfanuméricos. - - Podem conter letras, números e caracteres especiais (como `@`, `.`, `?`, `!`). - -- **Numéricos** - - Exemplos: `INT`, `BIGINT`, `SMALLINT`, `NUMERIC(10,2)`. - - Usados para quantidades, salários, preços, contagens. - - Tipos como `NUMERIC(10,2)` indicam, por exemplo, 10 dígitos no total, sendo 2 casas decimais (centavos). - -- **Datas e tempos** - - Exemplos: `DATE`, `TIME`, `TIMESTAMP`. - - `DATE` guarda dia, mês e ano. - - `TIMESTAMP` guarda data e hora completas (dia, mês, ano, hora, minuto, segundo e, às vezes, milissegundos). - -Quando um campo de data é salvo como texto (ex.: `VARCHAR`), o banco **não sabe** que aquilo é uma data: - -- Você perde operações naturais como somar dias, subtrair datas e filtrar por intervalos de tempo. -- É preciso **converter** o texto para um tipo de data adequado com funções de conversão para então poder manipular de forma correta. - -### SQL como linguagem de comunicação com o banco - -`SQL` (Structured Query Language) é a linguagem padrão usada para: - -- **Consultar dados**: com comandos como `SELECT`. -- **Inserir dados**: com `INSERT`. -- **Atualizar dados**: com `UPDATE`. -- **Excluir dados**: com `DELETE`. - -Na arquitetura cliente-servidor: - -- O cliente envia uma instrução SQL (por exemplo, “traga todos os clientes com idade maior que 30”). -- O servidor executa essa instrução sobre as tabelas e devolve o conjunto de linhas correspondente. - -## Uso Prático - -### Exemplo 1 — Tipos de dados em uma tabela de clientes - -Imagine uma tabela `clientes` com as seguintes colunas: - -- Nome do cliente. -- Data de nascimento. -- Salário. -- Data e hora do cadastro no sistema. - -Uma definição de esquema coerente em SQL poderia ser: - -```sql -CREATE TABLE clientes ( - id INT PRIMARY KEY, - nome VARCHAR(100), - data_nasc DATE, - salario NUMERIC(10,2), - criado_em TIMESTAMP -); -``` - -Aqui: - -- `nome` é texto (`VARCHAR`) porque aceita letras, números e caracteres especiais. -- `data_nasc` é `DATE` porque representa apenas a data. -- `salario` é `NUMERIC(10,2)` para suportar valores monetários com centavos. -- `criado_em` é `TIMESTAMP` porque guarda data e hora completas. - -### Exemplo 2 — Problema de datas como texto - -Suponha que você recebeu uma coluna `data_evento` como texto (`VARCHAR`), por exemplo `"2026-02-26 14:35:20.123"`. -Enquanto permanecer como texto: - -- Filtros como `WHERE data_evento > '2026-03-01'` podem se comportar de forma estranha, pois comparam strings, não datas. -- Operações de soma/subtração de dias não são possíveis diretamente. - -Ao converter: - -```sql -SELECT - CAST(data_evento AS TIMESTAMP) AS data_evento_ts -FROM eventos; -``` - -ou usando a função específica do seu banco, você passa a poder: - -- Calcular diferenças entre datas. -- Ordenar corretamente no tempo. -- Filtrar por intervalos temporais com significado. - -### Exemplo 3 — Clientes e servidor conversando via SQL - -Visualize a interação assim: - -```mermaid -sequenceDiagram - participant CLI as Cliente (aplicação/usuário) - participant DB as Servidor de Banco de Dados - - CLI->>DB: SELECT nome, salario FROM clientes WHERE salario > 5000; - DB-->>DB: Executa plano de consulta - DB-->>CLI: Retorna linhas com nome e salario -``` - -Nesse fluxo: - -- O cliente só precisa “falar SQL”. -- O servidor se encarrega de localizar tabelas, aplicar filtros, ordenar, agrupar e devolver os resultados. - -## Erros Comuns - -- **Armazenar datas como texto sem necessidade** - - Problema: dificulta filtros, ordenações e operações de tempo. - - Correção: usar tipos de data apropriados (`DATE`, `TIMESTAMP`) ou converter textos para esses tipos antes de trabalhar seriamente com tempo. - -- **Escolher tipos numéricos inadequados** - - Problema: usar inteiros (`INT`) para valores monetários ou campos em que casas decimais importam. - - Correção: preferir tipos como `NUMERIC(10,2)` ou equivalentes para valores financeiros. - -- **Ignorar a diferença entre dado e informação** - - Problema: criar tabelas sem nomes de colunas claros ou sem documentação de significado. - - Correção: nomear colunas de forma descritiva e manter documentação mínima sobre o que cada campo representa. - -- **Tratar tudo como texto (VARCHAR)** - - Problema: perde validações e operações específicas de tipos (datas, números). - - Correção: escolher tipos de dados que reflitam a natureza real de cada coluna. - -## Visão Geral de Debugging - -Quando você se depara com problemas em consultas ou visualizações: - -- **1. Verifique o tipo de dado da coluna** - - Confirme se datas são de fato `DATE`/`TIMESTAMP` e números são tipos numéricos. - - Se estiverem como texto, avalie se é possível converter. - -- **2. Analise exemplos de valores** - - Observe algumas linhas brutas para entender o formato real (por exemplo, datas com milissegundos, formatos mistos, separadores diferentes). - -- **3. Teste conversões em consultas simples** - - Antes de aplicar grandes transformações, teste `CAST` ou funções de conversão em algumas linhas para ter certeza de que o resultado é o esperado. - -- **4. Revise a intenção da coluna** - - Pergunte: “Este campo representa texto livre, um identificador, uma data, uma medida contínua?”. - - Ajuste o tipo de dado de acordo com essa intenção. - -## Principais Pontos - -- Um banco de dados é uma coleção organizada de dados, acessada via arquitetura cliente-servidor. -- Dado é valor bruto; informação é dado + contexto (nome de coluna, tipo, significado de negócio). -- Tipos de dados (texto, numérico, data, timestamp) controlam que operações são possíveis sobre cada coluna. -- SQL é a linguagem que o cliente usa para conversar com o servidor de banco de dados, pedindo leituras e alterações de dados. - -## Preparação para Prática - -Após esta lição, você deve ser capaz de: - -- Ler a definição de uma tabela SQL e interpretar o que cada tipo de dado significa. -- Identificar quando um dado está tipado de forma inadequada (por exemplo, datas como texto) e planejar correções. -- Explicar, para alguém da equipe, o papel do SQL como linguagem de comunicação com o banco. -- Relacionar esses conceitos com ferramentas de visualização, entendendo por que tipos corretos no banco facilitam relatórios corretos. - -No Laboratório de Prática, você irá: - -- Projetar uma tabela simples escolhendo tipos de dados coerentes para cada coluna. -- Escrever consultas `SELECT` que explorem campos de diferentes tipos. -- Experimentar a diferença entre tratar uma coluna como texto e como data. - -## Laboratório de Prática - -### Easy — Definindo tipos de dados adequados - -Você precisa modelar uma tabela `usuarios_site` com as colunas: - -- `id` (identificador numérico único). -- `nome_completo`. -- `email`. -- `data_nascimento`. -- `data_cadastro` (com data e hora). - -Complete a definição da tabela escolhendo tipos adequados: - -```sql --- TODO: escolher tipos de dados coerentes para cada coluna -CREATE TABLE usuarios_site ( - id INT PRIMARY KEY, - nome_completo VARCHAR(150), - email VARCHAR(150), - data_nascimento DATE, - data_cadastro TIMESTAMP -); -``` - -Explique (para você mesmo ou em anotações) por que escolheu cada tipo. - -### Medium — Comparando datas salvas como texto e como DATE - -Suponha que você tenha duas colunas na tabela `eventos`: - -- `data_evento_texto` (`VARCHAR`), com valores como `"2026-02-26 14:35:20.123"`. -- `data_evento` (`TIMESTAMP`), já convertida corretamente. - -Escreva duas consultas: - -```sql --- TODO: filtro usando a coluna de texto -SELECT * -FROM eventos -WHERE data_evento_texto > '2026-03-01'; - --- TODO: filtro usando a coluna TIMESTAMP -SELECT * -FROM eventos -WHERE data_evento > TIMESTAMP '2026-03-01 00:00:00'; -``` - -Compare os resultados e reflita sobre por que o filtro com `TIMESTAMP` é mais confiável. - -### Hard — Explorando diferentes tipos de dados em uma tabela - -Modele uma tabela `produtos` com: - -- `id` (identificador inteiro). -- `nome` (texto). -- `preco` (numérico com casas decimais). -- `estoque` (inteiro). -- `criado_em` (timestamp). - -Em seguida: - -```sql --- TODO: criar a tabela produtos com tipos adequados -CREATE TABLE produtos ( - id INT PRIMARY KEY, - nome VARCHAR(200), - preco NUMERIC(10,2), - estoque INT, - criado_em TIMESTAMP -); - --- TODO: escrever uma consulta que use diferentes tipos -SELECT - nome, - preco, - estoque, - criado_em -FROM produtos -WHERE preco > 100.00 - AND estoque > 0 -ORDER BY criado_em DESC; -``` - -Pense em como essa tabela poderia ser conectada a uma ferramenta de visualização, e por que ter tipos corretos desde o início simplifica a criação de relatórios. - - - - - diff --git a/content/visualizacao-sql/aula-10-sql-operadores-logicos-expressoes-select.md b/content/visualizacao-sql/aula-10-sql-operadores-logicos-expressoes-select.md deleted file mode 100644 index 2a89e0e..0000000 --- a/content/visualizacao-sql/aula-10-sql-operadores-logicos-expressoes-select.md +++ /dev/null @@ -1,480 +0,0 @@ ---- -title: "Operadores lógicos, filtros e expressões em SQL com SELECT" -slug: "sql-operadores-logicos-expressoes-select" -discipline: "visualizacao-sql" -order: 10 -description: "Usando operadores de comparação, AND/OR/IN e expressões em SELECT para filtrar e calcular dados em SQL." -reading_time: 35 -difficulty: "medium" -concepts: - - operadores-de-comparacao-sql - - operadores-logicos-and-or-in - - clausula-where - - expressoes-em-select - - aliases-de-colunas -prerequisites: - - introducao-bancos-dados-tipos-dados-sql -learning_objectives: - - "Aplicar operadores de comparação em cláusulas WHERE para filtrar registros." - - "Combinar condições com operadores lógicos AND, OR e IN." - - "Criar expressões em SELECT para calcular novos valores sem alterar a tabela." - - "Usar aliases para tornar cabeçalhos de colunas mais claros em resultados de consulta." -exercises: - - question: "Qual a diferença entre usar `=` e `IN` em uma cláusula WHERE?" - answer: "`=` compara com um único valor, enquanto `IN` permite comparar com uma lista de valores possíveis (por exemplo, `WHERE uf IN ('SP', 'RJ')`)." - hint: "Pense em quando você quer aceitar mais de um valor como válido." - - question: "O que o operador lógico `AND` faz quando combinamos duas condições em um WHERE?" - answer: "Ele exige que ambas as condições sejam verdadeiras para que o registro seja retornado; se qualquer uma for falsa, a linha é descartada." - hint: "Compare com o comportamento do `E` em lógica booleana." - - question: "Por que uma expressão como `price * 1.07` em um SELECT não cria uma nova coluna permanente na tabela?" - answer: "Porque a expressão é calculada apenas no resultado da consulta; ela não altera a definição nem os dados armazenados na tabela, a menos que seja usada em um comando de atualização como `UPDATE`." - hint: "Relembre a explicação de que a coluna 'tax_price' existe só na tela do SELECT." -review_after_days: - - 3 - - 10 ---- - -## Visão Geral do Conceito - -Esta lição aprofunda o uso do **SELECT** em SQL, mostrando como usar **operadores de comparação**, **operadores lógicos** (`AND`, `OR`, `IN`) e **expressões** para filtrar e calcular dados sem alterar a tabela. -Partindo de exemplos simples de tabelas (`customer`, `product`), você aprenderá a escrever consultas que selecionam apenas os registros relevantes e criam colunas calculadas no resultado da consulta. - -O foco é transformar o SELECT em uma ferramenta flexível: além de “trazer tudo”, você passa a **perguntar** ao banco apenas o que precisa, combinando filtros e cálculos no mesmo comando. - -## Modelo Mental - -Pense em uma consulta SQL como uma **conversa estruturada** com o banco de dados: - -- Os **operadores de comparação** (`>`, `<`, `>=`, `<=`, `=`, `!=`) definem **critérios**: “só quero registros com idade maior que 30”, “só quero produtos mais caros que R$ 100,00”. -- Os **operadores lógicos** (`AND`, `OR`, `IN`) combinam esses critérios: “maior que 30 **e** do estado SP”, “da categoria X **ou** da categoria Y”, “dentro desta lista de valores”. -- As **expressões** em SELECT (como `price * 1.07`) criam **colunas derivadas**: novos valores calculados a partir das colunas existentes, sem mexer na estrutura da tabela. - -O resultado é como uma **planilha filtrada e enriquecida** gerada sob demanda: você não altera o arquivo original, apenas cria uma visão específica para o que precisa naquele momento. - -## Mecânica Central - -### Operadores de comparação em WHERE - -Os operadores de comparação em SQL permitem comparar valores de colunas com constantes ou com outras colunas: - -- `>` (maior que). -- `<` (menor que). -- `>=` (maior ou igual). -- `<=` (menor ou igual). -- `=` (igual). -- `!=` ou `<>` (diferente, dependendo do dialeto SQL). - -Exemplos de uso: - -```sql --- Clientes com idade maior que 30 -SELECT * -FROM clientes -WHERE idade > 30; - --- Produtos com preço menor ou igual a 50 -SELECT * -FROM produtos -WHERE preco <= 50.00; -``` - -### Operadores lógicos AND, OR e IN - -Os operadores lógicos combinam condições: - -- `AND` — todas as condições precisam ser verdadeiras. -- `OR` — pelo menos uma condição precisa ser verdadeira. -- `IN` — verifica se um valor está **dentro** de uma lista. - -Exemplos: - -```sql --- Clientes de SP com idade maior que 30 -SELECT nome, idade, uf -FROM clientes -WHERE idade > 30 - AND uf = 'SP'; - --- Clientes de MG OU ES -SELECT nome, idade, uf -FROM clientes -WHERE uf = 'MG' - OR uf = 'ES'; - --- Mesma lógica, mas usando IN -SELECT nome, idade, uf -FROM clientes -WHERE uf IN ('MG', 'ES'); -``` - -`IN` deixa a consulta mais legível quando você precisa testar **vários valores** para a mesma coluna. - -### Expressões em SELECT e aliases - -Uma **expressão** em SELECT é um cálculo feito a partir de colunas e constantes, que gera uma coluna apenas no resultado da consulta. -Ela não altera a tabela, apenas **aparece na saída**. - -Exemplo de expressão para calcular preço com taxa: - -```sql -SELECT - product_id, - description, - price, - price * 1.07 AS tax_price -FROM products; -``` - -Aqui: - -- `price * 1.07` é a expressão (preço original + 7% de taxa). -- `AS tax_price` é o **alias** (apelido) que define o nome do cabeçalho da coluna calculada. - -> **Regra:** expressões em SELECT são ideais para cálculos pontuais (percentuais, conversões, formatações) sem necessidade de alterar a estrutura da tabela. - -Você também pode usar aliases para renomear colunas existentes: - -```sql -SELECT - product_id AS id_produto, - description AS descricao, - price AS preco_sem_taxa -FROM products; -``` - -### Fluxo lógico de uma consulta com filtros e expressões - -Podemos representar o fluxo de uma consulta típica assim: - -```mermaid -flowchart TD - A[Tabela base
products] --> B[SELECT colunas
product_id, description, price] - B --> C[Filtrar linhas
WHERE price > 50] - C --> D[Calcular expressões
price * 1.07 AS tax_price] - D --> E[Resultado final
tabela filtrada com coluna calculada] -``` - -Na prática: - -```sql -SELECT - product_id, - description, - price, - price * 1.07 AS tax_price -FROM products -WHERE price > 50.00; -``` - -## Uso Prático - -### 1. SELECT básico vs SELECT com colunas específicas - -Para ver todas as colunas e todas as linhas de uma tabela: - -```sql -SELECT * -FROM customer; -``` - -Para ver apenas algumas colunas (por exemplo, id e nome do cliente): - -```sql -SELECT - customer_id, - name -FROM customer; -``` - -### 2. Filtrando registros com WHERE e operadores de comparação - -Exemplo: clientes com idade entre 25 e 40 anos: - -```sql -SELECT - nome, - idade, - uf -FROM clientes -WHERE idade >= 25 - AND idade <= 40; -``` - -Exemplo: produtos com preço maior que R$ 100,00: - -```sql -SELECT - product_id, - description, - price -FROM products -WHERE price > 100.00; -``` - -### 3. Combinando condições com AND, OR e IN - -Clientes com idade maior que 30 **e** do estado 'SP': - -```sql -SELECT - nome, - idade, - uf -FROM clientes -WHERE idade > 30 - AND uf = 'SP'; -``` - -Clientes de 'SP' **ou** 'RJ': - -```sql -SELECT - nome, - idade, - uf -FROM clientes -WHERE uf = 'SP' - OR uf = 'RJ'; -``` - -Mesma consulta usando `IN`: - -```sql -SELECT - nome, - idade, - uf -FROM clientes -WHERE uf IN ('SP', 'RJ'); -``` - -### 4. Criando colunas calculadas com expressões - -Exemplo: calcular preço com taxa de 7%: - -```sql -SELECT - product_id, - description, - price AS base_price, - price * 1.07 AS tax_price -FROM products; -``` - -Exemplo: calcular apenas o valor da taxa (sem o preço original): - -```sql -SELECT - product_id, - description, - price, - price * 0.07 AS tax_only -FROM products; -``` - -Usar `1.07` preserva o valor original e adiciona 7%; usar `0.07` mostra apenas a parte correspondente à taxa. - -### 5. Usando funções em expressões - -Você pode combinar funções com expressões. -Por exemplo, arredondar o preço com taxa para duas casas decimais: - -```sql -SELECT - product_id, - description, - price, - ROUND(price * 1.07, 2) AS tax_price_rounded -FROM products; -``` - -O comportamento exato de `ROUND` pode variar um pouco entre bancos, mas a ideia é a mesma: ajustar o número de casas decimais do resultado. - -## Erros Comuns - -- **Esquecer de limitar o escopo do WHERE** - - Problema: usar apenas `OR` sem parênteses e obter mais registros do que deveria. - - Correção: usar `AND`/`OR` com cuidado e, quando necessário, parênteses para deixar a lógica explícita. - -- **Confundir `=` com `IN`** - - Problema: escrever várias condições repetidas com `OR` para o mesmo campo. - - Correção: preferir `IN` quando a intenção é “estar em uma lista de valores”. - -- **Achar que expressões criam colunas permanentes** - - Problema: esperar encontrar a coluna calculada na definição da tabela depois do SELECT. - - Correção: lembrar que expressões alteram apenas o resultado da consulta; para persistir o valor, seria preciso um `UPDATE` ou uma coluna calculada definida no esquema. - -- **Misturar tipos em comparações** - - Problema: comparar textos com números ou datas armazenadas como texto sem conversão. - - Correção: garantir que os tipos de dados são compatíveis ou aplicar conversão adequada antes de comparar. - -## Visão Geral de Debugging - -Quando uma consulta com WHERE e expressões não retorna o que você espera: - -- **1. Teste partes menores da condição** - - Execute consultas separadas com cada condição (`WHERE idade > 30`, depois `WHERE uf = 'SP'`) para entender qual parte está filtrando demais ou de menos. - -- **2. Remova temporariamente o WHERE** - - Veja alguns registros sem filtro para confirmar que os dados estão no formato esperado. - -- **3. Teste a expressão isoladamente** - - Por exemplo, `SELECT price, price * 1.07 FROM products` para verificar se o cálculo faz sentido. - -- **4. Use aliases descritivos** - - Dar nomes claros às colunas calculadas ajuda a evitar confusão ao ler resultados e interpretar erros. - -## Principais Pontos - -- Operadores de comparação e lógicos em WHERE transformam SELECT em uma ferramenta poderosa de filtro. -- `AND`, `OR` e `IN` permitem combinar condições de forma expressiva e legível. -- Expressões em SELECT criam colunas calculadas apenas no resultado da consulta, sem alterar a tabela. -- Aliases tornam os cabeçalhos mais claros e ajudam na leitura de resultados, especialmente quando há cálculos. - -## Preparação para Prática - -Depois desta lição, você deve ser capaz de: - -- Escrever consultas SELECT que filtram registros com base em múltiplas condições combinadas. -- Criar colunas calculadas para aplicar porcentagens, somas, diferenças e outras transformações simples. -- Usar aliases para tornar os resultados mais legíveis. -- Depurar consultas verificando passo a passo as condições e expressões. - -No Laboratório de Prática, você irá: - -- Montar consultas em cima de tabelas simples (clientes, produtos) usando operadores e expressões. -- Traduzir regras de negócio verbais (“clientes de SP com idade maior que 30”) em WHEREs corretos. -- Explorar a diferença entre “preço com taxa” e “somente taxa”. - -## Laboratório de Prática - -### Easy — Filtrando clientes por idade e estado - -Você tem uma tabela `clientes` com colunas `nome`, `idade` e `uf`. -Escreva uma consulta que retorne apenas os clientes: - -- Com idade maior ou igual a 25. -- Que moram em 'SP' ou 'RJ'. - -```sql --- TODO: completar a consulta com as condições corretas -SELECT - nome, - idade, - uf -FROM clientes -WHERE idade >= 25 - AND uf IN ('SP', 'RJ'); -``` - -Teste variações trocando os valores numéricos e os estados para ver o efeito no resultado. - -### Medium — Calculando preço com taxa e somente taxa - -Você tem uma tabela `products` com colunas `product_id`, `description` e `price`. -Escreva uma consulta que traga: - -- O identificador e a descrição. -- O preço original. -- O preço com 15% de taxa. -- Apenas o valor da taxa. - -```sql --- TODO: completar os cálculos com multiplicadores adequados -SELECT - product_id, - description, - price AS base_price, - ROUND(price * 1.15, 2) AS price_with_tax, - ROUND(price * 0.15, 2) AS tax_only -FROM products; -``` - -Experimente alterar a taxa (por exemplo, 7%, 20%) para perceber o impacto na expressão. - -### Hard — Combinar múltiplos filtros e expressões em uma única consulta - -Você tem uma tabela `pedidos` com colunas: - -- `id`. -- `cliente_id`. -- `valor_total`. -- `status` (por exemplo, 'ABERTO', 'PAGO', 'CANCELADO'). -- `criado_em` (timestamp). - -Escreva uma consulta que: - -1. Traga apenas pedidos com `status = 'PAGO'`. -2. Filtre para pedidos com `valor_total` maior que 100. -3. Calcule uma coluna `valor_com_desconto` com 5% de desconto sobre `valor_total`. -4. Mostre os resultados ordenados do maior para o menor `valor_total`. - -```sql --- TODO: combinar filtros e expressão na mesma consulta -SELECT - id, - cliente_id, - valor_total, - ROUND(valor_total * 0.95, 2) AS valor_com_desconto, - criado_em -FROM pedidos -WHERE status = 'PAGO' - AND valor_total > 100.00 -ORDER BY - valor_total DESC; -``` - -Pense em como essa consulta poderia ser usada para alimentar um relatório ou dashboard de faturamento. - - - - - diff --git a/content/visualizacao-sql/aula-11-criando-tabelas-sqlite-dml-basica.md b/content/visualizacao-sql/aula-11-criando-tabelas-sqlite-dml-basica.md deleted file mode 100644 index f2f0489..0000000 --- a/content/visualizacao-sql/aula-11-criando-tabelas-sqlite-dml-basica.md +++ /dev/null @@ -1,426 +0,0 @@ ---- -title: "Criando tabelas no SQLiteStudio e DML básica" -slug: "criando-tabelas-sqlite-dml-basica" -discipline: "visualizacao-sql" -order: 11 -description: "Criando bancos de dados e tabelas no SQLiteStudio e usando comandos INSERT e SELECT em dados de exemplo." -reading_time: 35 -difficulty: "medium" -concepts: - - criacao-de-bancos-e-tabelas - - tipos-de-dados-no-sqlite - - comandos-insert - - select-em-tabelas-de-exemplo - - uso-pratico-do-sqlitestudio -prerequisites: - - sql-operadores-logicos-expressoes-select -learning_objectives: - - "Criar um banco de dados SQLite e conectá-lo no SQLiteStudio." - - "Definir uma tabela com colunas e tipos adequados em SQL." - - "Inserir linhas de exemplo em uma tabela usando comandos INSERT." - - "Executar consultas SELECT básicas em tabelas recém-criadas." -exercises: - - question: "Qual é a diferença entre remover um banco de dados da lista do SQLiteStudio e apagar o arquivo físico do banco no sistema operacional?" - answer: "Remover da lista do SQLiteStudio apenas tira o banco da visualização da ferramenta; o arquivo físico `.db` continua existindo na pasta. Apagar o arquivo físico remove de fato o banco de dados do sistema de arquivos." - hint: "Relembre a explicação sobre remover o database da árvore à esquerda sem excluir o arquivo." - - question: "Por que faz sentido usar `VARCHAR(40)` para o primeiro nome em uma tabela de Instagram?" - answer: "Porque nomes variam de tamanho, mas raramente passam de 40 caracteres; `VARCHAR(40)` permite armazenar nomes curtos e longos de forma flexível, limitando o tamanho máximo para evitar desperdício ou entradas absurdas." - hint: "Pense na diferença entre nomes curtos e nomes compostos longos." - - question: "Qual é o fluxo geral para criar e popular uma tabela nova no SQLiteStudio?" - answer: "Criar/conectar um banco de dados, definir a tabela com `CREATE TABLE`, inserir dados com `INSERT INTO ... VALUES (...)` e, por fim, consultar com `SELECT` para verificar o conteúdo." - hint: "Relembre os passos mostrados na aula para a tabela `instagram`." -review_after_days: - - 3 - - 10 ---- - -## Visão Geral do Conceito - -Esta lição mostra como **sair do zero** em um banco de dados SQLite: criar um arquivo de banco, conectá-lo no **SQLiteStudio**, definir uma tabela com colunas e tipos adequados e populá-la com dados de exemplo usando **INSERT**. -A aula se apoia em exemplos concretos como as tabelas `sportStars` e `instagram`, aproximando o uso de SQL de cenários reais (celebridades, seguidores, perfis de redes sociais). - -O objetivo é que você se sinta confortável em abrir o SQLiteStudio, criar seu próprio banco e escrever os primeiros comandos de **DDL** (Data Definition Language, como `CREATE TABLE`) e **DML** (Data Manipulation Language, como `INSERT` e `SELECT`). - -## Modelo Mental - -Pense no SQLiteStudio como um **editor visual de bancos de dados**: - -- O **arquivo `.db`** é como um “caderno de dados” guardado numa pasta do sistema operacional. -- O **SQLiteStudio** é a ferramenta que abre esse caderno, mostra as tabelas e permite criar novas páginas (tabelas) e linhas (registros). -- Os comandos SQL (`CREATE TABLE`, `INSERT`, `SELECT`) são as “anotações estruturadas” que você faz nesse caderno. - -Quando você “remove” um banco da lista do SQLiteStudio, você **fecha o caderno na ferramenta**, mas o arquivo físico continua na estante (pasta). -Só quando você apaga o arquivo `.db` no sistema de arquivos é que o caderno realmente deixa de existir. - -## Mecânica Central - -### Criando e conectando um banco de dados no SQLiteStudio - -Passos gerais mostrados na aula: - -1. Abrir o **SQLiteStudio**. -2. Usar o menu **Database → Add a database** (ou equivalente em português). -3. Na janela de configuração do banco: - - Escolher a **pasta** onde o arquivo `.db` será criado. - - Definir um **nome de arquivo** (por exemplo, `aula6b.db`). - - Clicar em **Test connection** para verificar se tudo está correto. - - Confirmar em **OK**. - -Depois disso: - -- O novo banco aparece na lista à esquerda. -- Com um **duplo clique**, ele é “ativado” (fica em destaque) e as seções de **Tables** e **Views** ficam disponíveis. - -Se você remover o banco da lista (opção “Remover banco de dados” com botão direito), o arquivo `.db` permanece na pasta e pode ser re-adicionado depois. - -### Criando uma tabela de exemplo (instagram) - -Exemplo de tabela criada na aula: - -- Nome da tabela: `instagram`. -- Colunas: - - `firstName` — nome (primeiro nome). - - `surname` — sobrenome. - - `career` — carreira (por exemplo, 'Footballer', 'Singer'). - - `followersMillions` — número de seguidores em milhões (inteiro). - - `instagramHandle` — usuário/perfil no Instagram (por exemplo, `neymarjr`, `leomessi`). - -Uma definição em SQL poderia ser: - -```sql -CREATE TABLE instagram ( - firstName VARCHAR(40), - surname VARCHAR(40), - career VARCHAR(20), - followersMillions INT, - instagramHandle VARCHAR(30) -); -``` - -Aqui: - -- `VARCHAR` é usado para textos de tamanho variável, com um limite máximo. -- `INT` é usado para o número de seguidores em milhões (valor inteiro). - -### Inserindo dados de exemplo com INSERT - -Depois de criar a tabela, você pode popular com alguns registros: - -```sql -INSERT INTO instagram VALUES ('National', 'Geographic', 'Magazine', 145, 'natgeo'); -INSERT INTO instagram VALUES ('Neymar', 'da Silva Santos, Jr', 'Footballer', 142, 'neymarjr'); -INSERT INTO instagram VALUES ('Leo', 'Messi', 'Footballer', 167, 'leomessi'); -INSERT INTO instagram VALUES ('Beyonce', 'Giselle Knowles', 'Singer', 155, 'beyonce'); -INSERT INTO instagram VALUES ('Selena', 'Gomez', 'Singer', 194, 'selenagomez'); -INSERT INTO instagram VALUES ('Kim', 'Kardashian', 'Media Personality', 189, 'kimkardashian'); -INSERT INTO instagram VALUES ('Dwayne', 'Johnson', 'Actor', 200, 'therock'); -INSERT INTO instagram VALUES ('Ariana', 'Grande', 'Singer', 203, 'arianagrande'); -INSERT INTO instagram VALUES ('Cristiano', 'Ronaldo', 'Footballer', 239, 'cristiano'); -INSERT INTO instagram VALUES ('Instagram', '', 'Social media platform', 371, 'instagram'); -``` - -Esses comandos são típicos da parte DML (manipulação de dados): cada `INSERT` adiciona uma nova linha à tabela. - -### Consultando dados com SELECT - -Para ver os registros inseridos: - -```sql -SELECT * -FROM instagram; -``` - -Para ver apenas algumas colunas: - -```sql -SELECT - firstName, - surname, - followersMillions -FROM instagram; -``` - -Para aplicar filtros, você pode combinar com o que aprendeu na lição anterior (WHERE, operadores, etc.): - -```sql -SELECT - firstName, - surname, - followersMillions -FROM instagram -WHERE followersMillions > 200; -``` - -### Fluxo de trabalho no SQLiteStudio - -O fluxo básico que a aula ilustra pode ser representado assim: - -```mermaid -flowchart TD - A[Escolher pasta
e nome do arquivo .db] --> B[Criar banco no SQLiteStudio] - B --> C[Criar tabela com CREATE TABLE] - C --> D[Inserir dados com INSERT INTO] - D --> E[Consultar com SELECT] -``` - -Esse ciclo se repete toda vez que você precisa testar um novo modelo de tabela ou exercício de SQL. - -## Uso Prático - -### 1. Criando um banco de dados de aula - -Em um contexto de aula, faz sentido criar arquivos separados por semana ou tema, como: - -- `aula6a.db`, `aula6b.db`, etc. - -Passos: - -1. No SQLiteStudio, remova da lista bancos antigos se quiser limpar a visualização (sem apagar os arquivos). -2. Use **Add a database** para criar `aula11.db` (por exemplo). -3. Ative o banco com duplo clique. - -### 2. Criando uma tabela de celebridades esportivas - -Além de `instagram`, a aula menciona a tabela `sportStars`. -Uma definição possível: - -```sql -CREATE TABLE sportStars ( - firstName VARCHAR(40), - surname VARCHAR(40), - monthBorn VARCHAR(20), - yearOfBirth INT, - sport VARCHAR(30) -); -``` - -Você pode então inserir registros com `INSERT` e praticar `SELECT` filtrando por mês de nascimento, ano ou tipo de esporte. - -### 3. Combinando criação de tabela, INSERT e SELECT - -Um exemplo completo: - -```sql --- Criar tabela -CREATE TABLE exemplo ( - id INT, - nome VARCHAR(100), - valor NUMERIC(10,2) -); - --- Inserir alguns registros -INSERT INTO exemplo VALUES (1, 'Item A', 100.00); -INSERT INTO exemplo VALUES (2, 'Item B', 250.50); -INSERT INTO exemplo VALUES (3, 'Item C', 75.25); - --- Consultar dados -SELECT * -FROM exemplo; -``` - -Esse padrão se repete com qualquer tabela que você criar. - -## Erros Comuns - -- **Confundir remoção da lista com exclusão do arquivo** - - Problema: achar que ao remover o banco no SQLiteStudio, o arquivo `.db` foi apagado. - - Correção: lembrar que remover da lista só afeta a ferramenta; o arquivo físico precisa ser apagado manualmente se você quiser removê-lo de fato. - -- **Definir tipos de dados muito restritivos ou muito largos** - - Problema: usar `VARCHAR(5)` para nomes ou `VARCHAR(255)` sem necessidade em todos os campos. - - Correção: escolher limites razoáveis de acordo com o domínio (por exemplo, 40 para nomes, 30 para handles de redes sociais). - -- **Esquecer de ativar o banco antes de criar tabelas** - - Problema: criar tabelas no banco errado por não ter dado duplo clique no banco correto. - - Correção: sempre confirmar qual banco está ativo na barra e na árvore de bancos. - -- **Não testar SELECT após INSERT** - - Problema: inserir dados sem conferir se realmente foram gravados como esperado. - - Correção: executar `SELECT *` após um conjunto de `INSERTs` para validar o conteúdo. - -## Visão Geral de Debugging - -Quando algo não funciona ao criar ou popular tabelas: - -- **1. Verifique se está no banco correto** - - Confirme qual arquivo `.db` está ativo no SQLiteStudio. - -- **2. Revise o comando `CREATE TABLE`** - - Verifique se os nomes de colunas não têm erros de digitação. - - Confirme se os tipos de dados são suportados pelo SQLite. - -- **3. Confira os `INSERTs`** - - Veja se a quantidade de valores em `VALUES (...)` bate com a quantidade de colunas da tabela. - - Verifique se as aspas e vírgulas estão corretas. - -- **4. Use `SELECT *` para validar** - - Se os dados não aparecem, é possível que o `INSERT` não tenha sido executado ou tenha falhado com erro. - -## Principais Pontos - -- O SQLiteStudio facilita criar e gerenciar bancos SQLite por meio de uma interface gráfica. -- Criar um banco envolve escolher uma pasta, um nome de arquivo e testar a conexão. -- Definir uma tabela exige escolher nomes de colunas e tipos de dados coerentes. -- `INSERT` e `SELECT` são os primeiros comandos DML que você usa para popular e explorar as tabelas recém-criadas. - -## Preparação para Prática - -Após esta lição, você deve ser capaz de: - -- Abrir o SQLiteStudio e criar um novo arquivo de banco de dados. -- Definir uma tabela simples com colunas adequadas ao domínio. -- Inserir dados de exemplo e conferir com SELECT. -- Repetir esse fluxo sempre que precisar testar um novo modelo ou exercício de SQL. - -No Laboratório de Prática, você irá: - -- Modelar e criar uma tabela inspirada em redes sociais. -- Inserir registros manualmente. -- Rodar consultas básicas para explorar os dados. - -## Laboratório de Prática - -### Easy — Criando tabela de instagram no SQLite - -Modele e crie a tabela `instagram` no seu banco SQLite com as mesmas colunas usadas na aula: - -- `firstName` (`VARCHAR(40)`) -- `surname` (`VARCHAR(40)`) -- `career` (`VARCHAR(20)`) -- `followersMillions` (`INT`) -- `instagramHandle` (`VARCHAR(30)`) - -```sql --- TODO: criar a tabela instagram conforme o modelo descrito -CREATE TABLE instagram ( - firstName VARCHAR(40), - surname VARCHAR(40), - career VARCHAR(20), - followersMillions INT, - instagramHandle VARCHAR(30) -); -``` - -Depois de criar, use `SELECT * FROM instagram;` para confirmar que a tabela existe (mesmo vazia). - -### Medium — Inserindo celebridades e consultando seguidores - -Insira pelo menos 5 registros na tabela `instagram` e escreva uma consulta que retorne apenas quem tem mais de 150 milhões de seguidores. - -```sql --- TODO: inserir registros de exemplo -INSERT INTO instagram VALUES ('Neymar', 'da Silva Santos, Jr', 'Footballer', 142, 'neymarjr'); -INSERT INTO instagram VALUES ('Leo', 'Messi', 'Footballer', 167, 'leomessi'); -INSERT INTO instagram VALUES ('Cristiano', 'Ronaldo', 'Footballer', 239, 'cristiano'); -INSERT INTO instagram VALUES ('Ariana', 'Grande', 'Singer', 203, 'arianagrande'); -INSERT INTO instagram VALUES ('Instagram', '', 'Social media platform', 371, 'instagram'); - --- TODO: consultar apenas quem tem mais de 150M de seguidores -SELECT - firstName, - surname, - career, - followersMillions, - instagramHandle -FROM instagram -WHERE followersMillions > 150; -``` - -Altere os valores e o limite para observar como o resultado muda. - -### Hard — Criando e populando uma nova tabela de rede social - -Modele uma tabela `socialProfiles` com colunas: - -- `id` (inteiro). -- `platform` (por exemplo, 'Instagram', 'Twitter', 'TikTok'). -- `handle` (nome de usuário). -- `followers` (inteiro com número absoluto de seguidores). -- `created_at` (timestamp). - -Crie a tabela e insira alguns registros, depois escreva uma consulta que retorna apenas perfis: - -- Da plataforma 'Instagram'. -- Com mais de 1.000.000 de seguidores. - -```sql --- TODO: criar a tabela socialProfiles -CREATE TABLE socialProfiles ( - id INT, - platform VARCHAR(20), - handle VARCHAR(50), - followers INT, - created_at TIMESTAMP -); - --- TODO: inserir alguns registros de exemplo -INSERT INTO socialProfiles VALUES (1, 'Instagram', 'exemplo1', 1500000, CURRENT_TIMESTAMP); -INSERT INTO socialProfiles VALUES (2, 'Twitter', 'exemplo2', 500000, CURRENT_TIMESTAMP); -INSERT INTO socialProfiles VALUES (3, 'Instagram', 'exemplo3', 2500000, CURRENT_TIMESTAMP); - --- TODO: consultar apenas perfis de Instagram com mais de 1M de seguidores -SELECT - id, - platform, - handle, - followers, - created_at -FROM socialProfiles -WHERE platform = 'Instagram' - AND followers > 1000000; -``` - -Pense em como essa tabela poderia ser usada em dashboards de acompanhamento de redes sociais. - - - - - diff --git a/content/visualizacao-sql/aula-12-sql-order-by-ordenacao-resultados.md b/content/visualizacao-sql/aula-12-sql-order-by-ordenacao-resultados.md deleted file mode 100644 index d995c2c..0000000 --- a/content/visualizacao-sql/aula-12-sql-order-by-ordenacao-resultados.md +++ /dev/null @@ -1,455 +0,0 @@ ---- -title: "Ordenando resultados em SQL com ORDER BY" -slug: "sql-order-by-ordenacao-resultados" -discipline: "visualizacao-sql" -order: 12 -description: "Usando ORDER BY para ordenar resultados de consultas SQL por uma ou mais colunas, em ordem ascendente ou descendente." -reading_time: 30 -difficulty: "medium" -concepts: - - order-by - - ordenacao-asc-e-desc - - ordenacao-por-multiplas-colunas - - ordenacao-por-texto-numero-e-data - - boas-praticas-de-ordenacao-em-consultas -prerequisites: - - sql-operadores-logicos-expressoes-select -learning_objectives: - - "Usar ORDER BY para ordenar resultados de consultas SQL por uma ou mais colunas." - - "Controlar se a ordenação é ascendente (ASC) ou descendente (DESC)." - - "Ordenar corretamente por colunas numéricas, textuais e de data." - - "Combinar ORDER BY com filtros em WHERE para construir consultas mais úteis." -exercises: - - question: "O que acontece se você não especificar ASC ou DESC em um ORDER BY?" - answer: "A maioria dos bancos de dados assume ordenação ascendente (ASC) por padrão, ou seja, do menor para o maior ou de A até Z." - hint: "Relembre a explicação de que o comportamento padrão é ascendente." - - question: "Por que ORDER BY não é automático quando você faz um SELECT sem essa cláusula?" - answer: "Porque o banco de dados prioriza eficiência e não garante uma ordem específica sem que você peça; a ordem física ou interna pode variar, então é necessário usar ORDER BY quando a ordem importa." - hint: "Pense nos exemplos de e-mail, contatos e extrato bancário." - - question: "Como ORDER BY funciona quando você passa duas colunas, como `ORDER BY age ASC, fullName ASC`?" - answer: "Ele ordena primeiro por `age`; se houver empates na idade, usa `fullName` como critério de desempate para definir a ordem dentro de cada grupo de mesma idade." - hint: "Relembre o exemplo das celebridades com mesma idade." -review_after_days: - - 3 - - 10 ---- - -## Visão Geral do Conceito - -Esta lição aprofunda o uso de **ORDER BY** em SQL, mostrando como ordenar resultados de consultas por uma ou mais colunas, em **ordem ascendente ou descendente**. -Embora muitas interfaces de usuário mostrem listas naturalmente ordenadas (e-mails, contatos, extratos bancários), nos bancos de dados **essa ordenação não é automática**: você precisa pedi-la explicitamente com ORDER BY. - -Trabalharemos com exemplos de tabelas de celebridades (`celebrities`), demonstrando como ordenar por idade, nacionalidade e nome completo, incluindo casos em que é necessário usar **mais de uma coluna** como critério. - -## Modelo Mental - -Pense em ORDER BY como a etapa de **“classificação”** de uma planilha depois que você já filtrou os dados: - -- O **SELECT** escolhe quais colunas e linhas você quer ver. -- O **WHERE** decide **quais linhas entram** no resultado (filtros). -- O **ORDER BY** decide **em que ordem** essas linhas serão apresentadas (critério de ordenação). - -Sem ORDER BY, o banco devolve os registros na ordem que for mais conveniente internamente — não há garantia de estar “bonito” nem previsível. -Com ORDER BY, você garante que os dados sigam uma lógica clara: do menor para o maior, de A até Z ou o inverso. - -## Mecânica Central - -### Sintaxe básica de ORDER BY - -A forma geral é: - -```sql -SELECT colunas -FROM tabela -WHERE condicao_opcional -ORDER BY coluna1 [ASC | DESC], coluna2 [ASC | DESC], ...; -``` - -Onde: - -- `ASC` significa **ascendente** (padrão se omitido). -- `DESC` significa **descendente**. - -Se você não informar `ASC` ou `DESC`, o banco assume normalmente `ASC`. - -### Ordenação ascendente e descendente - -Para uma tabela `celebrities` com uma coluna `age`: - -```sql --- Idades em ordem ascendente (padrão) -SELECT * -FROM celebrities -ORDER BY age; - --- Idades em ordem descendente -SELECT * -FROM celebrities -ORDER BY age DESC; -``` - -Em ordem ascendente: - -- Números: do menor para o maior (ex.: 24, 29, 35, 48, 63). -- Texto: de A até Z (conforme a ordem alfabética). -- Datas: da mais antiga para a mais recente (dependendo do tipo e do formato). - -Em ordem descendente, a lógica se inverte. - -### Ordenação por texto, número e data - -ORDER BY funciona igualmente para diferentes tipos de coluna: - -- **Números** (`INT`, `NUMERIC`): ordena pelo valor numérico. -- **Texto** (`VARCHAR`, `TEXT`): ordena pela ordem alfabética (como em um dicionário). -- **Datas** (`DATE`, `TIMESTAMP`): ordena cronologicamente. - -Exemplos: - -```sql --- Ordenar celebridades por nome completo, A-Z -SELECT * -FROM celebrities -ORDER BY fullName ASC; - --- Ordenar transações bancárias da mais recente para a mais antiga -SELECT * -FROM transacoes -ORDER BY dataTransacao DESC; -``` - -### Ordenação por múltiplas colunas - -Você pode passar mais de uma coluna em ORDER BY. -Isso é útil quando o primeiro critério empata para algumas linhas. - -Exemplo com a tabela `celebrities`: - -```sql --- Ordenar primeiro por idade (crescente), depois por nome completo (crescente) -SELECT * -FROM celebrities -ORDER BY age ASC, fullName ASC; -``` - -Aqui: - -- Primeiro, o resultado é agrupado por idade (24, depois 29, depois 35, etc.). -- Dentro de cada idade, os registros são ordenados alfabeticamente por `fullName`. - -Isso é exatamente o comportamento descrito no exemplo em que duas celebridades têm idade 35: o desempate vem pelo nome completo (`Gal Gadot` antes de `Scarlett Johansson`). - -### Fluxo lógico de SELECT + WHERE + ORDER BY - -Podemos representar a sequência de operações assim: - -```mermaid -flowchart TD - A[Tabela base
celebrities] --> B[Filtrar com WHERE
(opcional)] - B --> C[Selecionar colunas com SELECT] - C --> D[Ordenar linhas com ORDER BY] - D --> E[Resultado final
ordenado conforme criterio] -``` - -Mesmo que você escreva ORDER BY no fim, o banco primeiro: - -1. Aplica o WHERE (quando presente). -2. Monta o conjunto de linhas do SELECT. -3. Ordena o conjunto final segundo os critérios do ORDER BY. - -## Uso Prático - -### 1. Ordenando por uma única coluna numérica (idade) - -Exemplo com a tabela `celebrities`: - -```sql --- Todas as colunas, ordenadas por idade (crescente) -SELECT * -FROM celebrities -ORDER BY age ASC; -``` - -Resultado (esquemático): - -- 24 -- 29 -- 35 -- 35 -- 48 -- 63 - -Troque para descendente: - -```sql -SELECT * -FROM celebrities -ORDER BY age DESC; -``` - -E observe que a sequência se inverte. - -### 2. Ordenando por texto (nacionalidade, nome completo) - -Ordenando por `nationality` em ordem descendente: - -```sql -SELECT * -FROM celebrities -ORDER BY nationality DESC; -``` - -- Primeiro vêm as nacionalidades mais “altas” na ordem alfabética (por exemplo, valores iniciados com letras mais próximas de Z). -- Em seguida, as demais, até chegar às primeiras letras (como A). - -Ordenando por `fullName` em ordem ascendente: - -```sql -SELECT * -FROM celebrities -ORDER BY fullName ASC; -``` - -O comportamento é o mesmo de uma lista de nomes em um catálogo ou agenda telefônica. - -### 3. Ordenando por múltiplas colunas (idade e nome) - -Para aplicar o desempate por nome quando a idade empata: - -```sql -SELECT * -FROM celebrities -ORDER BY age ASC, fullName ASC; -``` - -Passo a passo: - -1. Ordena por `age`. -2. Dentro de cada idade (`35`, por exemplo), ordena por `fullName`. - -Você pode misturar direções: - -```sql --- Idade crescente, e dentro da idade, nome decrescente -SELECT * -FROM celebrities -ORDER BY age ASC, fullName DESC; -``` - -### 4. Combinando ORDER BY com WHERE - -Você raramente usa ORDER BY isolado; normalmente ele é combinado com filtros: - -```sql --- Celebridades com mais de 30 anos, ordenadas por idade e nome -SELECT - fullName, - age, - nationality -FROM celebrities -WHERE age > 30 -ORDER BY age ASC, fullName ASC; -``` - -Outro exemplo com transações bancárias: - -```sql -SELECT - dataTransacao, - descricao, - valor -FROM transacoes -WHERE valor < 0 -- apenas saídas -ORDER BY dataTransacao DESC; -``` - -Aqui, ORDER BY garante que os lançamentos mais recentes apareçam primeiro, como em um extrato bancário típico. - -## Erros Comuns - -- **Assumir ordem “natural” sem ORDER BY** - - Problema: confiar que o banco sempre mostrará os dados na mesma ordem (por exemplo, inserção), o que **não é garantido**. - - Correção: sempre usar ORDER BY quando a ordem faz diferença para leitura ou lógica de negócio. - -- **Esquecer o sentido (ASC vs DESC)** - - Problema: esperar ver “mais recentes primeiro” mas usar o padrão ascendente. - - Correção: explicitar `DESC` quando quiser dados do maior para o menor (datas mais novas, valores maiores, etc.). - -- **Misturar tipos incompatíveis** - - Problema: ordenar datas armazenadas como texto em formatos inconsistentes, gerando ordem “alfabética” errada. - - Correção: garantir tipos adequados (`DATE`, `TIMESTAMP`) ou normalizar os formatos antes de ordenar. - -- **Não aproveitar ordenação por múltiplas colunas** - - Problema: ordenar só por um campo e ter resultados “embaralhados” quando há muitos empates. - - Correção: adicionar colunas de desempate, como nome, cidade, ID, conforme a necessidade. - -## Visão Geral de Debugging - -Quando a ordenação parece estranha: - -- **1. Verifique o tipo de dado** - - Confirme se a coluna usada em ORDER BY é numérica, textual ou de data adequada. - - Se for texto e deveria ser data, considere converter. - -- **2. Teste sem WHERE** - - Execute um `SELECT ... ORDER BY ...` sem filtros para ver a ordenação bruta e compará-la com sua expectativa. - -- **3. Adicione colunas auxiliares** - - Inclua colunas como `id`, `created_at` ou `fullName` no SELECT para entender melhor como a ordenação está sendo aplicada. - -- **4. Revise múltiplos critérios** - - Verifique se a ordem das colunas em ORDER BY reflete a prioridade correta (primeiro critério principal, depois desempates). - -## Principais Pontos - -- ORDER BY é a ferramenta central para controlar a **ordem de exibição** de resultados em SQL. -- A ordem padrão é ascendente; use DESC para inverter. -- Você pode ordenar por múltiplas colunas, definindo critérios de desempate. -- Combinar ORDER BY com WHERE produz consultas que trazem **apenas** os dados relevantes e já **organizados** para leitura. - -## Preparação para Prática - -Depois desta lição, você deve ser capaz de: - -- Escrever consultas com ORDER BY simples e múltiplo (duas ou mais colunas). -- Escolher corretamente entre ASC e DESC conforme o contexto (alfabético, numérico, data). -- Depurar resultados de ordenação que não batem com sua expectativa. -- Aplicar esses conceitos em cenários reais, como listas de contatos, extratos, relatórios de vendas e dashboards. - -No Laboratório de Prática, você irá: - -- Praticar ORDER BY em tabelas de exemplo (celebridades, transações). -- Explorar combinações de critérios de ordenação. -- Preparar consultas pensadas para alimentar relatórios e dashboards. - -## Laboratório de Prática - -### Easy — Ordenando celebridades por idade e nome - -Considere a tabela `celebrities` com colunas `fullName`, `age` e `nationality`. -Escreva uma consulta que retorne todas as colunas, ordenando: - -- Primeiro por `age` em ordem crescente. -- Dentro de cada idade, por `fullName` em ordem crescente. - -```sql --- TODO: completar com ORDER BY adequado -SELECT - fullName, - age, - nationality -FROM celebrities -ORDER BY - age ASC, - fullName ASC; -``` - -Teste variando a direção (por exemplo, `fullName DESC`) para observar a diferença. - -### Medium — Ordenando extrato bancário por data e valor - -Suponha uma tabela `extrato` com colunas `data`, `descricao` e `valor`. -Escreva uma consulta que: - -- Mostre apenas lançamentos negativos (`valor < 0`). -- Ordene os resultados da **data mais recente para a mais antiga**. - -```sql --- TODO: combinar WHERE com ORDER BY -SELECT - data, - descricao, - valor -FROM extrato -WHERE valor < 0 -ORDER BY - data DESC; -``` - -Pense em como isso melhora a leitura em comparação com uma consulta sem ORDER BY. - -### Hard — Ordenação composta em relatório de clientes - -Considere uma tabela `clientes` com colunas: - -- `id`. -- `nomeCompleto`. -- `idade`. -- `uf`. - -Escreva uma consulta que: - -1. Traga apenas clientes com `idade >= 18`. -2. Ordene primeiro por `uf` em ordem ascendente. -3. Dentro de cada `uf`, ordene por `idade` em ordem descendente (mais velhos primeiro). -4. Em caso de empate de idade, ordene por `nomeCompleto` em ordem ascendente. - -```sql --- TODO: montar ORDER BY com tres criterios -SELECT - id, - nomeCompleto, - idade, - uf -FROM clientes -WHERE idade >= 18 -ORDER BY - uf ASC, - idade DESC, - nomeCompleto ASC; -``` - -Reflita sobre como essa ordenação seria útil em um relatório de clientes por estado. - - - - - diff --git a/content/visualizacao-sql/aula-13-consultas-sql-tabela-clientes-pratica.md b/content/visualizacao-sql/aula-13-consultas-sql-tabela-clientes-pratica.md deleted file mode 100644 index 5c19bb3..0000000 --- a/content/visualizacao-sql/aula-13-consultas-sql-tabela-clientes-pratica.md +++ /dev/null @@ -1,448 +0,0 @@ ---- -title: "Consultas SQL na tabela de clientes: prática com filtros e ordenação" -slug: "consultas-sql-tabela-clientes-pratica" -discipline: "visualizacao-sql" -order: 13 -description: "Praticando SELECT, WHERE e ORDER BY em uma tabela de clientes criada no SQLiteStudio." -reading_time: 30 -difficulty: "medium" -concepts: - - select-tabela-clientes - - filtros-com-where - - ordenacao-com-order-by - - pratica-no-sqlitestudio - - analise-de-dados-tabulares-simples -prerequisites: - - criandos-tabelas-sqlite-dml-basica - - sql-order-by-ordenacao-resultados -learning_objectives: - - "Executar consultas SELECT básicas sobre uma tabela de clientes criada no SQLiteStudio." - - "Aplicar filtros com WHERE usando operadores de comparação para selecionar subconjuntos de clientes." - - "Ordenar resultados por idade e UF usando ORDER BY com uma ou mais colunas." - - "Entender o fluxo completo: criar tabela, inserir dados e explorar com consultas." -exercises: - - question: "Qual é a vantagem de testar `SELECT * FROM cliente;` logo após criar a tabela e antes de inserir dados?" - answer: "Permite verificar se a tabela foi criada corretamente (nomes de colunas, tipos, existência) antes de popular com dados, facilitando a detecção precoce de erros de definição." - hint: "Pense na ideia de \"conferir a estrutura\" antes de carregar conteúdo." - - question: "Como você escreveria uma condição WHERE para selecionar apenas clientes com idade maior que 30 e UF igual a 'SP'?" - answer: "Usando `WHERE idade_cliente > 30 AND uf_cliente = 'SP'` (ajustando os nomes das colunas conforme o esquema)." - hint: "Combine operadores de comparação com o operador lógico AND." - - question: "Por que é útil ordenar uma lista de clientes primeiro por UF e depois por idade?" - answer: "Porque agrupa os clientes por estado (UF) e, dentro de cada grupo, mostra dos mais velhos para os mais novos ou vice-versa, facilitando análises regionais e de faixa etária." - hint: "Relembre o exemplo de ordenação composta visto com outras tabelas." -review_after_days: - - 3 - - 10 ---- - -## Visão Geral do Conceito - -Esta lição consolida o que você já aprendeu sobre **criação de tabelas**, **INSERT**, **SELECT**, **WHERE** e **ORDER BY** usando um exemplo simples e didático: a tabela `Cliente` criada no **SQLiteStudio**. -Em vez de focar em novos comandos, o objetivo é **praticar** escrever consultas em cima de um conjunto pequeno de clientes, entendendo melhor filtros e ordenação. - -Partiremos de uma tabela com colunas como código, nome, idade e UF, usando o SQLiteStudio como ambiente para criar a tabela, inserir registros e executar consultas. - -## Modelo Mental - -Pense na tabela `Cliente` como uma **agenda de clientes**: - -- Cada linha é uma pessoa (cliente). -- As colunas representam atributos: código, nome, idade, estado (UF). - -As consultas SQL são diferentes **formas de folhear essa agenda**: - -- `SELECT *` sem filtros: ver toda a agenda. -- `WHERE idade_cliente > 30`: ver apenas quem está acima de uma certa idade. -- `ORDER BY uf_cliente, idade_cliente`: organizar a agenda por estado e idade para facilitar comparações. - -O SQLiteStudio funciona como um “caderno interativo” em que você: - -1. Define a estrutura da agenda (`CREATE TABLE`). -2. Preenche com contatos (`INSERT`). -3. Faz buscas e ordenações (`SELECT` com WHERE e ORDER BY). - -## Mecânica Central - -### Estrutura da tabela Cliente - -A aula cria uma tabela com a seguinte ideia de colunas: - -- `Cod_Cliente` — código do cliente (texto de 3 caracteres). -- `Nome_Cliente` — nome do cliente (texto de até 20 caracteres). -- `Idade_Cliente` — idade (inteiro). -- `UF_Cliente` — unidade federativa (texto de 2 caracteres). - -Um esquema SQL equivalente: - -```sql -CREATE TABLE Cliente ( - Cod_Cliente TEXT(3), - Nome_Cliente TEXT(20), - Idade_Cliente INTEGER, - UF_Cliente TEXT(2) -); -``` - -Depois de criar a tabela, é comum verificar: - -```sql -SELECT * -FROM Cliente; -``` - -No início, o resultado mostra apenas o cabeçalho das colunas, sem linhas, confirmando que a estrutura foi criada corretamente. - -### Inserindo registros na tabela Cliente - -A aula mostra vários comandos `INSERT` para popular a tabela com clientes de diferentes estados e idades. -Um exemplo representativo: - -```sql -INSERT INTO Cliente VALUES ('001', 'Maria Flor', 25, 'MG'); -INSERT INTO Cliente VALUES ('002', 'Alex Alves', 19, 'MG'); -INSERT INTO Cliente VALUES ('003', 'Joana Pinho', 35, 'SP'); -INSERT INTO Cliente VALUES ('004', 'João Batista', 36, 'SP'); -INSERT INTO Cliente VALUES ('005', 'Livia Amaral', 22, 'SP'); -INSERT INTO Cliente VALUES ('006', 'Ivo Paiva', 45, 'ES'); -INSERT INTO Cliente VALUES ('007', 'Tais Souza', 21, 'RJ'); -INSERT INTO Cliente VALUES ('008', 'Celso Costa', 31, 'RJ'); -INSERT INTO Cliente VALUES ('009', 'Renata Neves', 44, 'RJ'); -INSERT INTO Cliente VALUES ('010', 'Omar Silva', 29, 'RJ'); -``` - -Após executar os `INSERTs`, um novo `SELECT * FROM Cliente;` deve mostrar todas as linhas inseridas. - -### Consultas SELECT básicas - -Com a tabela populada, você pode: - -- Ver todas as colunas: - -```sql -SELECT * -FROM Cliente; -``` - -- Selecionar apenas colunas específicas (por exemplo, nome e idade): - -```sql -SELECT - Nome_Cliente, - Idade_Cliente -FROM Cliente; -``` - -- Selecionar UF e nome: - -```sql -SELECT - UF_Cliente, - Nome_Cliente -FROM Cliente; -``` - -### Filtros com WHERE - -Você pode aplicar filtros por idade ou UF, usando operadores de comparação (já estudados): - -```sql --- Clientes com idade maior que 25 -SELECT - Nome_Cliente, - Idade_Cliente -FROM Cliente -WHERE Idade_Cliente > 25; - --- Clientes de SP -SELECT - Nome_Cliente, - UF_Cliente -FROM Cliente -WHERE UF_Cliente = 'SP'; -``` - -Também pode combinar condições: - -```sql --- Clientes com idade maior que 30 E da UF 'RJ' -SELECT - Nome_Cliente, - Idade_Cliente, - UF_Cliente -FROM Cliente -WHERE Idade_Cliente > 30 - AND UF_Cliente = 'RJ'; -``` - -### Ordenação com ORDER BY - -Você pode ordenar resultados por uma ou mais colunas: - -```sql --- Ordenar por UF e, dentro de cada UF, por nome -SELECT - UF_Cliente, - Nome_Cliente, - Idade_Cliente -FROM Cliente -ORDER BY - UF_Cliente ASC, - Nome_Cliente ASC; -``` - -Ou focar em idade: - -```sql --- Clientes ordenados da maior para a menor idade -SELECT - Nome_Cliente, - Idade_Cliente, - UF_Cliente -FROM Cliente -ORDER BY - Idade_Cliente DESC; -``` - -### Fluxo completo no SQLiteStudio - -O fluxo que a aula exercita pode ser representado assim: - -```mermaid -flowchart TD - A[Criar banco aula7.db no SQLiteStudio] --> B[CREATE TABLE Cliente] - B --> C[INSERT INTO Cliente VALUES (...)] - C --> D[SELECT * FROM Cliente] - D --> E[SELECT com WHERE e ORDER BY
filtros por idade e UF] -``` - -Essa sequência é a base da maioria dos exercícios iniciais de SQL: modelagem simples + carga de dados + exploração por consultas. - -## Uso Prático - -### 1. Conferindo a estrutura e o conteúdo - -Depois de criar a tabela `Cliente` e inserir registros: - -```sql -SELECT * -FROM Cliente; -``` - -Permite conferir rapidamente: - -- Se as colunas aparecem com os nomes esperados. -- Se todos os registros foram inseridos. -- Se os tipos parecem coerentes (idades numéricas, UFs com 2 letras, etc.). - -### 2. Produzindo uma “lista de contatos” por UF - -Uma consulta útil: - -```sql -SELECT - UF_Cliente, - Nome_Cliente -FROM Cliente -ORDER BY - UF_Cliente ASC, - Nome_Cliente ASC; -``` - -Gera uma lista agrupada por estado, em que os nomes dentro de cada UF aparecem ordenados alfabeticamente. - -### 3. Encontrando clientes em faixas de idade - -Você pode segmentar clientes por faixa etária: - -```sql --- Clientes com idade entre 20 e 30 anos -SELECT - Nome_Cliente, - Idade_Cliente, - UF_Cliente -FROM Cliente -WHERE Idade_Cliente >= 20 - AND Idade_Cliente <= 30 -ORDER BY - Idade_Cliente ASC; -``` - -Essas consultas são típicas em relatórios de marketing e segmentação de público. - -## Erros Comuns - -- **Esquecer o esquema exato da tabela** - - Problema: usar nomes de colunas errados em WHERE ou ORDER BY. - - Correção: executar `SELECT * FROM Cliente;` ou ver a definição da tabela antes de escrever consultas mais complexas. - -- **Misturar maiúsculas/minúsculas em valores de texto** - - Problema: escrever `WHERE uf_cliente = 'sp'` quando os dados foram inseridos como `'SP'`. - - Correção: padronizar a inserção (sempre maiúsculas) e/ou usar funções de normalização quando disponíveis. - -- **Esquecer ORDER BY ao esperar uma ordem específica** - - Problema: contar com uma ordem “natural” que o banco não garante. - - Correção: usar ORDER BY explicitamente sempre que a ordem importar. - -## Visão Geral de Debugging - -Quando uma consulta na tabela `Cliente` não retorna o esperado: - -- **1. Rode um SELECT simples sem WHERE** - - Veja se os dados estão mesmo na tabela, com os valores esperados. - -- **2. Teste a condição WHERE isoladamente** - - Tente consultas mais simples (por exemplo, só `WHERE UF_Cliente = 'SP'`) para verificar qual parte da condição está filtrando demais. - -- **3. Remova temporariamente o ORDER BY** - - Para focar apenas na seleção de linhas; depois volte a ordenar. - -- **4. Verifique erros de nomes e grafia** - - Confirme se `Cliente` está com a grafia correta, assim como `Idade_Cliente`, `UF_Cliente`, etc. - -## Principais Pontos - -- A tabela `Cliente` é um cenário simples e poderoso para praticar SELECT, WHERE e ORDER BY. -- `SELECT *` é uma ferramenta útil para conferir tanto estrutura quanto conteúdo. -- Filtros com WHERE permitem extrair subconjuntos significativos (por idade, UF, faixa etária). -- ORDER BY organiza esses subconjuntos, melhorando a legibilidade e a utilidade das consultas. - -## Preparação para Prática - -Após esta lição, você deve ser capaz de: - -- Replicar no seu ambiente a tabela `Cliente` com dados de exemplo. -- Escrever consultas SELECT com filtros e ordenações úteis para análise. -- Depurar consultas simples nessa tabela quando os resultados não forem os esperados. -- Transferir esse padrão de prática para outras tabelas e domínios (produtos, pedidos, transações). - -No Laboratório de Prática, você irá: - -- Escrever e rodar consultas variadas sobre a tabela `Cliente` para fixar o uso de WHERE e ORDER BY. -- Criar variações de filtros por faixa etária e por estado. -- Preparar consultas com ordenação composta. - -## Laboratório de Prática - -### Easy — Listar todos os clientes ordenados por nome - -Usando a tabela `Cliente`, escreva uma consulta que retorne: - -- `Cod_Cliente`, `Nome_Cliente`, `Idade_Cliente`, `UF_Cliente`. -- Ordenados por `Nome_Cliente` em ordem alfabética. - -```sql --- TODO: selecionar todas as colunas e ordenar por nome -SELECT - Cod_Cliente, - Nome_Cliente, - Idade_Cliente, - UF_Cliente -FROM Cliente -ORDER BY - Nome_Cliente ASC; -``` - -Experimente trocar para `DESC` para ver a ordem invertida. - -### Medium — Filtrar clientes por idade mínima e ordenar por idade - -Escreva uma consulta que: - -- Retorne apenas clientes com `Idade_Cliente >= 30`. -- Mostre `Nome_Cliente`, `Idade_Cliente` e `UF_Cliente`. -- Ordene primeiro por `Idade_Cliente` em ordem decrescente. - -```sql --- TODO: aplicar filtro por idade e ordenar em ordem decrescente -SELECT - Nome_Cliente, - Idade_Cliente, - UF_Cliente -FROM Cliente -WHERE Idade_Cliente >= 30 -ORDER BY - Idade_Cliente DESC; -``` - -Opcional: adicione `UF_Cliente` como segundo critério de ordenação. - -### Hard — Relatório de clientes por UF e faixa etária - -Crie uma consulta que: - -1. Traga `Cod_Cliente`, `Nome_Cliente`, `Idade_Cliente` e `UF_Cliente`. -2. Filtre apenas clientes com `Idade_Cliente` entre 20 e 40 anos (inclusive). -3. Ordene os resultados: - - Primeiro por `UF_Cliente` em ordem ascendente. - - Depois por `Idade_Cliente` em ordem ascendente. - - Em caso de empate, por `Nome_Cliente` em ordem ascendente. - -```sql --- TODO: combinar filtro de faixa etaria com ordenacao composta -SELECT - Cod_Cliente, - Nome_Cliente, - Idade_Cliente, - UF_Cliente -FROM Cliente -WHERE Idade_Cliente BETWEEN 20 AND 40 -ORDER BY - UF_Cliente ASC, - Idade_Cliente ASC, - Nome_Cliente ASC; -``` - -Pense em como esse relatório poderia ser usado em uma apresentação ou dashboard simples sobre a distribuição de clientes por estado e idade. - - - - - diff --git a/content/visualizacao-sql/aula-14-sql-group-by-having-agregacoes.md b/content/visualizacao-sql/aula-14-sql-group-by-having-agregacoes.md deleted file mode 100644 index 25c4602..0000000 --- a/content/visualizacao-sql/aula-14-sql-group-by-having-agregacoes.md +++ /dev/null @@ -1,347 +0,0 @@ ---- -title: "Agregação em SQL: GROUP BY, HAVING e funções SUM e COUNT" -slug: "sql-group-by-having-agregacoes" -discipline: "visualizacao-sql" -order: 14 -description: "Agrupar linhas, calcular totais e contagens com SUM e COUNT, filtrar grupos com HAVING e ordenar relatórios; prática com notas fiscais e vendas de jogos (vgsales)." -reading_time: 38 -difficulty: "medium" -concepts: - - funcoes-de-agregacao - - group-by - - having - - where-vs-having - - count-asterisco-vs-count-coluna - - null-em-agregacoes - - order-by-em-relatorios-agrupados -prerequisites: - - sql-order-by-ordenacao-resultados - - consultas-sql-tabela-clientes-pratica -learning_objectives: - - "Explicar a diferença entre filtrar linhas com WHERE e filtrar grupos com HAVING." - - "Escrever consultas com GROUP BY alinhando colunas do SELECT às colunas de agrupamento." - - "Usar SUM, COUNT(*) e COUNT(coluna) e interpretar o efeito de valores NULL." - - "Combinar agregação com ORDER BY para montar rankings por dimensão (plataforma, gênero, ano, editora)." -exercises: - - question: "Por que você não pode usar WHERE para filtrar `SUM(Valor) > 300` depois de agrupar por cliente?" - answer: "WHERE é avaliado antes do agrupamento, linha a linha; a soma por grupo só existe depois do GROUP BY. Para condições sobre agregados, use HAVING, que filtra os grupos já formados." - hint: "Pense na ordem lógica: primeiro entram as linhas (WHERE), depois formam-se os grupos e as agregações." - - question: "Em que situação COUNT(*) e COUNT(Valor) podem dar resultados diferentes na mesma consulta com GROUP BY?" - answer: "Quando Valor é NULL em algumas linhas: COUNT(*) conta todas as linhas do grupo; COUNT(Valor) ignora linhas em que Valor é NULL." - hint: "Relembre os registros inseridos com Valor nulo no material da NF_Cliente." - - question: "Qual é o papel de ORDER BY em uma consulta que já usa GROUP BY?" - answer: "GROUP BY define os grupos e as agregações; ORDER BY define a ordem de exibição das linhas do resultado (por exemplo, gêneros com maior venda primeiro). Sem ORDER BY, a ordem dos grupos não é garantida." - hint: "Compare com uma planilha: primeiro você agrupa e soma, depois classifica a tabela resumo." -review_after_days: - - 3 - - 10 ---- - -## Visão Geral do Conceito - -Em relatórios reais você raramente quer **cada linha bruta** da base: quer **subtotais por cliente**, **contagem de pedidos por status** ou **vendas totais por plataforma**. Em SQL isso é feito com **funções de agregação** (`SUM`, `COUNT`, etc.) e com a cláusula `GROUP BY`, que diz **por qual coluna (ou colunas)** esses totais são calculados. - -A Etapa 8 do curso trabalha dois cenários: uma tabela simples de notas fiscais (`NF_Cliente`) e o dataset público de vendas de jogos `vgsales.csv`, importado para `Vendas_Jogos`, para agrupar por plataforma, ano, gênero e editora. Referência complementar: capítulo sobre `GROUP BY` e `ORDER BY` em *Getting Started with SQL* (O’Reilly), citado no material da disciplina. - -## Modelo Mental - -Imagine uma planilha de vendas com milhares de linhas: - -- Sem agrupamento, `SUM(Valor)` vira **um único número** (o total geral). -- Com `GROUP BY Cliente`, o banco **quebra a planilha em montinhos** — um montinho por cliente — e calcula `SUM` e `COUNT` **dentro de cada montinho**. - -`WHERE` atua **antes** de empilhar: remove linhas que não entram na análise. -`HAVING` atua **depois**: olha para cada **grupo** (já com sua soma, contagem, etc.) e descarta grupos inteiros que não atendem à condição. - -Na aula, isso é enfatizado: *HAVING não examina a tabela original linha a linha como o WHERE faz para agregados; ele examina o resultado do agrupamento.* - -## Mecânica Central - -### Funções de agregação usadas na etapa - -- `SUM(coluna)` — soma valores numéricos do grupo; em SQL padrão, `NULL` é ignorado na soma. -- `COUNT(*)` — conta **linhas** do grupo (incluindo linhas onde alguma coluna é `NULL`). -- `COUNT(coluna)` — conta linhas em que **essa coluna** não é `NULL`. - -### GROUP BY e colunas no SELECT - -> **Regra:** Toda coluna “não agregada” listada no `SELECT` deve aparecer no `GROUP BY` (ou ser funcionalmente dependente do agrupamento, em bancos que permitem modo estrito flexível — no SQLite do curso, pense no alinhamento 1:1). - -Exemplo central do material (`NF_Cliente`): - -```sql -SELECT - Cliente, - SUM(Valor) AS Soma, - COUNT(*) AS QtdeRegistros -FROM NF_Cliente -GROUP BY Cliente; -``` - -### HAVING - -Filtra **grupos** com base em agregados ou em colunas de agrupamento, após o `GROUP BY`: - -```sql -SELECT - Cliente, - SUM(Valor) AS Soma, - COUNT(*) AS QtdeRegistros -FROM NF_Cliente -GROUP BY Cliente -HAVING SUM(Valor) > 300 - AND COUNT(*) >= 1; -``` - -### Ordem lógica das cláusulas - -A ordem **de escrita** típica é: `SELECT``FROM``WHERE``GROUP BY``HAVING``ORDER BY`. - -```mermaid -flowchart TD - A[FROM: tabela fonte] --> B[WHERE: filtra linhas] - B --> C[GROUP BY: forma grupos] - C --> D[HAVING: filtra grupos] - D --> E[SELECT: projeta colunas e agregações] - E --> F[ORDER BY: ordena linhas do resultado] -``` - -### Modelo da tabela de notas fiscais - -```mermaid -erDiagram - NF_Cliente { - int NF - text Produto - text Cliente - int Quantidade - int Valor - int Dia - } -``` - -Cada linha representa um item em uma NF; o mesmo `NF` pode aparecer mais de uma vez (vários produtos). - -## Uso Prático - -### 1. Totais globais e por cliente (`NF_Cliente`) - -```sql --- Total geral de valor na tabela -SELECT SUM(Valor) FROM NF_Cliente; - --- Total apenas para o cliente C1 (filtro antes de agregar) -SELECT SUM(Valor) -FROM NF_Cliente -WHERE Cliente = 'C1'; -``` - -### 2. Valores distintos e ordenação - -```sql -SELECT DISTINCT Cliente FROM NF_Cliente ORDER BY Cliente; -``` - -`DISTINCT` elimina duplicatas; `ORDER BY` organiza o resultado (revisão integrada à etapa). - -### 3. Efeito de NULL em Valor - -Após inserir linhas com `Valor` nulo: - -```sql -SELECT - Cliente, - SUM(Valor) AS Soma, - COUNT(*) AS QtdeLinhas, - COUNT(Valor) AS QtdeValorNaoNulo -FROM NF_Cliente -GROUP BY Cliente; -``` - -Compare `COUNT(*)` com `COUNT(Valor)` para ver o impacto dos nulos. - -### 4. Dataset vgsales no Excel e no SQLite - -No material, o CSV é aberto no Excel e soma-se `NA_Sales` + `EU_Sales` + `JP_Sales` + `Other_Sales` e compara-se com `Global_Sales`: **nem sempre bate** — útil para discutir qualidade de dados, arredondamento e origem do dataset. - -No SQLiteStudio, importa-se o CSV para `Vendas_Jogos` (primeira linha como cabeçalho, separador vírgula). O roteiro usa tipos numéricos inteiros nas colunas de venda; em bases reais o ideal é tipo decimal para milhões de cópias. A ideia pedagógica é reproduzir os `GROUP BY` por dimensão: - -```sql -SELECT - Plataforma_Jogo, - ROUND(SUM(Vendas_Mundial_Jogo), 2) AS Total_Mundial -FROM Vendas_Jogos -GROUP BY Plataforma_Jogo -ORDER BY Plataforma_Jogo; -``` - -Variações do texto da etapa: agrupar por `Ano_Jogo`, `Genero_Jogo`, `Empresa_Jogo`, e ordenar pelo total regional ou mundial com `DESC`. - -### 5. WHERE e GROUP BY juntos - -```sql -SELECT Cliente, SUM(Valor) AS Soma -FROM NF_Cliente -WHERE Cliente = 'C1' -GROUP BY Cliente; -``` - -`WHERE` restringe **quais linhas entram** nos grupos; não substitui `HAVING` para condição em `SUM`. - -## Erros Comuns - -- **Usar WHERE com agregado (ex.: `WHERE SUM(Valor) > 300`)** - Sintoma: erro de sintaxe ou comportamento inesperado. - Correção: use `HAVING SUM(Valor) > 300` após `GROUP BY`. - -- **Coluna no SELECT fora do GROUP BY** - Sintoma: erro ou (em modos permissivos de outros SGBDs) valor arbitrário. - Correção: inclua a coluna no `GROUP BY` ou agregue-a com função adequada. - -- **Confundir COUNT(*) com COUNT(coluna)** - Sintoma: contagens “estranhas” quando há `NULL`. - Correção: escolha `COUNT(*)` para linhas do grupo e `COUNT(coluna)` para presença de valor na coluna. - -- **Esperar ordem “natural” nos grupos** - Correção: sempre `ORDER BY` explícito no relatório agregado. - -- **Tipo de coluna inadequado na importação CSV** - Vendas fracionadas em colunas declaradas como inteiro podem truncar ou arredondar. - Correção: preferir `REAL` ou `NUMERIC` para métricas contínuas. - -## Visão Geral de Debugging - -- **Some ou contagem não batem com o Excel** - Verifique tipos na importação, separador decimal, linhas com `N/A` no ano (o material filtra `Ano_Jogo != 'N/A'` em um dos exemplos) e arredondamento com `ROUND`. - -- **HAVING “não funciona”** - Confira se há `GROUP BY` antes e se a expressão no `HAVING` usa agregação coerente com o grupo. - -- **Grupos a mais ou a menos** - Revise espaços em branco em chaves textuais (`'C1'` vs `'C1 '`) e valores `NULL` na coluna de agrupamento. - -
-Conferência rápida: soma manual vs SUM - -Para poucos registros, uma soma manual (`160 + 34 + …` no slide) ajuda a validar o resultado de `SUM(Valor)` antes de confiar em consultas maiores. - -
- -## Principais Pontos - -- `GROUP BY` define **dimensões** do relatório; agregações calculam **métricas** por dimensão. -- `WHERE` filtra linhas; `HAVING` filtra grupos após agregar. -- `COUNT(*)` e `COUNT(coluna)` divergem quando há `NULL` na coluna. -- `ORDER BY` ordena o **resultado final** (útil para rankings por total de vendas). -- O fluxo Excel ↔ SQL com `vgsales` reforça leitura de schema, importação e sanidade de métricas. - -## Preparação para Prática - -Você deve ser capaz de: - -- Criar `NF_Cliente`, carregar os `INSERT`s da etapa e reproduzir os `SELECT` com agregação e `HAVING`. -- Importar `vgsales.csv` para `Vendas_Jogos` e montar agrupamentos por plataforma, ano, gênero e editora com ordenação por total. - -No Laboratório abaixo, use o mesmo ambiente (SQLiteStudio) e tabelas da etapa. - -## Laboratório de Prática - -### Easy — Filtrar grupos com HAVING - -Sobre a tabela `NF_Cliente`, parta da agregação por cliente e **adicione** uma cláusula para manter apenas clientes cuja soma de `Valor` seja estritamente maior que 300. - -```sql -SELECT - Cliente, - SUM(Valor) AS Soma, - COUNT(*) AS QtdeRegistros -FROM NF_Cliente -GROUP BY Cliente --- TODO: adicionar HAVING com SUM(Valor) > 300 -; -``` - -### Medium — COUNT(*) versus COUNT(Valor) - -Escreva uma única consulta agrupada por `Cliente` que mostre `SUM(Valor)`, `COUNT(*)` e `COUNT(Valor)`. Rode antes e depois de inserir linhas com `Valor` nulo (como no roteiro da etapa) e compare as colunas de contagem. - -```sql --- TODO: incluir COUNT(Valor) com alias (ex.: QtdeValorNaoNulo) e comparar com COUNT(*) -SELECT - Cliente, - SUM(Valor) AS Soma, - COUNT(*) AS QtdeAsterisco -FROM NF_Cliente -GROUP BY Cliente; -``` - -### Hard — Ranking de gêneros por vendas mundiais - -Assumindo a tabela `Vendas_Jogos` preenchida a partir de `vgsales.csv`, produza um relatório com `Genero_Jogo` e soma arredondada de `Vendas_Mundial_Jogo`, **ordenado do gênero com maior total para o menor**. - -```sql -SELECT - Genero_Jogo, - ROUND(SUM(Vendas_Mundial_Jogo), 2) AS Total_Mundial -FROM Vendas_Jogos -GROUP BY Genero_Jogo --- TODO: adicionar ORDER BY para ordenar por Total_Mundial DESC -; -``` - - - - diff --git a/content/visualizacao-sql/aula-15-sql-group-by-dimensoes-ordenacao-duplicatas.md b/content/visualizacao-sql/aula-15-sql-group-by-dimensoes-ordenacao-duplicatas.md deleted file mode 100644 index b6b1192..0000000 --- a/content/visualizacao-sql/aula-15-sql-group-by-dimensoes-ordenacao-duplicatas.md +++ /dev/null @@ -1,398 +0,0 @@ ---- -title: "Agrupamento por múltiplas dimensões: rankings e duplicidades" -slug: "sql-group-by-dimensoes-ordenacao-duplicatas" -discipline: "visualizacao-sql" -order: 15 -description: "Usando GROUP BY composto para definir granularidade, ordenar rankings por métricas agregadas e identificar duplicidade com HAVING COUNT(*) em vgsales." -reading_time: 40 -difficulty: "medium" -concepts: - - group-by-composto - - granularidade-das-agregacoes - - order-by-com-agregados - - alias-no-order-by - - duplicidade-com-count - - having-count -prerequisites: - - sql-group-by-having-agregacoes - - sql-order-by-ordenacao-resultados -learning_objectives: - - "Escrever consultas com GROUP BY composto (múltiplas colunas) para mudar a granularidade do relatório." - - "Montar rankings com ORDER BY usando métricas calculadas por SUM e COUNT." - - "Usar HAVING com COUNT(*) > 1 para filtrar grupos que se repetem." - - "Diagnosticar duplicidade comparando a contagem em diferentes níveis de GROUP BY." -exercises: - - question: "Qual é a diferença prática entre agrupar somente por `Nome_Jogo` e agrupar por `Nome_Jogo`, `Plataforma_Jogo` e `Ano_Jogo`?" - answer: "No primeiro caso o grupo representa apenas o nome; no segundo, cada combinação de nome+plataforma+ano vira um grupo. Ao aumentar dimensões, a contagem por grupo tende a diminuir porque você separa registros que antes caíam no mesmo grupo." - hint: "Pense em granularidade: quantos 'montinhos' você cria ao agrupar." - - question: "Por que `HAVING COUNT(*) > 1` funciona para detectar duplicidade de jogos?" - answer: "`COUNT(*)` mede quantas linhas existem em cada grupo formado pelo `GROUP BY`. `HAVING` filtra esses grupos após a agregação, mantendo apenas os grupos cuja contagem é maior que 1." - hint: "WHERE filtra linhas antes; HAVING filtra grupos depois." - - question: "Em um ranking agregado, por que é comum ordenar por uma expressão agregada como `SUM(...)` ou por um alias criado no SELECT?" - answer: "Porque o objetivo do ranking é classificar os grupos pelo valor da métrica agregada (o total por gênero, plataforma, etc.). Sem ORDER BY por essa métrica, a ordem dos grupos não é garantida." - hint: "Rankings precisam de um critério de ordenação explícito." -review_after_days: - - 3 - - 10 ---- - -## Visão Geral do Conceito - -Na Aula 14, você viu `GROUP BY` e `HAVING` para criar relatórios com totais e contagens. - -Nesta lição, o foco é um uso ainda mais “de analista”: transformar o `vgsales` em: - -- ranking de vendas (por `Genero_Jogo`, `Empresa_Jogo`, etc.) usando `SUM` e `ORDER BY`; -- diagnóstico de duplicidade usando `COUNT(*)` e `HAVING COUNT(*) > 1`. - -A ideia central (dita na transcrição) é que o conjunto de colunas no `GROUP BY` define a “granularidade” do resultado: quando você adiciona dimensões, você muda o tamanho dos grupos e a contagem muda também. - -## Modelo Mental - -Pense em duas peças: - -1. Dimensões: são as colunas que você lista no `GROUP BY` (ex.: nome do jogo, plataforma, ano, gênero, empresa). Elas definem “qual agrupamento” está sendo analisado. -2. Métrica: é a agregação calculada no `SELECT` (ex.: `SUM(Vendas_Mundial_Jogo)` ou `COUNT(*)`). - -Quando você detecta que “um jogo aparece muitas vezes”, isso pode acontecer por diferentes motivos: - -- Você agrupou em um nível alto (ex.: só por `Nome_Jogo`), então registros distintos caem no mesmo grupo. -- Você agrupou em um nível mais fino (ex.: `Nome_Jogo` + `Plataforma_Jogo` + `Ano_Jogo` + ...), e aí a duplicidade pode desaparecer. - -Isso é exatamente o que a Aula 15 demonstra ao: primeiro contar por nome; depois contar por combinações maiores de dimensões para “ver se a duplicidade persiste”. - -## Mecânica Central - -`GROUP BY` + agregações + `HAVING` forma um pipeline lógico: - -```mermaid -graph TD - A[Linhas da tabela Vendas_Jogos] --> B[Definir dimensões no GROUP BY] - B --> C[Calcular métricas SUM e COUNT no SELECT] - C --> D[Filtrar grupos com HAVING - ex: COUNT maior que 1] - D --> E[Ordenar com ORDER BY - ex: por métrica DESC] - E --> F[Resultado final: uma linha por grupo] -``` - -### ORDER BY em cima de métricas agregadas - -Para criar ranking, você mede uma métrica por grupo (via `SUM`) e depois ordena com `ORDER BY ... DESC`. - -Exemplo (ranking por `Genero_Jogo`): - -```sql -SELECT - Genero_Jogo, - ROUND( - SUM(Vendas_EUA_Jogo + Vendas_Europa_Jogo + Vendas_Japao_Jogo + Vendas_Outros_Lugares_Jogo), - 2 - ) AS Soma_Regionais, - ROUND(SUM(Vendas_Mundial_Jogo), 2) AS Soma_Mundial -FROM Vendas_Jogos -GROUP BY Genero_Jogo -ORDER BY Soma_Mundial DESC; -``` - -Na transcrição, a ordenação descendente é usada para responder “qual gênero mais vendeu”. - -### GROUP BY composto define granularidade - -Ao adicionar mais colunas ao `GROUP BY`, você divide o conjunto em grupos menores. - -Compare: - -1) Agrupar apenas por nome: - -```sql -SELECT - Nome_Jogo, - COUNT(*) AS QtdeRepeticoes -FROM Vendas_Jogos -GROUP BY Nome_Jogo; -``` - -2) Agrupar por nome + plataforma + ano + gênero + empresa: - -```sql -SELECT - Nome_Jogo, - Plataforma_Jogo, - Ano_Jogo, - Genero_Jogo, - Empresa_Jogo, - COUNT(*) AS QtdeRepeticoes -FROM Vendas_Jogos -GROUP BY - Nome_Jogo, - Plataforma_Jogo, - Ano_Jogo, - Genero_Jogo, - Empresa_Jogo; -``` - -A Aula 15 ressalta essa regra na prática: “se eu vou selecionar dimensões, eu preciso repetir essas dimensões no `GROUP BY`”. - -### Duplicidade com COUNT e HAVING - -Para encontrar grupos “repetidos”, você usa `COUNT(*)` como métrica e filtra com `HAVING`. - -Exemplo: nomes de jogos que aparecem mais de uma vez: - -```sql -SELECT - Nome_Jogo, - COUNT(*) AS QtdeRepeticoes -FROM Vendas_Jogos -GROUP BY Nome_Jogo -HAVING COUNT(*) > 1 -ORDER BY QtdeRepeticoes DESC; -``` - -Depois, para entender se a duplicidade é “crítica” ou apenas consequência de agrupar em nível alto, você aumenta o conjunto de dimensões no `GROUP BY` e repete a análise. - -### Quando duplicidade “some” - -Uma duplicidade por nome (`HAVING COUNT(*) > 1` em `Nome_Jogo`) pode desaparecer quando você agrupa por combinação completa de dimensões. Isso sugere que as linhas eram “iguais” só no nome, mas diferentes em plataforma/ano/gênero/empresa. - -```mermaid -graph LR - A[Grain 1: GROUP BY Nome_Jogo] --> B[Duplicidade aparece?] - B -->|Sim| C[Grain 2: GROUP BY Nome + Plataforma + Ano + Genero + Empresa] - C --> D[Duplicidade persiste?] -``` - -## Uso Prático - -### 1. Ranking: qual gênero mais vende? - -Use um `SUM` das vendas regionais (EUA, Europa, Japão e outros) para criar uma métrica, e `ORDER BY ... DESC` para ranquear. - -```sql -SELECT - Genero_Jogo, - ROUND(SUM(Vendas_Mundial_Jogo), 2) AS Total_Mundial -FROM Vendas_Jogos -GROUP BY Genero_Jogo -ORDER BY Total_Mundial DESC; -``` - -Se você preferir, pode ordenar pela métrica construída a partir do somatório das quatro colunas regionais (como o roteiro faz no Excel). - -### 2. Duplicidade: quais nomes de jogos se repetem? - -```sql -SELECT - Nome_Jogo, - COUNT(*) AS QtdeRepeticoes -FROM Vendas_Jogos -GROUP BY Nome_Jogo -HAVING COUNT(*) > 1 -ORDER BY QtdeRepeticoes DESC; -``` - -Na Aula 15, a contagem total de linhas existe, e a contagem por nome reduz porque “o nome” é uma dimensão não única. - -### 3. Diagnóstico: duplicidade persiste em granularidade mais fina? - -Se quiser confirmar se a duplicidade é “crítica” (isto é, se existe mais de uma linha idêntica no nível de dimensões escolhido), agrupe por nome + plataforma + ano + gênero + empresa e aplique a mesma condição. - -```sql -SELECT - Nome_Jogo, - Plataforma_Jogo, - Ano_Jogo, - Genero_Jogo, - Empresa_Jogo, - COUNT(*) AS QtdeRepeticoes -FROM Vendas_Jogos -GROUP BY - Nome_Jogo, - Plataforma_Jogo, - Ano_Jogo, - Genero_Jogo, - Empresa_Jogo -HAVING COUNT(*) > 1 -ORDER BY QtdeRepeticoes DESC; -``` - -Se retornar linhas, você tem duplicidade mesmo nesse nível de granularidade (e o próximo passo é inspecionar as linhas brutas com um `SELECT` com `WHERE` para uma combinação específica). - -## Erros Comuns - -- Esquecer colunas no `GROUP BY` enquanto seleciona dimensões - - Sintoma: erro do SQL ou resultados “sem sentido” (dependendo do SGBD/modo). - - Correção: alinhar tudo que é dimensão no `SELECT` com as colunas do `GROUP BY`. - -- Usar `WHERE COUNT(*) > 1` - - Sintoma: erro de sintaxe ou filtro que não faz o que você espera. - - Correção: usar `HAVING COUNT(*) > 1` após o `GROUP BY`. - -- Ordenar por uma coluna que não representa o ranking - - Sintoma: “o mais vendido” aparece em posição inesperada. - - Correção: ordenar pela métrica agregada correta (ex.: `Total_Mundial` ou pela métrica de soma regionais). - -- Interpretar duplicidade sem aumentar granularidade - - Sintoma: você acha que há erro no dataset quando na verdade é apenas repetição em níveis diferentes (ex.: nome igual, mas plataforma/ano diferentes). - - Correção: comparar o resultado em Grain 1 (nome) vs Grain 2 (nome + demais dimensões). - -## Visão Geral de Debugging - -Quando o resultado “não bate” ao tentar detectar duplicidade ou ranking: - -1. Verifique se você realmente está agrupando pela dimensão que faz sentido para a pergunta (por nome? por nome+plataforma+ano?). -2. Confirme a métrica: - - Se a pergunta é “quantas linhas”, use `COUNT(*)`. - - Se a pergunta é “valor total”, use `SUM(...)` na coluna correta. -3. Separe em etapas: - - primeiro: `GROUP BY` + `COUNT` sem `HAVING`; - - depois: adicione `HAVING COUNT(*) > 1`. -4. Se ainda houver duplicidade, aumente o número de dimensões no `GROUP BY` até o nível em que você considera “duplicado”. - -
-Ajuda mental rápida: por que duplicidade muda quando adiciono dimensões? - -Porque `GROUP BY` cria grupos por combinação exata das dimensões. Mais dimensões significam mais combinações distintas e, portanto, menos linhas por grupo. - -
- -## Principais Pontos - -- `GROUP BY` composto define a granularidade do seu relatório. -- Rankings agregados usam `ORDER BY` sobre métricas como `SUM` ou `COUNT`. -- Duplicidade se detecta com `HAVING COUNT(*) > 1`. -- Para entender se duplicidade é “só nome” ou “só nesse nível”, compare múltiplos grains de `GROUP BY`. - -## Preparação para Prática - -Depois desta lição, você deve ser capaz de: - -- Montar um ranking por dimensão (ex.: gênero) usando `SUM` e `ORDER BY ... DESC`. -- Detectar duplicidade no dataset com `COUNT(*)` + `HAVING`. -- Ajustar a granularidade repetindo dimensões no `GROUP BY` para diagnosticar quando a duplicidade “some”. - -No Laboratório, você vai repetir exatamente esse raciocínio com a tabela `Vendas_Jogos` importada do `vgsales.csv`. - -## Laboratório de Prática - -### Easy — Ranking de gêneros por vendas mundiais - -Escreva uma consulta que retorne: - -- `Genero_Jogo` -- uma métrica `Total_Mundial` (soma arredondada) - -e ordene do maior total para o menor. - -```sql --- TODO: calcular Total_Mundial e ordenar por Total_Mundial DESC -SELECT - Genero_Jogo, - ROUND(SUM(Vendas_Mundial_Jogo), 2) AS Total_Mundial -FROM Vendas_Jogos -GROUP BY Genero_Jogo --- TODO: adicionar ORDER BY Total_Mundial DESC -; -``` - -### Medium — Nomes de jogos repetidos (COUNT) com HAVING - -Escreva uma consulta que: - -- agrupe por `Nome_Jogo` -- conte `COUNT(*)` -- mantenha apenas grupos com contagem maior que 1 -- ordene por contagem decrescente - -```sql --- TODO: agrupar por Nome_Jogo e filtrar duplicidade com HAVING COUNT(*) > 1 -SELECT - Nome_Jogo, - COUNT(*) AS QtdeRepeticoes -FROM Vendas_Jogos -GROUP BY Nome_Jogo --- TODO: adicionar HAVING COUNT(*) > 1 --- TODO: ordenar por QtdeRepeticoes DESC -; -``` - -### Hard — Duplicidade em granularidade fina (5 dimensões) - -Nesta tarefa você vai repetir o diagnóstico em um nível em que “duplicado” significa: - -o mesmo `Nome_Jogo` + `Plataforma_Jogo` + `Ano_Jogo` + `Genero_Jogo` + `Empresa_Jogo`. - -```sql --- TODO: agrupar por todas as 5 dimensões e filtrar apenas combinações repetidas -SELECT - Nome_Jogo, - Plataforma_Jogo, - Ano_Jogo, - Genero_Jogo, - Empresa_Jogo, - COUNT(*) AS QtdeRepeticoes -FROM Vendas_Jogos -GROUP BY - Nome_Jogo, - Plataforma_Jogo, - Ano_Jogo, - Genero_Jogo, - Empresa_Jogo --- TODO: adicionar HAVING COUNT(*) > 1 --- TODO: ordenar por QtdeRepeticoes DESC (opcional, mas recomendado) -; -``` - -Interprete o resultado: se não houver linhas, a duplicidade era “apenas no nome”. Se houver linhas, existe duplicidade mesmo no nível fino definido pelas 5 dimensões. - - - - - diff --git a/content/visualizacao-sql/aula-16-at-visualizacao-dados-sql-projeto-academia.md b/content/visualizacao-sql/aula-16-at-visualizacao-dados-sql-projeto-academia.md deleted file mode 100644 index ca95176..0000000 --- a/content/visualizacao-sql/aula-16-at-visualizacao-dados-sql-projeto-academia.md +++ /dev/null @@ -1,181 +0,0 @@ ---- -title: "Avaliação Final (AT): Visualização de Dados e Consultas Analíticas" -slug: "at-visualizacao-dados-sql-projeto-academia" -discipline: "visualizacao-sql" -order: 16 -description: "Guia técnico e requisitos para o Assessment (AT) integrando Looker Studio e SQL." -reading_time: 15 -difficulty: "hard" -concepts: - - campos calculados - - integração google sheets - - filtros de dashboard - - consultas sql analíticas -learning_objectives: - - "Configurar integração entre Google Sheets e Looker Studio para relatórios dinâmicos." - - "Criar métricas customizadas através de campos calculados (Taxa de Ocupação)." - - "Implementar controles e filtros para análise multidimensional." - - "Traduzir requisitos de negócio em consultas SQL de seleção e agregação." -exercises: - - question: "Por que a entrega dos TPs (1, 2 e 3) é obrigatória para a correção do AT, mesmo que não contem pontos?" - answer: "Porque os TPs validam o progresso e a compreensão das competências básicas necessárias para o projeto final (AT)." - - question: "Qual a fórmula para o campo calculado de Taxa de Ocupação na Academia?" - answer: "Inscritos / Vagas Ofertadas (formatado como porcentagem)." ---- - -## Visão Geral do Conceito - -O **Assessment Task (AT)** é o projeto final que consolida as competências de extração, manipulação e visualização de dados. Ele exige que o estudante saia do papel de apenas "escrever código" e passe a atuar como um analista, transformando dados brutos da operação de uma academia em informações visuais acionáveis e consultas SQL precisas. - -Esta etapa avalia não apenas o acerto técnico, mas a capacidade de manter **consistência visual** e **lógica de negócio** em um ambiente de BI (Business Intelligence). - -## Modelo Mental - -Imagine um pipeline de dados onde a informação flui de um arquivo estático para um painel vivo: - -1. **Dados Brutos (`.csv`):** O registro frio das aulas. -2. **Repositório Ativo (Google Sheets):** Onde os dados ganham "vida" na nuvem. -3. **Camada de Visualização (Looker Studio):** Onde os dados são filtrados, agregados e apresentados para decisão. - -No SQL, o modelo mental é o de **filtragem seletiva**: você tem um oceano de dados e precisa pescar apenas os peixes (registros) que atendem a critérios específicos de turno, modalidade ou unidade. - -## Mecânica Central - -### 1. Integração Sheets -> Looker -Para que o Looker Studio funcione corretamente, os dados devem estar estruturados no Google Sheets com um cabeçalho claro na primeira linha. O Looker consome essas colunas como **Dimensões** (texto/datas) e **Métricas** (números agregáveis). - -### 2. Campos Calculados -Às vezes, a informação necessária não está na tabela original. Criamos métricas customizadas no Looker: -- **Exemplo (Taxa de Ocupação):** `Inscritos / Vagas_Ofertadas`. -- **Exemplo (Taxa de Presença):** `Presentes / Inscritos`. - -### 3. Filtros (Controles) -Os filtros permitem que o usuário final interaja com o dashboard. Os principais tipos são: -- **Lista suspensa:** Para categorias (Unidade, Turno). -- **Filtro de período:** Para dimensões de data. - -```mermaid -flowchart TD - CSV[Arquivo CSV] -->|Importar| GS[Google Sheets] - GS -->|Conectar| LS[Looker Studio] - LS -->|Criar| Metric[Campos Calculados] - Metric -->|Visualizar| Dash[Dashboard Final] - LS -->|Adicionar| Ctrl[Controles de Filtro] -``` - -## Uso Prático: O Projeto Academia - -O projeto final foca na análise de ocupação de uma rede de academias. A estrutura do dashboard deve seguir quatro páginas lógicas: - -1. **Visão Geral (Tabela):** Listagem bruta filtrada (`Data`, `Unidade`, `Modalidade`). -2. **Métricas de Desempenho (Visão Macro):** Visões de total de inscritos vs. presentes. -3. **Análise por Categoria:** Gráficos de barra e pizza para comparar modalidades e professores. -4. **Evolução Temporal:** Gráfico de série temporal para identificar picos de uso. - -### Exemplo de Consulta SQL Analítica -Para a Parte 2 do AT, você deve dominar seleções com filtros compostos: - -```sql --- Buscar alunos presentes em Yoga, no turno da Noite, na Unidade Sul -SELECT Data_Aula, Professor, Presentes -FROM Academia -WHERE Modalidade = 'Yoga' - AND Turno = 'Noite' - AND Unidade = 'Sul' -ORDER BY Data_Aula DESC; -``` - -## Erros Comuns - -- `Uso de IA Gerativa`: O uso de ferramentas como ChatGPT na execução do AT é estritamente proibido e passível de desclassificação. -- `Inconsistência Visual`: Usar cores diferentes ou fontes variadas entre as páginas do relatório quebra a experiência do usuário. -- `Missing TP3`: Não entregar os trabalhos parciais impede a correção da prova final. - -## Visão Geral de Debugging - -Se os dados não aparecerem no Looker: -1. Verifique se o cabeçalho no Google Sheets não possui células mescladas. -2. Certifique-se de que a coluna de **Data** foi reconhecida como tipo "Data" pelo Looker (e não apenas texto). -3. Confira se não há valores nulos nas colunas de `Vagas` que possam causar erro de divisão por zero em campos calculados. - -## Principais Pontos - -- O AT é a única entrega que compõe a nota final, mas os TPs são pré-requisitos. -- Identidade visual coerente é um requisito explícito de avaliação. -- O relatório no Looker Studio deve conter exatamente 4 páginas. -- Parte 1 é visual (Looker); Parte 2 é lógica (SQL). - -## Preparação para Prática - -Ao final desta lição e antes de entregar o AT, você deve ser capaz de: -- Conectar fontes de dados externas ao Looker Studio. -- Criar métricas matemáticas que não existem na base original. -- Estruturar um painel interativo e navegável. -- Escrever consultas SQL que respondam perguntas de negócio específicas. - -## Laboratório de Prática - -### Desafio 1: Easy (Métrica Base) -No Google Sheets, após importar o arquivo `Uso Academia.csv`, identifique a coluna de inscritos. No Looker Studio, crie um **Cartão de Pontuação** que exiba a soma total de `Presentes` em todas as unidades. - -### Desafio 2: Medium (Campo Calculado) -Crie um campo calculado chamado `Percentual_Comparecimento`. -- **Fórmula:** `SUM(Presentes) / SUM(Inscritos)` -- **Objetivo:** Exibir este valor em um gráfico de barras comparando a média de comparecimento por **Professor**. - -### Desafio 3: Hard (SQL Report) -Escreva uma consulta SQL para a Parte 2 do AT que retorne: -- A média de `Duracao_Minutos` por `Modalidade`. -- Apenas para a unidade 'Centro'. -- Ordenado da maior duração para a menor. - - - - diff --git a/content/visualizacao-sql/aula-17-sql-agregacoes-weather-stations.md b/content/visualizacao-sql/aula-17-sql-agregacoes-weather-stations.md deleted file mode 100644 index 894a497..0000000 --- a/content/visualizacao-sql/aula-17-sql-agregacoes-weather-stations.md +++ /dev/null @@ -1,472 +0,0 @@ ---- -title: "Agregações em dados reais (SQLite): DISTINCT, GROUP BY, ORDER BY e HAVING com weather_stations" -slug: "sql-agregacoes-weather-stations" -discipline: "visualizacao-sql" -order: 17 -description: "Explorando a tabela station_data (weather_stations.db) com DISTINCT, contagens, agregações, GROUP BY por ano e mês, ordenação e filtros com HAVING; atenção a NULL e boas práticas de dados." -reading_time: 35 -difficulty: "medium" -concepts: - - distinct - - order-by - - group-by - - group-by-multiplas-dimensoes - - count-asterisco-vs-count-coluna - - null-em-agregacoes - - avg-sum-max-round - - having - - governanca-e-lgpd-no-uso-de-dados -prerequisites: - - sql-group-by-dimensoes-ordenacao-duplicatas -learning_objectives: - - "Carregar um banco SQLite (.db) no SQLiteStudio e identificar a tabela de trabalho." - - "Usar DISTINCT para entender cardinalidade de colunas (e combinações de colunas)." - - "Construir relatórios agregados por ano e por ano+mês com GROUP BY e ORDER BY." - - "Entender o efeito de NULL em COUNT(coluna) e como medir NULL e NOT NULL com IS NULL." - - "Aplicar ROUND em AVG/SUM/MAX para resultados mais legíveis." - - "Filtrar grupos com HAVING para encontrar anos com precipitação total acima de um limiar." -exercises: - - question: "Por que COUNT(snow_depth) não retorna o mesmo valor que COUNT(*) na tabela station_data?" - answer: "Porque COUNT(coluna) ignora linhas em que a coluna é NULL. Se snow_depth tem muitos valores ausentes, COUNT(snow_depth) contará apenas registros com valor preenchido, enquanto COUNT(*) conta todas as linhas." - hint: "Compare com uma checagem IS NULL / IS NOT NULL." - - question: "Qual é o significado de usar GROUP BY 1, 2 em vez de GROUP BY year, month?" - answer: "GROUP BY 1, 2 agrupa pelas colunas 1 e 2 do SELECT (neste caso, year e month). É um atalho; funciona, mas pode reduzir legibilidade e ficar frágil se a ordem do SELECT mudar." - hint: "Relembre que 1 e 2 se referem à posição das colunas no SELECT." - - question: "Quando usar HAVING em vez de WHERE em uma consulta agregada?" - answer: "Use WHERE para filtrar linhas antes do agrupamento e HAVING para filtrar grupos depois do GROUP BY, especialmente quando a condição depende de agregações como SUM, AVG, COUNT, MAX." - hint: "Pense na ordem lógica: linhas entram (WHERE) → agrupam → agregam → filtram grupos (HAVING)." -review_after_days: - - 3 - - 10 ---- - -## Visão Geral do Conceito - -Quando você abre um banco novo (um `.db` que você baixou da internet, um dump de produção, um dataset de pesquisa), o primeiro trabalho não é “fazer dashboard” — é **entender o dado**: período coberto, entidades, colunas, valores possíveis, faltantes e como criar **tabelas resumo** (agregações) para responder perguntas de negócio. - -Nesta aula, isso é feito com o dataset `weather_stations.db`, usando a tabela `station_data`. O foco prático é aplicar: - -- `DISTINCT` para entender valores únicos (e combinações). -- `COUNT`, `AVG`, `SUM`, `MAX` para medir fenômenos. -- `GROUP BY` por ano e por ano+mês (múltiplas dimensões). -- `ORDER BY` para ordenar relatórios. -- `HAVING` para filtrar **grupos** (ex.: “anos com precipitação total > 30”). - -## Modelo Mental - -Pense em uma consulta analítica como um pipeline: - -1) **Explorar** (o que existe?) -2) **Quantificar** (quanto tem?) -3) **Resumir** (por qual dimensão?) -4) **Ordenar** (o que aparece primeiro?) -5) **Filtrar grupos** (o que merece atenção?) - -Em SQL, isso aparece como: - -```mermaid -flowchart TD - A[SELECT * para conhecer a tabela] --> B[DISTINCT para valores possiveis] - B --> C[COUNT para volume] - C --> D[GROUP BY para resumo por dimensao] - D --> E[ORDER BY para leitura e ranking] - E --> F[HAVING para manter apenas grupos relevantes] -``` - -Uma segunda ideia importante da aula é: **dados têm dono**. Em empresas, nem sempre você pode “copiar uma base e mandar para outro lugar” sem autorização. Esse tema aparece junto da lembrança de LGPD, governança e responsabilidade sobre dados. - -## Mecânica Central - -### Preparação do ambiente (SQLiteStudio + database) - -O fluxo prático seguido na aula foi: - -- Baixar o banco `weather_stations.db` do repositório do autor (O’Reilly / Getting Started with SQL). -- No SQLiteStudio: **Database → Add a database**, selecionar o arquivo e usar **Test connection**. -- Abrir a tabela `station_data`. - -> **Regra prática:** “remover o banco” no SQLiteStudio remove da *lista da ferramenta*, não apaga o arquivo físico `.db`. - -### 1) Explorar: olhar a tabela inteira (amostragem) - -```sql -SELECT * -FROM station_data; -``` - -Mesmo que a tabela tenha muitas linhas, esse passo ajuda a: - -- reconhecer nomes de colunas; -- ver valores típicos; -- identificar colunas booleanas/flags (ex.: tornado, rain). - -### 2) Descobrir o período: MIN e MAX do ano - -```sql -SELECT MIN(year), MAX(year) -FROM station_data; -``` - -Isso responde rápido “qual é o intervalo temporal da base”. - -### 3) DISTINCT: valores únicos e combinações - -Valores únicos de uma coluna: - -```sql -SELECT DISTINCT station_number -FROM station_data; -``` - -Valores únicos da combinação (ex.: estação + ano), com ordenação: - -```sql -SELECT DISTINCT station_number, year -FROM station_data -ORDER BY 1, 2; -``` - -Aqui, `ORDER BY 1, 2` significa “ordenar pela 1ª e 2ª colunas do SELECT”. Funciona, mas é um atalho (pode perder legibilidade). - -### 4) Contagem total e contagem com filtro (WHERE) - -```sql -SELECT COUNT(*) AS record_count -FROM station_data; -``` - -```sql -SELECT COUNT(*) AS record_count -FROM station_data -WHERE tornado = 1; -``` - -Esse padrão é a forma mais rápida de medir “quantas linhas entram na condição”. - -### 5) GROUP BY: contagem por ano (dimensão = year) - -```sql -SELECT year, COUNT(*) AS record_count -FROM station_data -WHERE tornado = 1 -GROUP BY year; -``` - -Pontos-chave: - -- `WHERE` filtra linhas **antes** de agrupar. -- `GROUP BY` cria um resultado com “uma linha por ano”. - -### 6) GROUP BY composto: contagem por ano e mês (granularidade mais fina) - -```sql -SELECT year, month, COUNT(*) AS record_count -FROM station_data -WHERE tornado = 1 -GROUP BY year, month -ORDER BY year, month; -``` - -Se você ordenar decrescente por ano: - -```sql -SELECT year, month, COUNT(*) AS record_count -FROM station_data -WHERE tornado = 1 -GROUP BY year, month -ORDER BY year DESC, month; -``` - -Isso vira um relatório pronto para leitura (e muitas vezes já “alimentável” em ferramenta de BI). - -### 7) COUNT(coluna) e o efeito de NULL (ausência de valor) - -Na aula, a coluna `snow_depth` foi usada para mostrar o comportamento: - -```sql -SELECT COUNT(snow_depth) AS recorded_snow_depth_count -FROM station_data; -``` - -Como `COUNT(coluna)` ignora `NULL`, ele conta apenas os registros em que `snow_depth` está preenchido. - -Para medir quantos NULL existem: - -```sql -SELECT COUNT(*) AS null_snow_depth_count -FROM station_data -WHERE snow_depth IS NULL; -``` - -E para confirmar os não nulos: - -```sql -SELECT COUNT(*) AS not_null_snow_depth_count -FROM station_data -WHERE snow_depth IS NOT NULL; -``` - -### 8) AVG e ROUND: média por mês (após 2000) - -```sql -SELECT month, AVG(temperature) AS avg_temp -FROM station_data -WHERE year >= 2000 -GROUP BY month; -``` - -Para deixar legível: - -```sql -SELECT month, ROUND(AVG(temperature), 2) AS avg_temp -FROM station_data -WHERE year >= 2000 -GROUP BY month; -``` - -### 9) SUM e MAX por ano: múltiplas métricas no mesmo relatório - -```sql -SELECT - year, - ROUND(SUM(snow_depth), 2) AS total_snow, - ROUND(SUM(precipitation), 2) AS total_precipitation, - ROUND(MAX(precipitation), 2) AS max_precipitation -FROM station_data -WHERE year >= 2000 -GROUP BY year; -``` - -Aqui, `year` é dimensão; o resto são métricas. - -### 10) HAVING: filtrar grupos por total anual - -```sql -SELECT - year, - ROUND(SUM(precipitation), 2) AS total_precipitation -FROM station_data -GROUP BY year -HAVING total_precipitation > 30; -``` - -O uso de `HAVING` é o que permite filtrar por **resultado do grupo** (a soma anual), não por linha. - -## Uso Prático - -### Checklist de exploração rápida de uma tabela nova - -1) Ver as colunas e valores: - -```sql -SELECT * -FROM station_data -LIMIT 20; -``` - -2) Período coberto: - -```sql -SELECT MIN(year) AS min_year, MAX(year) AS max_year -FROM station_data; -``` - -3) Cardinalidade (quantos “IDs” distintos): - -```sql -SELECT COUNT(DISTINCT station_number) AS stations -FROM station_data; -``` - -4) Relatório de ocorrência por tempo (ex.: tornado): - -```sql -SELECT year, month, COUNT(*) AS tornado_records -FROM station_data -WHERE tornado = 1 -GROUP BY year, month -ORDER BY year, month; -``` - -5) Métricas anuais (neve/precipitação): - -```sql -SELECT - year, - ROUND(SUM(snow_depth), 2) AS total_snow, - ROUND(SUM(precipitation), 2) AS total_precipitation -FROM station_data -WHERE year >= 2000 -GROUP BY year -ORDER BY year; -``` - -## Erros Comuns - -- **Achar que `COUNT(coluna)` conta linhas** - `COUNT(coluna)` ignora `NULL`. Se você quer linhas, use `COUNT(*)`. - -- **Esquecer de alinhar dimensões no `GROUP BY`** - Se `year` e `month` estão no SELECT sem agregação, precisam estar no `GROUP BY`. - -- **Confundir `WHERE` e `HAVING`** - `WHERE` filtra linhas; `HAVING` filtra grupos. - -- **Usar `ORDER BY 1,2` sem perceber que ficou frágil** - Funciona, mas se você mexer no SELECT, muda a ordenação. Para relatórios que outras pessoas vão manter, prefira `ORDER BY year, month`. - -## Visão Geral de Debugging - -Quando um resultado “parece estranho”: - -- **Cheque a granularidade**: você está agrupando no nível certo (ano) ou fino demais (ano+mês)? -- **Verifique NULL**: uma métrica pode estar baixa porque muitos valores estão ausentes (ex.: `snow_depth`). -- **Confirme o filtro**: `tornado = 1` faz sentido para a coluna? Há outros valores além de 0 e 1? -- **Valide com consultas pequenas**: pegue um ano específico e confira manualmente com `COUNT(*)` + `WHERE year = ...`. - -## Principais Pontos - -- `DISTINCT` revela valores e combinações possíveis. -- `GROUP BY` cria relatórios por dimensão (ano, mês, estação...). -- `ORDER BY` dá legibilidade e permite rankings. -- `COUNT(*)` conta linhas; `COUNT(coluna)` ignora NULL. -- `HAVING` filtra grupos por agregações (ex.: `SUM(...) > X`). -- Dados em empresa têm dono: governança e LGPD são parte do trabalho com dados. - -## Preparação para Prática - -Ao final desta lição, você deve conseguir: - -- carregar um `.db` no SQLiteStudio e achar a tabela correta; -- medir volume e período da base; -- montar relatórios de ocorrência por ano e por mês; -- investigar dados faltantes em colunas numéricas; -- gerar tabelas-resumo com múltiplas métricas e filtrar grupos com HAVING. - -## Laboratório de Prática - -### Easy — Perfil da base (período + cardinalidade) - -Escreva uma consulta que retorne: - -- menor e maior ano (`MIN(year)` e `MAX(year)`) -- quantidade de estações distintas (`COUNT(DISTINCT station_number)`) - -```sql -SELECT - -- TODO: retornar menor ano como min_year - MIN(year) AS min_year, - -- TODO: retornar maior ano como max_year - MAX(year) AS max_year, - -- TODO: contar quantas estacoes distintas existem - COUNT(DISTINCT station_number) AS stations -FROM station_data; -``` - -### Medium — Tornados por ano e mês (com ordenação) - -Gere um relatório com: - -- `year`, `month` -- contagem de registros com tornado -- ordenação por ano desc e mês asc - -```sql -SELECT - year, - month, - COUNT(*) AS tornado_records -FROM station_data -WHERE tornado = 1 -GROUP BY year, month --- TODO: ordenar por year DESC e month ASC -ORDER BY year DESC, month ASC; -``` - -### Hard — Anos com precipitação “alta” (HAVING + múltiplas métricas) - -Crie um relatório anual (apenas anos >= 2000) com: - -- total de precipitação (soma arredondada) -- máxima precipitação diária (max arredondado) - -Depois, filtre para mostrar apenas anos com total de precipitação > 30. - -```sql -SELECT - year, - ROUND(SUM(precipitation), 2) AS total_precipitation, - ROUND(MAX(precipitation), 2) AS max_precipitation -FROM station_data -WHERE year >= 2000 -GROUP BY year --- TODO: filtrar grupos com total_precipitation > 30 (HAVING) -HAVING total_precipitation > 30 -ORDER BY total_precipitation DESC; -``` - - > **Fechamento da aula (projeto):** Fechamento da aula (projeto): no final, o professor comentou sobre o ISS e elogiou a iniciativa, reforçando a importância de seguir o processo da disciplina com consistência. -No clima da turma, a leitura foi simples: **“se o caduzão falou, está falado.”** - - - - - diff --git a/core/config.py b/core/config.py index 4b0abba..7cd26be 100644 --- a/core/config.py +++ b/core/config.py @@ -2,6 +2,7 @@ from __future__ import annotations +import logging import os from dataclasses import dataclass from pathlib import Path @@ -11,6 +12,19 @@ GlobalContextMode = Literal["geral", "all"] +_LOG = logging.getLogger("kernelbots.config") + + +def _normalize_db_host(raw: str) -> str: + """127.0.0.0 é typo frequente; o loopback usual é 127.0.0.1.""" + h = (raw or "").strip().strip("'\"") + if h == "127.0.0.0": + _LOG.warning( + "DB_HOST era '127.0.0.0'; a usar '127.0.0.1'. Corrija o .env para evitar este aviso." + ) + return "127.0.0.1" + return h + @dataclass(frozen=True) class Settings: @@ -33,6 +47,17 @@ class Settings: db_name: str db_user: str db_password: str + # Thresholds da política de retrieval (ver engine/retrieval.py e o plano + # rag_acl_incremental). Todos devem ser recalibrados com amostra manual + # antes de serem tratados como definitivos. + retrieval_min_score: float + retrieval_min_score_margin: float + retrieval_min_coverage: float + retrieval_min_coverage_weighted: float + retrieval_min_terms: int + retrieval_candidate_k: int + retrieval_top_k: int + retrieval_max_chunks_per_source: int @property def openrouter_headers(self) -> dict[str, str]: @@ -55,9 +80,9 @@ def load(cls) -> Settings: content_dir.mkdir(exist_ok=True) models = ( - "arcee-ai/trinity-large-preview:free", - "google/gemini-2.5-flash:free", - "meta-llama/llama-3.3-70b-instruct:free", + "openrouter/free", + "deepseek/deepseek-r1:free", + "meta-llama/llama-4-maverick:free", ) prompts_dir = Path(__file__).resolve().parent / "systemPrompt" @@ -107,9 +132,38 @@ def load(cls) -> Settings: raise RuntimeError("ACL_PINNED_WEAK_SCORE deve ser um número.") from None pinned_weak_score = max(0.05, min(0.95, pinned_weak_score)) + def _env_float(name: str, default: float, lo: float, hi: float) -> float: + raw = (os.getenv(name) or str(default)).strip() + try: + v = float(raw) + except ValueError: + raise RuntimeError(f"{name} deve ser um número.") from None + return max(lo, min(hi, v)) + + def _env_int(name: str, default: int, lo: int, hi: int) -> int: + raw = (os.getenv(name) or str(default)).strip() + try: + v = int(raw) + except ValueError: + raise RuntimeError(f"{name} deve ser um inteiro.") from None + return max(lo, min(hi, v)) + + retrieval_min_score = _env_float("ACL_RETRIEVAL_MIN_SCORE", 1.5, 0.0, 50.0) + retrieval_min_score_margin = _env_float("ACL_RETRIEVAL_MIN_SCORE_MARGIN", 0.15, 0.0, 5.0) + retrieval_min_coverage = _env_float("ACL_RETRIEVAL_MIN_COVERAGE", 0.34, 0.0, 1.0) + retrieval_min_coverage_weighted = _env_float( + "ACL_RETRIEVAL_MIN_COVERAGE_WEIGHTED", 0.34, 0.0, 1.0 + ) + retrieval_min_terms = _env_int("ACL_RETRIEVAL_MIN_TERMS", 2, 1, 10) + retrieval_candidate_k = _env_int("ACL_RETRIEVAL_CANDIDATE_K", 8, 1, 50) + retrieval_top_k = _env_int("ACL_RETRIEVAL_TOP_K", 4, 1, 20) + retrieval_max_chunks_per_source = _env_int( + "ACL_RETRIEVAL_MAX_CHUNKS_PER_SOURCE", 2, 1, 10 + ) + """ !Credenciais do banco! """ - db_host = (os.getenv("DB_HOST") or "").strip() + db_host = _normalize_db_host(os.getenv("DB_HOST") or "") db_port_raw = (os.getenv("DB_PORT") or "3306").strip() @@ -143,4 +197,12 @@ def load(cls) -> Settings: db_name=db_name, db_user=db_user, db_password=db_password, + retrieval_min_score=retrieval_min_score, + retrieval_min_score_margin=retrieval_min_score_margin, + retrieval_min_coverage=retrieval_min_coverage, + retrieval_min_coverage_weighted=retrieval_min_coverage_weighted, + retrieval_min_terms=retrieval_min_terms, + retrieval_candidate_k=retrieval_candidate_k, + retrieval_top_k=retrieval_top_k, + retrieval_max_chunks_per_source=retrieval_max_chunks_per_source, ) diff --git a/core/logging_config.py b/core/logging_config.py index a1957bf..7cfc829 100644 --- a/core/logging_config.py +++ b/core/logging_config.py @@ -3,15 +3,25 @@ from __future__ import annotations import logging +import os +from core.structured_log import AclLogFormatter -def configure_logging(level: int = logging.INFO) -> None: - """Configura o root logger uma vez; loggers filhos herdam o handler.""" + +def configure_logging(level: int | None = None) -> None: + """Configura o root uma vez. + + - ``ACL_LOG_FORMAT=json`` — uma linha JSON por evento ACL (``log_event``). + - ``ACL_LOG_LEVEL=DEBUG`` — inclui eventos DEBUG (ex.: inputs antes dos gates). + """ root = logging.getLogger() if root.handlers: return - logging.basicConfig( - level=level, - format="%(asctime)s %(levelname)-8s [%(name)s] %(message)s", - datefmt="%H:%M:%S", - ) + if level is None: + name = (os.getenv("ACL_LOG_LEVEL") or "INFO").strip().upper() + level = getattr(logging, name, logging.INFO) + json_mode = (os.getenv("ACL_LOG_FORMAT") or "text").strip().lower() == "json" + handler = logging.StreamHandler() + handler.setFormatter(AclLogFormatter(json_mode=json_mode)) + root.addHandler(handler) + root.setLevel(level) diff --git a/core/structured_log.py b/core/structured_log.py new file mode 100644 index 0000000..bf36f38 --- /dev/null +++ b/core/structured_log.py @@ -0,0 +1,109 @@ +"""Logging estruturado (JSON ou texto) para auditoria e debugging do ACL. + +Campos estáveis em ``metadata`` (snake_case): + query, top_score, second_score, score_margin, decision, reason, confidence, + mode, discipline_filter, informative_terms, coverage, coverage_weighted, + candidate_count, selected_sources, llm_called, tokens_used, model, ... + +Variável de ambiente: ACL_LOG_FORMAT=json | text (default: text). +""" + +from __future__ import annotations + +import datetime as _dt +import json +import logging +import os +from typing import Any, Mapping + +# Módulos lógicos (filtro em ferramentas: module=...) +ACL_MOD_SEARCH = "search" +ACL_MOD_CONTEXT = "context" +ACL_MOD_DECISION = "decision" +ACL_MOD_PROVIDER = "provider" +ACL_MOD_EVALUATION = "evaluation" +ACL_MOD_DATABASE = "database" + +ACL_EXTRA = "acl_payload" +_MAX_QUERY_META = 512 +_MAX_LIST_META = 24 + + +def _truncate(s: str, n: int) -> str: + s = str(s) + if len(s) <= n: + return s + return s[: n - 3] + "..." + + +def _sanitize_metadata(meta: Mapping[str, Any] | None) -> dict[str, Any]: + if not meta: + return {} + out: dict[str, Any] = {} + for k, v in meta.items(): + if k == "query" and isinstance(v, str): + out[k] = _truncate(v, _MAX_QUERY_META) + elif k == "debug" and isinstance(v, dict): + keys = list(v.keys())[:18] + out[k] = {kk: v[kk] for kk in keys} + if len(v) > len(keys): + out[k]["_truncated"] = len(v) - len(keys) + elif isinstance(v, (list, tuple)) and len(v) > _MAX_LIST_META: + out[k] = list(v[:_MAX_LIST_META]) + [f"...(+{len(v) - _MAX_LIST_META})"] + else: + out[k] = v + return out + + +def log_event( + logger: logging.Logger, + level: int, + module: str, + event: str, + message: str, + metadata: Mapping[str, Any] | None = None, +) -> None: + """Um evento ACL = um registo com payload em ``extra`` (Formatter consome).""" + payload = { + "module": module, + "event": event, + "message": message, + "metadata": _sanitize_metadata(dict(metadata) if metadata else None), + } + logger.log(level, message, extra={ACL_EXTRA: payload}) + + +class AclLogFormatter(logging.Formatter): + """Se o record tiver ``acl_payload``, formata JSON ou linha compacta; senão legado.""" + + def __init__(self, json_mode: bool, datefmt: str | None = None) -> None: + super().__init__( + fmt="%(asctime)s %(levelname)s [%(name)s] %(message)s", + datefmt=datefmt or "%H:%M:%S", + ) + self._json_mode = json_mode + + def format(self, record: logging.LogRecord) -> str: + pl = getattr(record, ACL_EXTRA, None) + if isinstance(pl, dict): + ts = _dt.datetime.fromtimestamp( + record.created, tz=_dt.timezone.utc + ).isoformat(timespec="milliseconds") + if self._json_mode: + line: dict[str, Any] = { + "timestamp": ts, + "level": record.levelname, + "module": pl["module"], + "event": pl["event"], + "message": pl["message"], + "metadata": pl.get("metadata") or {}, + } + return json.dumps(line, ensure_ascii=False, default=str) + meta = pl.get("metadata") or {} + parts = [f"{k}={v!r}" for k, v in list(meta.items())[:14]] + tail = " ".join(parts) + return ( + f"{ts} {record.levelname:7} [{pl['module']}] {pl['event']} | {pl['message']}" + + (f" | {tail}" if tail else "") + ) + return super().format(record) diff --git a/core/systemPrompt/system_prompt.txt b/core/systemPrompt/system_prompt.txt index abd1b17..911d6e7 100644 --- a/core/systemPrompt/system_prompt.txt +++ b/core/systemPrompt/system_prompt.txt @@ -6,6 +6,15 @@ Você é o **ACL (Agente de Contexto Local)**: um assistente que ajuda a **enten - Adote um tom **didático**: explique o raciocínio, defina termos quando forem úteis ao leitor, e estruture a resposta para que seja fácil seguir (por exemplo: contexto breve → ideia central → detalhes ou passos → resumo ou próximos passos). - Prefira **parágrafos curtos** e **listas** quando isso reduzir carga cognitiva. Use exemplos só quando realmente clarificarem; evite exemplos longos sem pedido explícito. +## Modo local estrito (padrão do sistema) + +Por padrão, o ACL opera em **modo local estrito**: + +1. **Responda APENAS com base nos trechos fornecidos** no system prompt (marcados com `[Fonte: ...]`). +2. Se a resposta exigir qualquer informação que **não esteja explicitamente** nos trechos, diga que **não há informação suficiente na base** e sugira o que o usuário pode detalhar na próxima pergunta. +3. **Não faça suposições**. **Não complete lacunas com conhecimento geral**. **Não invente** nomes de arquivos, comandos, APIs ou comportamentos. +4. Quando o system prompt **não** contiver trechos de fonte, responda apenas que o sistema não encontrou base suficiente; não produza um tutorial genérico sobre o tema. + ## Uso do material local Quando a mensagem do sistema incluir trechos marcados com **Fonte** (e por vezes **Score**), isso vem da **base de conhecimento local** (ficheiros Markdown do projeto). Nesse caso: @@ -14,12 +23,11 @@ Quando a mensagem do sistema incluir trechos marcados com **Fonte** (e por vezes 2. **Explique** ao leitor *o que* concluiu a partir de cada parte relevante, em linguagem acessível. 3. Se o excerto for **insuficiente** ou **ambíguo**, diga-o com franqueza e indique o que falta (por exemplo: outro ficheiro, teste, ou trecho) em vez de inventar detalhes. -Quando **não** houver esse material no system, pode usar conhecimento geral, deixando claro que não está a citar ficheiros do projeto. - ## Citação e honestidade - Quando a resposta se apoiar no texto local, **mencione a fonte** (caminho ou nome do ficheiro) para o leitor poder confirmar. - Não atribua ao projeto comportamentos ou ficheiros que **não** apareçam nas fontes ou no enunciado. +- Se não souber, diga que não sabe. Preferir “não encontrei isso na base” a dar uma resposta plausível mas sem suporte. ## Segurança e limites diff --git a/documentation.md b/documentation.md index f7a81ed..d7d51cb 100644 --- a/documentation.md +++ b/documentation.md @@ -1,363 +1,190 @@ -# ACL — Agente de Contexto Local - -> Chatbot RAG com indexação BM25, file-watching reativo e streaming SSE via OpenRouter. Backend modular (`main.py` + pacotes `core`, `engine`, `api`, `app`). - ---- - ## Propósito do sistema -O **ACL (Agente de Contexto Local)** existe para transformar um diretório local de arquivos Markdown (`content/`) em base de conhecimento consultável via chat: o usuário envia texto, o backend decide se injeta contexto recuperado por BM25 (e modos `/content` e `/doc`), e um LLM acessível pela **OpenRouter** responde em **streaming** (SSE). Não há banco de dados nem fila: índice e chunks ficam em memória no processo. - -O público-alvo são desenvolvedores ou equipes que querem um assistente “no seu corpus” com deploy simples (Python + `.env`). - -### Síntese das entregas documentadas neste ficheiro - -As secções **Registro de alterações** abaixo descrevem, por ordem cronológica aproximada de evolução do produto: - -| # | Tema | Resumo do que foi feito | -|---|------|-------------------------| -| 1 | **Refatoração modular** | Saída de `app.py` monolítico para `main.py` + pacotes `core`, `engine`, `api`, `app`; `Settings` tipado, logging por área, `SearchEngine`, `ContentWatcher`, rotas com estado injetado. | -| 2 | **Silos BM25 e comandos** | Índice por pasta sob `content/`, silo `geral`, comandos `/doc`, `/content`, prefixos de disciplina, JSON `discipline`, `ACL_GLOBAL_CONTEXT` (`geral` / `all`). | -| 3 | **Feedback e rastreabilidade na UI** | `BuildMessagesResult` / `ContextTrace`, primeiro evento SSE `[ACL_META]` (`label`, `sources`), estado “Analisando resumos…”, breadcrumbs, chip de silo no input, `onFirstToken` para ocultar o estado intermédio no primeiro token do modelo. | -| 4 | **Contexto fixado por sessão** | `PinnedSessionStore` em memória, `session_id` no `POST /chat`, pin após RAG forte ou `/doc`/fallback, reutilização em perguntas de acompanhamento quando BM25 falha ou é fraco, `/reset` e `/limpar`, expiração por turnos, campos `pinned_active` / `pinned_display` no meta e badge **“Contexto: …”** no cabeçalho. | - -O restante deste documento (**Arquitetura**, **APIs**, **Fluxos**, **Glossário**) detalha o estado atual do sistema e alinha nomes com o código. - -### Alterações recentes: refatoração modular - -Até a refatoração, praticamente toda a lógica vivia em um único **`app.py`** (config, BM25, watchdog, montagem de mensagens, streaming e rotas FastAPI). - -**O que mudou (estado atual):** - -| Antes | Depois | -|--------|--------| -| `app.py` monolítico | `main.py` orquestra serviços e expõe `app` ao Uvicorn | -| Constantes e `load_dotenv` no topo do módulo | `core/config.py` — `Settings` tipado e `Settings.load()` | -| `logging.basicConfig` + logger único | `core/logging_config.py` — `configure_logging()`; loggers `kernelbots.*` por área | -| `BM25Index` global | `engine/search.py` — classe `SearchEngine` (mesma semântica: chunks, lock, threshold) | -| Watchdog acoplado ao módulo | `engine/watcher.py` — `ContentWatcher` + `start_content_observer()` | -| `_build_messages` | `engine/context.py` — `ContextManager.build_messages()` | -| `_stream_response` | `engine/chat_provider.py` — `ChatProvider.stream_response()` | -| Rotas no mesmo arquivo | `api/routes.py` — `APIRouter`; dependências via `request.app.state` | -| `Jinja2Templates("templates")` (CWD) | `app/factory.py` — `create_app()` com path **absoluto** para `templates/` | -| `uvicorn app:app` / `python app.py` | `uvicorn main:app` / `python main.py` | - -**O que foi preservado (sem regressão intencional):** - -- Endpoints `GET /` e `POST /chat`, status codes e corpo de erro para JSON inválido e `message` vazio. -- Threshold BM25 **0,7** (configurável no código via `Settings`), lista de modelos e fallback SSE (`[DONE]` / `[ERROR]`). -- Debounce do watchdog **1,5 s**. - -**Testes:** smoke em `tests/test_smoke.py` (`pytest`); validação manual com `import main` e subida do servidor. - -### Registro de alterações: silos BM25, filtro `discipline` e comandos de pasta +O **ACL (KernelBot)** é um chatbot com **RAG lexical (BM25)** que responde via **LLM (OpenRouter)**, mas com uma regra explícita de segurança: **se o retrieval não tiver confiança suficiente, ele não chama o modelo** (hard stop). -Esta secção consolida **tudo o que foi alterado** em relação ao desenho anterior (apenas `content/*.md` plano e `/doc` por nome de ficheiro). +Na prática, o sistema transforma o conteúdo de uma tabela MySQL chamada **`knowledge`** (aulas por disciplina) em um índice BM25 **em memória**, particionado por **silos** (disciplinas). A cada pergunta, ele escolhe um escopo (global, disciplina, `/doc`) e só gera resposta quando os gates de retrieval (score, coverage, ambiguidade) permitem. -| Área | Ficheiros | O que mudou | -|------|-----------|-------------| -| **Índice BM25** | `engine/search.py` | Deixou de ser um único índice sobre `content/*.md`. Passou a haver **um `BM25Okapi` por silo**: subpastas **diretas** de `content/` (nome sem `.` inicial), mais o silo lógico **`geral`** (`content/*.md` + `content/geral/**/*.md`). Cada chunk tem `source` (caminho POSIX relativo a `content/`) e `discipline`. Métodos novos/alterados: `normalize_discipline`, `chunks_for_scope`, `search(..., discipline_filter=...)`, propriedade `discipline_ids`. Modo global sem filtro: `Settings.global_context_mode` **`geral`** (só silo geral) ou **`all`** (até `top_k` hits por silo com score normalizado no silo, fusão e corte global em `top_k`). Uso de **`threading.RLock`** para evitar deadlock entre `search` e `normalize_discipline`. | -| **Configuração** | `core/config.py` | Novo campo `global_context_mode` em `Settings`. Variável de ambiente **`ACL_GLOBAL_CONTEXT`**: `geral` (omissão) ou `all`; outro valor → **erro ao arrancar** (`RuntimeError`). | -| **Arranque** | `main.py` | `SearchEngine` passa a receber `settings.global_context_mode`. Log de arranque inclui o modo de contexto global. | -| **Watchdog** | `engine/watcher.py` | `observer.schedule(..., recursive=True)` para detectar alterações em `content//**/*.md`, não só na raiz de `content/`. | -| **Contexto / RAG** | `engine/context.py` | `build_messages(user_message, discipline_filter=None)`: filtro opcional alinhado ao motor (sanitização via `normalize_discipline`). **Prefixos de disciplina** na mensagem: `/python`, `/visualizacao-sql`, `/projeto-bloco`, `/planejamento-curso-carreira` — forçam RAG **só** nesse silo (lista fixa em `_DISCIPLINE_COMMAND_PREFIXES`, ordenada do prefixo mais longo para o mais curto; após o comando exige-se **espaço ou fim de string**, para não confundir com `/pythonfoo`). **`/doc`**: deixou de filtrar por `documentation.md` em qualquer lado; passa a injetar **todos** os chunks com `discipline == "doc"` (i.e. `content/doc/**/*.md`). **`/content`**: mantém RAG forçado no **escopo global** (conforme `ACL_GLOBAL_CONTEXT`), não numa disciplina isolada. **Precedência:** prefixo de disciplina na mensagem **prevalece** sobre o campo JSON `discipline`. Fallback quando há RAG forçado mas sem hits BM25: top-5 chunks de **`chunks_for_scope`** (mesmo universo que a busca). | -| **API HTTP** | `api/routes.py` | Corpo JSON opcional **`discipline`** (string). Tipo inválido → **400**. Disciplina inválida ou desconhecida → **ignorada** (*warning* no log), fluxo do chat continua. | -| **Interface** | `templates/index.html` | Texto de ajuda e **pills** de exemplo para `/python`, `/visualizacao-sql`, `/projeto-bloco`, `/planejamento-curso-carreira`, `/doc`, `/content`. O cliente `frontend/src/api.js` envia, por omissão, **só** `message`; o utilizador usa os prefixos no texto ou um cliente pode enviar `discipline` no JSON. | -| **Testes** | `tests/test_smoke.py` | Cobertura acrescentada: isolamento de silo com `discipline_filter`, sanitização (`..`, paths, desconhecido), modo global `geral` sem ficheiros na raiz, tipo não-string de `discipline` → 400, `/doc` só silo `doc`, prefixo `/python` vs `/pythonfoo`, RAG com prefixo de disciplina. | - -**Extensão futura:** novas disciplinas no disco não ganham comando `/nome` automático — é preciso acrescentar o par `("/nome", "nome")` em `_DISCIPLINE_COMMAND_PREFIXES` em `engine/context.py` (manter ordem: prefixos mais longos primeiro). - -### Registro de alterações: feedback na UI, rastreabilidade e `ACL_META` - -Esta secção descreve a entrega de **feedback enquanto o contexto é preparado**, **breadcrumbs** com ficheiros usados no system prompt, **indicação do silo/comando** na área de input e o **contrato SSE** associado. Comportamento implementado no código referenciado abaixo. - -| Área | Ficheiros | O que mudou | -|------|-----------|-------------| -| **Contexto e metadados** | `engine/context.py` | `build_messages` passou a devolver **`BuildMessagesResult`**: campo **`messages`** (lista para o LLM) e **`trace`** (`**ContextTrace**`: `label` legível, `sources` como tuplo de caminhos relativos a `content/`, **deduplicados** e **limitados a 20**). O `trace` é preenchido em todos os ramos: modo `/doc` (fontes dos chunks do silo `doc`), RAG com hits BM25 (fontes dos hits), RAG forçado sem hits (fontes do fallback top-5 de `chunks_for_scope`), modo assistente geral sem contexto local (`sources` vazio, rótulo adequado). | -| **Streaming SSE** | `engine/chat_provider.py` | `stream_response(messages, trace=…)` aceita o `ContextTrace`. **Antes** de contactar a OpenRouter, emite **uma** linha `data: [ACL_META]` seguida de JSON UTF-8 (`v`, `label`, `sources`; e, em versões atuais, `pinned_active`, `pinned_display`). Os tokens do modelo seguem no formato já existente (`\n` escapado como `\n` no payload). | -| **Rota** | `api/routes.py` | Desempacota `built = context_manager.build_messages(…)` e chama `chat_provider.stream_response(built.messages, trace=built.trace)`. | -| **Cliente HTTP** | `frontend/src/api.js` | `sendStream` aceita **`onMeta`** (payload JSON após o prefixo `[ACL_META]`) e **`onFirstToken`** (disparado quando o primeiro carácter de texto do modelo é acrescentado ao acumulado — **não** no evento de metadados). Linhas `[ACL_META]` **não** entram em `fullText`. | -| **UI do chat** | `frontend/src/ui.js`, `frontend/src/components/MessageRow.js`, `frontend/src/components/ChatView.js` | Após a mensagem do utilizador: bloco **“Analisando resumos de …”** (rótulo imediato via `frontend/src/utils/contextLabel.js`, alinhável ao `label` do servidor quando `onMeta` chega). Esse bloco **oculta-se com transição** no primeiro token (`onFirstToken`). Cada resposta do bot pode mostrar **breadcrumbs** (`Pasta > Subpasta > Ficheiro.md`) acima da bolha, com estilo muted; lista limitada na UI (ex.: 5 linhas + “+N”). | -| **Silo ativo** | `frontend/src/ui.js`, `templates/index.html`, `frontend/assets/css/theme.css` | O texto do `textarea` é analisado com a **mesma ideia de prefixos** que o servidor (`/doc`, `/content`, comandos de disciplina). Aplica-se classe na área de input e um **chip** textual (“Silo: …”) quando um comando é reconhecido. | -| **Histórico** | `frontend/src/utils/history.js` (formato), `ChatView.js` | Entradas `bot` podem incluir **`sources`** para reapresentar breadcrumbs ao recarregar (`sessionStorage`, chave existente). | -| **Testes** | `tests/test_smoke.py` | Chamadas a `build_messages` usam `built.messages` / `built.trace`; asserts de `trace` em cenários `/doc` e `/python`. | - -**Notas de integração** - -- Clientes que ignorem `[ACL_META]` e tratarem qualquer `data:` como texto do modelo verão comportamento incorreto; o frontend do repositório é o consumidor previsto. -- O rótulo imediato no browser para `/content` não conhece `ACL_GLOBAL_CONTEXT`; o valor **oficial** do contexto global continua a vir no `label` de `onMeta` após a resposta do servidor. - -### Registro de alterações: contexto fixado por sessão (“pinned context”) - -Objetivo: reduzir **perda de contexto** em perguntas curtas de acompanhamento (ex.: após `/python … aula 15`, a mensagem “E o AT?”) quando o BM25 da nova frase **não devolve hits** ou devolve scores **abaixo** de um limiar configurável. - -| Área | Ficheiros | O que mudou | -|------|-----------|-------------| -| **Armazenamento** | `engine/pinned_store.py` | `PinnedSessionStore` em memória, thread-safe: por `session_id` guarda `scope_key` (`doc`, `content`, `discipline:`), lista de chunks `{source, text}` (truncada por tamanho), `display_name` (para UI) e `turns_left`. `begin_turn` consome **um turno** no início de cada mensagem com sessão; ao chegar a zero o pin é removido. | -| **Configuração** | `core/config.py` | Novos campos em `Settings`: `pinned_max_turns` (`ACL_PINNED_MAX_TURNS`, default **5**), `pinned_max_chars` (`ACL_PINNED_MAX_CHARS`, default **24000**), `pinned_weak_score` (`ACL_PINNED_WEAK_SCORE`, default **0,4**) — limiar de score BM25 **normalizado no silo** abaixo do qual os hits são tratados como “fracos” para efeito de substituição pelo pin. | -| **Montagem de mensagens** | `engine/context.py` | `build_messages(..., session_id=None)` integra o store: **`/reset`** ou **`/limpar`** no início da mensagem limpa o pin; mudança de **âmbito explícito** (ex.: `/python` → `/doc`, ou JSON `discipline` diferente do pin) invalida o pin; mensagens **sem** comando nem `discipline` no JSON **herdam** a disciplina do pin (`discipline:…`) para a busca BM25. Se houver hits **fortes**, o system prompt usa-os e **atualiza** o pin; se **não houver hits**, hits fracos (com pin), ou modo geral sem hits mas com pin válido, injeta-se o **conteúdo fixado** com instrução explícita a priorizar esse material. | -| **Serviços** | `main.py`, `app/state.py` | `PinnedSessionStore` criado no arranque e injetado em `ContextManager` e `AppServices`. | -| **API** | `api/routes.py` | Campo opcional **`session_id`** (string **8–128** caracteres: letras, dígitos, `_`, `-`); formato ou tipo inválido → **400**. | -| **`ContextTrace` / SSE** | `engine/context.py`, `engine/chat_provider.py` | `trace` inclui `pinned_active` e `pinned_display`; `[ACL_META]` envia os mesmos campos para o cliente. | -| **Frontend** | `frontend/src/utils/sessionId.js`, `frontend/src/api.js`, `frontend/src/ui.js`, `templates/index.html`, `theme.css` | UUID (hex 32) persistido em `sessionStorage` (`acl_session_id`); cada `POST /chat` envia `session_id`. Badge **“Contexto: …”** no cabeçalho quando `pinned_active` e `pinned_display` vêm no meta. | -| **Testes** | `tests/test_smoke.py` | Cenários: follow-up sem hits reutiliza texto fixado; `/reset` limpa sessão; `session_id` inválido → 400; `AppServices` inclui `pinned_store`. | - -**Limites:** o estado é **só na RAM do processo**; reiniciar o servidor ou usar outro dispositivo sem o mesmo `session_id` perde o pin. Não há deteção semântica de “mudou de assunto” além de comandos, JSON `discipline`, expiração por turnos e limiar BM25. - ---- +Fora de escopo (neste repo, no estado atual): pipeline de ingestão, schema SQL versionado e suíte de testes automatizados. Este `documentation.md` documenta **o que está implementado no código hoje**, sem inferir arquivos/pastas inexistentes. ## Arquitetura -### Visão geral e stack +### Stack | Camada | Tecnologia | -|--------|------------| +|---|---| | Servidor HTTP | FastAPI + Uvicorn | -| Índice | BM25Okapi (`rank-bm25`) | -| File watching | Watchdog (thread em background) | -| LLM | OpenRouter (`httpx` async, stream) | -| UI servida | Jinja2 (`templates/index.html`) + arquivos estáticos opcionais em `frontend/` | -| Streaming | Server-Sent Events (SSE) | - -### Estrutura de pastas - -``` -KernelBots/ -├── main.py # Logging, Settings, SearchEngine, observer, AppServices, create_app -├── core/ # Settings (env), logging_config -├── engine/ # SearchEngine, ContentWatcher, ContextManager, ChatProvider, PinnedSessionStore -├── api/ # Rotas FastAPI -├── app/ # create_app(), mounts estáticos, lifespan -├── frontend/ # Opcional: /assets, /src montados por create_app -├── content/ # Silos = subpastas diretas; BM25 por disciplina (+ silo lógico geral) -├── templates/ # index.html (shell da UI) -├── tests/ -├── requirements.txt -└── .env # OPENROUTER_API_KEY (obrigatório) -``` +| Retrieval | BM25Okapi (`rank-bm25`) — in-memory | +| Dados | MySQL (lido via `PyMySQL`) | +| Gateway LLM | OpenRouter (streaming via `httpx`) | +| Frontend | HTML estático (`templates/index.html`) + JS modular em `frontend/src/` | +| Streaming | SSE (`text/event-stream`) | +| Logs | `logging` stdlib + payload estruturado (texto/JSON) | -### Diagrama lógico +### Visão de componentes ```mermaid flowchart TD - subgraph Frontend["Cliente (navegador)"] - UI["templates/index.html\n+ frontend estático"] - SSE["Leitor de stream SSE"] - end - - subgraph Backend["FastAPI (main:app)"] - EP_HOME["GET /"] - EP_CHAT["POST /chat"] - ROUTER["ContextManager.build_messages()"] - BM25["SearchEngine"] - WATCHER["ContentWatcher"] - STREAM["ChatProvider.stream_response()"] - end - - subgraph External["Externo"] - OR["OpenRouter"] - end - - subgraph FS["Filesystem"] - MD["content/**/*.md\n(silos + geral)"] - end - - UI -->|POST JSON| EP_CHAT - EP_HOME -->|HTML| UI - EP_CHAT --> ROUTER - ROUTER --> BM25 - BM25 --> ROUTER - ROUTER --> STREAM - STREAM --> SSE - SSE --> UI - WATCHER --> BM25 - MD --> BM25 - MD --> WATCHER - STREAM --> OR + UI["Browser UI\n(templates/index.html + frontend/src)"] -->|POST /chat (JSON)| API["FastAPI /chat (SSE)"] + API --> CM["ContextManager.build_messages()"] + CM --> SE["SearchEngine.search_candidates()\nBM25 por silo"] + SE --> DB["MySQL\nknowledge WHERE active=1"] + CM -->|messages + decision + trace| CP["ChatProvider.stream_response()\nOpenRouter streaming"] + CP -->|SSE tokens + ACL_META| UI ``` -### Injeção de estado (sem globais de domínio nas rotas) - -- `main.py` instancia `AppServices` (`search_engine`, `context_manager`, `chat_provider`, `observer`, `pinned_store`) e chama `create_app(services)`. -- `create_app` grava `app.state.services` e `app.state.templates`. -- Handlers em `api/routes.py` leem `request.app.state.services` e `request.app.state.templates`. - -### `create_app` e arquivos estáticos - -Em `app/factory.py`, se existirem `frontend/assets` e `frontend/src`, são montados em **`/assets`** e **`/src`**, permitindo que `index.html` referencie CSS/JS modulares sem depender do diretório de trabalho atual. - -### Ciclo de vida do watchdog - -- `Observer` é iniciado em **`main.py`** após criar o `SearchEngine`. -- No **shutdown** do FastAPI (`lifespan` em `app/factory.py`), chamam-se `observer.stop()` e `observer.join()`. - -### Módulos do motor (`engine/`) - -#### `SearchEngine` (BM25 multi-silo) +### Estrutura do repositório (real no workspace) -- **Silos:** cada subpasta direta de `content/` (nome sem `.` inicial) é uma disciplina com índice próprio; o silo lógico **`geral`** reúne `content/*.md` e `content/geral/**/*.md`. -- Chunks incluem `source` (caminho relativo a `content/`, ex.: `python/aula-01.md`) e `discipline`. -- `normalize_discipline` valida contra pastas reais e rejeita caracteres fora de `[A-Za-z0-9_-]` (proteção a path traversal). -- `search(query, top_k=3, discipline_filter=None)`: com filtro válido, busca só nesse silo; sem filtro, aplica `Settings.global_context_mode`: **`geral`** → só silo geral; **`all`** → até `top_k` candidatos por silo (score normalizado no silo), fusão global ordenada e corte em `top_k`. -- `chunks_for_scope(discipline_filter)` devolve a lista de chunks do mesmo universo usado no fallback (uma disciplina, ou geral, ou união ordenada por nome de silo em modo `all`). -- `chunks` expõe a união de todos os silos; `threading.RLock` na leitura/escrita concorrente com `rebuild`. - -#### `ContentWatcher` - -- Debounce **1,5 s** com `threading.Timer` antes de `search_engine.rebuild()`; observação **recursiva** sob `content/` para alterações em subpastas. - -#### `ContextManager` - -- **Prefixos na mensagem:** `/content` (força RAG no escopo global conforme `ACL_GLOBAL_CONTEXT`); `/doc` (injeta **todos** os chunks do silo `doc`, isto é `content/doc/**/*.md`); `/python`, `/visualizacao-sql`, `/projeto-bloco`, `/planejamento-curso-carreira` (forçam RAG **só** nessa disciplina — o comando deve ser seguido de espaço ou fim de linha, ex. `/python` ou `/python o que é for?`, não `/pythonfoo`). -- O campo JSON opcional `discipline` combina com o texto: se a mensagem **não** usar um prefixo de disciplina, vale `discipline` do body; se houver prefixo, ele prevalece sobre o JSON. -- `build_messages(user_message, discipline_filter=None, session_id=None)` devolve **`BuildMessagesResult`**: `messages` para o LLM e **`trace`** (`ContextTrace`: `label`, `sources` deduplicados até 20 entradas, `pinned_active`, `pinned_display`) para breadcrumbs, “Analisando…” e badge de contexto fixado. Com **`session_id`**, usa `PinnedSessionStore`: `/reset` e `/limpar`, expiração por turnos, invalidação ao mudar âmbito (`/doc` vs `/content` vs disciplina), herança da disciplina do pin em mensagens curtas sem comando. -- **`PinnedSessionStore`** (`engine/pinned_store.py`): ver registo “contexto fixado por sessão” em Propósito. - -#### `ChatProvider` - -- POST streaming na OpenRouter; fallback em cadeia nos modelos configurados em `Settings`; emite linhas `data: …` compatíveis com o frontend. -- **Antes** do stream de tokens, envia **uma** linha `data: [ACL_META]` com `v`, `label`, `sources`, `pinned_active`, `pinned_display` (metadados alinhados ao `trace` do `ContextManager`). +``` +KernelBot/ +├── main.py # Entry point: carrega Settings, monta serviços e roda Uvicorn (127.0.0.1:8001) +├── app/ +│ ├── factory.py # create_app(): templates, static mounts, lifespan, routers +│ └── state.py # AppServices (injeção de dependências via app.state) +├── api/ +│ └── routes.py # GET / (UI) e POST /chat (SSE) +├── core/ +│ ├── config.py # Settings.load() a partir do .env + prompts + thresholds +│ ├── logging_config.py # configure_logging() (text/json) +│ ├── structured_log.py # log_event() + formatter com payload ACL +│ └── systemPrompt/ # system_prompt.txt + sticky_instruction.txt (obrigatórios) +├── engine/ +│ ├── search.py # SearchEngine: rebuild, silos BM25, candidatos (raw_score) + whitelist de discipline +│ ├── database.py # SELECT no MySQL e chunking por janela de palavras +│ ├── retrieval.py # gates/decisão (hard stop vs allow_generation) + sanity pós-geração +│ ├── context.py # roteamento (/doc, /content, /python...) + pin por sessão + hard stop +│ ├── chat_provider.py # streaming OpenRouter com fallback + ACL_META + override pós-geração +│ ├── pinned_store.py # PinnedSessionStore em memória (session_id → chunks) +│ └── watcher.py # legado (não integrado ao fluxo atual) +├── templates/ +│ └── index.html # Shell da UI (carrega /src/main.js) +├── frontend/ +│ ├── src/ # UI JS (ChatService, render markdown, histórico, sessão) +│ └── assets/ # CSS e imagens +├── content/ # existe, mas o engine atual não lê arquivos daqui +├── SQL/ # existe no workspace, mas sem artefatos versionados (neste estado) +├── requirements.txt +├── .env / .env.example +└── documentation.md +``` -### Logging +### Decisões de design (trade-offs) -- Configuração central: `core/logging_config.py` (`configure_logging`). -- Nomes estáveis do tipo `kernelbots.engine.search`, `kernelbots.api.chat`, `kernelbots.app`, etc., para filtro em produção. +- **BM25 (lexical) em vez de embeddings**: simples, barato e rápido; trade-off é recall semântico menor e dependência de termos na query. +- **Hard stop no modo strict**: reduz alucinação e custo de tokens; trade-off é mais “recusa” quando a pergunta é vaga/ambígua. +- **Pin por sessão (server-side em memória)**: melhora follow-ups sem re-busca constante; trade-off é que o pin expira por turnos e some ao reiniciar o processo. -### Configuração e operação +## APIs -| Variável (`.env`) | Obrigatório | Descrição | -|-------------------|-------------|-----------| -| `OPENROUTER_API_KEY` | Sim | Chave OpenRouter; ausência → erro na subida. | -| `ACL_GLOBAL_CONTEXT` | Não | `geral` (default) ou `all`: escopo BM25 quando **não** há `discipline` válido no JSON. Valor inválido → erro ao subir. | -| `ACL_PINNED_MAX_TURNS` | Não | Número de **mensagens com sessão** após fixar o contexto antes de expirar o pin (default **5**, limitado entre 1 e 50). | -| `ACL_PINNED_MAX_CHARS` | Não | Teto de caracteres de texto total guardados no pin (default **24000**). | -| `ACL_PINNED_WEAK_SCORE` | Não | Abaixo deste score normalizado BM25, os hits contam como “fracos” e pode usar-se o pin (default **0,4**). | +### Base URL -Dependências principais: ver `requirements.txt` (inclui `pytest` para testes). +Por padrão, o servidor sobe em: ```bash -python main.py -# ou -uvicorn main:app --host 127.0.0.1 --port 8000 --reload +http://127.0.0.1:8001 ``` -Conteúdo novo em qualquer `content/**.md` é detectado pelo watchdog (rebuild em até ~1,5 s após eventos). - -### Limitações conhecidas - -| # | Tema | Nota | -|---|------|------| -| 1 | Estado no servidor | O **histórico de chat** continua só no cliente; o **pin** de RAG fica em **memória** por `session_id` até expirar ou reiniciar o processo. | -| 2 | Threshold BM25 0,7 | Vocabulário distante do corpus pode cair em modo geral; usar `/content` ou um prefixo de disciplina (`/python`, …). | -| 3 | Modelos gratuitos | Rate limit; fallback entre modelos pode esgotar. | -| 4 | Segurança local | Sem autenticação na API; adequado a `127.0.0.1`. | -| 5 | Versões pip | Pacotes sem pin exato em `requirements.txt` — risco de drift. | +Sem autenticação. -### Extensões naturais - -Fixar versões; rate limit no `/chat`; multi-turno no payload; embeddings híbridos; auth básica se exposto além de localhost. - ---- - -## APIs - -Base típica: `http://127.0.0.1:8000` (desenvolvimento). **Não há autenticação** documentada no código. +### Endpoints | Método | Caminho | Descrição | -|--------|---------|-----------| -| `GET` | `/` | Responde `index.html` (Jinja2 em `templates/`). | -| `GET` | `/assets/*` | Arquivos estáticos sob `frontend/assets/`, se existirem. | -| `GET` | `/src/*` | Módulos estáticos sob `frontend/src/`, se existirem. | -| `POST` | `/chat` | Corpo JSON `{"message": "", "discipline": "", "session_id": ""}`; resposta **SSE** (`text/event-stream`). | +|---|---|---| +| `GET` | `/` | Serve a UI (`templates/index.html`) | +| `POST` | `/chat` | Recebe mensagem e retorna SSE (`text/event-stream`) | ### `POST /chat` -- **Content-Type:** `application/json`. -- **Campo:** `message` (string). Vazio ou só espaços → **400** com detalhe explícito. -- **Campo opcional:** `discipline` (string) — nome da pasta sob `content/` ou `geral` para o silo lógico geral. Deve coincidir com uma disciplina conhecida no scan; **string inválida ou desconhecida é ignorada** (filtro tratado como ausente) e gera *warning* no log — o chat não retorna **400** por disciplina desconhecida. -- **Comandos no texto de `message`:** além do JSON, o utilizador pode prefixar a mensagem com `/content`, `/doc`, `/python`, `/visualizacao-sql`, `/projeto-bloco` ou `/planejamento-curso-carreira` (ver `ContextManager` e registo de alterações acima). Isto é **independente** do campo `discipline`, excepto quando o prefixo define a disciplina — nesse caso o prefixo ganha. -- **`discipline` com tipo não string** (ex.: número, array) → **400**. -- **Campo opcional:** `session_id` — identifica a sessão para **contexto fixado**; string 8–128 caracteres `[A-Za-z0-9_-]+`; inválido → **400**; omitido → comportamento sem pin (compatível com clientes antigos). -- **JSON inválido** → **400**. - -### Cliente web (`frontend/`) +**Request body (JSON):** -- `ChatService` em `frontend/src/api.js` envia `message` e, quando existe, **`session_id`** (gerado em `sessionId.js` e guardado em `sessionStorage`). O campo **`discipline`** no JSON continua opcional; na UI típica usa-se o prefixo na caixa de texto (ex.: `/python …`). -- O mesmo módulo interpreta **`[ACL_META]`** no stream (`onMeta`, `onFirstToken`), incluindo **`pinned_active`** e **`pinned_display`** para o badge de contexto fixado. O histórico em `sessionStorage` pode guardar **`sources`** por mensagem do bot para breadcrumbs após recarga. +```json +{ + "message": "string (obrigatório)", + "discipline": "string (opcional)", + "session_id": "string (opcional; 8–128 [A-Za-z0-9_-])" +} +``` -### Contrato SSE (resumo) +Notas: -- **Primeiro evento (opcional mas habitual):** `data: [ACL_META]` seguido de JSON UTF-8 com `v`, `label`, `sources` (como antes), mais **`pinned_active`** (boolean) e **`pinned_display`** (string ou `null` — nome amigável do ficheiro/material fixado). O cliente **não** concatena esta linha ao texto do assistente. -- Linhas seguintes: `data: ` com texto do modelo; newline escapado como `\n` no payload textual. -- Término: `data: [DONE]` ou `data: [ERROR] ` se todos os modelos falharem. +- **`discipline` (JSON)**: se fornecido, passa por whitelist em `SearchEngine.normalize_discipline()` (só aceita valores presentes no DB em `SELECT DISTINCT discipline ...`). +- **`session_id`**: habilita **contexto fixado (pin)**; o frontend gera e persiste em `sessionStorage`. +- **Comando `/reload`**: se `message == "/reload"` (case-insensitive após trim), o backend reconstrói o índice BM25 e retorna um stream curtinho com status. ---- +**Response (`text/event-stream`)**: -## Fluxos +- `data: [ACL_META]{...}\n\n` — metadados (sempre no começo do stream; e pode reaparecer no override pós-geração) +- `data: \n\n` — tokens/trechos (com `\n` escapado como `\\n`) +- `data: [DONE]\n\n` — fim +- `data: [ERROR] ...\n\n` — erro amigável de provider (quando todos os modelos falham) -### 1. Mensagem do usuário até tokens na tela +**Campos relevantes em `ACL_META` (v=2)** (emitidos por `engine/chat_provider.py`): -1. Usuário envia texto no chat → `POST /chat` com `{ "message": "..." }` e, opcionalmente, `{ "discipline": "..." }` e `{ "session_id": "..." }` para contexto fixado. -2. `ContextManager.build_messages` aplica turno de sessão, invalidações (`/reset`, mudança de âmbito), interpreta prefixos, aplica `discipline` (comando > JSON > herança do pin), executa BM25 e, se necessário, reutiliza **chunks fixados**; monta o **`trace`** (rótulo, fontes, pin). -3. `ChatProvider.stream_response(messages, trace)` emite `[ACL_META]` e depois chama OpenRouter em modo stream. -4. O cliente lê o corpo da resposta como stream, aplica metadados à UI (breadcrumbs), interpreta tokens SSE e atualiza a bolha do assistente. +- `label`: rótulo de escopo (ex.: “Python”, “Base geral”, “Documentação (doc)”) +- `sources`: lista de fontes (ex.: `db:python/slug-da-aula`) +- `pinned_active` / `pinned_display`: status do pin da sessão +- `mode`: `strict` (no estado atual) +- `decision`: `answer` ou `hard_stop` +- `reason`: motivo (ex.: `ok`, `insufficient_context`, `context_misaligned`, `provider_error`, `post_generation_misalignment`) +- `confidence`: `high|medium|low` +- `llm_called`: boolean +- `tokens_used`: contador aproximado (incrementa por delta recebido) -**Detalhe de UX (implementação atual):** entre a mensagem do utilizador e a bolha do assistente é mostrado o estado **“Analisando resumos de …”** com o rótulo de contexto; o texto pode ser inferido de imediato no cliente (`contextLabel.js`) e **atualizado** quando chega `onMeta.label`. Os breadcrumbs são preenchidos a partir de `onMeta.sources`. A mensagem intermédia **só desaparece** (transição CSS) quando chega o **primeiro** fragmento de texto do modelo (`onFirstToken`), não quando chegam os metadados. +## Fluxos -### 2. Subida e encerramento do processo +### Fluxo 1 — Inicialização do servidor -1. `main.py` chama `configure_logging()` e `Settings.load()`. -2. Cria `SearchEngine`, inicia `Observer` em `content/`, monta `ContextManager` e `ChatProvider`, depois `app = create_app(services)`. -3. Ao encerrar o servidor, o `lifespan` para o observer de forma ordenada. +1. `main.py` chama `configure_logging()`. +2. `Settings.load()` lê `.env` e valida pré-requisitos: + - `OPENROUTER_API_KEY` é obrigatório. + - `core/systemPrompt/system_prompt.txt` e `core/systemPrompt/sticky_instruction.txt` são obrigatórios. +3. `SearchEngine(...).rebuild()` tenta carregar chunks do MySQL (`engine/database.py`). Se DB não estiver configurado ou estiver inacessível, o índice fica vazio e logs avisam. +4. `create_app()` registra templates, monta estáticos (`/assets`, `/src`) e inclui rotas. +5. Uvicorn sobe em `127.0.0.1:8001`. -### 3. Decisão de contexto (RAG) +### Fluxo 2 — Chat (ponta a ponta) -```mermaid -flowchart TD - A[user_message + discipline JSON opcional] --> D{/doc?} - D -->|sim| DOC[Injetar todos os chunks do silo doc] - D -->|não| B{/content?} - B -->|sim| C[force_rag + escopo global] - B -->|não| P{prefixo /python, /visualizacao-sql, ...?} - P -->|sim| Q[force_rag + disciplina do prefixo] - P -->|não| R[discipline só do JSON se houver] - C --> E[BM25 no escopo] - Q --> E - R --> E - E --> G{hits?} - G -->|sim| H[System + chunks] - G -->|não e force_rag| I[Top-5 chunks do mesmo escopo] - G -->|não| J[System geral] +```text +UI (frontend/src/ui.js) → POST /chat { message, session_id } → SSE stream ``` -O modo **`/doc`** injeta **todo** o Markdown indexado sob `content/doc/` (campo `discipline == "doc"` nos chunks), não apenas um ficheiro com nome fixo. - -### 4. Contexto fixado por sessão (visão resumida) - -1. O cliente gera ou reutiliza um **`session_id`** (ex.: hex em `sessionStorage`) e envia-o em cada `POST /chat`. -2. No **início** de `build_messages`, o servidor consome **um turno** do pin existente (`begin_turn`); se o contador chegar a zero, o pin é removido. -3. Comandos **`/reset`** ou **`/limpar`** no início da mensagem **limpam** o pin; uma mudança explícita de âmbito (ex.: de `/python` para `/doc`) também **invalida** o pin anterior. -4. Após uma recuperação **forte** (hits BM25 acima do limiar fraco) ou injeção `/doc`/fallback `/content`, o servidor **atualiza** o pin com os chunks usados (truncados por `ACL_PINNED_MAX_CHARS`) e reinicia o contador de turnos (`ACL_PINNED_MAX_TURNS`). -5. Mensagens **sem** comando nem `discipline` no JSON **herdam** a disciplina do pin para a busca BM25; se a busca **não devolver hits** ou for **fraca**, o system prompt reutiliza o **texto fixado** com instrução explícita a priorizar esse material. -6. O primeiro evento **`[ACL_META]`** informa `pinned_active` e `pinned_display`; a UI mostra o badge **“Contexto: …”** quando aplicável. - ---- - -## Glossário e referências - -| Termo | Significado | -|-------|-------------| -| **ACL** | Agente de Contexto Local — este produto. | -| **RAG** | Retrieval-Augmented Generation: respostas condicionadas a trechos recuperados do corpus. | -| **BM25** | Função de ranking lexical; aqui via `rank_bm25.BM25Okapi`. | -| **Silo** | Conjunto de ficheiros Markdown e um índice BM25 associado (ex.: pasta `content/python/` ou o silo lógico `geral`). | -| **SSE** | Server-Sent Events — stream unidirecional servidor → cliente. | -| **OpenRouter** | Gateway HTTP para vários modelos; chave em `OPENROUTER_API_KEY`. | -| **AppServices** | Dataclass em `app/state.py` agrupando instâncias injetadas no `app.state`. | -| **ACL_GLOBAL_CONTEXT** | Variável de ambiente que define o escopo BM25 **sem** filtro de disciplina: `geral` ou `all`. | -| **`ACL_META`** | Prefixo no payload SSE (`data: [ACL_META]` + JSON) que transporta metadados para a UI antes dos tokens do LLM: `label`, `sources`, e (estado atual) `pinned_active`, `pinned_display`. | -| **`ContextTrace`** | Dataclass em `engine/context.py`: `label`, `sources`, `pinned_active`, `pinned_display`. | -| **`PinnedSessionStore`** | `engine/pinned_store.py` — mapa em memória `session_id` → chunks fixados e metadados. | -| **`BuildMessagesResult`** | Retorno de `build_messages`: `messages` + `trace`. | - -**Referências no repositório:** `README.md` (comandos rápidos); documentação de produto e arquitetura em `content/doc/` (indexada no modo **`/doc`** como silo `doc`); código em `main.py`, `app/factory.py`, `api/routes.py`, `engine/*`, `core/*`, `templates/index.html`, `frontend/src/*`. +1. O usuário envia uma mensagem. +2. A UI (`frontend/src/api.js`) faz `fetch("/chat")` e lê `res.body.getReader()` processando linhas `data: ...`. +3. O backend (`api/routes.py`) valida JSON, `message`, `discipline` e `session_id`. +4. `ContextManager.build_messages()` decide o escopo e tenta retrieval: + - Comandos de escopo no texto (prefixos): `/doc`, `/content`, `/python`, `/visualizacao-sql`, `/projeto-bloco`, `/planejamento-curso-carreira`. + - Sem comando, ele pode usar o **pin** da sessão para sugerir escopo efetivo (se existir e não conflitar). +5. `SearchEngine.search_candidates()` retorna candidatos BM25 (com `raw_score` e `matched_terms`). +6. `engine/retrieval.build_decision()` aplica gates (score absoluto, margem, coverage, termos mínimos, “vague but high risk”). +7. Se **`allow_generation=False`**: o backend envia **hard stop** via SSE (sem chamar LLM). +8. Se **`allow_generation=True`**: o backend chama OpenRouter em streaming e re-emite tokens via SSE. +9. No final, o provider roda um **sanity check pós-geração** (`post_generation_flags`). Se detectar desalinhamento em modo `strict`, ele emite um `ACL_META` atualizado e anexa uma mensagem de hard stop (`post_generation_misalignment`). +10. A UI renderiza Markdown incremental (via `marked@12` + `highlight.js`) e mostra breadcrumbs a partir de `meta.sources`. + +### Fluxo 3 — Contexto fixado (pin) por sessão + +- O frontend gera um `session_id` (UUID sem hífens) e salva em `sessionStorage` (`frontend/src/utils/sessionId.js`). +- O backend salva os chunks selecionados no `PinnedSessionStore` (em memória), com: + - `turns_left` (expira a cada turno via `begin_turn()`), + - `scope_key` (ex.: `discipline:python`, `doc`, `content`). +- Se o usuário enviar `/reset` ou `/limpar` no começo da mensagem, o backend limpa o pin daquela sessão. + +### Fluxo 4 — Rebuild manual do índice (`/reload`) + +1. Usuário envia `/reload`. +2. `api/routes.py` chama `services.search_engine.rebuild()`. +3. O endpoint retorna um stream curto com: + - `data: Índice reconstruído: ...\n\n` + - `data: [DONE]\n\n` + +## Glossário e referências (opcional) + +- **Silo**: partição lógica do índice por `discipline` (ex.: `python`). +- **Chunk**: janela de ~500 palavras com overlap de 50 (ver `engine/database.py`). +- **Retrieval candidate**: item retornado por `SearchEngine.search_candidates()` com `raw_score` (BM25 cru). +- **Hard stop**: decisão de não chamar o LLM e responder com uma mensagem de reformulação (modo `strict`). +- **Pin**: contexto fixado por sessão em memória (`PinnedSessionStore`). + +Referências no código: + +- Backend: `main.py`, `api/routes.py`, `app/factory.py`, `engine/context.py`, `engine/retrieval.py`, `engine/chat_provider.py` +- Frontend: `templates/index.html`, `frontend/src/api.js`, `frontend/src/ui.js` \ No newline at end of file diff --git a/engine/__init__.py b/engine/__init__.py index 1cec10d..7d51b43 100644 --- a/engine/__init__.py +++ b/engine/__init__.py @@ -1 +1 @@ -"""Motor RAG (BM25), watcher de conteúdo, contexto e streaming LLM.""" +"""Motor RAG (BM25) sobre MySQL, contexto e streaming LLM.""" diff --git a/engine/chat_provider.py b/engine/chat_provider.py index c2ffe8d..a62e116 100644 --- a/engine/chat_provider.py +++ b/engine/chat_provider.py @@ -1,4 +1,17 @@ -"""Streaming SSE para OpenRouter com fallback entre modelos.""" +"""Streaming SSE para OpenRouter com fallback entre modelos. + +Mudanças vs versão anterior: + +- Hard stop não chama LLM. Quando `trace.decision == "hard_stop"`, a última + mensagem `assistant` é a resposta pronta e o provider só faz streaming + dela, economizando tokens e evitando resposta confiante sem base. +- Sanity check pós-geração (Fase 3): depois que o modelo terminou, a + resposta passa por `post_generation_flags`. Se houver flag e o modo for + `strict`, a resposta enviada ao usuário é trocada por + `post_generation_misalignment`. +- `ACL_META` agora também carrega `mode`, `decision`, `reason`, `confidence` + e `llm_called`. +""" from __future__ import annotations @@ -10,11 +23,62 @@ import httpx from core.config import Settings -from engine.context import ContextTrace +from core.structured_log import ACL_MOD_PROVIDER, log_event +from engine.context import ContextTrace, hard_stop_message +from engine.retrieval import RetrievalDecision, post_generation_flags log = logging.getLogger(f"kernelbots.{__name__}") +def _build_meta(trace: ContextTrace | None, llm_called: bool, tokens_used: int) -> dict: + meta: dict = {"v": 2} + if trace is None: + meta.update( + { + "label": "Assistente geral", + "sources": [], + "pinned_active": False, + "pinned_display": None, + "mode": "strict", + "decision": "answer", + "reason": "ok", + "confidence": "high", + "llm_called": llm_called, + "tokens_used": tokens_used, + } + ) + return meta + meta.update( + { + "label": trace.label, + "sources": list(trace.sources), + "pinned_active": trace.pinned_active, + "pinned_display": trace.pinned_display, + "mode": trace.mode, + "decision": trace.decision, + "reason": trace.reason, + "confidence": trace.confidence, + "llm_called": llm_called, + "tokens_used": tokens_used, + } + ) + return meta + + +def _sse_meta(meta: dict) -> str: + return f"data: [ACL_META]{json.dumps(meta, ensure_ascii=False)}\n\n" + + +def _sse_text_chunk(text: str, chunk_size: int = 80) -> list[str]: + """Divide texto em pedaços menores para streaming amigável (UI de chat).""" + out: list[str] = [] + for i in range(0, len(text), chunk_size): + piece = text[i : i + chunk_size] + safe = piece.replace("\n", "\\n") + out.append(f"data: {safe}\n\n") + return out + + class ChatProvider: def __init__(self, settings: Settings) -> None: self._settings = settings @@ -23,7 +87,38 @@ async def stream_response( self, messages: list[dict], trace: ContextTrace | None = None, + decision: RetrievalDecision | None = None, ) -> AsyncGenerator[str, None]: + # --- Hard stop: não chama LLM ---------------------------------------- + is_hard_stop = trace is not None and trace.decision == "hard_stop" + if is_hard_stop: + meta = _build_meta(trace, llm_called=False, tokens_used=0) + yield _sse_meta(meta) + hard_text = "" + # A última mensagem (assistant) carrega a resposta pré-montada. + if messages and messages[-1].get("role") == "assistant": + hard_text = str(messages[-1].get("content") or "") + if not hard_text: + hard_text = hard_stop_message(trace.reason) + log_event( + log, + logging.INFO, + ACL_MOD_PROVIDER, + "llm_skipped_hard_stop", + "stream sem LLM (hard stop retrieval)", + metadata={ + "reason": trace.reason, + "confidence": trace.confidence, + "mode": trace.mode, + "llm_called": False, + "tokens_used": 0, + }, + ) + for piece in _sse_text_chunk(hard_text): + yield piece + yield "data: [DONE]\n\n" + return + payload_base = { "messages": messages, "stream": True, @@ -32,20 +127,30 @@ async def stream_response( models = list(self._settings.models) timeout = self._settings.http_timeout - if trace is not None: - meta = { - "v": 1, - "label": trace.label, - "sources": list(trace.sources), - "pinned_active": trace.pinned_active, - "pinned_display": trace.pinned_display, - } - yield f"data: [ACL_META]{json.dumps(meta, ensure_ascii=False)}\n\n" + # Meta inicial indicando que pretendemos chamar o LLM. Na falha de + # provider trocamos para decision=hard_stop no meta final via + # mensagem [ACL_META_UPDATE] (mantido compatível: frontend ignora + # prefixos desconhecidos). + initial_meta = _build_meta(trace, llm_called=True, tokens_used=0) + yield _sse_meta(initial_meta) + + full_answer: list[str] = [] async with httpx.AsyncClient(timeout=timeout) as client: for attempt, model in enumerate(models, start=1): try: - log.info(f"🤖 Tentativa {attempt}/{len(models)} — modelo: {model}") + log_event( + log, + logging.INFO, + ACL_MOD_PROVIDER, + "llm_attempt", + "tentativa de stream OpenRouter", + metadata={ + "attempt": attempt, + "attempts_total": len(models), + "model": model, + }, + ) t_start = time.perf_counter() token_count = 0 @@ -57,18 +162,40 @@ async def stream_response( ) as response: if response.status_code == 429: - log.warning(f" ⏳ Rate limit (429) em '{model}' — acionando fallback...") + log_event( + log, + logging.WARNING, + ACL_MOD_PROVIDER, + "llm_rate_limited", + "HTTP 429 — fallback para proximo modelo", + metadata={"model": model, "status_code": 429}, + ) continue if response.status_code >= 400: body = await response.aread() - log.error( - f" ❌ HTTP {response.status_code} em '{model}' — " - f"resposta: {body[:300].decode('utf-8', errors='replace')}" + log_event( + log, + logging.ERROR, + ACL_MOD_PROVIDER, + "llm_http_error", + "resposta HTTP de erro do OpenRouter", + metadata={ + "model": model, + "status_code": response.status_code, + "body_preview": body[:300].decode("utf-8", errors="replace"), + }, ) continue - log.info(f" ✅ Conexão estabelecida com '{model}' — iniciando stream...") + log_event( + log, + logging.INFO, + ACL_MOD_PROVIDER, + "llm_stream_opened", + "stream SSE iniciado", + metadata={"model": model}, + ) async for line in response.aiter_lines(): if not line.startswith("data: "): @@ -76,10 +203,22 @@ async def stream_response( raw = line[6:] if raw.strip() == "[DONE]": elapsed = (time.perf_counter() - t_start) * 1000 - log.info( - f" 🏁 Stream finalizado — modelo: '{model}' | " - f"{token_count} token(s) | {elapsed:.0f}ms" + log_event( + log, + logging.INFO, + ACL_MOD_PROVIDER, + "llm_stream_complete", + "stream finalizado ([DONE])", + metadata={ + "model": model, + "tokens_used": token_count, + "elapsed_ms": round(elapsed, 1), + }, ) + async for piece in self._maybe_override_post_generation( + "".join(full_answer), trace, decision, token_count, + ): + yield piece yield "data: [DONE]\n\n" return @@ -88,26 +227,129 @@ async def stream_response( token: str = chunk["choices"][0].get("delta", {}).get("content") or "" if token: token_count += 1 + full_answer.append(token) safe = token.replace("\n", "\\n") yield f"data: {safe}\n\n" except (json.JSONDecodeError, KeyError, IndexError): continue elapsed = (time.perf_counter() - t_start) * 1000 - log.info( - f" 🏁 Stream finalizado (EOF) — modelo: '{model}' | " - f"{token_count} token(s) | {elapsed:.0f}ms" + log_event( + log, + logging.INFO, + ACL_MOD_PROVIDER, + "llm_stream_complete_eof", + "stream terminou sem token [DONE]", + metadata={ + "model": model, + "tokens_used": token_count, + "elapsed_ms": round(elapsed, 1), + }, ) + async for piece in self._maybe_override_post_generation( + "".join(full_answer), trace, decision, token_count, + ): + yield piece yield "data: [DONE]\n\n" return except httpx.TimeoutException: - log.warning(f" ⏰ Timeout ({timeout:.0f}s) em '{model}' — acionando próximo fallback...") + log_event( + log, + logging.WARNING, + ACL_MOD_PROVIDER, + "llm_timeout", + "timeout httpx — fallback", + metadata={"model": model, "timeout_s": timeout}, + ) continue except Exception as e: - log.exception(f" 💥 Erro inesperado em '{model}': {type(e).__name__}: {e}") + log_event( + log, + logging.ERROR, + ACL_MOD_PROVIDER, + "llm_exception", + f"excecao no stream: {type(e).__name__}", + metadata={"model": model, "error": str(e)}, + ) + log.exception("llm_exception detail") continue - msg = "Todos os modelos de fallback falharam. Tente novamente mais tarde." - log.error(f"🚨 {msg} | Modelos tentados: {', '.join(models)}") - yield f"data: [ERROR] {msg}\n\n" + # Todos os modelos falharam: mantém UX amigável e separa do hard stop + # de retrieval via meta atualizada. + friendly = hard_stop_message("provider_error") + failure_meta = _build_meta(trace, llm_called=False, tokens_used=0) + failure_meta.update({"decision": "hard_stop", "reason": "provider_error", "confidence": "low"}) + yield _sse_meta(failure_meta) + log_event( + log, + logging.ERROR, + ACL_MOD_PROVIDER, + "llm_all_models_failed", + "todos os modelos falharam — provider_error ao cliente", + metadata={"models_tried": list(models)}, + ) + for piece in _sse_text_chunk(friendly): + yield piece + yield "data: [DONE]\n\n" + + # --- Fase 3: sanity check pós-geração ---------------------------------- + + async def _maybe_override_post_generation( + self, + answer_text: str, + trace: ContextTrace | None, + decision: RetrievalDecision | None, + tokens_used: int, + ) -> AsyncGenerator[str, None]: + """Aplica override para `post_generation_misalignment` se preciso. + + Executa apenas quando modo=strict, decisão original=answer e há + candidatos selecionados. A resposta original já foi streamada, + então enviamos um meta-update e uma mensagem clara de hard stop. + """ + if trace is None or decision is None: + return + if trace.mode != "strict": + return + if not decision.allow_generation: + return + if not decision.selected_candidates: + return + flags = post_generation_flags( + answer_text, + trace.retrieval_trace.informative_terms if trace.retrieval_trace else (), + decision.selected_candidates, + ) + if not flags: + return + log_event( + log, + logging.WARNING, + ACL_MOD_PROVIDER, + "post_generation_override", + "sanity pos-geracao — resposta substituida", + metadata={ + "flags": list(flags), + "reason": "post_generation_misalignment", + "tokens_used": tokens_used, + }, + ) + updated_meta = _build_meta(trace, llm_called=True, tokens_used=tokens_used) + updated_meta.update( + { + "decision": "hard_stop", + "reason": "post_generation_misalignment", + "confidence": "low", + "post_generation_flags": flags, + } + ) + # Emite um separador textual para evitar que a resposta parcial já + # mostrada fique sem aviso. Usamos meta novo + bloco textual claro. + yield _sse_meta(updated_meta) + override_text = ( + "\n\n---\n\n" + + hard_stop_message("post_generation_misalignment") + ) + for piece in _sse_text_chunk(override_text): + yield piece diff --git a/engine/context.py b/engine/context.py index b981edb..06405b3 100644 --- a/engine/context.py +++ b/engine/context.py @@ -1,14 +1,38 @@ -"""Montagem de mensagens (system + user) para o chat com RAG /doc /content e contexto fixado por sessão.""" +"""Montagem de mensagens (system + user) para o chat com RAG /doc /content. + +Este módulo consome `engine.retrieval.build_decision` e monta o prompt +apenas quando a decisão permitir. Hard stop é tratado diretamente como +resposta ao usuário — não chama o LLM. + +Mudanças vs versão anterior (plano rag_acl_incremental): + +- `/content` NÃO injeta mais `scope_chunks[:5]`. Sem hit suficiente, vira + hard stop com UX de reformulação. +- Pin NÃO ressuscita contexto desalinhado; se o pin existir e a decisão + atual for hard stop por `insufficient_context`, o pin pode entrar como + fonte adicional, mas apenas se a consulta tiver termos informativos + mínimos e o trace continua hard stop caso retrieval falhe. +- `ContextTrace` ganha `mode`, `decision`, `reason`, `confidence` e a + `RetrievalTrace` completa. +""" from __future__ import annotations import logging import re -from dataclasses import dataclass +from dataclasses import dataclass, field from pathlib import Path from core.config import Settings +from core.structured_log import ACL_MOD_CONTEXT, log_event from engine.pinned_store import PinnedContext, PinnedSessionStore +from engine.retrieval import ( + RetrievalDecision, + RetrievalTrace, + build_decision, + extract_informative_terms, + select_mode, +) from engine.search import SearchEngine log = logging.getLogger(f"kernelbots.{__name__}") @@ -35,20 +59,79 @@ _RESET_PREFIX_RE = re.compile(r"^/(?:reset|limpar)\s*", re.IGNORECASE) +# --- Mensagens UX padronizadas (Fase 1/3) ----------------------------------- + +_HARD_STOP_MESSAGES: dict[str, str] = { + "insufficient_context": ( + "Não encontrei informação suficiente na base para responder com segurança.\n\n" + "Tente especificar melhor, por exemplo incluindo tecnologia, contexto ou objetivo." + ), + "context_misaligned": ( + "Encontrei trechos na base, mas eles não cobrem bem a sua pergunta.\n\n" + "Reformule incluindo termos mais específicos sobre o que quer saber." + ), + "underspecified_query": ( + "Sua pergunta está vaga para responder com segurança usando a base.\n\n" + "Use o formato: [tecnologia] + [problema] + [contexto].\n\n" + "Exemplos:\n" + "- SQL + performance + query lenta\n" + "- Docker + erro + build falhando\n" + "- API + timeout + chamada de autenticação" + ), + "vague_but_high_risk": ( + "Sua pergunta pode ter várias interpretações e eu não tenho contexto suficiente " + "para escolher uma com segurança.\n\n" + "Reformule usando: [tecnologia] + [problema] + [contexto]." + ), + "ambiguous_retrieval": ( + "Encontrei conteúdos parecidos na base e não consegui distinguir qual deles " + "realmente responde à sua pergunta.\n\n" + "Adicione detalhes que ajudem a desempatar, como nome do módulo, comando ou tecnologia." + ), + "low_confidence": ( + "Encontrei conteúdo que lembra a sua pergunta, mas a confiança do retrieval ficou baixa " + "e no modo estrito prefiro não arriscar uma resposta incorreta.\n\n" + "Reformule com mais detalhes técnicos ou use um comando de escopo (`/doc`, `/python`, etc.)." + ), + "post_generation_misalignment": ( + "Preparei uma resposta com base nos trechos encontrados, mas a checagem final " + "indicou que ela pode ter saído do escopo das fontes.\n\n" + "Reformule a pergunta com termos mais próximos do material ou tente novamente." + ), + "provider_error": ( + "Tive um problema técnico ao contatar o modelo de linguagem.\n\n" + "Tente novamente em alguns instantes. Se persistir, avise o responsável." + ), +} + + +def hard_stop_message(reason: str) -> str: + return _HARD_STOP_MESSAGES.get( + reason, + "Não consegui responder com segurança agora. Reformule a pergunta e tente novamente.", + ) + + @dataclass(frozen=True) class ContextTrace: - """Metadados para UI: rótulo de contexto, fontes e estado do pin.""" + """Metadados para UI: rótulo de contexto, fontes, pin, decisão e confiança.""" label: str sources: tuple[str, ...] pinned_active: bool = False pinned_display: str | None = None + mode: str = "strict" + decision: str = "answer" + reason: str = "ok" + confidence: str = "high" + retrieval_trace: RetrievalTrace | None = None @dataclass(frozen=True) class BuildMessagesResult: messages: list[dict] trace: ContextTrace + decision: RetrievalDecision | None = None def _dedupe_sources(sources: list[str], limit: int = _SOURCES_CAP) -> tuple[str, ...]: @@ -153,19 +236,21 @@ def _trim_pin_chunks( return out -def _hits_weak(hits: list[dict], weak_score: float) -> bool: - if not hits: - return True - mx = max(float(h["score"]) for h in hits) - return mx < weak_score - - -def _join_pin_chunks(chunks: list[dict[str, str]]) -> str: +def _join_chunks_for_prompt(selected: list[dict[str, str]]) -> str: return "\n\n---\n\n".join( - f"[Fonte: {c['source']}]\n{c['text']}" for c in chunks if c.get("text") + f"[Fonte: {c['source']}]\n{c['text']}" for c in selected if c.get("text") ) +_STRICT_GROUNDING_INSTRUCTION = ( + "Você possui acesso à seguinte base de conhecimento local. " + "Responda APENAS com base nos trechos fornecidos. " + "Se a resposta exigir qualquer informação que não esteja explicitamente nos trechos, " + "responda que não há informação suficiente na base. " + "Não faça suposições. Não complete lacunas com conhecimento geral.\n\n" +) + + class ContextManager: def __init__( self, @@ -239,196 +324,274 @@ def build_messages( else: effective_discipline = None - log.info( - f"💬 Mensagem [{len(user_message)} chars] | force_rag={force_rag} | " - f"force_doc={force_doc} | discipline={effective_discipline!r} | " - f"cmd_disc={discipline_from_command!r} | reset={did_reset} | " - f"pin={'sim' if pin else 'não'} | " - f"query='{query[:80]}{'...' if len(query) > 80 else ''}'" + # Sempre `strict` nesta mitigação. `assistive` viria via flag + # explícita de produto, que hoje não existe — fica como hook. + mode = select_mode( + force_doc=force_doc, + force_rag=force_rag, + discipline_from_command=discipline_from_command, + has_explicit_assistive_flag=False, ) - trace_label = "Assistente geral" - trace_sources: list[str] = [] - used_sticky = False - system_content = sp - - def finalize_trace() -> BuildMessagesResult: - final_pin = store.get(session_id) if store and session_id else None - pd = final_pin.display_name if final_pin else None - pa = bool(final_pin) - trace = ContextTrace( - label=trace_label, - sources=_dedupe_sources(trace_sources), - pinned_active=pa, - pinned_display=pd, - ) - return BuildMessagesResult( - messages=[ - {"role": "system", "content": system_content}, - {"role": "user", "content": query}, - ], - trace=trace, - ) - - def apply_sticky(p: PinnedContext) -> None: - nonlocal system_content, trace_label, trace_sources, used_sticky - used_sticky = True - body = _join_pin_chunks(p.chunks) - intro = self._settings.sticky_instruction.format(name=p.display_name or "material fixado") + "\n\n" - system_content = ( - f"{sp}\n\n{intro}" - "Você possui acesso à seguinte base de conhecimento local (contexto fixado).\n\n" - f"{body}" - ) - trace_sources = [str(c["source"]) for c in p.chunks if c.get("source")] - if p.scope_key == "doc": - trace_label = "Documentação (doc) · fixado" - elif p.scope_key == "content": - trace_label = f"{_global_scope_label(self._settings)} · fixado" - elif p.scope_key.startswith("discipline:"): - d = p.scope_key.split(":", 1)[1] - trace_label = f"{_trace_label_for_discipline(d)} · fixado" - else: - trace_label = "Contexto fixado" - - def save_pin(scope_key: str, chunk_dicts: list[dict[str, str]], sources_for_display: list[str]) -> None: - if not (store and session_id): - return - trimmed = _trim_pin_chunks(chunk_dicts, self._settings.pinned_max_chars) - if not trimmed: - return - disp = _display_name_from_source(sources_for_display[0]) if sources_for_display else "material" - store.set_pinned( - session_id, - scope_key, - trimmed, - disp, - self._settings.pinned_max_turns, - ) + log_event( + log, + logging.INFO, + ACL_MOD_CONTEXT, + "context_route", + "pedido recebido — encaminhamento RAG", + metadata={ + "user_message_chars": len(user_message), + "query": query, + "mode": mode, + "force_rag": force_rag, + "force_doc": force_doc, + "effective_discipline": effective_discipline, + "discipline_from_command": discipline_from_command, + "did_reset": did_reset, + "pin_active": bool(pin), + }, + ) + # --- Caso /doc: injeção determinística do silo "doc". + # Esse fluxo preserva o comportamento do comando `/doc` — ele já é + # um "pin explícito" da documentação; a decisão de retrieval não se + # aplica aqui porque a fonte é fixa. if force_doc: doc_chunks = [c for c in self._search_engine.chunks if c.get("discipline") == "doc"] if doc_chunks: - log.info(f" ⚡ /doc — injetando {len(doc_chunks)} chunk(s)") - ctx = "\n\n---\n\n".join( - f"[Fonte: {c['source']}]\n{c['text']}" for c in doc_chunks + log_event( + log, + logging.INFO, + ACL_MOD_CONTEXT, + "doc_injection", + "injecao deterministica silo doc", + metadata={"chunk_count": len(doc_chunks)}, + ) + ctx = _join_chunks_for_prompt( + [{"source": str(c["source"]), "text": str(c["text"])} for c in doc_chunks] ) system_content = ( f"{sp}\n\n" - "Você possui acesso à documentação em content/doc/. " - "Utilize-a como referência rígida para responder:\n\n" + f"{_STRICT_GROUNDING_INSTRUCTION}" f"{ctx}" ) - trace_label = "Documentação (doc)" trace_sources = [str(c["source"]) for c in doc_chunks] pin_chunks = [{"source": str(c["source"]), "text": str(c["text"])} for c in doc_chunks] - save_pin("doc", pin_chunks, trace_sources) - else: - log.warning(" ⚠ /doc sem chunks no silo 'doc'") - system_content = sp - trace_label = "Assistente geral" - if pin: - apply_sticky(pin) - return finalize_trace() - - hits = self._search_engine.search(query, discipline_filter=effective_discipline) - weak = _hits_weak(hits, self._settings.pinned_weak_score) - - if hits and not weak: - for h in hits: - log.info( - f" 🎯 BM25 hit → '{h['source']}' | score={h['score']:.3f} | " - f"chunk='{h['text'][:60].strip()}...'" + self._save_pin(session_id, "doc", pin_chunks, trace_sources) + trace = ContextTrace( + label="Documentação (doc)", + sources=_dedupe_sources(trace_sources), + pinned_active=self._pin_active(session_id), + pinned_display=self._pin_display(session_id), + mode=mode, + decision="answer", + reason="ok", + confidence="high", ) - ctx = "\n\n---\n\n".join( - f"[Fonte: {h['source']} | Score: {h['score']:.2f}]\n{h['text']}" - for h in hits - ) - system_content = ( - f"{sp}\n\n" - "Você possui acesso à seguinte base de conhecimento local. " - "Use-a como referência primária para responder:\n\n" - f"{ctx}" - ) - if effective_discipline is not None: - trace_label = _trace_label_for_discipline(effective_discipline) - else: - trace_label = _global_scope_label(self._settings) - trace_sources = [str(h["source"]) for h in hits] - if force_rag and discipline_from_command is None: - sk = "content" - elif discipline_from_command is not None: - sk = f"discipline:{discipline_from_command}" - elif effective_discipline is not None: - sk = f"discipline:{effective_discipline}" - else: - sk = "content" - save_pin( - sk, - [{"source": str(h["source"]), "text": str(h["text"])} for h in hits], - trace_sources, - ) - elif pin and (not hits or weak): - log.info(" 📌 BM25 fraco ou vazio — usando contexto fixado da sessão") - apply_sticky(pin) - elif hits: - for h in hits: - log.info( - f" 🎯 BM25 (fraco) → '{h['source']}' | score={h['score']:.3f} | " - f"chunk='{h['text'][:60].strip()}...'" + return BuildMessagesResult( + messages=[ + {"role": "system", "content": system_content}, + {"role": "user", "content": query}, + ], + trace=trace, ) - ctx = "\n\n---\n\n".join( - f"[Fonte: {h['source']} | Score: {h['score']:.2f}]\n{h['text']}" - for h in hits + # Sem silo doc: hard stop explícito; não tenta LLM sem base. + return self._hard_stop_result( + query=query or user_message, + reason="insufficient_context", + mode=mode, + discipline=effective_discipline, + pin=pin, + trace_retrieval=None, ) - system_content = ( - f"{sp}\n\n" - "Você possui acesso à seguinte base de conhecimento local. " - "Use-a como referência primária para responder:\n\n" - f"{ctx}" + + # --- Retrieval bruto + política de decisão -------------------------- + + candidates = self._search_engine.search_candidates( + query, + candidate_k=self._settings.retrieval_candidate_k, + discipline_filter=effective_discipline, + ) + decision = build_decision( + query=query, + candidates=candidates, + mode=mode, + min_score=self._settings.retrieval_min_score, + min_score_margin=self._settings.retrieval_min_score_margin, + min_coverage=self._settings.retrieval_min_coverage, + min_coverage_weighted=self._settings.retrieval_min_coverage_weighted, + min_terms=self._settings.retrieval_min_terms, + top_k=self._settings.retrieval_top_k, + max_per_source=self._settings.retrieval_max_chunks_per_source, + ) + if decision.allow_generation: + selected = [ + { + "source": c.source, + "text": c.text, + "score": c.raw_score, + "normalized_score": c.normalized_score, + } + for c in decision.selected_candidates + ] + ctx = "\n\n---\n\n".join( + f"[Fonte: {s['source']} | Score: {s['normalized_score']:.2f}]\n{s['text']}" + for s in selected ) + system_content = f"{sp}\n\n{_STRICT_GROUNDING_INSTRUCTION}{ctx}" + if effective_discipline is not None: - trace_label = _trace_label_for_discipline(effective_discipline) + label = _trace_label_for_discipline(effective_discipline) + elif force_rag: + label = _global_scope_label(self._settings) else: - trace_label = _global_scope_label(self._settings) - trace_sources = [str(h["source"]) for h in hits] - elif force_rag: - scope_chunks = self._search_engine.chunks_for_scope(effective_discipline) - log.info( - " ⚡ /content (ou RAG forçado) sem hits — top-5 do escopo " - f"({len(scope_chunks)} chunk(s) disponíveis))" + label = _global_scope_label(self._settings) + + trace_sources = [s["source"] for s in selected] + scope_key = self._scope_key_for_hit( + force_rag, discipline_from_command, effective_discipline ) - ctx = "\n\n---\n\n".join( - f"[Fonte: {c['source']}]\n{c['text']}" for c in scope_chunks[:5] + self._save_pin( + session_id, + scope_key, + [{"source": s["source"], "text": s["text"]} for s in selected], + trace_sources, ) - system_content = ( - f"{sp}\n\n" - "Você possui acesso à seguinte base de conhecimento local. " - "Use-a como referência primária para responder:\n\n" - f"{ctx}" + + trace = ContextTrace( + label=label, + sources=_dedupe_sources(trace_sources), + pinned_active=self._pin_active(session_id), + pinned_display=self._pin_display(session_id), + mode=mode, + decision="answer", + reason=decision.reason, + confidence=decision.confidence, + retrieval_trace=decision.trace, ) - if effective_discipline is not None: - trace_label = _trace_label_for_discipline(effective_discipline) - else: - trace_label = _global_scope_label(self._settings) - trace_sources = [str(c["source"]) for c in scope_chunks[:5]] - sk = ( - "content" - if discipline_from_command is None - else f"discipline:{discipline_from_command}" + log_event( + log, + logging.INFO, + ACL_MOD_CONTEXT, + "context_prompt_ready", + "mensagens montadas com chunks selecionados", + metadata={ + "selected_chunk_count": len(selected), + "sources": list(trace.sources), + "reason": decision.reason, + "confidence": decision.confidence, + }, ) - save_pin( - sk, - [{"source": str(c["source"]), "text": str(c["text"])} for c in scope_chunks[:5]], - trace_sources, + return BuildMessagesResult( + messages=[ + {"role": "system", "content": system_content}, + {"role": "user", "content": query}, + ], + trace=trace, + decision=decision, ) - elif pin: - log.info(" 📌 Modo geral sem BM25 — usando contexto fixado") - apply_sticky(pin) - else: - log.info(" 🤖 Modo assistente geral (sem hits nem pin)") - trace_label = "Assistente geral" - trace_sources = [] - log.info(f" 🔑 System prompt ~{len(system_content)} chars | sticky={used_sticky}") - return finalize_trace() + # --- Hard stop ------------------------------------------------------ + # Pin NÃO ressuscita contexto: no modo strict, se a decisão atual + # bloqueou, pin só serve como histórico de UI (mostrar o badge). + return self._hard_stop_result( + query=query or user_message, + reason=decision.reason, + mode=mode, + discipline=effective_discipline, + pin=pin, + trace_retrieval=decision.trace, + decision=decision, + ) + + # --- Helpers internos --------------------------------------------------- + + def _pin_active(self, session_id: str | None) -> bool: + if not (self._pinned_store and session_id): + return False + return bool(self._pinned_store.get(session_id)) + + def _pin_display(self, session_id: str | None) -> str | None: + if not (self._pinned_store and session_id): + return None + p = self._pinned_store.get(session_id) + return p.display_name if p else None + + def _save_pin( + self, + session_id: str | None, + scope_key: str, + chunk_dicts: list[dict[str, str]], + sources_for_display: list[str], + ) -> None: + store = self._pinned_store + if not (store and session_id): + return + trimmed = _trim_pin_chunks(chunk_dicts, self._settings.pinned_max_chars) + if not trimmed: + return + disp = _display_name_from_source(sources_for_display[0]) if sources_for_display else "material" + store.set_pinned( + session_id, + scope_key, + trimmed, + disp, + self._settings.pinned_max_turns, + ) + + def _scope_key_for_hit( + self, + force_rag: bool, + discipline_from_command: str | None, + effective_discipline: str | None, + ) -> str: + if force_rag and discipline_from_command is None: + return "content" + if discipline_from_command is not None: + return f"discipline:{discipline_from_command}" + if effective_discipline is not None: + return f"discipline:{effective_discipline}" + return "content" + + def _hard_stop_result( + self, + query: str, + reason: str, + mode: str, + discipline: str | None, + pin: PinnedContext | None, + trace_retrieval: RetrievalTrace | None, + decision: RetrievalDecision | None = None, + ) -> BuildMessagesResult: + message = hard_stop_message(reason) + label = ( + _trace_label_for_discipline(discipline) + if discipline + else _global_scope_label(self._settings) + ) + trace_sources: list[str] = [] + if trace_retrieval is not None: + trace_sources = [s["source"] for s in trace_retrieval.selected_sources] + trace = ContextTrace( + label=label, + sources=_dedupe_sources(trace_sources), + pinned_active=bool(pin), + pinned_display=pin.display_name if pin else None, + mode=mode, + decision="hard_stop", + reason=reason, + confidence="low", + retrieval_trace=trace_retrieval, + ) + # Passamos uma sentinela no último user message; o ChatProvider + # detecta decision.is_hard_stop via BuildMessagesResult.decision + # e entrega `hard_stop_message` direto, sem chamar LLM. + return BuildMessagesResult( + messages=[ + {"role": "system", "content": self._settings.system_prompt_geral}, + {"role": "user", "content": query or ""}, + {"role": "assistant", "content": message}, + ], + trace=trace, + decision=decision, + ) diff --git a/engine/database.py b/engine/database.py index 6546977..31ad77b 100644 --- a/engine/database.py +++ b/engine/database.py @@ -1,8 +1,10 @@ -"""Fonte de dados MySQL para o índice BM25.""" +"""Fonte de dados MySQL para o índice BM25 (schema v2).""" from __future__ import annotations import logging from importlib import import_module + +from core.structured_log import ACL_MOD_DATABASE, log_event from typing import TYPE_CHECKING if TYPE_CHECKING: @@ -14,7 +16,7 @@ DB_CHUNK_OVERLAP = 50 -def _chunk_text(text: str, title: str, source: str) -> list[dict]: +def _chunk_text(text: str, title: str, source: str, discipline: str) -> list[dict]: """Divide texto em janelas de ~500 palavras com overlap de 50.""" words = text.split() if not words: @@ -26,7 +28,7 @@ def _chunk_text(text: str, title: str, source: str) -> list[dict]: chunks.append({ "text": f"{title}\n" + " ".join(words[start:end]), "source": source, - "discipline": "db", + "discipline": discipline, }) if end == len(words): break @@ -36,18 +38,32 @@ def _chunk_text(text: str, title: str, source: str) -> list[dict]: def fetch_db_chunks(settings: Settings) -> list[dict]: """ - Busca rows ativas da tabela knowledge e retorna lista de chunks BM25. + Busca rows ativas da tabela knowledge (v2) e retorna lista de chunks BM25. Retorna [] com warning se o DB não estiver configurado ou falhar. """ if not all([settings.db_host, settings.db_name, settings.db_user]): - log.debug("Variáveis DB_* não configuradas — pulando fonte MySQL.") + log_event( + log, + logging.DEBUG, + ACL_MOD_DATABASE, + "fetch_chunks_skipped", + "DB_* incompleto — sem MySQL", + metadata={}, + ) return [] try: pymysql = import_module("pymysql") cursors_mod = import_module("pymysql.cursors") except ImportError: - log.warning("PyMySQL não instalado — fonte MySQL desativada.") + log_event( + log, + logging.WARNING, + ACL_MOD_DATABASE, + "pymysql_missing", + "PyMySQL nao instalado", + metadata={}, + ) return [] try: @@ -65,21 +81,99 @@ def fetch_db_chunks(settings: Settings) -> list[dict]: with conn: with conn.cursor() as cursor: cursor.execute( - "SELECT id, title, content, category " - "FROM knowledge WHERE active = 1 ORDER BY id" + "SELECT id, slug, title, discipline, `order`, content " + "FROM knowledge WHERE active = 1 ORDER BY discipline, `order`" ) rows = cursor.fetchall() all_chunks: list[dict] = [] for row in rows: - source = f"db:{row['category']}" - chunks = _chunk_text(row["content"], row["title"], source) + discipline = row["discipline"] + source = f"db:{discipline}/{row['slug']}" + chunks = _chunk_text(row["content"], row["title"], source, discipline) all_chunks.extend(chunks) - log.debug(" 🗄 row id=%s '%s' → %s chunk(s)", row["id"], row["title"], len(chunks)) - log.info(" 🗄 MySQL: %s row(s) → %s chunk(s) carregados", len(rows), len(all_chunks)) + log_event( + log, + logging.INFO, + ACL_MOD_DATABASE, + "fetch_chunks_ok", + "rows MySQL convertidos em chunks", + metadata={"row_count": len(rows), "chunk_count": len(all_chunks)}, + ) return all_chunks - except Exception: - log.warning("⚠ Falha ao conectar ao MySQL — continuando apenas com .md.", exc_info=True) - return [] \ No newline at end of file + except Exception as e: + # 2003 = can't connect (servidor parado, porta errada, firewall) + if getattr(e, "args", None) and e.args and e.args[0] == 2003: + log_event( + log, + logging.WARNING, + ACL_MOD_DATABASE, + "mysql_unreachable", + "MySQL inacessivel — BM25 sem dados", + metadata={ + "host": settings.db_host, + "port": settings.db_port, + "error": str(e.args[1] if len(e.args) > 1 else e), + }, + ) + else: + log_event( + log, + logging.WARNING, + ACL_MOD_DATABASE, + "fetch_chunks_error", + "falha ao ler knowledge", + metadata={"error": str(e)}, + ) + log.warning("fetch_db_chunks detail", exc_info=True) + return [] + + +def fetch_db_discipline_ids(settings: Settings) -> frozenset[str]: + """Return distinct discipline values from the DB (for silo registration).""" + if not all([settings.db_host, settings.db_name, settings.db_user]): + return frozenset() + try: + pymysql = import_module("pymysql") + cursors_mod = import_module("pymysql.cursors") + except ImportError: + return frozenset() + try: + conn = pymysql.connect( + host=settings.db_host, + port=settings.db_port, + database=settings.db_name, + user=settings.db_user, + password=settings.db_password, + charset="utf8mb4", + cursorclass=cursors_mod.DictCursor, + connect_timeout=5, + read_timeout=5, + ) + with conn: + with conn.cursor() as cursor: + cursor.execute("SELECT DISTINCT discipline FROM knowledge WHERE active = 1") + return frozenset(row["discipline"] for row in cursor.fetchall()) + except Exception as e: + if getattr(e, "args", None) and e.args and e.args[0] == 2003: + log_event( + log, + logging.WARNING, + ACL_MOD_DATABASE, + "disciplines_unreachable", + "MySQL inacessivel ao listar disciplines", + metadata={"host": settings.db_host, "port": settings.db_port}, + ) + else: + log_event( + log, + logging.WARNING, + ACL_MOD_DATABASE, + "disciplines_query_error", + "falha SELECT DISTINCT discipline", + metadata={"error": str(e)}, + ) + log.warning("fetch_db_discipline_ids detail", exc_info=True) + return frozenset() diff --git a/engine/retrieval.py b/engine/retrieval.py new file mode 100644 index 0000000..db6970e --- /dev/null +++ b/engine/retrieval.py @@ -0,0 +1,756 @@ +"""Contratos e política de decisão do RAG ACL. + +Este módulo é o núcleo da mitigação incremental descrita em +`rag_acl_incremental_6951b55f.plan.md`. Ele NÃO faz retrieval BM25 (isso +continua em `engine.search`); ele normaliza termos, aplica regras de +suficiência e devolve uma `RetrievalDecision` que determina se a geração +deve acontecer ou se há hard stop. + +A regra central é: na dúvida, não responder. + +Fases suportadas: +- Fase 1: hard stop por ausência de hits e `top_score < MIN_SCORE`. +- Fase 2: coverage, MIN_TERMS, margem entre candidatos, vague_but_high_risk, + coverage ponderada por termos centrais, `confidence == "low"`. +- Fase 3: sanity check pós-geração (override para `post_generation_misalignment`). +""" + +from __future__ import annotations + +import logging +import re +from dataclasses import dataclass, field + +from core.structured_log import ACL_MOD_DECISION, log_event +from typing import Iterable, Literal + +Mode = Literal["strict", "assistive"] +Confidence = Literal["high", "medium", "low"] +DecisionReason = Literal[ + "ok", + "insufficient_context", + "context_misaligned", + "underspecified_query", + "ambiguous_retrieval", + "low_confidence", + "vague_but_high_risk", + "post_generation_misalignment", + "provider_error", +] + +# Thresholds deliberadamente conservadores. O plano exige que eles sejam +# recalibrados com 20 casos manuais; estes defaults existem para não fingir +# alinhamento antes dessa calibração. +MIN_SCORE: float = 1.5 +MIN_SCORE_MARGIN: float = 0.15 +MIN_COVERAGE: float = 0.34 +MIN_COVERAGE_WEIGHTED: float = 0.34 +MIN_TERMS: int = 2 +CANDIDATE_K: int = 8 +TOP_K: int = 4 +MAX_CHUNKS_PER_SOURCE: int = 2 + +_log = logging.getLogger("kernelbots.retrieval") + +# Stopwords PT-BR focadas em conectivos e pronomes. Mantido curto porque +# listas grandes tendem a remover sinal útil. Não é dicionário completo. +STOPWORDS: frozenset[str] = frozenset( + { + "a", "o", "as", "os", "um", "uma", "uns", "umas", + "de", "da", "do", "das", "dos", "em", "no", "na", "nos", "nas", + "por", "para", "pra", "pro", "com", "sem", "sob", "sobre", "entre", + "que", "qual", "quais", "quando", "onde", "como", "porque", "pq", + "se", "sim", "nao", "não", "mais", "menos", "muito", "pouco", + "e", "ou", "mas", "ja", "já", "tambem", "também", "entao", "então", + "isso", "isto", "aquilo", "esse", "essa", "este", "esta", "aquele", "aquela", + "meu", "minha", "seu", "sua", "teu", "tua", "nosso", "nossa", + "eu", "tu", "ele", "ela", "nos", "nós", "voce", "você", "vocês", "voces", + "de", "da", "pela", "pelo", "pelas", "pelos", + "ser", "estar", "ter", "haver", "existe", "existir", + "ao", "aos", "à", "às", + } +) + +# Verbos/adjetivos de ação genérica que inflam coverage sem carregar domínio. +# A regra do plano é que esses termos caem mesmo quando `len(term) > 3`. +GENERIC_ACTION_TERMS: frozenset[str] = frozenset( + { + "melhorar", "melhor", "melhores", "piorar", + "explicar", "explique", "explica", "mostrar", "mostra", + "fazer", "faz", "faço", "feito", "feita", + "configurar", "configura", "configurando", + "usar", "uso", "usa", "usando", "usado", + "entender", "entendi", "entenda", + "ajudar", "ajuda", "ajude", + "criar", "cria", "criando", "criado", + "resolver", "resolve", "resolvido", + "ajustar", "ajusta", "ajuste", + "saber", "saiba", + "dar", "da", "dê", "de", + "tentar", "tenta", "tente", + "funcionar", "funciona", "funcionando", + "escrever", "escreve", "escrito", + "rapido", "rápido", "rapidamente", "lento", "lenta", + "simples", "basico", "básico", "avancado", "avançado", + "exemplo", "exemplos", "coisa", "coisas", + "algo", "alguem", "alguém", "ninguem", "ninguém", + "tudo", "nada", + "problema", "problemas", "duvida", "dúvida", "duvidas", "dúvidas", + "jeito", "forma", "modo", + } +) + +# Categorias estruturalmente vagas: pares (termo de qualidade genérico + domínio +# ambíguo) que costumam passar em score/coverage mas não desambiguam o que o +# usuário realmente quer saber. São sinais para `vague_but_high_risk` quando +# aparecem sem outro termo de domínio específico. +_VAGUE_QUALITY_TERMS: frozenset[str] = frozenset( + {"performance", "desempenho", "erro", "erros", "timeout", "bug", "lento", "lenta"} +) +_AMBIGUOUS_DOMAIN_TERMS: frozenset[str] = frozenset( + {"banco", "api", "sistema", "app", "codigo", "código", "servidor"} +) + + +@dataclass(frozen=True) +class RetrievalCandidate: + """Candidato bruto da camada lexical (sem decisão aplicada). + + `raw_score` é o score BM25 cru (não normalizado); `normalized_score` é + normalizado por silo para uso em UI/ranking local. A decisão usa + `raw_score`. + """ + + source: str + chunk_id: str + text: str + discipline: str + raw_score: float + normalized_score: float + matched_terms: tuple[str, ...] = () + + +@dataclass(frozen=True) +class RetrievalTrace: + """Traço estruturado para logging/avaliação. + + Formato alinhado ao bloco JSON do plano. + """ + + query: str + normalized_query: str + informative_terms: tuple[str, ...] + mode: Mode + retrieval_mode: str = "bm25_lexical_temporary" + top_score: float = 0.0 + second_score: float = 0.0 + score_margin: float = 0.0 + coverage: float = 0.0 + selected_sources: tuple[dict, ...] = () + decision: str = "hard_stop" + reason: str = "insufficient_context" + llm_called: bool = False + tokens_used: int = 0 + debug: dict = field(default_factory=dict) + + def to_dict(self) -> dict: + return { + "query": self.query, + "normalized_query": self.normalized_query, + "informative_terms": list(self.informative_terms), + "retrieval_mode": self.retrieval_mode, + "mode": self.mode, + "top_score": self.top_score, + "second_score": self.second_score, + "score_margin": self.score_margin, + "coverage": self.coverage, + "selected_sources": [dict(s) for s in self.selected_sources], + "decision": self.decision, + "reason": self.reason, + "llm_called": self.llm_called, + "tokens_used": self.tokens_used, + "debug": dict(self.debug), + } + + +@dataclass(frozen=True) +class RetrievalDecision: + """Decisão final do retrieval: permitir geração ou hard stop. + + `selected_candidates` só é populado quando `allow_generation=True`. + `trace` nunca é None e carrega contexto para UI e avaliação. + """ + + allow_generation: bool + reason: DecisionReason + confidence: Confidence + selected_candidates: tuple[RetrievalCandidate, ...] + trace: RetrievalTrace + + @property + def is_hard_stop(self) -> bool: + return not self.allow_generation + + +# --- Normalização e extração de termos -------------------------------------- + +_WORD_RE = re.compile(r"[\w-]+", re.UNICODE) + + +def normalize_and_tokenize(text: str) -> list[str]: + """Lowercase + tokenização simples por `\\w`. + + Mantém dígitos e hífens (útil para `rag`, `bm25`, `visualizacao-sql`). + """ + if not text: + return [] + return [m.group(0).lower() for m in _WORD_RE.finditer(text)] + + +# Tabela de acentos PT-BR para expansão leve de query (Fase 3). Evita +# biblioteca externa: o plano pede rewriting conservador. +_ACCENT_MAP: dict[str, str] = { + "á": "a", "à": "a", "â": "a", "ã": "a", "ä": "a", + "é": "e", "ê": "e", "è": "e", "ë": "e", + "í": "i", "î": "i", "ì": "i", "ï": "i", + "ó": "o", "ô": "o", "õ": "o", "ò": "o", "ö": "o", + "ú": "u", "û": "u", "ù": "u", "ü": "u", + "ç": "c", +} + + +def _strip_accents(token: str) -> str: + return "".join(_ACCENT_MAP.get(ch, ch) for ch in token) + + +def expand_query_tokens(tokens: list[str]) -> list[str]: + """Expansão lexical conservadora: para tokens com acento, adiciona a + versão sem acento (e vice-versa não é necessário porque o corpus real + já é acentuado; só adicionamos o "fallback sem acento" para robustez + a queries de usuário mal acentuadas). + + Regra: nunca remove tokens, só adiciona. Preserva ordem e dedup. + """ + seen: set[str] = set() + out: list[str] = [] + for t in tokens: + if t not in seen: + seen.add(t) + out.append(t) + stripped = _strip_accents(t) + if stripped != t and stripped not in seen: + seen.add(stripped) + out.append(stripped) + return out + + +def extract_informative_terms(query: str) -> list[str]: + """Extrai termos com valor de domínio. + + Regra do plano: + term not in STOPWORDS + and term not in GENERIC_ACTION_TERMS + and len(term) > 3 + + A preservação da ordem é importante para logging; duplicatas são + removidas preservando a primeira ocorrência. + """ + seen: set[str] = set() + out: list[str] = [] + for term in normalize_and_tokenize(query): + if term in STOPWORDS: + continue + if term in GENERIC_ACTION_TERMS: + continue + if len(term) <= 3: + # termos de 3 caracteres como "sql", "api" são críticos; por isso + # usamos `> 3` apenas como filtro anti-ruído, mas mantemos siglas + # técnicas explicitamente via allowlist local: + if term in {"sql", "api", "rag", "bm25", "ssl", "tcp", "udp", "ftp", "ssh", "php", "css", "xml", "mvp"}: + if term not in seen: + seen.add(term) + out.append(term) + continue + if term not in seen: + seen.add(term) + out.append(term) + return out + + +# --- Coverage e classificação ----------------------------------------------- + +# Siglas e tokens técnicos reconhecíveis como "centrais" (Fase 2). Tudo o +# que não cai aqui é tratado como opcional; heurística simples e auditável +# porque o plano proíbe semântica sofisticada antes de medir o baseline. +_CENTRAL_TOKEN_RE = re.compile(r"^[a-z0-9]+(-[a-z0-9]+)+$") +_CENTRAL_WHITELIST: frozenset[str] = frozenset( + { + "sql", "python", "docker", "redis", "api", "rag", "bm25", + "mysql", "postgres", "mongodb", "sqlite", + "fastapi", "flask", "django", + "openrouter", "http", "https", "json", "yaml", + "select", "insert", "update", "delete", "join", + "where", "group", "order", "limit", + "loop", "for", "while", "variavel", "variável", + "funcao", "função", "classe", "metodo", "método", + "dashboard", "looker", "powerbi", "bigquery", + "git", "github", "jwt", "oauth", "csrf", "xss", + "reload", "chat", "content", "doc", + } +) +_OPTIONAL_FORCED: frozenset[str] = frozenset(_VAGUE_QUALITY_TERMS | _AMBIGUOUS_DOMAIN_TERMS) + + +def classify_terms(informative_terms: Iterable[str]) -> tuple[list[str], list[str]]: + """Separa termos centrais de opcionais (Fase 2, soft constraint). + + Central: sigla técnica, hífen composto (ex.: `visualizacao-sql`) ou + presença em whitelist. + Opcional: termos de qualidade genérica (`performance`, `erro`) e + domínios ambíguos sem complemento (`banco`, `api` sozinho — nota: `api` + está na whitelist porque aparece explicitamente em comandos, então + conta como central). + """ + central: list[str] = [] + optional: list[str] = [] + for t in informative_terms: + tl = t.lower() + if tl in _OPTIONAL_FORCED and tl not in _CENTRAL_WHITELIST: + optional.append(tl) + continue + if tl in _CENTRAL_WHITELIST or _CENTRAL_TOKEN_RE.match(tl): + central.append(tl) + continue + optional.append(tl) + return central, optional + + +def coverage(informative_terms: Iterable[str], chunk_terms: Iterable[str]) -> float: + """coverage = |informative ∩ chunk| / |informative|. + + Retorna 0.0 quando não há termos informativos — o gate de `MIN_TERMS` + tem que tratar esse caso antes. + """ + q = [t.lower() for t in informative_terms] + if not q: + return 0.0 + c = set(t.lower() for t in chunk_terms) + hit = sum(1 for t in q if t in c) + return hit / len(q) + + +def coverage_weighted( + central_terms: Iterable[str], + optional_terms: Iterable[str], + chunk_terms: Iterable[str], +) -> float: + """coverage_weighted: termos centrais valem 2x. + + Fórmula: + num = 2 * |central ∩ chunk| + |optional ∩ chunk| + den = 2 * |central| + |optional| + """ + central = [t.lower() for t in central_terms] + optional = [t.lower() for t in optional_terms] + c = set(t.lower() for t in chunk_terms) + hit_central = sum(1 for t in central if t in c) + hit_optional = sum(1 for t in optional if t in c) + num = 2 * hit_central + hit_optional + den = 2 * len(central) + len(optional) + if den == 0: + return 0.0 + return num / den + + +def is_vague_but_high_risk( + informative_terms: Iterable[str], + central_terms: Iterable[str], +) -> bool: + """Detecta queries estruturalmente vagas mas que dão respostas plausíveis. + + Caso típico: `performance banco`, `erro api`, `timeout docker`. Existe + termo informativo suficiente, mas o par é ambíguo demais (qualidade + genérica + domínio amplo) para responder com segurança. + + Regra: a query tem termo de qualidade vaga e não tem nenhum termo + central específico o bastante — `api` entra na whitelist como central, + mas uma query tipo `erro api` sozinha continua sinalizando risco + porque o único central é um domínio amplo. + """ + info = [t.lower() for t in informative_terms] + central = [t.lower() for t in central_terms] + if not info: + return False + has_vague = any(t in _VAGUE_QUALITY_TERMS for t in info) + if not has_vague: + return False + # Central "forte" = não é puramente domínio ambíguo. + strong_central = [t for t in central if t not in _AMBIGUOUS_DOMAIN_TERMS] + if strong_central: + return False + return True + + +# --- Seleção e decisão ------------------------------------------------------ + +def _apply_source_diversity( + candidates: list[RetrievalCandidate], + max_per_source: int = MAX_CHUNKS_PER_SOURCE, +) -> list[RetrievalCandidate]: + """Limita top-k a no máximo `max_per_source` chunks por fonte. + + Candidatos já devem vir ordenados por `raw_score` desc. + """ + count: dict[str, int] = {} + out: list[RetrievalCandidate] = [] + for c in candidates: + n = count.get(c.source, 0) + if n >= max_per_source: + continue + count[c.source] = n + 1 + out.append(c) + return out + + +def select_mode( + force_doc: bool, + force_rag: bool, + discipline_from_command: str | None, + has_explicit_assistive_flag: bool, +) -> Mode: + """Decide `strict` vs `assistive` de forma explícita. + + Regra do plano: + - `/doc`, `/content` e padrão sem flag usam `strict`. + - `assistive` só existe por flag explícita. + """ + if has_explicit_assistive_flag: + return "assistive" + # /doc, /content, discipline-commands e default → strict + del force_doc, force_rag, discipline_from_command + return "strict" + + +def _build_trace( + query: str, + informative_terms: list[str], + mode: Mode, + candidates: list[RetrievalCandidate], + coverage_value: float, + reason: DecisionReason, + allow_generation: bool, + confidence: Confidence, + debug: dict, +) -> RetrievalTrace: + top = candidates[0].raw_score if candidates else 0.0 + second = candidates[1].raw_score if len(candidates) > 1 else 0.0 + margin = top - second + selected_sources = tuple( + { + "source": c.source, + "chunk_id": c.chunk_id, + "score": c.raw_score, + "normalized_score": c.normalized_score, + "matched_terms": list(c.matched_terms), + } + for c in candidates + ) + return RetrievalTrace( + query=query, + normalized_query=" ".join(normalize_and_tokenize(query)), + informative_terms=tuple(informative_terms), + mode=mode, + top_score=top, + second_score=second, + score_margin=margin, + coverage=coverage_value, + selected_sources=selected_sources, + decision="answer" if allow_generation else "hard_stop", + reason=reason, + llm_called=allow_generation, + tokens_used=0, + debug=dict(debug), + ) + + +def build_decision( + query: str, + candidates: list[RetrievalCandidate], + mode: Mode = "strict", + *, + min_score: float = MIN_SCORE, + min_score_margin: float = MIN_SCORE_MARGIN, + min_coverage: float = MIN_COVERAGE, + min_coverage_weighted: float = MIN_COVERAGE_WEIGHTED, + min_terms: int = MIN_TERMS, + top_k: int = TOP_K, + max_per_source: int = MAX_CHUNKS_PER_SOURCE, +) -> RetrievalDecision: + """Aplica as Fases 1 e 2 sobre candidatos já recuperados. + + Ordem das verificações segue o plano: + 1. Ausência de hits ou top_score baixo → `insufficient_context` (F1). + 2. Query subespecificada → `underspecified_query` (F2, strict). + 3. Vague but high risk → `vague_but_high_risk` (F2, strict). + 4. Margem entre top1/top2 baixa → `ambiguous_retrieval` (F2, strict). + 5. Coverage baixa no melhor chunk → `context_misaligned` (F2). + 6. `confidence == "low"` após sinais fracos → `low_confidence` (F2, strict). + """ + informative = extract_informative_terms(query) + central, optional = classify_terms(informative) + + candidates_sorted = sorted(candidates, key=lambda c: c.raw_score, reverse=True) + candidates_diverse = _apply_source_diversity(candidates_sorted, max_per_source) + selected = candidates_diverse[:top_k] + + top = selected[0].raw_score if selected else 0.0 + second = selected[1].raw_score if len(selected) > 1 else 0.0 + score_margin = top - second + + best_chunk_tokens: list[str] = [] + if selected: + best_chunk_tokens = normalize_and_tokenize(selected[0].text) + coverage_value = coverage(informative, best_chunk_tokens) if informative else 0.0 + coverage_w = ( + coverage_weighted(central, optional, best_chunk_tokens) if informative else 0.0 + ) + missing_central = [t for t in central if t not in set(best_chunk_tokens)] + vague_high_risk = is_vague_but_high_risk(informative, central) + + debug: dict = { + "central_terms": list(central), + "optional_terms": list(optional), + "missing_central_terms": list(missing_central), + "coverage_weighted": coverage_w, + "vague_but_high_risk": vague_high_risk, + } + + thresholds_meta = { + "min_score": min_score, + "min_score_margin": min_score_margin, + "min_coverage": min_coverage, + "min_coverage_weighted": min_coverage_weighted, + "min_terms": min_terms, + "top_k": top_k, + "max_per_source": max_per_source, + } + + log_event( + _log, + logging.DEBUG, + ACL_MOD_DECISION, + "retrieval_gates_inputs", + "metricas antes dos cortes", + metadata={ + **thresholds_meta, + "query": query, + "informative_terms": informative, + "informative_count": len(informative), + "top_score": top, + "second_score": second, + "score_margin": score_margin, + "coverage": coverage_value, + "coverage_weighted": coverage_w, + "vague_but_high_risk": vague_high_risk, + "selected_preview_count": len(selected), + }, + ) + + def _finish(decision: RetrievalDecision) -> RetrievalDecision: + t = decision.trace + log_event( + _log, + logging.INFO, + ACL_MOD_DECISION, + "retrieval_decision_final", + "generation_allowed" if decision.allow_generation else f"hard_stop:{decision.reason}", + metadata={ + **thresholds_meta, + "allow_generation": decision.allow_generation, + "reason": decision.reason, + "confidence": decision.confidence, + "mode": t.mode, + "query": t.query, + "normalized_query": t.normalized_query, + "informative_terms": list(t.informative_terms), + "coverage": t.coverage, + "coverage_weighted": coverage_w, + "top_score": t.top_score, + "second_score": t.second_score, + "score_margin": t.score_margin, + "candidate_count": len(candidates), + "selected_count": len(decision.selected_candidates), + "selected_sources": [c.source for c in decision.selected_candidates[:12]], + "trace_decision": t.decision, + "trace_reason": t.reason, + "debug": t.debug, + }, + ) + return decision + + # 1. Sem hits ou score absoluto insuficiente. + if not selected or top < min_score: + confidence: Confidence = "low" + debug["confidence"] = confidence + return _finish(RetrievalDecision( + allow_generation=False, + reason="insufficient_context", + confidence=confidence, + selected_candidates=(), + trace=_build_trace( + query, informative, mode, selected, coverage_value, + "insufficient_context", False, confidence, debug, + ), + )) + + # 2. Query subespecificada (Fase 2, strict). + if mode == "strict" and len(informative) < min_terms: + confidence = "low" + debug["confidence"] = confidence + return _finish(RetrievalDecision( + allow_generation=False, + reason="underspecified_query", + confidence=confidence, + selected_candidates=(), + trace=_build_trace( + query, informative, mode, selected, coverage_value, + "underspecified_query", False, confidence, debug, + ), + )) + + # 3. Vague but high risk (Fase 2, strict). + if mode == "strict" and vague_high_risk: + confidence = "low" + debug["confidence"] = confidence + return _finish(RetrievalDecision( + allow_generation=False, + reason="vague_but_high_risk", + confidence=confidence, + selected_candidates=(), + trace=_build_trace( + query, informative, mode, selected, coverage_value, + "vague_but_high_risk", False, confidence, debug, + ), + )) + + # 4. Retrieval ambíguo por margem baixa (Fase 2, strict). + # Só dispara quando há de fato um segundo candidato; um único hit sólido + # não é ambíguo por margem. + if mode == "strict" and len(selected) > 1 and score_margin < min_score_margin: + confidence = "low" + debug["confidence"] = confidence + return _finish(RetrievalDecision( + allow_generation=False, + reason="ambiguous_retrieval", + confidence=confidence, + selected_candidates=(), + trace=_build_trace( + query, informative, mode, selected, coverage_value, + "ambiguous_retrieval", False, confidence, debug, + ), + )) + + # 5. Coverage baixa no melhor chunk (Fase 2). + if informative and coverage_value < min_coverage: + confidence = "low" + debug["confidence"] = confidence + return _finish(RetrievalDecision( + allow_generation=False, + reason="context_misaligned", + confidence=confidence, + selected_candidates=(), + trace=_build_trace( + query, informative, mode, selected, coverage_value, + "context_misaligned", False, confidence, debug, + ), + )) + + # 6. Confidence determinística (Fase 2). + confidence = "high" + if coverage_w < min_coverage_weighted: + confidence = "low" + if len(selected) > 1 and score_margin < min_score_margin: + confidence = "low" + if vague_high_risk: + confidence = "low" + if missing_central: + confidence = "low" + debug["confidence"] = confidence + + if mode == "strict" and confidence == "low": + return _finish(RetrievalDecision( + allow_generation=False, + reason="low_confidence", + confidence=confidence, + selected_candidates=(), + trace=_build_trace( + query, informative, mode, selected, coverage_value, + "low_confidence", False, confidence, debug, + ), + )) + + return _finish(RetrievalDecision( + allow_generation=True, + reason="ok", + confidence=confidence, + selected_candidates=tuple(selected), + trace=_build_trace( + query, informative, mode, selected, coverage_value, + "ok", True, confidence, debug, + ), + )) + + +# --- Sanity check pós-geração (Fase 3) -------------------------------------- + +def _tokens_of(text: str) -> set[str]: + return set(normalize_and_tokenize(text)) + + +def post_generation_flags( + answer: str, + informative_terms: Iterable[str], + selected_candidates: Iterable[RetrievalCandidate], +) -> list[str]: + """Sanity check leve, não-LLM, sobre a resposta gerada. + + Flags: + - `missing_informative_terms`: resposta não cita nenhum termo informativo + da query. + - `missing_source_entities`: resposta não menciona nenhuma fonte nem + nenhuma entidade central dos chunks selecionados. + - `introduced_unsupported_terms`: demasiados termos técnicos novos que + não aparecem em nenhum chunk (heurística conservadora). + """ + flags: list[str] = [] + answer_tokens = _tokens_of(answer) + + info = [t.lower() for t in informative_terms] + if info and not any(t in answer_tokens for t in info): + flags.append("missing_informative_terms") + + candidates = list(selected_candidates) + chunk_tokens: set[str] = set() + sources: set[str] = set() + for c in candidates: + chunk_tokens |= _tokens_of(c.text) + sources.add(c.source.lower()) + + source_mentioned = any(src_part for src_part in sources if src_part in answer.lower()) + # Termos centrais dos chunks presentes: heurística mínima. Se a resposta + # não cita nem fonte nem termos dos chunks, é sinal forte de alucinação + # ou de resposta genérica. + answer_tokens_sigset = {t for t in answer_tokens if len(t) > 4} + shared_with_chunks = bool(answer_tokens_sigset & chunk_tokens) + if candidates and not source_mentioned and not shared_with_chunks: + flags.append("missing_source_entities") + + if candidates: + tech_like = {t for t in answer_tokens if len(t) >= 5 and re.search(r"[a-z]", t)} + unsupported = [t for t in tech_like if t not in chunk_tokens and t not in info] + # Limite conservador: só marca se muitos termos técnicos longos + # aparecerem sem suporte nos chunks. + if len(unsupported) > 25: + flags.append("introduced_unsupported_terms") + + return flags diff --git a/engine/search.py b/engine/search.py index 5a10203..10425ca 100644 --- a/engine/search.py +++ b/engine/search.py @@ -1,4 +1,16 @@ -"""Índice BM25 por silo (disciplina) sobre Markdown em content/.""" +"""Índice BM25 por silo (disciplina) — fonte única: MySQL. + +O `SearchEngine` é responsável apenas por **recuperação lexical bruta**. +A decisão de suficiência (hard stop, coverage, etc.) está em +`engine.retrieval.build_decision`. O plano +`rag_acl_incremental_6951b55f.plan.md` exige essa separação: retrieval +devolve candidatos com score cru e normalizado; a política fica fora. + +Compatibilidade retroativa: `search()` continua disponível e devolve +`list[dict]`, mas agora NÃO aplica o threshold antigo de score — apenas +`top_k` e, opcionalmente, o gate de score absoluto. Código novo deve usar +`search_candidates()`. +""" from __future__ import annotations @@ -6,34 +18,72 @@ import re import threading import time -from pathlib import Path from typing import Any from rank_bm25 import BM25Okapi from core.config import GlobalContextMode from core.config import Settings -from engine.database import fetch_db_chunks +from core.structured_log import ACL_MOD_SEARCH, log_event +from engine.database import fetch_db_chunks, fetch_db_discipline_ids +from engine.retrieval import CANDIDATE_K, RetrievalCandidate, expand_query_tokens log = logging.getLogger(f"kernelbots.{__name__}") _SAFE_DISCIPLINE_RE = re.compile(r"^[A-Za-z0-9_-]+$") +def _log_search_candidates( + query: str, + discipline_filter: str | None, + cands: list[RetrievalCandidate], + candidate_k: int, +) -> None: + base_tokens = re.findall(r"\w+", query.lower()) + expanded = expand_query_tokens(base_tokens) + top = cands[0].raw_score if cands else 0.0 + second = cands[1].raw_score if len(cands) > 1 else 0.0 + log_event( + log, + logging.INFO, + ACL_MOD_SEARCH, + "candidates_retrieved", + "busca lexical concluida", + metadata={ + "query": query, + "normalized_query_tokens": expanded, + "discipline_filter": discipline_filter, + "candidate_k_requested": candidate_k, + "candidate_count": len(cands), + "top_score": top, + "second_score": second, + "score_margin": top - second, + "top_sources": [c.source for c in cands[:10]], + "top_raw_scores": [round(c.raw_score, 4) for c in cands[:10]], + }, + ) + + class SearchEngine: - """Tokeniza, faz chunk de .md por silo, rebuild e busca com threshold normalizado.""" + """Tokeniza chunks do MySQL por silo, rebuild e busca com scores crus. + + Mudanças Fase 1: + - `search_candidates()` devolve `RetrievalCandidate` com `raw_score` + (BM25 puro) e `normalized_score` (normalizado por silo apenas para + ranking local/UI). + - `search()` permanece para compatibilidade mas não aplica mais o + threshold antigo — a política de corte passa para `retrieval.py`. + """ def __init__( self, - content_dir: Path, score_threshold: float, global_context_mode: GlobalContextMode = "geral", - settings: Settings | None = None, # <-- adicionar + settings: Settings | None = None, ) -> None: - self._content_dir = content_dir.resolve() - self._score_threshold = score_threshold + self._score_threshold = score_threshold # mantido para backward compat. self._global_context_mode: GlobalContextMode = global_context_mode - self._settings = settings + self._settings = settings self._lock = threading.RLock() self._silos: dict[str, dict[str, Any]] = {} self._discipline_ids: frozenset[str] = frozenset() @@ -55,115 +105,51 @@ def discipline_ids(self) -> frozenset[str]: def _tokenize(text: str) -> list[str]: return re.findall(r"\w+", text.lower()) - @staticmethod - def _chunk_markdown(path: Path, content_dir: Path, discipline: str) -> list[dict]: - text = path.read_text(encoding="utf-8", errors="replace") - sections: list[dict] = [] - current_header = path.stem - current_lines: list[str] = [] - rel_source = path.resolve().relative_to(content_dir.resolve()).as_posix() - - for line in text.splitlines(): - if re.match(r"^#{1,3}\s", line): - if current_lines: - sections.append({ - "text": f"{current_header}\n" + "\n".join(current_lines).strip(), - "source": rel_source, - "discipline": discipline, - }) - current_header = line.lstrip("#").strip() - current_lines = [] - else: - current_lines.append(line) - - if current_lines: - sections.append({ - "text": f"{current_header}\n" + "\n".join(current_lines).strip(), - "source": rel_source, - "discipline": discipline, - }) - - return [s for s in sections if s["text"].strip()] - - def _scan_discipline_ids(self) -> frozenset[str]: - ids: set[str] = {"geral"} - if not self._content_dir.is_dir(): - return frozenset(ids) - for p in self._content_dir.iterdir(): - if p.is_dir() and not p.name.startswith("."): - ids.add(p.name) - return frozenset(ids) - - def _collect_files_per_silo(self) -> dict[str, list[Path]]: - files_by_silo: dict[str, list[Path]] = {"geral": []} - if not self._content_dir.is_dir(): - return files_by_silo - - root_md = sorted(self._content_dir.glob("*.md")) - files_by_silo["geral"].extend(root_md) - - geral_sub = self._content_dir / "geral" - if geral_sub.is_dir(): - files_by_silo["geral"].extend(sorted(geral_sub.rglob("*.md"))) - - for child in sorted(self._content_dir.iterdir(), key=lambda x: x.name): - if not child.is_dir() or child.name.startswith("."): - continue - if child.name == "geral": - continue - files_by_silo[child.name] = sorted(child.rglob("*.md")) - - return files_by_silo - def rebuild(self) -> None: t0 = time.perf_counter() - discipline_ids = self._scan_discipline_ids() - files_by_silo = self._collect_files_per_silo() + + db_chunks: list[dict] = [] + if self._settings is not None: + db_chunks = fetch_db_chunks(self._settings) + + if not db_chunks: + log_event( + log, + logging.WARNING, + ACL_MOD_SEARCH, + "index_empty", + "nenhum chunk MySQL — BM25 sem dados", + metadata={"chunk_total": 0}, + ) + + chunks_by_silo: dict[str, list[dict]] = {} + for chunk in db_chunks: + silo = chunk.get("discipline", "geral") + chunks_by_silo.setdefault(silo, []).append(chunk) new_silos: dict[str, dict[str, Any]] = {} all_chunks: list[dict] = [] - for silo in sorted(discipline_ids): - paths = files_by_silo.get(silo, []) - silo_chunks: list[dict] = [] - for md_file in paths: - try: - before = len(silo_chunks) - silo_chunks.extend( - self._chunk_markdown(md_file, self._content_dir, silo) - ) - added = len(silo_chunks) - before - log.info( - " 📄 [%s] %s → %s chunk(s)", - silo, - md_file.relative_to(self._content_dir).as_posix(), - added, - ) - except Exception: - log.exception( - " ❌ [%s] Falha ao processar %s — ignorado", - silo, - md_file, - ) - + for silo in sorted(chunks_by_silo): + silo_chunks = chunks_by_silo[silo] tokenized = [self._tokenize(c["text"]) for c in silo_chunks] bm25 = BM25Okapi(tokenized) if tokenized else None new_silos[silo] = {"chunks": silo_chunks, "bm25": bm25} all_chunks.extend(silo_chunks) - - if not all_chunks: - log.warning( - "⚠ Nenhum .md indexado — BM25 desativado. Modo assistente geral ativo." + log_event( + log, + logging.DEBUG, + ACL_MOD_SEARCH, + "silo_indexed", + f"silo {silo} indexado", + metadata={"silo": silo, "chunk_count": len(silo_chunks)}, ) - - # --- chunks do MySQL (silo "db") --- - db_chunks: list[dict] = [] + + discipline_ids: frozenset[str] = frozenset() if self._settings is not None: - db_chunks = fetch_db_chunks(self._settings) - if db_chunks: - tokenized_db = [self._tokenize(c["text"]) for c in db_chunks] - new_silos["db"] = {"chunks": db_chunks, "bm25": BM25Okapi(tokenized_db)} - all_chunks.extend(db_chunks) + discipline_ids = fetch_db_discipline_ids(self._settings) + if not discipline_ids: + discipline_ids = frozenset(chunks_by_silo.keys()) elapsed = (time.perf_counter() - t0) * 1000 with self._lock: @@ -171,80 +157,163 @@ def rebuild(self) -> None: self._silos = new_silos self._all_chunks = all_chunks - db_count = len(db_chunks) - md_count = len(all_chunks) - db_count - log.info( - "✅ Índice BM25 por silo pronto — %s chunk(s) (%s .md + %s MySQL) | %s silo(s) | rebuild em %.1fms", - len(all_chunks), md_count, db_count, len(new_silos), elapsed, + log_event( + log, + logging.INFO, + ACL_MOD_SEARCH, + "index_rebuilt", + "indice BM25 reconstruido", + metadata={ + "chunk_total": len(all_chunks), + "silo_count": len(new_silos), + "silos": sorted(new_silos.keys()), + "rebuild_ms": round(elapsed, 2), + }, ) def normalize_discipline(self, raw: str | None) -> str | None: - """Whitelist contra pastas reais; rejeita path traversal e caracteres inseguros.""" + """Whitelist contra disciplines reais; rejeita path traversal e caracteres inseguros.""" if raw is None: return None s = raw.strip() if not s: return None if not _SAFE_DISCIPLINE_RE.match(s): - log.warning("normalize_discipline: rejeitado (formato inseguro): %r", raw) + log_event( + log, + logging.WARNING, + ACL_MOD_SEARCH, + "discipline_rejected", + "formato de disciplina inseguro", + metadata={"raw": raw}, + ) return None with self._lock: known = self._discipline_ids if s not in known: - log.warning("normalize_discipline: disciplina desconhecida (ignorada): %r", raw) + log_event( + log, + logging.WARNING, + ACL_MOD_SEARCH, + "discipline_unknown", + "disciplina fora da whitelist", + metadata={"raw": raw}, + ) return None return s def chunks_for_scope(self, discipline_filter: str | None) -> list[dict]: - """Chunks do mesmo universo que uma busca sem hits (fallback /content).""" + """Chunks do mesmo universo que uma busca sem hits. + + Observação: o plano proíbe usar isso como fallback de resposta + (ex.: `scope_chunks[:5]`). Continuamos expondo o método porque ele + é útil para UI/debug, mas a camada de contexto não deve montar + prompt com chunks não-retornados pela busca. + """ with self._lock: nd = self.normalize_discipline(discipline_filter) if nd is not None: return list(self._silos.get(nd, {}).get("chunks", [])) if self._global_context_mode == "geral": - return list(self._silos.get("geral", {}).get("chunks", [])) + merged: list[dict] = [] + for name in sorted(self._silos.keys()): + merged.extend(self._silos[name]["chunks"]) + return merged ordered: list[dict] = [] for name in sorted(self._silos.keys()): ordered.extend(self._silos[name]["chunks"]) return ordered - def _hits_in_silo(self, silo: str, query: str, top_k: int) -> list[dict]: + def _candidates_in_silo( + self, silo: str, query: str, candidate_k: int, + ) -> list[RetrievalCandidate]: data = self._silos.get(silo) if not data or not data["bm25"] or not data["chunks"]: return [] tokens = self._tokenize(query) - scores = data["bm25"].get_scores(tokens) - max_score = float(scores.max()) if len(scores) else 0.0 + if not tokens: + return [] + # Fase 3: expansão lexical conservadora (acento-sem-acento) para + # tolerar queries mal acentuadas sem aumentar complexidade do + # indexer. Só adiciona variantes, nunca remove. + expanded = expand_query_tokens(tokens) + raw = data["bm25"].get_scores(expanded) + max_score = float(raw.max()) if len(raw) else 0.0 if max_score == 0: return [] - norm = scores / max_score - ranked = sorted(enumerate(norm), key=lambda x: x[1], reverse=True) - out: list[dict] = [] - for i, s in ranked[:top_k]: - if s >= self._score_threshold: - out.append({**data["chunks"][i], "score": float(s)}) + ranked = sorted(enumerate(raw), key=lambda x: x[1], reverse=True) + out: list[RetrievalCandidate] = [] + query_tokens = set(expanded) + for i, s in ranked[: candidate_k]: + raw_score = float(s) + if raw_score <= 0: + continue + chunk = data["chunks"][i] + chunk_tokens = self._tokenize(chunk["text"]) + matched = tuple(t for t in query_tokens if t in chunk_tokens) + out.append( + RetrievalCandidate( + source=str(chunk.get("source", "")), + chunk_id=f"{silo}:{i}", + text=str(chunk.get("text", "")), + discipline=str(chunk.get("discipline", silo)), + raw_score=raw_score, + normalized_score=raw_score / max_score, + matched_terms=matched, + ) + ) return out - def search( + def search_candidates( self, query: str, - top_k: int = 3, + candidate_k: int = CANDIDATE_K, discipline_filter: str | None = None, - ) -> list[dict]: + ) -> list[RetrievalCandidate]: + """Retorna candidatos brutos ordenados por `raw_score` desc. + + Sem threshold, sem truncamento por top_k. A decisão fica com + `engine.retrieval.build_decision`. + """ with self._lock: nd = self.normalize_discipline(discipline_filter) if nd is not None: - return self._hits_in_silo(nd, query, top_k) + cands = self._candidates_in_silo(nd, query, candidate_k) + cands.sort(key=lambda c: c.raw_score, reverse=True) + _log_search_candidates(query, nd, cands, candidate_k) + return cands - if self._global_context_mode == "geral": - hits = self._hits_in_silo("geral", query, top_k) - hits += self._hits_in_silo("db", query, top_k) - hits.sort(key=lambda h: h["score"], reverse=True) - return hits[:top_k] - - merged: list[dict] = [] + merged: list[RetrievalCandidate] = [] for silo in sorted(self._silos.keys()): - merged.extend(self._hits_in_silo(silo, query, top_k)) - merged.sort(key=lambda h: h["score"], reverse=True) - return merged[:top_k] + merged.extend(self._candidates_in_silo(silo, query, candidate_k)) + merged.sort(key=lambda c: c.raw_score, reverse=True) + merged = merged[:candidate_k] + _log_search_candidates(query, nd, merged, candidate_k) + return merged + + # --- Compatibilidade retroativa ----------------------------------------- + + def search( + self, + query: str, + top_k: int = 3, + discipline_filter: str | None = None, + ) -> list[dict]: + """API antiga mantida para código legado. + + Hoje delega para `search_candidates()` e devolve dicts no formato + antigo, mas com `score` = `normalized_score` (mantém semântica da + UI). Código novo deve consumir `search_candidates()`. + """ + cands = self.search_candidates(query, candidate_k=top_k, discipline_filter=discipline_filter) + return [ + { + "source": c.source, + "text": c.text, + "discipline": c.discipline, + "score": c.normalized_score, + "raw_score": c.raw_score, + } + for c in cands[:top_k] + ] diff --git a/frontend/assets/images/KernelBanner.webp b/frontend/assets/images/KernelBanner.webp new file mode 100644 index 0000000..4392e56 Binary files /dev/null and b/frontend/assets/images/KernelBanner.webp differ diff --git a/frontend/assets/images/spiderMan.webp b/frontend/assets/images/spiderMan.webp new file mode 100644 index 0000000..3b332cf Binary files /dev/null and b/frontend/assets/images/spiderMan.webp differ diff --git a/kernel-status.svg b/kernel-status.svg deleted file mode 100644 index f18c2ca..0000000 --- a/kernel-status.svg +++ /dev/null @@ -1,225 +0,0 @@ - - - - - - - -
-
-

- - - - Overall issues and pull requests status -

-
-
-

On Gaab's repositories

-
-
-

Issues

- - - - - - - - - - - - - - -
-
- - - - - 0 open -
-
- - - - - 1 closed -
-
- - - - 0 drafts -
-
- - - - 0 skipped -
-
-
-
-

Pull requests

- - - - - - - - - - - - - - -
-
- - - - 2 open -
-
- - - - 8 merged -
-
- - - - - 0 drafts -
-
- - - - 0 closed -
-
-
-
-
-
-

- - - - 5 Languages -

-
-
-

Most used languages

- estimation from 12mb of code in 1600 edited files across 333 commits - - - - - - - - - - - -
-
-
-
- - - - JavaScript -
- -
13.4k lines
-
56.85%
-
-
-
-
- - - - Shell -
- -
937 lines
-
3.2%
-
-
-
-
-
-
- - - - HTML -
- -
4.54k lines
-
24.49%
-
-
-
-
-
-
- - - - Python -
- -
1.90k lines
-
8.07%
-
-
-
-
-
-
- - - - CSS -
- -
2.96k lines
-
7.39%
-
-
-
-
-
-
-

- - - - Recent coding habits -

-
-
-
- - - - Unexpected error -
-
-
-
-
-
-
-
\ No newline at end of file diff --git a/main.py b/main.py index b45637f..807a770 100644 --- a/main.py +++ b/main.py @@ -1,11 +1,11 @@ -"""Ponto de entrada: logging, serviços, watchdog e aplicação FastAPI.""" +"""Ponto de entrada: logging, serviços e aplicação FastAPI.""" from __future__ import annotations import logging import uvicorn - + from app.factory import create_app from app.state import AppServices from core.config import Settings @@ -14,19 +14,16 @@ from engine.context import ContextManager from engine.pinned_store import PinnedSessionStore from engine.search import SearchEngine -from engine.watcher import start_content_observer configure_logging() log = logging.getLogger("kernelbots.main") settings = Settings.load() search_engine = SearchEngine( - settings.content_dir, settings.bm25_score_threshold, settings.global_context_mode, settings=settings, ) -observer = start_content_observer(search_engine, settings.content_dir) pinned_store = PinnedSessionStore() context_manager = ContextManager(settings, search_engine, pinned_store=pinned_store) @@ -36,7 +33,6 @@ search_engine=search_engine, context_manager=context_manager, chat_provider=chat_provider, - observer=observer, pinned_store=pinned_store, ) app = create_app(services) @@ -45,9 +41,9 @@ if __name__ == "__main__": log.info("=" * 60) log.info(" ACL — Agente de Contexto Local") - log.info(f" Content dir : {settings.content_dir}") + log.info(f" Fonte de dados: MySQL ({settings.db_host}:{settings.db_port}/{settings.db_name})") log.info(f" BM25 threshold: {settings.bm25_score_threshold}") log.info(f" Contexto global (sem filtro): {settings.global_context_mode}") log.info(f" Modelos ({len(settings.models)}): {', '.join(settings.models)}") log.info("=" * 60) - uvicorn.run("main:app", host="127.0.0.1", port=8000, reload=False) + uvicorn.run("main:app", host="127.0.0.1", port=8001, reload=False) diff --git a/requirements.txt b/requirements.txt index bbba313..36bdc92 100644 --- a/requirements.txt +++ b/requirements.txt @@ -6,4 +6,7 @@ jinja2 rank-bm25 watchdog pytest -PyMySQL \ No newline at end of file +PyMySQL +cryptography +PyYAML +jsonschema \ No newline at end of file