epb_view_models 1.0.29 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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"