2424from pathlib import Path
2525from typing import Any
2626
27- from PIL import Image
28-
2927_COUNTRY_NAMES : dict [str , str ] = {
3028 "ao" : "Angola" ,
3129 "ar" : "Argentina" ,
@@ -177,47 +175,19 @@ def prune_obsolete_dashboard_dirs(
177175# GitHub Pages artifact limit (deploy fails above this).
178176GITHUB_PAGES_MAX_BYTES = 1_073_741_824
179177
180- # Map PNGs are ~45% of each dashboard; downscale for Pages to stay under 1 GB.
181- PAGES_MAP_MARKERS = ("map_actual" , "map_pred" )
182- # Linear scale before palette quantize (~65% side length, 128-color maps).
183- PAGES_MAP_SCALE = 0.65
184- PAGES_MAP_PALETTE_COLORS = 128
185- PAGES_MAP_PNG_OPTIMIZE = True
186-
187-
188- def _is_map_asset (filename : str ) -> bool :
189- return any (marker in filename for marker in PAGES_MAP_MARKERS )
190-
191178
192- def _flatten_for_map_export (img : Image .Image ) -> Image .Image :
193- """RGBA/LA choropleth panels → RGB on white (better PNG palette compression)."""
194- if img .mode in ("RGBA" , "LA" ):
195- background = Image .new ("RGB" , img .size , (255 , 255 , 255 ))
196- background .paste (img , mask = img .split ()[- 1 ])
197- return background
198- if img .mode != "RGB" :
199- return img .convert ("RGB" )
200- return img
201-
202-
203- def downgrade_map_png (
204- src : Path ,
205- dest : Path ,
206- * ,
207- scale : float = PAGES_MAP_SCALE ,
208- palette_colors : int = PAGES_MAP_PALETTE_COLORS ,
209- ) -> int :
210- """Downscale + palette-compress a map PNG for GitHub Pages."""
211- with Image .open (src ) as img :
212- img = _flatten_for_map_export (img )
213- width , height = img .size
214- new_size = (max (1 , int (width * scale )), max (1 , int (height * scale )))
215- if new_size != (width , height ):
216- img = img .resize (new_size , Image .Resampling .LANCZOS )
217- img = img .convert ("P" , palette = Image .Palette .ADAPTIVE , colors = palette_colors )
218- dest .parent .mkdir (parents = True , exist_ok = True )
219- img .save (dest , format = "PNG" , optimize = PAGES_MAP_PNG_OPTIMIZE )
220- return dest .stat ().st_size
179+ def _copy_assets (assets_src : Path , dest_assets : Path ) -> int :
180+ """Copy assets/; return number of files copied."""
181+ if dest_assets .exists ():
182+ shutil .rmtree (dest_assets )
183+ dest_assets .mkdir (parents = True )
184+ n_copied = 0
185+ for src_file in sorted (assets_src .iterdir ()):
186+ if not src_file .is_file ():
187+ continue
188+ shutil .copy2 (src_file , dest_assets / src_file .name )
189+ n_copied += 1
190+ return n_copied
221191
222192
223193def estimate_publish_bundle_size (publish_root : Path ) -> dict [str , Any ]:
@@ -259,47 +229,6 @@ def estimate_publish_bundle_size(publish_root: Path) -> dict[str, Any]:
259229 }
260230
261231
262- def apply_pages_lite_to_publish_root (
263- publish_root : Path ,
264- * ,
265- dry_run : bool = False ,
266- scale : float = PAGES_MAP_SCALE ,
267- ) -> tuple [int , int ]:
268- """Downscale map PNGs in every published dashboard (scatter/temporal unchanged).
269-
270- Returns (maps_processed, bytes_saved).
271- """
272- publish_root = publish_root .resolve ()
273- processed = 0
274- saved = 0
275- for child in publish_root .iterdir ():
276- if not child .is_dir () or child .name in {"assets" , ".git" }:
277- continue
278- assets = child / "assets"
279- if not assets .is_dir ():
280- continue
281- for asset in assets .glob ("*.png" ):
282- if not _is_map_asset (asset .name ):
283- continue
284- before = asset .stat ().st_size
285- if dry_run :
286- print (f"[DRY-RUN] downgrade { child .name } /assets/{ asset .name } " )
287- processed += 1
288- continue
289- tmp = asset .with_name (asset .name + ".tmp" )
290- after = downgrade_map_png (asset , tmp , scale = scale )
291- tmp .replace (asset )
292- processed += 1
293- saved += max (0 , before - after )
294- if processed :
295- action = "would downgrade" if dry_run else "downgraded"
296- print (
297- f"[OK] { action } { processed } map PNG(s)"
298- + (f", saved { saved / (1024 * 1024 ):.1f} MB" if saved else "" )
299- )
300- return processed , saved
301-
302-
303232def report_publish_bundle_size (publish_root : Path ) -> None :
304233 stats = estimate_publish_bundle_size (publish_root )
305234 print (
@@ -313,42 +242,18 @@ def report_publish_bundle_size(publish_root: Path) -> None:
313242 over_mb = stats ["total_bytes" ] / (1024 * 1024 ) - stats ["pages_limit_mb" ]
314243 print (
315244 f"[WARN] Over GitHub Pages artifact limit ({ stats ['pages_limit_mb' ]} MB) "
316- f"by ~{ over_mb :.0f} MB. Run --downgrade-maps-only or republish with --pages-lite. "
245+ f"by ~{ over_mb :.0f} MB."
317246 )
318247 else :
319248 headroom = stats ["pages_limit_mb" ] - stats ["total_mb" ]
320249 print (f"[OK] Within GitHub Pages limit ({ headroom :.0f} MB headroom)" )
321250
322251
323- def _copy_assets (
324- assets_src : Path ,
325- dest_assets : Path ,
326- * ,
327- pages_lite : bool ,
328- ) -> int :
329- """Copy assets/; return number of files copied."""
330- if dest_assets .exists ():
331- shutil .rmtree (dest_assets )
332- dest_assets .mkdir (parents = True )
333- n_copied = 0
334- for src_file in sorted (assets_src .iterdir ()):
335- if not src_file .is_file ():
336- continue
337- dest_file = dest_assets / src_file .name
338- if pages_lite and _is_map_asset (src_file .name ):
339- downgrade_map_png (src_file , dest_file )
340- else :
341- shutil .copy2 (src_file , dest_file )
342- n_copied += 1
343- return n_copied
344-
345-
346252def publish_bundle (
347253 * ,
348254 source_dir : Path ,
349255 dest_dir : Path ,
350256 title : str | None = None ,
351- pages_lite : bool = False ,
352257) -> Path :
353258 """Copy HTML + assets into dest_dir/dashboard.html (+ assets/)."""
354259 html_src , assets_src = _resolve_html_and_assets (source_dir )
@@ -359,12 +264,8 @@ def publish_bundle(
359264
360265 dest_assets = dest_dir / "assets"
361266 if assets_src and assets_src .is_dir ():
362- n_assets = _copy_assets (assets_src , dest_assets , pages_lite = pages_lite )
363- if pages_lite :
364- print (
365- f"[INFO] pages-lite: copied { n_assets } asset(s) "
366- f"(maps → { PAGES_MAP_SCALE :.0%} scale, { PAGES_MAP_PALETTE_COLORS } -color palette)"
367- )
267+ n_assets = _copy_assets (assets_src , dest_assets )
268+ print (f"[INFO] copied { n_assets } asset(s)" )
368269 elif dest_assets .exists ():
369270 shutil .rmtree (dest_assets )
370271
@@ -681,21 +582,6 @@ def main() -> None:
681582 action = "store_true" ,
682583 help = "Print prune actions without deleting" ,
683584 )
684- parser .add_argument (
685- "--pages-lite" ,
686- action = "store_true" ,
687- help = "Downscale map PNGs when copying assets (keeps all panels; fits GitHub Pages 1 GB limit)" ,
688- )
689- parser .add_argument (
690- "--downgrade-maps-only" ,
691- action = "store_true" ,
692- help = "Downscale map PNGs in existing published dashboards, then report size" ,
693- )
694- parser .add_argument (
695- "--strip-maps-only" ,
696- action = "store_true" ,
697- help = argparse .SUPPRESS ,
698- )
699585 parser .add_argument (
700586 "--report-size" ,
701587 action = "store_true" ,
@@ -710,12 +596,6 @@ def main() -> None:
710596 report_publish_bundle_size (publish_root )
711597 return
712598
713- if args .downgrade_maps_only or args .strip_maps_only :
714- apply_pages_lite_to_publish_root (publish_root , dry_run = args .dry_run )
715- if not args .dry_run :
716- report_publish_bundle_size (publish_root )
717- return
718-
719599 if args .prune_only :
720600 removed = prune_obsolete_dashboard_dirs (publish_root , dry_run = args .dry_run )
721601 print (f"[DONE] Pruned { len (removed )} obsolete folder(s)" )
@@ -737,7 +617,6 @@ def main() -> None:
737617 source_dir = source_dir ,
738618 dest_dir = dest_dir ,
739619 title = args .title ,
740- pages_lite = args .pages_lite ,
741620 )
742621 print (f"[DONE] Bundle: { html_path } " )
743622 if (dest_dir / "assets" ).is_dir ():
0 commit comments