circuitdata 0.3.5 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,106 @@
1
+ def self.compare_files(filehash, validate_origins=false)
2
+ # Prepare the return
3
+ ra = {
4
+ error: false,
5
+ errormessage: "",
6
+ summary: {},
7
+ conflicts: {},
8
+ product: nil,
9
+ columns: [],
10
+ mastercolumn: nil,
11
+ rows: []
12
+ }
13
+
14
+ #parsedfiles
15
+ unless filehash.is_a? Hash
16
+ ra[:error] = true
17
+ ra[:errormessage] = "You have to feed this function with a hash of names and hashes"
18
+ return ra
19
+ end
20
+
21
+ # extend the hash that is received
22
+ nh = {}
23
+ filehash.each do |fhk, fhv|
24
+ nh[fhk] = {
25
+ orig: fhv,
26
+ parsed: nil,
27
+ content: nil,
28
+ has: {}
29
+ }
30
+ # READ THE CONTENT
31
+ ra[:error], ra[:errormessage], nh[fhk][:content] = self.read_json(fhv)
32
+ ra[:summary] = {} if ra[:error]
33
+ ra[:conflicts] = {} if ra[:error]
34
+ return ra if ra[:error]
35
+ # VALIDATE THE FILES
36
+ if validate_origins
37
+ ra[:error], ra[:errormessage], validationserrors = self.validate(nh[fhk][:content])
38
+ ra[:summary] = {} if ra[:error]
39
+ ra[:conflicts] = {} if ra[:error]
40
+ return ra if ra[:error]
41
+ end
42
+
43
+
44
+ # SET THE PRODUCT NAME
45
+ nh[fhk][:has][:products], nh[fhk][:has][:stackup], nh[fhk][:has][:profile_default], nh[fhk][:has][:profile_restricted], nh[fhk][:has][:profile_enforced], nh[fhk][:has][:capabilities], nh[fhk][:has][:product] = self.content(nh[fhk][:content])
46
+ unless nh[fhk][:has][:product].nil?
47
+ #self.iterate(nh[fhk][:content])
48
+
49
+ #root_node = Tree::TreeNode.new("ROOT", "Root Content")
50
+ #root_node.print_tree
51
+
52
+ ra[:product] = nh[fhk][:has][:product] if ra[:product].nil?
53
+ if nh[fhk][:has][:product] != ra[:product]
54
+ ra[:error] = true
55
+ ra[:errormessage] = "Your files contains several different product names"
56
+ ra[:summary] = {}
57
+ ra[:conflicts] = {}
58
+ return ra
59
+ end
60
+ ra[:mastercolumn] = fhk if ra[:mastercolumn].nil?
61
+ end
62
+
63
+ # THIS IS WHERE I NEED THINGS TO HAPPEN
64
+
65
+ end
66
+
67
+ # RETURN IF THERE IS NO PRODUCT
68
+ if ra[:mastercolumn].nil?
69
+ ra[:error] = true
70
+ ra[:errormessage] = "none of the files contains a product"
71
+ ra[:summary] = {}
72
+ ra[:conflicts] = {}
73
+ return ra
74
+ end
75
+
76
+ {
77
+ current_level: 0,
78
+ current_key: nil,
79
+
80
+ }
81
+ # Populate the master column
82
+ #self.iterate(filehash[ra[:mastercolumn].to_sym])
83
+ #ra[:summary] = productjson[:open_trade_transfer_package][:products][ra[:product]][:printed_circuits_fabrication_data]
84
+
85
+ #test = {}
86
+ #self.save_pair(productjson[:open_trade_transfer_package][:products][ra[:product]][:printed_circuits_fabrication_data], test)
87
+ #puts test
88
+ # Populate the product rows
89
+ #productjson[:open_trade_transfer_package]["products"][ra[:product]]["printed_circuits_fabrication_data"].each do |key, value|
90
+ # if value.is_a? Hash
91
+ # value.each do |subkey, subvalue|
92
+ # ra[:rows][]
93
+ #end
94
+
95
+ # Do comparisons
96
+ #number = 1
97
+ #filehash.each do |key, value|
98
+ # unless key.to_s == productfile
99
+ # #puts self.compatibility_checker( productjson, value, false )
100
+ # number += 1
101
+ # end
102
+ # end
103
+ #puts JSON.pretty_generate(ra)
104
+ #puts JSON.pretty_generate(nh)
105
+ return ra
106
+ end
@@ -0,0 +1,119 @@
1
+ class Circuitdata::CompatibilityChecker
2
+ def initialize(product_file, check_file, validate_origins)
3
+ require 'json'
4
+ require 'json-schema'
5
+
6
+ @product_file = product_file
7
+ @check_file = check_file
8
+ @validate_origins = validate_origins
9
+ # Final hash
10
+ @fh = {error: false, message: nil, errors: {validation: {}, restricted: {}, enforced: {}, capabilities: {}}}
11
+ end
12
+
13
+ def start_check
14
+ # Initialize & validate
15
+ @fh[:error], @fh[:message], product_data = Circuitdata.read_json(@product_file)
16
+ return @fh if @fh[:error]
17
+ @fh[:error], @fh[:message], @fh[:errors][:validation] = Circuitdata.validate(product_data)
18
+ return @fh if @fh[:error]
19
+ if @check_file.present?
20
+ @fh[:error], @fh[:message], check_data = Circuitdata.read_json(@check_file)
21
+ return @fh if @fh[:error]
22
+ @fh[:error], @fh[:message], @fh[:errors][:validation] = Circuitdata.validate(check_data)
23
+ return @fh if @fh[:error]
24
+ f2_types = Circuitdata.get_data_summary(check_data)[1]
25
+ # read the schema
26
+ schema_path = File.join(File.dirname(__FILE__), 'schema_files/v1/ottp_circuitdata_skeleton_schema.json')
27
+ restricted_schema = enforced_schema = capability_schema = Circuitdata.read_json(schema_path)[2]
28
+ # Compare the content
29
+ perform_comparison(product_data, check_data, restricted_schema, 'restricted') if f2_types.include? 'profile_restricted'
30
+ perform_comparison(product_data, check_data, enforced_schema, 'enforced') if f2_types.include? 'profile_enforced'
31
+ perform_comparison(product_data, check_data, capability_schema, 'capabilities') if f2_types.include? 'capabilities'
32
+ end
33
+
34
+ @fh
35
+ end
36
+
37
+ def perform_comparison(product_data, check_data, schema, type)
38
+ case type
39
+ when 'restricted'
40
+ check_hash = check_data.dig(:open_trade_transfer_package, :profiles, :restricted, :printed_circuits_fabrication_data)
41
+ when 'enforced'
42
+ check_hash = check_data.dig(:open_trade_transfer_package, :profiles, :enforced, :printed_circuits_fabrication_data)
43
+ when 'capabilities'
44
+ check_hash = check_data.dig(:open_trade_transfer_package, :capabilities, :printed_circuits_fabrication_data)
45
+ else
46
+ check_hash = {}
47
+ end
48
+ # binding.pry
49
+
50
+ check_hash.each do |k, v|
51
+ v.each do |kl1, vl1| # level 1
52
+ common_hash = schema.dig(:properties, :open_trade_transfer_package, :properties, :products, :patternProperties, :'^(?!generic$).*', :properties, :printed_circuits_fabrication_data, :properties)
53
+ # binding.pry
54
+ common_hash[k.to_sym]||= {:type => 'object', :properties => {}}
55
+ common_hash[:stackup][:properties][:specified][:properties][k.to_sym] ||= {:type => 'object', :properties => {}}
56
+
57
+ case vl1.class.name
58
+ when 'String'
59
+ if vl1.match("^(\\d*|\\d*.\\d*)\\.\\.\\.(\\d*|\\d*.\\d*)$") #This is a value range
60
+ from, too = vl1.match("^(\\d*|\\d*.\\d*)\\.\\.\\.(\\d*|\\d*.\\d*)$").captures
61
+ case type
62
+ when 'restricted'
63
+ new_hash = {:not => {:allOf => [{:minimum => from.to_f},{:maximum => too.to_f}]}}
64
+ else
65
+ new_hash = eval("{:minimum => #{from}, :maximum => #{too}}")
66
+ end
67
+ else # This is a normal string - check for commas
68
+ enum = []
69
+ vl1.split(',').each {|enumvalue| enum << enumvalue.strip}
70
+ case type
71
+ when 'restricted'
72
+ new_hash = {:not => {:anyOf => [{ :enum => ''}]}}
73
+ new_hash[:not][:anyOf][0][:enum] = enum
74
+ else
75
+ new_hash = eval("{:enum => #{enum}}")
76
+ end
77
+ end
78
+ when 'Numeric' # This is a normal string
79
+ case type
80
+ when 'restricted'
81
+ new_hash = {:not => {:allOf => [{:minimum => vl1.to_f},{:maximum => vl1.to_f}]}}
82
+ else
83
+ new_hash = eval("{:enum => [#{vl1.to_s}]}")
84
+ end
85
+ end
86
+ common_hash[k.to_sym][:properties][kl1.to_sym] = new_hash
87
+ common_hash[:stackup][:properties][:specified][:properties][k.to_sym][:properties][kl1.to_sym] = new_hash
88
+ end if v.is_a? Hash
89
+ end
90
+
91
+ # perform validations
92
+ begin
93
+ validation_errors = JSON::Validator.fully_validate(schema.to_json, product_data, :errors_as_objects => true)
94
+
95
+ if validation_errors.any?
96
+ @fh[:error] = true
97
+ @fh[:message] = 'The product to check did not meet the requirements'
98
+
99
+ # format the errors well here
100
+
101
+ validation_errors.each do |error|
102
+ error_array = []
103
+ begin
104
+ error_array << error[:message].match("^(The\\sproperty\\s\\'[\\s\\S]*\\'\\s)([\\s\\S]*)(\\sin\\sschema[\\s\\S]*)$").captures[1]
105
+ rescue
106
+ error_array << error[:message]
107
+ end
108
+ @fh[:errors][type.to_sym][error[:fragment]] = error_array
109
+ end
110
+ end
111
+ rescue JSON::Schema::ReadFailed
112
+ @fh[:error] = true
113
+ @fh[:message] = "Could not read the submitted `#{type}` schema" # enforced_schema
114
+ rescue JSON::Schema::SchemaError
115
+ @fh[:error] = true
116
+ @fh[:message] = "Something was wrong with the submitted `#{type}` schema" # enforced_schema
117
+ end
118
+ end
119
+ end
@@ -0,0 +1,243 @@
1
+ class Circuitdata::FileComparer
2
+ def initialize(file_hash, validate_origins)
3
+ @file_hash = file_hash
4
+ @validate_origins = validate_origins
5
+ @rows = {}
6
+ @nh = {} # a new_hash to combine all the data
7
+ @columns = []
8
+ @default_column = nil
9
+ @master_column = nil
10
+ # Final hash
11
+ @fh = {error: false, message: nil, conflict: false, product_name: nil, columns: nil, master_column: nil, rows: nil}
12
+ end
13
+
14
+ def compare
15
+ # Initial check
16
+ unless @file_hash.is_a? Hash
17
+ @fh[:error] = true
18
+ @fh[:message] = 'You have to feed this function with a hash of names and hashes'
19
+ return @fh
20
+ end
21
+
22
+ # Process the hashes
23
+ products_array = []
24
+ @file_hash.each do |k, v|
25
+ # read content
26
+ @fh[:error], @fh[:message], file_content = Circuitdata.read_json(v)
27
+ return @fh if @fh[:error]
28
+ products, types = Circuitdata.get_data_summary(file_content)
29
+ products_array.push(*products) # add products to tracking array
30
+ # populate the new_hash to be used later
31
+ @nh[k] = {types: types, products: products, data: file_content}
32
+ end
33
+
34
+ # check if the files content meet the requirements
35
+ if valid_product?(products_array)
36
+ @fh[:product_name] = products_array.first.to_s
37
+ @columns = @nh.keys
38
+ # get all data with products in it
39
+ product_hashes = @nh.select{|k, v| v[:products].any?}
40
+ product_columns = product_hashes.keys
41
+
42
+ # Add conflicts into the new_hash
43
+ product_hashes.each do |column_k, column_v|
44
+ master_json = column_v.dig(:data)
45
+ @nh.each do |file_k, file_v|
46
+ products, data = file_v[:products], file_v[:data]
47
+ check_results = Circuitdata.compatibility_checker(master_json, data, false)
48
+ # format the conflicts correctly here
49
+ file_v[:conflicts] ||= {}
50
+ file_v[:conflicts][column_k] = get_validation_summary(check_results, file_k)
51
+ # initialize the rows format - for all the product items
52
+ product_hash = data.dig(:open_trade_transfer_package, :products, @fh[:product_name].to_sym, :printed_circuits_fabrication_data)
53
+ if products.any?
54
+ init_row_format(product_hash)
55
+ end
56
+ end
57
+ # Initialize the rows format - for all default profile items
58
+ @default_column, file_v = @nh.select{|k, v| v[:types].include?("profile_defaults")}.first # this should only be a single file
59
+ data = file_v[:data]
60
+ product_hash = data.dig(:open_trade_transfer_package, :profiles, :defaults, :printed_circuits_fabrication_data)
61
+ init_row_format(product_hash)
62
+ end
63
+
64
+ # populate the @rows
65
+ product_columns.each do |column|
66
+ @master_column = column
67
+ process_row_hash('populate')
68
+ end
69
+ # populate the @rows summary
70
+ product_columns.each do |column|
71
+ @master_column = column
72
+ process_row_hash('get_summary')
73
+ end
74
+ process_row_hash('populate_defaults')
75
+ end
76
+
77
+ @fh[:columns] = @columns.unshift(:summary)
78
+ @fh[:rows] = @rows
79
+ @fh
80
+ end
81
+
82
+ def init_row_format(product_hash)
83
+ product_hash.each do |k, v|
84
+ if v.is_a?(Hash)
85
+ @rows[k] ||= {}
86
+ v.each do |kl1, vl1|
87
+ @rows[k][kl1] ||= get_l1_hash(@columns)
88
+ end
89
+ else
90
+ @rows[k] ||= []
91
+ # if array functionality eg Holes
92
+ end if ['Hash', 'Array'].include?(v.class.name)
93
+ end
94
+ end
95
+
96
+ def process_row_hash(action)
97
+ @rows.each do |k, v| # product elements level
98
+ if v.is_a?(Hash)
99
+ v.each do |kl1, vl1| # specification level
100
+ value, conflict, conflicts_with, conflict_message = [], false, [], []
101
+ vl1.each do |kl2, vl2| # the specification column level - call the function from here
102
+ conflicts = @nh.dig(kl2, :conflicts, @master_column)
103
+ case action
104
+ when 'populate'
105
+ check = conflicts.any? && conflicts.dig(:rows, k, kl1).present?
106
+ vl2[:value] = @nh.dig(kl2, :data, :open_trade_transfer_package, :products, @fh[:product_name].to_sym, :printed_circuits_fabrication_data, k, kl1)
107
+ vl2[:conflict] = check unless vl2[:conflict] # update only when the status is false
108
+ vl2[:conflicts_with] = check ? vl2[:conflicts_with] << @master_column : []
109
+ vl2[:conflict_message] = check ? vl2[:conflict_message] + conflicts&.dig(:rows, k, kl1) : []
110
+
111
+ # update master_column conflicts with
112
+ if check
113
+ master_row = @rows.dig(k, kl1, @master_column)
114
+ master_row[:conflicts_with] = master_row[:conflicts_with] + conflicts.dig(:master_conflicts)
115
+ master_row[:conflict] = true
116
+ master_row[:conflict_message] = (master_row[:conflict_message] + vl2[:conflict_message]).uniq
117
+ end
118
+ when 'get_summary'
119
+ # get the summary items
120
+ if kl2 != :summary
121
+ items_v = vl2[:value]
122
+ master_value = vl1.dig(@master_column, :value)
123
+ # dont test if the @master_column value is also nil
124
+ if value.empty? || !value.include?(items_v)
125
+ value << items_v
126
+ conflicts_with << kl2
127
+ # jump the default column
128
+ if kl2 != @master_column # Add errors to the specific rows items
129
+ # get the item type
130
+ col_type = get_column_type(@nh.dig(kl2, :types))
131
+ vl2[:conflict] = true
132
+ vl2[:conflicts_with] = (vl2[:conflicts_with] << @master_column).uniq
133
+ vl2[:conflict_message] = (vl2[:conflict_message] << customize_conflict_message(col_type, kl2, @master_column)).uniq
134
+ # update the master row
135
+ master_row = @rows.dig(k, kl1, @master_column)
136
+ master_row[:conflicts_with] = master_row[:conflicts_with] << kl2
137
+ master_row[:conflict] = true
138
+ # get a customized error message here
139
+ master_row[:conflict_message] = (master_row[:conflict_message] << customize_conflict_message(col_type, @master_column, kl2)).uniq
140
+ end
141
+ end unless items_v.nil? || master_value.nil?
142
+ conflict = true if vl2[:conflict]
143
+ conflicts_with = conflicts_with + vl2[:conflicts_with]
144
+ conflict_message = conflict_message + vl2[:conflict_message]
145
+ end
146
+ when 'populate_defaults'
147
+ if kl2 == @default_column
148
+ vl2[:value] = @nh.dig(kl2, :data, :open_trade_transfer_package, :profiles, :defaults, :printed_circuits_fabrication_data, k, kl1)
149
+ vl2[:conflict] = false
150
+ vl2[:conflicts_with] = []
151
+ vl2[:conflict_message] = []
152
+ end
153
+ end
154
+ end
155
+ case action
156
+ when 'get_summary'
157
+ if value.count > 1
158
+ conflict = true
159
+ else
160
+ value = value.first
161
+ end
162
+ vl1[:summary] = {value: value, conflict: conflict, conflicts_with: conflicts_with.uniq, conflict_message: conflict_message.uniq}
163
+ when 'populate_defaults'
164
+ # if all the values are blank, use the default value
165
+ vl1[:summary][:value] ||= vl1.dig(@default_column, :value)
166
+ end
167
+ if action == 'get_summary'
168
+ end
169
+ @fh[:conflict] = true if conflict
170
+ end
171
+ else
172
+ # if array functionality eg Holes
173
+ end
174
+ end
175
+ end
176
+
177
+ def customize_conflict_message(type, col, conflicting_col)
178
+ case type
179
+ when :product
180
+ "#{col.to_s} value conflicts with value from #{conflicting_col.to_s}"
181
+ when :restricted
182
+ "#{col.to_s} value is restricted in #{conflicting_col.to_s}"
183
+ when :enforced
184
+ "#{col.to_s} value conflicts with the enforced value from #{conflicting_col.to_s}"
185
+ when :capability
186
+ "#{col.to_s} value is outside the capabilities of #{conflicting_col.to_s}"
187
+ else
188
+ "There were some value conflicts"
189
+ end
190
+ end
191
+
192
+ def get_column_type(types)
193
+ types ||= []
194
+ if types.include? "product"
195
+ :product
196
+ elsif types.include? "profile_restricted"
197
+ :restricted
198
+ elsif types.include? "profile_enforced"
199
+ :enforced
200
+ elsif types.include? "capabilities"
201
+ :capability
202
+ end
203
+ end
204
+
205
+ def get_validation_summary(validation, column)
206
+ summary = {}
207
+ if validation[:error]
208
+ summary[:master_conflicts] ||= []
209
+ summary[:master_conflicts] << column
210
+ summary[:conflicts], summary[:rows] = true, {}
211
+ validation[:errors].each do |type, errors| # validation, restricted, enforced, capabilities
212
+ errors.each do |k, v|
213
+ folders_stack = k.split('/')
214
+ folder, spec = folders_stack[5], folders_stack[6]
215
+ summary[:rows][folder.to_sym] ||= {}
216
+ spec_message = summary[:rows][folder.to_sym][spec.to_sym] || []
217
+ summary[:rows][folder.to_sym][spec.to_sym] = spec_message+v
218
+ end if errors.any?
219
+ end
220
+ end
221
+ summary
222
+ end
223
+
224
+ def get_l1_hash(columns)
225
+ l1_hash = {}
226
+ columns.each{|c| l1_hash[c]={} }
227
+ l1_hash
228
+ end
229
+
230
+ def valid_product?(products_array)
231
+ if products_array.uniq.count > 1
232
+ @fh[:error] = true
233
+ @fh[:message] = 'Your files contains several different product names'
234
+ return false # validation fails because of different product names
235
+ end
236
+ if products_array.empty?
237
+ @fh[:error] = true
238
+ @fh[:message] = 'None of the files contains a product'
239
+ return false # c=validation fails because there are no products
240
+ end
241
+ true
242
+ end
243
+ end