From 02eb878f8596a3c75afc5c79f668604c5fc68b5b Mon Sep 17 00:00:00 2001
From: Erwan Rouchet <rouchet@teklia.com>
Date: Tue, 24 Jan 2023 18:17:50 +0100
Subject: [PATCH] Fix more imports

---
 .../documents/tests/commands/test_cleanup.py  |  6 +--
 arkindex/ponos/api.py                         |  2 +-
 arkindex/ponos/authentication.py              |  2 +-
 arkindex/ponos/models.py                      |  4 +-
 arkindex/ponos/serializers.py                 | 40 +++++++++----------
 arkindex/ponos/tests/test_api.py              | 40 +++++++++----------
 arkindex/ponos/tests/test_models.py           |  6 +--
 .../ponos/tests/test_tasks_attribution.py     |  4 +-
 arkindex/ponos/tests/test_util.py             | 12 +++---
 arkindex/ponos/tests/test_workflow.py         |  4 +-
 arkindex/process/utils.py                     |  2 +-
 arkindex/project/tests/test_ponos_view.py     |  2 +-
 arkindex/users/models.py                      |  2 +-
 13 files changed, 63 insertions(+), 63 deletions(-)

diff --git a/arkindex/documents/tests/commands/test_cleanup.py b/arkindex/documents/tests/commands/test_cleanup.py
index 0f5f09dc0b..e886a8f24c 100644
--- a/arkindex/documents/tests/commands/test_cleanup.py
+++ b/arkindex/documents/tests/commands/test_cleanup.py
@@ -391,7 +391,7 @@ class TestCleanupCommand(FixtureTestCase):
         self.assertEqual(unsupported_s3_artifact.delete.call_count, 0)
         self.assertEqual(broken_s3_artifact.delete.call_count, 1)
 
-    @patch('ponos.models.s3')
+    @patch('arkindex.ponos.models.s3')
     def test_cleanup_expired_workflows(self, ponos_s3_mock, s3_mock):
         farm = Farm.objects.first()
 
@@ -496,7 +496,7 @@ class TestCleanupCommand(FixtureTestCase):
 
         return revision, artifact
 
-    @patch('ponos.models.s3')
+    @patch('arkindex.ponos.models.s3')
     def test_cleanup_expired_workflows_docker_images(self, ponos_s3_mock, s3_mock):
         """
         Artifacts used as Docker images for worker versions from expired workflows
@@ -577,7 +577,7 @@ class TestCleanupCommand(FixtureTestCase):
 
         self.assertEqual(ponos_s3_mock.Object().delete.call_count, 4)
 
-    @patch('ponos.models.s3')
+    @patch('arkindex.ponos.models.s3')
     def test_cleanup_expired_workflows_null(self, ponos_s3_mock, s3_mock):
         repo = Repository.objects.get(url='http://my_repo.fake/workers/worker')
 
diff --git a/arkindex/ponos/api.py b/arkindex/ponos/api.py
index 84e34f2bcb..a269c4bf91 100644
--- a/arkindex/ponos/api.py
+++ b/arkindex/ponos/api.py
@@ -88,7 +88,7 @@ class PublicKeyEndpoint(APIView):
 class WorkflowDetails(RetrieveUpdateAPIView):
     """
     Retrieve information about a workflow, or update its state.
-    Updating a workflow's state to :attr:`~ponos.models.State.Stopping` will cause it to stop.
+    Updating a workflow's state to :attr:`~arkindex.ponos.models.State.Stopping` will cause it to stop.
     """
 
     queryset = Workflow.objects.prefetch_related("tasks__parents").annotate(
diff --git a/arkindex/ponos/authentication.py b/arkindex/ponos/authentication.py
index 58192cb896..012206b3b8 100644
--- a/arkindex/ponos/authentication.py
+++ b/arkindex/ponos/authentication.py
@@ -69,5 +69,5 @@ class AgentAuthenticationExtension(SimpleJWTScheme):
     drf_spectacular extension to make it recognize the AgentAuthentication.
     """
 
-    target_class = "ponos.authentication.AgentAuthentication"
+    target_class = "arkindex.ponos.authentication.AgentAuthentication"
     name = "agentAuth"
diff --git a/arkindex/ponos/models.py b/arkindex/ponos/models.py
index 1adee90bbd..37784016c7 100644
--- a/arkindex/ponos/models.py
+++ b/arkindex/ponos/models.py
@@ -43,13 +43,13 @@ Can optionally be associated with a :class:`Task`.
 Action.action.__doc__ = """
 Type of action to perform.
 
-:type: ponos.models.ActionType
+:type: arkindex.ponos.models.ActionType
 """
 
 Action.task.__doc__ = """
 Optionally associated Task, when relevant.
 
-:type: ponos.models.Task
+:type: arkindex.ponos.models.Task
 """
 
 logger = logging.getLogger(__name__)
diff --git a/arkindex/ponos/serializers.py b/arkindex/ponos/serializers.py
index daaefd1a9a..ec3b8ac24e 100644
--- a/arkindex/ponos/serializers.py
+++ b/arkindex/ponos/serializers.py
@@ -31,7 +31,7 @@ logger = logging.getLogger(__name__)
 
 class FarmSerializer(serializers.ModelSerializer):
     """
-    Serializes a :class:`~ponos.models.Farm` instance.
+    Serializes a :class:`~arkindex.ponos.models.Farm` instance.
     """
 
     class Meta:
@@ -44,7 +44,7 @@ class FarmSerializer(serializers.ModelSerializer):
 
 class GPUSerializer(serializers.ModelSerializer):
     """
-    Serializes a :class:`~ponos.models.GPU` instance for public access.
+    Serializes a :class:`~arkindex.ponos.models.GPU` instance for public access.
     """
 
     class Meta:
@@ -65,7 +65,7 @@ class GPUSerializer(serializers.ModelSerializer):
 
 class AgentLightSerializer(serializers.ModelSerializer):
     """
-    Serializes a :class:`~ponos.models.Agent` instance for public access.
+    Serializes a :class:`~arkindex.ponos.models.Agent` instance for public access.
     """
 
     farm = FarmSerializer()
@@ -90,7 +90,7 @@ class AgentLightSerializer(serializers.ModelSerializer):
 
 class TaskLightSerializer(serializers.ModelSerializer):
     """
-    Serializes a :class:`~ponos.models.Task` instance without logs or agent information.
+    Serializes a :class:`~arkindex.ponos.models.Task` instance without logs or agent information.
     Used to list tasks inside a workflow.
     """
 
@@ -129,7 +129,7 @@ class TaskLightSerializer(serializers.ModelSerializer):
 
 class TaskSerializer(TaskLightSerializer):
     """
-    Serializes a :class:`~ponos.models.Task` instance with logs and agent information.
+    Serializes a :class:`~arkindex.ponos.models.Task` instance with logs and agent information.
     """
 
     logs = serializers.CharField(source="short_logs")
@@ -157,8 +157,8 @@ class TaskSerializer(TaskLightSerializer):
     def update(self, instance: Task, validated_data) -> Task:
         """
         Perform updates on a task instance.
-        If a task gets marked as :attr:`~ponos.models.State.Completed`, this will set
-        its child tasks to :attr:`~ponos.models.State.Pending` if all of their dependencies are met.
+        If a task gets marked as :attr:`~arkindex.ponos.models.State.Completed`, this will set
+        its child tasks to :attr:`~arkindex.ponos.models.State.Pending` if all of their dependencies are met.
         """
 
         # Free the GPU when a task gets a finished state update
@@ -217,7 +217,7 @@ class TaskSerializer(TaskLightSerializer):
 
 class TaskTinySerializer(TaskSerializer):
     """
-    Serializes a :class:`~ponos.models.Task` instance with only its state.
+    Serializes a :class:`~arkindex.ponos.models.Task` instance with only its state.
     Used by humans to update a task.
     """
 
@@ -249,7 +249,7 @@ class TaskTinySerializer(TaskSerializer):
 
 class AgentStateSerializer(AgentLightSerializer):
     """
-    Serialize an :class:`~ponos.models.Agent` with its state information
+    Serialize an :class:`~arkindex.ponos.models.Agent` with its state information
     And the GPU state
     """
 
@@ -261,7 +261,7 @@ class AgentStateSerializer(AgentLightSerializer):
 
 class AgentDetailsSerializer(AgentLightSerializer):
     """
-    Serialize an :class:`~ponos.models.Agent` with its running tasks
+    Serialize an :class:`~arkindex.ponos.models.Agent` with its running tasks
     """
 
     running_tasks = serializers.SerializerMethodField()
@@ -280,7 +280,7 @@ class AgentDetailsSerializer(AgentLightSerializer):
 
 class GPUCreateSerializer(serializers.Serializer):
     """
-    Serialize a :class:`~ponos.models.GPU` attached to an Agent
+    Serialize a :class:`~arkindex.ponos.models.GPU` attached to an Agent
     """
 
     id = serializers.UUIDField()
@@ -291,7 +291,7 @@ class GPUCreateSerializer(serializers.Serializer):
 
 class AgentCreateSerializer(serializers.ModelSerializer):
     """
-    Serializer used to register a new :class:`~ponos.models.Agent`.
+    Serializer used to register a new :class:`~arkindex.ponos.models.Agent`.
     """
 
     public_key = PublicKeyField(write_only=True)
@@ -387,7 +387,7 @@ class AgentCreateSerializer(serializers.ModelSerializer):
 
 class WorkflowSerializer(serializers.ModelSerializer):
     """
-    Serializes a :class:`~ponos.models.Workflow` instance with its tasks.
+    Serializes a :class:`~arkindex.ponos.models.Workflow` instance with its tasks.
     """
 
     tasks = TaskLightSerializer(many=True, read_only=True)
@@ -407,8 +407,8 @@ class WorkflowSerializer(serializers.ModelSerializer):
 
     def validate_state(self, state: State) -> None:
         """
-        When performing updates, prevents updating to any state other than :attr:`~ponos.models.State.Stopping`
-        and restricts to :attr:`~ponos.models.State.Pending` or :attr:`~ponos.models.State.Running` workflows.
+        When performing updates, prevents updating to any state other than :attr:`~arkindex.ponos.models.State.Stopping`
+        and restricts to :attr:`~arkindex.ponos.models.State.Pending` or :attr:`~arkindex.ponos.models.State.Running` workflows.
         """
         if state != State.Stopping:
             raise ValidationError("Can only change the state to 'stopping'")
@@ -420,7 +420,7 @@ class WorkflowSerializer(serializers.ModelSerializer):
 
 class ActionSerializer(serializers.Serializer):
     """
-    Serializes an :const:`~ponos.models.Action` instance.
+    Serializes an :const:`~arkindex.ponos.models.Action` instance.
     """
 
     action = EnumField(ActionType)
@@ -443,7 +443,7 @@ class AgentActionsSerializer(serializers.Serializer):
 
 class TaskDefinitionSerializer(serializers.ModelSerializer):
     """
-    Serializes a :class:`~ponos.models.Task` instance
+    Serializes a :class:`~arkindex.ponos.models.Task` instance
     to provide startup information to assigned agents.
     """
 
@@ -498,7 +498,7 @@ class TaskDefinitionSerializer(serializers.ModelSerializer):
 
 class ArtifactSerializer(serializers.ModelSerializer):
     """
-    Serializes a :class:`~ponos.models.Artifact` instance to allow
+    Serializes a :class:`~arkindex.ponos.models.Artifact` instance to allow
     agents to create them, and users to list and retrieve them
     """
 
@@ -552,7 +552,7 @@ class ArtifactSerializer(serializers.ModelSerializer):
 
 class NewTaskSerializer(serializers.ModelSerializer):
     """
-    Serializes a :class:`~ponos.models.Task` instance to permit creation by a parent.
+    Serializes a :class:`~arkindex.ponos.models.Task` instance to permit creation by a parent.
     """
 
     workflow_id = serializers.UUIDField()
@@ -612,7 +612,7 @@ class NewTaskSerializer(serializers.ModelSerializer):
 
 class ClearTextSecretSerializer(serializers.ModelSerializer):
     """
-    Serializes a :class:`~ponos.models.Secret` instance with its content in cleartext.
+    Serializes a :class:`~arkindex.ponos.models.Secret` instance with its content in cleartext.
     """
 
     content = serializers.SerializerMethodField(read_only=True)
diff --git a/arkindex/ponos/tests/test_api.py b/arkindex/ponos/tests/test_api.py
index 62cad0d07b..1335b793ba 100644
--- a/arkindex/ponos/tests/test_api.py
+++ b/arkindex/ponos/tests/test_api.py
@@ -203,8 +203,8 @@ class TestAPI(APITestCase):
         data = resp.json()
         self.assertEqual(data, {"state": ["Can only change the state to 'stopping'"]})
 
-    @patch("ponos.aws.s3")
-    @patch("ponos.models.s3")
+    @patch("arkindex.ponos.aws.s3")
+    @patch("arkindex.ponos.models.s3")
     def test_task_details(self, s3_mock, aws_s3_mock):
         s3_mock.Object.return_value.bucket_name = "ponos"
         s3_mock.Object.return_value.key = "somelog"
@@ -248,9 +248,9 @@ class TestAPI(APITestCase):
             call("get_object", Params={"Bucket": "ponos", "Key": "somelog"}),
         )
 
-    @patch("ponos.aws.s3")
-    @patch("ponos.models.s3")
-    @patch("ponos.serializers.timezone")
+    @patch("arkindex.ponos.aws.s3")
+    @patch("arkindex.ponos.models.s3")
+    @patch("arkindex.ponos.serializers.timezone")
     def test_task_final_updates_workflow_finished(
         self, timezone_mock, s3_mock, aws_s3_mock
     ):
@@ -293,9 +293,9 @@ class TestAPI(APITestCase):
                 self.workflow.refresh_from_db()
                 self.assertEqual(self.workflow.finished, expected_datetime)
 
-    @patch("ponos.signals.task_failure.send_robust")
-    @patch("ponos.aws.s3")
-    @patch("ponos.models.s3")
+    @patch("arkindex.ponos.signals.task_failure.send_robust")
+    @patch("arkindex.ponos.aws.s3")
+    @patch("arkindex.ponos.models.s3")
     def test_task_final_not_completed_triggers_signal(
         self, s3_mock, aws_s3_mock, send_mock
     ):
@@ -334,8 +334,8 @@ class TestAPI(APITestCase):
                 send_mock.reset_mock()
 
     @patch("django.contrib.auth")
-    @patch("ponos.aws.s3")
-    @patch("ponos.models.s3")
+    @patch("arkindex.ponos.aws.s3")
+    @patch("arkindex.ponos.models.s3")
     def test_task_update_gpu(self, s3_mock, aws_s3_mock, auth_mock):
         auth_mock.signals.user_logged_in = None
         s3_mock.Object.return_value.bucket_name = "ponos"
@@ -475,8 +475,8 @@ class TestAPI(APITestCase):
         # All GPUs are available
         self.assertFalse(GPU.objects.filter(task__isnull=False).exists())
 
-    @patch("ponos.aws.s3")
-    @patch("ponos.models.s3")
+    @patch("arkindex.ponos.aws.s3")
+    @patch("arkindex.ponos.models.s3")
     def test_task_logs_unicode_error(self, s3_mock, aws_s3_mock):
         """
         Ensure the Task.short_logs property is able to handle sliced off Unicode characters.
@@ -500,8 +500,8 @@ class TestAPI(APITestCase):
 
         self.assertEqual(data["logs"], "�Failed successfully")
 
-    @patch("ponos.serializers.check_agent_key")
-    @patch("ponos.serializers.timezone")
+    @patch("arkindex.ponos.serializers.check_agent_key")
+    @patch("arkindex.ponos.serializers.timezone")
     def test_agent_create(self, timezone_mock, check_mock):
         check_mock.return_value = True
         timezone_mock.now.return_value = timezone.datetime(2000, 1, 1, 12).astimezone()
@@ -583,8 +583,8 @@ class TestAPI(APITestCase):
             },
         )
 
-    @patch("ponos.serializers.check_agent_key")
-    @patch("ponos.serializers.timezone")
+    @patch("arkindex.ponos.serializers.check_agent_key")
+    @patch("arkindex.ponos.serializers.timezone")
     def test_agent_update(self, timezone_mock, check_mock):
         check_mock.return_value = True
         timezone_mock.now.return_value = timezone.datetime(2000, 1, 1, 12).astimezone()
@@ -653,8 +653,8 @@ class TestAPI(APITestCase):
             },
         )
 
-    @patch("ponos.serializers.check_agent_key")
-    @patch("ponos.serializers.timezone")
+    @patch("arkindex.ponos.serializers.check_agent_key")
+    @patch("arkindex.ponos.serializers.timezone")
     def test_agent_create_existing_gpu(self, timezone_mock, check_mock):
         check_mock.return_value = True
         timezone_mock.now.return_value = timezone.datetime(2000, 1, 1, 12).astimezone()
@@ -704,8 +704,8 @@ class TestAPI(APITestCase):
         # Existing agent no longer has any assigned GPUs, since the new agent stole them
         self.assertFalse(self.agent.gpus.exists())
 
-    @patch("ponos.serializers.check_agent_key")
-    @patch("ponos.serializers.timezone")
+    @patch("arkindex.ponos.serializers.check_agent_key")
+    @patch("arkindex.ponos.serializers.timezone")
     def test_agent_update_existing_gpu(self, timezone_mock, check_mock):
         check_mock.return_value = True
         timezone_mock.now.return_value = timezone.datetime(2000, 1, 1, 12).astimezone()
diff --git a/arkindex/ponos/tests/test_models.py b/arkindex/ponos/tests/test_models.py
index d7ba8ae4dc..50990acf0b 100644
--- a/arkindex/ponos/tests/test_models.py
+++ b/arkindex/ponos/tests/test_models.py
@@ -89,7 +89,7 @@ class TestModels(TestCase):
         with self.assertRaisesRegex(Exception, r"Missing a PONOS_PRIVATE_KEY"):
             build_aes_cipher(nonce=self.nonce)
 
-    @patch("ponos.models.settings")
+    @patch("arkindex.ponos.models.settings")
     def test_build_aes_cipher(self, settings_mock):
         """
         AES encryption key should be a derivate of the Ponos server secret key
@@ -105,7 +105,7 @@ class TestModels(TestCase):
         cipher = build_aes_cipher(nonce=self.nonce)
         self.assertNotEqual(cipher.encryptor().update(b"hey"), b"lM\x8d")
 
-    @patch("ponos.models.settings")
+    @patch("arkindex.ponos.models.settings")
     def test_secret_encrypt_decrypt(self, settings_mock):
         _, path = tempfile.mkstemp()
         settings_mock.PONOS_PRIVATE_KEY = path
@@ -159,7 +159,7 @@ class TestModels(TestCase):
         self.assertFalse(init_task.requires_gpu)
         self.assertTrue(worker_task.requires_gpu)
 
-    @patch("ponos.models.timezone")
+    @patch("arkindex.ponos.models.timezone")
     def test_task_expiry_default(self, timezone_mock):
         timezone_mock.now.return_value = timezone.datetime(3000, 1, 12).astimezone()
         # Expecting a default expiry 30 days after timezone.now
diff --git a/arkindex/ponos/tests/test_tasks_attribution.py b/arkindex/ponos/tests/test_tasks_attribution.py
index 52c2117768..fcf272031c 100644
--- a/arkindex/ponos/tests/test_tasks_attribution.py
+++ b/arkindex/ponos/tests/test_tasks_attribution.py
@@ -64,7 +64,7 @@ class TasksAttributionTestCase(TestCase):
         )
 
     def _active_agent(self, agent):
-        from ponos.models import AGENT_TIMEOUT
+        from arkindex.ponos.models import AGENT_TIMEOUT
 
         return timezone.now() - agent.last_ping < AGENT_TIMEOUT
 
@@ -168,7 +168,7 @@ class TasksAttributionTestCase(TestCase):
         new_tasks = agent.next_tasks()
         self.assertCountEqual(tasks, new_tasks)
 
-    @patch("ponos.models.AGENT_TIMEOUT", timedelta(seconds=1))
+    @patch("arkindex.ponos.models.AGENT_TIMEOUT", timedelta(seconds=1))
     def test_non_active_agent(self):
         """
         If an agent does not respond, task should be attributed to other running agents
diff --git a/arkindex/ponos/tests/test_util.py b/arkindex/ponos/tests/test_util.py
index a85f8c5381..9d21c2a28e 100644
--- a/arkindex/ponos/tests/test_util.py
+++ b/arkindex/ponos/tests/test_util.py
@@ -7,27 +7,27 @@ from arkindex.ponos import get_ponos_run, get_ponos_task_id, is_ponos_task
 
 class TestUtil(TestCase):
     def test_is_ponos_task(self):
-        with patch.dict("ponos.helpers.os.environ", clear=True):
+        with patch.dict("arkindex.ponos.helpers.os.environ", clear=True):
             self.assertFalse(is_ponos_task())
 
         with patch.dict(
-            "ponos.helpers.os.environ", PONOS_TASK=str(uuid.uuid4()), clear=True
+            "arkindex.ponos.helpers.os.environ", PONOS_TASK=str(uuid.uuid4()), clear=True
         ):
             self.assertTrue(is_ponos_task())
 
     def test_get_ponos_task_id(self):
-        with patch.dict("ponos.helpers.os.environ", clear=True):
+        with patch.dict("arkindex.ponos.helpers.os.environ", clear=True):
             self.assertIsNone(get_ponos_task_id())
 
         task_id = uuid.uuid4()
         with patch.dict(
-            "ponos.helpers.os.environ", PONOS_TASK=str(task_id), clear=True
+            "arkindex.ponos.helpers.os.environ", PONOS_TASK=str(task_id), clear=True
         ):
             self.assertEqual(get_ponos_task_id(), task_id)
 
     def test_get_ponos_run(self):
-        with patch.dict("ponos.helpers.os.environ", clear=True):
+        with patch.dict("arkindex.ponos.helpers.os.environ", clear=True):
             self.assertIsNone(get_ponos_run())
 
-        with patch.dict("ponos.helpers.os.environ", PONOS_RUN="42", clear=True):
+        with patch.dict("arkindex.ponos.helpers.os.environ", PONOS_RUN="42", clear=True):
             self.assertEqual(get_ponos_run(), 42)
diff --git a/arkindex/ponos/tests/test_workflow.py b/arkindex/ponos/tests/test_workflow.py
index b4e8ea1de7..d2324351a3 100644
--- a/arkindex/ponos/tests/test_workflow.py
+++ b/arkindex/ponos/tests/test_workflow.py
@@ -164,8 +164,8 @@ class WorkflowTestCase(TestCase):
         t3.save()
         self.assertEqual(w.state, State.Running)
 
-    @patch("ponos.models.s3")
-    @patch("ponos.models.Task.s3_logs_get_url")
+    @patch("arkindex.ponos.models.s3")
+    @patch("arkindex.ponos.models.Task.s3_logs_get_url")
     def test_task_parents_update(self, s3_mock, s3_logs_mock):
         w = Workflow.objects.create(farm=self.farm, recipe=MULTIPLE_PARENTS_TASK)
         w.start()
diff --git a/arkindex/process/utils.py b/arkindex/process/utils.py
index 912f1a531a..71f1b5060d 100644
--- a/arkindex/process/utils.py
+++ b/arkindex/process/utils.py
@@ -15,7 +15,7 @@ def get_default_farm_id():
     This method uses a global variable to store the cached value retrieved in a idempotant way.
     It could return an invalid farm ID in case the default farm is removed or updated during runtime.
     """
-    from ponos.models import Farm
+    from arkindex.ponos.models import Farm
 
     global __default_farm_id
     if __default_farm_id is not None:
diff --git a/arkindex/project/tests/test_ponos_view.py b/arkindex/project/tests/test_ponos_view.py
index cabb9626d3..c67a8b0b8a 100644
--- a/arkindex/project/tests/test_ponos_view.py
+++ b/arkindex/project/tests/test_ponos_view.py
@@ -42,7 +42,7 @@ class TestPonosView(FixtureAPITestCase):
             last_ping='1999-09-09',
         )
 
-    @patch("ponos.models.settings")
+    @patch("arkindex.ponos.models.settings")
     def test_internal_user_retrieve_secret(self, settings_mock):
         """
         Only Arkindex internal users should be able to retrieve a Ponos secret
diff --git a/arkindex/users/models.py b/arkindex/users/models.py
index d2488db84c..f5fb40b01e 100644
--- a/arkindex/users/models.py
+++ b/arkindex/users/models.py
@@ -126,7 +126,7 @@ class User(AbstractBaseUser):
 
     def has_scope(self, scope):
         assert isinstance(scope, Scope), 'Scope should be from the Scope enum'
-        # TODO: Could be optimized in a way similar to ponos.models.Workflow.get_state()
+        # TODO: Could be optimized in a way similar to arkindex.ponos.models.Workflow.get_state()
         # See https://gitlab.com/teklia/arkindex/ponos/-/blob/e5989f3e1dd1f6d7d93ba940b7ed2c5471ddd34f/ponos/models.py#L370
         return self.user_scopes.filter(scope=scope).exists()
 
-- 
GitLab