views.py 8.7 KB

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