Skip to content
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
2 changes: 1 addition & 1 deletion backend/proteins/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,7 @@ class CameraAdmin(SpectrumOwner, VersionAdmin):
class SpectrumAdminForm(forms.ModelForm):
"""Custom form to handle the Spectrum.data property in admin."""

data = forms.CharField(
data = forms.CharField( # type: ignore[assignment]
widget=forms.Textarea(attrs={"rows": 10, "cols": 80}),
required=False,
help_text="Spectrum data as [[wavelength, value], ...] pairs",
Expand Down
4 changes: 2 additions & 2 deletions backend/proteins/views/protein.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@
def check_switch_type(object, request):
suggested = suggested_switch_type(object)
if suggested and object.switch_type != suggested:
disp = dict(Protein.SwitchingChoices).get(suggested).lower()
disp = dict(Protein.SwitchingChoices.choices).get(suggested).lower()
actual = object.get_switch_type_display().lower()
msg = (
"<i class='fa fa-exclamation-circle me-2'></i><strong>Warning:</strong> "
Expand Down Expand Up @@ -683,7 +683,7 @@ def problems_inconsistencies(request):
for prot in p:
suggestion = suggested_switch_type(prot)
if (prot.switch_type or suggestion) and (prot.switch_type != suggestion):
bad_switch.append((prot, dict(Protein.SwitchingChoices).get(suggestion)))
bad_switch.append((prot, dict(Protein.SwitchingChoices.choices).get(suggestion)))

return render(
request,
Expand Down
53 changes: 53 additions & 0 deletions backend/tests/test_proteins/test_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -465,3 +465,56 @@ def test_scope_report_json_full_workflow_integration(self, client):
# Verify fluors dict contains our fluorophores
assert state.slug in data["fluors"], f"State {state.slug} missing from fluors"
assert dye_state.slug in data["fluors"], f"DyeState {dye_state.slug} missing from fluors"


class TestProteinSubmitMultiState(TestCase):
"""Regression tests for FPBASE-6H7: protein submission with multiple states."""

def setUp(self) -> None:
self.admin_user = User.objects.create_superuser(
username="admin", email="[email protected]", password="password"
)

def test_protein_submit_with_two_states(self):
"""Test protein submission with multiple states doesn't raise ValueError.

Regression test for FPBASE-6H7: When a protein has 2+ states but no transitions,
the check_switch_type function is called and previously crashed with ValueError
due to incorrect dict(Protein.SwitchingChoices) usage.
"""
self.client.login(username="admin", password="password")
initial_count = Protein.objects.count()

# Submit protein with 2 states (no transitions) - triggers check_switch_type
# with suggested switch_type='o' (OTHER)
response = self.client.post(
reverse("proteins:submit"),
data={
"name": "MultiStateProtein",
"reference_doi": "10.1038/nmeth.2413",
# Two states, no transitions = triggers 'OTHER' suggestion
"states-0-name": "on",
"states-0-ex_max": 488,
"states-0-em_max": 525,
"states-1-name": "off",
"states-1-ex_max": 400,
"states-1-em_max": 450,
"confirmation": True,
"lineage-TOTAL_FORMS": 1,
"lineage-INITIAL_FORMS": 0,
"lineage-MIN_NUM_FORMS": 0,
"lineage-MAX_NUM_FORMS": 1,
"states-TOTAL_FORMS": 2,
"states-INITIAL_FORMS": 0,
"states-MIN_NUM_FORMS": 0,
"states-MAX_NUM_FORMS": 1000,
},
)

# Should redirect to protein detail (success), not error
assert response.status_code == 302
assert Protein.objects.count() == initial_count + 1

new_prot = cast("Protein", Protein.objects.get(name="MultiStateProtein"))
assert response.url == new_prot.get_absolute_url()
assert new_prot.states.count() == 2
Loading