Skip to content

Commit f8ee2fd

Browse files
authored
Merge pull request #431 from ImagingDataCommons/idc-test-sp
Sprint 15
2 parents b2948b3 + f923443 commit f8ee2fd

File tree

15 files changed

+968
-121
lines changed

15 files changed

+968
-121
lines changed

idc/settings.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -473,6 +473,7 @@ def GET_BQ_COHORT_SETTINGS():
473473
ACCOUNT_EMAIL_REQUIRED = True
474474
ACCOUNT_USERNAME_REQUIRED = bool(os.environ.get('ACCOUNT_USERNAME_REQUIRED', 'False') == 'True')
475475
ACCOUNT_EMAIL_VERIFICATION = os.environ.get('ACCOUNT_EMAIL_VERIFICATION', 'mandatory').lower()
476+
476477
ACCOUNT_EMAIL_SUBJECT_PREFIX = "[Imaging Data Commons] "
477478
ACCOUNTS_PASSWORD_EXPIRATION = os.environ.get('ACCOUNTS_PASSWORD_EXPIRATION',120) # Max password age in days
478479
ACCOUNTS_PASSWORD_HISTORY = os.environ.get('ACCOUNTS_PASSWORD_HISTORY', 5) # Max password history kept

idc/urls.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
from django.contrib import admin
2222
from django.conf import settings
2323

24-
from . import views
24+
from . import views, views_api
2525

2626
admin.autodiscover()
2727

@@ -32,6 +32,7 @@
3232
url(r'^test_methods/', views.test_methods, name='test_methods'),
3333
url(r'^style_guide/', views.css_test),
3434
url(r'^users/(?P<user_id>\d+)/$', views.user_detail, name='user_detail'),
35+
url(r'^users/api/', views_api.user_detail, name='user_detail_api'),
3536
url(r'^cohorts/', include('cohorts.urls')),
3637
path('admin/', admin.site.urls),
3738
url(r'^accounts/', include('accounts.urls')),

idc/views.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -235,9 +235,10 @@ def explore_data_page(request):
235235
with_derived = (req.get('with_derived', "True").lower() == "true")
236236
collapse_on = req.get('collapse_on', 'SeriesInstanceUID')
237237
is_json = (req.get('is_json', "False").lower() == "true")
238+
uniques = json.loads(req.get('uniques', '[]'))
238239

239240
context = build_explorer_context(is_dicofdic, source, versions, filters, fields, order_docs, counts_only,
240-
with_related, with_derived, collapse_on, is_json)
241+
with_related, with_derived, collapse_on, is_json, uniques=uniques)
241242

242243
except Exception as e:
243244
logger.error("[ERROR] While attempting to load the search page:")

idc/views_api.py

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
###
2+
# Copyright 2015-2020, Institute for Systems Biology
3+
#
4+
# Licensed under the Apache License, Version 2.0 (the "License");
5+
# you may not use this file except in compliance with the License.
6+
# You may obtain a copy of the License at
7+
#
8+
# http://www.apache.org/licenses/LICENSE-2.0
9+
#
10+
# Unless required by applicable law or agreed to in writing, software
11+
# distributed under the License is distributed on an "AS IS" BASIS,
12+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
# See the License for the specific language governing permissions and
14+
# limitations under the License.
15+
###
16+
17+
import json
18+
from json import JSONEncoder
19+
import logging
20+
import sys
21+
22+
23+
from django.conf import settings
24+
from django.contrib.auth.models import User
25+
from allauth.socialaccount.models import SocialAccount
26+
from django.http import HttpResponse, JsonResponse
27+
28+
debug = settings.DEBUG
29+
logger = logging.getLogger('main_logger')
30+
from datetime import datetime
31+
32+
from django.views.decorators.csrf import csrf_exempt
33+
from django.views.decorators.http import require_http_methods
34+
from cohorts.decorators import api_auth
35+
36+
37+
class DateTimeEncoder(JSONEncoder):
38+
# Override the default method
39+
def default(self, obj):
40+
if isinstance(obj, datetime):
41+
encoded_object = obj.strftime('%s')
42+
else:
43+
encoded_object = super(self, obj)
44+
return encoded_object
45+
# if isinstance(obj, (datetime.date, datetime.datetime)):
46+
# return obj.isoformat()
47+
48+
# class JsonResponse(HttpResponse):
49+
# def __init__(self, content, mimetype='application/json', status=None, content_type='application/json'):
50+
# json_text = json.dumps(content, cls=DateTimeEncoder)
51+
# super(JsonResponse, self).__init__(
52+
# content=json_text,
53+
# status=status,
54+
# content_type=content_type)
55+
56+
@csrf_exempt
57+
@api_auth
58+
@require_http_methods(["GET"])
59+
def user_detail(request):
60+
if debug: logger.debug('Called ' + sys._getframe().f_code.co_name)
61+
62+
try:
63+
user = User.objects.get(email=request.GET.get('email', ''))
64+
except Exception as e:
65+
logger.error("[ERROR] {} is not a registered IDC web app user".format(request.GET.get('email', '')))
66+
logger.exception(e)
67+
user_details = {
68+
"message": "Not a registered IDC web app user",
69+
"code": 404
70+
}
71+
return JsonResponse(user_details)
72+
73+
try:
74+
social_account = SocialAccount.objects.get(user=user, provider='google')
75+
except Exception as e:
76+
# This is a local account
77+
social_account = None
78+
user_details = {
79+
'date_joined': user.date_joined,
80+
'email': user.email,
81+
'id': user.id,
82+
'last_login': user.last_login
83+
}
84+
85+
if social_account:
86+
user_details['extra_data'] = social_account.extra_data if social_account else None
87+
user_details['first_name'] = user.first_name
88+
user_details['last_name'] = user.last_name
89+
else:
90+
user_details['username'] = user.username
91+
92+
results = {"user_details": user_details}
93+
94+
return JsonResponse(results, encoder=DateTimeEncoder)
95+
96+
97+

shell/index.sh

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
CORE=${1:-test_core}
2+
SKIP_DOWNLOAD=${2:-true}
3+
4+
echo ""
5+
echo "Beginning index run for core ${CORE}"
6+
echo "-------------------------------------------------------"
7+
echo ""
8+
9+
if [[ "$SKIP_DOWNLOAD" = false ]]; then
10+
echo "Deleting current CSV files."
11+
rm dicom_derived_all_*.csv
12+
13+
echo "Downloading new index data."
14+
gsutil cp gs://idc-dev-files/dicom_derived_all_*.csv ./
15+
else
16+
echo "Skipping download. Files are assumed to be present in the directory."
17+
fi
18+
19+
echo ""
20+
echo "Deleting and re-creating the core."
21+
sudo -u solr /opt/bitnami/apache-solr/bin/solr delete -c $CORE
22+
sudo -u solr /opt/bitnami/apache-solr/bin/solr create -c $CORE -s 2 -rf 2
23+
24+
echo ""
25+
echo "Creating the core schema."
26+
echo '{"add-field":['$(cat core_schema.json)']}' | curl -u idc:$SOLR_PASSWORD -X POST -H 'Content-type:application/json' --data-binary @- https://localhost:8983/solr/$CORE/schema --cacert solr-ssl.pem
27+
28+
echo ""
29+
echo "Indexing the following files:"
30+
ls -l dicom_derived_all_*.csv
31+
32+
echo ""
33+
for CSV in $(ls dicom_derived_all_*.csv)
34+
do
35+
echo "Indexing file ${CSV}..."
36+
curl -u idc:$SOLR_PASSWORD -X POST https://localhost:8983/solr/$CORE/update?commit=yes --data-binary @$CSV -H 'Content-type:application/csv' --cacert solr-ssl.pem
37+
echo "...done."
38+
done

shell/install-deps.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,5 +135,5 @@ if [ -n "${CI}" ]; then
135135
TIER=${DEPLOYMENT_TIER,,}
136136
fi
137137
SHA=$(git rev-list -1 HEAD)
138-
echo "APP_VERSION=${TIER}.$(date '+%Y%m%d%H%M').${SHA: -6}" > ${HOMEROOT}/version.env
138+
echo "APP_VERSION=${TIER}.$(date '+%Y%m%d%H%M').${SHA:0:7}" > ${HOMEROOT}/version.env
139139
fi

static/css/search.css

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -303,6 +303,12 @@ tr {
303303
word-wrap: break-word; }
304304

305305

306+
#cases_table td, #cases_table_head th {
307+
width: 25%;
308+
word-wrap: break-word; }
309+
310+
311+
306312
#studies_table_head th, #studies_table td {
307313
word-break: break-word; }
308314
#studies_table_head th.project-name, th.case-id, th.study-id,

static/css/style.css

Lines changed: 23 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

static/js/cohorts/cohort-details.js

Lines changed: 121 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -55,28 +55,137 @@ require([
5555
], function($, jqueryui, base, tippy, bootstrap) {
5656
A11y.Core();
5757

58+
tippy('.manifest-size-warning',{
59+
content: 'Your cohort is too large to be downloaded in its entirety, and will be truncated at 65,000 records ' +
60+
'ordered by PatientID, StudyID, SeriesID, and InstanceID.',
61+
theme: 'light',
62+
placement: 'left',
63+
arrow: false
64+
});
65+
5866
var downloadToken = new Date().getTime();
59-
$('#download-manifest').prop("href", $('#download-manifest').prop("href") + "?downloadToken="+downloadToken);
60-
$('#download-manifest').data('downloadToken',downloadToken);
6167

62-
$('#download-manifest').on('click', function() {
63-
var self=$(this);
68+
$('#download-csv').on('click', function(e) {
69+
download_manifest("csv", $(this), e)
70+
});
71+
72+
$('#download-tsv').on('click', function(e) {
73+
download_manifest("tsv", $(this), e)
74+
});
75+
76+
$('#download-json').on('click', function(e) {
77+
download_manifest("json", $(this), e)
78+
});
79+
80+
var download_manifest = function(file_type, clicked_button, e) {
81+
$('#unallowed-chars-alert').hide();
82+
$('#name-too-long-alert-modal').hide();
83+
84+
var name = $('#export-manifest-name').val();
85+
var unallowed = (name.match(base.blacklist) || []);
86+
87+
if (name.length == 0) {
88+
$('#download-csv').prop('title','Please input the name.');
89+
$('#export-manifest-name')[0].focus();
90+
e.preventDefault();
91+
return false;
92+
}
6493

65-
self.attr('disabled','disabled');
94+
if (clicked_button.is('[disabled=disabled]')) {
95+
e.preventDefault();
96+
return false;
97+
}
98+
99+
if (unallowed.length > 0) {
100+
$('.unallowed-chars').text(unallowed.join(", "));
101+
$('#unallowed-chars-alert').show();
102+
e.preventDefault();
103+
return false;
104+
}
105+
106+
if (name.length > 255) {
107+
$('#name-too-long-alert-modal').show();
108+
e.preventDefault();
109+
return false;
110+
}
111+
112+
$('#export-manifest-form').submit();
113+
114+
$('#download-csv').attr('disabled','disabled');
115+
$('#download-tsv').attr('disabled','disabled');
116+
$('#download-json').attr('disabled','disabled');
66117

67118
$('#download-in-progress').modal('show');
68119

69120
base.blockResubmit(function() {
70-
self.removeAttr('disabled');
121+
$('#download-csv').removeAttr('disabled');
122+
$('#download-tsv').removeAttr('disabled');
123+
$('#download-json').removeAttr('disabled');
71124
$('#download-in-progress').modal('hide');
72125
},downloadToken, 'downloadToken');
126+
127+
var checked_fields = [];
128+
$('.field-checkbox').each(function()
129+
{
130+
var cb = $(this)[0];
131+
if (cb.checked)
132+
{
133+
checked_fields.push(cb.value);
134+
}
135+
});
136+
137+
var checked_columns = [];
138+
$('.column-checkbox').each(function()
139+
{
140+
var cb = $(this)[0];
141+
if (cb.checked)
142+
{
143+
checked_columns.push(cb.value);
144+
}
145+
});
146+
147+
var url = BASE_URL + '/cohorts/download_manifest/' + cohort_id + '/';
148+
url += ("?file_type=" + file_type);
149+
url += ("&file_name=" + name);
150+
url += ("&header_fields=" + JSON.stringify(checked_fields));
151+
url += ("&columns=" + JSON.stringify(checked_columns));
152+
url += ("&downloadToken=" + downloadToken);
153+
154+
location.href = url;
155+
};
156+
157+
$('.column-checkbox').change(function() {
158+
update_download_manifest_buttons();
73159
});
74160

75-
tippy('.manifest-size-warning',{
76-
content: 'Your cohort is too large to be downloaded in its entirety, and will be truncated at 65,000 records ' +
77-
'ordered by PatientID, StudyID, SeriesID, and InstanceID.',
78-
theme: 'light',
79-
placement: 'left',
80-
arrow: false
161+
$("#export-manifest-name").change(function(){
162+
update_download_manifest_buttons();
81163
});
164+
165+
var update_download_manifest_buttons = function(){
166+
var num_selected_column =$('.column-checkbox:checked').length;
167+
var input_cohort_name_len = $('#export-manifest-name').val().length;
168+
169+
if (input_cohort_name_len == 0 || num_selected_column == 0) {
170+
$('#download-csv').attr('disabled', 'disabled');
171+
$('#download-tsv').attr('disabled', 'disabled');
172+
$('#download-json').attr('disabled', 'disabled');
173+
}
174+
else
175+
{
176+
$('#download-csv').removeAttr('disabled');
177+
$('#download-tsv').removeAttr('disabled');
178+
$('#download-json').removeAttr('disabled');
179+
}
180+
181+
if (num_selected_column == 0) {
182+
$('#no-column-alert-modal').show();
183+
}
184+
else
185+
{
186+
$('#no-column-alert-modal').hide();
187+
}
188+
};
189+
190+
update_download_manifest_buttons();
82191
});

0 commit comments

Comments
 (0)