From 1aa4b16eef514754429c5190ea3f6d7f80121354 Mon Sep 17 00:00:00 2001 From: Fizzlepoof Date: Sat, 16 May 2026 16:03:48 +0000 Subject: [PATCH] Add experimental New to You dashboard section --- .../doris-dashboard/bin/generate_dashboard.py | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/home/doris-dashboard/bin/generate_dashboard.py b/home/doris-dashboard/bin/generate_dashboard.py index fb12d51..87c4f81 100755 --- a/home/doris-dashboard/bin/generate_dashboard.py +++ b/home/doris-dashboard/bin/generate_dashboard.py @@ -263,6 +263,39 @@ def select_items(items:list[dict[str,Any]],prefs:dict[str,Any],max_items:int=14) consider(3) return out +def select_exploration_items(items:list[dict[str,Any]],prefs:dict[str,Any],exclude_urls:set[str]|None=None,max_items:int=4)->list[dict[str,Any]]: + now=datetime.now(timezone.utc) + exclude_urls=exclude_urls or set() + boosted={str(x).strip() for x in prefs.get('boost_categories',[])} + quiet={str(x).strip() for x in prefs.get('quiet_categories',[])} + preferred=['tech','smart_home','gaming','cooking','security'] + recent=[i for i in items if (now-parse_time(i.get('published_at',''))).total_seconds()<96*3600] + recent.sort(key=lambda i:(score_item(i,prefs),i.get('published_at','')),reverse=True) + + out=[]; seen=set(exclude_urls); categories=Counter(); sources=Counter() + + def consider(allowed_boosted:bool, only_categories:set[str]|None=None, per_category_limit:int=1)->None: + for i in recent: + if len(out)>=max_items: break + url=i.get('url',''); src=i.get('source',''); cat=(i.get('category') or 'other').strip() or 'other' + if not url or url in seen or sources[src]>=1 or categories[cat]>=per_category_limit: + continue + if cat in quiet: + continue + if only_categories is not None and cat not in only_categories: + continue + if not allowed_boosted and cat in boosted: + continue + out.append(i); seen.add(url); categories[cat]+=1; sources[src]+=1 + + # Keep this lane genuinely exploratory: preferred off-profile categories first, + # then broader off-profile picks, allowing a little depth if the pool is narrow. + consider(False, set(preferred), 1) + consider(False, set(preferred), 3) + consider(False, None, 1) + consider(False, None, 3) + return out + def label(cat:str)->str: e,n=LABELS.get(cat,('🧩',(cat or 'Other').title())); return f'{e} {n}' @@ -555,6 +588,7 @@ def render()->str: candidates=load_json(DIGEST_ROOT/'data/candidates.json',[]); prefs=load_json(DIGEST_ROOT/'data/preferences.json',{}) donetick_todos=fetch_donetick_todos(); todos=inject_dashboard_todos(donetick_todos or load_json(DATA/'todos.json',[])); calendar=load_json(DATA/'calendar.json',[]); notes=load_json(DATA/'notes.json',[]) selected=select_items(candidates,prefs); top_briefing=selected[:6]; top_urls={i.get('url','') for i in top_briefing if i.get('url')}; groups={'space':[],'homelab':[],'local':[],'signals':[]} + exploration=select_exploration_items(candidates,prefs,{i.get('url','') for i in selected if i.get('url')},4) for i in selected: if i.get('url','') in top_urls: continue cat=i.get('category') @@ -590,6 +624,7 @@ def render()->str: {section('Sky / Space','SpaceX, NASA, planetary science, and astronomy.',groups['space'][:5],'No sky signal right now.',compact=True)} {section('Homelab / Smart Home / Radio','Infrastructure, self-hosting, smart home, and ham radio.',groups['homelab'][:4],'No homelab signal right now.',compact=True)} {section('Local','Clarksville / Middle Tennessee items that seem useful.',groups['local'][:3],'No local signal right now.',compact=True)} +{section('New to You','Intentional oddballs outside your usual top lanes, so you can see if any new categories deserve a slot.',exploration[:4],'No worthwhile experimental picks right now.',compact=True)} {section('Other signals','Everything else that survived filtering.',groups['signals'][:3],'No extra signals.',compact=True)} {feedback_bootstrap()}"""