mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Add a wizard for easily searching date fields in your library. To use it click the advanced search button to the left of the search field. Allows you to search for books whose date is older/newer than a specified date or less than a number of days ago and so on.
This commit is contained in:
parent
85caee19c2
commit
cd878a6bb5
@ -2,6 +2,7 @@ __license__ = 'GPL v3'
|
||||
__copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||
|
||||
import re, copy
|
||||
from datetime import date
|
||||
|
||||
from PyQt4.Qt import QDialog, QDialogButtonBox
|
||||
|
||||
@ -10,15 +11,45 @@ from calibre.library.caches import CONTAINS_MATCH, EQUALS_MATCH
|
||||
from calibre.gui2 import gprefs
|
||||
from calibre.utils.icu import sort_key
|
||||
from calibre.utils.config import tweaks
|
||||
from calibre.utils.date import now
|
||||
|
||||
box_values = {}
|
||||
last_matchkind = CONTAINS_MATCH
|
||||
|
||||
def init_dateop(cb):
|
||||
for op, desc in [
|
||||
('=', _('equal to')),
|
||||
('<', _('before')),
|
||||
('>', _('after')),
|
||||
('<=', _('before or equal to')),
|
||||
('>=', _('after or equal to')),
|
||||
]:
|
||||
cb.addItem(desc, op)
|
||||
|
||||
def current_dateop(cb):
|
||||
return unicode(cb.itemData(cb.currentIndex()).toString())
|
||||
|
||||
class SearchDialog(QDialog, Ui_Dialog):
|
||||
|
||||
def __init__(self, parent, db):
|
||||
QDialog.__init__(self, parent)
|
||||
self.setupUi(self)
|
||||
for val, text in [(0, '')] + [(i, date(2010, i, 1).strftime('%B')) for i in xrange(1, 13)]:
|
||||
self.date_month.addItem(text, val)
|
||||
for val, text in [('today', _('Today')), ('yesterday', _('Yesterday')), ('thismonth', _('This month'))]:
|
||||
self.date_human.addItem(text, val)
|
||||
self.date_year.setValue(now().year)
|
||||
vals = [((v['search_terms'] or [k])[0], v['name'] or k) for k, v in db.field_metadata.iteritems() if v.get('datatype', None) == 'datetime']
|
||||
for k, v in sorted(vals, key=lambda (k, v): sort_key(v)):
|
||||
self.date_field.addItem(v, k)
|
||||
|
||||
self.date_year.valueChanged.connect(lambda : self.sel_date.setChecked(True))
|
||||
self.date_month.currentIndexChanged.connect(lambda : self.sel_date.setChecked(True))
|
||||
self.date_day.valueChanged.connect(lambda : self.sel_date.setChecked(True))
|
||||
self.date_daysago.valueChanged.connect(lambda : self.sel_daysago.setChecked(True))
|
||||
self.date_human.currentIndexChanged.connect(lambda : self.sel_human.setChecked(True))
|
||||
init_dateop(self.dateop_date)
|
||||
self.sel_date.setChecked(True)
|
||||
self.mc = ''
|
||||
searchables = sorted(db.field_metadata.searchable_fields(),
|
||||
key=lambda x: sort_key(x if x[0] != '#' else x[1:]))
|
||||
@ -50,11 +81,7 @@ class SearchDialog(QDialog, Ui_Dialog):
|
||||
self.general_combo.setCurrentIndex(
|
||||
self.general_combo.findText(self.box_last_values['general_index']))
|
||||
|
||||
self.buttonBox.accepted.connect(self.advanced_search_button_pushed)
|
||||
self.tab_2_button_box.accepted.connect(self.accept)
|
||||
self.tab_2_button_box.rejected.connect(self.reject)
|
||||
self.clear_button.clicked.connect(self.clear_button_pushed)
|
||||
self.adv_search_used = False
|
||||
|
||||
current_tab = gprefs.get('advanced search dialog current tab', 0)
|
||||
self.tabWidget.setCurrentIndex(current_tab)
|
||||
@ -77,14 +104,8 @@ class SearchDialog(QDialog, Ui_Dialog):
|
||||
return QDialog.reject(self)
|
||||
|
||||
def tab_changed(self, idx):
|
||||
if idx == 1:
|
||||
self.tab_2_button_box.button(QDialogButtonBox.Ok).setDefault(True)
|
||||
else:
|
||||
self.buttonBox.button(QDialogButtonBox.Ok).setDefault(True)
|
||||
|
||||
def advanced_search_button_pushed(self):
|
||||
self.adv_search_used = True
|
||||
self.accept()
|
||||
bb = (self.buttonBox, self.tab_2_button_box, self.tab_3_button_box)[idx]
|
||||
bb.button(QDialogButtonBox.Ok).setDefault(True)
|
||||
|
||||
def clear_button_pushed(self):
|
||||
self.title_box.setText('')
|
||||
@ -101,10 +122,25 @@ class SearchDialog(QDialog, Ui_Dialog):
|
||||
return ['"' + self.mc + t + '"' for t in phrases + [r.strip() for r in raw.split()]]
|
||||
|
||||
def search_string(self):
|
||||
if self.adv_search_used:
|
||||
return self.adv_search_string()
|
||||
else:
|
||||
return self.box_search_string()
|
||||
i = self.tabWidget.currentIndex()
|
||||
return (self.adv_search_string, self.box_search_string, self.date_search_string)[i]()
|
||||
|
||||
def date_search_string(self):
|
||||
field = unicode(self.date_field.itemData(self.date_field.currentIndex()).toString())
|
||||
op = current_dateop(self.dateop_date)
|
||||
prefix = '%s:%s' % (field, op)
|
||||
if self.sel_date.isChecked():
|
||||
ans = '%s%s' % (prefix, self.date_year.value())
|
||||
m = self.date_month.itemData(self.date_month.currentIndex()).toPyObject()
|
||||
if m > 0:
|
||||
ans += '-%s' % m
|
||||
d = self.date_day.value()
|
||||
if d > 0:
|
||||
ans += '-%s' % d
|
||||
return ans
|
||||
if self.sel_daysago.isChecked():
|
||||
return '%s%sdaysago' % (prefix, self.date_daysago.value())
|
||||
return '%s%s' % (prefix, unicode(self.date_human.itemData(self.date_human.currentIndex()).toString()))
|
||||
|
||||
def adv_search_string(self):
|
||||
mk = self.matchkind.currentIndex()
|
||||
|
@ -6,7 +6,7 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>731</width>
|
||||
<width>872</width>
|
||||
<height>411</height>
|
||||
</rect>
|
||||
</property>
|
||||
@ -36,7 +36,7 @@
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string><p>You can also perform other kinds of advanced searches, for example searching date columns for dates after a particular date, or checking for books that have no covers and so on. See the <a href="http://manual.calibre-ebook.com/gui.html#the-search-interface">The Search Interface</a> for more information.</string>
|
||||
<string><p>You can also perform other kinds of advanced searches, for example checking for books that have no covers, combining multiple search expression using Boolean operators and so on. See the <a href="http://manual.calibre-ebook.com/gui.html#the-search-interface">The Search Interface</a> for more information.</string>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
@ -49,6 +49,35 @@
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_5">
|
||||
<property name="text">
|
||||
<string>&What kind of match to use:</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>matchkind</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QComboBox" name="matchkind">
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Contains: the word or phrase matches anywhere in the metadata field</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Equals: the word or phrase must match the entire metadata field</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Regular expression: the expression must match anywhere in the metadata field</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0" colspan="2">
|
||||
<widget class="QTabWidget" name="tabWidget">
|
||||
<property name="currentIndex">
|
||||
@ -289,35 +318,222 @@
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_5">
|
||||
<widget class="QWidget" name="tab_3">
|
||||
<attribute name="title">
|
||||
<string>&Date searches</string>
|
||||
</attribute>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_4">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_5">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_6">
|
||||
<property name="text">
|
||||
<string>&What kind of match to use:</string>
|
||||
<string>&Search the</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>matchkind</cstring>
|
||||
<cstring>date_field</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QComboBox" name="matchkind">
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Contains: the word or phrase matches anywhere in the metadata field</string>
|
||||
</property>
|
||||
<widget class="QComboBox" name="date_field"/>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_13">
|
||||
<property name="text">
|
||||
<string>Equals: the word or phrase must match the entire metadata field</string>
|
||||
<string>date column for books whose date is </string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Regular expression: the expression must match anywhere in the metadata field</string>
|
||||
</property>
|
||||
<widget class="QComboBox" name="dateop_date"/>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_17">
|
||||
<property name="text">
|
||||
<string>...</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_7">
|
||||
<item>
|
||||
<widget class="QRadioButton" name="sel_date">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_14">
|
||||
<property name="text">
|
||||
<string>&year</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>date_year</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QSpinBox" name="date_year">
|
||||
<property name="minimum">
|
||||
<number>102</number>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>10000</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_15">
|
||||
<property name="text">
|
||||
<string>&month</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>date_month</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="date_month"/>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_16">
|
||||
<property name="text">
|
||||
<string>&day</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>date_day</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QSpinBox" name="date_day">
|
||||
<property name="specialValueText">
|
||||
<string> </string>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>31</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_8">
|
||||
<item>
|
||||
<widget class="QRadioButton" name="sel_daysago">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QSpinBox" name="date_daysago">
|
||||
<property name="maximum">
|
||||
<number>999999</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_18">
|
||||
<property name="text">
|
||||
<string>days ago</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_3">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_9">
|
||||
<item>
|
||||
<widget class="QRadioButton" name="sel_human">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="date_human"/>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_4">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer_3">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDialogButtonBox" name="tab_3_button_box">
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
@ -360,8 +576,8 @@
|
||||
<slot>accept()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>248</x>
|
||||
<y>254</y>
|
||||
<x>256</x>
|
||||
<y>396</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>157</x>
|
||||
@ -376,8 +592,8 @@
|
||||
<slot>reject()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>316</x>
|
||||
<y>260</y>
|
||||
<x>324</x>
|
||||
<y>396</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>286</x>
|
||||
@ -385,5 +601,69 @@
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>tab_3_button_box</sender>
|
||||
<signal>accepted()</signal>
|
||||
<receiver>Dialog</receiver>
|
||||
<slot>accept()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>101</x>
|
||||
<y>370</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>697</x>
|
||||
<y>0</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>tab_3_button_box</sender>
|
||||
<signal>rejected()</signal>
|
||||
<receiver>Dialog</receiver>
|
||||
<slot>reject()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>101</x>
|
||||
<y>370</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>696</x>
|
||||
<y>36</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>tab_2_button_box</sender>
|
||||
<signal>accepted()</signal>
|
||||
<receiver>Dialog</receiver>
|
||||
<slot>accept()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>508</x>
|
||||
<y>378</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>697</x>
|
||||
<y>353</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>tab_2_button_box</sender>
|
||||
<signal>rejected()</signal>
|
||||
<receiver>Dialog</receiver>
|
||||
<slot>reject()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>350</x>
|
||||
<y>376</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>697</x>
|
||||
<y>269</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
</ui>
|
||||
|
Loading…
x
Reference in New Issue
Block a user