PageRenderTime 50ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 0ms

/odk_viewer/views.py

https://github.com/zoulema/formhub
Python | 736 lines | 708 code | 25 blank | 3 comment | 16 complexity | ef127173d6ddd5b5d8668bafdb4b1d87 MD5 | raw file
Possible License(s): BSD-3-Clause
  1. import json
  2. import os
  3. from datetime import datetime
  4. from tempfile import NamedTemporaryFile
  5. from time import strftime, strptime
  6. from django.views.decorators.http import require_POST
  7. from django.contrib.auth.models import User
  8. from django.core.urlresolvers import reverse
  9. from django.http import HttpResponseForbidden,\
  10. HttpResponseRedirect, HttpResponseNotFound, HttpResponseBadRequest,\
  11. HttpResponse
  12. from django.shortcuts import render_to_response, get_object_or_404, redirect
  13. from django.template import RequestContext
  14. from django.utils.translation import ugettext as _
  15. from django.core.files.storage import FileSystemStorage
  16. from django.core.files.storage import get_storage_class
  17. from main.models import UserProfile, MetaData, TokenStorageModel
  18. from odk_logger.models import XForm, Attachment
  19. from odk_logger.views import download_jsonform
  20. from odk_viewer.models import DataDictionary, ParsedInstance
  21. from odk_viewer.pandas_mongo_bridge import NoRecordsFoundError
  22. from utils.image_tools import image_url
  23. from xls_writer import XlsWriter
  24. from utils.logger_tools import response_with_mimetype_and_name,\
  25. disposition_ext_and_date
  26. from utils.viewer_tools import image_urls
  27. from odk_viewer.tasks import create_async_export
  28. from utils.user_auth import has_permission, get_xform_and_perms,\
  29. helper_auth_helper
  30. from utils.google import google_export_xls, redirect_uri
  31. # TODO: using from main.views import api breaks the application, why?
  32. from odk_viewer.models import Export
  33. from utils.export_tools import generate_export, should_create_new_export
  34. from utils.export_tools import kml_export_data
  35. from utils.export_tools import newset_export_for
  36. from utils.viewer_tools import export_def_from_filename
  37. from utils.viewer_tools import create_attachments_zipfile
  38. from utils.log import audit_log, Actions
  39. from common_tags import SUBMISSION_TIME
  40. def encode(time_str):
  41. time = strptime(time_str, "%Y_%m_%d_%H_%M_%S")
  42. return strftime("%Y-%m-%d %H:%M:%S", time)
  43. def dd_for_params(id_string, owner, request):
  44. start = end = None
  45. dd = DataDictionary.objects.get(id_string=id_string,
  46. user=owner)
  47. if request.GET.get('start'):
  48. try:
  49. start = encode(request.GET['start'])
  50. except ValueError:
  51. # bad format
  52. return [False,
  53. HttpResponseBadRequest(
  54. _(u'Start time format must be YY_MM_DD_hh_mm_ss'))
  55. ]
  56. dd.surveys_for_export = \
  57. lambda d: d.surveys.filter(date_created__gte=start)
  58. if request.GET.get('end'):
  59. try:
  60. end = encode(request.GET['end'])
  61. except ValueError:
  62. # bad format
  63. return [False,
  64. HttpResponseBadRequest(
  65. _(u'End time format must be YY_MM_DD_hh_mm_ss'))
  66. ]
  67. dd.surveys_for_export = \
  68. lambda d: d.surveys.filter(date_created__lte=end)
  69. if start and end:
  70. dd.surveys_for_export = \
  71. lambda d: d.surveys.filter(date_created__lte=end,
  72. date_created__gte=start)
  73. return [True, dd]
  74. def parse_label_for_display(pi, xpath):
  75. label = pi.data_dictionary.get_label(xpath)
  76. if not type(label) == dict:
  77. label = {'Unknown': label}
  78. return label.items()
  79. def average(values):
  80. if len(values):
  81. return sum(values, 0.0) / len(values)
  82. return None
  83. def map_view(request, username, id_string, template='map.html'):
  84. owner = get_object_or_404(User, username=username)
  85. xform = get_object_or_404(XForm, id_string=id_string, user=owner)
  86. if not has_permission(xform, owner, request):
  87. return HttpResponseForbidden(_(u'Not shared.'))
  88. context = RequestContext(request)
  89. context.content_user = owner
  90. context.xform = xform
  91. context.profile, created = UserProfile.objects.get_or_create(user=owner)
  92. context.form_view = True
  93. context.jsonform_url = reverse(download_jsonform,
  94. kwargs={"username": username,
  95. "id_string": id_string})
  96. context.enketo_edit_url = reverse('edit_data',
  97. kwargs={"username": username,
  98. "id_string": id_string,
  99. "data_id": 0})
  100. context.enketo_add_url = reverse('enter_data',
  101. kwargs={"username": username,
  102. "id_string": id_string})
  103. context.enketo_add_with_url = reverse('add_submission_with',
  104. kwargs={"username": username,
  105. "id_string": id_string})
  106. context.mongo_api_url = reverse('mongo_view_api',
  107. kwargs={"username": username,
  108. "id_string": id_string})
  109. context.delete_data_url = reverse('delete_data',
  110. kwargs={"username": username,
  111. "id_string": id_string})
  112. context.mapbox_layer = MetaData.mapbox_layer_upload(xform)
  113. audit = {
  114. "xform": xform.id_string
  115. }
  116. audit_log(Actions.FORM_MAP_VIEWED, request.user, owner,
  117. _("Requested map on '%(id_string)s'.")
  118. % {'id_string': xform.id_string}, audit, request)
  119. return render_to_response(template, context_instance=context)
  120. def map_embed_view(request, username, id_string):
  121. return map_view(request, username, id_string, template='map_embed.html')
  122. def add_submission_with(request, username, id_string):
  123. import uuid
  124. import requests
  125. from django.conf import settings
  126. from django.template import loader, Context
  127. from dpath import util as dpath_util
  128. from dict2xml import dict2xml
  129. def geopoint_xpaths(username, id_string):
  130. d = DataDictionary.objects.get(user__username=username, id_string=id_string)
  131. return [e.get_abbreviated_xpath()
  132. for e in d.get_survey_elements()
  133. if e.bind.get(u'type') == u'geopoint']
  134. value = request.GET.get('coordinates')
  135. xpaths = geopoint_xpaths(username, id_string)
  136. xml_dict = {}
  137. for path in xpaths:
  138. dpath_util.new(xml_dict, path, value)
  139. context = {'username': username,
  140. 'id_string': id_string,
  141. 'xml_content': dict2xml(xml_dict)}
  142. instance_xml = loader.get_template("instance_add.xml").render(Context(context))
  143. url = settings.ENKETO_API_INSTANCE_IFRAME_URL
  144. return_url = reverse('thank_you_submission', kwargs={"username": username,
  145. "id_string": id_string})
  146. if settings.DEBUG:
  147. openrosa_url = "https://dev.formhub.org/{}".format(username)
  148. else:
  149. openrosa_url = request.build_absolute_uri("/{}".format(username))
  150. payload = {'return_url': return_url,
  151. 'form_id': id_string,
  152. 'server_url': openrosa_url,
  153. 'instance': instance_xml,
  154. 'instance_id': uuid.uuid4().hex}
  155. r = requests.post(url, data=payload,
  156. auth=(settings.ENKETO_API_TOKEN, ''), verify=False)
  157. return HttpResponse(r.text, mimetype='application/json')
  158. def thank_you_submission(request, username, id_string):
  159. return HttpResponse("Thank You")
  160. # TODO: do a good job of displaying hierarchical data
  161. def survey_responses(request, instance_id):
  162. pi = get_object_or_404(ParsedInstance, instance=instance_id)
  163. xform, is_owner, can_edit, can_view = \
  164. get_xform_and_perms(pi.instance.user.username,
  165. pi.instance.xform.id_string, request)
  166. # no access
  167. if not (xform.shared_data or can_view or
  168. request.session.get('public_link') == xform.uuid):
  169. return HttpResponseRedirect('/')
  170. data = pi.to_dict()
  171. # get rid of keys with leading underscores
  172. data_for_display = {}
  173. for k, v in data.items():
  174. if not k.startswith(u"_"):
  175. data_for_display[k] = v
  176. xpaths = data_for_display.keys()
  177. xpaths.sort(cmp=pi.data_dictionary.get_xpath_cmp())
  178. label_value_pairs = [
  179. (parse_label_for_display(pi, xpath),
  180. data_for_display[xpath]) for xpath in xpaths
  181. ]
  182. languages = label_value_pairs[-1][0]
  183. audit = {
  184. "xform": xform.id_string,
  185. "instance_id": instance_id
  186. }
  187. audit_log(
  188. Actions.FORM_DATA_VIEWED, request.user, xform.user,
  189. _("Requested survey with id '%(instance_id)s' on '%(id_string)s'.") %
  190. {
  191. 'id_string': xform.id_string,
  192. 'instance_id': instance_id
  193. }, audit, request)
  194. return render_to_response('survey.html', {
  195. 'label_value_pairs': label_value_pairs,
  196. 'image_urls': image_urls(pi.instance),
  197. 'languages': languages,
  198. 'default_language': languages[0][0]
  199. })
  200. def data_export(request, username, id_string, export_type):
  201. owner = get_object_or_404(User, username=username)
  202. xform = get_object_or_404(XForm, id_string=id_string, user=owner)
  203. helper_auth_helper(request)
  204. if not has_permission(xform, owner, request):
  205. return HttpResponseForbidden(_(u'Not shared.'))
  206. query = request.GET.get("query")
  207. extension = export_type
  208. # check if we should force xlsx
  209. force_xlsx = request.GET.get('xls') != 'true'
  210. if export_type == Export.XLS_EXPORT and force_xlsx:
  211. extension = 'xlsx'
  212. elif export_type == Export.CSV_ZIP_EXPORT:
  213. extension = 'zip'
  214. audit = {
  215. "xform": xform.id_string,
  216. "export_type": export_type
  217. }
  218. # check if we need to re-generate,
  219. # we always re-generate if a filter is specified
  220. if should_create_new_export(xform, export_type) or query or\
  221. 'start' in request.GET or 'end' in request.GET:
  222. format_date_for_mongo = lambda x, datetime: datetime.strptime(
  223. x, '%y_%m_%d_%H_%M_%S').strftime('%Y-%m-%dT%H:%M:%S')
  224. # check for start and end params
  225. if 'start' in request.GET or 'end' in request.GET:
  226. if not query:
  227. query = '{}'
  228. query = json.loads(query)
  229. query[SUBMISSION_TIME] = {}
  230. try:
  231. if request.GET.get('start'):
  232. query[SUBMISSION_TIME]['$gte'] = format_date_for_mongo(
  233. request.GET['start'], datetime)
  234. if request.GET.get('end'):
  235. query[SUBMISSION_TIME]['$lte'] = format_date_for_mongo(
  236. request.GET['end'], datetime)
  237. except ValueError:
  238. return HttpResponseBadRequest(
  239. _("Dates must be in the format YY_MM_DD_hh_mm_ss"))
  240. else:
  241. query = json.dumps(query)
  242. try:
  243. export = generate_export(
  244. export_type, extension, username, id_string, None, query)
  245. audit_log(
  246. Actions.EXPORT_CREATED, request.user, owner,
  247. _("Created %(export_type)s export on '%(id_string)s'.") %
  248. {
  249. 'id_string': xform.id_string,
  250. 'export_type': export_type.upper()
  251. }, audit, request)
  252. except NoRecordsFoundError:
  253. return HttpResponseNotFound(_("No records found to export"))
  254. else:
  255. export = newset_export_for(xform, export_type)
  256. # log download as well
  257. audit_log(
  258. Actions.EXPORT_DOWNLOADED, request.user, owner,
  259. _("Downloaded %(export_type)s export on '%(id_string)s'.") %
  260. {
  261. 'id_string': xform.id_string,
  262. 'export_type': export_type.upper()
  263. }, audit, request)
  264. if not export.filename:
  265. # tends to happen when using newset_export_for.
  266. return HttpResponseNotFound("File does not exist!")
  267. # get extension from file_path, exporter could modify to
  268. # xlsx if it exceeds limits
  269. path, ext = os.path.splitext(export.filename)
  270. ext = ext[1:]
  271. if request.GET.get('raw'):
  272. id_string = None
  273. response = response_with_mimetype_and_name(
  274. Export.EXPORT_MIMES[ext], id_string, extension=ext,
  275. file_path=export.filepath)
  276. return response
  277. @require_POST
  278. def create_export(request, username, id_string, export_type):
  279. owner = get_object_or_404(User, username=username)
  280. xform = get_object_or_404(XForm, id_string=id_string, user=owner)
  281. if not has_permission(xform, owner, request):
  282. return HttpResponseForbidden(_(u'Not shared.'))
  283. query = request.POST.get("query")
  284. force_xlsx = request.POST.get('xls') != 'true'
  285. # export options
  286. group_delimiter = request.POST.get("options[group_delimiter]", '/')
  287. if group_delimiter not in ['.', '/']:
  288. return HttpResponseBadRequest(
  289. _("%s is not a valid delimiter" % group_delimiter))
  290. # default is True, so when dont_.. is yes
  291. # split_select_multiples becomes False
  292. split_select_multiples = request.POST.get(
  293. "options[dont_split_select_multiples]", "no") == "no"
  294. options = {
  295. 'group_delimiter': group_delimiter,
  296. 'split_select_multiples': split_select_multiples
  297. }
  298. try:
  299. create_async_export(xform, export_type, query, force_xlsx, options)
  300. except Export.ExportTypeError:
  301. return HttpResponseBadRequest(
  302. _("%s is not a valid export type" % export_type))
  303. else:
  304. audit = {
  305. "xform": xform.id_string,
  306. "export_type": export_type
  307. }
  308. audit_log(
  309. Actions.EXPORT_CREATED, request.user, owner,
  310. _("Created %(export_type)s export on '%(id_string)s'.") %
  311. {
  312. 'export_type': export_type.upper(),
  313. 'id_string': xform.id_string,
  314. }, audit, request)
  315. return HttpResponseRedirect(reverse(
  316. export_list,
  317. kwargs={
  318. "username": username,
  319. "id_string": id_string,
  320. "export_type": export_type
  321. })
  322. )
  323. def _get_google_token(request, redirect_to_url):
  324. token = None
  325. if request.user.is_authenticated():
  326. try:
  327. ts = TokenStorageModel.objects.get(id=request.user)
  328. except TokenStorageModel.DoesNotExist:
  329. pass
  330. else:
  331. token = ts.token
  332. elif request.session.get('access_token'):
  333. token = request.session.get('access_token')
  334. if token is None:
  335. request.session["google_redirect_url"] = redirect_to_url
  336. return HttpResponseRedirect(redirect_uri)
  337. return token
  338. def export_list(request, username, id_string, export_type):
  339. if export_type == Export.GDOC_EXPORT:
  340. redirect_url = reverse(
  341. export_list,
  342. kwargs={
  343. 'username': username, 'id_string': id_string,
  344. 'export_type': export_type})
  345. token = _get_google_token(request, redirect_url)
  346. if isinstance(token, HttpResponse):
  347. return token
  348. owner = get_object_or_404(User, username=username)
  349. xform = get_object_or_404(XForm, id_string=id_string, user=owner)
  350. if not has_permission(xform, owner, request):
  351. return HttpResponseForbidden(_(u'Not shared.'))
  352. if should_create_new_export(xform, export_type):
  353. try:
  354. create_async_export(
  355. xform, export_type, query=None, force_xlsx=True)
  356. except Export.ExportTypeError:
  357. return HttpResponseBadRequest(
  358. _("%s is not a valid export type" % export_type))
  359. context = RequestContext(request)
  360. context.username = owner.username
  361. context.xform = xform
  362. # TODO: better output e.g. Excel instead of XLS
  363. context.export_type = export_type
  364. context.export_type_name = Export.EXPORT_TYPE_DICT[export_type]
  365. exports = Export.objects.filter(xform=xform, export_type=export_type)\
  366. .order_by('-created_on')
  367. context.exports = exports
  368. return render_to_response('export_list.html', context_instance=context)
  369. def export_progress(request, username, id_string, export_type):
  370. owner = get_object_or_404(User, username=username)
  371. xform = get_object_or_404(XForm, id_string=id_string, user=owner)
  372. if not has_permission(xform, owner, request):
  373. return HttpResponseForbidden(_(u'Not shared.'))
  374. # find the export entry in the db
  375. export_ids = request.GET.getlist('export_ids')
  376. exports = Export.objects.filter(xform=xform, id__in=export_ids)
  377. statuses = []
  378. for export in exports:
  379. status = {
  380. 'complete': False,
  381. 'url': None,
  382. 'filename': None,
  383. 'export_id': export.id
  384. }
  385. if export.status == Export.SUCCESSFUL:
  386. status['url'] = reverse(export_download, kwargs={
  387. 'username': owner.username,
  388. 'id_string': xform.id_string,
  389. 'export_type': export.export_type,
  390. 'filename': export.filename
  391. })
  392. status['filename'] = export.filename
  393. if export.export_type == Export.GDOC_EXPORT and \
  394. export.export_url is None:
  395. redirect_url = reverse(
  396. export_progress,
  397. kwargs={
  398. 'username': username, 'id_string': id_string,
  399. 'export_type': export_type})
  400. token = _get_google_token(request, redirect_url)
  401. if isinstance(token, HttpResponse):
  402. return token
  403. status['url'] = None
  404. try:
  405. url = google_export_xls(
  406. export.full_filepath, xform.title, token, blob=True)
  407. except Exception, e:
  408. status['error'] = True
  409. status['message'] = e.message
  410. else:
  411. export.export_url = url
  412. export.save()
  413. status['url'] = url
  414. # mark as complete if it either failed or succeeded but NOT pending
  415. if export.status == Export.SUCCESSFUL \
  416. or export.status == Export.FAILED:
  417. status['complete'] = True
  418. statuses.append(status)
  419. return HttpResponse(
  420. json.dumps(statuses), mimetype='application/json')
  421. def export_download(request, username, id_string, export_type, filename):
  422. owner = get_object_or_404(User, username=username)
  423. xform = get_object_or_404(XForm, id_string=id_string, user=owner)
  424. helper_auth_helper(request)
  425. if not has_permission(xform, owner, request):
  426. return HttpResponseForbidden(_(u'Not shared.'))
  427. # find the export entry in the db
  428. export = get_object_or_404(Export, xform=xform, filename=filename)
  429. if export_type == Export.GDOC_EXPORT and export.export_url is not None:
  430. return HttpResponseRedirect(export.export_url)
  431. ext, mime_type = export_def_from_filename(export.filename)
  432. audit = {
  433. "xform": xform.id_string,
  434. "export_type": export.export_type
  435. }
  436. audit_log(
  437. Actions.EXPORT_DOWNLOADED, request.user, owner,
  438. _("Downloaded %(export_type)s export '%(filename)s' "
  439. "on '%(id_string)s'.") %
  440. {
  441. 'export_type': export.export_type.upper(),
  442. 'filename': export.filename,
  443. 'id_string': xform.id_string,
  444. }, audit, request)
  445. if request.GET.get('raw'):
  446. id_string = None
  447. default_storage = get_storage_class()()
  448. if not isinstance(default_storage, FileSystemStorage):
  449. return HttpResponseRedirect(default_storage.url(export.filepath))
  450. basename = os.path.splitext(export.filename)[0]
  451. response = response_with_mimetype_and_name(
  452. mime_type, name=basename, extension=ext,
  453. file_path=export.filepath, show_date=False)
  454. return response
  455. @require_POST
  456. def delete_export(request, username, id_string, export_type):
  457. owner = get_object_or_404(User, username=username)
  458. xform = get_object_or_404(XForm, id_string=id_string, user=owner)
  459. if not has_permission(xform, owner, request):
  460. return HttpResponseForbidden(_(u'Not shared.'))
  461. export_id = request.POST.get('export_id')
  462. # find the export entry in the db
  463. export = get_object_or_404(Export, id=export_id)
  464. export.delete()
  465. audit = {
  466. "xform": xform.id_string,
  467. "export_type": export.export_type
  468. }
  469. audit_log(
  470. Actions.EXPORT_DOWNLOADED, request.user, owner,
  471. _("Deleted %(export_type)s export '%(filename)s'"
  472. " on '%(id_string)s'.") %
  473. {
  474. 'export_type': export.export_type.upper(),
  475. 'filename': export.filename,
  476. 'id_string': xform.id_string,
  477. }, audit, request)
  478. return HttpResponseRedirect(reverse(
  479. export_list,
  480. kwargs={
  481. "username": username,
  482. "id_string": id_string,
  483. "export_type": export_type
  484. }))
  485. def zip_export(request, username, id_string):
  486. owner = get_object_or_404(User, username=username)
  487. xform = get_object_or_404(XForm, id_string=id_string, user=owner)
  488. helper_auth_helper(request)
  489. if not has_permission(xform, owner, request):
  490. return HttpResponseForbidden(_(u'Not shared.'))
  491. if request.GET.get('raw'):
  492. id_string = None
  493. attachments = Attachment.objects.filter(instance__xform=xform)
  494. zip_file = create_attachments_zipfile(attachments)
  495. audit = {
  496. "xform": xform.id_string,
  497. "export_type": Export.ZIP_EXPORT
  498. }
  499. audit_log(
  500. Actions.EXPORT_CREATED, request.user, owner,
  501. _("Created ZIP export on '%(id_string)s'.") %
  502. {
  503. 'id_string': xform.id_string,
  504. }, audit, request)
  505. # log download as well
  506. audit_log(
  507. Actions.EXPORT_DOWNLOADED, request.user, owner,
  508. _("Downloaded ZIP export on '%(id_string)s'.") %
  509. {
  510. 'id_string': xform.id_string,
  511. }, audit, request)
  512. if request.GET.get('raw'):
  513. id_string = None
  514. response = response_with_mimetype_and_name('zip', id_string,
  515. file_path=zip_file,
  516. use_local_filesystem=True)
  517. return response
  518. def kml_export(request, username, id_string):
  519. # read the locations from the database
  520. context = RequestContext(request)
  521. context.message = "HELLO!!"
  522. owner = get_object_or_404(User, username=username)
  523. xform = get_object_or_404(XForm, id_string=id_string, user=owner)
  524. helper_auth_helper(request)
  525. if not has_permission(xform, owner, request):
  526. return HttpResponseForbidden(_(u'Not shared.'))
  527. context.data = kml_export_data(id_string, user=owner)
  528. response = \
  529. render_to_response("survey.kml", context_instance=context,
  530. mimetype="application/vnd.google-earth.kml+xml")
  531. response['Content-Disposition'] = \
  532. disposition_ext_and_date(id_string, 'kml')
  533. audit = {
  534. "xform": xform.id_string,
  535. "export_type": Export.KML_EXPORT
  536. }
  537. audit_log(
  538. Actions.EXPORT_CREATED, request.user, owner,
  539. _("Created KML export on '%(id_string)s'.") %
  540. {
  541. 'id_string': xform.id_string,
  542. }, audit, request)
  543. # log download as well
  544. audit_log(
  545. Actions.EXPORT_DOWNLOADED, request.user, owner,
  546. _("Downloaded KML export on '%(id_string)s'.") %
  547. {
  548. 'id_string': xform.id_string,
  549. }, audit, request)
  550. return response
  551. def google_xls_export(request, username, id_string):
  552. token = None
  553. if request.user.is_authenticated():
  554. try:
  555. ts = TokenStorageModel.objects.get(id=request.user)
  556. except TokenStorageModel.DoesNotExist:
  557. pass
  558. else:
  559. token = ts.token
  560. elif request.session.get('access_token'):
  561. token = request.session.get('access_token')
  562. if token is None:
  563. request.session["google_redirect_url"] = reverse(
  564. google_xls_export,
  565. kwargs={'username': username, 'id_string': id_string})
  566. return HttpResponseRedirect(redirect_uri)
  567. owner = get_object_or_404(User, username=username)
  568. xform = get_object_or_404(XForm, id_string=id_string, user=owner)
  569. if not has_permission(xform, owner, request):
  570. return HttpResponseForbidden(_(u'Not shared.'))
  571. valid, dd = dd_for_params(id_string, owner, request)
  572. if not valid:
  573. return dd
  574. ddw = XlsWriter()
  575. tmp = NamedTemporaryFile(delete=False)
  576. ddw.set_file(tmp)
  577. ddw.set_data_dictionary(dd)
  578. temp_file = ddw.save_workbook_to_file()
  579. temp_file.close()
  580. url = google_export_xls(tmp.name, xform.title, token, blob=True)
  581. os.unlink(tmp.name)
  582. audit = {
  583. "xform": xform.id_string,
  584. "export_type": "google"
  585. }
  586. audit_log(
  587. Actions.EXPORT_CREATED, request.user, owner,
  588. _("Created Google Docs export on '%(id_string)s'.") %
  589. {
  590. 'id_string': xform.id_string,
  591. }, audit, request)
  592. return HttpResponseRedirect(url)
  593. def data_view(request, username, id_string):
  594. owner = get_object_or_404(User, username=username)
  595. xform = get_object_or_404(XForm, id_string=id_string, user=owner)
  596. if not has_permission(xform, owner, request):
  597. return HttpResponseForbidden(_(u'Not shared.'))
  598. context = RequestContext(request)
  599. context.owner = owner
  600. context.xform = xform
  601. audit = {
  602. "xform": xform.id_string,
  603. }
  604. audit_log(
  605. Actions.FORM_DATA_VIEWED, request.user, owner,
  606. _("Requested data view for '%(id_string)s'.") %
  607. {
  608. 'id_string': xform.id_string,
  609. }, audit, request)
  610. return render_to_response("data_view.html", context_instance=context)
  611. def attachment_url(request, size='medium'):
  612. media_file = request.GET.get('media_file')
  613. # TODO: how to make sure we have the right media file,
  614. # this assumes duplicates are the same file
  615. result = Attachment.objects.filter(media_file=media_file)[0:1]
  616. if result.count() == 0:
  617. return HttpResponseNotFound(_(u'Attachment not found'))
  618. attachment = result[0]
  619. if not attachment.mimetype.startswith('image'):
  620. return redirect(attachment.media_file.url)
  621. try:
  622. media_url = image_url(attachment, size)
  623. except:
  624. # TODO: log this somewhere
  625. # image not found, 404, S3ResponseError timeouts
  626. pass
  627. else:
  628. if media_url:
  629. return redirect(media_url)
  630. return HttpResponseNotFound(_(u'Error: Attachment not found'))
  631. def instance(request, username, id_string):
  632. xform, is_owner, can_edit, can_view = get_xform_and_perms(
  633. username, id_string, request)
  634. # no access
  635. if not (xform.shared_data or can_view or
  636. request.session.get('public_link') == xform.uuid):
  637. return HttpResponseForbidden(_(u'Not shared.'))
  638. context = RequestContext(request)
  639. audit = {
  640. "xform": xform.id_string,
  641. }
  642. audit_log(
  643. Actions.FORM_DATA_VIEWED, request.user, xform.user,
  644. _("Requested instance view for '%(id_string)s'.") %
  645. {
  646. 'id_string': xform.id_string,
  647. }, audit, request)
  648. return render_to_response('instance.html', {
  649. 'username': username,
  650. 'id_string': id_string,
  651. 'xform': xform,
  652. 'can_edit': can_edit
  653. }, context_instance=context)