keka-log 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- keka_log-0.1.0/LICENSE +21 -0
- keka_log-0.1.0/MANIFEST.in +1 -0
- keka_log-0.1.0/PKG-INFO +227 -0
- keka_log-0.1.0/README.md +200 -0
- keka_log-0.1.0/keka_log/Orey Ajaamu Lagettaroy.mp3 +0 -0
- keka_log-0.1.0/keka_log/__init__.py +3 -0
- keka_log-0.1.0/keka_log/are_tu_jaa_re.mp3 +0 -0
- keka_log-0.1.0/keka_log/keka.py +174 -0
- keka_log-0.1.0/keka_log/keka_menubar.py +110 -0
- keka_log-0.1.0/keka_log/menubar_entry.py +18 -0
- keka_log-0.1.0/keka_log.egg-info/PKG-INFO +227 -0
- keka_log-0.1.0/keka_log.egg-info/SOURCES.txt +16 -0
- keka_log-0.1.0/keka_log.egg-info/dependency_links.txt +1 -0
- keka_log-0.1.0/keka_log.egg-info/entry_points.txt +3 -0
- keka_log-0.1.0/keka_log.egg-info/requires.txt +3 -0
- keka_log-0.1.0/keka_log.egg-info/top_level.txt +3 -0
- keka_log-0.1.0/pyproject.toml +50 -0
- keka_log-0.1.0/setup.cfg +4 -0
keka_log-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Quid Keka Log contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
include keka_log/*.mp3
|
keka_log-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: keka-log
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: CLI and optional macOS menu bar app for Keka attendance tracking.
|
|
5
|
+
Author: Quid Keka Log contributors
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://pypi.org/project/keka-log/
|
|
8
|
+
Keywords: keka,attendance,time-tracking,macos,menubar
|
|
9
|
+
Classifier: Development Status :: 4 - Beta
|
|
10
|
+
Classifier: Environment :: MacOS X
|
|
11
|
+
Classifier: Intended Audience :: End Users/Desktop
|
|
12
|
+
Classifier: Operating System :: MacOS :: MacOS X
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
20
|
+
Classifier: Topic :: Office/Business
|
|
21
|
+
Requires-Python: >=3.8
|
|
22
|
+
Description-Content-Type: text/markdown
|
|
23
|
+
License-File: LICENSE
|
|
24
|
+
Provides-Extra: menubar
|
|
25
|
+
Requires-Dist: rumps>=0.4.0; extra == "menubar"
|
|
26
|
+
Dynamic: license-file
|
|
27
|
+
|
|
28
|
+
# Keka Log
|
|
29
|
+
|
|
30
|
+
A Python package for checking and tracking Keka attendance. Includes a CLI tool and a Mac menu bar app to keep track of your shift time.
|
|
31
|
+
|
|
32
|
+
## Prerequisites & Configuration (`.zshrc`)
|
|
33
|
+
|
|
34
|
+
Before using this tool, you need to extract your `KEKA_TOKEN` from your browser and configure it in your shell.
|
|
35
|
+
|
|
36
|
+
### 1. Get your Keka Token
|
|
37
|
+
1. Log in to your Keka portal in your web browser.
|
|
38
|
+
2. Open Developer Tools (Inspect Element) -> **Network** tab.
|
|
39
|
+
3. Refresh the page or navigate to the attendance section.
|
|
40
|
+
4. Look for an API request (e.g., `summary` or `attendance`).
|
|
41
|
+
5. Check the **Request Headers** for the `Authorization` header. It will look like `Bearer eyJ...`.
|
|
42
|
+
6. Copy the long token string (everything after `Bearer `).
|
|
43
|
+
|
|
44
|
+
### 2. Add to your `.zshrc`
|
|
45
|
+
You need to configure the token and your preferred notification language in your shell configuration file (e.g., `~/.zshrc`).
|
|
46
|
+
|
|
47
|
+
1. Open your `.zshrc` file in your terminal:
|
|
48
|
+
```bash
|
|
49
|
+
nano ~/.zshrc
|
|
50
|
+
```
|
|
51
|
+
2. Add the following lines at the bottom of the file:
|
|
52
|
+
```bash
|
|
53
|
+
# Keka Attendance App Configuration
|
|
54
|
+
export KEKA_TOKEN="paste_your_copied_token_here"
|
|
55
|
+
|
|
56
|
+
# Optional: Set notification audio language ('hindi', 'telugu', or 'none')
|
|
57
|
+
# Defaults to 'hindi' if not set. Use 'none' to disable audio entirely.
|
|
58
|
+
export KEKA_LANGUAGE="telugu"
|
|
59
|
+
```
|
|
60
|
+
3. Save the file (in nano: `Ctrl+O`, `Enter`, `Ctrl+X`) and reload your configuration:
|
|
61
|
+
```bash
|
|
62
|
+
source ~/.zshrc
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## Installation
|
|
66
|
+
|
|
67
|
+
### From PyPI (recommended)
|
|
68
|
+
|
|
69
|
+
Install the CLI from [PyPI](https://pypi.org/project/keka-log/) (replace the version if you pin one):
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
python3 -m venv .venv
|
|
73
|
+
source .venv/bin/activate
|
|
74
|
+
pip install keka-log
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
On **macOS**, install with the `menubar` extra so `keka-menubar` works (pulls in `rumps` and its dependencies):
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
pip install keka-log[menubar]
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
The base package has no third-party dependencies; only `keka-log[menubar]` adds the menu bar stack.
|
|
84
|
+
|
|
85
|
+
### Without a virtual environment (direct install)
|
|
86
|
+
|
|
87
|
+
Yes — you can install **normally** with `pip` into your user account or system Python, no `.venv` required.
|
|
88
|
+
|
|
89
|
+
**CLI only:**
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
pip3 install keka-log
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
**macOS menu bar as well:**
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
pip3 install "keka-log[menubar]"
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
On **macOS with Homebrew Python** (and some Linux distros), plain `pip install` may be blocked with *externally-managed-environment*. In that case install into your user site-packages instead:
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
pip3 install --user "keka-log[menubar]"
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
Make sure your shell can find the scripts. User installs usually put executables under `~/.local/bin`. If `keka-log` is not found, add this to `~/.zshrc` and open a new terminal:
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
export PATH="$HOME/.local/bin:$PATH"
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
**Alternative — isolated apps without managing a venv yourself:** use [pipx](https://pypa.github.io/pipx/) (install with `brew install pipx` on macOS, then `pipx ensurepath`):
|
|
114
|
+
|
|
115
|
+
```bash
|
|
116
|
+
pipx install "keka-log[menubar]"
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
That installs the package in its own environment and puts `keka-log` / `keka-menubar` on your `PATH`.
|
|
120
|
+
|
|
121
|
+
### From a local clone
|
|
122
|
+
|
|
123
|
+
With a venv (good for development):
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
cd path/to/quid-keka-log
|
|
127
|
+
python3 -m venv .venv
|
|
128
|
+
source .venv/bin/activate
|
|
129
|
+
pip install ".[menubar]" # macOS: CLI + menubar
|
|
130
|
+
# or
|
|
131
|
+
pip install . # CLI only (no rumps)
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
Or install directly from the folder without a venv:
|
|
135
|
+
|
|
136
|
+
```bash
|
|
137
|
+
cd path/to/quid-keka-log
|
|
138
|
+
pip3 install --user ".[menubar]"
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
If you use a venv, run `source .venv/bin/activate` in each new terminal session where you want those commands.
|
|
142
|
+
|
|
143
|
+
### Publishing a new release (maintainers)
|
|
144
|
+
|
|
145
|
+
1. Bump `version` in `pyproject.toml` (and optionally `keka_log/__init__.py`).
|
|
146
|
+
2. Build and upload to PyPI (use [API tokens](https://pypi.org/manage/account/token/), not your password):
|
|
147
|
+
|
|
148
|
+
```bash
|
|
149
|
+
pip install build twine
|
|
150
|
+
python -m build
|
|
151
|
+
twine upload dist/*
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
For TestPyPI first:
|
|
155
|
+
|
|
156
|
+
```bash
|
|
157
|
+
twine upload --repository testpypi dist/*
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
Then install with:
|
|
161
|
+
|
|
162
|
+
```bash
|
|
163
|
+
pip install -i https://test.pypi.org/simple/ keka-log
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
## Usage
|
|
167
|
+
|
|
168
|
+
If you installed inside a **virtual environment**, activate it first:
|
|
169
|
+
|
|
170
|
+
```bash
|
|
171
|
+
source .venv/bin/activate
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
If you installed with **`pip install --user`**, **`pipx`**, or a **system** `pip3 install`, you do not need a venv — open a normal terminal (ensure `~/.local/bin` is on your `PATH` if you used `--user`).
|
|
175
|
+
|
|
176
|
+
### Run and stop commands
|
|
177
|
+
|
|
178
|
+
| What | Run | Stop |
|
|
179
|
+
|------|-----|------|
|
|
180
|
+
| **CLI** (one-shot print) | `keka-log` | Nothing to stop — it exits after printing. |
|
|
181
|
+
| **CLI** (refresh cache) | `keka-log refresh` | Same — exits when done. |
|
|
182
|
+
| **Menu bar app** (stays running) | `keka-menubar` | See **Stopping the menu bar app** below. |
|
|
183
|
+
|
|
184
|
+
**Stopping the menu bar app** (`keka-menubar` keeps running until you quit):
|
|
185
|
+
|
|
186
|
+
1. **From the menu bar (recommended):** Click the Keka item in the top-right menu bar → **Quit**.
|
|
187
|
+
2. **If you started it in a terminal** and that window is still open: focus that terminal and press **Ctrl+C**.
|
|
188
|
+
3. **From another terminal** (if you closed the window or need to force-quit):
|
|
189
|
+
|
|
190
|
+
```bash
|
|
191
|
+
pkill -f keka-menubar
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
If nothing matches, list Python processes and stop the one running the app:
|
|
195
|
+
|
|
196
|
+
```bash
|
|
197
|
+
ps aux | grep -i keka
|
|
198
|
+
kill <PID>
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
Replace `<PID>` with the process id from the `ps` output.
|
|
202
|
+
|
|
203
|
+
---
|
|
204
|
+
|
|
205
|
+
### 1. CLI tool
|
|
206
|
+
|
|
207
|
+
Print current attendance info, effective hours, and optimistic logout time:
|
|
208
|
+
|
|
209
|
+
```bash
|
|
210
|
+
keka-log
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
Refresh cached data from the Keka server:
|
|
214
|
+
|
|
215
|
+
```bash
|
|
216
|
+
keka-log refresh
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
### 2. Mac menu bar app (requires `pip install keka-log[menubar]`)
|
|
220
|
+
|
|
221
|
+
Start the live countdown in the macOS menu bar:
|
|
222
|
+
|
|
223
|
+
```bash
|
|
224
|
+
keka-menubar
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
This shows a timer in the menu bar. When your shift is complete, it can play audio (unless `KEKA_LANGUAGE=none`) and show a notification. Use **Quit** in the menu or the stop commands above when you are done for the day.
|
keka_log-0.1.0/README.md
ADDED
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
# Keka Log
|
|
2
|
+
|
|
3
|
+
A Python package for checking and tracking Keka attendance. Includes a CLI tool and a Mac menu bar app to keep track of your shift time.
|
|
4
|
+
|
|
5
|
+
## Prerequisites & Configuration (`.zshrc`)
|
|
6
|
+
|
|
7
|
+
Before using this tool, you need to extract your `KEKA_TOKEN` from your browser and configure it in your shell.
|
|
8
|
+
|
|
9
|
+
### 1. Get your Keka Token
|
|
10
|
+
1. Log in to your Keka portal in your web browser.
|
|
11
|
+
2. Open Developer Tools (Inspect Element) -> **Network** tab.
|
|
12
|
+
3. Refresh the page or navigate to the attendance section.
|
|
13
|
+
4. Look for an API request (e.g., `summary` or `attendance`).
|
|
14
|
+
5. Check the **Request Headers** for the `Authorization` header. It will look like `Bearer eyJ...`.
|
|
15
|
+
6. Copy the long token string (everything after `Bearer `).
|
|
16
|
+
|
|
17
|
+
### 2. Add to your `.zshrc`
|
|
18
|
+
You need to configure the token and your preferred notification language in your shell configuration file (e.g., `~/.zshrc`).
|
|
19
|
+
|
|
20
|
+
1. Open your `.zshrc` file in your terminal:
|
|
21
|
+
```bash
|
|
22
|
+
nano ~/.zshrc
|
|
23
|
+
```
|
|
24
|
+
2. Add the following lines at the bottom of the file:
|
|
25
|
+
```bash
|
|
26
|
+
# Keka Attendance App Configuration
|
|
27
|
+
export KEKA_TOKEN="paste_your_copied_token_here"
|
|
28
|
+
|
|
29
|
+
# Optional: Set notification audio language ('hindi', 'telugu', or 'none')
|
|
30
|
+
# Defaults to 'hindi' if not set. Use 'none' to disable audio entirely.
|
|
31
|
+
export KEKA_LANGUAGE="telugu"
|
|
32
|
+
```
|
|
33
|
+
3. Save the file (in nano: `Ctrl+O`, `Enter`, `Ctrl+X`) and reload your configuration:
|
|
34
|
+
```bash
|
|
35
|
+
source ~/.zshrc
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Installation
|
|
39
|
+
|
|
40
|
+
### From PyPI (recommended)
|
|
41
|
+
|
|
42
|
+
Install the CLI from [PyPI](https://pypi.org/project/keka-log/) (replace the version if you pin one):
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
python3 -m venv .venv
|
|
46
|
+
source .venv/bin/activate
|
|
47
|
+
pip install keka-log
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
On **macOS**, install with the `menubar` extra so `keka-menubar` works (pulls in `rumps` and its dependencies):
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
pip install keka-log[menubar]
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
The base package has no third-party dependencies; only `keka-log[menubar]` adds the menu bar stack.
|
|
57
|
+
|
|
58
|
+
### Without a virtual environment (direct install)
|
|
59
|
+
|
|
60
|
+
Yes — you can install **normally** with `pip` into your user account or system Python, no `.venv` required.
|
|
61
|
+
|
|
62
|
+
**CLI only:**
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
pip3 install keka-log
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
**macOS menu bar as well:**
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
pip3 install "keka-log[menubar]"
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
On **macOS with Homebrew Python** (and some Linux distros), plain `pip install` may be blocked with *externally-managed-environment*. In that case install into your user site-packages instead:
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
pip3 install --user "keka-log[menubar]"
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Make sure your shell can find the scripts. User installs usually put executables under `~/.local/bin`. If `keka-log` is not found, add this to `~/.zshrc` and open a new terminal:
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
export PATH="$HOME/.local/bin:$PATH"
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
**Alternative — isolated apps without managing a venv yourself:** use [pipx](https://pypa.github.io/pipx/) (install with `brew install pipx` on macOS, then `pipx ensurepath`):
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
pipx install "keka-log[menubar]"
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
That installs the package in its own environment and puts `keka-log` / `keka-menubar` on your `PATH`.
|
|
93
|
+
|
|
94
|
+
### From a local clone
|
|
95
|
+
|
|
96
|
+
With a venv (good for development):
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
cd path/to/quid-keka-log
|
|
100
|
+
python3 -m venv .venv
|
|
101
|
+
source .venv/bin/activate
|
|
102
|
+
pip install ".[menubar]" # macOS: CLI + menubar
|
|
103
|
+
# or
|
|
104
|
+
pip install . # CLI only (no rumps)
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
Or install directly from the folder without a venv:
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
cd path/to/quid-keka-log
|
|
111
|
+
pip3 install --user ".[menubar]"
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
If you use a venv, run `source .venv/bin/activate` in each new terminal session where you want those commands.
|
|
115
|
+
|
|
116
|
+
### Publishing a new release (maintainers)
|
|
117
|
+
|
|
118
|
+
1. Bump `version` in `pyproject.toml` (and optionally `keka_log/__init__.py`).
|
|
119
|
+
2. Build and upload to PyPI (use [API tokens](https://pypi.org/manage/account/token/), not your password):
|
|
120
|
+
|
|
121
|
+
```bash
|
|
122
|
+
pip install build twine
|
|
123
|
+
python -m build
|
|
124
|
+
twine upload dist/*
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
For TestPyPI first:
|
|
128
|
+
|
|
129
|
+
```bash
|
|
130
|
+
twine upload --repository testpypi dist/*
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
Then install with:
|
|
134
|
+
|
|
135
|
+
```bash
|
|
136
|
+
pip install -i https://test.pypi.org/simple/ keka-log
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
## Usage
|
|
140
|
+
|
|
141
|
+
If you installed inside a **virtual environment**, activate it first:
|
|
142
|
+
|
|
143
|
+
```bash
|
|
144
|
+
source .venv/bin/activate
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
If you installed with **`pip install --user`**, **`pipx`**, or a **system** `pip3 install`, you do not need a venv — open a normal terminal (ensure `~/.local/bin` is on your `PATH` if you used `--user`).
|
|
148
|
+
|
|
149
|
+
### Run and stop commands
|
|
150
|
+
|
|
151
|
+
| What | Run | Stop |
|
|
152
|
+
|------|-----|------|
|
|
153
|
+
| **CLI** (one-shot print) | `keka-log` | Nothing to stop — it exits after printing. |
|
|
154
|
+
| **CLI** (refresh cache) | `keka-log refresh` | Same — exits when done. |
|
|
155
|
+
| **Menu bar app** (stays running) | `keka-menubar` | See **Stopping the menu bar app** below. |
|
|
156
|
+
|
|
157
|
+
**Stopping the menu bar app** (`keka-menubar` keeps running until you quit):
|
|
158
|
+
|
|
159
|
+
1. **From the menu bar (recommended):** Click the Keka item in the top-right menu bar → **Quit**.
|
|
160
|
+
2. **If you started it in a terminal** and that window is still open: focus that terminal and press **Ctrl+C**.
|
|
161
|
+
3. **From another terminal** (if you closed the window or need to force-quit):
|
|
162
|
+
|
|
163
|
+
```bash
|
|
164
|
+
pkill -f keka-menubar
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
If nothing matches, list Python processes and stop the one running the app:
|
|
168
|
+
|
|
169
|
+
```bash
|
|
170
|
+
ps aux | grep -i keka
|
|
171
|
+
kill <PID>
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
Replace `<PID>` with the process id from the `ps` output.
|
|
175
|
+
|
|
176
|
+
---
|
|
177
|
+
|
|
178
|
+
### 1. CLI tool
|
|
179
|
+
|
|
180
|
+
Print current attendance info, effective hours, and optimistic logout time:
|
|
181
|
+
|
|
182
|
+
```bash
|
|
183
|
+
keka-log
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
Refresh cached data from the Keka server:
|
|
187
|
+
|
|
188
|
+
```bash
|
|
189
|
+
keka-log refresh
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
### 2. Mac menu bar app (requires `pip install keka-log[menubar]`)
|
|
193
|
+
|
|
194
|
+
Start the live countdown in the macOS menu bar:
|
|
195
|
+
|
|
196
|
+
```bash
|
|
197
|
+
keka-menubar
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
This shows a timer in the menu bar. When your shift is complete, it can play audio (unless `KEKA_LANGUAGE=none`) and show a notification. Use **Quit** in the menu or the stop commands above when you are done for the day.
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import sys
|
|
3
|
+
import json
|
|
4
|
+
import urllib.request
|
|
5
|
+
import urllib.error
|
|
6
|
+
from datetime import datetime, date
|
|
7
|
+
|
|
8
|
+
# Gets the KEKA_TOKEN from environment variable
|
|
9
|
+
KEKA_TOKEN = os.environ.get("KEKA_TOKEN")
|
|
10
|
+
|
|
11
|
+
def get_cache_file():
|
|
12
|
+
cache_dir = os.environ.get("XDG_CACHE_HOME", os.path.join(os.path.expanduser("~"), ".cache"))
|
|
13
|
+
mlog_dir = os.path.join(cache_dir, "mlog")
|
|
14
|
+
os.makedirs(mlog_dir, exist_ok=True)
|
|
15
|
+
return os.path.join(mlog_dir, "keka_attendance.json")
|
|
16
|
+
|
|
17
|
+
def to_seconds(time_str):
|
|
18
|
+
if isinstance(time_str, (int, float)):
|
|
19
|
+
return int(time_str * 3600 + 0.5)
|
|
20
|
+
|
|
21
|
+
time_str = str(time_str).strip()
|
|
22
|
+
if ':' in time_str:
|
|
23
|
+
parts = time_str.split(':')
|
|
24
|
+
h = int(parts[0]) if len(parts) > 0 else 0
|
|
25
|
+
m = int(parts[1]) if len(parts) > 1 else 0
|
|
26
|
+
s = int(parts[2]) if len(parts) > 2 else 0
|
|
27
|
+
return h * 3600 + m * 60 + s
|
|
28
|
+
|
|
29
|
+
try:
|
|
30
|
+
hours = float(time_str)
|
|
31
|
+
return int(hours * 3600 + 0.5)
|
|
32
|
+
except ValueError:
|
|
33
|
+
raise ValueError(f"unsupported duration format: {time_str}")
|
|
34
|
+
|
|
35
|
+
def format_hms(total_seconds):
|
|
36
|
+
total_seconds = int(total_seconds)
|
|
37
|
+
hours = total_seconds // 3600
|
|
38
|
+
minutes = (total_seconds % 3600) // 60
|
|
39
|
+
seconds = total_seconds % 60
|
|
40
|
+
return f"{hours:02d}:{minutes:02d}:{seconds:02d}"
|
|
41
|
+
|
|
42
|
+
def parse_login_time(raw_timestamp):
|
|
43
|
+
if not raw_timestamp or raw_timestamp == "null":
|
|
44
|
+
raise ValueError("Invalid timestamp")
|
|
45
|
+
|
|
46
|
+
try:
|
|
47
|
+
# Handle epoch time (Unix timestamp)
|
|
48
|
+
if isinstance(raw_timestamp, (int, float)) or str(raw_timestamp).isdigit():
|
|
49
|
+
epoch = int(raw_timestamp)
|
|
50
|
+
if epoch > 9999999999: # milliseconds to seconds
|
|
51
|
+
epoch = epoch / 1000.0
|
|
52
|
+
return datetime.fromtimestamp(epoch).strftime("%H:%M:%S")
|
|
53
|
+
|
|
54
|
+
# Handle ISO string
|
|
55
|
+
raw_timestamp = str(raw_timestamp)
|
|
56
|
+
# Simplistic ISO 8601 parsing
|
|
57
|
+
raw_timestamp = raw_timestamp.replace('Z', '+00:00')
|
|
58
|
+
dt = datetime.fromisoformat(raw_timestamp)
|
|
59
|
+
# Convert to local time
|
|
60
|
+
dt = dt.astimezone()
|
|
61
|
+
return dt.strftime("%H:%M:%S")
|
|
62
|
+
except Exception as e:
|
|
63
|
+
raise ValueError(f"unable to parse login timestamp: {raw_timestamp}")
|
|
64
|
+
|
|
65
|
+
def fetch_and_cache():
|
|
66
|
+
if not KEKA_TOKEN:
|
|
67
|
+
print("mlog: KEKA_TOKEN environment variable is not set", file=sys.stderr)
|
|
68
|
+
print("Please export KEKA_TOKEN or pass it inline before running the script.", file=sys.stderr)
|
|
69
|
+
sys.exit(1)
|
|
70
|
+
|
|
71
|
+
url = "https://quid.keka.com/k/attendance/api/mytime/attendance/summary"
|
|
72
|
+
req = urllib.request.Request(url)
|
|
73
|
+
req.add_header("accept", "application/json, text/plain, */*")
|
|
74
|
+
req.add_header("authorization", f"Bearer {KEKA_TOKEN}")
|
|
75
|
+
|
|
76
|
+
try:
|
|
77
|
+
with urllib.request.urlopen(req) as response:
|
|
78
|
+
data = json.loads(response.read().decode())
|
|
79
|
+
except urllib.error.URLError as e:
|
|
80
|
+
print(f"mlog: failed to fetch data from Keka API: {e}", file=sys.stderr)
|
|
81
|
+
sys.exit(1)
|
|
82
|
+
|
|
83
|
+
try:
|
|
84
|
+
latest_data = data.get("data", [])[-1]
|
|
85
|
+
effective_hours = latest_data.get("totalEffectiveHours", 0)
|
|
86
|
+
|
|
87
|
+
time_entries = latest_data.get("timeEntries", [])
|
|
88
|
+
if not time_entries:
|
|
89
|
+
raw_timestamp = None
|
|
90
|
+
else:
|
|
91
|
+
raw_timestamp = time_entries[-1]["actualTimestamp"]
|
|
92
|
+
except (KeyError, IndexError, TypeError):
|
|
93
|
+
print("mlog: unable to parse required fields from Keka response", file=sys.stderr)
|
|
94
|
+
sys.exit(1)
|
|
95
|
+
|
|
96
|
+
last_login = parse_login_time(raw_timestamp) if raw_timestamp else None
|
|
97
|
+
cache_date = date.today().isoformat()
|
|
98
|
+
|
|
99
|
+
cache_data = {
|
|
100
|
+
"cache_date": cache_date,
|
|
101
|
+
"effective_hours": effective_hours,
|
|
102
|
+
"last_login": last_login
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
cache_file = get_cache_file()
|
|
106
|
+
with open(cache_file, "w") as f:
|
|
107
|
+
json.dump(cache_data, f)
|
|
108
|
+
|
|
109
|
+
return cache_data
|
|
110
|
+
|
|
111
|
+
def load_cached_or_fetch(action):
|
|
112
|
+
cache_file = get_cache_file()
|
|
113
|
+
cache_date = date.today().isoformat()
|
|
114
|
+
|
|
115
|
+
if action == "refresh" or not os.path.exists(cache_file):
|
|
116
|
+
return fetch_and_cache()
|
|
117
|
+
|
|
118
|
+
with open(cache_file, "r") as f:
|
|
119
|
+
try:
|
|
120
|
+
cache_data = json.load(f)
|
|
121
|
+
except json.JSONDecodeError:
|
|
122
|
+
return fetch_and_cache()
|
|
123
|
+
|
|
124
|
+
# Invalidate cache if it's from a previous day or corrupted
|
|
125
|
+
if cache_data.get("cache_date") != cache_date or "effective_hours" not in cache_data or "last_login" not in cache_data:
|
|
126
|
+
return fetch_and_cache()
|
|
127
|
+
|
|
128
|
+
return cache_data
|
|
129
|
+
|
|
130
|
+
def mlog():
|
|
131
|
+
action = sys.argv[1] if len(sys.argv) > 1 else None
|
|
132
|
+
if action and action != "refresh":
|
|
133
|
+
print("Usage: python keka.py [refresh]", file=sys.stderr)
|
|
134
|
+
sys.exit(1)
|
|
135
|
+
|
|
136
|
+
required_seconds = 27000 # 7.5 hours
|
|
137
|
+
|
|
138
|
+
payload = load_cached_or_fetch(action)
|
|
139
|
+
cached_effective_hours = payload["effective_hours"]
|
|
140
|
+
last_login = payload.get("last_login")
|
|
141
|
+
|
|
142
|
+
if not last_login:
|
|
143
|
+
print("No time entries logged for today yet.")
|
|
144
|
+
sys.exit(0)
|
|
145
|
+
|
|
146
|
+
cached_effective_seconds = to_seconds(cached_effective_hours)
|
|
147
|
+
last_login_seconds = to_seconds(last_login)
|
|
148
|
+
|
|
149
|
+
now = datetime.now()
|
|
150
|
+
now_seconds = now.hour * 3600 + now.minute * 60 + now.second
|
|
151
|
+
|
|
152
|
+
# Time elapsed in the current session (since last login)
|
|
153
|
+
elapsed_seconds = now_seconds - last_login_seconds
|
|
154
|
+
if elapsed_seconds < 0:
|
|
155
|
+
elapsed_seconds += 86400
|
|
156
|
+
|
|
157
|
+
dynamic_effective_seconds = cached_effective_seconds + elapsed_seconds
|
|
158
|
+
|
|
159
|
+
if dynamic_effective_seconds >= required_seconds:
|
|
160
|
+
dynamic_remaining_seconds = 0
|
|
161
|
+
logout_seconds = now_seconds
|
|
162
|
+
else:
|
|
163
|
+
dynamic_remaining_seconds = required_seconds - dynamic_effective_seconds
|
|
164
|
+
logout_seconds = last_login_seconds + (required_seconds - cached_effective_seconds)
|
|
165
|
+
if logout_seconds >= 86400:
|
|
166
|
+
logout_seconds -= 86400
|
|
167
|
+
|
|
168
|
+
print(f"Last login: {last_login}")
|
|
169
|
+
print(f"Effective hours: {format_hms(dynamic_effective_seconds)}")
|
|
170
|
+
print(f"Time remaining: {format_hms(dynamic_remaining_seconds)}")
|
|
171
|
+
print(f"Optimistic logout time: {format_hms(logout_seconds)}")
|
|
172
|
+
|
|
173
|
+
if __name__ == "__main__":
|
|
174
|
+
mlog()
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import rumps
|
|
2
|
+
import sys
|
|
3
|
+
import os
|
|
4
|
+
from datetime import datetime
|
|
5
|
+
from . import keka
|
|
6
|
+
|
|
7
|
+
class KekaStatusBarApp(rumps.App):
|
|
8
|
+
def __init__(self):
|
|
9
|
+
super(KekaStatusBarApp, self).__init__("Keka: Loading...")
|
|
10
|
+
self.has_notified = False
|
|
11
|
+
|
|
12
|
+
# We fetch the initial data once during startup
|
|
13
|
+
try:
|
|
14
|
+
self.payload = keka.load_cached_or_fetch(None)
|
|
15
|
+
except Exception as e:
|
|
16
|
+
self.title = "Keka: Error"
|
|
17
|
+
self.payload = None
|
|
18
|
+
|
|
19
|
+
@rumps.timer(1)
|
|
20
|
+
def update_clock(self, _):
|
|
21
|
+
if not self.payload:
|
|
22
|
+
return
|
|
23
|
+
|
|
24
|
+
try:
|
|
25
|
+
cached_effective_hours = self.payload["effective_hours"]
|
|
26
|
+
last_login = self.payload.get("last_login")
|
|
27
|
+
|
|
28
|
+
if not last_login:
|
|
29
|
+
self.title = "Keka: No logs today"
|
|
30
|
+
self.menu.clear()
|
|
31
|
+
refresh_btn = rumps.MenuItem("Refresh from Server")
|
|
32
|
+
refresh_btn.set_callback(self.manual_refresh)
|
|
33
|
+
self.menu.add(refresh_btn)
|
|
34
|
+
self.menu.add(rumps.MenuItem("Quit", callback=rumps.quit_application))
|
|
35
|
+
return
|
|
36
|
+
|
|
37
|
+
cached_effective_seconds = keka.to_seconds(cached_effective_hours)
|
|
38
|
+
last_login_seconds = keka.to_seconds(last_login)
|
|
39
|
+
|
|
40
|
+
now = datetime.now()
|
|
41
|
+
now_seconds = now.hour * 3600 + now.minute * 60 + now.second
|
|
42
|
+
|
|
43
|
+
elapsed_seconds = now_seconds - last_login_seconds
|
|
44
|
+
if elapsed_seconds < 0:
|
|
45
|
+
elapsed_seconds += 86400
|
|
46
|
+
|
|
47
|
+
dynamic_effective_seconds = cached_effective_seconds + elapsed_seconds
|
|
48
|
+
required_seconds = 27000 # 7.5 hours
|
|
49
|
+
|
|
50
|
+
if dynamic_effective_seconds >= required_seconds:
|
|
51
|
+
dynamic_remaining_seconds = 0
|
|
52
|
+
logout_seconds = now_seconds
|
|
53
|
+
self.title = f"✅ Keka: Done!"
|
|
54
|
+
|
|
55
|
+
# Send a notification if we haven't already
|
|
56
|
+
if not self.has_notified:
|
|
57
|
+
os.system('''osascript -e 'display notification "You have completed your 7.5 hours for today." with title "Keka Shift Complete! 🎉" sound name "Glass"' ''')
|
|
58
|
+
|
|
59
|
+
# Determine which audio to play based on language preference
|
|
60
|
+
language = os.environ.get("KEKA_LANGUAGE", "hindi").lower()
|
|
61
|
+
|
|
62
|
+
if language != "none":
|
|
63
|
+
if language == "telugu":
|
|
64
|
+
mp3_filename = "Orey Ajaamu Lagettaroy.mp3"
|
|
65
|
+
else:
|
|
66
|
+
mp3_filename = "are_tu_jaa_re.mp3"
|
|
67
|
+
|
|
68
|
+
# Use absolute path and quotes to handle spaces in the filename
|
|
69
|
+
mp3_path = os.path.join(os.path.dirname(__file__), mp3_filename)
|
|
70
|
+
os.system(f"afplay '{mp3_path}' &")
|
|
71
|
+
|
|
72
|
+
self.has_notified = True
|
|
73
|
+
else:
|
|
74
|
+
self.has_notified = False # reset if somehow they go back under
|
|
75
|
+
dynamic_remaining_seconds = required_seconds - dynamic_effective_seconds
|
|
76
|
+
logout_seconds = last_login_seconds + (required_seconds - cached_effective_seconds)
|
|
77
|
+
if logout_seconds >= 86400:
|
|
78
|
+
logout_seconds -= 86400
|
|
79
|
+
|
|
80
|
+
self.title = f"⏳ {keka.format_hms(dynamic_remaining_seconds)} "
|
|
81
|
+
|
|
82
|
+
# Update the dropdown menu with the specific details
|
|
83
|
+
self.menu.clear()
|
|
84
|
+
self.menu.add(rumps.MenuItem(f"Logged in at: {last_login}"))
|
|
85
|
+
self.menu.add(rumps.MenuItem(f"Logout time: {keka.format_hms(logout_seconds)}"))
|
|
86
|
+
self.menu.add(rumps.MenuItem(f"Effective so far: {keka.format_hms(dynamic_effective_seconds)}"))
|
|
87
|
+
self.menu.add(rumps.separator)
|
|
88
|
+
|
|
89
|
+
refresh_btn = rumps.MenuItem("Refresh from Server")
|
|
90
|
+
refresh_btn.set_callback(self.manual_refresh)
|
|
91
|
+
self.menu.add(refresh_btn)
|
|
92
|
+
|
|
93
|
+
self.menu.add(rumps.MenuItem("Quit", callback=rumps.quit_application))
|
|
94
|
+
|
|
95
|
+
except Exception as e:
|
|
96
|
+
self.title = "Keka: Error"
|
|
97
|
+
|
|
98
|
+
def manual_refresh(self, _):
|
|
99
|
+
self.title = "Keka: Refreshing..."
|
|
100
|
+
try:
|
|
101
|
+
self.payload = keka.load_cached_or_fetch("refresh")
|
|
102
|
+
self.update_clock(None)
|
|
103
|
+
except Exception as e:
|
|
104
|
+
self.title = "Keka: Auth Error"
|
|
105
|
+
|
|
106
|
+
def main():
|
|
107
|
+
KekaStatusBarApp().run()
|
|
108
|
+
|
|
109
|
+
if __name__ == "__main__":
|
|
110
|
+
main()
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"""Console entry for keka-menubar; imports rumps before loading the menubar app."""
|
|
2
|
+
|
|
3
|
+
import sys
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def main():
|
|
7
|
+
try:
|
|
8
|
+
import rumps # noqa: F401
|
|
9
|
+
except ImportError:
|
|
10
|
+
print(
|
|
11
|
+
"keka-menubar needs optional dependencies. On macOS install with:\n"
|
|
12
|
+
" pip install keka-log[menubar]",
|
|
13
|
+
file=sys.stderr,
|
|
14
|
+
)
|
|
15
|
+
sys.exit(1)
|
|
16
|
+
from keka_log.keka_menubar import main as run
|
|
17
|
+
|
|
18
|
+
run()
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: keka-log
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: CLI and optional macOS menu bar app for Keka attendance tracking.
|
|
5
|
+
Author: Quid Keka Log contributors
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://pypi.org/project/keka-log/
|
|
8
|
+
Keywords: keka,attendance,time-tracking,macos,menubar
|
|
9
|
+
Classifier: Development Status :: 4 - Beta
|
|
10
|
+
Classifier: Environment :: MacOS X
|
|
11
|
+
Classifier: Intended Audience :: End Users/Desktop
|
|
12
|
+
Classifier: Operating System :: MacOS :: MacOS X
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
20
|
+
Classifier: Topic :: Office/Business
|
|
21
|
+
Requires-Python: >=3.8
|
|
22
|
+
Description-Content-Type: text/markdown
|
|
23
|
+
License-File: LICENSE
|
|
24
|
+
Provides-Extra: menubar
|
|
25
|
+
Requires-Dist: rumps>=0.4.0; extra == "menubar"
|
|
26
|
+
Dynamic: license-file
|
|
27
|
+
|
|
28
|
+
# Keka Log
|
|
29
|
+
|
|
30
|
+
A Python package for checking and tracking Keka attendance. Includes a CLI tool and a Mac menu bar app to keep track of your shift time.
|
|
31
|
+
|
|
32
|
+
## Prerequisites & Configuration (`.zshrc`)
|
|
33
|
+
|
|
34
|
+
Before using this tool, you need to extract your `KEKA_TOKEN` from your browser and configure it in your shell.
|
|
35
|
+
|
|
36
|
+
### 1. Get your Keka Token
|
|
37
|
+
1. Log in to your Keka portal in your web browser.
|
|
38
|
+
2. Open Developer Tools (Inspect Element) -> **Network** tab.
|
|
39
|
+
3. Refresh the page or navigate to the attendance section.
|
|
40
|
+
4. Look for an API request (e.g., `summary` or `attendance`).
|
|
41
|
+
5. Check the **Request Headers** for the `Authorization` header. It will look like `Bearer eyJ...`.
|
|
42
|
+
6. Copy the long token string (everything after `Bearer `).
|
|
43
|
+
|
|
44
|
+
### 2. Add to your `.zshrc`
|
|
45
|
+
You need to configure the token and your preferred notification language in your shell configuration file (e.g., `~/.zshrc`).
|
|
46
|
+
|
|
47
|
+
1. Open your `.zshrc` file in your terminal:
|
|
48
|
+
```bash
|
|
49
|
+
nano ~/.zshrc
|
|
50
|
+
```
|
|
51
|
+
2. Add the following lines at the bottom of the file:
|
|
52
|
+
```bash
|
|
53
|
+
# Keka Attendance App Configuration
|
|
54
|
+
export KEKA_TOKEN="paste_your_copied_token_here"
|
|
55
|
+
|
|
56
|
+
# Optional: Set notification audio language ('hindi', 'telugu', or 'none')
|
|
57
|
+
# Defaults to 'hindi' if not set. Use 'none' to disable audio entirely.
|
|
58
|
+
export KEKA_LANGUAGE="telugu"
|
|
59
|
+
```
|
|
60
|
+
3. Save the file (in nano: `Ctrl+O`, `Enter`, `Ctrl+X`) and reload your configuration:
|
|
61
|
+
```bash
|
|
62
|
+
source ~/.zshrc
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## Installation
|
|
66
|
+
|
|
67
|
+
### From PyPI (recommended)
|
|
68
|
+
|
|
69
|
+
Install the CLI from [PyPI](https://pypi.org/project/keka-log/) (replace the version if you pin one):
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
python3 -m venv .venv
|
|
73
|
+
source .venv/bin/activate
|
|
74
|
+
pip install keka-log
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
On **macOS**, install with the `menubar` extra so `keka-menubar` works (pulls in `rumps` and its dependencies):
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
pip install keka-log[menubar]
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
The base package has no third-party dependencies; only `keka-log[menubar]` adds the menu bar stack.
|
|
84
|
+
|
|
85
|
+
### Without a virtual environment (direct install)
|
|
86
|
+
|
|
87
|
+
Yes — you can install **normally** with `pip` into your user account or system Python, no `.venv` required.
|
|
88
|
+
|
|
89
|
+
**CLI only:**
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
pip3 install keka-log
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
**macOS menu bar as well:**
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
pip3 install "keka-log[menubar]"
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
On **macOS with Homebrew Python** (and some Linux distros), plain `pip install` may be blocked with *externally-managed-environment*. In that case install into your user site-packages instead:
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
pip3 install --user "keka-log[menubar]"
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
Make sure your shell can find the scripts. User installs usually put executables under `~/.local/bin`. If `keka-log` is not found, add this to `~/.zshrc` and open a new terminal:
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
export PATH="$HOME/.local/bin:$PATH"
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
**Alternative — isolated apps without managing a venv yourself:** use [pipx](https://pypa.github.io/pipx/) (install with `brew install pipx` on macOS, then `pipx ensurepath`):
|
|
114
|
+
|
|
115
|
+
```bash
|
|
116
|
+
pipx install "keka-log[menubar]"
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
That installs the package in its own environment and puts `keka-log` / `keka-menubar` on your `PATH`.
|
|
120
|
+
|
|
121
|
+
### From a local clone
|
|
122
|
+
|
|
123
|
+
With a venv (good for development):
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
cd path/to/quid-keka-log
|
|
127
|
+
python3 -m venv .venv
|
|
128
|
+
source .venv/bin/activate
|
|
129
|
+
pip install ".[menubar]" # macOS: CLI + menubar
|
|
130
|
+
# or
|
|
131
|
+
pip install . # CLI only (no rumps)
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
Or install directly from the folder without a venv:
|
|
135
|
+
|
|
136
|
+
```bash
|
|
137
|
+
cd path/to/quid-keka-log
|
|
138
|
+
pip3 install --user ".[menubar]"
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
If you use a venv, run `source .venv/bin/activate` in each new terminal session where you want those commands.
|
|
142
|
+
|
|
143
|
+
### Publishing a new release (maintainers)
|
|
144
|
+
|
|
145
|
+
1. Bump `version` in `pyproject.toml` (and optionally `keka_log/__init__.py`).
|
|
146
|
+
2. Build and upload to PyPI (use [API tokens](https://pypi.org/manage/account/token/), not your password):
|
|
147
|
+
|
|
148
|
+
```bash
|
|
149
|
+
pip install build twine
|
|
150
|
+
python -m build
|
|
151
|
+
twine upload dist/*
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
For TestPyPI first:
|
|
155
|
+
|
|
156
|
+
```bash
|
|
157
|
+
twine upload --repository testpypi dist/*
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
Then install with:
|
|
161
|
+
|
|
162
|
+
```bash
|
|
163
|
+
pip install -i https://test.pypi.org/simple/ keka-log
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
## Usage
|
|
167
|
+
|
|
168
|
+
If you installed inside a **virtual environment**, activate it first:
|
|
169
|
+
|
|
170
|
+
```bash
|
|
171
|
+
source .venv/bin/activate
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
If you installed with **`pip install --user`**, **`pipx`**, or a **system** `pip3 install`, you do not need a venv — open a normal terminal (ensure `~/.local/bin` is on your `PATH` if you used `--user`).
|
|
175
|
+
|
|
176
|
+
### Run and stop commands
|
|
177
|
+
|
|
178
|
+
| What | Run | Stop |
|
|
179
|
+
|------|-----|------|
|
|
180
|
+
| **CLI** (one-shot print) | `keka-log` | Nothing to stop — it exits after printing. |
|
|
181
|
+
| **CLI** (refresh cache) | `keka-log refresh` | Same — exits when done. |
|
|
182
|
+
| **Menu bar app** (stays running) | `keka-menubar` | See **Stopping the menu bar app** below. |
|
|
183
|
+
|
|
184
|
+
**Stopping the menu bar app** (`keka-menubar` keeps running until you quit):
|
|
185
|
+
|
|
186
|
+
1. **From the menu bar (recommended):** Click the Keka item in the top-right menu bar → **Quit**.
|
|
187
|
+
2. **If you started it in a terminal** and that window is still open: focus that terminal and press **Ctrl+C**.
|
|
188
|
+
3. **From another terminal** (if you closed the window or need to force-quit):
|
|
189
|
+
|
|
190
|
+
```bash
|
|
191
|
+
pkill -f keka-menubar
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
If nothing matches, list Python processes and stop the one running the app:
|
|
195
|
+
|
|
196
|
+
```bash
|
|
197
|
+
ps aux | grep -i keka
|
|
198
|
+
kill <PID>
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
Replace `<PID>` with the process id from the `ps` output.
|
|
202
|
+
|
|
203
|
+
---
|
|
204
|
+
|
|
205
|
+
### 1. CLI tool
|
|
206
|
+
|
|
207
|
+
Print current attendance info, effective hours, and optimistic logout time:
|
|
208
|
+
|
|
209
|
+
```bash
|
|
210
|
+
keka-log
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
Refresh cached data from the Keka server:
|
|
214
|
+
|
|
215
|
+
```bash
|
|
216
|
+
keka-log refresh
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
### 2. Mac menu bar app (requires `pip install keka-log[menubar]`)
|
|
220
|
+
|
|
221
|
+
Start the live countdown in the macOS menu bar:
|
|
222
|
+
|
|
223
|
+
```bash
|
|
224
|
+
keka-menubar
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
This shows a timer in the menu bar. When your shift is complete, it can play audio (unless `KEKA_LANGUAGE=none`) and show a notification. Use **Quit** in the menu or the stop commands above when you are done for the day.
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
LICENSE
|
|
2
|
+
MANIFEST.in
|
|
3
|
+
README.md
|
|
4
|
+
pyproject.toml
|
|
5
|
+
keka_log/Orey Ajaamu Lagettaroy.mp3
|
|
6
|
+
keka_log/__init__.py
|
|
7
|
+
keka_log/are_tu_jaa_re.mp3
|
|
8
|
+
keka_log/keka.py
|
|
9
|
+
keka_log/keka_menubar.py
|
|
10
|
+
keka_log/menubar_entry.py
|
|
11
|
+
keka_log.egg-info/PKG-INFO
|
|
12
|
+
keka_log.egg-info/SOURCES.txt
|
|
13
|
+
keka_log.egg-info/dependency_links.txt
|
|
14
|
+
keka_log.egg-info/entry_points.txt
|
|
15
|
+
keka_log.egg-info/requires.txt
|
|
16
|
+
keka_log.egg-info/top_level.txt
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "keka-log"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "CLI and optional macOS menu bar app for Keka attendance tracking."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = "MIT"
|
|
11
|
+
license-files = ["LICENSE"]
|
|
12
|
+
requires-python = ">=3.8"
|
|
13
|
+
authors = [
|
|
14
|
+
{ name = "Quid Keka Log contributors" },
|
|
15
|
+
]
|
|
16
|
+
keywords = ["keka", "attendance", "time-tracking", "macos", "menubar"]
|
|
17
|
+
classifiers = [
|
|
18
|
+
"Development Status :: 4 - Beta",
|
|
19
|
+
"Environment :: MacOS X",
|
|
20
|
+
"Intended Audience :: End Users/Desktop",
|
|
21
|
+
"Operating System :: MacOS :: MacOS X",
|
|
22
|
+
"Programming Language :: Python :: 3",
|
|
23
|
+
"Programming Language :: Python :: 3.8",
|
|
24
|
+
"Programming Language :: Python :: 3.9",
|
|
25
|
+
"Programming Language :: Python :: 3.10",
|
|
26
|
+
"Programming Language :: Python :: 3.11",
|
|
27
|
+
"Programming Language :: Python :: 3.12",
|
|
28
|
+
"Programming Language :: Python :: 3.13",
|
|
29
|
+
"Topic :: Office/Business",
|
|
30
|
+
]
|
|
31
|
+
|
|
32
|
+
dependencies = []
|
|
33
|
+
|
|
34
|
+
[project.optional-dependencies]
|
|
35
|
+
menubar = [
|
|
36
|
+
"rumps>=0.4.0",
|
|
37
|
+
]
|
|
38
|
+
|
|
39
|
+
[project.urls]
|
|
40
|
+
Homepage = "https://pypi.org/project/keka-log/"
|
|
41
|
+
|
|
42
|
+
[project.scripts]
|
|
43
|
+
keka-log = "keka_log.keka:mlog"
|
|
44
|
+
keka-menubar = "keka_log.menubar_entry:main"
|
|
45
|
+
|
|
46
|
+
[tool.setuptools.packages.find]
|
|
47
|
+
where = ["."]
|
|
48
|
+
|
|
49
|
+
[tool.setuptools.package-data]
|
|
50
|
+
keka_log = ["*.mp3"]
|
keka_log-0.1.0/setup.cfg
ADDED