grobid-client-python 0.2.1__tar.gz → 0.2.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.
- {grobid_client_python-0.2.1 → grobid_client_python-0.2.2}/PKG-INFO +4 -1
- {grobid_client_python-0.2.1 → grobid_client_python-0.2.2}/Readme.md +3 -0
- {grobid_client_python-0.2.1 → grobid_client_python-0.2.2}/grobid_client/grobid_client.py +36 -18
- {grobid_client_python-0.2.1 → grobid_client_python-0.2.2}/grobid_client_python.egg-info/PKG-INFO +4 -1
- grobid_client_python-0.2.2/grobid_client_python.egg-info/scm_version.json +8 -0
- {grobid_client_python-0.2.1 → grobid_client_python-0.2.2}/pyproject.toml +1 -1
- {grobid_client_python-0.2.1 → grobid_client_python-0.2.2}/tests/test_grobid_client.py +124 -6
- grobid_client_python-0.2.1/grobid_client_python.egg-info/scm_version.json +0 -8
- {grobid_client_python-0.2.1 → grobid_client_python-0.2.2}/.github/dependabot.yml +0 -0
- {grobid_client_python-0.2.1 → grobid_client_python-0.2.2}/.github/workflows/ci-build.yml +0 -0
- {grobid_client_python-0.2.1 → grobid_client_python-0.2.2}/.github/workflows/ci-release.yml +0 -0
- {grobid_client_python-0.2.1 → grobid_client_python-0.2.2}/.gitignore +0 -0
- {grobid_client_python-0.2.1 → grobid_client_python-0.2.2}/LICENSE +0 -0
- {grobid_client_python-0.2.1 → grobid_client_python-0.2.2}/MANIFEST.in +0 -0
- {grobid_client_python-0.2.1 → grobid_client_python-0.2.2}/config.json +0 -0
- {grobid_client_python-0.2.1 → grobid_client_python-0.2.2}/example.py +0 -0
- {grobid_client_python-0.2.1 → grobid_client_python-0.2.2}/grobid_client/__init__.py +0 -0
- {grobid_client_python-0.2.1 → grobid_client_python-0.2.2}/grobid_client/client.py +0 -0
- {grobid_client_python-0.2.1 → grobid_client_python-0.2.2}/grobid_client/format/TEI2LossyJSON.py +0 -0
- {grobid_client_python-0.2.1 → grobid_client_python-0.2.2}/grobid_client/format/TEI2LossyJSON_cli.py +0 -0
- {grobid_client_python-0.2.1 → grobid_client_python-0.2.2}/grobid_client/format/TEI2Markdown.py +0 -0
- {grobid_client_python-0.2.1 → grobid_client_python-0.2.2}/grobid_client/format/TEI2Markdown_cli.py +0 -0
- {grobid_client_python-0.2.1 → grobid_client_python-0.2.2}/grobid_client/format/__init__.py +0 -0
- {grobid_client_python-0.2.1 → grobid_client_python-0.2.2}/grobid_client/format/__main__.py +0 -0
- {grobid_client_python-0.2.1 → grobid_client_python-0.2.2}/grobid_client/format/validate_json_refs.py +0 -0
- {grobid_client_python-0.2.1 → grobid_client_python-0.2.2}/grobid_client/py.typed +0 -0
- {grobid_client_python-0.2.1 → grobid_client_python-0.2.2}/grobid_client_python.egg-info/SOURCES.txt +0 -0
- {grobid_client_python-0.2.1 → grobid_client_python-0.2.2}/grobid_client_python.egg-info/dependency_links.txt +0 -0
- {grobid_client_python-0.2.1 → grobid_client_python-0.2.2}/grobid_client_python.egg-info/entry_points.txt +0 -0
- {grobid_client_python-0.2.1 → grobid_client_python-0.2.2}/grobid_client_python.egg-info/requires.txt +0 -0
- {grobid_client_python-0.2.1 → grobid_client_python-0.2.2}/grobid_client_python.egg-info/scm_file_list.json +0 -0
- {grobid_client_python-0.2.1 → grobid_client_python-0.2.2}/grobid_client_python.egg-info/top_level.txt +0 -0
- {grobid_client_python-0.2.1 → grobid_client_python-0.2.2}/pytest.ini +0 -0
- {grobid_client_python-0.2.1 → grobid_client_python-0.2.2}/requirements.txt +0 -0
- {grobid_client_python-0.2.1 → grobid_client_python-0.2.2}/resources/20180928112135.png +0 -0
- {grobid_client_python-0.2.1 → grobid_client_python-0.2.2}/resources/20180928112138.csv +0 -0
- {grobid_client_python-0.2.1 → grobid_client_python-0.2.2}/resources/test_pdf/0046d83a-edd6-4631-b57c-755cdcce8b7f.pdf +0 -0
- {grobid_client_python-0.2.1 → grobid_client_python-0.2.2}/resources/test_pdf/0046d83a-edd6-4631-b57c-755cdcce8b7f.tei.xml +0 -0
- {grobid_client_python-0.2.1 → grobid_client_python-0.2.2}/resources/test_pdf/mjb3wlzxcb2mc-migowebupload-1766042162782.grobid.tei.xml +0 -0
- {grobid_client_python-0.2.1 → grobid_client_python-0.2.2}/resources/test_txt/reference-strings-1.txt +0 -0
- {grobid_client_python-0.2.1 → grobid_client_python-0.2.2}/resources/test_txt/reference-strings-2.txt +0 -0
- {grobid_client_python-0.2.1 → grobid_client_python-0.2.2}/resources/test_txt/reference-strings-3.txt +0 -0
- {grobid_client_python-0.2.1 → grobid_client_python-0.2.2}/resources/test_txt/reference-strings-4.txt +0 -0
- {grobid_client_python-0.2.1 → grobid_client_python-0.2.2}/resources/test_txt/reference-strings-5.txt +0 -0
- {grobid_client_python-0.2.1 → grobid_client_python-0.2.2}/resources/test_txt/reference-strings-6.txt +0 -0
- {grobid_client_python-0.2.1 → grobid_client_python-0.2.2}/resources/test_txt/reference-strings-7.txt +0 -0
- {grobid_client_python-0.2.1 → grobid_client_python-0.2.2}/setup.cfg +0 -0
- {grobid_client_python-0.2.1 → grobid_client_python-0.2.2}/tests/__init__.py +0 -0
- {grobid_client_python-0.2.1 → grobid_client_python-0.2.2}/tests/conftest.py +0 -0
- {grobid_client_python-0.2.1 → grobid_client_python-0.2.2}/tests/resources/0046d83a-edd6-4631-b57c-755cdcce8b7f.tei.xml +0 -0
- {grobid_client_python-0.2.1 → grobid_client_python-0.2.2}/tests/resources/__init__.py +0 -0
- {grobid_client_python-0.2.1 → grobid_client_python-0.2.2}/tests/resources/article_withdrawn.grobid.tei.xml +0 -0
- {grobid_client_python-0.2.1 → grobid_client_python-0.2.2}/tests/resources/refs_offsets/10.1038_s41477-023-01501-1.grobid.tei.xml +0 -0
- {grobid_client_python-0.2.1 → grobid_client_python-0.2.2}/tests/resources/refs_offsets/10.1038_s41477-023-01501-1.json +0 -0
- {grobid_client_python-0.2.1 → grobid_client_python-0.2.2}/tests/resources/refs_offsets/10.1038_s41586-023-05895-y.grobid.tei.xml +0 -0
- {grobid_client_python-0.2.1 → grobid_client_python-0.2.2}/tests/resources/refs_offsets/10.1038_s41586-023-05895-y.json +0 -0
- {grobid_client_python-0.2.1 → grobid_client_python-0.2.2}/tests/resources/refs_offsets/10.1038_s41598-023-32039-z.grobid.tei.xml +0 -0
- {grobid_client_python-0.2.1 → grobid_client_python-0.2.2}/tests/resources/refs_offsets/10.1038_s41598-023-32039-z.json +0 -0
- {grobid_client_python-0.2.1 → grobid_client_python-0.2.2}/tests/resources/refs_offsets/10.1371_journal.pone.0218311.grobid.tei.xml +0 -0
- {grobid_client_python-0.2.1 → grobid_client_python-0.2.2}/tests/resources/refs_offsets/10.1371_journal.pone.0218311.json +0 -0
- {grobid_client_python-0.2.1 → grobid_client_python-0.2.2}/tests/resources/refs_offsets/10.7554_elife.78558.grobid.tei.xml +0 -0
- {grobid_client_python-0.2.1 → grobid_client_python-0.2.2}/tests/resources/refs_offsets/10.7554_elife.78558.json +0 -0
- {grobid_client_python-0.2.1 → grobid_client_python-0.2.2}/tests/resources/refs_offsets/2021.naacl-main.224.grobid.tei.xml +0 -0
- {grobid_client_python-0.2.1 → grobid_client_python-0.2.2}/tests/resources/repeated_text/ijms-24-05988.grobid.tei.xml +0 -0
- {grobid_client_python-0.2.1 → grobid_client_python-0.2.2}/tests/resources/repeated_text/ijms-24-05988.json +0 -0
- {grobid_client_python-0.2.1 → grobid_client_python-0.2.2}/tests/resources/repeated_text/ijms-24-05988.md +0 -0
- {grobid_client_python-0.2.1 → grobid_client_python-0.2.2}/tests/run_tests.py +0 -0
- {grobid_client_python-0.2.1 → grobid_client_python-0.2.2}/tests/test_client.py +0 -0
- {grobid_client_python-0.2.1 → grobid_client_python-0.2.2}/tests/test_config.json +0 -0
- {grobid_client_python-0.2.1 → grobid_client_python-0.2.2}/tests/test_conversions.py +0 -0
- {grobid_client_python-0.2.1 → grobid_client_python-0.2.2}/tests/test_integration.py +0 -0
- {grobid_client_python-0.2.1 → grobid_client_python-0.2.2}/tests/test_s3.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: grobid-client-python
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.2
|
|
4
4
|
Summary: Simple python client for GROBID REST services
|
|
5
5
|
Author-email: Patrice Lopez <patrice.lopez@science-miner.com>
|
|
6
6
|
Maintainer-email: Patrice Lopez <patrice.lopez@science-miner.com>, Luca Foppiano <lucanoro@duck.com>
|
|
@@ -411,6 +411,9 @@ grobid_client --input "~/data/**/*.pdf" --output ~/results processFulltextDocu
|
|
|
411
411
|
|
|
412
412
|
> [!NOTE]
|
|
413
413
|
> `--input` accepts a directory, a single file, an **archive**, or a **glob pattern**:
|
|
414
|
+
> - **Directories** are processed recursively and the output mirrors the input tree: `~/pdfs/2023/a.pdf` →
|
|
415
|
+
> `~/results/2023/a.grobid.tei.xml` (subdirectories of `--output` are created as needed, and the JSON/Markdown
|
|
416
|
+
> outputs and error files sit next to each TEI file). Archives keep their internal paths the same way.
|
|
414
417
|
> - **Archives** (`.zip`, `.tar`, `.tar.gz`/`.tgz`, `.tar.bz2`/`.tbz2`) are streamed: eligible entries are read into
|
|
415
418
|
> memory in chunks of `queue_size` and sent to GROBID straight from there — the archive is never fully decompressed and
|
|
416
419
|
> nothing but the results in `--output` ever touches the disk. If `--output` is omitted, results go to a directory named
|
|
@@ -189,6 +189,9 @@ grobid_client --input "~/data/**/*.pdf" --output ~/results processFulltextDocu
|
|
|
189
189
|
|
|
190
190
|
> [!NOTE]
|
|
191
191
|
> `--input` accepts a directory, a single file, an **archive**, or a **glob pattern**:
|
|
192
|
+
> - **Directories** are processed recursively and the output mirrors the input tree: `~/pdfs/2023/a.pdf` →
|
|
193
|
+
> `~/results/2023/a.grobid.tei.xml` (subdirectories of `--output` are created as needed, and the JSON/Markdown
|
|
194
|
+
> outputs and error files sit next to each TEI file). Archives keep their internal paths the same way.
|
|
192
195
|
> - **Archives** (`.zip`, `.tar`, `.tar.gz`/`.tgz`, `.tar.bz2`/`.tbz2`) are streamed: eligible entries are read into
|
|
193
196
|
> memory in chunks of `queue_size` and sent to GROBID straight from there — the archive is never fully decompressed and
|
|
194
197
|
> nothing but the results in `--output` ever touches the disk. If `--output` is omitted, results go to a directory named
|
|
@@ -34,7 +34,7 @@ import shutil
|
|
|
34
34
|
import tarfile
|
|
35
35
|
import tempfile
|
|
36
36
|
import zipfile
|
|
37
|
-
from typing import Any, BinaryIO, Callable, Optional, Tuple, Union
|
|
37
|
+
from typing import Any, BinaryIO, Callable, List, Optional, Tuple, Union
|
|
38
38
|
import copy
|
|
39
39
|
|
|
40
40
|
from .format.TEI2LossyJSON import TEI2LossyJSONConverter
|
|
@@ -449,10 +449,15 @@ class GrobidClient(ApiClient):
|
|
|
449
449
|
input_file_path = pathlib.Path(input_file)
|
|
450
450
|
|
|
451
451
|
if output is not None:
|
|
452
|
-
#
|
|
452
|
+
# Mirror the input tree under the output directory: the path of the
|
|
453
|
+
# file relative to input_path is kept (only the extension changes),
|
|
454
|
+
# so sub/paper.pdf -> <output>/sub/paper.grobid.tei.xml. Keeping the
|
|
455
|
+
# subdirectories also prevents two inputs that share a basename
|
|
456
|
+
# from mapping to the same output file.
|
|
453
457
|
input_path_abs = pathlib.Path(input_path).resolve()
|
|
454
458
|
input_file_rel = input_file_path.resolve().relative_to(input_path_abs)
|
|
455
|
-
filename = pathlib.Path(output) /
|
|
459
|
+
filename = pathlib.Path(output) / input_file_rel.with_name(
|
|
460
|
+
f"{input_file_rel.stem}.grobid.tei.xml")
|
|
456
461
|
else:
|
|
457
462
|
# Use the same directory as the input file
|
|
458
463
|
filename = input_file_path.parent / f"{input_file_path.stem}.grobid.tei.xml"
|
|
@@ -629,9 +634,14 @@ class GrobidClient(ApiClient):
|
|
|
629
634
|
|
|
630
635
|
# Partition into archives (streamed), remote loose files (s3) and local
|
|
631
636
|
# filesystem files (directories are expanded to their eligible files).
|
|
637
|
+
# Local files are grouped with the base directory their output paths
|
|
638
|
+
# are computed from: a directory given as input is the base of the
|
|
639
|
+
# files found inside it (so the output mirrors its tree), while loose
|
|
640
|
+
# files (single paths, glob matches) share a common ancestor.
|
|
632
641
|
archive_paths = []
|
|
633
642
|
remote_files = []
|
|
634
|
-
|
|
643
|
+
fs_groups: List[Tuple[str, list]] = []
|
|
644
|
+
loose_files = []
|
|
635
645
|
for path in matched_paths:
|
|
636
646
|
if self._is_s3(path):
|
|
637
647
|
if self._looks_like_archive(path):
|
|
@@ -643,14 +653,20 @@ class GrobidClient(ApiClient):
|
|
|
643
653
|
elif self._is_archive(path):
|
|
644
654
|
archive_paths.append(path)
|
|
645
655
|
elif os.path.isdir(path):
|
|
646
|
-
|
|
656
|
+
dir_files = self._collect_directory_files(path, service)
|
|
657
|
+
if dir_files:
|
|
658
|
+
fs_groups.append((path, dir_files))
|
|
647
659
|
elif os.path.isfile(path) and self._is_eligible_input(os.path.basename(path), service):
|
|
648
|
-
|
|
660
|
+
loose_files.append(path)
|
|
649
661
|
elif not os.path.exists(path):
|
|
650
662
|
self.logger.warning(f"Input path does not exist: {path}")
|
|
651
663
|
else:
|
|
652
664
|
self.logger.debug(f"Skipping input (not an eligible file/dir/archive): {path}")
|
|
653
665
|
|
|
666
|
+
if loose_files:
|
|
667
|
+
fs_groups.append((self._common_base(loose_files), loose_files))
|
|
668
|
+
fs_files = [f for _, files in fs_groups for f in files]
|
|
669
|
+
|
|
654
670
|
if not fs_files and not archive_paths and not remote_files:
|
|
655
671
|
self.logger.warning(f"No eligible files found in input(s): {inputs}")
|
|
656
672
|
return
|
|
@@ -669,16 +685,17 @@ class GrobidClient(ApiClient):
|
|
|
669
685
|
# Local files gathered from directories and/or loose glob matches
|
|
670
686
|
if fs_files:
|
|
671
687
|
print(f"Found {len(fs_files)} local file(s) to process")
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
688
|
+
for base_dir, group_files in fs_groups:
|
|
689
|
+
bp, be, bs = self._run_file_batches(
|
|
690
|
+
service, group_files, base_dir, output, n,
|
|
691
|
+
generate_ids, consolidate_header, consolidate_citations,
|
|
692
|
+
include_raw_citations, include_raw_affiliations, tei_coordinates,
|
|
693
|
+
segment_sentences, force, verbose, flavor, json_output, markdown_output,
|
|
694
|
+
skip_errors=skip_errors
|
|
695
|
+
)
|
|
696
|
+
processed_files_count += bp
|
|
697
|
+
errors_files_count += be
|
|
698
|
+
skipped_files_count += bs
|
|
682
699
|
total_files += len(fs_files)
|
|
683
700
|
|
|
684
701
|
# Loose remote (s3) files: streamed into memory one chunk at a time
|
|
@@ -1096,8 +1113,9 @@ class GrobidClient(ApiClient):
|
|
|
1096
1113
|
the chunk size and nothing but the results ever touches the disk. (The
|
|
1097
1114
|
exception is ``processCitationList``, whose ``.txt`` inputs are read
|
|
1098
1115
|
from a path and therefore still go through a temporary directory.)
|
|
1099
|
-
Output files follow the same
|
|
1100
|
-
processing
|
|
1116
|
+
Output files follow the same naming convention as directory
|
|
1117
|
+
processing: the entry's path inside the archive is mirrored under
|
|
1118
|
+
``output`` (``nested/y.pdf`` -> ``<output>/nested/y.grobid.tei.xml``).
|
|
1101
1119
|
"""
|
|
1102
1120
|
start_time = time.time()
|
|
1103
1121
|
self._warn_on_consolidation_timeout(consolidate_citations)
|
{grobid_client_python-0.2.1 → grobid_client_python-0.2.2}/grobid_client_python.egg-info/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: grobid-client-python
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.2
|
|
4
4
|
Summary: Simple python client for GROBID REST services
|
|
5
5
|
Author-email: Patrice Lopez <patrice.lopez@science-miner.com>
|
|
6
6
|
Maintainer-email: Patrice Lopez <patrice.lopez@science-miner.com>, Luca Foppiano <lucanoro@duck.com>
|
|
@@ -411,6 +411,9 @@ grobid_client --input "~/data/**/*.pdf" --output ~/results processFulltextDocu
|
|
|
411
411
|
|
|
412
412
|
> [!NOTE]
|
|
413
413
|
> `--input` accepts a directory, a single file, an **archive**, or a **glob pattern**:
|
|
414
|
+
> - **Directories** are processed recursively and the output mirrors the input tree: `~/pdfs/2023/a.pdf` →
|
|
415
|
+
> `~/results/2023/a.grobid.tei.xml` (subdirectories of `--output` are created as needed, and the JSON/Markdown
|
|
416
|
+
> outputs and error files sit next to each TEI file). Archives keep their internal paths the same way.
|
|
414
417
|
> - **Archives** (`.zip`, `.tar`, `.tar.gz`/`.tgz`, `.tar.bz2`/`.tbz2`) are streamed: eligible entries are read into
|
|
415
418
|
> memory in chunks of `queue_size` and sent to GROBID straight from there — the archive is never fully decompressed and
|
|
416
419
|
> nothing but the results in `--output` ever touches the disk. If `--output` is omitted, results go to a directory named
|
|
@@ -226,6 +226,17 @@ class TestGrobidClient:
|
|
|
226
226
|
|
|
227
227
|
assert result == expected
|
|
228
228
|
|
|
229
|
+
def test_output_file_name_preserves_subdirectories(self):
|
|
230
|
+
"""The path relative to the input directory is mirrored under the output."""
|
|
231
|
+
with patch('grobid_client.grobid_client.GrobidClient._test_server_connection'):
|
|
232
|
+
with patch('grobid_client.grobid_client.GrobidClient._configure_logging'):
|
|
233
|
+
client = GrobidClient(check_server=False)
|
|
234
|
+
|
|
235
|
+
result = client._output_file_name('/input/path/sub/deeper/document.pdf', '/input/path', '/output/path')
|
|
236
|
+
expected = os.path.join('/output/path', 'sub', 'deeper', 'document.grobid.tei.xml')
|
|
237
|
+
|
|
238
|
+
assert result == expected
|
|
239
|
+
|
|
229
240
|
def test_output_file_name_without_output_path(self):
|
|
230
241
|
"""Test _output_file_name method without output path."""
|
|
231
242
|
with patch('grobid_client.grobid_client.GrobidClient._test_server_connection'):
|
|
@@ -753,11 +764,13 @@ class TestArchiveInput:
|
|
|
753
764
|
|
|
754
765
|
@staticmethod
|
|
755
766
|
def _tei_outputs(output_dir):
|
|
767
|
+
"""TEI outputs under output_dir, as paths relative to it (posix separators)."""
|
|
756
768
|
found = []
|
|
757
769
|
for root, _, files in os.walk(output_dir):
|
|
758
770
|
for f in files:
|
|
759
771
|
if f.endswith('.grobid.tei.xml'):
|
|
760
|
-
|
|
772
|
+
rel = os.path.relpath(os.path.join(root, f), output_dir)
|
|
773
|
+
found.append(rel.replace(os.sep, '/'))
|
|
761
774
|
return sorted(found)
|
|
762
775
|
|
|
763
776
|
def test_is_archive(self):
|
|
@@ -798,7 +811,7 @@ class TestArchiveInput:
|
|
|
798
811
|
posted = self._run(client, zip_path, out)
|
|
799
812
|
|
|
800
813
|
# all 3 PDFs processed, the .txt ignored
|
|
801
|
-
assert self._tei_outputs(out) == ['a.grobid.tei.xml', '
|
|
814
|
+
assert self._tei_outputs(out) == ['a.grobid.tei.xml', 'c.grobid.tei.xml', 'sub/b.grobid.tei.xml']
|
|
802
815
|
# each entry was posted straight from memory, under its archive name
|
|
803
816
|
assert sorted(posted) == [
|
|
804
817
|
('a.pdf', b'%PDF-a'),
|
|
@@ -813,7 +826,8 @@ class TestArchiveInput:
|
|
|
813
826
|
self._make_targz(tar_path, {'x.pdf': b'%PDF-x', 'nested/y.pdf': b'%PDF-y'}, d)
|
|
814
827
|
out = os.path.join(d, 'out')
|
|
815
828
|
posted = self._run(client, tar_path, out)
|
|
816
|
-
|
|
829
|
+
# the entry's path inside the archive is mirrored in the output
|
|
830
|
+
assert self._tei_outputs(out) == ['nested/y.grobid.tei.xml', 'x.grobid.tei.xml']
|
|
817
831
|
assert sorted(posted) == [
|
|
818
832
|
(os.path.join('nested', 'y.pdf'), b'%PDF-y'),
|
|
819
833
|
('x.pdf', b'%PDF-x'),
|
|
@@ -855,7 +869,7 @@ class TestArchiveInput:
|
|
|
855
869
|
client.process('processFulltextDocument', zip_path, output=out, force=True)
|
|
856
870
|
|
|
857
871
|
mock_mkdtemp.assert_not_called()
|
|
858
|
-
assert self._tei_outputs(out) == ['a.grobid.tei.xml', 'b.grobid.tei.xml']
|
|
872
|
+
assert self._tei_outputs(out) == ['a.grobid.tei.xml', 'sub/b.grobid.tei.xml']
|
|
859
873
|
|
|
860
874
|
def test_citation_lists_are_still_extracted_to_disk(self):
|
|
861
875
|
"""process_txt reads from a path, so citation lists keep the temp-dir route."""
|
|
@@ -1039,11 +1053,13 @@ class TestGlobInput:
|
|
|
1039
1053
|
|
|
1040
1054
|
@staticmethod
|
|
1041
1055
|
def _tei_outputs(output_dir):
|
|
1056
|
+
"""TEI outputs under output_dir, as paths relative to it (posix separators)."""
|
|
1042
1057
|
found = []
|
|
1043
1058
|
for root, _, files in os.walk(output_dir):
|
|
1044
1059
|
for f in files:
|
|
1045
1060
|
if f.endswith('.grobid.tei.xml'):
|
|
1046
|
-
|
|
1061
|
+
rel = os.path.relpath(os.path.join(root, f), output_dir)
|
|
1062
|
+
found.append(rel.replace(os.sep, '/'))
|
|
1047
1063
|
return sorted(found)
|
|
1048
1064
|
|
|
1049
1065
|
def _run(self, client, pattern, output):
|
|
@@ -1085,7 +1101,8 @@ class TestGlobInput:
|
|
|
1085
1101
|
open(os.path.join(d, 'sub2', 'note.txt'), 'wb').close()
|
|
1086
1102
|
out = os.path.join(d, 'out')
|
|
1087
1103
|
self._run(client, os.path.join(d, '**', '*.pdf'), out)
|
|
1088
|
-
|
|
1104
|
+
# loose glob matches are placed relative to their common ancestor
|
|
1105
|
+
assert self._tei_outputs(out) == ['sub1/a.grobid.tei.xml', 'sub2/b.grobid.tei.xml']
|
|
1089
1106
|
|
|
1090
1107
|
def test_glob_no_match_warns(self):
|
|
1091
1108
|
client = self._client()
|
|
@@ -1107,6 +1124,107 @@ class TestGlobInput:
|
|
|
1107
1124
|
assert f1.startswith(base) and f2.startswith(base)
|
|
1108
1125
|
|
|
1109
1126
|
|
|
1127
|
+
class TestDirectoryInput:
|
|
1128
|
+
"""A directory input is mirrored under --output (see issue about flat outputs).
|
|
1129
|
+
|
|
1130
|
+
The TEI of ``<in>/2023/p.pdf`` goes to ``<out>/2023/p.grobid.tei.xml``: the
|
|
1131
|
+
subdirectories are kept, so two documents sharing a basename in different
|
|
1132
|
+
subdirectories do not collide on the same output file.
|
|
1133
|
+
"""
|
|
1134
|
+
|
|
1135
|
+
def _client(self, queue_size=50):
|
|
1136
|
+
with patch('grobid_client.grobid_client.GrobidClient._test_server_connection'):
|
|
1137
|
+
with patch('grobid_client.grobid_client.GrobidClient._configure_logging'):
|
|
1138
|
+
client = GrobidClient(check_server=False)
|
|
1139
|
+
client.logger = Mock()
|
|
1140
|
+
client.config['queue_size'] = queue_size
|
|
1141
|
+
return client
|
|
1142
|
+
|
|
1143
|
+
@staticmethod
|
|
1144
|
+
def _touch(*parts):
|
|
1145
|
+
path = os.path.join(*parts)
|
|
1146
|
+
os.makedirs(os.path.dirname(path), exist_ok=True)
|
|
1147
|
+
open(path, 'wb').close()
|
|
1148
|
+
return path
|
|
1149
|
+
|
|
1150
|
+
@staticmethod
|
|
1151
|
+
def _tei_outputs(output_dir):
|
|
1152
|
+
found = []
|
|
1153
|
+
for root, _, files in os.walk(output_dir):
|
|
1154
|
+
for f in files:
|
|
1155
|
+
if f.endswith('.grobid.tei.xml'):
|
|
1156
|
+
rel = os.path.relpath(os.path.join(root, f), output_dir)
|
|
1157
|
+
found.append(rel.replace(os.sep, '/'))
|
|
1158
|
+
return sorted(found)
|
|
1159
|
+
|
|
1160
|
+
def _run(self, client, input_dir, output, force=True):
|
|
1161
|
+
posted = []
|
|
1162
|
+
|
|
1163
|
+
def fake_post(url, files=None, data=None, headers=None, timeout=None):
|
|
1164
|
+
posted.append(files['input'][0])
|
|
1165
|
+
resp = Mock()
|
|
1166
|
+
resp.text = '<TEI>ok</TEI>'
|
|
1167
|
+
return (resp, 200)
|
|
1168
|
+
|
|
1169
|
+
with patch.object(GrobidClient, 'post', side_effect=fake_post):
|
|
1170
|
+
client.process('processFulltextDocument', input_dir, output=output, force=force)
|
|
1171
|
+
return posted
|
|
1172
|
+
|
|
1173
|
+
def test_output_mirrors_input_tree(self):
|
|
1174
|
+
client = self._client()
|
|
1175
|
+
with tempfile.TemporaryDirectory() as d:
|
|
1176
|
+
self._touch(d, 'in', 'top.pdf')
|
|
1177
|
+
self._touch(d, 'in', '2023', 'p.pdf')
|
|
1178
|
+
self._touch(d, 'in', '2024', 'p.pdf') # same basename as 2023/p.pdf
|
|
1179
|
+
self._touch(d, 'in', '2024', 'deep', 'q.pdf')
|
|
1180
|
+
out = os.path.join(d, 'out')
|
|
1181
|
+
posted = self._run(client, os.path.join(d, 'in'), out)
|
|
1182
|
+
assert len(posted) == 4
|
|
1183
|
+
assert self._tei_outputs(out) == [
|
|
1184
|
+
'2023/p.grobid.tei.xml',
|
|
1185
|
+
'2024/deep/q.grobid.tei.xml',
|
|
1186
|
+
'2024/p.grobid.tei.xml',
|
|
1187
|
+
'top.grobid.tei.xml',
|
|
1188
|
+
]
|
|
1189
|
+
|
|
1190
|
+
def test_same_basename_in_subdirectories_both_skipped_on_rerun(self):
|
|
1191
|
+
"""With flat outputs the second p.pdf used to be reported as already done."""
|
|
1192
|
+
client = self._client()
|
|
1193
|
+
with tempfile.TemporaryDirectory() as d:
|
|
1194
|
+
self._touch(d, 'in', '2023', 'p.pdf')
|
|
1195
|
+
self._touch(d, 'in', '2024', 'p.pdf')
|
|
1196
|
+
out = os.path.join(d, 'out')
|
|
1197
|
+
assert len(self._run(client, os.path.join(d, 'in'), out)) == 2
|
|
1198
|
+
# second run without --force: both outputs exist, nothing is posted
|
|
1199
|
+
assert self._run(client, os.path.join(d, 'in'), out, force=False) == []
|
|
1200
|
+
assert self._tei_outputs(out) == ['2023/p.grobid.tei.xml', '2024/p.grobid.tei.xml']
|
|
1201
|
+
|
|
1202
|
+
def test_single_subdirectory_keeps_its_level(self):
|
|
1203
|
+
"""The base is the directory given as input, not the common path of the files."""
|
|
1204
|
+
client = self._client()
|
|
1205
|
+
with tempfile.TemporaryDirectory() as d:
|
|
1206
|
+
self._touch(d, 'in', '2024', 'a.pdf')
|
|
1207
|
+
self._touch(d, 'in', '2024', 'b.pdf')
|
|
1208
|
+
out = os.path.join(d, 'out')
|
|
1209
|
+
self._run(client, os.path.join(d, 'in'), out)
|
|
1210
|
+
assert self._tei_outputs(out) == ['2024/a.grobid.tei.xml', '2024/b.grobid.tei.xml']
|
|
1211
|
+
|
|
1212
|
+
def test_trailing_slash_and_relative_input(self):
|
|
1213
|
+
client = self._client()
|
|
1214
|
+
with tempfile.TemporaryDirectory() as d:
|
|
1215
|
+
self._touch(d, 'in', 'sub', 'a.pdf')
|
|
1216
|
+
out = os.path.join(d, 'out')
|
|
1217
|
+
self._run(client, os.path.join(d, 'in') + os.sep, out)
|
|
1218
|
+
assert self._tei_outputs(out) == ['sub/a.grobid.tei.xml']
|
|
1219
|
+
|
|
1220
|
+
def test_no_output_writes_next_to_input(self):
|
|
1221
|
+
client = self._client()
|
|
1222
|
+
with tempfile.TemporaryDirectory() as d:
|
|
1223
|
+
self._touch(d, 'in', 'sub', 'a.pdf')
|
|
1224
|
+
self._run(client, os.path.join(d, 'in'), None)
|
|
1225
|
+
assert os.path.isfile(os.path.join(d, 'in', 'sub', 'a.grobid.tei.xml'))
|
|
1226
|
+
|
|
1227
|
+
|
|
1110
1228
|
class TestAtomicWrite:
|
|
1111
1229
|
"""A killed task must never leave a partial output that resume accepts.
|
|
1112
1230
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{grobid_client_python-0.2.1 → grobid_client_python-0.2.2}/grobid_client/format/TEI2LossyJSON.py
RENAMED
|
File without changes
|
{grobid_client_python-0.2.1 → grobid_client_python-0.2.2}/grobid_client/format/TEI2LossyJSON_cli.py
RENAMED
|
File without changes
|
{grobid_client_python-0.2.1 → grobid_client_python-0.2.2}/grobid_client/format/TEI2Markdown.py
RENAMED
|
File without changes
|
{grobid_client_python-0.2.1 → grobid_client_python-0.2.2}/grobid_client/format/TEI2Markdown_cli.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{grobid_client_python-0.2.1 → grobid_client_python-0.2.2}/grobid_client/format/validate_json_refs.py
RENAMED
|
File without changes
|
|
File without changes
|
{grobid_client_python-0.2.1 → grobid_client_python-0.2.2}/grobid_client_python.egg-info/SOURCES.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{grobid_client_python-0.2.1 → grobid_client_python-0.2.2}/grobid_client_python.egg-info/requires.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{grobid_client_python-0.2.1 → grobid_client_python-0.2.2}/resources/test_txt/reference-strings-1.txt
RENAMED
|
File without changes
|
{grobid_client_python-0.2.1 → grobid_client_python-0.2.2}/resources/test_txt/reference-strings-2.txt
RENAMED
|
File without changes
|
{grobid_client_python-0.2.1 → grobid_client_python-0.2.2}/resources/test_txt/reference-strings-3.txt
RENAMED
|
File without changes
|
{grobid_client_python-0.2.1 → grobid_client_python-0.2.2}/resources/test_txt/reference-strings-4.txt
RENAMED
|
File without changes
|
{grobid_client_python-0.2.1 → grobid_client_python-0.2.2}/resources/test_txt/reference-strings-5.txt
RENAMED
|
File without changes
|
{grobid_client_python-0.2.1 → grobid_client_python-0.2.2}/resources/test_txt/reference-strings-6.txt
RENAMED
|
File without changes
|
{grobid_client_python-0.2.1 → grobid_client_python-0.2.2}/resources/test_txt/reference-strings-7.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|