diff --git a/panoptes_client/set_member_subject.py b/panoptes_client/set_member_subject.py index 298e52f..c7ab2db 100644 --- a/panoptes_client/set_member_subject.py +++ b/panoptes_client/set_member_subject.py @@ -4,7 +4,9 @@ class SetMemberSubject(PanoptesObject): _api_slug = 'set_member_subjects' _link_slug = 'set_member_subjects' - _edit_attributes = () + _edit_attributes = ( + 'priority', + ) LinkResolver.register(SetMemberSubject) LinkResolver.register(SetMemberSubject, 'set_member_subject') diff --git a/panoptes_client/subject.py b/panoptes_client/subject.py index db15562..1a9f23b 100644 --- a/panoptes_client/subject.py +++ b/panoptes_client/subject.py @@ -1,4 +1,5 @@ from panoptes_client.subject_workflow_status import SubjectWorkflowStatus +from panoptes_client.set_member_subject import SetMemberSubject _OLD_STR_TYPES = (str,) try: @@ -467,6 +468,38 @@ def save_attached_image( upload_exec.shutdown(wait=True) return future_result + def update_priority(self, priority, subject_set_id=None): + """ + Update the priority of this subject in the subject set. + + If subject_set_id is not provided, the priority will be updated in all subject sets that this subject belongs to. + + - **priority** is an integer value that represents the priority of the subject in the subject set. + + Examples:: + + subject.update_priority(1) + subject.update_priority(2, subject_set_id=1234) + """ + + if self.id is None: + raise ObjectNotSavedException + + self.metadata['priority'] = priority + self.save() + + if subject_set_id is not None: + subject_sets = [subject_set_id] + else: + subject_sets = [s.id for s in self.links.subject_sets] + + for ss_id in subject_sets: + sms = next(SetMemberSubject.where( + subject_set_id=ss_id, + subject_id=self.id)) + sms.priority = priority + sms.save() + class UnknownMediaException(Exception): pass diff --git a/panoptes_client/tests/test_subject.py b/panoptes_client/tests/test_subject.py index 15b5e83..c1c1a8a 100644 --- a/panoptes_client/tests/test_subject.py +++ b/panoptes_client/tests/test_subject.py @@ -1,9 +1,10 @@ import io +import mimetypes import unittest -from unittest.mock import patch, mock_open +from unittest.mock import patch, mock_open, MagicMock +from panoptes_client.panoptes import ObjectNotSavedException from panoptes_client.subject import Subject, UnknownMediaException -import mimetypes class TestSubject(unittest.TestCase): @@ -39,7 +40,7 @@ def test_add_location_magic_detection(self, mock_magic): self.assertIn("locations", self.subject.modified_attributes) mock_magic.from_buffer.assert_called_with(data, mime=True) - @patch.object(mimetypes, 'guess_type', return_value=("image/jpeg", None)) + @patch("panoptes_client.subject.mimetypes.guess_type", return_value=("image/jpeg", None)) def test_add_location_mimetypes_detection(self, mock_guess_type): import panoptes_client.subject as subject_module subject_module.MEDIA_TYPE_DETECTION = 'mimetypes' @@ -56,4 +57,75 @@ def test_add_location_invalid_manual_mimetype(self): data = b"fake data" fake_file = io.BytesIO(data) with self.assertRaises(UnknownMediaException): - self.subject.add_location(fake_file, manual_mimetype="application/javascript") \ No newline at end of file + self.subject.add_location(fake_file, manual_mimetype="application/javascript") + + def test_update_priority_requires_saved_subject(self): + with self.assertRaises(ObjectNotSavedException): + self.subject.update_priority(1) + + def test_update_priority_updates_priority_for_saved_subject(self): + self.subject.id = 123 + set_member_subject_mock = MagicMock() + + with patch.object(self.subject, "save") as mock_save: + with patch( + "panoptes_client.subject.SetMemberSubject.where", + return_value=iter([set_member_subject_mock]), + ) as mock_where: + self.subject.update_priority( + 5, + subject_set_id=456, + ) + + self.assertEqual(self.subject.metadata["priority"], 5) + + mock_save.assert_called_once_with() + mock_where.assert_called_once_with( + subject_set_id=456, + subject_id=123, + ) + + self.assertEqual(set_member_subject_mock.priority, 5) + set_member_subject_mock.save.assert_called_once_with() + + def test_update_priority_updates_all_subject_sets(self): + self.subject.id = 123 + + subject_set_1 = MagicMock(id=456) + subject_set_2 = MagicMock(id=789) + set_member_subject_1 = MagicMock() + set_member_subject_2 = MagicMock() + + with patch.object(self.subject, "save") as mock_save, \ + patch( + "panoptes_client.panoptes.LinkResolver.__getattr__", + return_value=[subject_set_1, subject_set_2], + ), \ + patch( + "panoptes_client.subject.SetMemberSubject.where", + side_effect=[ + iter([set_member_subject_1]), + iter([set_member_subject_2]), + ], + ) as mock_where: + self.subject.update_priority(5) + + self.assertEqual(self.subject.metadata["priority"], 5) + + mock_save.assert_called_once_with() + + self.assertEqual(mock_where.call_count, 2) + mock_where.assert_any_call( + subject_set_id=456, + subject_id=123, + ) + mock_where.assert_any_call( + subject_set_id=789, + subject_id=123, + ) + + self.assertEqual(set_member_subject_1.priority, 5) + set_member_subject_1.save.assert_called_once_with() + + self.assertEqual(set_member_subject_2.priority, 5) + set_member_subject_2.save.assert_called_once_with()