Skip to content

Commit 231d135

Browse files
authored
Merge branch 'dev' into 1125-add-target-and-dataproduct-filters-to-the-reduceddatums-drf-api
2 parents e4c9e91 + 68b4893 commit 231d135

3 files changed

Lines changed: 294 additions & 11 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ docs/_build/
8484

8585
# VS Code
8686
.vscode/
87+
.vs/
8788
*.code-workspace
8889

8990
# PyBuilder

tom_catalogs/harvesters/mpc.py

Lines changed: 72 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import re
2+
import logging
13
import requests
24
from math import sqrt, degrees
35

@@ -6,6 +8,9 @@
68

79
from astroquery.mpc import MPC
810

11+
logger = logging.getLogger(__name__)
12+
# logger.setLevel(logging.DEBUG)
13+
914

1015
class MPCHarvester(AbstractHarvester):
1116
"""
@@ -17,22 +22,79 @@ class MPCHarvester(AbstractHarvester):
1722
name = 'MPC'
1823

1924
def query(self, term):
20-
self.catalog_data = MPC.query_object('asteroid', name=term)
25+
self._object_type = 'asteroid'
26+
self._query_type = 'name'
27+
numbered_object = re.compile(r'(\d+)(P?)\s*$')
28+
provisional_desig = re.compile(r'^\s*(\d{4}\s*[A-Z]{2}\d*)')
29+
provisional_comets = re.compile(r'([C,P,A,D]/\d{4} [A-H,J-Y]{1,2}\d+)')
30+
31+
match = re.search(provisional_desig, term)
32+
if match:
33+
logger.debug("Desig match")
34+
self._query_type = 'desig'
35+
self._object_term = match.groups()[0]
36+
logger.debug(self._object_term, self._object_type)
37+
self.catalog_data = MPC.query_object(self._object_type, designation=self._object_term)
38+
else:
39+
match = re.search(provisional_comets, term)
40+
if match:
41+
logger.debug("Comet match")
42+
self._object_type = 'comet'
43+
self._query_type = 'desig'
44+
self._object_term = match.groups()[0]
45+
self.catalog_data = MPC.query_object(self._object_type, designation=self._object_term)
46+
else:
47+
match = re.search(numbered_object, term)
48+
if match:
49+
# Numbered object (asteroid or comet)
50+
logger.debug("Num Match")
51+
self._object_term = match.groups()[0]
52+
self._query_type = 'number'
53+
if match.groups()[1] == 'P':
54+
# Periodic comet
55+
self._object_type = 'comet'
56+
self._object_term = ''.join(match.groups())
57+
self.catalog_data = MPC.query_object(self._object_type, number=self._object_term)
58+
else:
59+
logger.debug("No match")
60+
self._object_term = term
61+
self.catalog_data = MPC.query_object(self._object_type, name=self._object_term)
2162

2263
def to_target(self):
2364
target = super().to_target()
2465
result = self.catalog_data[0]
2566
target.type = 'NON_SIDEREAL'
26-
target.name = result['name']
27-
target.extra_names = [result['designation']] if result['designation'] else []
67+
if result.get('number', None) is not None:
68+
if result.get('name', None) is not None:
69+
target.name = str(result['number'])
70+
target.extra_names = [str(result['name']),]
71+
target.extra_names += [result['designation']] if result['designation'] else []
72+
else:
73+
target.name = str(result['number'])
74+
if result.get('object_type'):
75+
# Add comet object type if it exists
76+
target.name += result['object_type']
77+
else:
78+
target.name = result['designation']
2879
target.epoch_of_elements = self.jd_to_mjd(result['epoch_jd'])
29-
target.mean_anomaly = result['mean_anomaly']
30-
target.arg_of_perihelion = result['argument_of_perihelion']
31-
target.eccentricity = result['eccentricity']
32-
target.lng_asc_node = result['ascending_node']
33-
target.inclination = result['inclination']
34-
target.mean_daily_motion = result['mean_daily_motion']
35-
target.semimajor_axis = result['semimajor_axis']
80+
target.arg_of_perihelion = float(result['argument_of_perihelion'])
81+
target.eccentricity = float(result['eccentricity'])
82+
target.lng_asc_node = float(result['ascending_node'])
83+
target.inclination = float(result['inclination'])
84+
target.mean_daily_motion = float(result['mean_daily_motion'])
85+
object_type = result.get('object_type', '')
86+
target.scheme = 'MPC_MINOR_PLANET'
87+
if object_type == 'C' or object_type == 'P':
88+
target.scheme = 'MPC_COMET'
89+
target.perihdist = float(result['perihelion_distance'])
90+
try:
91+
# Convert JD to MJD as string (avoid losing precision)
92+
target.epoch_of_perihelion = float(result['perihelion_date_jd'][2:]) - 0.5
93+
except ValueError:
94+
raise
95+
else:
96+
target.mean_anomaly = float(result['mean_anomaly'])
97+
target.semimajor_axis = float(result['semimajor_axis'])
3698
return target
3799

38100

tom_catalogs/tests/harvesters/test_mpc.py

Lines changed: 221 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,227 @@
44
from django.test import tag, TestCase
55
from unittest.mock import MagicMock, patch
66

7-
from tom_catalogs.harvesters.mpc import MPCExplorerHarvester
7+
from tom_catalogs.harvesters.mpc import MPCHarvester, MPCExplorerHarvester
8+
9+
10+
class TestMPCHarvester(TestCase):
11+
def setUp(self):
12+
self.broker = MPCHarvester()
13+
self.test_response = [{'foo': 42}]
14+
15+
@patch('astroquery.mpc.MPC.query_object')
16+
def test_query_name(self, mock_query):
17+
mock_query.return_value = self.test_response
18+
19+
self.broker.query('didymos')
20+
self.assertEqual(self.broker._object_type, 'asteroid')
21+
self.assertEqual(self.broker._object_term, 'didymos')
22+
self.assertEqual(self.broker._query_type, 'name')
23+
self.assertEqual(self.broker.catalog_data, self.test_response)
24+
25+
@patch('astroquery.mpc.MPC.query_object')
26+
def test_query_asteroid_number(self, mock_query):
27+
mock_query.return_value = self.test_response
28+
29+
self.broker.query('1627')
30+
self.assertEqual(self.broker._object_type, 'asteroid')
31+
self.assertEqual(self.broker._query_type, 'number')
32+
self.assertEqual(self.broker._object_term, '1627')
33+
self.assertEqual(self.broker.catalog_data, self.test_response)
34+
35+
@patch('astroquery.mpc.MPC.query_object')
36+
def test_query_asteroid_number_ws(self, mock_query):
37+
mock_query.return_value = self.test_response
38+
39+
self.broker.query(' 1627 ')
40+
self.assertEqual(self.broker._object_type, 'asteroid')
41+
self.assertEqual(self.broker._query_type, 'number')
42+
self.assertEqual(self.broker._object_term, '1627')
43+
self.assertEqual(self.broker.catalog_data, self.test_response)
44+
45+
@patch('astroquery.mpc.MPC.query_object')
46+
def test_query_comet_number(self, mock_query):
47+
mock_query.return_value = self.test_response
48+
49+
self.broker.query('67P')
50+
self.assertEqual(self.broker._object_type, 'comet')
51+
self.assertEqual(self.broker._query_type, 'number')
52+
self.assertEqual(self.broker._object_term, '67P')
53+
self.assertEqual(self.broker.catalog_data, self.test_response)
54+
55+
@patch('astroquery.mpc.MPC.query_object')
56+
def test_query_comet_number_ws(self, mock_query):
57+
mock_query.return_value = self.test_response
58+
59+
self.broker.query(' 67P ')
60+
self.assertEqual(self.broker._object_type, 'comet')
61+
self.assertEqual(self.broker._query_type, 'number')
62+
self.assertEqual(self.broker._object_term, '67P')
63+
self.assertEqual(self.broker.catalog_data, self.test_response)
64+
65+
@patch('astroquery.mpc.MPC.query_object')
66+
def test_query_provisional_cometlike(self, mock_query):
67+
# This tests if we have a provisional id such as '1999PA123' or '2025PM' which shouldn't
68+
# match with periodic comets despite being "number" followed by "P"
69+
mock_query.return_value = self.test_response
70+
71+
self.broker.query('2025PM')
72+
self.assertNotEqual(self.broker._object_type, 'comet')
73+
self.assertNotEqual(self.broker._query_type, 'number')
74+
self.assertEqual(self.broker._object_term, '2025PM')
75+
self.assertEqual(self.broker.catalog_data, self.test_response)
76+
77+
@patch('astroquery.mpc.MPC.query_object')
78+
def test_query_provisional_cometlike_ws(self, mock_query):
79+
# This tests if we have a provisional id such as '1999 PA123' or '2025 PM' which shouldn't
80+
# match with periodic comets despite being "number" followed by " P"
81+
mock_query.return_value = self.test_response
82+
83+
self.broker.query('2025 PM')
84+
self.assertNotEqual(self.broker._object_type, 'comet')
85+
self.assertNotEqual(self.broker._query_type, 'number')
86+
self.assertEqual(self.broker._object_term, '2025 PM')
87+
self.assertEqual(self.broker.catalog_data, self.test_response)
88+
89+
@patch('astroquery.mpc.MPC.query_object')
90+
def test_designation(self, mock_query):
91+
mock_query.return_value = self.test_response
92+
93+
self.broker.query('2025 MB18')
94+
self.assertEqual(self.broker._object_type, 'asteroid')
95+
self.assertEqual(self.broker._query_type, 'desig')
96+
self.assertEqual(self.broker._object_term, '2025 MB18')
97+
self.assertEqual(self.broker.catalog_data, self.test_response)
98+
99+
@patch('astroquery.mpc.MPC.query_object')
100+
def test_designation_cometish(self, mock_query):
101+
mock_query.return_value = self.test_response
102+
103+
self.broker.query('2022PA')
104+
self.assertEqual(self.broker._object_type, 'asteroid')
105+
self.assertEqual(self.broker._query_type, 'desig')
106+
self.assertEqual(self.broker._object_term, '2022PA')
107+
self.assertEqual(self.broker.catalog_data, self.test_response)
108+
109+
@patch('astroquery.mpc.MPC.query_object')
110+
def test_designation_cometish_with_ws(self, mock_query):
111+
mock_query.return_value = self.test_response
112+
113+
self.broker.query('2022 PA')
114+
self.assertEqual(self.broker._object_type, 'asteroid')
115+
self.assertEqual(self.broker._query_type, 'desig')
116+
self.assertEqual(self.broker._object_term, '2022 PA')
117+
self.assertEqual(self.broker.catalog_data, self.test_response)
118+
119+
@patch('astroquery.mpc.MPC.query_object')
120+
def test_designation_ws(self, mock_query):
121+
mock_query.return_value = self.test_response
122+
123+
self.broker.query(' 2025 MB18 ')
124+
self.assertEqual(self.broker._object_type, 'asteroid')
125+
self.assertEqual(self.broker._query_type, 'desig')
126+
self.assertEqual(self.broker._object_term, '2025 MB18')
127+
self.assertEqual(self.broker.catalog_data, self.test_response)
128+
129+
@patch('astroquery.mpc.MPC.query_object')
130+
def test_designation_nospace(self, mock_query):
131+
mock_query.return_value = self.test_response
132+
133+
self.broker.query('2025MB18')
134+
self.assertEqual(self.broker._object_type, 'asteroid')
135+
self.assertEqual(self.broker._query_type, 'desig')
136+
self.assertEqual(self.broker._object_term, '2025MB18')
137+
self.assertEqual(self.broker.catalog_data, self.test_response)
138+
139+
@patch('astroquery.mpc.MPC.query_object')
140+
def test_provisional_comets(self, mock_query):
141+
mock_query.return_value = self.test_response
142+
143+
comets = ['C/2024 S4', 'P/2017 A1', 'D/1853 X1', 'C/2001 OG108', 'P/2002 EJ57']
144+
for comet in comets:
145+
self.broker.query(comet)
146+
self.assertEqual(self.broker._object_type, 'comet', msg=f'Failure on _object_type for {comet}')
147+
self.assertEqual(self.broker._query_type, 'desig', msg=f'Failure on _query_type for {comet}')
148+
self.assertEqual(self.broker._object_term, comet, msg=f'Failure on _object_term for {comet}')
149+
self.assertEqual(self.broker.catalog_data, self.test_response)
150+
151+
152+
@tag('canary')
153+
class TestMPCHarvesterCanary(TestCase):
154+
def setUp(self):
155+
self.broker = MPCHarvester()
156+
157+
def test_query_number_only(self):
158+
self.broker.query('700000')
159+
target = self.broker.to_target()
160+
target.save(names=getattr(target, 'extra_names', []))
161+
# Only test things that are not likely to change (much) with time
162+
self.assertEqual(target.name, '700000')
163+
self.assertEqual(target.names, ['700000'])
164+
self.assertEqual(target.type, 'NON_SIDEREAL')
165+
self.assertEqual(target.scheme, 'MPC_MINOR_PLANET')
166+
self.assertEqual(target.ra, None)
167+
self.assertEqual(target.dec, None)
168+
self.assertAlmostEqual(target.eccentricity, 0.092, places=3)
169+
self.assertAlmostEqual(target.inclination, 4.1688, places=4)
170+
self.assertAlmostEqual(target.mean_anomaly, 315.8420, places=4)
171+
self.assertAlmostEqual(target.semimajor_axis, 2.6555, places=4)
172+
173+
def test_query_designation_only(self):
174+
self.broker.query('2025 MB18')
175+
target = self.broker.to_target()
176+
target.save(names=getattr(target, 'extra_names', []))
177+
# Only test things that are not likely to change (much) with time
178+
self.assertEqual(target.name, '2025 MB18')
179+
self.assertEqual(target.names, ['2025 MB18'])
180+
self.assertEqual(target.type, 'NON_SIDEREAL')
181+
self.assertEqual(target.scheme, 'MPC_MINOR_PLANET')
182+
self.assertEqual(target.ra, None)
183+
self.assertEqual(target.dec, None)
184+
self.assertAlmostEqual(target.eccentricity, 0.1398, places=4)
185+
self.assertAlmostEqual(target.inclination, 19.3561, places=4)
186+
187+
def test_query_name(self):
188+
self.broker.query('1627')
189+
target = self.broker.to_target()
190+
target.save(names=getattr(target, 'extra_names', []))
191+
# Only test things that are not likely to change (much) with time
192+
self.assertEqual(target.name, '1627')
193+
self.assertEqual(target.names, ['1627', 'Ivar'])
194+
self.assertEqual(target.type, 'NON_SIDEREAL')
195+
self.assertEqual(target.scheme, 'MPC_MINOR_PLANET')
196+
self.assertEqual(target.ra, None)
197+
self.assertEqual(target.dec, None)
198+
self.assertAlmostEqual(target.eccentricity, 0.3972, places=4)
199+
self.assertAlmostEqual(target.inclination, 8.4561, places=4)
200+
201+
def test_query_comet_name(self):
202+
self.broker.query('29P')
203+
target = self.broker.to_target()
204+
target.save(names=getattr(target, 'extra_names', []))
205+
# Only test things that are not likely to change (much) with time
206+
self.assertEqual(target.name, '29P')
207+
self.assertEqual(target.names, ['29P'])
208+
self.assertEqual(target.type, 'NON_SIDEREAL')
209+
self.assertEqual(target.scheme, 'MPC_COMET')
210+
self.assertEqual(target.ra, None)
211+
self.assertEqual(target.dec, None)
212+
self.assertAlmostEqual(target.eccentricity, 0.0433, places=4)
213+
self.assertAlmostEqual(target.lng_asc_node, 312.4041, places=4)
214+
215+
def test_query_comet_designation(self):
216+
self.broker.query('C/2017 K2')
217+
target = self.broker.to_target()
218+
target.save(names=getattr(target, 'extra_names', []))
219+
# Only test things that are not likely to change (much) with time
220+
self.assertEqual(target.name, 'C/2017 K2')
221+
self.assertEqual(target.names, ['C/2017 K2'])
222+
self.assertEqual(target.type, 'NON_SIDEREAL')
223+
self.assertEqual(target.scheme, 'MPC_COMET')
224+
self.assertEqual(target.ra, None)
225+
self.assertEqual(target.dec, None)
226+
self.assertAlmostEqual(target.perihdist, 1.7998, places=4)
227+
self.assertAlmostEqual(target.arg_of_perihelion, 236.17910, places=4)
8228

9229

10230
class TestMPCExplorerHarvester(TestCase):

0 commit comments

Comments
 (0)