Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • arkindex/backend
1 result
Show changes
Commits on Source (6)
Showing
with 73 additions and 52 deletions
repos:
- repo: https://github.com/pre-commit/mirrors-isort
rev: v5.10.1
hooks:
- id: isort
- repo: https://github.com/pycqa/flake8
rev: 3.9.2
hooks:
- id: flake8
additional_dependencies:
- 'flake8-copyright==0.2.2'
- 'flake8-debugger==3.1.0'
- 'flake8-quotes==3.3.2'
- repo: https://github.com/astral-sh/ruff-pre-commit
# Ruff version.
rev: v0.3.7
......
......@@ -440,7 +440,7 @@ class ManageClassificationsSelection(SelectionMixin, CorpusACLMixin, CreateAPIVi
mode = serializer.validated_data["mode"]
if mode == ClassificationMode.Create:
return self.create(corpus, request, *args, **kwargs)
elif mode == ClassificationMode.Validate:
if mode == ClassificationMode.Validate:
elements = self.get_selection(corpus.id)
Classification.objects.filter(
element__in=elements,
......
......@@ -45,7 +45,7 @@ class InterpretedDate(object):
"""
if self.month and self.day:
return DatePrecision.Day
elif self.month:
if self.month:
return DatePrecision.Month
return DatePrecision.Year
......
......@@ -667,8 +667,7 @@ class ElementSerializer(ElementTinySerializer):
validated_data.update(image=image, polygon=polygon)
instance = super().update(instance, validated_data)
return instance
return super().update(instance, validated_data)
class ElementNeighborsSerializer(serializers.ModelSerializer):
......
......@@ -32,8 +32,7 @@ class EntityTypeSerializer(serializers.ModelSerializer):
corpus = data.get("corpus")
if self.instance and corpus:
raise ValidationError({"corpus": ["It is not possible to update an Entity Type's corpus."]})
data = super().to_internal_value(data)
return data
return super().to_internal_value(data)
def validate(self, data):
name = data.get("name") if "name" in data else self.instance.name
......
......@@ -21,7 +21,7 @@ from arkindex.documents.models import (
Transcription,
TranscriptionEntity,
)
from arkindex.ponos.models import Task
from arkindex.ponos.models import Artifact, Task
from arkindex.process.models import Process, ProcessDatasetSet, ProcessElement, WorkerActivity, WorkerRun
from arkindex.training.models import DatasetElement, DatasetSet
from arkindex.users.models import User
......@@ -41,6 +41,10 @@ def corpus_delete(corpus_id: str) -> None:
corpus.top_level_type_id = None
corpus.save(update_fields=["top_level_type_id"])
# Set process.element to NULL in all processes, to avoid IntegrityErrors when deleting elements, as
# elements are deleted before processes.
Process.objects.filter(corpus_id=corpus_id).exclude(element_id=None).update(element_id=None)
# Delete all related objects, bypassing RESTRICTs deletion rules
# and Django's way of loading everything into memory before deleting.
querysets = [
......@@ -67,6 +71,8 @@ def corpus_delete(corpus_id: str) -> None:
Selection.objects.filter(element__corpus_id=corpus_id),
corpus.memberships.all(),
corpus.exports.all(),
# Delete task artifacts
Artifact.objects.filter(task__process__corpus_id=corpus_id),
# ProcessDatasetSet M2M
ProcessDatasetSet.objects.filter(set__dataset__corpus_id=corpus_id),
ProcessDatasetSet.objects.filter(process__corpus_id=corpus_id),
......
......@@ -363,7 +363,7 @@ class TestEditElementPath(FixtureTestCase):
path_id = elements["B"].paths.get().id
if path1.id == path_id:
return str(path1.path[0])
elif path2.id == path_id:
if path2.id == path_id:
return str(path2.path[0])
raise AssertionError("Unexpected top-level path ID")
......
......@@ -47,7 +47,7 @@ class ImageServerManager(models.Manager):
f'The URL "{url}" does not match any existing image server. '
"Please ask an instance administrator to register the IIIF server for this image."
)
elif servers_count > 1:
if servers_count > 1:
raise self.model.MultipleObjectsReturned(
f'The URL "{url}" matched multiple existing image servers'
)
......
......@@ -108,7 +108,7 @@ class ImageServer(models.Model):
src_url, dest_url = map(urllib.parse.urlsplit, (self.url, other.url))
if src_url.scheme != dest_url.scheme:
raise ValueError("Cannot merge into an image server of a different protocol")
elif src_url.netloc != dest_url.netloc:
if src_url.netloc != dest_url.netloc:
raise ValueError("Cannot merge into an image server on a different domain")
# Check paths
......
......@@ -68,7 +68,7 @@ class ImageSerializer(serializers.ModelSerializer):
if not self.instance:
# Force the Unchecked status when creating a new image
return S3FileStatus.Unchecked
elif value == S3FileStatus.Checked:
if value == S3FileStatus.Checked:
# Perform image validation if we are updating an existing image to Checked
try:
if self.instance.server.s3_bucket and not self.instance.server.s3_read_only_bucket:
......
......@@ -402,8 +402,9 @@ class ProcessDetails(ProcessACLMixin, ProcessQuerysetMixin, RetrieveUpdateDestro
class ProcessRetry(ProcessACLMixin, ProcessQuerysetMixin, GenericAPIView):
"""
Retry a process. Can only be used on processes with Error, Failed, Stopped or Completed states.\n\n
r"""
Retry a process. Can only be used on processes with Error, Failed, Stopped or Completed states.
Requires an **admin** access to the process and **guest** access to the process' farm.
"""
permission_classes = (IsVerified, )
......@@ -427,9 +428,9 @@ class ProcessRetry(ProcessACLMixin, ProcessQuerysetMixin, GenericAPIView):
# Allow 'retrying' a process that has no Ponos tasks (that has never been started)
if len(process.tasks.all()) and state in (State.Unscheduled, State.Pending):
raise ValidationError({"__all__": ["This process is already pending"]})
elif state == State.Running:
if state == State.Running:
raise ValidationError({"__all__": ["This process is already running"]})
elif state == State.Stopping:
if state == State.Stopping:
raise ValidationError({"__all__": ["This process is stopping"]})
@extend_schema(
......@@ -476,8 +477,9 @@ class FilesProcess(CreateAPIView):
)
)
class CorpusProcess(SelectionMixin, CorpusACLMixin, CreateAPIView):
"""
Create a distributed process from elements of an Arkindex corpus.\n\n
r"""
Create a distributed process from elements of an Arkindex corpus.
Requires an **admin** access to the corpus.
"""
permission_classes = (IsVerified, )
......@@ -542,8 +544,9 @@ class CorpusProcess(SelectionMixin, CorpusACLMixin, CreateAPIView):
),
)
class StartProcess(CorpusACLMixin, CreateAPIView):
"""
Start a process, used to build a Workflow with Workers.\n\n
r"""
Start a process, used to build a Workflow with Workers.
Requires an **admin** access to the corpus of this process.
"""
permission_classes = (IsVerified, )
......@@ -1555,8 +1558,9 @@ class WorkerRunDetails(ProcessACLMixin, RetrieveUpdateDestroyAPIView):
tags=["process"]
))
class ListProcessElements(CorpusACLMixin, ListAPIView):
"""
List all elements for a process with workers.\n\n
r"""
List all elements for a process with workers.
Requires an **admin** access to the process corpus.
"""
pagination_class = CountCursorPagination
......@@ -1579,8 +1583,7 @@ class ListProcessElements(CorpusACLMixin, ListAPIView):
def get_serializer_class(self):
if self.with_image:
return ProcessElementSerializer
else:
return ProcessElementLightSerializer
return ProcessElementLightSerializer
def get_queryset(self):
if not self.has_admin_access(self.process.corpus):
......@@ -1829,8 +1832,9 @@ class WorkerActivityBase(ListAPIView):
)
)
class CorpusWorkersActivity(CorpusACLMixin, WorkerActivityBase):
"""
Retrieve corpus wise statistics about the activity of all its worker processes.\n
r"""
Retrieve corpus wise statistics about the activity of all its worker processes.
Requires a **guest** access.
"""
......@@ -1846,8 +1850,9 @@ class CorpusWorkersActivity(CorpusACLMixin, WorkerActivityBase):
)
)
class ProcessWorkersActivity(ProcessACLMixin, WorkerActivityBase):
"""
Retrieve process statistics about the activity of its workers.\n
r"""
Retrieve process statistics about the activity of its workers.
Requires a **guest** access.
"""
......
......@@ -232,8 +232,7 @@ class Process(IndexableModel):
"""
if self.has_prefetched_tasks:
return max((t.expiry for t in self.tasks.all()), default=None)
else:
return self.tasks.aggregate(models.Max("expiry"))["expiry__max"]
return self.tasks.aggregate(models.Max("expiry"))["expiry__max"]
def get_last_run(self) -> int:
"""
......
......@@ -545,7 +545,7 @@ class ApplyProcessTemplateSerializer(ProcessACLMixin, serializers.Serializer):
access_level = self.process_access_level(process)
if not access_level:
raise ValidationError(detail="Process with this ID does not exist.")
elif access_level < Role.Contributor.value:
if access_level < Role.Contributor.value:
raise PermissionDenied(detail="You do not have a contributor access to this process.")
errors = []
......@@ -627,7 +627,7 @@ class CorpusProcessSerializer(serializers.Serializer):
access_level = get_max_level(self.context["request"].user, corpus)
if not access_level:
raise ValidationError(["Corpus with this ID does not exist."])
elif access_level < Role.Admin.value:
if access_level < Role.Admin.value:
raise ValidationError(["You do not have an admin access to this corpus."])
return corpus
......
......@@ -37,8 +37,7 @@ class ModelArgument(object):
text_filter = {f"{self.text_search_field}__{self.text_search_lookup}": arg}
if self.many:
return qs.filter(**text_filter)
else:
return qs.get(**text_filter)
return qs.get(**text_filter)
class CorpusArgument(ModelArgument):
......
......@@ -304,4 +304,4 @@ class NullField(serializers.CharField):
def to_internal_value(self, data):
if data is not None:
self.fail("invalid")
return None
return
......@@ -26,7 +26,7 @@ class TestConfig(TestCase):
data = data.value
if data is None:
return self.represent_none(data)
elif isinstance(data, (bool, int, float, bytes, str)):
if isinstance(data, (bool, int, float, bytes, str)):
return self.represent_data(data)
return self.represent_str(str(data))
......
......@@ -10,6 +10,11 @@ FROM "documents_corpus"
WHERE "documents_corpus"."id" = '{corpus_id}'::uuid
LIMIT 21;
UPDATE "process_process"
SET "element_id" = NULL
WHERE ("process_process"."corpus_id" = '{corpus_id}'::uuid
AND NOT ("process_process"."element_id" IS NULL));
SELECT "django_content_type"."id",
"django_content_type"."app_label",
"django_content_type"."model"
......@@ -152,6 +157,15 @@ DELETE
FROM "documents_corpusexport"
WHERE "documents_corpusexport"."corpus_id" = '{corpus_id}'::uuid;
DELETE
FROM "ponos_artifact"
WHERE "ponos_artifact"."id" IN
(SELECT U0."id"
FROM "ponos_artifact" U0
INNER JOIN "ponos_task" U1 ON (U0."task_id" = U1."id")
INNER JOIN "process_process" U2 ON (U1."process_id" = U2."id")
WHERE U2."corpus_id" = '{corpus_id}'::uuid);
DELETE
FROM "process_processdatasetset"
WHERE "process_processdatasetset"."id" IN
......
......@@ -14,6 +14,11 @@ UPDATE "documents_corpus"
SET "top_level_type_id" = NULL
WHERE "documents_corpus"."id" = '{corpus_id}'::uuid;
UPDATE "process_process"
SET "element_id" = NULL
WHERE ("process_process"."corpus_id" = '{corpus_id}'::uuid
AND NOT ("process_process"."element_id" IS NULL));
SELECT "django_content_type"."id",
"django_content_type"."app_label",
"django_content_type"."model"
......@@ -156,6 +161,15 @@ DELETE
FROM "documents_corpusexport"
WHERE "documents_corpusexport"."corpus_id" = '{corpus_id}'::uuid;
DELETE
FROM "ponos_artifact"
WHERE "ponos_artifact"."id" IN
(SELECT U0."id"
FROM "ponos_artifact" U0
INNER JOIN "ponos_task" U1 ON (U0."task_id" = U1."id")
INNER JOIN "process_process" U2 ON (U1."process_id" = U2."id")
WHERE U2."corpus_id" = '{corpus_id}'::uuid);
DELETE
FROM "process_processdatasetset"
WHERE "process_processdatasetset"."id" IN
......
......@@ -48,8 +48,7 @@ class DatasetAdmin(admin.ModelAdmin):
self.readonly_fields = self.__class__.readonly_fields
if obj is not None:
self.readonly_fields += ("unique_elements",)
form = super().get_form(request, obj=None, **kwargs)
return form
return super().get_form(request, obj=None, **kwargs)
admin.site.register(Model, ModelAdmin)
......
......@@ -417,8 +417,7 @@ class ModelsList(TrainingModelMixin, ListCreateAPIView):
"id": str(existing_model.id),
"name": "A model with this name already exists",
})
else:
raise PermissionDenied()
raise PermissionDenied()
return serializer.save()
......@@ -753,7 +752,7 @@ class DatasetSetBase():
)
if self.request.method == "DELETE" and not Corpus.objects.admin(self.request.user).filter(pk=dataset.corpus_id).exists():
raise PermissionDenied(detail="You do not have admin access to this dataset.")
elif self.request.method != "DELETE" and not Corpus.objects.writable(self.request.user).filter(pk=dataset.corpus_id).exists():
if self.request.method != "DELETE" and not Corpus.objects.writable(self.request.user).filter(pk=dataset.corpus_id).exists():
raise PermissionDenied(detail="You do not have contributor access to this dataset.")
if dataset.state != DatasetState.Open:
raise ValidationError(detail="You can only add or update sets from a dataset in an open state.")
......