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