Skip to content

refactor: no edits to existing fields #240

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
May 19, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion backend/fpbase/tests/test_end2end.py
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,8 @@ def test_fret(self):
self.browser.switch_to.active_element.send_keys(Keys.ENTER)

elem = self.browser.find_element(value="QYD")
assert float(elem.text) == donor.default_state.qy
if elem.text:
assert float(elem.text) == donor.default_state.qy

elem = self.browser.find_element(value="QYA")
WebDriverWait(self.browser, 1.5).until(lambda d: bool(elem.text))
Expand Down
55 changes: 49 additions & 6 deletions backend/proteins/forms/forms.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import re
from typing import cast

from crispy_forms.helper import FormHelper
from crispy_forms.layout import HTML, Div, Layout
from crispy_forms.layout import HTML, Div, Field, Layout
from dal import autocomplete
from django import forms
from django.forms.models import inlineformset_factory # ,BaseInlineFormSet
Expand Down Expand Up @@ -102,19 +103,45 @@
seq = SequenceField(required=False, help_text="Amino acid sequence", label="Sequence")
# reference_pmid = forms.CharField(max_length=24, label='Reference Pubmed ID',
# required=False, help_text='e.g. 23524392 (must provide either DOI or PMID)')
confirmation = forms.BooleanField(
required=True,
label=mark_safe(
"<span class='small'>I understand that I am contributing to the <em>public</em> "
"FPbase database, and confirm that I have verified the validity of the data</span>"
),
)

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# self.fields['seq'].disabled = self.instance.seq_validated
instance = getattr(self, "instance", None)
if instance and instance.pk:
if instance.seq_validated:
self.fields["seq"].widget.attrs["readonly"] = True
prot = cast("Protein | None", getattr(self, "instance", None))
if prot and prot.pk:
for attr, field in [

Check warning on line 118 in backend/proteins/forms/forms.py

View check run for this annotation

Codecov / codecov/patch

backend/proteins/forms/forms.py#L118

Added line #L118 was not covered by tests
("name", "name"),
("aliases", "aliases"),
("seq_validated", "seq"),
("primary_reference", "reference_doi"),
("ipg_id", "ipg_id"),
("cofactor", "cofactor"),
("genbank", "genbank"),
("uniprot", "uniprot"),
("pdb", "pdb"),
]:
if bool(getattr(prot, attr)):
self.fields[field].widget.attrs["readonly"] = True
for attr, field in [

Check warning on line 131 in backend/proteins/forms/forms.py

View check run for this annotation

Codecov / codecov/patch

backend/proteins/forms/forms.py#L129-L131

Added lines #L129 - L131 were not covered by tests
("agg", "agg"),
("parent_organism", "parent_organism"),
("cofactor", "cofactor"),
("switch_type", "switch_type"),
]:
if bool(getattr(prot, attr)):
self.fields[field].widget.attrs["disabled"] = True

Check warning on line 138 in backend/proteins/forms/forms.py

View check run for this annotation

Codecov / codecov/patch

backend/proteins/forms/forms.py#L137-L138

Added lines #L137 - L138 were not covered by tests

self.helper = FormHelper(self)
self.helper.form_tag = False
self.helper.error_text_inline = True
self.helper.layout = Layout(
Field("confirmation", css_class="custom-checkbox"),
Div(
Div("name", css_class="col-md-4 col-sm-12"),
Div("aliases", css_class="col-md-4 col-sm-6"),
Expand Down Expand Up @@ -241,6 +268,22 @@

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)

state = cast("State | None", getattr(self, "instance", None))
if state and state.pk:
for attr, field in [

Check warning on line 274 in backend/proteins/forms/forms.py

View check run for this annotation

Codecov / codecov/patch

backend/proteins/forms/forms.py#L274

Added line #L274 was not covered by tests
("name", "name"),
("ex_max", "ex_max"),
("em_max", "em_max"),
("ext_coeff", "ext_coeff"),
("qy", "qy"),
("pka", "pka"),
("lifetime", "lifetime"),
("maturation", "maturation"),
]:
if getattr(state, attr):
self.fields[field].widget.attrs["readonly"] = True

Check warning on line 285 in backend/proteins/forms/forms.py

View check run for this annotation

Codecov / codecov/patch

backend/proteins/forms/forms.py#L284-L285

Added lines #L284 - L285 were not covered by tests

self.helper = FormHelper()
self.helper.form_tag = False
self.helper.disable_csrf = True
Expand Down
17 changes: 11 additions & 6 deletions backend/proteins/templates/proteins/protein_form.html
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,17 @@ <h1>Update {{ object.name }}</h1>
<h1>Submit a new protein</h1>
{% endif %}

<p class='text-muted mb-4 mt-3 font-italic'>Thank you for contributing to FPbase! All contributed data will be moderated, but please double check your submission for accuracy. The only strictly required fields are name and DOI.</p></div>
<div class="small alert alert-info fade show mt-4" role="alert">
<div class='h5 text-info'><i class="fas fa-eye mr-2 "></i> <strong>Thank you for contributing to FPbase!</strong></div>
<p>You are about to suggest changes for the <em>public</em> database (not your private collection).</p>

{% if not object %}
<p>The only strictly required fields are name and DOI. You may enter some fields now, and update with additional info later.</p>
{% endif %}

<p><strong>Once submitted, data cannot be edited or deleted without contacting FPbase. Please double check your submission for accuracy</strong></p>
<p>If you think there is an error or wish to make a correction, please <a href="{% url 'contact' %}">contact us</a></p>
</div>

{% if states.non_form_errors %}
<h5>There were errors in the form...</h5>
Expand Down Expand Up @@ -55,10 +65,6 @@ <h5>There were errors in the form...</h5>
</div>
{% endfor %} <!-- end formset loop -->

{% if protein.seq_validated or protein.lineage.children.exists %}
<p class='small text-muted font-italic'>Note: Once moderated and approved, sequence and lineage fields are uneditable. If you think there is an error in either, please <a href="{% url 'contact' %}">contact us</a></p>
{% endif %}

<div class="small alert alert-info alert-dismissible fade show mt-4" role="alert">
<div class='h5 text-info'><i class="fas fa-info-circle mr-2 "></i> <strong>States</strong></div>
FPbase uses <strong>states</strong> to encapsulate the various fluorescence characteristics that a protein can have. Even a constitutively active "basic" protein is given a (single) state. Rather than directly specifying the "type" of protein, FPbase uses the number of states and transitions between them to determine the type of fluorescent protein.<br><br>
Expand All @@ -69,7 +75,6 @@ <h5>There were errors in the form...</h5>
</button>
</div>


{{ states.management_form|crispy }}
{% for stateform in states.forms %}
{% for hidden in stateform.hidden_fields %}
Expand Down
14 changes: 8 additions & 6 deletions backend/proteins/tests/test_forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ def setUp(self):

def test_clean_proteinname_success(self):
# Instantiate the form with a new protein
form = ProteinForm({"name": "New Protein"})
form = ProteinForm({"name": "New Protein", "confirmation": True})
# Run is_valid() to trigger the validation
valid = form.is_valid()
self.assertTrue(valid, "Form is not valid")
Expand All @@ -28,7 +28,7 @@ def test_clean_proteinname_success(self):

def test_clean_proteinname_exists(self):
# Instantiate the form with existing protein name
form = ProteinForm({"name": "Test Protein"})
form = ProteinForm({"name": "Test Protein", "confirmation": True})
# Run is_valid() to trigger the validation, which is going to fail
# because the name is already taken
valid = form.is_valid()
Expand All @@ -39,21 +39,21 @@ def test_clean_proteinname_exists(self):
self.assertTrue("name" in form.errors)

def test_clean_proteinseq_success(self):
form = ProteinForm({"name": "New Protein", "seq": "ghilkmfpstwy varndceq"})
form = ProteinForm({"name": "New Protein", "seq": "ghilkmfpstwy varndceq", "confirmation": True})
valid = form.is_valid()
self.assertTrue(valid, "Form is not valid")
seq = form.clean_seq()
self.assertEqual("GHILKMFPSTWYVARNDCEQ", seq)

def test_clean_proteinseq_exists(self):
form = ProteinForm({"name": "New Protein", "seq": "ARNDCEQGHILKMFPSTWYV"})
form = ProteinForm({"name": "New Protein", "seq": "ARNDCEQGHILKMFPSTWYV", "confirmation": True})
valid = form.is_valid()
self.assertFalse(valid)
self.assertTrue(len(form.errors) == 1)
self.assertTrue("seq" in form.errors)

def test_clean_proteinseq_invalid(self):
form = ProteinForm({"name": "New Protein", "seq": "ARNDCEQGHILKMBZXFPSTWYV"})
form = ProteinForm({"name": "New Protein", "seq": "ARNDCEQGHILKMBZXFPSTWYV", "confirmation": True})
valid = form.is_valid()
self.assertFalse(valid)
self.assertTrue(len(form.errors) == 1)
Expand All @@ -65,7 +65,7 @@ def test_clean_refdoi_success(self):
"https://doi.org/10.1038/nmeth.2413",
"10.1038/nmeth.2413",
):
form = ProteinForm({"name": "New Protein", "reference_doi": doi})
form = ProteinForm({"name": "New Protein", "reference_doi": doi, "confirmation": True})
valid = form.is_valid()
self.assertTrue(valid, "Form is not valid")
self.assertEqual("10.1038/nmeth.2413", form.cleaned_data["reference_doi"])
Expand All @@ -75,6 +75,7 @@ def test_clean_refdoi_failure(self):
{
"name": "New Protein",
"reference_doi": "30.1038/nmeth.2413", # Invalid DOI
"confirmation": True,
}
)
valid = form.is_valid()
Expand All @@ -90,6 +91,7 @@ def test_ids_already_exist(self):
"genbank": "NC_000001.10",
"uniprot": "P12345",
"pdb": ["4HHB"],
"confirmation": True,
}
)
valid = form.is_valid()
Expand Down
1 change: 1 addition & 0 deletions backend/proteins/tests/test_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ def test_protein_submit(self):
"states-0-name": "default",
"states-0-ex_max": 488,
"states-0-em_max": 525,
"confirmation": True,
}
| INLINE_FORMSET,
)
Expand Down
Loading