From 6e44e3301b0b6963c6d41c68a01d073e8ba1cef4 Mon Sep 17 00:00:00 2001 From: Ryan Lerch Date: Feb 14 2018 22:48:23 +0000 Subject: add new issues widget to replace pagureissues and githubissues This adds a new widget called issues that replaces the githubissues and pagureissues widgets. It consolodates these into a single widget that draws the repos from the team hub config --- diff --git a/hubs/default_config.py b/hubs/default_config.py index 5ffd641..060d5e4 100644 --- a/hubs/default_config.py +++ b/hubs/default_config.py @@ -57,19 +57,17 @@ EMAIL_PORT = 25 WIDGETS = [ 'hubs.widgets.badges:Badges', - 'hubs.widgets.bugzilla:Bugzilla', 'hubs.widgets.library:Library', 'hubs.widgets.weeklyactivity:WeeklyActivity', 'hubs.widgets.feed:Feed', - 'hubs.widgets.githubissues:GitHubIssues', 'hubs.widgets.halp:Halp', 'hubs.widgets.irc:IRC', + 'hubs.widgets.newestissues:NewestIssues', 'hubs.widgets.meetings:Meetings', 'hubs.widgets.mailinglist:MailingList', 'hubs.widgets.memberships:Memberships', 'hubs.widgets.my_hubs:MyHubs', 'hubs.widgets.pullrequests:PullRequests', - 'hubs.widgets.pagureissues:PagureIssues', 'hubs.widgets.repositories:Repositories', 'hubs.widgets.sticky:Sticky', 'hubs.widgets.issues:Issues', diff --git a/hubs/tests/utils/test_views.py b/hubs/tests/utils/test_views.py index 835041c..c4abbb0 100644 --- a/hubs/tests/utils/test_views.py +++ b/hubs/tests/utils/test_views.py @@ -56,7 +56,8 @@ class ViewUtilsTest(APPTest): def test_move_widget(self): hub = Hub(name="testing", hub_type="team") self.session.add(hub) - widget_names = ["meetings", "badges", "bugzilla", "my_hubs", "sticky"] + widget_names = ["meetings", "badges", "newestissues", + "my_hubs", "sticky"] def get_names(): return [ @@ -83,23 +84,23 @@ class ViewUtilsTest(APPTest): # From middle to middle forwards check_move( "badges", 3, - ["meetings", "bugzilla", "my_hubs", "badges", "sticky"]) + ["meetings", "newestissues", "my_hubs", "badges", "sticky"]) # From middle to middle backwards check_move( "badges", 1, - ["meetings", "badges", "bugzilla", "my_hubs", "sticky"]) + ["meetings", "badges", "newestissues", "my_hubs", "sticky"]) # From middle to start check_move( "my_hubs", 0, - ["my_hubs", "meetings", "badges", "bugzilla", "sticky"]) + ["my_hubs", "meetings", "badges", "newestissues", "sticky"]) # From middle to end check_move( "badges", 5, - ["my_hubs", "meetings", "bugzilla", "sticky", "badges"]) + ["my_hubs", "meetings", "newestissues", "sticky", "badges"]) # From start to end check_move( "my_hubs", 5, - ["meetings", "bugzilla", "sticky", "badges", "my_hubs"]) + ["meetings", "newestissues", "sticky", "badges", "my_hubs"]) def test_move_widget_disabled(self): # Make sure moving works even if there are disabled widgets diff --git a/hubs/tests/views/test_api_hub_widget.py b/hubs/tests/views/test_api_hub_widget.py index 8016d83..7c7bd2c 100644 --- a/hubs/tests/views/test_api_hub_widget.py +++ b/hubs/tests/views/test_api_hub_widget.py @@ -200,32 +200,35 @@ class TestAPIHubWidgets(APPTest): def test_post_invalid_config(self): hub = Hub.by_name("ralph", "user") self.session.add(Widget( - hub=hub, plugin="bugzilla", index=0)) + hub=hub, plugin="newestissues", index=0)) self.session.commit() self.assertEqual( Widget.query.filter_by( - hub=hub, plugin="bugzilla").count(), 1) + hub=hub, plugin="newestissues").count(), 1) + widget_class = registry["newestissues"] data = { - "name": "bugzilla", + "name": "newestissues", "config": {"foo": "bar"}, 'position': 'right', } user = FakeAuthorization('ralph') - with auth_set(app, user): - result = self.app.post( - '/api/hubs/%s/widgets/' % hub.id, - content_type="application/json", - data=json.dumps(data)) + with patch.object( + widget_class, "parameters", [{"name": "required"}]): + with auth_set(app, user): + result = self.app.post( + '/api/hubs/%s/widgets/' % hub.id, + content_type="application/json", + data=json.dumps(data)) self.assertEqual(result.status_code, 200) self.assertEqual( json.loads(result.get_data(as_text=True)), { "status": "ERROR", - "message": "You must provide a value for: username", + "message": "You must provide a value for: required", }) self.assertEqual( Widget.query.filter_by( - hub=hub, plugin="bugzilla").count(), 1) + hub=hub, plugin="newestissues").count(), 1) class TestAPIHubWidget(APPTest): diff --git a/hubs/tests/widgets/test_githubissues.py b/hubs/tests/widgets/test_githubissues.py deleted file mode 100644 index d36c243..0000000 --- a/hubs/tests/widgets/test_githubissues.py +++ /dev/null @@ -1,54 +0,0 @@ -from __future__ import unicode_literals - -from . import WidgetTest - - -class TestGithubIssues(WidgetTest): - - plugin = "githubissues" - initial_widget_config = { - "org": "fedora-infra", - "repo": "fedora-hubs", - } - - def populate(self): - super(TestGithubIssues, self).populate() - self._add_widget_under_test() - - def _get_should_invalidate_result(self, msg): - func = self.widget.module.get_cached_functions()['GetIssues'] - return func(self.widget).should_invalidate(msg) - - def test_should_invalidate_wrong_topic(self): - msg = {'topic': 'hubs.widget.update.WRONG.TOPIC'} - self.assertFalse(self._get_should_invalidate_result(msg)) - - def test_should_invalidate_good_match(self): - msg = { - 'topic': 'tests.github.issue.opened', - 'msg': { - 'organization': 'fedora-infra', - 'repository': 'fedora-hubs', - } - } - self.assertTrue(self._get_should_invalidate_result(msg)) - - def test_should_invalidate_wrong_org(self): - msg = { - 'topic': 'tests.github.issue.opened', - 'msg': { - 'organization': 'not_fedora_infra', - 'repository': 'fedora-hubs', - } - } - self.assertFalse(self._get_should_invalidate_result(msg)) - - def test_should_invalidate_wrong_repo(self): - msg = { - 'topic': 'tests.github.issue.opened', - 'msg': { - 'organization': 'fedora-infra', - 'repository': 'not-fedora-hubs', - } - } - self.assertFalse(self._get_should_invalidate_result(msg)) diff --git a/hubs/tests/widgets/test_newestissues.py b/hubs/tests/widgets/test_newestissues.py new file mode 100644 index 0000000..046ae22 --- /dev/null +++ b/hubs/tests/widgets/test_newestissues.py @@ -0,0 +1,69 @@ +from __future__ import unicode_literals + +from . import WidgetTest + + +class TestNewestIssues(WidgetTest): + + plugin = "newestissues" + + def populate(self): + super(TestNewestIssues, self).populate() + self._add_widget_under_test() + + def _get_pagure_should_invalidate_result(self, msg): + func = self.widget.module.get_cached_functions()['GetPagureIssues'] + return func(self.widget).should_invalidate(msg) + + def _get_github_should_invalidate_result(self, msg): + func = self.widget.module.get_cached_functions()['GetGithubIssues'] + return func(self.widget).should_invalidate(msg) + + def test_should_invalidate_wrong_topic(self): + msg = {'topic': 'hubs.widget.update.WRONG.TOPIC'} + self.assertFalse(self._get_pagure_should_invalidate_result(msg)) + self.assertFalse(self._get_github_should_invalidate_result(msg)) + + def test_github_should_invalidate_good_match(self): + self.widget.hub.config["github"] = ["fedora-infra/bodhi"] + + msg = { + 'topic': 'tests.github.issue.opened', + 'msg': { + 'repository': { + 'fullname': 'fedora-infra/bodhi', + }, + } + } + self.assertTrue(self._get_github_should_invalidate_result(msg)) + + def test_pagure_should_invalidate_good_match(self): + self.widget.hub.config["pagure"] = ["fedora-hubs"] + msg = { + 'topic': 'tests.pagure.issue.new', + 'msg': { + "project": { + "name": "fedora-hubs", + }, + }, + } + self.assertTrue(self._get_pagure_should_invalidate_result(msg)) + + def test_should_invalidate_wrong_repo(self): + msg = { + 'topic': 'tests.pagure.issue.new', + 'msg': { + "project": { + "name": "not-fedora-hubs", + }, + }, + } + self.assertFalse(self._get_pagure_should_invalidate_result(msg)) + + msg = { + 'topic': 'tests.github.issue.opened', + 'msg': { + 'fullname': 'not_fedora_infra/fedora-hubs', + } + } + self.assertFalse(self._get_github_should_invalidate_result(msg)) diff --git a/hubs/tests/widgets/test_pagureissues.py b/hubs/tests/widgets/test_pagureissues.py deleted file mode 100644 index a55c272..0000000 --- a/hubs/tests/widgets/test_pagureissues.py +++ /dev/null @@ -1,45 +0,0 @@ -from __future__ import unicode_literals - -from . import WidgetTest - - -class TestPagureIssues(WidgetTest): - - plugin = "pagureissues" - initial_widget_config = { - "repo": "fedora-hubs", - } - - def populate(self): - super(TestPagureIssues, self).populate() - self._add_widget_under_test() - - def _get_should_invalidate_result(self, msg): - func = self.widget.module.get_cached_functions()['GetIssues'] - return func(self.widget).should_invalidate(msg) - - def test_should_invalidate_wrong_topic(self): - msg = {'topic': 'hubs.widget.update.WRONG.TOPIC'} - self.assertFalse(self._get_should_invalidate_result(msg)) - - def test_should_invalidate_good_match(self): - msg = { - 'topic': 'tests.pagure.issue.new', - 'msg': { - "project": { - "name": "fedora-hubs", - }, - }, - } - self.assertTrue(self._get_should_invalidate_result(msg)) - - def test_should_invalidate_wrong_repo(self): - msg = { - 'topic': 'tests.pagure.issue.new', - 'msg': { - "project": { - "name": "not-fedora-hubs", - }, - }, - } - self.assertFalse(self._get_should_invalidate_result(msg)) diff --git a/hubs/utils/github.py b/hubs/utils/github.py index 3096ca3..148efa6 100644 --- a/hubs/utils/github.py +++ b/hubs/utils/github.py @@ -58,6 +58,30 @@ def github_pulls(repo, token=None): ) +def github_issues(repo, token=None): + log.info("Finding github issues for %r" % (repo)) + tmpl = "https://api.github.com/repos/{repo}/" + \ + "issues?per_page=100" + url = tmpl.format(repo=repo) + if token: + token = dict(access_token=token) + for result in _github_results(url, token): + yield dict( + issue_project_url='/'.join(["https://github.com", repo]), + issue_url='/'.join(["https://github.com", + repo, "pull", str(result['number'])]), + issue_project_name=repo, + issue_id=result['number'], + issue_title=result['title'][:40], + issue_title_full=result['title'], + issue_openedby=result['user']['login'], + issue_assignee=result['assignee'], + issue_repo=repo, + issue_hosting_type="Github", + issue_opened_date=arrow.get(result['created_at']).timestamp, + ) + + def _github_results(url, auth): link = dict(next=url) while 'next' in link: diff --git a/hubs/utils/pagure.py b/hubs/utils/pagure.py index dc89c03..b2807a6 100644 --- a/hubs/utils/pagure.py +++ b/hubs/utils/pagure.py @@ -40,5 +40,48 @@ def pagure_pulls(repo): pr_assignee=request['assignee'], pr_repo=repo, pr_hosting_type="Pagure", - pr_opened_date=arrow.get(int(request['date_created'])), + pr_opened_date=arrow.get(int(request['date_created']) + ).timestamp, + ) + + +def pagure_issues(repo): + url = '/'.join([PAGURE_URL, "api", "0", repo, "issues"]) + response = requests.get(url) + data = response.json() + + for issue in data['issues']: + + issue_assignee = None + if issue['assignee']: + issue_assignee = issue['assignee']['name'] + + issue_project_user = None + issue_project_name = repo + if 'project' in issue: + issue_project_name = issue['project']['name'] + if issue['project']['parent']: + issue_project_user = issue['project']['user']['username'] + else: + if '/' in repo: + issue_project_name, issue_project_user = \ + repo.split('/', 1) + else: + issue_project_name = repo + + yield dict( + issue_project_url="/".join([PAGURE_URL, repo]), + issue_url="/".join([PAGURE_URL, repo, + "issue", str(issue['id'])]), + issue_project_name=issue_project_name, + issue_project_user=issue_project_user, + issue_id=issue['id'], + issue_title=issue['title'][:45], + issue_title_full=issue['title'], + issue_openedby=issue['user']['name'], + issue_assignee=issue_assignee, + issue_repo=repo, + issue_hosting_type="Pagure", + issue_opened_date=arrow.get(int(issue['date_created']) + ).timestamp, ) diff --git a/hubs/widgets/bugzilla/__init__.py b/hubs/widgets/bugzilla/__init__.py deleted file mode 100644 index 79cc6e0..0000000 --- a/hubs/widgets/bugzilla/__init__.py +++ /dev/null @@ -1,85 +0,0 @@ -from __future__ import unicode_literals - -import logging - -import pkgwat.api - -from hubs.utils import validators -from hubs.utils.packages import get_user_packages -from hubs.widgets.base import Widget -from hubs.widgets.view import RootWidgetView -from hubs.widgets.caching import CachedFunction - - -log = logging.getLogger('hubs.widgets') - - -class Bugzilla(Widget): - - name = "bugzilla" - label = "Bugzilla issues" - position = "right" - parameters = [dict( - name="username", - label="Username", - default=None, - validator=validators.Username, - help="A FAS username.", - ), dict( - name="max", - label="Max number of issues", - default=3, - validator=validators.Integer, - help="The maximum number of issues to display.", - )] - - -class BaseView(RootWidgetView): - - def get_context(self, instance, *args, **kwargs): - get_issues = GetIssues(instance) - return dict( - username=instance.config["username"], - issues=get_issues() or [] - ) - - -class GetIssues(CachedFunction): - """Returns data for Bugzilla widget. - - Queries Bugzilla for issues on the user's packages. - """ - - def execute(self): - username = self.instance.config["username"] - max_num = int(self.instance.config.get("max", 3)) - owned = get_user_packages(username) - issues = [] - for pkg_name in owned: - if len(issues) == max_num: - break - try: - pkg_details = pkgwat.api.bugs(pkg_name) - except ValueError: - # Usually a JSON decoding error. Fail now and don't cache. - return None - for row in pkg_details['rows']: - if len(issues) == max_num: - break - issues.append( - dict( - id=row['id'], - title=row['description'], - pkg_name=pkg_name, - ) - ) - return issues - - def should_invalidate(self, message): - try: - component = message['msg']['bug']['component'] - except KeyError: - return False - username = self.instance.config["username"] - owned = get_user_packages(username) - return (component in owned) diff --git a/hubs/widgets/bugzilla/templates/root.html b/hubs/widgets/bugzilla/templates/root.html deleted file mode 100644 index d88d6bd..0000000 --- a/hubs/widgets/bugzilla/templates/root.html +++ /dev/null @@ -1,17 +0,0 @@ - diff --git a/hubs/widgets/githubissues/__init__.py b/hubs/widgets/githubissues/__init__.py deleted file mode 100644 index 6140a62..0000000 --- a/hubs/widgets/githubissues/__init__.py +++ /dev/null @@ -1,104 +0,0 @@ -from __future__ import unicode_literals - -import requests - -from hubs.utils import validators -from hubs.widgets.base import Widget -from hubs.widgets.view import RootWidgetView -from hubs.widgets.caching import CachedFunction - - -# TODO: use a checkbox set to select which repos to use from the hub's -# configuration. - - -class GitHubIssues(Widget): - - name = "githubissues" - label = "Github: Newest Open Tickets" - position = "right" - parameters = [ - dict( - name="org", - label="Username", - default=None, - validator=validators.GithubOrganization, - help="Github Organization or username", - ), dict( - name="repo", - label="Repository", - default=None, - validator=validators.GithubRepo, - help="Github repository", - ), dict( - name="display_number", - label="Number of tickets", - default=10, - validator=validators.Integer, - help="The number of tickets to display.", - )] - - def validate_parameters(self, values): - config = {} - config["org"] = \ - validators.GithubOrganization(values["org"]) - config["repo"] = \ - validators.GithubRepo('/'.join([values["org"], - values["repo"]])) - config["display_number"] = \ - validators.Integer(values["display_number"]) - return config - - -class BaseView(RootWidgetView): - - def get_context(self, instance, *args, **kwargs): - get_issues = GetIssues(instance) - return dict( - org=instance.config["org"], - repo=instance.config["repo"], - display_number=instance.config["display_number"], - all_issues=get_issues(), - ) - - -class GetIssues(CachedFunction): - ''' Data for Github Issues widget. Queries github api for issues ''' - - def execute(self): - org = self.instance.config["org"] - repo = self.instance.config["repo"] - display_number = int(self.instance.config["display_number"]) - url = '/'.join(['https://api.github.com/repos', org, repo, "issues"]) - issues = requests.get(url).json() - - all_issues = [] - for issue in issues[:display_number]: - issue_details = {} - issue_details['num'] = issue['number'] - issue_details['title'] = issue['title'] - issue_details['openedby'] = issue['user']['login'] - - issue_assignee = None - if issue['assignee'] is not None: - issue_assignee = issue['assignee']['login'] - - issue_details['assignee'] = issue_assignee - all_issues.append(issue_details) - - all_issues.reverse() - return all_issues - - def should_invalidate(self, message): - if ".github.issue." not in message["topic"]: - return False - try: - org = message['msg']['organization'] - repo = message['msg']['repository'] - except KeyError: - return False - return ( - org == self.instance.config["org"] - and - repo == self.instance.config["repo"] - ) diff --git a/hubs/widgets/githubissues/templates/root.html b/hubs/widgets/githubissues/templates/root.html deleted file mode 100644 index 4200aca..0000000 --- a/hubs/widgets/githubissues/templates/root.html +++ /dev/null @@ -1,40 +0,0 @@ - - -
-
All Issues
-
diff --git a/hubs/widgets/newestissues/__init__.py b/hubs/widgets/newestissues/__init__.py new file mode 100644 index 0000000..99ec123 --- /dev/null +++ b/hubs/widgets/newestissues/__init__.py @@ -0,0 +1,104 @@ +from __future__ import unicode_literals + +from hubs.utils import pagure +from hubs.utils.github import github_issues +from hubs.utils.fedmsg import get_fedmsg_config +from hubs.widgets.base import Widget +from hubs.widgets.view import RootWidgetView +from hubs.widgets.caching import CachedFunction + +fedmsg_config = get_fedmsg_config() + + +class NewestIssues(Widget): + + name = "newestissues" + label = "Newest Open Issues" + position = "right" + hub_types = ['team'] + + +class BaseView(RootWidgetView): + + def get_context(self, instance, *args, **kwargs): + get_pagure_issues = GetPagureIssues(instance) + get_github_issues = GetGithubIssues(instance) + + all_issues = get_github_issues() + get_pagure_issues() + + all_issues = sorted(all_issues, + key=lambda x: x["issue_opened_date"], + reverse=True) + + return dict(all_issues=all_issues) + + +class GetPagureIssues(CachedFunction): + def __init__(self, instance): + self.instance = instance + self.invalidate() + + def execute(self): + all_issues = list() + for repo in self.instance.hub.config["pagure"]: + try: + issues = pagure.pagure_issues(repo) + except ValueError: + continue + + for issue in issues: + all_issues.append(issue) + + return all_issues + + def should_invalidate(self, message): + pagure_repos = self.instance.hub.config["pagure"] + if ".pagure.issue." in message["topic"]: + try: + repo = message['msg']['project']['name'] + except KeyError: + print("KeyError") + return False + return (repo in pagure_repos) + elif message["topic"].endswith('.hubs.hub.updated'): + if ("pagure" in message["msg"]["changed_keys"]): + hub_id = message["msg"]["hub_id"] + if hub_id == self.instance.hub.id: + return True + else: + return False + else: + return False + + +class GetGithubIssues(CachedFunction): + def __init__(self, instance): + self.instance = instance + self.invalidate() + + def execute(self): + all_issues = list() + + for repo in self.instance.hub.config["github"]: + for issue in github_issues(repo, token=None): + all_issues.append(issue) + + return all_issues + + def should_invalidate(self, message): + github_repos = self.instance.hub.config["github"] + if ".github.issue." in message["topic"]: + try: + repo = message['msg']['repository']['fullname'] + except KeyError: + return False + return (repo in github_repos) + elif message["topic"].endswith('.hubs.hub.updated'): + if ("github" in message["msg"]["changed_keys"]): + hub_id = message["msg"]["hub_id"] + if hub_id == self.instance.hub.id: + return True + else: + return False + else: + return False diff --git a/hubs/widgets/newestissues/templates/root.html b/hubs/widgets/newestissues/templates/root.html new file mode 100644 index 0000000..18b53eb --- /dev/null +++ b/hubs/widgets/newestissues/templates/root.html @@ -0,0 +1,28 @@ + \ No newline at end of file diff --git a/hubs/widgets/pagureissues/__init__.py b/hubs/widgets/pagureissues/__init__.py deleted file mode 100644 index 4afcaed..0000000 --- a/hubs/widgets/pagureissues/__init__.py +++ /dev/null @@ -1,96 +0,0 @@ -from __future__ import unicode_literals - -import requests - -from hubs.utils import validators, pagure -from hubs.widgets.base import Widget -from hubs.widgets.view import RootWidgetView -from hubs.widgets.caching import CachedFunction - -# TODO: use a checkbox set to select which repos to use from the hub's -# configuration. - - -class PagureIssues(Widget): - - name = "pagureissues" - label = "Pagure: Newest Open Tickets" - position = "right" - parameters = [ - dict( - name="repo", - label="Repository", - default=None, - validator=validators.PagureRepo, - help="Pagure repo name", - )] - - -class BaseView(RootWidgetView): - - def get_context(self, instance, *args, **kwargs): - get_issues = GetIssues(instance) - context = dict( - repo=instance.config["repo"], - ) - context.update(get_issues()) - return context - - -class GetIssues(CachedFunction): - ''' Data for pagure issues widget - Queries Pagure api for issues ''' - - def execute(self): - repo = self.instance.config["repo"] - - url = '/'.join([pagure.PAGURE_URL, "api", "0", repo, "issues"]) - issue_response = requests.get(url) - data = issue_response.json() - total = data['total_issues'] - - all_issues = list() - for issue in data['issues']: - - issue_assignee = None - if issue['assignee']: - issue_assignee = issue['assignee']['name'] - - issue_project_user = None - if 'project' in issue: - issue_project_name = issue['project']['name'] - if issue['project']['parent']: - issue_project_user = issue['project']['user']['username'] - else: - if '/' in repo: - issue_project_name, issue_project_user = \ - repo.split('/', 1) - else: - issue_project_name = repo - - all_issues.append( - dict( - issue_project_name=issue_project_name, - issue_project_user=issue_project_user, - issue_id=issue['id'], - issue_title=issue['title'][:45], - issue_title_full=issue['title'], - issue_openedby=issue['user']['name'], - issue_assignee=issue_assignee, - ) - ) - - all_issues.reverse() - return dict( - total=total, - all_issues=all_issues, - ) - - def should_invalidate(self, message): - if ".pagure.issue." not in message["topic"]: - return False - try: - repo = message['msg']['project']['name'] - except KeyError: - return False - return (repo == self.instance.config['repo']) diff --git a/hubs/widgets/pagureissues/templates/root.html b/hubs/widgets/pagureissues/templates/root.html deleted file mode 100644 index 53823b7..0000000 --- a/hubs/widgets/pagureissues/templates/root.html +++ /dev/null @@ -1,49 +0,0 @@ - - All Issues - -
- - diff --git a/populate.py b/populate.py index 08330cf..2927bc6 100755 --- a/populate.py +++ b/populate.py @@ -23,6 +23,8 @@ hubs.widgets.registry.register_list([ "hubs.widgets.meetings:Meetings", "hubs.widgets.feed:Feed", "hubs.widgets.memberships:Memberships", + "hubs.widgets.newestissues:NewestIssues", + ]) @@ -194,7 +196,8 @@ def create_teams(): hub.config["mailing_list"] = 'design-team@lists.fedoraproject.org' hub.config["chat_domain"] = 'irc.freenode.net' hub.config["chat_channel"] = '#fedora-design' - hub.config["pagure"] = ['design'] + hub.config["pagure"] = ['design', "fedora-badges"] + hub.config["github"] = ['fedora-infra/bodhi'] hub.config["calendar"] = 'design' widget = hubs.models.Widget(plugin='memberships', index=1) @@ -203,6 +206,9 @@ def create_teams(): widget = hubs.models.Widget(plugin='meetings', index=2) hub.widgets.append(widget) + widget = hubs.models.Widget(plugin='newestissues', index=3) + hub.widgets.append(widget) + widget = hubs.models.Widget(plugin='feed', index=1, left=True) hub.widgets.append(widget)