Tracks weekly grocery discounts at Mercator Hipermarket Rudnik (Supernova Ljubljana) and ranks the best deals by percentage off.
Live site: https://pikammmmm.github.io/lj-discounts/ (Rebuilds twice daily at 07:00 and 12:00 Europe/Ljubljana.)
Hits Mercator's online assortment API pinned to the Rudnik distribution center (HM LJUBLJANA RUDNIK, id=30) so the returned products match what's actually available at the store.
Download the latest lj-discounts-windows.zip,
unzip, and double-click run-lj-discounts.cmd. The desktop app window
opens, scrapes, and displays the report. lj-discounts-cli.exe is bundled
for scheduled/headless use.
Grab download/lj-discounts.apk (also attached
to every tagged release)
and install it — you may need to enable Install unknown apps for your
browser. The APK is a tiny WebView shell around the live site that runs
fullscreen with no URL bar, so it always shows the freshest scrape without
going through the Play Store.
To rebuild it locally (Debian/Ubuntu):
sudo apt-get install -y android-sdk-build-tools smali \
default-jdk-headless zip curl
./android/build.sh # writes download/lj-discounts.apkThe script fetches android-34.jar from a GitHub mirror on first run
(cached under android/.cache/) since Debian doesn't ship a recent
android-sdk-platform-* package.
The signing keystore at android/release.keystore is committed on purpose
(password ljdiscounts) so anyone with the repo can sign updates that
existing installs accept. Override KS_PASS / KEY_ALIAS env vars if you
generate your own.
Install Python 3.12+, then double-click refresh.bat (or run
.\refresh.ps1 from PowerShell). A .venv is created, dependencies are
installed, and the app window opens.
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
python run.py --openmacOS users can double-click refresh.command.
python run.py [--html PATH] [--db PATH] [--top N] [--stale-days N] [--open|--no-open]| Flag | Default | Purpose |
|---|---|---|
--html PATH |
offers.html |
Where to write the generated HTML report |
--db PATH |
offers.db |
SQLite cache path |
--top N |
20 |
How many % discounts to print to stdout |
--stale-days N |
8 |
Purge undated, unseen offers after N days |
--open |
auto | Open the HTML in the default browser |
--no-open |
— | Skip browser launch |
.
├── app.py # pywebview desktop shell
├── run.py # CLI entry point + HTML renderer
├── scrapers/
│ ├── mercator.py # Mercator online API scraper
│ └── __init__.py
├── models.py # Offer dataclass
├── stores.py # physical-store label
├── weird.py # hot/suspicious deal flagging
├── categorize.py # grocery vs non-grocery filter
├── assets/ # app icon (ICO + PNG) + generator
├── manifest.json # PWA manifest (served from gh-pages)
├── android/ # WebView-wrapper APK source (smali + aapt2)
│ ├── AndroidManifest.xml
│ ├── smali/ # MainActivity.smali (Dalvik assembly)
│ ├── res/ # icons, strings, theme
│ ├── release.keystore # committed signing key (pass: ljdiscounts)
│ └── build.sh # apt-only local build
├── download/lj-discounts.apk # latest signed APK (refreshed by android.yml)
├── windows/ # Windows-only launchers bundled in the exe zip
├── refresh.{bat,ps1,command} # source-run launchers per platform
├── requirements.txt # scrape-only deps (requests, beautifulsoup4)
├── requirements-windows.txt # adds pywebview for the desktop app
└── .github/workflows/
├── scrape.yml # daily scrape → gh-pages
├── windows.yml # tag-triggered exe build + release
└── android.yml # tag-triggered APK build + release
- Scrape (scheduled):
scrape.ymlrunspython run.pyon GitHub-hosted Ubuntu daily at 05:00 and 10:00 UTC (07:00 / 12:00 local). - Deploy: generated
offers.htmlis copied toindex.html, icons andmanifest.jsonare staged, and the result is force-pushed togh-pages. - Release (tag-triggered): pushing a
v*tag runswindows.yml, which buildslj-discounts.exe(desktop) andlj-discounts-cli.exe, zips them with README and launcher, uploads as an artifact, and attaches the zip to the GitHub Release. - Android APK:
android.ymlruns./android/build.shon every push tomainthat touchesandroid/**(also on tag pushes and via Run workflow). It installs Debian'sandroid-sdk-build-toolsandsmalipackages, fetchesandroid-34.jarfrom a GitHub mirror so the APK targets a current Android API (Debian's newest platform jar is API 23, which Android 14 rejects), assemblesMainActivity.smali, links resources withaapt2, signs with the committed keystore, commits the rebuiltdownload/lj-discounts.apkback tomain([skip ci]-tagged so it doesn't loop), and attaches it to tagged releases.
MIT © 2026 pikammmmm