From 5f1e0d22803cbc50d28deebe9ddd340233263f92 Mon Sep 17 00:00:00 2001 From: Ryan Lerch Date: Jan 19 2018 15:06:21 +0000 Subject: [PATCH 1/2] update calendar widget for streams This Changes the calendar (aka the meetings) widget so it only shows on streams or teams. Additionally on the stream page, it shows the meetings upcoming for all the hubs that a user is a member of. fixes #489 --- diff --git a/hubs/widgets/meetings/__init__.py b/hubs/widgets/meetings/__init__.py index 7f9ce27..d2ede22 100644 --- a/hubs/widgets/meetings/__init__.py +++ b/hubs/widgets/meetings/__init__.py @@ -4,7 +4,9 @@ import arrow import collections import datetime import requests +from collections import OrderedDict as ordereddict +import hubs.models from hubs.utils import validators from hubs.utils.text import markup from hubs.widgets.base import Widget @@ -24,6 +26,7 @@ class Meetings(Widget): validator=validators.Integer, help="The number of meetings to display.", )] + hub_types = ["team", "stream"] def get_template_environment(self): env = super(Meetings, self).get_template_environment() @@ -37,14 +40,18 @@ class BaseView(RootWidgetView): def get_context(self, instance, *args, **kwargs): get_meetings = GetMeetings(instance) now = datetime.datetime.utcnow() + results = get_meetings() meetings = { title: meeting - for title, meeting in get_meetings().items() + for title, meeting in results["meetings"].items() if meeting['start_dt'] > now } + meetings = ordereddict(sorted(meetings.items(), + key=lambda x: x[1]["start_dt"])) return dict( - calendar=instance.hub.config.get("calendar"), + calendars=results["calendars"], meetings=meetings, + hub_type=instance.hub.hub_type, ) @@ -52,33 +59,66 @@ class GetMeetings(CachedFunction): TOPIC = ".fedocal.calendar." + def __init__(self, instance): + self.instance = instance + # there is no way to invalidate the cache if a + # different hub config changes. Since streams + # calendar pulls from the hubs the user is a member + # of, we just invalidate the cache for streams. + self.invalidate() + if self.instance.hub.hub_type == "stream": + self.invalidate() + def execute(self): calendar = self.instance.hub.config.get("calendar") + calendars = [] if calendar is None: - return {} - n_meetings = self.instance.config.get("n_meetings", 4) - base = ('https://apps.fedoraproject.org/calendar/api/meetings/' - '?calendar=%s') - url = base % calendar - response = requests.get(url).json() - - tmp = collections.defaultdict(list) - for meeting in response['meetings']: - if meeting.get('meeting_information_html'): - meeting['meeting_information_html'] = markup( - meeting['meeting_information']) - tmp[meeting['meeting_name']].append(meeting) - + if self.instance.hub.hub_type == "stream": + username = self.instance.hub.name + user = hubs.models.User.by_username(username) + for hub in user.memberships: + if hub.name != username: + c = hub.config.get("calendar") + # if a hub admin adds a calendar, then removes it + # the calendar value is '' rather than null. This + # works around that. + if c != '': + calendars.append(hub.config.get("calendar")) + if calendars is []: + calendars = None + else: + calendars = None + else: + # if a hub admin adds a calendar, then removes it + # the calendar value is '' rather than null. This + # works around that. + if calendar != '': + calendars.append(calendar) meetings = {} - for title, items in tmp.items(): - selected = next_meeting(items) - if not selected: - continue - meetings[title] = selected - if len(meetings) >= n_meetings: - break - - return meetings + if calendars: + for c in calendars: + n_meetings = self.instance.config.get("n_meetings", 4) + base = ('https://apps.fedoraproject.org/calendar/api/meetings/' + '?calendar=%s') + url = base % c + response = requests.get(url).json() + + tmp = collections.defaultdict(list) + for meeting in response['meetings']: + if meeting.get('meeting_information_html'): + meeting['meeting_information_html'] = markup( + meeting['meeting_information']) + tmp[meeting['meeting_name']].append(meeting) + + for title, items in tmp.items(): + selected = next_meeting(items) + if not selected: + continue + meetings[title] = selected + if len(meetings) >= n_meetings: + break + + return {"meetings": meetings, "calendars": calendars} def should_invalidate(self, message): # Hub update diff --git a/hubs/widgets/meetings/templates/root.html b/hubs/widgets/meetings/templates/root.html index 2596d32..ea10b92 100644 --- a/hubs/widgets/meetings/templates/root.html +++ b/hubs/widgets/meetings/templates/root.html @@ -1,7 +1,13 @@ -{% if not calendar %} +{% if not calendars %} + {% if hub_type == 'team' %}

You must configure a calendar in the hub configuration in order to use this widget.

+ {% elif hub_type == 'stream'%} +

+ The hubs you are subscribed to are not using calendars +

+ {% endif %} {% else %} {% for title, next in meetings.items() %}
From e46b2ebd104e4b873bf5c26784ac549747a32aa6 Mon Sep 17 00:00:00 2001 From: Aurélien Bompard Date: Jan 19 2018 15:06:21 +0000 Subject: [PATCH 2/2] Use the new fedmsg to invalidate the meetings --- diff --git a/hubs/widgets/meetings/__init__.py b/hubs/widgets/meetings/__init__.py index d2ede22..f6fbfa8 100644 --- a/hubs/widgets/meetings/__init__.py +++ b/hubs/widgets/meetings/__init__.py @@ -57,83 +57,85 @@ class BaseView(RootWidgetView): class GetMeetings(CachedFunction): - TOPIC = ".fedocal.calendar." - - def __init__(self, instance): - self.instance = instance - # there is no way to invalidate the cache if a - # different hub config changes. Since streams - # calendar pulls from the hubs the user is a member - # of, we just invalidate the cache for streams. - self.invalidate() + def get_calendars(self): + calendar = self.instance.hub.config.get("calendar") + calendars = [calendar] if self.instance.hub.hub_type == "stream": - self.invalidate() + username = self.instance.hub.name + user = hubs.models.User.query.get(username) + for hub in user.subscriptions: + if hub.name != username: + calendars.append(hub.config.get("calendar")) + # Filter out None or empty strings + return [c for c in calendars if c] def execute(self): - calendar = self.instance.hub.config.get("calendar") - calendars = [] - if calendar is None: - if self.instance.hub.hub_type == "stream": - username = self.instance.hub.name - user = hubs.models.User.by_username(username) - for hub in user.memberships: - if hub.name != username: - c = hub.config.get("calendar") - # if a hub admin adds a calendar, then removes it - # the calendar value is '' rather than null. This - # works around that. - if c != '': - calendars.append(hub.config.get("calendar")) - if calendars is []: - calendars = None - else: - calendars = None - else: - # if a hub admin adds a calendar, then removes it - # the calendar value is '' rather than null. This - # works around that. - if calendar != '': - calendars.append(calendar) + calendars = self.get_calendars() meetings = {} - if calendars: - for c in calendars: - n_meetings = self.instance.config.get("n_meetings", 4) - base = ('https://apps.fedoraproject.org/calendar/api/meetings/' - '?calendar=%s') - url = base % c - response = requests.get(url).json() - - tmp = collections.defaultdict(list) - for meeting in response['meetings']: - if meeting.get('meeting_information_html'): - meeting['meeting_information_html'] = markup( - meeting['meeting_information']) - tmp[meeting['meeting_name']].append(meeting) - - for title, items in tmp.items(): - selected = next_meeting(items) - if not selected: - continue - meetings[title] = selected - if len(meetings) >= n_meetings: - break + for c in calendars: + n_meetings = self.instance.config.get("n_meetings", 4) + base = ('https://apps.fedoraproject.org/calendar/api/meetings/' + '?calendar=%s') + url = base % c + response = requests.get(url).json() + + tmp = collections.defaultdict(list) + for meeting in response['meetings']: + if meeting.get('meeting_information_html'): + meeting['meeting_information_html'] = markup( + meeting['meeting_information']) + tmp[meeting['meeting_name']].append(meeting) + + for title, items in tmp.items(): + selected = next_meeting(items) + if not selected: + continue + meetings[title] = selected + if len(meetings) >= n_meetings: + break return {"meetings": meetings, "calendars": calendars} def should_invalidate(self, message): + # Invalidate when: + # - a subscribed hub changes its calendar + # - the user subscribes to a new hub that has a calendar + # - the user unsubscribes from a hub that has a calendar + # - one of the subscribed calendars has an update + # Hub update if message["topic"].endswith('.hubs.hub.updated'): if "calendar" not in message["msg"]["changed_keys"]: return False - return message["msg"]["hub_id"] == self.instance.hub.id - # Calendar update - if self.TOPIC not in message["topic"]: - return False - try: - calendar = message["msg"]["calendar"]["calendar_name"] - except KeyError: + hub_id = message["msg"]["hub_id"] + if hub_id == self.instance.hub.id: + return True + if self.instance.hub.hub_type == "stream": + user = hubs.models.User.query.get(self.instance.hub.name) + if hub_id in [h.id for h in user.subscriptions]: + return True return False - return (calendar == self.instance.hub.config.get("calendar")) + + # User subscription + if ".hubs.user.role." in message["topic"]: + if message["msg"]["username"] != self.instance.hub.name: + return False + if message["msg"]["role"] != "subscriber": + return False + hub = hubs.models.Hub.query.get(message["msg"]["hub_id"]) + if not hub: + return False + return bool(hub.config.get("calendar")) + + # Calendar update + if ".fedocal.calendar." in message["topic"]: + try: + calendar = message["msg"]["calendar"]["calendar_name"] + except KeyError: + return False + return calendar in self.get_calendars() + + return False def next_meeting(meetings):