Пример #1
0
 def test_experiment_firefox_channel_sort_does_sorting(self):
     ExperimentFactory.create(firefox_channel=Experiment.CHANNEL_NIGHTLY)
     ExperimentFactory.create(firefox_channel=Experiment.CHANNEL_RELEASE)
     ExperimentFactory.create(firefox_channel=Experiment.CHANNEL_BETA)
     ExperimentFactory.create(firefox_channel="")
     sorted_experiments = (Experiment.objects.annotate(
         firefox_channel_sort=Experiment.firefox_channel_sort()).order_by(
             "firefox_channel_sort").values("firefox_channel_sort"))
     self.assertEqual(
         [r["firefox_channel_sort"] for r in sorted_experiments],
         [
             Experiment.CHANNEL_UNSET_ORDER,
             Experiment.CHANNEL_NIGHTLY_ORDER,
             Experiment.CHANNEL_BETA_ORDER,
             Experiment.CHANNEL_RELEASE_ORDER,
         ],
     )
Пример #2
0
    def test_changelog_values(self):
        experiment = Experiment()
        experiment.save()

        ds_url = "{base}DS-123".format(base=settings.DS_ISSUE_HOST)
        bug_url = "{base}show_bug.cgi?id=123".format(
            base=settings.BUGZILLA_HOST)
        related_exp = ExperimentFactory.create()
        project = ProjectFactory.create()

        data = {
            "type": Experiment.TYPE_PREF,
            "name": "A new experiment!",
            "short_description": "Let us learn new things",
            "data_science_issue_url": ds_url,
            "owner": self.user.id,
            "analysis_owner": self.user.id,
            "engineering_owner": "Lisa the Engineer",
            "public_description": "Let us learn new public things",
            "related_to": [related_exp],
            "feature_bugzilla_url": bug_url,
            "related_work": "Designs: https://www.example.com/myproject/",
            "projects": [project],
        }

        form = ExperimentOverviewForm(request=self.request,
                                      data=data,
                                      instance=experiment)
        self.assertTrue(form.is_valid())
        experiment = form.save()
        latest_changes = experiment.changes.latest()

        expected_data = {
            "analysis_owner": {
                "display_name": "Data Science Owner",
                "new_value": experiment.analysis_owner.id,
                "old_value": None,
            },
            "data_science_issue_url": {
                "display_name": "Data Science Issue URL",
                "new_value": "https://jira.example.com/browse/DS-123",
                "old_value": None,
            },
            "engineering_owner": {
                "display_name": "Engineering Owner",
                "new_value": "Lisa the Engineer",
                "old_value": None,
            },
            "feature_bugzilla_url": {
                "display_name": "Feature Bugzilla URL",
                "new_value":
                "https://bugzilla.allizom.org/show_bug.cgi?id=123",
                "old_value": None,
            },
            "name": {
                "display_name": "Public Name",
                "new_value": "A new experiment!",
                "old_value": None,
            },
            "owner": {
                "display_name": "Delivery Owner",
                "new_value": experiment.owner.id,
                "old_value": None,
            },
            "projects": {
                "display_name": "Related Projects",
                "new_value": [{
                    "slug": project.slug
                }],
                "old_value": None,
            },
            "public_description": {
                "display_name": "Public Description",
                "new_value": "Let us learn new public things",
                "old_value": None,
            },
            "related_to": {
                "display_name": "Related Deliveries",
                "new_value": [related_exp.id],
                "old_value": None,
            },
            "related_work": {
                "display_name": "Related Work URLs",
                "new_value": "Designs: https://www.example.com/myproject/",
                "old_value": None,
            },
            "short_description": {
                "display_name": "Internal Description",
                "new_value": "Let us learn new things",
                "old_value": None,
            },
        }
        self.maxDiff = None
        self.assertEqual(expected_data, latest_changes.changed_values)
Пример #3
0
 def get_queryset(self):
     qs = super().get_queryset()
     qs = qs.annotate(
         firefox_channel_sort=Experiment.firefox_channel_sort())
     return qs
Пример #4
0
class ExperimentOverviewForm(ChangeLogMixin, forms.ModelForm):

    type = forms.ChoiceField(
        label="Type",
        choices=Experiment.FEATURE_TYPE_CHOICES(),
        help_text=Experiment.TYPE_HELP_TEXT,
    )
    owner = forms.ModelChoiceField(
        required=True,
        label="Delivery Owner",
        help_text=Experiment.OWNER_HELP_TEXT,
        queryset=get_user_model().objects.all().order_by("email"),
        # This one forces the <select> widget to not include a blank
        # option which would otherwise be included because the model field
        # is nullable.
        empty_label=None,
    )
    name = forms.CharField(
        required=True, label="Public Name", help_text=Experiment.PUBLIC_NAME_HELP_TEXT
    )
    slug = forms.CharField(required=False, widget=forms.HiddenInput())
    public_description = forms.CharField(
        required=True,
        label="Public Description",
        help_text=Experiment.PUBLIC_DESCRIPTION_HELP_TEXT,
        max_length=1024,
        widget=forms.Textarea(attrs={"rows": 3}),
    )
    short_description = forms.CharField(
        required=True,
        label="Internal Description",
        help_text=Experiment.SHORT_DESCRIPTION_HELP_TEXT,
        widget=forms.Textarea(attrs={"rows": 3}),
    )
    data_science_issue_url = DSIssueURLField(
        required=False,
        label="Data Science Issue URL",
        help_text=Experiment.DATA_SCIENCE_ISSUE_HELP_TEXT,
    )
    engineering_owner = forms.CharField(
        required=False,
        label="Engineering Owner",
        help_text=Experiment.ENGINEERING_OWNER_HELP_TEXT,
    )
    analysis_owner = forms.ModelChoiceField(
        required=False,
        label="Data Science Owner",
        help_text=Experiment.ANALYSIS_OWNER_HELP_TEXT,
        queryset=get_user_model().objects.all().order_by("email"),
        # This one forces the <select> widget to not include a blank
        # option which would otherwise be included because the model field
        # is nullable.
        empty_label="Data Science Owner",
    )
    feature_bugzilla_url = BugzillaURLField(
        required=False,
        label="Feature Bugzilla URL",
        help_text=Experiment.FEATURE_BUGZILLA_HELP_TEXT,
    )
    related_to = forms.ModelMultipleChoiceField(
        label="Related Deliveries",
        required=False,
        help_text="Is this related to a previously run delivery?",
        queryset=Experiment.objects.all(),
    )
    projects = forms.ModelMultipleChoiceField(
        required=False,
        label="Projects",
        help_text=(
            """Is this delivery related to a specific project?
         Ask in #ask-experimenter on Slack if you need to add a new project"""
        ),
        queryset=Project.objects.all(),
    )

    class Meta:
        model = Experiment
        fields = [
            "type",
            "owner",
            "name",
            "slug",
            "public_description",
            "short_description",
            "data_science_issue_url",
            "analysis_owner",
            "engineering_owner",
            "feature_bugzilla_url",
            "related_to",
            "projects",
        ]

    related_to.widget.attrs.update({"data-live-search": "true"})

    def clean_name(self):
        name = self.cleaned_data["name"]
        slug = slugify(name)

        if not slug:
            raise forms.ValidationError(
                "This name must include non-punctuation characters."
            )

        if (
            self.instance.pk is None
            and slug
            and self.Meta.model.objects.filter(slug=slug).exists()
        ):
            raise forms.ValidationError("This name is already in use.")

        return name

    def clean(self):
        cleaned_data = super().clean()

        if self.instance.slug:
            del cleaned_data["slug"]
        else:
            name = cleaned_data.get("name")
            cleaned_data["slug"] = slugify(name)

        if cleaned_data["type"] != ExperimentConstants.TYPE_ROLLOUT:
            required_msg = "This field is required."
            required_fields = (
                "data_science_issue_url",
                "public_description",
            )
            for required_field in required_fields:
                if (
                    not cleaned_data.get(required_field)
                    and required_field not in self._errors
                ):
                    self._errors[required_field] = [required_msg]

        return cleaned_data

    def save(self, *args, **kwargs):
        created = not self.instance.id
        experiment = super().save(*args, **kwargs)

        if created and experiment.is_message_experiment:
            experiment.locales.add(
                *Locale.objects.filter(code__in=experiment.MESSAGE_DEFAULT_LOCALES)
            )
            experiment.countries.add(
                *Country.objects.filter(code__in=experiment.MESSAGE_DEFAULT_COUNTRIES)
            )

        return experiment