#511 update calendar widget for streams
Closed 6 years ago by abompard. Opened 6 years ago by ryanlerch.
ryanlerch/fedora-hubs calendar  into  develop

@@ -4,7 +4,9 @@ 

  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 @@ 

              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 @@ 

      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,

              )

  

  
@@ -53,33 +60,66 @@ 

      TOPIC = ".fedocal.calendar."

      invalidate_on_hub_config_change = True

  

+     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()

There are two calls to invalidate() here.

However I'm thinking of a different approach. It will be common for widgets on stream pages to invalidate when the user hub config changes, since that's where the config is. So it should be centralized. I would recommend the following change:

diff --git a/hubs/models/hub.py b/hubs/models/hub.py
index f09dbfdc..db58e228 100644
--- a/hubs/models/hub.py
+++ b/hubs/models/hub.py
@@ -214,7 +214,10 @@ class Hub(ObjectAuthzMixin, BASE):
             hubs.defaults.add_stream_widgets(self)

     def on_updated(self, old_config):
-        for widget_instance in self.widgets:
+        widgets = self.widgets
+        if self.hub_type == "user":
+            widgets.extend(Hub.by_name(self.name, "stream").widgets)
+        for widget_instance in widgets:
             if not widget_instance.enabled:
                 continue
             widget = widget_instance.module

This way stream widgets that have asked to be reloaded on (user) hub config change will be reloaded too.

+ 

      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):

          if self.TOPIC not in message["topic"]:

@@ -1,7 +1,13 @@ 

- {% if not calendar %}

+ {% if not calendars %}

+   {% if hub_type == 'team' %}

    <p class="text-warning mb-0">

      <em>You must configure a calendar in the hub configuration in order to use this widget.</em>

    </p>

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

+   <p class="text-warning mb-0">

+     <em>The hubs you are subscribed to are not using calendars</em>

+   </p>

+   {% endif %}

  {% else %}

    {% for title, next in meetings.items() %}

    <div class="meeting">

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

rebased onto baee8b5

6 years ago

Metadata Update from @abompard:
- Request assigned

6 years ago

There are two calls to invalidate() here.

However I'm thinking of a different approach. It will be common for widgets on stream pages to invalidate when the user hub config changes, since that's where the config is. So it should be centralized. I would recommend the following change:

diff --git a/hubs/models/hub.py b/hubs/models/hub.py
index f09dbfdc..db58e228 100644
--- a/hubs/models/hub.py
+++ b/hubs/models/hub.py
@@ -214,7 +214,10 @@ class Hub(ObjectAuthzMixin, BASE):
             hubs.defaults.add_stream_widgets(self)

     def on_updated(self, old_config):
-        for widget_instance in self.widgets:
+        widgets = self.widgets
+        if self.hub_type == "user":
+            widgets.extend(Hub.by_name(self.name, "stream").widgets)
+        for widget_instance in widgets:
             if not widget_instance.enabled:
                 continue
             widget = widget_instance.module

This way stream widgets that have asked to be reloaded on (user) hub config change will be reloaded too.

Whoops! The first one there is a mistake -- i had it in there for debugging to make it not use the cache when testing.

the reason for the other invalidate is because we are reading config values from group hubs here -- namely the calendar values of the hubs that the user is subscribed to. Does that patch above cover that too?

Indeed, it's more complex than I thought. But running invalidate() in the constructor basically means that you're not using the cache at all.

I'll try to think of something else.

OK, I think I have a solution. The solution is using fedmsg to publish config changes in Hubs, instead of directly injecting into the workers queue.

As a result, cached functions can write a should_invalidate() method that will listen for changes in other hubs, in your case the hubs that the user is subscribed to.

Please check out PR #518 for my implementation.

The only downside is that a new process must be run to emit fedmsg from Hubs (fedmsg-relay, but I have updated the honcho config file to run it. It works locally.

Feel free to reach out to me if you have questions, it's probably still a bit rough around the edges.

I've rewritten the cached function to use the fedmsg publishing infra in #518. I've opened PR #520 so you can check it out.

Pull-Request has been closed by abompard

6 years ago