electionnepal / constituency_utils.py
sumitasthaai's picture
Deploy Nepal Election Analysis app
6f4592b unverified
"""
Nepal Federal Election - Constituency Utilities Module
========================================================
Reusable utilities for constituency-wise analysis and data management.
This module provides:
- Comprehensive constituency mapping (165 federal constituencies)
- Province-District-Constituency hierarchical relationships
- Constituency validation and lookup functions
- Helper functions for constituency-based queries
- Integration with election results and voter databases
Author: Election Analysis Project
Date: January 2026
"""
import json
from typing import Dict, List, Optional, Tuple
import pandas as pd
# ============================================================================
# CONSTITUENCY DATABASE - Nepal Federal Election 2079 (165 Constituencies)
# ============================================================================
# Province Names (English and Nepali)
PROVINCE_NAMES = {
1: {"en": "Koshi", "np": "कोशी"},
2: {"en": "Madhesh", "np": "मधेश"},
3: {"en": "Bagmati", "np": "बागमती"},
4: {"en": "Gandaki", "np": "गण्डकी"},
5: {"en": "Lumbini", "np": "लुम्बिनी"},
6: {"en": "Karnali", "np": "कर्णाली"},
7: {"en": "Sudurpashchim", "np": "सुदूरपश्चिम"}
}
# Complete District-to-Constituency Mapping (Based on Election 2079 Data)
# Each constituency is uniquely identified by combining district and constituency number
# Format: "District-ConstNumber" (e.g., "Jhapa-1", "Kathmandu-5")
CONSTITUENCY_MAPPING = {
# ========== Province 1: कोशी (28 constituencies) ==========
1: {
"ताप्लेजुंग": ["ताप्लेजुंग-१"],
"पाँचथर": ["पाँचथर-१"],
"इलाम": ["इलाम-१", "इलाम-२"],
"झापा": ["झापा-१", "झापा-२", "झापा-३", "झापा-४", "झापा-५"],
"संखुवासभा": ["संखुवासभा-१"],
"तेर्हथुम": ["तेर्हथुम-१"],
"भोजपुर": ["भोजपुर-१"],
"धनकुटा": ["धनकुटा-१"],
"मोरङ्ग": ["मोरङ्ग-१", "मोरङ्ग-२", "मोरङ्ग-३", "मोरङ्ग-४", "मोरङ्ग-५", "मोरङ्ग-६"],
"सुनसरी": ["सुनसरी-१", "सुनसरी-२", "सुनसरी-३"],
"सोलुखुम्बु": ["सोलुखुम्बु-१"],
"खोटाङ्ग": ["खोटाङ्ग-१"],
"ओखलढुंगा": ["ओखलढुंगा-१"],
"उदयपुर": ["उदयपुर-१"],
},
# ========== Province 2: मधेश (32 constituencies) ==========
2: {
"सप्तरी": ["सप्तरी-१", "सप्तरी-२", "सप्तरी-३"],
"सिराहा": ["सिराहा-१", "सिराहा-२", "सिराहा-३"],
"धनुषा": ["धनुषा-१", "धनुषा-२", "धनुषा-३", "धनुषा-४"],
"महोत्तरी": ["महोत्तरी-१", "महोत्तरी-२", "महोत्तरी-३", "महोत्तरी-४"],
"सर्लाही": ["सर्लाही-१", "सर्लाही-२", "सर्लाही-३", "सर्लाही-४"],
"रौतहट": ["रौतहट-१", "रौतहट-२", "रौतहट-३"],
"बारा": ["बारा-१", "बारा-२", "बारा-३", "बारा-४"],
"पर्सा": ["पर्सा-१", "पर्सा-२", "पर्सा-३", "पर्सा-४"],
},
# ========== Province 3: बागमती (52 constituencies) ==========
3: {
"सिन्धुली": ["सिन्धुली-१", "सिन्धुली-२"],
"रामेछाप": ["रामेछाप-१"],
"दोलखा": ["दोलखा-१"],
"भक्तपुर": ["भक्तपुर-१", "भक्तपुर-२"],
"धादिङ्ग": ["धादिङ्ग-१", "धादिङ्ग-२"],
"काठमाडौं": ["काठमाडौं-१", "काठमाडौं-२", "काठमाडौं-३", "काठमाडौं-४",
"काठमाडौं-५", "काठमाडौं-६", "काठमाडौं-७", "काठमाडौं-८",
"काठमाडौं-९", "काठमाडौं-१०"],
"काभ्रेपलाञ्चोक": ["काभ्रेपलाञ्चोक-१", "काभ्रेपलाञ्चोक-२", "काभ्रेपलाञ्चोक-३"],
"ललितपुर": ["ललितपुर-१", "ललितपुर-२", "ललितपुर-३"],
"नुवाकोट": ["नुवाकोट-१"],
"रसुवा": ["रसुवा-१"],
"सिन्धुपाल्चोक": ["सिन्धुपाल्चोक-१", "सिन्धुपाल्चोक-२"],
"चितवन": ["चितवन-१", "चितवन-२", "चितवन-३"],
"मकवानपुर": ["मकवानपुर-१", "मकवानपुर-२", "मकवानपुर-३"],
},
# ========== Province 4: गण्डकी (18 constituencies) ==========
4: {
"गोरखा": ["गोरखा-१", "गोरखा-२"],
"लमजुंग": ["लमजुंग-१"],
"तनहुँ": ["तनहुँ-१", "तनहुँ-२"],
"स्याङ्जा": ["स्याङ्जा-१", "स्याङ्जा-२"],
"कास्की": ["कास्की-१", "कास्की-२", "कास्की-३"],
"मनाङ्ग": ["मनाङ्ग-१"],
"मुस्तांग": ["मुस्तांग-१"],
"पर्वत": ["पर्वत-१"],
"म्याग्दी": ["म्याग्दी-१"],
"बागलुङ": ["बागलुङ-१", "बागलुङ-२"],
"नवलपरासी (बर्दघाट सुस्ता पूर्व)": ["नवलपरासी (बर्दघाट सुस्ता पूर्व)-१", "नवलपरासी (बर्दघाट सुस्ता पूर्व)-२"],
},
# ========== Province 5: लुम्बिनी (24 constituencies) ==========
5: {
"गुल्मी": ["गुल्मी-१", "गुल्मी-२"],
"पाल्पा": ["पाल्पा-१", "पाल्पा-२"],
"रुपन्देही": ["रुपन्देही-१", "रुपन्देही-२", "रुपन्देही-३", "रुपन्देही-४", "रुपन्देही-५"],
"अर्घाखांची": ["अर्घाखांची-१"],
"कपिलवस्तु": ["कपिलवस्तु-१", "कपिलवस्तु-२", "कपिलवस्तु-३"],
"नवलपरासी (बर्दघाट सुस्ता पश्चिम)": ["नवलपरासी (बर्दघाट सुस्ता पश्चिम)-१"],
"प्यूठान": ["प्यूठान-१"],
"रोल्पा": ["रोल्पा-१"],
"रुकुम पूर्व": ["रुकुम पूर्व-१"],
"दाङ": ["दाङ-१", "दाङ-२", "दाङ-३"],
"बाँके": ["बाँके-१", "बाँके-२", "बाँके-३"],
"बर्दिया": ["बर्दिया-१", "बर्दिया-२"],
},
# ========== Province 6: कर्णाली (12 constituencies) ==========
6: {
"रुकुम पश्चिम": ["रुकुम पश्चिम-१"],
"सल्यान": ["सल्यान-१"],
"डोल्पा": ["डोल्पा-१"],
"जुम्ला": ["जुम्ला-१"],
"कालिकोट": ["कालिकोट-१"],
"मुगु": ["मुगु-१"],
"हुम्ला": ["हुम्ला-१"],
"दैलेख": ["दैलेख-१"],
"जाजरकोट": ["जाजरकोट-१"],
"सुर्खेत": ["सुर्खेत-१", "सुर्खेत-२"],
},
# ========== Province 7: सुदूरपश्चिम (19 constituencies) ==========
7: {
"बाजुरा": ["बाजुरा-१"],
"बझाङ्ग": ["बझाङ्ग-१"],
"अछाम": ["अछाम-१", "अछाम-२"],
"डोटी": ["डोटी-१"],
"कैलाली": ["कैलाली-१", "कैलाली-२", "कैलाली-३", "कैलाली-४"],
"कन्चनपुर": ["कन्चनपुर-१", "कन्चनपुर-२"],
"डडेलधुरा": ["डडेलधुरा-१"],
"बैतडी": ["बैतडी-१"],
"दार्चुला": ["दार्चुला-१"],
}
}
# ============================================================================
# HELPER FUNCTIONS
# ============================================================================
def get_all_constituencies() -> List[str]:
"""
Get a complete list of all 165 federal constituencies.
Returns:
List[str]: List of all constituency identifiers
"""
all_constituencies = []
for province_id, districts in CONSTITUENCY_MAPPING.items():
for district_name, constituencies in districts.items():
all_constituencies.extend(constituencies)
return sorted(all_constituencies)
def get_constituencies_by_province(province_id: int) -> List[str]:
"""
Get all constituencies for a specific province.
Args:
province_id (int): Province ID (1-7)
Returns:
List[str]: List of constituencies in the province
"""
if province_id not in CONSTITUENCY_MAPPING:
return []
constituencies = []
for district_name, const_list in CONSTITUENCY_MAPPING[province_id].items():
constituencies.extend(const_list)
return sorted(constituencies)
def get_constituencies_by_district(district_name: str, province_id: Optional[int] = None) -> List[str]:
"""
Get all constituencies for a specific district.
Args:
district_name (str): District name in Nepali
province_id (int, optional): Province ID to narrow down search
Returns:
List[str]: List of constituencies in the district
"""
if province_id:
if province_id in CONSTITUENCY_MAPPING and district_name in CONSTITUENCY_MAPPING[province_id]:
return CONSTITUENCY_MAPPING[province_id][district_name]
return []
# Search across all provinces
for prov_id, districts in CONSTITUENCY_MAPPING.items():
if district_name in districts:
return districts[district_name]
return []
def get_district_from_constituency(constituency: str) -> Optional[str]:
"""
Extract district name from constituency identifier.
Args:
constituency (str): Constituency identifier (e.g., "काठमाडौं-५")
Returns:
Optional[str]: District name or None if invalid
"""
if '-' not in constituency:
return None
return constituency.split('-')[0]
def get_constituency_number(constituency: str) -> Optional[str]:
"""
Extract constituency number from constituency identifier.
Args:
constituency (str): Constituency identifier (e.g., "काठमाडौं-५")
Returns:
Optional[str]: Constituency number or None if invalid
"""
if '-' not in constituency:
return None
parts = constituency.split('-')
return parts[-1] if len(parts) == 2 else None
def get_province_from_constituency(constituency: str) -> Optional[int]:
"""
Get province ID for a given constituency.
Args:
constituency (str): Constituency identifier
Returns:
Optional[int]: Province ID (1-7) or None if not found
"""
district = get_district_from_constituency(constituency)
if not district:
return None
for province_id, districts in CONSTITUENCY_MAPPING.items():
if district in districts:
return province_id
return None
def validate_constituency(constituency: str) -> bool:
"""
Validate if a constituency identifier is valid.
Args:
constituency (str): Constituency identifier to validate
Returns:
bool: True if valid, False otherwise
"""
return constituency in get_all_constituencies()
def get_constituency_info(constituency: str) -> Optional[Dict]:
"""
Get detailed information about a constituency.
Args:
constituency (str): Constituency identifier
Returns:
Optional[Dict]: Dictionary with constituency details or None
"""
if not validate_constituency(constituency):
return None
district = get_district_from_constituency(constituency)
number = get_constituency_number(constituency)
province_id = get_province_from_constituency(constituency)
if not all([district, number, province_id]):
return None
return {
'constituency_id': constituency,
'district': district,
'number': number,
'province_id': province_id,
'province_name_np': PROVINCE_NAMES[province_id]['np'],
'province_name_en': PROVINCE_NAMES[province_id]['en']
}
def get_constituency_summary_stats() -> Dict:
"""
Get summary statistics about constituencies.
Returns:
Dict: Summary statistics including counts by province
"""
total = 0
by_province = {}
for province_id, districts in CONSTITUENCY_MAPPING.items():
count = sum(len(constituencies) for constituencies in districts.values())
by_province[province_id] = {
'count': count,
'name_np': PROVINCE_NAMES[province_id]['np'],
'name_en': PROVINCE_NAMES[province_id]['en']
}
total += count
return {
'total_constituencies': total,
'total_provinces': 7,
'by_province': by_province
}
def create_constituency_dataframe() -> pd.DataFrame:
"""
Create a pandas DataFrame with all constituency information.
Returns:
pd.DataFrame: DataFrame with columns: constituency_id, district, number, province_id, province_name
"""
data = []
for province_id, districts in CONSTITUENCY_MAPPING.items():
for district_name, constituencies in districts.items():
for constituency in constituencies:
number = get_constituency_number(constituency)
data.append({
'constituency_id': constituency,
'district': district_name,
'constituency_number': number,
'province_id': province_id,
'province_name_np': PROVINCE_NAMES[province_id]['np'],
'province_name_en': PROVINCE_NAMES[province_id]['en']
})
return pd.DataFrame(data)
def search_constituencies(search_term: str, search_in: str = 'all') -> List[Dict]:
"""
Search constituencies by name, district, or province.
Args:
search_term (str): Term to search for
search_in (str): Where to search - 'all', 'district', 'constituency'
Returns:
List[Dict]: List of matching constituency information
"""
results = []
search_term = search_term.lower()
for province_id, districts in CONSTITUENCY_MAPPING.items():
for district_name, constituencies in districts.items():
for constituency in constituencies:
include = False
if search_in == 'all':
include = (search_term in constituency.lower() or
search_term in district_name.lower() or
search_term in PROVINCE_NAMES[province_id]['np'].lower())
elif search_in == 'district':
include = search_term in district_name.lower()
elif search_in == 'constituency':
include = search_term in constituency.lower()
if include:
results.append(get_constituency_info(constituency))
return results
def get_districts_by_province(province_id: int) -> List[str]:
"""
Get all districts in a province.
Args:
province_id (int): Province ID (1-7)
Returns:
List[str]: List of district names in Nepali
"""
if province_id not in CONSTITUENCY_MAPPING:
return []
return sorted(list(CONSTITUENCY_MAPPING[province_id].keys()))
def export_constituency_mapping_json(filepath: str = "constituency_mapping.json"):
"""
Export constituency mapping to JSON file.
Args:
filepath (str): Output file path
"""
export_data = {
'metadata': {
'total_constituencies': len(get_all_constituencies()),
'total_provinces': 7,
'total_districts': sum(len(districts) for districts in CONSTITUENCY_MAPPING.values()),
'created_date': '2026-01-28',
'election': 'Federal Election 2079'
},
'province_names': PROVINCE_NAMES,
'constituencies': CONSTITUENCY_MAPPING
}
with open(filepath, 'w', encoding='utf-8') as f:
json.dump(export_data, f, ensure_ascii=False, indent=2)
print(f"✅ Constituency mapping exported to: {filepath}")
# ============================================================================
# INTEGRATION HELPERS FOR DATABASE QUERIES
# ============================================================================
def build_constituency_filter_query(constituency: Optional[str] = None,
district: Optional[str] = None,
province_id: Optional[int] = None) -> Tuple[str, Dict]:
"""
Build SQL WHERE clause for filtering by constituency hierarchy.
Args:
constituency (str, optional): Specific constituency
district (str, optional): District name
province_id (int, optional): Province ID
Returns:
Tuple[str, Dict]: SQL WHERE clause and parameters dict
"""
conditions = []
params = {}
if constituency:
conditions.append("constituency_id = :constituency")
params['constituency'] = constituency
elif district:
conditions.append("district = :district")
params['district'] = district
elif province_id:
conditions.append("province_id = :province_id")
params['province_id'] = province_id
where_clause = " AND ".join(conditions) if conditions else "1=1"
return where_clause, params
def get_constituency_dropdown_options(province_id: Optional[int] = None,
district: Optional[str] = None) -> List[Tuple[str, str]]:
"""
Get constituency options for dropdown/selectbox (display_text, value).
Args:
province_id (int, optional): Filter by province
district (str, optional): Filter by district
Returns:
List[Tuple[str, str]]: List of (display_text, value) tuples
"""
if district:
constituencies = get_constituencies_by_district(district, province_id)
elif province_id:
constituencies = get_constituencies_by_province(province_id)
else:
constituencies = get_all_constituencies()
return [(f"🗳️ {const}", const) for const in constituencies]
# ============================================================================
# EXAMPLE USAGE & TESTING
# ============================================================================
def print_module_info():
"""Print module information and usage examples."""
stats = get_constituency_summary_stats()
print("=" * 70)
print("🗳️ Nepal Federal Election - Constituency Utilities")
print("=" * 70)
print(f"\n📊 SUMMARY:")
print(f" Total Constituencies: {stats['total_constituencies']}")
print(f" Total Provinces: {stats['total_provinces']}")
print(f"\n📍 CONSTITUENCIES BY PROVINCE:")
for prov_id, info in stats['by_province'].items():
print(f" {prov_id}. {info['name_np']:<20} ({info['name_en']:<15}): {info['count']:>2} constituencies")
print(f"\n✅ Module loaded successfully!")
print(f" Available functions: {len([f for f in dir() if not f.startswith('_')])} functions")
print("=" * 70)
if __name__ == "__main__":
# Test the module
print_module_info()
# Example usage
print("\n🔍 EXAMPLE USAGE:\n")
# Example 1: Get all constituencies in Province 3
print("1. Get constituencies in Bagmati Province:")
bagmati_const = get_constituencies_by_province(3)
print(f" Total: {len(bagmati_const)} constituencies")
print(f" First 5: {bagmati_const[:5]}")
# Example 2: Get constituency info
print("\n2. Get info for 'काठमाडौं-५':")
info = get_constituency_info("काठमाडौं-५")
print(f" {info}")
# Example 3: Search constituencies
print("\n3. Search for 'काठमाडौं':")
results = search_constituencies("काठमाडौं", "district")
print(f" Found {len(results)} constituencies")
# Example 4: Create DataFrame
print("\n4. Create constituency DataFrame:")
df = create_constituency_dataframe()
print(f" Shape: {df.shape}")
print(f" Columns: {df.columns.tolist()}")
print(f"\n Sample rows:")
print(df.head(3).to_string(index=False))