Browse Source

Python pep8 format

master
Joshua Rubingh 10 months ago
parent
commit
5aebd97777
  1. 3
      .pep8
  2. 9
      VRE/apps/api/exceptions.py
  3. 64
      VRE/apps/api/urls.py
  4. 2
      VRE/apps/study/__init__.py
  5. 10
      VRE/apps/study/admin.py
  6. 7
      VRE/apps/study/apps.py
  7. 53
      VRE/apps/study/models.py
  8. 3
      VRE/apps/study/permissions.py
  9. 47
      VRE/apps/study/tests.py
  10. 13
      VRE/apps/university/models.py

3
.pep8

@ -0,0 +1,3 @@ @@ -0,0 +1,3 @@
[pycodestyle]
max_line_length = 120
ignore = E501

9
VRE/apps/api/exceptions.py

@ -25,13 +25,16 @@ def json_schema_error_message(exc, context): @@ -25,13 +25,16 @@ def json_schema_error_message(exc, context):
# }
# ]
system_errors = exc.get_full_details()
try:
system_errors = exc.get_full_details()
except Exception as ex:
system_errors = [{'exception' : {'code' : 'exception', 'message' : 'Something went wrong'}}]
if isinstance(system_errors, list):
system_errors = {'header' : system_errors}
system_errors = {'_no_field' : system_errors}
elif system_errors.get('message') is not None:
# If there is a message keyword in the main system errors, then it is not a form error, but a more general error
system_errors = {'header' : [system_errors]}
system_errors = {'_no_field' : [system_errors]}
json_errors = []
for errorfield, errors in system_errors.items():

64
VRE/apps/api/urls.py

@ -1,7 +1,5 @@ @@ -1,7 +1,5 @@
from django.urls import path, re_path, include
from rest_framework import permissions, routers
from drf_yasg.views import get_schema_view
from drf_yasg import openapi
@ -12,25 +10,25 @@ from . import views @@ -12,25 +10,25 @@ from . import views
# from apps.storage.views import StorageEngineViewSet, StorageLocationViewSet
from apps.study.views import Studies
from apps.virtual_machine.views import (VirtualMachineViewSet,
VirtualMachineOperatingSystemViewSet,
VirtualMachineProfileViewSet,
VirtualMachineMemoryViewSet,
VirtualMachineNetworkViewSet,
VirtualMachineStorageViewSet,
VirtualMachineGPUViewSet,
VirtualMachineAccessViewSet)
VirtualMachineOperatingSystemViewSet,
VirtualMachineProfileViewSet,
VirtualMachineMemoryViewSet,
VirtualMachineNetworkViewSet,
VirtualMachineStorageViewSet,
VirtualMachineGPUViewSet,
VirtualMachineAccessViewSet)
schema_view = get_schema_view(
openapi.Info(
title="Virtual Research Environment API",
default_version='v1',
description="Here you can see a list of API endpoints and actions that are available to communicate with the VRE API",
terms_of_service="https://www.rug.nl",
contact=openapi.Contact(email="vre_team@rug.nl"),
license=openapi.License(name="MIT License"),
),
public=True,
permission_classes=(permissions.AllowAny,),
openapi.Info(
title="Virtual Research Environment API",
default_version='v1',
description="Here you can see a list of API endpoints and actions that are available to communicate with the VRE API",
terms_of_service="https://www.rug.nl",
contact=openapi.Contact(email="vre_team@rug.nl"),
license=openapi.License(name="MIT License"),
),
public=True,
permission_classes=(permissions.AllowAny,),
)
api_router_v1 = routers.DefaultRouter()
@ -60,20 +58,20 @@ api_router_v1.urls.append(path('vrw/', include('apps.vrw.urls'))) @@ -60,20 +58,20 @@ api_router_v1.urls.append(path('vrw/', include('apps.vrw.urls')))
# Main namespace for the API urls
app_name = 'api'
urlpatterns = [
re_path(r'^swagger(?P<format>\.json|\.yaml)$', schema_view.without_ui(cache_timeout=0), name='schema-json'),
path('swagger/', schema_view.with_ui('swagger', cache_timeout=0), name='schema-swagger-ui'),
path('redoc/', schema_view.with_ui('redoc', cache_timeout=0), name='schema-redoc'),
re_path(r'^swagger(?P<format>\.json|\.yaml)$', schema_view.without_ui(cache_timeout=0), name='schema-json'),
path('swagger/', schema_view.with_ui('swagger', cache_timeout=0), name='schema-swagger-ui'),
path('redoc/', schema_view.with_ui('redoc', cache_timeout=0), name='schema-redoc'),
# Authentication urls
path('auth/', include('djoser.urls')),
path('auth/', include('djoser.urls.jwt')),
# Custom login for getting the HAWK keys
path('auth/hawk/create/', views.Login.as_view(), name='api-login'),
# Authentication urls
path('auth/', include('djoser.urls')),
path('auth/', include('djoser.urls.jwt')),
# Custom login for getting the HAWK keys
path('auth/hawk/create/', views.Login.as_view(), name='api-login'),
# Extra /api/info path for checking if the Hawk authentication is working.
# Also this will give the full url to the OpenAPI documentation
path('info/', views.Info.as_view(), name='api-info'),
# Extra /api/info path for checking if the Hawk authentication is working.
# Also this will give the full url to the OpenAPI documentation
path('info/', views.Info.as_view(), name='api-info'),
# Add extra namespace for versioning the API
path('v1/', include((api_router_v1.urls,'v1'))),
]
# Add extra namespace for versioning the API
path('v1/', include((api_router_v1.urls, 'v1'))),
]

2
VRE/apps/study/__init__.py

@ -1 +1 @@ @@ -1 +1 @@
default_app_config = 'apps.study.apps.StudyConfig'
default_app_config = 'apps.study.apps.StudyConfig'

10
VRE/apps/study/admin.py

@ -7,18 +7,20 @@ from .models import Study, StudyRole @@ -7,18 +7,20 @@ from .models import Study, StudyRole
# Register your models here.
admin.site.register(StudyRole)
class role_inline(admin.TabularInline):
model = StudyRole
extra = 1
@admin.register(Study)
class StudyAdmin(admin.ModelAdmin):
inlines = (role_inline,)
list_display = ('name', 'total_files', 'total_file_size', 'total_invitations', 'created_at')
ordering = ('-created_at','name', )
search_fields = ('name', )
list_display = ('name', 'total_files', 'total_file_size', 'total_invitations', 'created_at')
ordering = ('-created_at', 'name', )
search_fields = ('name', )
readonly_fields = ('upload_uuid', 'api_upload_url', 'total_files', 'total_file_size', 'total_invitations', 'created_at', 'updated_at')
def total_file_size(self,obj):
def total_file_size(self, obj):
return filesizeformat(obj.total_file_size)

7
VRE/apps/study/apps.py

@ -2,20 +2,21 @@ from django.apps import AppConfig @@ -2,20 +2,21 @@ from django.apps import AppConfig
from django.conf import settings
from django.utils.translation import ugettext_lazy as _
class StudyConfig(AppConfig):
name = 'apps.study'
label = 'study'
verbose_name = _('Study')
verbose_name_plural = _('Studies')
if not hasattr(settings,'DEFAULT_VM_PROFILE'):
if not hasattr(settings, 'DEFAULT_VM_PROFILE'):
settings.DEFAULT_VM_PROFILE = 2
if not hasattr(settings,'DEFAULT_VM_OS'):
if not hasattr(settings, 'DEFAULT_VM_OS'):
settings.DEFAULT_VM_OS = 2
def ready(self):
"""
Load custom signals for creating :term:`VRW` models
"""
import apps.study.signals
import apps.study.signals

53
VRE/apps/study/models.py

@ -13,6 +13,8 @@ from apps.university.models import StudyField @@ -13,6 +13,8 @@ from apps.university.models import StudyField
import uuid
# Create your models here.
class StudyManager(models.Manager):
"""
This is a custom study manager for getting extra information from a study model like:
@ -31,25 +33,26 @@ class StudyManager(models.Manager): @@ -31,25 +33,26 @@ class StudyManager(models.Manager):
This will overrule/alter the existing queryset.
"""
return super().get_queryset().select_related('storagelocation').annotate(
_total_files=Count('files', distinct=True),
_total_file_size=Sum('files__filesize', distinct=True),
_total_invitations=Count('invitations', distinct=True),
)
_total_files=Count('files', distinct=True),
_total_file_size=Sum('files__filesize', distinct=True),
_total_invitations=Count('invitations', distinct=True),
)
class StudyRoleNames(models.TextChoices):
"""Research study role options
Valid status values:
- Administrator
- Owner
- Researcher
- Member
"""
ADMIN = ('ADMIN', _('Administrator'))
OWNER = ('OWNER', _('Owner'))
ADMIN = ('ADMIN', _('Administrator'))
# OWNER = ('OWNER', _('Owner'))
RESEARCHER = ('RESEARCHER', _('Researcher'))
MEMBER = ('MEMBER', _('Member'))
MEMBER = ('MEMBER', _('Member'))
class Study(MetaDataModel):
"""
@ -75,35 +78,34 @@ class Study(MetaDataModel): @@ -75,35 +78,34 @@ class Study(MetaDataModel):
verbose_name_plural = _('studies')
ordering = ['name']
owner = models.ForeignKey(Researcher, verbose_name=Researcher._meta.verbose_name, on_delete=models.CASCADE, help_text=_('The researcher that is the owner this study. By default the researcher that has created this study.'))
name = models.CharField(_('Name'), max_length=200, help_text=_('Name of the research study.'))
description = models.TextField(_('Description'), blank=True, null=True, help_text=_('Enter a short description for this study.'))
owner = models.ForeignKey(Researcher, verbose_name=Researcher._meta.verbose_name, on_delete=models.CASCADE, help_text=_('The researcher that is the owner this study. By default the researcher that has created this study.'))
name = models.CharField(_('Name'), max_length=200, help_text=_('Name of the research study.'))
description = models.TextField(_('Description'), blank=True, null=True, help_text=_('Enter a short description for this study.'))
code = models.CharField(_('Code'), max_length=50, help_text=_('The research study code.'))
human_subject = models.BooleanField(_('Human subject'), help_text=_('Is this research study using real humans.'))
field = models.ForeignKey(StudyField, verbose_name=StudyField._meta.verbose_name, on_delete=models.CASCADE, help_text=_('The study field for this reaserch study.'))
code = models.CharField(_('Code'), max_length=50, help_text=_('The research study code.'))
human_subject = models.BooleanField(_('Human subject'), help_text=_('Is this research study using real humans.'))
field = models.ForeignKey(StudyField, verbose_name=StudyField._meta.verbose_name, on_delete=models.CASCADE, help_text=_('The study field for this reaserch study.'))
contributors = models.ManyToManyField(Researcher, related_name='contributors', through='StudyRole', through_fields=('study','researcher'))
contributors = models.ManyToManyField(Researcher, related_name='contributors', through='StudyRole', through_fields=('study', 'researcher'))
# These are used for accepting files (datadrops)
upload_code = models.CharField(_('Upload code'), max_length=20, default=get_random_int_value, editable=False, help_text=_('A unique upload code. Will be generated when a new study is saved.'))
upload_uuid = models.UUIDField(_('Upload url key'), unique=True, default=uuid.uuid4, editable=False, help_text=_('A unique upload url. Will be generated when a new study is saved.'))
upload_code = models.CharField(_('Upload code'), max_length=20, default=get_random_int_value, editable=False, help_text=_('A unique upload code. Will be generated when a new study is saved.'))
upload_uuid = models.UUIDField(_('Upload url key'), unique=True, default=uuid.uuid4, editable=False, help_text=_('A unique upload url. Will be generated when a new study is saved.'))
# Here we load our custom StudyManager so we always have the amount of files, total file size and amount of invitations directly accessable
objects = StudyManager()
objects = StudyManager()
@property
def has_storage(self):
"""boolean: Returns true when there is at least one storage location connected to this study"""
#print(dir(self.storagelocation))
# print(dir(self.storagelocation))
return self.storagelocation is not None
@property
def get_absolute_url(self):
"""str: Returns the full url to the study detail page."""
return 'aaa'
#return reverse('study:detail', kwargs={'study_id': self.pk})
# return reverse('study:detail', kwargs={'study_id': self.pk})
@property
def web_upload_url(self):
@ -134,12 +136,13 @@ class Study(MetaDataModel): @@ -134,12 +136,13 @@ class Study(MetaDataModel):
"""str: Returns a readable string for the study."""
return f'{self.name}'
class StudyRole(MetaDataModel):
study = models.ForeignKey(Study, verbose_name=Study._meta.verbose_name, on_delete=models.CASCADE, help_text=_('Study'))
study = models.ForeignKey(Study, verbose_name=Study._meta.verbose_name, on_delete=models.CASCADE, help_text=_('Study'))
researcher = models.ForeignKey(Researcher, verbose_name=Researcher._meta.verbose_name, on_delete=models.CASCADE, help_text=_('Researcher'))
role = models.CharField(_('Role'), max_length=10, choices=StudyRoleNames.choices, default=StudyRoleNames.MEMBER, help_text=_('The role withing this research study.'))
active = models.BooleanField(_('Active'), default=True, help_text=_(''))
role = models.CharField(_('Role'), max_length=10, choices=StudyRoleNames.choices, default=StudyRoleNames.MEMBER, help_text=_('The role withing this research study.'))
active = models.BooleanField(_('Active'), default=True, help_text=_(''))
def __str__(self):
"""str: Returns a readable string for the study role."""
return f'{self.researcher} is {self.role} of {self.study.name}'
return f'{self.researcher} is {self.role} of {self.study.name}'

3
VRE/apps/study/permissions.py

@ -1,5 +1,6 @@ @@ -1,5 +1,6 @@
from rest_framework.permissions import BasePermission
class IsStudyContributor(BasePermission):
def has_object_permission(self, request, view, obj):
return obj.contributors.filter(researcher=request.user.researcher).exists()
return obj.contributors.filter(researcher=request.user.researcher).exists()

47
VRE/apps/study/tests.py

@ -14,24 +14,26 @@ from django.urls import reverse @@ -14,24 +14,26 @@ from django.urls import reverse
import random
# Create your tests here.
class StudyCreateTest(TestCase):
fixtures = ('university_initial_data','virtual_machine_initial_data')
fixtures = ('university_initial_data', 'virtual_machine_initial_data')
@classmethod
def setUpTestData(cls):
cls.firstname = 'Jan'
cls.lastname = 'Doedel'
cls.email = 'dummy@rug.nl'
cls.username = 'dummy@rug.nl'
cls.password = ';t2QG*LLtzdnC*'
cls.lastname = 'Doedel'
cls.email = 'dummy@rug.nl'
cls.username = 'dummy@rug.nl'
cls.password = ';t2QG*LLtzdnC*'
cls.faculty = Faculty.objects.get(pk=9)
cls.faculty = Faculty.objects.get(pk=9)
# Create a new user
cls.user = User.objects.create_user(username = cls.username, password = cls.password, email = cls.email)
cls.user = User.objects.create_user(username=cls.username, password=cls.password, email=cls.email)
cls.user.first_name = cls.firstname
cls.user.last_name = cls.lastname
cls.user.last_name = cls.lastname
cls.user.save()
# Make a researcher member of a faculty
@ -42,7 +44,7 @@ class StudyCreateTest(TestCase): @@ -42,7 +44,7 @@ class StudyCreateTest(TestCase):
# Create some extra researchers.....
for x in range(5):
user = User.objects.create_user(username = f'Username_{x}', password = f'{cls.password}_{x}', email = f'researcher_{x}@rug.nl')
user = User.objects.create_user(username=f'Username_{x}', password=f'{cls.password}_{x}', email=f'researcher_{x}@rug.nl')
user.first_name = f'Rs_{x}'
user.last_name = f'LN_{x}'
user.save()
@ -52,7 +54,7 @@ class StudyCreateTest(TestCase): @@ -52,7 +54,7 @@ class StudyCreateTest(TestCase):
cls.valid_contributor = user
else:
user.researcher.faculty = Faculty.objects.get(pk=random.randint(2,8))
user.researcher.faculty = Faculty.objects.get(pk=random.randint(2, 8))
cls.invalid_contributor = user
user.researcher.save()
@ -62,7 +64,7 @@ class StudyCreateTest(TestCase): @@ -62,7 +64,7 @@ class StudyCreateTest(TestCase):
self.client = RequestsClient()
# Get a JWT token
login_data = {'username' : self.username, 'password' : self.password }
login_data = {'username': self.username, 'password': self.password}
endpoint = 'http://testserver/api/auth/jwt/create/'
response = self.client.post(endpoint, json=login_data)
@ -95,7 +97,7 @@ class StudyCreateTest(TestCase): @@ -95,7 +97,7 @@ class StudyCreateTest(TestCase):
# Check if we are the only reseacher....
self.assertEqual(len(study['contributors']), 1)
# And make sure we are 'ADMIN'
self.assertEqual(study['contributors'][0]['role'],'ADMIN')
self.assertEqual(study['contributors'][0]['role'], 'ADMIN')
self.assertEqual(study['contributors'][0]['researcher']['id'], self.user.researcher.id)
# And now we should have 1 study
@ -121,7 +123,7 @@ class StudyCreateTest(TestCase): @@ -121,7 +123,7 @@ class StudyCreateTest(TestCase):
}
response = self.client.post(endpoint, json=data)
self.assertEqual(response.status_code, 400)
self.assertEqual(response.json()[0]['message'],f'Study field {study_field} is not valid for faculty {self.faculty}')
self.assertEqual(response.json()[0]['message'], f'Study field {study_field} is not valid for faculty {self.faculty}')
def test_update_study(self):
# We need a new study to test with.....:(
@ -130,7 +132,7 @@ class StudyCreateTest(TestCase): @@ -130,7 +132,7 @@ class StudyCreateTest(TestCase):
# Get the first study through Django Models, which should be the new created study
study = Study.objects.get(contributors__in=[self.user.researcher])
endpoint = 'http://testserver' + reverse('api:v1:study-detail',kwargs={ 'pk' : study.id})
endpoint = 'http://testserver' + reverse('api:v1:study-detail', kwargs={'pk': study.id})
data = {
"name": "Test Onderzoek - UPDATE",
"description": "Doe maar een lange onderzoek - UPDATE",
@ -140,7 +142,7 @@ class StudyCreateTest(TestCase): @@ -140,7 +142,7 @@ class StudyCreateTest(TestCase):
}
response = self.client.put(endpoint, json=data)
# And now we should have 1 study
# And now we should have 1 study
endpoint = 'http://testserver' + reverse('api:v1:study-list')
response = self.client.get(endpoint)
@ -149,7 +151,6 @@ class StudyCreateTest(TestCase): @@ -149,7 +151,6 @@ class StudyCreateTest(TestCase):
self.assertEqual(len(response.json()['results']), 1)
self.assertEqual(response.json()['results'][0]['id'], study.id)
def test_add_new_contributor(self):
# We need a new study to test with.....:(
self.test_create_study()
@ -157,10 +158,10 @@ class StudyCreateTest(TestCase): @@ -157,10 +158,10 @@ class StudyCreateTest(TestCase):
# Get the first study through Django Models, which should be the new created study
study = Study.objects.get(contributors__in=[self.user.researcher])
endpoint = 'http://testserver' + reverse('api:v1:study-contributors', kwargs={ 'pk' : study.id})
endpoint = 'http://testserver' + reverse('api:v1:study-contributors', kwargs={'pk': study.id})
data = [{
"researcher_id" : self.valid_contributor.researcher.id,
"role" : "RESEARCHER"
"researcher_id": self.valid_contributor.researcher.id,
"role": "RESEARCHER"
}]
response = self.client.post(endpoint, json=data)
@ -176,12 +177,12 @@ class StudyCreateTest(TestCase): @@ -176,12 +177,12 @@ class StudyCreateTest(TestCase):
# Get the first study through Django Models, which should be the new created study
study = Study.objects.get(contributors__in=[self.user.researcher])
endpoint = 'http://testserver' + reverse('api:v1:study-contributors', kwargs={ 'pk' : study.id})
endpoint = 'http://testserver' + reverse('api:v1:study-contributors', kwargs={'pk': study.id})
data = [{
"researcher_id" : self.invalid_contributor.researcher.id,
"role" : "RESEARCHER"
"researcher_id": self.invalid_contributor.researcher.id,
"role": "RESEARCHER"
}]
response = self.client.post(endpoint, json=data)
# We expect ONLY 1 contributors now. The invalid researcher should not be added
self.assertEqual(len(response.json()), 1)
self.assertEqual(len(response.json()), 1)

13
VRE/apps/university/models.py

@ -5,6 +5,8 @@ from django.utils.translation import gettext_lazy as _ @@ -5,6 +5,8 @@ from django.utils.translation import gettext_lazy as _
from lib.models.base import MetaDataModel
# Create your models here.
class University(MetaDataModel):
"""
A model to represent a University. This model is used to combine faculties and their study fields. Also researchers are member of a university.
@ -25,8 +27,8 @@ class University(MetaDataModel): @@ -25,8 +27,8 @@ class University(MetaDataModel):
verbose_name_plural = _('universities')
ordering = ['name']
name = models.CharField(_('Name'), max_length=200, help_text=_('The name of the university.'))
email = models.EmailField(_('Email address'),max_length=200, help_text=_('The general email address for this university.'))
name = models.CharField(_('Name'), max_length=200, help_text=_('The name of the university.'))
email = models.EmailField(_('Email address'), max_length=200, help_text=_('The general email address for this university.'))
website = models.CharField(_('Website'), max_length=200, help_text=_('The full url to this university website.'))
def __str__(self):
@ -52,8 +54,8 @@ class Faculty(MetaDataModel): @@ -52,8 +54,8 @@ class Faculty(MetaDataModel):
verbose_name_plural = _('faculties')
ordering = ['name']
name = models.CharField(_('Name'), max_length=200, help_text=_('The name of the faculty.'))
university = models.ForeignKey(University, verbose_name=University._meta.verbose_name, on_delete=models.CASCADE, help_text=_('To wich university belongs this faculty'))
name = models.CharField(_('Name'), max_length=200, help_text=_('The name of the faculty.'))
university = models.ForeignKey(University, verbose_name=University._meta.verbose_name, on_delete=models.CASCADE, help_text=_('To wich university belongs this faculty'))
def __str__(self):
"""str: Returns a readable string for the facutlty."""
@ -78,10 +80,9 @@ class StudyField(MetaDataModel): @@ -78,10 +80,9 @@ class StudyField(MetaDataModel):
verbose_name_plural = _('study fields')
ordering = ['name']
name = models.CharField(_('Name'), max_length=200, help_text=_('The name of the study field.'))
name = models.CharField(_('Name'), max_length=200, help_text=_('The name of the study field.'))
faculty = models.ForeignKey(Faculty, verbose_name=Faculty._meta.verbose_name, on_delete=models.CASCADE, help_text=_('To wich faculty belongs this study'))
def __str__(self):
"""str: Returns a readable string for the studyfield."""
return f'{self.name}'

Loading…
Cancel
Save