Django Library PyGal
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

userviews.py 18KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544
  1. from django.shortcuts import render
  2. from django.utils.translation import gettext as _
  3. import geo
  4. import mimetypes
  5. from ..models import get_item_type, get_item_by_rel_path, TYPE_AUDIO, TYPE_FOLDER, TYPE_IMAGE, TYPE_OTHER, TYPE_VIDEO
  6. import os
  7. import pygal
  8. from ..queries import search_result_query
  9. import random
  10. import stringtools
  11. import themes
  12. ADDTAG_ENTRY = 'addtag-main'
  13. INFOVIEW_ENTRY = 'infoview-main'
  14. DOWNLOAD_ENTRY = 'download-main'
  15. DOWNLOAD_FLAT_ENTRY = 'download-flat-sub'
  16. DOWNLOAD_STRUCT_ENTRY = 'download-struct-sub'
  17. FAVOURITE_ENTRY = 'favourite-main'
  18. REPEAT_ENTRY = 'repeat-main'
  19. SHUFFLE_ENTRY = 'shuffle-main'
  20. class ItemDoesNotEsistWithinParent(Exception):
  21. pass
  22. def get_wrapper_class(full_path):
  23. return {
  24. TYPE_FOLDER: folder_view,
  25. TYPE_IMAGE: image_view,
  26. TYPE_VIDEO: video_view,
  27. TYPE_AUDIO: audio_view,
  28. TYPE_OTHER: other_view,
  29. }.get(get_item_type(full_path), base_view)
  30. def get_wrapper_instance(full_path, request, is_nxt_prv_item=False):
  31. return get_wrapper_class(full_path)(request, full_path, is_nxt_prv_item)
  32. def random_copy(request, lst):
  33. shuffle_id = pygal.get_shuffle_id(request)
  34. #
  35. rv = lst[:]
  36. if shuffle_id is not None:
  37. r = random.Random()
  38. r.seed(shuffle_id)
  39. r.shuffle(rv)
  40. return rv
  41. class base_view(object):
  42. def __init__(self, request, full_path, is_nxt_prv_item):
  43. self.request = request
  44. self.full_path = full_path
  45. self.is_nxt_prv_item = is_nxt_prv_item
  46. #
  47. self.item = get_item_by_rel_path(pygal.get_rel_path(full_path))
  48. if self.item is None:
  49. raise LookupError("Error while initialising base_view, but item for %s does not exist." % full_path)
  50. self.is_item = self.item.type != TYPE_FOLDER
  51. #
  52. self.mime_type = mimetypes.types_map.get(os.path.splitext(self.full_path)[1])
  53. self.template = 'pygal/empty.html'
  54. @property
  55. def tags(self):
  56. return self.item.tag_set.all()
  57. def may_read(self):
  58. return self.item.may_read(self.request.user)
  59. def may_modify(self):
  60. return self.item.may_modify(self.request.user)
  61. def context_adaption(self, context):
  62. context['item'] = self
  63. context['thumbnail_size'] = pygal.get_thumbnail_size(self.request)
  64. context['title'] = self.name or ''
  65. context[context.ACTIONBAR].extend(self.actionbar)
  66. @property
  67. def heading(self):
  68. return '%s' % self.item.name
  69. @property
  70. def general_information(self):
  71. rv = []
  72. full_path = pygal.get_full_path(self.item.rel_path)
  73. rv.append({'description': _('Name'), 'data': os.path.basename(full_path), 'url': None})
  74. rv.append({'description': _('Creation Time'), 'data': self.item.item_data.formatted_datetime, 'url': None})
  75. rv.append({'description': _('Path'), 'data': os.path.dirname(full_path), 'url': None})
  76. rv.append({'description': _('Size'), 'data': stringtools.physical_value_repr(self.item.item_data.size, 'B'), 'url': None})
  77. rv.append({'description': _('UID'), 'data': self.item.current_uid(), 'url': None})
  78. return rv
  79. def __actionbar__(self):
  80. b = themes.bar()
  81. if self.is_item:
  82. if self.is_my_favourite:
  83. b.append_entry(
  84. FAVOURITE_ENTRY,
  85. '',
  86. themes.color_icon_url(self.request, 'is_favourite.png'),
  87. pygal.url_favourite_unset(self.request, self.item.rel_path),
  88. True,
  89. False
  90. )
  91. else:
  92. b.append_entry(
  93. FAVOURITE_ENTRY,
  94. '',
  95. themes.color_icon_url(self.request, 'favourite.png'),
  96. pygal.url_favourite_set(self.request, self.item.rel_path),
  97. True,
  98. False
  99. )
  100. if pygal.is_infoview(self.request):
  101. b.append_entry(
  102. INFOVIEW_ENTRY,
  103. _('Info'),
  104. themes.color_icon_url(self.request, 'info.png'),
  105. pygal.url_infoview(self.request, self.item.rel_path),
  106. True,
  107. True
  108. )
  109. else:
  110. b.append_entry(
  111. INFOVIEW_ENTRY,
  112. _('Info'),
  113. themes.color_icon_url(self.request, 'info.png'),
  114. pygal.url_infoview(self.request, self.item.rel_path, search=pygal.SEARCH_KEEP),
  115. True,
  116. False
  117. )
  118. b.append_entry(
  119. DOWNLOAD_ENTRY,
  120. _('Download'),
  121. themes.color_icon_url(self.request, 'download.png'),
  122. pygal.url_download(self.request, self.item.rel_path),
  123. True,
  124. False
  125. )
  126. if type(self) is not folder_view:
  127. if pygal.is_repeatview(self.request):
  128. b.append_entry(
  129. REPEAT_ENTRY,
  130. _('Repeat'),
  131. themes.color_icon_url(self.request, 'stop.png'),
  132. pygal.url_userview(self.request, self.item.rel_path, search=pygal.SEARCH_KEEP),
  133. True,
  134. True
  135. )
  136. else:
  137. b.append_entry(
  138. REPEAT_ENTRY,
  139. _('Repeat'),
  140. themes.color_icon_url(self.request, 'play.png'),
  141. pygal.url_repeatview(self.request, self.item.rel_path),
  142. True,
  143. False
  144. )
  145. if pygal.get_shuffle_id(self.request) is None:
  146. b.append_entry(
  147. SHUFFLE_ENTRY,
  148. _('Shuffle'),
  149. themes.color_icon_url(self.request, 'shuffle.png'),
  150. pygal.url_userview(self.request, self.item.rel_path, shuffle=pygal.SHUFFLE_ENABLE, search=pygal.SEARCH_KEEP),
  151. True,
  152. False
  153. )
  154. else:
  155. b.append_entry(
  156. SHUFFLE_ENTRY,
  157. _('Shuffle'),
  158. themes.color_icon_url(self.request, 'shuffle.png'),
  159. pygal.url_userview(self.request, self.item.rel_path, shuffle=pygal.SHUFFLE_DISABLE, search=pygal.SEARCH_KEEP),
  160. True,
  161. True
  162. )
  163. if self.is_item and self.may_modify():
  164. b.append_entry(
  165. ADDTAG_ENTRY,
  166. _('Add Tag'),
  167. themes.color_icon_url(self.request, 'edit2.png'),
  168. pygal.url_addtag(self.request, self.item.rel_path),
  169. True,
  170. False
  171. )
  172. return b
  173. @property
  174. def is_my_favourite(self):
  175. return self.request.user in self.item.favourite_of.all()
  176. @property
  177. def actionbar(self):
  178. return self.__actionbar__()
  179. def __tagbar__(self):
  180. b = themes.bar(self.request)
  181. i = 0
  182. for t in self.item.tag_set.all():
  183. i += 1
  184. edit_url = pygal.url_tagedit(self.request, t.id) if self.may_modify() else None
  185. search_query = 'tag:%s' % t.text
  186. search_url = pygal.url_userview(self.request, '', search=search_query)
  187. b.append_entry('tag-%d' % i, t.text, themes.color_icon_url(self.request, '%d.png' % (i % 10)), edit_url, True, False)
  188. if pygal.get_search_query(self.request) != search_query:
  189. b.append_entry_to_entry('tag-%d' % i, 'tagedit-%d' % i, _('Edit'), themes.gray_icon_url(self.request, 'edit2.png'), edit_url, True, False)
  190. b.append_entry_to_entry('tag-%d' % i, 'tagsearch-%d' % i, _('Search'), themes.gray_icon_url(self.request, 'search.png'), search_url, True, False)
  191. return b
  192. @property
  193. def tagbar(self):
  194. return self.__tagbar__()
  195. @property
  196. def duration(self):
  197. return self.item.item_data.duration + 1.5
  198. @property
  199. def url_item(self):
  200. return pygal.url_item(self.request, self.item.rel_path)
  201. @property
  202. def url_repeatview(self):
  203. return pygal.url_repeatview(self.request, self.item.rel_path)
  204. @property
  205. def is_repeatview(self):
  206. return pygal.is_repeatview(self.request)
  207. @property
  208. def url_thumbnail(self):
  209. return pygal.url_thumbnail(self.request, self.item.rel_path)
  210. @property
  211. def url_userview(self):
  212. if self.is_nxt_prv_item:
  213. return pygal.url_userview(self.request, self.item.rel_path, search=pygal.SEARCH_KEEP, repeat=pygal.REPEAT_KEEP)
  214. else:
  215. return pygal.url_userview(self.request, self.item.rel_path, search=pygal.SEARCH_KEEP, repeat=pygal.REPEAT_DISABLE)
  216. @property
  217. def url_webnail(self):
  218. return pygal.url_webnail(self.request, self.item.rel_path)
  219. @property
  220. def name(self):
  221. return self.item.name
  222. @property
  223. def date_txt(self):
  224. return self.item.item_data.formatted_datetime
  225. def __parent__(self):
  226. if pygal.is_searchview(self.request) or pygal.is_favouriteview(self.request):
  227. return query_view(self.request, search_result_query(self.request))
  228. else:
  229. return get_wrapper_instance(os.path.dirname(self.full_path), self.request)
  230. def __nxt_prv__(self, direction):
  231. if direction not in [-1, 1]:
  232. raise ValueError("Parameter direction is incorrect: %s" % repr(direction))
  233. fp_il = random_copy(self.request, self.__parent__().sorted_fullpathlist())
  234. try:
  235. i = fp_il.index(self.full_path)
  236. except ValueError as e:
  237. raise ItemDoesNotEsistWithinParent(e)
  238. lgt = len(fp_il)
  239. for i in range(i + direction, i + direction * lgt, direction):
  240. full_path = fp_il[i % lgt]
  241. if get_item_type(full_path) != TYPE_FOLDER:
  242. return get_wrapper_instance(full_path, self.request, is_nxt_prv_item=True)
  243. return self
  244. @property
  245. def nxt(self):
  246. return self.__nxt_prv__(1)
  247. @property
  248. def prv(self):
  249. return self.__nxt_prv__(-1)
  250. def render(self, context):
  251. return render(self.request, self.template, context=context)
  252. class query_view(object):
  253. def __init__(self, request, query):
  254. self.request = request
  255. self.query = query
  256. #
  257. self.template = 'pygal/overview.html'
  258. #
  259. self.__item_list__ = None
  260. def context_adaption(self, context):
  261. context['item'] = self
  262. context['thumbnail_size'] = pygal.get_thumbnail_size(self.request)
  263. context['title'] = self.name or ''
  264. context[context.ACTIONBAR].extend(self.actionbar)
  265. @property
  266. def name(self):
  267. if pygal.is_favouriteview(self.request):
  268. return _('My Favourites')
  269. elif pygal.is_searchview(self.request):
  270. return _('Search: "%s"' % pygal.get_search_query(self.request))
  271. else:
  272. return '-'
  273. def __actionbar__(self):
  274. b = themes.bar()
  275. if pygal.is_infoview(self.request):
  276. b.append_entry(
  277. INFOVIEW_ENTRY,
  278. _('Info'),
  279. themes.color_icon_url(self.request, 'info.png'),
  280. pygal.url_infoview(self.request, ''),
  281. True,
  282. True
  283. )
  284. else:
  285. b.append_entry(
  286. INFOVIEW_ENTRY,
  287. _('Info'),
  288. themes.color_icon_url(self.request, 'info.png'),
  289. pygal.url_infoview(self.request, ''),
  290. True,
  291. False
  292. )
  293. b.append_entry(
  294. DOWNLOAD_ENTRY,
  295. _('Download'),
  296. themes.color_icon_url(self.request, 'download.png'),
  297. None,
  298. True,
  299. False
  300. )
  301. b.append_entry_to_entry(
  302. DOWNLOAD_ENTRY,
  303. DOWNLOAD_STRUCT_ENTRY,
  304. _('Download (Structured)'),
  305. themes.gray_icon_url(self.request, 'download.png'),
  306. pygal.url_download(self.request, ''),
  307. True,
  308. False
  309. )
  310. b.append_entry_to_entry(
  311. DOWNLOAD_ENTRY,
  312. DOWNLOAD_FLAT_ENTRY,
  313. _('Download (Flat)'),
  314. themes.gray_icon_url(self.request, 'download.png'),
  315. pygal.url_download(self.request, '', flat=True),
  316. True,
  317. False
  318. )
  319. if pygal.get_shuffle_id(self.request) is None:
  320. b.append_entry(
  321. SHUFFLE_ENTRY,
  322. _('Shuffle'),
  323. themes.color_icon_url(self.request, 'shuffle.png'),
  324. pygal.url_userview(self.request, '', shuffle=pygal.SHUFFLE_ENABLE, search=pygal.SEARCH_KEEP),
  325. True,
  326. False
  327. )
  328. else:
  329. b.append_entry(
  330. SHUFFLE_ENTRY,
  331. _('Shuffle'),
  332. themes.color_icon_url(self.request, 'shuffle.png'),
  333. pygal.url_userview(self.request, '', shuffle=pygal.SHUFFLE_DISABLE, search=pygal.SEARCH_KEEP),
  334. True,
  335. True
  336. )
  337. return b
  338. @property
  339. def actionbar(self):
  340. return self.__actionbar__()
  341. def sorted_fullpathlist(self):
  342. il = []
  343. for item_model in self.query:
  344. if item_model.may_read(self.request.user):
  345. il.append(item_model)
  346. il.sort(key=lambda entry: entry.sort_string(), reverse=True)
  347. return [pygal.get_full_path(i.rel_path) for i in il]
  348. def __init_itemlist__(self):
  349. if self.__item_list__ is None:
  350. self.__item_list__ = []
  351. for full_path in self.sorted_fullpathlist():
  352. w = get_wrapper_instance(full_path, self.request)
  353. self.__item_list__.append(w)
  354. @property
  355. def item_list(self):
  356. self.__init_itemlist__()
  357. return random_copy(self.request, self.__item_list__)
  358. def render(self, context):
  359. return render(self.request, self.template, context=context)
  360. class folder_view(base_view):
  361. def __init__(self, *args, **kwargs):
  362. base_view.__init__(self, *args, **kwargs)
  363. #
  364. self.template = 'pygal/overview.html'
  365. #
  366. self.__item_list__ = None
  367. def __actionbar__(self):
  368. b = base_view.__actionbar__(self)
  369. b.append_entry_to_entry(
  370. DOWNLOAD_ENTRY,
  371. DOWNLOAD_STRUCT_ENTRY,
  372. _('Download (Structured)'),
  373. themes.gray_icon_url(self.request, 'download.png'),
  374. pygal.url_download(self.request, self.item.rel_path),
  375. True,
  376. False
  377. )
  378. b.append_entry_to_entry(
  379. DOWNLOAD_ENTRY,
  380. DOWNLOAD_FLAT_ENTRY,
  381. _('Download (Flat)'),
  382. themes.gray_icon_url(self.request, 'download.png'),
  383. pygal.url_download(self.request, self.item.rel_path, flat=True),
  384. True,
  385. False
  386. )
  387. return b
  388. def sorted_fullpathlist(self):
  389. return [pygal.get_full_path(i.rel_path) for i in self.item.sorted_itemlist()]
  390. def __init_fullpathlist__(self):
  391. if self.__item_list__ is None:
  392. self.__item_list__ = []
  393. for full_path in self.sorted_fullpathlist():
  394. w = get_wrapper_instance(full_path, self.request)
  395. self.__item_list__.append(w)
  396. @property
  397. def item_list(self):
  398. self.__init_fullpathlist__()
  399. return random_copy(self.request, self.__item_list__)
  400. @property
  401. def url_thumbnail(self):
  402. return pygal.url_thumbnail(self. request, self.item.thumbnail_item().rel_path)
  403. class image_view(base_view):
  404. def __init__(self, *args, **kwargs):
  405. base_view.__init__(self, *args, **kwargs)
  406. #
  407. self.template = 'pygal/image.html'
  408. def __actionbar__(self):
  409. b = base_view.__actionbar__(self)
  410. gps_data = self.item.item_data.gps
  411. if gps_data is not None:
  412. b.append_entry(
  413. 'actionbar-gpslink',
  414. _('GPS'),
  415. themes.color_icon_url(self.request, 'gps.png'),
  416. geo.osm.landmark_link(geo.gps.coordinate(**gps_data)),
  417. True,
  418. False
  419. )
  420. return b
  421. @property
  422. def width(self):
  423. if self.item.item_data.orientation in [5, 6, 7, 8]:
  424. return self.item.item_data.height
  425. else:
  426. return self.item.item_data.width
  427. @property
  428. def height(self):
  429. if self.item.item_data.orientation in [5, 6, 7, 8]:
  430. return self.item.item_data.width
  431. else:
  432. return self.item.item_data.height
  433. class video_view(base_view):
  434. def __init__(self, *args, **kwargs):
  435. base_view.__init__(self, *args, **kwargs)
  436. #
  437. self.template = 'pygal/video.html'
  438. @property
  439. def use_internal_player(self):
  440. return self.mime_type in [mimetypes.types_map.get(ext) for ext in ['.mp4', '.webm', '.ogv', '.flv', '.3gp', ]]
  441. @property
  442. def webnail_width(self):
  443. w, h = self.item.item_data.width, self.item.item_data.height
  444. return int(pygal.get_webnail_size(self.request) * w / max(w, h))
  445. @property
  446. def webnail_height(self):
  447. w, h = self.item.item_data.width, self.item.item_data.height
  448. return int(pygal.get_webnail_size(self.request) * h / max(w, h))
  449. class audio_view(base_view):
  450. def __init__(self, *args, **kwargs):
  451. base_view.__init__(self, *args, **kwargs)
  452. #
  453. self.template = 'pygal/audio.html'
  454. @property
  455. def heading(self):
  456. return '%02d - %s' % (self.item.item_data.track, self.item.item_data.title)
  457. @property
  458. def audio_information(self):
  459. rv = []
  460. entries = (
  461. (_('Artist'), self.item.artist_c),
  462. (_('Album'), self.item.album_c),
  463. (_('Year'), self.item.year_c),
  464. (_('Title'), self.item.title_c),
  465. (_('Duration'), stringtools.time_repr(self.item.duration_c)),
  466. )
  467. for description, data in entries:
  468. if data is not None:
  469. rv.append({'description': description, 'data': data, 'url': None})
  470. return rv
  471. class other_view(base_view):
  472. def __init__(self, *args, **kwargs):
  473. base_view.__init__(self, *args, **kwargs)
  474. #
  475. self.template = 'pygal/other.html'