"""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 _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)