1
1
import sys
2
2
from pydantic import Field , field_validator , model_validator
3
- from typing import Tuple , Union , Optional
3
+ from typing import Tuple , Any , Optional
4
4
import ifcopenshell
5
5
from ifcopenshell import validate , SchemaError , simple_spf
6
6
import re
12
12
import yaml
13
13
import os
14
14
from config import ConfiguredBaseModel
15
- from mvd_parser import parse_mvd
16
15
17
16
18
17
def ifcopenshell_pre_validation (file ):
@@ -80,8 +79,7 @@ def validate_and_split_originating_system(attributes):
80
79
81
80
82
81
class HeaderStructure (ConfiguredBaseModel ):
83
- file : Union [ifcopenshell .file , simple_spf .file ]
84
- purepythonparser : bool = False
82
+ file : Optional [simple_spf .file ] = None
85
83
validation_errors : list = Field (default_factory = list )
86
84
87
85
description : Optional [Tuple [str , ...]] = Field (default = None )
@@ -98,20 +96,19 @@ class HeaderStructure(ConfiguredBaseModel):
98
96
application_name : Optional [str ] = Field (default = None )
99
97
schema_identifier : Optional [str ] = Field (default = None )
100
98
version : Optional [str ] = Field (default = None )
101
- mvd : Optional [str ] = Field (default = None )
99
+ mvd : Optional [Any ] = Field (default = None ) # Expected: ifcopenshell.util.mvd_info.AutoCommitList
100
+ comments : Optional [Any ] = Field (default = None ) # Expected: ifcopenshell.util.mvd_info.AutoCommitList
102
101
103
102
104
103
@model_validator (mode = 'before' )
105
104
def populate_header (cls , values ):
106
105
if (file := values .get ('file' )):
107
106
108
- purepythonparser = values .get ('purepythonparser' )
109
107
header = file .header
110
108
file_description = header .file_description
111
109
file_name = header .file_name
112
110
113
111
fields = [
114
- (file_description , 'description' , 0 ),
115
112
(file_description , 'implementation_level' , 1 ),
116
113
(file_name , 'name' , 0 ),
117
114
(file_name , 'time_stamp' , 1 ),
@@ -122,41 +119,35 @@ def populate_header(cls, values):
122
119
(file_name , 'authorization' , 6 )
123
120
]
124
121
125
- attributes = {field : getattr (obj , field ) if not purepythonparser else obj [index ]
126
- for obj , field , index in fields
127
- }
122
+ attributes = {field : getattr (obj , field ) for obj , field , index in fields }
128
123
129
124
attributes ['validation_errors' ] = []
130
- attributes ['mvd' ] = ''
125
+ attributes ['mvd' ] = file .mvd .view_definitions
126
+ attributes ['comments' ] = file .mvd .comments
131
127
attributes ['schema_identifier' ] = ''
132
128
133
129
attributes = validate_and_split_originating_system (attributes )
134
130
135
- errors_from_pre_validation = ifcopenshell_pre_validation (file ) if not purepythonparser else []
136
- for error in errors_from_pre_validation :
137
- attributes ['validation_errors' ].append (error )
138
- attributes [error ] = None
131
+ if file .mvd .description :
132
+ errors_from_pre_validation = ifcopenshell_pre_validation (file )
133
+ for error in errors_from_pre_validation :
134
+ attributes ['validation_errors' ].append (error )
135
+ attributes [error ] = None
139
136
140
- values .update (attributes )
137
+ values .update (attributes )
141
138
142
139
return values
143
140
144
-
145
- @field_validator ('description ' )
141
+
142
+ @field_validator ('comments' , mode = 'after ' )
146
143
def validate_description (cls , v , values ):
147
144
"""
148
145
https://github.com/buildingSMART/IFC4.x-IF/tree/header-policy/docs/IFC-file-header#description
149
146
For grammar refer to https://standards.buildingsmart.org/documents/Implementation/ImplementationGuide_IFCHeaderData_Version_1.0.2.pdf
150
147
"""
151
- if v :
152
- header_description_text = ' ' .join (v )
153
- parsed_description = parse_mvd (header_description_text )
154
- view_definitions = parsed_description .mvd
155
- values .data ['mvd' ] = view_definitions
156
-
157
-
148
+ if v :
158
149
# comments is a free textfield, but constrainted to 256 characters
159
- if len (parsed_description . comments ) > 256 :
150
+ if len (v ) > 256 :
160
151
values .data .get ('validation_errors' ).append (values .field_name )
161
152
return v if type (v ) == tuple else v
162
153
@@ -167,30 +158,30 @@ def validate_description(cls, v, values):
167
158
@field_validator ('mvd' , mode = 'after' )
168
159
def validate_and_set_mvd (cls , v , values ):
169
160
"""
161
+ https://github.com/buildingSMART/IFC4.x-IF/tree/header-policy/docs/IFC-file-header#description
162
+ For grammar refer to https://standards.buildingsmart.org/documents/Implementation/ImplementationGuide_IFCHeaderData_Version_1.0.2.pdf
170
163
This function runs after the other fields. It validates the mvd based on the grammar done in
171
164
the 'description' field. The function checks the constraints on the mvds and returns
172
165
the comma separated list into a single string.
173
166
"""
174
- view_definitions = values .data .get ('mvd' )
175
- if not view_definitions :
167
+ if not v :
176
168
values .data .get ('validation_errors' ).append ('description' )
177
169
return v
178
170
179
171
allowed_descriptions = yaml .safe_load (open (os .path .join (os .path .dirname (__file__ ), 'valid_descriptions.yaml' )))
180
172
schema_identifier = values .data .get ('file' ).schema_identifier
181
173
182
- view_definitions_set = {view for view in view_definitions }
174
+ view_definitions_set = {view for view in v }
183
175
184
176
if any ("AddOnView" in view for view in view_definitions_set ):
185
177
if not any ("CoordinationView" in view for view in view_definitions_set ):
186
178
values .data .get ('validation_errors' ).append ('description' ) # AddOnView MVD without CoordinationView
187
179
188
- for mvd in view_definitions :
180
+ for mvd in v :
189
181
if mvd not in allowed_descriptions .get (schema_identifier , [False ]) and not 'AddOnView' in mvd :
190
182
values .data .get ('validation_errors' ).append ('description' )
191
183
192
- return ', ' .join (view_definitions )
193
-
184
+ return ', ' .join (v )
194
185
195
186
196
187
@field_validator ('time_stamp' )
@@ -238,11 +229,10 @@ def main():
238
229
239
230
filename = sys .argv [1 ]
240
231
try :
241
- file = ifcopenshell .open (filename )
242
- header = HeaderStructure (file = file , purepythonparser = False )
243
- except SchemaError as e :
244
- file = ifcopenshell .simple_spf .open (filename )
245
- header = HeaderStructure (file = file , purepythonparser = True )
232
+ file = ifcopenshell .simple_spf .open (filename , only_header = True )
233
+ header = HeaderStructure (file = file )
234
+ except ifcopenshell .simple_spf .SyntaxError :
235
+ header = HeaderStructure (file = None , validation_errors = ["syntax_error" ])
246
236
except Exception as e :
247
237
print (f"Error opening file '{ filename } ': { e } " )
248
238
sys .exit (1 )
0 commit comments