dump-things-pyclient 0.2.3__py3-none-any.whl → 0.2.5__py3-none-any.whl
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.
- dump_things_pyclient/commands/dtc_plugins/auto_curate.py +93 -46
- dump_things_pyclient/commands/dtc_plugins/maintenance.py +73 -0
- dump_things_pyclient/communicate.py +29 -0
- {dump_things_pyclient-0.2.3.dist-info → dump_things_pyclient-0.2.5.dist-info}/METADATA +1 -1
- {dump_things_pyclient-0.2.3.dist-info → dump_things_pyclient-0.2.5.dist-info}/RECORD +8 -7
- {dump_things_pyclient-0.2.3.dist-info → dump_things_pyclient-0.2.5.dist-info}/WHEEL +0 -0
- {dump_things_pyclient-0.2.3.dist-info → dump_things_pyclient-0.2.5.dist-info}/entry_points.txt +0 -0
- {dump_things_pyclient-0.2.3.dist-info → dump_things_pyclient-0.2.5.dist-info}/top_level.txt +0 -0
|
@@ -2,8 +2,19 @@ import json
|
|
|
2
2
|
import logging
|
|
3
3
|
import re
|
|
4
4
|
import sys
|
|
5
|
+
from itertools import (
|
|
6
|
+
count,
|
|
7
|
+
chain,
|
|
8
|
+
)
|
|
9
|
+
from typing import (
|
|
10
|
+
cast,
|
|
11
|
+
Iterable,
|
|
12
|
+
)
|
|
5
13
|
|
|
6
14
|
import rich_click as click
|
|
15
|
+
from rich import print as rprint
|
|
16
|
+
from rich.console import Console
|
|
17
|
+
from rich.progress import track
|
|
7
18
|
|
|
8
19
|
from ...communicate import (
|
|
9
20
|
HTTPError,
|
|
@@ -16,6 +27,7 @@ from ...communicate import (
|
|
|
16
27
|
|
|
17
28
|
logger = logging.getLogger('auto-curate')
|
|
18
29
|
|
|
30
|
+
console = Console(file=sys.stderr)
|
|
19
31
|
stl_info = False
|
|
20
32
|
|
|
21
33
|
|
|
@@ -32,17 +44,17 @@ stl_info = False
|
|
|
32
44
|
@click.option(
|
|
33
45
|
'--destination-service-url',
|
|
34
46
|
metavar='DEST_SERVICE_URL',
|
|
35
|
-
help='select a different dump-thing-service, i.e. not SERVICE_URL, as destination for auto-curated records',
|
|
47
|
+
help='select a different dump-thing-service, i.e. not SERVICE_URL, as destination for auto-curated records (the default is SERVICE_URL)',
|
|
36
48
|
)
|
|
37
49
|
@click.option(
|
|
38
50
|
'--destination-collection',
|
|
39
51
|
metavar='DEST_COLLECTION',
|
|
40
|
-
help='select a different collection, i.e. not
|
|
52
|
+
help='select a different collection, i.e. not COLLECTION, as destination for auto-curated records',
|
|
41
53
|
)
|
|
42
54
|
@click.option(
|
|
43
55
|
'--destination-token',
|
|
44
56
|
metavar='DEST_TOKEN',
|
|
45
|
-
help='if provided, this token will be used the authenticate
|
|
57
|
+
help='if provided, this token will be used the authenticate against DEST_SERVICE_URL, which defaults to SERVICE_URL (the default is the token provided via --token)',
|
|
46
58
|
)
|
|
47
59
|
@click.option(
|
|
48
60
|
'--pid', '-p',
|
|
@@ -114,7 +126,11 @@ def cli(
|
|
|
114
126
|
dry_run,
|
|
115
127
|
)
|
|
116
128
|
except HTTPError as e:
|
|
117
|
-
|
|
129
|
+
rprint(
|
|
130
|
+
f'[red]Error[/red]: {e}: {e.response.text}',
|
|
131
|
+
file=sys.stderr,
|
|
132
|
+
flush=True,
|
|
133
|
+
)
|
|
118
134
|
return 1
|
|
119
135
|
|
|
120
136
|
|
|
@@ -135,14 +151,14 @@ def auto_curate(
|
|
|
135
151
|
curator_token = obj
|
|
136
152
|
|
|
137
153
|
if curator_token is None:
|
|
138
|
-
print(
|
|
139
|
-
f'ERROR: no token was provided (use --token or DTC_TOKEN environment variable)',
|
|
140
|
-
file=sys.stderr,
|
|
141
|
-
flush=True,
|
|
142
|
-
)
|
|
154
|
+
console.print(f'[red]Error[/red]: no token was provided (use --token or DTC_TOKEN environment variable)')
|
|
143
155
|
return 1
|
|
144
156
|
|
|
145
|
-
|
|
157
|
+
if destination_service_url is None:
|
|
158
|
+
destination_service_url = service_url
|
|
159
|
+
|
|
160
|
+
if destination_token is None:
|
|
161
|
+
destination_token = curator_token
|
|
146
162
|
|
|
147
163
|
output = None
|
|
148
164
|
|
|
@@ -157,12 +173,12 @@ def auto_curate(
|
|
|
157
173
|
if list_labels:
|
|
158
174
|
output = []
|
|
159
175
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
)
|
|
165
|
-
|
|
176
|
+
all_labels = incoming_read_labels(
|
|
177
|
+
service_url=service_url,
|
|
178
|
+
collection=collection,
|
|
179
|
+
token=obj,
|
|
180
|
+
)
|
|
181
|
+
for label in all_labels:
|
|
166
182
|
if include and label not in include:
|
|
167
183
|
logger.debug('ignoring non-included incoming label: %s', label)
|
|
168
184
|
continue
|
|
@@ -178,13 +194,34 @@ def auto_curate(
|
|
|
178
194
|
if list_records:
|
|
179
195
|
output[label] = []
|
|
180
196
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
197
|
+
# Get the total number of entries for the
|
|
198
|
+
record_source = incoming_read_records(
|
|
199
|
+
service_url=service_url,
|
|
200
|
+
collection=collection,
|
|
201
|
+
label=label,
|
|
202
|
+
token=obj,
|
|
203
|
+
)
|
|
187
204
|
|
|
205
|
+
# Get the first entry to find the total number of records
|
|
206
|
+
try:
|
|
207
|
+
first_record, _, _, _, total = next(record_source)
|
|
208
|
+
except StopIteration:
|
|
209
|
+
console.print(f'no records in incoming area [green]{label}[/green], skipping it')
|
|
210
|
+
continue
|
|
211
|
+
|
|
212
|
+
# Get the first entry an all other entries
|
|
213
|
+
for index, (record, _, _, _, total) in track(
|
|
214
|
+
zip(
|
|
215
|
+
count(),
|
|
216
|
+
chain(
|
|
217
|
+
[(first_record, None, None, None, None)],
|
|
218
|
+
cast(Iterable, record_source),
|
|
219
|
+
),
|
|
220
|
+
),
|
|
221
|
+
description=f'processing [green]{label}[/green]',
|
|
222
|
+
total=total,
|
|
223
|
+
console=console,
|
|
224
|
+
):
|
|
188
225
|
if list_records:
|
|
189
226
|
output[label].append(record)
|
|
190
227
|
continue
|
|
@@ -204,43 +241,53 @@ def auto_curate(
|
|
|
204
241
|
class_name = re.search('([_A-Za-z0-9]*$)', record['schema_type']).group(0)
|
|
205
242
|
except (IndexError, KeyError):
|
|
206
243
|
global stl_info
|
|
244
|
+
console.print(f'[yellow]Warning[/yellow]: ignoring record with pid {record["pid"]} because `schema_type` attribute is missing.')
|
|
207
245
|
if not stl_info:
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
with a "Schema Type Layer", i.e., "record_dir+stl" or
|
|
213
|
-
"sqlite+stl".""",
|
|
246
|
+
console.print(
|
|
247
|
+
' Please ensure that `schema_type` is stored in the records '
|
|
248
|
+
'or that the associated incoming area store has a backend with a '
|
|
249
|
+
'"Schema Type Layer", i.e., "record_dir+stl" or "sqlite+stl"."',
|
|
214
250
|
)
|
|
215
251
|
stl_info = True
|
|
216
|
-
else:
|
|
217
|
-
logger.warning(f'ignoring record with pid {record["pid"]}, `schema_type` attribute is missing.')
|
|
218
252
|
continue
|
|
219
253
|
|
|
220
254
|
if dry_run:
|
|
221
|
-
print(f'WRITE record "{record["pid"]}" of class "{class_name}" to "{destination_collection}@{destination_service_url}"')
|
|
222
|
-
print(f'DELETE record "{record["pid"]}" from inbox "{label}" of "{collection}@{service_url}"')
|
|
255
|
+
console.print(f'WRITE record [green]"{record["pid"]}"[/green] of class "{class_name}" to "{destination_collection}@{destination_service_url}"')
|
|
256
|
+
console.print(f'DELETE record [green]"{record["pid"]}"[/green] from inbox "{label}" of "{collection}@{service_url}"')
|
|
223
257
|
continue
|
|
224
258
|
|
|
225
259
|
# Store record in destination collection
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
260
|
+
try:
|
|
261
|
+
curated_write_record(
|
|
262
|
+
service_url=destination_service_url,
|
|
263
|
+
collection=destination_collection,
|
|
264
|
+
class_name=class_name,
|
|
265
|
+
record=record,
|
|
266
|
+
token=destination_token,
|
|
267
|
+
)
|
|
268
|
+
except HTTPError as e:
|
|
269
|
+
console.print(
|
|
270
|
+
f'[red]Error[/red]: writing record with pid {record["pid"]} failed: {e}: {e.response.text}',
|
|
271
|
+
)
|
|
272
|
+
raise
|
|
232
273
|
|
|
233
274
|
# Delete record from incoming area
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
275
|
+
try:
|
|
276
|
+
incoming_delete_record(
|
|
277
|
+
service_url=service_url,
|
|
278
|
+
collection=collection,
|
|
279
|
+
label=label,
|
|
280
|
+
pid=record['pid'],
|
|
281
|
+
token=curator_token,
|
|
282
|
+
)
|
|
283
|
+
except HTTPError as e:
|
|
284
|
+
console.print(
|
|
285
|
+
f'[red]ERROR[/red]: deleting record with pid {record["pid"]} failed: {e}: {e.response.text}',
|
|
286
|
+
)
|
|
287
|
+
raise
|
|
241
288
|
|
|
242
289
|
if output is not None:
|
|
243
|
-
|
|
290
|
+
rprint(json.dumps(output, ensure_ascii=False))
|
|
244
291
|
|
|
245
292
|
return 0
|
|
246
293
|
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
|
|
3
|
+
import rich_click as click
|
|
4
|
+
|
|
5
|
+
from ...communicate import (
|
|
6
|
+
HTTPError,
|
|
7
|
+
maintenance as communicate_maintenance,
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
logger = logging.getLogger('maintenance')
|
|
12
|
+
|
|
13
|
+
subcommand_name = 'maintenance'
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@click.command(short_help='Activate or deactivate maintenance mode on a collection')
|
|
17
|
+
@click.pass_obj
|
|
18
|
+
@click.argument(
|
|
19
|
+
'service_url',
|
|
20
|
+
metavar='SERVICE_URL',
|
|
21
|
+
)
|
|
22
|
+
@click.argument(
|
|
23
|
+
'collection',
|
|
24
|
+
metavar='COLLECTION',
|
|
25
|
+
)
|
|
26
|
+
@click.argument(
|
|
27
|
+
'active',
|
|
28
|
+
metavar='ACTIVE',
|
|
29
|
+
type=click.Choice(['On', 'Off'], case_sensitive=False),
|
|
30
|
+
)
|
|
31
|
+
def cli(
|
|
32
|
+
obj,
|
|
33
|
+
service_url: str,
|
|
34
|
+
collection: str,
|
|
35
|
+
active: bool,
|
|
36
|
+
):
|
|
37
|
+
"""Activate or deactivate maintenance mode on collection COLLECTION on the
|
|
38
|
+
service SERVICE_URL. The argument ACTIVE should be either `On` or `Off`
|
|
39
|
+
(case-insensitive).
|
|
40
|
+
|
|
41
|
+
A token with curator rights is required.
|
|
42
|
+
|
|
43
|
+
This command expects a server version >= 5.4.0"""
|
|
44
|
+
try:
|
|
45
|
+
return maintenance(
|
|
46
|
+
obj,
|
|
47
|
+
service_url,
|
|
48
|
+
collection,
|
|
49
|
+
active,
|
|
50
|
+
)
|
|
51
|
+
except HTTPError as e:
|
|
52
|
+
click.echo(f'ERROR: {e}: {e.response.text}', err=True)
|
|
53
|
+
return 1
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def maintenance(
|
|
57
|
+
obj: str,
|
|
58
|
+
service_url: str,
|
|
59
|
+
collection: str,
|
|
60
|
+
active: bool,
|
|
61
|
+
):
|
|
62
|
+
token = obj
|
|
63
|
+
if token is None:
|
|
64
|
+
click.echo('ERROR: no token provided', err=True)
|
|
65
|
+
return 1
|
|
66
|
+
|
|
67
|
+
communicate_maintenance(
|
|
68
|
+
service_url=service_url,
|
|
69
|
+
collection=collection,
|
|
70
|
+
active=active,
|
|
71
|
+
token=token,
|
|
72
|
+
)
|
|
73
|
+
return 0
|
|
@@ -37,6 +37,8 @@ __all__ = [
|
|
|
37
37
|
'incoming_read_records_of_class',
|
|
38
38
|
'incoming_read_record_with_pid',
|
|
39
39
|
'incoming_write_record',
|
|
40
|
+
'maintenance',
|
|
41
|
+
'server',
|
|
40
42
|
]
|
|
41
43
|
|
|
42
44
|
|
|
@@ -694,6 +696,33 @@ def server(
|
|
|
694
696
|
return _do_request(requests.get, url=url, token=None, params=None)
|
|
695
697
|
|
|
696
698
|
|
|
699
|
+
def maintenance(
|
|
700
|
+
service_url: str,
|
|
701
|
+
collection: str,
|
|
702
|
+
active: bool,
|
|
703
|
+
token: str,
|
|
704
|
+
) -> None:
|
|
705
|
+
"""Activate or deactivate maintenance mode of a collection
|
|
706
|
+
|
|
707
|
+
:param service_url: the base URL of the service, i.e., the URL up to
|
|
708
|
+
`/<collection>/...`, `/maintenance`, or `/server`
|
|
709
|
+
:param collection: the name of the collection
|
|
710
|
+
:param active: whether maintenance mode should be active (`True`) or
|
|
711
|
+
non-active (`False`).
|
|
712
|
+
:param token: a token to authenticate against the endpoint, the token
|
|
713
|
+
must have curator-rights for the collection
|
|
714
|
+
"""
|
|
715
|
+
url = (
|
|
716
|
+
(f'{service_url[:-1]}' if service_url.endswith('/') else service_url)
|
|
717
|
+
+ '/maintenance'
|
|
718
|
+
)
|
|
719
|
+
_post_to_url(
|
|
720
|
+
url=url,
|
|
721
|
+
token=token,
|
|
722
|
+
json={'collection': collection, 'active': active}
|
|
723
|
+
)
|
|
724
|
+
|
|
725
|
+
|
|
697
726
|
def _get_from_url(url: str,
|
|
698
727
|
token: str | None,
|
|
699
728
|
params: dict[str, str] | None = None,
|
|
@@ -1,20 +1,21 @@
|
|
|
1
1
|
dump_things_pyclient/__init__.py,sha256=cn-U3TRIalN6aYHp1cMBRkQm1x98XBwquLFbgFEIf_Q,113
|
|
2
|
-
dump_things_pyclient/communicate.py,sha256=
|
|
2
|
+
dump_things_pyclient/communicate.py,sha256=OSSrPRmeURiW1tOuBXecBzUgC_Qeb8TBs05l6QyoNF8,31904
|
|
3
3
|
dump_things_pyclient/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
4
4
|
dump_things_pyclient/commands/dtc.py,sha256=dxW5RuogqwhzfVujZ_EEsQMk8BcVMbZyMdg5c8EvYIA,1726
|
|
5
5
|
dump_things_pyclient/commands/json2ttl.py,sha256=8BkvdjLWZ_H0L6fTmuR2M2MglKiMUiuNUcuWr_w6_dQ,2133
|
|
6
6
|
dump_things_pyclient/commands/redirect.py,sha256=kl8pGj8khjxk4lhk8AJLfgtCIm5PtjeMAl0J6K5FB7M,264
|
|
7
7
|
dump_things_pyclient/commands/dtc_plugins/__init__.py,sha256=0YLByLiofhHkhJcDCkokldcCw3Jj0rsKJinRX4tt3Hc,514
|
|
8
|
-
dump_things_pyclient/commands/dtc_plugins/auto_curate.py,sha256=
|
|
8
|
+
dump_things_pyclient/commands/dtc_plugins/auto_curate.py,sha256=mDYHkXVS_2Nrcqd90nC9LIBCQnzYfRdj66fmZweyX_M,9094
|
|
9
9
|
dump_things_pyclient/commands/dtc_plugins/clean_incoming.py,sha256=slk3xn1-DgMl88WZqgyemyscwof97TMXt3rley4mU1w,2086
|
|
10
10
|
dump_things_pyclient/commands/dtc_plugins/delete_record.py,sha256=TVXyGetob8d75zuSBGuRKjtQPAcRqqjw-CcwYYHSC28,3626
|
|
11
11
|
dump_things_pyclient/commands/dtc_plugins/export.py,sha256=FF1DmSmIA3GIhosUrTrXy_qyypMSJUalxdLJ844XhIE,4850
|
|
12
12
|
dump_things_pyclient/commands/dtc_plugins/get_records.py,sha256=00W-agALqO_qFhjq14MI9N6TnJCgscMioy-Ll1ClrB4,7451
|
|
13
13
|
dump_things_pyclient/commands/dtc_plugins/list_incoming.py,sha256=tmM0Qs4MVwMMLyERsWCxWGTM90rSNOShLpHH32wObd8,1959
|
|
14
|
+
dump_things_pyclient/commands/dtc_plugins/maintenance.py,sha256=WkVVxDVd04Mi-cRIdAGSwn-7aP2bDlLjaCzt-H4_sJU,1553
|
|
14
15
|
dump_things_pyclient/commands/dtc_plugins/post_records.py,sha256=s3j9THe-RszKxyIISkQZRCTKplWWLlomHbS5dyRlep0,2908
|
|
15
16
|
dump_things_pyclient/commands/dtc_plugins/read_pages.py,sha256=hpw7vtG7joIMrNqEqZFCwzbQFd3ATzv7iyySsX8nKWk,3385
|
|
16
|
-
dump_things_pyclient-0.2.
|
|
17
|
-
dump_things_pyclient-0.2.
|
|
18
|
-
dump_things_pyclient-0.2.
|
|
19
|
-
dump_things_pyclient-0.2.
|
|
20
|
-
dump_things_pyclient-0.2.
|
|
17
|
+
dump_things_pyclient-0.2.5.dist-info/METADATA,sha256=Bnwt2hpG4eycg9gnso1oi_HWhNjLePPbgxpeZ6kTrNc,999
|
|
18
|
+
dump_things_pyclient-0.2.5.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
19
|
+
dump_things_pyclient-0.2.5.dist-info/entry_points.txt,sha256=U1QhQtk767G_OXdZwPdTXYbIPfcDU13Z2u1d6exX8uE,470
|
|
20
|
+
dump_things_pyclient-0.2.5.dist-info/top_level.txt,sha256=Asvruw-SyLoYhWis1CFOx89RGxpjXoTZVGoq4JSGt88,21
|
|
21
|
+
dump_things_pyclient-0.2.5.dist-info/RECORD,,
|
|
File without changes
|
{dump_things_pyclient-0.2.3.dist-info → dump_things_pyclient-0.2.5.dist-info}/entry_points.txt
RENAMED
|
File without changes
|
|
File without changes
|