Skip to content

Commit

Permalink
Cleanup
Browse files Browse the repository at this point in the history
  • Loading branch information
rabstejnek committed Jan 27, 2025
1 parent 6e654fe commit cd8621e
Show file tree
Hide file tree
Showing 5 changed files with 54 additions and 166 deletions.
3 changes: 3 additions & 0 deletions hawc/apps/common/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -459,6 +459,9 @@ def validate(self, value):
if not form.is_valid():
raise forms.ValidationError(self.error_messages["invalid"])

class NewDynamicFormField(DynamicFormField):
widget = widgets.NewDynamicFormWidget


class InlineRadioChoiceField(forms.ChoiceField):
"""Choice widget that uses radio buttons that are inline."""
Expand Down
25 changes: 16 additions & 9 deletions hawc/apps/common/widgets.py
Original file line number Diff line number Diff line change
Expand Up @@ -148,40 +148,47 @@ def add_prefix(self, field_name):

def format_value(self, value):
"""Value used in rendering."""
#import pdb; pdb.set_trace()
if value is not None and not isinstance(value,dict):
value = json.loads(value)
if value:
value = {self.add_prefix(k): v for k, v in value.items()}
return self.form_class(data=value, **self.form_kwargs)

def value_from_datadict(self, data, files, name):
"""Parse value from POST request."""
self.form_kwargs.pop("prefix",None)
form = self.form_class(data=data,prefix=name, **self.form_kwargs)
form.full_clean()
return form.cleaned_data

class Media:
js = ["js/udf.js"]

class NewDynamicFormWidget(DynamicFormWidget):
"""Dynamic form widget that uses name instead of prefix."""
def get_context(self, name, value, attrs):
if value is not None and not isinstance(value,dict):
value = json.loads(value)
if value:
value = {f"{name}-{k}": v for k, v in value.items()}
foobar= self.form_class(data=value, prefix=name)
formatted_value = self.form_class(data=value, prefix=name)
return {
"widget": {
"name": name,
"is_hidden": self.is_hidden,
"required": self.is_required,
"value": foobar,
"value": formatted_value,
"attrs": self.build_attrs(self.attrs, attrs),
"template_name": self.template_name,
},
}

def value_from_datadict(self, data, files, name):
"""Parse value from POST request."""
form = self.form_class(data=data, **self.form_kwargs)
self.form_kwargs.pop("prefix",None)
form = self.form_class(data=data,prefix=name, **self.form_kwargs)
form.full_clean()
#import pdb; pdb.set_trace()
return form.cleaned_data

class Media:
js = ["js/udf.js"]


class ColorInput(TextInput):
input_type = "color"
181 changes: 25 additions & 156 deletions hawc/apps/udf/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,60 +11,17 @@
from ..assessment.models import Assessment
from ..common.autocomplete.forms import AutocompleteSelectMultipleWidget
from ..common.dynamic_forms.schemas import Schema
from ..common.forms import BaseFormHelper, PydanticValidator, form_actions_big, DynamicFormField
from ..common.forms import BaseFormHelper, PydanticValidator, form_actions_big, NewDynamicFormField
from ..common.helper import get_current_user
from ..common.views import create_object_log
from ..lit.models import ReferenceFilterTag
from ..myuser.autocomplete import UserAutocomplete
from . import cache, constants, models

class FormAsField(forms.Field):
# render a form as a field, something like a fieldset
### probably use DynamicFormField
pass

class ArrayWidgetOld(forms.Widget):
template_name = "udf/widget.html"

class Media:
js = (f"{settings.STATIC_URL}js/array.js",)

class ArrayFieldOld(forms.Field):
# on render have the underlying field disabled & hidden. this will be copy & pasted on "create" click
# add [] suffix to fields here so that they're POSTed as arrays (AlpineJS ideally, likely jQuery)
# create & delete element buttons
# arrow buttons for reordering
widget = ArrayWidgetOld

def __init__(self,field):
self.field = field
super().__init__()

def widget_attrs(self, widget):
attrs = super().widget_attrs(widget)
if isinstance(widget, ArrayWidgetOld) and "field" not in widget.attrs:
attrs.setdefault("field", self.field)
return attrs

class SchemaFieldForm(forms.Form):
# use dynamic form w/ conditional? since we want different forms
# depending on what field type is chosen
# conditional logic may clash with js from array field (ie suffixes added to names)
# may have to bring over HERO AlpineJS implementation of conditional logic to make it work
pass

class ArrayWidget(forms.MultiWidget):
# TODO: override template to create hidden input,
# "Add" button that grabs hidden input and appends it,
# "Delete" button that removes field
# Ideally add & delete with alpineJS, if not then jQuery most likely

template_name = "udf/widget.html"

#class Media:
# may not be necessary if done entirely with alpineJS
#js = (f"{settings.STATIC_URL}js/array.js",)

def __init__(self, default_widget, attrs=None):
super(forms.MultiWidget,self).__init__(attrs)
self.default_widget = default_widget
Expand All @@ -87,26 +44,20 @@ def initialize_widgets_from_fields(self, fields:list):
self.initialize_widgets(widgets)

def value_from_datadict(self, data, files, name):
#import pdb; pdb.set_trace()
try:
getter = data.getlist
except AttributeError:
getter = data.get

# TODO replace this logic with index based logic;
# should look cleaner here, but trickier on frontend
keys = [_ for _ in data.keys() if _.startswith(name+self.suffix)]
lists = [getter(key) for key in keys]
values = [{(name + k.removeprefix(name+self.suffix)):v[i] for i,k in enumerate(keys)} for v in zip(*lists)]


if values:
foo = [
return [
self.default_widget.value_from_datadict(value, files, name)
for value in values
]
#import pdb; pdb.set_trace()
return foo

return getter(name) or []

Expand Down Expand Up @@ -153,7 +104,6 @@ def initialize_fields(self, fields:list):
self.widget.initialize_widgets_from_fields(self.fields)

def initialize_fields_from_value(self, value:list):
#import pdb; pdb.set_trace()
fields = [deepcopy(self.field) for _ in value]
self.initialize_fields(fields)

Expand Down Expand Up @@ -221,23 +171,14 @@ def helper(self):
return helper


class Foobar(forms.Form):
fields = ArrayField(DynamicFormField(prefix="schema_builder-fields",form_class=FieldForm))
#conditions = ArrayField(DynamicFormField(required=False,prefix="schema_builder-conditions",form_class=ConditionForm))
class SchemaForm(forms.Form):
fields = ArrayField(NewDynamicFormField(prefix="schema_builder-fields",form_class=FieldForm))
#conditions = ArrayField(NewDynamicFormField(required=False,prefix="schema_builder-conditions",form_class=ConditionForm))

def __init__(self,*args,**kwargs):
#kwargs.pop("instance")
#kwargs.pop("user")
#kwargs["data"] = {"fields":[1,2,3]}
super().__init__(*args,**kwargs)
#import pdb; pdb.set_trace()
#self.fields["fields"].initialize_fields_from_value(kwargs.get("data",{}).get("fields",[]))
#self.fields["conditions"].initialize_fields_from_value(kwargs.get("data",{}).get("conditions",[]))
#import pdb; pdb.set_trace()
self.fields["fields"].initialize_fields_from_value(self.fields["fields"].widget.value_from_datadict(self.data, None, 'schema_builder-fields') or [])
#self.fields["conditions"].initialize_fields_from_value(self.fields["conditions"].widget.value_from_datadict(self.data, None, "conditions") or [])
#import pdb; pdb.set_trace()
return
#self.fields["conditions"].initialize_fields_from_value(self.fields["conditions"].widget.value_from_datadict(self.data, None, "schema_builder-conditions") or [])


@property
Expand All @@ -246,13 +187,8 @@ def helper(self):
helper.form_tag = False
return helper

class UDFForm(forms.ModelForm):
schema_builder = DynamicFormField(
prefix="schema_builder",
form_class=Foobar,
validators=[PydanticValidator(Schema)],)


class UDFForm(forms.ModelForm):
schema = forms.JSONField(
initial=Schema(fields=[]).model_dump(),
validators=[PydanticValidator(Schema)],
Expand All @@ -261,8 +197,6 @@ class UDFForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
user = kwargs.pop("user")
super().__init__(*args, **kwargs)
if schema:=self.initial.get("schema"):
self.initial["schema_builder"] = schema
if self.instance.id is None:
self.instance.creator = user

Expand Down Expand Up @@ -302,16 +236,6 @@ def clean_name(self):
raise forms.ValidationError("The UDF name must be unique for your account.")
return name

def clean(self):
#import pdb; pdb.set_trace()
# remove errors if schema has manually been changed
cleaned_data = super().clean()
if "schema" in self.changed_data:
self._errors.pop("schema_builder",None)
elif "schema_builder" in cleaned_data:
cleaned_data["schema"] = cleaned_data["schema_builder"]
return cleaned_data

@transaction.atomic
def save(self, commit=True):
verb = "Created" if self.instance.id is None else "Updated"
Expand Down Expand Up @@ -363,9 +287,6 @@ def helper(self):
),
cfl.Fieldset(
"Form Schema",
cfl.Row(
cfl.Column("schema_builder"),
),
cfl.Row(
cfl.Column("schema"),
),
Expand All @@ -383,81 +304,28 @@ def helper(self):
return helper







class UDFForm1(forms.ModelForm):
schema = forms.JSONField(
initial=Schema(fields=[]).model_dump(),
validators=[PydanticValidator(Schema)],
)
class NewUDFForm(UDFForm):
schema_builder = NewDynamicFormField(
prefix="schema_builder",
form_class=SchemaForm,
validators=[PydanticValidator(Schema)],)

def __init__(self, *args, **kwargs):
user = kwargs.pop("user")
super().__init__(*args, **kwargs)
if self.instance.id is None:
self.instance.creator = user

# filter assessment list to a subset of those available
self.fields["assessments"].queryset = (
Assessment.objects.all().user_can_view(self.instance.creator).order_by("name")
)
self.fields[
"assessments"
].help_text += (
f" Only assessments the creator ({self.instance.creator}) can view are shown."
)
if schema:=self.initial.get("schema"):
self.initial["schema_builder"] = schema

self.fields["schema"].label_append_button = {
"btn_attrs": {
"hx-indicator": "#spinner",
"id": "schema-preview-btn",
"hx-post": reverse_lazy("udf:schema_preview"),
"hx-target": "#schema-preview-fieldset",
"hx-swap": "innerHTML",
"class": "ml-2 btn btn-primary",
},
"btn_content": "Preview",
}
self.fields["schema"].help_text = (
models.UserDefinedForm.schema.field.help_text
+ "&nbsp;<a id='load-example' href='#'>Load an example</a>."
)

def clean_name(self):
# check unique_together ("creator", "name")
name = self.cleaned_data.get("name")
if (
name != self.instance.name
and self._meta.model.objects.filter(creator=self.instance.creator, name=name).exists()
):
raise forms.ValidationError("The UDF name must be unique for your account.")
return name
def clean(self):
# remove errors if schema has manually been changed
cleaned_data = super().clean()
if "schema" in self.changed_data:
self._errors.pop("schema_builder",None)
elif "schema_builder" in cleaned_data:
cleaned_data["schema"] = cleaned_data["schema_builder"]
return cleaned_data

@transaction.atomic
def save(self, commit=True):
verb = "Created" if self.instance.id is None else "Updated"
instance = super().save(commit=commit)
if commit:
create_object_log(verb, instance, None, self.instance.creator_id, "")
return instance

class Meta:
model = models.UserDefinedForm
fields = (
"name",
"description",
"schema",
"editors",
"assessments",
"published",
"deprecated",
)
widgets = {
"editors": AutocompleteSelectMultipleWidget(UserAutocomplete),
}

@property
def helper(self):
Expand Down Expand Up @@ -487,6 +355,9 @@ def helper(self):
),
cfl.Fieldset(
"Form Schema",
cfl.Row(
cfl.Column("schema_builder"),
),
cfl.Row(
cfl.Column("schema"),
),
Expand All @@ -503,8 +374,6 @@ def helper(self):
)
return helper



class SchemaPreviewForm(forms.Form):
"""Form for previewing a Dynamic Form schema."""

Expand Down
10 changes: 9 additions & 1 deletion hawc/apps/udf/views.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from django.conf import settings
from django.core.exceptions import BadRequest, PermissionDenied
from django.db.models.query import QuerySet
from django.http import HttpRequest
Expand Down Expand Up @@ -56,10 +57,13 @@ class CreateUDFView(LoginRequiredMixin, MessageMixin, CreateView):
success_url = reverse_lazy("udf:udf_list")
success_message = "Form created."

def get_form_class(self):
"""Return the form class to use in this view."""
return forms.NewUDFForm if settings.HAWC_FEATURES.ENABLE_SCHEMA_BUILDER else forms.UDFForm

def get_form_kwargs(self):
kwargs = super().get_form_kwargs()
kwargs.update(user=self.request.user)
#kwargs.update(data={"fields":["a","b","c"]})
return kwargs


Expand All @@ -70,6 +74,10 @@ class UpdateUDFView(MessageMixin, UpdateView):
success_url = reverse_lazy("udf:udf_list")
success_message = "Form updated."

def get_form_class(self):
"""Return the form class to use in this view."""
return forms.NewUDFForm if settings.HAWC_FEATURES.ENABLE_SCHEMA_BUILDER else forms.UDFForm

def get_object(self, **kw):
obj = super().get_object(**kw)
if not obj.user_can_edit(self.request.user):
Expand Down
1 change: 1 addition & 0 deletions hawc/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ class FeatureFlags(BaseModel):
ENABLE_FILTER_DOWNLOADS: bool = False
ENABLE_DOCS_LINK: bool = False
ENABLE_NEW_HERO: bool = False
ENABLE_SCHEMA_BUILDER: bool = False

@classmethod
def from_env(cls, variable) -> "FeatureFlags":
Expand Down

0 comments on commit cd8621e

Please sign in to comment.