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'') def _strip_images(text): diff --git a/src/octoprint/plugins/announcements/static/js/announcements.js b/src/octoprint/plugins/announcements/static/js/announcements.js index 812d7005..55c20815 100644 --- a/src/octoprint/plugins/announcements/static/js/announcements.js +++ b/src/octoprint/plugins/announcements/static/js/announcements.js @@ -97,6 +97,10 @@ $(function() { }) }; + self.refreshAnnouncements = function() { + self.retrieveData(true); + }; + self.retrieveData = function(force) { if (!self.loginState.isAdmin()) return; diff --git a/src/octoprint/plugins/announcements/templates/announcements_settings.jinja2 b/src/octoprint/plugins/announcements/templates/announcements_settings.jinja2 index d9851abe..3d62eeb3 100644 --- a/src/octoprint/plugins/announcements/templates/announcements_settings.jinja2 +++ b/src/octoprint/plugins/announcements/templates/announcements_settings.jinja2 @@ -31,4 +31,4 @@ - + diff --git a/src/octoprint/translations/de/LC_MESSAGES/messages.mo b/src/octoprint/translations/de/LC_MESSAGES/messages.mo index 01adf3cc..aa52755f 100644 Binary files a/src/octoprint/translations/de/LC_MESSAGES/messages.mo and b/src/octoprint/translations/de/LC_MESSAGES/messages.mo differ diff --git a/src/octoprint/translations/de/LC_MESSAGES/messages.po b/src/octoprint/translations/de/LC_MESSAGES/messages.po index 41ef251c..e0194ed0 100644 --- a/src/octoprint/translations/de/LC_MESSAGES/messages.po +++ b/src/octoprint/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ßge \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" " Documentation.\n" "

\n" -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.

" +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ßge \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" " Documentation.\n" "

\n" -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.

" +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 \n" "Language-Team: LANGUAGE \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