feat(nimbus): Subscribe/Unsubscribe to experiment on new summary page (#11639)

Because

- We want to add functionality on the new summary page so that we can
subscribe to the experiment

This commit

- Subscribe/Unsubscribe button option on the summary page

Fixes #11611
<img width="1354" alt="Screenshot 2024-10-29 at 1 10 13 PM"
src="https://github.com/user-attachments/assets/6e3435d2-3355-4b2e-ba42-01a439a90cfe">
<img width="1354" alt="Screenshot 2024-10-29 at 1 10 07 PM"
src="https://github.com/user-attachments/assets/4b1246b9-e652-4ba3-b9f7-508842e7545d">
<img width="1354" alt="Screenshot 2024-10-29 at 1 04 16 PM"
src="https://github.com/user-attachments/assets/8639fce2-ad06-45a0-b83f-5a6f55b2d931">
This commit is contained in:
Yashika Khurana 2024-11-15 13:06:46 -08:00 коммит произвёл GitHub
Родитель a277789cf8
Коммит cabee0783b
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: B5690EEEBB952194
7 изменённых файлов: 158 добавлений и 9 удалений

Просмотреть файл

@ -191,3 +191,31 @@ class MetricsForm(NimbusChangeLogFormMixin, forms.ModelForm):
def get_changelog_message(self):
return f"{self.request.user} updated metrics"
class SubscribeForm(NimbusChangeLogFormMixin, forms.ModelForm):
class Meta:
model = NimbusExperiment
fields = []
def save(self, commit=True):
experiment = super().save(commit=commit)
experiment.subscribers.add(self.request.user)
return experiment
def get_changelog_message(self):
return f"{self.request.user} added subscriber"
class UnsubscribeForm(NimbusChangeLogFormMixin, forms.ModelForm):
class Meta:
model = NimbusExperiment
fields = []
def save(self, commit=True):
experiment = super().save(commit=commit)
experiment.subscribers.remove(self.request.user)
return experiment
def get_changelog_message(self):
return f"{self.request.user} removed subscriber"

Просмотреть файл

@ -93,22 +93,16 @@
</tr>
<tr>
<th>Team projects</th>
<td>
<td colspan="2">
{% for project in experiment.projects.all %}
<p>{{ project }}</p>
{% empty %}
<span class="text-danger">Not set</span>
{% endfor %}
</td>
<th>Subscribers</th>
<td>
{% for subscriber in experiment.subscribers.all %}
<p>{{ subscriber }}</p>
{% empty %}
<span class="text-danger">Not set</span>
{% endfor %}
</td>
</tr>
{% include 'nimbus_experiments/subscribers_list.html' %}
</tbody>
</table>
</div>

Просмотреть файл

@ -0,0 +1,35 @@
<tr class="subscribers-section">
<th>Subscribers</th>
<td id="subscribers-list"
colspan="2"
style="max-height: 150px;
overflow-y: auto;
overflow-x: hidden">
{% for subscriber in experiment.subscribers.all %}
<p>{{ subscriber.email }}</p>
{% empty %}
<span class="text-danger">Not Set</span>
{% endfor %}
</td>
<td style="text-align: right;">
{% if request.user in experiment.subscribers.all %}
<form method="post"
action="{% url 'nimbus-new-unsubscribe' slug=experiment.slug %}"
hx-post="{% url 'nimbus-new-unsubscribe' slug=experiment.slug %}"
hx-target=".subscribers-section"
hx-swap="outerHTML">
{% csrf_token %}
<button type="submit" class="btn btn-danger">Unsubscribe</button>
</form>
{% else %}
<form method="post"
action="{% url 'nimbus-new-subscribe' slug=experiment.slug %}"
hx-post="{% url 'nimbus-new-subscribe' slug=experiment.slug %}"
hx-target=".subscribers-section"
hx-swap="outerHTML">
{% csrf_token %}
<button type="submit" class="btn btn-success">Subscribe</button>
</form>
{% endif %}
</td>
</tr>

Просмотреть файл

@ -9,7 +9,9 @@ from experimenter.nimbus_ui_new.forms import (
NimbusExperimentCreateForm,
QAStatusForm,
SignoffForm,
SubscribeForm,
TakeawaysForm,
UnsubscribeForm,
)
from experimenter.openidc.tests.factories import UserFactory
from experimenter.outcomes import Outcomes
@ -275,3 +277,34 @@ class TestMetricsForm(RequestFormTestCase):
self.assertIn("primary_outcomes", form.errors)
self.assertIn("secondary_outcomes", form.errors)
self.assertIn("segments", form.errors)
class SubscriptionFormTests(RequestFormTestCase):
def setUp(self):
super().setUp()
self.experiment = NimbusExperimentFactory.create(
name="Test Experiment",
owner=self.user,
qa_signoff=False,
vp_signoff=False,
legal_signoff=False,
)
def test_subscribe_form_adds_subscriber(self):
form = SubscribeForm(instance=self.experiment, data={}, request=self.request)
self.assertTrue(form.is_valid())
form.save()
self.assertIn(self.request.user, self.experiment.subscribers.all())
changelog = self.experiment.changes.get()
self.assertEqual(changelog.changed_by, self.user)
self.assertIn("dev@example.com added subscriber", changelog.message)
def test_unsubscribe_form_removes_subscriber(self):
self.experiment.subscribers.add(self.request.user)
form = UnsubscribeForm(instance=self.experiment, data={}, request=self.request)
self.assertTrue(form.is_valid())
form.save()
self.assertNotIn(self.request.user, self.experiment.subscribers.all())
changelog = self.experiment.changes.get()
self.assertEqual(changelog.changed_by, self.user)
self.assertIn("dev@example.com removed subscriber", changelog.message)

Просмотреть файл

@ -1090,6 +1090,33 @@ class NimbusExperimentDetailViewTest(AuthTestCase):
self.assertTrue(self.experiment.vp_signoff)
self.assertTrue(self.experiment.legal_signoff)
def test_subscribe_to_experiment(self):
self.assertNotIn(self.user, self.experiment.subscribers.all())
response = self.client.post(
reverse("nimbus-new-subscribe", kwargs={"slug": self.experiment.slug})
)
self.experiment.refresh_from_db()
self.assertIn(self.user, self.experiment.subscribers.all())
self.assertEqual(response.status_code, 200)
def test_unsubscribe_from_experiment(self):
self.experiment.subscribers.add(self.user)
self.experiment.save()
self.assertIn(self.user, self.experiment.subscribers.all())
response = self.client.post(
reverse("nimbus-new-unsubscribe", kwargs={"slug": self.experiment.slug})
)
self.experiment.refresh_from_db()
self.assertNotIn(self.user, self.experiment.subscribers.all())
self.assertEqual(response.status_code, 200)
class TestNimbusExperimentsCreateView(AuthTestCase):
def test_post_creates_experiment(self):

Просмотреть файл

@ -8,7 +8,9 @@ from experimenter.nimbus_ui_new.views import (
NimbusExperimentsListTableView,
QAStatusUpdateView,
SignoffUpdateView,
SubscribeView,
TakeawaysUpdateView,
UnsubscribeView,
)
urlpatterns = [
@ -52,4 +54,14 @@ urlpatterns = [
NimbusExperimentsCreateView.as_view(),
name="nimbus-new-create",
),
re_path(
r"^(?P<slug>[\w-]+)/subscribe/",
SubscribeView.as_view(),
name="nimbus-new-subscribe",
),
re_path(
r"^(?P<slug>[\w-]+)/unsubscribe/",
UnsubscribeView.as_view(),
name="nimbus-new-unsubscribe",
),
]

Просмотреть файл

@ -18,7 +18,9 @@ from experimenter.nimbus_ui_new.forms import (
NimbusExperimentCreateForm,
QAStatusForm,
SignoffForm,
SubscribeForm,
TakeawaysForm,
UnsubscribeForm,
)
@ -215,3 +217,21 @@ class MetricsUpdateView(NimbusExperimentViewMixin, RequestFormMixin, UpdateView)
def form_valid(self, form):
super().form_valid(form)
return self.render_to_response(self.get_context_data(form=form))
class SubscribeView(NimbusExperimentViewMixin, RequestFormMixin, UpdateView):
form_class = SubscribeForm
template_name = "nimbus_experiments/subscribers_list.html"
def form_valid(self, form):
super().form_valid(form)
return self.render_to_response(self.get_context_data(form=form))
class UnsubscribeView(NimbusExperimentViewMixin, RequestFormMixin, UpdateView):
form_class = UnsubscribeForm
template_name = "nimbus_experiments/subscribers_list.html"
def form_valid(self, form):
super().form_valid(form)
return self.render_to_response(self.get_context_data(form=form))