-
Notifications
You must be signed in to change notification settings - Fork 10
/
fleur_schema.py
235 lines (185 loc) · 9.12 KB
/
fleur_schema.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
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
"""
CLI commands for interacting with the fleur schemas in the masci-tools repository
"""
from .root import cli
import click
import masci_tools
from masci_tools.cmdline.utils import echo
from masci_tools.util.xml.common_functions import clear_xml, validate_xml
from masci_tools.util.xml.converters import convert_str_version_number
from masci_tools.io.io_fleurxml import load_inpxml, load_outxml
from masci_tools.io.parsers.fleur import inpxml_parser, outxml_parser
from masci_tools.io.parsers.fleur_schema import InputSchemaDict, OutputSchemaDict, list_available_versions
from pathlib import Path
import os
import sys
import shutil
import tempfile
import tabulate
from lxml import etree
GITLAB_URL = 'https://iffgit.fz-juelich.de'
try:
import gitlab
except ImportError:
gitlab = None
@cli.group('fleur-schema')
def fleur_schema():
"""Commands related to the Fleur XML Schemas"""
@fleur_schema.command('add')
@click.argument('schema-file', type=click.Path(path_type=Path, resolve_path=True))
@click.option('--overwrite', is_flag=True, help='Overwrite any existing schema-file')
@click.option('--branch',
type=str,
help='If the file does not exist the branch can be specified in the fleur git',
default='develop')
@click.option('--api-key', type=str, help='API key for access to the Iff Gitlab instance', default='')
@click.option('--test-xml-file',
type=click.Path(exists=True, path_type=Path, resolve_path=True),
default=None,
help='Example xmlfile for this schema version to test the file parser against')
@click.option('--from-git', is_flag=True, help='Add the schema from the fleur git repository')
def add_fleur_schema(schema_file, test_xml_file, overwrite, branch, api_key, from_git):
"""
Adds a new xml schema file to the folder in
`masci_tools/io/parsers/fleur_schema`
corresponding to its version number
"""
PACKAGE_ROOT = Path(masci_tools.__file__).parent.resolve()
if not isinstance(schema_file, Path):
if isinstance(schema_file, bytes):
schema_file = os.fsdecode(schema_file)
schema_file = Path(schema_file)
file_name = schema_file.name
if file_name not in ('FleurInputSchema.xsd', 'FleurOutputSchema.xsd'):
echo.echo_critical(
"The Fleur file schema has to be named either 'FleurInputSchema.xsd' or 'FleurOutputSchema.xsd'")
tmp_dir = None
if not schema_file.is_file() or from_git:
if not schema_file.is_file():
echo.echo_warning(f'{schema_file} does not exist')
if not from_git and not click.confirm(f'Do you want to download from the fleur git ({branch})'):
echo.echo_critical('Cannot add Schema file')
if gitlab is None:
echo.echo_critical(
'Cannot download Schema file. Please install python-gitlab or the cmdline-extras requirements')
api_key = api_key or os.getenv('IFFGIT_APIKEY', '')
gl = gitlab.Gitlab(GITLAB_URL, private_token=api_key)
try:
gl.auth()
except gitlab.exceptions.GitlabAuthenticationError:
echo.echo_critical('I am not authorized to access the fleur repository.\n'
'Please set the environment variable IFFGIT_APIKEY to access the gitlab instance')
groups = gl.groups.list(search='fleur')
for g in groups:
if g.name == 'fleur':
repo_id = [repo.id for repo in g.projects.list(all=True) if repo.name == 'fleur'][0]
project = gl.projects.get(repo_id)
break
echo.echo_info(f'Downloading {file_name} from branch {branch} to {schema_file}')
tmp_dir = tempfile.mkdtemp()
schema_file = Path(tmp_dir) / file_name
with open(schema_file, 'wb') as f:
project.files.raw(file_path=f'io/xml/{file_name}', ref=branch, streamed=True, action=f.write)
echo.echo_success('Download successful')
xmlschema = etree.parse(schema_file)
xmlschema, _ = clear_xml(xmlschema)
namespaces = {'xsd': 'http://www.w3.org/2001/XMLSchema'}
schema_version = xmlschema.xpath('/xsd:schema/@version', namespaces=namespaces)[0]
schema_folder = Path(PACKAGE_ROOT) / Path(f'io/parsers/fleur_schema/{schema_version}')
if schema_file.name == 'FleurInputSchema.xsd':
destination = schema_folder / 'FleurInputSchema.xsd'
input_schema = True
if destination.is_file() and not overwrite:
echo.echo_critical(
f'Input Schema for version {schema_version} already exists. Use overwrite=True to replace the Schema')
echo.echo_info(f"Copying Input Schema for version '{schema_version}' to: '{destination}'")
elif schema_file.name == 'FleurOutputSchema.xsd':
input_schema = False
destination = schema_folder / 'FleurOutputSchema.xsd'
if destination.is_file() and not overwrite:
echo.echo_critical(
f'Output Schema for version {schema_version} already exists. Use overwrite=True to replace the Schema')
echo.echo_info(f"Copying Output Schema for version '{schema_version}' to: '{destination}'")
else:
echo.echo_critical("Fleur file schema has to be named 'FleurInputSchema.xsd' or 'FleurOutputSchema.xsd'")
os.makedirs(schema_folder, exist_ok=True)
shutil.copy(schema_file, destination)
echo.echo_success('Copied Schema file to masci-tools repository')
if tmp_dir is not None:
shutil.rmtree(tmp_dir)
#Make sure that construction of SchemaDicts works for this schema
if input_schema:
schema_dict = InputSchemaDict.fromVersion(schema_version, no_cache=True)
else:
schema_dict = OutputSchemaDict.fromVersion(schema_version, no_cache=True)
echo.echo_success('Created Schema dictionary for the given schema file')
if test_xml_file is not None:
echo.echo_info(f'Testing Schema for file: {test_xml_file}')
if input_schema:
_, schema_dict = load_inpxml(test_xml_file)
if schema_dict['inp_version'] != schema_version:
echo.echo_error(
f"Test file ({schema_dict['inp_version']}) does not correspond to input version of inserted schema ({schema_version})"
)
sys.exit(1)
parser_info = {}
parser_dict = inpxml_parser(test_xml_file, parser_info_out=parser_info)
else:
xmltree, schema_dict = load_outxml(test_xml_file)
if schema_dict['out_version'] != schema_version:
echo.echo_error(
f"Test file ({schema_dict['out_version']}) does not correspond to output version of inserted schema ({schema_version})"
)
sys.exit(1)
parser_info = {}
parser_dict = outxml_parser(test_xml_file, parser_info_out=parser_info)
echo.echo_info('Parser output:')
echo.echo_dictionary(parser_dict)
echo.echo_info('Parser warnings/information:')
echo.echo_dictionary(parser_info)
echo.echo_success(f'Parser finished for: {test_xml_file}')
@fleur_schema.command('validate-input')
@click.argument('xml-file', type=click.Path(exists=True))
def validate_inpxmlfile(xml_file):
"""
Validate the given inp.xml file against the Fleur schema stored for the
version of the input
"""
xml_file = os.path.abspath(xml_file)
xmltree, schema_dict = load_inpxml(xml_file)
try:
validate_xml(xmltree, schema_dict.xmlschema, error_header='Input file does not validate against the schema')
except etree.DocumentInvalid as err:
echo.echo_error(str(err))
else:
echo.echo_success(f"{xml_file} validates against the schema for version {schema_dict['inp_version']}")
@fleur_schema.command('validate-output')
@click.argument('xml-file', type=click.Path(exists=True))
def validate_outxmlfile(xml_file):
"""
Validate the given out.xml file against the Fleur schema stored for the
version of the output
"""
xml_file = os.path.abspath(xml_file)
xmltree, schema_dict = load_outxml(xml_file)
try:
validate_xml(xmltree, schema_dict.xmlschema, error_header='Output file does not validate against the schema')
except etree.DocumentInvalid as err:
echo.echo_error(str(err))
else:
echo.echo_success(f"{xml_file} validates against the schema for version {schema_dict['out_version']}")
@fleur_schema.command('list')
def list_available_schemas():
"""
Show the available fleur schemas
"""
input_versions = list_available_versions(output_schema=False)
output_versions = list_available_versions(output_schema=True)
all_versions = set(input_versions) | set(output_versions)
all_versions = sorted(all_versions, key=convert_str_version_number)
input_versions = [version in input_versions for version in all_versions]
output_versions = [version in output_versions for version in all_versions]
echo.echo_info('The following Schemas were found')
echo.echo(
tabulate.tabulate(list(zip(all_versions, input_versions, output_versions)),
headers=['Version', 'Input Schema available', 'Output Schema available']))