diff --git a/pricemon/urls.py b/pricemon/urls.py index 3f33e14..414a664 100644 --- a/pricemon/urls.py +++ b/pricemon/urls.py @@ -5,6 +5,11 @@ urlpatterns = [ path('', views.DashboardView.as_view(), name='pricemon-dashboard'), path('add/', views.WatcherCreateView.as_view(), name='pricemon-add'), path('/edit/', views.WatcherEditView.as_view(), name='pricemon-edit'), + path( + '/toggle-enabled/', + views.WatcherToggleEnabledView.as_view(), + name='pricemon-toggle-enabled', + ), path('/delete/', views.WatcherDeleteView.as_view(), name='pricemon-delete'), path('/history/', views.WatcherHistoryView.as_view(), name='pricemon-history'), path('/check/', views.CheckNowView.as_view(), name='pricemon-check-now'), diff --git a/pricemon/views.py b/pricemon/views.py index d2d5f84..b514edb 100644 --- a/pricemon/views.py +++ b/pricemon/views.py @@ -21,6 +21,7 @@ class DashboardView(TemplateView): active_alerts = PriceAlert.objects.filter(dismissed=False).select_related('watcher') ctx['watchers'] = watchers ctx['total_watchers'] = watchers.count() + ctx['enabled_watchers_count'] = watchers.filter(enabled=True).count() ctx['active_alerts_count'] = active_alerts.count() ctx['active_alerts'] = active_alerts[:10] return ctx @@ -75,6 +76,16 @@ class WatcherDeleteView(View): return redirect('pricemon-dashboard') +class WatcherToggleEnabledView(View): + def post(self, request, pk): + watcher = get_object_or_404(PriceWatcher, pk=pk) + watcher.enabled = not watcher.enabled + watcher.save(update_fields=['enabled']) + state = 'enabled' if watcher.enabled else 'disabled' + messages.success(request, f'Watcher "{watcher.name}" {state}.') + return redirect('pricemon-dashboard') + + class WatcherHistoryView(TemplateView): template_name = 'pricemon/history.html' diff --git a/templates/pricemon/dashboard.html b/templates/pricemon/dashboard.html index 5bc6181..633585c 100644 --- a/templates/pricemon/dashboard.html +++ b/templates/pricemon/dashboard.html @@ -35,12 +35,7 @@
{% trans "Enabled" %}
-
- {% with watchers|length as total %} - {% for w in watchers %}{% if w.enabled %}{% endif %}{% endfor %} - {{ watchers|length }} - {% endwith %} -
+
{{ enabled_watchers_count }}
@@ -147,6 +142,23 @@ d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z"/> +
+ {% csrf_token %} + +
{% csrf_token %} @@ -262,6 +274,23 @@ d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z"/> + + {% csrf_token %} + +
{% csrf_token %} diff --git a/tests/test_pricemon.py b/tests/test_pricemon.py new file mode 100644 index 0000000..ee2bad1 --- /dev/null +++ b/tests/test_pricemon.py @@ -0,0 +1,55 @@ +from unittest.mock import patch + +import pytest +from django.urls import reverse + +from pricemon.models import PriceWatcher + + +@pytest.fixture +def watcher(): + with patch('pricemon.tasks.schedule_watcher'): + return PriceWatcher.objects.create( + name='Test product', + url='https://example.com/product', + css_selector='.price', + ) + + +@pytest.mark.django_db +class TestPriceMonitorToggle: + def test_dashboard_shows_disable_action_for_enabled_watcher(self, client, watcher): + response = client.get(reverse('pricemon-dashboard')) + + assert response.status_code == 200 + assert reverse('pricemon-toggle-enabled', args=[watcher.pk]) in response.content.decode() + assert 'Disable monitoring' in response.content.decode() + assert response.context['enabled_watchers_count'] == 1 + + def test_toggle_disables_watcher_and_unschedules_job(self, client, watcher): + with patch('pricemon.tasks.unschedule_watcher') as unschedule_watcher: + response = client.post(reverse('pricemon-toggle-enabled', args=[watcher.pk])) + + watcher.refresh_from_db() + assert response.status_code == 302 + assert response.url == reverse('pricemon-dashboard') + assert watcher.enabled is False + unschedule_watcher.assert_called_once() + + def test_toggle_enables_paused_watcher_and_schedules_job(self, client, watcher): + with patch('pricemon.tasks.unschedule_watcher'): + watcher.enabled = False + watcher.save(update_fields=['enabled']) + + with patch('pricemon.tasks.schedule_watcher') as schedule_watcher: + response = client.post(reverse('pricemon-toggle-enabled', args=[watcher.pk])) + + watcher.refresh_from_db() + assert response.status_code == 302 + assert watcher.enabled is True + schedule_watcher.assert_called_once() + + def test_toggle_rejects_get_requests(self, client, watcher): + response = client.get(reverse('pricemon-toggle-enabled', args=[watcher.pk])) + + assert response.status_code == 405