views.py 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450
  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, ImageDraw
  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['javascript'] = cfg.expes_configuration[expe_name]['javascript']
  39. return data
  40. def expe_list(request):
  41. # get all scenes from dataset
  42. scenes = api.get_scenes()
  43. # get list of experimentss
  44. expes = cfg.expe_name_list
  45. data = get_base_data()
  46. # by default user restart expe
  47. request.session['expe_started'] = False
  48. # get base data
  49. data = get_base_data()
  50. # expe data
  51. data['scenes'] = scenes
  52. data['expes'] = expes
  53. #data['scenes'] = {}
  54. #for expe in expes:
  55. # if 'scene' in cfg.expes_configuration[expe]:
  56. # data['scenes'][expe] = cfg.expes_configuration[expe]['scenes']
  57. # else:
  58. # data['scenes'][expe] = scenes
  59. return render(request, 'expe/expe_list.html', data)
  60. def indications(request):
  61. #random.seed(10)
  62. # get param
  63. expe_name = request.GET.get('expe')
  64. scene_name = None
  65. if 'scene' in request.GET:
  66. scene_name = request.GET.get('scene')
  67. if scene_name is None or scene_name == 'null':
  68. scene_name = random.choice(cfg.expes_configuration[expe_name]['scenes'])
  69. example_number = request.GET.get('example')
  70. print(example_number)
  71. # get base data
  72. data = get_base_data()
  73. # expe parameters
  74. data['expe_name'] = expe_name
  75. data['scene_name'] = scene_name
  76. data['question'] = cfg.expes_configuration[expe_name]['text']['question']
  77. data['indication'] = cfg.expes_configuration[expe_name]['text']['indication']
  78. number_of_examples = len(cfg.expes_configuration[expe_name]['text']['examples']['images'])
  79. start_experiment = False
  80. if (int(example_number) >= number_of_examples):
  81. start_experiment = True
  82. else:
  83. # run expe method using `expe_name`
  84. function_name = 'example_' + expe_name
  85. try:
  86. run_example_method = getattr(run_expe, function_name)
  87. except AttributeError:
  88. raise NotImplementedError("Run expe method `{}` not implement `{}`".format(run_expe.__name__, function_name))
  89. data_example = run_example_method(request, expe_name, scene_name)
  90. data.update(data_example)
  91. # get expected image qualities indices (load noisy and ref image)
  92. # params_image = cfg.expes_configuration[expe_name]['text']['examples']['images'][int(example_number)]
  93. # qualities = api.get_scene_qualities(scene_name)
  94. #
  95. # noisy_quality = qualities[params_image[0]]
  96. # ref_quality = qualities[params_image[1]]
  97. #
  98. # noisy_image = api.get_image(scene_name, noisy_quality)
  99. # ref_image = api.get_image(scene_name, ref_quality)
  100. #
  101. # # get crop params from configuration
  102. # crop_params = cfg.expes_configuration[expe_name]['text']['examples']['crop_params'][int(example_number)]
  103. #
  104. # img_merge, percentage, orientation, position = crop_images(noisy_image,
  105. # ref_image,
  106. # per=crop_params[0],
  107. # orien=crop_params[1],
  108. # swap_img=crop_params[2])
  109. # width, height = img_merge.size
  110. # if orientation==0:
  111. # left, top, right, bottom = percentage*width, 0, percentage*width, height #vertical
  112. # else:
  113. # left, top, right, bottom = 0, percentage*height, width, percentage*height #horizontal
  114. # if int(example_number) % 2 != 0 :
  115. # if noisy_quality != qualities[-1]:#-noisy_quality > qualities[-1]-(10*qualities[-1])/100 :
  116. # draw = ImageDraw.Draw(img_merge)
  117. # draw.line((left, top, right, bottom), fill='black', width=5)
  118. # example_sentence = cfg.expes_configuration[expe_name]['text']['examples']['sentence'][int(example_number)]
  119. #
  120. # if orientation == 0:
  121. # example_sentence = example_sentence.format('vertically', str(percentage*100))
  122. # else:
  123. # example_sentence = example_sentence.format('horizontally', str(percentage*100))
  124. #
  125. # data['example_sentence'] = example_sentence
  126. #
  127. #
  128. # # Temporary save of image
  129. # tmp_folder = os.path.join(settings.MEDIA_ROOT, cfg.output_tmp_folder)
  130. #
  131. # if not os.path.exists(tmp_folder):
  132. # os.makedirs(tmp_folder)
  133. #
  134. # # generate tmp merged image (pass as BytesIO was complicated..)
  135. # filepath_img = os.path.join(tmp_folder, 'example_' + scene_name + '' + expe_name + '.png')
  136. #
  137. # # replace img_merge if necessary (new iteration of expe)
  138. # if img_merge is not None:
  139. # img_merge.save(filepath_img)
  140. #
  141. # print(filepath_img)
  142. # data['example'] = filepath_img
  143. data['start'] = start_experiment
  144. return render(request, 'expe/expe_indications.html', data)
  145. # Create your views here.
  146. def expe(request):
  147. # get param
  148. expe_name = request.GET.get('expe')
  149. scene_name = request.GET.get('scene')
  150. # unique user ID during session (user can launch multiple exeperiences)
  151. if 'id' not in request.session:
  152. request.session['id'] = functions.uniqueID()
  153. # first time expe is launched add expe information
  154. if 'expe' not in request.session or expe_name != request.session.get('expe'):
  155. refresh_data(request, expe_name, scene_name)
  156. # refresh if scene_name changed
  157. if 'scene' not in request.session or scene_name != request.session.get('scene'):
  158. refresh_data(request, expe_name, scene_name)
  159. # create output folder for expe_result
  160. current_day = datetime.strftime(datetime.utcnow(), "%Y-%m-%d")
  161. user_identifier = request.session.get('id')
  162. experiment_id = request.session.get('experimentId')
  163. print("ExperimentId is : " + experiment_id)
  164. # check if experimentId is used or not
  165. if len(experiment_id) == 0:
  166. output_expe_folder = cfg.output_expe_folder_name_day.format(expe_name, current_day, user_identifier)
  167. else:
  168. output_expe_folder = cfg.output_expe_folder_name_id_day.format(expe_name, experiment_id, current_day, user_identifier)
  169. results_folder = os.path.join(settings.MEDIA_ROOT, output_expe_folder)
  170. if not os.path.exists(results_folder):
  171. os.makedirs(results_folder)
  172. result_filename = scene_name + '_' + request.session.get('timestamp') +".csv"
  173. results_filepath = os.path.join(results_folder, result_filename)
  174. if not os.path.exists(results_filepath):
  175. output_file = open(results_filepath, 'w')
  176. functions.write_header_expe(output_file, expe_name)
  177. else:
  178. output_file = open(results_filepath, 'a')
  179. # TODO : add crontab task to erase generated img and model data
  180. # create `quest` object if not exists
  181. models_folder = os.path.join(settings.MEDIA_ROOT, cfg.model_expe_folder.format(expe_name, current_day))
  182. if not os.path.exists(models_folder):
  183. os.makedirs(models_folder)
  184. model_filename = result_filename.replace('.csv', '.obj')
  185. model_filepath = os.path.join(models_folder, model_filename)
  186. # run expe method using `expe_name`
  187. function_name = 'run_' + expe_name
  188. try:
  189. run_expe_method = getattr(run_expe, function_name)
  190. except AttributeError:
  191. raise NotImplementedError("Run expe method `{}` not implement `{}`".format(run_expe.__name__, function_name))
  192. expe_data = run_expe_method(request, model_filepath, output_file)
  193. if request.session.get('expe_finished'):
  194. # reinit session as default value
  195. # here generic expe params
  196. del request.session['expe']
  197. del request.session['scene']
  198. del request.session['experimentId']
  199. del request.session['qualities']
  200. del request.session['timestamp']
  201. # specific current expe session params (see `config.py`)
  202. for key in cfg.expes_configuration[expe_name]['session_params']:
  203. del request.session[key]
  204. # set expe current data into session (replace only if experiments data changed)
  205. if expe_data is not None:
  206. request.session['expe_data'] = expe_data
  207. # get base data
  208. data = get_base_data(expe_name)
  209. # other experimentss information
  210. data['expe_name'] = expe_name
  211. data['end_text'] = cfg.expes_configuration[expe_name]['text']['end_text']
  212. return render(request, cfg.expes_configuration[expe_name]['template'], data)
  213. @login_required(login_url="login/")
  214. def list_results(request, expe=None):
  215. """
  216. Return all results obtained from experimentss
  217. """
  218. if expe is None:
  219. folders = cfg.expe_name_list
  220. return render(request, 'expe/expe_results.html', {'expe': expe, 'folders': folders})
  221. else:
  222. if expe in cfg.expe_name_list:
  223. folder_date_path = os.path.join(settings.MEDIA_ROOT, cfg.output_expe_folder_date, expe)
  224. folder_id_path = os.path.join(settings.MEDIA_ROOT, cfg.output_expe_folder_id, expe)
  225. # extract date files
  226. folders_date = {}
  227. if os.path.exists(folder_date_path):
  228. days = sorted(os.listdir(folder_date_path), reverse=True)
  229. # get all days
  230. for day in days:
  231. day_path = os.path.join(folder_date_path, day)
  232. users = os.listdir(day_path)
  233. folders_user = {}
  234. # get all users files
  235. for user in users:
  236. user_path = os.path.join(day_path, user)
  237. filenames = os.listdir(user_path)
  238. folders_user[user] = filenames
  239. # attach users to this day
  240. folders_date[day] = folders_user
  241. # extract expe id files
  242. folders_id = {}
  243. if os.path.exists(folder_id_path):
  244. ids = sorted(os.listdir(folder_id_path), reverse=True)
  245. # get all days
  246. for identifier in ids:
  247. id_path = os.path.join(folder_id_path, identifier)
  248. days = sorted(os.listdir(id_path), reverse=True)
  249. folder_days = {}
  250. # get all days
  251. for day in days:
  252. day_path = os.path.join(id_path, day)
  253. users = os.listdir(day_path)
  254. folders_user = {}
  255. # get all users files
  256. for user in users:
  257. user_path = os.path.join(day_path, user)
  258. filenames = os.listdir(user_path)
  259. folders_user[user] = filenames
  260. # attach users to this day
  261. folder_days[day] = folders_user
  262. folders_id[identifier] = folder_days
  263. folders = { 'date': folders_date, 'expeId': folders_id}
  264. else:
  265. raise Http404("Expe does not exists")
  266. # get base data
  267. data = get_base_data()
  268. # expe parameters
  269. data['expe'] = expe
  270. data['folders'] = folders
  271. data['infos'] = cfg.expes_configuration[expe]['text']
  272. return render(request, 'expe/expe_results.html', data)
  273. @login_required(login_url="login/")
  274. def download_result(request):
  275. path = request.POST.get('path')
  276. folder_path = os.path.join(settings.MEDIA_ROOT, cfg.output_expe_folder, path)
  277. # Folder is required
  278. if os.path.exists(folder_path):
  279. # Open BytesIO to grab in-memory ZIP contents
  280. s = BytesIO()
  281. # check if file or folder
  282. if os.path.isdir(folder_path):
  283. # get files from a specific day
  284. filenames = os.listdir(folder_path)
  285. # Folder name in ZIP archive which contains the above files
  286. # E.g [thearchive.zip]/somefiles/file2.txt
  287. # FIXME: Set this to something better
  288. zip_subdir = folder_path.split('/')[-1]
  289. zip_filename = "%s.zip" % zip_subdir
  290. # The zip compressor
  291. zf = zipfile.ZipFile(s, "w")
  292. for fpath in filenames:
  293. fpath = os.path.join(folder_path, fpath)
  294. # Calculate path for file in zip
  295. fdir, fname = os.path.split(fpath)
  296. zip_path = os.path.join(zip_subdir, fname)
  297. # Add file, at correct path
  298. zf.write(fpath, zip_path)
  299. # Must close zip for all contents to be written
  300. zf.close()
  301. output_filename = zip_filename
  302. content = s.getvalue()
  303. else:
  304. with open(folder_path, 'rb') as f:
  305. content = f.readlines()
  306. # filename only
  307. fdir, fname = os.path.split(path)
  308. output_filename = fname
  309. # Grab ZIP file from in-memory, make response with correct MIME-type
  310. resp = HttpResponse(content, content_type="application/gzip")
  311. # ..and correct content-disposition
  312. resp['Content-Disposition'] = 'attachment; filename=%s' % output_filename
  313. return resp
  314. else:
  315. return Http404("Path does not exist")
  316. def refresh_data(request, expe_name, scene_name):
  317. '''
  318. Utils method to refresh data from session
  319. '''
  320. request.session['expe'] = expe_name
  321. request.session['scene'] = scene_name
  322. request.session['expe_started'] = False
  323. request.session['expe_finished'] = False
  324. request.session['qualities'] = api.get_scene_qualities(scene_name)
  325. # update unique timestamp each time new experiments is launched
  326. request.session['timestamp'] = datetime.strftime(datetime.utcnow(), "%Y-%m-%d_%Hh%Mm%Ss")
  327. # retrieve and store experimentId
  328. expe_id = request.GET.get('experimentId')
  329. request.session['experimentId'] = expe_id
  330. # TODO : add in cache ref_image
  331. # get reference image
  332. #ref_image = api.get_image(scene_name, 'max')
  333. # save ref image as list (can't save python object)
  334. #request.session['ref_img'] = np.array(ref_image).tolist()