Repository: TOMToolkit/tom_base

Issue:
Broker alerts: bulk target creation.
The result list for broker queries should allow for bulk creation of targets. The user should be able to select multiple (or all) returned alerts and create targets from them at once.


Discussion:

Resolving patch:
diff --git a/tom_alerts/views.py b/tom_alerts/views.py
--- a/tom_alerts/views.py
+++ b/tom_alerts/views.py
@@ -4,6 +4,7 @@
 from django.shortcuts import redirect, get_object_or_404
 from django.urls import reverse, reverse_lazy
 from django.contrib.auth.mixins import LoginRequiredMixin
+from django.contrib import messages
 import django_filters
 
 from tom_alerts.models import BrokerQuery
@@ -112,15 +113,24 @@ def get_context_data(self, *args, **kwargs):
 
 
 class CreateTargetFromAlertView(LoginRequiredMixin, View):
-    def post(self, *args, **kwargs):
+    def post(self, request, *args, **kwargs):
+        query_id = self.request.POST['query_id']
         broker_name = self.request.POST['broker']
         broker_class = get_service_class(broker_name)
         alerts = self.request.POST.getlist('alerts')
+        if not alerts:
+            messages.warning(request, 'Please select at least one alert from which to create a target.')
+            return redirect(reverse('tom_alerts:run', kwargs={'pk': query_id}))
         for alert in alerts:
             alert = broker_class().fetch_alert(alert)
             target = broker_class().to_target(alert)
             broker_class().process_reduced_data(target, alert)
             target.save()
-        return redirect(reverse(
-            'tom_targets:list')
-        )
+        if (len(alerts) == 1):
+            return redirect(reverse(
+                'tom_targets:detail', kwargs={'pk': target.id})
+            )
+        else:
+            return redirect(reverse(
+                'tom_targets:list')
+            )
diff --git a/tom_dataproducts/models.py b/tom_dataproducts/models.py
--- a/tom_dataproducts/models.py
+++ b/tom_dataproducts/models.py
@@ -119,7 +119,10 @@ class ReducedDatum(models.Model):
     data_product = models.ForeignKey(DataProduct, null=True, on_delete=models.CASCADE)
     data_type = models.CharField(
         max_length=100,
-        choices=getattr(settings, 'DATA_TYPES', ('', '')),
+        choices=getattr(settings, 'DATA_TYPES', (
+            ('SPECTROSCOPY', 'Spectroscopy'),
+            ('PHOTOMETRY', 'Photometry')
+        )),
         default=''
     )
     timestamp = models.DateTimeField(null=False, blank=False, db_index=True)
diff --git a/tom_dataproducts/views.py b/tom_dataproducts/views.py
--- a/tom_dataproducts/views.py
+++ b/tom_dataproducts/views.py
@@ -27,7 +27,9 @@ def post(self, request, *args, **kwargs):
         service_class = get_service_class(request.POST['facility'])
         observation_record = ObservationRecord.objects.get(pk=kwargs['pk'])
         products = request.POST.getlist('products')
-        if products[0] == 'ALL':
+        if not products:
+            messages.warning(request, 'No products were saved, please select at least one dataproduct')
+        elif products[0] == 'ALL':
             products = service_class().save_data_products(observation_record)
             messages.success(request, 'Saved all available data products')
         else:


Test patch:
diff --git a/tom_alerts/tests/tests_generic.py b/tom_alerts/tests/tests_generic.py
--- a/tom_alerts/tests/tests_generic.py
+++ b/tom_alerts/tests/tests_generic.py
@@ -179,15 +179,47 @@ def test_update_query(self):
         self.assertEqual(broker_query.parameters_as_dict['name'], update_data['name'])
 
     def test_create_target(self):
-        BrokerQuery.objects.create(
+        query = BrokerQuery.objects.create(
             name='find hoth',
             broker='TEST',
             parameters='{"name": "Hoth"}',
         )
         post_data = {
             'broker': 'TEST',
+            'query_id': query.id,
             'alerts': [2]
         }
-        self.client.post(reverse('tom_alerts:create-target'), data=post_data)
+        response = self.client.post(reverse('tom_alerts:create-target'), data=post_data)
         self.assertEqual(Target.objects.count(), 1)
         self.assertEqual(Target.objects.first().name, 'Hoth')
+        self.assertRedirects(response, reverse('tom_targets:detail', kwargs={'pk': Target.objects.first().id}))
+
+    def test_create_multiple_targets(self):
+        query = BrokerQuery.objects.create(
+            name='find anything',
+            broker='TEST',
+            parameters='{"score__gt": "19"}'
+        )
+        post_data = {
+            'broker': 'TEST',
+            'query_id': query.id,
+            'alerts': [1, 2]
+        }
+        response = self.client.post(reverse('tom_alerts:create-target'), data=post_data)
+        self.assertEqual(Target.objects.count(), 2)
+        self.assertRedirects(response, reverse('tom_targets:list'))
+
+    def test_create_no_targets(self):
+        query = BrokerQuery.objects.create(
+            name='find anything',
+            broker='TEST',
+            parameters='{"name": "Alderaan"}'
+        )
+        post_data = {
+            'broker': 'TEST',
+            'query_id': query.id,
+            'alerts': []
+        }
+        response = self.client.post(reverse('tom_alerts:create-target'), data=post_data, follow=True)
+        self.assertEqual(Target.objects.count(), 0)
+        self.assertRedirects(response, reverse('tom_alerts:run', kwargs={'pk': query.id}))