Automation assistant for attendance management.
Project description
Always Attend
An automation helper to submit weekly attendance codes. Now in Public Beta.
⚠️ Use responsibly and follow your institution’s policies.
[!WARNING]
This project is currently in Public Beta. Features may change and bugs are expected.
Receive Feedback Here:
📥 Get Always Attend
Install the CLI using one of these two supported flows:
Option 1 — uv tool (recommended)
- Install uv if it is not already available:
curl -LsSf https://astral.sh/uv/install.sh | sh
- Install
always-attendand expose the bundledplaywrightexecutable:uv tool install --with-executables-from playwright always-attend
- Verify the CLI:
attend --help
The app prefers your installed Chrome/Edge. If Playwright Chromium is needed and missing, it will be downloaded automatically on first run.
If attend is not on your shell PATH yet, run:
uv tool update-shell
Option 2 — pipx
- Install pipx if needed:
python3 -m pip install --user pipx python3 -m pipx ensurepath
- Install
always-attend:pipx install always-attend
- Expose the
playwrightexecutable from the injected package:pipx inject --include-apps always-attend playwright
- Verify the CLI:
attend --help
🚀 Run The CLI
Public command:
attend
📋 Application Workflow
The application follows a structured 4-step workflow with multiple execution modes:
Core Workflow
- Environment Setup - Bootstrap and install dependencies
- Python Check - Verify Python environment and packages
- App Start - Load config, privacy policy, and first-run setup
- Choose Mode - Select execution mode:
Execution Modes
- 🔍 Stats (
--stats) - View attendance statistics (read-only) - 📤 Submit (default) - Main workflow for code submission
- 🔑 Login Only (
--login-only) - Refresh session and exit
Submit Workflow Path
When using Submit mode, the application follows this sequence:
- Session Check → Sign-in if needed
- Submission → Dry-run or submit codes
- Done → Save results and exit
Login Only Workflow Path
When using Login Only mode:
- Session Check → Sign-in if needed
- Done → Exit after refreshing session
Quick Start
# Basic execution
attend
# View statistics
attend stats
# Refresh login session
attend login
# Inspect resolved runtime paths for integrations
attend paths --json
# Run specific week
attend week 4
# Show browser (headed mode)
attend --headed
Install with uv tool or pipx, then run attend --help.
Runtime files now default to standard user directories:
- Linux:
~/.config/always-attend/.env,~/.local/state/always-attend/,~/.local/share/always-attend/codes/ - macOS:
~/Library/Application Support/always-attend/config/.env,~/Library/Application Support/always-attend/state/,~/Library/Application Support/always-attend/data/codes/ - Windows:
%APPDATA%\\always-attend\\config\\.env,%LOCALAPPDATA%\\always-attend\\state\\,%LOCALAPPDATA%\\always-attend\\data\\codes\\ - Override any location with env vars such as
ENV_FILE,STORAGE_STATE,ATTENDANCE_STATS_FILE, orCODES_DB_PATH
Integration contract:
- CLI:
attend paths --json - Python:
from always_attend import get_runtime_paths_dict
🧰 CLI Installation Details
Option A — uv tool (recommended)
- Fastest setup for a standalone CLI install.
- Keeps the command isolated from your project environments.
- Use this when you want
attendavailable globally for your user.
uv tool install --with-executables-from playwright always-attend
attend --dry-run
If Chromium is needed and not installed yet, the app will download it automatically.
Upgrade later:
uv tool upgrade always-attend
Option B — pipx
- Good fit if you already manage Python CLIs with
pipx. - Keeps
always-attendisolated in its own application environment.
pipx install always-attend
pipx inject --include-apps always-attend playwright
attend --dry-run
Upgrade later:
pipx upgrade always-attend
📦 Attendance Database
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:
data/
FIT1045/
3.json # [{"slot": "Workshop 01", "code": "LCPPH"}, ...]
FIT1047/
7.json
If you maintain your codes in a separate Git repository, point the tool at it:
export CODES_DB_REPO="git@github.com:you/attendance-db.git"
export CODES_DB_BRANCH="main"
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.
📊 Statistics Tracking
The tool now automatically tracks your attendance submission statistics:
# View detailed statistics
attend stats
# Or use the stats module directly
python stats.py
Statistics include:
- Total runs and success rate
- Codes submitted per course
- Recent activity timeline
- Error history
✨ Rich CLI Experience
Always Attend ships with a polished terminal UI powered by Rich:
- Animated Monash-blue typewriter banner with optional spark highlights (auto-disables on non-TTY output).
- Live, single-line progress bars with square block fills and cached ASCII spinners when
CLI_PROGRESS_RICH=1. - Animated logging for major workflow steps without leaking ANSI fragments when launched via
.command/.bat.
UI Control Flags
| Variable | Values | Effect |
|---|---|---|
CLI_STYLE |
fancy (default), simple, minimal |
Toggle banner + log animations |
FORCE_ANIMATIONS |
true / false |
Override TTY detection (useful for debugging) |
CLI_PROGRESS_RICH |
1 / 0 |
Enable/disable Rich live progress tracker |
Examples
# Full experience: animated banner, live block progress
CLI_STYLE=fancy CLI_PROGRESS_RICH=1 attend --dry-run
# Quiet fallback suitable for basic terminals
CLI_STYLE=minimal CLI_PROGRESS_RICH=0 attend
Set these in your .env to persist the chosen style across runs.
Troubleshooting
- If login keeps asking for MFA: re-run the headed login to refresh the saved session state
- If the browser fails to launch: make sure Google Chrome or Microsoft Edge is installed, or set
BROWSER_CHANNELtochrome/msedge. - If
attendis not found after install: restart the terminal, then runuv tool update-shellorpython3 -m pipx ensurepath. - When running, please do NOT use a VPN, as this may cause Okta to refuse the connection.
FAQ (Windows)
- Use
pyinstead ofpython: Ifpythonisn't found or points to another version, usepyfor bootstrap commands such aspy -m pip install --user pipx. - 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. - Path escaping issues: PowerShell uses backslashes (
\) and may treat them as escape characters. Wrap paths in quotes or use double backslashes likeC:\path\to\file. Git Bash uses forward slashes (/).
Command-Line Arguments
Primary command: attend
| Argument | Type | Description | Example |
|---|---|---|---|
--browser |
string | Browser engine (chromium/firefox/webkit) |
--browser chromium |
--channel |
string | System browser channel (chromium only: chrome, chrome-beta, msedge, etc.) |
--channel chrome |
--headed |
flag | Show browser UI (sets HEADLESS=0) |
--headed |
--dry-run |
flag | Print parsed codes and exit without submitting | --dry-run |
--week |
int | Submit codes for a specific week (sets WEEK_NUMBER) |
--week 4 |
--login-only |
flag | Refresh the session and exit without submitting | --login-only |
--stats |
flag | Display cached attendance statistics and exit | --stats |
--setup |
flag | Launch the configuration wizard interactively | --setup |
--debug |
flag | Enable debug logging profile | --debug |
--verbose |
flag | Enable verbose logging profile | --verbose |
--skip-update |
flag | Skip the git update check before running | --skip-update |
Release Automation
- Push a version tag such as
v0.1.1to trigger.github/workflows/release.yml. - The workflow validates that the tag matches
pyproject.toml, buildssdistandwheel, creates a GitHub Release, and publishes to PyPI. - 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.
Environment Variables
| Variable | Type | Required | Description | Example |
|---|---|---|---|---|
PORTAL_URL |
string URL | Yes | Attendance portal base URL | https://attendance.monash.edu.my |
CODES_DB_PATH |
string path | No | Root folder containing COURSE/WEEK.json files |
/srv/attendance-data |
CODES_DB_REPO |
string URL | No | Git repository that mirrors the data tree | git@github.com:you/attendance-db.git |
CODES_DB_BRANCH |
string | No | Branch to checkout when syncing the repository | main |
WEEK_NUMBER |
int | No | Force a specific week instead of auto-detecting | 4 |
SUBMIT_CONCURRENCY |
int | No | Maximum courses processed concurrently | 2 |
SUBMIT_TARGET_CONCURRENCY |
int | No | Parallel submission workers per course | 3 |
USERNAME |
string | No | Okta username for auto-login | student@example.edu |
PASSWORD |
string | No | Okta password for auto-login | correcthorsebattery |
TOTP_SECRET |
string (base32) | No | MFA TOTP secret for auto-login | JBSWY3DPEHPK3PXP |
AUTO_LOGIN |
flag (0/1) | No | Toggle automatic login | 1 |
BROWSER |
string | No | Engine override (chromium/firefox/webkit) |
chromium |
BROWSER_CHANNEL |
string | No | System channel (chrome/msedge/etc.) |
chrome |
HEADLESS |
flag (0/1 or true/false) | No | Run without UI (0 disables) | 0 |
USER_DATA_DIR |
string path | No | Persistent browser profile directory | ~/.always-attend-profile |
LOG_PROFILE |
string | No | Logging profile (user/quiet/debug/verbose) |
verbose |
LOG_FILE |
string path | No | Optional log file destination | /tmp/always-attend.log |
SKIP_UPDATE_CHECK |
flag (0/1 or true/false) | No | Skip remote git pull when set | 1 |
Disclaimer
- 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.
- 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.
- 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.
License
- This project is licensed under the GNU General Public License v3.0 (GPL‑3.0). See the full text in the
LICENSEfile. - You may copy, modify, and distribute this software under the terms of the GPL‑3.0. It is provided “as is”, without any warranty.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file always_attend-0.1.1.tar.gz.
File metadata
- Download URL: always_attend-0.1.1.tar.gz
- Upload date:
- Size: 87.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5b516098f74354ec7d916b5ca1d41cd0830dbcb27bca9f49f5902057ec71ba11
|
|
| MD5 |
3d08f6d6228e164e2a91916868aaab4b
|
|
| BLAKE2b-256 |
24f4f5ba23b83ee168b8226ee48928074828665de621ddb1af8e7627ee32d33d
|
Provenance
The following attestation bundles were made for always_attend-0.1.1.tar.gz:
Publisher:
release.yml on bunizao/always-attend
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
always_attend-0.1.1.tar.gz -
Subject digest:
5b516098f74354ec7d916b5ca1d41cd0830dbcb27bca9f49f5902057ec71ba11 - Sigstore transparency entry: 1084353498
- Sigstore integration time:
-
Permalink:
bunizao/always-attend@853f64a6c03e6be548348a763d92d0066bb799a8 -
Branch / Tag:
refs/tags/v0.1.1 - Owner: https://github.com/bunizao
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@853f64a6c03e6be548348a763d92d0066bb799a8 -
Trigger Event:
push
-
Statement type:
File details
Details for the file always_attend-0.1.1-py3-none-any.whl.
File metadata
- Download URL: always_attend-0.1.1-py3-none-any.whl
- Upload date:
- Size: 94.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5706dcc932098a64642fe1887bf7e0a0b06edc439f62096ba0efd9e2cd6469c8
|
|
| MD5 |
b08ea3c3ef7bed714ae77758d9dfa7f0
|
|
| BLAKE2b-256 |
7ea763380aa6e670f3a1326af0ef8e71ff8b6f5c259d7a39845466824550eea3
|
Provenance
The following attestation bundles were made for always_attend-0.1.1-py3-none-any.whl:
Publisher:
release.yml on bunizao/always-attend
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
always_attend-0.1.1-py3-none-any.whl -
Subject digest:
5706dcc932098a64642fe1887bf7e0a0b06edc439f62096ba0efd9e2cd6469c8 - Sigstore transparency entry: 1084353550
- Sigstore integration time:
-
Permalink:
bunizao/always-attend@853f64a6c03e6be548348a763d92d0066bb799a8 -
Branch / Tag:
refs/tags/v0.1.1 - Owner: https://github.com/bunizao
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@853f64a6c03e6be548348a763d92d0066bb799a8 -
Trigger Event:
push
-
Statement type: