elf 0.1dev__tar.gz → 1.0.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.
Files changed (64) hide show
  1. elf-1.0.0/LICENSE +21 -0
  2. elf-1.0.0/PKG-INFO +450 -0
  3. elf-1.0.0/README.md +419 -0
  4. elf-1.0.0/elf/__init__.py +42 -0
  5. elf-1.0.0/elf/_version.py +17 -0
  6. elf-1.0.0/elf/answer.py +450 -0
  7. elf-1.0.0/elf/aoc_client.py +120 -0
  8. elf-1.0.0/elf/cache.py +35 -0
  9. elf-1.0.0/elf/cli.py +293 -0
  10. elf-1.0.0/elf/client.py +199 -0
  11. elf-1.0.0/elf/constants.py +6 -0
  12. elf-1.0.0/elf/exceptions.py +62 -0
  13. elf-1.0.0/elf/guesses.py +76 -0
  14. elf-1.0.0/elf/helpers.py +63 -0
  15. elf-1.0.0/elf/input.py +94 -0
  16. elf-1.0.0/elf/leaderboard.py +125 -0
  17. elf-1.0.0/elf/messages.py +68 -0
  18. elf-1.0.0/elf/models.py +175 -0
  19. elf-1.0.0/elf/open.py +26 -0
  20. elf-1.0.0/elf/status.py +249 -0
  21. elf-1.0.0/elf/utils.py +173 -0
  22. elf-1.0.0/elf.egg-info/PKG-INFO +450 -0
  23. elf-1.0.0/elf.egg-info/SOURCES.txt +34 -0
  24. elf-1.0.0/elf.egg-info/entry_points.txt +2 -0
  25. elf-1.0.0/elf.egg-info/requires.txt +5 -0
  26. elf-1.0.0/pyproject.toml +64 -0
  27. elf-1.0.0/setup.cfg +4 -0
  28. elf-1.0.0/tests/test_answer_guardrails.py +113 -0
  29. elf-1.0.0/tests/test_cli_commands.py +130 -0
  30. elf-1.0.0/tests/test_client_api.py +271 -0
  31. elf-1.0.0/tests/test_guess_cache.py +144 -0
  32. elf-1.0.0/tests/test_models.py +49 -0
  33. elf-1.0.0/tests/test_parsing.py +81 -0
  34. elf-0.1dev/MANIFEST.in +0 -3
  35. elf-0.1dev/PKG-INFO +0 -10
  36. elf-0.1dev/README.txt +0 -19
  37. elf-0.1dev/elf/config/__init__.py +0 -0
  38. elf-0.1dev/elf/config/deployment.ini_tmpl +0 -63
  39. elf-0.1dev/elf/config/environment.py +0 -45
  40. elf-0.1dev/elf/config/middleware.py +0 -69
  41. elf-0.1dev/elf/config/routing.py +0 -26
  42. elf-0.1dev/elf/controllers/__init__.py +0 -0
  43. elf-0.1dev/elf/controllers/error.py +0 -46
  44. elf-0.1dev/elf/model/__init__.py +0 -36
  45. elf-0.1dev/elf/model/meta.py +0 -15
  46. elf-0.1dev/elf/public/bg.png +0 -0
  47. elf-0.1dev/elf/public/favicon.ico +0 -0
  48. elf-0.1dev/elf/public/index.html +0 -137
  49. elf-0.1dev/elf/public/pylons-logo.gif +0 -0
  50. elf-0.1dev/elf/tests/__init__.py +0 -36
  51. elf-0.1dev/elf/tests/functional/__init__.py +0 -0
  52. elf-0.1dev/elf/tests/test_models.py +0 -0
  53. elf-0.1dev/elf/websetup.py +0 -14
  54. elf-0.1dev/elf.egg-info/PKG-INFO +0 -10
  55. elf-0.1dev/elf.egg-info/SOURCES.txt +0 -30
  56. elf-0.1dev/elf.egg-info/entry_points.txt +0 -7
  57. elf-0.1dev/elf.egg-info/not-zip-safe +0 -1
  58. elf-0.1dev/elf.egg-info/paster_plugins.txt +0 -2
  59. elf-0.1dev/elf.egg-info/requires.txt +0 -2
  60. elf-0.1dev/setup.cfg +0 -32
  61. elf-0.1dev/setup.py +0 -37
  62. /elf-0.1dev/elf/__init__.py → /elf-1.0.0/elf/py.typed +0 -0
  63. {elf-0.1dev → elf-1.0.0}/elf.egg-info/dependency_links.txt +0 -0
  64. {elf-0.1dev → elf-1.0.0}/elf.egg-info/top_level.txt +0 -0
elf-1.0.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 snally-labs
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.
elf-1.0.0/PKG-INFO ADDED
@@ -0,0 +1,450 @@
1
+ Metadata-Version: 2.4
2
+ Name: elf
3
+ Version: 1.0.0
4
+ Summary: Advent of Code helper for Python: fetch and cache inputs, submit answers, and pull private leaderboards.
5
+ Author-email: cak <cak@typeerror.com>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/cak/elf
8
+ Project-URL: Documentation, https://github.com/cak/elf/
9
+ Project-URL: Repository, https://github.com/cak/elf
10
+ Project-URL: Issue-Tracker, https://github.com/cak/elf/issues
11
+ Keywords: advent of code,aoc,cli,python,puzzles
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: Programming Language :: Python :: 3 :: Only
14
+ Classifier: Programming Language :: Python :: 3.11
15
+ Classifier: Programming Language :: Python :: 3.12
16
+ Classifier: Programming Language :: Python :: 3.13
17
+ Classifier: Programming Language :: Python :: 3.14
18
+ Classifier: Topic :: Games/Entertainment :: Puzzle Games
19
+ Classifier: Topic :: Software Development :: Libraries
20
+ Classifier: Topic :: Utilities
21
+ Classifier: Typing :: Typed
22
+ Requires-Python: >=3.11
23
+ Description-Content-Type: text/markdown
24
+ License-File: LICENSE
25
+ Requires-Dist: beautifulsoup4>=4.14.2
26
+ Requires-Dist: httpx>=0.28.1
27
+ Requires-Dist: pydantic>=2.12.4
28
+ Requires-Dist: rich>=14.2.0
29
+ Requires-Dist: typer>=0.20.0
30
+ Dynamic: license-file
31
+
32
+ # elf: Advent of Code helper for Python
33
+
34
+ <p align="center">
35
+ <img src="https://snally.com/assets/elf-logo.png" width="200" alt="elf logo">
36
+ </p>
37
+
38
+ A fast, modern Advent of Code CLI with caching, guardrails, leaderboards, and a lightweight Python API.
39
+
40
+ Works on macOS, Linux, and Windows. Most networked commands require an AoC session cookie (`AOC_SESSION`).
41
+
42
+ <p align="center">
43
+ <img src="https://img.shields.io/pypi/v/elf.svg">
44
+ <img src="https://img.shields.io/badge/License-MIT-success.svg">
45
+ <img src="https://img.shields.io/badge/Advent%20of%20Code-Ready-00cc66">
46
+ <img src="https://img.shields.io/badge/North%20Pole%20API-Compliant-blue">
47
+ </p>
48
+
49
+ ## Why I Built This
50
+
51
+ Advent of Code has become one of my favorite Christmas traditions. I am never the fastest solver, and some puzzles definitely keep me humble, but the challenges always spark new ideas and mark the start of the holiday season for me.
52
+
53
+ Thank you to **Eric Wastl**, the creator of [Advent of Code](https://adventofcode.com/). His work brings an incredible community together every December and inspires people around the world to learn, explore new ideas, and enjoy the fun of programming puzzles.
54
+
55
+ After refining a small helper tool I have used personally for the past few years, I decided to turn it into a package others can benefit from as well. I originally built an early version for my own workflows in [my personal AoC repo](https://github.com/cak/advent-of-code/tree/dc9c02a5a77a36b725a8e01cff18a6de46e0db0d?tab=readme-ov-file#%EF%B8%8F-automating-tasks-with-the-elf-cli).
56
+
57
+ If Advent of Code is part of your December ritual too, I hope this little elf makes the journey smoother, more fun, and a bit more magical.
58
+
59
+ ## Highlights
60
+
61
+ - Fetch inputs instantly with **automatic caching**
62
+ - Submit answers safely with **smart guardrails** (duplicate, low, high, locked, cooldown)
63
+ - Explore private leaderboards as **tables**, **JSON**, or **Pydantic models**
64
+ - View your **status calendar** (table, JSON, or model)
65
+ - Inspect your **guess history** with timestamps and per-part details
66
+ - Open any AoC page (puzzle, input, or website) straight from the CLI
67
+ - Use elf as a **CLI tool** or a full **Python API**
68
+
69
+ ## Installation
70
+
71
+ ### Using uv (recommended)
72
+
73
+ #### Install as a tool
74
+
75
+ ```sh
76
+ uv tool install elf
77
+ ```
78
+
79
+ #### Inside a project
80
+
81
+ ```sh
82
+ uv add elf
83
+ ```
84
+
85
+ ### Using pip
86
+
87
+ ```sh
88
+ pip install elf
89
+ ```
90
+
91
+ ### Requirements
92
+
93
+ - Python 3.11 or newer
94
+ - An Advent of Code account
95
+ - `AOC_SESSION` cookie set in your environment for most commands
96
+
97
+ ## Configure your AoC Session
98
+
99
+ Most features in elf require your Advent of Code session cookie so the CLI can access your personal puzzle inputs and progress.
100
+
101
+ To get it:
102
+
103
+ 1. Log in to https://adventofcode.com using GitHub, Google, or Reddit.
104
+ 2. Open your browser’s developer tools.
105
+ - Chrome: View → Developer → Developer Tools
106
+ - Firefox: Tools → Browser Tools → Web Developer Tools
107
+ 3. Go to the **Application** (Chrome) or **Storage** (Firefox) tab.
108
+ 4. Look for **Cookies** for the domain `adventofcode.com`.
109
+ 5. Find the cookie named **`session`**.
110
+ 6. Copy the value (a long hex string).
111
+ 7. Set it as an environment variable:
112
+
113
+ ```sh
114
+ export AOC_SESSION="your-session-token"
115
+ ```
116
+
117
+ On Windows you can persist the cookie with PowerShell:
118
+
119
+ ```powershell
120
+ $env:AOC_SESSION = "your-session-token"
121
+ setx AOC_SESSION "your-session-token"
122
+ ```
123
+
124
+ Or via CMD:
125
+
126
+ ```cmd
127
+ setx AOC_SESSION "your-session-token"
128
+ ```
129
+
130
+ Most commands require this. You can also pass it via `--session` in the CLI or `session=` in the API.
131
+
132
+ ## Quick Start
133
+
134
+ ```sh
135
+ export AOC_SESSION="your-session-token"
136
+
137
+ elf input # fetch today's input
138
+ elf answer 2025 1 1 123 # submit answer for part 1
139
+ elf status # view progress
140
+ elf leaderboard 2025 123456 --view-key ABCDEF
141
+ ```
142
+
143
+ ## CLI Documentation
144
+
145
+ ### `elf --help`
146
+
147
+ ```sh
148
+ Usage: elf [OPTIONS] COMMAND [ARGS]...
149
+
150
+ Advent of Code CLI
151
+
152
+ Options:
153
+ --version, -V
154
+ --debug
155
+ --install-completion
156
+ --show-completion
157
+ --help
158
+
159
+ Commands:
160
+ input Fetch the input for a given year/day
161
+ answer Submit an answer
162
+ leaderboard Fetch/display a private leaderboard
163
+ guesses Show cached guesses
164
+ status Show yearly star status
165
+ open Open puzzle/input/website
166
+ cache Show cache information
167
+ ```
168
+
169
+ Enable detailed tracebacks with `--debug` or `ELF_DEBUG=1` when troubleshooting.
170
+
171
+ ---
172
+
173
+ ## Commands
174
+
175
+ ### `elf input`
176
+
177
+ Fetch puzzle input with caching. Requires a session cookie.
178
+
179
+ ```sh
180
+ Usage: elf input [YEAR] [DAY]
181
+
182
+ Options:
183
+ --session TEXT AOC session cookie (env: AOC_SESSION)
184
+ ```
185
+
186
+ Defaults:
187
+
188
+ - `year`: current year
189
+ - `day`: current day in December, otherwise 1 (Dec 1)
190
+ - Caches to `~/.cache/elf/<year>/<day>/input.txt` (or platform equivalent)
191
+
192
+ ### `elf answer`
193
+
194
+ Submit an answer with safety guardrails. Requires a session cookie. Guardrails use your local guess cache to short-circuit duplicate answers and infer too-high/too-low for integer guesses.
195
+
196
+ ```sh
197
+ Usage: elf answer YEAR DAY PART ANSWER
198
+
199
+ Options:
200
+ --session TEXT AOC session cookie
201
+ ```
202
+
203
+ Behaviors:
204
+
205
+ - Year and day are required to avoid accidental submissions.
206
+ - Detects locked puzzles (future days or future years) and shows unlock timestamp.
207
+ - Identifies too high / too low / duplicate guesses from local cache
208
+ - Caches cooldown responses locally for ~60 seconds to avoid hammering the site
209
+ - Writes to guess cache automatically (per part)
210
+
211
+ Exit codes:
212
+
213
+ - `0` when the answer is correct or already completed on AoC
214
+ - `2` when AoC rate-limits you (cooldown/WAIT)
215
+ - `1` for incorrect/too-high/too-low/unknown guesses
216
+
217
+ Duplicate guesses reuse cached submissions, so the exit code follows the cached result: e.g., a cached correct/completed guess still exits `0`, while cached incorrect/high/low responses return `1`.
218
+
219
+ Example errors:
220
+
221
+ ```sh
222
+ ❄️ Puzzle YYYY‑MM‑DD not unlocked yet
223
+ 1234 is not correct.
224
+ You submitted an answer recently. Please wait...
225
+ 12345 is correct. Star awarded.
226
+ ```
227
+
228
+ ### `elf guesses`
229
+
230
+ Display local guess history (per part). Requires a cached `guesses.csv` from previous submissions. Displays all guesses for both parts automatically.
231
+
232
+ ```sh
233
+ Usage: elf guesses [YEAR] [DAY]
234
+ ```
235
+
236
+ Example table:
237
+
238
+ ```sh
239
+ Time (UTC) Guess Status
240
+ 2025‑12‑05 ... 959 too_low
241
+ 2025‑12‑05 ... 6951 correct
242
+ ```
243
+
244
+ ### `elf leaderboard`
245
+
246
+ Fetch private leaderboards. Provide a view key for read-only access or a session cookie for authenticated access. A view key is the read-only share token you can generate on your AoC leaderboard page.
247
+
248
+ ```sh
249
+ Usage: elf leaderboard YEAR BOARD_ID
250
+
251
+ Options:
252
+ --view-key TEXT
253
+ --session TEXT
254
+ --format table|json|model
255
+ ```
256
+
257
+ Supports:
258
+
259
+ - **table:** pretty Rich table
260
+ - **json:** raw JSON
261
+ - **model:** structured Pydantic model
262
+
263
+ ### `elf status`
264
+
265
+ View your Advent of Code star calendar. Requires a session cookie.
266
+
267
+ ```sh
268
+ Usage: elf status [YEAR]
269
+
270
+ Options:
271
+ --session TEXT
272
+ --format table|json|model
273
+ ```
274
+
275
+ Defaults:
276
+
277
+ - `year`: current year if omitted
278
+
279
+ Prints stars for each day.
280
+
281
+ ### `elf open`
282
+
283
+ Opens puzzle pages in your browser.
284
+
285
+ ```sh
286
+ Usage: elf open [YEAR] [DAY]
287
+
288
+ Options:
289
+ --kind puzzle|input|website
290
+ ```
291
+
292
+ ### `elf cache`
293
+
294
+ Display cache directory information.
295
+
296
+ ```sh
297
+ Usage: elf cache
298
+ ```
299
+
300
+ Shows:
301
+
302
+ - Cache root directory (platform-aware)
303
+ - Number of cached files
304
+ - Reminder to delete the directory manually to clear cache
305
+
306
+ ---
307
+
308
+ ## Caching Behavior
309
+
310
+ - Default cache dir: macOS/Linux `~/.cache/elf`, Windows `%LOCALAPPDATA%\elf`
311
+ - Override location with `ELF_CACHE_DIR` (respects `XDG_CACHE_HOME` on Linux)
312
+ - Inputs stored under: `<cache>/<year>/<day>/input.txt`
313
+ - Guess history stored as `<cache>/<year>/<day>/guesses.csv`
314
+ - Duplicate/high/low guesses are short‑circuited locally when possible
315
+ - Cooldown responses are cached locally for ~60 seconds to avoid hammering AoC
316
+ - Guardrails rely on your local guess history only (new machines/cleared cache = fresh)
317
+ - Delete the cache directory to clear everything
318
+
319
+ ### Concurrency note
320
+
321
+ The library reuses a process-global `httpx.Client` to keep CLI calls fast. For heavy multi-threaded usage, create separate `elf.aoc_client.AOCClient` instances per thread to avoid sharing the global session.
322
+
323
+ ---
324
+
325
+ ## Library Usage
326
+
327
+ ```python
328
+ from elf import (
329
+ get_puzzle_input,
330
+ submit_puzzle_answer,
331
+ get_private_leaderboard,
332
+ get_user_status,
333
+ OutputFormat,
334
+ )
335
+
336
+ # AOC_SESSION is used automatically if not passed explicitly
337
+ input_text = get_puzzle_input(2023, 5)
338
+
339
+ result = submit_puzzle_answer(2023, 5, 1, "12345")
340
+ print(result.is_correct, result.message)
341
+
342
+ leaderboard = get_private_leaderboard(
343
+ 2023, session=None, board_id=123456, view_key=None, fmt=OutputFormat.MODEL
344
+ )
345
+
346
+ status = get_user_status(2023, fmt=OutputFormat.TABLE)
347
+ print(status)
348
+ ```
349
+
350
+ ### Puzzle Helpers
351
+
352
+ `elf.helpers` includes some small utilities you can use in your own AoC solutions:
353
+
354
+ ```python
355
+ from elf.helpers import parse_input, read_test_input, timer
356
+ ```
357
+
358
+ ### Leaderboard Example
359
+
360
+ ```sh
361
+ ❯ elf leaderboard 2025 3913340
362
+ Advent of Code 2025 – Private Leaderboard
363
+ ┏━━━━━━┳━━━━━━━━━━━━━━━━┳━━━━━━━┳━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━┓
364
+ ┃ Rank ┃ Name ┃ Stars ┃ Local Score ┃ Last Star (UTC) ┃
365
+ ┡━━━━━━╇━━━━━━━━━━━━━━━━╇━━━━━━━╇━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━┩
366
+ │ 1 │ User A │ 45 │ 900 │ 2025-12-26 00:53:17 │
367
+ │ 2 │ User B │ 45 │ 855 │ 2025-12-26 00:53:56 │
368
+ │ 3 │ User C │ 36 │ 622 │ 2025-12-26 03:29:21 │
369
+ └──────┴────────────────┴───────┴─────────────┴─────────────────────┘
370
+ ```
371
+
372
+ JSON format:
373
+
374
+ ```sh
375
+ elf leaderboard 2025 3982840 --format json
376
+ ```
377
+
378
+ Model format (Pydantic):
379
+
380
+ ```python
381
+ lb = get_private_leaderboard(2025, board_id=3982840, fmt=OutputFormat.MODEL)
382
+ members = sorted(lb.members.values(), key=lambda m: (-m.local_score, -m.stars))
383
+ print(members[0].name, members[0].stars)
384
+ ```
385
+
386
+ ### Status Example
387
+
388
+ ```sh
389
+ ❯ elf status 2023
390
+ Advent of Code
391
+ 2023 – cak
392
+ (AoC++) [34⭐]
393
+ ┏━━━━━┳━━━━━━━┓
394
+ ┃ Day ┃ Stars ┃
395
+ ┡━━━━━╇━━━━━━━┩
396
+ │ 1 │ ★★ │
397
+ │ 2 │ ★★ │
398
+ │ 3 │ ★★ │
399
+ │ 4 │ ★☆ │
400
+ │ 5 │ ★☆ │
401
+ │ 6 │ ★★ │
402
+ │ 7 │ ★★ │
403
+ │ 8 │ ★☆ │
404
+ │ 9 │ ☆☆ │
405
+ │ 10 │ ☆☆ │
406
+ │ .. │ .. │
407
+ │ 25 │ ☆☆ │
408
+ └─────┴───────┘
409
+ ```
410
+
411
+ JSON format:
412
+
413
+ ```sh
414
+ elf status 2023 --format json
415
+ ```
416
+
417
+ Model format:
418
+
419
+ ```python
420
+ status = get_user_status(2023, fmt=OutputFormat.MODEL)
421
+ print(status.days[0].day, status.days[0].stars)
422
+ ```
423
+
424
+ ---
425
+
426
+ ## Development / Testing
427
+
428
+ Install the dev dependencies (currently just `pytest`) with:
429
+
430
+ ```sh
431
+ pip install -e ".[dev]"
432
+ ```
433
+
434
+ Run the test suite via:
435
+
436
+ ```sh
437
+ uv run pytest
438
+ ```
439
+
440
+ Alternatively, you can still run:
441
+
442
+ ```sh
443
+ python -m pytest
444
+ ```
445
+
446
+ ## Special Thanks
447
+
448
+ This project exists thanks to the generosity and creativity of others.
449
+
450
+ Thanks to **Solos** for donating the `elf` PyPI name.