Skip to content

Commit 3967603

Browse files
authored
Merge pull request #80 from vitorfs/dev
Release v2.0.4
2 parents 6d98e63 + 69b5f85 commit 3967603

File tree

7 files changed

+91
-53
lines changed

7 files changed

+91
-53
lines changed

CHANGELOG.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,13 @@
1+
Parsifal 2.0.4 (2021-09-10)
2+
===========================
3+
4+
Bugfixes
5+
--------
6+
7+
- Improve quality assessment table performance. (#76)
8+
- Improve data extraction table performance. (#78)
9+
10+
111
Parsifal 2.0.3 (2021-09-08)
212
===========================
313

bin/gunicorn_start

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ DIR=/home/parsifal/parsifal
55
USER=parsifal
66
GROUP=parsifal
77
WORKERS=3
8+
TIMEOUT=120
89
BIND=unix:/home/parsifal/run/gunicorn.sock
910
DJANGO_SETTINGS_MODULE=parsifal.settings.production
1011
DJANGO_WSGI_MODULE=parsifal.wsgi
@@ -18,6 +19,7 @@ export PYTHONPATH=$DIR:$PYTHONPATH
1819
exec /home/parsifal/venv/bin/gunicorn ${DJANGO_WSGI_MODULE}:application \
1920
--name $NAME \
2021
--workers $WORKERS \
22+
--timeout $TIMEOUT \
2123
--user=$USER \
2224
--group=$GROUP \
2325
--bind=$BIND \

parsifal/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
from parsifal.utils.version import get_version
22

3-
VERSION = (2, 0, 3, "final", 0)
3+
VERSION = (2, 0, 4, "final", 0)
44

55
__version__ = get_version(VERSION)

parsifal/apps/reviews/conducting/views.py

Lines changed: 42 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,13 @@
55
from django.conf import settings as django_settings
66
from django.contrib import messages
77
from django.contrib.auth.decorators import login_required
8-
from django.db.models import Count
8+
from django.db.models import Count, Sum, Value
9+
from django.db.models.functions import Coalesce
910
from django.http import Http404, HttpResponse, HttpResponseBadRequest
1011
from django.shortcuts import get_object_or_404, redirect, render
1112
from django.template.context_processors import csrf
1213
from django.urls import reverse as r
13-
from django.utils.html import escape
14+
from django.utils.html import escape, format_html
1415
from django.utils.translation import gettext as _
1516
from django.views.decorators.http import require_POST
1617

@@ -227,7 +228,12 @@ def study_selection(request, username, review_name):
227228

228229

229230
def build_quality_assessment_table(request, review, order):
230-
selected_studies = review.get_accepted_articles().order_by(order)
231+
selected_studies = (
232+
review.get_accepted_articles()
233+
.prefetch_related("qualityassessment_set")
234+
.annotate(score=Coalesce(Sum("qualityassessment__answer__weight"), Value(0.0)))
235+
.order_by(order)
236+
)
231237
quality_questions = review.get_quality_assessment_questions()
232238
quality_answers = review.get_quality_assessment_answers()
233239

@@ -242,43 +248,35 @@ def build_quality_assessment_table(request, review, order):
242248
243249
<table class="table" id="tbl-quality" article-id="{2}" csrf-token="{3}">
244250
<tbody>""".format(
245-
escape(study.title), study.get_score(), study.id, str(csrf(request)["csrf_token"]), escape(study.year)
251+
escape(study.title), study.score, study.id, str(csrf(request)["csrf_token"]), escape(study.year)
246252
)
247253

248-
quality_assessment = study.get_quality_assesment()
249-
250254
for question in quality_questions:
251-
str_table += (
252-
'''<tr question-id="'''
253-
+ str(question.id)
254-
+ """">
255-
<td>"""
256-
+ escape(question.description)
257-
+ """</td>"""
255+
str_table += format_html(
256+
'<tr question-id="{question_id}"><td>{question_description}</td>',
257+
question_id=question.pk,
258+
question_description=question.description,
258259
)
259260

260-
try:
261-
question_answer = quality_assessment.filter(question__id=question.id).get()
262-
except Exception:
263-
question_answer = None
261+
question_answer_id = None
262+
for qa in study.qualityassessment_set.all():
263+
if qa.question_id == question.pk:
264+
question_answer_id = qa.answer_id
265+
break
264266

265267
for answer in quality_answers:
266268
selected_answer = ""
267-
if question_answer is not None:
268-
if answer.id == question_answer.answer.id:
269-
selected_answer = " selected-answer"
270-
str_table += (
271-
"""<td class="answer"""
272-
+ selected_answer
273-
+ '''" answer-id="'''
274-
+ str(answer.id)
275-
+ """">"""
276-
+ escape(answer.description)
277-
+ """</td>"""
269+
if answer.id == question_answer_id:
270+
selected_answer = " selected-answer"
271+
str_table += format_html(
272+
'<td class="answer {selected}" answer-id="{answer_id}">{answer_description}</td>',
273+
selected=selected_answer,
274+
answer_id=answer.pk,
275+
answer_description=answer.description,
278276
)
279-
str_table += """</tr>"""
277+
str_table += "</tr>"
280278

281-
str_table += """</tbody></table></div>"""
279+
str_table += "</tbody></table></div>"
282280
return str_table
283281
else:
284282
return ""
@@ -367,10 +365,11 @@ def quality_assessment(request, username, review_name):
367365
def build_data_extraction_field_row(article, field):
368366
str_field = ""
369367

370-
try:
371-
extraction = DataExtraction.objects.get(article=article, field=field)
372-
except Exception:
373-
extraction = None
368+
extraction = None
369+
for data_extraction in article.dataextraction_set.all():
370+
if data_extraction.field_id == field.pk:
371+
extraction = data_extraction
372+
break
374373

375374
if field.field_type == DataExtractionField.BOOLEAN_FIELD:
376375
true = ""
@@ -403,7 +402,7 @@ def build_data_extraction_field_row(article, field):
403402
<option value="">Select...</option>""".format(
404403
article.id, field.id
405404
)
406-
for value in field.get_select_values():
405+
for value in field.dataextractionlookup_set.all():
407406
if extraction is not None and extraction.get_value() is not None and extraction.get_value().id == value.id:
408407
selected = " selected"
409408
else:
@@ -412,7 +411,7 @@ def build_data_extraction_field_row(article, field):
412411
str_field += "</select>"
413412

414413
elif field.field_type == DataExtractionField.SELECT_MANY_FIELD:
415-
for value in field.get_select_values():
414+
for value in field.dataextractionlookup_set.all():
416415
if extraction is not None and value in extraction.get_value():
417416
checked = " checked"
418417
else:
@@ -442,10 +441,13 @@ def build_data_extraction_field_row(article, field):
442441

443442

444443
def build_data_extraction_table(review, is_finished):
445-
selected_studies = review.get_final_selection_articles()
444+
selected_studies = review.get_final_selection_articles().prefetch_related(
445+
"dataextraction_set__field",
446+
"dataextraction_set__select_values",
447+
)
446448
if is_finished is not None:
447449
selected_studies = selected_studies.filter(finished_data_extraction=is_finished)
448-
data_extraction_fields = review.get_data_extraction_fields()
450+
data_extraction_fields = review.get_data_extraction_fields().prefetch_related("dataextractionlookup_set")
449451
has_quality_assessment = review.has_quality_assessment_checklist()
450452
if selected_studies and data_extraction_fields:
451453
str_table = '<div class="panel-group">'
@@ -455,7 +457,7 @@ def build_data_extraction_table(review, is_finished):
455457
<div class="panel-heading">
456458
<h3 class="panel-title">{0}
457459
<span class="badge">{1}</span>""".format(
458-
escape(study.title), study.get_score()
460+
escape(study.title), study.score
459461
)
460462

461463
if study.finished_data_extraction:
@@ -552,6 +554,7 @@ def data_extraction(request, username, review_name):
552554
try:
553555
data_extraction_table = build_data_extraction_table(review, is_finished)
554556
except Exception:
557+
logger.exception("An error occurred while trying to build data extraction table.")
555558
data_extraction_table = "<h3>Something went wrong while rendering the data extraction form.</h3>"
556559

557560
return render(
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# Generated by Django 3.2.7 on 2021-09-10 03:11
2+
3+
from django.db import migrations, models
4+
5+
6+
class Migration(migrations.Migration):
7+
8+
dependencies = [
9+
('reviews', '0036_auto_20210906_2320'),
10+
]
11+
12+
operations = [
13+
migrations.AlterField(
14+
model_name='article',
15+
name='title',
16+
field=models.CharField(blank=True, db_index=True, max_length=1000, null=True),
17+
),
18+
migrations.AlterField(
19+
model_name='article',
20+
name='year',
21+
field=models.CharField(blank=True, db_index=True, max_length=10, null=True),
22+
),
23+
]

parsifal/apps/reviews/models.py

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@
22

33
from django.contrib.auth.models import User
44
from django.db import models
5-
from django.db.models import Sum
5+
from django.db.models import Sum, Value
6+
from django.db.models.functions import Coalesce
67
from django.urls import reverse
78
from django.utils.html import escape
89
from django.utils.text import slugify
@@ -127,15 +128,12 @@ def get_accepted_articles(self):
127128
return Article.objects.filter(review__id=self.id, status=Article.ACCEPTED)
128129

129130
def get_final_selection_articles(self):
130-
accepted_articles = Article.objects.filter(review__id=self.id, status=Article.ACCEPTED)
131+
accepted_articles = Article.objects.filter(review__id=self.id, status=Article.ACCEPTED).annotate(
132+
score=Coalesce(Sum("qualityassessment__answer__weight"), Value(0.0))
133+
)
131134
if self.has_quality_assessment_checklist() and self.quality_assessment_cutoff_score > 0.0:
132-
articles = accepted_articles
133-
for article in accepted_articles:
134-
if article.get_score() <= self.quality_assessment_cutoff_score:
135-
articles = articles.exclude(id=article.id)
136-
return articles
137-
else:
138-
return accepted_articles
135+
accepted_articles = accepted_articles.filter(score__gt=self.quality_assessment_cutoff_score)
136+
return accepted_articles
139137

140138
def has_quality_assessment_checklist(self):
141139
has_questions = self.qualityquestion_set.exists()
@@ -279,10 +277,10 @@ class Article(models.Model):
279277

280278
review = models.ForeignKey(Review, on_delete=models.CASCADE)
281279
bibtex_key = models.CharField(max_length=100)
282-
title = models.CharField(max_length=1000, null=True, blank=True)
280+
title = models.CharField(max_length=1000, null=True, blank=True, db_index=True)
283281
author = models.CharField(max_length=1000, null=True, blank=True)
284282
journal = models.CharField(max_length=1000, null=True, blank=True)
285-
year = models.CharField(max_length=10, null=True, blank=True)
283+
year = models.CharField(max_length=10, null=True, blank=True, db_index=True)
286284
source = models.ForeignKey(Source, on_delete=models.CASCADE, null=True)
287285
pages = models.CharField(max_length=20, null=True, blank=True)
288286
volume = models.CharField(max_length=100, null=True, blank=True)

parsifal/templates/includes/footer.html

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,10 @@
44
<div class="col-xs-4 legal">
55
<span>© 2021 Simple Complex</span>
66
</div>
7-
<div class="col-xs-4" style="text-align: center;padding-top: 13px;">
8-
<small class="text-muted">v{{ parsifal_version }}</small>
7+
<div class="col-xs-4" style="text-align: center;padding-top: 14px;">
8+
<small class="text-muted">
9+
<a href="https://github.com/vitorfs/parsifal/releases/latest" rel="noopener">v{{ parsifal_version }}</a>
10+
</small>
911
</div>
1012
<div class="col-xs-4 social">
1113
<a href="https://github.com/vitorfs/parsifal" target="_blank" data-toggle="tooltip" title="Github" data-placement="top"><span class="fa fa-github"></span></a>

0 commit comments

Comments
 (0)