mirror of
https://github.com/tiennm99/serena.git
synced 2026-10-13 10:16:00 +00:00
Dashboard: Improve handling of read news (saving each id individually)
Fixes #1338
This commit is contained in:
1 parent
505d520c80
commit
e1b29b8c28
3 files changed
+63
-14
No files matched your search
@@ -10,6 +10,9 @@ Status of the `main` branch. Changes prior to the next official version change w
|
||||
- Fix: When scanning for `.gitignore` files, the presence of files that could not be made relative
|
||||
to the project root would cause the scan to fail. #1317
|
||||
|
||||
Dashboard:
|
||||
- Fix handling of read news, saving each read news entry separately #1338
|
||||
|
||||
JetBrains:
|
||||
- Improve handling of `relative_path` parameter
|
||||
- Improve its documentation to avoid usage errors
|
||||
|
||||
@@ -83,7 +83,11 @@ class SerenaPaths:
|
||||
If a name of a mode matches a name of a mode in SERENAS_OWN_MODES_YAML_DIR,
|
||||
the user mode will override the default mode definition.
|
||||
"""
|
||||
self.news_snippet_id_file: str = os.path.join(self.serena_user_home_dir, "last_read_news_snippet_id.txt")
|
||||
self.news_legacy_last_read_id_file: str = os.path.join(self.serena_user_home_dir, "last_read_news_snippet_id.txt")
|
||||
"""
|
||||
file containing the ID of the last read news snippet
|
||||
"""
|
||||
self.news_read_items_file: str = os.path.join(self.serena_user_home_dir, "news_read.pkl")
|
||||
"""
|
||||
file containing the ID of the last read news snippet
|
||||
"""
|
||||
|
||||
+55
-13
@@ -15,6 +15,7 @@ from flask import Flask, Response, redirect, request, send_from_directory
|
||||
from PIL import Image
|
||||
from pydantic import BaseModel
|
||||
from sensai.util import logging
|
||||
from sensai.util.pickle import dump_pickle, load_pickle
|
||||
|
||||
from serena.analytics import ToolUsageStats
|
||||
from serena.config.serena_config import SerenaConfig, SerenaPaths
|
||||
@@ -132,6 +133,54 @@ class QueuedExecution(BaseModel):
|
||||
)
|
||||
|
||||
|
||||
class ReadNews:
|
||||
def __init__(self, read_ids: list[str], legacy_last_read_id: str | None = None):
|
||||
self._read_ids = set(read_ids)
|
||||
self._legacy_last_read_id = legacy_last_read_id
|
||||
|
||||
@staticmethod
|
||||
def load() -> "ReadNews":
|
||||
read_news_path = SerenaPaths().news_read_items_file
|
||||
legacy_last_read_id_path = SerenaPaths().news_legacy_last_read_id_file
|
||||
|
||||
def load_legacy_last_read_id() -> str | None:
|
||||
if not os.path.exists(legacy_last_read_id_path):
|
||||
return None
|
||||
with open(legacy_last_read_id_path, encoding="utf-8") as f:
|
||||
last_read_news_id = f.read().strip()
|
||||
if last_read_news_id == "20262103":
|
||||
last_read_news_id = "20260321" # fix originally misnamed news id
|
||||
return last_read_news_id
|
||||
|
||||
if os.path.exists(read_news_path):
|
||||
return load_pickle(read_news_path)
|
||||
else:
|
||||
instance = ReadNews(read_ids=[], legacy_last_read_id=load_legacy_last_read_id())
|
||||
instance._save()
|
||||
try:
|
||||
os.unlink(legacy_last_read_id_path)
|
||||
except:
|
||||
pass
|
||||
return instance
|
||||
|
||||
def _save(self) -> None:
|
||||
dump_pickle(self, SerenaPaths().news_read_items_file)
|
||||
|
||||
def is_read(self, identifier: str) -> bool:
|
||||
if identifier in self._read_ids:
|
||||
return True
|
||||
if self._legacy_last_read_id is not None and identifier <= self._legacy_last_read_id:
|
||||
return True
|
||||
return False
|
||||
|
||||
def mark_read(self, identifier: str) -> None:
|
||||
"""
|
||||
Marks the given news snippet as read, saving the new state to disk
|
||||
"""
|
||||
self._read_ids.add(identifier)
|
||||
self._save()
|
||||
|
||||
|
||||
class SerenaDashboardAPI:
|
||||
log = logging.getLogger(__qualname__)
|
||||
|
||||
@@ -150,6 +199,7 @@ class SerenaDashboardAPI:
|
||||
self._loaded_news: dict[str, str] = {}
|
||||
self._news_ready = threading.Event()
|
||||
self._setup_routes()
|
||||
self._read_news = ReadNews.load()
|
||||
# Fetch remote news in background on startup (non-blocking)
|
||||
threading.Thread(target=self._fetch_news, daemon=True).start()
|
||||
|
||||
@@ -362,7 +412,6 @@ class SerenaDashboardAPI:
|
||||
self._news_ready.wait()
|
||||
all_news = self._loaded_news
|
||||
|
||||
# Filter news items by installation date
|
||||
serena_config_creation_date = SerenaConfig.get_config_file_creation_date()
|
||||
if serena_config_creation_date is None:
|
||||
# should not normally happen, since config file should exist when the dashboard is started
|
||||
@@ -370,17 +419,12 @@ class SerenaDashboardAPI:
|
||||
log.error("Serena config file not found when starting the dashboard")
|
||||
return {}
|
||||
serena_config_creation_date = serena_config_creation_date.strftime("%Y%m%d")
|
||||
# Only include news items published on or after the installation date
|
||||
|
||||
# filter for news after the installation date
|
||||
post_installation_news = {k: v for k, v in all_news.items() if k >= serena_config_creation_date}
|
||||
|
||||
news_snippet_id_file = SerenaPaths().news_snippet_id_file
|
||||
if not os.path.exists(news_snippet_id_file):
|
||||
return post_installation_news
|
||||
with open(news_snippet_id_file, encoding="utf-8") as f:
|
||||
last_read_news_id = f.read().strip()
|
||||
if last_read_news_id == "20262103":
|
||||
last_read_news_id = "20260321" # fix originally misnamed news id
|
||||
return {k: v for k, v in post_installation_news.items() if k > last_read_news_id}
|
||||
# read unread news
|
||||
return {k: v for k, v in post_installation_news.items() if not self._read_news.is_read(k)}
|
||||
|
||||
try:
|
||||
unread_news = _fetch_unread_news()
|
||||
@@ -393,9 +437,7 @@ class SerenaDashboardAPI:
|
||||
try:
|
||||
request_data = request.get_json()
|
||||
news_snippet_id = str(request_data.get("news_snippet_id"))
|
||||
news_snippet_id_file = SerenaPaths().news_snippet_id_file
|
||||
with open(news_snippet_id_file, "w", encoding="utf-8") as f:
|
||||
f.write(news_snippet_id)
|
||||
self._read_news.mark_read(news_snippet_id)
|
||||
return {"status": "success", "message": f"Marked news snippet {news_snippet_id} as read"}
|
||||
except Exception as e:
|
||||
return {"status": "error", "message": str(e)}
|
||||
|
||||
Reference in new issue
Block a user