ocrd 3.0.0b3__tar.gz → 3.0.0b5__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.
Files changed (155) hide show
  1. {ocrd-3.0.0b3/src/ocrd.egg-info → ocrd-3.0.0b5}/PKG-INFO +1 -1
  2. ocrd-3.0.0b5/VERSION +1 -0
  3. {ocrd-3.0.0b3 → ocrd-3.0.0b5}/src/ocrd/cli/bashlib.py +6 -4
  4. {ocrd-3.0.0b3 → ocrd-3.0.0b5}/src/ocrd/cli/ocrd_tool.py +1 -1
  5. {ocrd-3.0.0b3 → ocrd-3.0.0b5}/src/ocrd/cli/validate.py +6 -3
  6. {ocrd-3.0.0b3 → ocrd-3.0.0b5}/src/ocrd/cli/workspace.py +52 -47
  7. {ocrd-3.0.0b3 → ocrd-3.0.0b5}/src/ocrd/decorators/ocrd_cli_options.py +1 -0
  8. {ocrd-3.0.0b3 → ocrd-3.0.0b5}/src/ocrd/lib.bash +24 -21
  9. {ocrd-3.0.0b3 → ocrd-3.0.0b5}/src/ocrd/mets_server.py +39 -8
  10. {ocrd-3.0.0b3 → ocrd-3.0.0b5}/src/ocrd/processor/base.py +42 -24
  11. {ocrd-3.0.0b3 → ocrd-3.0.0b5}/src/ocrd/processor/builtin/dummy_processor.py +0 -2
  12. {ocrd-3.0.0b3 → ocrd-3.0.0b5}/src/ocrd/processor/helpers.py +6 -5
  13. {ocrd-3.0.0b3 → ocrd-3.0.0b5}/src/ocrd/processor/ocrd_page_result.py +2 -2
  14. {ocrd-3.0.0b3 → ocrd-3.0.0b5}/src/ocrd/workspace.py +7 -1
  15. {ocrd-3.0.0b3 → ocrd-3.0.0b5/src/ocrd.egg-info}/PKG-INFO +1 -1
  16. {ocrd-3.0.0b3 → ocrd-3.0.0b5}/src/ocrd_models/ocrd_page_generateds.py +44 -11
  17. {ocrd-3.0.0b3 → ocrd-3.0.0b5}/src/ocrd_utils/str.py +2 -1
  18. {ocrd-3.0.0b3 → ocrd-3.0.0b5}/tests/test_decorators.py +5 -1
  19. {ocrd-3.0.0b3 → ocrd-3.0.0b5}/tests/test_mets_server.py +15 -1
  20. ocrd-3.0.0b3/VERSION +0 -1
  21. {ocrd-3.0.0b3 → ocrd-3.0.0b5}/LICENSE +0 -0
  22. {ocrd-3.0.0b3 → ocrd-3.0.0b5}/MANIFEST.in +0 -0
  23. {ocrd-3.0.0b3 → ocrd-3.0.0b5}/README.md +0 -0
  24. {ocrd-3.0.0b3 → ocrd-3.0.0b5}/README_bashlib.md +0 -0
  25. {ocrd-3.0.0b3 → ocrd-3.0.0b5}/README_ocrd.md +0 -0
  26. {ocrd-3.0.0b3 → ocrd-3.0.0b5}/README_ocrd_modelfactory.md +0 -0
  27. {ocrd-3.0.0b3 → ocrd-3.0.0b5}/README_ocrd_models.md +0 -0
  28. {ocrd-3.0.0b3 → ocrd-3.0.0b5}/README_ocrd_network.md +0 -0
  29. {ocrd-3.0.0b3 → ocrd-3.0.0b5}/README_ocrd_utils.md +0 -0
  30. {ocrd-3.0.0b3 → ocrd-3.0.0b5}/README_ocrd_validators.md +0 -0
  31. {ocrd-3.0.0b3 → ocrd-3.0.0b5}/pyproject.toml +0 -0
  32. {ocrd-3.0.0b3 → ocrd-3.0.0b5}/requirements.txt +0 -0
  33. {ocrd-3.0.0b3 → ocrd-3.0.0b5}/setup.cfg +0 -0
  34. {ocrd-3.0.0b3 → ocrd-3.0.0b5}/src/ocrd/__init__.py +0 -0
  35. {ocrd-3.0.0b3 → ocrd-3.0.0b5}/src/ocrd/cli/__init__.py +0 -0
  36. {ocrd-3.0.0b3 → ocrd-3.0.0b5}/src/ocrd/cli/log.py +0 -0
  37. {ocrd-3.0.0b3 → ocrd-3.0.0b5}/src/ocrd/cli/network.py +0 -0
  38. {ocrd-3.0.0b3 → ocrd-3.0.0b5}/src/ocrd/cli/process.py +0 -0
  39. {ocrd-3.0.0b3 → ocrd-3.0.0b5}/src/ocrd/cli/resmgr.py +0 -0
  40. {ocrd-3.0.0b3 → ocrd-3.0.0b5}/src/ocrd/cli/zip.py +0 -0
  41. {ocrd-3.0.0b3 → ocrd-3.0.0b5}/src/ocrd/constants.py +0 -0
  42. {ocrd-3.0.0b3 → ocrd-3.0.0b5}/src/ocrd/decorators/__init__.py +0 -0
  43. {ocrd-3.0.0b3 → ocrd-3.0.0b5}/src/ocrd/decorators/loglevel_option.py +0 -0
  44. {ocrd-3.0.0b3 → ocrd-3.0.0b5}/src/ocrd/decorators/mets_find_options.py +0 -0
  45. {ocrd-3.0.0b3 → ocrd-3.0.0b5}/src/ocrd/decorators/parameter_option.py +0 -0
  46. {ocrd-3.0.0b3 → ocrd-3.0.0b5}/src/ocrd/ocrd-all-tool.json +0 -0
  47. {ocrd-3.0.0b3 → ocrd-3.0.0b5}/src/ocrd/processor/__init__.py +0 -0
  48. {ocrd-3.0.0b3 → ocrd-3.0.0b5}/src/ocrd/processor/builtin/__init__.py +0 -0
  49. {ocrd-3.0.0b3 → ocrd-3.0.0b5}/src/ocrd/processor/builtin/dummy/__init__.py +0 -0
  50. {ocrd-3.0.0b3 → ocrd-3.0.0b5}/src/ocrd/processor/builtin/dummy/ocrd-tool.json +0 -0
  51. {ocrd-3.0.0b3 → ocrd-3.0.0b5}/src/ocrd/resolver.py +0 -0
  52. {ocrd-3.0.0b3 → ocrd-3.0.0b5}/src/ocrd/resource_list.yml +0 -0
  53. {ocrd-3.0.0b3 → ocrd-3.0.0b5}/src/ocrd/resource_manager.py +0 -0
  54. {ocrd-3.0.0b3 → ocrd-3.0.0b5}/src/ocrd/task_sequence.py +0 -0
  55. {ocrd-3.0.0b3 → ocrd-3.0.0b5}/src/ocrd/workspace_backup.py +0 -0
  56. {ocrd-3.0.0b3 → ocrd-3.0.0b5}/src/ocrd/workspace_bagger.py +0 -0
  57. {ocrd-3.0.0b3 → ocrd-3.0.0b5}/src/ocrd.egg-info/SOURCES.txt +0 -0
  58. {ocrd-3.0.0b3 → ocrd-3.0.0b5}/src/ocrd.egg-info/dependency_links.txt +0 -0
  59. {ocrd-3.0.0b3 → ocrd-3.0.0b5}/src/ocrd.egg-info/entry_points.txt +0 -0
  60. {ocrd-3.0.0b3 → ocrd-3.0.0b5}/src/ocrd.egg-info/requires.txt +0 -0
  61. {ocrd-3.0.0b3 → ocrd-3.0.0b5}/src/ocrd.egg-info/top_level.txt +0 -0
  62. {ocrd-3.0.0b3 → ocrd-3.0.0b5}/src/ocrd_modelfactory/__init__.py +0 -0
  63. {ocrd-3.0.0b3 → ocrd-3.0.0b5}/src/ocrd_models/__init__.py +0 -0
  64. {ocrd-3.0.0b3 → ocrd-3.0.0b5}/src/ocrd_models/constants.py +0 -0
  65. {ocrd-3.0.0b3 → ocrd-3.0.0b5}/src/ocrd_models/mets-empty.xml +0 -0
  66. {ocrd-3.0.0b3 → ocrd-3.0.0b5}/src/ocrd_models/ocrd_agent.py +0 -0
  67. {ocrd-3.0.0b3 → ocrd-3.0.0b5}/src/ocrd_models/ocrd_exif.py +0 -0
  68. {ocrd-3.0.0b3 → ocrd-3.0.0b5}/src/ocrd_models/ocrd_file.py +0 -0
  69. {ocrd-3.0.0b3 → ocrd-3.0.0b5}/src/ocrd_models/ocrd_mets.py +0 -0
  70. {ocrd-3.0.0b3 → ocrd-3.0.0b5}/src/ocrd_models/ocrd_page.py +0 -0
  71. {ocrd-3.0.0b3 → ocrd-3.0.0b5}/src/ocrd_models/ocrd_xml_base.py +0 -0
  72. {ocrd-3.0.0b3 → ocrd-3.0.0b5}/src/ocrd_models/report.py +0 -0
  73. {ocrd-3.0.0b3 → ocrd-3.0.0b5}/src/ocrd_models/utils.py +0 -0
  74. {ocrd-3.0.0b3 → ocrd-3.0.0b5}/src/ocrd_network/__init__.py +0 -0
  75. {ocrd-3.0.0b3 → ocrd-3.0.0b5}/src/ocrd_network/cli/__init__.py +0 -0
  76. {ocrd-3.0.0b3 → ocrd-3.0.0b5}/src/ocrd_network/cli/client.py +0 -0
  77. {ocrd-3.0.0b3 → ocrd-3.0.0b5}/src/ocrd_network/cli/processing_server.py +0 -0
  78. {ocrd-3.0.0b3 → ocrd-3.0.0b5}/src/ocrd_network/cli/processing_worker.py +0 -0
  79. {ocrd-3.0.0b3 → ocrd-3.0.0b5}/src/ocrd_network/cli/processor_server.py +0 -0
  80. {ocrd-3.0.0b3 → ocrd-3.0.0b5}/src/ocrd_network/client.py +0 -0
  81. {ocrd-3.0.0b3 → ocrd-3.0.0b5}/src/ocrd_network/client_utils.py +0 -0
  82. {ocrd-3.0.0b3 → ocrd-3.0.0b5}/src/ocrd_network/constants.py +0 -0
  83. {ocrd-3.0.0b3 → ocrd-3.0.0b5}/src/ocrd_network/database.py +0 -0
  84. {ocrd-3.0.0b3 → ocrd-3.0.0b5}/src/ocrd_network/logging_utils.py +0 -0
  85. {ocrd-3.0.0b3 → ocrd-3.0.0b5}/src/ocrd_network/models/__init__.py +0 -0
  86. {ocrd-3.0.0b3 → ocrd-3.0.0b5}/src/ocrd_network/models/job.py +0 -0
  87. {ocrd-3.0.0b3 → ocrd-3.0.0b5}/src/ocrd_network/models/messages.py +0 -0
  88. {ocrd-3.0.0b3 → ocrd-3.0.0b5}/src/ocrd_network/models/ocrd_tool.py +0 -0
  89. {ocrd-3.0.0b3 → ocrd-3.0.0b5}/src/ocrd_network/models/workflow.py +0 -0
  90. {ocrd-3.0.0b3 → ocrd-3.0.0b5}/src/ocrd_network/models/workspace.py +0 -0
  91. {ocrd-3.0.0b3 → ocrd-3.0.0b5}/src/ocrd_network/param_validators.py +0 -0
  92. {ocrd-3.0.0b3 → ocrd-3.0.0b5}/src/ocrd_network/process_helpers.py +0 -0
  93. {ocrd-3.0.0b3 → ocrd-3.0.0b5}/src/ocrd_network/processing_server.py +0 -0
  94. {ocrd-3.0.0b3 → ocrd-3.0.0b5}/src/ocrd_network/processing_worker.py +0 -0
  95. {ocrd-3.0.0b3 → ocrd-3.0.0b5}/src/ocrd_network/processor_server.py +0 -0
  96. {ocrd-3.0.0b3 → ocrd-3.0.0b5}/src/ocrd_network/rabbitmq_utils/__init__.py +0 -0
  97. {ocrd-3.0.0b3 → ocrd-3.0.0b5}/src/ocrd_network/rabbitmq_utils/connector.py +0 -0
  98. {ocrd-3.0.0b3 → ocrd-3.0.0b5}/src/ocrd_network/rabbitmq_utils/constants.py +0 -0
  99. {ocrd-3.0.0b3 → ocrd-3.0.0b5}/src/ocrd_network/rabbitmq_utils/consumer.py +0 -0
  100. {ocrd-3.0.0b3 → ocrd-3.0.0b5}/src/ocrd_network/rabbitmq_utils/helpers.py +0 -0
  101. {ocrd-3.0.0b3 → ocrd-3.0.0b5}/src/ocrd_network/rabbitmq_utils/ocrd_messages.py +0 -0
  102. {ocrd-3.0.0b3 → ocrd-3.0.0b5}/src/ocrd_network/rabbitmq_utils/publisher.py +0 -0
  103. {ocrd-3.0.0b3 → ocrd-3.0.0b5}/src/ocrd_network/runtime_data/__init__.py +0 -0
  104. {ocrd-3.0.0b3 → ocrd-3.0.0b5}/src/ocrd_network/runtime_data/config_parser.py +0 -0
  105. {ocrd-3.0.0b3 → ocrd-3.0.0b5}/src/ocrd_network/runtime_data/connection_clients.py +0 -0
  106. {ocrd-3.0.0b3 → ocrd-3.0.0b5}/src/ocrd_network/runtime_data/deployer.py +0 -0
  107. {ocrd-3.0.0b3 → ocrd-3.0.0b5}/src/ocrd_network/runtime_data/hosts.py +0 -0
  108. {ocrd-3.0.0b3 → ocrd-3.0.0b5}/src/ocrd_network/runtime_data/network_agents.py +0 -0
  109. {ocrd-3.0.0b3 → ocrd-3.0.0b5}/src/ocrd_network/runtime_data/network_services.py +0 -0
  110. {ocrd-3.0.0b3 → ocrd-3.0.0b5}/src/ocrd_network/server_cache.py +0 -0
  111. {ocrd-3.0.0b3 → ocrd-3.0.0b5}/src/ocrd_network/server_utils.py +0 -0
  112. {ocrd-3.0.0b3 → ocrd-3.0.0b5}/src/ocrd_network/tcp_to_uds_mets_proxy.py +0 -0
  113. {ocrd-3.0.0b3 → ocrd-3.0.0b5}/src/ocrd_network/utils.py +0 -0
  114. {ocrd-3.0.0b3 → ocrd-3.0.0b5}/src/ocrd_utils/__init__.py +0 -0
  115. {ocrd-3.0.0b3 → ocrd-3.0.0b5}/src/ocrd_utils/config.py +0 -0
  116. {ocrd-3.0.0b3 → ocrd-3.0.0b5}/src/ocrd_utils/constants.py +0 -0
  117. {ocrd-3.0.0b3 → ocrd-3.0.0b5}/src/ocrd_utils/deprecate.py +0 -0
  118. {ocrd-3.0.0b3 → ocrd-3.0.0b5}/src/ocrd_utils/image.py +0 -0
  119. {ocrd-3.0.0b3 → ocrd-3.0.0b5}/src/ocrd_utils/introspect.py +0 -0
  120. {ocrd-3.0.0b3 → ocrd-3.0.0b5}/src/ocrd_utils/logging.py +0 -0
  121. {ocrd-3.0.0b3 → ocrd-3.0.0b5}/src/ocrd_utils/ocrd_logging.conf +0 -0
  122. {ocrd-3.0.0b3 → ocrd-3.0.0b5}/src/ocrd_utils/os.py +0 -0
  123. {ocrd-3.0.0b3 → ocrd-3.0.0b5}/src/ocrd_validators/__init__.py +0 -0
  124. {ocrd-3.0.0b3 → ocrd-3.0.0b5}/src/ocrd_validators/bagit-profile.yml +0 -0
  125. {ocrd-3.0.0b3 → ocrd-3.0.0b5}/src/ocrd_validators/constants.py +0 -0
  126. {ocrd-3.0.0b3 → ocrd-3.0.0b5}/src/ocrd_validators/json_validator.py +0 -0
  127. {ocrd-3.0.0b3 → ocrd-3.0.0b5}/src/ocrd_validators/message_processing.schema.yml +0 -0
  128. {ocrd-3.0.0b3 → ocrd-3.0.0b5}/src/ocrd_validators/message_result.schema.yml +0 -0
  129. {ocrd-3.0.0b3 → ocrd-3.0.0b5}/src/ocrd_validators/mets.xsd +0 -0
  130. {ocrd-3.0.0b3 → ocrd-3.0.0b5}/src/ocrd_validators/ocrd_network_message_validator.py +0 -0
  131. {ocrd-3.0.0b3 → ocrd-3.0.0b5}/src/ocrd_validators/ocrd_tool.schema.yml +0 -0
  132. {ocrd-3.0.0b3 → ocrd-3.0.0b5}/src/ocrd_validators/ocrd_tool_validator.py +0 -0
  133. {ocrd-3.0.0b3 → ocrd-3.0.0b5}/src/ocrd_validators/ocrd_zip_validator.py +0 -0
  134. {ocrd-3.0.0b3 → ocrd-3.0.0b5}/src/ocrd_validators/page.xsd +0 -0
  135. {ocrd-3.0.0b3 → ocrd-3.0.0b5}/src/ocrd_validators/page_validator.py +0 -0
  136. {ocrd-3.0.0b3 → ocrd-3.0.0b5}/src/ocrd_validators/parameter_validator.py +0 -0
  137. {ocrd-3.0.0b3 → ocrd-3.0.0b5}/src/ocrd_validators/processing_server_config.schema.yml +0 -0
  138. {ocrd-3.0.0b3 → ocrd-3.0.0b5}/src/ocrd_validators/processing_server_config_validator.py +0 -0
  139. {ocrd-3.0.0b3 → ocrd-3.0.0b5}/src/ocrd_validators/resource_list_validator.py +0 -0
  140. {ocrd-3.0.0b3 → ocrd-3.0.0b5}/src/ocrd_validators/workspace_validator.py +0 -0
  141. {ocrd-3.0.0b3 → ocrd-3.0.0b5}/src/ocrd_validators/xlink.xsd +0 -0
  142. {ocrd-3.0.0b3 → ocrd-3.0.0b5}/src/ocrd_validators/xsd_mets_validator.py +0 -0
  143. {ocrd-3.0.0b3 → ocrd-3.0.0b5}/src/ocrd_validators/xsd_page_validator.py +0 -0
  144. {ocrd-3.0.0b3 → ocrd-3.0.0b5}/src/ocrd_validators/xsd_validator.py +0 -0
  145. {ocrd-3.0.0b3 → ocrd-3.0.0b5}/tests/test_logging.py +0 -0
  146. {ocrd-3.0.0b3 → ocrd-3.0.0b5}/tests/test_logging_conf.py +0 -0
  147. {ocrd-3.0.0b3 → ocrd-3.0.0b5}/tests/test_model_factory.py +0 -0
  148. {ocrd-3.0.0b3 → ocrd-3.0.0b5}/tests/test_resolver.py +0 -0
  149. {ocrd-3.0.0b3 → ocrd-3.0.0b5}/tests/test_resolver_oai.py +0 -0
  150. {ocrd-3.0.0b3 → ocrd-3.0.0b5}/tests/test_resource_manager.py +0 -0
  151. {ocrd-3.0.0b3 → ocrd-3.0.0b5}/tests/test_task_sequence.py +0 -0
  152. {ocrd-3.0.0b3 → ocrd-3.0.0b5}/tests/test_utils.py +0 -0
  153. {ocrd-3.0.0b3 → ocrd-3.0.0b5}/tests/test_version.py +0 -0
  154. {ocrd-3.0.0b3 → ocrd-3.0.0b5}/tests/test_workspace.py +0 -0
  155. {ocrd-3.0.0b3 → ocrd-3.0.0b5}/tests/test_workspace_remove.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: ocrd
3
- Version: 3.0.0b3
3
+ Version: 3.0.0b5
4
4
  Summary: OCR-D framework
5
5
  Author-email: Konstantin Baierer <unixprog@gmail.com>
6
6
  License: Apache License 2.0
ocrd-3.0.0b5/VERSION ADDED
@@ -0,0 +1 @@
1
+ 3.0.0b5
@@ -76,10 +76,10 @@ def bashlib_constants(name):
76
76
  @click.option('--ocrd-tool', help="path to ocrd-tool.json of processor to feed", default=None)
77
77
  @click.option('--executable', help="name of processor executable in ocrd-tool.json", default=None)
78
78
  @click.option('-m', '--mets', help="METS to process", default=DEFAULT_METS_BASENAME)
79
- @click.option('-w', '--working-dir', help="Working Directory")
79
+ @click.option('-U', '--mets-server-url', help='TCP host URI or UDS path of METS server', default=None)
80
+ @click.option('-d', '--working-dir', help="Working Directory")
80
81
  @click.option('-I', '--input-file-grp', help='File group(s) used as input.', default=None)
81
82
  @click.option('-O', '--output-file-grp', help='File group(s) used as output.', default=None)
82
- # repeat some other processor options for convenience (will be ignored here)
83
83
  @click.option('-g', '--page-id', help="ID(s) of the pages to process")
84
84
  @click.option('--overwrite', is_flag=True, default=False, help="Remove output pages/images if they already exist\n"
85
85
  "(with '--page-id', remove only those).\n"
@@ -126,9 +126,10 @@ def bashlib_input_files(ocrd_tool, executable, **kwargs):
126
126
  def executable(self):
127
127
  # needed for ocrd_tool lookup
128
128
  return executable
129
+ processor_class = FullBashlibProcessor
129
130
  else:
130
131
  # we have no true metadata file, so fill in just to make it work
131
- class FullBashlibProcessor(BashlibProcessor):
132
+ class UnknownBashlibProcessor(BashlibProcessor):
132
133
  @property
133
134
  def ocrd_tool(self):
134
135
  # needed to satisfy the validator
@@ -142,5 +143,6 @@ def bashlib_input_files(ocrd_tool, executable, **kwargs):
142
143
  def version(self):
143
144
  # needed to satisfy the validator and wrapper
144
145
  return '1.0'
146
+ processor_class = UnknownBashlibProcessor
145
147
 
146
- ocrd_cli_wrap_processor(FullBashlibProcessor, **kwargs)
148
+ ocrd_cli_wrap_processor(processor_class, **kwargs)
@@ -125,7 +125,7 @@ def ocrd_tool_tool_list_resources(ctx):
125
125
  @click.argument('res_name')
126
126
  @pass_ocrd_tool
127
127
  def ocrd_tool_tool_resolve_resource(ctx, res_name):
128
- ctx.processor(None).resolve_resource(res_name)
128
+ print(ctx.processor(None).resolve_resource(res_name))
129
129
 
130
130
  @ocrd_tool_tool.command('show-resource', help="Dump a tool's file resource")
131
131
  @click.argument('res_name')
@@ -102,16 +102,19 @@ def validate_page(page, **kwargs):
102
102
  @validate_cli.command('tasks')
103
103
  @click.option('--workspace', nargs=1, required=False, help='Workspace directory these tasks are to be run. If omitted, only validate syntax')
104
104
  @click.option('-M', '--mets-basename', nargs=1, default=DEFAULT_METS_BASENAME, help='Basename of the METS file, used in conjunction with --workspace')
105
+ @click.option('-U', '--mets-server-url', help='TCP host URI or UDS path of METS server')
105
106
  @click.option('--overwrite', is_flag=True, default=False, help='When checking against a concrete workspace, simulate overwriting output or page range.')
106
107
  @click.option('-g', '--page-id', help="ID(s) of the pages to process")
107
108
  @click.argument('tasks', nargs=-1, required=True)
108
- def validate_process(tasks, workspace, mets_basename, overwrite, page_id):
109
+ def validate_process(tasks, workspace, mets_basename, mets_server_url, overwrite, page_id):
109
110
  '''
110
111
  Validate a sequence of tasks passable to `ocrd process`
111
112
  '''
112
113
  if workspace:
113
- _inform_of_result(validate_tasks([ProcessorTask.parse(t) for t in tasks],
114
- Workspace(Resolver(), directory=workspace, mets_basename=mets_basename), page_id=page_id, overwrite=overwrite))
114
+ _inform_of_result(validate_tasks(
115
+ [ProcessorTask.parse(t) for t in tasks],
116
+ Workspace(Resolver(), directory=workspace, mets_basename=mets_basename, mets_server_url=mets_server_url),
117
+ page_id=page_id, overwrite=overwrite))
115
118
  else:
116
119
  for t in [ProcessorTask.parse(t) for t in tasks]:
117
120
  _inform_of_result(t.validate())
@@ -36,6 +36,17 @@ class WorkspaceCtx():
36
36
  = self.resolver.resolve_mets_arguments(directory, mets_url, mets_basename, mets_server_url)
37
37
  self.automatic_backup = automatic_backup
38
38
 
39
+ def workspace(self):
40
+ return Workspace(
41
+ self.resolver,
42
+ directory=self.directory,
43
+ mets_basename=self.mets_basename,
44
+ automatic_backup=self.automatic_backup,
45
+ mets_server_url=self.mets_server_url,
46
+ )
47
+ def backup_manager(self):
48
+ return WorkspaceBackupManager(self.workspace())
49
+
39
50
 
40
51
  pass_workspace = click.make_pass_decorator(WorkspaceCtx)
41
52
 
@@ -138,6 +149,7 @@ def workspace_clone(ctx, clobber_mets, download, file_grp, file_id, page_id, mim
138
149
  LOG.warning(DeprecationWarning("Use 'ocrd workspace --directory DIR clone' instead of argument 'WORKSPACE_DIR' ('%s')" % workspace_dir))
139
150
  ctx.directory = workspace_dir
140
151
 
152
+ assert not ctx.mets_server_url
141
153
  workspace = ctx.resolver.workspace_from_url(
142
154
  mets_url,
143
155
  dst_dir=ctx.directory,
@@ -173,10 +185,11 @@ def workspace_init(ctx, clobber_mets, directory):
173
185
  if directory:
174
186
  LOG.warning(DeprecationWarning("Use 'ocrd workspace --directory DIR init' instead of argument 'DIRECTORY' ('%s')" % directory))
175
187
  ctx.directory = directory
188
+ assert not ctx.mets_server_url
176
189
  workspace = ctx.resolver.workspace_from_nothing(
177
190
  directory=ctx.directory,
178
191
  mets_basename=ctx.mets_basename,
179
- clobber_mets=clobber_mets
192
+ clobber_mets=clobber_mets,
180
193
  )
181
194
  workspace.save_mets()
182
195
  print(workspace.directory)
@@ -200,13 +213,7 @@ def workspace_add_file(ctx, file_grp, file_id, mimetype, page_id, ignore, check_
200
213
  Add a file or http(s) URL FNAME to METS in a workspace.
201
214
  If FNAME is not an http(s) URL and is not a workspace-local existing file, try to copy to workspace.
202
215
  """
203
- workspace = Workspace(
204
- ctx.resolver,
205
- directory=ctx.directory,
206
- mets_basename=ctx.mets_basename,
207
- automatic_backup=ctx.automatic_backup,
208
- mets_server_url=ctx.mets_server_url,
209
- )
216
+ workspace = ctx.workspace()
210
217
 
211
218
  log = getLogger('ocrd.cli.workspace.add')
212
219
  if not mimetype:
@@ -313,13 +320,7 @@ def workspace_cli_bulk_add(ctx, regex, mimetype, page_id, file_id, url, local_fi
313
320
 
314
321
  """
315
322
  log = getLogger('ocrd.cli.workspace.bulk-add') # pylint: disable=redefined-outer-name
316
- workspace = Workspace(
317
- ctx.resolver,
318
- directory=ctx.directory,
319
- mets_basename=ctx.mets_basename,
320
- automatic_backup=ctx.automatic_backup,
321
- mets_server_url=ctx.mets_server_url,
322
- )
323
+ workspace = ctx.workspace()
323
324
 
324
325
  try:
325
326
  pat = re.compile(regex)
@@ -455,12 +456,7 @@ def workspace_find(ctx, file_grp, mimetype, page_id, file_id, output_field, incl
455
456
  output_field = [snake_to_camel.get(x, x) for x in output_field]
456
457
  modified_mets = False
457
458
  ret = []
458
- workspace = Workspace(
459
- ctx.resolver,
460
- directory=ctx.directory,
461
- mets_basename=ctx.mets_basename,
462
- mets_server_url=ctx.mets_server_url,
463
- )
459
+ workspace = ctx.workspace()
464
460
  with pushd_popd(workspace.directory):
465
461
  for f in workspace.find_files(
466
462
  file_id=file_id,
@@ -510,7 +506,7 @@ def workspace_remove_file(ctx, id, force, keep_file): # pylint: disable=redefin
510
506
  (If any ``ID`` starts with ``//``, then its remainder
511
507
  will be interpreted as a regular expression.)
512
508
  """
513
- workspace = Workspace(ctx.resolver, directory=ctx.directory, mets_basename=ctx.mets_basename, automatic_backup=ctx.automatic_backup)
509
+ workspace = ctx.workspace()
514
510
  for i in id:
515
511
  workspace.remove_file(i, force=force, keep_file=keep_file)
516
512
  workspace.save_mets()
@@ -528,7 +524,7 @@ def rename_group(ctx, old, new):
528
524
  """
529
525
  Rename fileGrp (USE attribute ``NEW`` to ``OLD``).
530
526
  """
531
- workspace = Workspace(ctx.resolver, directory=ctx.directory, mets_basename=ctx.mets_basename)
527
+ workspace = ctx.workspace()
532
528
  workspace.rename_file_group(old, new)
533
529
  workspace.save_mets()
534
530
 
@@ -549,7 +545,7 @@ def remove_group(ctx, group, recursive, force, keep_files):
549
545
  (If any ``GROUP`` starts with ``//``, then its remainder
550
546
  will be interpreted as a regular expression.)
551
547
  """
552
- workspace = Workspace(ctx.resolver, directory=ctx.directory, mets_basename=ctx.mets_basename)
548
+ workspace = ctx.workspace()
553
549
  for g in group:
554
550
  workspace.remove_file_group(g, recursive=recursive, force=force, keep_files=keep_files)
555
551
  workspace.save_mets()
@@ -571,7 +567,7 @@ def prune_files(ctx, file_grp, mimetype, page_id, file_id):
571
567
  (If any ``FILTER`` starts with ``//``, then its remainder
572
568
  will be interpreted as a regular expression.)
573
569
  """
574
- workspace = Workspace(ctx.resolver, directory=ctx.directory, mets_basename=ctx.mets_basename, automatic_backup=ctx.automatic_backup)
570
+ workspace = ctx.workspace()
575
571
  with pushd_popd(workspace.directory):
576
572
  for f in workspace.find_files(
577
573
  file_id=file_id,
@@ -608,8 +604,7 @@ def clean(ctx, dry_run, directories, path_glob):
608
604
  If no PATH_GLOB are specified, then all files and directories
609
605
  may match.
610
606
  """
611
- log = getLogger('ocrd.cli.workspace.clean')
612
- workspace = Workspace(ctx.resolver, directory=ctx.directory, mets_basename=ctx.mets_basename, automatic_backup=ctx.automatic_backup)
607
+ workspace = ctx.workspace()
613
608
  allowed_files = [normpath(f.local_filename) for f in workspace.find_files(local_only=True)]
614
609
  allowed_files.append(relpath(workspace.mets_target, start=workspace.directory))
615
610
  allowed_dirs = set(dirname(path) for path in allowed_files)
@@ -627,7 +622,7 @@ def clean(ctx, dry_run, directories, path_glob):
627
622
  if normpath(path) in allowed_files:
628
623
  continue
629
624
  if dry_run:
630
- log.info('unlink(%s)' % path)
625
+ ctx.log.info('unlink(%s)' % path)
631
626
  else:
632
627
  unlink(path)
633
628
  if not directories:
@@ -637,7 +632,7 @@ def clean(ctx, dry_run, directories, path_glob):
637
632
  if normpath(path) in allowed_dirs:
638
633
  continue
639
634
  if dry_run:
640
- log.info('rmdir(%s)' % path)
635
+ ctx.log.info('rmdir(%s)' % path)
641
636
  else:
642
637
  rmdir(path)
643
638
 
@@ -651,7 +646,7 @@ def list_groups(ctx):
651
646
  """
652
647
  List fileGrp USE attributes
653
648
  """
654
- workspace = Workspace(ctx.resolver, directory=ctx.directory, mets_basename=ctx.mets_basename)
649
+ workspace = ctx.workspace()
655
650
  print("\n".join(workspace.mets.file_groups))
656
651
 
657
652
  # ----------------------------------------------------------------------
@@ -677,7 +672,7 @@ def list_pages(ctx, output_field, output_format, chunk_number, chunk_index, page
677
672
  (If any ``FILTER`` starts with ``//``, then its remainder
678
673
  will be interpreted as a regular expression.)
679
674
  """
680
- workspace = Workspace(ctx.resolver, directory=ctx.directory, mets_basename=ctx.mets_basename)
675
+ workspace = ctx.workspace()
681
676
  find_kwargs = {}
682
677
  if page_id_range and 'ID' in output_field:
683
678
  find_kwargs['pageId'] = page_id_range
@@ -724,7 +719,7 @@ def get_id(ctx):
724
719
  """
725
720
  Get METS id if any
726
721
  """
727
- workspace = Workspace(ctx.resolver, directory=ctx.directory, mets_basename=ctx.mets_basename)
722
+ workspace = ctx.workspace()
728
723
  ID = workspace.mets.unique_identifier
729
724
  if ID:
730
725
  print(ID)
@@ -744,7 +739,7 @@ def set_id(ctx, id): # pylint: disable=redefined-builtin
744
739
 
745
740
  Otherwise will create a new <mods:identifier type="purl">{{ ID }}</mods:identifier>.
746
741
  """
747
- workspace = Workspace(ctx.resolver, directory=ctx.directory, mets_basename=ctx.mets_basename, automatic_backup=ctx.automatic_backup)
742
+ workspace = ctx.workspace()
748
743
  workspace.mets.unique_identifier = id
749
744
  workspace.save_mets()
750
745
 
@@ -767,7 +762,7 @@ def update_page(ctx, attr_value_pairs, order, orderlabel, contentids, page_id):
767
762
  if contentids:
768
763
  update_kwargs['CONTENTIDS'] = contentids
769
764
  try:
770
- workspace = Workspace(ctx.resolver, directory=ctx.directory, mets_basename=ctx.mets_basename, automatic_backup=ctx.automatic_backup)
765
+ workspace = ctx.workspace()
771
766
  workspace.mets.update_physical_page_attributes(page_id, **update_kwargs)
772
767
  workspace.save_mets()
773
768
  except Exception as err:
@@ -805,7 +800,7 @@ def merge(ctx, overwrite, force, copy_files, filegrp_mapping, fileid_mapping, pa
805
800
  mets_path = Path(mets_path)
806
801
  if filegrp_mapping:
807
802
  filegrp_mapping = loads(filegrp_mapping)
808
- workspace = Workspace(ctx.resolver, directory=ctx.directory, mets_basename=ctx.mets_basename, automatic_backup=ctx.automatic_backup)
803
+ workspace = ctx.workspace()
809
804
  other_workspace = Workspace(ctx.resolver, directory=str(mets_path.parent), mets_basename=str(mets_path.name))
810
805
  workspace.merge(
811
806
  other_workspace,
@@ -829,11 +824,12 @@ def merge(ctx, overwrite, force, copy_files, filegrp_mapping, fileid_mapping, pa
829
824
  # ----------------------------------------------------------------------
830
825
 
831
826
  @workspace_cli.group('backup')
832
- @click.pass_context
827
+ @pass_workspace
833
828
  def workspace_backup_cli(ctx): # pylint: disable=unused-argument
834
829
  """
835
830
  Backing and restoring workspaces - dev edition
836
831
  """
832
+ assert not ctx.mets_server_url, "Workspace backups currently not interoperable with METS Server"
837
833
 
838
834
  @workspace_backup_cli.command('add')
839
835
  @pass_workspace
@@ -841,7 +837,7 @@ def workspace_backup_add(ctx):
841
837
  """
842
838
  Create a new backup
843
839
  """
844
- backup_manager = WorkspaceBackupManager(Workspace(ctx.resolver, directory=ctx.directory, mets_basename=ctx.mets_basename, automatic_backup=ctx.automatic_backup))
840
+ backup_manager = ctx.backup_manager()
845
841
  backup_manager.add()
846
842
 
847
843
  @workspace_backup_cli.command('list')
@@ -850,7 +846,7 @@ def workspace_backup_list(ctx):
850
846
  """
851
847
  List backups
852
848
  """
853
- backup_manager = WorkspaceBackupManager(Workspace(ctx.resolver, directory=ctx.directory, mets_basename=ctx.mets_basename, automatic_backup=ctx.automatic_backup))
849
+ backup_manager = ctx.backup_manager()
854
850
  for b in backup_manager.list():
855
851
  print(b)
856
852
 
@@ -862,7 +858,7 @@ def workspace_backup_restore(ctx, choose_first, bak):
862
858
  """
863
859
  Restore backup BAK
864
860
  """
865
- backup_manager = WorkspaceBackupManager(Workspace(ctx.resolver, directory=ctx.directory, mets_basename=ctx.mets_basename, automatic_backup=ctx.automatic_backup))
861
+ backup_manager = ctx.backup_manager()
866
862
  backup_manager.restore(bak, choose_first)
867
863
 
868
864
  @workspace_backup_cli.command('undo')
@@ -871,7 +867,7 @@ def workspace_backup_undo(ctx):
871
867
  """
872
868
  Restore the last backup
873
869
  """
874
- backup_manager = WorkspaceBackupManager(Workspace(ctx.resolver, directory=ctx.directory, mets_basename=ctx.mets_basename, automatic_backup=ctx.automatic_backup))
870
+ backup_manager = ctx.backup_manager()
875
871
  backup_manager.undo()
876
872
 
877
873
 
@@ -888,15 +884,24 @@ def workspace_serve_cli(ctx): # pylint: disable=unused-argument
888
884
  @workspace_serve_cli.command('stop')
889
885
  @pass_workspace
890
886
  def workspace_serve_stop(ctx): # pylint: disable=unused-argument
891
- """Stop the METS server"""
892
- workspace = Workspace(
893
- ctx.resolver,
894
- directory=ctx.directory,
895
- mets_basename=ctx.mets_basename,
896
- mets_server_url=ctx.mets_server_url,
897
- )
887
+ """Stop the METS server (saving changes to disk)"""
888
+ workspace = ctx.workspace()
898
889
  workspace.mets.stop()
899
890
 
891
+ @workspace_serve_cli.command('reload')
892
+ @pass_workspace
893
+ def workspace_serve_reload(ctx): # pylint: disable=unused-argument
894
+ """Reload the METS server from disk"""
895
+ workspace = ctx.workspace()
896
+ workspace.mets.reload()
897
+
898
+ @workspace_serve_cli.command('save')
899
+ @pass_workspace
900
+ def workspace_serve_save(ctx): # pylint: disable=unused-argument
901
+ """Save the METS changes to disk"""
902
+ workspace = ctx.workspace()
903
+ workspace.mets.save()
904
+
900
905
  @workspace_serve_cli.command('start')
901
906
  @pass_workspace
902
907
  def workspace_serve_start(ctx): # pylint: disable=unused-argument
@@ -43,6 +43,7 @@ def ocrd_cli_options(f):
43
43
  option('--address', type=ServerAddressParamType()),
44
44
  option('--queue', type=QueueServerParamType()),
45
45
  option('--database', type=DatabaseParamType()),
46
+ option('-R', '--resolve-resource'),
46
47
  option('-C', '--show-resource'),
47
48
  option('-L', '--list-resources', is_flag=True, default=False),
48
49
  option('-J', '--dump-json', is_flag=True, default=False),
@@ -27,8 +27,8 @@ ocrd__log () {
27
27
  ## Ensure minimum version
28
28
  # ht https://stackoverflow.com/posts/4025065
29
29
  ocrd__minversion () {
30
- local minversion_raw="$1"
31
30
  set -e
31
+ local minversion_raw="$1"
32
32
  local version_raw=$(ocrd --version|sed 's/ocrd, version //')
33
33
  local version_mmp=$(echo "$version_raw" | grep -Eo '([0-9]+\.?){3}')
34
34
  local version_prerelease_suffix="${version_raw#$version_mmp}"
@@ -123,6 +123,7 @@ ocrd__usage () {
123
123
  ## declare -A ocrd__argv=()
124
124
  ## ```
125
125
  ocrd__parse_argv () {
126
+ set -e
126
127
 
127
128
  # if [[ -n "$ZSH_VERSION" ]];then
128
129
  # print -r -- ${+ocrd__argv} ${(t)ocrd__argv}
@@ -135,11 +136,16 @@ ocrd__parse_argv () {
135
136
  ocrd__raise "Must set \$params (declare -A params)"
136
137
  fi
137
138
 
139
+ if ! declare -p "params_json" >/dev/null 2>/dev/null ;then
140
+ ocrd__raise "Must set \$params_json (declare params_json)"
141
+ fi
142
+
138
143
  if [[ $# = 0 ]];then
139
144
  ocrd__usage
140
145
  exit 1
141
146
  fi
142
147
 
148
+ ocrd__argv[debug]=false
143
149
  ocrd__argv[overwrite]=false
144
150
  ocrd__argv[profile]=false
145
151
  ocrd__argv[profile_file]=
@@ -170,6 +176,7 @@ ocrd__parse_argv () {
170
176
  -w|--working-dir) ocrd__argv[working_dir]=$(realpath "$2") ; shift ;;
171
177
  -m|--mets) ocrd__argv[mets_file]=$(realpath "$2") ; shift ;;
172
178
  -U|--mets-server-url) ocrd__argv[mets_server_url]="$2" ; shift ;;
179
+ --debug) ocrd__argv[debug]=true ;;
173
180
  --overwrite) ocrd__argv[overwrite]=true ;;
174
181
  --profile) ocrd__argv[profile]=true ;;
175
182
  --profile-file) ocrd__argv[profile_file]=$(realpath "$2") ; shift ;;
@@ -242,17 +249,6 @@ ocrd__parse_argv () {
242
249
  trap showtime DEBUG
243
250
  fi
244
251
 
245
- # check fileGrps
246
- local _valopts=( --workspace "${ocrd__argv[working_dir]}" --mets-basename "$(basename ${ocrd__argv[mets_file]})" )
247
- if [[ ${ocrd__argv[overwrite]} = true ]]; then
248
- _valopts+=( --overwrite )
249
- fi
250
- if [[ -n "${ocrd__argv[page_id]:-}" ]]; then
251
- _valopts+=( --page-id "${ocrd__argv[page_id]}" )
252
- fi
253
- _valopts+=( "${OCRD_TOOL_NAME#ocrd-} -I ${ocrd__argv[input_file_grp]} -O ${ocrd__argv[output_file_grp]} ${__parameters[*]@Q} ${__parameter_overrides[*]@Q}" )
254
- ocrd validate tasks "${_valopts[@]}" || exit $?
255
-
256
252
  # check parameters
257
253
  local params_parsed retval
258
254
  params_parsed="$(ocrd ocrd-tool "$OCRD_TOOL_JSON" tool $OCRD_TOOL_NAME parse-params "${__parameters[@]}" "${__parameter_overrides[@]}")" || {
@@ -261,10 +257,12 @@ ocrd__parse_argv () {
261
257
  $params_parsed"
262
258
  }
263
259
  eval "$params_parsed"
260
+ params_json="$(ocrd ocrd-tool "$OCRD_TOOL_JSON" tool $OCRD_TOOL_NAME parse-params --json "${__parameters[@]}" "${__parameter_overrides[@]}")"
264
261
 
265
262
  }
266
263
 
267
264
  ocrd__wrap () {
265
+ set -e
268
266
 
269
267
  declare -gx OCRD_TOOL_JSON="$1"
270
268
  declare -gx OCRD_TOOL_NAME="$2"
@@ -272,6 +270,7 @@ ocrd__wrap () {
272
270
  shift
273
271
  declare -Agx params
274
272
  params=()
273
+ declare -g params_json
275
274
  declare -Agx ocrd__argv
276
275
  ocrd__argv=()
277
276
 
@@ -293,22 +292,26 @@ ocrd__wrap () {
293
292
 
294
293
  ocrd__parse_argv "$@"
295
294
 
296
- i=0
297
- declare -ag ocrd__files=()
298
- while read line; do
299
- eval declare -Ag "ocrd__file$i=( $line )"
300
- eval "ocrd__files[$i]=ocrd__file$i"
301
- let ++i
302
- done < <(ocrd bashlib input-files \
295
+ declare -ag ocrd__files
296
+ IFS=$'\n'
297
+ ocrd__files=( $(ocrd bashlib input-files \
303
298
  --ocrd-tool $OCRD_TOOL_JSON \
304
299
  --executable $OCRD_TOOL_NAME \
300
+ $(if [[ ${ocrd__argv[debug]} = true ]]; then echo --debug; fi) \
301
+ $(if [[ ${ocrd__argv[overwrite]} = true ]]; then echo --overwrite; fi) \
305
302
  -m "${ocrd__argv[mets_file]}" \
303
+ -d "${ocrd__argv[working_dir]}" \
304
+ ${ocrd__argv[mets_server_url]:+-U} ${ocrd__argv[mets_server_url]:-} \
305
+ -p "$params_json" \
306
306
  -I "${ocrd__argv[input_file_grp]}" \
307
307
  -O "${ocrd__argv[output_file_grp]}" \
308
- ${ocrd__argv[page_id]:+-g} ${ocrd__argv[page_id]:-})
308
+ ${ocrd__argv[page_id]:+-g} ${ocrd__argv[page_id]:-}) )
309
+ IFS=$' \t\n'
309
310
  }
310
311
 
311
312
  ## usage: pageId=$(ocrd__input_file 3 pageId)
312
313
  ocrd__input_file() {
313
- eval echo "\${${ocrd__files[$1]}[$2]}"
314
+ declare -A input_file
315
+ eval input_file=( "${ocrd__files[$1]}" )
316
+ eval echo "${input_file[$2]}"
314
317
  }
@@ -88,6 +88,14 @@ class OcrdFileGroupListModel(BaseModel):
88
88
  return OcrdFileGroupListModel(file_groups=file_groups)
89
89
 
90
90
 
91
+ class OcrdPageListModel(BaseModel):
92
+ physical_pages: List[str] = Field()
93
+
94
+ @staticmethod
95
+ def create(physical_pages: List[str]):
96
+ return OcrdPageListModel(physical_pages=physical_pages)
97
+
98
+
91
99
  class OcrdAgentListModel(BaseModel):
92
100
  agents: List[OcrdAgentModel] = Field()
93
101
 
@@ -210,6 +218,17 @@ class ClientSideOcrdMets:
210
218
  ).json()["text"]
211
219
  return self.ws_dir_path
212
220
 
221
+ @property
222
+ def physical_pages(self) -> List[str]:
223
+ if not self.multiplexing_mode:
224
+ return self.session.request("GET", f"{self.url}/physical_pages").json()["physical_pages"]
225
+ else:
226
+ return self.session.request(
227
+ "POST",
228
+ self.url,
229
+ json=MpxReq.physical_pages(self.ws_dir_path)
230
+ ).json()["physical_pages"]
231
+
213
232
  @property
214
233
  def file_groups(self):
215
234
  if not self.multiplexing_mode:
@@ -284,15 +303,17 @@ class ClientSideOcrdMets:
284
303
  file_id=ID, page_id=pageId,
285
304
  mimetype=mimetype, url=url, local_filename=local_filename
286
305
  )
306
+ # add force+ignore
307
+ kwargs = {**kwargs, **data.dict()}
287
308
 
288
309
  if not self.multiplexing_mode:
289
- r = self.session.request("POST", f"{self.url}/file", data=data.dict())
290
- if not r:
291
- raise RuntimeError("Add file failed. Please check provided parameters")
310
+ r = self.session.request("POST", f"{self.url}/file", data=kwargs)
311
+ if not r.ok:
312
+ raise RuntimeError(f"Failed to add file ({str(data)}): {r.json()}")
292
313
  else:
293
- r = self.session.request("POST", self.url, json=MpxReq.add_file(self.ws_dir_path, data.dict()))
294
- if "error" in r:
295
- raise RuntimeError(f"Add file failed: Msg: {r['error']}")
314
+ r = self.session.request("POST", self.url, json=MpxReq.add_file(self.ws_dir_path, kwargs))
315
+ if not r.ok:
316
+ raise RuntimeError(f"Failed to add file ({str(data)}): {r.json()[errors]}")
296
317
 
297
318
  return ClientSideOcrdFile(
298
319
  None, fileGrp=file_grp,
@@ -347,6 +368,11 @@ class MpxReq:
347
368
  return MpxReq.__args_wrapper(
348
369
  ws_dir_path, method_type="GET", response_type="text", request_url="workspace_path", request_data={})
349
370
 
371
+ @staticmethod
372
+ def physical_pages(ws_dir_path: str) -> Dict:
373
+ return MpxReq.__args_wrapper(
374
+ ws_dir_path, method_type="GET", response_type="dict", request_url="physical_pages", request_data={})
375
+
350
376
  @staticmethod
351
377
  def file_groups(ws_dir_path: str) -> Dict:
352
378
  return MpxReq.__args_wrapper(
@@ -466,6 +492,10 @@ class OcrdMetsServer:
466
492
  async def workspace_path():
467
493
  return Response(content=workspace.directory, media_type="text/plain")
468
494
 
495
+ @app.get(path='/physical_pages', response_model=OcrdPageListModel)
496
+ async def physical_pages():
497
+ return {'physical_pages': workspace.mets.physical_pages}
498
+
469
499
  @app.get(path='/file_groups', response_model=OcrdFileGroupListModel)
470
500
  async def file_groups():
471
501
  return {'file_groups': workspace.mets.file_groups}
@@ -505,7 +535,8 @@ class OcrdMetsServer:
505
535
  page_id: Optional[str] = Form(),
506
536
  mimetype: str = Form(),
507
537
  url: Optional[str] = Form(None),
508
- local_filename: Optional[str] = Form(None)
538
+ local_filename: Optional[str] = Form(None),
539
+ force: bool = Form(False),
509
540
  ):
510
541
  """
511
542
  Add a file
@@ -517,7 +548,7 @@ class OcrdMetsServer:
517
548
  )
518
549
  # Add to workspace
519
550
  kwargs = file_resource.dict()
520
- workspace.add_file(**kwargs)
551
+ workspace.add_file(**kwargs, force=force)
521
552
  return file_resource
522
553
 
523
554
  # ------------- #