Source code for autowisp.browser_interface.home.create_project_view

"""Define the view for creating new AutoWISP projects."""

import os
from argparse import Namespace
import json

from django.shortcuts import render, redirect
from django.http import HttpResponseRedirect

from autowisp.database.interface import set_project_home, DB_URL_FNAME
from autowisp.database.initialize_database import initialize_database
from autowisp.database.user_interface import (
    apply_master_config,
    master_config_json_to_settings,
    parse_config_overwrites,
)
from autowisp import database
from autowisp.browser_interface.core.walk_fs_view import WalkFSView
from .models import Project


[docs] class CreateProjectView(WalkFSView): """View to create a new AutoWISP project.""" template = "home/create_project.html" """The template used to display the project creation page.""" url_name = "home:new_project" """The URL name for this view.""" cancel_url_name = "home:new_project" """The URL name to redirect to when the cancel button is pressed.""" mode = "create_project" """ What mode this view is in. Possible values: ``"select_home"``: Display the project home director selection page. ``"create_dir"``: Allow specifying name of directory to create. ``"create_project"``: Create a new project in the specified directory. ``"change_master_usage"``: Toggle the """ db_fname = "autowisp.db" """The base filename of the SQLite database tracking all projects."""
[docs] def _get_context(self, config, search_dir): """Return the context required by the home selection template.""" context = super()._get_context(config, search_dir) context["unselectable"] = context.pop("file_list") context["file_list"] = [] currentdir = context["parent_dir_list"][-1][0] already_exists = os.path.exists( os.path.join(currentdir, self.db_fname) ) or os.path.exists(os.path.join(currentdir, DB_URL_FNAME)) if already_exists: context["invalid_home_message"] = ( f"Directory {currentdir} already appears to contain an AutoWISP" " project." ) context["valid_home"] = False else: context["invalid_home_message"] = "valid home" context["valid_home"] = True return context
[docs] def _create_project(self, config): """Create a new project following the given configuration.""" db_url = config.get("db-url", "").strip() or None project_home = config["project-home"] assert not os.path.exists( os.path.join(project_home, self.db_fname) ) and not os.path.exists( os.path.join(project_home, DB_URL_FNAME) ), f"Directory {project_home} appears to already contain a project." # new_project refuses a database that already holds AutoWISP # tables -- initialize_database below drops them all, which on a # shared/centralised server would wipe another project. set_project_home(project_home, db_url=db_url, new_project=True) initialize_database( Namespace(drop_hdf5_structure_tables=False, drop_all_tables=True), *apply_master_config(config), parse_config_overwrites(config["custom-config"].splitlines()), ) # Recorded only once the project database exists, so a refused or # failed creation does not leave a Project row pointing at nothing. Project( name=config["project-name"], path=project_home, description=config["project-description"], ).save()
[docs] def _save_form(self, request): """Save the current state of the form to the session.""" for key in [ "project-name", "project-description", "project-home", "custom-config", "db-url", ]: request.session[key] = request.POST.get(key, "") for master_type in database.defaults.master_info: if master_type == "lowflat": continue if master_type == "highflat": master_type = "flat" for param in ["enabled", "split", "match"]: key = f"master-{master_type}-{param}" request.session[key] = ( request.POST[key] if param == "enabled" else list(filter(None, request.POST.getlist(key))) )
[docs] def _load_master_config(self, request): """Load master configuration from user-supplied JSON file.""" master_settings = master_config_json_to_settings( json.load(request.FILES["import-master-config"]) ) for key, value in master_settings.items(): request.session[key] = value
[docs] def get(self, request, dirname=None): """ Display the appropriate project cretion page per the current mode. The expected arguments depend on the mode: Args: dirname (str, optional): Directory name to display contents of when selecting project home or where new directory or new project will be created. """ def get_master_usage(used_by): """Return usage string for master with given "used_by" entries.""" if not used_by: return "output only" if used_by[0][2]: return "optional" return "required" def get_master_info(master_type, master_config): """Return the entry in context to add for the given master type.""" if master_type == "highflat": master_type = "flat" return ( master_type, str( request.session.get( f"master-{master_type}-enabled", ( 1 if master_type in ["zero", "dark", "flat"] else "always" ), ) ), get_master_usage(master_config["used_by"]), request.session.get( f"master-{master_type}-split", master_config["split_by"], ), request.session.get( f"master-{master_type}-match", master_config["must_match"], ), ) print(f"Mode: {self.mode!r}, dirname: {dirname!r}") if self.mode == "create_dir": print(f"Creating directory under {dirname!r}") context = self._get_context(request.GET, dirname) print(f"Context: {context!r}") return render(request, self.template, context) if self.mode == "create_project": print("Session:") for key, value in request.session.items(): print(f"\t{key} => {value}") print( f"Create project {request.session.get('project-name', '')} in " f"{request.session.get('project-home', '')!r}" ) return render( request, "home/create_project.html", { "path": request.session.get("project-home", ""), "name": request.session.get("project-name", ""), "description": request.session.get( "project-description", "" ), "config": request.session.get("custom-config", ""), "db_url": request.session.get("db-url", ""), "master_info": [ get_master_info(master_type, master_config) for ( master_type, master_config, ) in database.defaults.master_info.items() if master_type != "lowflat" ], }, ) assert self.mode == "select_home", f"Invalid mode {self.mode!r}" return super().get(request, dirname=dirname)
[docs] def post(self, request, *_args, **_kwargs): """ Handle POST request to create a new directory. Args: request: The HTTP request object. """ print(f"Received POST request {request!r}: {request.POST!r}") if "create-project-submit" in request.POST: print(f"Creating project from {request.POST}") self._create_project(request.POST) return redirect("home:home") if "set-project-home" in request.POST: print(f"Setting project home from {request.POST}") request.session["project-home"] = request.POST["currentdir"] return redirect("home:new_project") if "import-master-config" in getattr(request, "FILES", []): self._save_form(request) self._load_master_config(request) return redirect("home:new_project") if "redirect" in request.POST: self._save_form(request) return HttpResponseRedirect(request.POST["redirect"]) if "create-dir" in request.POST: new_dir = os.path.join( request.POST["currentdir"], request.POST["create-dir"] ) try: os.mkdir(new_dir) except OSError: print(f"Failed to create directory {new_dir!r}") return redirect( "home:create_directory", dirname=request.POST["currentdir"] ) else: new_dir = request.POST["currentdir"] return redirect("home:select_project_home", dirname=new_dir)
[docs] class MasterConfigView(CreateProjectView): """Handle master configuration part of the project creation view."""
[docs] def get( # pylint: disable=arguments-renamed self, request, master_type, **kwargs ): """ Handle a change to the master configuration by the user. If kwargs is empty, the master is toggled between enabled and disabled. Otherwise, it should contain a single key (``"split"`` or ``"match"``) with a value a single tuple specifying the change: ``(old expression, new expression)``. If old expression is None, the corresponding list is extended. """ if not kwargs: assert master_type in ["zero", "dark", "flat"] key = f"master-{master_type}-enabled" request.session[key] = 1 - int(request.session[key]) return super().get(request)