from typing import Dict, Union from BaseUtility import Definitions from etl.epc_clean.epc_attributes.attribute_utils import clean_description, find_keyword, handle_mixed_translation class HotWaterAttributes(Definitions): # HEATER_TYPES refer to the main devices used for heating water. These devices can be powered by different energy # sources. HEATER_TYPES = [ 'gas instantaneous', # A water heater that provides hot water only as it is needed 'electric heat pump', # A device that transfers heat from a source (like the ground or air) into a building to provide hot water 'electric immersion', # An electric device that heats water using electric resistance 'gas boiler', # A boiler that uses gas as fuel to heat water 'oil boiler', # A boiler that uses oil as fuel to heat water 'electric instantaneous', # Similar to gas instantaneous, but uses electricity as its energy source 'gas multipoint', # A gas water heater that can supply hot water to multiple points of use at once 'heat pump', # A general category for heat pumps, regardless of the energy source 'solid fuel boiler', # burns solid materials to generate heat for water heating and/or space heating 'solid fuel range cooker', 'room heaters', # Generic/unspecified category 'electric multipoint', 'single-point gas', ] # SYSTEM_TYPES refer to the larger system within which the heater operates. SYSTEM_TYPES = [ 'from main system', # The hot water is provided by the main heating system of the building 'from secondary system', # The hot water is provided by a secondary (or supplementary) heating system in the building 'from second main heating system', # Same as 'from secondary system' 'community scheme', # The hot water is provided by a community heating system "water heater", ] # THERMOSTAT_CHARACTERISTICS refer to features related to temperature control in the system. THERMOSTAT_CHARACTERISTICS = [ 'no cylinder thermostat', # Indicates that the hot water cylinder does not have a thermostat ] # HEATING_SCOPE refers to the specific role of the heater in the heating system. HEATING_SCOPE = [ 'water heating only', # Indicates that the heater is used only for water heating, not space heating ] # ENERGY_RECOVERY refers to systems or epc_attributes that recover and utilize waste energy. ENERGY_RECOVERY = [ 'waste water heat recovery', # A system that recovers heat from waste hot water (e.g., from showers) to preheat incoming cold water 'flue gas heat recovery', # A system that recovers heat from the flue gases of a boiler or furnace ] # TARIFF_TYPE relates to the energy pricing structure or tariff that applies to the system. TARIFF_TYPE = [ 'standard tariff', # The energy used by the system is billed at a standard rate 'off-peak', # The energy used by the system is primarily used during off-peak hours when rates are lower ] # EXTRA_FEATURES are additional features or systems that enhance the efficiency or capability of the hot water # system. EXTRA_FEATURES = [ 'plus solar', # Indicates that the system includes solar thermal panels to assist in water heating ] # CHP_SYSTEMS is specifically for the Combined Heat and Power systems. CHP_SYSTEMS = [ 'chp', # Combined Heat and Power system, a system that simultaneously produces heat and electricity from the same # energy source ] # NO_SYSTEM_PRESENT_KEYWORDS indicates there is no specific hot water system present. NO_SYSTEM_PRESENT_KEYWORDS = [ 'no system present', ] # DISTRIBUTION_SYSTEM_KEYWORDS refers to components that assist in the distribution of the heated water. DISTRIBUTION_SYSTEM_KEYWORDS = [ 'circulator', # A pump used to circulate hot water in the system ] # Indicate if the information is assumed ASSUMED = [ 'assumed' ] # in some rare instances, especially in older homes, a range cooker can be part of a larger system that also # includes a boiler for heating water. In these cases, the cooker may help to heat the water, but this setup is # not common, especially in modern homes. APPLIANCE_SYSTEMS = [ 'gas range cooker', # A gas-powered range cooker 'oil range cooker' ] # Descriptions which represent the same thing SYNONYMS = { 'from second main heating system': 'from secondary system', } WELSH_TEXT = { "ogçör brif system": "from main system", "o r brif system": "from main system", "o’r brif system": "from main system", "ogçör brif system, adfer gwres nwyon ffliw": "from main system, flue gas heat recovery", "bwyler/cylchredydd nwy": "gas boiler/circulator", "ogçör brif system, dim thermostat ar y silindr": "from main system, no cylinder thermostat", "o r brif system, dim thermostat ar y silindr": "from main system, no cylinder thermostat", "twymwr tanddwr, an-frig": "electric immersion, off-peak", "ogçör brif system, gydag ynnigçör haul": "from main system, plus solar", "twymwr tanddwr, tarriff safonol": "electric immersion, standard tariff", "trydan ar unwaith yn y fan lle maegçön cael ei ddefnyddio": 'electric instantaneous at point of use', "o gynllun cymunedol": "community scheme", "o'r brif system": "from main system", "trydan ar unwaith yn y fan lle mae'n cael ei ddefnyddio": 'electric instantaneous at point of use', "popty estynedig olew, dim thermostat ar y silindr": "oil range cooker, no cylinder thermostat", "cynllun cymunedol": "community scheme", "nwy wrth fwy nag un pwynt": "gas multipoint", "popty estynedig olew": "oil range cooker", "dim system ar gael rhagdybir bod twymwr tanddwr trydan": "no system present electric immersion assumed", "o'r brif system, dim thermostat ar y silindr": "from main system, no cylinder thermostat", "trydan ar unwaith yn y fan lle maegçön cael ei ddefnyddio, adfer gwres d+¦r gwastraff": "electric " "instantaneous at " "point of use, " "waste water heat " "recovery", "ogçör brif system, adfer gwres d+¦r gwastraff": "from main system, waste water heat recovery", "twymwr tanddwr, tarriff safonol, adfer gwres d+¦r gwastraff": "electric immersion, standard tariff, waste " "water heat recovery", "ogçör brif system, dim thermostat ar y silindr, adfer gwres nwyon ffliw": "from main system, no cylinder " "thermostat, flue gas heat recovery", "ogçör brif system, gydag ynnigçör haul, adfer gwres nwyon ffliw": "from main system, plus solar, flue gas " "heat recovery", "o r brif system, gydag ynni r haul, dim thermostat ar y silindr": "from main system, plus solar, no cylinder " "thermostat", "o r brif system, gydag ynni r haul": "from main system, plus solar", "pwmp gwres": "heat pump" } NODATA_DESCRIPTIONS = [ "sap05 hot-water", "sap hot-water" ] def __init__(self, description: str): self.description: str = clean_description(description.lower()).strip() self.nodata = not self.description or description in self.DATA_ANOMALY_MATCHES or ( self.description in self.NODATA_DESCRIPTIONS ) translation = self.WELSH_TEXT.get(self.description) if translation: self.nodata = False self.description = translation # We handle seemind occurances of mixed translations self.description = handle_mixed_translation(self.description) if not self.nodata and not any( self._keyword_in_description(keywords) for keywords in [ self.HEATER_TYPES, self.SYSTEM_TYPES, self.THERMOSTAT_CHARACTERISTICS, self.HEATING_SCOPE, self.ENERGY_RECOVERY, self.TARIFF_TYPE, self.EXTRA_FEATURES, self.CHP_SYSTEMS, self.NO_SYSTEM_PRESENT_KEYWORDS, self.APPLIANCE_SYSTEMS, self.DISTRIBUTION_SYSTEM_KEYWORDS ] ): raise ValueError('Invalid description') def _keyword_in_description(self, keywords): return any(keyword in self.description for keyword in keywords) def process(self) -> Dict[str, Union[str, bool]]: if self.nodata: return { "heater_type": None, "system_type": None, "thermostat_characteristics": None, "heating_scope": None, "energy_recovery": None, "tariff_type": None, "extra_features": None, "chp_systems": None, "distribution_system": None, "no_system_present": None, "assumed": None, "appliance": None, } result: Dict[str, Union[str, bool]] = { "heater_type": find_keyword(self.description, self.HEATER_TYPES), "system_type": find_keyword(self.description, self.SYSTEM_TYPES, self.SYNONYMS), "thermostat_characteristics": find_keyword(self.description, self.THERMOSTAT_CHARACTERISTICS), "heating_scope": find_keyword(self.description, self.HEATING_SCOPE), "energy_recovery": find_keyword(self.description, self.ENERGY_RECOVERY), "tariff_type": find_keyword(self.description, self.TARIFF_TYPE), "extra_features": find_keyword(self.description, self.EXTRA_FEATURES), "chp_systems": find_keyword(self.description, self.CHP_SYSTEMS), "distribution_system": find_keyword(self.description, self.DISTRIBUTION_SYSTEM_KEYWORDS), "no_system_present": find_keyword(self.description, self.NO_SYSTEM_PRESENT_KEYWORDS), "appliance": find_keyword(self.description, self.APPLIANCE_SYSTEMS), } assumed_found = find_keyword(self.description, self.ASSUMED) result["assumed"] = True if assumed_found else False return result