Django Library PyGal
Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

userviews.py 17KB

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