epb_view_models 1.0.29 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -5,8 +5,8 @@ require_relative "lib/epb_view_models"
5
5
  Gem::Specification.new do |spec|
6
6
  spec.name = "epb_view_models"
7
7
  spec.version = EpbViewModels::VERSION
8
- spec.authors = ["MHCLG Energy Performance of Buildings"]
9
- spec.email = ["mhclg.digital-services@communities.gov.uk"]
8
+ spec.authors = ["DLUHC Energy Performance of Buildings"]
9
+ spec.email = ["dluhc.digital-services@levellingup.gov.uk"]
10
10
  spec.summary = "Library used to parse Energy Performance Certificates (EPC)"
11
11
  spec.homepage = "https://github.com/communitiesuk/epb-view-models"
12
12
  spec.license = "MIT"
@@ -5,7 +5,7 @@ loader = Zeitwerk::Loader.for_gem(warn_on_extra_files: false)
5
5
  loader.setup
6
6
 
7
7
  module EpbViewModels
8
- VERSION = "1.0.29"
8
+ VERSION = "1.1.0"
9
9
  end
10
10
 
11
11
  # Monkey patching to avoid using ActiveRecord::Type::Boolean.new.cast
@@ -372,6 +372,7 @@ module Helper
372
372
  ]
373
373
 
374
374
  schemes_that_use_l = %i[
375
+ SAP-Schema-19.1.0
375
376
  SAP-Schema-19.0.0
376
377
  SAP-Schema-18.0.0
377
378
  SAP-Schema-17.1
@@ -576,6 +577,7 @@ module Helper
576
577
  ]
577
578
 
578
579
  sap = %i[
580
+ SAP-Schema-19.1.0
579
581
  SAP-Schema-19.0.0
580
582
  SAP-Schema-18.0.0
581
583
  SAP-Schema-17.1
@@ -26,6 +26,7 @@ module ViewModel
26
26
  RdSAP-Schema-NI-17.3
27
27
  ].freeze
28
28
  TYPES_OF_SAP = %i[
29
+ SAP-Schema-19.1.0
29
30
  SAP-Schema-19.0.0
30
31
  SAP-Schema-18.0.0
31
32
  SAP-Schema-17.1
@@ -0,0 +1,618 @@
1
+ module ViewModel
2
+ module SapSchema1910
3
+ class CommonSchema < ViewModel::DomesticEpcViewModel
4
+ THRESHOLD_LOW_ENERGY_LIGHTING_EFFICACY = 65
5
+
6
+ def assessment_id
7
+ xpath(%w[Report-Header RRN])
8
+ end
9
+
10
+ def address_line1
11
+ xpath(%w[Report-Header Property Address Address-Line-1])
12
+ end
13
+
14
+ def address_line2
15
+ xpath(%w[Report-Header Property Address Address-Line-2])
16
+ end
17
+
18
+ def address_line3
19
+ xpath(%w[Report-Header Property Address Address-Line-3])
20
+ end
21
+
22
+ def town
23
+ xpath(%w[Report-Header Property Address Post-Town])
24
+ end
25
+
26
+ def postcode
27
+ xpath(%w[Report-Header Property Address Postcode])
28
+ end
29
+
30
+ def scheme_assessor_id
31
+ xpath(%w[Certificate-Number])
32
+ end
33
+
34
+ def assessor_name
35
+ xpath(%w[Home-Inspector Name])
36
+ end
37
+
38
+ def assessor_email
39
+ xpath(%w[Home-Inspector/E-Mail])
40
+ end
41
+
42
+ def assessor_telephone
43
+ xpath(%w[Home-Inspector/Telephone])
44
+ end
45
+
46
+ def date_of_assessment
47
+ xpath(%w[Inspection-Date])
48
+ end
49
+
50
+ def date_of_registration
51
+ xpath(%w[Registration-Date])
52
+ end
53
+
54
+ def date_of_completion
55
+ xpath(%w[Completion-Date])
56
+ end
57
+
58
+ def address_id
59
+ xpath(%w[UPRN])
60
+ end
61
+
62
+ def date_of_expiry
63
+ expires_at = (Date.parse(date_of_registration) - 1) >> 12 * 10
64
+
65
+ expires_at.to_s
66
+ end
67
+
68
+ def property_summary
69
+ @xml_doc.search("Energy-Assessment Property-Summary").children.select(
70
+ &:element?
71
+ ).map { |node|
72
+ next if xpath(%w[Energy-Efficiency-Rating], node).nil?
73
+
74
+ {
75
+ energy_efficiency_rating:
76
+ xpath(%w[Energy-Efficiency-Rating], node).to_i,
77
+ environmental_efficiency_rating:
78
+ xpath(%w[Environmental-Efficiency-Rating], node).to_i,
79
+ name: node.name.underscore,
80
+ description: xpath(%w[Description], node),
81
+ }
82
+ }.compact
83
+ end
84
+
85
+ def related_party_disclosure_text
86
+ xpath(%w[Related-Party-Disclosure-Text])
87
+ end
88
+
89
+ def related_party_disclosure_number
90
+ xpath(%w[Related-Party-Disclosure-Number])&.to_i
91
+ end
92
+
93
+ def improvements
94
+ @xml_doc
95
+ .search("Suggested-Improvements Improvement")
96
+ .map do |node|
97
+ {
98
+ energy_performance_rating_improvement:
99
+ xpath(%w[Energy-Performance-Rating], node).to_i,
100
+ environmental_impact_rating_improvement:
101
+ xpath(%w[Environmental-Impact-Rating], node).to_i,
102
+ green_deal_category_code: xpath(%w[Green-Deal-Category], node),
103
+ improvement_category: xpath(%w[Improvement-Category], node),
104
+ improvement_code:
105
+ xpath(%w[Improvement-Details Improvement-Number], node),
106
+ improvement_description: xpath(%w[Improvement-Description], node),
107
+ improvement_title: improvement_title(node),
108
+ improvement_type: xpath(%w[Improvement-Type], node),
109
+ indicative_cost: xpath(%w[Indicative-Cost], node),
110
+ sequence: xpath(%w[Sequence], node).to_i,
111
+ typical_saving: xpath(%w[Typical-Saving], node),
112
+ }
113
+ end
114
+ end
115
+
116
+ def recommendations_for_report
117
+ @xml_doc
118
+ .search("Suggested-Improvements Improvement")
119
+ .map do |node|
120
+ {
121
+ sequence: xpath(%w[Sequence], node).to_i,
122
+ improvement_summary: xpath(%w[Improvement-Summary], node),
123
+ improvement_description: xpath(%w[Improvement-Description], node),
124
+ improvement_code:
125
+ xpath(%w[Improvement-Details Improvement-Number], node),
126
+ indicative_cost: xpath(%w[Indicative-Cost], node),
127
+ }
128
+ end
129
+ end
130
+
131
+ def hot_water_cost_potential
132
+ xpath(%w[Hot-Water-Cost-Potential])
133
+ end
134
+
135
+ def heating_cost_potential
136
+ xpath(%w[Heating-Cost-Potential])
137
+ end
138
+
139
+ def lighting_cost_potential
140
+ xpath(%w[Lighting-Cost-Potential])
141
+ end
142
+
143
+ def hot_water_cost_current
144
+ xpath(%w[Hot-Water-Cost-Current])
145
+ end
146
+
147
+ def heating_cost_current
148
+ xpath(%w[Heating-Cost-Current])
149
+ end
150
+
151
+ def lighting_cost_current
152
+ xpath(%w[Lighting-Cost-Current])
153
+ end
154
+
155
+ def potential_carbon_emission
156
+ xpath(%w[CO2-Emissions-Potential])
157
+ end
158
+
159
+ def current_carbon_emission
160
+ xpath(%w[CO2-Emissions-Current])
161
+ end
162
+
163
+ def potential_energy_rating
164
+ xpath(%w[Energy-Rating-Potential]).to_i
165
+ end
166
+
167
+ def current_energy_rating
168
+ xpath(%w[Energy-Rating-Current]).to_i
169
+ end
170
+
171
+ def primary_energy_use
172
+ xpath(%w[Energy-Consumption-Current])
173
+ end
174
+
175
+ def energy_consumption_potential
176
+ xpath(%w[Energy-Consumption-Potential])
177
+ end
178
+
179
+ def estimated_energy_cost; end
180
+
181
+ def total_floor_area
182
+ xpath(%w[Property-Summary Total-Floor-Area]).to_i
183
+ end
184
+
185
+ def dwelling_type
186
+ xpath(%w[Dwelling-Type])
187
+ end
188
+
189
+ def potential_energy_saving; end
190
+
191
+ def property_age_band
192
+ xpath(%w[Construction-Year])
193
+ end
194
+
195
+ def main_dwelling_construction_age_band_or_year
196
+ sap_building_parts =
197
+ @xml_doc.xpath("//SAP-Building-Parts/SAP-Building-Part")
198
+ sap_building_parts.each do |sap_building_part|
199
+ building_part_number = sap_building_part.at("Building-Part-Number")
200
+
201
+ # Identifies the Main Dwelling
202
+ if building_part_number&.content == "1"
203
+ return(
204
+ sap_building_part.at_xpath(
205
+ "Construction-Age-Band | Construction-Year",
206
+ )&.content
207
+ )
208
+ end
209
+ end
210
+ nil
211
+ end
212
+
213
+ def tenure
214
+ xpath(%w[Tenure])
215
+ end
216
+
217
+ def transaction_type
218
+ xpath(%w[Transaction-Type])
219
+ end
220
+
221
+ def current_space_heating_demand
222
+ xpath(%w[Space-Heating]) or xpath(%w[Space-Heating-Existing-Dwelling])
223
+ end
224
+
225
+ def current_water_heating_demand
226
+ xpath(%w[Water-Heating])
227
+ end
228
+
229
+ def impact_of_cavity_insulation
230
+ if xpath(%w[Impact-Of-Cavity-Insulation])
231
+ xpath(%w[Impact-Of-Cavity-Insulation]).to_i
232
+ end
233
+ end
234
+
235
+ def impact_of_loft_insulation
236
+ if xpath(%w[Impact-Of-Loft-Insulation])
237
+ xpath(%w[Impact-Of-Loft-Insulation]).to_i
238
+ end
239
+ end
240
+
241
+ def impact_of_solid_wall_insulation
242
+ if xpath(%w[Impact-Of-Solid-Wall-Insulation])
243
+ xpath(%w[Impact-Of-Solid-Wall-Insulation]).to_i
244
+ end
245
+ end
246
+
247
+ def all_sap_floor_dimensions
248
+ @xml_doc.search("SAP-Floor-Dimension").select(&:element?).map { |node|
249
+ { total_floor_area: xpath(%w[Total-Floor-Area], node).to_f }
250
+ }.compact
251
+ end
252
+
253
+ def type_of_assessment
254
+ "SAP"
255
+ end
256
+
257
+ def level
258
+ xpath(%w[Level])
259
+ end
260
+
261
+ def top_storey
262
+ flat_level_code = xpath(%w[SAP-FlatLevelCode])
263
+ flat_level_code == "3" ? "Y" : "N"
264
+ end
265
+
266
+ def storey_count
267
+ xpath(%w[Storeys])
268
+ end
269
+
270
+ def building_part_number
271
+ xpath(%w[Building-Part-Number])
272
+ end
273
+
274
+ def all_building_parts
275
+ @xml_doc
276
+ .search("SAP-Building-Parts/SAP-Building-Part")
277
+ .map do |part|
278
+ {
279
+ roof_insulation_thickness:
280
+ if part.xpath("Roof-Insulation-Thickness").empty?
281
+ nil
282
+ else
283
+ part.xpath("Roof-Insulation-Thickness").text
284
+ end,
285
+ rafter_insulation_thickness:
286
+ xpath(%w[Rafter-Insulation-Thickness], part),
287
+ flat_roof_insulation_thickness:
288
+ xpath(%w[Flat-Roof-Insulation-Thickness], part),
289
+ sloping_ceiling_insulation_thickness:
290
+ xpath(%w[Sloping-Ceiling-Insulation-Thickness], part),
291
+ roof_u_value: xpath(%w[Roof-U-Value], part),
292
+ }
293
+ end
294
+ end
295
+
296
+ def floor_heat_loss
297
+ xpath(%w[Floor-Heat-Loss])
298
+ end
299
+
300
+ def immersion_heating_type
301
+ xpath(%w[Immersion-Heating-Type])
302
+ end
303
+
304
+ def main_fuel_type
305
+ xpath(%w[Main-Fuel-Type])
306
+ end
307
+
308
+ def secondary_fuel_type
309
+ xpath(%w[Secondary-Fuel-Type])
310
+ end
311
+
312
+ def water_heating_fuel
313
+ xpath(%w[Water-Fuel-Type])
314
+ end
315
+
316
+ def boiler_flue_type
317
+ xpath(%w[Boiler-Flue-Type])
318
+ end
319
+
320
+ def meter_type
321
+ xpath(%w[Meter-Type])
322
+ end
323
+
324
+ def sap_main_heating_code
325
+ xpath(%w[SAP-Main-Heating-Code])
326
+ end
327
+
328
+ def country_code
329
+ xpath(%w[Country-Code])
330
+ end
331
+
332
+ def environmental_impact_current
333
+ xpath(%w[Environmental-Impact-Current])
334
+ end
335
+
336
+ def environmental_impact_potential
337
+ xpath(%w[Environmental-Impact-Potential])
338
+ end
339
+
340
+ def co2_emissions_current_per_floor_area
341
+ xpath(%w[CO2-Emissions-Current-Per-Floor-Area])
342
+ end
343
+
344
+ def mains_gas
345
+ nil
346
+ end
347
+
348
+ def main_heating_controls
349
+ xpath(%w[Main-Heating-Controls Description])
350
+ end
351
+
352
+ def multiple_glazed_proportion
353
+ xpath(%w[Multiple-Glazed-Percentage])
354
+ end
355
+
356
+ def glazed_area
357
+ nil
358
+ end
359
+
360
+ def habitable_room_count
361
+ nil
362
+ end
363
+
364
+ def heated_room_count
365
+ nil
366
+ end
367
+
368
+ def fixed_lighting_outlets_count
369
+ @xml_doc.search("Fixed-Lights/Fixed-Light/Lighting-Outlets").map(&:content).map(&:to_i).sum
370
+ end
371
+
372
+ def format_fixed_light_array
373
+ @xml_doc
374
+ .search("Fixed-Lights/Fixed-Light")
375
+ .map do |part|
376
+ {
377
+ lighting_efficacy:
378
+ xpath(%w[Lighting-Efficacy], part),
379
+ lighting_outlets:
380
+ xpath(%w[Lighting-Outlets], part),
381
+ }
382
+ end
383
+ end
384
+
385
+ def low_energy_lighting
386
+ total_energy_outlet_count = []
387
+ low_energy_outlet_count = []
388
+
389
+ format_fixed_light_array.each do |outlet|
390
+ if outlet[:lighting_efficacy].to_i > THRESHOLD_LOW_ENERGY_LIGHTING_EFFICACY
391
+ low_energy_outlet_count << outlet[:lighting_outlets].to_i
392
+ end
393
+ total_energy_outlet_count << outlet[:lighting_outlets].to_f
394
+ end
395
+
396
+ if total_energy_outlet_count.sum.zero?
397
+ return 0
398
+ end
399
+
400
+ (low_energy_outlet_count.sum / total_energy_outlet_count.sum * 100).round
401
+ end
402
+
403
+ def low_energy_fixed_lighting_outlets_count
404
+ low_energy_outlet_count = []
405
+
406
+ format_fixed_light_array.each do |outlet|
407
+ if outlet[:lighting_efficacy].to_i > THRESHOLD_LOW_ENERGY_LIGHTING_EFFICACY
408
+ low_energy_outlet_count << outlet[:lighting_outlets].to_i
409
+ end
410
+ end
411
+
412
+ low_energy_outlet_count.sum
413
+ end
414
+
415
+ def open_fireplaces_count
416
+ xpath(%w[Open-Chimneys-Count])
417
+ end
418
+
419
+ def hot_water_description
420
+ xpath(%w[Hot-Water Description])
421
+ end
422
+
423
+ def hot_water_energy_efficiency_rating
424
+ xpath(%w[Hot-Water Energy-Efficiency-Rating])
425
+ end
426
+
427
+ def hot_water_environmental_efficiency_rating
428
+ xpath(%w[Hot-Water Environmental-Efficiency-Rating])
429
+ end
430
+
431
+ def window_description
432
+ xpath(%w[Windows Description])
433
+ end
434
+
435
+ def window_energy_efficiency_rating
436
+ xpath(%w[Windows Energy-Efficiency-Rating])
437
+ end
438
+
439
+ def window_environmental_efficiency_rating
440
+ xpath(%w[Windows Environmental-Efficiency-Rating])
441
+ end
442
+
443
+ def secondary_heating_description
444
+ xpath(%w[Secondary-Heating Description])
445
+ end
446
+
447
+ def secondary_heating_energy_efficiency_rating
448
+ xpath(%w[Secondary-Heating Energy-Efficiency-Rating])
449
+ end
450
+
451
+ def secondary_heating_environmental_efficiency_rating
452
+ xpath(%w[Secondary-Heating Environmental-Efficiency-Rating])
453
+ end
454
+
455
+ def lighting_description
456
+ xpath(%w[Lighting Description])
457
+ end
458
+
459
+ def lighting_energy_efficiency_rating
460
+ xpath(%w[Lighting Energy-Efficiency-Rating])
461
+ end
462
+
463
+ def lighting_environmental_efficiency_rating
464
+ xpath(%w[Lighting Environmental-Efficiency-Rating])
465
+ end
466
+
467
+ def photovoltaic_roof_area_percent
468
+ nil
469
+ end
470
+
471
+ def built_form
472
+ xpath(%w[Built-Form])
473
+ end
474
+
475
+ def wind_turbine_count
476
+ @xml_doc.search("Wind-Turbines/Wind-Turbine").map(&:content).count
477
+ end
478
+
479
+ def unheated_corridor_length
480
+ nil
481
+ end
482
+
483
+ def heat_loss_corridor
484
+ nil
485
+ end
486
+
487
+ def all_main_heating_descriptions
488
+ @xml_doc.search("Main-Heating/Description").map(&:content)
489
+ end
490
+
491
+ def all_main_heating_controls_descriptions
492
+ @xml_doc.search("Main-Heating-Controls/Description").map(&:content)
493
+ end
494
+
495
+ def report_type
496
+ xpath(%w[Report-Type])
497
+ end
498
+
499
+ def all_roof_descriptions
500
+ @xml_doc.search("Roof/Description").map(&:content)
501
+ end
502
+
503
+ def all_roof_env_energy_efficiency_rating
504
+ @xml_doc.search("Roof/Environmental-Efficiency-Rating").map(&:content)
505
+ end
506
+
507
+ def all_roof_energy_efficiency_rating
508
+ @xml_doc.search("Roof/Environmental-Efficiency-Rating").map(&:content)
509
+ end
510
+
511
+ def all_wall_descriptions
512
+ @xml_doc.search("Walls/Description").map(&:content)
513
+ end
514
+
515
+ def all_wall_energy_efficiency_rating
516
+ @xml_doc.search("Walls/Energy-Efficiency-Rating").map(&:content)
517
+ end
518
+
519
+ def all_wall_env_energy_efficiency_rating
520
+ @xml_doc.search("Walls/Environmental-Efficiency-Rating").map(&:content)
521
+ end
522
+
523
+ def energy_tariff
524
+ xpath(%w[Electricity-Tariff])
525
+ end
526
+
527
+ def floor_level
528
+ xpath(%w[SAP-Flat-Details Level])
529
+ end
530
+
531
+ def all_main_heating_energy_efficiency
532
+ @xml_doc.search("Main-Heating/Energy-Efficiency-Rating").map(&:content)
533
+ end
534
+
535
+ def extensions_count
536
+ nil
537
+ end
538
+
539
+ def solar_water_heating_flag
540
+ nil
541
+ end
542
+
543
+ def mechanical_ventilation
544
+ nil
545
+ end
546
+
547
+ def floor_height
548
+ @xml_doc.search("Storey-Height").map(&:content)
549
+ end
550
+
551
+ def all_floor_descriptions
552
+ @xml_doc.search("Property-Summary/Floor/Description").map(&:content)
553
+ end
554
+
555
+ def all_floor_energy_efficiency_rating
556
+ @xml_doc
557
+ .search("Property-Summary/Floor/Energy-Efficiency-Rating")
558
+ .map(&:content)
559
+ end
560
+
561
+ def all_floor_env_energy_efficiency_rating
562
+ @xml_doc
563
+ .search("Property-Summary/Floor/Environmental-Efficiency-Rating")
564
+ .map(&:content)
565
+ end
566
+
567
+ def all_main_heating_controls_energy_efficiency
568
+ @xml_doc
569
+ .search("Main-Heating-Controls/Energy-Efficiency-Rating")
570
+ .map(&:content)
571
+ end
572
+
573
+ def all_main_heating_controls_environmental_efficiency
574
+ @xml_doc
575
+ .search("Main-Heating-Controls/Environmental-Efficiency-Rating")
576
+ .map(&:content)
577
+ end
578
+
579
+ def all_main_heating_environmental_efficiency
580
+ @xml_doc
581
+ .search("Main-Heating/Environmental-Efficiency-Rating")
582
+ .map(&:content)
583
+ end
584
+
585
+ def cylinder_insul_thickness
586
+ xpath(%w[Hot-Water-Store-Insulation-Thickness])
587
+ end
588
+
589
+ def cylinder_insulation_type
590
+ xpath(%w[Hot-Water-Store-Insulation-Type])
591
+ end
592
+
593
+ def cylinder_size
594
+ xpath(%w[Hot-Water-Store-Size])
595
+ end
596
+
597
+ def has_cylinder_thermostat
598
+ xpath(%w[Has-Cylinder-Thermostat])
599
+ end
600
+
601
+ def mech_vent_sys_index_number
602
+ xpath(%w[Mechanical-Vent-System-Index-Number])
603
+ end
604
+
605
+ def mechanical_vent_data_source
606
+ xpath(%w[Mechanical-Ventilation-Data-Source])
607
+ end
608
+
609
+ def thermal_store
610
+ xpath(%w[Thermal-Store])
611
+ end
612
+
613
+ def ventilation_type
614
+ xpath(%w[Ventilation-Type])
615
+ end
616
+ end
617
+ end
618
+ end
@@ -50,6 +50,8 @@ module ViewModel
50
50
 
51
51
  def build_view_model(xml_doc, schema_type, report_type)
52
52
  case schema_type
53
+ when :"SAP-Schema-19.1.0"
54
+ return ViewModel::SapSchema1910::CommonSchema.new xml_doc
53
55
  when :"SAP-Schema-19.0.0"
54
56
  return ViewModel::SapSchema1900::CommonSchema.new xml_doc
55
57
  when :"SAP-Schema-18.0.0"