Refine Doris dashboard story dedupe
This commit is contained in:
@@ -1534,10 +1534,11 @@ def focus_card(title:str,kicker:str,headline:str,body:str,tone:str='',href:str='
|
|||||||
return f"<a class='focus-card-link' href='{esc(href)}'{target_attr}>{card_html}</a>"
|
return f"<a class='focus-card-link' href='{esc(href)}'{target_attr}>{card_html}</a>"
|
||||||
|
|
||||||
|
|
||||||
def build_focus_cards(local_items:list[dict[str,Any]], todos:list[dict[str,Any]], pulse:list[dict[str,str]], paperless:dict[str,Any], hermes:dict[str,Any])->tuple[str,str]:
|
def build_focus_cards(local_items:list[dict[str,Any]], todos:list[dict[str,Any]], pulse:list[dict[str,str]], paperless:dict[str,Any], hermes:dict[str,Any], excluded_story_keys:set[str] | None=None)->tuple[str,str]:
|
||||||
urgent_todos=[item for item in todos if (item.get('state') or '') in {'overdue','today'} and (item.get('source') or '') != 'Dashboard']
|
urgent_todos=[item for item in todos if (item.get('state') or '') in {'overdue','today'} and (item.get('source') or '') != 'Dashboard']
|
||||||
warn_items=[item for item in pulse if item.get('state')=='warn']
|
warn_items=[item for item in pulse if item.get('state')=='warn']
|
||||||
local_story=local_items[0] if local_items else None
|
excluded_story_keys=excluded_story_keys or set()
|
||||||
|
local_story=next((item for item in local_items if story_key(item) not in excluded_story_keys), None)
|
||||||
cron_summary=hermes_cron_summary()
|
cron_summary=hermes_cron_summary()
|
||||||
session_window=find_usage_window(hermes.get('account_windows',[]), 'session') if hermes.get('ok') else None
|
session_window=find_usage_window(hermes.get('account_windows',[]), 'session') if hermes.get('ok') else None
|
||||||
session_remaining=session_window.get('remaining_percent') if session_window else None
|
session_remaining=session_window.get('remaining_percent') if session_window else None
|
||||||
@@ -1564,6 +1565,12 @@ def build_focus_cards(local_items:list[dict[str,Any]], todos:list[dict[str,Any]]
|
|||||||
hermes_headline='Hermes telemetry unavailable'
|
hermes_headline='Hermes telemetry unavailable'
|
||||||
hermes_body='Could not read local Hermes usage stats right now.'
|
hermes_body='Could not read local Hermes usage stats right now.'
|
||||||
local_state='attention' if local_story else 'ok'
|
local_state='attention' if local_story else 'ok'
|
||||||
|
local_headline=str(local_story.get('title') or '') if local_story else ('Local lane already covered' if local_items else 'Local lane quiet')
|
||||||
|
local_body=(
|
||||||
|
f"{local_story.get('source','Local feed')} • {label(local_story.get('category','local'))}"
|
||||||
|
if local_story else
|
||||||
|
('The strongest local item is already promoted above, so this slot stays out of the way.' if local_items else 'Nothing local survived filtering hard enough to earn the homepage.')
|
||||||
|
)
|
||||||
chores_state='warn' if urgent_todos else 'ok'
|
chores_state='warn' if urgent_todos else 'ok'
|
||||||
paperless_state='warn' if paperless.get('flagged_count',0) else ('ok' if not paperless.get('active_count',0) else 'attention')
|
paperless_state='warn' if paperless.get('flagged_count',0) else ('ok' if not paperless.get('active_count',0) else 'attention')
|
||||||
homelab_state='warn' if warn_items else 'ok'
|
homelab_state='warn' if warn_items else 'ok'
|
||||||
@@ -1573,8 +1580,8 @@ def build_focus_cards(local_items:list[dict[str,Any]], todos:list[dict[str,Any]]
|
|||||||
focus_card(
|
focus_card(
|
||||||
'Local watch',
|
'Local watch',
|
||||||
'Useful nearby',
|
'Useful nearby',
|
||||||
(local_story.get('title') if local_story else 'Local lane quiet'),
|
local_headline,
|
||||||
(f"{local_story.get('source','Local feed')} • {label(local_story.get('category','local'))}" if local_story else 'Nothing local survived filtering hard enough to earn the homepage.'),
|
local_body,
|
||||||
f'local {local_state}'.strip(),
|
f'local {local_state}'.strip(),
|
||||||
href=str(local_story.get('url') or '') if local_story else '' ,
|
href=str(local_story.get('url') or '') if local_story else '' ,
|
||||||
target='_blank' if local_story and local_story.get('url') else ''
|
target='_blank' if local_story and local_story.get('url') else ''
|
||||||
@@ -1671,6 +1678,39 @@ def choose_feature_story(preferred_items:list[dict[str,Any]], fallback_items:lis
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def choose_secondary_feature_story(preferred_items:list[dict[str,Any]], fallback_items:list[dict[str,Any]], primary_story:dict[str,Any] | None, prefs:dict[str,Any])->dict[str,Any] | None:
|
||||||
|
primary_key=story_key(primary_story) if primary_story else ''
|
||||||
|
state=load_lead_warrant_state()
|
||||||
|
feedback=load_dashboard_feedback()
|
||||||
|
acknowledged=set(state.get('acknowledged',[]))
|
||||||
|
ignored_sources=set(state.get('ignored_sources',[]))
|
||||||
|
ignored_topics=set(state.get('ignored_topics',[]))
|
||||||
|
candidates=[]
|
||||||
|
fallback=[]
|
||||||
|
seen=set()
|
||||||
|
for pool_name,pool in (('preferred',preferred_items),('fallback',fallback_items)):
|
||||||
|
for item in pool:
|
||||||
|
key=story_key(item)
|
||||||
|
dedupe=key or f"{str(item.get('source') or '').strip().lower()}::{str(item.get('title') or '').strip().lower()}"
|
||||||
|
if dedupe in seen or (primary_key and key == primary_key):
|
||||||
|
continue
|
||||||
|
seen.add(dedupe)
|
||||||
|
fallback.append(item)
|
||||||
|
if good_lead_candidate(item,prefs,feedback,ignored_sources,ignored_topics):
|
||||||
|
candidates.append(item)
|
||||||
|
for item in candidates:
|
||||||
|
if story_key(item) not in acknowledged:
|
||||||
|
return item
|
||||||
|
if candidates:
|
||||||
|
return candidates[0]
|
||||||
|
for item in fallback:
|
||||||
|
if story_key(item) not in acknowledged:
|
||||||
|
return item
|
||||||
|
if fallback:
|
||||||
|
return fallback[0]
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
def build_feature_story(feature_item:dict[str,Any] | None)->str:
|
def build_feature_story(feature_item:dict[str,Any] | None)->str:
|
||||||
if not feature_item:
|
if not feature_item:
|
||||||
return ''
|
return ''
|
||||||
@@ -1680,7 +1720,7 @@ def build_feature_story(feature_item:dict[str,Any] | None)->str:
|
|||||||
when=(feature_item.get('published_at','')[:16].replace('T',' '))
|
when=(feature_item.get('published_at','')[:16].replace('T',' '))
|
||||||
category=str(feature_item.get('category') or 'other')
|
category=str(feature_item.get('category') or 'other')
|
||||||
url=str(feature_item.get('url') or '')
|
url=str(feature_item.get('url') or '')
|
||||||
feature_label='Lead local item' if category=='local' else ('Lead homelab item' if category in {'homelab','smart_home','radio'} else 'Lead signal')
|
feature_label='Next local signal' if category=='local' else ('Next homelab signal' if category in {'homelab','smart_home','radio'} else 'Next signal')
|
||||||
why_text=why(feature_item)
|
why_text=why(feature_item)
|
||||||
return f"""
|
return f"""
|
||||||
<section class='feature-story'>
|
<section class='feature-story'>
|
||||||
@@ -1695,7 +1735,7 @@ def build_feature_story(feature_item:dict[str,Any] | None)->str:
|
|||||||
</div>
|
</div>
|
||||||
<div class='feature-actions'>
|
<div class='feature-actions'>
|
||||||
<a href='{esc(url)}' target='_blank' rel='noreferrer' class='feature-open'>Open story</a>
|
<a href='{esc(url)}' target='_blank' rel='noreferrer' class='feature-open'>Open story</a>
|
||||||
<span class='feature-note'>Promoted so you get one strong item instead of four equal tiles.</span>
|
<span class='feature-note'>Secondary signal so the warrant and the next-click card are not the same story.</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</article>
|
</article>
|
||||||
@@ -2105,15 +2145,17 @@ def render()->str:
|
|||||||
pulse_metrics=''.join(metric_card(x['label'],x['value'],x['state'],x['state']) for x in pulse)
|
pulse_metrics=''.join(metric_card(x['label'],x['value'],x['state'],x['state']) for x in pulse)
|
||||||
forecast_html=forecast_panel(weather)
|
forecast_html=forecast_panel(weather)
|
||||||
hero_summary=build_hero_summary(groups['local'],todos,pulse,paperless,hermes)
|
hero_summary=build_hero_summary(groups['local'],todos,pulse,paperless,hermes)
|
||||||
focus_html,focus_state=build_focus_cards(groups['local'],todos,pulse,paperless,hermes)
|
feature_item=choose_feature_story(top_briefing, selected, prefs)
|
||||||
|
secondary_feature_item=choose_secondary_feature_story(top_briefing, selected, feature_item, prefs)
|
||||||
|
excluded_focus_story_keys={key for key in [story_key(feature_item), story_key(secondary_feature_item)] if key}
|
||||||
|
focus_html,focus_state=build_focus_cards(groups['local'],todos,pulse,paperless,hermes,excluded_story_keys=excluded_focus_story_keys)
|
||||||
focus_signature=donetick_focus_signature(todos)
|
focus_signature=donetick_focus_signature(todos)
|
||||||
hermes_html=build_hermes_section(hermes)
|
hermes_html=build_hermes_section(hermes)
|
||||||
system_html=build_system_section(system)
|
system_html=build_system_section(system)
|
||||||
hero_tokens=compact_number(hermes.get('total_tokens',0)) if hermes.get('ok') else '—'
|
hero_tokens=compact_number(hermes.get('total_tokens',0)) if hermes.get('ok') else '—'
|
||||||
snapshot_html=build_operator_snapshot(todos,pulse,paperless,hermes)
|
snapshot_html=build_operator_snapshot(todos,pulse,paperless,hermes)
|
||||||
feature_item=choose_feature_story(top_briefing, selected, prefs)
|
|
||||||
hero_caseboard_html=build_hero_caseboard(feature_item,todos,paperless)
|
hero_caseboard_html=build_hero_caseboard(feature_item,todos,paperless)
|
||||||
feature_html=build_feature_story(feature_item)
|
feature_html=build_feature_story(secondary_feature_item)
|
||||||
session_window=find_usage_window(hermes.get('account_windows',[]),'session') if hermes.get('ok') else None
|
session_window=find_usage_window(hermes.get('account_windows',[]),'session') if hermes.get('ok') else None
|
||||||
primary_html=''.join([
|
primary_html=''.join([
|
||||||
feature_html,
|
feature_html,
|
||||||
|
|||||||
Reference in New Issue
Block a user