File size: 4,874 Bytes
0733aae
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
"""
Knowledge Universe โ€” HTML Format Adapter
Rich HTML cards for UI rendering.
Each format gets its own card layout:
  video โ†’ thumbnail + play button
  jupyter โ†’ code preview + Colab link
  dataset โ†’ stats + download button
  podcast โ†’ artwork + episode count
  pdf โ†’ page count + download button
  github โ†’ stars + clone button
"""

from typing import Any
from src.api.models import Source, SourceFormat
from src.format_adapters.base_adapter import BaseFormatAdapter


class HTMLFormatAdapter(BaseFormatAdapter):
    """
    Produces rich HTML cards per source type.
    Used by the Streamlit demo and any HTML-consuming client.
    """

    FORMAT_ICONS = {
        "pdf":        "๐Ÿ“„",
        "video":      "โ–ถ๏ธ",
        "jupyter":    "๐Ÿ““",
        "github":     "๐Ÿ™",
        "dataset":    "๐Ÿ“Š",
        "podcast":    "๐ŸŽง",
        "audio":      "๐Ÿ”Š",
        "html":       "๐ŸŒ",
        "epub":       "๐Ÿ“š",
        "markdown":   "๐Ÿ“",
        "sandbox":    "๐ŸŽฎ",
        "simulation": "๐Ÿ”ฌ",
        "knowledge_graph": "๐Ÿ•ธ๏ธ",
        "problem_set":"๐Ÿ“",
        "flashcards": "๐Ÿƒ",
        "3d_model":   "๐ŸงŠ",
        "map":        "๐Ÿ—บ๏ธ",
        "timeseries": "๐Ÿ“ˆ",
        "stackoverflow": "๐Ÿ’ฌ",
        "colab":      "โšก",
    }

    def transform(self, source: Source) -> str:
        fmt = source.formats[0].value if source.formats else "html"
        icon = self.FORMAT_ICONS.get(fmt, "๐Ÿ”—")
        stars = "โญ" * source.difficulty

        access_links = "".join([
            f'<a href="{link.url}" target="_blank" '
            f'style="background:#0066cc;color:white;padding:6px 12px;'
            f'border-radius:4px;text-decoration:none;margin-right:8px;font-size:12px;">'
            f'{link.type.upper()}</a>'
            for link in source.links[:2]
        ])

        format_badges = "".join([
            f'<span style="background:#e8f0fe;color:#1a73e8;padding:2px 8px;'
            f'border-radius:12px;font-size:11px;margin-right:4px;">{f.value}</span>'
            for f in source.formats[:4]
        ])

        # Platform-specific extras
        extras = self._platform_extras(source)

        quality_color = (
            "#22c55e" if source.quality_score >= 7.5 else
            "#f59e0b" if source.quality_score >= 5.0 else
            "#ef4444"
        )

        thumbnail_html = (
            f'<img src="{source.thumbnail_url}" style="width:100%;height:140px;'
            f'object-fit:cover;border-radius:6px 6px 0 0;" />'
            if source.thumbnail_url else ""
        )

        html = f"""
<div style="border:1px solid #e5e7eb;border-radius:8px;margin:8px 0;
            font-family:Arial,sans-serif;overflow:hidden;max-width:480px;">
  {thumbnail_html}
  <div style="padding:12px;">
    <div style="display:flex;justify-content:space-between;align-items:flex-start;">
      <span style="font-size:11px;color:#6b7280;text-transform:uppercase;
                   letter-spacing:0.5px;">{icon} {source.source_platform}</span>
      <span style="background:{quality_color};color:white;padding:2px 8px;
                   border-radius:12px;font-size:12px;font-weight:bold;">
        {source.quality_score:.1f}/10
      </span>
    </div>
    <h3 style="margin:6px 0;font-size:14px;line-height:1.4;color:#111827;">
      {source.title[:80]}{'โ€ฆ' if len(source.title) > 80 else ''}
    </h3>
    <p style="margin:4px 0;font-size:12px;color:#374151;line-height:1.5;">
      {source.summary[:200]}{'โ€ฆ' if len(source.summary) > 200 else ''}
    </p>
    <div style="margin:8px 0;">{format_badges}</div>
    <div style="display:flex;justify-content:space-between;
                font-size:11px;color:#6b7280;margin:4px 0;">
      <span>Difficulty: {stars}</span>
      <span>{'๐Ÿ”“ Open' if source.open_access else '๐Ÿ”’ Restricted'}</span>
    </div>
    {extras}
    <div style="margin-top:10px;">{access_links}</div>
  </div>
</div>"""
        return html

    def _platform_extras(self, source: Source) -> str:
        """Platform-specific metadata rows."""
        extras = []

        if source.stars is not None:
            extras.append(f"โญ {source.stars:,} stars")
        if source.views is not None:
            extras.append(f"๐Ÿ‘ {source.views:,} views")
        if source.downloads is not None:
            extras.append(f"โฌ‡ {source.downloads:,} downloads")
        if source.duration_seconds:
            mins = source.duration_seconds // 60
            extras.append(f"โฑ {mins} min")
        if source.page_count:
            extras.append(f"๐Ÿ“ƒ {source.page_count} pages")
        if source.citation_count:
            extras.append(f"๐Ÿ“š {source.citation_count} citations")

        if not extras:
            return ""

        items = " ยท ".join(extras)
        return f'<div style="font-size:11px;color:#6b7280;margin:4px 0;">{items}</div>'