Skip to content

Commit

Permalink
Release v0.5.0
Browse files Browse the repository at this point in the history
  • Loading branch information
lucalianas committed Jun 29, 2020
2 parents 600d779 + 7d21b35 commit 85b9d18
Show file tree
Hide file tree
Showing 46 changed files with 3,169 additions and 64 deletions.
6 changes: 5 additions & 1 deletion Gruntfile.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,11 @@ module.exports = function(grunt) {
"promort/src/js/clinical_annotations_manager/clinical_annotations_manager.services.js",
"promort/src/js/clinical_annotations_manager/clinical_annotations_manager.controllers.js",
"promort/src/js/clinical_annotations_manager/clinical_annotations_manager.directives.js",
"promort/src/js/ome_seadragon_tools/cell_count_guide.js"
"promort/src/js/ome_seadragon_tools/cell_count_guide.js",
"promort/src/js/questionnaires_manager/questionnaires_manager.module.js",
"promort/src/js/questionnaires_manager/questionnaires_manager.services.js",
"promort/src/js/questionnaires_manager/questionnaires_manager.controller.js",
"promort/src/js/questionnaires_manager/questionnaires_manager.directives.js"
];

grunt.initConfig({
Expand Down
2 changes: 1 addition & 1 deletion promort/VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
0.4.15
0.5.0
1 change: 1 addition & 0 deletions promort/config/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ django:
- localhost
static_root: ./static/
session_cookie: promort_sessionid
session_expire_on_close: false

database:
engine: sqlite3
Expand Down
5 changes: 4 additions & 1 deletion promort/promort/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,8 @@
'rois_manager',
'clinical_annotations_manager',
'odin',
'utils'
'utils',
'questionnaires_manager'
)

MIDDLEWARE = (
Expand Down Expand Up @@ -98,6 +99,8 @@

WSGI_APPLICATION = 'promort.wsgi.application'

SESSION_EXPIRE_AT_BROWSER_CLOSE = cfg['django']['session_expire_on_close']

# Django logger
LOGGING = {
'version': 1,
Expand Down
36 changes: 20 additions & 16 deletions promort/promort/urls.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,3 @@
"""promort URL Configuration
The `urlpatterns` list routes URLs to views. For more information please see:
https://docs.djangoproject.com/en/1.8/topics/http/urls/
Examples:
Function views
1. Add an import: from my_app import views
2. Add a URL to urlpatterns: url(r'^$', views.home, name='home')
Class-based views
1. Add an import: from other_app.views import Home
2. Add a URL to urlpatterns: url(r'^$', Home.as_view(), name='home')
Including another URLconf
1. Add an import: from blog import urls as blog_urls
2. Add a URL to urlpatterns: url(r'^blog/', include(blog_urls))
"""
# Copyright (c) 2019, CRS4
#
# Permission is hereby granted, free of charge, to any person obtaining a copy of
Expand Down Expand Up @@ -41,7 +26,8 @@
from authentication.views import LoginView, LogoutView, \
GroupListView, GroupDetailsView, CheckUserView
from slides_manager.views import LaboratoryList, LaboratoryDetail, LaboratoryCaseLink, \
CaseList, CaseDetail, SlideList, SlideDetail, SlideEvaluationDetail
CaseList, CaseDetail, SlideList, SlideDetail, SlideEvaluationDetail, SlidesSetList, SlidesSetDetail
import questionnaires_manager.views as qmv
import reviews_manager.views as rmv
from worklist_manager.views import UserWorkList, UserWorklistROIsAnnotation,\
UserWorklistClinicalAnnotation, WorkListAdmin
Expand Down Expand Up @@ -71,6 +57,24 @@
url(r'^api/cases/(?P<pk>[\w\-.]+)/$', CaseDetail.as_view()),
url(r'^api/slides/$', SlideList.as_view()),
url(r'^api/slides/(?P<pk>[\w\-.]+)/$', SlideDetail.as_view()),
url(r'^api/slides_set/$', SlidesSetList.as_view()),
url(r'^api/slides_set/(?P<pk>[\w\-.]+)/$', SlidesSetDetail.as_view()),

# slides questionnaire
url(r'api/questions_sets/$', qmv.QuestionsSetList.as_view()),
url(r'api/questions_sets/(?P<pk>[\w\-.]+)/$', qmv.QuestionsSetDetail.as_view()),
url(r'api/questionnaires/$', qmv.QuestionnaireList.as_view()),
url(r'api/questionnaires/(?P<pk>[\w\-.]+)/$', qmv.QuestionnaireDetail.as_view()),
url(r'api/questionnaires/(?P<quest_pk>[\w\-.]+)/(?P<step_index>[0-9]+)/$', qmv.QuestionnaireStepDetail.as_view()),

# slides questionnaire worklist and answers
url(r'api/questionnaire_requests/(?P<label>[\w\-.]+)/$', qmv.QuestionnaireRequestDetail.as_view()),
url(r'api/questionnaire_requests/(?P<label>[\w\-.]+)/status/$', qmv.QuestionnaireRequestStatus.as_view()),
url(r'api/questionnaire_requests/(?P<label>[\w\-.]+)/answers/$', qmv.QuestionnaireRequestAnswers.as_view()),
url(r'api/questionnaire_requests/(?P<label>[\w\-.]+)/(?P<panel>panel_a|panel_b)/$',
qmv.QuestionnaireRequestPanelDetail.as_view()),
url(r'api/questionnaire_requests/(?P<label>[\w\-.]+)/(?P<panel>panel_a|panel_b)/answers/$',
qmv.QuestionnairePanelAnswersDetail.as_view()),

# ROIs annotation steps details
url(r'api/rois_annotation_steps/(?P<label>[A-Fa-f0-9\-.]+)/clinical_annotation_steps/$',
Expand Down
27 changes: 0 additions & 27 deletions tools/clear_db.py → promort/questionnaires_manager/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,30 +16,3 @@
# COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
# IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

# =======
# WARNING: this script is intended to be only used during the development stage of the project
# to clean completely the database of the application
# =======

from reviews_manager.models import Review, ReviewStep
from slides_manager.models import Case, Slide, SlideQualityControl
from rois_manager.models import Slice

# delete all slices (cores and focus regions will be deleted as well)
Slice.objects.all().delete()

# delete all review steps
ReviewStep.objects.all().delete()

# delete all reviews
Review.objects.all().delete()

# delete all quality control data
SlideQualityControl.objects.all().delete()

# delete all slides
Slide.objects.all().delete()

# ... finally delete all cases
Case.objects.all().delete()
18 changes: 18 additions & 0 deletions promort/questionnaires_manager/management/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Copyright (c) 2020, CRS4
#
# Permission is hereby granted, free of charge, to any person obtaining a copy of
# this software and associated documentation files (the "Software"), to deal in
# the Software without restriction, including without limitation the rights to
# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
# the Software, and to permit persons to whom the Software is furnished to do so,
# subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
# FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
# COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
# IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
18 changes: 18 additions & 0 deletions promort/questionnaires_manager/management/commands/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Copyright (c) 2020, CRS4
#
# Permission is hereby granted, free of charge, to any person obtaining a copy of
# this software and associated documentation files (the "Software"), to deal in
# the Software without restriction, including without limitation the rights to
# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
# the Software, and to permit persons to whom the Software is furnished to do so,
# subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
# FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
# COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
# IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
# Copyright (c) 2020, CRS4
#
# Permission is hereby granted, free of charge, to any person obtaining a copy of
# this software and associated documentation files (the "Software"), to deal in
# the Software without restriction, including without limitation the rights to
# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
# the Software, and to permit persons to whom the Software is furnished to do so,
# subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
# FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
# COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
# IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

from django.core.management.base import BaseCommand, CommandError
from django.contrib.auth.models import User
from django.db import IntegrityError
from questionnaires_manager.models import QuestionnaireRequest, Questionnaire

from csv import DictReader
from uuid import uuid4
import logging

logger = logging.getLogger('promort_commands')


class Command(BaseCommand):
help = """
"""

def add_arguments(self, parser):
parser.add_argument('--questionnaire-requests', dest='questionnaire_requests', type=str,
required=True, help='The CSV containing the questionnaire requests')

def _load_requests_map(self, qreq_file):
try:
with open(qreq_file) as f:
reader = DictReader(f)
requests_map = dict()
for row in reader:
requests_map.setdefault(row['reviewer'], []).append(row)
return requests_map
except OSError:
raise CommandError('File %s does not exist' % qreq_file)

def _get_request_random_label(self):
return str(uuid4())

def _create_requests(self, reviewer, requests):
logger.info('-- Creating %d questionnare requests for user %s', len(requests), reviewer)
try:
reviewer_obj = User.objects.get(username=reviewer)
except User.DoesNotExist:
logger.error('There is no reviewer with username %s', reviewer)
return None
for req in requests:
if req['label'] in (None, ''):
logger.info('Missing label, assigning a random one')
req['label'] = self._get_request_random_label()
if req['questionnaire_a'] is None:
logger.error('Missing mandatody questionnaire_a, skipping row')
continue
try:
q_panel_a_obj = Questionnaire.objects.get(label=req['questionnaire_a'])
except Questionnaire.DoesNotExist:
logger.error('There is no Questionnaire object with label %s [Quest_A]', req['questionnaire_a'])
continue
req_details = {
'label': req['label'],
'questionnaire_panel_a': q_panel_a_obj,
'reviewer': reviewer_obj
}
if not req['questionnaire_b'] is None:
try:
req_details['questionnaire_panel_b'] = Questionnaire.objects.get(label=req['questionnaire_b'])
except Questionnaire.DoesNotExist:
logger.error('There is no Questionnaire object with label %s [Quest_B]', req['questionnaire_b'])
continue
try:
q_req = QuestionnaireRequest(**req_details)
q_req.save()
except IntegrityError:
logger.error('There is already a QuestionnaireRequest with label %s', req_details['label'])

def handle(self, *args, **opts):
logger.info('=== Starting import job ===')
requests_map = self._load_requests_map(opts['questionnaire_requests'])
for k, v in requests_map.iteritems():
self._create_requests(k, v)
logger.info('=== Import job completed ===')
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
# Copyright (c) 2020, CRS4
#
# Permission is hereby granted, free of charge, to any person obtaining a copy of
# this software and associated documentation files (the "Software"), to deal in
# the Software without restriction, including without limitation the rights to
# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
# the Software, and to permit persons to whom the Software is furnished to do so,
# subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
# FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
# COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
# IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

from django.core.management.base import BaseCommand, CommandError
from django.db import IntegrityError
from questionnaires_manager.models import Questionnaire, QuestionnaireStep, QuestionsSet
from slides_manager.models import SlidesSet

from csv import DictReader
from uuid import uuid4
import logging

logger = logging.getLogger('promort_commands')


class Command(BaseCommand):
help = """
"""

def add_arguments(self, parser):
parser.add_argument('--questionnaire-steps', dest='questionnaire_steps_file', type=str, required=True,
help='The CSV file containing the questionnare steps definitions')

def _load_questionnaires_map(self, qsteps_file):
try:
with open(qsteps_file) as f:
reader = DictReader(f)
questionnaire_map = dict()
for row in reader:
questionnaire_map.setdefault(row['questionnaire'], []).append(row)
return questionnaire_map
except OSError:
raise CommandError('File %s does not exist' % qsteps_file)

def _get_slides_set_random_label(self):
return str(uuid4())

def _import_questionnaire_steps(self, questionnaire_label, steps):
logger.info('-- Creating %d steps for Questionnaire %s', len(steps), questionnaire_label)
try:
questionnaire_obj = Questionnaire.objects.get(label=questionnaire_label)
# create random labels to be used for all the steps of the questionnaire if label is not specified in CSV
step_a_random_label = self._get_slides_set_random_label()
step_b_random_label = self._get_slides_set_random_label()
for i, step in enumerate(steps):
step_config = {
'questionnaire': questionnaire_obj,
'step_index': i
}
logger.debug('Creating step %d for questionnaire %s', i, questionnaire_label)
try:
step_config['questions'] = QuestionsSet.objects.get(label=step['questions_set'])
except QuestionsSet.DoesNotExist:
logger.error('There is no QuestionsSet object with label %s', step['questions_set'])
break
if not step['slides_set_a'] is None:
if step['slides_set_a_label'] in (None, ''):
logger.info('Using random label %s for slides set A', step_a_random_label)
step['slides_set_a_label'] = step_a_random_label
try:
step_config.update({
'slides_set_a': SlidesSet.objects.get(id=step['slides_set_a']),
'slides_set_a_label': step['slides_set_a_label']
})
except SlidesSet.DoesNotExist:
logger.error('There is no SlidesSet object with ID %s', step['slides_set_a'])
break
if not step['slides_set_b'] is None:
if step['slides_set_b_label'] in (None, ''):
logger.info('Using random label %s for slides set B', step_b_random_label)
step['slides_set_b_label'] = step_b_random_label
try:
step_config.update({
'slides_set_b': SlidesSet.objects.get(id=step['slides_set_b']),
'slides_set_b_label': step['slides_set_b_label']
})
except SlidesSet.DoesNotExist:
logger.error('There is no SlidesSet object with ID %s', step['slides_set_b'])
break
try:
qstep = QuestionnaireStep(**step_config)
qstep.save()
logger.info('Saved questionnaire step with index %d', i)
except IntegrityError:
logger.error('Questionnaire %s already has a step with index %d', questionnaire_label, i)
break
except Questionnaire.DoesNotExist:
logger.error('There is no Questionnaire object with label %s, skipping records',
questionnaire_label)

def handle(self, *args, **opts):
logger.info('=== Starting import job ===')
questionnaires_map = self._load_questionnaires_map(opts['questionnaire_steps_file'])
for k, v in questionnaires_map.iteritems():
self._import_questionnaire_steps(k, v)
logger.info('=== Import job completed ===')
Loading

0 comments on commit 85b9d18

Please sign in to comment.