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(count):
34 print _("Found").encode(default_encoding), ":", 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'))
58 print_count(authors.count())
61 for author in authors:
62 names = filter(None, (author.surname, author.name, author.misc_name))
63 fullname = u' '.join(names)
64 print fullname.encode(default_encoding), \
65 (u"(%s: %d)" % (_('books'), author.count))\
66 .encode(default_encoding),
68 print "(id=%d)" % author.id,
74 def _search_books(case_sensitive, search_type, args):
76 values = _get_values(args, 'title', 'series', 'archive', 'file', 'id')
77 if case_sensitive is None:
78 test_values = values.copy()
79 test_values.update(_get_values(args, 'surname', 'name', 'misc_name',
80 'fullname', 'ext', 'gname', 'gtitle',
82 case_sensitive = _guess_case_sensitivity(test_values)
83 avalues = _get_values(args, 'surname', 'name', 'misc_name', 'fullname')
85 avalues['id'] = args.aid
87 if (args.surname or args.name or args.misc_name) and args.fullname:
89 "Cannot search by names and full name at the same time\n")
90 main_parser.print_help()
93 join_expressions.append(Book.j.authors)
97 CONCAT(Author.q.surname, ' ', Author.q.name, ' ',
99 unicode(value, default_encoding)
101 conditions = mk_search_conditions(
102 Author, search_type, case_sensitive, avalues, expressions)
103 join_expressions.extend(conditions)
106 evalues['name'] = args.ext
108 evalues['id'] = args.eid
110 join_expressions.append(Book.j.extension)
111 conditions = mk_search_conditions(
112 Extension, search_type, case_sensitive, evalues)
113 join_expressions.extend(conditions)
115 for column in 'name', 'title':
116 value = getattr(args, 'g' + column)
118 gvalues[column] = unicode(value, default_encoding)
120 gvalues['id'] = args.gid
122 join_expressions.append(Book.j.genres)
123 conditions = mk_search_conditions(
124 Genre, search_type, case_sensitive, gvalues)
125 join_expressions.extend(conditions)
128 lvalues['name'] = args.lang
130 lvalues['id'] = args.lid
132 join_expressions.append(Book.j.language)
133 conditions = mk_search_conditions(
134 Language, search_type, case_sensitive, lvalues)
135 join_expressions.extend(conditions)
136 books = search_books(search_type, case_sensitive, values, join_expressions,
137 orderBy=('series', 'ser_no', 'title'))
139 print_count(books.count())
143 print book.title.encode(default_encoding),
144 if args.verbose >= 1:
145 print "(id=%d)" % book.id,
147 if args.verbose >= 1:
148 print " ", _("Author(s)").encode(default_encoding), ":",
149 for author in book.authors:
151 (author.surname, author.name, author.misc_name))
152 fullname = u' '.join(names)
153 print fullname.encode(default_encoding),
155 print " ", _("Genre(s)").encode(default_encoding), ":",
156 for genre in book.genres:
157 print (genre.title or genre.name).encode(default_encoding),
160 print " ", _("Series").encode(default_encoding), ":",
161 print book.series.encode(default_encoding), \
164 if args.verbose >= 2:
165 print " ", _("Date").encode(default_encoding), ":", book.date
166 print " ", _("Language").encode(default_encoding), ":", \
167 book.language.name.encode(default_encoding)
169 if args.verbose >= 3:
170 print " ", _("Archive").encode(default_encoding), ":", book.archive
171 print " ", _("File").encode(default_encoding), ":", book.file
172 print " ", _("Extension").encode(default_encoding), ":", \
173 book.extension.name.encode(default_encoding)
174 print " ", _("Size").encode(default_encoding), ":", \
175 book.size, _("bytes").encode(default_encoding)
176 print " ", _("Deleted").encode(default_encoding), ":", \
177 _(str(book.deleted)).encode(default_encoding)
182 def _search_extensions(case_sensitive, search_type, args):
183 values = _get_values(args, 'name', 'id')
184 if case_sensitive is None:
185 case_sensitive = _guess_case_sensitivity(values)
188 extensions = search_extensions(search_type, case_sensitive, values,
191 print_count(extensions.count())
194 for ext in extensions:
195 print ext.name.encode(default_encoding), \
196 (u"(%s: %d)" % (_('books'), ext.count)).encode(default_encoding),
197 if args.verbose >= 1:
198 print "(id=%d)" % ext.id,
204 def _search_genres(case_sensitive, search_type, args):
205 values = _get_values(args, 'name', 'title', 'id')
206 if case_sensitive is None:
207 case_sensitive = _guess_case_sensitivity(values)
208 genres = search_genres(search_type, case_sensitive, values, orderBy='name')
210 print_count(genres.count())
214 names = filter(None, (genre.name, genre.title))
215 fullname = u' '.join(names)
216 print fullname.encode(default_encoding), \
217 (u"(%s: %d)" % (_('books'), genre.count)).encode(default_encoding),
218 if args.verbose >= 1:
219 print "(id=%d)" % genre.id,
225 def _search_languages(case_sensitive, search_type, args):
226 values = _get_values(args, 'name', 'id')
227 if case_sensitive is None:
228 case_sensitive = _guess_case_sensitivity(values)
231 languages = search_languages(search_type, case_sensitive, values,
234 print_count(languages.count())
237 for lang in languages:
238 print lang.name.encode(default_encoding), \
239 (u"(%s: %d)" % (_('books'), lang.count)).encode(default_encoding),
240 if args.verbose >= 1:
241 print "(id=%d)" % lang.id,
247 if __name__ == '__main__':
248 main_parser = argparse.ArgumentParser(description='Search')
249 main_parser.add_argument('-d', '--database', help='database URI')
250 main_parser.add_argument('-i', '--ignore-case', action='store_true',
252 '(default is to guess)')
253 main_parser.add_argument('-I', '--case-sensitive', action='store_true',
254 help='don\'t ignore case')
255 main_parser.add_argument('-t', '--start', action='store_true',
256 help='search substring at the start '
257 '(this is the default)')
258 main_parser.add_argument('-s', '--substring', action='store_true',
259 help='search substring anywhere')
260 main_parser.add_argument('-f', '--full', action='store_true',
261 help='match the entire string')
262 main_parser.add_argument('-c', '--count', action='store_true',
263 help='count instead of output')
264 main_parser.add_argument('-v', '--verbose', action='count',
265 help='output more details about books; '
266 'repeat for even more details')
267 subparsers = main_parser.add_subparsers(help='Commands')
269 parser = subparsers.add_parser('authors', help='Search authors')
270 parser.add_argument('-s', '--surname', help='search by surname')
271 parser.add_argument('-n', '--name', help='search by name')
272 parser.add_argument('-m', '--misc-name', help='search by misc. name')
273 parser.add_argument('--id', help='search by database id')
274 parser.add_argument('fullname', nargs='?', help='search by full name')
275 parser.set_defaults(func=_search_authors)
277 parser = subparsers.add_parser('books', help='Search books')
278 parser.add_argument('-t', '--title', help='search by title')
279 parser.add_argument('-s', '--series', help='search by series')
280 parser.add_argument('-a', '--archive', help='search by archive (zip file)')
281 parser.add_argument('-f', '--file', help='search by file name')
282 parser.add_argument('--id', help='search by database id')
283 parser.add_argument('--surname', help='search by author\'s surname')
284 parser.add_argument('--name', help='search by author\'s name')
285 parser.add_argument('--misc-name', help='search by author\'s misc. name')
286 parser.add_argument('--fullname', help='search by author\'s full name')
287 parser.add_argument('--aid', help='search by author\'s id')
288 parser.add_argument('-e', '--ext', help='search by file extension')
289 parser.add_argument('--eid', help='search by extension\'s id')
290 parser.add_argument('--gname', help='search by genre\'s name')
291 parser.add_argument('--gtitle', help='search by genre\'s title')
292 parser.add_argument('--gid', help='search by genre\'s id')
293 parser.add_argument('-l', '--lang', help='search by language')
294 parser.add_argument('--lid', help='search by language\'s id')
295 parser.set_defaults(func=_search_books)
297 parser = subparsers.add_parser('ext', help='Search extensions')
298 parser.add_argument('name', nargs='?', help='search by name')
299 parser.add_argument('--id', help='search by database id')
300 parser.set_defaults(func=_search_extensions)
302 parser = subparsers.add_parser('genres', help='Search genres')
303 parser.add_argument('-n', '--name', help='search by name')
304 parser.add_argument('-t', '--title', help='search by title')
305 parser.add_argument('--id', help='search by database id')
306 parser.set_defaults(func=_search_genres)
308 parser = subparsers.add_parser('lang', help='Search languages')
309 parser.add_argument('name', nargs='?', help='search by name')
310 parser.add_argument('--id', help='search by database id')
311 parser.set_defaults(func=_search_languages)
313 args = main_parser.parse_args()
315 if args.case_sensitive:
318 "Cannot search case sensitive and case insensitive "
319 "at the same time\n")
320 main_parser.print_help()
323 case_sensitive = True
324 elif args.ignore_case:
325 case_sensitive = False
327 case_sensitive = None # guess case sensitivity
329 if int(args.start) + int(args.substring) + int(args.full) > 1:
331 "Cannot search case sensitive and case insensitive "
332 "at the same time\n")
333 main_parser.print_help()
336 search_type = 'start'
338 search_type = 'substring'
342 search_type = 'start'
344 open_db(args.database)
345 args.func(case_sensitive, search_type, args)