diff --git a/src/octoprint/plugins/announcements/__init__.py b/src/octoprint/plugins/announcements/__init__.py
index 366f5130..d7b1fa67 100644
--- a/src/octoprint/plugins/announcements/__init__.py
+++ b/src/octoprint/plugins/announcements/__init__.py
@@ -29,8 +29,12 @@ class AnnouncementPlugin(octoprint.plugin.AssetPlugin,
octoprint.plugin.TemplatePlugin):
def __init__(self):
- self._cached_channels = dict()
- self._cached_channels_mutex = threading.RLock()
+ self._cached_channel_configs = None
+ self._cached_channel_configs_mutex = threading.RLock()
+
+ from slugify import Slugify
+ self._slugify = Slugify()
+ self._slugify.safe_chars = "-_."
# StartupPlugin
@@ -88,13 +92,18 @@ class AnnouncementPlugin(octoprint.plugin.AssetPlugin,
@restricted_access
@admin_permission.require(403)
def get_channel_data(self):
+ from octoprint.settings import valid_boolean_trues
+
result = dict()
- channel_data = self._fetch_all_channels()
+ force = "force" in flask.request.values and flask.request.values["force"] in valid_boolean_trues
+
+ channel_data = self._fetch_all_channels(force=force)
+ channel_configs = self._get_channel_configs(force=force)
- channel_configs = self._get_channel_configs()
enabled = self._settings.get(["enabled_channels"])
forced = self._settings.get(["forced_channels"])
+
for key, data in channel_configs.items():
read_until = channel_configs[key].get("read_until", None)
entries = sorted(self._to_internal_feed(channel_data.get(key, []), read_until=read_until), key=lambda e: e["published"], reverse=True)
@@ -106,7 +115,7 @@ class AnnouncementPlugin(octoprint.plugin.AssetPlugin,
result[key] = dict(channel=data["name"],
url=data["url"],
- priority=data["priority"],
+ priority=data.get("priority", 2),
enabled=key in enabled or key in forced,
forced=key in forced,
data=entries,
@@ -140,6 +149,8 @@ class AnnouncementPlugin(octoprint.plugin.AssetPlugin,
# Internal Tools
def _mark_read_until(self, channel, until):
+ """Set read_until timestamp of a channel."""
+
current_read_until = None
channel_data = self._settings.get(["channels", channel], merged=True)
if channel_data:
@@ -148,10 +159,14 @@ class AnnouncementPlugin(octoprint.plugin.AssetPlugin,
defaults = dict(plugins=dict(announcements=dict(channels=dict())))
defaults["plugins"]["announcements"]["channels"][channel] = dict(read_until=current_read_until)
- self._settings.set(["channels", channel, "read_until"], until, defaults=defaults)
- self._settings.save()
+ with self._cached_channel_configs_mutex:
+ self._settings.set(["channels", channel, "read_until"], until, defaults=defaults)
+ self._settings.save()
+ self._cached_channel_configs = None
def _toggle(self, channel):
+ """Toggle enable/disabled state of a channel."""
+
enabled_channels = list(self._settings.get(["enabled_channels"]))
if channel in enabled_channels:
@@ -162,36 +177,67 @@ class AnnouncementPlugin(octoprint.plugin.AssetPlugin,
self._settings.set(["enabled_channels"], enabled_channels)
self._settings.save()
- def _get_channel_configs(self):
- return self._settings.get(["channels"], merged=True)
+ def _get_channel_configs(self, force=False):
+ """Retrieve all channel configs with sanitized keys."""
- def _fetch_all_channels(self):
- with self._cached_channels_mutex:
- channels = self._get_channel_configs()
- enabled = self._settings.get(["enabled_channels"])
- forced = self._settings.get(["forced_channels"])
+ with self._cached_channel_configs_mutex:
+ if self._cached_channel_configs is None or force:
+ configs = self._settings.get(["channels"], merged=True)
+ result = dict()
+ for key, config in configs.items():
+ if "url" not in config or "name" not in config:
+ # strip invalid entries
+ continue
+ result[self._slugify(key)] = config
+ self._cached_channel_configs = result
+ return self._cached_channel_configs
- all_channels = dict()
- for key, config in channels.items():
- if not key in enabled and not key in forced:
- continue
+ def _get_channel_config(self, key, force=False):
+ """Retrieve specific channel config for channel."""
- data = self._get_channel_data(key, config)
- if data is not None:
- all_channels[key] = data
+ safe_key = self._slugify(key)
+ return self._get_channel_configs(force=force).get(safe_key)
- self._cached_channels = all_channels
+ def _fetch_all_channels(self, force=False):
+ """Fetch all channel feeds from cache or network."""
- return self._cached_channels
+ channels = self._get_channel_configs(force=force)
+ enabled = self._settings.get(["enabled_channels"])
+ forced = self._settings.get(["forced_channels"])
+
+ all_channels = dict()
+ for key, config in channels.items():
+ if not key in enabled and not key in forced:
+ continue
+
+ if not "url" in config:
+ continue
+
+ data = self._get_channel_data(key, config, force=force)
+ if data is not None:
+ all_channels[key] = data
+
+ return all_channels
+
+ def _get_channel_data(self, key, config, force=False):
+ """Fetch individual channel feed from cache/network."""
+
+ data = None
+
+ if not force:
+ # we may use the cache, see if we have something in there
+ data = self._get_channel_data_from_cache(key, config)
- def _get_channel_data(self, key, config):
- data = self._get_channel_data_from_cache(key, config)
if data is None:
+ # cache not allowed or empty, fetch from network
data = self._get_channel_data_from_network(key, config)
+
return data
def _get_channel_data_from_cache(self, key, config):
- channel_path = os.path.join(self.get_plugin_data_folder(), "{}.cache".format(key))
+ """Fetch channel feed from cache."""
+
+ channel_path = self._get_channel_cache_path(key)
if os.path.exists(channel_path):
if "ttl" in config and isinstance(config["ttl"], int):
@@ -203,30 +249,35 @@ class AnnouncementPlugin(octoprint.plugin.AssetPlugin,
now = time.time()
if os.stat(channel_path).st_mtime + ttl > now:
d = feedparser.parse(channel_path)
- self._logger.info("Loaded channel {} from cache".format(key))
+ self._logger.debug(u"Loaded channel {} from cache at {}".format(key, channel_path))
return d
return None
def _get_channel_data_from_network(self, key, config):
+ """Fetch channel feed from network."""
+
import requests
url = config["url"]
try:
+ start = time.time()
r = requests.get(url)
- self._logger.info("Loaded channel {} from {}".format(key, config["url"]))
+ self._logger.info(u"Loaded channel {} from {} in {:.2}s".format(key, config["url"], time.time() - start))
except Exception as e:
self._logger.exception(
- "Could not fetch channel {} from {}: {}".format(key, config["url"], str(e)))
+ u"Could not fetch channel {} from {}: {}".format(key, config["url"], str(e)))
return None
response = r.text
- channel_path = os.path.join(self.get_plugin_data_folder(), "{}.cache".format(key))
+ channel_path = self._get_channel_cache_path(key)
with codecs.open(channel_path, mode="w", encoding="utf-8") as f:
f.write(response)
return feedparser.parse(response)
def _to_internal_feed(self, feed, read_until=None):
+ """Convert feed to internal data structure."""
+
result = []
if "entries" in feed:
for entry in feed["entries"]:
@@ -236,6 +287,8 @@ class AnnouncementPlugin(octoprint.plugin.AssetPlugin,
return result
def _to_internal_entry(self, entry, read_until=None):
+ """Convert feed entries to internal data structure."""
+
published = calendar.timegm(entry["published_parsed"])
read = True
@@ -250,6 +303,12 @@ class AnnouncementPlugin(octoprint.plugin.AssetPlugin,
link=entry["link"],
read=read)
+ def _get_channel_cache_path(self, key):
+ """Retrieve cache path for channel key."""
+
+ safe_key = self._slugify(key)
+ return os.path.join(self.get_plugin_data_folder(), "{}.cache".format(safe_key))
+
_image_tag_re = re.compile(r'
Du nutzt eine unveröffentlichte Version von OctoPrint, trackst aber OctoPrint Releases." -"
Du willst vermutlich, dass OctoPrint stattdessen die entsprechende Entwicklungsversion trackt. Falls Du dein lokales OctoPrint-Checkoutverzeichnis auf einen anderen Branch gewechselt hast, dann wechsle das Tracking einfach auf \"Commit\". Ansonsten wirf einen Blick in die Dokumentation.
" +msgstr "Du nutzt eine unveröffentlichte Version von OctoPrint, trackst aber OctoPrint Releases.
Du willst vermutlich, dass OctoPrint stattdessen die entsprechende Entwicklungsversion trackt. Falls Du dein lokales OctoPrint-Checkoutverzeichnis auf einen anderen Branch gewechselt hast, dann wechsle das Tracking einfach auf \"Commit\". Ansonsten wirf einen Blick in die Dokumentation.
" #: src/octoprint/plugins/softwareupdate/templates/softwareupdate_settings.jinja2:23 msgid "Current versions" @@ -2922,3 +2921,5 @@ msgstr "Zeitrafferaufnahme rendern" #~ msgid "Supporters" #~ msgstr "Unterstützer" +#~ msgid "Show Announcements..." +#~ msgstr "Ankündigungen anzeigen..." diff --git a/translations/de/LC_MESSAGES/messages.mo b/translations/de/LC_MESSAGES/messages.mo index 01adf3cc..aa52755f 100644 Binary files a/translations/de/LC_MESSAGES/messages.mo and b/translations/de/LC_MESSAGES/messages.mo differ diff --git a/translations/de/LC_MESSAGES/messages.po b/translations/de/LC_MESSAGES/messages.po index 41ef251c..e0194ed0 100644 --- a/translations/de/LC_MESSAGES/messages.po +++ b/translations/de/LC_MESSAGES/messages.po @@ -5,20 +5,21 @@ # msgid "" msgstr "" -"Project-Id-Version: OctoPrint\n" +"Project-Id-Version: OctoPrint\n" "Report-Msgid-Bugs-To: i18n@octoprint.org\n" -"POT-Creation-Date: 2016-05-04 09:51+0200\n" -"PO-Revision-Date: 2016-05-04 09:47+0100\n" +"POT-Creation-Date: 2016-05-04 13:26+0200\n" +"PO-Revision-Date: 2016-05-04 13:27+0100\n" "Last-Translator: Gina HäußgeDu nutzt eine unveröffentlichte Version von OctoPrint, trackst aber OctoPrint Releases." -"
Du willst vermutlich, dass OctoPrint stattdessen die entsprechende Entwicklungsversion trackt. Falls Du dein lokales OctoPrint-Checkoutverzeichnis auf einen anderen Branch gewechselt hast, dann wechsle das Tracking einfach auf \"Commit\". Ansonsten wirf einen Blick in die Dokumentation.
" +msgstr "Du nutzt eine unveröffentlichte Version von OctoPrint, trackst aber OctoPrint Releases.
Du willst vermutlich, dass OctoPrint stattdessen die entsprechende Entwicklungsversion trackt. Falls Du dein lokales OctoPrint-Checkoutverzeichnis auf einen anderen Branch gewechselt hast, dann wechsle das Tracking einfach auf \"Commit\". Ansonsten wirf einen Blick in die Dokumentation.
" #: src/octoprint/plugins/softwareupdate/templates/softwareupdate_settings.jinja2:23 msgid "Current versions" @@ -2922,3 +2921,5 @@ msgstr "Zeitrafferaufnahme rendern" #~ msgid "Supporters" #~ msgstr "Unterstützer" +#~ msgid "Show Announcements..." +#~ msgstr "Ankündigungen anzeigen..." diff --git a/translations/messages.pot b/translations/messages.pot index 673116a5..0e283491 100644 --- a/translations/messages.pot +++ b/translations/messages.pot @@ -6,9 +6,9 @@ #, fuzzy msgid "" msgstr "" -"Project-Id-Version: OctoPrint 1.2.11.dev31+g5a8c365.dirty\n" +"Project-Id-Version: OctoPrint 1.2.11.dev32+g76959e0.dirty\n" "Report-Msgid-Bugs-To: i18n@octoprint.org\n" -"POT-Creation-Date: 2016-05-04 09:51+0200\n" +"POT-Creation-Date: 2016-05-04 13:26+0200\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME