HackathonSpaceRecommender / ui /templates.py
tejasashinde's picture
Integrate HF Bucket storage
4784470
Raw
History Blame Contribute Delete
4.87 kB
from __future__ import annotations
import html
from collections import Counter
from backend.models import SpaceItem
from backend.passport import PassportState, get_badges, get_next_track, get_rank
from backend.storage import get_recent_feedback
from backend.tracks import TRACK_NAMES, get_track_metadata
ABOUT_TEXT = """
# About Build Small Quest Finder
This app is a community discovery layer for the Build Small Hackathon Spaces.
It does not own or modify the original Spaces.
## How recommendations work
- The app scores your query against each Space's title, summary, tags, track, difficulty, and README text when available.
- The LLM sharpens the query intent and helps explain why a Space is a confident match.
- Results are ordered by a lightweight relevance score.
## How quests work
- Each Space gets a short, track-based mission.
- You can mark quests complete locally during your session.
- Shared comments are saved in the SQLite database so everyone can read them.
- Your progress lives in `gr.State`, not a database.
## How data is fetched
- The app first tries live Hugging Face Hub fetching.
- If that is unavailable, it falls back to `data/spaces_cache.json`.
- If no cache exists, it uses `data/sample_spaces.json`.
## What builders get
- A more fun way to discover hackathon demos
- Quest cards that explain why a Space matches
- Comment modals that let you read community notes
- A simple passport with badges and progress
- Shared reflections saved for the community
"""
def _escape(text: str) -> str:
return html.escape(text or "")
def render_passport(state: dict, spaces: list[SpaceItem]) -> str:
passport = PassportState.from_dict(state)
space_lookup = {space.repo_id: space for space in spaces}
completed_spaces = [space_lookup[repo_id] for repo_id in passport.completed_repo_ids if repo_id in space_lookup]
track_counts = Counter(space.track or "Unassigned" for space in completed_spaces)
badges = get_badges(state, spaces)
rank = get_rank(len(passport.completed_repo_ids))
next_track = get_next_track(state, spaces)
explored = len(passport.tracks_explored)
total_tracks = len(TRACK_NAMES)
completed_list = "".join(
f"<li>{_escape(space.emoji)} {_escape(space.title)} <span class='passport-zone'>({_escape(space.track or space.zone)})</span></li>"
for space in completed_spaces[-8:]
) or "<li>Complete a quest to start your passport.</li>"
badge_html = "".join(f"<span class='badge-pill'>{_escape(badge)}</span>" for badge in badges)
if not badge_html:
badge_html = "<span class='passport-muted'>No badges yet. Complete 3 quests in the same zone to earn one.</span>"
track_lines = []
for track in TRACK_NAMES:
meta = get_track_metadata(track)
track_lines.append(
f"<div class='passport-zone-row'><span>{meta['emoji']} {_escape(track)}</span><strong>{track_counts.get(track, 0)}</strong></div>"
)
recent_feedback = get_recent_feedback(limit=5)
feedback_list = "".join(
f"<li><strong>{_escape(row['space_title'])}</strong>: {_escape(row['answer'])}</li>"
for row in recent_feedback
) or "<li>No reflections yet.</li>"
return f"""
<div class="passport-shell">
<div class="passport-header">
<div>
<div class="hero-badge">🎒 Passport</div>
<h2>Quest Progress</h2>
</div>
<div class="passport-rank">{_escape(rank)}</div>
</div>
<div class="passport-grid">
<div class="passport-card">
<h3>Progress</h3>
<div class="passport-stat"><span>Quests completed</span><strong>{len(passport.completed_repo_ids)}</strong></div>
<div class="passport-stat"><span>Tracks explored</span><strong>{explored} / {total_tracks}</strong></div>
<div class="passport-stat"><span>Suggested next track</span><strong>{_escape(next_track)}</strong></div>
</div>
<div class="passport-card">
<h3>Badges</h3>
<div class="badge-row">{badge_html}</div>
</div>
</div>
<div class="passport-grid passport-grid-wide">
<div class="passport-card">
<h3>Track Progress</h3>
<div class="passport-zones">
{''.join(track_lines)}
</div>
</div>
<div class="passport-card">
<h3>Recently completed</h3>
<ul class="passport-list">
{completed_list}
</ul>
</div>
</div>
<div class="passport-card">
<h3>Community reflections</h3>
<ul class="passport-list">
{feedback_list}
</ul>
</div>
</div>
"""