views.py 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265
  1. # -*- coding: utf-8 -*-
  2. from flask import request, g, redirect, url_for, abort, \
  3. render_template, flash, json, session, Response, Markup
  4. from flask.ext.babel import gettext as _
  5. import itsdangerous
  6. import docutils.core
  7. import ispformat.specs
  8. from datetime import date, time, timedelta, datetime
  9. from urlparse import urlunsplit
  10. import locale
  11. locale.setlocale(locale.LC_ALL, '')
  12. from time import time
  13. import os.path
  14. from . import forms
  15. from .constants import *
  16. from . import app, db, cache
  17. from .models import ISP, ISPWhoosh
  18. from .crawler import WebValidator, PrettyValidator
  19. @app.route('/')
  20. def home():
  21. return render_template('index.html', active_button="home")
  22. @app.route('/isp/')
  23. def project_list():
  24. return render_template('project_list.html', projects=ISP.query.filter_by(is_disabled=False))
  25. # this needs to be cached
  26. @app.route('/isp/map_data.json', methods=['GET'])
  27. def isp_map_data():
  28. isps=ISP.query.filter_by(is_disabled=False)
  29. data=[]
  30. for isp in isps:
  31. d=dict(isp.json)
  32. for k in d.keys():
  33. if k not in ('name', 'shortname', 'coordinates'):
  34. del d[k]
  35. d['id']=isp.id
  36. d['ffdn_member']=isp.is_ffdn_member
  37. d['popup']=render_template('map_popup.html', isp=isp)
  38. data.append(d)
  39. return Response(json.dumps(data), mimetype='application/json')
  40. @app.route('/isp/<projectid>/covered_areas.json', methods=['GET'])
  41. def isp_covered_areas(projectid):
  42. p=ISP.query.filter_by(id=projectid, is_disabled=False).first()
  43. if not p:
  44. abort(404)
  45. return Response(json.dumps(p.json['coveredAreas']), mimetype='application/json')
  46. @app.route('/isp/<projectid>/')
  47. def project(projectid):
  48. p=ISP.query.filter_by(id=projectid, is_disabled=False).first()
  49. if not p:
  50. abort(404)
  51. return render_template('project_detail.html', project_row=p, project=p.json)
  52. @app.route('/isp/<projectid>/edit', methods=['GET', 'POST'])
  53. def edit_project(projectid):
  54. isp=ISP.query.filter_by(id=projectid, is_disabled=False).first()
  55. if not isp:
  56. abort(404)
  57. form = forms.ProjectForm.edit_json(isp.json)
  58. if form.validate_on_submit():
  59. isp.name = form.name.data
  60. isp.shortname = form.shortname.data or None
  61. isp.json=form.to_json(isp.json)
  62. db.session.add(isp)
  63. db.session.commit()
  64. flash(_(u'Project modified'), 'info')
  65. return redirect(url_for('project', projectid=isp.id))
  66. return render_template('project_form.html', form=form, project=isp)
  67. @app.route('/add-a-project', methods=['GET'])
  68. def add_project():
  69. return render_template('add_project.html')
  70. @app.route('/isp/create/form', methods=['GET', 'POST'])
  71. def create_project_form():
  72. form = forms.ProjectForm()
  73. if form.validate_on_submit():
  74. isp=ISP()
  75. isp.name = form.name.data
  76. isp.shortname = form.shortname.data or None
  77. isp.tech_email = form.tech_email.data
  78. isp.json=form.to_json(isp.json)
  79. db.session.add(isp)
  80. db.session.commit()
  81. flash(_(u'Project created'), 'info')
  82. return redirect(url_for('project', projectid=isp.id))
  83. return render_template('add_project_form.html', form=form)
  84. @app.route('/isp/create/validator', methods=['GET'])
  85. def json_url_validator():
  86. if 'form_json' not in session or \
  87. session['form_json'].get('validated', False):
  88. abort(403)
  89. v=session['form_json'].get('validator')
  90. if v is not None:
  91. if v > time()-5:
  92. abort(429)
  93. else:
  94. session['form_json']['validator']=time()
  95. validator=WebValidator(session._get_current_object(), 'form_json')
  96. return Response(validator(session['form_json']['url']),
  97. mimetype="text/event-stream")
  98. @app.route('/isp/create', methods=['GET', 'POST'])
  99. def create_project_json():
  100. form = forms.ProjectJSONForm()
  101. if form.validate_on_submit():
  102. u=list(form.url.data)
  103. u[2]='/isp.json' # new path
  104. url=urlunsplit(u)
  105. session['form_json'] = {'url': url, 'tech_email': form.tech_email.data}
  106. return render_template('project_json_validator.html')
  107. return render_template('add_project_json_form.html', form=form)
  108. @app.route('/isp/create/confirm', methods=['POST'])
  109. def create_project_json_confirm():
  110. if 'form_json' in session and session['form_json'].get('validated', False):
  111. if not forms.is_url_unique(session['form_json']['url']):
  112. abort(409)
  113. jdict=session['form_json']['jdict']
  114. isp=ISP()
  115. isp.name=jdict['name']
  116. if 'shortname' in jdict:
  117. isp.shortname=jdict['shortname']
  118. isp.json_url=session['form_json']['url']
  119. isp.json=jdict
  120. isp.tech_email=session['form_json']['tech_email']
  121. del session['form_json']
  122. db.session.add(isp)
  123. db.session.commit()
  124. flash(_(u'Project created'), 'info')
  125. return redirect(url_for('project', projectid=isp.id))
  126. else:
  127. return redirect(url_for('create_project_json'))
  128. @app.route('/isp/reactivate-validator', methods=['GET'])
  129. def reactivate_validator():
  130. if 'form_reactivate' not in session or \
  131. session['form_reactivate'].get('validated', False):
  132. abort(403)
  133. p=ISP.query.get(session['form_reactivate']['isp_id'])
  134. if not p:
  135. abort(403)
  136. v=session['form_reactivate'].get('validator')
  137. if v is not None:
  138. if v > time()-5:
  139. abort(429)
  140. else:
  141. session['form_reactivate']['validator']=time()
  142. validator=PrettyValidator(session._get_current_object(), 'form_reactivate')
  143. return Response(validator(p.json_url, p.cache_info or {}),
  144. mimetype="text/event-stream")
  145. @app.route('/isp/<projectid>/reactivate', methods=['GET', 'POST'])
  146. def reactivate_isp(projectid):
  147. """
  148. Allow to reactivate an ISP after it has been disabled
  149. because of problems with the JSON file.
  150. """
  151. p=ISP.query.filter(ISP.id==projectid, ISP.is_disabled==False,
  152. ISP.update_error_strike>=3).first_or_404()
  153. if request.method == 'GET':
  154. key = request.args.get('key')
  155. try:
  156. s=itsdangerous.URLSafeSerializer(app.secret_key,
  157. salt='reactivate')
  158. d=s.loads(key)
  159. except Exception as e:
  160. abort(403)
  161. if (len(d) != 2 or d[0] != p.id or
  162. d[1] != str(p.last_update_attempt)):
  163. abort(403)
  164. session['form_reactivate'] = {'isp_id': p.id}
  165. return render_template('reactivate_validator.html', isp=p)
  166. else:
  167. if 'form_reactivate' not in session or \
  168. not session['form_reactivate'].get('validated', False):
  169. abort(409)
  170. p=ISP.query.get(session['form_reactivate']['isp_id'])
  171. p.json=session['form_reactivate']['jdict']
  172. p.cache_info=session['form_reactivate']['cache_info']
  173. p.last_update_attempt=datetime.now()
  174. p.last_update_success=p.last_update_attempt
  175. db.session.add(p)
  176. db.session.commit()
  177. flash(_(u'Automatic updates activated'), 'info')
  178. return redirect(url_for('project', projectid=p.id))
  179. @app.route('/search', methods=['GET', 'POST'])
  180. def search():
  181. terms=request.args.get('q')
  182. if not terms:
  183. return redirect(url_for('home'))
  184. res=ISPWhoosh.search(terms)
  185. return render_template('search_results.html', results=res, search_terms=terms)
  186. @app.route('/format', methods=['GET'])
  187. def format():
  188. parts = cache.get('format-spec')
  189. if parts is None:
  190. spec=open(ispformat.specs.versions[0.1]).read()
  191. overrides = {
  192. 'initial_header_level' : 3,
  193. }
  194. parts = docutils.core.publish_parts(spec,
  195. source_path=os.path.dirname(ispformat.specs.versions[0.1]),
  196. destination_path=None, writer_name='html',
  197. settings_overrides=overrides)
  198. cache.set('format-spec', parts, timeout=60*60*24)
  199. return render_template('format_spec.html', spec=Markup(parts['html_body']))
  200. #------
  201. # Filters
  202. @app.template_filter('step_to_label')
  203. def step_to_label(step):
  204. if step:
  205. return u"<a href='#' rel='tooltip' data-placement='right' title='" + STEPS[step] + "'><span class='badge badge-" + STEPS_LABELS[step] + "'>" + str(step) + "</span></a>"
  206. else:
  207. return u'-'
  208. @app.template_filter('stepname')
  209. def stepname(step):
  210. return STEPS[step]