Announcements: Ensure validity of channel keys, made refreshable
This commit is contained in:
parent
76959e06c9
commit
4830d60269
8 changed files with 129 additions and 64 deletions
|
|
@ -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):
|
||||
|
|
|
|||
|
|
@ -97,6 +97,10 @@ $(function() {
|
|||
})
|
||||
};
|
||||
|
||||
self.refreshAnnouncements = function() {
|
||||
self.retrieveData(true);
|
||||
};
|
||||
|
||||
self.retrieveData = function(force) {
|
||||
if (!self.loginState.isAdmin()) return;
|
||||
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -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..."
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -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..."
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in a new issue