1 | #!/usr/bin/python |
---|
2 | # License: MIT or GPL2+, your choice. |
---|
3 | |
---|
4 | import sqlite3 |
---|
5 | import sys |
---|
6 | |
---|
7 | def load(filename): |
---|
8 | connection = sqlite3.connect(filename) |
---|
9 | cursor = connection.cursor() |
---|
10 | data = [] |
---|
11 | people = list(cursor.execute("SELECT _id, name, notes, times_contacted, last_time_contacted, starred, primary_phone, primary_organization, primary_email, photo_version, custom_ringtone, send_to_voicemail, phonetic_name FROM people")) |
---|
12 | for people_id, name, notes, times_contacted, last_time_contacted, starred, primary_phone_id, primary_organization_id, primary_email_id, photo_version, custom_ringtone, send_to_voicemail, phonetic_name \ |
---|
13 | in people: |
---|
14 | person = { |
---|
15 | 'name': name, |
---|
16 | 'notes': notes, |
---|
17 | 'times_contacted': times_contacted, |
---|
18 | 'last_time_contacted': last_time_contacted, |
---|
19 | 'starred': starred, |
---|
20 | 'photo_version': photo_version, |
---|
21 | 'custom_ringtone': custom_ringtone, |
---|
22 | 'send_to_voicemail': send_to_voicemail, |
---|
23 | 'phonetic_name': phonetic_name, |
---|
24 | } |
---|
25 | # primary_phone_id |
---|
26 | # primary_organization_id |
---|
27 | # primary_email_id |
---|
28 | phones = list(cursor.execute("SELECT type, number, label from phones where person=?", (people_id,))) |
---|
29 | #print phones |
---|
30 | person['phones'] = [] |
---|
31 | for phone_type, phone_number, label in phones: |
---|
32 | if not label: |
---|
33 | label = { |
---|
34 | 0: 'Home', |
---|
35 | 1: 'Mobile', |
---|
36 | 2: 'Work', |
---|
37 | 3: 'Work-Fax', |
---|
38 | 4: 'Home-Fax', |
---|
39 | 5: 'Pager', |
---|
40 | 6: 'Other', |
---|
41 | }[phone_type] |
---|
42 | person['phones'].append({ |
---|
43 | 'number': phone_number, |
---|
44 | 'label': label, |
---|
45 | }) |
---|
46 | data.append(person) |
---|
47 | return data |
---|
48 | |
---|
49 | |
---|
50 | def norm_phone_number(phone_number): |
---|
51 | phone_number = phone_number.replace('-', '') |
---|
52 | if not phone_number.startswith('+'): |
---|
53 | phone_number = phone_number[-10:] |
---|
54 | phone_number = '-'.join(part for part in ( |
---|
55 | phone_number[:-10], |
---|
56 | phone_number[-10:-7], |
---|
57 | phone_number[-7:-4], |
---|
58 | phone_number[-4:], |
---|
59 | ) if part) |
---|
60 | return phone_number |
---|
61 | |
---|
62 | |
---|
63 | def clean_data(data): |
---|
64 | """Cleans phone data *in-place*""" |
---|
65 | # Coallesce "John Doe" and "John Doe1" into one contact |
---|
66 | names = [c['name'] for c in data] |
---|
67 | assert len(names) == len(set(names)) |
---|
68 | by_name = dict((c['name'], c) for c in data) |
---|
69 | bad_names = [n for n in names if n.endswith('1')] |
---|
70 | for bad_name in bad_names: |
---|
71 | name = bad_name.rstrip('1') |
---|
72 | bad_contact = by_name[bad_name] |
---|
73 | if name in by_name: |
---|
74 | contact = by_name[name] |
---|
75 | data.remove(bad_contact) |
---|
76 | contact['phones'].extend(bad_contact['phones']) |
---|
77 | if bad_contact['notes']: |
---|
78 | if contact['notes']: |
---|
79 | contact['notes'] += '\n' |
---|
80 | contact['notes'] += bad_contact['notes'] |
---|
81 | else: |
---|
82 | bad_contact['name'] = name |
---|
83 | |
---|
84 | # Clean up duplicate phone numbers and labels |
---|
85 | for contact in data: |
---|
86 | phones = contact['phones'] |
---|
87 | new_phones = [] |
---|
88 | new_numbers = set() |
---|
89 | new_labels = set() |
---|
90 | for phone in contact['phones']: |
---|
91 | phone_number = norm_phone_number(phone['number']) |
---|
92 | if phone_number in new_numbers: |
---|
93 | continue # skip duplicates |
---|
94 | proposed_label = phone['label'] |
---|
95 | n=2 |
---|
96 | while proposed_label in new_labels: |
---|
97 | proposed_label = "%s%s" % (phone['label'], n) |
---|
98 | n += 1 |
---|
99 | new_numbers.add(phone_number) |
---|
100 | new_labels.add(proposed_label) |
---|
101 | new_phones.append({'label': proposed_label, 'number': phone_number}) |
---|
102 | contact['phones'] = new_phones |
---|
103 | |
---|
104 | |
---|
105 | def as_vcard(contact): |
---|
106 | if not contact['phones']: |
---|
107 | sys.stderr.write("no phone: %s\n" % contact['name']) |
---|
108 | name_parts = contact['name'].split() |
---|
109 | if len(name_parts) == 2: |
---|
110 | first_name = name_parts[0] |
---|
111 | last_name = name_parts[1] |
---|
112 | else: |
---|
113 | first_name = contact['name'] |
---|
114 | last_name = '' |
---|
115 | card_lines = [ |
---|
116 | "BEGIN:VCARD", |
---|
117 | "VERSION:2.1", |
---|
118 | "N:%s;%s;;;" % (last_name, first_name), |
---|
119 | "FN:%s" % contact['name'], |
---|
120 | ] |
---|
121 | for phone in contact['phones']: |
---|
122 | card_lines.append("TEL;X-%s:%s" % (phone['label'], phone['number'])) |
---|
123 | if contact['notes']: |
---|
124 | notes = contact['notes'] |
---|
125 | if '\n' in notes: |
---|
126 | sys.stderr.write("multiline note is: %r\n" % notes) |
---|
127 | notes = notes.replace("\n", ". ").strip() |
---|
128 | card_lines.append("NOTE:%s" % notes) |
---|
129 | card_lines.append("END:VCARD") |
---|
130 | return "\n".join(card_lines) |
---|
131 | |
---|
132 | |
---|
133 | def as_vcards(contacts): |
---|
134 | return "\n".join(as_vcard(c) for c in contacts if c['phones']) |
---|
135 | |
---|
136 | |
---|
137 | def main(argv): |
---|
138 | filename = argv[1] |
---|
139 | data = load(filename) |
---|
140 | clean_data(data) |
---|
141 | print as_vcards(data) |
---|
142 | |
---|
143 | |
---|
144 | if __name__ == '__main__': |
---|
145 | sys.exit(main(sys.argv)) |
---|