added english localization
[meabook] / database / SQLite.py
1 #!/usr/bin/python
2 # -*- coding: utf-8 -*-
3
4
5 import os
6 import sqlite3
7 from meabook.constants import *
8
9 DATABASE_NAME = 'contacts.db'
10
11 SCHEMA = """
12     begin;
13
14     create table data(
15         id integer,
16         field_id text,
17         value text
18     );
19     create index i_data on data (id);
20
21     create table fields(
22         id integer primary key,
23         name text
24     );
25     create index i_fields on fields (id);
26
27     create table relation(
28         data_id integer,
29         struct_id integer
30     );
31     create index i_relation_data on relation(data_id);
32
33     create table struct(
34         id integer primary key,
35         name text,
36         parent integer
37     );
38     create index i_struct_id on struct(parent);
39     commit;
40
41 """
42
43
44 class SQLite:
45     def __init__(self, basedir):
46         self._path = os.path.join(basedir, DATABASE_NAME)
47         self.conn = None
48         if not os.path.exists(self._path):
49             self.new()
50         else:
51             self.connect()
52
53     def connect(self):
54         """Connects to database."""
55
56         self.conn = sqlite3.connect(self._path, isolation_level="EXCLUSIVE")
57
58     def new(self):
59         """Creates new databse."""
60
61         self.connect()
62         self.conn.executescript(SCHEMA)
63
64     def close(self):
65         """Closes connection with database."""
66
67         self.conn.close()
68
69     def save(self):
70         """Save all changes."""
71
72         self.conn.commit()
73
74     def clear(self):
75         """Clear all database tables."""
76
77         execute = self.conn.execute
78         execute("DELETE from data")
79         execute("DELETE from fields")
80         execute("DELETE from struct")
81         execute("DELETE from relation")
82         self.conn.commit()
83
84     def get_fields(self):
85         """Returns all fields from FIELDS table."""
86
87         return [item[0] for item in self.conn.execute("SELECT name FROM \
88             fields").fetchall() if item[0]!='image']
89
90     # operations with DATA table
91     def add_entry(self, entry):
92         """Adds new entry to database."""
93
94         execute = self.conn.execute
95
96         try:
97             _id = execute("SELECT MAX(id) FROM data").fetchone()[0] \
98                 + 1
99         except TypeError:
100             _id = 1
101
102         for field, values in entry.items():
103             # update FIELDS table
104             field_id = execute("SELECT id FROM fields WHERE name=?", \
105                 (field,)).fetchone()
106             if field_id is None:
107                 execute("INSERT INTO fields values(NULL, ?)", (field,))
108                 field_id = execute("SELECT last_insert_rowid()").fetchone()[0]
109             else:
110                 field_id = field_id[0]
111
112             # update DATA table
113             for value in values:
114                 execute("INSERT INTO data values(?,?,?)", \
115                     (_id, field_id, value))
116
117         # update STRUCT table
118         name = entry[FIELD_TOPLEVEL][0]
119         parent_id = execute("SELECT id FROM struct WHERE name=? ", \
120             (name,)).fetchone()
121         if parent_id is None: # update STRUCT table (FIELD_TOPLEVEL)
122             execute("INSERT INTO struct values(NULL,?,0)", (name,))
123             parent_id = execute("SELECT last_insert_rowid()").fetchone()[0]
124         else:
125             parent_id = parent_id[0]
126
127         name = entry[FIELD_MIDDLELEVEL][0]
128         child_id = execute("SELECT id FROM struct WHERE name=? AND parent=?", \
129             (name, parent_id)).fetchone()
130         if child_id is None: # update STRUCT table (FIELD_MIDDLELEVEL)
131             execute("INSERT INTO struct values(NULL,?,?)", (name, parent_id))
132             child_id = execute("SELECT last_insert_rowid()").fetchone()[0]
133         else:
134             child_id = child_id[0]
135
136         # update RELATION table
137         execute("INSERT INTO relation values(?,?)", (_id, child_id))
138
139     def get_folders(self, parent=None):
140         """
141         Returns list of all folders (items with folders and files)
142         from STRUCT table.
143         """
144
145         if parent is None:  # return all folders on level2
146             return self.conn.execute("SELECT DISTINCT name, id FROM struct \
147                 WHERE parent!=0 ORDER BY name ASC").fetchall()
148         else:
149             return self.conn.execute("SELECT DISTINCT name, id FROM struct \
150                 WHERE parent=? ORDER BY name ASC", (parent,)).fetchall()
151
152     def get_files(self, fields, parent=0):
153         """Finds all files with selected parent"""
154
155         items_dict = {}
156         execute = self.conn.execute
157         fields = dict(execute("SELECT id, name FROM fields WHERE name IN (%s)" \
158             % ','.join('%r' % (field,) for field in fields)).fetchall())
159         """
160         query = "SELECT id, field_id, value FROM data \
161             left join relation on relation.data_id=data.id \
162             where relation.struct_id=? and field_id in (%s)"
163         """
164         query = "SELECT id, field_id, value FROM data \
165             WHERE id IN (SELECT data_id FROM relation WHERE struct_id=?) \
166             AND field_id IN (%s)"
167         data = execute(query % ','.join('%r' % f for f in fields.keys()), \
168             (parent,)).fetchall()
169         for data_id, field_id, value in data:
170             if not items_dict.has_key(data_id):
171                 items_dict[data_id] = {}
172             items_dict[data_id][fields[int(field_id)]] = value
173         return items_dict
174
175     def get_files_by_pattern(self, fields, key='cn', pattern='', \
176         search_from_start=False):
177         """Returns list of all files from DATA table."""
178
179         items_dict = {}
180         if key not in fields:
181             fields.append(str(key))
182         execute = self.conn.execute
183         fields = dict(execute("SELECT id, name FROM fields WHERE name IN (%s)" \
184             % ','.join('%r' % (field,) for field in fields)).fetchall())
185         key_id = 1 
186         for k, val in fields.items():
187             if val == key:
188                 key_id = k
189                 break
190         if search_from_start:
191             s = '%s%%' % pattern.capitalize()
192         else:
193             s = '%%%s%%' % pattern
194         data = execute("SELECT id, field_id, value FROM data WHERE id IN \
195             (SELECT id FROM data WHERE value LIKE '%s' AND field_id=? LIMIT \
196             50) AND field_id in (%s)" % (s, ','.join('%r' % f for f in \
197             fields.keys())), (key_id,)).fetchall()
198         for data_id, field_id, value in data:
199             if not items_dict.has_key(data_id):
200                 items_dict[data_id] = {}
201             items_dict[data_id][fields[int(field_id)]] = value
202         return items_dict
203
204     def get_entry(self, _id):
205         """Returns full entry by it id."""
206
207         execute = self.conn.execute
208         entry_dict = {}
209         entry_dict = dict(execute("SELECT fields.name, value FROM data \
210             LEFT JOIN fields ON fields.id=data.field_id \
211             WHERE data.id=?", (_id,)).fetchall())
212         sql_res = execute('SELECT photo FROM photo WHERE data_id=?', \
213             (_id,)).fetchone()
214         if sql_res is not None:
215             entry_dict['image'] = sql_res[0]
216         return entry_dict
217
218
219
220 if __name__ == "__main__":
221     db = SQLite('/tmp')
222     # put test code here
223     db.close()