Skip to content

Commit

Permalink
Add some helper methods to RequestFile and ReleaseRequest
Browse files Browse the repository at this point in the history
To identify uploading status of a file and an overall request. This
will allow us to determine whether to disable or enable the "release
files" button and what tooltips to show.
  • Loading branch information
rebkwok committed Feb 12, 2025
1 parent 89382ac commit 44eb65f
Show file tree
Hide file tree
Showing 2 changed files with 116 additions and 1 deletion.
33 changes: 32 additions & 1 deletion airlock/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -481,7 +481,7 @@ class RequestFile:
filetype: RequestFileType = RequestFileType.OUTPUT
released_at: datetime | None = None
uploaded: bool = False
upload_attempts: int | None = 0
upload_attempts: int = 0
uploaded_at: datetime | None = None

@classmethod
Expand Down Expand Up @@ -538,6 +538,18 @@ def changes_requested_reviews(self):
if review.status == RequestFileVote.CHANGES_REQUESTED
]

def upload_in_progress(self):
return (
self.released_at is not None
and not self.uploaded
and self.upload_attempts < settings.UPLOAD_MAX_ATTEMPTS
)

def upload_failed(self):
return (
not self.uploaded and self.upload_attempts >= settings.UPLOAD_MAX_ATTEMPTS
)


@dataclass(frozen=True)
class FileGroup:
Expand Down Expand Up @@ -890,6 +902,25 @@ def can_be_released(self) -> bool:
and self.all_files_approved()
)

def can_be_rereleased(self) -> bool:
"""
An approved request can be re-released if all of its file are
either uploaded already, or have have failed to upload after the
maximum number of attempts
"""
return self.status == RequestStatus.APPROVED and all(
rf.uploaded or rf.upload_failed() for rf in self.output_files().values()
)

def upload_in_progress(self) -> bool:
"""
A request is uploading if it has been approved and not all of its
output files have been uploaded
"""
return self.status == RequestStatus.APPROVED and any(
rf.upload_in_progress() for rf in self.output_files().values()
)

def is_final(self):
return self.status_owner() == RequestStatusOwner.SYSTEM

Expand Down
84 changes: 84 additions & 0 deletions tests/unit/test_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -416,6 +416,90 @@ def test_request_file_manifest_data_content_hash_mismatch(mock_notifications, bl
bll.add_file_to_request(release_request, UrlPath("bar.txt"), user, "group")


def test_request_file_upload_in_progress_failed(mock_notifications, mock_old_api, bll):
release_request = factories.create_request_at_status(
"workspace",
status=RequestStatus.APPROVED,
files=[
factories.request_file(contents="1", approved=True, path="test/file1.txt")
],
)
relpath = UrlPath("test/file1.txt")
request_file = release_request.get_request_file_from_output_path(relpath)
assert request_file.upload_in_progress()
assert request_file.upload_attempts == 0

bll.register_file_upload_attempt(release_request, relpath)
release_request = factories.refresh_release_request(release_request)
request_file = release_request.get_request_file_from_output_path(relpath)
assert request_file.upload_in_progress()
assert request_file.upload_attempts == 1

for _ in range(settings.UPLOAD_MAX_ATTEMPTS - 1):
bll.register_file_upload_attempt(release_request, relpath)

release_request = factories.refresh_release_request(release_request)
request_file = release_request.get_request_file_from_output_path(relpath)
assert not request_file.upload_in_progress()
assert request_file.upload_attempts == settings.UPLOAD_MAX_ATTEMPTS
assert request_file.upload_failed()


def test_request_can_be_rereleased(mock_notifications, mock_old_api, bll):
release_request = factories.create_request_at_status(
"workspace",
status=RequestStatus.APPROVED,
files=[
factories.request_file(contents="1", approved=True, path="test/file1.txt"),
factories.request_file(contents="2", approved=True, path="test/file2.txt"),
],
)
assert release_request.can_be_released()
assert not release_request.can_be_rereleased()
assert release_request.upload_in_progress()

for relpath in [UrlPath("test/file1.txt"), UrlPath("test/file2.txt")]:
for _ in range(settings.UPLOAD_MAX_ATTEMPTS):
bll.register_file_upload_attempt(release_request, relpath)
release_request = factories.refresh_release_request(release_request)

assert release_request.can_be_released()
assert release_request.can_be_rereleased()
assert not release_request.upload_in_progress()


def test_request_upload_in_progress(mock_notifications, mock_old_api, bll):
checker = factories.get_default_output_checkers()[0]
release_request = factories.create_request_at_status(
"workspace",
status=RequestStatus.APPROVED,
files=[
factories.request_file(contents="1", approved=True, path="test/file1.txt"),
factories.request_file(contents="2", approved=True, path="test/file2.txt"),
factories.request_file(contents="3", approved=True, path="test/file3.txt"),
],
)

assert release_request.upload_in_progress()
# upload file 1, files 2 and 3 still in progress
bll.register_file_upload(release_request, UrlPath("test/file1.txt"), checker)

release_request = factories.refresh_release_request(release_request)
assert release_request.upload_in_progress()

# max out attempts for file 2, file 3 still in progress
for _ in range(settings.UPLOAD_MAX_ATTEMPTS):
bll.register_file_upload_attempt(release_request, UrlPath("test/file2.txt"))
release_request = factories.refresh_release_request(release_request)
assert release_request.upload_in_progress()

# upload file 3
bll.register_file_upload(release_request, UrlPath("test/file3.txt"), checker)
release_request = factories.refresh_release_request(release_request)
assert not release_request.upload_in_progress()
assert release_request.can_be_rereleased()


def test_code_repo_container():
workspace = factories.create_workspace("workspace")
factories.write_workspace_file(workspace, "foo.txt")
Expand Down

0 comments on commit 44eb65f

Please sign in to comment.