mainegeo.lookups
1__docformat__ = 'google' 2 3__all__ = [ 4 'Overrides', 5 'TownshipData', 6 'CountyData' 7] 8 9import json 10from ruamel.yaml import YAML 11from dataclasses import dataclass 12from functools import cache, cached_property 13from typing import ClassVar 14from pathlib import Path 15 16from mainegeo._vendor import invert_list_of_dicts 17 18from mainegeo.paths import ( 19 OVERRIDES_YAML, 20 COUNTIES_JSON, 21 TOWNSHIPS_JSON 22) 23 24yaml = YAML() 25yaml.indent(mapping = 2, sequence = 4) 26 27@dataclass 28class Lookup: 29 DATA_SOURCE: ClassVar[Path] = None 30 31 def __post_init__(self): 32 self.set_data() 33 self.set_convenience_attrs() 34 35 @classmethod 36 @cache 37 def get_lookup(cls): 38 return cls() 39 40 @property 41 def loader(self): 42 extension = self.DATA_SOURCE.suffix.lower() 43 if extension == '.json': 44 return json.load 45 elif extension in ('.yaml', '.yml'): 46 return yaml.load 47 else: 48 raise ValueError(f'Unsupported extension: {extension}') 49 50 def load_data(self): 51 with open(self.DATA_SOURCE) as f: 52 return self.loader(f) 53 54 def set_data(self): 55 data = self.load_data() 56 if isinstance(data, list): 57 data = invert_list_of_dicts(data) 58 self.data = data 59 60 def set_convenience_attrs(self): 61 for key in self.data.keys(): 62 if key in self.__dataclass_fields__.keys(): 63 setattr(self, key, self.data[key]) 64 65@dataclass 66class Overrides(Lookup): 67 known_typos: list[dict] = None 68 ambiguous_groups: list[dict] = None 69 70 DATA_SOURCE: ClassVar[Path] = OVERRIDES_YAML 71 72 def add_typo(self, original: str, replacement: str, notes: str = None): 73 data = self.load_data() 74 entry_template = { 75 'original': original, 76 'replacement': replacement, 77 'notes': notes 78 } 79 entry = {k: v for k, v in entry_template.items() if v is not None} 80 data['known_typos'].append(entry) 81 82 with open(self.DATA_SOURCE, 'w') as f: 83 yaml.dump(data, f) 84 85 self.__post_init__() 86 87 def add_group_pattern(self, pattern: str, replacement: str, notes: str = None): 88 data = self.load_data() 89 entry_template = { 90 'pattern': pattern, 91 'replacement': replacement, 92 'notes': notes 93 } 94 entry = {k: v for k, v in entry_template.items() if v is not None} 95 data['ambiguous_groups'].append(entry) 96 97 with open(self.DATA_SOURCE, 'w') as f: 98 yaml.dump(data, f) 99 100 self.__post_init__() 101 102@dataclass 103class TownshipData(Lookup): 104 town: list[str] = None 105 106 DATA_SOURCE: ClassVar[Path] = TOWNSHIPS_JSON 107 108@dataclass 109class CountyData(Lookup): 110 county_name: list[str] = None 111 sos_county: list[str] = None 112 county_fips: list[str] | list[int] = None 113 114 DATA_SOURCE: ClassVar[Path] = COUNTIES_JSON 115 116 def __post_init__(self): 117 self.set_data() 118 self.set_convenience_attrs() 119 self.county_fips = [int(fips) for fips in self.county_fips] 120 121 @cached_property 122 def code_to_fips(self): 123 return dict(zip(self.sos_county, self.county_fips)) 124 125 @cached_property 126 def name_to_fips(self): 127 return dict(zip(self.county_name, self.county_fips)) 128 129 @cached_property 130 def fips_to_code(self): 131 return dict(zip(self.county_fips, self.sos_county)) 132 133 @cached_property 134 def name_to_code(self): 135 return dict(zip(self.county_name, self.sos_county)) 136 137 @cached_property 138 def code_to_name(self): 139 return dict(zip(self.sos_county, self.county_name)) 140 141 @cached_property 142 def fips_to_name(self): 143 return dict(zip(self.county_fips, self.county_name))
66@dataclass 67class Overrides(Lookup): 68 known_typos: list[dict] = None 69 ambiguous_groups: list[dict] = None 70 71 DATA_SOURCE: ClassVar[Path] = OVERRIDES_YAML 72 73 def add_typo(self, original: str, replacement: str, notes: str = None): 74 data = self.load_data() 75 entry_template = { 76 'original': original, 77 'replacement': replacement, 78 'notes': notes 79 } 80 entry = {k: v for k, v in entry_template.items() if v is not None} 81 data['known_typos'].append(entry) 82 83 with open(self.DATA_SOURCE, 'w') as f: 84 yaml.dump(data, f) 85 86 self.__post_init__() 87 88 def add_group_pattern(self, pattern: str, replacement: str, notes: str = None): 89 data = self.load_data() 90 entry_template = { 91 'pattern': pattern, 92 'replacement': replacement, 93 'notes': notes 94 } 95 entry = {k: v for k, v in entry_template.items() if v is not None} 96 data['ambiguous_groups'].append(entry) 97 98 with open(self.DATA_SOURCE, 'w') as f: 99 yaml.dump(data, f) 100 101 self.__post_init__()
DATA_SOURCE: ClassVar[pathlib.Path] =
PosixPath('/Users/lydia-rosekesich/repositories/maine-geography/src/mainegeo/data/overrides.yaml')
def
add_typo(self, original: str, replacement: str, notes: str = None):
73 def add_typo(self, original: str, replacement: str, notes: str = None): 74 data = self.load_data() 75 entry_template = { 76 'original': original, 77 'replacement': replacement, 78 'notes': notes 79 } 80 entry = {k: v for k, v in entry_template.items() if v is not None} 81 data['known_typos'].append(entry) 82 83 with open(self.DATA_SOURCE, 'w') as f: 84 yaml.dump(data, f) 85 86 self.__post_init__()
def
add_group_pattern(self, pattern: str, replacement: str, notes: str = None):
88 def add_group_pattern(self, pattern: str, replacement: str, notes: str = None): 89 data = self.load_data() 90 entry_template = { 91 'pattern': pattern, 92 'replacement': replacement, 93 'notes': notes 94 } 95 entry = {k: v for k, v in entry_template.items() if v is not None} 96 data['ambiguous_groups'].append(entry) 97 98 with open(self.DATA_SOURCE, 'w') as f: 99 yaml.dump(data, f) 100 101 self.__post_init__()
Inherited Members
103@dataclass 104class TownshipData(Lookup): 105 town: list[str] = None 106 107 DATA_SOURCE: ClassVar[Path] = TOWNSHIPS_JSON
DATA_SOURCE: ClassVar[pathlib.Path] =
PosixPath('/Users/lydia-rosekesich/repositories/maine-geography/src/mainegeo/data/townships.json')
Inherited Members
109@dataclass 110class CountyData(Lookup): 111 county_name: list[str] = None 112 sos_county: list[str] = None 113 county_fips: list[str] | list[int] = None 114 115 DATA_SOURCE: ClassVar[Path] = COUNTIES_JSON 116 117 def __post_init__(self): 118 self.set_data() 119 self.set_convenience_attrs() 120 self.county_fips = [int(fips) for fips in self.county_fips] 121 122 @cached_property 123 def code_to_fips(self): 124 return dict(zip(self.sos_county, self.county_fips)) 125 126 @cached_property 127 def name_to_fips(self): 128 return dict(zip(self.county_name, self.county_fips)) 129 130 @cached_property 131 def fips_to_code(self): 132 return dict(zip(self.county_fips, self.sos_county)) 133 134 @cached_property 135 def name_to_code(self): 136 return dict(zip(self.county_name, self.sos_county)) 137 138 @cached_property 139 def code_to_name(self): 140 return dict(zip(self.sos_county, self.county_name)) 141 142 @cached_property 143 def fips_to_name(self): 144 return dict(zip(self.county_fips, self.county_name))
CountyData( county_name: list[str] = None, sos_county: list[str] = None, county_fips: list[str] | list[int] = None)
DATA_SOURCE: ClassVar[pathlib.Path] =
PosixPath('/Users/lydia-rosekesich/repositories/maine-geography/src/mainegeo/data/counties.json')