implemented search by second name
[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         """Returns list of all files from DATA table."""
154
155         items_dict = {}
156         execute = self.conn.execute
157         fields = execute("SELECT id, name FROM fields WHERE name IN (%s)" \
158             % ','.join('%r' % (field,) for field in fields)).fetchall()
159         if parent == 0: # get all files
160             data_ids = [_id[0] for _id in execute("SELECT DISTINCT id FROM \
161                 data").fetchall()]
162         else: # get files for selected parent
163             data_ids = [_id[0] for _id in execute("SELECT data_id FROM \
164                 relation WHERE struct_id=?", (parent,)).fetchall()]
165         for data_id in data_ids:
166             items_dict[data_id] = {}
167             for field_id, field_name in fields:
168                 field_value = execute("SELECT value FROM data WHERE id=? \
169                     AND field_id=?", (data_id, field_id)).fetchone()[0]
170                 items_dict[data_id][field_name] = field_value
171         return items_dict
172
173     def get_files_by_pattern(self, pattern):
174         """Returns list of all files, matched pattern string."""
175
176         if not pattern:
177             return []
178         execute = self.conn.execute
179         field_id = execute("SELECT id FROM fields WHERE name=?", \
180             ('cn',)).fetchone()[0]
181         return execute("SELECT id, value FROM data WHERE value LIKE '%s%%' \
182             AND field_id=? LIMIT 50" % pattern.capitalize(), (field_id,)).fetchall()
183
184     def get_entry(self, _id):
185         """Returns full entry by it id."""
186
187         execute = self.conn.execute
188         entry_dict = {}
189         for field_id, value in execute("SELECT field_id, value FROM data WHERE \
190             id=?", (_id,)).fetchall():
191             field_name = execute("SELECT name FROM fields WHERE id=?", \
192                 (field_id,)).fetchone()[0]
193             entry_dict[field_name] = value
194         sql_res = execute('select photo from photo where data_id=?', \
195             (_id,)).fetchone()
196         if sql_res is not None:
197             entry_dict['image'] = sql_res[0]
198         return entry_dict