|
1 | | -<h1 align="center">Always Attend</h1> |
2 | | -<p align="center"> |
3 | | - <img src="https://img.shields.io/badge/python-3.11%2B-blue.svg"> |
4 | | - <img src="https://img.shields.io/badge/License-GPLv3-blue.svg"> |
5 | | - <img src="https://img.shields.io/github/last-commit/bunizao/always-attend"> |
6 | | - <img src="https://img.shields.io/badge/PRs-welcome-brightgreen.svg"> |
7 | | - <img src="https://img.shields.io/badge/platform-macOS%20%7C%20Linux%20%7C%20Windows-lightgrey"> |
8 | | - <p align="center"> |
9 | | - <img src="https://img.shields.io/badge/status-Public%20Beta-orange?style=for-the-badge"> |
10 | | -<p align="center"> |
11 | | - An automation helper to submit weekly attendance codes. Now in Public Beta.<br> |
12 | | - ⚠️ <b>Use responsibly and follow your institution’s policies.</b> |
13 | | -</p> |
| 1 | +# Always Attend |
14 | 2 |
|
15 | | -<p align="center"> |
16 | | - <a href="README_zh.md"><b>中文文档</b></a> |
17 | | -</p> |
18 | | - |
19 | | -> [!WARNING] |
20 | | -> This project is currently in **Public Beta**. Features may change and bugs are expected. |
21 | | -> Receive Feedback Here: [](https://github.com/bunizao/always-attend/issues/new) |
22 | | -
|
23 | | - |
24 | | - |
25 | | - |
26 | | - |
27 | | - |
28 | | - |
29 | | -## 📥 Get Always Attend |
30 | | - |
31 | | -Install the CLI using one of these two supported flows: |
32 | | - |
33 | | -### Option 1 — `uv tool` (recommended) |
34 | | -1. Install [uv](https://docs.astral.sh/uv/) if it is not already available: |
35 | | - ```bash |
36 | | - curl -LsSf https://astral.sh/uv/install.sh | sh |
37 | | - ``` |
38 | | -2. Install `always-attend` and expose the bundled `playwright` executable: |
39 | | - ```bash |
40 | | - uv tool install --with-executables-from playwright always-attend |
41 | | - ``` |
42 | | -3. Verify the CLI: |
43 | | - ```bash |
44 | | - attend --help |
45 | | - ``` |
46 | | - |
47 | | -The app prefers your installed Chrome/Edge. If Playwright Chromium is needed and missing, it will be downloaded automatically on first run. |
48 | | - |
49 | | -If `attend` is not on your shell `PATH` yet, run: |
50 | | - |
51 | | -```bash |
52 | | -uv tool update-shell |
53 | | -``` |
54 | | - |
55 | | -### Option 2 — `pipx` |
56 | | -1. Install [pipx](https://pipx.pypa.io/stable/installation/) if needed: |
57 | | - ```bash |
58 | | - python3 -m pip install --user pipx |
59 | | - python3 -m pipx ensurepath |
60 | | - ``` |
61 | | -2. Install `always-attend`: |
62 | | - ```bash |
63 | | - pipx install always-attend |
64 | | - ``` |
65 | | -3. Expose the `playwright` executable from the injected package: |
66 | | - ```bash |
67 | | - pipx inject --include-apps always-attend playwright |
68 | | - ``` |
69 | | -4. Verify the CLI: |
70 | | - ```bash |
71 | | - attend --help |
72 | | - ``` |
73 | | - |
74 | | -## 🚀 Run The CLI |
75 | | - |
76 | | -Public command: |
77 | | - |
78 | | -- `attend` |
79 | | - |
80 | | -## 🤖 Agents-First Workflow |
81 | | - |
82 | | -`attend` is now the single agent-facing entrypoint. The intended execution model is: |
83 | | - |
84 | | -1. `attend auth ...` delegates Okta login and session checks to the external `okta` CLI. |
85 | | -2. `attend fetch ...` calls `moodle-cli`, `edstem`, or `gogcli` and passes shared Okta cookie context through environment variables. |
86 | | -3. `attend resolve ...` normalizes an agent-authored submission plan into a stable JSON shape. |
87 | | -4. `attend submit ...` materializes the resolved plan into the local codes store and reuses the Playwright submitter for the final portal submission. |
88 | | - |
89 | | -### Quick Start |
90 | | - |
91 | | -```bash |
92 | | -# Inspect runtime locations for integrations |
93 | | -attend paths --json |
94 | | - |
95 | | -# Refresh or create the shared Okta session |
96 | | -attend auth login https://attendance.example.edu --json |
97 | | - |
98 | | -# Check whether the shared session is still valid |
99 | | -attend auth check https://attendance.example.edu --json |
100 | | - |
101 | | -# Fetch source data |
102 | | -attend fetch --source edstem --json |
103 | | -attend fetch --source edstem --course FIT2099 --kind threads --json |
104 | | - |
105 | | -# Validate a submission plan |
106 | | -attend resolve --plan plan.json --json |
107 | | - |
108 | | -# Submit a validated plan |
109 | | -attend submit --plan plan.json --portal-url https://attendance.example.edu --json |
110 | | -``` |
111 | | - |
112 | | -### Shared Session Contract |
113 | | - |
114 | | -When `attend fetch` runs, it exports the shared Okta session to child CLIs using these environment variables: |
115 | | - |
116 | | -- `ALWAYS_ATTEND_OKTA_URL` |
117 | | -- `ALWAYS_ATTEND_OKTA_COOKIES_JSON` |
118 | | -- `ALWAYS_ATTEND_OKTA_COOKIE_HEADER` |
119 | | -- `OKTA_COOKIES_JSON` |
120 | | -- `OKTA_COOKIE_HEADER` |
121 | | - |
122 | | -Adapters should consume these values instead of implementing their own interactive login flow. |
123 | | - |
124 | | -Runtime files default to standard user directories: |
125 | | -- Linux: |
126 | | - `~/.config/always-attend/.env`, |
127 | | - `~/.local/state/always-attend/`, |
128 | | - `~/.local/share/always-attend/codes/` |
129 | | -- macOS: |
130 | | - `~/Library/Application Support/always-attend/config/.env`, |
131 | | - `~/Library/Application Support/always-attend/state/`, |
132 | | - `~/Library/Application Support/always-attend/data/codes/` |
133 | | -- Windows: |
134 | | - `%APPDATA%\\always-attend\\config\\.env`, |
135 | | - `%LOCALAPPDATA%\\always-attend\\state\\`, |
136 | | - `%LOCALAPPDATA%\\always-attend\\data\\codes\\` |
137 | | -- Override any location with env vars such as `ENV_FILE`, `STORAGE_STATE`, `ATTENDANCE_STATS_FILE`, or `CODES_DB_PATH` |
138 | | - |
139 | | -Integration contract: |
140 | | -- CLI: `attend paths --json` |
141 | | -- Python: `from always_attend import get_runtime_paths_dict` |
142 | | - |
143 | | -## 📦 Attendance Database |
144 | | - |
145 | | -Always Attend now reads attendance codes exclusively from `codes_db_path` (by default a dedicated `codes/` directory under the app data directory, or from `CODES_DB_PATH` if overridden). Each course gets its own subfolder and every week is represented by a JSON file: |
146 | | - |
147 | | -``` |
148 | | -data/ |
149 | | - FIT1045/ |
150 | | - 3.json # [{"slot": "Workshop 01", "code": "LCPPH"}, ...] |
151 | | - FIT1047/ |
152 | | - 7.json |
153 | | -``` |
154 | | - |
155 | | -If you maintain your codes in a separate Git repository, point the tool at it: |
156 | | - |
157 | | -```bash |
158 | | -export CODES_DB_REPO="git@github.com:you/attendance-db.git" |
159 | | -export CODES_DB_BRANCH="main" |
160 | | -``` |
161 | | - |
162 | | -On every run the repository is cloned into `codes_db_path` (if missing) or updated in place before submission. Without a repository the tool simply reads whatever JSON files already exist there. |
163 | | - |
164 | | -## 📊 Statistics Tracking |
165 | | - |
166 | | -The tool now automatically tracks your attendance submission statistics: |
167 | | - |
168 | | -```bash |
169 | | -# View detailed statistics |
170 | | -attend stats |
171 | | - |
172 | | -# Or use the stats module directly |
173 | | -python stats.py |
174 | | -``` |
175 | | - |
176 | | -Statistics include: |
177 | | -- Total runs and success rate |
178 | | -- Codes submitted per course |
179 | | -- Recent activity timeline |
180 | | -- Error history |
181 | | - |
182 | | -## Troubleshooting |
183 | | - |
184 | | -- If login keeps asking for MFA: re-run the headed login to refresh the saved session state |
185 | | -- If the browser fails to launch: make sure Google Chrome or Microsoft Edge is installed, or set `BROWSER_CHANNEL` to `chrome`/`msedge`. |
186 | | -- If `attend` is not found after install: restart the terminal, then run `uv tool update-shell` or `python3 -m pipx ensurepath`. |
187 | | -- When running, please do NOT use a VPN, as this may cause Okta to refuse the connection. |
188 | | - |
189 | | -## FAQ (Windows) |
190 | | - |
191 | | -- **Use `py` instead of `python`**: If `python` isn't found or points to another version, use `py` for bootstrap commands such as `py -m pip install --user pipx`. |
192 | | -- **Switching between Git Bash and PowerShell**: In terminals like VS Code, use the dropdown to open a new Git Bash or PowerShell window. Some commands (e.g., `source`) only work in Git Bash, while PowerShell uses `.\` for scripts. |
193 | | -- **Path escaping issues**: PowerShell uses backslashes (`\`) and may treat them as escape characters. Wrap paths in quotes or use double backslashes like `C:\path\to\file`. Git Bash uses forward slashes (`/`). |
194 | | - |
195 | | -## Command-Line Arguments |
196 | | - |
197 | | -Primary command: `attend` |
198 | | - |
199 | | -| Argument | Type | Description | Example | |
200 | | -| --- | --- | --- | --- | |
201 | | -| `--browser` | string | Browser engine (`chromium`/`firefox`/`webkit`) | `--browser chromium` | |
202 | | -| `--channel` | string | System browser channel (chromium only: `chrome`, `chrome-beta`, `msedge`, etc.) | `--channel chrome` | |
203 | | -| `--headed` | flag | Show browser UI (sets `HEADLESS=0`) | `--headed` | |
204 | | -| `--dry-run` | flag | Print parsed codes and exit without submitting | `--dry-run` | |
205 | | -| `--week` | int | Submit codes for a specific week (sets `WEEK_NUMBER`) | `--week 4` | |
206 | | -| `--login-only` | flag | Refresh the session and exit without submitting | `--login-only` | |
207 | | -| `--stats` | flag | Display cached attendance statistics and exit | `--stats` | |
208 | | -| `--setup` | flag | Launch the configuration wizard interactively | `--setup` | |
209 | | -| `--debug` | flag | Enable debug logging profile | `--debug` | |
210 | | -| `--verbose` | flag | Enable verbose logging profile | `--verbose` | |
211 | | -| `--skip-update` | flag | Skip the git update check before running | `--skip-update` | |
212 | | - |
213 | | -## Release Automation |
214 | | - |
215 | | -- Push a version tag such as `v0.1.1` to trigger `.github/workflows/release.yml`. |
216 | | -- The workflow validates that the tag matches `pyproject.toml`, builds `sdist` and `wheel`, creates a GitHub Release, and publishes to PyPI. |
217 | | -- PyPI publishing is configured for Trusted Publishing, so the GitHub repository still needs to be registered as a trusted publisher in the target PyPI project. |
| 3 | +> This is something I wrote in my FIT1045 H3 report. |
| 4 | +> |
| 5 | +> Always-Attend is a work I refined with great care and one I am proud of. |
| 6 | +> No matter the outcome, I am still proud of it. |
218 | 7 |
|
219 | 8 | ## Reflection |
220 | 9 |
|
221 | | -> I kept building, not to compete with AI, but to finish what I started. I kept adding new ideas, refactoring, polishing small things that no one else would notice. Slowly, the project became not just a tool but a reflection of my curiosity and patience. Finishing it reminded me that the value isn't only in what a program does, but in what I learn while making it. |
222 | | -
|
223 | | -## Environment Variables |
224 | | - |
225 | | -| Variable | Type | Required | Description | Example | |
226 | | -| --- | --- | --- | --- | --- | |
227 | | -| `PORTAL_URL` | string URL | Yes | Attendance portal base URL | `https://attendance.monash.edu.my` | |
228 | | -| `CODES_DB_PATH` | string path | No | Root folder containing `COURSE/WEEK.json` files | `/srv/attendance-data` | |
229 | | -| `CODES_DB_REPO` | string URL | No | Git repository that mirrors the data tree | `git@github.com:you/attendance-db.git` | |
230 | | -| `CODES_DB_BRANCH` | string | No | Branch to checkout when syncing the repository | `main` | |
231 | | -| `WEEK_NUMBER` | int | No | Force a specific week instead of auto-detecting | `4` | |
232 | | -| `SUBMIT_CONCURRENCY` | int | No | Maximum courses processed concurrently | `2` | |
233 | | -| `SUBMIT_TARGET_CONCURRENCY` | int | No | Parallel submission workers per course | `3` | |
234 | | -| `USERNAME` | string | No | Okta username for auto-login | `student@example.edu` | |
235 | | -| `PASSWORD` | string | No | Okta password for auto-login | `correcthorsebattery` | |
236 | | -| `TOTP_SECRET` | string (base32) | No | MFA TOTP secret for auto-login | `JBSWY3DPEHPK3PXP` | |
237 | | -| `AUTO_LOGIN` | flag (0/1) | No | Toggle automatic login | `1` | |
238 | | -| `BROWSER` | string | No | Engine override (`chromium`/`firefox`/`webkit`) | `chromium` | |
239 | | -| `BROWSER_CHANNEL` | string | No | System channel (`chrome`/`msedge`/etc.) | `chrome` | |
240 | | -| `HEADLESS` | flag (0/1 or true/false) | No | Run without UI (0 disables) | `0` | |
241 | | -| `USER_DATA_DIR` | string path | No | Persistent browser profile directory | `~/.always-attend-profile` | |
242 | | -| `LOG_PROFILE` | string | No | Logging profile (`user`/`quiet`/`debug`/`verbose`) | `verbose` | |
243 | | -| `LOG_FILE` | string path | No | Optional log file destination | `/tmp/always-attend.log` | |
244 | | -| `SKIP_UPDATE_CHECK` | flag (0/1 or true/false) | No | Skip remote git pull when set | `1` | |
245 | | - |
246 | | -## Disclaimer |
247 | | - |
248 | | -- This project is for educational and personal use only. Use it responsibly and follow your institution’s policies and the website’s terms of use. |
249 | | -- This project is not affiliated with, endorsed by, or sponsored by any university or service provider. All product names, logos, and brands are property of their respective owners. |
250 | | -- You are solely responsible for any use of this tool and any consequences that may arise. The authors provide no guarantee that it will work for your specific setup. |
251 | | - |
252 | | -## License |
253 | | - |
254 | | -- This project is licensed under the GNU General Public License v3.0 (GPL‑3.0). See the full text in the `LICENSE` file. |
255 | | -- You may copy, modify, and distribute this software under the terms of the GPL‑3.0. It is provided “as is”, without any warranty. |
| 10 | +> I've been developing Always-Attend for two months now. There are already more than 150+ commits, and the project has grown from a small 500-line script into something I'm genuinely proud of. Today it has over 30+ Python files and around 6000+ lines of code. It feels modern, elegant, and useful. It is something I believe many people would enjoy using. |
| 11 | +> |
| 12 | +> Along the way, I've had help from AI agents. Codex and Claude Code helped me set up test frameworks, edit files according to each iteration, and fix bugs that once took me hours to find. Always-Attend would not have reached this stage without them. Working with these tools also taught me how to think more clearly, how to describe a problem precisely, and how to review my own code with a calmer mind. |
| 13 | +> |
| 14 | +> Then ChatGPT Atlas arrived, and everything changed. I downloaded it the moment it was released and tried it right away. I asked it to find the week's attendance code in Gmail, open a GitHub issue, and submit the code on the website. It moved through my browser, filled the forms, and formatted text like a human. Watching it, I honestly felt a shock. For a moment, I thought Always-Attend no longer mattered. It felt like watching the story of so many startups I knew, small projects replaced by products from bigger, well-known companies. Maybe in this fast changing age of AI, that's the only ending for small projects like Always-Attend. |
| 15 | +> |
| 16 | +> Thinking about that made me feel truly discouraged, but I didn't stop. I kept building, not to compete with AI, but to finish what I started. I kept adding new ideas, refactoring, polishing small things that no one else would notice. Slowly, the project became not just a tool but a reflection of my curiosity and patience. Finishing it reminded me that the value isn't only in what a program does, but in what I learn while making it. |
| 17 | +> |
| 18 | +> AI will keep advancing. Tools will get faster, smarter, and more capable. What I keep is the part that does not ever change: the curiosity, the discipline, and the quiet satisfaction of watching something I built come alive line by line. |
0 commit comments