views.py 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292
  1. # django imports
  2. from django.shortcuts import render
  3. from django.http import HttpResponse
  4. from django.conf import settings
  5. from django.contrib.auth.decorators import login_required
  6. from django.http import Http404
  7. # main imports
  8. import os
  9. import json
  10. import base64
  11. import random
  12. import numpy as np
  13. from datetime import datetime
  14. import pickle
  15. import time
  16. import zipfile
  17. from io import BytesIO
  18. # expe imports
  19. from .expes.classes.quest_plus import QuestPlus
  20. from .expes.classes.quest_plus import psychometric_fun
  21. from .expes import run as run_expe
  22. # image processing imports
  23. import io
  24. from PIL import Image
  25. # module imports
  26. from .utils import api
  27. from .utils import functions
  28. from .utils.processing import crop_images
  29. from . import config as cfg
  30. def get_base_data(expe_name=None):
  31. '''
  32. Used to store default data to send for each view
  33. '''
  34. data = {}
  35. data['BASE'] = settings.WEBEXPE_PREFIX_URL
  36. # if expe name is used
  37. if expe_name is not None:
  38. data['js'] = cfg.expes_configuration[expe_name]['js']
  39. return data
  40. def expe_list(request):
  41. # get all scenes from dataset
  42. scenes = api.get_scenes()
  43. # get list of experiences
  44. expes = cfg.expe_name_list
  45. # by default user restart expe
  46. request.session['expe_started'] = False
  47. # get base data
  48. data = get_base_data()
  49. # expe data
  50. data['scenes'] = scenes
  51. data['expes'] = expes
  52. return render(request, 'expe/expe_list.html', data)
  53. def indications(request):
  54. # get param
  55. expe_name = request.GET.get('expe')
  56. # get base data
  57. data = get_base_data()
  58. # expe parameters
  59. data['expe_name'] = expe_name
  60. data['question'] = cfg.expes_configuration[expe_name]['text']['question']
  61. data['indication'] = cfg.expes_configuration[expe_name]['text']['indication']
  62. return render(request, 'expe/expe_indications.html', data)
  63. # Create your views here.
  64. def expe(request):
  65. # get param
  66. expe_name = request.GET.get('expe')
  67. scene_name = request.GET.get('scene')
  68. # unique user ID during session (user can launch multiple exeperiences)
  69. if 'id' not in request.session:
  70. request.session['id'] = functions.uniqueID()
  71. # first time expe is launched add expe information
  72. if 'expe' not in request.session or expe_name != request.session.get('expe'):
  73. refresh_data(request, expe_name, scene_name)
  74. # refresh if scene_name changed
  75. if 'scene' not in request.session or scene_name != request.session.get('scene'):
  76. refresh_data(request, expe_name, scene_name)
  77. # create output folder for expe_result
  78. current_day = datetime.strftime(datetime.utcnow(), "%Y-%m-%d")
  79. results_folder = os.path.join(settings.MEDIA_ROOT, cfg.output_expe_folder_name_day.format(expe_name, current_day))
  80. if not os.path.exists(results_folder):
  81. os.makedirs(results_folder)
  82. result_filename = scene_name + '_' + request.session.get('id') + '_' + request.session.get('timestamp') +".csv"
  83. results_filepath = os.path.join(results_folder, result_filename)
  84. if not os.path.exists(results_filepath):
  85. output_file = open(results_filepath, 'w')
  86. functions.write_header_expe(output_file, expe_name)
  87. else:
  88. output_file = open(results_filepath, 'a')
  89. # TODO : add crontab task to erase generated img and model data
  90. # create `quest` object if not exists
  91. models_folder = os.path.join(settings.MEDIA_ROOT, cfg.model_expe_folder.format(expe_name, current_day))
  92. if not os.path.exists(models_folder):
  93. os.makedirs(models_folder)
  94. model_filename = result_filename.replace('.csv', '.obj')
  95. model_filepath = os.path.join(models_folder, model_filename)
  96. # run expe method using `expe_name`
  97. function_name = 'run_' + expe_name
  98. try:
  99. run_expe_method = getattr(run_expe, function_name)
  100. except AttributeError:
  101. raise NotImplementedError("Run expe method `{}` not implement `{}`".format(run_expe.__name__, function_name))
  102. expe_data = run_expe_method(request, model_filepath, output_file)
  103. if request.session.get('expe_finished'):
  104. # reinit session as default value
  105. # here generic expe params
  106. del request.session['expe']
  107. del request.session['scene']
  108. del request.session['qualities']
  109. del request.session['timestamp']
  110. del request.session['answer_time']
  111. # specific current expe session params (see `config.py`)
  112. for key in cfg.expes_configuration[expe_name]['session_params']:
  113. del request.session[key]
  114. # get base data
  115. data = get_base_data(expe_name)
  116. # expe parameters
  117. data['expe_name'] = expe_name
  118. data['expe_data'] = expe_data
  119. data['end_text'] = cfg.expes_configuration[expe_name]['text']['end_text']
  120. return render(request, cfg.expes_configuration[expe_name]['template'], data)
  121. @login_required(login_url="login/")
  122. def list_results(request, expe=None):
  123. """
  124. Return all results obtained from experiences
  125. """
  126. if expe is None:
  127. folders = cfg.expe_name_list
  128. return render(request, 'expe/expe_results.html', {'expe': expe, 'folders': folders})
  129. else:
  130. if expe in cfg.expe_name_list:
  131. folder_path = os.path.join(settings.MEDIA_ROOT, cfg.output_expe_folder, expe)
  132. # init folder dictionnary
  133. folders = {}
  134. if os.path.exists(folder_path):
  135. days = sorted(os.listdir(folder_path), reverse=True)
  136. for day in days:
  137. day_path = os.path.join(folder_path, day)
  138. filenames = os.listdir(day_path)
  139. folders[day] = filenames
  140. else:
  141. raise Http404("Expe does not exists")
  142. # get base data
  143. data = get_base_data()
  144. # expe parameters
  145. data['expe'] = expe
  146. data['folders'] = folders
  147. data['infos'] = cfg.expes_configuration[expe]['text']
  148. return render(request, 'expe/expe_results.html', data)
  149. @login_required(login_url="login/")
  150. def download_result(request):
  151. path = request.POST.get('path')
  152. folder_path = os.path.join(settings.MEDIA_ROOT, cfg.output_expe_folder, path)
  153. # Folder is required
  154. if os.path.exists(folder_path):
  155. # Open BytesIO to grab in-memory ZIP contents
  156. s = BytesIO()
  157. # check if file or folder
  158. if os.path.isdir(folder_path):
  159. # get files from a specific day
  160. filenames = os.listdir(folder_path)
  161. # Folder name in ZIP archive which contains the above files
  162. # E.g [thearchive.zip]/somefiles/file2.txt
  163. # FIXME: Set this to something better
  164. zip_subdir = folder_path.split('/')[-1]
  165. zip_filename = "%s.zip" % zip_subdir
  166. # The zip compressor
  167. zf = zipfile.ZipFile(s, "w")
  168. for fpath in filenames:
  169. fpath = os.path.join(folder_path, fpath)
  170. # Calculate path for file in zip
  171. fdir, fname = os.path.split(fpath)
  172. zip_path = os.path.join(zip_subdir, fname)
  173. # Add file, at correct path
  174. zf.write(fpath, zip_path)
  175. # Must close zip for all contents to be written
  176. zf.close()
  177. output_filename = zip_filename
  178. content = s.getvalue()
  179. else:
  180. with open(folder_path, 'rb') as f:
  181. content = f.readlines()
  182. # filename only
  183. fdir, fname = os.path.split(path)
  184. output_filename = fname
  185. # Grab ZIP file from in-memory, make response with correct MIME-type
  186. resp = HttpResponse(content, content_type="application/gzip")
  187. # ..and correct content-disposition
  188. resp['Content-Disposition'] = 'attachment; filename=%s' % output_filename
  189. return resp
  190. else:
  191. return Http404("Path does not exist")
  192. def refresh_data(request, expe_name, scene_name):
  193. '''
  194. Utils method to refresh data from session
  195. '''
  196. request.session['expe'] = expe_name
  197. request.session['scene'] = scene_name
  198. request.session['expe_started'] = False
  199. request.session['expe_finished'] = False
  200. request.session['qualities'] = api.get_scene_qualities(scene_name)
  201. # update unique timestamp each time new experience is launched
  202. request.session['timestamp'] = datetime.strftime(datetime.utcnow(), "%Y-%m-%d_%Hh%Mm%Ss")
  203. # TODO : add in cache ref_image
  204. # get reference image
  205. #ref_image = api.get_image(scene_name, 'max')
  206. # save ref image as list (can't save python object)
  207. #request.session['ref_img'] = np.array(ref_image).tolist()