We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
enhance release page with moar links
Like this page https://bodhi.fedoraproject.org/releases/F23

Let's extend the table on the left to include some more stats:
- number of updates in stable
- number of updates in testing
- number of security updates
- number of bugfix updates

etc.. every type, status, and request.. let's put them there.

_importantly_, let's make each one of those entries _also_ a link to a page that shows you the _list_ of each of those kinds of updates, so people don't have to manually frob the querystring to find what they want.


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `bodhi/services/releases.py`
Content:
```
1 # This program is free software; you can redistribute it and/or
2 # modify it under the terms of the GNU General Public License
3 # as published by the Free Software Foundation; either version 2
4 # of the License, or (at your option) any later version.
5 #
6 # This program is distributed in the hope that it will be useful,
7 # but WITHOUT ANY WARRANTY; without even the implied warranty of
8 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
9 # GNU General Public License for more details.
10 #
11 # You should have received a copy of the GNU General Public License
12 # along with this program; if not, write to the Free Software
13 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
14 
15 import math
16 
17 from cornice import Service
18 from pyramid.exceptions import HTTPNotFound
19 from sqlalchemy import func, distinct
20 from sqlalchemy.sql import or_
21 
22 from bodhi import log
23 from bodhi.models import Update, Build, Package, Release
24 import bodhi.schemas
25 import bodhi.security
26 from bodhi.validators import (
27     validate_tags,
28     validate_enums,
29     validate_updates,
30     validate_packages,
31     validate_release,
32 )
33 
34 
35 release = Service(name='release', path='/releases/{name}',
36                   description='Fedora Releases',
37                   cors_origins=bodhi.security.cors_origins_ro)
38 releases = Service(name='releases', path='/releases/',
39                    description='Fedora Releases',
40                    # Note, this 'rw' is not a typo.  the @comments service has
41                    # a ``post`` section at the bottom.
42                    cors_origins=bodhi.security.cors_origins_rw)
43 
44 @release.get(accept="text/html", renderer="release.html")
45 def get_release_html(request):
46     id = request.matchdict.get('name')
47     release = Release.get(id, request.db)
48     if not release:
49         request.errors.add('body', 'name', 'No such release')
50         request.errors.status = HTTPNotFound.code
51     updates = request.db.query(Update).filter(
52         Update.release==release).order_by(
53             Update.date_submitted.desc())
54 
55     updates_count = request.db.query(Update.date_submitted, Update.type).filter(
56         Update.release==release).order_by(
57             Update.date_submitted.desc())
58 
59     date_commits = {}
60     dates = set()
61 
62     for update in updates_count.all():
63         d = update.date_submitted
64         yearmonth = str(d.year) + '/' + str(d.month).zfill(2)
65         dates.add(yearmonth)
66         if not update.type.description in date_commits:
67             date_commits[update.type.description] = {}
68         if yearmonth in date_commits[update.type.description]:
69             date_commits[update.type.description][yearmonth] += 1
70         else:
71             date_commits[update.type.description][yearmonth] = 0
72 
73     return dict(release=release,
74                 latest_updates=updates.limit(25).all(),
75                 count=updates.count(),
76                 date_commits=date_commits,
77                 dates = sorted(dates))
78 
79 @release.get(accept=('application/json', 'text/json'), renderer='json')
80 @release.get(accept=('application/javascript'), renderer='jsonp')
81 def get_release_json(request):
82     id = request.matchdict.get('name')
83     release = Release.get(id, request.db)
84     if not release:
85         request.errors.add('body', 'name', 'No such release')
86         request.errors.status = HTTPNotFound.code
87     return release
88 
89 @releases.get(accept="text/html", schema=bodhi.schemas.ListReleaseSchema,
90               renderer='releases.html',
91               validators=(validate_release, validate_updates,
92                           validate_packages))
93 def query_releases_html(request):
94     def collect_releases(releases):
95         x = {}
96         for r in releases:
97             if r['state'] in x:
98                 x[r['state']].append(r)
99             else:
100                 x[r['state']] = [r]
101         return x
102 
103     db = request.db
104     releases = db.query(Release).order_by(Release.id.desc()).all()
105     return dict(releases=collect_releases(releases))
106 
107 @releases.get(accept=('application/json', 'text/json'),
108               schema=bodhi.schemas.ListReleaseSchema, renderer='json',
109               validators=(validate_release, validate_updates,
110                           validate_packages))
111 def query_releases_json(request):
112     db = request.db
113     data = request.validated
114     query = db.query(Release)
115 
116     name = data.get('name')
117     if name is not None:
118         query = query.filter(Release.name.like(name))
119 
120     updates = data.get('updates')
121     if updates is not None:
122         query = query.join(Release.builds).join(Build.update)
123         args = \
124             [Update.title == update.title for update in updates] +\
125             [Update.alias == update.alias for update in updates]
126         query = query.filter(or_(*args))
127 
128     packages = data.get('packages')
129     if packages is not None:
130         query = query.join(Release.builds).join(Build.package)
131         query = query.filter(or_(*[Package.id == p.id for p in packages]))
132 
133     # We can't use ``query.count()`` here because it is naive with respect to
134     # all the joins that we're doing above.
135     count_query = query.with_labels().statement\
136         .with_only_columns([func.count(distinct(Release.id))])\
137         .order_by(None)
138     total = db.execute(count_query).scalar()
139 
140     page = data.get('page')
141     rows_per_page = data.get('rows_per_page')
142     pages = int(math.ceil(total / float(rows_per_page)))
143     query = query.offset(rows_per_page * (page - 1)).limit(rows_per_page)
144 
145     return dict(
146         releases=query.all(),
147         page=page,
148         pages=pages,
149         rows_per_page=rows_per_page,
150         total=total,
151     )
152 
153 @releases.post(schema=bodhi.schemas.SaveReleaseSchema,
154                acl=bodhi.security.admin_only_acl, renderer='json',
155                validators=(validate_tags, validate_enums)
156                )
157 def save_release(request):
158     """Save a release
159 
160     This entails either creating a new release, or editing an existing one. To
161     edit an existing release, the release's original name must be specified in
162     the ``edited`` parameter.
163     """
164     data = request.validated
165 
166     edited = data.pop("edited", None)
167 
168     # This has already been validated at this point, but we need to ditch
169     # it since the models don't care about a csrf argument.
170     data.pop('csrf_token')
171 
172     try:
173         if edited is None:
174             log.info("Creating a new release: %s" % data['name'])
175             r = Release(**data)
176 
177         else:
178             log.info("Editing release: %s" % edited)
179             r = request.db.query(Release).filter(Release.name==edited).one()
180             for k, v in data.items():
181                 setattr(r, k, v)
182 
183     except Exception as e:
184         log.exception(e)
185         request.errors.add('body', 'release',
186                            'Unable to create update: %s' % e)
187         return
188 
189 
190     request.db.add(r)
191     request.db.flush()
192 
193     return r
194
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/bodhi/services/releases.py b/bodhi/services/releases.py
--- a/bodhi/services/releases.py
+++ b/bodhi/services/releases.py
@@ -20,7 +20,14 @@
 from sqlalchemy.sql import or_
 
 from bodhi import log
-from bodhi.models import Update, Build, Package, Release
+from bodhi.models import (
+    Update,
+    UpdateStatus,
+    UpdateType,
+    Build,
+    Package,
+    Release,
+)
 import bodhi.schemas
 import bodhi.security
 from bodhi.validators import (
@@ -70,11 +77,46 @@
         else:
             date_commits[update.type.description][yearmonth] = 0
 
+    base_count_query = request.db.query(Update)\
+        .filter(Update.release==release)
+
+    num_updates_pending = base_count_query\
+        .filter(Update.status==UpdateStatus.pending).count()
+    num_updates_testing = base_count_query\
+        .filter(Update.status==UpdateStatus.testing).count()
+    num_updates_stable = base_count_query\
+        .filter(Update.status==UpdateStatus.stable).count()
+    num_updates_unpushed = base_count_query\
+        .filter(Update.status==UpdateStatus.unpushed).count()
+    num_updates_obsolete = base_count_query\
+        .filter(Update.status==UpdateStatus.obsolete).count()
+
+    num_updates_security = base_count_query\
+        .filter(Update.type==UpdateType.security).count()
+    num_updates_bugfix = base_count_query\
+        .filter(Update.type==UpdateType.bugfix).count()
+    num_updates_enhancement = base_count_query\
+        .filter(Update.type==UpdateType.enhancement).count()
+    num_updates_newpackage = base_count_query\
+        .filter(Update.type==UpdateType.newpackage).count()
+
     return dict(release=release,
                 latest_updates=updates.limit(25).all(),
                 count=updates.count(),
                 date_commits=date_commits,
-                dates = sorted(dates))
+                dates=sorted(dates),
+
+                num_updates_pending=num_updates_pending,
+                num_updates_testing=num_updates_testing,
+                num_updates_stable=num_updates_stable,
+                num_updates_unpushed=num_updates_unpushed,
+                num_updates_obsolete=num_updates_obsolete,
+
+                num_updates_security=num_updates_security,
+                num_updates_bugfix=num_updates_bugfix,
+                num_updates_enhancement=num_updates_enhancement,
+                num_updates_newpackage=num_updates_newpackage,
+                )
 
 @release.get(accept=('application/json', 'text/json'), renderer='json')
 @release.get(accept=('application/javascript'), renderer='jsonp')

{"golden_diff": "diff --git a/bodhi/services/releases.py b/bodhi/services/releases.py\n--- a/bodhi/services/releases.py\n+++ b/bodhi/services/releases.py\n@@ -20,7 +20,14 @@\n from sqlalchemy.sql import or_\n \n from bodhi import log\n-from bodhi.models import Update, Build, Package, Release\n+from bodhi.models import (\n+    Update,\n+    UpdateStatus,\n+    UpdateType,\n+    Build,\n+    Package,\n+    Release,\n+)\n import bodhi.schemas\n import bodhi.security\n from bodhi.validators import (\n@@ -70,11 +77,46 @@\n         else:\n             date_commits[update.type.description][yearmonth] = 0\n \n+    base_count_query = request.db.query(Update)\\\n+        .filter(Update.release==release)\n+\n+    num_updates_pending = base_count_query\\\n+        .filter(Update.status==UpdateStatus.pending).count()\n+    num_updates_testing = base_count_query\\\n+        .filter(Update.status==UpdateStatus.testing).count()\n+    num_updates_stable = base_count_query\\\n+        .filter(Update.status==UpdateStatus.stable).count()\n+    num_updates_unpushed = base_count_query\\\n+        .filter(Update.status==UpdateStatus.unpushed).count()\n+    num_updates_obsolete = base_count_query\\\n+        .filter(Update.status==UpdateStatus.obsolete).count()\n+\n+    num_updates_security = base_count_query\\\n+        .filter(Update.type==UpdateType.security).count()\n+    num_updates_bugfix = base_count_query\\\n+        .filter(Update.type==UpdateType.bugfix).count()\n+    num_updates_enhancement = base_count_query\\\n+        .filter(Update.type==UpdateType.enhancement).count()\n+    num_updates_newpackage = base_count_query\\\n+        .filter(Update.type==UpdateType.newpackage).count()\n+\n     return dict(release=release,\n                 latest_updates=updates.limit(25).all(),\n                 count=updates.count(),\n                 date_commits=date_commits,\n-                dates = sorted(dates))\n+                dates=sorted(dates),\n+\n+                num_updates_pending=num_updates_pending,\n+                num_updates_testing=num_updates_testing,\n+                num_updates_stable=num_updates_stable,\n+                num_updates_unpushed=num_updates_unpushed,\n+                num_updates_obsolete=num_updates_obsolete,\n+\n+                num_updates_security=num_updates_security,\n+                num_updates_bugfix=num_updates_bugfix,\n+                num_updates_enhancement=num_updates_enhancement,\n+                num_updates_newpackage=num_updates_newpackage,\n+                )\n \n @release.get(accept=('application/json', 'text/json'), renderer='json')\n @release.get(accept=('application/javascript'), renderer='jsonp')\n", "issue": "enhance release page with moar links\nLike this page https://bodhi.fedoraproject.org/releases/F23\n\nLet's extend the table on the left to include some more stats:\n- number of updates in stable\n- number of updates in testing\n- number of security updates\n- number of bugfix updates\n\netc.. every type, status, and request.. let's put them there.\n\n_importantly_, let's make each one of those entries _also_ a link to a page that shows you the _list_ of each of those kinds of updates, so people don't have to manually frob the querystring to find what they want.\n\n", "before_files": [{"content": "# This program is free software; you can redistribute it and/or\n# modify it under the terms of the GNU General Public License\n# as published by the Free Software Foundation; either version 2\n# of the License, or (at your option) any later version.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU General Public License for more details.\n#\n# You should have received a copy of the GNU General Public License\n# along with this program; if not, write to the Free Software\n# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.\n\nimport math\n\nfrom cornice import Service\nfrom pyramid.exceptions import HTTPNotFound\nfrom sqlalchemy import func, distinct\nfrom sqlalchemy.sql import or_\n\nfrom bodhi import log\nfrom bodhi.models import Update, Build, Package, Release\nimport bodhi.schemas\nimport bodhi.security\nfrom bodhi.validators import (\n    validate_tags,\n    validate_enums,\n    validate_updates,\n    validate_packages,\n    validate_release,\n)\n\n\nrelease = Service(name='release', path='/releases/{name}',\n                  description='Fedora Releases',\n                  cors_origins=bodhi.security.cors_origins_ro)\nreleases = Service(name='releases', path='/releases/',\n                   description='Fedora Releases',\n                   # Note, this 'rw' is not a typo.  the @comments service has\n                   # a ``post`` section at the bottom.\n                   cors_origins=bodhi.security.cors_origins_rw)\n\n@release.get(accept=\"text/html\", renderer=\"release.html\")\ndef get_release_html(request):\n    id = request.matchdict.get('name')\n    release = Release.get(id, request.db)\n    if not release:\n        request.errors.add('body', 'name', 'No such release')\n        request.errors.status = HTTPNotFound.code\n    updates = request.db.query(Update).filter(\n        Update.release==release).order_by(\n            Update.date_submitted.desc())\n\n    updates_count = request.db.query(Update.date_submitted, Update.type).filter(\n        Update.release==release).order_by(\n            Update.date_submitted.desc())\n\n    date_commits = {}\n    dates = set()\n\n    for update in updates_count.all():\n        d = update.date_submitted\n        yearmonth = str(d.year) + '/' + str(d.month).zfill(2)\n        dates.add(yearmonth)\n        if not update.type.description in date_commits:\n            date_commits[update.type.description] = {}\n        if yearmonth in date_commits[update.type.description]:\n            date_commits[update.type.description][yearmonth] += 1\n        else:\n            date_commits[update.type.description][yearmonth] = 0\n\n    return dict(release=release,\n                latest_updates=updates.limit(25).all(),\n                count=updates.count(),\n                date_commits=date_commits,\n                dates = sorted(dates))\n\n@release.get(accept=('application/json', 'text/json'), renderer='json')\n@release.get(accept=('application/javascript'), renderer='jsonp')\ndef get_release_json(request):\n    id = request.matchdict.get('name')\n    release = Release.get(id, request.db)\n    if not release:\n        request.errors.add('body', 'name', 'No such release')\n        request.errors.status = HTTPNotFound.code\n    return release\n\n@releases.get(accept=\"text/html\", schema=bodhi.schemas.ListReleaseSchema,\n              renderer='releases.html',\n              validators=(validate_release, validate_updates,\n                          validate_packages))\ndef query_releases_html(request):\n    def collect_releases(releases):\n        x = {}\n        for r in releases:\n            if r['state'] in x:\n                x[r['state']].append(r)\n            else:\n                x[r['state']] = [r]\n        return x\n\n    db = request.db\n    releases = db.query(Release).order_by(Release.id.desc()).all()\n    return dict(releases=collect_releases(releases))\n\n@releases.get(accept=('application/json', 'text/json'),\n              schema=bodhi.schemas.ListReleaseSchema, renderer='json',\n              validators=(validate_release, validate_updates,\n                          validate_packages))\ndef query_releases_json(request):\n    db = request.db\n    data = request.validated\n    query = db.query(Release)\n\n    name = data.get('name')\n    if name is not None:\n        query = query.filter(Release.name.like(name))\n\n    updates = data.get('updates')\n    if updates is not None:\n        query = query.join(Release.builds).join(Build.update)\n        args = \\\n            [Update.title == update.title for update in updates] +\\\n            [Update.alias == update.alias for update in updates]\n        query = query.filter(or_(*args))\n\n    packages = data.get('packages')\n    if packages is not None:\n        query = query.join(Release.builds).join(Build.package)\n        query = query.filter(or_(*[Package.id == p.id for p in packages]))\n\n    # We can't use ``query.count()`` here because it is naive with respect to\n    # all the joins that we're doing above.\n    count_query = query.with_labels().statement\\\n        .with_only_columns([func.count(distinct(Release.id))])\\\n        .order_by(None)\n    total = db.execute(count_query).scalar()\n\n    page = data.get('page')\n    rows_per_page = data.get('rows_per_page')\n    pages = int(math.ceil(total / float(rows_per_page)))\n    query = query.offset(rows_per_page * (page - 1)).limit(rows_per_page)\n\n    return dict(\n        releases=query.all(),\n        page=page,\n        pages=pages,\n        rows_per_page=rows_per_page,\n        total=total,\n    )\n\n@releases.post(schema=bodhi.schemas.SaveReleaseSchema,\n               acl=bodhi.security.admin_only_acl, renderer='json',\n               validators=(validate_tags, validate_enums)\n               )\ndef save_release(request):\n    \"\"\"Save a release\n\n    This entails either creating a new release, or editing an existing one. To\n    edit an existing release, the release's original name must be specified in\n    the ``edited`` parameter.\n    \"\"\"\n    data = request.validated\n\n    edited = data.pop(\"edited\", None)\n\n    # This has already been validated at this point, but we need to ditch\n    # it since the models don't care about a csrf argument.\n    data.pop('csrf_token')\n\n    try:\n        if edited is None:\n            log.info(\"Creating a new release: %s\" % data['name'])\n            r = Release(**data)\n\n        else:\n            log.info(\"Editing release: %s\" % edited)\n            r = request.db.query(Release).filter(Release.name==edited).one()\n            for k, v in data.items():\n                setattr(r, k, v)\n\n    except Exception as e:\n        log.exception(e)\n        request.errors.add('body', 'release',\n                           'Unable to create update: %s' % e)\n        return\n\n\n    request.db.add(r)\n    request.db.flush()\n\n    return r\n", "path": "bodhi/services/releases.py"}], "after_files": [{"content": "# This program is free software; you can redistribute it and/or\n# modify it under the terms of the GNU General Public License\n# as published by the Free Software Foundation; either version 2\n# of the License, or (at your option) any later version.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU General Public License for more details.\n#\n# You should have received a copy of the GNU General Public License\n# along with this program; if not, write to the Free Software\n# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.\n\nimport math\n\nfrom cornice import Service\nfrom pyramid.exceptions import HTTPNotFound\nfrom sqlalchemy import func, distinct\nfrom sqlalchemy.sql import or_\n\nfrom bodhi import log\nfrom bodhi.models import (\n    Update,\n    UpdateStatus,\n    UpdateType,\n    Build,\n    Package,\n    Release,\n)\nimport bodhi.schemas\nimport bodhi.security\nfrom bodhi.validators import (\n    validate_tags,\n    validate_enums,\n    validate_updates,\n    validate_packages,\n    validate_release,\n)\n\n\nrelease = Service(name='release', path='/releases/{name}',\n                  description='Fedora Releases',\n                  cors_origins=bodhi.security.cors_origins_ro)\nreleases = Service(name='releases', path='/releases/',\n                   description='Fedora Releases',\n                   # Note, this 'rw' is not a typo.  the @comments service has\n                   # a ``post`` section at the bottom.\n                   cors_origins=bodhi.security.cors_origins_rw)\n\n@release.get(accept=\"text/html\", renderer=\"release.html\")\ndef get_release_html(request):\n    id = request.matchdict.get('name')\n    release = Release.get(id, request.db)\n    if not release:\n        request.errors.add('body', 'name', 'No such release')\n        request.errors.status = HTTPNotFound.code\n    updates = request.db.query(Update).filter(\n        Update.release==release).order_by(\n            Update.date_submitted.desc())\n\n    updates_count = request.db.query(Update.date_submitted, Update.type).filter(\n        Update.release==release).order_by(\n            Update.date_submitted.desc())\n\n    date_commits = {}\n    dates = set()\n\n    for update in updates_count.all():\n        d = update.date_submitted\n        yearmonth = str(d.year) + '/' + str(d.month).zfill(2)\n        dates.add(yearmonth)\n        if not update.type.description in date_commits:\n            date_commits[update.type.description] = {}\n        if yearmonth in date_commits[update.type.description]:\n            date_commits[update.type.description][yearmonth] += 1\n        else:\n            date_commits[update.type.description][yearmonth] = 0\n\n    base_count_query = request.db.query(Update)\\\n        .filter(Update.release==release)\n\n    num_updates_pending = base_count_query\\\n        .filter(Update.status==UpdateStatus.pending).count()\n    num_updates_testing = base_count_query\\\n        .filter(Update.status==UpdateStatus.testing).count()\n    num_updates_stable = base_count_query\\\n        .filter(Update.status==UpdateStatus.stable).count()\n    num_updates_unpushed = base_count_query\\\n        .filter(Update.status==UpdateStatus.unpushed).count()\n    num_updates_obsolete = base_count_query\\\n        .filter(Update.status==UpdateStatus.obsolete).count()\n\n    num_updates_security = base_count_query\\\n        .filter(Update.type==UpdateType.security).count()\n    num_updates_bugfix = base_count_query\\\n        .filter(Update.type==UpdateType.bugfix).count()\n    num_updates_enhancement = base_count_query\\\n        .filter(Update.type==UpdateType.enhancement).count()\n    num_updates_newpackage = base_count_query\\\n        .filter(Update.type==UpdateType.newpackage).count()\n\n    return dict(release=release,\n                latest_updates=updates.limit(25).all(),\n                count=updates.count(),\n                date_commits=date_commits,\n                dates=sorted(dates),\n\n                num_updates_pending=num_updates_pending,\n                num_updates_testing=num_updates_testing,\n                num_updates_stable=num_updates_stable,\n                num_updates_unpushed=num_updates_unpushed,\n                num_updates_obsolete=num_updates_obsolete,\n\n                num_updates_security=num_updates_security,\n                num_updates_bugfix=num_updates_bugfix,\n                num_updates_enhancement=num_updates_enhancement,\n                num_updates_newpackage=num_updates_newpackage,\n                )\n\n@release.get(accept=('application/json', 'text/json'), renderer='json')\n@release.get(accept=('application/javascript'), renderer='jsonp')\ndef get_release_json(request):\n    id = request.matchdict.get('name')\n    release = Release.get(id, request.db)\n    if not release:\n        request.errors.add('body', 'name', 'No such release')\n        request.errors.status = HTTPNotFound.code\n    return release\n\n@releases.get(accept=\"text/html\", schema=bodhi.schemas.ListReleaseSchema,\n              renderer='releases.html',\n              validators=(validate_release, validate_updates,\n                          validate_packages))\ndef query_releases_html(request):\n    def collect_releases(releases):\n        x = {}\n        for r in releases:\n            if r['state'] in x:\n                x[r['state']].append(r)\n            else:\n                x[r['state']] = [r]\n        return x\n\n    db = request.db\n    releases = db.query(Release).order_by(Release.id.desc()).all()\n    return dict(releases=collect_releases(releases))\n\n@releases.get(accept=('application/json', 'text/json'),\n              schema=bodhi.schemas.ListReleaseSchema, renderer='json',\n              validators=(validate_release, validate_updates,\n                          validate_packages))\ndef query_releases_json(request):\n    db = request.db\n    data = request.validated\n    query = db.query(Release)\n\n    name = data.get('name')\n    if name is not None:\n        query = query.filter(Release.name.like(name))\n\n    updates = data.get('updates')\n    if updates is not None:\n        query = query.join(Release.builds).join(Build.update)\n        args = \\\n            [Update.title == update.title for update in updates] +\\\n            [Update.alias == update.alias for update in updates]\n        query = query.filter(or_(*args))\n\n    packages = data.get('packages')\n    if packages is not None:\n        query = query.join(Release.builds).join(Build.package)\n        query = query.filter(or_(*[Package.id == p.id for p in packages]))\n\n    # We can't use ``query.count()`` here because it is naive with respect to\n    # all the joins that we're doing above.\n    count_query = query.with_labels().statement\\\n        .with_only_columns([func.count(distinct(Release.id))])\\\n        .order_by(None)\n    total = db.execute(count_query).scalar()\n\n    page = data.get('page')\n    rows_per_page = data.get('rows_per_page')\n    pages = int(math.ceil(total / float(rows_per_page)))\n    query = query.offset(rows_per_page * (page - 1)).limit(rows_per_page)\n\n    return dict(\n        releases=query.all(),\n        page=page,\n        pages=pages,\n        rows_per_page=rows_per_page,\n        total=total,\n    )\n\n@releases.post(schema=bodhi.schemas.SaveReleaseSchema,\n               acl=bodhi.security.admin_only_acl, renderer='json',\n               validators=(validate_tags, validate_enums)\n               )\ndef save_release(request):\n    \"\"\"Save a release\n\n    This entails either creating a new release, or editing an existing one. To\n    edit an existing release, the release's original name must be specified in\n    the ``edited`` parameter.\n    \"\"\"\n    data = request.validated\n\n    edited = data.pop(\"edited\", None)\n\n    # This has already been validated at this point, but we need to ditch\n    # it since the models don't care about a csrf argument.\n    data.pop('csrf_token')\n\n    try:\n        if edited is None:\n            log.info(\"Creating a new release: %s\" % data['name'])\n            r = Release(**data)\n\n        else:\n            log.info(\"Editing release: %s\" % edited)\n            r = request.db.query(Release).filter(Release.name==edited).one()\n            for k, v in data.items():\n                setattr(r, k, v)\n\n    except Exception as e:\n        log.exception(e)\n        request.errors.add('body', 'release',\n                           'Unable to create update: %s' % e)\n        return\n\n\n    request.db.add(r)\n    request.db.flush()\n\n    return r\n", "path": "bodhi/services/releases.py"}]}