Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
e70b525
Reduce direct uses of `Entity.objects`
jonc125 Nov 28, 2019
c24b1d3
Add basic `FittingSpec` model in a new `fitting` app
jonc125 Nov 28, 2019
1f2dd97
Drive-by docstring fixing
jonc125 Nov 28, 2019
84aecb8
Add repocache for fitting specs
jonc125 Nov 28, 2019
9f77284
Illustrate a potential approach to fitting views
jonc125 Nov 29, 2019
ac0e86b
Add `url_type` and `display_type` entity properties
jonc125 Dec 2, 2019
4deafbd
Add a link to the list of fitting specs
jonc125 Dec 2, 2019
cf27456
Better way of defining _type class properties
jonc125 Dec 2, 2019
fa9f9f4
Make the `current_namespace` always available
jonc125 Dec 2, 2019
6ab4518
Let `EntityTypeMixin` know about fitting specs
jonc125 Dec 2, 2019
918a316
Add improved helpers for querying shared/visible entities
jonc125 Dec 4, 2019
96cafdc
flake8
jonc125 Dec 4, 2019
33c7b47
Add protocol field to create fitting spec form
jonc125 Dec 4, 2019
29aa23d
Start generalising entity URL handling
jonc125 Dec 4, 2019
2d8a265
Add repocache migration for fitting specs
jonc125 Dec 4, 2019
97ceb67
Nicer error message
jonc125 Dec 4, 2019
8e5f2b4
Add 'detail' and 'newversion' views for fitting specs
jonc125 Dec 4, 2019
caa2000
Fix signal registration for fitting specs
jonc125 Dec 4, 2019
327898d
Restructure entity tags to be namespace aware...
jonc125 Dec 13, 2019
5d43ece
Fix style checks
jonc125 Dec 13, 2019
a6a0405
Merge branch '217-multiple-caches' into 203-split-entity-table
jonc125 Jan 3, 2020
ff8cf5f
Adapt to new base class names
jonc125 Jan 3, 2020
f9a9b41
Re-run expts is not an option for fitting specs
jonc125 Jan 10, 2020
584cf8c
Make more entity views work for fitting specs
jonc125 Jan 10, 2020
5cb545c
Fix flake8
jonc125 Jan 13, 2020
96ea752
Merge branch 'master' into 203-split-entity-table
jonc125 Jan 13, 2020
e667e81
Merge DB migrations
jonc125 Jan 13, 2020
1d6ea5c
Remove obsolete Files button
jonc125 Jan 15, 2020
9bceda6
Merge pull request #236 from ModellingWebLab/no-files-button
jonc125 Jan 16, 2020
88fdcdd
Add link to corresponding protocol from fitting spec view
jonc125 Jan 16, 2020
7970a24
Include datasets in the 'my files' list view buttons
jonc125 Jan 16, 2020
52c14e4
Merge remote-tracking branch 'origin/master' into 203-split-entity-table
jonc125 Jan 16, 2020
1f84a6e
Merge DB migrations from master
jonc125 Jan 16, 2020
2c04797
Make test cross-platform friendly
jonc125 Jan 17, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 2 additions & 4 deletions weblab/accounts/managers.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,7 @@ def admins(self):

def create_user(self, email, full_name, institution='', password=None):
"""
Creates and saves a superuser with the given email, date of
birth and password.
Creates and saves a user with the given details and password.
"""
user = self.model(
email=self.normalize_email(email),
Expand All @@ -22,8 +21,7 @@ def create_user(self, email, full_name, institution='', password=None):

def create_superuser(self, email, full_name, institution, password):
"""
Creates and saves a superuser with the given email, date of
birth and password.
Creates and saves a superuser with the given details and password.
"""
user = self.create_user(
email,
Expand Down
1 change: 1 addition & 0 deletions weblab/accounts/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ def get_success_url(self):

def get_context_data(self, **kwargs):
perms = {
'entities.create_fittingspec',
'entities.create_protocol',
'entities.create_model',
}
Expand Down
1 change: 1 addition & 0 deletions weblab/config/settings/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@
'datasets',
'entities',
'experiments',
'fitting',
'repocache',
]

Expand Down
1 change: 1 addition & 0 deletions weblab/config/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,5 +32,6 @@
url(r'^entities/', include('entities.urls', namespace='entities')),
url(r'^datasets/', include('datasets.urls', namespace='datasets')),
url(r'^experiments/', include('experiments.urls', namespace='experiments')),
url(r'^fitting/', include('fitting.urls', namespace='fitting')),
url(r'^admin/', admin.site.urls),
]
1 change: 1 addition & 0 deletions weblab/core/context_processors.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,5 @@ def common(request):
'VISIBILITY_HELP': visibility.HELP_TEXT,
'ERROR_MESSAGES': error_messages,
'INFO_MESSAGES': info_messages,
'current_namespace': request.resolver_match.namespace,
}
5 changes: 5 additions & 0 deletions weblab/core/recipes.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@
'ProtocolEntity',
entity_type='protocol', name=seq('myprotocol')
)
fittingspec = Recipe(
'FittingSpec',
entity_type='fittingspec', name=seq('myspec'),
protocol=foreign_key(protocol),
)

model_file = Recipe('EntityFile', entity=foreign_key(model))
protocol_file = Recipe('EntityFile', entity=foreign_key(protocol))
Expand Down
6 changes: 4 additions & 2 deletions weblab/entities/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ def clean_name(self):
name = self.cleaned_data['name']
if self._meta.model.objects.filter(name=name).exists():
raise ValidationError(
'You already have a %s named "%s"' % (self.entity_type, name))
'You already have a %s named "%s"' % (self._meta.model.display_type, name))

return name

Expand Down Expand Up @@ -69,7 +69,9 @@ class EntityVersionForm(forms.Form):
def __init__(self, *args, **kwargs):
entity_type = kwargs.pop('entity_type')
super().__init__(*args, **kwargs)
self.fields['rerun_expts'].label = self.fields['rerun_expts'].label % entity_type
rerun_field = self.fields.get('rerun_expts', None)
if rerun_field:
rerun_field.label = rerun_field.label % entity_type


class EntityChangeVisibilityForm(UserKwargModelFormMixin, forms.Form):
Expand Down
24 changes: 24 additions & 0 deletions weblab/entities/migrations/0015_auto_20191128_1601.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.20 on 2019-11-28 16:01
from __future__ import unicode_literals

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('entities', '0014_entity_is_fitting_spec'),
]

operations = [
migrations.AlterModelOptions(
name='entity',
options={'ordering': ['name'], 'permissions': (('create_model', 'Can create models'), ('create_protocol', 'Can create protocols'), ('create_fittingspec', 'Can create fitting specifications'), ('edit_entity', 'Can edit entity'), ('moderator', 'Can promote public entity versions to moderated'))},
),
migrations.AlterField(
model_name='entity',
name='entity_type',
field=models.CharField(choices=[('model', 'model'), ('protocol', 'protocol'), ('fittingspec', 'fittingspec')], max_length=16),
),
]
47 changes: 45 additions & 2 deletions weblab/entities/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,28 @@


class Entity(UserCreatedModelMixin, models.Model):
"""
Base class for 'entities' - conceptual entities backed by git repositories.

Subclasses describe (CellML) models, protocols, and fitting specifications.

The entity_type column states which concrete type each DB row represents, and is fixed by each subclass.
In addition, other class properties defined in subclasses refer to these types in helpful ways:
- ``other_type`` refers to the other axis on a models vs protocols matrix
- ``display_type`` is used to display the type of the entity to users in templates
- ``url_type`` is used as a URL fragment to refer to this entity type
"""
DEFAULT_VISIBILITY = Visibility.PRIVATE

VISIBILITY_HELP = VIS_HELP_TEXT

ENTITY_TYPE_MODEL = 'model'
ENTITY_TYPE_PROTOCOL = 'protocol'
ENTITY_TYPE_FITTINGSPEC = 'fittingspec'
ENTITY_TYPE_CHOICES = (
(ENTITY_TYPE_MODEL, ENTITY_TYPE_MODEL),
(ENTITY_TYPE_PROTOCOL, ENTITY_TYPE_PROTOCOL),
(ENTITY_TYPE_FITTINGSPEC, ENTITY_TYPE_FITTINGSPEC),
)

entity_type = models.CharField(
Expand All @@ -48,6 +61,7 @@ class Meta:
permissions = (
('create_model', 'Can create models'),
('create_protocol', 'Can create protocols'),
('create_fittingspec', 'Can create fitting specifications'),
# Edit entity is used as an object-level permission
('edit_entity', 'Can edit entity'),
('moderator', 'Can promote public entity versions to moderated'),
Expand Down Expand Up @@ -269,18 +283,45 @@ def create(self, **kwargs):
kwargs['entity_type'] = self.model.entity_type
return super().create(**kwargs)

def visible_to_user(self, user):
"""Query over all managed entities that the given user can view.

This includes those entities of the managed ``entity_type`` for which either:
- the user is the author
- the entity has at least one non-private version
- or the entity is explicitly shared with the user
"""
from repocache.models import CACHED_VERSION_TYPE_MAP
CachedEntityVersion = CACHED_VERSION_TYPE_MAP[self.model.entity_type]
non_private = self.annotate(
non_private=models.Exists(
CachedEntityVersion.objects.filter(
entity__entity=models.OuterRef('pk'),
visibility__in=['public', 'moderated'],
)
)
).filter(
non_private=True,
)
owned = self.filter(author=user)
shared = self.shared_with_user(user)
return owned | non_private | shared

def shared_with_user(self, user):
"""Query over all managed entities shared explicitly with the given user."""
if user.is_authenticated:
return get_objects_for_user(user, 'entities.edit_entity', with_superuser=False).filter(
entity_type=self.model.entity_type)
shared_pks = get_objects_for_user(
user, 'entities.edit_entity', with_superuser=False).values_list('pk', flat=True)
return self.get_queryset().filter(pk__in=shared_pks)
else:
return self.none()


class ModelEntity(Entity):
entity_type = Entity.ENTITY_TYPE_MODEL
other_type = Entity.ENTITY_TYPE_PROTOCOL
display_type = 'model'
url_type = 'model'

objects = EntityManager()

Expand All @@ -292,6 +333,8 @@ class Meta:
class ProtocolEntity(Entity):
entity_type = Entity.ENTITY_TYPE_PROTOCOL
other_type = Entity.ENTITY_TYPE_MODEL
display_type = 'protocol'
url_type = 'protocol'

objects = EntityManager()

Expand Down
3 changes: 2 additions & 1 deletion weblab/entities/signals.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,5 @@ def entity_deleted(sender, instance, **kwargs):
"""
Signal callback when an entity is about to be deleted.
"""
instance.repo.delete()
if instance.repo_abs_path.exists():
instance.repo.delete()
Loading