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