diff --git a/epdb/logic.py b/epdb/logic.py index 0fc2e909..bfaeaac2 100644 --- a/epdb/logic.py +++ b/epdb/logic.py @@ -35,6 +35,11 @@ class UserManager(object): def get_user(user_url): pass + @staticmethod + def get_user_lp(user_url: str): + uuid = user_url.strip().split('/')[-1] + return get_user_model().objects.get(uuid=uuid) + @staticmethod def get_users(): return [] @@ -52,6 +57,11 @@ class GroupManager(object): return g + @staticmethod + def get_group_lp(group_url: str): + uuid = group_url.strip().split('/')[-1] + return Group.objects.get(uuid=uuid) + @staticmethod def get_group_by_url(user, group_url): return GroupManager.get_group_by_id(user, group_url.split('/')[-1]) @@ -80,7 +90,7 @@ class PackageManager(object): def readable(user, package): # TODO Owner! if UserPackagePermission.objects.filter(package=package, user=user).exists() or \ - GroupPackagePermission.objects.filter(package=package, group__in=user.groups.all()) or \ + GroupPackagePermission.objects.filter(package=package, group__in=GroupManager.get_groups(user)) or \ package.reviewed is True or \ user.is_superuser: return True @@ -91,7 +101,7 @@ class PackageManager(object): def writable(user, package): # TODO Owner! if UserPackagePermission.objects.filter(package=package, user=user, permission=Permission.WRITE).exists() or \ - GroupPackagePermission.objects.filter(package=package, group__in=user.groups.all(), + GroupPackagePermission.objects.filter(package=package, group__in=GroupManager.get_groups(user), permission=Permission.WRITE) or \ user.is_superuser: return True @@ -125,8 +135,12 @@ class PackageManager(object): if user.is_superuser: qs = Package.objects.all() else: - up = UserPackagePermission.objects.filter(user=user).values('package').distinct() - qs = Package.objects.filter(id__in=up) + user_package_qs = Package.objects.filter( + id__in=UserPackagePermission.objects.filter(user=user).values('package').distinct()) + group_package_qs = Package.objects.filter( + id__in=GroupPackagePermission.objects.filter(group__in=GroupManager.get_groups(user)).values( + 'package').distinct()) + qs = user_package_qs | group_package_qs if include_reviewed: qs |= Package.objects.filter(reviewed=True) @@ -142,9 +156,19 @@ class PackageManager(object): if user.is_superuser: qs = Package.objects.all() else: - up = UserPackagePermission.objects.filter(user=user, permission=Permission.WRITE).values( - 'package').distinct() - qs = Package.objects.filter(id__in=up) + write_user_packs = UserPackagePermission.objects.filter(user=user, permission=Permission.WRITE[0]).values('package').distinct() + owner_user_packs = UserPackagePermission.objects.filter(user=user, permission=Permission.ALL[0]).values('package').distinct() + + user_packs = write_user_packs | owner_user_packs + user_package_qs = Package.objects.filter(id__in=user_packs) + + write_group_packs = GroupPackagePermission.objects.filter(group__in=GroupManager.get_groups(user), permission=Permission.WRITE[0]).values( 'package').distinct() + owner_group_packs = GroupPackagePermission.objects.filter(group__in=GroupManager.get_groups(user), permission=Permission.ALL[0]).values( 'package').distinct() + + group_packs = write_group_packs | owner_group_packs + group_package_qs = Package.objects.filter(id__in=group_packs) + + qs = user_package_qs | group_package_qs qs = qs.filter(reviewed=False) @@ -170,6 +194,34 @@ class PackageManager(object): return p + @staticmethod + @transaction.atomic + def update_permissions(caller: User, package: Package, grantee: Union[User, Group], new_perm: Optional[str]): + if not PackageManager.writable(caller, package): + raise ValueError(f"User {caller} is not allowed to modify permissions on {package}") + + data = { + 'package': package, + } + + if isinstance(grantee, User): + perm_cls = UserPackagePermission + data['user'] = grantee + else: + perm_cls = GroupPackagePermission + data['group'] = grantee + + if new_perm is None: + qs = perm_cls.objects.filter(**data) + if qs.count() > 1: + raise ValueError("Got more Permission objects than expected!") + if qs.count() != 0: + logger.info(f"Deleting Perm {qs.first()}") + qs.delete() + else: + logger.debug(f"No Permission object for {perm_cls} with filter {data} found!") + else: + _ = perm_cls.objects.update_or_create(defaults={'permission': new_perm}, **data) class SettingManager(object): setting_pattern = re.compile(r".*/setting/[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$") diff --git a/epdb/management/commands/bootstrap.py b/epdb/management/commands/bootstrap.py index 4b9aea44..8a4d50b4 100644 --- a/epdb/management/commands/bootstrap.py +++ b/epdb/management/commands/bootstrap.py @@ -63,7 +63,7 @@ class Command(BaseCommand): up = UserPackagePermission() up.user = owner up.package = pack - up.permission = up.ALL + up.permission = up.ALL[0] up.save() # Stores old_id to new_id diff --git a/epdb/migrations/0001_initial.py b/epdb/migrations/0001_initial.py deleted file mode 100644 index 6032a089..00000000 --- a/epdb/migrations/0001_initial.py +++ /dev/null @@ -1,571 +0,0 @@ -# Generated by Django 5.2.1 on 2025-06-16 13:57 - -import datetime -import django.contrib.auth.models -import django.contrib.auth.validators -import django.contrib.postgres.fields -import django.db.models.deletion -import django.utils.timezone -import model_utils.fields -import uuid -from django.conf import settings -from django.db import migrations, models - - -class Migration(migrations.Migration): - - initial = True - - dependencies = [ - ('auth', '0012_alter_user_first_name_max_length'), - ('contenttypes', '0002_remove_content_type_name'), - ] - - operations = [ - migrations.CreateModel( - name='Compound', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('created', model_utils.fields.AutoCreatedField(default=django.utils.timezone.now, editable=False, verbose_name='created')), - ('modified', model_utils.fields.AutoLastModifiedField(default=django.utils.timezone.now, editable=False, verbose_name='modified')), - ('uuid', models.UUIDField(default=uuid.uuid4, unique=True, verbose_name='UUID of this object')), - ('name', models.TextField(default='no name', verbose_name='Name')), - ('description', models.TextField(default='no description', verbose_name='Descriptions')), - ('kv', models.JSONField(blank=True, default=dict, null=True)), - ('aliases', django.contrib.postgres.fields.ArrayField(base_field=models.TextField(), default=list, size=None, verbose_name='Aliases')), - ], - ), - migrations.CreateModel( - name='EPModel', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('created', model_utils.fields.AutoCreatedField(default=django.utils.timezone.now, editable=False, verbose_name='created')), - ('modified', model_utils.fields.AutoLastModifiedField(default=django.utils.timezone.now, editable=False, verbose_name='modified')), - ('uuid', models.UUIDField(default=uuid.uuid4, unique=True, verbose_name='UUID of this object')), - ('name', models.TextField(default='no name', verbose_name='Name')), - ('description', models.TextField(default='no description', verbose_name='Descriptions')), - ('kv', models.JSONField(blank=True, default=dict, null=True)), - ('polymorphic_ctype', models.ForeignKey(editable=False, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='polymorphic_%(app_label)s.%(class)s_set+', to='contenttypes.contenttype')), - ], - options={ - 'abstract': False, - 'base_manager_name': 'objects', - }, - ), - migrations.CreateModel( - name='Permission', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('created', model_utils.fields.AutoCreatedField(default=django.utils.timezone.now, editable=False, verbose_name='created')), - ('modified', model_utils.fields.AutoLastModifiedField(default=django.utils.timezone.now, editable=False, verbose_name='modified')), - ('permission', models.CharField(choices=[('read', 'Read'), ('write', 'Write'), ('all', 'All')], max_length=32)), - ], - ), - migrations.CreateModel( - name='Package', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('created', model_utils.fields.AutoCreatedField(default=django.utils.timezone.now, editable=False, verbose_name='created')), - ('modified', model_utils.fields.AutoLastModifiedField(default=django.utils.timezone.now, editable=False, verbose_name='modified')), - ('uuid', models.UUIDField(default=uuid.uuid4, unique=True, verbose_name='UUID of this object')), - ('name', models.TextField(default='no name', verbose_name='Name')), - ('description', models.TextField(default='no description', verbose_name='Descriptions')), - ('kv', models.JSONField(blank=True, default=dict, null=True)), - ('reviewed', models.BooleanField(default=False, verbose_name='Reviewstatus')), - ], - options={ - 'abstract': False, - }, - ), - migrations.CreateModel( - name='Rule', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('created', model_utils.fields.AutoCreatedField(default=django.utils.timezone.now, editable=False, verbose_name='created')), - ('modified', model_utils.fields.AutoLastModifiedField(default=django.utils.timezone.now, editable=False, verbose_name='modified')), - ('uuid', models.UUIDField(default=uuid.uuid4, unique=True, verbose_name='UUID of this object')), - ('name', models.TextField(default='no name', verbose_name='Name')), - ('description', models.TextField(default='no description', verbose_name='Descriptions')), - ('kv', models.JSONField(blank=True, default=dict, null=True)), - ('aliases', django.contrib.postgres.fields.ArrayField(base_field=models.TextField(), default=list, size=None, verbose_name='Aliases')), - ('package', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='epdb.package', verbose_name='Package')), - ('polymorphic_ctype', models.ForeignKey(editable=False, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='polymorphic_%(app_label)s.%(class)s_set+', to='contenttypes.contenttype')), - ], - options={ - 'abstract': False, - 'base_manager_name': 'objects', - }, - ), - migrations.CreateModel( - name='User', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('password', models.CharField(max_length=128, verbose_name='password')), - ('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')), - ('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')), - ('username', models.CharField(error_messages={'unique': 'A user with that username already exists.'}, help_text='Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.', max_length=150, unique=True, validators=[django.contrib.auth.validators.UnicodeUsernameValidator()], verbose_name='username')), - ('first_name', models.CharField(blank=True, max_length=150, verbose_name='first name')), - ('last_name', models.CharField(blank=True, max_length=150, verbose_name='last name')), - ('is_staff', models.BooleanField(default=False, help_text='Designates whether the user can log into this admin site.', verbose_name='staff status')), - ('is_active', models.BooleanField(default=True, help_text='Designates whether this user should be treated as active. Unselect this instead of deleting accounts.', verbose_name='active')), - ('date_joined', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date joined')), - ('email', models.EmailField(max_length=254, unique=True)), - ('uuid', models.UUIDField(default=uuid.uuid4, unique=True, verbose_name='UUID of this object')), - ('user_permissions', models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set', related_query_name='user', to='auth.permission', verbose_name='user permissions')), - ], - options={ - 'verbose_name': 'user', - 'verbose_name_plural': 'users', - 'abstract': False, - }, - managers=[ - ('objects', django.contrib.auth.models.UserManager()), - ], - ), - migrations.CreateModel( - name='APIToken', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('hashed_key', models.CharField(max_length=128, unique=True)), - ('created', models.DateTimeField(auto_now_add=True)), - ('expires_at', models.DateTimeField(blank=True, default=datetime.datetime(2025, 9, 14, 13, 57, 57, 138459, tzinfo=datetime.timezone.utc), null=True)), - ('name', models.CharField(blank=True, help_text='Optional name for the token', max_length=100)), - ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), - ], - ), - migrations.CreateModel( - name='CompoundStructure', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('created', model_utils.fields.AutoCreatedField(default=django.utils.timezone.now, editable=False, verbose_name='created')), - ('modified', model_utils.fields.AutoLastModifiedField(default=django.utils.timezone.now, editable=False, verbose_name='modified')), - ('uuid', models.UUIDField(default=uuid.uuid4, unique=True, verbose_name='UUID of this object')), - ('name', models.TextField(default='no name', verbose_name='Name')), - ('description', models.TextField(default='no description', verbose_name='Descriptions')), - ('kv', models.JSONField(blank=True, default=dict, null=True)), - ('aliases', django.contrib.postgres.fields.ArrayField(base_field=models.TextField(), default=list, size=None, verbose_name='Aliases')), - ('smiles', models.TextField(verbose_name='SMILES')), - ('normalized_structure', models.BooleanField(default=False)), - ('compound', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='epdb.compound')), - ], - options={ - 'abstract': False, - }, - ), - migrations.AddField( - model_name='compound', - name='default_structure', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='compound_default_structure', to='epdb.compoundstructure', verbose_name='Default Structure'), - ), - migrations.CreateModel( - name='Edge', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('created', model_utils.fields.AutoCreatedField(default=django.utils.timezone.now, editable=False, verbose_name='created')), - ('modified', model_utils.fields.AutoLastModifiedField(default=django.utils.timezone.now, editable=False, verbose_name='modified')), - ('uuid', models.UUIDField(default=uuid.uuid4, unique=True, verbose_name='UUID of this object')), - ('name', models.TextField(default='no name', verbose_name='Name')), - ('description', models.TextField(default='no description', verbose_name='Descriptions')), - ('kv', models.JSONField(blank=True, default=dict, null=True)), - ('aliases', django.contrib.postgres.fields.ArrayField(base_field=models.TextField(), default=list, size=None, verbose_name='Aliases')), - ('polymorphic_ctype', models.ForeignKey(editable=False, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='polymorphic_%(app_label)s.%(class)s_set+', to='contenttypes.contenttype')), - ], - options={ - 'abstract': False, - 'base_manager_name': 'objects', - }, - ), - migrations.CreateModel( - name='EnviFormer', - fields=[ - ('epmodel_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='epdb.epmodel')), - ('threshold', models.FloatField(default=0.5)), - ], - options={ - 'abstract': False, - 'base_manager_name': 'objects', - }, - bases=('epdb.epmodel',), - ), - migrations.CreateModel( - name='PluginModel', - fields=[ - ('epmodel_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='epdb.epmodel')), - ], - options={ - 'abstract': False, - 'base_manager_name': 'objects', - }, - bases=('epdb.epmodel',), - ), - migrations.CreateModel( - name='RuleBaseRelativeReasoning', - fields=[ - ('epmodel_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='epdb.epmodel')), - ], - options={ - 'abstract': False, - 'base_manager_name': 'objects', - }, - bases=('epdb.epmodel',), - ), - migrations.CreateModel( - name='Group', - fields=[ - ('created', model_utils.fields.AutoCreatedField(default=django.utils.timezone.now, editable=False, verbose_name='created')), - ('modified', model_utils.fields.AutoLastModifiedField(default=django.utils.timezone.now, editable=False, verbose_name='modified')), - ('uuid', models.UUIDField(default=uuid.uuid4, primary_key=True, serialize=False, verbose_name='UUID of this object')), - ('name', models.TextField(verbose_name='Group name')), - ('description', models.TextField(default='no description', verbose_name='Descriptions')), - ('group_member', models.ManyToManyField(related_name='groups_in_group', to='epdb.group', verbose_name='Group member')), - ('owner', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='Group Owner')), - ('user_member', models.ManyToManyField(related_name='users_in_group', to=settings.AUTH_USER_MODEL, verbose_name='User members')), - ], - options={ - 'abstract': False, - }, - ), - migrations.AddField( - model_name='user', - name='default_group', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='default_group', to='epdb.group', verbose_name='Default Group'), - ), - migrations.AddField( - model_name='user', - name='groups', - field=models.ManyToManyField(to='epdb.group', verbose_name='groups'), - ), - migrations.CreateModel( - name='Node', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('created', model_utils.fields.AutoCreatedField(default=django.utils.timezone.now, editable=False, verbose_name='created')), - ('modified', model_utils.fields.AutoLastModifiedField(default=django.utils.timezone.now, editable=False, verbose_name='modified')), - ('uuid', models.UUIDField(default=uuid.uuid4, unique=True, verbose_name='UUID of this object')), - ('name', models.TextField(default='no name', verbose_name='Name')), - ('description', models.TextField(default='no description', verbose_name='Descriptions')), - ('kv', models.JSONField(blank=True, default=dict, null=True)), - ('aliases', django.contrib.postgres.fields.ArrayField(base_field=models.TextField(), default=list, size=None, verbose_name='Aliases')), - ('depth', models.IntegerField(verbose_name='Node depth')), - ('default_node_label', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='default_node_structure', to='epdb.compoundstructure', verbose_name='Default Node Label')), - ('node_labels', models.ManyToManyField(related_name='node_structures', to='epdb.compoundstructure', verbose_name='All Node Labels')), - ('out_edges', models.ManyToManyField(to='epdb.edge', verbose_name='Outgoing Edges')), - ], - options={ - 'abstract': False, - }, - ), - migrations.AddField( - model_name='edge', - name='end_nodes', - field=models.ManyToManyField(related_name='edge_products', to='epdb.node', verbose_name='End Nodes'), - ), - migrations.AddField( - model_name='edge', - name='start_nodes', - field=models.ManyToManyField(related_name='edge_educts', to='epdb.node', verbose_name='Start Nodes'), - ), - migrations.AddField( - model_name='epmodel', - name='package', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='epdb.package', verbose_name='Package'), - ), - migrations.AddField( - model_name='compound', - name='package', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='epdb.package', verbose_name='Package'), - ), - migrations.AddField( - model_name='user', - name='default_package', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='epdb.package', verbose_name='Default Package'), - ), - migrations.CreateModel( - name='SequentialRule', - fields=[ - ('rule_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='epdb.rule')), - ], - options={ - 'abstract': False, - 'base_manager_name': 'objects', - }, - bases=('epdb.rule',), - ), - migrations.CreateModel( - name='SimpleRule', - fields=[ - ('rule_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='epdb.rule')), - ], - options={ - 'abstract': False, - 'base_manager_name': 'objects', - }, - bases=('epdb.rule',), - ), - migrations.CreateModel( - name='Pathway', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('created', model_utils.fields.AutoCreatedField(default=django.utils.timezone.now, editable=False, verbose_name='created')), - ('modified', model_utils.fields.AutoLastModifiedField(default=django.utils.timezone.now, editable=False, verbose_name='modified')), - ('uuid', models.UUIDField(default=uuid.uuid4, unique=True, verbose_name='UUID of this object')), - ('name', models.TextField(default='no name', verbose_name='Name')), - ('description', models.TextField(default='no description', verbose_name='Descriptions')), - ('kv', models.JSONField(blank=True, default=dict, null=True)), - ('aliases', django.contrib.postgres.fields.ArrayField(base_field=models.TextField(), default=list, size=None, verbose_name='Aliases')), - ('package', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='epdb.package', verbose_name='Package')), - ], - options={ - 'abstract': False, - }, - ), - migrations.AddField( - model_name='node', - name='pathway', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='epdb.pathway', verbose_name='belongs to'), - ), - migrations.AddField( - model_name='edge', - name='pathway', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='epdb.pathway', verbose_name='belongs to'), - ), - migrations.CreateModel( - name='Reaction', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('created', model_utils.fields.AutoCreatedField(default=django.utils.timezone.now, editable=False, verbose_name='created')), - ('modified', model_utils.fields.AutoLastModifiedField(default=django.utils.timezone.now, editable=False, verbose_name='modified')), - ('uuid', models.UUIDField(default=uuid.uuid4, unique=True, verbose_name='UUID of this object')), - ('name', models.TextField(default='no name', verbose_name='Name')), - ('description', models.TextField(default='no description', verbose_name='Descriptions')), - ('kv', models.JSONField(blank=True, default=dict, null=True)), - ('aliases', django.contrib.postgres.fields.ArrayField(base_field=models.TextField(), default=list, size=None, verbose_name='Aliases')), - ('multi_step', models.BooleanField(verbose_name='Multistep Reaction')), - ('medline_references', django.contrib.postgres.fields.ArrayField(base_field=models.TextField(), null=True, size=None, verbose_name='Medline References')), - ('educts', models.ManyToManyField(related_name='reaction_educts', to='epdb.compoundstructure', verbose_name='Educts')), - ('package', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='epdb.package', verbose_name='Package')), - ('products', models.ManyToManyField(related_name='reaction_products', to='epdb.compoundstructure', verbose_name='Products')), - ('rules', models.ManyToManyField(related_name='reaction_rule', to='epdb.rule', verbose_name='Rule')), - ], - options={ - 'abstract': False, - }, - ), - migrations.AddField( - model_name='edge', - name='edge_label', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='epdb.reaction', verbose_name='Edge label'), - ), - migrations.CreateModel( - name='Scenario', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('created', model_utils.fields.AutoCreatedField(default=django.utils.timezone.now, editable=False, verbose_name='created')), - ('modified', model_utils.fields.AutoLastModifiedField(default=django.utils.timezone.now, editable=False, verbose_name='modified')), - ('uuid', models.UUIDField(default=uuid.uuid4, unique=True, verbose_name='UUID of this object')), - ('name', models.TextField(default='no name', verbose_name='Name')), - ('description', models.TextField(default='no description', verbose_name='Descriptions')), - ('kv', models.JSONField(blank=True, default=dict, null=True)), - ('date', models.DateField(null=True, verbose_name='Study date')), - ('type', models.CharField(default='Not specified', max_length=256)), - ('additional_information', models.JSONField(verbose_name='Additional Information')), - ('package', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='epdb.package', verbose_name='Package')), - ], - options={ - 'abstract': False, - }, - ), - migrations.AddField( - model_name='rule', - name='scenarios', - field=models.ManyToManyField(to='epdb.scenario', verbose_name='Attached Scenarios'), - ), - migrations.AddField( - model_name='reaction', - name='scenarios', - field=models.ManyToManyField(to='epdb.scenario', verbose_name='Attached Scenarios'), - ), - migrations.AddField( - model_name='pathway', - name='scenarios', - field=models.ManyToManyField(to='epdb.scenario', verbose_name='Attached Scenarios'), - ), - migrations.AddField( - model_name='node', - name='scenarios', - field=models.ManyToManyField(to='epdb.scenario', verbose_name='Attached Scenarios'), - ), - migrations.AddField( - model_name='edge', - name='scenarios', - field=models.ManyToManyField(to='epdb.scenario', verbose_name='Attached Scenarios'), - ), - migrations.AddField( - model_name='compoundstructure', - name='scenarios', - field=models.ManyToManyField(to='epdb.scenario', verbose_name='Attached Scenarios'), - ), - migrations.AddField( - model_name='compound', - name='scenarios', - field=models.ManyToManyField(to='epdb.scenario', verbose_name='Attached Scenarios'), - ), - migrations.CreateModel( - name='Setting', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('created', model_utils.fields.AutoCreatedField(default=django.utils.timezone.now, editable=False, verbose_name='created')), - ('modified', model_utils.fields.AutoLastModifiedField(default=django.utils.timezone.now, editable=False, verbose_name='modified')), - ('uuid', models.UUIDField(default=uuid.uuid4, unique=True, verbose_name='UUID of this object')), - ('name', models.TextField(default='no name', verbose_name='Name')), - ('description', models.TextField(default='no description', verbose_name='Descriptions')), - ('kv', models.JSONField(blank=True, default=dict, null=True)), - ('public', models.BooleanField(default=False)), - ('global_default', models.BooleanField(default=False)), - ('max_depth', models.IntegerField(default=5, verbose_name='Setting Max Depth')), - ('max_nodes', models.IntegerField(default=30, verbose_name='Setting Max Number of Nodes')), - ('model_threshold', models.FloatField(blank=True, default=0.25, null=True, verbose_name='Setting Model Threshold')), - ('model', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='epdb.epmodel', verbose_name='Setting EPModel')), - ('rule_packages', models.ManyToManyField(related_name='setting_rule_packages', to='epdb.package', verbose_name='Setting Rule Packages')), - ], - options={ - 'abstract': False, - }, - ), - migrations.AddField( - model_name='user', - name='default_setting', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='epdb.setting', verbose_name='The users default settings'), - ), - migrations.CreateModel( - name='MLRelativeReasoning', - fields=[ - ('epmodel_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='epdb.epmodel')), - ('threshold', models.FloatField(default=0.5)), - ('model_status', models.CharField(choices=[('INITIAL', 'Initial'), ('INITIALIZING', 'Model is initializing.'), ('BUILDING', 'Model is building.'), ('BUILT_NOT_EVALUATED', 'Model is built and can be used for predictions, Model is not evaluated yet.'), ('EVALUATING', 'Model is evaluating'), ('FINISHED', 'Model has finished building and evaluation.'), ('ERROR', 'Model has failed.')], default='INITIAL')), - ('eval_results', models.JSONField(blank=True, default=dict, null=True)), - ('data_packages', models.ManyToManyField(related_name='data_packages', to='epdb.package', verbose_name='Data Packages')), - ('eval_packages', models.ManyToManyField(related_name='eval_packages', to='epdb.package', verbose_name='Evaluation Packages')), - ('rule_packages', models.ManyToManyField(related_name='rule_packages', to='epdb.package', verbose_name='Rule Packages')), - ], - options={ - 'abstract': False, - 'base_manager_name': 'objects', - }, - bases=('epdb.epmodel',), - ), - migrations.CreateModel( - name='ApplicabilityDomain', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('created', model_utils.fields.AutoCreatedField(default=django.utils.timezone.now, editable=False, verbose_name='created')), - ('modified', model_utils.fields.AutoLastModifiedField(default=django.utils.timezone.now, editable=False, verbose_name='modified')), - ('uuid', models.UUIDField(default=uuid.uuid4, unique=True, verbose_name='UUID of this object')), - ('name', models.TextField(default='no name', verbose_name='Name')), - ('description', models.TextField(default='no description', verbose_name='Descriptions')), - ('kv', models.JSONField(blank=True, default=dict, null=True)), - ('num_neighbours', models.FloatField(default=5)), - ('reliability_threshold', models.FloatField(default=0.5)), - ('local_compatibilty_threshold', models.FloatField(default=0.5)), - ('model', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='epdb.mlrelativereasoning')), - ], - options={ - 'abstract': False, - }, - ), - migrations.CreateModel( - name='SimpleAmbitRule', - fields=[ - ('simplerule_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='epdb.simplerule')), - ('smirks', models.TextField(verbose_name='SMIRKS')), - ('reactant_filter_smarts', models.TextField(null=True, verbose_name='Reactant Filter SMARTS')), - ('product_filter_smarts', models.TextField(null=True, verbose_name='Product Filter SMARTS')), - ], - options={ - 'abstract': False, - 'base_manager_name': 'objects', - }, - bases=('epdb.simplerule',), - ), - migrations.CreateModel( - name='SimpleRDKitRule', - fields=[ - ('simplerule_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='epdb.simplerule')), - ('reaction_smarts', models.TextField(verbose_name='SMIRKS')), - ], - options={ - 'abstract': False, - 'base_manager_name': 'objects', - }, - bases=('epdb.simplerule',), - ), - migrations.CreateModel( - name='SequentialRuleOrdering', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('order_index', models.IntegerField()), - ('sequential_rule', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='epdb.sequentialrule')), - ('simple_rule', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='epdb.simplerule')), - ], - ), - migrations.AddField( - model_name='sequentialrule', - name='simple_rules', - field=models.ManyToManyField(through='epdb.SequentialRuleOrdering', to='epdb.simplerule', verbose_name='Simple rules'), - ), - migrations.CreateModel( - name='ParallelRule', - fields=[ - ('rule_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='epdb.rule')), - ('simple_rules', models.ManyToManyField(to='epdb.simplerule', verbose_name='Simple rules')), - ], - options={ - 'abstract': False, - 'base_manager_name': 'objects', - }, - bases=('epdb.rule',), - ), - migrations.AlterUniqueTogether( - name='compound', - unique_together={('uuid', 'package')}, - ), - migrations.CreateModel( - name='GroupPackagePermission', - fields=[ - ('permission_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, to='epdb.permission')), - ('uuid', models.UUIDField(default=uuid.uuid4, primary_key=True, serialize=False, verbose_name='UUID of this object')), - ('group', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='epdb.group', verbose_name='Permission to')), - ('package', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='epdb.package', verbose_name='Permission on')), - ], - options={ - 'unique_together': {('package', 'group')}, - }, - bases=('epdb.permission',), - ), - migrations.CreateModel( - name='UserPackagePermission', - fields=[ - ('permission_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, to='epdb.permission')), - ('uuid', models.UUIDField(default=uuid.uuid4, primary_key=True, serialize=False, verbose_name='UUID of this object')), - ('package', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='epdb.package', verbose_name='Permission on')), - ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='Permission to')), - ], - options={ - 'unique_together': {('package', 'user')}, - }, - bases=('epdb.permission',), - ), - migrations.CreateModel( - name='UserSettingPermission', - fields=[ - ('permission_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, to='epdb.permission')), - ('uuid', models.UUIDField(default=uuid.uuid4, primary_key=True, serialize=False, verbose_name='UUID of this object')), - ('setting', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='epdb.setting', verbose_name='Permission on')), - ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='Permission to')), - ], - options={ - 'unique_together': {('setting', 'user')}, - }, - bases=('epdb.permission',), - ), - ] diff --git a/epdb/models.py b/epdb/models.py index fa6da7ed..d3882c74 100644 --- a/epdb/models.py +++ b/epdb/models.py @@ -83,8 +83,7 @@ class APIToken(models.Model): class Group(TimeStampedModel): - uuid = models.UUIDField(null=False, blank=False, verbose_name='UUID of this object', primary_key=True, - default=uuid4) + uuid = models.UUIDField(null=False, blank=False, verbose_name='UUID of this object', unique=True, default=uuid4) name = models.TextField(blank=False, null=False, verbose_name='Group name') owner = models.ForeignKey("User", verbose_name='Group Owner', on_delete=models.CASCADE) description = models.TextField(blank=False, null=False, verbose_name='Descriptions', default='no description') @@ -110,6 +109,15 @@ class Permission(TimeStampedModel): ] permission = models.CharField(max_length=32, choices=PERMS, null=False) + def has_read(self): + return self.permission in [p[0] for p in self.PERMS] + + def has_write(self): + return self.permission in [self.WRITE[0], self.ALL[0]] + + def has_all(self): + return self.permission == self.ALL[0] + class Meta: abstract: True diff --git a/epdb/views.py b/epdb/views.py index 3fdce399..e3f7fbee 100644 --- a/epdb/views.py +++ b/epdb/views.py @@ -10,7 +10,8 @@ from django.views.decorators.csrf import csrf_exempt from utilities.chem import FormatConverter, IndigoUtils from .logic import GroupManager, PackageManager, UserManager, SettingManager from .models import Package, GroupPackagePermission, Group, CompoundStructure, Compound, Reaction, Rule, Pathway, Node, \ - EPModel, EnviFormer, MLRelativeReasoning, RuleBaseRelativeReasoning, Scenario, SimpleAmbitRule, APIToken + EPModel, EnviFormer, MLRelativeReasoning, RuleBaseRelativeReasoning, Scenario, SimpleAmbitRule, APIToken, \ + UserPackagePermission, Permission logger = logging.getLogger(__name__) @@ -512,11 +513,30 @@ def package(request, package_uuid): context['breadcrumbs'] = breadcrumbs(current_package) context['package'] = current_package - context['package_group'] = GroupPackagePermission.objects.filter(package=current_package, - permission=GroupPackagePermission.ALL) + # context['package_group'] = GroupPackagePermission.objects.filter(package=current_package, + # permission=GroupPackagePermission.ALL) + + user_perms = UserPackagePermission.objects.filter(package=current_package) + users = get_user_model().objects.exclude( + id__in=UserPackagePermission.objects.filter(package=current_package).values_list('user_id', flat=True)) + + group_perms = GroupPackagePermission.objects.filter(package=current_package) + groups = Group.objects.exclude( + id__in=GroupPackagePermission.objects.filter(package=current_package).values_list('group_id', flat=True)) + + context['users'] = users + context['groups'] = groups + context['user_permissions'] = user_perms + context['group_permissions'] = group_perms + return render(request, 'objects/package.html', context) elif request.method == 'POST': + + if s.DEBUG: + for k, v in request.POST.items(): + print(k, v) + if hidden := request.POST.get('hidden', None): if hidden == 'delete-package': print(current_package.delete()) @@ -527,6 +547,12 @@ def package(request, package_uuid): new_package_name = request.POST.get('package-name') new_package_description = request.POST.get('package-description') + grantee_url = request.POST.get('grantee') + read = request.POST.get('read') == 'on' + write = request.POST.get('write') == 'on' + owner = request.POST.get('owner') == 'on' + + if new_package_name: current_package.name = new_package_name @@ -536,6 +562,23 @@ def package(request, package_uuid): if any([new_package_name, new_package_description]): current_package.save() return redirect(current_package.url) + + elif any([grantee_url, read, write, owner]): + if 'user' in grantee_url: + grantee = UserManager.get_user_lp(grantee_url) + else: + grantee = GroupManager.get_group_lp(grantee_url) + + max_perm = None + if read: + max_perm = Permission.READ[0] + if write: + max_perm = Permission.WRITE[0] + if owner: + max_perm = Permission.ALL[0] + + PackageManager.update_permissions(current_user, current_package, grantee, max_perm) + return redirect(current_package.url) else: return HttpResponseBadRequest() diff --git a/fixtures/EAWAG-BBD.json b/fixtures/EAWAG-BBD.json index 46c642a8..605dce91 100644 --- a/fixtures/EAWAG-BBD.json +++ b/fixtures/EAWAG-BBD.json @@ -93740,7 +93740,7 @@ "scenarios" : [ { "id" : "http://localhost:8080/package/32de3cf4-e3e6-4168-956e-32fa5ddb0ce1/scenario/9e51c3f0-699e-4e4d-b443-6bcc436a1ea6" } ] , - "smirks" : "[#7:7]\\[#6:1](-[#7:8])=[#7]\\?[#7+](-[#8-])=O>>[#7:8]-[#6:1](-[#7:7])=O" + "smirks" : "[#7:7]\\[#6:1](-[#7:8])=[#7]\\[#7+](-[#8-])=O>>[#7:8]-[#6:1](-[#7:7])=O" }, { "aliases" : [ ] , "creationDate" : "2015-11-18 11:33:12.221" , @@ -93777,7 +93777,7 @@ "scenarios" : [ { "id" : "http://localhost:8080/package/32de3cf4-e3e6-4168-956e-32fa5ddb0ce1/scenario/9e51c3f0-699e-4e4d-b443-6bcc436a1ea6" } ] , - "smirks" : "[#7:7]\\[#6:1](-[#7:8])=[#7]\\?[#7+](-[#8-])=O>>[#7:8]-[#6:1](-[#7:7])=O" + "smirks" : "[#7:7]\\[#6:1](-[#7:8])=[#7]\\[#7+](-[#8-])=O>>[#7:8]-[#6:1](-[#7:7])=O" } , { "aliases" : [ ] , @@ -439312,4 +439312,4 @@ ] , "scenarios" : [ ] }] -} \ No newline at end of file +} diff --git a/templates/modals/objects/edit_package_modal.html b/templates/modals/objects/edit_package_modal.html index 1f496953..f9f6b6f4 100644 --- a/templates/modals/objects/edit_package_modal.html +++ b/templates/modals/objects/edit_package_modal.html @@ -4,7 +4,7 @@