changed database functions
[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     
140     def get_folders(self, parent=None):
141         """
142         Returns list of all folders (items with folders and files)
143         from STRUCT table.
144         """
145
146         if parent is None:  # return all folders on level2
147             return self.conn.execute("SELECT DISTINCT name, id FROM struct \
148                 WHERE parent!=0 ORDER BY name ASC").fetchall()
149         else:
150             return self.conn.execute("SELECT DISTINCT name, id FROM struct \
151                 WHERE parent=? ORDER BY name ASC", (parent,)).fetchall()
152     
153     def _get_files(self, fields, parent=0):
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 where id in (select data_id from relation where struct_id=?) and field_id in (%s)"
165         
166         data = execute(query % ','.join('%r' % f for f in fields.keys()), (parent,)).fetchall()
167                 
168         for id, field_id, value in data:
169             if not items_dict.has_key(id):
170                 items_dict[id] = {}
171             items_dict[id][fields[int(field_id)]] = value
172         return items_dict
173
174
175     
176     def get_files(self, fields, parent=0, pattern=''):
177         """Returns list of all files from DATA table."""
178
179         items_dict = {}
180         execute = self.conn.execute
181         fields = execute("SELECT id, name FROM fields WHERE name IN (%s)" \
182             % ','.join('%r' % (field,) for field in fields)).fetchall()
183         if parent == 0: # get all files
184             #data_ids = [_id[0] for _id in execute("SELECT DISTINCT id FROM \
185             #    data").fetchall()]
186             data_ids = [_id[0] for _id in execute("SELECT DISTINCT id FROM \
187                 data WHERE value LIKE '%s%%' LIMIT 50" % pattern.capitalize()).fetchall()]
188         else: # get files for selected parent
189             data_ids = [_id[0] for _id in execute("SELECT data_id FROM \
190                 relation WHERE struct_id=?", (parent,)).fetchall()]
191         for data_id in data_ids:
192             items_dict[data_id] = {}
193             for field_id, field_name in fields:
194                 field_value = execute("SELECT value FROM data WHERE id=? \
195                     AND field_id=?", (data_id, field_id)).fetchone()[0]
196                 items_dict[data_id][field_name] = field_value
197         return items_dict
198
199     def _get_files_by_pattern(self, fields, key='cn', pattern='', search_from_start=False):
200         """Returns list of all files from DATA table."""
201
202         items_dict = {}
203         if key not in fields:
204             fields = fields + (key,)
205         execute = self.conn.execute
206         fields = dict(execute("SELECT id, name FROM fields WHERE name IN (%s)" \
207             % ','.join('%r' % (field,) for field in fields)).fetchall())
208         key_id = 1 
209         for k, val in fields.items():
210             if val == key:
211                 key_id = k
212                 break
213         if search_from_start:
214             s = '%s%%' % pattern.capitalize()
215         else:
216             s = '%%%s%%' % pattern
217
218         data = execute("SELECT id, field_id, value FROM data WHERE id in \
219             (select id from data where value LIKE '%s' and field_id=? LIMIT 50) \
220             and field_id in (%s)" % (s, ','.join('%r' % f for f in fields.keys())), (key_id,)).fetchall()
221         
222         for id, field_id, value in data:
223             if not items_dict.has_key(id):
224                 items_dict[id] = {}
225             items_dict[id][fields[int(field_id)]] = value
226         return items_dict
227
228
229     def get_files_by_pattern(self, pattern):
230         """Returns list of all files, matched pattern string."""
231
232         if not pattern:
233             return []
234         execute = self.conn.execute
235         field_id = execute("SELECT id FROM fields WHERE name=?", \
236             ('cn',)).fetchone()[0]
237         return execute("SELECT id, value FROM data WHERE value LIKE '%s%%' \
238             AND field_id=? LIMIT 50" % pattern.capitalize(), (field_id,)).fetchall()
239
240     def _get_entry(self, _id):
241         """Returns full entry by it id."""
242
243         execute = self.conn.execute
244         entry_dict = {}
245         entry_dict = dict(execute("select fields.name, value from data \
246             left join fields on fields.id=data.field_id \
247             where data.id=?", (_id,)).fetchall())
248         sql_res = execute('select photo from photo where data_id=?', \
249             (_id,)).fetchone()
250         if sql_res is not None:
251             entry_dict['image'] = sql_res[0]
252         return entry_dict
253
254     def get_entry(self, _id):
255         """Returns full entry by it id."""
256
257         execute = self.conn.execute
258         entry_dict = {}
259         for field_id, value in execute("SELECT field_id, value FROM data WHERE \
260             id=?", (_id,)).fetchall():
261             field_name = execute("SELECT name FROM fields WHERE id=?", \
262                 (field_id,)).fetchone()[0]
263             entry_dict[field_name] = value
264         sql_res = execute('select photo from photo where data_id=?', \
265             (_id,)).fetchone()
266         if sql_res is not None:
267             entry_dict['image'] = sql_res[0]
268         return entry_dict
269
270 if __name__ == "__main__":
271     db = SQLite('/tmp')
272     #print db.get_files(('cn', 'o', 'ou'), 0)
273     print db._get_files_by_pattern(('cn', 'o', 'ou'), 'cn', 'Ра', True)