We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Open Graph protocol features in header
This is a request to add [Open Graph protocol (OGP)](http://ogp.me/) tags in the header of each paper page. 
Some websites that supports OGP can show users richer information, such as Slack with arXiv:
![Screenshot from 2019-06-10 16-05-52](https://user-images.githubusercontent.com/1023695/59178501-1b414e80-8b9a-11e9-8731-58748331f8c2.png)


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `bin/anthology/papers.py`
Content:
```
1 # -*- coding: utf-8 -*-
2 #
3 # Copyright 2019 Marcel Bollmann <marcel@bollmann.me>
4 #
5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at
8 #
9 #     http://www.apache.org/licenses/LICENSE-2.0
10 #
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
16 
17 import logging as log
18 from .utils import (
19     parse_element,
20     infer_attachment_url,
21     remove_extra_whitespace,
22     is_journal,
23     is_volume_id,
24 )
25 from . import data
26 
27 # For BibTeX export
28 from .formatter import bibtex_encode, bibtex_make_entry
29 
30 class Paper:
31     def __init__(self, paper_id, ingest_date, volume, formatter):
32         self.parent_volume = volume
33         self.formatter = formatter
34         self._id = paper_id
35         self._ingest_date = ingest_date
36         self._bibkey = False
37         self.is_volume = paper_id == '0'
38 
39         # initialize metadata with keys inherited from volume
40         self.attrib = {}
41         for key, value in volume.attrib.items():
42             # Only inherit 'editor' for frontmatter
43             if (key == 'editor' and not self.is_volume) or key in ('collection_id', 'booktitle', 'id', 'meta_data', 'meta_journal_title', 'meta_volume', 'meta_issue', 'sigs', 'venues', 'meta_date', 'url'):
44                 continue
45 
46             self.attrib[key] = value
47 
48     def from_xml(xml_element, *args):
49         ingest_date = xml_element.get('ingest-date', data.UNKNOWN_INGEST_DATE)
50 
51         # Default to paper ID "0" (for front matter)
52         paper = Paper(xml_element.get("id", '0'), ingest_date, *args)
53 
54         # Set values from parsing the XML element (overwriting
55         # and changing some initialized from the volume metadata)
56         for key, value in parse_element(xml_element).items():
57             if key == 'author' and 'editor' in paper.attrib:
58                 del paper.attrib['editor']
59             paper.attrib[key] = value
60 
61         # Frontmatter title is the volume 'booktitle'
62         if paper.is_volume:
63             paper.attrib['xml_title'] = paper.attrib['xml_booktitle']
64             paper.attrib['xml_title'].tag = 'title'
65 
66         # Remove booktitle for frontmatter and journals
67         if paper.is_volume or is_journal(paper.full_id):
68             del paper.attrib['xml_booktitle']
69 
70         # Expand URLs with paper ID
71         for tag in ('revision', 'erratum'):
72             if tag in paper.attrib:
73                 for item in paper.attrib[tag]:
74                     if not item['url'].startswith(paper.full_id):
75                         log.error(
76                             "{} must begin with paper ID '{}', but is '{}'".format(
77                                 tag, paper.full_id, item['url']
78                             )
79                         )
80                     item['url'] = data.ANTHOLOGY_URL.format(item['url'])
81 
82         if 'attachment' in paper.attrib:
83             for item in paper.attrib['attachment']:
84                 item['url'] = infer_attachment_url(item['url'], paper.full_id)
85 
86         # Explicitly construct URL of original version of the paper
87         # -- this is a bit hacky, but it's not given in the XML
88         # explicitly
89         if 'revision' in paper.attrib:
90             paper.attrib['revision'].insert(0, {
91                 "value": "{}v1".format(paper.full_id),
92                 "id": "1",
93                 "url": data.ANTHOLOGY_URL.format( "{}v1".format(paper.full_id)) } )
94 
95         paper.attrib["title"] = paper.get_title("plain")
96         if "booktitle" in paper.attrib:
97             paper.attrib["booktitle"] = paper.get_booktitle("plain")
98 
99         if "editor" in paper.attrib:
100             if paper.is_volume:
101                 if "author" in paper.attrib:
102                     log.warn(
103                         "Paper {} has both <editor> and <author>; ignoring <author>".format(
104                             paper.full_id
105                         )
106                     )
107                 # Proceedings editors are considered authors for their front matter
108                 paper.attrib["author"] = paper.attrib["editor"]
109                 del paper.attrib["editor"]
110             else:
111                 log.warn(
112                     "Paper {} has <editor> but is not a proceedings volume; ignoring <editor>".format(
113                         paper.full_id
114                     )
115                 )
116         if "pages" in paper.attrib:
117             if paper.attrib["pages"] is not None:
118                 paper._interpret_pages()
119             else:
120                 del paper.attrib["pages"]
121         return paper
122 
123     def _interpret_pages(self):
124         """Splits up 'pages' field into first and last page, if possible.
125 
126         This is used for metadata in the generated HTML."""
127         for s in ("--", "-", "–"):
128             if self.attrib["pages"].count(s) == 1:
129                 self.attrib["page_first"], self.attrib["page_last"] = self.attrib[
130                     "pages"
131                 ].split(s)
132                 self.attrib["pages"] = self.attrib["pages"].replace(s, "–")
133                 return
134 
135     @property
136     def ingest_date(self):
137         """Inherit publication date from parent, but self overrides. May be undefined."""
138         if self._ingest_date:
139             return self._ingest_date
140         if self.parent_volume:
141             return self.parent_volume.ingest_date
142         return data.UNKNOWN_INGEST_DATE
143 
144     @property
145     def collection_id(self):
146         return self.parent_volume.collection_id
147 
148     @property
149     def volume_id(self):
150         return self.parent_volume.volume_id
151 
152     @property
153     def paper_id(self):
154         if self.collection_id[0] == "W" or self.collection_id == "C69":
155             # If volume is a workshop, use the last two digits of ID
156             _id = "{}{:02d}".format(self.volume_id, int(self._id))
157         else:
158             # If not, only the last three
159             _id = "{}{:03d}".format(self.volume_id, int(self._id))
160         # Just to be sure
161         assert len(_id) == 4
162         return _id
163 
164     @property
165     def full_id(self):
166         return "{}-{}".format(self.collection_id, self.paper_id)
167 
168     @property
169     def bibkey(self):
170         if not self._bibkey:
171             self._bibkey = self.full_id  # fallback
172         return self._bibkey
173 
174     @bibkey.setter
175     def bibkey(self, value):
176         self._bibkey = value
177 
178     @property
179     def bibtype(self):
180         if is_journal(self.full_id):
181             return "article"
182         elif self.is_volume:
183             return "proceedings"
184         else:
185             return "inproceedings"
186 
187     @property
188     def parent_volume_id(self):
189         if self.parent_volume is not None:
190             return self.parent_volume.full_id
191         return None
192 
193     @property
194     def has_abstract(self):
195         return "xml_abstract" in self.attrib
196 
197     def get(self, name, default=None):
198         try:
199             return self.attrib[name]
200         except KeyError:
201             return default
202 
203     def get_title(self, form="xml"):
204         """Returns the paper title, optionally formatting it.
205 
206         Accepted formats:
207           - xml:   Include any contained XML tags unchanged
208           - plain: Strip all XML tags, returning only plain text
209           - html:  Convert XML tags into valid HTML tags
210           - latex: Convert XML tags into LaTeX commands
211         """
212         return self.formatter(self.get("xml_title"), form)
213 
214     def get_abstract(self, form="xml"):
215         """Returns the abstract, optionally formatting it.
216 
217         See `get_title()` for details.
218         """
219         return self.formatter(self.get("xml_abstract"), form, allow_url=True)
220 
221     def get_booktitle(self, form="xml"):
222         """Returns the booktitle, optionally formatting it.
223 
224         See `get_title()` for details.
225         """
226         return self.formatter(self.get("xml_booktitle"), form)
227 
228     def as_bibtex(self):
229         """Return the BibTeX entry for this paper."""
230         # Build BibTeX entry
231         bibkey = self.bibkey
232         bibtype = self.bibtype
233         entries = [("title", self.get_title(form="latex"))]
234         for people in ("author", "editor"):
235             if people in self.attrib:
236                 entries.append(
237                     (people, "  and  ".join(p.as_bibtex() for p, _ in self.get(people)))
238                 )
239         if is_journal(self.full_id):
240             entries.append(
241                 ("journal", bibtex_encode(self.parent_volume.get("meta_journal_title")))
242             )
243             journal_volume = self.parent_volume.get(
244                 "meta_volume", self.parent_volume.get("volume")
245             )
246             if journal_volume:
247                 entries.append(("volume", journal_volume))
248             journal_issue = self.parent_volume.get(
249                 "meta_issue", self.parent_volume.get("issue")
250             )
251             if journal_issue:
252                 entries.append(("number", journal_issue))
253         else:
254             # not is_journal(self.full_id)
255             if "xml_booktitle" in self.attrib:
256                 entries.append(("booktitle", self.get_booktitle(form="latex")))
257             elif bibtype != "proceedings":
258                 entries.append(
259                     ("booktitle", self.parent_volume.get_title(form="latex"))
260                 )
261         for entry in ("month", "year", "address", "publisher", "note"):
262             if self.get(entry) is not None:
263                 entries.append((entry, bibtex_encode(self.get(entry))))
264         for entry in ("url", "doi"):
265             if entry in self.attrib:
266                 # don't want latex escapes such as
267                 # doi = "10.1162/coli{\_}a{\_}00008",
268                 entries.append((entry, self.get(entry)))
269         if "pages" in self.attrib:
270             entries.append(("pages", self.get("pages").replace("–", "--")))
271         if "xml_abstract" in self.attrib:
272             entries.append(("abstract", self.get_abstract(form="latex")))
273 
274         # Serialize it
275         return bibtex_make_entry(bibkey, bibtype, entries)
276 
277     def as_dict(self):
278         value = self.attrib
279         value["paper_id"] = self.paper_id
280         value["parent_volume_id"] = self.parent_volume_id
281         value["bibkey"] = self.bibkey
282         value["bibtype"] = self.bibtype
283         return value
284 
285     def items(self):
286         return self.attrib.items()
287
```
--- 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/bin/anthology/papers.py b/bin/anthology/papers.py
--- a/bin/anthology/papers.py
+++ b/bin/anthology/papers.py
@@ -93,8 +93,7 @@
                 "url": data.ANTHOLOGY_URL.format( "{}v1".format(paper.full_id)) } )
 
         paper.attrib["title"] = paper.get_title("plain")
-        if "booktitle" in paper.attrib:
-            paper.attrib["booktitle"] = paper.get_booktitle("plain")
+        paper.attrib["booktitle"] = paper.get_booktitle("plain")
 
         if "editor" in paper.attrib:
             if paper.is_volume:
@@ -118,6 +117,12 @@
                 paper._interpret_pages()
             else:
                 del paper.attrib["pages"]
+
+        if 'author' in paper.attrib:
+            # for x in paper.attrib['author']:
+            #     print('X', x[0].full)
+            paper.attrib["author_string"] = ', '.join([x[0].full for x in paper.attrib["author"]])
+
         return paper
 
     def _interpret_pages(self):
@@ -218,12 +223,17 @@
         """
         return self.formatter(self.get("xml_abstract"), form, allow_url=True)
 
-    def get_booktitle(self, form="xml"):
+    def get_booktitle(self, form="xml", default=''):
         """Returns the booktitle, optionally formatting it.
 
         See `get_title()` for details.
         """
-        return self.formatter(self.get("xml_booktitle"), form)
+        if 'xml_booktitle' in self.attrib:
+            return self.formatter(self.get("xml_booktitle"), form)
+        elif self.parent_volume is not None:
+            return self.parent_volume.get('title')
+        else:
+            return default
 
     def as_bibtex(self):
         """Return the BibTeX entry for this paper."""

{"golden_diff": "diff --git a/bin/anthology/papers.py b/bin/anthology/papers.py\n--- a/bin/anthology/papers.py\n+++ b/bin/anthology/papers.py\n@@ -93,8 +93,7 @@\n                 \"url\": data.ANTHOLOGY_URL.format( \"{}v1\".format(paper.full_id)) } )\n \n         paper.attrib[\"title\"] = paper.get_title(\"plain\")\n-        if \"booktitle\" in paper.attrib:\n-            paper.attrib[\"booktitle\"] = paper.get_booktitle(\"plain\")\n+        paper.attrib[\"booktitle\"] = paper.get_booktitle(\"plain\")\n \n         if \"editor\" in paper.attrib:\n             if paper.is_volume:\n@@ -118,6 +117,12 @@\n                 paper._interpret_pages()\n             else:\n                 del paper.attrib[\"pages\"]\n+\n+        if 'author' in paper.attrib:\n+            # for x in paper.attrib['author']:\n+            #     print('X', x[0].full)\n+            paper.attrib[\"author_string\"] = ', '.join([x[0].full for x in paper.attrib[\"author\"]])\n+\n         return paper\n \n     def _interpret_pages(self):\n@@ -218,12 +223,17 @@\n         \"\"\"\n         return self.formatter(self.get(\"xml_abstract\"), form, allow_url=True)\n \n-    def get_booktitle(self, form=\"xml\"):\n+    def get_booktitle(self, form=\"xml\", default=''):\n         \"\"\"Returns the booktitle, optionally formatting it.\n \n         See `get_title()` for details.\n         \"\"\"\n-        return self.formatter(self.get(\"xml_booktitle\"), form)\n+        if 'xml_booktitle' in self.attrib:\n+            return self.formatter(self.get(\"xml_booktitle\"), form)\n+        elif self.parent_volume is not None:\n+            return self.parent_volume.get('title')\n+        else:\n+            return default\n \n     def as_bibtex(self):\n         \"\"\"Return the BibTeX entry for this paper.\"\"\"\n", "issue": "Open Graph protocol features in header\nThis is a request to add [Open Graph protocol (OGP)](http://ogp.me/) tags in the header of each paper page. \r\nSome websites that supports OGP can show users richer information, such as Slack with arXiv:\r\n![Screenshot from 2019-06-10 16-05-52](https://user-images.githubusercontent.com/1023695/59178501-1b414e80-8b9a-11e9-8731-58748331f8c2.png)\r\n\n", "before_files": [{"content": "# -*- coding: utf-8 -*-\n#\n# Copyright 2019 Marcel Bollmann <marcel@bollmann.me>\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport logging as log\nfrom .utils import (\n    parse_element,\n    infer_attachment_url,\n    remove_extra_whitespace,\n    is_journal,\n    is_volume_id,\n)\nfrom . import data\n\n# For BibTeX export\nfrom .formatter import bibtex_encode, bibtex_make_entry\n\nclass Paper:\n    def __init__(self, paper_id, ingest_date, volume, formatter):\n        self.parent_volume = volume\n        self.formatter = formatter\n        self._id = paper_id\n        self._ingest_date = ingest_date\n        self._bibkey = False\n        self.is_volume = paper_id == '0'\n\n        # initialize metadata with keys inherited from volume\n        self.attrib = {}\n        for key, value in volume.attrib.items():\n            # Only inherit 'editor' for frontmatter\n            if (key == 'editor' and not self.is_volume) or key in ('collection_id', 'booktitle', 'id', 'meta_data', 'meta_journal_title', 'meta_volume', 'meta_issue', 'sigs', 'venues', 'meta_date', 'url'):\n                continue\n\n            self.attrib[key] = value\n\n    def from_xml(xml_element, *args):\n        ingest_date = xml_element.get('ingest-date', data.UNKNOWN_INGEST_DATE)\n\n        # Default to paper ID \"0\" (for front matter)\n        paper = Paper(xml_element.get(\"id\", '0'), ingest_date, *args)\n\n        # Set values from parsing the XML element (overwriting\n        # and changing some initialized from the volume metadata)\n        for key, value in parse_element(xml_element).items():\n            if key == 'author' and 'editor' in paper.attrib:\n                del paper.attrib['editor']\n            paper.attrib[key] = value\n\n        # Frontmatter title is the volume 'booktitle'\n        if paper.is_volume:\n            paper.attrib['xml_title'] = paper.attrib['xml_booktitle']\n            paper.attrib['xml_title'].tag = 'title'\n\n        # Remove booktitle for frontmatter and journals\n        if paper.is_volume or is_journal(paper.full_id):\n            del paper.attrib['xml_booktitle']\n\n        # Expand URLs with paper ID\n        for tag in ('revision', 'erratum'):\n            if tag in paper.attrib:\n                for item in paper.attrib[tag]:\n                    if not item['url'].startswith(paper.full_id):\n                        log.error(\n                            \"{} must begin with paper ID '{}', but is '{}'\".format(\n                                tag, paper.full_id, item['url']\n                            )\n                        )\n                    item['url'] = data.ANTHOLOGY_URL.format(item['url'])\n\n        if 'attachment' in paper.attrib:\n            for item in paper.attrib['attachment']:\n                item['url'] = infer_attachment_url(item['url'], paper.full_id)\n\n        # Explicitly construct URL of original version of the paper\n        # -- this is a bit hacky, but it's not given in the XML\n        # explicitly\n        if 'revision' in paper.attrib:\n            paper.attrib['revision'].insert(0, {\n                \"value\": \"{}v1\".format(paper.full_id),\n                \"id\": \"1\",\n                \"url\": data.ANTHOLOGY_URL.format( \"{}v1\".format(paper.full_id)) } )\n\n        paper.attrib[\"title\"] = paper.get_title(\"plain\")\n        if \"booktitle\" in paper.attrib:\n            paper.attrib[\"booktitle\"] = paper.get_booktitle(\"plain\")\n\n        if \"editor\" in paper.attrib:\n            if paper.is_volume:\n                if \"author\" in paper.attrib:\n                    log.warn(\n                        \"Paper {} has both <editor> and <author>; ignoring <author>\".format(\n                            paper.full_id\n                        )\n                    )\n                # Proceedings editors are considered authors for their front matter\n                paper.attrib[\"author\"] = paper.attrib[\"editor\"]\n                del paper.attrib[\"editor\"]\n            else:\n                log.warn(\n                    \"Paper {} has <editor> but is not a proceedings volume; ignoring <editor>\".format(\n                        paper.full_id\n                    )\n                )\n        if \"pages\" in paper.attrib:\n            if paper.attrib[\"pages\"] is not None:\n                paper._interpret_pages()\n            else:\n                del paper.attrib[\"pages\"]\n        return paper\n\n    def _interpret_pages(self):\n        \"\"\"Splits up 'pages' field into first and last page, if possible.\n\n        This is used for metadata in the generated HTML.\"\"\"\n        for s in (\"--\", \"-\", \"\u2013\"):\n            if self.attrib[\"pages\"].count(s) == 1:\n                self.attrib[\"page_first\"], self.attrib[\"page_last\"] = self.attrib[\n                    \"pages\"\n                ].split(s)\n                self.attrib[\"pages\"] = self.attrib[\"pages\"].replace(s, \"\u2013\")\n                return\n\n    @property\n    def ingest_date(self):\n        \"\"\"Inherit publication date from parent, but self overrides. May be undefined.\"\"\"\n        if self._ingest_date:\n            return self._ingest_date\n        if self.parent_volume:\n            return self.parent_volume.ingest_date\n        return data.UNKNOWN_INGEST_DATE\n\n    @property\n    def collection_id(self):\n        return self.parent_volume.collection_id\n\n    @property\n    def volume_id(self):\n        return self.parent_volume.volume_id\n\n    @property\n    def paper_id(self):\n        if self.collection_id[0] == \"W\" or self.collection_id == \"C69\":\n            # If volume is a workshop, use the last two digits of ID\n            _id = \"{}{:02d}\".format(self.volume_id, int(self._id))\n        else:\n            # If not, only the last three\n            _id = \"{}{:03d}\".format(self.volume_id, int(self._id))\n        # Just to be sure\n        assert len(_id) == 4\n        return _id\n\n    @property\n    def full_id(self):\n        return \"{}-{}\".format(self.collection_id, self.paper_id)\n\n    @property\n    def bibkey(self):\n        if not self._bibkey:\n            self._bibkey = self.full_id  # fallback\n        return self._bibkey\n\n    @bibkey.setter\n    def bibkey(self, value):\n        self._bibkey = value\n\n    @property\n    def bibtype(self):\n        if is_journal(self.full_id):\n            return \"article\"\n        elif self.is_volume:\n            return \"proceedings\"\n        else:\n            return \"inproceedings\"\n\n    @property\n    def parent_volume_id(self):\n        if self.parent_volume is not None:\n            return self.parent_volume.full_id\n        return None\n\n    @property\n    def has_abstract(self):\n        return \"xml_abstract\" in self.attrib\n\n    def get(self, name, default=None):\n        try:\n            return self.attrib[name]\n        except KeyError:\n            return default\n\n    def get_title(self, form=\"xml\"):\n        \"\"\"Returns the paper title, optionally formatting it.\n\n        Accepted formats:\n          - xml:   Include any contained XML tags unchanged\n          - plain: Strip all XML tags, returning only plain text\n          - html:  Convert XML tags into valid HTML tags\n          - latex: Convert XML tags into LaTeX commands\n        \"\"\"\n        return self.formatter(self.get(\"xml_title\"), form)\n\n    def get_abstract(self, form=\"xml\"):\n        \"\"\"Returns the abstract, optionally formatting it.\n\n        See `get_title()` for details.\n        \"\"\"\n        return self.formatter(self.get(\"xml_abstract\"), form, allow_url=True)\n\n    def get_booktitle(self, form=\"xml\"):\n        \"\"\"Returns the booktitle, optionally formatting it.\n\n        See `get_title()` for details.\n        \"\"\"\n        return self.formatter(self.get(\"xml_booktitle\"), form)\n\n    def as_bibtex(self):\n        \"\"\"Return the BibTeX entry for this paper.\"\"\"\n        # Build BibTeX entry\n        bibkey = self.bibkey\n        bibtype = self.bibtype\n        entries = [(\"title\", self.get_title(form=\"latex\"))]\n        for people in (\"author\", \"editor\"):\n            if people in self.attrib:\n                entries.append(\n                    (people, \"  and  \".join(p.as_bibtex() for p, _ in self.get(people)))\n                )\n        if is_journal(self.full_id):\n            entries.append(\n                (\"journal\", bibtex_encode(self.parent_volume.get(\"meta_journal_title\")))\n            )\n            journal_volume = self.parent_volume.get(\n                \"meta_volume\", self.parent_volume.get(\"volume\")\n            )\n            if journal_volume:\n                entries.append((\"volume\", journal_volume))\n            journal_issue = self.parent_volume.get(\n                \"meta_issue\", self.parent_volume.get(\"issue\")\n            )\n            if journal_issue:\n                entries.append((\"number\", journal_issue))\n        else:\n            # not is_journal(self.full_id)\n            if \"xml_booktitle\" in self.attrib:\n                entries.append((\"booktitle\", self.get_booktitle(form=\"latex\")))\n            elif bibtype != \"proceedings\":\n                entries.append(\n                    (\"booktitle\", self.parent_volume.get_title(form=\"latex\"))\n                )\n        for entry in (\"month\", \"year\", \"address\", \"publisher\", \"note\"):\n            if self.get(entry) is not None:\n                entries.append((entry, bibtex_encode(self.get(entry))))\n        for entry in (\"url\", \"doi\"):\n            if entry in self.attrib:\n                # don't want latex escapes such as\n                # doi = \"10.1162/coli{\\_}a{\\_}00008\",\n                entries.append((entry, self.get(entry)))\n        if \"pages\" in self.attrib:\n            entries.append((\"pages\", self.get(\"pages\").replace(\"\u2013\", \"--\")))\n        if \"xml_abstract\" in self.attrib:\n            entries.append((\"abstract\", self.get_abstract(form=\"latex\")))\n\n        # Serialize it\n        return bibtex_make_entry(bibkey, bibtype, entries)\n\n    def as_dict(self):\n        value = self.attrib\n        value[\"paper_id\"] = self.paper_id\n        value[\"parent_volume_id\"] = self.parent_volume_id\n        value[\"bibkey\"] = self.bibkey\n        value[\"bibtype\"] = self.bibtype\n        return value\n\n    def items(self):\n        return self.attrib.items()\n", "path": "bin/anthology/papers.py"}], "after_files": [{"content": "# -*- coding: utf-8 -*-\n#\n# Copyright 2019 Marcel Bollmann <marcel@bollmann.me>\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport logging as log\nfrom .utils import (\n    parse_element,\n    infer_attachment_url,\n    remove_extra_whitespace,\n    is_journal,\n    is_volume_id,\n)\nfrom . import data\n\n# For BibTeX export\nfrom .formatter import bibtex_encode, bibtex_make_entry\n\nclass Paper:\n    def __init__(self, paper_id, ingest_date, volume, formatter):\n        self.parent_volume = volume\n        self.formatter = formatter\n        self._id = paper_id\n        self._ingest_date = ingest_date\n        self._bibkey = False\n        self.is_volume = paper_id == '0'\n\n        # initialize metadata with keys inherited from volume\n        self.attrib = {}\n        for key, value in volume.attrib.items():\n            # Only inherit 'editor' for frontmatter\n            if (key == 'editor' and not self.is_volume) or key in ('collection_id', 'booktitle', 'id', 'meta_data', 'meta_journal_title', 'meta_volume', 'meta_issue', 'sigs', 'venues', 'meta_date', 'url'):\n                continue\n\n            self.attrib[key] = value\n\n    def from_xml(xml_element, *args):\n        ingest_date = xml_element.get('ingest-date', data.UNKNOWN_INGEST_DATE)\n\n        # Default to paper ID \"0\" (for front matter)\n        paper = Paper(xml_element.get(\"id\", '0'), ingest_date, *args)\n\n        # Set values from parsing the XML element (overwriting\n        # and changing some initialized from the volume metadata)\n        for key, value in parse_element(xml_element).items():\n            if key == 'author' and 'editor' in paper.attrib:\n                del paper.attrib['editor']\n            paper.attrib[key] = value\n\n        # Frontmatter title is the volume 'booktitle'\n        if paper.is_volume:\n            paper.attrib['xml_title'] = paper.attrib['xml_booktitle']\n            paper.attrib['xml_title'].tag = 'title'\n\n        # Remove booktitle for frontmatter and journals\n        if paper.is_volume or is_journal(paper.full_id):\n            del paper.attrib['xml_booktitle']\n\n        # Expand URLs with paper ID\n        for tag in ('revision', 'erratum'):\n            if tag in paper.attrib:\n                for item in paper.attrib[tag]:\n                    if not item['url'].startswith(paper.full_id):\n                        log.error(\n                            \"{} must begin with paper ID '{}', but is '{}'\".format(\n                                tag, paper.full_id, item['url']\n                            )\n                        )\n                    item['url'] = data.ANTHOLOGY_URL.format(item['url'])\n\n        if 'attachment' in paper.attrib:\n            for item in paper.attrib['attachment']:\n                item['url'] = infer_attachment_url(item['url'], paper.full_id)\n\n        # Explicitly construct URL of original version of the paper\n        # -- this is a bit hacky, but it's not given in the XML\n        # explicitly\n        if 'revision' in paper.attrib:\n            paper.attrib['revision'].insert(0, {\n                \"value\": \"{}v1\".format(paper.full_id),\n                \"id\": \"1\",\n                \"url\": data.ANTHOLOGY_URL.format( \"{}v1\".format(paper.full_id)) } )\n\n        paper.attrib[\"title\"] = paper.get_title(\"plain\")\n        paper.attrib[\"booktitle\"] = paper.get_booktitle(\"plain\")\n\n        if \"editor\" in paper.attrib:\n            if paper.is_volume:\n                if \"author\" in paper.attrib:\n                    log.warn(\n                        \"Paper {} has both <editor> and <author>; ignoring <author>\".format(\n                            paper.full_id\n                        )\n                    )\n                # Proceedings editors are considered authors for their front matter\n                paper.attrib[\"author\"] = paper.attrib[\"editor\"]\n                del paper.attrib[\"editor\"]\n            else:\n                log.warn(\n                    \"Paper {} has <editor> but is not a proceedings volume; ignoring <editor>\".format(\n                        paper.full_id\n                    )\n                )\n        if \"pages\" in paper.attrib:\n            if paper.attrib[\"pages\"] is not None:\n                paper._interpret_pages()\n            else:\n                del paper.attrib[\"pages\"]\n\n        if 'author' in paper.attrib:\n            # for x in paper.attrib['author']:\n            #     print('X', x[0].full)\n            paper.attrib[\"author_string\"] = ', '.join([x[0].full for x in paper.attrib[\"author\"]])\n\n        return paper\n\n    def _interpret_pages(self):\n        \"\"\"Splits up 'pages' field into first and last page, if possible.\n\n        This is used for metadata in the generated HTML.\"\"\"\n        for s in (\"--\", \"-\", \"\u2013\"):\n            if self.attrib[\"pages\"].count(s) == 1:\n                self.attrib[\"page_first\"], self.attrib[\"page_last\"] = self.attrib[\n                    \"pages\"\n                ].split(s)\n                self.attrib[\"pages\"] = self.attrib[\"pages\"].replace(s, \"\u2013\")\n                return\n\n    @property\n    def ingest_date(self):\n        \"\"\"Inherit publication date from parent, but self overrides. May be undefined.\"\"\"\n        if self._ingest_date:\n            return self._ingest_date\n        if self.parent_volume:\n            return self.parent_volume.ingest_date\n        return data.UNKNOWN_INGEST_DATE\n\n    @property\n    def collection_id(self):\n        return self.parent_volume.collection_id\n\n    @property\n    def volume_id(self):\n        return self.parent_volume.volume_id\n\n    @property\n    def paper_id(self):\n        if self.collection_id[0] == \"W\" or self.collection_id == \"C69\":\n            # If volume is a workshop, use the last two digits of ID\n            _id = \"{}{:02d}\".format(self.volume_id, int(self._id))\n        else:\n            # If not, only the last three\n            _id = \"{}{:03d}\".format(self.volume_id, int(self._id))\n        # Just to be sure\n        assert len(_id) == 4\n        return _id\n\n    @property\n    def full_id(self):\n        return \"{}-{}\".format(self.collection_id, self.paper_id)\n\n    @property\n    def bibkey(self):\n        if not self._bibkey:\n            self._bibkey = self.full_id  # fallback\n        return self._bibkey\n\n    @bibkey.setter\n    def bibkey(self, value):\n        self._bibkey = value\n\n    @property\n    def bibtype(self):\n        if is_journal(self.full_id):\n            return \"article\"\n        elif self.is_volume:\n            return \"proceedings\"\n        else:\n            return \"inproceedings\"\n\n    @property\n    def parent_volume_id(self):\n        if self.parent_volume is not None:\n            return self.parent_volume.full_id\n        return None\n\n    @property\n    def has_abstract(self):\n        return \"xml_abstract\" in self.attrib\n\n    def get(self, name, default=None):\n        try:\n            return self.attrib[name]\n        except KeyError:\n            return default\n\n    def get_title(self, form=\"xml\"):\n        \"\"\"Returns the paper title, optionally formatting it.\n\n        Accepted formats:\n          - xml:   Include any contained XML tags unchanged\n          - plain: Strip all XML tags, returning only plain text\n          - html:  Convert XML tags into valid HTML tags\n          - latex: Convert XML tags into LaTeX commands\n        \"\"\"\n        return self.formatter(self.get(\"xml_title\"), form)\n\n    def get_abstract(self, form=\"xml\"):\n        \"\"\"Returns the abstract, optionally formatting it.\n\n        See `get_title()` for details.\n        \"\"\"\n        return self.formatter(self.get(\"xml_abstract\"), form, allow_url=True)\n\n    def get_booktitle(self, form=\"xml\", default=''):\n        \"\"\"Returns the booktitle, optionally formatting it.\n\n        See `get_title()` for details.\n        \"\"\"\n        if 'xml_booktitle' in self.attrib:\n            return self.formatter(self.get(\"xml_booktitle\"), form)\n        elif self.parent_volume is not None:\n            return self.parent_volume.get('title')\n        else:\n            return default\n\n    def as_bibtex(self):\n        \"\"\"Return the BibTeX entry for this paper.\"\"\"\n        # Build BibTeX entry\n        bibkey = self.bibkey\n        bibtype = self.bibtype\n        entries = [(\"title\", self.get_title(form=\"latex\"))]\n        for people in (\"author\", \"editor\"):\n            if people in self.attrib:\n                entries.append(\n                    (people, \"  and  \".join(p.as_bibtex() for p, _ in self.get(people)))\n                )\n        if is_journal(self.full_id):\n            entries.append(\n                (\"journal\", bibtex_encode(self.parent_volume.get(\"meta_journal_title\")))\n            )\n            journal_volume = self.parent_volume.get(\n                \"meta_volume\", self.parent_volume.get(\"volume\")\n            )\n            if journal_volume:\n                entries.append((\"volume\", journal_volume))\n            journal_issue = self.parent_volume.get(\n                \"meta_issue\", self.parent_volume.get(\"issue\")\n            )\n            if journal_issue:\n                entries.append((\"number\", journal_issue))\n        else:\n            # not is_journal(self.full_id)\n            if \"xml_booktitle\" in self.attrib:\n                entries.append((\"booktitle\", self.get_booktitle(form=\"latex\")))\n            elif bibtype != \"proceedings\":\n                entries.append(\n                    (\"booktitle\", self.parent_volume.get_title(form=\"latex\"))\n                )\n        for entry in (\"month\", \"year\", \"address\", \"publisher\", \"note\"):\n            if self.get(entry) is not None:\n                entries.append((entry, bibtex_encode(self.get(entry))))\n        for entry in (\"url\", \"doi\"):\n            if entry in self.attrib:\n                # don't want latex escapes such as\n                # doi = \"10.1162/coli{\\_}a{\\_}00008\",\n                entries.append((entry, self.get(entry)))\n        if \"pages\" in self.attrib:\n            entries.append((\"pages\", self.get(\"pages\").replace(\"\u2013\", \"--\")))\n        if \"xml_abstract\" in self.attrib:\n            entries.append((\"abstract\", self.get_abstract(form=\"latex\")))\n\n        # Serialize it\n        return bibtex_make_entry(bibkey, bibtype, entries)\n\n    def as_dict(self):\n        value = self.attrib\n        value[\"paper_id\"] = self.paper_id\n        value[\"parent_volume_id\"] = self.parent_volume_id\n        value[\"bibkey\"] = self.bibkey\n        value[\"bibtype\"] = self.bibtype\n        return value\n\n    def items(self):\n        return self.attrib.items()\n", "path": "bin/anthology/papers.py"}]}