Skip to content

Commit 42d1ca3

Browse files
Merge pull request #848 from scieloorg/beta
Incorporação de códigos estáveis
2 parents 4069c36 + 80a10b9 commit 42d1ca3

16 files changed

+971
-17
lines changed

scielomanager/api/tests.py

+1
Original file line numberDiff line numberDiff line change
@@ -1169,6 +1169,7 @@ def test_api_v1_data_checkin(self):
11691169
u'article',
11701170
u'attempt_ref',
11711171
u'created_at',
1172+
u'expiration_at',
11721173
u'id',
11731174
u'package_name',
11741175
u'rejected_at',

scielomanager/articletrack/migrations/0010_auto__add_field_checkin_expiration_at.py

+283
Large diffs are not rendered by default.

scielomanager/articletrack/modelmanagers.py

+5-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@ def all(self, get_all_collections=user_request_context.get_current_user_collecti
1515

1616
def active(self, get_active_collection=user_request_context.get_current_user_active_collection):
1717
return self.filter(
18-
article__journals__collections=get_active_collection()).distinct()
18+
article__journals__collections=get_active_collection()
19+
).exclude(status='expired').distinct()
1920

2021
def accepted(self):
2122
return self.filter(status='accepted')
@@ -29,6 +30,9 @@ def rejected(self):
2930
def review(self):
3031
return self.filter(status='review')
3132

33+
def expired(self):
34+
return self.filter(status='expired')
35+
3236

3337
class CheckinManager(UserObjectManager):
3438

scielomanager/articletrack/models.py

+40-3
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
#coding: utf-8
1+
# coding: utf-8
22
import caching.base
33
import datetime
44
import logging
@@ -9,6 +9,7 @@
99
from django.utils.translation import ugettext_lazy as _
1010
from django.contrib.auth.models import User
1111
from django.core.exceptions import SuspiciousOperation, ValidationError
12+
from django.conf import settings
1213

1314
from articletrack import modelmanagers
1415
from journalmanager.models import Journal
@@ -20,6 +21,7 @@
2021
MSG_WORKFLOW_REVIEWED = 'Checkin Reviewed'
2122
MSG_WORKFLOW_SENT_TO_PENDING = 'Checkin Sent to Pending'
2223
MSG_WORKFLOW_SENT_TO_REVIEW = 'Checkin Sent to Review'
24+
MSG_WORKFLOW_EXPIRED = 'Checkin Expired'
2325

2426

2527
class Notice(caching.base.CachingMixin, models.Model):
@@ -45,6 +47,7 @@ def __unicode__(self):
4547
('review', _('Review')),
4648
('accepted', _('Accepted')),
4749
('rejected', _('Rejected')),
50+
('expired', _('Expired')),
4851
)
4952

5053

@@ -57,7 +60,7 @@ def decorated(*args, **kwargs):
5760
log.checkin = args[0]
5861
log.status = args[0].status
5962
log.created_at = datetime.datetime.now()
60-
log.user = args[1]
63+
log.user = args[1] if len(args) >= 2 else None
6164
log.description = message + (" - Reason: %s" % args[0].rejected_cause if getattr(args[0], 'rejected_cause') else '')
6265
log.save()
6366

@@ -91,6 +94,8 @@ class Checkin(caching.base.CachingMixin, models.Model):
9194

9295
submitted_by = models.ForeignKey(User, related_name='checkins_submitted_by', null=True, blank=True)
9396

97+
expiration_at = models.DateTimeField(_(u'Expiration Date'), null=True, blank=True)
98+
9499
class Meta:
95100
ordering = ['-created_at']
96101
permissions = (("list_checkin", "Can list Checkin"),)
@@ -118,6 +123,14 @@ def is_newest_checkin(self):
118123
"""
119124
return self.pk == self.get_newest_checkin.pk
120125

126+
@property
127+
def is_expirable(self):
128+
"""
129+
Return True if the expiration_at's date is equal to datetime.date.today()
130+
Compared with <= to catch possibly unprocessed checkins
131+
"""
132+
return self.status == 'pending' and self.expiration_at.date() <= datetime.date.today()
133+
121134
def is_accepted(self):
122135
"""
123136
Checks if this checkin has been accepted
@@ -270,7 +283,6 @@ def do_review(self, responsible):
270283
else:
271284
raise ValueError('This checkin does not comply with the conditions to be reviewed')
272285

273-
274286
@log_workflow_status(MSG_WORKFLOW_REJECTED)
275287
def do_reject(self, responsible, reason):
276288
"""
@@ -295,6 +307,18 @@ def do_reject(self, responsible, reason):
295307
else:
296308
raise ValueError('This checkin does not comply with the conditions to change status to "rejected"')
297309

310+
@log_workflow_status(MSG_WORKFLOW_EXPIRED)
311+
def do_expires(self, responsible=None):
312+
"""
313+
Change self.status to 'expired'.
314+
Change self.expiration_at to now()
315+
This action generates a ``CheckinWorkflowLog`` entry.
316+
"""
317+
if self.status != 'expired':
318+
self.status = 'expired'
319+
self.expiration_at = datetime.datetime.now()
320+
self.save()
321+
298322
def clean(self):
299323
# validation for status "accepted"
300324
if self.status == 'accepted' and not bool(self.accepted_by and self.accepted_at and self.reviewed_by and self.reviewed_at):
@@ -307,6 +331,12 @@ def clean(self):
307331

308332
def save(self, *args, **kwargs):
309333
if self.status == 'pending':
334+
if self.expiration_at is None:
335+
# update expiration info
336+
now = datetime.datetime.now()
337+
time_span = settings.CHECKIN_EXPIRATION_TIME_SPAN
338+
delta_next = datetime.timedelta(days=time_span)
339+
self.expiration_at = now + delta_next
310340
# clear 'review' fields
311341
self.reviewed_by = None
312342
self.reviewed_at = None
@@ -318,6 +348,8 @@ def save(self, *args, **kwargs):
318348
self.rejected_at = None
319349
self.rejected_cause = None
320350
elif self.status == 'review':
351+
# update expiration info
352+
self.expiration_at = None
321353
# clear 'accepted' fields
322354
self.accepted_by = None
323355
self.accepted_at = None
@@ -326,12 +358,17 @@ def save(self, *args, **kwargs):
326358
self.rejected_at = None
327359
self.rejected_cause = None
328360
elif self.status == 'rejected':
361+
# update expiration info
362+
self.expiration_at = None
329363
# clear 'review' fields
330364
self.reviewed_by = None
331365
self.reviewed_at = None
332366
# clear 'accepted' fields
333367
self.accepted_by = None
334368
self.accepted_at = None
369+
else:
370+
# update expiration info
371+
self.expiration_at = None
335372
super(Checkin, self).save(*args, **kwargs)
336373

337374
def __unicode__(self):

scielomanager/articletrack/tasks.py

+53
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
# coding: utf-8
2+
import datetime
3+
import logging
4+
from xmlrpclib import Fault, ProtocolError
5+
6+
from .balaio import BalaioRPC
7+
from .models import Checkin
8+
from scielomanager.celery import app
9+
10+
logger = logging.getLogger(__name__)
11+
12+
13+
@app.task(bind=True)
14+
def do_expires_checkin(self, checkin):
15+
"""
16+
For this checkin:
17+
- call do_expires() that change status and create workflow logs.
18+
- call BalaioRPC.expire_attempt API method to trigger action on Balaio's side.
19+
if API is down or RPC call raise exception -> task will be retried
20+
"""
21+
rpc_client = BalaioRPC()
22+
logger.debug('[do_expires_checkin] START process to expire checkin (pk=%s) at %s' % (checkin.pk, datetime.datetime.now()))
23+
checkin.do_expires()
24+
logger.info('[do_expires_checkin] EXPIRED checkin pk: %s' % checkin.pk)
25+
if rpc_client.is_up():
26+
try:
27+
rpc_response = rpc_client.call('expire_attempt', [checkin.attempt_ref, ])
28+
except (Fault, ProtocolError, ValuerError) as e:
29+
logger.debug('[do_expires_checkin] FAIL, retrying: exception' % e)
30+
raise self.retry(exc=e)
31+
logger.info('[do_expires_checkin] BALAIO RPC method call success!.')
32+
else:
33+
raise self.retry(exc=Exception("Balaio is down!"))
34+
logger.error('[do_expires_checkin] BALAIO RPC is Down. Could not call "expire_attempt" method.')
35+
logger.debug('[do_expires_checkin] FINISH process at %s' % datetime.datetime.now())
36+
37+
38+
@app.task(bind=True)
39+
def process_expirable_checkins(self):
40+
"""
41+
RUN: daily!
42+
For each checkin that is "expirable":
43+
call the task: do_expires_checkin to change the checkins as expired
44+
"""
45+
logger.debug('[process_expirable_checkins] START process at %s' % datetime.datetime.now())
46+
checkins = Checkin.objects.filter(status='pending', expiration_at__isnull=False)
47+
logger.info('[process_expirable_checkins] Found %s checkins matching conditions' % checkins.count())
48+
for checkin in checkins:
49+
logger.info('[process_expirable_checkins] Processing checkin (pk=%s)' % checkin.pk)
50+
if checkin.is_expirable:
51+
do_expires_checkin.apply_async(args=[checkin, ])
52+
53+
logger.debug('[process_expirable_checkins] FINISH process at %s' % datetime.datetime.now())

scielomanager/articletrack/templates/articletrack/includes/article_and_checkin_information.html

+9
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,15 @@ <h4>{% trans "Check-in Information" %}:</h4>
8282

8383
{% endif %}
8484

85+
{% if checkin.status == "pending" and checkin.expiration_at %}
86+
<dt>{% trans "Will Expire at" %}:</dt>
87+
<dd>
88+
<span class="label label-important">
89+
{{ checkin.expiration_at|date:"d/m/Y - H:i" }}
90+
</span>
91+
</dd>
92+
{% endif %}
93+
8594
</dl>
8695
</div>
8796
</div>

scielomanager/articletrack/templates/articletrack/includes/checkin_status_notice_block.html

+28
Original file line numberDiff line numberDiff line change
@@ -33,4 +33,32 @@ <h4><i class="icon-ok-circle"></i> {% trans "ACCEPTED" %}:</h4>
3333
</div>
3434
</div>
3535

36+
{% elif checkin.is_expirable %}
37+
38+
<div class="row-fluid show-grid">
39+
<div class="span12">
40+
<div class="alert alert-error alert-block">
41+
<h4><i class="icon-ok-circle"></i> {% trans "THIS CHECKIN WILL EXPIRE TODAY" %}:</h4>
42+
<p>
43+
{% trans "This checkin will expire at: " %}
44+
{% trans "at" %} {{ checkin.expiration_at|date:"d/m/Y - H:i" }}
45+
</p>
46+
<p>
47+
{% trans "Before that date, the checkin will be unreachable." %}
48+
</p>
49+
</div>
50+
</div>
51+
</div>
52+
53+
{% elif checkin.status == "expired" %}
54+
55+
<div class="row-fluid show-grid">
56+
<div class="span12">
57+
<div class="alert alert-success alert-block">
58+
<h4><i class="icon-ok-circle"></i>{% trans "THIS CHECKIN IS EXPIRED!" %}</h4>
59+
</div>
60+
</div>
61+
</div>
62+
63+
3664
{% endif %}

scielomanager/articletrack/tests/doubles.py

+10-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# -*- coding: utf-8 -*-
22
from os import path
3-
from articletrack.balaio import BalaioAPI
3+
from articletrack.balaio import BalaioAPI, BalaioRPC
44
from django.conf import settings
55
from urllib2 import urlopen
66

@@ -47,3 +47,12 @@ def is_up(self):
4747

4848
def list_files_members_by_attempt(self, attempt_id):
4949
raise ValueError
50+
51+
52+
class BalaioRPCDouble(BalaioRPC):
53+
54+
def is_up(self):
55+
return True
56+
57+
def call(self, method, args=()):
58+
return None

scielomanager/articletrack/tests/tests_models.py

+32-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
import datetime
33
from django.test import TestCase
44
from django_factory_boy import auth
5-
5+
from django.conf import settings
66
from articletrack import models
77
from . import modelfactories
88

@@ -185,6 +185,24 @@ def test_is_newest_checkin(self):
185185
self.assertTrue(checkin2.is_newest_checkin)
186186
self.assertFalse(checkin1.is_newest_checkin)
187187

188+
def test_new_checkin_has_correct_expiration_date_and_is_not_expirable(self):
189+
checkin = modelfactories.CheckinFactory()
190+
191+
now = datetime.datetime.now()
192+
days_delta = datetime.timedelta(days=settings.CHECKIN_EXPIRATION_TIME_SPAN)
193+
next_week_date = now + days_delta
194+
195+
self.assertEqual(checkin.expiration_at.date(), next_week_date.date())
196+
self.assertFalse(checkin.is_expirable)
197+
198+
def test_if_expiration_date_is_today_then_checkin_is_expirable(self):
199+
now = datetime.datetime.now()
200+
201+
checkin = modelfactories.CheckinFactory(expiration_at=now)
202+
203+
self.assertEqual(checkin.expiration_at.date(), now.date())
204+
self.assertTrue(checkin.is_expirable)
205+
188206

189207
class ArticleTests(TestCase):
190208

@@ -329,3 +347,16 @@ def test_checkin_send_to_pending_log(self):
329347
self.assertEqual(logs.count(), 1)
330348
self.assertEqual(logs[0].user, user2_send_to_review)
331349
self.assertEqual(logs[0].description, models.MSG_WORKFLOW_SENT_TO_PENDING)
350+
351+
def test_do_expires_generate_log_entry(self):
352+
checkin = modelfactories.CheckinFactory()
353+
354+
# do_expires
355+
checkin.do_expires()
356+
357+
logs = models.CheckinWorkflowLog.objects.filter(checkin=checkin, status=checkin.status)
358+
359+
self.assertEqual(logs.count(), 1)
360+
self.assertIsNone(logs[0].user)
361+
self.assertEqual(logs[0].status, 'expired')
362+
self.assertEqual(logs[0].description, models.MSG_WORKFLOW_EXPIRED)

0 commit comments

Comments
 (0)