papers-cli 2.15.5__tar.gz → 2.16.2__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.
- {papers_cli-2.15.5 → papers_cli-2.16.2}/.github/workflows/pypi.yml +11 -4
- papers_cli-2.16.2/.github/workflows/release.yml +58 -0
- {papers_cli-2.15.5 → papers_cli-2.16.2}/.github/workflows/tests.yml +3 -3
- {papers_cli-2.15.5 → papers_cli-2.16.2}/PKG-INFO +10 -3
- {papers_cli-2.15.5 → papers_cli-2.16.2}/README.md +8 -1
- {papers_cli-2.15.5 → papers_cli-2.16.2}/papers/__main__.py +27 -25
- {papers_cli-2.15.5 → papers_cli-2.16.2}/papers/_version.py +3 -3
- {papers_cli-2.15.5 → papers_cli-2.16.2}/papers/bib.py +162 -126
- {papers_cli-2.15.5 → papers_cli-2.16.2}/papers/config.py +2 -2
- {papers_cli-2.15.5 → papers_cli-2.16.2}/papers/duplicate.py +108 -25
- {papers_cli-2.15.5 → papers_cli-2.16.2}/papers/encoding.py +50 -23
- papers_cli-2.16.2/papers/entries.py +107 -0
- {papers_cli-2.15.5 → papers_cli-2.16.2}/papers/extract.py +21 -25
- {papers_cli-2.15.5 → papers_cli-2.16.2}/papers/filename.py +12 -9
- {papers_cli-2.15.5 → papers_cli-2.16.2}/papers_cli.egg-info/PKG-INFO +10 -3
- {papers_cli-2.15.5 → papers_cli-2.16.2}/papers_cli.egg-info/SOURCES.txt +12 -0
- {papers_cli-2.15.5 → papers_cli-2.16.2}/papers_cli.egg-info/requires.txt +1 -1
- {papers_cli-2.15.5 → papers_cli-2.16.2}/pyproject.toml +7 -5
- {papers_cli-2.15.5 → papers_cli-2.16.2}/requirements.txt +1 -1
- papers_cli-2.16.2/scripts/README.md +38 -0
- papers_cli-2.16.2/scripts/benchmark_bibtexparser_load.py +112 -0
- papers_cli-2.16.2/scripts/generate_dummy_bib.py +51 -0
- {papers_cli-2.15.5 → papers_cli-2.16.2}/tests/test_add.py +23 -10
- papers_cli-2.16.2/tests/test_bib_unit.py +135 -0
- papers_cli-2.16.2/tests/test_check.py +103 -0
- papers_cli-2.16.2/tests/test_config_unit.py +86 -0
- {papers_cli-2.15.5 → papers_cli-2.16.2}/tests/test_duplicates.py +190 -8
- papers_cli-2.16.2/tests/test_encoding.py +166 -0
- {papers_cli-2.15.5 → papers_cli-2.16.2}/tests/test_extract.py +4 -4
- papers_cli-2.16.2/tests/test_extract_unit.py +133 -0
- papers_cli-2.16.2/tests/test_filecheck.py +102 -0
- papers_cli-2.16.2/tests/test_filename_unit.py +17 -0
- {papers_cli-2.15.5 → papers_cli-2.16.2}/tests/test_install.py +9 -0
- papers_cli-2.16.2/tests/test_latexenc.py +85 -0
- {papers_cli-2.15.5 → papers_cli-2.16.2}/tests/test_list.py +55 -0
- papers_cli-2.16.2/tests/test_utils.py +118 -0
- papers_cli-2.15.5/tests/test_encoding.py +0 -41
- papers_cli-2.15.5/tests/test_filecheck.py +0 -48
- {papers_cli-2.15.5 → papers_cli-2.16.2}/.gitattributes +0 -0
- {papers_cli-2.15.5 → papers_cli-2.16.2}/.github/FUNDING.yml +0 -0
- {papers_cli-2.15.5 → papers_cli-2.16.2}/.gitignore +0 -0
- {papers_cli-2.15.5 → papers_cli-2.16.2}/LICENSE +0 -0
- {papers_cli-2.15.5 → papers_cli-2.16.2}/papers/__init__.py +0 -0
- {papers_cli-2.15.5 → papers_cli-2.16.2}/papers/latexenc.py +0 -0
- {papers_cli-2.15.5 → papers_cli-2.16.2}/papers/utils.py +0 -0
- {papers_cli-2.15.5 → papers_cli-2.16.2}/papers_cli.egg-info/dependency_links.txt +0 -0
- {papers_cli-2.15.5 → papers_cli-2.16.2}/papers_cli.egg-info/entry_points.txt +0 -0
- {papers_cli-2.15.5 → papers_cli-2.16.2}/papers_cli.egg-info/top_level.txt +0 -0
- {papers_cli-2.15.5 → papers_cli-2.16.2}/setup.cfg +0 -0
- {papers_cli-2.15.5 → papers_cli-2.16.2}/setup.py +0 -0
- {papers_cli-2.15.5 → papers_cli-2.16.2}/tests/__init__.py +0 -0
- {papers_cli-2.15.5 → papers_cli-2.16.2}/tests/common.py +0 -0
- {papers_cli-2.15.5 → papers_cli-2.16.2}/tests/config.py +0 -0
- {papers_cli-2.15.5 → papers_cli-2.16.2}/tests/download.py +0 -0
- {papers_cli-2.15.5 → papers_cli-2.16.2}/tests/downloadedpapers/.gitignore +0 -0
- {papers_cli-2.15.5 → papers_cli-2.16.2}/tests/downloadedpapers/bg-8-515-2011.txt +0 -0
- {papers_cli-2.15.5 → papers_cli-2.16.2}/tests/test_biblio.py +0 -0
- {papers_cli-2.15.5 → papers_cli-2.16.2}/tests/test_misc.py +0 -0
- {papers_cli-2.15.5 → papers_cli-2.16.2}/tests/test_undo.py +0 -0
|
@@ -1,9 +1,13 @@
|
|
|
1
|
+
# Publishes to PyPI on tag push (e.g. v2.16.1).
|
|
2
|
+
# If this job fails with "400 Bad Request", the package may still be on PyPI: that usually
|
|
3
|
+
# means the workflow ran twice (e.g. tag pushed twice or job re-run) and the second upload
|
|
4
|
+
# failed because the version already exists. Use verbose: true output to see the real error.
|
|
1
5
|
name: Publish to PyPI
|
|
2
6
|
|
|
3
7
|
on:
|
|
4
8
|
push:
|
|
5
9
|
tags:
|
|
6
|
-
- '*'
|
|
10
|
+
- 'v*'
|
|
7
11
|
|
|
8
12
|
jobs:
|
|
9
13
|
build-and-publish:
|
|
@@ -16,10 +20,10 @@ jobs:
|
|
|
16
20
|
|
|
17
21
|
steps:
|
|
18
22
|
- name: Checkout code
|
|
19
|
-
uses: actions/checkout@
|
|
23
|
+
uses: actions/checkout@v5
|
|
20
24
|
|
|
21
25
|
- name: Set up Python
|
|
22
|
-
uses: actions/setup-python@
|
|
26
|
+
uses: actions/setup-python@v5
|
|
23
27
|
with:
|
|
24
28
|
python-version: '3.x'
|
|
25
29
|
|
|
@@ -33,4 +37,7 @@ jobs:
|
|
|
33
37
|
python setup.py sdist bdist_wheel
|
|
34
38
|
|
|
35
39
|
- name: Publish to PyPI
|
|
36
|
-
uses: pypa/gh-action-pypi-publish@release/v1
|
|
40
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
41
|
+
with:
|
|
42
|
+
# Show full PyPI error (e.g. "400 File already exists") when upload fails
|
|
43
|
+
verbose: true
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
name: Create GitHub Release
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags:
|
|
6
|
+
- 'v*'
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
create-release:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
permissions:
|
|
12
|
+
contents: write # Required to create releases
|
|
13
|
+
|
|
14
|
+
steps:
|
|
15
|
+
- name: Checkout code
|
|
16
|
+
uses: actions/checkout@v4
|
|
17
|
+
with:
|
|
18
|
+
fetch-depth: 0 # Fetch all history for changelog generation
|
|
19
|
+
|
|
20
|
+
- name: Get tag name
|
|
21
|
+
id: tag
|
|
22
|
+
run: echo "tag=${GITHUB_REF#refs/tags/}" >> $GITHUB_OUTPUT
|
|
23
|
+
|
|
24
|
+
- name: Generate changelog
|
|
25
|
+
id: changelog
|
|
26
|
+
run: |
|
|
27
|
+
# Get the previous tag
|
|
28
|
+
PREV_TAG=$(git describe --tags --abbrev=0 HEAD^ 2>/dev/null || echo "")
|
|
29
|
+
|
|
30
|
+
if [ -z "$PREV_TAG" ]; then
|
|
31
|
+
# First release - get all commits
|
|
32
|
+
CHANGES=$(git log --pretty=format:"- %s (%h)" --no-merges)
|
|
33
|
+
else
|
|
34
|
+
# Get commits since previous tag
|
|
35
|
+
CHANGES=$(git log ${PREV_TAG}..HEAD --pretty=format:"- %s (%h)" --no-merges)
|
|
36
|
+
fi
|
|
37
|
+
|
|
38
|
+
# Save to file to handle multiline content
|
|
39
|
+
echo "$CHANGES" > changelog.txt
|
|
40
|
+
|
|
41
|
+
# Also output to GitHub output
|
|
42
|
+
echo "changelog<<EOF" >> $GITHUB_OUTPUT
|
|
43
|
+
echo "$CHANGES" >> $GITHUB_OUTPUT
|
|
44
|
+
echo "EOF" >> $GITHUB_OUTPUT
|
|
45
|
+
|
|
46
|
+
- name: Create Release
|
|
47
|
+
uses: actions/create-release@v1
|
|
48
|
+
env:
|
|
49
|
+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
50
|
+
with:
|
|
51
|
+
tag_name: ${{ steps.tag.outputs.tag }}
|
|
52
|
+
release_name: Release ${{ steps.tag.outputs.tag }}
|
|
53
|
+
body: |
|
|
54
|
+
## Changes in ${{ steps.tag.outputs.tag }}
|
|
55
|
+
|
|
56
|
+
${{ steps.changelog.outputs.changelog }}
|
|
57
|
+
draft: false
|
|
58
|
+
prerelease: false
|
|
@@ -10,12 +10,12 @@ jobs:
|
|
|
10
10
|
runs-on: ubuntu-latest
|
|
11
11
|
strategy:
|
|
12
12
|
matrix:
|
|
13
|
-
python-version: [ "3.9", "3.10", "3.11", "3.12", "3.13" ]
|
|
13
|
+
python-version: [ "3.9", "3.10", "3.11", "3.12", "3.13", "3.14", "3.14t" ]
|
|
14
14
|
|
|
15
15
|
steps:
|
|
16
|
-
- uses: actions/checkout@
|
|
16
|
+
- uses: actions/checkout@v5
|
|
17
17
|
- name: Set up Python ${{ matrix.python-version }}
|
|
18
|
-
uses: actions/setup-python@
|
|
18
|
+
uses: actions/setup-python@v5
|
|
19
19
|
with:
|
|
20
20
|
python-version: ${{ matrix.python-version }}
|
|
21
21
|
- name: Install dependencies
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: papers-cli
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.16.2
|
|
4
4
|
Summary: utilities to keep your PDF library organized
|
|
5
5
|
Author-email: Mahé Perrette <mahe.perrette@gmail.com>
|
|
6
6
|
License: MIT
|
|
@@ -13,7 +13,7 @@ License-File: LICENSE
|
|
|
13
13
|
Requires-Dist: PyMuPDF
|
|
14
14
|
Requires-Dist: unidecode
|
|
15
15
|
Requires-Dist: crossrefapi
|
|
16
|
-
Requires-Dist: bibtexparser
|
|
16
|
+
Requires-Dist: bibtexparser>=2.0.0b2
|
|
17
17
|
Requires-Dist: scholarly
|
|
18
18
|
Requires-Dist: rapidfuzz
|
|
19
19
|
Requires-Dist: python-slugify
|
|
@@ -21,7 +21,8 @@ Requires-Dist: bs4
|
|
|
21
21
|
Dynamic: license-file
|
|
22
22
|
|
|
23
23
|
[](https://pypi.org/project/papers-cli)
|
|
24
|
-
[]()
|
|
25
|
+
[]()
|
|
25
26
|
[](https://github.com/perrette/papers/actions)
|
|
26
27
|
|
|
27
28
|
# papers
|
|
@@ -100,6 +101,12 @@ That is equivalent to doing:
|
|
|
100
101
|
|
|
101
102
|
See [Control fields when renaming file](#control-fields-when-renaming-file) for how to specify file naming patterns.
|
|
102
103
|
|
|
104
|
+
It is also possible to do this on a full directory of files, recursively:
|
|
105
|
+
|
|
106
|
+
papers add --rename --recursive /home/perette/playground/papers/papers_test
|
|
107
|
+
|
|
108
|
+
where, above, the `papers_test` directory contains a few PDF files. For each PDF, `papers` will attempt to extract the metadata and add the relevant file to the bibliography and renamed files to the files directory.
|
|
109
|
+
|
|
103
110
|
### Add library entry from its DOI
|
|
104
111
|
|
|
105
112
|
If you already know the DOI of a PDF, and don't want to gamble the fulltext search and match, you can indicate it via `--doi`:
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
[](https://pypi.org/project/papers-cli)
|
|
2
|
-
[]()
|
|
3
|
+
[]()
|
|
3
4
|
[](https://github.com/perrette/papers/actions)
|
|
4
5
|
|
|
5
6
|
# papers
|
|
@@ -78,6 +79,12 @@ That is equivalent to doing:
|
|
|
78
79
|
|
|
79
80
|
See [Control fields when renaming file](#control-fields-when-renaming-file) for how to specify file naming patterns.
|
|
80
81
|
|
|
82
|
+
It is also possible to do this on a full directory of files, recursively:
|
|
83
|
+
|
|
84
|
+
papers add --rename --recursive /home/perette/playground/papers/papers_test
|
|
85
|
+
|
|
86
|
+
where, above, the `papers_test` directory contains a few PDF files. For each PDF, `papers` will attempt to extract the metadata and add the relevant file to the bibliography and renamed files to the files directory.
|
|
87
|
+
|
|
81
88
|
### Add library entry from its DOI
|
|
82
89
|
|
|
83
90
|
If you already know the DOI of a PDF, and don't want to gamble the fulltext search and match, you can indicate it via `--doi`:
|
|
@@ -18,9 +18,11 @@ from papers.extract import extract_pdf_doi, isvaliddoi, extract_pdf_metadata
|
|
|
18
18
|
from papers.extract import fetch_bibtex_by_doi, fetch_bibtex_by_fulltext_crossref, fetch_bibtex_by_fulltext_scholar
|
|
19
19
|
from papers.encoding import parse_file, format_file, family_names, format_entries, standard_name, format_entry, parse_keywords, format_key
|
|
20
20
|
from papers.config import bcolors, Config, search_config, CONFIG_FILE, CONFIG_FILE_LOCAL, DATA_DIR, CONFIG_FILE_LEGACY, BACKUP_DIR
|
|
21
|
-
from papers.duplicate import list_duplicates, list_uniques, edit_entries
|
|
21
|
+
from papers.duplicate import list_duplicates, list_uniques, edit_entries, title_id
|
|
22
|
+
from papers.entries import get_entry_val
|
|
22
23
|
from papers.bib import (Biblio, FUZZY_RATIO, DEFAULT_SIMILARITY, entry_filecheck,
|
|
23
|
-
backupfile as backupfile_func, isvalidkey, DuplicateKeyError, clean_filesdir
|
|
24
|
+
backupfile as backupfile_func, isvalidkey, DuplicateKeyError, clean_filesdir,
|
|
25
|
+
are_duplicates)
|
|
24
26
|
from papers.utils import move, checksum, view_pdf, open_folder
|
|
25
27
|
from papers import __version__
|
|
26
28
|
|
|
@@ -149,10 +151,10 @@ def _restore_from_backupdir_wrapped(config, restore_files=False):
|
|
|
149
151
|
assert len(biblio.entries) == len(biblio_clean.entries)
|
|
150
152
|
|
|
151
153
|
for e, e_clean in zip(biblio.entries, biblio_clean.entries):
|
|
152
|
-
assert e
|
|
154
|
+
assert get_entry_val(e, 'ID', '') == get_entry_val(e_clean, 'ID', ''), f"{get_entry_val(e, 'ID', '')} != {get_entry_val(e_clean, 'ID', '')})"
|
|
153
155
|
files = biblio.get_files(e)
|
|
154
156
|
files_clean = biblio_clean.get_files(e_clean)
|
|
155
|
-
assert len(files) == len(files_clean), f"{e
|
|
157
|
+
assert len(files) == len(files_clean), f"{get_entry_val(e, 'ID', '')} :: files {len(files)} != {len(files_clean)})"
|
|
156
158
|
|
|
157
159
|
new_files = []
|
|
158
160
|
|
|
@@ -162,7 +164,7 @@ def _restore_from_backupdir_wrapped(config, restore_files=False):
|
|
|
162
164
|
logger.debug(f"BACKUP FILE DOES NOT EXISTS: {f_clean} ")
|
|
163
165
|
logger.debug(f"BACKUP ENTRY: {e_clean['file']} ")
|
|
164
166
|
logger.debug(f"BROKEN ENTRY: {e['file']} ")
|
|
165
|
-
logger.warning(f"{e
|
|
167
|
+
logger.warning(f"{get_entry_val(e, 'ID', '')} :: file link broken => {f} ")
|
|
166
168
|
new_files.append(f)
|
|
167
169
|
continue
|
|
168
170
|
|
|
@@ -176,7 +178,7 @@ def _restore_from_backupdir_wrapped(config, restore_files=False):
|
|
|
176
178
|
new_files.append(f)
|
|
177
179
|
|
|
178
180
|
else:
|
|
179
|
-
logger.warning(f"{e
|
|
181
|
+
logger.warning(f"{get_entry_val(e, 'ID', '')} :: file found but does not match backup (keep pointer to backup): {f} != {f_clean}")
|
|
180
182
|
new_files.append(f_clean)
|
|
181
183
|
|
|
182
184
|
# original bibtex link is broken, --restore-file is active
|
|
@@ -187,7 +189,7 @@ def _restore_from_backupdir_wrapped(config, restore_files=False):
|
|
|
187
189
|
|
|
188
190
|
except Exception as error:
|
|
189
191
|
logger.error(f"{error}")
|
|
190
|
-
logger.error(f"{e
|
|
192
|
+
logger.error(f"{get_entry_val(e, 'ID', '')} :: failed to restore file (keep pointer to backup)")
|
|
191
193
|
new_files.append(f_clean)
|
|
192
194
|
pass
|
|
193
195
|
|
|
@@ -661,11 +663,11 @@ def addcmd(parser, o, config):
|
|
|
661
663
|
# compare entries to inform user
|
|
662
664
|
# this is not very efficient but I have yet to hear a complaint about speed
|
|
663
665
|
|
|
664
|
-
old_set = set(e
|
|
665
|
-
old_entries_by_key = {e
|
|
666
|
-
new_set = set(e
|
|
667
|
-
new_entries_by_key = {e
|
|
668
|
-
modified_set = set(e
|
|
666
|
+
old_set = set(get_entry_val(e, 'ID', '') for e in biblio_init.entries)
|
|
667
|
+
old_entries_by_key = {get_entry_val(e, 'ID', ''): e for e in biblio_init.db.entries}
|
|
668
|
+
new_set = set(get_entry_val(e, 'ID', '') for e in biblio.entries)
|
|
669
|
+
new_entries_by_key = {get_entry_val(e, 'ID', ''): e for e in biblio.db.entries}
|
|
670
|
+
modified_set = set(get_entry_val(e, 'ID', '') for e in entries).intersection(set.intersection(old_set, new_set))
|
|
669
671
|
|
|
670
672
|
for ID in sorted(old_set - new_set):
|
|
671
673
|
print(format_entry(biblio_init, old_entries_by_key[ID], prefix="Removed"))
|
|
@@ -699,7 +701,7 @@ def checkcmd(parser, o, config):
|
|
|
699
701
|
# o.fix_key = True
|
|
700
702
|
|
|
701
703
|
for e in biblio.entries:
|
|
702
|
-
if o.keys and e
|
|
704
|
+
if o.keys and get_entry_val(e, 'ID', '') not in o.keys:
|
|
703
705
|
continue
|
|
704
706
|
biblio.fix_entry(e, fix_doi=o.fix_doi, fetch=o.fetch, fetch_all=o.fetch_all, fix_key=o.fix_key,
|
|
705
707
|
auto_key=o.auto_key, format_name=o.format_name, encoding=o.encoding,
|
|
@@ -816,11 +818,11 @@ def listcmd(parser, o, config):
|
|
|
816
818
|
return _match(word, target, fuzzy=o.fuzzy, substring=not o.strict)
|
|
817
819
|
|
|
818
820
|
def _nfiles(e):
|
|
819
|
-
return len(parse_file(e
|
|
821
|
+
return len(parse_file(get_entry_val(e, 'file', ''), relative_to=biblio.relative_to))
|
|
820
822
|
|
|
821
823
|
|
|
822
824
|
def _requiresreview(e):
|
|
823
|
-
if not isvalidkey(e
|
|
825
|
+
if not isvalidkey(get_entry_val(e, 'ID', '')): return True
|
|
824
826
|
if 'doi' in e and not isvaliddoi(e['doi']): return True
|
|
825
827
|
if 'author' not in e: return True
|
|
826
828
|
if 'title' not in e: return True
|
|
@@ -844,17 +846,17 @@ def listcmd(parser, o, config):
|
|
|
844
846
|
if 'doi' in e and not isvaliddoi(e['doi']):
|
|
845
847
|
e['doi'] = bcolors.FAIL + e['doi'] + bcolors.ENDC
|
|
846
848
|
if o.has_file:
|
|
847
|
-
entries = [e for e in entries if e
|
|
849
|
+
entries = [e for e in entries if get_entry_val(e, 'file', '')]
|
|
848
850
|
if o.no_file:
|
|
849
|
-
entries = [e for e in entries if not e
|
|
851
|
+
entries = [e for e in entries if not get_entry_val(e, 'file', '')]
|
|
850
852
|
if o.broken_file:
|
|
851
|
-
entries = [e for e in entries if e
|
|
853
|
+
entries = [e for e in entries if get_entry_val(e, 'file', '') and any([not os.path.exists(f) for f in parse_file(e['file'], relative_to=biblio.relative_to)])]
|
|
852
854
|
|
|
853
855
|
|
|
854
856
|
if o.doi:
|
|
855
857
|
entries = [e for e in entries if 'doi' in e and _longmatch(e['doi'], o.doi)]
|
|
856
858
|
if o.key:
|
|
857
|
-
entries = [e for e in entries if
|
|
859
|
+
entries = [e for e in entries if _longmatch(get_entry_val(e, 'ID', ''), o.key)]
|
|
858
860
|
if o.year:
|
|
859
861
|
entries = [e for e in entries if 'year' in e and _longmatch(e['year'], o.year)]
|
|
860
862
|
if o.first_author:
|
|
@@ -881,7 +883,7 @@ def listcmd(parser, o, config):
|
|
|
881
883
|
if o.duplicates_key:
|
|
882
884
|
entries = list_dup(entries, key=biblio.key, issorted=True)
|
|
883
885
|
if o.duplicates_doi:
|
|
884
|
-
entries = list_dup(entries, key=lambda e:e
|
|
886
|
+
entries = list_dup(entries, key=lambda e: get_entry_val(e, 'doi', ''), filter_key=isvaliddoi)
|
|
885
887
|
if o.duplicates_tit:
|
|
886
888
|
entries = list_dup(entries, key=title_id)
|
|
887
889
|
if o.duplicates:
|
|
@@ -889,7 +891,7 @@ def listcmd(parser, o, config):
|
|
|
889
891
|
# I think that's because we might need to be inclusive here, whereas the default is conservative (parameter used for several functions with possibly differing requirements).
|
|
890
892
|
# (otherwise we'd have used the command-line option o.similarity, or possibly DEFAULT_SIMILARITY)
|
|
891
893
|
# Might need to revise later (the question mark is from a review after a long time without use)
|
|
892
|
-
eq = lambda a, b: a
|
|
894
|
+
eq = lambda a, b: get_entry_val(a, 'ID', '') == get_entry_val(b, 'ID', '') or are_duplicates(a, b, similarity="PARTIAL", fuzzy_ratio=o.fuzzy_ratio)
|
|
893
895
|
entries = list_dup(entries, eq=eq)
|
|
894
896
|
|
|
895
897
|
if o.add_keywords:
|
|
@@ -919,7 +921,7 @@ def listcmd(parser, o, config):
|
|
|
919
921
|
otherentries = [e for e in biblio.db.entries if e not in entries]
|
|
920
922
|
try:
|
|
921
923
|
entries = edit_entries(entries)
|
|
922
|
-
biblio.
|
|
924
|
+
biblio.entries = otherentries + entries
|
|
923
925
|
except Exception as error:
|
|
924
926
|
logger.error(str(error))
|
|
925
927
|
return
|
|
@@ -933,7 +935,7 @@ def listcmd(parser, o, config):
|
|
|
933
935
|
|
|
934
936
|
elif o.delete:
|
|
935
937
|
for e in entries:
|
|
936
|
-
biblio.db.
|
|
938
|
+
biblio.db.remove(e)
|
|
937
939
|
savebib(biblio, config)
|
|
938
940
|
|
|
939
941
|
elif o.open:
|
|
@@ -943,10 +945,10 @@ def listcmd(parser, o, config):
|
|
|
943
945
|
elif o.field:
|
|
944
946
|
# entries = [{k:e[k] for k in e if k in o.field+['ID','ENTRYTYPE']} for e in entries]
|
|
945
947
|
for e in entries:
|
|
946
|
-
print(format_key(e, no_key=o.no_key),*[e
|
|
948
|
+
print(format_key(e, no_key=o.no_key),*[get_entry_val(e, k, "") for k in o.field])
|
|
947
949
|
elif o.key_only:
|
|
948
950
|
for e in entries:
|
|
949
|
-
print(e
|
|
951
|
+
print(get_entry_val(e, 'ID', ''))
|
|
950
952
|
elif o.one_liner:
|
|
951
953
|
for e in entries:
|
|
952
954
|
print(format_entry(biblio, e, no_key=o.no_key))
|
|
@@ -28,7 +28,7 @@ version_tuple: VERSION_TUPLE
|
|
|
28
28
|
commit_id: COMMIT_ID
|
|
29
29
|
__commit_id__: COMMIT_ID
|
|
30
30
|
|
|
31
|
-
__version__ = version = '2.
|
|
32
|
-
__version_tuple__ = version_tuple = (2,
|
|
31
|
+
__version__ = version = '2.16.2'
|
|
32
|
+
__version_tuple__ = version_tuple = (2, 16, 2)
|
|
33
33
|
|
|
34
|
-
__commit_id__ = commit_id = '
|
|
34
|
+
__commit_id__ = commit_id = 'g60ea4dbb9'
|