Files
monorepo/band/band_contacts_to_google_contacts.py
T

149 lines
6.0 KiB
Python

import csv
import argparse
import datetime
import re
def clean_instrument_name(instrument_raw):
"""Cleans the instrument name based on Percussion, Winds, and Default rules."""
# Strip numerical prefix (e.g., "12 - Percussion (electronics)" -> "Percussion (electronics)")
if '-' in instrument_raw:
instrument = instrument_raw.split('-', 1)[-1].strip()
else:
instrument = instrument_raw.strip()
lower_inst = instrument.lower()
# 1. The Percussion Rule
if 'percussion' in lower_inst and '(' in instrument and ')' in instrument:
match = re.search(r'\((.*?)\)', instrument)
if match:
return match.group(1).strip().title()
# 2. The Winds/General Rule
elif '(' in instrument and ')' in instrument:
return re.sub(r'\(.*?\)', '', instrument).strip()
# 3. Default Rule
return instrument
def get_category(instrument):
"""Categorize the instrument into Woodwinds, Brass, Percussion, or Colorguard."""
inst_lower = instrument.lower()
if any(x in inst_lower for x in ['flute', 'clarinet', 'sax', 'oboe', 'bassoon', 'piccolo']):
return 'Woodwinds'
if any(x in inst_lower for x in ['trumpet', 'mellophone', 'horn', 'trombone', 'baritone', 'euphonium', 'tuba', 'sousaphone']):
return 'Brass'
if any(x in inst_lower for x in ['percussion', 'snare', 'tenor', 'drum', 'cymbal', 'marimba', 'vibraphone', 'timpani', 'bells', 'electronics']):
return 'Percussion'
if 'guard' in inst_lower or 'color' in inst_lower:
return 'Colorguard'
return 'Leadership' # Fallback for generic roles like Drum Major
def format_phone(phone_str):
"""Clean and standardize the phone numbers."""
phone = str(phone_str).strip()
if not phone:
return ""
# Leave non-US international codes as they are
if phone.startswith('+') and not phone.startswith('+1'):
return phone
# Standardize US numbers to XXX-XXX-XXXX
digits = re.sub(r'\D', '', phone)
if len(digits) == 10:
return f"{digits[:3]}-{digits[3:6]}-{digits[6:]}"
elif len(digits) == 11 and digits.startswith('1'):
return f"{digits[1:4]}-{digits[4:7]}-{digits[7:]}"
return phone
def main():
parser = argparse.ArgumentParser(description="Convert Band Leadership CSV to Google Contacts format.")
parser.add_argument("input_file", help="Path to the input CSV file")
parser.add_argument("-y", "--year", type=int, default=datetime.datetime.now().year,
help="Target year for labels and grade calculation (defaults to current year)")
parser.add_argument("-o", "--output", default="google_contacts_import.csv",
help="Output file name")
args = parser.parse_args()
target_year = args.year
# Google Contacts Headers matching your expected output
google_headers = [
"First Name", "Middle Name", "Last Name", "Phonetic First Name", "Phonetic Middle Name",
"Phonetic Last Name", "Name Prefix", "Name Suffix", "Nickname", "File As",
"Organization Name", "Organization Title", "Organization Department", "Birthday", "Notes",
"Photo", "Labels", "E-mail 1 - Label", "E-mail 1 - Value", "Phone 1 - Label",
"Phone 1 - Value", "Phone 2 - Label", "Phone 2 - Value", "Phone 3 - Label", "Phone 3 - Value",
"Address 1 - Label", "Address 1 - Formatted", "Address 1 - Street", "Address 1 - City",
"Address 1 - PO Box", "Address 1 - Region", "Address 1 - Postal Code", "Address 1 - Country",
"Address 1 - Extended Address"
]
with open(args.input_file, mode='r', encoding='utf-8') as infile, \
open(args.output, mode='w', encoding='utf-8', newline='') as outfile:
reader = csv.DictReader(infile)
writer = csv.DictWriter(outfile, fieldnames=google_headers)
writer.writeheader()
for row in reader:
# Skip empty rows or the trailing blank commas in the source file
name_field = row.get('NAME', '').strip()
if not name_field or name_field == ',':
continue
# Parse Name ("Last, First")
name_parts = name_field.split(',')
last_name = name_parts[0].strip() if len(name_parts) > 0 else ""
first_name = name_parts[1].strip() if len(name_parts) > 1 else ""
# Parse and Clean Instrument
section_raw = row.get('SECTION', '').strip()
instrument = clean_instrument_name(section_raw)
if not instrument:
instrument = "Unknown"
# Parse Grade and formulate Notes
grade_raw = row.get('GRADE', '').strip()
grade_match = re.match(r"(\d{4})\s*\((.*?)\)", grade_raw)
if grade_match:
grad_year = grade_match.group(1)
grade_level = grade_match.group(2).capitalize()
notes = f"Class of {grad_year}. A {grade_level} in {target_year}"
else:
notes = grade_raw
# Determine Label (Appended with Marching Band)
category = get_category(instrument)
label = f"{target_year} {category} ::: Marching Band {target_year} ::: * myContacts"
# Build the output row
out_row = {key: "" for key in google_headers}
out_row["First Name"] = first_name
out_row["Last Name"] = f"{last_name} ({instrument})"
out_row["Notes"] = notes
out_row["Labels"] = label
# Map Email
email = row.get('EMAIL', '').strip()
if email:
out_row["E-mail 1 - Label"] = "Home"
out_row["E-mail 1 - Value"] = email
# Map Phone
phone = row.get('PHONE', '').strip()
if phone:
out_row["Phone 1 - Label"] = "Mobile"
out_row["Phone 1 - Value"] = format_phone(phone)
writer.writerow(out_row)
print(f"Success! Formatted contacts saved to {args.output}")
if __name__ == '__main__':
main()