Skip to content

Commit e4b388b

Browse files
authored
Merge pull request #1621 from ScilifelabDataCentre/dev
Prep for release: v2.12.0
2 parents 2455b3e + eec0a7c commit e4b388b

File tree

60 files changed

+568
-138
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

60 files changed

+568
-138
lines changed

.adr-dir

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
doc/architecture/decisions
1+
docs/architecture/decisions

.github/pull_request_template.md

Lines changed: 3 additions & 13 deletions

.github/workflows/publish_and_trivyscan.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ jobs:
4040
updmap-sys &&
4141
pandoc
4242
--output=dds_web/static/dds-technical-overview.pdf
43-
doc/technical-overview.md
43+
docs/technical-overview.md
4444
"
4545
- name: Upload technical overview PDF
4646
uses: actions/upload-artifact@v4
@@ -64,7 +64,7 @@ jobs:
6464
updmap-sys &&
6565
pandoc
6666
--output=dds_web/static/dds-troubleshooting.pdf
67-
doc/troubleshooting.md
67+
docs/troubleshooting.md
6868
"
6969
- name: Upload troubleshooting PDF
7070
uses: actions/upload-artifact@v4

.github/workflows/release-drafter.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
name: Release Drafter
55

66
on:
7+
workflow_dispatch: # Allows manual triggering of the workflow
78
# Run on push to dev and master
89
push:
910
branches:

.prettierignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,4 @@ dds_web/static/css
33
dds_web/static/node_modules
44
# Would like to enable this but prettier isn't correctly formatting grid tables for pandoc
55
# We need grid table markdown due to having tables with multi-column/row spans
6-
doc/technical-overview.md
6+
docs/technical-overview.md

CHANGELOG.rst

Lines changed: 15 additions & 0 deletions

CONTRIBUTING.md

Lines changed: 1 addition & 1 deletion

README.md

Lines changed: 2 additions & 2 deletions

SPRINTLOG.md

Lines changed: 15 additions & 3 deletions

dds_web/api/project.py

Lines changed: 84 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,7 @@ def post(self):
149149
curr_date = dds_web.utils.current_time()
150150
delete_message = ""
151151
is_aborted = False
152+
is_queue_operation = False
152153

153154
# Moving to Available
154155
if new_status == "Available":
@@ -169,33 +170,23 @@ def post(self):
169170
)
170171
elif new_status == "Archived":
171172
is_aborted = json_input.get("is_aborted", False)
173+
is_queue_operation = True
172174
new_status_row, delete_message = self.archive_project(
173-
project=project, current_time=curr_date, aborted=is_aborted
175+
project=project,
176+
current_time=curr_date,
177+
aborted=is_aborted,
174178
)
175179
else:
176180
raise DDSArgumentError(message="Invalid status")
177181

178-
try:
179-
project.project_statuses.append(new_status_row)
180-
project.busy = False # TODO: Use set_busy instead?
181-
db.session.commit()
182-
flask.current_app.logger.info(
183-
f"Busy status set. Project: '{project.public_id}', Busy: False"
184-
)
185-
except (sqlalchemy.exc.OperationalError, sqlalchemy.exc.SQLAlchemyError) as err:
186-
flask.current_app.logger.exception(err)
187-
db.session.rollback()
188-
raise DatabaseError(
189-
message=str(err),
190-
alt_message=(
191-
"Status was not updated"
192-
+ (
193-
": Database malfunction."
194-
if isinstance(err, sqlalchemy.exc.OperationalError)
195-
else ": Server Error."
196-
)
197-
),
198-
) from err
182+
return_message = ""
183+
184+
if not is_queue_operation:
185+
# If operations was not handled by a queue, commit the changes
186+
self.update_project_status(project=project, new_status_row=new_status_row)
187+
return_message += f"Project {project.public_id} updated to status {new_status}"
188+
else:
189+
return_message += f"The status of project {project.public_id} is being updated to {new_status}. The DDS is handling this in the background. It may take some time to complete."
199190

200191
# Mail users once project is made available
201192
if new_status == "Available" and send_email:
@@ -204,10 +195,6 @@ def post(self):
204195
userobj=user.researchuser, mail_type="project_release", project=project
205196
)
206197

207-
return_message = f"{project.public_id} updated to status {new_status}" + (
208-
" (aborted)" if new_status == "Archived" and is_aborted else ""
209-
)
210-
211198
if new_status != "Available":
212199
return_message += delete_message + "."
213200
else:
@@ -235,9 +222,11 @@ def patch(self):
235222
# Get json input from request
236223
json_input = flask.request.get_json(silent=True) # Already checked by json_required
237224

238-
# the status has changed at least two times,
239-
# next time the project expires it wont change again -> error
240-
if project.times_expired >= 2:
225+
# A project can have the expired status 3 times
226+
# It can be released / extended 3 times
227+
# If more attempts --> cannot be extended again
228+
# will be automatically archived by the system
229+
if project.times_expired > 3:
241230
raise DDSArgumentError(
242231
"Project availability limit: The maximum number of changes in data availability has been reached."
243232
)
@@ -411,9 +400,10 @@ def release_project(
411400
days=deadline_in
412401
)
413402

414-
# Project can only move from Expired 2 times
403+
# Project can only MOVE FROM Expired 2 times
404+
# but can BE IN Exired 3 times
415405
if project.current_status == "Expired":
416-
if project.times_expired > 2:
406+
if project.times_expired > 3:
417407
raise DDSArgumentError(
418408
"Project availability limit: Project cannot be made Available any more times"
419409
)
@@ -506,7 +496,10 @@ def delete_project(self, project: models.Project, current_time: datetime.datetim
506496
return models.ProjectStatuses(status="Deleted", date_created=current_time), delete_message
507497

508498
def archive_project(
509-
self, project: models.Project, current_time: datetime.datetime, aborted: bool = False
499+
self,
500+
project: models.Project,
501+
current_time: datetime.datetime,
502+
aborted: bool = False,
510503
):
511504
"""Archive project: Make status Archived.
512505
@@ -522,20 +515,47 @@ def archive_project(
522515
"Please abort the project if you wish to proceed."
523516
)
524517

518+
# Get redis connection to add a job if needed
519+
redis_url = flask.current_app.config.get("REDIS_URL")
520+
r = Redis.from_url(redis_url)
521+
q = Queue(connection=r)
522+
523+
job_delete_contents = q.enqueue(
524+
self.queue_helper_function_delete_contents,
525+
project_id=project.public_id, # It is not possible to pass the project object directly to the queue
526+
clear_proj_info=aborted, # If aborted, clear project info
527+
)
528+
529+
job_update_db = q.enqueue(
530+
self.queue_helper_function_update_proj_status,
531+
project_id=project.public_id,
532+
current_time=current_time,
533+
new_status="Archived",
534+
aborted=aborted,
535+
depends_on=job_delete_contents, # This job is only executed after success of delete contents
536+
)
537+
538+
return None, "" # Dummy returns to not break the main function
539+
540+
@dbsession
541+
def queue_helper_function_delete_contents(self, project_id: int, clear_proj_info: bool = False):
542+
"""Delete the project contents for the archiving operation.
543+
Function to be called by the queue."""
544+
545+
project = models.Project.query.filter_by(public_id=project_id).one_or_none()
546+
525547
try:
526548
# Deletes files (also commits session in the function - possibly refactor later)
527549
RemoveContents().delete_project_contents(
528550
project_id=project.public_id, delete_bucket=True
529551
)
530-
delete_message = f"\nAll files in {project.public_id} deleted"
531552
self.rm_project_user_keys(project=project)
532553

533554
# Only mark as inactive after all deletion operations succeed
534555
project.is_active = False
535556
# Delete metadata from project row
536-
if aborted:
557+
if clear_proj_info:
537558
project = self.delete_project_info(project)
538-
delete_message += " and project info cleared"
539559

540560
except (TypeError, DatabaseError, DeletionError, BucketNotFoundError) as err:
541561
flask.current_app.logger.exception(err)
@@ -546,11 +566,35 @@ def archive_project(
546566
pass_message=True,
547567
) from err
548568

549-
return (
550-
models.ProjectStatuses(
551-
status="Archived", date_created=current_time, is_aborted=aborted
552-
),
553-
delete_message,
569+
@dbsession
570+
def queue_helper_function_update_proj_status(
571+
self,
572+
project_id: int,
573+
current_time: datetime.datetime,
574+
new_status: str,
575+
aborted: bool = False,
576+
):
577+
"""When the delete contents operation has being sucesfully executed. Perform the update in the DB.
578+
Function ONLY to be called by the queue.
579+
"""
580+
581+
project = models.Project.query.filter_by(public_id=project_id).one_or_none()
582+
583+
new_status_row = models.ProjectStatuses(
584+
status=new_status, date_created=current_time, is_aborted=aborted
585+
)
586+
self.update_project_status(project=project, new_status_row=new_status_row)
587+
588+
@dbsession
589+
def update_project_status(
590+
self, project: models.Project, new_status_row: models.ProjectStatuses
591+
):
592+
"""Update the project status in the database."""
593+
project.project_statuses.append(new_status_row)
594+
project.busy = False # TODO: Use set_busy instead?
595+
db.session.commit()
596+
flask.current_app.logger.info(
597+
f"Busy status set. Project: '{project.public_id}', Busy: False"
554598
)
555599

556600
def rm_project_user_keys(self, project):

0 commit comments

Comments
 (0)