* refactors CheckableMixin and RecordableMixin into CheckableObject and RecordableObject
* adds ShareableObject for wrapping share related fields and functionality
* adds share functionality to EcoAccount and EMA, just like Intervention
This commit is contained in:
2021-10-26 15:09:30 +02:00
parent 7c7a21052a
commit c4af63cea2
11 changed files with 220 additions and 117 deletions

View File

@@ -21,6 +21,7 @@ from compensation.settings import COMPENSATION_IDENTIFIER_TEMPLATE, COMPENSATION
from ema.settings import EMA_ACCOUNT_IDENTIFIER_LENGTH, EMA_ACCOUNT_IDENTIFIER_TEMPLATE
from intervention.settings import INTERVENTION_IDENTIFIER_LENGTH, INTERVENTION_IDENTIFIER_TEMPLATE
from konova.settings import INTERVENTION_REVOCATION_DOC_PATH
from konova.utils import generators
from konova.utils.generators import generate_random_string
from user.models import UserActionLogEntry, UserAction
@@ -315,12 +316,23 @@ class Geometry(BaseResource):
geom = MultiPolygonField(null=True, blank=True, srid=DEFAULT_SRID)
class RecordableMixin:
""" Mixin to be combined with BaseObject class
Provides functionality related to un/recording of data
class RecordableObject(models.Model):
""" Wraps record related fields and functionality
"""
# Refers to "verzeichnen"
recorded = models.OneToOneField(
UserActionLogEntry,
on_delete=models.SET_NULL,
null=True,
blank=True,
help_text="Holds data on user and timestamp of this action",
related_name="+"
)
class Meta:
abstract = True
def set_unrecorded(self, user: User):
""" Perform unrecording
@@ -370,12 +382,19 @@ class RecordableMixin:
self.set_unrecorded(user)
class CheckableMixin:
""" Mixin to be combined with BaseObject class
class CheckableObject(models.Model):
# Checks - Refers to "Genehmigen" but optional
checked = models.OneToOneField(
UserActionLogEntry,
on_delete=models.SET_NULL,
null=True,
blank=True,
help_text="Holds data on user and timestamp of this action",
related_name="+"
)
class Meta:
abstract = True
Provides functionality related to un/checking of data
"""
def set_unchecked(self, user: User):
""" Perform unrecording
@@ -417,3 +436,53 @@ class CheckableMixin:
self.set_checked(user)
else:
self.set_unchecked(user)
class ShareableObject(models.Model):
# Users having access on this object
users = models.ManyToManyField(User, help_text="Users having access (data shared with)")
access_token = models.CharField(
max_length=255,
null=True,
blank=True,
help_text="Used for sharing access",
)
class Meta:
abstract = True
def generate_access_token(self, make_unique: bool = False, rec_depth: int = 5):
""" Creates a new access token for the data
Tokens are not used for identification of a table row. The share logic checks the intervention id as well
as the given token. Therefore two different interventions can hold the same access_token without problems.
For (possible) future changes to the share logic, the make_unique parameter may be used for checking whether
the access_token is already used in any intervention. If so, tokens will be generated as long as a free token
can be found.
Args:
make_unique (bool): Perform check on uniqueness over all intervention entries
rec_depth (int): How many tries for generating a free random token (only if make_unique)
Returns:
"""
# Make sure we won't end up in an infinite loop of trying to generate access_tokens
rec_depth = rec_depth - 1
if rec_depth < 0 and make_unique:
raise RuntimeError(
"Access token generating for {} does not seem to find a free random token! Aborted!".format(self.id)
)
# Create random token
token = generators.generate_random_string(15, True, True, False)
# Check dynamically wheter there is another instance of that model, which holds this random access token
_model = self._meta.concrete_model
token_used_in = _model.objects.filter(access_token=token)
# Make sure the token is not used anywhere as access_token, yet.
# Make use of QuerySet lazy method for checking if it exists or not.
if token_used_in and make_unique:
self.generate_access_token(make_unique, rec_depth)
else:
self.access_token = token
self.save()