From 2629e8f2cdd45ae385adea20ab5b4c964c4856a0 Mon Sep 17 00:00:00 2001 From: Yuyao Huang Date: Sat, 9 May 2026 10:12:37 +0800 Subject: [PATCH] feat: move config.py to config.example.py, exclude config.py from git tracking - Copy config.py to config.example.py as template (tracked in git) - Add config.py to .gitignore (local config per developer) - Update README.md with configuration setup instructions - Fix outdated references (TinyDB -> SQLite, add missing files) - Persist goal selection across task and note pages --- .gitignore | 3 ++ README.md | 55 +++++++++++++++++++--------------- config.py => config.example.py | 0 static/js/notes.js | 13 ++++---- 4 files changed, 41 insertions(+), 30 deletions(-) rename config.py => config.example.py (100%) diff --git a/.gitignore b/.gitignore index b265fbb..2e1a160 100644 --- a/.gitignore +++ b/.gitignore @@ -23,6 +23,9 @@ uv.lock .DS_Store Thumbs.db +# Configuration (each developer must create their own) +config.py + # Environment variables .env .env.local diff --git a/README.md b/README.md index fbc12a0..6baec4d 100644 --- a/README.md +++ b/README.md @@ -26,6 +26,9 @@ A web-based task management application focused on goal-oriented task tracking w git clone cd GoalsBreakDown +# Create your local configuration from the example +cp config.example.py config.py + # Install dependencies with uv uv sync @@ -44,32 +47,33 @@ The application will start at **http://127.0.0.1:5000** ## Configuration -Edit `config.py` to customize settings: +Local configuration is managed via `config.py` (not tracked in git). Use `config.example.py` as a template: -```python -# Database path -DB_PATH = "data/db.json" - -# Default admin credentials (change these!) -DEFAULT_ADMIN_USERNAME = "admin" -DEFAULT_ADMIN_PASSWORD = "admin123" - -# Default max goals for new users -DEFAULT_MAX_GOALS = 5 - -# Flask settings -SECRET_KEY = "your-secret-key-here" # Change in production -DEBUG = True -HOST = "0.0.0.0" -PORT = 5000 +```bash +# Copy the example config and edit it +cp config.example.py config.py ``` +Available settings in `config.py`: + +| Setting | Description | Default | +|---|---|---| +| `DB_PATH` | SQLite database path | `data/db.sqlite` | +| `DEFAULT_ADMIN_USERNAME` | Default admin username | `admin` | +| `DEFAULT_ADMIN_PASSWORD` | Default admin password | `admin123` | +| `DEFAULT_MAX_GOALS` | Max goals per new user | `5` | +| `SECRET_KEY` | Flask session secret (change in production!) | — | +| `DEBUG` | Debug mode | `True` | +| `HOST` | Server bind address | `0.0.0.0` | +| `PORT` | Server port | `5000` | + ### Production Deployment -1. Change `SECRET_KEY` to a random secure string -2. Set `DEBUG = False` -3. Change default admin credentials -4. Use a production WSGI server (e.g., gunicorn): +1. Copy `config.example.py` to `config.py` +2. Change `SECRET_KEY` to a random secure string +3. Set `DEBUG = False` +4. Change default admin credentials +5. Use a production WSGI server (e.g., gunicorn): ```bash uv add gunicorn @@ -81,9 +85,11 @@ uv run gunicorn -w 4 -b 0.0.0.0:5000 app:app ``` GoalsBreakDown/ ├── app.py # Flask application -├── config.py # Configuration -├── database.py # TinyDB operations +├── config.example.py # Configuration template (tracked in git) +├── config.py # Local configuration (NOT tracked in git) +├── database.py # SQLite operations ├── auth.py # Authentication helpers +├── schema.py # Database schema & migration ├── templates/ # HTML templates ├── static/ │ ├── css/ # Stylesheets @@ -94,8 +100,9 @@ GoalsBreakDown/ ## Tech Stack - **Backend:** Python + Flask -- **Database:** TinyDB +- **Database:** SQLite - **Frontend:** Vanilla JS + HTML/CSS - **Drag-and-Drop:** SortableJS +- **Markdown:** marked.js - **Authentication:** bcrypt + Flask sessions - **Environment:** uv diff --git a/config.py b/config.example.py similarity index 100% rename from config.py rename to config.example.py diff --git a/static/js/notes.js b/static/js/notes.js index 7f3be3d..4502e93 100644 --- a/static/js/notes.js +++ b/static/js/notes.js @@ -107,7 +107,7 @@ async function openNote(noteId) { document.getElementById("note-modal").classList.add("active"); } -function openNewNote() { +async function openNewNote() { editingNoteId = null; document.getElementById("note-modal-title").textContent = "New Note"; document.getElementById("note-id").value = ""; @@ -120,7 +120,11 @@ function openNewNote() { document.getElementById("note-modal").classList.add("active"); if (savedGoalId) { - populateTasks(savedGoalId); + await populateTasks(savedGoalId); + const goal = goals.find(g => g.id === savedGoalId); + if (goal && goal.selected_task_id) { + document.getElementById("note-task").value = goal.selected_task_id; + } } else { document.getElementById("note-task").innerHTML = ''; } @@ -225,10 +229,7 @@ document.addEventListener("DOMContentLoaded", () => { populateTasks(goalId); }); - document.getElementById("filter-goal").addEventListener("change", async () => { - const goalId = parseInt(document.getElementById("filter-goal").value) || null; - savedGoalId = goalId; - await patch("/api/user/selected-goal", { goal_id: goalId }); + document.getElementById("filter-goal").addEventListener("change", () => { loadNotes(); });