updated setup scripts
[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 \
165             where id in (select data_id from relation where struct_id=?) \
166             and field_id in (%s)"
167         
168         data = execute(query % ','.join('%r' % f for f in fields.keys()), (parent,)).fetchall()
169                 
170         for id, field_id, value in data:
171             if not items_dict.has_key(id):
172                 items_dict[id] = {}
173             items_dict[id][fields[int(field_id)]] = value
174         return items_dict
175
176
177     def get_files_by_pattern(self, fields, key='cn', pattern='', search_from_start=False):
178         """Returns list of all files from DATA table."""
179
180         items_dict = {}
181         if key not in fields:
182             fields.append(str(key))
183         execute = self.conn.execute
184         fields = dict(execute("SELECT id, name FROM fields WHERE name IN (%s)" \
185             % ','.join('%r' % (field,) for field in fields)).fetchall())
186         key_id = 1 
187         for k, val in fields.items():
188             if val == key:
189                 key_id = k
190                 break
191         if search_from_start:
192             s = '%s%%' % pattern.capitalize()
193         else:
194             s = '%%%s%%' % pattern
195
196         data = execute("SELECT id, field_id, value FROM data WHERE id in \
197             (select id from data where value LIKE '%s' and field_id=? LIMIT 50) \
198             and field_id in (%s)" % (s, ','.join('%r' % f for f in fields.keys())), (key_id,)).fetchall()
199         
200         for id, field_id, value in data:
201             if not items_dict.has_key(id):
202                 items_dict[id] = {}
203             items_dict[id][fields[int(field_id)]] = value
204         return items_dict
205
206
207     def get_entry(self, _id):
208         """Returns full entry by it id."""
209
210         execute = self.conn.execute
211         entry_dict = {}
212         entry_dict = dict(execute("select fields.name, value from data \
213             left join fields on fields.id=data.field_id \
214             where data.id=?", (_id,)).fetchall())
215         sql_res = execute('select photo from photo where data_id=?', \
216             (_id,)).fetchone()
217         if sql_res is not None:
218             entry_dict['image'] = sql_res[0]
219         return entry_dict
220
221 if __name__ == "__main__":
222     db = SQLite('/tmp')
223     #print db.get_files(('cn', 'o', 'ou'), 0)
224     print db._get_files_by_pattern(('cn', 'o', 'ou'), 'cn', 'Ра', True)