diff --git a/shotgun_api3/lib/mockgun/mockgun.py b/shotgun_api3/lib/mockgun/mockgun.py index 36b98dd5d..3b06e4e0e 100644 --- a/shotgun_api3/lib/mockgun/mockgun.py +++ b/shotgun_api3/lib/mockgun/mockgun.py @@ -293,6 +293,27 @@ def find( # handle the ordering of the recordset if order: # order: [{"field_name": "code", "direction": "asc"}, ... ] + + def sort_none(k, order_field): + """ + Handle sorting of None consistently. + + Note: Doesn't handle [checkbox, serializable, url]. + """ + field_type = self._get_field_type(k["type"], order_field) + value = k[order_field] + if value is not None: + return value + elif field_type in ("number", "percent", "duration"): + return 0 + elif field_type == "float": + return 0.0 + elif field_type in ("text", "entity_type", "date", "list", "status_list"): + return "" + elif field_type == "date_time": + return datetime.datetime(datetime.MINYEAR, 1, 1) + return None + for order_entry in order: if "field_name" not in order_entry: raise ValueError("Order clauses must be list of dicts with keys 'field_name' and 'direction'!") @@ -305,7 +326,11 @@ def find( else: raise ValueError("Unknown ordering direction") - results = sorted(results, key=lambda k: k[order_field], reverse=desc_order) + results = sorted( + results, + key=lambda k: sort_none(k, order_field), + reverse=desc_order, + ) if fields is None: fields = set(["type", "id"]) diff --git a/tests/test_mockgun.py b/tests/test_mockgun.py index 84e5cb2e7..5a0407dab 100644 --- a/tests/test_mockgun.py +++ b/tests/test_mockgun.py @@ -35,6 +35,7 @@ and can be run on their own by typing "python test_mockgun.py". """ +import datetime import re import os import unittest @@ -402,7 +403,9 @@ def setUp(self): "Shot", { "code": "shot1", - "project": self._prj1_link + "project": self._prj1_link, + "description": "a", + "sg_cut_order": 2 } ) @@ -410,7 +413,8 @@ def setUp(self): "Shot", { "code": "shot2", - "project": self._prj1_link + "project": self._prj1_link, + "sg_cut_order": 1 } ) @@ -418,10 +422,20 @@ def setUp(self): "Shot", { "code": "shot3", - "project": self._prj2_link + "project": self._prj2_link, + "description": "b" } ) + self._user1 = self._mockgun.create( + "HumanUser", {"login": "user1", "password_strength": 0.2} + ) + + self._user2 = self._mockgun.create( + "HumanUser", + {"login": "user2", "created_at": datetime.datetime(2025, 1, 1)} + ) + def test_simple_filter_operators(self): """ Tests a simple use of the filter_operator. @@ -452,6 +466,50 @@ def test_simple_filter_operators(self): self.assertEqual(len(shots), 0) + def test_ordered_filter_operator(self): + """ + Test use of the order feature of filter_operator on supported data types. + """ + find_args = ["Shot", [], ["code"]] + + # str field + shots = self._mockgun.find( + *find_args, + order=[{"field_name": "description", "direction": "asc"}] + ) + self.assertEqual([s["code"] for s in shots], ["shot2", "shot1", "shot3"]) + + shots = self._mockgun.find( + *find_args, + order=[{"field_name": "description", "direction": "desc"}] + ) + self.assertEqual([s["code"] for s in shots], ["shot3", "shot1", "shot2"]) + + # int field + shots = self._mockgun.find( + *find_args, + order=[{"field_name": "sg_cut_order", "direction": "asc"}] + ) + self.assertEqual([s["code"] for s in shots], ["shot3", "shot2", "shot1"]) + + # float field + users = self._mockgun.find( + "HumanUser", + [], + ["login"], + order=[{"field_name": "password_strength", "direction": "asc"}] + ) + self.assertEqual([u["login"] for u in users], ["user2", "user1"]) + + # date_time field + users = self._mockgun.find( + "HumanUser", + [], + ["login"], + order=[{"field_name": "created_at", "direction": "asc"}] + ) + self.assertEqual([u["login"] for u in users], ["user1", "user2"]) + def test_nested_filter_operators(self): """ Tests a the use of the filter_operator nested