Fixing initial display of letter count
[gc-dialer] / src / backends / file_backend.py
1 #!/usr/bin/python
2
3 """
4 DialCentral - Front end for Google's Grand Central service.
5 Copyright (C) 2008  Eric Warnke ericew AT gmail DOT com
6
7 This library is free software; you can redistribute it and/or
8 modify it under the terms of the GNU Lesser General Public
9 License as published by the Free Software Foundation; either
10 version 2.1 of the License, or (at your option) any later version.
11
12 This library is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 Lesser General Public License for more details.
16
17 You should have received a copy of the GNU Lesser General Public
18 License along with this library; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
20
21 Filesystem backend for contact support
22 """
23
24
25 import os
26 import csv
27
28
29 class CsvAddressBook(object):
30         """
31         Currently supported file format
32         @li Has the first line as a header
33         @li Escapes with quotes
34         @li Comma as delimiter
35         @li Column 0 is name, column 1 is number
36         """
37
38         def __init__(self, name, csvPath):
39                 self._name = name
40                 self._csvPath = csvPath
41                 self._contacts = {}
42
43         @property
44         def name(self):
45                 return self._name
46
47         def update_contacts(self, force = True):
48                 if not force or not self._contacts:
49                         return
50                 self._contacts = dict(
51                         self._read_csv(self._csvPath)
52                 )
53
54         def get_contacts(self):
55                 """
56                 @returns Iterable of (contact id, contact name)
57                 """
58                 if not self._contacts:
59                         self._contacts = dict(
60                                 self._read_csv(self._csvPath)
61                         )
62                 return self._contacts
63
64         def _read_csv(self, csvPath):
65                 try:
66                         csvReader = iter(csv.reader(open(csvPath, "rU")))
67                 except IOError, e:
68                         if e.errno != 2:
69                                 raise
70                         return
71
72                 header = csvReader.next()
73                 nameColumn, phoneColumns = self._guess_columns(header)
74
75                 yieldCount = 0
76                 for row in csvReader:
77                         contactDetails = []
78                         for (phoneType, phoneColumn) in phoneColumns:
79                                 try:
80                                         if len(row[phoneColumn]) == 0:
81                                                 continue
82                                         contactDetails.append({
83                                                 "phoneType": phoneType,
84                                                 "phoneNumber": row[phoneColumn],
85                                         })
86                                 except IndexError:
87                                         pass
88                         if 0 < len(contactDetails):
89                                 yield str(yieldCount), {
90                                         "contactId": "%s-%d" % (self._name, yieldCount),
91                                         "name": row[nameColumn],
92                                         "numbers": contactDetails,
93                                 }
94                                 yieldCount += 1
95
96         @classmethod
97         def _guess_columns(cls, row):
98                 names = []
99                 phones = []
100                 for i, item in enumerate(row):
101                         if 0 <= item.lower().find("name"):
102                                 names.append((item, i))
103                         elif 0 <= item.lower().find("phone"):
104                                 phones.append((item, i))
105                         elif 0 <= item.lower().find("mobile"):
106                                 phones.append((item, i))
107                 if len(names) == 0:
108                         names.append(("Name", 0))
109                 if len(phones) == 0:
110                         phones.append(("Phone", 1))
111
112                 return names[0][1], phones
113
114
115 class FilesystemAddressBookFactory(object):
116
117         FILETYPE_SUPPORT = {
118                 "csv": CsvAddressBook,
119         }
120
121         def __init__(self, path):
122                 self._path = path
123
124         def get_addressbooks(self):
125                 for root, dirs, filenames in os.walk(self._path):
126                         for filename in filenames:
127                                 try:
128                                         name, ext = filename.rsplit(".", 1)
129                                 except ValueError:
130                                         continue
131
132                                 try:
133                                         cls = self.FILETYPE_SUPPORT[ext]
134                                 except KeyError:
135                                         continue
136                                 yield cls(name, os.path.join(root, filename))