goldrush 0.0.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
goldrush-0.0.1/LICENSE ADDED
@@ -0,0 +1,25 @@
1
+ Redistribution and use in source and binary forms, with or without
2
+ modification, are permitted provided that the following conditions are met:
3
+
4
+ 1. Redistributions of source code must retain the above copyright notice, this
5
+ list of conditions and the following disclaimer.
6
+
7
+ 2. Redistributions in binary form must reproduce the above copyright notice,
8
+ this list of conditions and the following disclaimer in the documentation
9
+ and/or other materials provided with the distribution.
10
+
11
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
12
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
13
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
14
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
15
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
16
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
17
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
18
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
19
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
20
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
21
+
22
+ Copyright for this project is held by its many contributors, including:
23
+
24
+ Jane Sandberg <js7389@princeton.edu>
25
+ Ed Summers <ehs@pobox.com>
@@ -0,0 +1,67 @@
1
+ Metadata-Version: 2.3
2
+ Name: goldrush
3
+ Version: 0.0.1
4
+ Summary: Generate Gold Rush key for pymarc records
5
+ Author: Jane Sandberg, Ed Summers
6
+ Author-email: Jane Sandberg <js7389@princeton.edu>, Ed Summers <ehs@pobox.com>
7
+ License: Redistribution and use in source and binary forms, with or without
8
+ modification, are permitted provided that the following conditions are met:
9
+
10
+ 1. Redistributions of source code must retain the above copyright notice, this
11
+ list of conditions and the following disclaimer.
12
+
13
+ 2. Redistributions in binary form must reproduce the above copyright notice,
14
+ this list of conditions and the following disclaimer in the documentation
15
+ and/or other materials provided with the distribution.
16
+
17
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
18
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
21
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
23
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
24
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
25
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
+
28
+ Copyright for this project is held by its many contributors, including:
29
+
30
+ Jane Sandberg <js7389@princeton.edu>
31
+ Ed Summers <ehs@pobox.com>
32
+ Requires-Dist: pymarc>=5.3.1
33
+ Requires-Dist: unidecode>=1.4.0
34
+ Requires-Python: >=3.13
35
+ Description-Content-Type: text/markdown
36
+
37
+ # goldrush
38
+
39
+ **Note: This is alpha software. If you rely on this library you should do so with the
40
+ understanding that you might find errors in the Gold Rush key that is generated.
41
+ Lets make it better together!**
42
+
43
+ ---
44
+
45
+ [![Build status](https://gitlab.com/pymarc/goldrush/badges/main/pipeline.svg)](https://gitlab.com/pymarc/goldrush/-/commits/main)
46
+
47
+ *goldrush* is a Python implementation of the [Gold Rush match key algorithm]
48
+ for identifying "duplicate" MARC records, or records that appear to be about
49
+ the same book. It provides a function that you pass a pymarc Record and get
50
+ back the Gold Rush key as a string.
51
+
52
+ goldrush was largely adapted from [pymarc_dedupe], which included other
53
+ functionality unrelated to Gold Rush, and was not available on PyPI as a
54
+ dependency.
55
+
56
+ ## Usage
57
+
58
+ ```python
59
+ >>> from goldrush import goldrush
60
+ >>> from pymarc import MARCReader
61
+ >>> record = next(MARCReader(open('marc.dat', 'rb')))
62
+ >>> goldrush(record)
63
+ 'pragmaticprogrammerfromjourneymantomaster_____________________________2000321____addisa_______________________________________________________hunt________________p'
64
+ ```
65
+
66
+ [Gold Rush match key algorithm]: https://coalliance.org/sites/default/files/GoldRush-Match_KeyJanuary2024_0.doc
67
+ [pymarc_dedupe]: https://github.com/pulibrary/pymarc_dedupe
@@ -0,0 +1,31 @@
1
+ # goldrush
2
+
3
+ **Note: This is alpha software. If you rely on this library you should do so with the
4
+ understanding that you might find errors in the Gold Rush key that is generated.
5
+ Lets make it better together!**
6
+
7
+ ---
8
+
9
+ [![Build status](https://gitlab.com/pymarc/goldrush/badges/main/pipeline.svg)](https://gitlab.com/pymarc/goldrush/-/commits/main)
10
+
11
+ *goldrush* is a Python implementation of the [Gold Rush match key algorithm]
12
+ for identifying "duplicate" MARC records, or records that appear to be about
13
+ the same book. It provides a function that you pass a pymarc Record and get
14
+ back the Gold Rush key as a string.
15
+
16
+ goldrush was largely adapted from [pymarc_dedupe], which included other
17
+ functionality unrelated to Gold Rush, and was not available on PyPI as a
18
+ dependency.
19
+
20
+ ## Usage
21
+
22
+ ```python
23
+ >>> from goldrush import goldrush
24
+ >>> from pymarc import MARCReader
25
+ >>> record = next(MARCReader(open('marc.dat', 'rb')))
26
+ >>> goldrush(record)
27
+ 'pragmaticprogrammerfromjourneymantomaster_____________________________2000321____addisa_______________________________________________________hunt________________p'
28
+ ```
29
+
30
+ [Gold Rush match key algorithm]: https://coalliance.org/sites/default/files/GoldRush-Match_KeyJanuary2024_0.doc
31
+ [pymarc_dedupe]: https://github.com/pulibrary/pymarc_dedupe
@@ -0,0 +1,26 @@
1
+ [project]
2
+ name = "goldrush"
3
+ version = "0.0.1"
4
+ description = "Generate Gold Rush key for pymarc records"
5
+ readme = "README.md"
6
+ license = {file = "LICENSE"}
7
+ authors = [
8
+ { name = "Jane Sandberg", email = "js7389@princeton.edu" },
9
+ { name = "Ed Summers", email = "ehs@pobox.com" }
10
+ ]
11
+ requires-python = ">=3.13"
12
+ dependencies = [
13
+ "pymarc>=5.3.1",
14
+ "unidecode>=1.4.0",
15
+ ]
16
+
17
+ [build-system]
18
+ requires = ["uv_build>=0.11.6,<0.12.0"]
19
+ build-backend = "uv_build"
20
+
21
+ [dependency-groups]
22
+ dev = [
23
+ "pytest>=9.0.3",
24
+ "ruff>=0.15.11",
25
+ "ty>=0.0.31",
26
+ ]
@@ -0,0 +1,219 @@
1
+ import re
2
+ from functools import cache
3
+ from unidecode import unidecode
4
+
5
+ from pymarc import Record
6
+
7
+ from goldrush.record import GoldrushRecord
8
+
9
+
10
+ def goldrush(pymarc_rec: Record) -> str:
11
+ """
12
+ Pass in a pymarc.Record object and get back a Gold Rush key as a string.
13
+ """
14
+ rec = GoldrushRecord(pymarc_rec)
15
+ return (
16
+ _title(rec)
17
+ + (_publication_year(rec))
18
+ + _pagination(rec)
19
+ + _edition(rec)
20
+ + _publisher_name(rec)
21
+ + rec.type_of()
22
+ + _title_part(rec)
23
+ + _title_number(rec)
24
+ + _title_inclusive_dates(rec)
25
+ + _author(rec)
26
+ + _gov_doc_number(rec)
27
+ + _format_character(rec)
28
+ )
29
+
30
+
31
+ def _title(rec: GoldrushRecord) -> str:
32
+ some_string = _normalize_title(rec.title())
33
+ return _pad(some_string, 70)
34
+
35
+
36
+ def _title_part(rec: GoldrushRecord) -> str:
37
+ some_string = _normalize_title(rec.title_part())
38
+ return _pad(some_string, 30)
39
+
40
+
41
+ def _publication_year(rec: GoldrushRecord) -> str:
42
+ some_string = str(rec.publication_year() or "")
43
+ return _pad(some_string, 4)
44
+
45
+
46
+ def _pagination(rec: GoldrushRecord) -> str:
47
+ try:
48
+ nums = re.findall(r"\d+", rec.pagination())[0]
49
+ except (TypeError, IndexError):
50
+ nums = ""
51
+ return _pad(nums, 4)
52
+
53
+
54
+ def _edition(rec: GoldrushRecord) -> str:
55
+ lowercase_ed = ""
56
+ try:
57
+ lowercase_ed = rec.edition().lower()
58
+ chars = re.findall(r"\d+", lowercase_ed)[0]
59
+ except (TypeError, IndexError):
60
+ try:
61
+ chars = re.findall(r"\w+", lowercase_ed)[0]
62
+ chars = chars[0:3]
63
+ for ordinal, numeral in _edition_dictionary().items():
64
+ chars = chars.replace(ordinal, numeral)
65
+ except (TypeError, IndexError):
66
+ chars = ""
67
+ return unidecode(_pad(chars, 3))
68
+
69
+
70
+ def _publisher_name(rec: GoldrushRecord) -> str:
71
+ publisher_name = rec.publisher_name().lower()
72
+ publisher_name = publisher_name.translate(
73
+ str.maketrans(_title_translation_dictionary())
74
+ )
75
+ return _pad(publisher_name, 5)
76
+
77
+
78
+ def _title_number(rec: GoldrushRecord) -> str:
79
+ title_number = rec.title_number() or ""
80
+ return _pad(title_number, 10)
81
+
82
+
83
+ def _title_inclusive_dates(rec: GoldrushRecord) -> str:
84
+ title_dates = rec.title_inclusive_dates() or ""
85
+ title_dates = _strip_punctuation(title_dates)
86
+ return _pad(title_dates, 15)
87
+
88
+
89
+ def _author(rec: GoldrushRecord) -> str:
90
+ author = rec.author() or ""
91
+ author = _strip_punctuation(author)
92
+ return _pad(author, 5)
93
+
94
+
95
+ def _gov_doc_number(rec: GoldrushRecord) -> str:
96
+ gov_doc_number = rec.gov_doc_number() or ""
97
+ gov_doc_number = _strip_punctuation(gov_doc_number)
98
+ return _pad(gov_doc_number, 15)
99
+
100
+
101
+ def _format_character(rec: GoldrushRecord) -> str:
102
+ if rec.is_electronic_resource():
103
+ return "e"
104
+ return "p"
105
+
106
+
107
+ def _normalize_title(some_string: str) -> str:
108
+ some_string = some_string.strip().lower()
109
+ # remove beginning English articles
110
+ some_string = re.sub("^the +", "", some_string)
111
+ some_string = re.sub("^a +", "", some_string)
112
+ some_string = re.sub("^an +", "", some_string)
113
+ some_string = some_string.translate(str.maketrans(_title_translation_dictionary()))
114
+ return unidecode(some_string)
115
+
116
+
117
+ def _pad(string: str, length: int) -> str:
118
+ return string.ljust(length, "_")[0:length]
119
+
120
+
121
+ def _strip_punctuation(some_string: str) -> str:
122
+ some_string = re.sub(" ", " ", some_string).strip().lower()
123
+ some_string = re.sub("^the +", "", some_string)
124
+ some_string = some_string.translate(str.maketrans(_translation_dictionary()))
125
+ return unidecode(some_string)
126
+
127
+
128
+ @cache
129
+ def _edition_dictionary() -> dict[str, str]:
130
+ return {
131
+ "fir": "1__",
132
+ "sec": "2__",
133
+ "thr": "3__",
134
+ "fou": "4__",
135
+ "fiv": "5__",
136
+ "six": "6__",
137
+ "sev": "7__",
138
+ "eig": "8__",
139
+ "nin": "9__",
140
+ "ten": "10_",
141
+ }
142
+
143
+
144
+ @cache
145
+ def _title_translation_dictionary() -> dict[str, str]:
146
+ return {
147
+ "&": "and",
148
+ "%": "",
149
+ "'": "",
150
+ "{": "",
151
+ "}": "",
152
+ " ": "",
153
+ "!": "",
154
+ '"': "",
155
+ "#": "",
156
+ "$": "",
157
+ "(": "",
158
+ ")": "",
159
+ "*": "",
160
+ "+": "",
161
+ ",": "",
162
+ "-": "",
163
+ ".": "",
164
+ "/": "",
165
+ ":": "",
166
+ ";": "",
167
+ "<": "",
168
+ "=": "",
169
+ ">": "",
170
+ "?": "",
171
+ "@": "",
172
+ "[": "",
173
+ "\\": "",
174
+ "]": "",
175
+ "^": "",
176
+ "`": "",
177
+ "|": "",
178
+ "~": "",
179
+ "©": "",
180
+ }
181
+
182
+
183
+ @cache
184
+ def _translation_dictionary() -> dict[str, str]:
185
+ return {
186
+ "&": "and",
187
+ "%": "_",
188
+ "'": "",
189
+ "{": "",
190
+ "}": "",
191
+ " ": "_",
192
+ "!": "_",
193
+ '"': "_",
194
+ "#": "_",
195
+ "$": "_",
196
+ "(": "_",
197
+ ")": "_",
198
+ "*": "_",
199
+ "+": "_",
200
+ ",": "_",
201
+ "-": "_",
202
+ ".": "_",
203
+ "/": "_",
204
+ ":": "_",
205
+ ";": "_",
206
+ "<": "_",
207
+ "=": "_",
208
+ ">": "_",
209
+ "?": "_",
210
+ "@": "_",
211
+ "[": "_",
212
+ "\\": "_",
213
+ "]": "_",
214
+ "^": "_",
215
+ "`": "_",
216
+ "|": "_",
217
+ "~": "_",
218
+ "©": "_",
219
+ }
File without changes
@@ -0,0 +1,292 @@
1
+ import re
2
+ import string
3
+
4
+ import pymarc
5
+
6
+
7
+ class GoldrushRecord:
8
+ def __init__(self, record):
9
+ self.record = record
10
+
11
+ def id(self):
12
+ try:
13
+ return self.record.get("001").data
14
+ except (KeyError, AttributeError):
15
+ return ""
16
+
17
+ def title(self):
18
+ if self.__vernacular_title_field():
19
+ title_field = self.__vernacular_title_field()
20
+ else:
21
+ title_field = self.__title_from_245()
22
+ try:
23
+ subfield_a = str(title_field.get("a") or "")
24
+ subfield_b = str(title_field.get("b") or "")
25
+ subfield_p = str(title_field.get("p") or "")
26
+ title = " ".join([subfield_a, subfield_b, subfield_p])
27
+ title = self.__strip_ending_punctuation(title)
28
+ return title
29
+ except (KeyError, AttributeError):
30
+ return ""
31
+
32
+ def transliterated_title(self):
33
+ title_field = self.__title_from_245()
34
+ try:
35
+ subfield_a = str(title_field.get("a") or "")
36
+ subfield_b = str(title_field.get("b") or "")
37
+ subfield_p = str(title_field.get("p") or "")
38
+ title = " ".join([subfield_a, subfield_b, subfield_p])
39
+ title = self.__strip_ending_punctuation(title)
40
+ return title
41
+ except (KeyError, AttributeError):
42
+ return ""
43
+
44
+ def __title_from_245(self):
45
+ try:
46
+ title = self.record["245"]
47
+ return title
48
+ except KeyError:
49
+ return ""
50
+
51
+ def __vernacular_title_field(self):
52
+ try:
53
+ return self.record.get_linked_fields(self.record["245"])[0]
54
+ except (KeyError, IndexError, pymarc.exceptions.MissingLinkedFields):
55
+ return ""
56
+
57
+ def publication_year(self):
58
+ pub_year = None
59
+ if self.date_one() and self.date_two():
60
+ pub_year = self.date_two()
61
+ elif self.date_one() and not self.date_two():
62
+ pub_year = self.date_one()
63
+ elif (
64
+ not self.date_one() and not self.date_two() and self.__date_of_production()
65
+ ):
66
+ pub_year = self.__date_of_production()
67
+ elif (
68
+ not self.date_one()
69
+ and not self.date_two()
70
+ and not self.__date_of_production()
71
+ and self.__date_of_publication()
72
+ ):
73
+ pub_year = self.__date_of_publication()
74
+ return pub_year
75
+
76
+ def pagination(self):
77
+ try:
78
+ subfield_a = self.record["300"].get("a")
79
+ if subfield_a:
80
+ return self.__normalize_extent(subfield_a)
81
+ return ""
82
+ except KeyError:
83
+ return ""
84
+
85
+ def edition(self):
86
+ try:
87
+ return self.__normalize_edition(self.record["250"].get("a"))
88
+ except KeyError:
89
+ return ""
90
+
91
+ def publisher_name(self):
92
+ try:
93
+ pub = self.record["264"]["b"]
94
+ except KeyError:
95
+ try:
96
+ pub = self.record["260"]["b"]
97
+ except KeyError:
98
+ return ""
99
+ return self.__strip_punctuation(pub)
100
+
101
+ def type_of(self):
102
+ return self.record.leader.type_of_record
103
+
104
+ def title_part(self):
105
+ try:
106
+ parts = self.record["245"].get_subfields("p")[1:]
107
+ return self.__strip_punctuation(" ".join(parts))
108
+ except KeyError:
109
+ return ""
110
+
111
+ def title_number(self):
112
+ try:
113
+ num = self.record["245"].get("n")
114
+ if num:
115
+ return self.__strip_punctuation(num)
116
+ return ""
117
+ except KeyError:
118
+ return ""
119
+
120
+ def author(self):
121
+ if self.__vernacular_author_field():
122
+ author_field = self.__vernacular_author_field()
123
+ else:
124
+ author_field = self.__author_from_1xx()
125
+
126
+ if author_field:
127
+ try:
128
+ return self.__strip_ending_punctuation(author_field.get("a"))
129
+ except AttributeError:
130
+ return ""
131
+ return ""
132
+
133
+ def __author_from_1xx(self):
134
+ try:
135
+ return self.record["100"]
136
+ except KeyError:
137
+ try:
138
+ return self.record["110"]
139
+ except KeyError:
140
+ try:
141
+ return self.record["111"]
142
+ except KeyError:
143
+ return ""
144
+
145
+ def __vernacular_author_field(self):
146
+ try:
147
+ return self.record.get_linked_fields(self.record["100"])[0]
148
+ except (KeyError, IndexError, pymarc.exceptions.MissingLinkedFields):
149
+ try:
150
+ return self.record.get_linked_fields(self.record["110"])[0]
151
+ except (KeyError, IndexError, pymarc.exceptions.MissingLinkedFields):
152
+ try:
153
+ return self.record.get_linked_fields(self.record["111"])[0]
154
+ except (KeyError, IndexError, pymarc.exceptions.MissingLinkedFields):
155
+ return ""
156
+
157
+ def title_inclusive_dates(self):
158
+ try:
159
+ date = self.record["245"].get("f")
160
+ if date:
161
+ return self.__strip_ending_punctuation(date)
162
+ return ""
163
+ except KeyError:
164
+ return ""
165
+
166
+ def gov_doc_number(self):
167
+ try:
168
+ return self.record["086"].get("a")
169
+ except KeyError:
170
+ return ""
171
+
172
+ def is_electronic_resource(self):
173
+ return bool(
174
+ self.__is_electronic_resource_from_title()
175
+ or self.__is_electronic_resource_from_reproduction()
176
+ or self.__is_electronic_resource_from_description()
177
+ or self.__is_electronic_resource_from_007()
178
+ )
179
+
180
+ def __is_electronic_resource_from_title(self):
181
+ try:
182
+ return self.record["245"].get("h") == "[electronic resource]"
183
+ except KeyError:
184
+ return False
185
+
186
+ def __is_electronic_resource_from_reproduction(self):
187
+ try:
188
+ return re.match(
189
+ "electronic reproduction", self.record["533"].get("a"), re.IGNORECASE
190
+ )
191
+ except (KeyError, TypeError):
192
+ return False
193
+
194
+ def __is_electronic_resource_from_description(self):
195
+ try:
196
+ subfield_a = self.record["300"].get("a")
197
+ if subfield_a:
198
+ return bool(re.search("online resource", subfield_a, re.IGNORECASE))
199
+ return False
200
+ except KeyError:
201
+ return False
202
+
203
+ def __is_electronic_resource_from_007(self):
204
+ try:
205
+ return bool(self.record["007"].data[0] == "c")
206
+ except KeyError:
207
+ return False
208
+
209
+ def __normalize_edition(self, edition):
210
+ edition_mapping = {r"Ed\.": "Edition", r"ed\.": "edition"}
211
+ try:
212
+ for key, value in edition_mapping.items():
213
+ edition = re.sub(key, value, edition)
214
+ return self.__strip_punctuation(edition)
215
+ except TypeError:
216
+ return ""
217
+
218
+ def __normalize_extent(self, extent):
219
+ extent_mapping = {
220
+ r"p\.": "pages",
221
+ r"v\.": "volumes",
222
+ r"vol\.": "volumes",
223
+ r"ℓ\.": "leaves",
224
+ }
225
+ for key, value in extent_mapping.items():
226
+ extent = re.sub(key, value, extent)
227
+ return self.__strip_punctuation(extent)
228
+
229
+ def __strip_ending_punctuation(self, some_string):
230
+ punctuation_to_strip = string.punctuation.replace(")", "")
231
+ return some_string.strip(punctuation_to_strip + " ")
232
+
233
+ def __strip_punctuation(self, some_string):
234
+ punctuation_to_strip = string.punctuation.replace("&", "")
235
+ some_string = some_string.translate(str.maketrans("", "", punctuation_to_strip))
236
+ some_string = re.sub(" ", " ", some_string).strip()
237
+ return some_string
238
+
239
+ def is_valid_date(self, date_string):
240
+ valid = True
241
+ if date_string == "9999":
242
+ valid = False
243
+ elif date_string == " ":
244
+ valid = False
245
+ elif self.number_of_characters(date_string) != 4:
246
+ valid = False
247
+ try:
248
+ int(date_string)
249
+ except (ValueError, TypeError):
250
+ valid = False
251
+ return valid
252
+
253
+ def number_of_characters(self, date_string):
254
+ try:
255
+ return len(date_string)
256
+ except TypeError:
257
+ return False
258
+
259
+ def date_one(self):
260
+ try:
261
+ date_string = self.record["008"].data[7:11]
262
+ return self.__as_date(date_string)
263
+ except KeyError:
264
+ return None
265
+
266
+ def date_two(self):
267
+ try:
268
+ date_string = self.record["008"].data[11:15]
269
+ return self.__as_date(date_string)
270
+ except KeyError:
271
+ return None
272
+
273
+ def __date_of_production(self):
274
+ try:
275
+ date_string = self.record["264"]["c"]
276
+ except KeyError:
277
+ return None
278
+ return self.__as_date(date_string)
279
+
280
+ def __date_of_publication(self):
281
+ try:
282
+ date_string = self.record["260"]["c"]
283
+ except KeyError:
284
+ return ""
285
+ return self.__as_date(date_string)
286
+
287
+ def __as_date(self, date_string):
288
+ # Remove punctuation (for 260 and 264 fields)
289
+ date_string = self.__strip_punctuation(date_string)
290
+ if self.is_valid_date(date_string):
291
+ return int(date_string)
292
+ return ""