Added download stats to rngit

This commit is contained in:
Mark Qvist
2026-05-15 20:12:07 +02:00
parent f3f4d9bca3
commit c92872a81b
2 changed files with 116 additions and 14 deletions
+14 -1
View File
@@ -1116,6 +1116,8 @@ class NomadNetworkNode():
f_peak = stats["fetches"]["peak"]
p_total = stats["pushes"]["total"]
p_peak = stats["pushes"]["peak"]
d_total = stats["downloads_combined"]["total"]
d_peak = stats["downloads_combined"]["peak"]
content_parts.append(f"\n`F66dViews`f : {v_total:>5} total {self.CLR_DIM}(peak: {v_peak:>3})`f\n")
content_parts.append(f"`F0a0Fetches`f : {f_total:>5} total {self.CLR_DIM}(peak: {f_peak:>3})\n`f")
@@ -1140,6 +1142,12 @@ class NomadNetworkNode():
content_parts.append("\n")
content_parts.append(self.render_chart(stats["pushes"]["daily"], stats["timeline_labels"], color="aa0"))
content_parts.append("\n")
if d_total > 0:
content_parts.append(self.m_heading(f"Downloads", 2))
content_parts.append("\n")
content_parts.append(self.render_chart(stats["downloads_combined"]["daily"], stats["timeline_labels"], color="a22"))
content_parts.append("\n")
if stats["activity_score"] > 0:
content_parts.append(self.m_heading("Combined Activity", 2))
@@ -1604,6 +1612,7 @@ class NomadNetworkNode():
RNS.log(f"Artifact file resolved for artifact request {group_name}/{repo_name}/{tag}/{artifact}", RNS.LOG_DEBUG)
self.owner.release_download_succeeded(group_name, repo_name, remote_identity)
return [open(artifact_path, "rb"), {"name": artifact.encode("utf-8")}]
def serve_download(self, path, data, request_id, link_id, remote_identity, requested_at):
@@ -1641,7 +1650,10 @@ class NomadNetworkNode():
else:
stream = self.get_blob_stream(repo_path, resolved_ref, file_path)
if stream is not None: return [stream, {"name": file_name.encode("utf-8")}]
if stream is not None:
self.owner.download_succeeded(group_name, repo_name, remote_identity)
return [stream, {"name": file_name.encode("utf-8")}]
else:
RNS.log(f"Could not resolve blob stream for download request {group_name}/{repo_name}/{ref}/{file_path}", RNS.LOG_WARNING)
return None
@@ -1706,6 +1718,7 @@ class NomadNetworkNode():
if content:
if fmt == "micron": file_name = f"{title}.mu"
else: file_name = f"{title}.md"
self.owner.download_succeeded(group_name, repo_name, remote_identity)
return [file_name, content.encode("utf-8")]
return None
+102 -13
View File
@@ -2976,6 +2976,13 @@ class ReticulumGitNode():
RNS.log(f"Error setting permissions: {e}", RNS.LOG_ERROR)
return self.RES_REMOTE_FAIL.to_bytes(1, "big") + b"Error setting permissions"
###################
# Node Statistics #
###################
STATS_INIT_REPO = {"view": {}, "fetch": {}, "push": {}, "download": {}, "release_download": {}}
STATS_INIT_GROUP = {"view": {}, "repositories": {}}
def repository_stats(self, remote_identity, group_name, repository_name, lookback_days=14):
if not self.resolve_permission(remote_identity, group_name, repository_name, self.PERM_STATS): return None
else:
@@ -2995,9 +3002,12 @@ class ReticulumGitNode():
repo_stats = { "group": group_name, "repository": repository_name,
"lookback_days": lookback_days, "date_range": f"{day_labels[0]} - {day_labels[-1]}",
"days": days, "day_labels": day_labels, "timeline_labels": timeline_labels,
"views": {"daily": [], "total": 0, "peak": 0, "peak_day": None},
"fetches": {"daily": [], "total": 0, "peak": 0, "peak_day": None},
"pushes": {"daily": [], "total": 0, "peak": 0, "peak_day": None} }
"views": {"daily": [], "total": 0, "peak": 0, "peak_day": None},
"fetches": {"daily": [], "total": 0, "peak": 0, "peak_day": None},
"pushes": {"daily": [], "total": 0, "peak": 0, "peak_day": None},
"downloads": {"daily": [], "total": 0, "peak": 0, "peak_day": None},
"release_downloads": {"daily": [], "total": 0, "peak": 0, "peak_day": None},
"downloads_combined": {"daily": [], "total": 0, "peak": 0, "peak_day": None} }
group_stats = self.stats.get("groups", {}).get(group_name, {})
repo_data = group_stats.get("repositories", {}).get(repository_name, {})
@@ -3029,9 +3039,38 @@ class ReticulumGitNode():
repo_stats["pushes"]["peak"] = count
repo_stats["pushes"]["peak_day"] = day
total_score = ( repo_stats["views"]["total"] * 0.2 +
repo_stats["fetches"]["total"] * 2 +
repo_stats["pushes"]["total"] * 5 )
download_stats = repo_data.get("download", {})
for day in days:
count = download_stats.get(day, 0)
repo_stats["downloads"]["daily"].append(count)
repo_stats["downloads"]["total"] += count
if count > repo_stats["downloads"]["peak"]:
repo_stats["downloads"]["peak"] = count
repo_stats["downloads"]["peak_day"] = day
release_download_stats = repo_data.get("release_download", {})
for day in days:
count = release_download_stats.get(day, 0)
repo_stats["release_downloads"]["daily"].append(count)
repo_stats["release_downloads"]["total"] += count
if count > repo_stats["release_downloads"]["peak"]:
repo_stats["release_downloads"]["peak"] = count
repo_stats["release_downloads"]["peak_day"] = day
for day in days:
count = download_stats.get(day, 0) + release_download_stats.get(day, 0)
repo_stats["downloads_combined"]["daily"].append(count)
repo_stats["downloads_combined"]["total"] += count
if count > repo_stats["downloads_combined"]["peak"]:
repo_stats["downloads_combined"]["peak"] = count
repo_stats["downloads_combined"]["peak_day"] = day
view_total = repo_stats["views"]["total"] + repo_stats["downloads"]["total"] + repo_stats["release_downloads"]["total"]
fetch_total = repo_stats["fetches"]["total"]
push_total = repo_stats["pushes"]["total"]
total_score = ( view_total * 0.2 +
fetch_total * 2.0 +
push_total * 5 )
repo_stats["activity_score"] = int(total_score)
@@ -3078,6 +3117,16 @@ class ReticulumGitNode():
if self.stats_enabled:
if group_name and repository_name: self.record_push(group_name, repository_name)
def download_succeeded(self, group_name, repository_name, remote_identity):
if remote_identity and remote_identity.hash in self.stats_ignored: return
if self.stats_enabled:
if group_name and repository_name: self.record_download(group_name, repository_name)
def release_download_succeeded(self, group_name, repository_name, remote_identity):
if remote_identity and remote_identity.hash in self.stats_ignored: return
if self.stats_enabled:
if group_name and repository_name: self.record_release_download(group_name, repository_name)
def _get_day(self):
timefmt = "%Y-%m-%d"
timestamp = time.localtime(time.time())
@@ -3100,7 +3149,8 @@ class ReticulumGitNode():
try:
with self.stats_lock:
day = self._get_day()
if not group_name in self.stats["groups"]: self.stats["groups"][group_name] = {"view": {}, "repositories": {}}
if not group_name in self.stats["groups"]: self.stats["groups"][group_name] = self.STATS_INIT_GROUP
if not "view" in self.stats["groups"][group_name]: self.stats["groups"][group_name]["view"] = {}
stats = self.stats["groups"][group_name]["view"]
if not day in stats: stats[day] = 0
@@ -3115,9 +3165,10 @@ class ReticulumGitNode():
try:
with self.stats_lock:
day = self._get_day()
if not group_name in self.stats["groups"]: self.stats["groups"][group_name] = {"view": {}, "repositories": {}}
if not group_name in self.stats["groups"]: self.stats["groups"][group_name] = self.STATS_INIT_GROUP
repos = self.stats["groups"][group_name]["repositories"]
if not repository_name in repos: repos[repository_name] = {"view": {}, "fetch": {}, "push": {}}
if not repository_name in repos: repos[repository_name] = self.STATS_INIT_REPO
if not "view" in repos[repository_name]: repos[repository_name]["view"] = {}
stats = repos[repository_name]["view"]
if not day in stats: stats[day] = 0
@@ -3132,9 +3183,10 @@ class ReticulumGitNode():
try:
with self.stats_lock:
day = self._get_day()
if not group_name in self.stats["groups"]: self.stats["groups"][group_name] = {"view": {}, "repositories": {}}
if not group_name in self.stats["groups"]: self.stats["groups"][group_name] = self.STATS_INIT_GROUP
repos = self.stats["groups"][group_name]["repositories"]
if not repository_name in repos: repos[repository_name] = {"view": {}, "fetch": {}, "push": {}}
if not repository_name in repos: repos[repository_name] = self.STATS_INIT_REPO
if not "fetch" in repos[repository_name]: repos[repository_name]["fetch"] = {}
stats = repos[repository_name]["fetch"]
if not day in stats: stats[day] = 0
@@ -3149,9 +3201,10 @@ class ReticulumGitNode():
try:
with self.stats_lock:
day = self._get_day()
if not group_name in self.stats["groups"]: self.stats["groups"][group_name] = {"view": {}, "repositories": {}}
if not group_name in self.stats["groups"]: self.stats["groups"][group_name] = self.STATS_INIT_GROUP
repos = self.stats["groups"][group_name]["repositories"]
if not repository_name in repos: repos[repository_name] = {"view": {}, "fetch": {}, "push": {}}
if not repository_name in repos: repos[repository_name] = self.STATS_INIT_REPO
if not "push" in repos[repository_name]: repos[repository_name]["push"] = {}
stats = repos[repository_name]["push"]
if not day in stats: stats[day] = 0
@@ -3161,6 +3214,42 @@ class ReticulumGitNode():
threading.Thread(target=job, daemon=True).start()
def record_download(self, group_name, repository_name):
def job():
try:
with self.stats_lock:
day = self._get_day()
if not group_name in self.stats["groups"]: self.stats["groups"][group_name] = self.STATS_INIT_GROUP
repos = self.stats["groups"][group_name]["repositories"]
if not repository_name in repos: repos[repository_name] = self.STATS_INIT_REPO
if not "download" in repos[repository_name]: repos[repository_name]["download"] = {}
stats = repos[repository_name]["download"]
if not day in stats: stats[day] = 0
stats[day] += 1
except Exception as e: RNS.log(f"Error while recording download stats: {e}", RNS.LOG_ERROR)
threading.Thread(target=job, daemon=True).start()
def record_release_download(self, group_name, repository_name):
def job():
try:
with self.stats_lock:
day = self._get_day()
if not group_name in self.stats["groups"]: self.stats["groups"][group_name] = self.STATS_INIT_GROUP
repos = self.stats["groups"][group_name]["repositories"]
if not repository_name in repos: repos[repository_name] = self.STATS_INIT_REPO
if not "release_download" in repos[repository_name]: repos[repository_name]["release_download"] = {}
stats = repos[repository_name]["release_download"]
if not day in stats: stats[day] = 0
stats[day] += 1
except Exception as e: RNS.log(f"Error while recording release download stats: {e}", RNS.LOG_ERROR)
threading.Thread(target=job, daemon=True).start()
__default_rngit_config__ = '''# This is the default rngit config file.
# You will need to edit it to specify repository locations and