We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Add notification bubble on Main Nav for super-librarians # pending MRs
<!-- A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] -->

### Describe the problem that you'd like solved
<!-- A clear and concise description of what you want to happen. -->

Options are to add a bubble to the avatar and then a entry + bubble into the sub-navigation within the hamburger

![testing openlibrary org_merges_mode=closed](https://user-images.githubusercontent.com/978325/182037307-3ba26478-0b2e-4fc3-9676-a63fd4e87dba.png)

Or to add a bubble directly to the black IA topbar which when clicked goes directly to /merges

![testing openlibrary org_merges_mode=closed (1)](https://user-images.githubusercontent.com/978325/182037463-db0a2ca7-45c7-44ad-9238-146a5ea37daa.png)

```
.mr-notifications {
    position: absolute;
    z-index: 4;
    background: #02598b;
    color: white;
    border-radius: 8px;
    padding: 3px 7px;
    font-size: 12px;
    margin-left: 9px;
    margin-top: 35px;
    font-weight: bold;
 }
```

### Proposal & Constraints
<!-- What is the proposed solution / implementation? Is there a precedent of this approach succeeding elsewhere? -->

<!-- Which suggestions or requirements should be considered for how feature needs to appear or be implemented? -->

### Additional context
<!-- Add any other context or screenshots about the feature request here. -->

### Stakeholders
<!-- @ tag stakeholders of this bug -->




--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `openlibrary/core/edits.py`
Content:
```
1 import datetime
2 import json
3 from typing import Optional
4 import web
5 
6 from infogami.utils.view import public
7 
8 from openlibrary.i18n import gettext as _
9 
10 from . import db
11 
12 
13 @public
14 def get_status_for_view(status_code: int) -> str:
15     """Returns localized status string that corresponds with the given status code."""
16     if status_code == CommunityEditsQueue.STATUS['DECLINED']:
17         return _('Declined')
18     if status_code == CommunityEditsQueue.STATUS['PENDING']:
19         return _('Pending')
20     if status_code == CommunityEditsQueue.STATUS['MERGED']:
21         return _('Merged')
22     return _('Unknown')
23 
24 
25 class CommunityEditsQueue:
26 
27     """Schema
28     id: Primary identifier
29     submitter: username of person that made the request
30     reviewer: The username of the person who reviewed the request
31     url: URL of the merge request
32     status: Either "Pending", "Merged", or "Declined"
33     comment: Short note from reviewer (json blobs (can store timestamps, etc))
34     created: created timestamp
35     updated: update timestamp
36     """
37 
38     STATUS = {
39         'DECLINED': 0,
40         'PENDING': 1,
41         'MERGED': 2,
42     }
43 
44     MODES = {
45         'all': [STATUS['DECLINED'], STATUS['PENDING'], STATUS['MERGED']],
46         'open': [STATUS['PENDING']],
47         'closed': [STATUS['DECLINED'], STATUS['MERGED']],
48     }
49 
50     @classmethod
51     def get_requests(
52         cls,
53         limit: int = 50,
54         page: int = 1,
55         mode: str = 'all',
56         order: str = None,
57         **kwargs,
58     ):
59         oldb = db.get_db()
60 
61         query_kwargs = {
62             "limit": limit,
63             "offset": limit * (page - 1),
64             "vars": {**kwargs},
65         }
66 
67         query_kwargs['where'] = cls.where_clause(mode, **kwargs)
68 
69         if order:
70             query_kwargs['order'] = order
71         return oldb.select("community_edits_queue", **query_kwargs)
72 
73     @classmethod
74     def get_counts_by_mode(cls, mode='all', **kwargs):
75         oldb = db.get_db()
76 
77         query = 'SELECT count(*) from community_edits_queue'
78 
79         where_clause = cls.where_clause(mode, **kwargs)
80         if where_clause:
81             query = f'{query} WHERE {where_clause}'
82         return oldb.query(query, vars=kwargs)[0]['count']
83 
84     @classmethod
85     def where_clause(cls, mode, **kwargs):
86         wheres = []
87 
88         if kwargs.get('reviewer') is not None:
89             wheres.append(
90                 # if reviewer="" then get all unassigned MRs
91                 "reviewer IS NULL"
92                 if not kwargs.get('reviewer')
93                 else "reviewer=$reviewer"
94             )
95         if "submitter" in kwargs:
96             wheres.append(
97                 # If submitter not specified, default to any
98                 "submitter IS NOT NULL"
99                 if kwargs.get("submitter") is None
100                 else "submitter=$submitter"
101             )
102         if "url" in kwargs:
103             wheres.append("url=$url")
104         if "id" in kwargs:
105             wheres.append("id=$id")
106 
107         status_list = (
108             [f'status={status}' for status in cls.MODES[mode]] if mode != 'all' else []
109         )
110 
111         where_clause = ''
112 
113         if wheres:
114             where_clause = f'{" AND ".join(wheres)}'
115         if status_list:
116             status_query = f'({" OR ".join(status_list)})'
117             if where_clause:
118                 where_clause = f'{where_clause} AND {status_query}'
119             else:
120                 where_clause = status_query
121 
122         return where_clause
123 
124     @classmethod
125     def submit_work_merge_request(
126         cls,
127         work_ids: list[str],
128         submitter: str,
129         comment: str = None,
130         reviewer: str = None,
131         status: int = STATUS['PENDING'],
132     ):
133         """
134         Creates new work merge requests with the given work olids.
135 
136         Precondition: OLIDs in work_ids list must be sanitized and normalized.
137         """
138         url = f"/works/merge?records={','.join(work_ids)}"
139         if not cls.exists(url):
140             return cls.submit_request(
141                 url,
142                 submitter=submitter,
143                 comment=comment,
144                 reviewer=reviewer,
145                 status=status,
146                 title=cls.get_work_merge_title(work_ids),
147             )
148 
149     @staticmethod
150     def get_work_merge_title(olids):
151         title = None
152         for olid in olids:
153             book = web.ctx.site.get(f'/works/{olid}')
154             if book and book.title:
155                 title = book.title
156                 break
157         return title
158 
159     @classmethod
160     def submit_author_merge_request(cls, author_ids, submitter, comment=None):
161         if not comment:
162             # some default note from submitter
163             pass
164         # XXX IDs should be santiized & normalized
165         url = f"/authors/merge?key={'&key='.join(author_ids)}"
166         cls.submit_request(url, submitter=submitter, comment=comment)
167 
168     @classmethod
169     def submit_delete_request(cls, olid, submitter, comment=None):
170         if not comment:
171             # some default note from submitter
172             pass
173         url = f"{olid}/-/edit?m=delete"
174         cls.submit_request(cls, url, submitter=submitter, comment=comment)
175 
176     @classmethod
177     def submit_request(
178         cls,
179         url: str,
180         submitter: str,
181         reviewer: str = None,
182         status: int = STATUS['PENDING'],
183         comment: str = None,
184         title: str = None,
185     ):
186         """
187         Inserts a new record into the table.
188 
189         Preconditions: All data validations should be completed before calling this method.
190         """
191         oldb = db.get_db()
192 
193         comments = [cls.create_comment(submitter, comment)] if comment else []
194         json_comment = json.dumps({"comments": comments})
195 
196         return oldb.insert(
197             "community_edits_queue",
198             submitter=submitter,
199             reviewer=reviewer,
200             url=url,
201             status=status,
202             comments=json_comment,
203             title=title,
204         )
205 
206     @classmethod
207     def assign_request(
208         cls, rid: int, reviewer: Optional[str]
209     ) -> dict[str, Optional[str]]:
210         """Changes assignees to the request with the given ID.
211 
212         This method only modifies requests that are not closed.
213 
214         If the given reviewer is the same as the request's reviewer, nothing is
215         modified
216         """
217         request = cls.find_by_id(rid)
218 
219         if request['status'] not in cls.MODES['closed']:
220             if request['reviewer'] == reviewer:
221                 return {
222                     'status': 'error',
223                     'error': f'{reviewer} is already assigned to this request',
224                 }
225             oldb = db.get_db()
226 
227             oldb.update(
228                 "community_edits_queue",
229                 where="id=$rid",
230                 reviewer=reviewer,
231                 status=cls.STATUS['PENDING'],
232                 updated=datetime.datetime.utcnow(),
233                 vars={"rid": rid},
234             )
235             return {
236                 'reviewer': reviewer,
237                 'newStatus': get_status_for_view(cls.STATUS['PENDING']),
238             }
239         return {'status': 'error', 'error': 'This request has already been closed'}
240 
241     @classmethod
242     def unassign_request(cls, rid: int):
243         """
244         Changes status of given request to "Pending", and sets reviewer to None.
245         """
246         oldb = db.get_db()
247         oldb.update(
248             "community_edits_queue",
249             where="id=$rid",
250             status=cls.STATUS['PENDING'],
251             reviewer=None,
252             updated=datetime.datetime.utcnow(),
253             vars={"rid": rid},
254         )
255 
256     @classmethod
257     def update_request_status(
258         cls, rid: int, status: int, reviewer: str, comment: str = None
259     ) -> int:
260         """
261         Changes the status of the request with the given rid.
262 
263         If a comment is included, existing comments list for this request are fetched and
264         the new comment is appended.
265         """
266         oldb = db.get_db()
267 
268         update_kwargs = {}
269 
270         # XXX Trim whitespace from comment first
271         if comment:
272             comments = cls.get_comments(rid)
273             comments['comments'].append(cls.create_comment(reviewer, comment))
274             update_kwargs['comments'] = json.dumps(comments)
275 
276         return oldb.update(
277             "community_edits_queue",
278             where="id=$rid",
279             status=status,
280             reviewer=reviewer,
281             updated=datetime.datetime.utcnow(),
282             vars={"rid": rid},
283             **update_kwargs,
284         )
285 
286     @classmethod
287     def comment_request(cls, rid: int, username: str, comment: str) -> int:
288         oldb = db.get_db()
289 
290         comments = cls.get_comments(rid)
291         comments['comments'].append(cls.create_comment(username, comment))
292 
293         return oldb.update(
294             "community_edits_queue",
295             where="id=$rid",
296             comments=json.dumps(comments),
297             updated=datetime.datetime.utcnow(),
298             vars={"rid": rid},
299         )
300 
301     @classmethod
302     def find_by_id(cls, rid: int):
303         """Returns the record with the given ID."""
304         return cls.get_requests(id=rid)[0] or None
305 
306     @classmethod
307     def exists(cls, url: str) -> bool:
308         """Returns True if a request with the given URL exists in the table."""
309         return len(cls.get_requests(limit=1, url=url)) > 0
310 
311     @classmethod
312     def get_comments(cls, rid: int):
313         """Fetches the comments for the given request, or an empty comments object."""
314         return cls.get_requests(id=rid)[0]['comments'] or {'comments': []}
315 
316     @classmethod
317     def create_comment(cls, username: str, message: str) -> dict[str, str]:
318         """Creates and returns a new comment with the given name and message.
319         Timestamp set as current time.
320         """
321         return {
322             # isoformat to avoid to-json issues
323             "timestamp": datetime.datetime.utcnow().isoformat(),
324             "username": username,
325             "message": message,
326             # XXX It may be easier to update these comments if they had IDs
327         }
328
```
--- 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/openlibrary/core/edits.py b/openlibrary/core/edits.py
--- a/openlibrary/core/edits.py
+++ b/openlibrary/core/edits.py
@@ -6,6 +6,8 @@
 from infogami.utils.view import public
 
 from openlibrary.i18n import gettext as _
+from openlibrary.core import cache
+from openlibrary.utils import dateutil
 
 from . import db
 
@@ -325,3 +327,12 @@
             "message": message,
             # XXX It may be easier to update these comments if they had IDs
         }
+
+
+@public
+def cached_get_counts_by_mode(mode='all', **kwargs):
+    return cache.memcache_memoize(
+        CommunityEditsQueue.get_counts_by_mode,
+        f"librarian_queue_counts_{mode}",
+        timeout=dateutil.MINUTE_SECS,
+    )(mode, **kwargs)

{"golden_diff": "diff --git a/openlibrary/core/edits.py b/openlibrary/core/edits.py\n--- a/openlibrary/core/edits.py\n+++ b/openlibrary/core/edits.py\n@@ -6,6 +6,8 @@\n from infogami.utils.view import public\n \n from openlibrary.i18n import gettext as _\n+from openlibrary.core import cache\n+from openlibrary.utils import dateutil\n \n from . import db\n \n@@ -325,3 +327,12 @@\n             \"message\": message,\n             # XXX It may be easier to update these comments if they had IDs\n         }\n+\n+\n+@public\n+def cached_get_counts_by_mode(mode='all', **kwargs):\n+    return cache.memcache_memoize(\n+        CommunityEditsQueue.get_counts_by_mode,\n+        f\"librarian_queue_counts_{mode}\",\n+        timeout=dateutil.MINUTE_SECS,\n+    )(mode, **kwargs)\n", "issue": "Add notification bubble on Main Nav for super-librarians # pending MRs\n<!-- A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] -->\r\n\r\n### Describe the problem that you'd like solved\r\n<!-- A clear and concise description of what you want to happen. -->\r\n\r\nOptions are to add a bubble to the avatar and then a entry + bubble into the sub-navigation within the hamburger\r\n\r\n![testing openlibrary org_merges_mode=closed](https://user-images.githubusercontent.com/978325/182037307-3ba26478-0b2e-4fc3-9676-a63fd4e87dba.png)\r\n\r\nOr to add a bubble directly to the black IA topbar which when clicked goes directly to /merges\r\n\r\n![testing openlibrary org_merges_mode=closed (1)](https://user-images.githubusercontent.com/978325/182037463-db0a2ca7-45c7-44ad-9238-146a5ea37daa.png)\r\n\r\n```\r\n.mr-notifications {\r\n    position: absolute;\r\n    z-index: 4;\r\n    background: #02598b;\r\n    color: white;\r\n    border-radius: 8px;\r\n    padding: 3px 7px;\r\n    font-size: 12px;\r\n    margin-left: 9px;\r\n    margin-top: 35px;\r\n    font-weight: bold;\r\n }\r\n```\r\n\r\n### Proposal & Constraints\r\n<!-- What is the proposed solution / implementation? Is there a precedent of this approach succeeding elsewhere? -->\r\n\r\n<!-- Which suggestions or requirements should be considered for how feature needs to appear or be implemented? -->\r\n\r\n### Additional context\r\n<!-- Add any other context or screenshots about the feature request here. -->\r\n\r\n### Stakeholders\r\n<!-- @ tag stakeholders of this bug -->\r\n\r\n\r\n\n", "before_files": [{"content": "import datetime\nimport json\nfrom typing import Optional\nimport web\n\nfrom infogami.utils.view import public\n\nfrom openlibrary.i18n import gettext as _\n\nfrom . import db\n\n\n@public\ndef get_status_for_view(status_code: int) -> str:\n    \"\"\"Returns localized status string that corresponds with the given status code.\"\"\"\n    if status_code == CommunityEditsQueue.STATUS['DECLINED']:\n        return _('Declined')\n    if status_code == CommunityEditsQueue.STATUS['PENDING']:\n        return _('Pending')\n    if status_code == CommunityEditsQueue.STATUS['MERGED']:\n        return _('Merged')\n    return _('Unknown')\n\n\nclass CommunityEditsQueue:\n\n    \"\"\"Schema\n    id: Primary identifier\n    submitter: username of person that made the request\n    reviewer: The username of the person who reviewed the request\n    url: URL of the merge request\n    status: Either \"Pending\", \"Merged\", or \"Declined\"\n    comment: Short note from reviewer (json blobs (can store timestamps, etc))\n    created: created timestamp\n    updated: update timestamp\n    \"\"\"\n\n    STATUS = {\n        'DECLINED': 0,\n        'PENDING': 1,\n        'MERGED': 2,\n    }\n\n    MODES = {\n        'all': [STATUS['DECLINED'], STATUS['PENDING'], STATUS['MERGED']],\n        'open': [STATUS['PENDING']],\n        'closed': [STATUS['DECLINED'], STATUS['MERGED']],\n    }\n\n    @classmethod\n    def get_requests(\n        cls,\n        limit: int = 50,\n        page: int = 1,\n        mode: str = 'all',\n        order: str = None,\n        **kwargs,\n    ):\n        oldb = db.get_db()\n\n        query_kwargs = {\n            \"limit\": limit,\n            \"offset\": limit * (page - 1),\n            \"vars\": {**kwargs},\n        }\n\n        query_kwargs['where'] = cls.where_clause(mode, **kwargs)\n\n        if order:\n            query_kwargs['order'] = order\n        return oldb.select(\"community_edits_queue\", **query_kwargs)\n\n    @classmethod\n    def get_counts_by_mode(cls, mode='all', **kwargs):\n        oldb = db.get_db()\n\n        query = 'SELECT count(*) from community_edits_queue'\n\n        where_clause = cls.where_clause(mode, **kwargs)\n        if where_clause:\n            query = f'{query} WHERE {where_clause}'\n        return oldb.query(query, vars=kwargs)[0]['count']\n\n    @classmethod\n    def where_clause(cls, mode, **kwargs):\n        wheres = []\n\n        if kwargs.get('reviewer') is not None:\n            wheres.append(\n                # if reviewer=\"\" then get all unassigned MRs\n                \"reviewer IS NULL\"\n                if not kwargs.get('reviewer')\n                else \"reviewer=$reviewer\"\n            )\n        if \"submitter\" in kwargs:\n            wheres.append(\n                # If submitter not specified, default to any\n                \"submitter IS NOT NULL\"\n                if kwargs.get(\"submitter\") is None\n                else \"submitter=$submitter\"\n            )\n        if \"url\" in kwargs:\n            wheres.append(\"url=$url\")\n        if \"id\" in kwargs:\n            wheres.append(\"id=$id\")\n\n        status_list = (\n            [f'status={status}' for status in cls.MODES[mode]] if mode != 'all' else []\n        )\n\n        where_clause = ''\n\n        if wheres:\n            where_clause = f'{\" AND \".join(wheres)}'\n        if status_list:\n            status_query = f'({\" OR \".join(status_list)})'\n            if where_clause:\n                where_clause = f'{where_clause} AND {status_query}'\n            else:\n                where_clause = status_query\n\n        return where_clause\n\n    @classmethod\n    def submit_work_merge_request(\n        cls,\n        work_ids: list[str],\n        submitter: str,\n        comment: str = None,\n        reviewer: str = None,\n        status: int = STATUS['PENDING'],\n    ):\n        \"\"\"\n        Creates new work merge requests with the given work olids.\n\n        Precondition: OLIDs in work_ids list must be sanitized and normalized.\n        \"\"\"\n        url = f\"/works/merge?records={','.join(work_ids)}\"\n        if not cls.exists(url):\n            return cls.submit_request(\n                url,\n                submitter=submitter,\n                comment=comment,\n                reviewer=reviewer,\n                status=status,\n                title=cls.get_work_merge_title(work_ids),\n            )\n\n    @staticmethod\n    def get_work_merge_title(olids):\n        title = None\n        for olid in olids:\n            book = web.ctx.site.get(f'/works/{olid}')\n            if book and book.title:\n                title = book.title\n                break\n        return title\n\n    @classmethod\n    def submit_author_merge_request(cls, author_ids, submitter, comment=None):\n        if not comment:\n            # some default note from submitter\n            pass\n        # XXX IDs should be santiized & normalized\n        url = f\"/authors/merge?key={'&key='.join(author_ids)}\"\n        cls.submit_request(url, submitter=submitter, comment=comment)\n\n    @classmethod\n    def submit_delete_request(cls, olid, submitter, comment=None):\n        if not comment:\n            # some default note from submitter\n            pass\n        url = f\"{olid}/-/edit?m=delete\"\n        cls.submit_request(cls, url, submitter=submitter, comment=comment)\n\n    @classmethod\n    def submit_request(\n        cls,\n        url: str,\n        submitter: str,\n        reviewer: str = None,\n        status: int = STATUS['PENDING'],\n        comment: str = None,\n        title: str = None,\n    ):\n        \"\"\"\n        Inserts a new record into the table.\n\n        Preconditions: All data validations should be completed before calling this method.\n        \"\"\"\n        oldb = db.get_db()\n\n        comments = [cls.create_comment(submitter, comment)] if comment else []\n        json_comment = json.dumps({\"comments\": comments})\n\n        return oldb.insert(\n            \"community_edits_queue\",\n            submitter=submitter,\n            reviewer=reviewer,\n            url=url,\n            status=status,\n            comments=json_comment,\n            title=title,\n        )\n\n    @classmethod\n    def assign_request(\n        cls, rid: int, reviewer: Optional[str]\n    ) -> dict[str, Optional[str]]:\n        \"\"\"Changes assignees to the request with the given ID.\n\n        This method only modifies requests that are not closed.\n\n        If the given reviewer is the same as the request's reviewer, nothing is\n        modified\n        \"\"\"\n        request = cls.find_by_id(rid)\n\n        if request['status'] not in cls.MODES['closed']:\n            if request['reviewer'] == reviewer:\n                return {\n                    'status': 'error',\n                    'error': f'{reviewer} is already assigned to this request',\n                }\n            oldb = db.get_db()\n\n            oldb.update(\n                \"community_edits_queue\",\n                where=\"id=$rid\",\n                reviewer=reviewer,\n                status=cls.STATUS['PENDING'],\n                updated=datetime.datetime.utcnow(),\n                vars={\"rid\": rid},\n            )\n            return {\n                'reviewer': reviewer,\n                'newStatus': get_status_for_view(cls.STATUS['PENDING']),\n            }\n        return {'status': 'error', 'error': 'This request has already been closed'}\n\n    @classmethod\n    def unassign_request(cls, rid: int):\n        \"\"\"\n        Changes status of given request to \"Pending\", and sets reviewer to None.\n        \"\"\"\n        oldb = db.get_db()\n        oldb.update(\n            \"community_edits_queue\",\n            where=\"id=$rid\",\n            status=cls.STATUS['PENDING'],\n            reviewer=None,\n            updated=datetime.datetime.utcnow(),\n            vars={\"rid\": rid},\n        )\n\n    @classmethod\n    def update_request_status(\n        cls, rid: int, status: int, reviewer: str, comment: str = None\n    ) -> int:\n        \"\"\"\n        Changes the status of the request with the given rid.\n\n        If a comment is included, existing comments list for this request are fetched and\n        the new comment is appended.\n        \"\"\"\n        oldb = db.get_db()\n\n        update_kwargs = {}\n\n        # XXX Trim whitespace from comment first\n        if comment:\n            comments = cls.get_comments(rid)\n            comments['comments'].append(cls.create_comment(reviewer, comment))\n            update_kwargs['comments'] = json.dumps(comments)\n\n        return oldb.update(\n            \"community_edits_queue\",\n            where=\"id=$rid\",\n            status=status,\n            reviewer=reviewer,\n            updated=datetime.datetime.utcnow(),\n            vars={\"rid\": rid},\n            **update_kwargs,\n        )\n\n    @classmethod\n    def comment_request(cls, rid: int, username: str, comment: str) -> int:\n        oldb = db.get_db()\n\n        comments = cls.get_comments(rid)\n        comments['comments'].append(cls.create_comment(username, comment))\n\n        return oldb.update(\n            \"community_edits_queue\",\n            where=\"id=$rid\",\n            comments=json.dumps(comments),\n            updated=datetime.datetime.utcnow(),\n            vars={\"rid\": rid},\n        )\n\n    @classmethod\n    def find_by_id(cls, rid: int):\n        \"\"\"Returns the record with the given ID.\"\"\"\n        return cls.get_requests(id=rid)[0] or None\n\n    @classmethod\n    def exists(cls, url: str) -> bool:\n        \"\"\"Returns True if a request with the given URL exists in the table.\"\"\"\n        return len(cls.get_requests(limit=1, url=url)) > 0\n\n    @classmethod\n    def get_comments(cls, rid: int):\n        \"\"\"Fetches the comments for the given request, or an empty comments object.\"\"\"\n        return cls.get_requests(id=rid)[0]['comments'] or {'comments': []}\n\n    @classmethod\n    def create_comment(cls, username: str, message: str) -> dict[str, str]:\n        \"\"\"Creates and returns a new comment with the given name and message.\n        Timestamp set as current time.\n        \"\"\"\n        return {\n            # isoformat to avoid to-json issues\n            \"timestamp\": datetime.datetime.utcnow().isoformat(),\n            \"username\": username,\n            \"message\": message,\n            # XXX It may be easier to update these comments if they had IDs\n        }\n", "path": "openlibrary/core/edits.py"}], "after_files": [{"content": "import datetime\nimport json\nfrom typing import Optional\nimport web\n\nfrom infogami.utils.view import public\n\nfrom openlibrary.i18n import gettext as _\nfrom openlibrary.core import cache\nfrom openlibrary.utils import dateutil\n\nfrom . import db\n\n\n@public\ndef get_status_for_view(status_code: int) -> str:\n    \"\"\"Returns localized status string that corresponds with the given status code.\"\"\"\n    if status_code == CommunityEditsQueue.STATUS['DECLINED']:\n        return _('Declined')\n    if status_code == CommunityEditsQueue.STATUS['PENDING']:\n        return _('Pending')\n    if status_code == CommunityEditsQueue.STATUS['MERGED']:\n        return _('Merged')\n    return _('Unknown')\n\n\nclass CommunityEditsQueue:\n\n    \"\"\"Schema\n    id: Primary identifier\n    submitter: username of person that made the request\n    reviewer: The username of the person who reviewed the request\n    url: URL of the merge request\n    status: Either \"Pending\", \"Merged\", or \"Declined\"\n    comment: Short note from reviewer (json blobs (can store timestamps, etc))\n    created: created timestamp\n    updated: update timestamp\n    \"\"\"\n\n    STATUS = {\n        'DECLINED': 0,\n        'PENDING': 1,\n        'MERGED': 2,\n    }\n\n    MODES = {\n        'all': [STATUS['DECLINED'], STATUS['PENDING'], STATUS['MERGED']],\n        'open': [STATUS['PENDING']],\n        'closed': [STATUS['DECLINED'], STATUS['MERGED']],\n    }\n\n    @classmethod\n    def get_requests(\n        cls,\n        limit: int = 50,\n        page: int = 1,\n        mode: str = 'all',\n        order: str = None,\n        **kwargs,\n    ):\n        oldb = db.get_db()\n\n        query_kwargs = {\n            \"limit\": limit,\n            \"offset\": limit * (page - 1),\n            \"vars\": {**kwargs},\n        }\n\n        query_kwargs['where'] = cls.where_clause(mode, **kwargs)\n\n        if order:\n            query_kwargs['order'] = order\n        return oldb.select(\"community_edits_queue\", **query_kwargs)\n\n    @classmethod\n    def get_counts_by_mode(cls, mode='all', **kwargs):\n        oldb = db.get_db()\n\n        query = 'SELECT count(*) from community_edits_queue'\n\n        where_clause = cls.where_clause(mode, **kwargs)\n        if where_clause:\n            query = f'{query} WHERE {where_clause}'\n        return oldb.query(query, vars=kwargs)[0]['count']\n\n    @classmethod\n    def where_clause(cls, mode, **kwargs):\n        wheres = []\n\n        if kwargs.get('reviewer') is not None:\n            wheres.append(\n                # if reviewer=\"\" then get all unassigned MRs\n                \"reviewer IS NULL\"\n                if not kwargs.get('reviewer')\n                else \"reviewer=$reviewer\"\n            )\n        if \"submitter\" in kwargs:\n            wheres.append(\n                # If submitter not specified, default to any\n                \"submitter IS NOT NULL\"\n                if kwargs.get(\"submitter\") is None\n                else \"submitter=$submitter\"\n            )\n        if \"url\" in kwargs:\n            wheres.append(\"url=$url\")\n        if \"id\" in kwargs:\n            wheres.append(\"id=$id\")\n\n        status_list = (\n            [f'status={status}' for status in cls.MODES[mode]] if mode != 'all' else []\n        )\n\n        where_clause = ''\n\n        if wheres:\n            where_clause = f'{\" AND \".join(wheres)}'\n        if status_list:\n            status_query = f'({\" OR \".join(status_list)})'\n            if where_clause:\n                where_clause = f'{where_clause} AND {status_query}'\n            else:\n                where_clause = status_query\n\n        return where_clause\n\n    @classmethod\n    def submit_work_merge_request(\n        cls,\n        work_ids: list[str],\n        submitter: str,\n        comment: str = None,\n        reviewer: str = None,\n        status: int = STATUS['PENDING'],\n    ):\n        \"\"\"\n        Creates new work merge requests with the given work olids.\n\n        Precondition: OLIDs in work_ids list must be sanitized and normalized.\n        \"\"\"\n        url = f\"/works/merge?records={','.join(work_ids)}\"\n        if not cls.exists(url):\n            return cls.submit_request(\n                url,\n                submitter=submitter,\n                comment=comment,\n                reviewer=reviewer,\n                status=status,\n                title=cls.get_work_merge_title(work_ids),\n            )\n\n    @staticmethod\n    def get_work_merge_title(olids):\n        title = None\n        for olid in olids:\n            book = web.ctx.site.get(f'/works/{olid}')\n            if book and book.title:\n                title = book.title\n                break\n        return title\n\n    @classmethod\n    def submit_author_merge_request(cls, author_ids, submitter, comment=None):\n        if not comment:\n            # some default note from submitter\n            pass\n        # XXX IDs should be santiized & normalized\n        url = f\"/authors/merge?key={'&key='.join(author_ids)}\"\n        cls.submit_request(url, submitter=submitter, comment=comment)\n\n    @classmethod\n    def submit_delete_request(cls, olid, submitter, comment=None):\n        if not comment:\n            # some default note from submitter\n            pass\n        url = f\"{olid}/-/edit?m=delete\"\n        cls.submit_request(cls, url, submitter=submitter, comment=comment)\n\n    @classmethod\n    def submit_request(\n        cls,\n        url: str,\n        submitter: str,\n        reviewer: str = None,\n        status: int = STATUS['PENDING'],\n        comment: str = None,\n        title: str = None,\n    ):\n        \"\"\"\n        Inserts a new record into the table.\n\n        Preconditions: All data validations should be completed before calling this method.\n        \"\"\"\n        oldb = db.get_db()\n\n        comments = [cls.create_comment(submitter, comment)] if comment else []\n        json_comment = json.dumps({\"comments\": comments})\n\n        return oldb.insert(\n            \"community_edits_queue\",\n            submitter=submitter,\n            reviewer=reviewer,\n            url=url,\n            status=status,\n            comments=json_comment,\n            title=title,\n        )\n\n    @classmethod\n    def assign_request(\n        cls, rid: int, reviewer: Optional[str]\n    ) -> dict[str, Optional[str]]:\n        \"\"\"Changes assignees to the request with the given ID.\n\n        This method only modifies requests that are not closed.\n\n        If the given reviewer is the same as the request's reviewer, nothing is\n        modified\n        \"\"\"\n        request = cls.find_by_id(rid)\n\n        if request['status'] not in cls.MODES['closed']:\n            if request['reviewer'] == reviewer:\n                return {\n                    'status': 'error',\n                    'error': f'{reviewer} is already assigned to this request',\n                }\n            oldb = db.get_db()\n\n            oldb.update(\n                \"community_edits_queue\",\n                where=\"id=$rid\",\n                reviewer=reviewer,\n                status=cls.STATUS['PENDING'],\n                updated=datetime.datetime.utcnow(),\n                vars={\"rid\": rid},\n            )\n            return {\n                'reviewer': reviewer,\n                'newStatus': get_status_for_view(cls.STATUS['PENDING']),\n            }\n        return {'status': 'error', 'error': 'This request has already been closed'}\n\n    @classmethod\n    def unassign_request(cls, rid: int):\n        \"\"\"\n        Changes status of given request to \"Pending\", and sets reviewer to None.\n        \"\"\"\n        oldb = db.get_db()\n        oldb.update(\n            \"community_edits_queue\",\n            where=\"id=$rid\",\n            status=cls.STATUS['PENDING'],\n            reviewer=None,\n            updated=datetime.datetime.utcnow(),\n            vars={\"rid\": rid},\n        )\n\n    @classmethod\n    def update_request_status(\n        cls, rid: int, status: int, reviewer: str, comment: str = None\n    ) -> int:\n        \"\"\"\n        Changes the status of the request with the given rid.\n\n        If a comment is included, existing comments list for this request are fetched and\n        the new comment is appended.\n        \"\"\"\n        oldb = db.get_db()\n\n        update_kwargs = {}\n\n        # XXX Trim whitespace from comment first\n        if comment:\n            comments = cls.get_comments(rid)\n            comments['comments'].append(cls.create_comment(reviewer, comment))\n            update_kwargs['comments'] = json.dumps(comments)\n\n        return oldb.update(\n            \"community_edits_queue\",\n            where=\"id=$rid\",\n            status=status,\n            reviewer=reviewer,\n            updated=datetime.datetime.utcnow(),\n            vars={\"rid\": rid},\n            **update_kwargs,\n        )\n\n    @classmethod\n    def comment_request(cls, rid: int, username: str, comment: str) -> int:\n        oldb = db.get_db()\n\n        comments = cls.get_comments(rid)\n        comments['comments'].append(cls.create_comment(username, comment))\n\n        return oldb.update(\n            \"community_edits_queue\",\n            where=\"id=$rid\",\n            comments=json.dumps(comments),\n            updated=datetime.datetime.utcnow(),\n            vars={\"rid\": rid},\n        )\n\n    @classmethod\n    def find_by_id(cls, rid: int):\n        \"\"\"Returns the record with the given ID.\"\"\"\n        return cls.get_requests(id=rid)[0] or None\n\n    @classmethod\n    def exists(cls, url: str) -> bool:\n        \"\"\"Returns True if a request with the given URL exists in the table.\"\"\"\n        return len(cls.get_requests(limit=1, url=url)) > 0\n\n    @classmethod\n    def get_comments(cls, rid: int):\n        \"\"\"Fetches the comments for the given request, or an empty comments object.\"\"\"\n        return cls.get_requests(id=rid)[0]['comments'] or {'comments': []}\n\n    @classmethod\n    def create_comment(cls, username: str, message: str) -> dict[str, str]:\n        \"\"\"Creates and returns a new comment with the given name and message.\n        Timestamp set as current time.\n        \"\"\"\n        return {\n            # isoformat to avoid to-json issues\n            \"timestamp\": datetime.datetime.utcnow().isoformat(),\n            \"username\": username,\n            \"message\": message,\n            # XXX It may be easier to update these comments if they had IDs\n        }\n\n\n@public\ndef cached_get_counts_by_mode(mode='all', **kwargs):\n    return cache.memcache_memoize(\n        CommunityEditsQueue.get_counts_by_mode,\n        f\"librarian_queue_counts_{mode}\",\n        timeout=dateutil.MINUTE_SECS,\n    )(mode, **kwargs)\n", "path": "openlibrary/core/edits.py"}]}