FEST: in-repo flattener bundles slim.json; release builds auto-refresh when stale

Amends decision #8: the producer now lives in tools/fest_flatten.py
(stdlib Python, streaming iterparse over the 115 MB M30 XML) instead of
an out-of-repo server job. It pulls fest251.zip from dmp.no, flattens
8934 active human-use brand entries (name/strength/unit/form/ATC +
modal package size from pakningsinfo), and writes the asset checked in
at app/src/main/assets/fest/slim.json — so autocomplete works offline
out of the box. preReleaseBuild depends on refreshFestData: fast-exits
under 30 days, keeps the old file on download failure (offline release
builds never break). FestRepository: downloaded cache wins over the
bundled asset.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Ole-Morten Duesund 2026-06-10 15:19:49 +02:00
commit 293ca212ec
6 changed files with 260 additions and 15 deletions

File diff suppressed because one or more lines are too long

View file

@ -39,18 +39,34 @@ class FestRepository(
private val json = Json { ignoreUnknownKeys = true }
private val cacheFile get() = File(context.filesDir, "fest-slim.json")
private companion object {
const val BUNDLED_ASSET = "fest/slim.json"
}
@Volatile private var loaded: FestDataset? = null
/** Cached dataset, loading from disk on first use. Null until downloaded once. */
/**
* Cached dataset: a downloaded refresh wins over the asset bundled at build
* time, but the bundle means autocomplete works out of the box, offline,
* with zero configuration.
*/
fun dataset(): FestDataset? {
loaded?.let { return it }
synchronized(this) {
loaded?.let { return it }
if (!cacheFile.exists()) return null
if (cacheFile.exists()) {
try {
return json.decodeFromString<FestDataset>(cacheFile.readText()).also { loaded = it }
} catch (e: Exception) {
// corrupt cache → fall through to the bundled asset
}
}
return try {
json.decodeFromString<FestDataset>(cacheFile.readText()).also { loaded = it }
context.assets.open(BUNDLED_ASSET).bufferedReader().use { reader ->
json.decodeFromString<FestDataset>(reader.readText()).also { loaded = it }
}
} catch (e: Exception) {
null // corrupt cache → behave as not-downloaded; refresh overwrites
null
}
}
}