Skip to content

Commit

Permalink
Implement DatasetWizardView with skippable steps and refactor form ha…
Browse files Browse the repository at this point in the history
…ndling
  • Loading branch information
moustaphacheikh committed Aug 3, 2023
1 parent 2d408aa commit 63223bd
Show file tree
Hide file tree
Showing 13 changed files with 316 additions and 23 deletions.
5 changes: 4 additions & 1 deletion core/forms/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
from .legal_basis import LegalBasisForm
from .publication import PublicationForm, PickPublicationForm
from .share import ShareForm
from .use_restriction import UseRestrictionForm
from .access import AccessForm



Expand All @@ -34,5 +36,6 @@
"PublicationForm",
"ShareForm",
"PickPublicationForm",
"UseRestrictionForm"
"UseRestrictionForm",
"AccessForm",
]
13 changes: 8 additions & 5 deletions core/forms/access.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
from django.forms import ModelForm, DateInput, Textarea

from core.forms.dataset import SkipFieldValidationMixin
from core.models import Access


class AccessForm(ModelForm):
class AccessForm(SkipFieldValidationMixin, ModelForm):
class Meta:
model = Access
fields = "__all__"
Expand All @@ -15,15 +16,17 @@ class Meta:
# Textareas
"access_notes": Textarea(attrs={"rows": 2, "cols": 40}),
}

heading = "Access"
heading_help = "Please provide a help text for Access form"
def __init__(self, *args, **kwargs):
dataset = kwargs.pop("dataset", None)
super().__init__(*args, **kwargs)
# we don't allow editing dataset
self.fields.pop("dataset")
self.fields["defined_on_locations"].choices = [
(d.id, d) for d in dataset.data_locations.all()
]
if dataset:
self.fields["defined_on_locations"].choices = [
(d.id, d) for d in dataset.data_locations.all()
]

field_order = [
"contact",
Expand Down
5 changes: 4 additions & 1 deletion core/forms/data_declaration.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from django.shortcuts import get_object_or_404
from django.urls import reverse_lazy

from core.forms.dataset import SkipFieldValidationMixin
from core.forms.use_restriction import UseRestrictionForm
from core.models import DataDeclaration, Partner, Contract, GDPRRole
from core.models.contract import PartnerRole
Expand Down Expand Up @@ -169,10 +170,12 @@ def after_save(self, data_declaration):
data_declaration.save()


class DataDeclarationForm(forms.ModelForm):
class DataDeclarationForm(SkipFieldValidationMixin, forms.ModelForm):
class Meta:
model = DataDeclaration
fields = ['title']
heading = "Data declaration"
heading_help = "Please provide a help text for the Data declaration form"

def __init__(self, *args, **kwargs):
self.dataset = kwargs.pop('dataset')
Expand Down
15 changes: 14 additions & 1 deletion core/forms/dataset.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,22 @@
from django import forms
from django.core.exceptions import ObjectDoesNotExist
from django.shortcuts import get_object_or_404
from django.forms import ValidationError
from core.models import Dataset, User, Project
from core.models.contract import Contract


class SkipFieldValidationMixin:
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['skip_wizard'] = forms.BooleanField(initial=False, required=False, widget=forms.HiddenInput())

def is_valid(self):
if self.data.get(f'{self.prefix}-skip_wizard') == 'True':
return True
return super().is_valid()


class DatasetForm(forms.ModelForm):
class Meta:
model = Dataset
Expand All @@ -13,7 +25,8 @@ class Meta:
widgets = {
'comments': forms.Textarea(attrs={'rows': 2, 'cols': 40}),
}

heading = "Dataset"
heading_help = "Please provide a help text for the dataset form"

def __init__(self, *args, **kwargs):
dataset = None
Expand Down
8 changes: 6 additions & 2 deletions core/forms/legal_basis.py
Original file line number Diff line number Diff line change
@@ -1,20 +1,24 @@
from django.forms import ModelForm

from core.forms.dataset import SkipFieldValidationMixin
from core.models import LegalBasis


class LegalBasisForm(ModelForm):
class LegalBasisForm(SkipFieldValidationMixin,ModelForm):
class Meta:
model = LegalBasis
fields = '__all__'
exclude = []
heading = "Data Legal Basis"
heading_help = "Please provide a title for this data declaration"

def __init__(self, *args, **kwargs):
dataset = kwargs.pop('dataset', None)
super().__init__(*args, **kwargs)
# we don't allow editing dataset
self.fields.pop('dataset')
self.fields['data_declarations'].choices = [(d.id, d.title) for d in dataset.data_declarations.all()]
if dataset:
self.fields['data_declarations'].choices = [(d.id, d.title) for d in dataset.data_declarations.all()]

field_order = [
'data_declarations',
Expand Down
9 changes: 7 additions & 2 deletions core/forms/storage_location.py
Original file line number Diff line number Diff line change
@@ -1,19 +1,24 @@
from django import forms

from core.forms.dataset import SkipFieldValidationMixin
from core.models.storage_location import DataLocation


class StorageLocationForm(forms.ModelForm):
class StorageLocationForm(SkipFieldValidationMixin, forms.ModelForm):
class Meta:
model = DataLocation
fields = '__all__'
exclude = []
heading = "Add a new storage location"
heading_help = "Please provide a title for this storage location"

def __init__(self, *args, **kwargs):
dataset = kwargs.pop('dataset', None)
super().__init__(*args, **kwargs)
# we don't allow editing dataset
self.fields.pop('dataset')
self.fields['data_declarations'].choices = [(d.id, d.title) for d in dataset.data_declarations.all()]
if dataset:
self.fields['data_declarations'].choices = [(d.id, d.title) for d in dataset.data_declarations.all()]

field_order = [
'category',
Expand Down
105 changes: 105 additions & 0 deletions web/static/css/dataset_wizard.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
.step-wizard-list {
background: #fff;
color: #333;
list-style-type: none;
border-radius: 10px;
display: flex;
padding: 20px 10px;
position: relative;
z-index: 10;
}

.step-wizard-item {
padding: 0 20px;
flex-basis: 0;
-webkit-box-flex: 1;
-ms-flex-positive: 1;
flex-grow: 1;
max-width: 100%;
display: flex;
flex-direction: column;
text-align: center;
min-width: 170px;
position: relative;
}

.step-wizard-item + .step-wizard-item:after {
content: "";
position: absolute;
left: 0;
top: 19px;
background: #023452;
width: 100%;
height: 2px;
transform: translateX(-50%);
z-index: -10;
}

.progress-count {
height: 40px;
width: 40px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 50%;
font-weight: 600;
margin: 0 auto;
position: relative;
z-index: 10;
color: transparent;
}

.progress-count:after {
content: "";
height: 40px;
width: 40px;
background: #023452;
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
border-radius: 50%;
z-index: -10;
}

.progress-count:before {
content: "";
height: 10px;
width: 20px;
border-left: 3px solid #fff;
border-bottom: 3px solid #fff;
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -60%) rotate(-45deg);
transform-origin: center center;
}

.progress-label {
font-size: 14px;
font-weight: 600;
margin-top: 10px;
}

.current-item .progress-count:before,
.current-item ~ .step-wizard-item .progress-count:before {
display: none;
}

.current-item ~ .step-wizard-item .progress-count:after {
height: 10px;
width: 10px;
}

.current-item ~ .step-wizard-item .progress-label {
opacity: 0.5;
}

.current-item .progress-count:after {
background: #fff;
border: 2px solid #023452;
}

.current-item .progress-count {
color: #023452;
}
29 changes: 29 additions & 0 deletions web/static/js/data_declaration.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
const skipInput = document.getElementById(skipInputID)
const skipButton = document.getElementById('skipButton')
if (!skipInput) {
skipButton.remove();
}
if (skipButton) {
skipButton.addEventListener('click', function () {
if (skipInput) {
skipInput.value = 'True';
document.getElementById('wizard-form').submit();
}
});
}
$(document).ready(function () {
$('input[type=radio]').change(function () {
const submit_button = $("#buttons")
$("#sub-form").remove();
const sub_form = $("<div id='sub-form'>");
submit_button.before(sub_form);
const declaration_type = this.value;
const forms_url = dataDeclarationsAddSubFormUrl + '?declaration_type=' + declaration_type + '&dataset_id=' + dataset_id;
sub_form.load(forms_url, function () {
sub_form.find('select').select2();
sub_form.bootstrapMaterialDesign();
}
)
;
});
});
8 changes: 2 additions & 6 deletions web/templates/_includes/forms.html
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,9 @@
{% include '_includes/field.html' with field=field %}
{% endfor %}
{% if not hide_submit %}
{% if wizard and wizard_url_name %}
<div class="row">
{% if wizard %}
<div class="row" id="buttons">
<div class="col">
{% if wizard.steps.prev %}
<a href="{% url wizard_url_name wizard.steps.prev %}"
class="btn btn-outline-secondary btn-raised float-left">previous step</a>
{% endif %}
<button {% if submit_disabled %}disabled="disabled"{% endif %} type="submit"
class="btn btn-primary btn-raised float-right">{{ submit_label | default:"Submit" }}</button>
</div>
Expand Down
69 changes: 69 additions & 0 deletions web/templates/datasets/dataset_wizard_form.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
{% extends 'layout.html' %}
{% load static %}
{% block head_end %}
{{ wizard.form.media }}
<link rel="stylesheet" href="{% static 'css/dataset_wizard.css' %}">
{% endblock %}

{% block content %}
<div class="row">
<div class="jumbotron mt-5 py-2 pt-4 px-4 w-100">
<ul class="step-wizard-list">
{% for step, step_verbose_name in steps_verbose_data %}
<li class="step-wizard-item {% if wizard.steps.current == step %}current-item{% endif %}">
<span class="progress-count">{{ forloop.counter }}</span>
<span class="progress-label">{{ step_verbose_name }}</span>
</li>
{% endfor %}
</ul>
</div>

<div class="col-md-12 card">
<div class="d-flex justify-content-between mt-3">
<a href="{% url 'wizard' %}?reset=true"
class="btn btn-outline-secondary btn-raised float-right">
<i class="material-icons" style="line-height:1.5;">refresh</i>
</a>
<a type="button" id="skipButton" class="btn btn-outline-secondary btn-raised float-left">
<i class="material-icons" style="line-height:1.5;">skip_next</i>
</a>
</div>
<form action="" method="post" class="form col-md-12 nice-selects" id="wizard-form">
{% csrf_token %}
<table>
{{ wizard.management_form }}
{% if wizard.form.forms %}
<!-- If wizard.form.forms is not empty or evaluates to True -->
{{ wizard.form.management_form }}
{% for form in wizard.form.forms %}
<!-- Display form information -->
<div class="jumbotron mt-5 p-4">
<h1 class="display-4">{{ form.Meta.heading }}</h1>
<p class="text-muted">{{ form.Meta.heading_help }}</p>
</div>
{% include '_includes/forms.html' with form=form %}
{% endfor %}
{% else %}
<!-- If wizard.form.forms is empty or evaluates to False -->
<!-- Display form information -->
<div class="jumbotron mt-5 p-4">
<h1 class="display-4">{{ form.Meta.heading }}</h1>
<p class="text-muted">{{ form.Meta.heading_help }}</p>
</div>
{% include '_includes/forms.html' with form=wizard.form %}
{% endif %}
</table>
</form>
</div>
</div>
{% endblock %}

{% block js %}
<script>
const dataDeclarationsAddSubFormUrl = "{% url 'data_declarations_add_sub_form' %}";
const dataset_id = '{{ dataset_id }}';
const stepName = '{{ step_name|safe }}';
const skipInputID = 'id_' + '{{ step_name|safe }}' + '-skip_wizard';
</script>
<script src="{% static 'js/data_declaration.js' %}"></script>
{% endblock %}
2 changes: 1 addition & 1 deletion web/templates/search/search_page.html
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ <h1 class="display-4">Filter the results</h1>


{% block floating-buttons %}
<a class="btn btn-primary bmd-btn-fab float-right" href="{% url add_url %}{% if reset %}?reset=true{% endif %}">
<a class="btn btn-primary bmd-btn-fab float-right" href="{% url 'wizard' %}{% if reset %}?reset=true{% endif %}">
<i class="material-icons">add</i>
</a>
<a class="btn btn-secondary bmd-btn-fab d-md-none float-right" href="#sidebar-responsive-left">
Expand Down
Loading

0 comments on commit 63223bd

Please sign in to comment.