Use field_metadata in more places. Fixed problems with duplicate column names in preferences, viewing the library, and sorting

This commit is contained in:
Charles Haley 2010-05-27 18:00:24 +01:00
parent 2ea3fdf5a7
commit 68a7cc5c80
9 changed files with 72 additions and 61 deletions

View File

@ -371,7 +371,7 @@ class ConfigDialog(ResizableDialog, Ui_Dialog):
hidden_cols = state['hidden_columns']
positions = state['column_positions']
colmap.sort(cmp=lambda x,y: cmp(positions[x], positions[y]))
self.custcols = copy.deepcopy(self.db.custom_column_label_map)
self.custcols = copy.deepcopy(self.db.field_metadata.get_custom_field_metadata())
for col in colmap:
item = QListWidgetItem(self.model.headers[col], self.columns)
item.setData(Qt.UserRole, QVariant(col))
@ -713,20 +713,20 @@ class ConfigDialog(ResizableDialog, Ui_Dialog):
must_restart = False
for c in self.custcols:
if self.custcols[c]['num'] is None:
if self.custcols[c]['colnum'] is None:
self.db.create_custom_column(
label=c,
label=self.custcols[c]['label'],
name=self.custcols[c]['name'],
datatype=self.custcols[c]['datatype'],
is_multiple=self.custcols[c]['is_multiple'],
display = self.custcols[c]['display'])
must_restart = True
elif '*deleteme' in self.custcols[c]:
self.db.delete_custom_column(label=c)
self.db.delete_custom_column(label=self.custcols[c]['label'])
must_restart = True
elif '*edited' in self.custcols[c]:
cc = self.custcols[c]
self.db.set_custom_column_metadata(cc['num'], name=cc['name'],
self.db.set_custom_column_metadata(cc['colnum'], name=cc['name'],
label=cc['label'],
display = self.custcols[c]['display'])
if '*must_restart' in self.custcols[c]:

View File

@ -69,12 +69,13 @@ class CreateCustomColumn(QDialog, Ui_QCreateCustomColumn):
self.column_name_box.setText(c['label'])
self.column_heading_box.setText(c['name'])
ct = c['datatype'] if not c['is_multiple'] else '*text'
self.orig_column_number = c['num']
self.orig_column_number = c['colnum']
self.orig_column_name = col
column_numbers = dict(map(lambda x:(self.column_types[x]['datatype'], x), self.column_types))
self.column_type_box.setCurrentIndex(column_numbers[ct])
self.column_type_box.setEnabled(False)
if ct == 'datetime':
if c['display'].get('date_format', None):
self.date_format_box.setText(c['display'].get('date_format', ''))
self.datatype_changed()
self.exec_()
@ -90,7 +91,9 @@ class CreateCustomColumn(QDialog, Ui_QCreateCustomColumn):
def accept(self):
col = unicode(self.column_name_box.text())
col = unicode(self.column_name_box.text()).lower()
if not col.isalnum():
return self.simple_error('', _('The label must contain only letters and digits'))
col_heading = unicode(self.column_heading_box.text())
col_type = self.column_types[self.column_type_box.currentIndex()]['datatype']
if col_type == '*text':
@ -104,14 +107,14 @@ class CreateCustomColumn(QDialog, Ui_QCreateCustomColumn):
return self.simple_error('', _('No column heading was provided'))
bad_col = False
if col in self.parent.custcols:
if not self.editing_col or self.parent.custcols[col]['num'] != self.orig_column_number:
if not self.editing_col or self.parent.custcols[col]['colnum'] != self.orig_column_number:
bad_col = True
if bad_col:
return self.simple_error('', _('The lookup name %s is already used')%col)
bad_head = False
for t in self.parent.custcols:
if self.parent.custcols[t]['name'] == col_heading:
if not self.editing_col or self.parent.custcols[t]['num'] != self.orig_column_number:
if not self.editing_col or self.parent.custcols[t]['colnum'] != self.orig_column_number:
bad_head = True
for t in self.standard_colheads:
if self.standard_colheads[t] == col_heading:
@ -129,14 +132,15 @@ class CreateCustomColumn(QDialog, Ui_QCreateCustomColumn):
date_format = {'date_format': None}
if not self.editing_col:
self.parent.custcols[col] = {
self.parent.db.field_metadata
self.parent.custcols[self.parent.db.field_metadata.custom_field_prefix+col] = {
'label':col,
'name':col_heading,
'datatype':col_type,
'editable':True,
'display':date_format,
'normalized':None,
'num':None,
'colnum':None,
'is_multiple':is_multiple,
}
item = QListWidgetItem(col_heading, self.parent.columns)

View File

@ -65,7 +65,7 @@
</size>
</property>
<property name="toolTip">
<string>Used for searching the column. Must be lower case and not contain spaces or colons.</string>
<string>Used for searching the column. Must contain only digits and lower case letters.</string>
</property>
</widget>
</item>

View File

@ -171,7 +171,8 @@ class TagsDelegate(QStyledItemDelegate): # {{{
if not index.model().is_custom_column(col):
editor = TagsLineEdit(parent, self.db.all_tags())
else:
editor = TagsLineEdit(parent, sorted(list(self.db.all_custom(label=col))))
editor = TagsLineEdit(parent,
sorted(list(self.db.all_custom(label=self.db.field_metadata.key_to_label(col)))))
return editor
else:
editor = EnLineEdit(parent)
@ -209,7 +210,7 @@ class CcDateDelegate(QStyledItemDelegate): # {{{
m = index.model()
# db col is not named for the field, but for the table number. To get it,
# gui column -> column label -> table number -> db column
val = m.db.data[index.row()][m.db.FIELD_MAP[m.custom_columns[m.column_map[index.column()]]['num']]]
val = m.db.data[index.row()][m.custom_columns[m.column_map[index.column()]]['rec_index']]
if val is None:
val = now()
editor.setDate(val)
@ -243,7 +244,7 @@ class CcTextDelegate(QStyledItemDelegate): # {{{
editor.setDecimals(2)
else:
editor = EnLineEdit(parent)
complete_items = sorted(list(m.db.all_custom(label=col)))
complete_items = sorted(list(m.db.all_custom(label=m.db.field_metadata.key_to_label(col))))
completer = QCompleter(complete_items, self)
completer.setCaseSensitivity(Qt.CaseInsensitive)
completer.setCompletionMode(QCompleter.PopupCompletion)
@ -260,9 +261,7 @@ class CcCommentsDelegate(QStyledItemDelegate): # {{{
def createEditor(self, parent, option, index):
m = index.model()
col = m.column_map[index.column()]
# db col is not named for the field, but for the table number. To get it,
# gui column -> column label -> table number -> db column
text = m.db.data[index.row()][m.db.FIELD_MAP[m.custom_columns[col]['num']]]
text = m.db.data[index.row()][m.custom_columns[col]['rec_index']]
editor = CommentsDialog(parent, text)
d = editor.exec_()
if d:
@ -297,9 +296,7 @@ class CcBoolDelegate(QStyledItemDelegate): # {{{
def setEditorData(self, editor, index):
m = index.model()
# db col is not named for the field, but for the table number. To get it,
# gui column -> column label -> table number -> db column
val = m.db.data[index.row()][m.db.FIELD_MAP[m.custom_columns[m.column_map[index.column()]]['num']]]
val = m.db.data[index.row()][m.custom_columns[m.column_map[index.column()]]['rec_index']]
if tweaks['bool_custom_columns_are_tristate'] == 'no':
val = 1 if not val else 0
else:

View File

@ -111,15 +111,15 @@ class BooksModel(QAbstractTableModel): # {{{
def set_database(self, db):
self.db = db
self.custom_columns = self.db.custom_column_label_map
self.custom_columns = self.db.field_metadata.get_custom_field_metadata()
self.column_map = list(self.orig_headers.keys()) + \
list(self.custom_columns)
def col_idx(name):
if name == 'ondevice':
return -1
if name not in self.db.FIELD_MAP:
if name not in self.db.field_metadata:
return 100000
return self.db.FIELD_MAP[name]
return self.db.field_metadata[name]['rec_index']
self.column_map.sort(cmp=lambda x,y: cmp(col_idx(x), col_idx(y)))
for col in self.column_map:
@ -232,11 +232,12 @@ class BooksModel(QAbstractTableModel): # {{{
return
self.about_to_be_sorted.emit(self.db.id)
ascending = order == Qt.AscendingOrder
self.db.sort(self.column_map[col], ascending)
label = self.column_map[col]
self.db.sort(label, ascending)
if reset:
self.clear_caches()
self.reset()
self.sorted_on = (self.column_map[col], order)
self.sorted_on = (label, order)
self.sort_history.insert(0, self.sorted_on)
self.sorting_done.emit(self.db.index)
@ -551,36 +552,36 @@ class BooksModel(QAbstractTableModel): # {{{
self.dc = {
'title' : functools.partial(text_type,
idx=self.db.FIELD_MAP['title'], mult=False),
idx=self.db.field_metadata['title']['rec_index'], mult=False),
'authors' : functools.partial(authors,
idx=self.db.FIELD_MAP['authors']),
idx=self.db.field_metadata['authors']['rec_index']),
'size' : functools.partial(size,
idx=self.db.FIELD_MAP['size']),
idx=self.db.field_metadata['size']['rec_index']),
'timestamp': functools.partial(datetime_type,
idx=self.db.FIELD_MAP['timestamp']),
idx=self.db.field_metadata['timestamp']['rec_index']),
'pubdate' : functools.partial(datetime_type,
idx=self.db.FIELD_MAP['pubdate']),
idx=self.db.field_metadata['pubdate']['rec_index']),
'rating' : functools.partial(rating_type,
idx=self.db.FIELD_MAP['rating']),
idx=self.db.field_metadata['rating']['rec_index']),
'publisher': functools.partial(text_type,
idx=self.db.FIELD_MAP['publisher'], mult=False),
idx=self.db.field_metadata['publisher']['rec_index'], mult=False),
'tags' : functools.partial(tags,
idx=self.db.FIELD_MAP['tags']),
idx=self.db.field_metadata['tags']['rec_index']),
'series' : functools.partial(series,
idx=self.db.FIELD_MAP['series'],
siix=self.db.FIELD_MAP['series_index']),
idx=self.db.field_metadata['series']['rec_index'],
siix=self.db.field_metadata['series_index']['rec_index']),
'ondevice' : functools.partial(text_type,
idx=self.db.FIELD_MAP['ondevice'], mult=False),
idx=self.db.field_metadata['ondevice']['rec_index'], mult=False),
}
self.dc_decorator = {
'ondevice':functools.partial(ondevice_decorator,
idx=self.db.FIELD_MAP['ondevice']),
idx=self.db.field_metadata['ondevice']['rec_index']),
}
# Add the custom columns to the data converters
for col in self.custom_columns:
idx = self.db.FIELD_MAP[self.custom_columns[col]['num']]
idx = self.custom_columns[col]['rec_index']
datatype = self.custom_columns[col]['datatype']
if datatype in ('text', 'comments'):
self.dc[col] = functools.partial(text_type, idx=idx, mult=self.custom_columns[col]['is_multiple'])
@ -632,8 +633,6 @@ class BooksModel(QAbstractTableModel): # {{{
return None
if role == Qt.ToolTipRole:
ht = self.column_map[section]
if self.is_custom_column(self.column_map[section]):
ht = self.db.field_metadata.custom_field_prefix + ht
if ht == 'timestamp': # change help text because users know this field as 'date'
ht = 'date'
return QVariant(_('The lookup/search name is "{0}"').format(ht))
@ -652,7 +651,7 @@ class BooksModel(QAbstractTableModel): # {{{
if colhead in self.editable_cols:
flags |= Qt.ItemIsEditable
elif self.is_custom_column(colhead):
if self.custom_columns[colhead]['editable']:
if self.custom_columns[colhead]['is_editable']:
flags |= Qt.ItemIsEditable
return flags
@ -679,7 +678,9 @@ class BooksModel(QAbstractTableModel): # {{{
if not val.isValid():
return False
val = qt_to_dt(val, as_utc=False)
self.db.set_custom(self.db.id(row), val, label=colhead, num=None, append=False, notify=True)
self.db.set_custom(self.db.id(row), val,
label=self.db.field_metadata.key_to_label(colhead),
num=None, append=False, notify=True)
return True
def setData(self, index, value, role):

View File

@ -150,9 +150,8 @@ class ResultCache(SearchQueryParser):
'''
Stores sorted and filtered metadata in memory.
'''
def __init__(self, FIELD_MAP, cc_label_map, field_metadata):
def __init__(self, FIELD_MAP, field_metadata):
self.FIELD_MAP = FIELD_MAP
self.custom_column_label_map = cc_label_map
self._map = self._map_filtered = self._data = []
self.first_sort = True
self.search_restriction = ''
@ -598,9 +597,9 @@ class ResultCache(SearchQueryParser):
elif field == 'title': field = 'sort'
elif field == 'authors': field = 'author_sort'
as_string = field not in ('size', 'rating', 'timestamp')
if field in self.custom_column_label_map:
as_string = self.custom_column_label_map[field]['datatype'] in ('comments', 'text')
field = self.custom_column_label_map[field]['num']
if self.field_metadata[field]['is_custom']:
as_string = self.field_metadata[field]['datatype'] in ('comments', 'text')
field = self.field_metadata[field]['colnum']
if self.first_sort:
subsort = True

View File

@ -154,8 +154,9 @@ class CustomColumns(object):
tn = 'custom_column_{0}'.format(v['num'])
self.field_metadata.add_custom_field(label=v['label'],
table=tn, column='value', datatype=v['datatype'],
is_multiple=is_m, colnum=v['num'], name=v['name'],
is_category=is_category)
colnum=v['num'], name=v['name'], display=v['display'],
is_multiple=is_m, is_category=is_category,
is_editable=v['editable'])
def get_custom(self, idx, label=None, num=None, index_is_id=False):
if label is not None:

View File

@ -232,8 +232,7 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
self.conn.commit()
self.book_on_device_func = None
self.data = ResultCache(self.FIELD_MAP, self.custom_column_label_map,
self.field_metadata)
self.data = ResultCache(self.FIELD_MAP, self.field_metadata)
self.search = self.data.search
self.refresh = functools.partial(self.data.refresh, self)
self.sort = self.data.sort

View File

@ -282,7 +282,9 @@ class FieldMetadata(dict):
self.custom_label_to_key_map = {}
for k,v in self._field_metadata:
self._tb_cats[k] = v
self._tb_cats[k]['label'] = k # saved some typing above...
self._tb_cats[k]['label'] = k
self._tb_cats[k]['display'] = {}
self._tb_cats[k]['is_editable'] = True
self._add_search_terms_to_map(k, self._tb_cats[k]['search_terms'])
self.custom_field_prefix = '#'
self.get = self._tb_cats.get
@ -300,12 +302,12 @@ class FieldMetadata(dict):
for key in self._tb_cats:
yield key
def has_key(self, key):
return key in self._tb_cats
def __contains__(self, key):
return self.has_key(key)
def has_key(self, key):
return key in self._tb_cats
def keys(self):
return self._tb_cats.keys()
@ -339,8 +341,15 @@ class FieldMetadata(dict):
def get_custom_fields(self):
return [l for l in self._tb_cats if self._tb_cats[l]['is_custom']]
def add_custom_field(self, label, table, column, datatype, colnum,
name, is_multiple, is_category):
def get_custom_field_metadata(self):
l = {}
for k in self._tb_cats:
if self._tb_cats[k]['is_custom']:
l[k] = self._tb_cats[k]
return l
def add_custom_field(self, label, table, column, datatype, colnum, name,
display, is_editable, is_multiple, is_category):
key = self.custom_field_prefix + label
if key in self._tb_cats:
raise ValueError('Duplicate custom field [%s]'%(label))
@ -348,8 +357,9 @@ class FieldMetadata(dict):
'datatype':datatype, 'is_multiple':is_multiple,
'kind':'field', 'name':name,
'search_terms':[key], 'label':label,
'colnum':colnum, 'is_custom':True,
'is_category':is_category}
'colnum':colnum, 'display':display,
'is_custom':True, 'is_category':is_category,
'is_editable': is_editable,}
self._add_search_terms_to_map(key, [key])
self.custom_label_to_key_map[label] = key