def load_file(directory, rel_path): """ Load the file using the right function according to the type map to its extension. Catch every exception raised by the corresponding loading function or the parser. Return: - (PLTP/PL, []) if the PLTP/PL was loaded successfully - (PLTP/PL, warning_list) if the PLTP/PL was loaded with warnings - (None, error_msg) if PLTP/PL couldn't be loaded / an exception was catch """ try: typ = get_type(directory, rel_path) if typ == 'pltp': return load_pltp(directory, rel_path) elif typ == 'pl': return load_pl(directory, rel_path) elif typ == 'pla': return load_pla(directory, rel_path) except Exception as e: if not settings.DEBUG: return None, htmlprint.code(str(e)) return (None, (htmlprint.code(str(e)) + '<br>DEBUG set to True - Showing Traceback :<br>' + htmlprint.html_exc()))
def delete_resource(request): """Delete a file or folder """ post = json.loads(request.body.decode()) path = post.get('path') if not path: return HttpResponseBadRequest(missing_parameter('path')) if is_root(path): return HttpResponseBadRequest('cannot delete a root folder') try: path = join_fb_root(path) shutil.rmtree( path, ignore_errors=True) if os.path.isdir(path) else os.remove(path) if not path.startswith("lib/"): add_commit_path(request, path, action="deleted") return JsonResponse({'success': True}) except Exception as e: # pragma: no cover path = path.replace(settings.FILEBROWSER_ROOT, '') error = htmlprint.code(str(e).replace(settings.FILEBROWSER_ROOT, '')) msg = "Impossible to delete '%s' : %s" % (path, error) if settings.DEBUG: msg += ("DEBUG: " + htmlprint.html_exc()) return HttpResponseNotFound(msg)
def current_pl_template(self, request, context=None): """Return a template of the PL with the session exercise context. If given, will use context instead.""" session_exercise = self.session_exercise() try: pl = session_exercise.pl if pl: return session_exercise.get_pl(request, context) else: dic = dict(session_exercise.context if not context else context) dic['user_settings__'] = session_exercise.session_activity.user.profile dic['user__'] = session_exercise.session_activity.user first_pls = session_exercise.session_activity.activity.indexed_pl() if first_pls: dic['first_pl__'] = first_pls[0].id env = Jinja2.get_default() for key in dic: if type(dic[key]) is str: dic[key] = env.from_string(dic[key]).render(context=dic, request=request) return get_template("activity/activity_type/pltp/pltp.html").render(dic, request) except Exception as e: # pragma: no cover error_msg = str(e) if request.user.profile.can_load(): error_msg += "<br><br>" + htmlprint.html_exc() return get_template("playexo/error.html").render({"error_msg": error_msg})
def create_resource(request): """Create a new file or folder """ post = json.loads(request.body.decode()) path = post.get('path') if not path: return HttpResponseBadRequest(missing_parameter('path')) path = join_fb_root(path) name = os.path.basename(path) try: if any(c in name for c in settings.FILEBROWSER_DISALLOWED_CHAR): msg = "Can't create '%s': name should not contain any of %s." return HttpResponseBadRequest(msg % (name, str(settings.FILEBROWSER_DISALLOWED_CHAR))) if os.path.exists(path): return HttpResponseBadRequest("Can't create '%s': this name is already used." % name) if post.get('type', '') == 'file': with open(path, "w") as f: print(post.get('content', ''), file=f) return JsonResponse({'path': rm_fb_root(path)}) else: os.mkdir(path) return JsonResponse({'path': rm_fb_root(path)}) except Exception as e: # pragma: no cover msg = "Impossible to create '{0}' : {1}".format(name, htmlprint.code( str(type(e)) + ' - ' + str(e))) messages.error(request, msg) if settings.DEBUG: msg += ("DEBUG set to True: " + htmlprint.html_exc()) return HttpResponseNotFound(msg)
def rename_resource(request): """Rename a file or folder """ post = json.loads(request.body.decode()) path = post.get('path') target = post.get('target') if not path: return HttpResponseBadRequest(missing_parameter('path')) if not target: return HttpResponseBadRequest(missing_parameter('target')) path = join_fb_root(path) name = os.path.basename(path) new_path = os.path.join(os.path.dirname(path), target) try: if any(c in target for c in settings.FILEBROWSER_DISALLOWED_CHAR): msg = "Can't rename '{0}' to '{1}': name should not contain any of {2}." \ .format(name, target, settings.FILEBROWSER_DISALLOWED_CHAR) return HttpResponseBadRequest(msg) if os.path.exists(new_path): msg = "Can't rename '{0}' to '{1}': this name is already used.".format(name, target) return HttpResponseBadRequest(msg) os.rename(path, new_path) return JsonResponse( {'path': rm_fb_root(new_path), 'status': 200}) except Exception as e: # pragma: no cover msg = "Impossible to rename '{0}' to '{1}': {2}".format(name, target, htmlprint.code( str(type(e)) + ' - ' + str(e))) if settings.DEBUG: msg += ("DEBUG set to True: " + htmlprint.html_exc()) return HttpResponseNotFound(msg)
def reload_activity(path, activity): try: path_components = path.split('/') directory = Directory.objects.get(name=path_components[0]) relative = os.path.join(*(path_components[1:])) pltp, warnings = rp(directory, relative, activity) if not pltp and not warnings: # pragma: no cover return HttpResponse("This PLTP is already loaded") elif not pltp: # pragma: no cover return HttpResponseNotFound( f"Failed to load '{os.path.basename(path)}': \n{warnings}") else: activity.reload() msg = '' if warnings: # pragma: no cover for warning in warnings: msg += str(warning) return HttpResponse(msg + "L'activité <b>'" + pltp.name + "'</b> a bien été rechargé.") except Exception as e: # pragma: no cover msg = "Impossible to load '" + os.path.basename( path) + "' : " + htmlprint.code(str(type(e)) + ' - ' + str(e)) if settings.DEBUG: msg += ("DEBUG set to True: " + htmlprint.html_exc()) return HttpResponseNotFound(msg)
def upload_resource(request): # TODO ADD TEST """ Allow the user to upload a file in the filebrowser """ if request.method != 'POST': return HttpResponseNotAllowed(['POST']) f = request.FILES.get('file') if not f: return HttpResponseBadRequest(missing_parameter('file')) path = request.POST.get('path') if not path: return HttpResponseBadRequest(missing_parameter('path')) name = f.name try: path = os.path.join(join_fb_root(path), name) if os.path.exists(path): return HttpResponseBadRequest("This file's name is already used : " + name) else: with open(path, 'wb+') as dest: for chunk in f.chunks(): dest.write(chunk) return HttpResponse() except Exception as e: # pragma: no cover msg = "Impossible to upload '" + path + "' : " + htmlprint.code( str(type(e)) + ' - ' + str(e)) if settings.DEBUG: messages.error(request, "DEBUG set to True: " + htmlprint.html_exc()) return HttpResponseNotFound(msg)
def reload_pla(directory, rel_path, original): """Reload the given file as a PLTP. Also reload its PL, but without modyfing their ID. original is a pltp returned by load_pltp or reload_pltp Return: - (PLTP, []) if the PLTP was loaded successfully - (PLTP, warning_list) if the PLTP was loaded with warnings """ try: dic, warnings = parse_file(directory, rel_path) original_type = get_activity_type_class(original.activity_type)() if "type" in dic and original.activity_type != dic["type"]: return None, f"Activity type must remain '{original.activity_type}'" original.activity_data = { **dic, **original_type.install(original), "__reload_path": os.path.join(directory.name, rel_path) } pl_list = list() for item in dic['__pl']: pl_directory = Directory.objects.get(name=item['directory_name']) pl, pl_warnings = load_pl(pl_directory, item['path']) if pl is None: return None, pl_warnings warnings += pl_warnings pl_list.append(pl) originals = list(original.indexed_pl()) original.pl.clear() for pl in pl_list: correspond = list( filter( lambda i: i.directory == pl.directory and i.rel_path == pl. rel_path, originals)) if correspond: correspond = correspond[0] correspond.json = pl.json correspond.save() logger.info("PL '" + str(correspond.id) + " (" + correspond.name + ")' has been updated.") PLPosition.objects.create(parent=original, pl=correspond) else: pl.save() logger.info("PL '" + str(pl.id) + " (" + pl.name + ")' has been created.") PLPosition.objects.create(parent=original, pl=pl) original.save() logger.info("Activity '" + original.name + "' has been reloaded.") return original, [htmlprint.code(warning) for warning in warnings] except Exception as e: # pragma: no cover if not settings.DEBUG: return None, htmlprint.code(str(e)) return (None, (htmlprint.code(str(e)) + '<br>DEBUG set to True - Showing Traceback :<br>' + htmlprint.html_exc()))
def test_html_exc(self): try: raise ValueError("This is a test") except: s = h.html_exc() self.assertTrue('ValueError: This is a test' in s) self.assertTrue('<code' in s) self.assertTrue('<pre' in s) self.assertTrue('</code>' in s) self.assertTrue('</pre>' in s)
def preview_pl(request): """ Used by the PL editor to preview a PL""" post = json.loads(request.body.decode()) exists = True path = post.get('path') if not path: exists = False path = os.path.join(HOME_DIR, str(uuid.uuid4())) path += '.pl' content = post.get('content', '') path_components = path.split('/') directory = path_components[0] try: path = os.path.join(settings.FILEBROWSER_ROOT, path) if exists: shutil.copyfile(path, path + ".bk") with open(path, 'w+') as f: # Writting editor content into the file print(content, file=f) directory = Directory.objects.get(name=directory) relative = os.path.join(*(path_components[1:])) pl, warnings = load_file(directory, relative) if not pl: preview = '<div class="alert alert-danger" role="alert"> 1 Failed to load \'' \ + os.path.basename(relative) + "': \n" + warnings + "</div>" else: if warnings: [messages.warning(request, warning) for warning in warnings] pl.save() exercise = SessionTest.objects.create(pl=pl, user=request.user) preview = exercise.get_exercise(request) except Exception as e: # pragma: no cover preview = ('<div class="alert alert-danger" role="alert"> 3 Failed to load \'' + os.path.basename(relative) + "': \n\n" + htmlprint.code(str(e))) if settings.DEBUG: preview += "\n\nDEBUG set to True:\n" + htmlprint.html_exc() preview += "</div>" finally: if exists: shutil.move(path + ".bk", path) else: os.remove(path) preview = get_template("filebrowser/preview.html").render({'preview': preview}, request) return HttpResponse( json.dumps({'preview': preview}), content_type='application/json', status=200 )
def get_exercise(self, request, answer=None): """Return a template of the PL. If answer is given, will determine if the seed must be reroll base on its grade.""" try: return self.get_pl(request, answer) except Exception as e: # pragma: no cover error_msg = str(e) if request.user.profile.can_load(): error_msg += "<br><br>" + htmlprint.html_exc() return get_template("playexo/error.html").render( {"error_msg": error_msg})
def compile(request): path = request.GET.get('path') try: parser = find_parser(path) ast = parser.parse(path) return JsonResponse({'ast': ast}) except Exception as e: # pragma: no cover traceback.print_exc() msg = "Impossible to open '" + path + "' : " + htmlprint.code( str(type(e)) + ' - ' + str(e)) if settings.DEBUG: messages.error(request, "DEBUG set to True: " + htmlprint.html_exc()) return HttpResponseNotFound(msg)
def reload_pltp(directory, rel_path, original): """Reload the given file as a PLTP. Also reload its PL, but without modyfing their ID. original is a pltp returned by load_pltp or reload_pltp Return: - (PLTP, []) if the PLTP was loaded successfully - (PLTP, warning_list) if the PLTP was loaded with warnings """ try: dic, warnings = parse_file(directory, rel_path) pl_list = list() for item in dic['__pl']: pl_directory = Directory.objects.get(name=item['directory_name']) pl, pl_warnings = load_pl(pl_directory, item['path']) warnings += pl_warnings pl_list.append(pl) originals = list(original.pl.all()) original.pl.clear() for pl in pl_list: correspond = list( filter( lambda i: i.directory == pl.directory and i.rel_path == pl. rel_path, originals)) if correspond: correspond = correspond[0] correspond.json = pl.json correspond.save() logger.info("PL '" + str(correspond.id) + " (" + correspond.name + ")' has been updated.") Index.objects.create(pltp=original, pl=correspond) else: pl.save() logger.info("PL '" + str(pl.id) + " (" + pl.name + ")' has been created.") Index.objects.create(pltp=original, pl=pl) original.json = dic original.save() logger.info("PLTP '" + original.sha1 + " (" + original.name + ")' has been updated.") return original, [htmlprint.code(warning) for warning in warnings] except Exception as e: # pragma: no cover if not settings.DEBUG: return None, htmlprint.code(str(e)) return (None, (htmlprint.code(str(e)) + '<br>DEBUG set to True - Showing Traceback :<br>' + htmlprint.html_exc()))
def evaluate(self, response): dic = self.intern_build() dic['response'] = response if 'evaluator' not in self.dic: try: if 'timeout' in locals(): sandbox_session = SandboxSession(self.dic, response['answer'], timeout=timeout) else: sandbox_session = SandboxSession(self.dic, response['answer']) response = json.loads(sandbox_session.call()) state = response['grade'] feedback = response['feedback'] if 'error' in response: feedback += '\n\n' + htmlprint.code(response['error']) return (None, feedback) if state == "info" else ( True, feedback) if state else (False, feedback) except KeyError as e: return (None, ( "La réponse reçu par la sandbox n'est pas au bon format :<br>" + htmlprint.html_exc)) except Exception as e: s = ( "/!\ ATTENTION: La fonction d'évaluation de cet exercice est incorrecte," + "merci de prévenir votre professeur:<br>" + htmlprint.html_exc()) return None, s else: try: exec(dic['evaluator'], dic) if not 'grade' in dic \ or dic['grade'][0] not in [False, True, None] \ or type(dic['grade'][1]) != str: return None, ( "/!\ ATTENTION: La fonction d'évaluation de cet" + "exercice est incorrecte, merci de prévenir votre professeur:<br>" + "evaluator/before should declare a tuple called 'grade' (bool, str)." ) return dic['grade'] except Exception as e: return None, ( "/!\ ATTENTION: La fonction d'évaluation de cet exercice est incorrecte" + "merci de prévenir votre professeur:<br>" + htmlprint.exc_format())
def rename_option(request, filebrowser, target): """ Rename targeted entry with POST['name'] """ if request.method != 'POST': return HttpResponseNotAllowed(['POST']) name = request.POST.get('name', None) if not name: return HttpResponse(status=200) try: path = join(filebrowser.full_path(), target) if '/' in name: raise ValueError() if isfile(join(filebrowser.full_path(), name)): raise OSError() if not filebrowser.directory: d = Directory.objects.get(name=target) d.name = name d.save() os.rename(path, join(filebrowser.full_path(), name)) messages.success( request, "'" + target + "' successfully renamed to '" + name + "' !") except (IntegrityError, OSError, IsADirectoryError): messages.error( request, "Can't rename '" + target + "' to '" + name + "' : this name is already used.") except ValueError: messages.error( request, "Can't rename '" + target + "' to '" + name + "' : name contains a '/'.") except Exception as e: msg = "Impossible to rename '" + target + "' : " + htmlprint.code( str(type(e)) + ' - ' + str(e)) if settings.DEBUG: msg += "<br/><br/> Debug set to True:" + htmlprint.html_exc() messages.error(request, msg.replace(settings.FILEBROWSER_ROOT + "/", "")) return redirect_fb(request.POST.get('relative_h', '.'))
def get_resource(request): """Return the content of <path>.""" path = request.GET.get('path') if not path: return HttpResponseBadRequest('"path" parameter is missing') try: with open(join_fb_root(path)) as f: content = f.read() return JsonResponse({'content': content}) except Exception as e: # pragma: no cover msg = "Impossible to open '" + path + "' : " + htmlprint.code( str(type(e)) + ' - ' + str(e)) if settings.DEBUG: messages.error(request, "DEBUG set to True: " + htmlprint.html_exc()) return HttpResponseNotFound(msg)
def get_resource(request): """Return the content of <path>.""" path = request.GET.get('path') if not path: return HttpResponseBadRequest(missing_parameter('path')) try: path = join_fb_root(path) return JsonResponse({ 'content': get_content(path), 'meta': get_meta(path) }) except Exception as e: # pragma: no cover msg = f"Impossible to open '{rm_fb_root(path)}' : {htmlprint.code(str(e))}" if settings.DEBUG: messages.error(request, "DEBUG set to True: " + htmlprint.html_exc()) msg += f"{request} : DEBUG set to True: {htmlprint.html_exc()}" return HttpResponseNotFound(msg)
def load_pltp(request): path = request.GET.get('path') if not path: return HttpResponseBadRequest(missing_parameter('path')) try: path_components = path.split('/') directory = Directory.objects.get(name=path_components[0]) relative = os.path.join(*(path_components[1:])) pltp, warnings = load_file(directory, relative) if not pltp and not warnings: # pragma: no cover return HttpResponseBadRequest("This PLTP is already loaded") elif not pltp: # pragma: no cover return HttpResponseBadRequest("Failed to load '" + path + "': \n" + warnings) else: msg = '' if warnings: # pragma: no cover for warning in warnings: msg += str(warning) pltp.teacher.add(request.user) url_lti = request.build_absolute_uri( reverse("activity:play", args=[pltp.pk])) msg += "L'activité <b>'" + pltp.name + "'</b> a bien été créée et a pour URL LTI: \ <br>    <input id=\"copy\" style=\"width: 700px;\" value=\"" + \ url_lti + "\" readonly> \ <a target='_blank' rel='noopener noreferrer' class='btn btn-dark' href='" + \ url_lti + "'><i class='far fa-eye'></i> OPEN\ </a>" return HttpResponse(msg) except Exception as e: # pragma: no cover msg = "Impossible to load '" + path + "' : " + htmlprint.code( str(type(e)) + ' - ' + str(e)) if settings.DEBUG: msg += ("DEBUG set to True: " + htmlprint.html_exc()) return HttpResponseBadRequest(msg)
def resolve_path(request): # TODO ADD TEST path = request.GET.get('path') if not path: return HttpResponseBadRequest(missing_parameter('path')) target = request.GET.get('target') if not target: return HttpResponseBadRequest(missing_parameter('target')) try: path_components = path.split('/') directory = Directory.objects.get(name=path_components[0]) current = path_components[1] if len(path_components) == 2 else \ os.path.join(*path_components[1:-1]) directory, path = get_location(directory, target, current=current) return HttpResponse(os.path.join(directory, path)) except Exception as e: msg = "Impossible to resolve the path '" + request.GET.get( 'target') + "' : " + htmlprint.code(str(type(e)) + ' - ' + str(e)) if settings.DEBUG: messages.error(request, "DEBUG set to True: " + htmlprint.html_exc()) return HttpResponseNotFound(msg)
def reload_pltp(request): """Reload a given activity with the targeted PLTP.""" post = json.loads(request.body.decode()) path = post.get('path') if not path: return HttpResponseBadRequest("parameter 'path' is missing") activity_id = post.get('activity_id') if not activity_id: return HttpResponseBadRequest("Missing 'activity_id' parameter") try: activity = Activity.objects.get(id=activity_id) path_components = path.split('/') directory = Directory.objects.get(name=path_components[0]) file_path = os.path.join(*(path_components[1:])) pltp, warnings = rp(directory, file_path, activity.pltp) if not pltp and not warnings: # pragma: no cover return HttpResponse("This PLTP is already loaded") elif not pltp: # pragma: no cover return HttpResponseNotFound( "Failed to load '%s': \n%s" % (os.path.basename(path), warnings.join("\n"))) else: activity.reload() msg = '' if warnings: # pragma: no cover for warning in warnings: msg += str(warning) return HttpResponse(msg + "L'activité <b>'" + pltp.name + "'</b> a bien été rechargé.") except Exception as e: # pragma: no cover msg = "Impossible to load '" + os.path.basename( path) + "' : " + htmlprint.code(str(type(e)) + ' - ' + str(e)) if settings.DEBUG: msg += ("DEBUG set to True: " + htmlprint.html_exc()) return HttpResponseNotFound(msg)
def load_file(directory, rel_path, force=False): """ Load the file using the right function according to the type map to its extension. Process every exception raised by the corresponding loading function or the parser. Return: - (PLTP/PL, []) if the PLTP/PL was loaded successfully - (PLTP/PL, warning_list) if the PLTP/PL was loaded with warnings - (None, error_msg) if PLTP/PL couldn't be loaded - (None, None) if PLTP/PL is already loaded """ try: if get_type(directory, rel_path) == 'pltp': return load_PLTP(directory, rel_path, force) else: return load_PL(directory, rel_path) except Exception as e: if not DEBUG: return (None, htmlprint.code(str(e))) return (None, ( htmlprint.code(str(e)) + '<br>DEBUG set to True - Showing Traceback :<br>' + htmlprint.html_exc()))
def preview_pl(request): """ Used by the PL editor to preview a PL and test the preview's answers""" post = json.loads(request.body.decode()) if post.get('requested_action', '') == 'preview': # Asking for preview path = post.get('path') if not path: return HttpResponseBadRequest("Missing parameter 'path'") path_components = path.split('/') directory = path_components[0] try: path = os.path.join(settings.FILEBROWSER_ROOT, path) shutil.copyfile(path, path + ".bk") with open(path, 'w+') as f: # Writting editor content into the file print(post.get('content', ''), file=f) directory = Directory.objects.get(name=directory) file_path = os.path.join(*(path_components[1:])) pl, warnings = load_file(directory, file_path) if not pl: preview = '<div class="alert alert-danger" role="alert"> Failed to load \'' \ + os.path.basename(file_path) + "': \n" + warnings + "</div>" else: if warnings: [ messages.warning(request, warning) for warning in warnings ] pl.save() exercise = SessionTest.objects.create(pl=pl, user=request.user) preview = exercise.get_exercise(request) except Exception as e: # pragma: no cover preview = ( '<div class="alert alert-danger" role="alert"> Failed to load \'' + os.path.basename(file_path) + "': \n\n" + htmlprint.code(str(e))) if settings.DEBUG: preview += "\n\nDEBUG set to True:\n" + htmlprint.html_exc() preview += "</div>" finally: shutil.move(path + ".bk", path) return HttpResponse(json.dumps({'preview': preview}), content_type='application/json', status=200) elif post.get('requested_action', '') == 'submit': # Answer from the preview data = post.get('data', {}) if 'session_id' not in data or not data['session_id']: return HttpResponseBadRequest( content="Couldn't resolve ajax request") exercise = SessionTest.objects.get(pk=data['session_id']) answer, feedback = exercise.evaluate(request, data['answers'], test=True) return HttpResponse(json.dumps({ "navigation": None, "exercise": exercise.get_exercise(request, answer=answer), "feedback": feedback, }), content_type='application/json') return HttpResponseBadRequest(content="Couldn't resolve ajax request")