Announcements: Ensure validity of channel keys, made refreshable

This commit is contained in:
Gina Häußge 2016-05-04 13:28:28 +02:00
parent 76959e06c9
commit 4830d60269
8 changed files with 129 additions and 64 deletions

View file

@ -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'<img.*?/?>')
def _strip_images(text):

View file

@ -97,6 +97,10 @@ $(function() {
})
};
self.refreshAnnouncements = function() {
self.retrieveData(true);
};
self.retrieveData = function(force) {
if (!self.loginState.isAdmin()) return;

View file

@ -31,4 +31,4 @@
</ul>
</div>
<button class="btn btn-block" data-bind="click: $root.showAnnouncementDialog">{{ _('Show Announcements...') }}</button>
<button class="btn btn-block" data-bind="click: $root.refreshAnnouncements"><i class="icon-refresh"></i> {{ _('Refresh Announcements') }}</button>

View file

@ -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ßge <osd@foosel.net>\n"
"Language: de\n"
"Language-Team: German (http://www.transifex.com/projects/p/octoprint/language/de/)\n"
"Plural-Forms: nplurals=2; plural=(n != 1)\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 2.2.0\n"
"X-Generator: Poedit 1.6.8\n"
#: src/octoprint/plugins/announcements/__init__.py:81
#: src/octoprint/plugins/announcements/__init__.py:85
#: src/octoprint/plugins/announcements/templates/announcements.jinja2:4
#: src/octoprint/plugins/announcements/templates/announcements_navbar.jinja2:1
msgid "Announcements"
@ -36,15 +37,15 @@ msgstr "Kanal deaktivieren"
msgid "Enable Channel"
msgstr "Kanal aktivieren"
#: src/octoprint/plugins/announcements/static/js/announcements.js:245
#: src/octoprint/plugins/announcements/static/js/announcements.js:249
msgid "Later"
msgstr "Später"
#: src/octoprint/plugins/announcements/static/js/announcements.js:251
#: src/octoprint/plugins/announcements/static/js/announcements.js:255
msgid "Mark read"
msgstr "Gelesen"
#: src/octoprint/plugins/announcements/static/js/announcements.js:257
#: src/octoprint/plugins/announcements/static/js/announcements.js:261
msgid "Read..."
msgstr "Lesen..."
@ -88,8 +89,8 @@ msgid "Actions"
msgstr "Aktionen"
#: src/octoprint/plugins/announcements/templates/announcements_settings.jinja2:34
msgid "Show Announcements..."
msgstr "Ankündigungen anzeigen..."
msgid "Refresh Announcements"
msgstr "Ankündigungen aktualisieren"
#: src/octoprint/plugins/cura/templates/cura_settings.jinja2:1
#: src/octoprint/templates/tabs/control.jinja2:97
@ -821,9 +822,7 @@ msgid ""
" take care of that. Otherwise please take a look at the\n"
" <a href=\"https://github.com/foosel/OctoPrint/wiki/Plugin:-Software-Update\" target=\"_blank\">Documentation</a>.\n"
" </p>\n"
msgstr ""
" <p> <strong>Du nutzt eine unveröffentlichte Version von OctoPrint, trackst aber OctoPrint Releases.</strong> "
" </p> <p> Du willst vermutlich, dass OctoPrint stattdessen die entsprechende Entwicklungsversion trackt. Falls Du dein lokales OctoPrint-Checkoutverzeichnis auf einen anderen Branch gewechselt hast, dann <strong>wechsle das Tracking einfach auf \"Commit\"</strong>. Ansonsten wirf einen Blick in die <a href=\"https://github.com/foosel/OctoPrint/wiki/Plugin:-Software-Update\" target=\"_blank\">Dokumentation</a>. </p>"
msgstr " <p> <strong>Du nutzt eine unveröffentlichte Version von OctoPrint, trackst aber OctoPrint Releases.</strong </p> <p> Du willst vermutlich, dass OctoPrint stattdessen die entsprechende Entwicklungsversion trackt. Falls Du dein lokales OctoPrint-Checkoutverzeichnis auf einen anderen Branch gewechselt hast, dann <strong>wechsle das Tracking einfach auf \"Commit\"</strong>. Ansonsten wirf einen Blick in die <a href=\"https://github.com/foosel/OctoPrint/wiki/Plugin:-Software-Update\" target=\"_blank\">Dokumentation</a>. </p>"
#: 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..."

View file

@ -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ßge <osd@foosel.net>\n"
"Language: de\n"
"Language-Team: German (http://www.transifex.com/projects/p/octoprint/language/de/)\n"
"Plural-Forms: nplurals=2; plural=(n != 1)\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 2.2.0\n"
"X-Generator: Poedit 1.6.8\n"
#: src/octoprint/plugins/announcements/__init__.py:81
#: src/octoprint/plugins/announcements/__init__.py:85
#: src/octoprint/plugins/announcements/templates/announcements.jinja2:4
#: src/octoprint/plugins/announcements/templates/announcements_navbar.jinja2:1
msgid "Announcements"
@ -36,15 +37,15 @@ msgstr "Kanal deaktivieren"
msgid "Enable Channel"
msgstr "Kanal aktivieren"
#: src/octoprint/plugins/announcements/static/js/announcements.js:245
#: src/octoprint/plugins/announcements/static/js/announcements.js:249
msgid "Later"
msgstr "Später"
#: src/octoprint/plugins/announcements/static/js/announcements.js:251
#: src/octoprint/plugins/announcements/static/js/announcements.js:255
msgid "Mark read"
msgstr "Gelesen"
#: src/octoprint/plugins/announcements/static/js/announcements.js:257
#: src/octoprint/plugins/announcements/static/js/announcements.js:261
msgid "Read..."
msgstr "Lesen..."
@ -88,8 +89,8 @@ msgid "Actions"
msgstr "Aktionen"
#: src/octoprint/plugins/announcements/templates/announcements_settings.jinja2:34
msgid "Show Announcements..."
msgstr "Ankündigungen anzeigen..."
msgid "Refresh Announcements"
msgstr "Ankündigungen aktualisieren"
#: src/octoprint/plugins/cura/templates/cura_settings.jinja2:1
#: src/octoprint/templates/tabs/control.jinja2:97
@ -821,9 +822,7 @@ msgid ""
" take care of that. Otherwise please take a look at the\n"
" <a href=\"https://github.com/foosel/OctoPrint/wiki/Plugin:-Software-Update\" target=\"_blank\">Documentation</a>.\n"
" </p>\n"
msgstr ""
" <p> <strong>Du nutzt eine unveröffentlichte Version von OctoPrint, trackst aber OctoPrint Releases.</strong> "
" </p> <p> Du willst vermutlich, dass OctoPrint stattdessen die entsprechende Entwicklungsversion trackt. Falls Du dein lokales OctoPrint-Checkoutverzeichnis auf einen anderen Branch gewechselt hast, dann <strong>wechsle das Tracking einfach auf \"Commit\"</strong>. Ansonsten wirf einen Blick in die <a href=\"https://github.com/foosel/OctoPrint/wiki/Plugin:-Software-Update\" target=\"_blank\">Dokumentation</a>. </p>"
msgstr " <p> <strong>Du nutzt eine unveröffentlichte Version von OctoPrint, trackst aber OctoPrint Releases.</strong </p> <p> Du willst vermutlich, dass OctoPrint stattdessen die entsprechende Entwicklungsversion trackt. Falls Du dein lokales OctoPrint-Checkoutverzeichnis auf einen anderen Branch gewechselt hast, dann <strong>wechsle das Tracking einfach auf \"Commit\"</strong>. Ansonsten wirf einen Blick in die <a href=\"https://github.com/foosel/OctoPrint/wiki/Plugin:-Software-Update\" target=\"_blank\">Dokumentation</a>. </p>"
#: 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..."

View file

@ -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 <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@ -17,7 +17,7 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 2.2.0\n"
#: src/octoprint/plugins/announcements/__init__.py:81
#: src/octoprint/plugins/announcements/__init__.py:85
#: src/octoprint/plugins/announcements/templates/announcements.jinja2:4
#: src/octoprint/plugins/announcements/templates/announcements_navbar.jinja2:1
msgid "Announcements"
@ -35,15 +35,15 @@ msgstr ""
msgid "Enable Channel"
msgstr ""
#: src/octoprint/plugins/announcements/static/js/announcements.js:245
#: src/octoprint/plugins/announcements/static/js/announcements.js:249
msgid "Later"
msgstr ""
#: src/octoprint/plugins/announcements/static/js/announcements.js:251
#: src/octoprint/plugins/announcements/static/js/announcements.js:255
msgid "Mark read"
msgstr ""
#: src/octoprint/plugins/announcements/static/js/announcements.js:257
#: src/octoprint/plugins/announcements/static/js/announcements.js:261
msgid "Read..."
msgstr ""
@ -87,7 +87,7 @@ msgid "Actions"
msgstr ""
#: src/octoprint/plugins/announcements/templates/announcements_settings.jinja2:34
msgid "Show Announcements..."
msgid "Refresh Announcements"
msgstr ""
#: src/octoprint/plugins/cura/templates/cura_settings.jinja2:1