Skip to content

Config loader

Configuration and command-line argument handling for MAY.

build_filters(config)

Build the geographical filter from config.

A run either loads all units or filters to one level's codes, both declared under geography: in the config. Levels are referenced by their config label (no hardcoded SGU/MGU/LGU), so any level naming works.

Parameters:

Name Type Description Default
config

Configuration dictionary

required

Returns:

Type Description

Filter dictionary with 'level' and 'codes' keys, or None if no filter

Source code in may/config_loader.py
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
def build_filters(config):
    """
    Build the geographical filter from config.

    A run either loads all units or filters to one level's codes, both declared
    under `geography:` in the config. Levels are referenced by their config label
    (no hardcoded SGU/MGU/LGU), so any level naming works.

    Args:
        config: Configuration dictionary

    Returns:
        Filter dictionary with 'level' and 'codes' keys, or None if no filter
    """
    geo_config = config.get('geography', {})

    if geo_config.get('load_all', False):
        return None

    filter_config = geo_config.get('filter', {})
    if not filter_config or not filter_config.get('level'):
        return None

    level = filter_config['level']
    codes = []

    # Load codes from file if specified
    if filter_config.get('file'):
        codes = list(Geography.load_codes_from_file(filter_config['file']))
    # Otherwise use inline codes
    elif filter_config.get('codes'):
        codes = filter_config['codes']

    # Only return filter if we have codes
    if codes:
        return {'level': level, 'codes': codes}

    return None

load_config(config_path='config.yaml')

Load configuration from YAML file.

Parameters:

Name Type Description Default
config_path

Path to the config file

'config.yaml'

Returns:

Type Description

Dictionary with configuration

Source code in may/config_loader.py
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
def load_config(config_path="config.yaml"):
    """
    Load configuration from YAML file.

    Args:
        config_path: Path to the config file

    Returns:
        Dictionary with configuration
    """
    if not os.path.exists(config_path):
        logger.warning(f"Config file not found: {config_path}, using defaults")
        return {}

    with open(config_path, 'r', encoding='utf-8-sig') as f:
        config = yaml.safe_load(f)

    logger.info(f"Loaded configuration from {config_path}")
    return config

setup_geography(config=None)

Set up geography from config.

Parameters:

Name Type Description Default
config

Configuration dict (if None, loads the default config file)

None

Returns:

Type Description

Tuple of (Geography object, filters dict)

Source code in may/config_loader.py
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
def setup_geography(config=None):
    """
    Set up geography from config.

    Args:
        config: Configuration dict (if None, loads the default config file)

    Returns:
        Tuple of (Geography object, filters dict)
    """
    if config is None:
        config = load_config()

    # Build filters from config
    filters = build_filters(config)

    if filters:
        logger.info(f"Using filter: {filters['level']} with {len(filters['codes'])} codes")
    else:
        logger.info("Loading all geographical units (no filters)")

    # Get data directory and levels from config
    geo_config = config.get('geography', {})
    data_dir = pr.resolve(geo_config.get('data_dir', 'data/geography'))
    levels = geo_config.get('levels')  # required; Geography fails loud if absent

    # File keys are explicit: hierarchy_file is a path or list of paths,
    # coord_files maps level label -> path or list of paths. Both accept
    # ${...} templating; relative paths resolve against data_dir.
    hierarchy_file = _resolve_spec(geo_config.get('hierarchy_file'))
    coord_files = {
        level: _resolve_spec(spec)
        for level, spec in (geo_config.get('coord_files') or {}).items()
    }

    # Create Geography object
    geo = Geography(
        data_dir=data_dir,
        filters=filters,
        levels=levels,
        hierarchy_file=hierarchy_file,
        coord_files=coord_files,
    )

    return geo, filters