mirror of
https://github.com/nubenetes/awesome-kubernetes.git
synced 2026-08-25 07:57:25 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7d845fab3e | ||
|
|
fbfa85c2d3 | ||
|
|
dc4591fb3e | ||
|
|
96706aff90 | ||
|
|
f3cb3a1ff3 | ||
|
|
cc6633dcdd | ||
|
|
759c10ad2c | ||
|
|
58601de338 | ||
|
|
11ebda6187 | ||
|
|
227dc23933 | ||
|
|
56db4d5e53 | ||
|
|
e332af2ea8 | ||
|
|
9d3321d0f0 | ||
|
|
9ed1318a9c | ||
|
|
91da9c8495 | ||
|
|
c9a7b2a8cb | ||
|
|
ad59cb75f0 | ||
|
|
88f891fd4e | ||
|
|
61733937be | ||
|
|
10c70025a2 | ||
|
|
cff4903166 | ||
|
|
16ad26f427 | ||
|
|
3f3a86b62c | ||
|
|
239dd05e6f | ||
|
|
932770b89b | ||
|
|
c8ca878f66 | ||
|
|
64c6e9ea18 | ||
|
|
53b19f1b09 | ||
|
|
f6e60a6487 | ||
|
|
5bfcaf1b03 | ||
|
|
b8144d9253 | ||
|
|
b321169459 | ||
|
|
34610f4671 | ||
|
|
f50fdffd64 | ||
|
|
f5fae99a38 | ||
|
|
ebae3c6e27 | ||
|
|
a8941afd24 |
@@ -60,7 +60,20 @@ jobs:
|
||||
- name: Installation of Dependencies
|
||||
run: |
|
||||
pip install --no-cache-dir pydantic PyGithub httpx fake-useragent pytz python-dotenv pyyaml tenacity
|
||||
|
||||
|
||||
- name: Get current date for digest cache key
|
||||
id: digest-date
|
||||
run: echo "date=$(date -u +%Y-%m-%d)" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Restore News Digest Cache
|
||||
id: cache-digest
|
||||
uses: actions/cache/restore@v5
|
||||
with:
|
||||
path: data/news_digest.json
|
||||
key: news-digest-${{ steps.digest-date.outputs.date }}
|
||||
restore-keys: |
|
||||
news-digest-
|
||||
|
||||
- name: Execute Video Portal Generator
|
||||
env:
|
||||
PYTHONPATH: ${{ github.workspace }}
|
||||
@@ -87,6 +100,7 @@ jobs:
|
||||
python -u -m src.enrichment || echo "Enrichment pipeline skipped (no token or error)"
|
||||
|
||||
- name: Generate News Digest
|
||||
if: steps.cache-digest.outputs.cache-hit != 'true'
|
||||
env:
|
||||
PYTHONPATH: ${{ github.workspace }}
|
||||
GEMINI_API_KEY_1: ${{ secrets.GEMINI_API_KEY_1 }}
|
||||
@@ -94,6 +108,19 @@ jobs:
|
||||
run: |
|
||||
python -u -m src.news_digest || echo "News digest generation skipped (no API key or error)"
|
||||
|
||||
- name: Generate RSS Feed
|
||||
env:
|
||||
PYTHONPATH: ${{ github.workspace }}
|
||||
run: |
|
||||
python -u -m src.rss_generator || echo "RSS generation skipped"
|
||||
|
||||
- name: Save News Digest Cache
|
||||
if: steps.cache-digest.outputs.cache-hit != 'true' && hashFiles('data/news_digest.json') != ''
|
||||
uses: actions/cache/save@v5
|
||||
with:
|
||||
path: data/news_digest.json
|
||||
key: news-digest-${{ steps.digest-date.outputs.date }}
|
||||
|
||||
- name: Run V2 Publisher (Render-Only)
|
||||
env:
|
||||
PYTHONPATH: ${{ github.workspace }}
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
name: 09. Weekly Intelligence Digest
|
||||
|
||||
on:
|
||||
schedule:
|
||||
# Every Monday at 06:00 UTC (08:00 Madrid time)
|
||||
- cron: '0 6 * * 1'
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
|
||||
concurrency:
|
||||
group: develop-git-write-lock
|
||||
cancel-in-progress: false
|
||||
|
||||
jobs:
|
||||
weekly-digest:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Repository Synchronization
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
ref: develop
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Python 3.11 Environment Provisioning
|
||||
uses: actions/setup-python@v6
|
||||
with:
|
||||
python-version: '3.11'
|
||||
cache: 'pip'
|
||||
|
||||
- name: Install Dependencies
|
||||
run: pip install -r requirements.txt
|
||||
|
||||
- name: Get current date for digest cache key
|
||||
id: digest-date
|
||||
run: echo "date=$(date -u +%Y-%m-%d)" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Restore News Digest Cache
|
||||
id: cache-digest
|
||||
uses: actions/cache/restore@v5
|
||||
with:
|
||||
path: data/news_digest.json
|
||||
key: news-digest-${{ steps.digest-date.outputs.date }}
|
||||
restore-keys: |
|
||||
news-digest-
|
||||
|
||||
- name: Generate News Digest (Gemini)
|
||||
if: steps.cache-digest.outputs.cache-hit != 'true'
|
||||
env:
|
||||
PYTHONPATH: ${{ github.workspace }}
|
||||
GEMINI_API_KEY_1: ${{ secrets.GEMINI_API_KEY_1 }}
|
||||
GEMINI_API_KEY_2: ${{ secrets.GEMINI_API_KEY_2 }}
|
||||
run: |
|
||||
python -u -m src.news_digest
|
||||
|
||||
- name: Save News Digest Cache
|
||||
if: steps.cache-digest.outputs.cache-hit != 'true' && hashFiles('data/news_digest.json') != ''
|
||||
uses: actions/cache/save@v5
|
||||
with:
|
||||
path: data/news_digest.json
|
||||
key: news-digest-${{ steps.digest-date.outputs.date }}
|
||||
|
||||
- name: Generate RSS Feed
|
||||
env:
|
||||
PYTHONPATH: ${{ github.workspace }}
|
||||
run: |
|
||||
python -u -m src.rss_generator || echo "RSS generation skipped"
|
||||
|
||||
- name: Render V2 Portal (Digest Pages Only)
|
||||
env:
|
||||
PYTHONPATH: ${{ github.workspace }}
|
||||
PYTHONUNBUFFERED: "1"
|
||||
run: |
|
||||
python -u -m src.v2_optimizer --render-only
|
||||
|
||||
- name: Commit and Push Weekly Digest
|
||||
run: |
|
||||
git config --global user.name "Nubenetes Bot"
|
||||
git config --global user.email "bot@nubenetes.com"
|
||||
git add data/news_digest.json v2-docs/tech-digest.md v2-docs/industry-digest.md v2-docs/feed.xml || true
|
||||
if git diff --staged --quiet; then
|
||||
echo "No digest changes to commit."
|
||||
else
|
||||
git commit -m "feat: weekly intelligence digest update [skip ci]"
|
||||
for i in {1..3}; do
|
||||
git pull origin develop --rebase && git push origin develop && break || sleep 10
|
||||
done
|
||||
fi
|
||||
|
||||
- name: Trigger V2 Publisher
|
||||
uses: peter-evans/repository-dispatch@v3
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
event-type: weekly-digest-ready
|
||||
@@ -135,14 +135,14 @@ Additionally, as of May 2026, Nubenetes has reached the **Platinum Operational T
|
||||
## 2. Repository Metrics and Evolution
|
||||
|
||||
### 2.1. The "Heart" of Nubenetes
|
||||
(Stats as of 2026-06-18)
|
||||
(Stats as of 2026-06-19)
|
||||
|
||||
<!-- HEART_STATS_START -->
|
||||
| Metric | Value |
|
||||
| :--- | :--- |
|
||||
| **Total Technical Resources (Links)** | **18647+** |
|
||||
| **Specialized MD Pages** | **162** |
|
||||
| **Total Commits** | **6013+** |
|
||||
| **Total Commits** | **6082+** |
|
||||
| **Primary AI Engine** | **Google Gemini (Agentic)** |
|
||||
<!-- HEART_STATS_END -->
|
||||
|
||||
@@ -180,7 +180,7 @@ The growth of Nubenetes reflects the acceleration of the Cloud Native ecosystem.
|
||||
| 6 | 2023 | 30 | 123 | Maintenance & Refinement |
|
||||
| 7 | 2024 | 53 | 218 | Curation Strategy Pivot |
|
||||
| 8 | 2025 | 5 | 20 | Stability & Research Phase |
|
||||
| 9 | 2026 | 2454 | 10,135 | **Agentic AI Surge** (May 2026 Inception) |
|
||||
| 9 | 2026 | 2523 | 10,419 | **Agentic AI Surge** (May 2026 Inception) |
|
||||
<!-- ANNUAL_GROWTH_END -->
|
||||
|
||||
<!-- ANNUAL_CHART_START -->
|
||||
@@ -196,8 +196,8 @@ xychart-beta
|
||||
title "Nubenetes Annual Growth Metrics (2018–2026)"
|
||||
x-axis ["2018", "2019", "2020", "2021", "2022", "2023", "2024", "2025", "2026"]
|
||||
y-axis "Volume (Commits / Estimated New Refs)" 0 --> 11000
|
||||
bar [1445, 586, 8449, 2193, 1660, 123, 218, 20, 10135]
|
||||
bar [350, 142, 2046, 531, 402, 30, 53, 5, 2454]
|
||||
bar [1445, 586, 8449, 2193, 1660, 123, 218, 20, 10419]
|
||||
bar [350, 142, 2046, 531, 402, 30, 53, 5, 2523]
|
||||
```
|
||||
<!-- ANNUAL_CHART_END -->
|
||||
|
||||
@@ -207,7 +207,7 @@ xychart-beta
|
||||
| :--- | :---: | :---: | :--- |
|
||||
| 2026-04 | 25 | 103 | Active Curation |
|
||||
| 2026-05 | 2101 | 8,677 | **Agentic Inception (Gemini Era)** |
|
||||
| 2026-06 | 328 | 1,354 | Active Curation |
|
||||
| 2026-06 | 397 | 1,639 | Active Curation |
|
||||
<!-- MONTHLY_SURGE_END -->
|
||||
|
||||
### 2.4. Content Distribution and Semantic Clustering
|
||||
|
||||
Vendored
+78
@@ -352,6 +352,22 @@ a {
|
||||
border-color: #2dd4bf !important;
|
||||
}
|
||||
|
||||
.hero-badge-card--amber {
|
||||
border-color: rgba(245, 158, 11, 0.2);
|
||||
background: rgba(245, 158, 11, 0.02);
|
||||
}
|
||||
.hero-badge-card--amber:hover {
|
||||
background: rgba(245, 158, 11, 0.06) !important;
|
||||
box-shadow: 0 8px 24px rgba(245, 158, 11, 0.18);
|
||||
border-color: #f59e0b !important;
|
||||
}
|
||||
|
||||
.hero-badge-icon {
|
||||
font-size: 2rem;
|
||||
line-height: 1;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.hero-badge-title {
|
||||
font-weight: bold;
|
||||
font-size: 0.95rem;
|
||||
@@ -364,6 +380,56 @@ a {
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
/* Intelligence Digest mini-preview on index */
|
||||
.digest-preview {
|
||||
background: rgba(245, 158, 11, 0.04);
|
||||
border: 1px solid rgba(245, 158, 11, 0.15);
|
||||
border-radius: 12px;
|
||||
padding: 20px 24px;
|
||||
margin: 24px 0;
|
||||
}
|
||||
.digest-preview-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
.digest-preview-title {
|
||||
font-weight: 700;
|
||||
font-size: 1rem;
|
||||
color: var(--md-primary-fg-color);
|
||||
}
|
||||
.digest-preview-link {
|
||||
font-size: 0.8rem;
|
||||
color: #f59e0b;
|
||||
text-decoration: none;
|
||||
font-weight: 600;
|
||||
letter-spacing: 0.02em;
|
||||
}
|
||||
.digest-preview-link:hover { text-decoration: underline; }
|
||||
.digest-preview-list {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
.digest-preview-list li {
|
||||
padding: 5px 0;
|
||||
border-bottom: 1px solid rgba(245, 158, 11, 0.08);
|
||||
font-size: 0.875rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
.digest-preview-list li:last-child { border-bottom: none; }
|
||||
.digest-preview-cat {
|
||||
font-size: 0.7rem;
|
||||
color: #f59e0b;
|
||||
background: rgba(245, 158, 11, 0.1);
|
||||
border-radius: 4px;
|
||||
padding: 1px 6px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
/* Hero Showcase Image wrapper (4 cars in a container) */
|
||||
.hero-showcase-wrapper {
|
||||
margin: 24px auto;
|
||||
@@ -977,6 +1043,18 @@ input[type="text"] {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.trending-section__updated {
|
||||
font-size: 0.55em;
|
||||
font-weight: 500;
|
||||
padding: 2px 10px;
|
||||
border-radius: 20px;
|
||||
background: rgba(14, 165, 233, 0.12);
|
||||
color: var(--md-accent-fg-color);
|
||||
border: 1px solid rgba(14, 165, 233, 0.25);
|
||||
letter-spacing: 0.02em;
|
||||
}
|
||||
|
||||
.trending-grid {
|
||||
|
||||
+2
-2
@@ -71,7 +71,7 @@ def find_title_duplicates(inventory: Dict, threshold: float = 0.85) -> List[Tupl
|
||||
norm = normalize_title(title)
|
||||
if len(norm) < 10:
|
||||
continue
|
||||
entries.append((url, norm, entry.get("stars", 0)))
|
||||
entries.append((url, norm, entry.get("stars") or 0))
|
||||
|
||||
log_event(f"[Dedup] Building title index for {len(entries)} entries...")
|
||||
|
||||
@@ -105,7 +105,7 @@ def find_title_duplicates(inventory: Dict, threshold: float = 0.85) -> List[Tupl
|
||||
|
||||
def _entry_score(entry: Dict) -> Tuple:
|
||||
return (
|
||||
entry.get("stars", 0),
|
||||
entry.get("stars") or 0,
|
||||
1 if entry.get("ai_summary") else 0,
|
||||
1 if entry.get("hierarchy") else 0,
|
||||
len(entry.get("tags", [])),
|
||||
|
||||
@@ -306,6 +306,7 @@ async def detect_license_changes(inventory: Dict) -> List[Dict]:
|
||||
log_event("[License] No repos with stored licenses to check")
|
||||
return []
|
||||
|
||||
candidates = candidates[:MAX_REPOS_DEFAULT] # cap to same limit as activity enrichment
|
||||
log_event(f"[License] Checking {len(candidates)} repos for license changes")
|
||||
|
||||
changes: List[Dict] = []
|
||||
|
||||
+30
-3
@@ -182,11 +182,38 @@ class NewsDigestEngine:
|
||||
return None
|
||||
|
||||
def _get_entry_geo(self, entry: dict) -> str | None:
|
||||
"""Return the geo digest category if ``geo_region`` matches."""
|
||||
"""Return the geo digest category using geo_region field, falling back to URL TLD inference."""
|
||||
region = entry.get("geo_region", "")
|
||||
for geo_name, geo_val in self.GEO_CATEGORIES.items():
|
||||
if region == geo_val:
|
||||
return geo_name
|
||||
# Fallback: infer from URL TLD
|
||||
return self._infer_geo_from_url(entry.get("url", ""))
|
||||
|
||||
@staticmethod
|
||||
def _infer_geo_from_url(url: str) -> str | None:
|
||||
"""Infer geo category from URL TLD. Returns GEO_CATEGORIES key or None."""
|
||||
try:
|
||||
from urllib.parse import urlparse
|
||||
host = urlparse(url).hostname or ""
|
||||
# Ordered longest-first to avoid .uk matching before .co.uk
|
||||
tld_to_region = [
|
||||
(".com.au", "Asia-Pacific"), (".co.uk", "Europe"), (".co.jp", "Asia-Pacific"),
|
||||
(".co.kr", "Asia-Pacific"), (".com.br", "Americas"), (".com.mx", "Americas"),
|
||||
(".es", "España"), (".de", "Europe"), (".fr", "Europe"), (".it", "Europe"),
|
||||
(".pt", "Europe"), (".nl", "Europe"), (".be", "Europe"), (".se", "Europe"),
|
||||
(".dk", "Europe"), (".fi", "Europe"), (".no", "Europe"), (".ch", "Europe"),
|
||||
(".at", "Europe"), (".pl", "Europe"), (".cz", "Europe"), (".uk", "Europe"),
|
||||
(".ie", "Europe"), (".eu", "Europe"), (".cn", "Asia-Pacific"),
|
||||
(".jp", "Asia-Pacific"), (".kr", "Asia-Pacific"), (".sg", "Asia-Pacific"),
|
||||
(".in", "Asia-Pacific"), (".au", "Asia-Pacific"), (".nz", "Asia-Pacific"),
|
||||
(".ca", "Americas"), (".mx", "Americas"), (".br", "Americas"),
|
||||
]
|
||||
for tld, region in tld_to_region:
|
||||
if host.endswith(tld):
|
||||
return region
|
||||
except Exception:
|
||||
pass
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
@@ -319,8 +346,8 @@ class NewsDigestEngine:
|
||||
for cat_name, entries in category_pools.items():
|
||||
entries.sort(
|
||||
key=lambda x: (
|
||||
x.get("stars", 0),
|
||||
x.get("discovered_at", ""),
|
||||
x.get("stars") or 0,
|
||||
x.get("discovered_at") or "",
|
||||
),
|
||||
reverse=True,
|
||||
)
|
||||
|
||||
@@ -0,0 +1,111 @@
|
||||
"""RSS 2.0 feed generator for the Nubenetes Intelligence Digest.
|
||||
|
||||
Reads data/news_digest.json and writes v2-docs/feed.xml with the top
|
||||
items from the 3-month digest window across all tech categories.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import os
|
||||
from datetime import datetime
|
||||
from email.utils import format_datetime
|
||||
from xml.sax.saxutils import escape
|
||||
|
||||
from src.logger import log_event
|
||||
|
||||
DIGEST_PATH = "data/news_digest.json"
|
||||
OUTPUT_PATH = "v2-docs/feed.xml"
|
||||
FEED_TITLE = "Nubenetes Intelligence Digest"
|
||||
FEED_LINK = "https://nubenetes.com/"
|
||||
FEED_DESCRIPTION = "AI-curated top picks from the Cloud Native & Kubernetes ecosystem"
|
||||
FEED_LANGUAGE = "en"
|
||||
ITEMS_PER_FEED = 20
|
||||
|
||||
TECH_CATS = [
|
||||
"Kubernetes & Orchestration", "AI & Agents", "Security & Compliance",
|
||||
"CI/CD & GitOps", "Observability, SRE & Testing", "Infrastructure as Code",
|
||||
"Containers & Runtime", "Networking & Service Mesh", "Cloud Providers & FinOps",
|
||||
"MLOps & Data Science", "Data, Messaging & Storage",
|
||||
]
|
||||
|
||||
|
||||
def _rfc822(dt: datetime) -> str:
|
||||
return format_datetime(dt)
|
||||
|
||||
|
||||
def generate_rss() -> None:
|
||||
if not os.path.exists(DIGEST_PATH):
|
||||
log_event("[WARN] rss_generator: news_digest.json not found, skipping RSS generation")
|
||||
return
|
||||
|
||||
try:
|
||||
with open(DIGEST_PATH, "r", encoding="utf-8") as f:
|
||||
digest = json.load(f)
|
||||
except Exception as e:
|
||||
log_event(f"[WARN] rss_generator: failed to load digest: {str(e)[:100]}")
|
||||
return
|
||||
|
||||
period_data = digest.get("3_months", {})
|
||||
items: list[dict] = []
|
||||
|
||||
for cat in TECH_CATS:
|
||||
for entry in period_data.get(cat, []):
|
||||
items.append({**entry, "_cat": cat})
|
||||
|
||||
# Sort by impact then date
|
||||
impact_rank = {"critical": 3, "high": 2, "medium": 1}
|
||||
items.sort(
|
||||
key=lambda x: (impact_rank.get(x.get("impact", "medium"), 0), x.get("date", "")),
|
||||
reverse=True,
|
||||
)
|
||||
items = items[:ITEMS_PER_FEED]
|
||||
|
||||
build_date = _rfc822(datetime.utcnow())
|
||||
|
||||
lines = [
|
||||
'<?xml version="1.0" encoding="UTF-8"?>',
|
||||
'<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">',
|
||||
" <channel>",
|
||||
f" <title>{escape(FEED_TITLE)}</title>",
|
||||
f" <link>{FEED_LINK}</link>",
|
||||
f" <description>{escape(FEED_DESCRIPTION)}</description>",
|
||||
f" <language>{FEED_LANGUAGE}</language>",
|
||||
f" <lastBuildDate>{build_date}</lastBuildDate>",
|
||||
f' <atom:link href="{FEED_LINK}feed.xml" rel="self" type="application/rss+xml"/>',
|
||||
]
|
||||
|
||||
for item in items:
|
||||
title = escape(item.get("title", "Unknown"))
|
||||
url = item.get("url", "#")
|
||||
why = escape(item.get("why", ""))
|
||||
cat = escape(item.get("_cat", ""))
|
||||
impact = item.get("impact", "medium")
|
||||
date_str = item.get("date", "")
|
||||
try:
|
||||
pub_date = _rfc822(datetime.strptime(date_str, "%Y-%m-%d")) if date_str else build_date
|
||||
except Exception:
|
||||
pub_date = build_date
|
||||
|
||||
lines += [
|
||||
" <item>",
|
||||
f" <title>{title}</title>",
|
||||
f" <link>{url}</link>",
|
||||
f" <guid isPermaLink=\"true\">{url}</guid>",
|
||||
f" <pubDate>{pub_date}</pubDate>",
|
||||
f" <category>{cat}</category>",
|
||||
f" <description>[{impact.upper()}] {why}</description>",
|
||||
" </item>",
|
||||
]
|
||||
|
||||
lines += [" </channel>", "</rss>"]
|
||||
|
||||
os.makedirs(os.path.dirname(OUTPUT_PATH), exist_ok=True)
|
||||
with open(OUTPUT_PATH, "w", encoding="utf-8") as f:
|
||||
f.write("\n".join(lines) + "\n")
|
||||
|
||||
log_event(f"[INFO] rss_generator: wrote {len(items)} items to {OUTPUT_PATH}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
generate_rss()
|
||||
+25
-10
@@ -629,7 +629,7 @@ class V2VisionEngine:
|
||||
# 1. GitHub Objective Reality (Mandate 43)
|
||||
raw_gh = item.get("gh_stars", 0)
|
||||
gh_stars = int(raw_gh) if str(raw_gh).isdigit() else 0
|
||||
curator_stars = int(item.get("stars", 0))
|
||||
curator_stars = int(item.get("stars") or 0)
|
||||
|
||||
if gh_stars > 15000 or curator_stars >= 5:
|
||||
tags.add("[DE FACTO STANDARD]")
|
||||
@@ -639,7 +639,7 @@ class V2VisionEngine:
|
||||
if "[COMMUNITY-TOOL]" in tags: tags.remove("[COMMUNITY-TOOL]")
|
||||
|
||||
# 2. Type Mapping (AI based labels)
|
||||
res_type = item.get("resource_type", "Reference").lower()
|
||||
res_type = (item.get("resource_type") or "Reference").lower()
|
||||
if any(x in res_type for x in ["guide", "tutorial", "hands-on", "learning", "course"]):
|
||||
tags.add("[GUIDE]")
|
||||
if any(x in res_type for x in ["case study", "report", "whitepaper", "success story", "usage"]):
|
||||
@@ -753,7 +753,7 @@ class V2VisionEngine:
|
||||
current["__links__"].append(item)
|
||||
|
||||
def sort_rec(node):
|
||||
if "__links__" in node: node["__links__"].sort(key=lambda x: (-x.get("stars", 1), -(int(x["year"]) if str(x.get("year", "")).isdigit() else 0)))
|
||||
if "__links__" in node: node["__links__"].sort(key=lambda x: (-(x.get("stars") or 1), -(int(x["year"]) if str(x.get("year", "")).isdigit() else 0)))
|
||||
for k, v in node.items():
|
||||
if k != "__links__" and isinstance(v, dict): sort_rec(v)
|
||||
|
||||
@@ -953,8 +953,9 @@ class V2VisionEngine:
|
||||
geo_cats = ["Americas", "Europe", "España", "Asia-Pacific"]
|
||||
period_labels = {"3_months": "Last 3 Months", "6_months": "Last 6 Months", "12_months": "Last 12 Months"}
|
||||
|
||||
def render_digest_page(title, categories, digest_data):
|
||||
md = f"# {title}\n\n"
|
||||
def render_digest_page(title, categories, digest_data, search_boost=1):
|
||||
md = f"---\nsearch:\n boost: {search_boost}\n---\n\n"
|
||||
md += f"# {title}\n\n"
|
||||
md += "!!! tip \"Nubenetes Intelligence Digest\"\n"
|
||||
md += " AI-curated ranking of the most impactful resources, updated monthly.\n\n"
|
||||
for period_key, period_label in period_labels.items():
|
||||
@@ -976,11 +977,11 @@ class V2VisionEngine:
|
||||
md += "\n"
|
||||
return md
|
||||
|
||||
tech_md = render_digest_page("📊 Nubenetes Tech & Cloud Intelligence Digest", tech_cats, digest_data)
|
||||
tech_md = render_digest_page("📊 Nubenetes Tech & Cloud Intelligence Digest", tech_cats, digest_data, search_boost=2)
|
||||
with open(os.path.join(V2_DIR, "tech-digest.md"), "w", encoding="utf-8") as f:
|
||||
f.write(tech_md)
|
||||
|
||||
industry_md = render_digest_page("🌍 Nubenetes Industry & Geo Intelligence Digest", geo_cats, digest_data)
|
||||
industry_md = render_digest_page("🌍 Nubenetes Industry & Geo Intelligence Digest", geo_cats, digest_data, search_boost=2)
|
||||
with open(os.path.join(V2_DIR, "industry-digest.md"), "w", encoding="utf-8") as f:
|
||||
f.write(industry_md)
|
||||
|
||||
@@ -1006,7 +1007,14 @@ class V2VisionEngine:
|
||||
top_items = top_items[:6]
|
||||
|
||||
impact_icons = {"critical": "🔴", "high": "🟡", "medium": "🔵"}
|
||||
cards_html = '<div class="trending-section">\n<div class="trending-section__title">🔥 Trending Now — Cloud Native Intelligence</div>\n<div class="trending-grid">\n'
|
||||
try:
|
||||
digest_mtime = os.path.getmtime(digest_path)
|
||||
from datetime import datetime as _dt
|
||||
digest_updated = _dt.fromtimestamp(digest_mtime).strftime("%b %d, %Y")
|
||||
except Exception:
|
||||
digest_updated = ""
|
||||
updated_badge = f'<span class="trending-section__updated">Updated {digest_updated}</span>' if digest_updated else ""
|
||||
cards_html = f'<div class="trending-section">\n<div class="trending-section__title">🔥 Trending Now — Cloud Native Intelligence {updated_badge}</div>\n<div class="trending-grid">\n'
|
||||
for item in top_items:
|
||||
impact = item.get("impact", "medium")
|
||||
cards_html += (
|
||||
@@ -1025,7 +1033,7 @@ class V2VisionEngine:
|
||||
cards_html += '</div>\n</div>\n'
|
||||
pulse_md = cards_html
|
||||
else:
|
||||
trending_pool = sorted([dict(meta, url=url) for url, meta in self.inventory.items() if isinstance(meta, dict) and meta.get("stars", 0) >= 4], key=lambda x: (str(x.get("year", "0000")) if str(x.get("year", "")).isdigit() else "0000", -x.get("stars", 0)), reverse=True)
|
||||
trending_pool = sorted([dict(meta, url=url) for url, meta in self.inventory.items() if isinstance(meta, dict) and (meta.get("stars") or 0) >= 4], key=lambda x: (str(x.get("year", "0000")) if str(x.get("year", "")).isdigit() else "0000", -(x.get("stars") or 0)), reverse=True)
|
||||
pulse_md = "## The Agentic Pulse\n" + "\n".join([f"- **({l.get('year', 'N/A')})** [**=={nuclear_strip(l['title'])}==**]({l['url'].strip()}) {'🌟'*l.get('stars',3)}" for l in trending_pool[:5]])
|
||||
|
||||
# Calculate coverage for the index
|
||||
@@ -1093,6 +1101,13 @@ class V2VisionEngine:
|
||||
" <div class=\"hero-badge-subtitle\">Agentic Ecosystem</div>\n"
|
||||
" </div>\n"
|
||||
" </a>\n"
|
||||
" <a href=\"./tech-digest/\" style=\"text-decoration: none; color: inherit; display: block;\">\n"
|
||||
" <div class=\"hero-badge-card hero-badge-card--amber\">\n"
|
||||
" <div class=\"hero-badge-icon\">📊</div>\n"
|
||||
" <div class=\"hero-badge-title\">Intelligence Digest</div>\n"
|
||||
" <div class=\"hero-badge-subtitle\">Top picks · 3/6/12 months</div>\n"
|
||||
" </div>\n"
|
||||
" </a>\n"
|
||||
" <a href=\"./videos/\" style=\"text-decoration: none; color: inherit; display: block;\">\n"
|
||||
" <div class=\"hero-badge-card hero-badge-card--pink\">\n"
|
||||
" <img src=\"/images/video_hub_logo.png\" alt=\"Agentic Video Hub\"/>\n"
|
||||
@@ -1438,7 +1453,7 @@ class V2VisionEngine:
|
||||
md += f"<summary>{summary_text}</summary>\n\n"
|
||||
|
||||
# Sort links under this tag by impact stars and then by year
|
||||
sorted_links = sorted(by_tag[tag], key=lambda x: (-x.get("stars", 1), -(int(x["year"]) if str(x.get("year", "")).isdigit() else 0)))
|
||||
sorted_links = sorted(by_tag[tag], key=lambda x: (-(x.get("stars") or 1), -(int(x["year"]) if str(x.get("year", "")).isdigit() else 0)))
|
||||
|
||||
rendered_links = sorted_links[:100]
|
||||
for l in rendered_links:
|
||||
|
||||
@@ -39,6 +39,13 @@
|
||||
<div class="hero-badge-subtitle">Agentic Ecosystem</div>
|
||||
</div>
|
||||
</a>
|
||||
<a href="./tech-digest/" style="text-decoration: none; color: inherit; display: block;">
|
||||
<div class="hero-badge-card hero-badge-card--amber">
|
||||
<div class="hero-badge-icon">📊</div>
|
||||
<div class="hero-badge-title">Intelligence Digest</div>
|
||||
<div class="hero-badge-subtitle">Top picks · 3/6/12 months</div>
|
||||
</div>
|
||||
</a>
|
||||
<a href="./videos/" style="text-decoration: none; color: inherit; display: block;">
|
||||
<div class="hero-badge-card hero-badge-card--pink">
|
||||
<img src="/images/video_hub_logo.png" alt="Agentic Video Hub"/>
|
||||
|
||||
+4
-1
@@ -96,10 +96,13 @@ extra:
|
||||
version:
|
||||
provider: mike # Ready for version switching
|
||||
|
||||
extra_head:
|
||||
- '<link rel="alternate" type="application/rss+xml" title="Nubenetes Intelligence Digest" href="/feed.xml"/>'
|
||||
|
||||
extra_css:
|
||||
- https://fonts.googleapis.com/css2?family=Inter:wght@400;500;700&display=swap
|
||||
- static/extra.css
|
||||
- static/v2_elite.css?v=2.4.0
|
||||
- static/v2_elite.css?v=2.7.0
|
||||
|
||||
extra_javascript:
|
||||
- static/v2_filter.js
|
||||
|
||||
Reference in New Issue
Block a user