Django Library PyGal
選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。

userviews.py 17KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536
  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 themes
  11. import time
  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': self.__size_txt__(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 __size_txt__(self, size):
  215. unit = {
  216. 0: '',
  217. 1: 'k',
  218. 2: 'M',
  219. 3: 'G',
  220. 4: 'T',
  221. }
  222. u = 0
  223. while u < 4 and size > 1000.:
  224. u += 1
  225. size /= 1000.
  226. if size < 100.:
  227. return '%.2f %s' % (size, unit.get(u, '?'))
  228. else:
  229. return '%.1f %s' % (size, unit.get(u, '?'))
  230. def __duration_txt__(self, duration):
  231. if duration >= 3600:
  232. return time.strftime('%H:%M:%S', time.gmtime(duration))
  233. else:
  234. return time.strftime('%M:%S', time.gmtime(duration))
  235. def __parent__(self):
  236. if pygal.is_searchview(self.request):
  237. return query_view(self.request, search_result_query(self.request))
  238. else:
  239. return get_wrapper_instance(os.path.dirname(self.full_path), self.request)
  240. def __nxt_prv__(self, direction):
  241. if direction not in [-1, 1]:
  242. raise ValueError("Parameter direction is incorrect: %s" % repr(direction))
  243. fp_il = random_copy(self.request, self.__parent__().sorted_fullpathlist())
  244. i = fp_il.index(self.full_path)
  245. lgt = len(fp_il)
  246. for i in range(i + direction, i + direction * lgt, direction):
  247. full_path = fp_il[i % lgt]
  248. if get_item_type(full_path) != TYPE_FOLDER:
  249. return get_wrapper_instance(full_path, self.request)
  250. return self
  251. @property
  252. def nxt(self):
  253. return self.__nxt_prv__(1)
  254. @property
  255. def prv(self):
  256. return self.__nxt_prv__(-1)
  257. def render(self, context):
  258. return render(self.request, self.template, context=context)
  259. class query_view(object):
  260. def __init__(self, request, query):
  261. self.request = request
  262. self.query = query
  263. #
  264. self.template = 'pygal/overview.html'
  265. #
  266. self.__item_list__ = None
  267. def context_adaption(self, context):
  268. context['item'] = self
  269. context['thumbnail_size'] = pygal.get_thumbnail_size(self.request)
  270. context['title'] = self.name or ''
  271. context[context.ACTIONBAR].extend(self.actionbar)
  272. @property
  273. def name(self):
  274. if pygal.is_favouriteview(self.request):
  275. return _('My Favourites')
  276. elif pygal.is_searchview(self.request):
  277. return _('Search: "%s"' % pygal.get_search_query(self.request))
  278. else:
  279. return '-'
  280. def __actionbar__(self):
  281. b = themes.bar()
  282. if pygal.is_infoview(self.request):
  283. b.append_entry(
  284. INFOVIEW_ENTRY,
  285. _('Info'),
  286. themes.color_icon_url(self.request, 'info.png'),
  287. pygal.url_infoview(self.request, ''),
  288. True,
  289. True
  290. )
  291. else:
  292. b.append_entry(
  293. INFOVIEW_ENTRY,
  294. _('Info'),
  295. themes.color_icon_url(self.request, 'info.png'),
  296. pygal.url_infoview(self.request, ''),
  297. True,
  298. False
  299. )
  300. b.append_entry(
  301. DOWNLOAD_ENTRY,
  302. _('Download'),
  303. themes.color_icon_url(self.request, 'download.png'),
  304. None,
  305. True,
  306. False
  307. )
  308. b.append_entry_to_entry(
  309. DOWNLOAD_ENTRY,
  310. DOWNLOAD_STRUCT_ENTRY,
  311. _('Download (Structured)'),
  312. themes.gray_icon_url(self.request, 'download.png'),
  313. pygal.url_download(self.request, ''),
  314. True,
  315. False
  316. )
  317. b.append_entry_to_entry(
  318. DOWNLOAD_ENTRY,
  319. DOWNLOAD_FLAT_ENTRY,
  320. _('Download (Flat)'),
  321. themes.gray_icon_url(self.request, 'download.png'),
  322. pygal.url_download(self.request, '', flat=True),
  323. True,
  324. False
  325. )
  326. if pygal.get_shuffle_id(self.request) is None:
  327. b.append_entry(
  328. SHUFFLE_ENTRY,
  329. _('Shuffle'),
  330. themes.color_icon_url(self.request, 'shuffle.png'),
  331. pygal.url_userview(self.request, '', shuffle=pygal.SHUFFLE_ENABLE, search=pygal.SEARCH_KEEP),
  332. True,
  333. False
  334. )
  335. else:
  336. b.append_entry(
  337. SHUFFLE_ENTRY,
  338. _('Shuffle'),
  339. themes.color_icon_url(self.request, 'shuffle.png'),
  340. pygal.url_userview(self.request, '', shuffle=pygal.SHUFFLE_DISABLE, search=pygal.SEARCH_KEEP),
  341. True,
  342. True
  343. )
  344. return b
  345. @property
  346. def actionbar(self):
  347. return self.__actionbar__()
  348. def sorted_fullpathlist(self):
  349. il = []
  350. for item_model in self.query:
  351. if item_model.may_read(self.request.user):
  352. il.append(item_model)
  353. il.sort(key=lambda entry: entry.sort_string(), reverse=True)
  354. return [pygal.get_full_path(i.rel_path) for i in il]
  355. def __init_itemlist__(self):
  356. if self.__item_list__ is None:
  357. self.__item_list__ = []
  358. for full_path in self.sorted_fullpathlist():
  359. w = get_wrapper_instance(full_path, self.request)
  360. self.__item_list__.append(w)
  361. @property
  362. def item_list(self):
  363. self.__init_itemlist__()
  364. return random_copy(self.request, self.__item_list__)
  365. def render(self, context):
  366. return render(self.request, self.template, context=context)
  367. class folder_view(base_view):
  368. def __init__(self, *args, **kwargs):
  369. base_view.__init__(self, *args, **kwargs)
  370. #
  371. self.template = 'pygal/overview.html'
  372. #
  373. self.__item_list__ = None
  374. def __actionbar__(self):
  375. b = base_view.__actionbar__(self)
  376. b.append_entry_to_entry(
  377. DOWNLOAD_ENTRY,
  378. DOWNLOAD_STRUCT_ENTRY,
  379. _('Download (Structured)'),
  380. themes.gray_icon_url(self.request, 'download.png'),
  381. pygal.url_download(self.request, self.item.rel_path),
  382. True,
  383. False
  384. )
  385. b.append_entry_to_entry(
  386. DOWNLOAD_ENTRY,
  387. DOWNLOAD_FLAT_ENTRY,
  388. _('Download (Flat)'),
  389. themes.gray_icon_url(self.request, 'download.png'),
  390. pygal.url_download(self.request, self.item.rel_path, flat=True),
  391. True,
  392. False
  393. )
  394. return b
  395. def sorted_fullpathlist(self):
  396. return [pygal.get_full_path(i.rel_path) for i in self.item.sorted_itemlist()]
  397. def __init_fullpathlist__(self):
  398. if self.__item_list__ is None:
  399. self.__item_list__ = []
  400. for full_path in self.sorted_fullpathlist():
  401. w = get_wrapper_instance(full_path, self.request)
  402. self.__item_list__.append(w)
  403. @property
  404. def item_list(self):
  405. self.__init_fullpathlist__()
  406. return random_copy(self.request, self.__item_list__)
  407. @property
  408. def url_thumbnail(self):
  409. return pygal.url_thumbnail(self. request, self.item.thumbnail_item().rel_path)
  410. class image_view(base_view):
  411. def __init__(self, *args, **kwargs):
  412. base_view.__init__(self, *args, **kwargs)
  413. #
  414. self.template = 'pygal/image.html'
  415. def __actionbar__(self):
  416. b = base_view.__actionbar__(self)
  417. gps_data = self.item.item_data.gps
  418. if gps_data is not None:
  419. b.append_entry(
  420. 'actionbar-gpslink',
  421. _('GPS'),
  422. themes.color_icon_url(self.request, 'gps.png'),
  423. geo.osm.landmark_link(geo.gps.coordinate(**gps_data)),
  424. True,
  425. False
  426. )
  427. return b
  428. @property
  429. def width(self):
  430. if self.item.item_data.orientation in [5, 6, 7, 8]:
  431. return self.item.item_data.height
  432. else:
  433. return self.item.item_data.width
  434. @property
  435. def height(self):
  436. if self.item.item_data.orientation in [5, 6, 7, 8]:
  437. return self.item.item_data.width
  438. else:
  439. return self.item.item_data.height
  440. class video_view(base_view):
  441. def __init__(self, *args, **kwargs):
  442. base_view.__init__(self, *args, **kwargs)
  443. #
  444. self.template = 'pygal/video.html'
  445. @property
  446. def use_internal_player(self):
  447. return self.mime_type in [mimetypes.types_map.get(ext) for ext in ['.mp4', '.webm', '.ogv', '.flv', '.3gp', ]]
  448. @property
  449. def webnail_width(self):
  450. w, h = self.item.item_data.width, self.item.item_data.height
  451. return int(pygal.get_webnail_size(self.request) * w / max(w, h))
  452. @property
  453. def webnail_height(self):
  454. w, h = self.item.item_data.width, self.item.item_data.height
  455. return int(pygal.get_webnail_size(self.request) * h / max(w, h))
  456. class audio_view(base_view):
  457. def __init__(self, *args, **kwargs):
  458. base_view.__init__(self, *args, **kwargs)
  459. #
  460. self.template = 'pygal/audio.html'
  461. @property
  462. def heading(self):
  463. return '%02d - %s' % (self.item.item_data.track, self.item.item_data.title)
  464. class other_view(base_view):
  465. def __init__(self, *args, **kwargs):
  466. base_view.__init__(self, *args, **kwargs)
  467. #
  468. self.template = 'pygal/other.html'