1
|
|
|
""" |
2
|
|
|
OCR-D CLI: ocrd-tool.json management |
3
|
|
|
|
4
|
|
|
.. click:: ocrd.cli.ocrd_tool:ocrd_tool_cli |
5
|
|
|
:prog: ocrd ocrd-tool |
6
|
|
|
:nested: full |
7
|
|
|
|
8
|
|
|
""" |
9
|
|
|
from inspect import getmodule |
10
|
|
|
from json import dumps |
11
|
|
|
import codecs |
12
|
|
|
import sys |
13
|
|
|
import os |
14
|
|
|
import click |
15
|
|
|
|
16
|
|
|
from ocrd.decorators import parameter_option, parameter_override_option |
17
|
|
|
from ocrd.processor import Processor |
18
|
|
|
from ocrd_utils import ( |
19
|
|
|
set_json_key_value_overrides, |
20
|
|
|
parse_json_string_or_file, |
21
|
|
|
parse_json_string_with_comments as loads |
22
|
|
|
) |
23
|
|
|
from ocrd_validators import ParameterValidator, OcrdToolValidator |
24
|
|
|
|
25
|
|
|
class OcrdToolCtx(): |
26
|
|
|
|
27
|
|
|
def __init__(self, filename): |
28
|
|
|
self.filename = filename |
29
|
|
|
with codecs.open(filename, encoding='utf-8') as f: |
30
|
|
|
self.content = f.read() |
31
|
|
|
# perhaps the validator should _always_ run (for default expansion) |
32
|
|
|
# so validate command only for the report? |
33
|
|
|
self.json = loads(self.content) |
34
|
|
|
self.tool_name = '' |
35
|
|
|
|
36
|
|
|
class BashProcessor(Processor): |
37
|
|
|
@property |
38
|
|
|
def metadata(inner_self): # pylint: disable=no-self-argument,arguments-renamed |
39
|
|
|
return self.json |
40
|
|
|
@property |
41
|
|
|
def executable(inner_self): # pylint: disable=no-self-argument,arguments-renamed |
42
|
|
|
return self.tool_name |
43
|
|
|
@property |
44
|
|
|
def moduledir(inner_self): # pylint: disable=no-self-argument,arguments-renamed |
45
|
|
|
return os.path.dirname(self.filename) |
46
|
|
|
# set docstrings to empty |
47
|
|
|
__doc__ = None |
48
|
|
|
# HACK: override the module-level docstring, too |
49
|
|
|
getmodule(OcrdToolCtx).__doc__ = None |
50
|
|
|
def process(inner_self): # pylint: disable=no-self-argument,arguments-renamed |
51
|
|
|
return super() |
52
|
|
|
|
53
|
|
|
self.processor = BashProcessor |
54
|
|
|
|
55
|
|
|
pass_ocrd_tool = click.make_pass_decorator(OcrdToolCtx) |
56
|
|
|
|
57
|
|
|
# ---------------------------------------------------------------------- |
58
|
|
|
# ocrd ocrd-tool |
59
|
|
|
# ---------------------------------------------------------------------- |
60
|
|
|
|
61
|
|
|
@click.group('ocrd-tool', help='Work with ocrd-tool.json JSON_FILE') |
62
|
|
|
@click.argument('json_file') |
63
|
|
|
@click.pass_context |
64
|
|
|
def ocrd_tool_cli(ctx, json_file): |
65
|
|
|
ctx.obj = OcrdToolCtx(json_file) |
66
|
|
|
|
67
|
|
|
# ---------------------------------------------------------------------- |
68
|
|
|
# ocrd ocrd-tool version |
69
|
|
|
# ---------------------------------------------------------------------- |
70
|
|
|
|
71
|
|
|
@ocrd_tool_cli.command('version', help='Version of ocrd-tool.json') |
72
|
|
|
@pass_ocrd_tool |
73
|
|
|
def ocrd_tool_version(ctx): |
74
|
|
|
print(ctx.json['version']) |
75
|
|
|
|
76
|
|
|
# ---------------------------------------------------------------------- |
77
|
|
|
# ocrd ocrd-tool validate |
78
|
|
|
# ---------------------------------------------------------------------- |
79
|
|
|
|
80
|
|
|
@ocrd_tool_cli.command('validate', help='Validate an ocrd-tool.json') |
81
|
|
|
@pass_ocrd_tool |
82
|
|
|
def ocrd_tool_validate(ctx): |
83
|
|
|
report = OcrdToolValidator.validate(ctx.json) |
84
|
|
|
print(report.to_xml()) |
85
|
|
|
if not report.is_valid: |
86
|
|
|
return 128 |
87
|
|
|
|
88
|
|
|
# ---------------------------------------------------------------------- |
89
|
|
|
# ocrd ocrd-tool list-tools |
90
|
|
|
# ---------------------------------------------------------------------- |
91
|
|
|
|
92
|
|
|
@ocrd_tool_cli.command('list-tools', help="List tools") |
93
|
|
|
@pass_ocrd_tool |
94
|
|
|
def ocrd_tool_list(ctx): |
95
|
|
|
for tool in ctx.json['tools']: |
96
|
|
|
print(tool) |
97
|
|
|
|
98
|
|
|
# ---------------------------------------------------------------------- |
99
|
|
|
# ocrd ocrd-tool dump-tools |
100
|
|
|
# ---------------------------------------------------------------------- |
101
|
|
|
|
102
|
|
|
@ocrd_tool_cli.command('dump-tools', help="Dump tools") |
103
|
|
|
@pass_ocrd_tool |
104
|
|
|
def ocrd_tool_dump(ctx): |
105
|
|
|
print(dumps(ctx.json['tools'], indent=True)) |
106
|
|
|
|
107
|
|
|
# ---------------------------------------------------------------------- |
108
|
|
|
# ocrd ocrd-tool tool |
109
|
|
|
# ---------------------------------------------------------------------- |
110
|
|
|
|
111
|
|
|
@ocrd_tool_cli.group('tool', help='Work with a single tool TOOL_NAME') |
112
|
|
|
@click.argument('tool_name') |
113
|
|
|
@pass_ocrd_tool |
114
|
|
|
def ocrd_tool_tool(ctx, tool_name): |
115
|
|
|
if tool_name not in ctx.json['tools']: |
116
|
|
|
raise Exception("No such tool: %s" % tool_name) |
117
|
|
|
ctx.tool_name = tool_name |
118
|
|
|
|
119
|
|
|
# ---------------------------------------------------------------------- |
120
|
|
|
# ocrd ocrd-tool tool description |
121
|
|
|
# ---------------------------------------------------------------------- |
122
|
|
|
|
123
|
|
|
@ocrd_tool_tool.command('description', help="Describe tool") |
124
|
|
|
@pass_ocrd_tool |
125
|
|
|
def ocrd_tool_tool_description(ctx): |
126
|
|
|
print(ctx.json['tools'][ctx.tool_name]['description']) |
127
|
|
|
|
128
|
|
|
@ocrd_tool_tool.command('list-resources', help="List tool's file resources") |
129
|
|
|
@pass_ocrd_tool |
130
|
|
|
def ocrd_tool_tool_list_resources(ctx): |
131
|
|
|
ctx.processor(None).list_resources() |
132
|
|
|
|
133
|
|
|
@ocrd_tool_tool.command('resolve-resource', help="Get a tool's file resource full path name") |
134
|
|
|
@click.argument('res_name') |
135
|
|
|
@pass_ocrd_tool |
136
|
|
|
def ocrd_tool_tool_resolve_resource(ctx, res_name): |
137
|
|
|
print(ctx.processor(None).resolve_resource(res_name)) |
138
|
|
|
|
139
|
|
|
@ocrd_tool_tool.command('show-resource', help="Dump a tool's file resource") |
140
|
|
|
@click.argument('res_name') |
141
|
|
|
@pass_ocrd_tool |
142
|
|
|
def ocrd_tool_tool_show_resource(ctx, res_name): |
143
|
|
|
ctx.processor(None).show_resource(res_name) |
144
|
|
|
|
145
|
|
|
@ocrd_tool_tool.command('help', help="Generate help for processors") |
146
|
|
|
@click.argument('subcommand', required=False) |
147
|
|
|
@pass_ocrd_tool |
148
|
|
|
def ocrd_tool_tool_params_help(ctx, subcommand): |
149
|
|
|
ctx.processor(None).show_help(subcommand=subcommand) |
150
|
|
|
|
151
|
|
|
# ---------------------------------------------------------------------- |
152
|
|
|
# ocrd ocrd-tool tool categories |
153
|
|
|
# ---------------------------------------------------------------------- |
154
|
|
|
|
155
|
|
|
@ocrd_tool_tool.command('categories', help="Categories of tool") |
156
|
|
|
@pass_ocrd_tool |
157
|
|
|
def ocrd_tool_tool_categories(ctx): |
158
|
|
|
print('\n'.join(ctx.json['tools'][ctx.tool_name]['categories'])) |
159
|
|
|
|
160
|
|
|
# ---------------------------------------------------------------------- |
161
|
|
|
# ocrd ocrd-tool tool steps |
162
|
|
|
# ---------------------------------------------------------------------- |
163
|
|
|
|
164
|
|
|
@ocrd_tool_tool.command('steps', help="Steps of tool") |
165
|
|
|
@pass_ocrd_tool |
166
|
|
|
def ocrd_tool_tool_steps(ctx): |
167
|
|
|
print('\n'.join(ctx.json['tools'][ctx.tool_name]['steps'])) |
168
|
|
|
|
169
|
|
|
# ---------------------------------------------------------------------- |
170
|
|
|
# ocrd ocrd-tool tool dump |
171
|
|
|
# ---------------------------------------------------------------------- |
172
|
|
|
|
173
|
|
|
@ocrd_tool_tool.command('dump', help="Dump JSON of tool") |
174
|
|
|
@pass_ocrd_tool |
175
|
|
|
def ocrd_tool_tool_dump(ctx): |
176
|
|
|
print(dumps(ctx.json['tools'][ctx.tool_name], indent=True)) |
177
|
|
|
|
178
|
|
|
# ---------------------------------------------------------------------- |
179
|
|
|
# ocrd ocrd-tool tool parse-params |
180
|
|
|
# ---------------------------------------------------------------------- |
181
|
|
|
|
182
|
|
|
@ocrd_tool_tool.command('parse-params') |
183
|
|
|
@parameter_option |
184
|
|
|
@parameter_override_option |
185
|
|
|
@click.option('-j', '--json', help='Output JSON instead of shell variables', is_flag=True, default=False) |
186
|
|
|
@pass_ocrd_tool |
187
|
|
|
def ocrd_tool_tool_parse_params(ctx, parameter, parameter_override, json): |
188
|
|
|
""" |
189
|
|
|
Parse parameters with fallback to defaults and output as shell-eval'able assignments to params var. |
190
|
|
|
""" |
191
|
|
|
parameter = set_json_key_value_overrides(parse_json_string_or_file(*parameter), *parameter_override) |
192
|
|
|
parameterValidator = ParameterValidator(ctx.json['tools'][ctx.tool_name]) |
193
|
|
|
report = parameterValidator.validate(parameter) |
194
|
|
|
if not report.is_valid: |
195
|
|
|
print(report.to_xml()) |
196
|
|
|
sys.exit(1) |
197
|
|
|
if json: |
198
|
|
|
print(dumps(parameter)) |
199
|
|
|
else: |
200
|
|
|
for k in parameter: |
201
|
|
|
print('params["%s"]="%s"' % (k, parameter[k])) |
202
|
|
|
|