5 from sqlobject.sqlbuilder import CONCAT
7 from m_lib.defenc import default_encoding
8 from m_librarian.db import Author, Book, Extension, Genre, Language, open_db
9 from m_librarian.search import mk_search_conditions, \
10 search_authors, search_books, \
11 search_extensions, search_genres, search_languages
13 from m_librarian.translations import translations
14 _ = translations.ugettext
17 def _get_values(args, *columns):
19 for column in columns:
20 value = getattr(args, column)
22 values[column] = unicode(value, default_encoding)
26 def _guess_case_sensitivity(values):
27 for value in values.values():
28 if not value.islower():
33 def print_count(query):
34 print _("Found").encode(default_encoding), ":", query.count()
37 def _search_authors(case_sensitive, search_type, args):
38 if (args.surname or args.name or args.misc_name) and args.fullname:
40 "Cannot search by names and full name at the same time\n")
41 main_parser.print_help()
44 values = _get_values(args, 'surname', 'name', 'misc_name', 'id')
49 CONCAT(Author.q.surname, ' ', Author.q.name, ' ',
51 unicode(value, default_encoding)
53 if case_sensitive is None:
54 case_sensitive = _guess_case_sensitivity(values)
55 authors = search_authors(search_type, case_sensitive, values, expressions,
56 orderBy=('surname', 'name', 'misc_name'))
60 for author in authors:
61 names = filter(None, (author.surname, author.name, author.misc_name))
62 fullname = u' '.join(names)
63 print fullname.encode(default_encoding), \
64 (u"(%s: %d)" % (_('books'), author.count))\
65 .encode(default_encoding),
67 print "(id=%d)" % author.id,
72 def _search_books(case_sensitive, search_type, args):
74 values = _get_values(args, 'title', 'series', 'archive', 'file', 'id')
75 if case_sensitive is None:
76 test_values = values.copy()
77 test_values.update(_get_values(args, 'surname', 'name', 'misc_name',
78 'fullname', 'ext', 'gname', 'gtitle',
80 case_sensitive = _guess_case_sensitivity(test_values)
81 avalues = _get_values(args, 'surname', 'name', 'misc_name', 'fullname')
83 avalues['id'] = args.aid
85 if (args.surname or args.name or args.misc_name) and args.fullname:
87 "Cannot search by names and full name at the same time\n")
88 main_parser.print_help()
91 join_expressions.append(Book.j.authors)
95 CONCAT(Author.q.surname, ' ', Author.q.name, ' ',
97 unicode(value, default_encoding)
99 conditions = mk_search_conditions(
100 Author, search_type, case_sensitive, avalues, expressions)
101 join_expressions.extend(conditions)
104 evalues['name'] = args.ext
106 evalues['id'] = args.eid
108 join_expressions.append(Book.j.extension)
109 conditions = mk_search_conditions(
110 Extension, search_type, case_sensitive, evalues)
111 join_expressions.extend(conditions)
113 for column in 'name', 'title':
114 value = getattr(args, 'g' + column)
116 gvalues[column] = unicode(value, default_encoding)
118 gvalues['id'] = args.gid
120 join_expressions.append(Book.j.genres)
121 conditions = mk_search_conditions(
122 Genre, search_type, case_sensitive, gvalues)
123 join_expressions.extend(conditions)
126 lvalues['name'] = args.lang
128 lvalues['id'] = args.lid
130 join_expressions.append(Book.j.language)
131 conditions = mk_search_conditions(
132 Language, search_type, case_sensitive, lvalues)
133 join_expressions.extend(conditions)
134 books = search_books(search_type, case_sensitive, values, join_expressions,
135 orderBy=('series', 'ser_no', 'title'))
140 print book.title.encode(default_encoding),
141 if args.verbose >= 1:
142 print "(id=%d)" % book.id,
144 if args.verbose >= 1:
145 print " ", _("Author(s)").encode(default_encoding), ":",
146 for author in book.authors:
148 (author.surname, author.name, author.misc_name))
149 fullname = u' '.join(names)
150 print fullname.encode(default_encoding),
152 print " ", _("Genre(s)").encode(default_encoding), ":",
153 for genre in book.genres:
154 print (genre.title or genre.name).encode(default_encoding),
157 print " ", _("Series").encode(default_encoding), ":",
158 print book.series.encode(default_encoding), \
161 if args.verbose >= 2:
162 print " ", _("Date").encode(default_encoding), ":", book.date
163 print " ", _("Language").encode(default_encoding), ":", \
164 book.language.name.encode(default_encoding)
166 if args.verbose >= 3:
167 print " ", _("Archive").encode(default_encoding), ":", book.archive
168 print " ", _("File").encode(default_encoding), ":", book.file
169 print " ", _("Extension").encode(default_encoding), ":", \
170 book.extension.name.encode(default_encoding)
171 print " ", _("Size").encode(default_encoding), ":", \
172 book.size, _("bytes").encode(default_encoding)
173 print " ", _("Deleted").encode(default_encoding), ":", \
174 _(str(book.deleted)).encode(default_encoding)
178 def _search_extensions(case_sensitive, search_type, args):
179 values = _get_values(args, 'name', 'id')
180 if case_sensitive is None:
181 case_sensitive = _guess_case_sensitivity(values)
184 extensions = search_extensions(search_type, case_sensitive, values,
187 print_count(extensions)
189 for ext in extensions:
190 print ext.name.encode(default_encoding), \
191 (u"(%s: %d)" % (_('books'), ext.count)).encode(default_encoding),
192 if args.verbose >= 1:
193 print "(id=%d)" % ext.id,
195 print_count(extensions)
198 def _search_genres(case_sensitive, search_type, args):
199 values = _get_values(args, 'name', 'title', 'id')
200 if case_sensitive is None:
201 case_sensitive = _guess_case_sensitivity(values)
202 genres = search_genres(search_type, case_sensitive, values, orderBy='name')
207 names = filter(None, (genre.name, genre.title))
208 fullname = u' '.join(names)
209 print fullname.encode(default_encoding), \
210 (u"(%s: %d)" % (_('books'), genre.count)).encode(default_encoding),
211 if args.verbose >= 1:
212 print "(id=%d)" % genre.id,
217 def _search_languages(case_sensitive, search_type, args):
218 values = _get_values(args, 'name', 'id')
219 if case_sensitive is None:
220 case_sensitive = _guess_case_sensitivity(values)
223 languages = search_languages(search_type, case_sensitive, values,
226 print_count(languages)
228 for lang in languages:
229 print lang.name.encode(default_encoding), \
230 (u"(%s: %d)" % (_('books'), lang.count)).encode(default_encoding),
231 if args.verbose >= 1:
232 print "(id=%d)" % lang.id,
234 print_count(languages)
237 if __name__ == '__main__':
238 main_parser = argparse.ArgumentParser(description='Search')
239 main_parser.add_argument('-d', '--database', help='database URI')
240 main_parser.add_argument('-i', '--ignore-case', action='store_true',
242 '(default is to guess)')
243 main_parser.add_argument('-I', '--case-sensitive', action='store_true',
244 help='don\'t ignore case')
245 main_parser.add_argument('-t', '--start', action='store_true',
246 help='search substring at the start '
247 '(this is the default)')
248 main_parser.add_argument('-s', '--substring', action='store_true',
249 help='search substring anywhere')
250 main_parser.add_argument('-f', '--full', action='store_true',
251 help='match the entire string')
252 main_parser.add_argument('-c', '--count', action='store_true',
253 help='count instead of output')
254 main_parser.add_argument('-v', '--verbose', action='count',
255 help='output more details about books; '
256 'repeat for even more details')
257 subparsers = main_parser.add_subparsers(help='Commands')
259 parser = subparsers.add_parser('authors', help='Search authors')
260 parser.add_argument('-s', '--surname', help='search by surname')
261 parser.add_argument('-n', '--name', help='search by name')
262 parser.add_argument('-m', '--misc-name', help='search by misc. name')
263 parser.add_argument('--id', help='search by database id')
264 parser.add_argument('fullname', nargs='?', help='search by full name')
265 parser.set_defaults(func=_search_authors)
267 parser = subparsers.add_parser('books', help='Search books')
268 parser.add_argument('-t', '--title', help='search by title')
269 parser.add_argument('-s', '--series', help='search by series')
270 parser.add_argument('-a', '--archive', help='search by archive (zip file)')
271 parser.add_argument('-f', '--file', help='search by file name')
272 parser.add_argument('--id', help='search by database id')
273 parser.add_argument('--surname', help='search by author\'s surname')
274 parser.add_argument('--name', help='search by author\'s name')
275 parser.add_argument('--misc-name', help='search by author\'s misc. name')
276 parser.add_argument('--fullname', help='search by author\'s full name')
277 parser.add_argument('--aid', help='search by author\'s id')
278 parser.add_argument('-e', '--ext', help='search by file extension')
279 parser.add_argument('--eid', help='search by extension\'s id')
280 parser.add_argument('--gname', help='search by genre\'s name')
281 parser.add_argument('--gtitle', help='search by genre\'s title')
282 parser.add_argument('--gid', help='search by genre\'s id')
283 parser.add_argument('-l', '--lang', help='search by language')
284 parser.add_argument('--lid', help='search by language\'s id')
285 parser.set_defaults(func=_search_books)
287 parser = subparsers.add_parser('ext', help='Search extensions')
288 parser.add_argument('name', nargs='?', help='search by name')
289 parser.add_argument('--id', help='search by database id')
290 parser.set_defaults(func=_search_extensions)
292 parser = subparsers.add_parser('genres', help='Search genres')
293 parser.add_argument('-n', '--name', help='search by name')
294 parser.add_argument('-t', '--title', help='search by title')
295 parser.add_argument('--id', help='search by database id')
296 parser.set_defaults(func=_search_genres)
298 parser = subparsers.add_parser('lang', help='Search languages')
299 parser.add_argument('name', nargs='?', help='search by name')
300 parser.add_argument('--id', help='search by database id')
301 parser.set_defaults(func=_search_languages)
303 args = main_parser.parse_args()
305 if args.case_sensitive:
308 "Cannot search case sensitive and case insensitive "
309 "at the same time\n")
310 main_parser.print_help()
313 case_sensitive = True
314 elif args.ignore_case:
315 case_sensitive = False
317 case_sensitive = None # guess case sensitivity
319 if int(args.start) + int(args.substring) + int(args.full) > 1:
321 "Cannot search case sensitive and case insensitive "
322 "at the same time\n")
323 main_parser.print_help()
326 search_type = 'start'
328 search_type = 'substring'
332 search_type = 'start'
334 open_db(args.database)
335 args.func(case_sensitive, search_type, args)