We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
not having a LASAIR token in Settings.py breaks
Fail more gracefully if no LASAIR token in settings.py

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `tom_alerts/brokers/lasair.py`
Content:
```
1 import requests
2 from urllib.parse import urlencode
3 
4 from crispy_forms.layout import HTML, Layout, Div, Fieldset, Row, Column
5 from django import forms
6 from django.conf import settings
7 
8 from tom_alerts.alerts import GenericQueryForm, GenericAlert, GenericBroker
9 from tom_targets.models import Target
10 
11 LASAIR_URL = 'https://lasair-ztf.lsst.ac.uk'
12 
13 
14 class LasairBrokerForm(GenericQueryForm):
15     cone_ra = forms.CharField(required=False, label='RA', help_text='Object RA (Decimal Degrees)',
16                               widget=forms.TextInput(attrs={'placeholder': '1.2345'}))
17     cone_dec = forms.CharField(required=False, label='Dec', help_text='Object Dec (Decimal Degrees)',
18                                widget=forms.TextInput(attrs={'placeholder': '1.2345'}))
19     cone_radius = forms.CharField(required=False, label='Radius', help_text='Search Radius (Arcsec)', initial='10',
20                                   widget=forms.TextInput(attrs={'placeholder': '10'}))
21     sqlquery = forms.CharField(required=False, label='SQL Query Conditions',
22                                help_text='The "WHERE" criteria to restrict which objects are returned. '
23                                          '(i.e. gmag < 12.0)')
24 
25     def __init__(self, *args, **kwargs):
26         super().__init__(*args, **kwargs)
27         self.helper.layout = Layout(
28             HTML('''
29                     <p>
30                     Please see the <a href="https://lasair-ztf.lsst.ac.uk/api">Lasair website</a> for more detailed
31                     instructions on querying the broker.
32                 '''),
33             self.common_layout,
34             Fieldset(
35                 'Cone Search',
36                 Row(
37                     Column('cone_ra', css_class='form-group col-md-4 mb-0'),
38                     Column('cone_dec', css_class='form-group col-md-4 mb-0'),
39                     Column('cone_radius', css_class='form-group col-md-4 mb-0'),
40                     css_class='form-row'
41                 ),
42                 HTML("""<br>
43                     <h4>SQL Query Search</h4>
44                     """),
45 
46                 Div('sqlquery')
47             )
48         )
49 
50     def clean(self):
51         cleaned_data = super().clean()
52 
53         # Ensure that either cone search or sqlquery are populated
54         if not ((cleaned_data['cone_ra'] and cleaned_data['cone_dec']) or cleaned_data['sqlquery']):
55             raise forms.ValidationError('Either RA/Dec or Freeform SQL Query must be populated.')
56 
57         return cleaned_data
58 
59 
60 def get_lasair_object(obj):
61     """Parse lasair object table"""
62     objectid = obj['objectId']
63     jdmax = obj['candidates'][0]['mjd']
64     ra = obj['objectData']['ramean']
65     dec = obj['objectData']['decmean']
66     glon = obj['objectData']['glonmean']
67     glat = obj['objectData']['glatmean']
68     magpsf = obj['candidates'][0]['magpsf']
69     return {
70         'alert_id': objectid,
71         'timestamp': jdmax,
72         'ra': ra,
73         'dec': dec,
74         'galactic_lng': glon,
75         'galactic_lat': glat,
76         'mag': magpsf
77     }
78 
79 
80 class LasairBroker(GenericBroker):
81     """
82     The ``LasairBroker`` is the interface to the Lasair alert broker. For information regarding the query format for
83     Lasair, please see https://lasair-ztf.lsst.ac.uk/.
84 
85     Requires a LASAIR_TOKEN in settings.py.
86     See https://lasair-ztf.lsst.ac.uk/api for details about how to acquire an authorization token.
87     """
88 
89     name = 'Lasair'
90     form = LasairBrokerForm
91 
92     def fetch_alerts(self, parameters):
93         token = settings.LASAIR_TOKEN
94         alerts = []
95         broker_feedback = ''
96         object_ids = ''
97 
98         # Check for Cone Search
99         if 'cone_ra' in parameters and len(parameters['cone_ra'].strip()) > 0 and\
100                 'cone_dec' in parameters and len(parameters['cone_dec'].strip()) > 0:
101 
102             cone_query = {'ra': parameters['cone_ra'].strip(),
103                           'dec': parameters['cone_dec'].strip(),
104                           'radius': parameters['cone_radius'].strip(),  # defaults to 10"
105                           'requestType': 'all'  # Return all objects within radius
106                           }
107             parsed_cone_query = urlencode(cone_query)
108 
109             # Query LASAIR Cone Search API
110             cone_response = requests.get(
111                 LASAIR_URL + '/api/cone/?' + parsed_cone_query + f'&token={token}&format=json'
112             )
113             search_results = cone_response.json()
114             # Successful Search ~ [{'object': 'ZTF19abuaekk', 'separation': 205.06135003141878},...]
115             # Unsuccessful Search ~ {'error': 'No object found ...'}
116             try:
117                 # Provide comma separated string of Object IDs matching search criteria
118                 object_ids = ','.join([result['object'] for result in search_results])
119             except TypeError:
120                 for key in search_results:
121                     broker_feedback += f'{key}:{search_results[key]}'
122 
123         # Check for SQL Condition Query
124         elif 'sqlquery' in parameters and len(parameters['sqlquery'].strip()) > 0:
125             sql_query = {'selected': 'objectId',  # The only parameter we need returned is the objectId
126                          'tables': 'objects',  # The only table we need to search is the objects table
127                          'conditions': parameters['sqlquery'].strip(),
128                          'limit': '1000'  # limit number of returned objects to 1000
129                          }
130             parsed_sql_query = urlencode(sql_query)
131 
132             # Query LASAIR SQLQuery API
133             query_response = requests.get(
134                 LASAIR_URL + '/api/query/?' + parsed_sql_query + f'&token={token}&format=json'
135             )
136 
137             search_results = query_response.json()
138             # Successful Search ~ [{'objectId': 'ZTF18aagzzzz'},...]
139             # Unsuccessful Search ~ []
140             try:
141                 # Provide comma separated string of Object IDs matching search criteria
142                 object_ids = ','.join([result['objectId'] for result in search_results])
143             except TypeError:
144                 for key in search_results:
145                     broker_feedback += f'{key}:{search_results[key]}'
146 
147             # Supply feedback for empty results
148             if not object_ids and not broker_feedback:
149                 broker_feedback += f"No objects found with conditions: {sql_query['conditions']}"
150         else:
151             return iter(alerts), broker_feedback
152 
153         if object_ids:
154             # Query LASAIR object API
155             obj_response = requests.get(
156                 LASAIR_URL + '/api/objects/' + f'?objectIds={object_ids}&token={token}&format=json'
157             )
158             obj_results = obj_response.json()
159             # Successful Search ~ [{'objectId': 'ZTF19abuaekk', 'objectData': {...}},...]
160 
161             for obj in obj_results:
162                 alerts.append(get_lasair_object(obj))
163         return iter(alerts), broker_feedback
164 
165     def fetch_alert(self, alert_id):
166         url = LASAIR_URL + '/object/' + alert_id + '/json/'
167         response = requests.get(url)
168         response.raise_for_status()
169         parsed = response.json()
170         return parsed
171 
172     def process_reduced_data(self, target, alert=None):
173         pass
174 
175     def to_generic_alert(self, alert):
176         return GenericAlert(
177             url=LASAIR_URL + '/object/' + alert['alert_id'],
178             id=alert['alert_id'],
179             name=alert['alert_id'],
180             ra=alert['ra'],
181             dec=alert['dec'],
182             timestamp=alert['timestamp'],
183             mag=alert['mag'],
184             score=1,  # dunno what this means ..?
185         )
186 
187     def to_target(self, alert):
188         for c in alert['candidates']:
189             if 'candid' in c:
190                 break
191         return Target.objects.create(
192             name=alert.get('objectId'),
193             type='SIDEREAL',
194             ra=alert['objectData']['ramean'],
195             dec=alert['objectData']['decmean'],
196             galactic_lng=alert['objectData']['glonmean'],
197             galactic_lat=alert['objectData']['glatmean'],
198         )
199
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/tom_alerts/brokers/lasair.py b/tom_alerts/brokers/lasair.py
--- a/tom_alerts/brokers/lasair.py
+++ b/tom_alerts/brokers/lasair.py
@@ -90,10 +90,15 @@
     form = LasairBrokerForm
 
     def fetch_alerts(self, parameters):
-        token = settings.LASAIR_TOKEN
         alerts = []
         broker_feedback = ''
         object_ids = ''
+        try:
+            token = settings.LASAIR_TOKEN
+        except AttributeError:
+            broker_feedback += 'Requires a LASAIR_TOKEN in settings.py. See https://lasair-ztf.lsst.ac.uk/api' \
+                               ' for details about how to acquire an authorization token.'
+            return iter(alerts), broker_feedback
 
         # Check for Cone Search
         if 'cone_ra' in parameters and len(parameters['cone_ra'].strip()) > 0 and\

{"golden_diff": "diff --git a/tom_alerts/brokers/lasair.py b/tom_alerts/brokers/lasair.py\n--- a/tom_alerts/brokers/lasair.py\n+++ b/tom_alerts/brokers/lasair.py\n@@ -90,10 +90,15 @@\n     form = LasairBrokerForm\n \n     def fetch_alerts(self, parameters):\n-        token = settings.LASAIR_TOKEN\n         alerts = []\n         broker_feedback = ''\n         object_ids = ''\n+        try:\n+            token = settings.LASAIR_TOKEN\n+        except AttributeError:\n+            broker_feedback += 'Requires a LASAIR_TOKEN in settings.py. See https://lasair-ztf.lsst.ac.uk/api' \\\n+                               ' for details about how to acquire an authorization token.'\n+            return iter(alerts), broker_feedback\n \n         # Check for Cone Search\n         if 'cone_ra' in parameters and len(parameters['cone_ra'].strip()) > 0 and\\\n", "issue": "not having a LASAIR token in Settings.py breaks\nFail more gracefully if no LASAIR token in settings.py\n", "before_files": [{"content": "import requests\nfrom urllib.parse import urlencode\n\nfrom crispy_forms.layout import HTML, Layout, Div, Fieldset, Row, Column\nfrom django import forms\nfrom django.conf import settings\n\nfrom tom_alerts.alerts import GenericQueryForm, GenericAlert, GenericBroker\nfrom tom_targets.models import Target\n\nLASAIR_URL = 'https://lasair-ztf.lsst.ac.uk'\n\n\nclass LasairBrokerForm(GenericQueryForm):\n    cone_ra = forms.CharField(required=False, label='RA', help_text='Object RA (Decimal Degrees)',\n                              widget=forms.TextInput(attrs={'placeholder': '1.2345'}))\n    cone_dec = forms.CharField(required=False, label='Dec', help_text='Object Dec (Decimal Degrees)',\n                               widget=forms.TextInput(attrs={'placeholder': '1.2345'}))\n    cone_radius = forms.CharField(required=False, label='Radius', help_text='Search Radius (Arcsec)', initial='10',\n                                  widget=forms.TextInput(attrs={'placeholder': '10'}))\n    sqlquery = forms.CharField(required=False, label='SQL Query Conditions',\n                               help_text='The \"WHERE\" criteria to restrict which objects are returned. '\n                                         '(i.e. gmag < 12.0)')\n\n    def __init__(self, *args, **kwargs):\n        super().__init__(*args, **kwargs)\n        self.helper.layout = Layout(\n            HTML('''\n                    <p>\n                    Please see the <a href=\"https://lasair-ztf.lsst.ac.uk/api\">Lasair website</a> for more detailed\n                    instructions on querying the broker.\n                '''),\n            self.common_layout,\n            Fieldset(\n                'Cone Search',\n                Row(\n                    Column('cone_ra', css_class='form-group col-md-4 mb-0'),\n                    Column('cone_dec', css_class='form-group col-md-4 mb-0'),\n                    Column('cone_radius', css_class='form-group col-md-4 mb-0'),\n                    css_class='form-row'\n                ),\n                HTML(\"\"\"<br>\n                    <h4>SQL Query Search</h4>\n                    \"\"\"),\n\n                Div('sqlquery')\n            )\n        )\n\n    def clean(self):\n        cleaned_data = super().clean()\n\n        # Ensure that either cone search or sqlquery are populated\n        if not ((cleaned_data['cone_ra'] and cleaned_data['cone_dec']) or cleaned_data['sqlquery']):\n            raise forms.ValidationError('Either RA/Dec or Freeform SQL Query must be populated.')\n\n        return cleaned_data\n\n\ndef get_lasair_object(obj):\n    \"\"\"Parse lasair object table\"\"\"\n    objectid = obj['objectId']\n    jdmax = obj['candidates'][0]['mjd']\n    ra = obj['objectData']['ramean']\n    dec = obj['objectData']['decmean']\n    glon = obj['objectData']['glonmean']\n    glat = obj['objectData']['glatmean']\n    magpsf = obj['candidates'][0]['magpsf']\n    return {\n        'alert_id': objectid,\n        'timestamp': jdmax,\n        'ra': ra,\n        'dec': dec,\n        'galactic_lng': glon,\n        'galactic_lat': glat,\n        'mag': magpsf\n    }\n\n\nclass LasairBroker(GenericBroker):\n    \"\"\"\n    The ``LasairBroker`` is the interface to the Lasair alert broker. For information regarding the query format for\n    Lasair, please see https://lasair-ztf.lsst.ac.uk/.\n\n    Requires a LASAIR_TOKEN in settings.py.\n    See https://lasair-ztf.lsst.ac.uk/api for details about how to acquire an authorization token.\n    \"\"\"\n\n    name = 'Lasair'\n    form = LasairBrokerForm\n\n    def fetch_alerts(self, parameters):\n        token = settings.LASAIR_TOKEN\n        alerts = []\n        broker_feedback = ''\n        object_ids = ''\n\n        # Check for Cone Search\n        if 'cone_ra' in parameters and len(parameters['cone_ra'].strip()) > 0 and\\\n                'cone_dec' in parameters and len(parameters['cone_dec'].strip()) > 0:\n\n            cone_query = {'ra': parameters['cone_ra'].strip(),\n                          'dec': parameters['cone_dec'].strip(),\n                          'radius': parameters['cone_radius'].strip(),  # defaults to 10\"\n                          'requestType': 'all'  # Return all objects within radius\n                          }\n            parsed_cone_query = urlencode(cone_query)\n\n            # Query LASAIR Cone Search API\n            cone_response = requests.get(\n                LASAIR_URL + '/api/cone/?' + parsed_cone_query + f'&token={token}&format=json'\n            )\n            search_results = cone_response.json()\n            # Successful Search ~ [{'object': 'ZTF19abuaekk', 'separation': 205.06135003141878},...]\n            # Unsuccessful Search ~ {'error': 'No object found ...'}\n            try:\n                # Provide comma separated string of Object IDs matching search criteria\n                object_ids = ','.join([result['object'] for result in search_results])\n            except TypeError:\n                for key in search_results:\n                    broker_feedback += f'{key}:{search_results[key]}'\n\n        # Check for SQL Condition Query\n        elif 'sqlquery' in parameters and len(parameters['sqlquery'].strip()) > 0:\n            sql_query = {'selected': 'objectId',  # The only parameter we need returned is the objectId\n                         'tables': 'objects',  # The only table we need to search is the objects table\n                         'conditions': parameters['sqlquery'].strip(),\n                         'limit': '1000'  # limit number of returned objects to 1000\n                         }\n            parsed_sql_query = urlencode(sql_query)\n\n            # Query LASAIR SQLQuery API\n            query_response = requests.get(\n                LASAIR_URL + '/api/query/?' + parsed_sql_query + f'&token={token}&format=json'\n            )\n\n            search_results = query_response.json()\n            # Successful Search ~ [{'objectId': 'ZTF18aagzzzz'},...]\n            # Unsuccessful Search ~ []\n            try:\n                # Provide comma separated string of Object IDs matching search criteria\n                object_ids = ','.join([result['objectId'] for result in search_results])\n            except TypeError:\n                for key in search_results:\n                    broker_feedback += f'{key}:{search_results[key]}'\n\n            # Supply feedback for empty results\n            if not object_ids and not broker_feedback:\n                broker_feedback += f\"No objects found with conditions: {sql_query['conditions']}\"\n        else:\n            return iter(alerts), broker_feedback\n\n        if object_ids:\n            # Query LASAIR object API\n            obj_response = requests.get(\n                LASAIR_URL + '/api/objects/' + f'?objectIds={object_ids}&token={token}&format=json'\n            )\n            obj_results = obj_response.json()\n            # Successful Search ~ [{'objectId': 'ZTF19abuaekk', 'objectData': {...}},...]\n\n            for obj in obj_results:\n                alerts.append(get_lasair_object(obj))\n        return iter(alerts), broker_feedback\n\n    def fetch_alert(self, alert_id):\n        url = LASAIR_URL + '/object/' + alert_id + '/json/'\n        response = requests.get(url)\n        response.raise_for_status()\n        parsed = response.json()\n        return parsed\n\n    def process_reduced_data(self, target, alert=None):\n        pass\n\n    def to_generic_alert(self, alert):\n        return GenericAlert(\n            url=LASAIR_URL + '/object/' + alert['alert_id'],\n            id=alert['alert_id'],\n            name=alert['alert_id'],\n            ra=alert['ra'],\n            dec=alert['dec'],\n            timestamp=alert['timestamp'],\n            mag=alert['mag'],\n            score=1,  # dunno what this means ..?\n        )\n\n    def to_target(self, alert):\n        for c in alert['candidates']:\n            if 'candid' in c:\n                break\n        return Target.objects.create(\n            name=alert.get('objectId'),\n            type='SIDEREAL',\n            ra=alert['objectData']['ramean'],\n            dec=alert['objectData']['decmean'],\n            galactic_lng=alert['objectData']['glonmean'],\n            galactic_lat=alert['objectData']['glatmean'],\n        )\n", "path": "tom_alerts/brokers/lasair.py"}], "after_files": [{"content": "import requests\nfrom urllib.parse import urlencode\n\nfrom crispy_forms.layout import HTML, Layout, Div, Fieldset, Row, Column\nfrom django import forms\nfrom django.conf import settings\n\nfrom tom_alerts.alerts import GenericQueryForm, GenericAlert, GenericBroker\nfrom tom_targets.models import Target\n\nLASAIR_URL = 'https://lasair-ztf.lsst.ac.uk'\n\n\nclass LasairBrokerForm(GenericQueryForm):\n    cone_ra = forms.CharField(required=False, label='RA', help_text='Object RA (Decimal Degrees)',\n                              widget=forms.TextInput(attrs={'placeholder': '1.2345'}))\n    cone_dec = forms.CharField(required=False, label='Dec', help_text='Object Dec (Decimal Degrees)',\n                               widget=forms.TextInput(attrs={'placeholder': '1.2345'}))\n    cone_radius = forms.CharField(required=False, label='Radius', help_text='Search Radius (Arcsec)', initial='10',\n                                  widget=forms.TextInput(attrs={'placeholder': '10'}))\n    sqlquery = forms.CharField(required=False, label='SQL Query Conditions',\n                               help_text='The \"WHERE\" criteria to restrict which objects are returned. '\n                                         '(i.e. gmag < 12.0)')\n\n    def __init__(self, *args, **kwargs):\n        super().__init__(*args, **kwargs)\n        self.helper.layout = Layout(\n            HTML('''\n                    <p>\n                    Please see the <a href=\"https://lasair-ztf.lsst.ac.uk/api\">Lasair website</a> for more detailed\n                    instructions on querying the broker.\n                '''),\n            self.common_layout,\n            Fieldset(\n                'Cone Search',\n                Row(\n                    Column('cone_ra', css_class='form-group col-md-4 mb-0'),\n                    Column('cone_dec', css_class='form-group col-md-4 mb-0'),\n                    Column('cone_radius', css_class='form-group col-md-4 mb-0'),\n                    css_class='form-row'\n                ),\n                HTML(\"\"\"<br>\n                    <h4>SQL Query Search</h4>\n                    \"\"\"),\n\n                Div('sqlquery')\n            )\n        )\n\n    def clean(self):\n        cleaned_data = super().clean()\n\n        # Ensure that either cone search or sqlquery are populated\n        if not ((cleaned_data['cone_ra'] and cleaned_data['cone_dec']) or cleaned_data['sqlquery']):\n            raise forms.ValidationError('Either RA/Dec or Freeform SQL Query must be populated.')\n\n        return cleaned_data\n\n\ndef get_lasair_object(obj):\n    \"\"\"Parse lasair object table\"\"\"\n    objectid = obj['objectId']\n    jdmax = obj['candidates'][0]['mjd']\n    ra = obj['objectData']['ramean']\n    dec = obj['objectData']['decmean']\n    glon = obj['objectData']['glonmean']\n    glat = obj['objectData']['glatmean']\n    magpsf = obj['candidates'][0]['magpsf']\n    return {\n        'alert_id': objectid,\n        'timestamp': jdmax,\n        'ra': ra,\n        'dec': dec,\n        'galactic_lng': glon,\n        'galactic_lat': glat,\n        'mag': magpsf\n    }\n\n\nclass LasairBroker(GenericBroker):\n    \"\"\"\n    The ``LasairBroker`` is the interface to the Lasair alert broker. For information regarding the query format for\n    Lasair, please see https://lasair-ztf.lsst.ac.uk/.\n\n    Requires a LASAIR_TOKEN in settings.py.\n    See https://lasair-ztf.lsst.ac.uk/api for details about how to acquire an authorization token.\n    \"\"\"\n\n    name = 'Lasair'\n    form = LasairBrokerForm\n\n    def fetch_alerts(self, parameters):\n        alerts = []\n        broker_feedback = ''\n        object_ids = ''\n        try:\n            token = settings.LASAIR_TOKEN\n        except AttributeError:\n            broker_feedback += 'Requires a LASAIR_TOKEN in settings.py. See https://lasair-ztf.lsst.ac.uk/api' \\\n                               ' for details about how to acquire an authorization token.'\n            return iter(alerts), broker_feedback\n\n        # Check for Cone Search\n        if 'cone_ra' in parameters and len(parameters['cone_ra'].strip()) > 0 and\\\n                'cone_dec' in parameters and len(parameters['cone_dec'].strip()) > 0:\n\n            cone_query = {'ra': parameters['cone_ra'].strip(),\n                          'dec': parameters['cone_dec'].strip(),\n                          'radius': parameters['cone_radius'].strip(),  # defaults to 10\"\n                          'requestType': 'all'  # Return all objects within radius\n                          }\n            parsed_cone_query = urlencode(cone_query)\n\n            # Query LASAIR Cone Search API\n            cone_response = requests.get(\n                LASAIR_URL + '/api/cone/?' + parsed_cone_query + f'&token={token}&format=json'\n            )\n            search_results = cone_response.json()\n            # Successful Search ~ [{'object': 'ZTF19abuaekk', 'separation': 205.06135003141878},...]\n            # Unsuccessful Search ~ {'error': 'No object found ...'}\n            try:\n                # Provide comma separated string of Object IDs matching search criteria\n                object_ids = ','.join([result['object'] for result in search_results])\n            except TypeError:\n                for key in search_results:\n                    broker_feedback += f'{key}:{search_results[key]}'\n\n        # Check for SQL Condition Query\n        elif 'sqlquery' in parameters and len(parameters['sqlquery'].strip()) > 0:\n            sql_query = {'selected': 'objectId',  # The only parameter we need returned is the objectId\n                         'tables': 'objects',  # The only table we need to search is the objects table\n                         'conditions': parameters['sqlquery'].strip(),\n                         'limit': '1000'  # limit number of returned objects to 1000\n                         }\n            parsed_sql_query = urlencode(sql_query)\n\n            # Query LASAIR SQLQuery API\n            query_response = requests.get(\n                LASAIR_URL + '/api/query/?' + parsed_sql_query + f'&token={token}&format=json'\n            )\n\n            search_results = query_response.json()\n            # Successful Search ~ [{'objectId': 'ZTF18aagzzzz'},...]\n            # Unsuccessful Search ~ []\n            try:\n                # Provide comma separated string of Object IDs matching search criteria\n                object_ids = ','.join([result['objectId'] for result in search_results])\n            except TypeError:\n                for key in search_results:\n                    broker_feedback += f'{key}:{search_results[key]}'\n\n            # Supply feedback for empty results\n            if not object_ids and not broker_feedback:\n                broker_feedback += f\"No objects found with conditions: {sql_query['conditions']}\"\n        else:\n            return iter(alerts), broker_feedback\n\n        if object_ids:\n            # Query LASAIR object API\n            obj_response = requests.get(\n                LASAIR_URL + '/api/objects/' + f'?objectIds={object_ids}&token={token}&format=json'\n            )\n            obj_results = obj_response.json()\n            # Successful Search ~ [{'objectId': 'ZTF19abuaekk', 'objectData': {...}},...]\n\n            for obj in obj_results:\n                alerts.append(get_lasair_object(obj))\n        return iter(alerts), broker_feedback\n\n    def fetch_alert(self, alert_id):\n        url = LASAIR_URL + '/object/' + alert_id + '/json/'\n        response = requests.get(url)\n        response.raise_for_status()\n        parsed = response.json()\n        return parsed\n\n    def process_reduced_data(self, target, alert=None):\n        pass\n\n    def to_generic_alert(self, alert):\n        return GenericAlert(\n            url=LASAIR_URL + '/object/' + alert['alert_id'],\n            id=alert['alert_id'],\n            name=alert['alert_id'],\n            ra=alert['ra'],\n            dec=alert['dec'],\n            timestamp=alert['timestamp'],\n            mag=alert['mag'],\n            score=1,  # dunno what this means ..?\n        )\n\n    def to_target(self, alert):\n        for c in alert['candidates']:\n            if 'candid' in c:\n                break\n        return Target.objects.create(\n            name=alert.get('objectId'),\n            type='SIDEREAL',\n            ra=alert['objectData']['ramean'],\n            dec=alert['objectData']['decmean'],\n            galactic_lng=alert['objectData']['glonmean'],\n            galactic_lat=alert['objectData']['glatmean'],\n        )\n", "path": "tom_alerts/brokers/lasair.py"}]}