@@ -1366,10 +1366,51 @@ class UpdateStatusForm(NimbusChangeLogFormMixin, forms.ModelForm):
13661366 publish_status = None
13671367 is_paused = None
13681368
1369+ required_status = None
1370+ required_status_next = None
1371+ required_publish_status = None
1372+ required_is_paused = None
1373+
13691374 class Meta :
13701375 model = NimbusExperiment
13711376 fields = []
13721377
1378+ def clean (self ):
1379+ cleaned_data = super ().clean ()
1380+
1381+ required_state = (
1382+ self .required_status ,
1383+ self .required_status_next ,
1384+ self .required_publish_status ,
1385+ self .required_is_paused ,
1386+ )
1387+ current_state = (
1388+ self .instance .status ,
1389+ self .instance .status_next ,
1390+ self .instance .publish_status ,
1391+ self .instance .is_paused ,
1392+ )
1393+
1394+ state_mismatch = (
1395+ self .required_status != self .instance .status
1396+ or self .required_status_next != self .instance .status_next
1397+ or self .required_publish_status != self .instance .publish_status
1398+ or (
1399+ self .required_is_paused is not None
1400+ and self .required_is_paused != self .instance .is_paused
1401+ )
1402+ )
1403+
1404+ if state_mismatch :
1405+ raise forms .ValidationError (
1406+ NimbusUIConstants .ERROR_INVALID_STATE_TRANSITION .format (
1407+ required_state = required_state ,
1408+ current_state = current_state ,
1409+ )
1410+ )
1411+
1412+ return cleaned_data
1413+
13731414 @transaction .atomic
13741415 def save (self , commit = True ):
13751416 self .instance .status = self .status
@@ -1401,9 +1442,15 @@ def save(self, commit=True):
14011442
14021443
14031444class DraftToPreviewForm (UpdateStatusForm ):
1445+ required_status = NimbusExperiment .Status .DRAFT
1446+ required_status_next = None
1447+ required_publish_status = NimbusExperiment .PublishStatus .IDLE
1448+ required_is_paused = False
1449+
14041450 status = NimbusExperiment .Status .PREVIEW
1405- status_next = NimbusExperiment . Status . PREVIEW
1451+ status_next = None
14061452 publish_status = NimbusExperiment .PublishStatus .IDLE
1453+ is_paused = False
14071454
14081455 def get_changelog_message (self ):
14091456 return f"{ self .request .user } launched experiment to Preview"
@@ -1418,29 +1465,49 @@ def save(self, commit=True):
14181465
14191466
14201467class DraftToReviewForm (SlackNotificationMixin , UpdateStatusForm ):
1468+ required_status = NimbusExperiment .Status .DRAFT
1469+ required_status_next = None
1470+ required_publish_status = NimbusExperiment .PublishStatus .IDLE
1471+ required_is_paused = False
1472+
14211473 status = NimbusExperiment .Status .DRAFT
14221474 status_next = NimbusExperiment .Status .LIVE
14231475 publish_status = NimbusExperiment .PublishStatus .REVIEW
1476+ is_paused = False
1477+
14241478 slack_action = NimbusConstants .SLACK_ACTION_LAUNCH_REQUEST
14251479
14261480 def get_changelog_message (self ):
14271481 return f"{ self .request .user } requested launch without Preview"
14281482
14291483
14301484class PreviewToReviewForm (SlackNotificationMixin , UpdateStatusForm ):
1485+ required_status = NimbusExperiment .Status .PREVIEW
1486+ required_status_next = None
1487+ required_publish_status = NimbusExperiment .PublishStatus .IDLE
1488+ required_is_paused = False
1489+
14311490 status = NimbusExperiment .Status .DRAFT
14321491 status_next = NimbusExperiment .Status .LIVE
14331492 publish_status = NimbusExperiment .PublishStatus .REVIEW
1493+ is_paused = False
1494+
14341495 slack_action = NimbusConstants .SLACK_ACTION_LAUNCH_REQUEST
14351496
14361497 def get_changelog_message (self ):
14371498 return f"{ self .request .user } requested launch from Preview"
14381499
14391500
14401501class PreviewToDraftForm (UpdateStatusForm ):
1502+ required_status = NimbusExperiment .Status .PREVIEW
1503+ required_status_next = None
1504+ required_publish_status = NimbusExperiment .PublishStatus .IDLE
1505+ required_is_paused = False
1506+
14411507 status = NimbusExperiment .Status .DRAFT
1442- status_next = NimbusExperiment . Status . DRAFT
1508+ status_next = None
14431509 publish_status = NimbusExperiment .PublishStatus .IDLE
1510+ is_paused = False
14441511
14451512 def get_changelog_message (self ):
14461513 return f"{ self .request .user } moved the experiment back to Draft"
@@ -1453,9 +1520,16 @@ def save(self, commit=True):
14531520
14541521
14551522class ReviewToDraftForm (UpdateStatusForm ):
1523+ required_status = NimbusExperiment .Status .DRAFT
1524+ required_status_next = NimbusExperiment .Status .LIVE
1525+ required_publish_status = NimbusExperiment .PublishStatus .REVIEW
1526+ required_is_paused = False
1527+
14561528 status = NimbusExperiment .Status .DRAFT
1457- status_next = NimbusExperiment . Status . DRAFT
1529+ status_next = None
14581530 publish_status = NimbusExperiment .PublishStatus .IDLE
1531+ is_paused = False
1532+
14591533 changelog_message = forms .CharField (
14601534 required = False , label = "Changelog Message" , max_length = 1000
14611535 )
@@ -1474,9 +1548,15 @@ def get_changelog_message(self):
14741548
14751549
14761550class ReviewToApproveForm (UpdateStatusForm ):
1551+ required_status = NimbusExperiment .Status .DRAFT
1552+ required_status_next = NimbusExperiment .Status .LIVE
1553+ required_publish_status = NimbusExperiment .PublishStatus .REVIEW
1554+ required_is_paused = False
1555+
14771556 status = NimbusExperiment .Status .DRAFT
14781557 status_next = NimbusExperiment .Status .LIVE
14791558 publish_status = NimbusExperiment .PublishStatus .APPROVED
1559+ is_paused = False
14801560
14811561 def get_changelog_message (self ):
14821562 return f"{ self .request .user } approved the review."
@@ -1493,46 +1573,43 @@ def save(self, commit=True):
14931573
14941574
14951575class LiveToEndEnrollmentForm (SlackNotificationMixin , UpdateStatusForm ):
1576+ required_status = NimbusExperiment .Status .LIVE
1577+ required_status_next = None
1578+ required_publish_status = NimbusExperiment .PublishStatus .IDLE
1579+ required_is_paused = False
1580+
14961581 status = NimbusExperiment .Status .LIVE
14971582 status_next = NimbusExperiment .Status .LIVE
14981583 publish_status = NimbusExperiment .PublishStatus .REVIEW
14991584 is_paused = True
1585+
15001586 slack_action = NimbusConstants .SLACK_ACTION_END_ENROLLMENT_REQUEST
15011587
15021588 def clean (self ):
1589+ cleaned_data = super ().clean ()
1590+
15031591 if self .instance and self .instance .is_rollout_dirty :
15041592 raise forms .ValidationError (NimbusExperiment .ERROR_CANNOT_PAUSE_UNPUBLISHED )
15051593
1506- if not self .instance .should_show_end_enrollment :
1507- if (
1508- self .instance .is_draft
1509- or self .instance .is_preview
1510- or self .instance .is_complete
1511- ):
1512- raise forms .ValidationError (NimbusExperiment .ERROR_CANNOT_PAUSE_NOT_LIVE )
1513-
1514- if not self .instance .is_enrolling :
1515- raise forms .ValidationError (NimbusExperiment .ERROR_CANNOT_PAUSE_PAUSED )
1594+ if self .instance .is_rollout and not self .instance .is_firefox_labs_opt_in :
1595+ raise forms .ValidationError (NimbusExperiment .ERROR_CANNOT_PAUSE_ROLLOUT )
15161596
1517- if self .instance .is_rollout and not self .instance .is_firefox_labs_opt_in :
1518- raise forms .ValidationError (NimbusExperiment .ERROR_CANNOT_PAUSE_ROLLOUT )
1519-
1520- # The conditions for Experiment.should_show_enrollment have changed
1521- # but this function has become out of sync.
1522- raise forms .ValidationError (
1523- NimbusExperiment .ERROR_CANNOT_PAUSE_INVALID
1524- ) # pragma: no cover
1525-
1526- return super ().clean ()
1597+ return cleaned_data
15271598
15281599 def get_changelog_message (self ):
15291600 return f"{ self .request .user } requested review to end enrollment"
15301601
15311602
15321603class ApproveEndEnrollmentForm (UpdateStatusForm ):
1604+ required_status = NimbusExperiment .Status .LIVE
1605+ required_status_next = NimbusExperiment .Status .LIVE
1606+ required_publish_status = NimbusExperiment .PublishStatus .REVIEW
1607+ required_is_paused = True
1608+
15331609 status = NimbusExperiment .Status .LIVE
15341610 status_next = NimbusExperiment .Status .LIVE
15351611 publish_status = NimbusExperiment .PublishStatus .APPROVED
1612+ is_paused = True
15361613
15371614 def get_changelog_message (self ):
15381615 return f"{ self .request .user } approved the end enrollment request"
@@ -1547,19 +1624,32 @@ def save(self, commit=True):
15471624
15481625
15491626class LiveToCompleteForm (SlackNotificationMixin , UpdateStatusForm ):
1627+ required_status = NimbusExperiment .Status .LIVE
1628+ required_status_next = None
1629+ required_publish_status = NimbusExperiment .PublishStatus .IDLE
1630+ required_is_paused = False
1631+
15501632 status = NimbusExperiment .Status .LIVE
15511633 status_next = NimbusExperiment .Status .COMPLETE
15521634 publish_status = NimbusExperiment .PublishStatus .REVIEW
1635+ is_paused = False
1636+
15531637 slack_action = NimbusConstants .SLACK_ACTION_END_EXPERIMENT_REQUEST
15541638
15551639 def get_changelog_message (self ):
15561640 return f"{ self .request .user } requested review to end experiment"
15571641
15581642
15591643class ApproveEndExperimentForm (UpdateStatusForm ):
1644+ required_status = NimbusExperiment .Status .LIVE
1645+ required_status_next = NimbusExperiment .Status .COMPLETE
1646+ required_publish_status = NimbusExperiment .PublishStatus .REVIEW
1647+ required_is_paused = None
1648+
15601649 status = NimbusExperiment .Status .LIVE
15611650 status_next = NimbusExperiment .Status .COMPLETE
15621651 publish_status = NimbusExperiment .PublishStatus .APPROVED
1652+ is_paused = True
15631653
15641654 def get_changelog_message (self ):
15651655 return f"{ self .request .user } approved the end experiment request"
@@ -1574,10 +1664,16 @@ def save(self, commit=True):
15741664
15751665
15761666class CancelEndEnrollmentForm (UpdateStatusForm ):
1667+ required_status = NimbusExperiment .Status .LIVE
1668+ required_status_next = NimbusExperiment .Status .LIVE
1669+ required_publish_status = NimbusExperiment .PublishStatus .REVIEW
1670+ required_is_paused = True
1671+
15771672 status = NimbusExperiment .Status .LIVE
15781673 status_next = None
15791674 publish_status = NimbusExperiment .PublishStatus .IDLE
15801675 is_paused = False
1676+
15811677 changelog_message = forms .CharField (
15821678 required = False , label = "Changelog Message" , max_length = 1000
15831679 )
@@ -1596,9 +1692,15 @@ def get_changelog_message(self):
15961692
15971693
15981694class CancelEndExperimentForm (UpdateStatusForm ):
1695+ required_status = NimbusExperiment .Status .LIVE
1696+ required_status_next = NimbusExperiment .Status .COMPLETE
1697+ required_publish_status = NimbusExperiment .PublishStatus .REVIEW
1698+ required_is_paused = None
1699+
15991700 status = NimbusExperiment .Status .LIVE
16001701 status_next = None
16011702 publish_status = NimbusExperiment .PublishStatus .IDLE
1703+
16021704 changelog_message = forms .CharField (
16031705 required = False , label = "Changelog Message" , max_length = 1000
16041706 )
@@ -1621,19 +1723,33 @@ def get_changelog_message(self):
16211723
16221724
16231725class LiveToUpdateRolloutForm (SlackNotificationMixin , UpdateStatusForm ):
1726+ required_status = NimbusExperiment .Status .LIVE
1727+ required_status_next = None
1728+ required_publish_status = NimbusExperiment .PublishStatus .IDLE
1729+ required_is_paused = False
1730+
16241731 status = NimbusExperiment .Status .LIVE
16251732 status_next = NimbusExperiment .Status .LIVE
16261733 publish_status = NimbusExperiment .PublishStatus .REVIEW
1734+ is_paused = False
1735+
16271736 slack_action = NimbusConstants .SLACK_ACTION_UPDATE_REQUEST
16281737
16291738 def get_changelog_message (self ):
16301739 return f"{ self .request .user } requested review to update Audience"
16311740
16321741
16331742class CancelUpdateRolloutForm (UpdateStatusForm ):
1743+ required_status = NimbusExperiment .Status .LIVE
1744+ required_status_next = NimbusExperiment .Status .LIVE
1745+ required_publish_status = NimbusExperiment .PublishStatus .REVIEW
1746+ required_is_paused = False
1747+
16341748 status = NimbusExperiment .Status .LIVE
16351749 status_next = None
16361750 publish_status = NimbusExperiment .PublishStatus .IDLE
1751+ is_paused = False
1752+
16371753 changelog_message = forms .CharField (
16381754 required = False , label = "Changelog Message" , max_length = 1000
16391755 )
@@ -1652,9 +1768,15 @@ def get_changelog_message(self):
16521768
16531769
16541770class ApproveUpdateRolloutForm (UpdateStatusForm ):
1771+ required_status = NimbusExperiment .Status .LIVE
1772+ required_status_next = NimbusExperiment .Status .LIVE
1773+ required_publish_status = NimbusExperiment .PublishStatus .REVIEW
1774+ required_is_paused = False
1775+
16551776 status = NimbusExperiment .Status .LIVE
16561777 status_next = NimbusExperiment .Status .LIVE
16571778 publish_status = NimbusExperiment .PublishStatus .APPROVED
1779+ is_paused = False
16581780
16591781 def get_changelog_message (self ):
16601782 return f"{ self .request .user } approved the update review request"
0 commit comments