generated from usnistgov/opensource-repo
-
Notifications
You must be signed in to change notification settings - Fork 3
/
loosen_nerdm.py
executable file
·168 lines (136 loc) · 5.83 KB
/
loosen_nerdm.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
#! /usr/bin/env python3
#
import os, sys, json, argparse, traceback, re
from pathlib import Path
from collections import OrderedDict
from collections.abc import Mapping
description="""create copies of NERDm schemas with loosened requirements appropriate for the
metadata drafting process (i.e. within MIDAS)"""
epilog=""
def_progname = "loosen_nerdm"
def define_options(progname, parser=None):
"""
define command-line arguments
"""
if not parser:
parser = argparse.ArgumentParser(progname, None, description, epilog)
parser.add_argument("srcdir", metavar="SRCDIR", type=str,
help="the directory containing the NERDm schemas")
parser.add_argument("destdir", metavar="DESTDIR", type=str,
help="the directory write loosened schemas to")
parser.add_argument("-D", "--no-dedocument", dest="dedoc", action="store_false", default=True,
help="do not remove documentation from source schemas")
parser.add_argument("-J", "--assume-post2020", dest="post2020", action="store_true", default=False,
help="assume schemas are compliant with a post-2020 JSON Schema specification "+
"(and uses $defs)")
parser.add_argument("-m", "--make-dest-dir", dest="mkdest", action="store_true", default=False,
help="create the destination directory if it does not exist")
return parser
def set_options(progname, args):
"""
define and parse the command-line options
"""
return define_options(progname).parse_args(args)
directives_by_file = {
"nerdm-schema.json": {
"derequire": [ "Resource", "Organization" ]
},
"nerdm-pub-schema.json": {
"derequire": [ "PublicDataResource", "Person" ]
},
"nerdm-rls-schema.json": {
"derequire": [ "ReleasedResource" ]
}
}
def find_nistoar_code():
execdir = Path(__file__).resolve().parents[0]
basedir = execdir.parents[0]
mdpydir = basedir / "metadata" / "python"
return mdpydir
try:
import nistoar.nerdm.utils as nerdm_utils
except ImportError:
sys.path.insert(0, str(find_nistoar_code()))
import nistoar.nerdm.utils as nerdm_utils
def loosen_schema(schema: Mapping, directives: Mapping, opts=None):
"""
apply the given loosening directive to the given JSON Schema. The directives is a
dictionary describes what to do with the following properties (the directives) supported:
``derequire``
a list of type definitions within the schema from which the required property
should be removed (via :py:func:`~nistoar.nerdm.utils.unrequire_props_in`). Each
type name listed will be assumed to be an item under the "definitions" node in the
schema this directive is applied to.
``dedocument``
a boolean indicating whether the documentation annotations should be removed from
the schema. If not set, the default is determined by opts.dedoc if opts is given or
True, otherwise.
:param dict schema: the schema document as a JSON Schema schema dictionary
:param dict directives: the dictionary of directives to apply
:param opt: an options object (containing scripts command-line options)
"""
if opts:
if not opts.dedoc:
directives["dedocument"] = False
directives["post2020"] = opts.post2020
nerdm_utils.loosen_schema(schema, directives)
def process_nerdm_schemas(srcdir, destdir, opts=None):
"""
process all NERDm schemas (core and extensions) found in the source directory
and write the modified schemas to the output directory
"""
if not os.path.isdir(srcdir):
raise RuntimeException(f"{srcdir}: schema source directory does not exist as directory")
if not os.path.exists(destdir):
if opts and opts.mkdest:
os.makedirs(destdir)
else:
raise FileNotFoundError(destdir)
if not os.path.isdir(srcdir):
raise RuntimeException(f"{destdir}: schema destination is not a directory")
nerdfilere = re.compile(r"^nerdm-([a-zA-Z][^\-]*\-)?schema.json$")
schfiles = [f for f in os.listdir(srcdir) if nerdfilere.match(f)]
failed={}
for f in schfiles:
try:
with open(os.path.join(srcdir, f)) as fd:
schema = json.load(fd, object_pairs_hook=OrderedDict)
except IOError as ex:
failed[f] = f"Trouble reading schema file: {str(ex)}"
continue
directives = directives_by_file.get(f, {})
try:
loosen_schema(schema, directives, opts)
except Exception as ex:
failed[f] = f"Trouble processing schema file: {str(ex)}"
continue
with open(os.path.join(destdir, f), 'w') as fd:
json.dump(schema, fd, indent=2)
fd.write("\n")
return failed
def main(progname=None, args=[]):
global def_progname;
if not progname:
progname = def_progname
else:
progname = os.path.basename(progname)
if progname.endswith(".py"):
progname = progname[:-1*len(".py")]
opts = set_options(progname, args)
failed = process_nerdm_schemas(opts.srcdir, opts.destdir, opts) # may raise exceptions
if failed:
print(f"{progname}: WARNING: failed to process the following schemas:", file=sys.stderr)
for f, err in failed:
print(f" {f}: {err}", file=sys.stderr)
return 3
return 0
if __name__ == "__main__":
try:
sys.exit(main(sys.argv[0], sys.argv[1:]))
except RuntimeError as ex:
print(f"{progname}: {str(ex)}", file=sys.stderr)
sys.exit(1)
except Exception as ex:
print("Unexpected error: "+str(ex), file=sys.stderr)
traceback.print_tb(sys.exc_info()[2])
sys.exit(4)