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))
@dataclass
class Overrides(Lookup):
 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__()
Overrides(known_typos: list[dict] = None, ambiguous_groups: list[dict] = None)
known_typos: list[dict] = None
ambiguous_groups: list[dict] = None
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__()
@dataclass
class TownshipData(Lookup):
103@dataclass       
104class TownshipData(Lookup):
105    town: list[str] = None
106
107    DATA_SOURCE: ClassVar[Path] = TOWNSHIPS_JSON
TownshipData(town: list[str] = None)
town: list[str] = None
DATA_SOURCE: ClassVar[pathlib.Path] = PosixPath('/Users/lydia-rosekesich/repositories/maine-geography/src/mainegeo/data/townships.json')
@dataclass
class CountyData(Lookup):
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)
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')
code_to_fips
122    @cached_property
123    def code_to_fips(self):
124        return dict(zip(self.sos_county, self.county_fips))
name_to_fips
126    @cached_property
127    def name_to_fips(self):
128        return dict(zip(self.county_name, self.county_fips))
fips_to_code
130    @cached_property
131    def fips_to_code(self):
132        return dict(zip(self.county_fips, self.sos_county))
name_to_code
134    @cached_property
135    def name_to_code(self):
136        return dict(zip(self.county_name, self.sos_county))
code_to_name
138    @cached_property
139    def code_to_name(self):
140        return dict(zip(self.sos_county, self.county_name))
fips_to_name
142    @cached_property
143    def fips_to_name(self):
144        return dict(zip(self.county_fips, self.county_name))