Skip to content

Commit

Permalink
Merge pull request #31 from RSE-Sheffield/feat/dynamic-survey-forms
Browse files Browse the repository at this point in the history
feat: add dynamic form generation
  • Loading branch information
twinkarma authored Jan 14, 2025
2 parents e7504f5 + 051bd7a commit 063fc41
Show file tree
Hide file tree
Showing 37 changed files with 421 additions and 615 deletions.
1 change: 0 additions & 1 deletion SORT/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,6 @@

"home",
"survey",
"invites",
]

MIDDLEWARE = [
Expand Down
3 changes: 3 additions & 0 deletions home/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@ class Meta:
def clean_email(self):
email = self.cleaned_data.get('email')

if email == self.instance.email:
return email

if User.objects.exclude(pk=self.instance.pk).filter(email=email).exists():
raise forms.ValidationError("This email is already in use.")
return email
Expand Down
1 change: 0 additions & 1 deletion home/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
path('logout/', views.LogoutInterfaceView.as_view(), name='logout'),
path('signup/', views.SignupView.as_view(), name='signup'),
path('', include('survey.urls'), name='survey'),
path('invite/', include('invites.urls'), name='invites'),
path('profile/', views.ProfileView.as_view(), name='profile'),
path('password_reset/', views.CustomPasswordResetView.as_view(), name='password_reset'),
path('password_reset/done/', views.CustomPasswordResetDoneView.as_view(), name='password_reset_done'),
Expand Down
8 changes: 3 additions & 5 deletions home/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
from django.contrib.auth.views import LoginView, LogoutView
from django.views.generic.edit import CreateView, UpdateView
from django.shortcuts import redirect
from survey.models import Questionnaire
from django.shortcuts import render
from django.views import View
from .forms import ManagerSignupForm, ManagerLoginForm, UserProfileForm
Expand Down Expand Up @@ -43,11 +42,10 @@ def dispatch(self, request, *args, **kwargs):
class HomeView(LoginRequiredMixin, View):
template_name = 'home/welcome.html'
login_url = 'login'
def get(self, request):
consent_questionnaire = Questionnaire.objects.get(
title="Consent")
return render(request, 'home/welcome.html', {'questionnaire': consent_questionnaire})

def get(self, request, *args, **kwargs):

return render(request, self.template_name)
class ProfileView(LoginRequiredMixin, UpdateView):
model = User
form_class = UserProfileForm
Expand Down
Empty file removed invites/__init__.py
Empty file.
3 changes: 0 additions & 3 deletions invites/admin.py

This file was deleted.

6 changes: 0 additions & 6 deletions invites/apps.py

This file was deleted.

9 changes: 0 additions & 9 deletions invites/forms.py

This file was deleted.

39 changes: 0 additions & 39 deletions invites/migrations/0001_initial.py

This file was deleted.

19 changes: 0 additions & 19 deletions invites/migrations/0002_alter_invitation_token.py

This file was deleted.

Empty file removed invites/migrations/__init__.py
Empty file.
22 changes: 0 additions & 22 deletions invites/models.py

This file was deleted.

3 changes: 0 additions & 3 deletions invites/tests.py

This file was deleted.

7 changes: 0 additions & 7 deletions invites/urls.py

This file was deleted.

49 changes: 0 additions & 49 deletions invites/views.py

This file was deleted.

1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,4 @@ traitlets==5.14.3
typing_extensions==4.12.2
tzdata==2024.1
wcwidth==0.2.13
strenum==0.4.15
7 changes: 3 additions & 4 deletions survey/admin.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
from django.contrib import admin
from .models import Questionnaire, Question, Answer
from .models import Survey, SurveyResponse

admin.site.register(Questionnaire)
admin.site.register(Question)
admin.site.register(Answer)
admin.site.register(Survey)
admin.site.register(SurveyResponse)
100 changes: 61 additions & 39 deletions survey/forms.py
Original file line number Diff line number Diff line change
@@ -1,40 +1,62 @@
from django import forms
from .models import Answer, Question
class AnswerForm(forms.ModelForm):
class Meta:
model = Answer
fields = [] # no defaults needed

def __init__(self, *args, **kwargs):
questionnaire = kwargs.pop('questionnaire', None) # get the questionnaire passed in
super().__init__(*args, **kwargs)

if questionnaire:

for index, question in enumerate(questionnaire.questions.all(), start=1):
if question.question_type == 'boolean':
self.fields[f'question_{question.id}'] = forms.ChoiceField(
label=f"{index}. {question.question_text}",
choices=[('agree', 'I agree'), ('disagree', 'I disagree')],
widget=forms.RadioSelect,
required=True
)

elif question.question_type == 'rating':

self.fields[f'question_{question.id}'] = forms.ChoiceField(
label=question.question_text,
choices=[(i, str(i)) for i in range(1, 6)], # assuming a 1-5 rating
required=True
)


else:

pass

# self.fields[f'question_{question.id}'] = forms.CharField(
# label=question.question_text,
# required=True,
# widget=forms.Textarea(attrs={'rows': 4, 'style': 'width: 80%;'}),
# )
from django.forms import BaseFormSet, formset_factory
from strenum import StrEnum
from django.core.validators import EmailValidator

class InvitationForm(forms.Form):
email = forms.EmailField(label='Participant Email',
max_length=100,
required=True,
validators=[EmailValidator()])

class FormFieldType(StrEnum):
CHAR = "char"
TEXT = "text"
RADIO = "radio"
CHECKBOX = "checkbox"
LIKERT = "likert"



def create_field_from_config(field_config: dict):
"""
Convert a field configuration into the correct django field
"""
if field_config['type'] == FormFieldType.CHAR:
field = forms.CharField(label=field_config["label"])
elif field_config['type'] == FormFieldType.TEXT:
field = forms.CharField(label=field_config["label"],
widget=forms.Textarea)
elif field_config['type'] == FormFieldType.RADIO:
field = forms.ChoiceField(label=field_config["label"],
choices=field_config["options"],
widget=forms.RadioSelect)
elif field_config['type'] == FormFieldType.CHECKBOX:
field = forms.MultipleChoiceField(label=field_config["label"],
widget=forms.CheckboxSelectMultiple,
choices=field_config["options"])
else:
field = forms.CharField(label=field_config["label"],
widget=forms.Textarea)

if "required" in field_config:
field.required = field_config["required"]

return field



def create_dynamic_formset(field_configs: list):
"""
Create a dynamic form set from a list of field configurations.
"""
class BlankDynamicForm(forms.Form):
pass

class BaseTestFormSet(BaseFormSet):
def add_fields(self, form, index):
super().add_fields(form, index)
for field_config in field_configs:
form.fields[field_config["name"]] = create_field_from_config(field_config)

return formset_factory(BlankDynamicForm, BaseTestFormSet, min_num=1, max_num=1)
Loading

0 comments on commit 063fc41

Please sign in to comment.