PageRenderTime 51ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 0ms

/mobile/forums.py

https://bitbucket.org/Zopieux/sdzmoar
Python | 193 lines | 150 code | 12 blank | 31 comment | 13 complexity | e1b756325b62936919cc2861a0ea3041 MD5 | raw file
Possible License(s): BSD-3-Clause
  1. #!/usr/bin/env python
  2. # -*- coding: utf8 -*-
  3. ###
  4. # Copyright (c) 2011, Alexandre `Zopieux` Macabies
  5. # All rights reserved.
  6. #
  7. # Redistribution and use in source and binary forms, with or without
  8. # modification, are permitted provided that the following conditions are met:
  9. #
  10. # * Redistributions of source code must retain the above copyright notice,
  11. # this list of conditions, and the following disclaimer.
  12. # * Redistributions in binary form must reproduce the above copyright notice,
  13. # this list of conditions, and the following disclaimer in the
  14. # documentation and/or other materials provided with the distribution.
  15. # * Neither the name of the author of this software nor the name of
  16. # contributors to this software may be used to endorse or promote products
  17. # derived from this software without specific prior written consent.
  18. #
  19. # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  20. # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  21. # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  22. # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
  23. # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  24. # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
  25. # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
  26. # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
  27. # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  28. # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  29. # POSSIBILITY OF SUCH DAMAGE.
  30. ###
  31. import re
  32. from annoying.decorators import render_to
  33. from sdzmoar.common.utils import *
  34. regex_forumid = re.compile(r'forum\-81\-(?P<id>\d+)', re.I)
  35. regex_topicid = re.compile(r'forum\-83\-(?P<id>\d+)', re.I)
  36. def _index():
  37. soup = get_soup_or_404('http://www.siteduzero.com/forum.html', timeout=Times.month)
  38. categories = []
  39. tmpcatname = None
  40. tmpforums = []
  41. for elem in soup.findall('//table[@class="liste_cat"]/tbody/tr'):
  42. if elem.get('class') == 'grosse_cat':
  43. if tmpforums:
  44. categories.append({'name': tmpcatname, 'forums': tmpforums})
  45. tmpforums = []
  46. tmpcatname = clean_text(elem.find('td/a').text)
  47. else:
  48. url = elem.find('td[@class="nom_forum"]/a').get('href')
  49. name = clean_text(elem.find('td[@class="nom_forum"]/a').text)
  50. try:
  51. description = clean_text(elem.find('td[@class="nom_forum"]/span').text)
  52. except AttributeError:
  53. description = None
  54. parsed = regex_forumid.search(url)
  55. if parsed is None:
  56. tmpforums.append({
  57. 'redirection': url,
  58. 'name': name,
  59. 'description': description,
  60. })
  61. else:
  62. tmpforums.append({
  63. 'id': parsed.group('id'),
  64. 'name': name,
  65. 'description': description,
  66. 'count_topics': clean_int(elem.find('td[3]').text),
  67. 'count_messages': clean_int(elem.find('td[4]').text),
  68. })
  69. # final item
  70. if tmpforums:
  71. categories.append({'name': tmpcatname, 'forums': tmpforums})
  72. return {'categories': categories}
  73. @render_to('mobile/forums/index.html')
  74. def index(request):
  75. return _index()
  76. def _category(id, page=None):
  77. if page is None: # handles reversed page order
  78. url = 'http://www.siteduzero.com/forum-81-%s.html' % id
  79. else:
  80. url = 'http://www.siteduzero.com/forum-81-%s-p%i.html' % (id, page)
  81. soup = get_soup_or_404(url, timeout=Times.day)
  82. title = clean_text(soup.find('//h1').text)
  83. topics = []
  84. for elem in soup.findall('//table[@class="liste_cat"]/tbody/tr'):
  85. if u'postit' in elem.get('class', ''):
  86. continue
  87. meta = elem.find('td[2]')
  88. subtitle = elem.find('td[3]/span')
  89. moved = meta.find(u'img[@alt="Déplacé"]') is not None
  90. if subtitle is not None:
  91. subtitle = clean_text(subtitle.text)
  92. res = {
  93. 'id': regex_topicid.search(elem.find('td[3]/a').get('href')).group('id'),
  94. 'title': clean_text(elem.find('td[3]/a').text),
  95. 'created_on': sdz_datetime(elem.find('td[3]/a').get('title')[6:]), # Cree <date>
  96. 'subtitle': subtitle,
  97. 'author': sdz_member(elem.find('td[5]/a')),
  98. 'moved': moved,
  99. }
  100. if not moved:
  101. res.update({
  102. 'postit': meta.find(u'img[@alt="Post-it"]') is not None,
  103. 'closed': meta.find(u'img[@alt="Fermé"]') is not None,
  104. 'solved': meta.find(u'img[@alt="Résolu"]') is not None,
  105. 'messages': clean_int(elem.find('td[6]').text),
  106. })
  107. topics.append(res)
  108. pager = SdzPager.fromHtml(soup.find('//table[@class="liste_cat"]'))
  109. return {'topics': topics, 'pager': pager, 'title': title}
  110. @render_to('mobile/forums/category.html')
  111. def category(request, id):
  112. page = get_page(request, default=None)
  113. return _category(id, page)
  114. def _topic(id, page=None):
  115. soup = get_soup_or_404('http://www.siteduzero.com/forum-83-%s-p%i.html' % (id, page), timeout=10 * Times.minute)
  116. topic_title = clean_text(soup.find('//h1').text)
  117. messages = []
  118. tempmeta = {}
  119. for elem in soup.findall('//table[@class="liste_messages"]/tbody/tr'):
  120. if 'header_message' in elem.get('class', ''):
  121. author = sdz_member(elem.find('td[1]//a'))
  122. if clean_text(elem.find('td[2]//span/a').tail).lower().startswith(u'message supprimé'):
  123. reason = clean_text(elem.find('td[2]//span/em').text)
  124. messages.append({
  125. 'author': author,
  126. 'deleted': True,
  127. 'deleted_self': reason.lower() == u"cette réponse a été supprimée par l'utilisateur",
  128. 'deletion_reason': reason,
  129. })
  130. else:
  131. tempmeta = {
  132. 'author': author,
  133. 'posted_on': sdz_datetime(clean_text(elem.find('td[2]//span/a').tail)[6:]), # Posté <date>
  134. }
  135. else:
  136. res = {}
  137. message_td = elem.find('td[2]')
  138. helping_answer = message_td.get('class') == 'message_bonne_reponse'
  139. content = message_td.find('div[@class="boite_message"]')
  140. if helping_answer:
  141. notice_help = content.find('div[@class="info_bonne_reponse"]')
  142. content.remove(notice_help) # helping_answer bool is enough...
  143. edition = content.find('div[@class="message_edite"]')
  144. if edition is not None:
  145. span = edition.find('span')
  146. edition_by_other = span is not None
  147. if edition_by_other:
  148. parent = span
  149. else:
  150. parent = edition
  151. edition_author = sdz_member(parent.find('a'))
  152. date_edition = sdz_datetime(clean_text(parent.text)[6:-3].strip())
  153. content.remove(edition)
  154. res['edition_author'] = edition_author
  155. res['date_edition'] = date_edition
  156. res['edition_by_other'] = edition_by_other
  157. sig = content.find('div[@class="signature"]')
  158. if sig is not None:
  159. content.remove(sig)
  160. res['content'] = zcode_parser(html_string(content))
  161. res['helping'] = helping_answer
  162. res.update(tempmeta)
  163. messages.append(res)
  164. pager = SdzPager.fromHtml(soup.find('//table[@class="liste_messages"]'))
  165. return {'messages': messages, 'pager': pager, 'topic_title': topic_title}
  166. @render_to('mobile/forums/topic.html')
  167. def topic(request, id):
  168. page = get_page(request)
  169. return _topic(id, page)