Skip to content

Commit

Permalink
Move logic of QuotaManager creation into ProjectFiles.
Browse files Browse the repository at this point in the history
Quota management behavior depends on the project file storage.  For
GCS storage, a GCSQuotaManager is required.

(Until now, the SubmissionInfo.quota_manager method has been broken on
GCS, and the GCS backend has carefully avoided using this API - which
unfortunately has also blocked development of the quota system.)

Rather than including this logic in SubmissionInfo, it seemingly makes
more sense for quota policy to be part of ProjectFiles; so a new
abstract method 'project_quota_manager' is defined.
  • Loading branch information
Benjamin Moody committed Sep 22, 2023
1 parent be02787 commit 89755b1
Show file tree
Hide file tree
Showing 4 changed files with 32 additions and 14 deletions.
15 changes: 1 addition & 14 deletions physionet-django/project/modelcomponents/submission.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
from django.contrib.contenttypes.fields import GenericForeignKey, GenericRelation
from django.contrib.contenttypes.models import ContentType
from django.db import models
from project.quota import DemoQuotaManager
from django.conf import settings

from physionet.settings.base import StorageTypes
Expand Down Expand Up @@ -245,19 +244,7 @@ def quota_manager(self):
(represented by the bytes_used and inodes_used properties of
the QuotaManager object.)
"""
allowance = self.core_project.storage_allowance
published = self.core_project.total_published_size
limit = allowance - published

# DemoQuotaManager needs to know the project's toplevel
# directory as well as its creation time (so that files
# present in multiple versions can be correctly attributed to
# the version where they first appeared.)
quota_manager = DemoQuotaManager(
project_path=self.file_root(),
creation_time=self.creation_datetime)
quota_manager.set_limits(bytes_hard=limit, bytes_soft=limit)
return quota_manager
return self.files.project_quota_manager(self)

@functools.cached_property
def files(self):
Expand Down
5 changes: 5 additions & 0 deletions physionet-django/project/projectfiles/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,11 @@ def get_file_root(self, slug, version, access_policy, klass):
"""Project directory."""
raise NotImplementedError

@abc.abstractmethod
def project_quota_manager(self, project):
"""Create a quota manager for a project."""
raise NotImplementedError

@abc.abstractmethod
def active_project_storage_used(self, project):
"""Total storage used in bytes - active project."""
Expand Down
10 changes: 10 additions & 0 deletions physionet-django/project/projectfiles/gcs.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from google.cloud.exceptions import Conflict, NotFound
from physionet.gcs import GCSObject, GCSObjectException, create_bucket, delete_bucket
from project.projectfiles.base import BaseProjectFiles
from project.quota import GCSQuotaManager
from project.utility import DirectoryInfo, FileInfo, readable_size


Expand Down Expand Up @@ -127,6 +128,15 @@ def get_project_file_root(self, slug, version, access_policy, klass):
def get_file_root(self, slug, version, access_policy, klass):
return self.get_project_file_root(slug, version, access_policy, klass)

def project_quota_manager(self, project):
allowance = project.core_project.storage_allowance
published = project.core_project.total_published_size
limit = allowance - published

quota_manager = GCSQuotaManager(project.file_root())
quota_manager.set_limits(bytes_hard=limit, bytes_soft=limit)
return quota_manager

def active_project_storage_used(self, project):
return self._storage_used(project)

Expand Down
16 changes: 16 additions & 0 deletions physionet-django/project/projectfiles/local.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from django.conf import settings
from physionet.utility import serve_file, sorted_tree_files, zip_dir
from project.projectfiles.base import BaseProjectFiles
from project.quota import DemoQuotaManager
from project.utility import (
clear_directory,
get_directory_info,
Expand Down Expand Up @@ -131,6 +132,21 @@ def get_project_file_root(self, slug, version, access_policy, klass):
def get_file_root(self, slug, version, access_policy, klass):
return os.path.join(self.get_project_file_root(slug, version, access_policy, klass), version)

def project_quota_manager(self, project):
allowance = project.core_project.storage_allowance
published = project.core_project.total_published_size
limit = allowance - published

# DemoQuotaManager needs to know the project's toplevel
# directory as well as its creation time (so that files
# present in multiple versions can be correctly attributed to
# the version where they first appeared.)
quota_manager = DemoQuotaManager(
project_path=project.file_root(),
creation_time=project.creation_datetime)
quota_manager.set_limits(bytes_hard=limit, bytes_soft=limit)
return quota_manager

def active_project_storage_used(self, project):
return project.quota_manager().bytes_used

Expand Down

0 comments on commit 89755b1

Please sign in to comment.