def set_up_new_course(request): # type: (http.HttpRequest) -> http.HttpResponse if request.method == "POST": form = CourseCreationForm(request.POST) if form.is_valid(): new_course = form.save(commit=False) from course.content import get_course_repo_path repo_path = get_course_repo_path(new_course) try: import os os.makedirs(repo_path) repo = None try: with transaction.atomic(): repo = Repo.init(repo_path) client, remote_path = \ get_dulwich_client_and_remote_path_from_course( new_course) remote_refs = client.fetch(remote_path, repo) if remote_refs is None: raise RuntimeError( _("No refs found in remote repository" " (i.e. no master branch, no HEAD). " "This looks very much like a blank repository. " "Please create course.yml in the remote " "repository before creating your course.")) transfer_remote_refs(repo, remote_refs) new_sha = repo[b"HEAD"] = remote_refs[b"HEAD"] vrepo = repo if new_course.course_root_path: from course.content import SubdirRepoWrapper vrepo = SubdirRepoWrapper( vrepo, new_course.course_root_path) from course.validation import validate_course_content validate_course_content( # type: ignore vrepo, new_course.course_file, new_course.events_file, new_sha) del vrepo new_course.active_git_commit_sha = new_sha.decode() new_course.save() # {{{ set up a participation for the course creator part = Participation() part.user = request.user part.course = new_course part.status = participation_status.active part.save() part.roles.set([ # created by signal handler for course creation ParticipationRole.objects.get( course=new_course, identifier="instructor") ]) # }}} messages.add_message( request, messages.INFO, _("Course content validated, creation " "succeeded.")) except: # Don't coalesce this handler with the one below. We only want # to delete the directory if we created it. Trust me. # Work around read-only files on Windows. # https://docs.python.org/3.5/library/shutil.html#rmtree-example import os import stat import shutil # Make sure files opened for 'repo' above are actually closed. if repo is not None: # noqa repo.close() # noqa def remove_readonly(func, path, _): # noqa "Clear the readonly bit and reattempt the removal" os.chmod(path, stat.S_IWRITE) func(path) try: shutil.rmtree(repo_path, onerror=remove_readonly) except OSError: messages.add_message( request, messages.WARNING, ugettext("Failed to delete unused " "repository directory '%s'.") % repo_path) raise except Exception as e: from traceback import print_exc print_exc() messages.add_message( request, messages.ERROR, string_concat(_("Course creation failed"), ": %(err_type)s: %(err_str)s") % { "err_type": type(e).__name__, "err_str": str(e) }) else: return redirect("relate-course_page", new_course.identifier) else: form = CourseCreationForm() return render(request, "generic-form.html", { "form_description": _("Set up new course"), "form": form })
def set_up_new_course(request): if not request.user.is_staff: raise PermissionDenied(_("only staff may create courses")) if request.method == "POST": form = CourseCreationForm(request.POST) if form.is_valid(): new_course = form.save(commit=False) from course.content import get_course_repo_path repo_path = get_course_repo_path(new_course) try: import os os.makedirs(repo_path) try: with transaction.atomic(): from dulwich.repo import Repo repo = Repo.init(repo_path) client, remote_path = \ get_dulwich_client_and_remote_path_from_course( new_course) remote_refs = client.fetch(remote_path, repo) new_sha = repo["HEAD"] = remote_refs["HEAD"] vrepo = repo if new_course.course_root_path: from course.content import SubdirRepoWrapper vrepo = SubdirRepoWrapper( vrepo, new_course.course_root_path) from course.validation import validate_course_content validate_course_content(vrepo, new_course.course_file, new_course.events_file, new_sha) del repo del vrepo new_course.valid = True new_course.active_git_commit_sha = new_sha new_course.save() # {{{ set up a participation for the course creator part = Participation() part.user = request.user part.course = new_course part.role = participation_role.instructor part.status = participation_status.active part.save() # }}} messages.add_message( request, messages.INFO, _("Course content validated, creation " "succeeded.")) except: # Don't coalesce this handler with the one below. We only want # to delete the directory if we created it. Trust me. # Work around read-only files on Windows. # https://docs.python.org/3.5/library/shutil.html#rmtree-example import os import stat import shutil # Make sure files opened for 'repo' above are actually closed. import gc gc.collect() def remove_readonly(func, path, _): # noqa "Clear the readonly bit and reattempt the removal" os.chmod(path, stat.S_IWRITE) func(path) try: shutil.rmtree(repo_path, onerror=remove_readonly) except OSError: messages.add_message( request, messages.WARNING, ugettext("Failed to delete unused " "repository directory '%s'.") % repo_path) raise except Exception as e: from traceback import print_exc print_exc() messages.add_message( request, messages.ERROR, string_concat(_("Course creation failed"), ": %(err_type)s: %(err_str)s") % { "err_type": type(e).__name__, "err_str": str(e) }) else: return redirect("relate-course_page", new_course.identifier) else: form = CourseCreationForm() return render(request, "generic-form.html", { "form_description": _("Set up new course"), "form": form })