Skip to content

Commit a973e67

Browse files
authored
Merge pull request #86 from con/enh-orcid
Various little enhancements while dealing with interactive ORCID entry + add final empty line in pretty json dump
2 parents 98f3eac + 1b5cda9 commit a973e67

4 files changed

Lines changed: 74 additions & 55 deletions

File tree

tributors/main/orcid.py

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
"""
1010

1111
from tributors.utils.file import write_file, get_tmpfile
12-
from tributors.utils.prompt import choice_prompt
12+
from tributors.utils.prompt import choice_prompt, entry_prompt
1313
import logging
1414
import os
1515
import requests
@@ -193,16 +193,34 @@ def record_search(url, email, interactive=False, search_type=""):
193193

194194
# If interactive, ask for choice prompt
195195
if interactive:
196-
choices = [str(i) for i, _ in enumerate(results, 1)] + ["s", "S", "skip"]
197-
prefix = "1:%s or s to skip" % min(10, len(results))
196+
skip_choices = ["s", "S", "skip"]
197+
enter_choices = ["e", "E", "enter"]
198+
quit_choices = ["q"]
199+
choices = (
200+
[str(i) for i, _ in enumerate(results, 1)]
201+
+ skip_choices
202+
+ enter_choices
203+
+ quit_choices
204+
)
205+
prefix = "1:%s or s to skip, e to enter, q to quit the loop" % min(
206+
10, len(results)
207+
)
198208
choice = choice_prompt(
199-
"Please enter a choice, or s to skip.",
209+
"Please enter a choice, or s to skip, e to enter.",
200210
choices=choices,
201211
choice_prefix=prefix,
202212
multiple=False,
203213
)
214+
if choice in quit_choices:
215+
raise StopIteration("Requested by user")
216+
217+
if choice in enter_choices:
218+
return entry_prompt(
219+
f"Please enter the ORCID for {email}.",
220+
regex="[0-9]{4}-[0-9]{4}-[0-9]{4}-[0-9]{3}[0-9X]$",
221+
)
204222

205-
if not choice or choice in ["s", "S", "skip"]:
223+
if not choice or choice in skip_choices:
206224
return
207225

208226
# Return the orcid identifier

tributors/main/parsers/base.py

Lines changed: 23 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -142,23 +142,26 @@ def update_cache(self, update_lookup=True):
142142

143143
# Then add an Orcid lookup
144144
for login, entry in self.cache.items():
145-
# If we have an email, and orcid isn't defined
146-
if "orcid" not in entry:
147-
orcid = get_orcid(
148-
entry.get("email"), entry.get("name"), interactive=interactive
149-
)
150-
if orcid:
151-
entry["orcid"] = orcid
152-
cli = OrcidIdentifier(orcid)
153-
154-
# If we found the record, update metadata
155-
if (
156-
cli.found
157-
and not entry.get("name")
158-
or entry.get("name") == login
159-
):
160-
entry["name"] = "%s %s" % (cli.firstName, cli.lastName)
161-
if cli.affiliation and not entry.get("affiliation"):
162-
entry["affiliation"] = cli.affiliation
163-
164-
self.cache[login] = entry
145+
try:
146+
# If we have an email, and orcid isn't defined
147+
if "orcid" not in entry:
148+
orcid = get_orcid(
149+
entry.get("email"), entry.get("name"), interactive=interactive
150+
)
151+
if orcid:
152+
entry["orcid"] = orcid
153+
cli = OrcidIdentifier(orcid)
154+
155+
# If we found the record, update metadata
156+
if (
157+
cli.found
158+
and not entry.get("name")
159+
or entry.get("name") == login
160+
):
161+
entry["name"] = "%s %s" % (cli.firstName, cli.lastName)
162+
if cli.affiliation and not entry.get("affiliation"):
163+
entry["affiliation"] = cli.affiliation
164+
165+
self.cache[login] = entry
166+
except StopIteration:
167+
break

tributors/utils/file.py

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -20,15 +20,13 @@ def write_json(json_obj, filename, pretty=True):
2020
- json_obj (dict) : the dict to print to json
2121
- filename (str) : the output file to write to
2222
"""
23+
kw = dict(indent=4, separators=(",", ": ")) if pretty else {}
2324
with open(filename, "w", encoding="utf8") as filey:
24-
if pretty:
25-
filey.writelines(
26-
json.dumps(
27-
json_obj, indent=4, ensure_ascii=False, separators=(",", ": ")
28-
)
29-
)
30-
else:
31-
filey.writelines(json.dumps(json_obj), ensure_ascii=False)
25+
dump = json.dumps(json_obj, ensure_ascii=False, **kw)
26+
if pretty and not dump.endswith(os.linesep):
27+
# Add newline as it is typically desired
28+
dump += os.linesep
29+
filey.write(dump)
3230
return filename
3331

3432

tributors/utils/prompt.py

Lines changed: 22 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,15 @@
11
"""
22
33
Copyright (C) 2020-2022 Vanessa Sochat.
4+
2024 Yaroslav O. Halchenko
45
56
This Source Code Form is subject to the terms of the
67
Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed
78
with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
89
910
"""
1011

11-
12-
enter_input = getattr(__builtins__, "raw_input", input)
13-
14-
15-
def request_input():
16-
"""Wait for the user to input some string, optionally with multiple lines."""
17-
lines = []
18-
19-
# The message can be multiple lines
20-
while True:
21-
try:
22-
line = enter_input()
23-
except EOFError:
24-
break
25-
if line:
26-
lines.append(line)
27-
28-
return "\n".join(lines)
12+
import re
2913

3014

3115
def choice_prompt(prompt, choices, choice_prefix=None, multiple=False):
@@ -41,15 +25,12 @@ def choice_prompt(prompt, choices, choice_prefix=None, multiple=False):
4125
choice = None
4226
print(prompt)
4327

44-
# Support for Python 2 (raw_input)
45-
get_input = getattr(__builtins__, "raw_input", input)
46-
4728
if not choice_prefix:
4829
choice_prefix = "/".join(choices)
4930
message = "[%s] : " % (choice_prefix)
5031

5132
while choice not in choices:
52-
choice = get_input(message).strip()
33+
choice = input(message).strip()
5334

5435
# If multiple allowed, add selection to choices if includes all valid
5536
if multiple is True:
@@ -58,3 +39,22 @@ def choice_prompt(prompt, choices, choice_prefix=None, multiple=False):
5839
choices.append(choice)
5940
message = "Please enter a valid option in [%s]" % choice_prefix
6041
return choice
42+
43+
44+
def entry_prompt(prompt, regex=None):
45+
"""Ask the user for a prompt, and only return when a valid entry is provided.
46+
47+
Parameters
48+
==========
49+
prompt: the prompt to ask the user
50+
regex: a regular expression to match the entry
51+
"""
52+
entry = None
53+
print(prompt)
54+
message = "Please enter a value. Empty to skip: "
55+
while not entry:
56+
entry = input(message).strip()
57+
if entry and regex is not None and not re.match(regex, entry):
58+
entry = None
59+
message = r"Please enter a valid response. Should match regex {regex!r}"
60+
return entry

0 commit comments

Comments
 (0)