Skip to content

Commit 50833ed

Browse files
authored
Merge pull request #54 from diging/develop
Develop
2 parents fa7f941 + cbf6fc1 commit 50833ed

File tree

12 files changed

+362
-35
lines changed

12 files changed

+362
-35
lines changed

celerybeat-schedule.db

16 KB
Binary file not shown.

cookies/giles.py

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ def wrapper(user, *args, **kwargs):
2727
user.giles_token.delete()
2828
except AssertionError:
2929
pass
30-
30+
3131
get_user_auth_token(user, **kwargs)
3232
user.refresh_from_db()
3333
# TODO: we could put some Exception handling here.
@@ -85,14 +85,13 @@ def get_user_auth_token(user, **kwargs):
8585
user.save()
8686
return user.giles_token.token
8787
except Exception as E:
88-
template = "Failed to retrieve access token for user {u}: {m}"
89-
msg = template.format(u=user.username, m=data.get('errorMsg', 'nope'))
88+
template = "Failed to retrieve access token for user {u}"
89+
msg = template.format(u=user.username)
9090
if kwargs.get('raise_exception', False):
9191
raise E
9292
logger.error(msg)
9393

9494

95-
9695
# @handle_status_exception
9796
@api_request
9897
def get_auth_token(user, **kwargs):
@@ -472,9 +471,6 @@ def process_resources(user, session, **kwargs):
472471
_process_document_data(session, file_data, user, **kwargs)
473472

474473

475-
476-
477-
478474
def handle_giles_callback(request, **kwargs):
479475
"""
480476
When a user has uploaded images to Giles, they may visit a callback URL that

cookies/metadata.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -236,8 +236,8 @@ def filter_relations(source=None, predicate=None, target=None,
236236
-------
237237
:class:`django.db.models.query.QuerySet`
238238
"""
239-
# if user and not user.is_superuser:
240-
# qs = authorization.apply_filter(user, 'view_relation', qs)
239+
if user and not user.is_superuser:
240+
qs = authorization.apply_filter(user, 'view_relation', qs)
241241

242242
for field, qfield, value in [('source', 'source_instance_id', source),
243243
('target', 'target_instance_id', target)]:
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
# -*- coding: utf-8 -*-
2+
# Generated by Django 1.9.6 on 2016-11-07 16:25
3+
from __future__ import unicode_literals
4+
5+
from django.db import migrations, models
6+
7+
8+
class Migration(migrations.Migration):
9+
10+
dependencies = [
11+
('cookies', '0026_auto_20161104_1416'),
12+
]
13+
14+
operations = [
15+
migrations.AlterField(
16+
model_name='collection',
17+
name='public',
18+
field=models.BooleanField(default=False, help_text=b'If a resource is not public it will only be accessible to logged-in users and will not appear in public search results. If this option is selected, you affirm that you have the right to upload and distribute this resource.'),
19+
),
20+
migrations.AlterField(
21+
model_name='conceptentity',
22+
name='public',
23+
field=models.BooleanField(default=False, help_text=b'If a resource is not public it will only be accessible to logged-in users and will not appear in public search results. If this option is selected, you affirm that you have the right to upload and distribute this resource.'),
24+
),
25+
migrations.AlterField(
26+
model_name='relation',
27+
name='public',
28+
field=models.BooleanField(default=False, help_text=b'If a resource is not public it will only be accessible to logged-in users and will not appear in public search results. If this option is selected, you affirm that you have the right to upload and distribute this resource.'),
29+
),
30+
migrations.AlterField(
31+
model_name='resource',
32+
name='public',
33+
field=models.BooleanField(default=False, help_text=b'If a resource is not public it will only be accessible to logged-in users and will not appear in public search results. If this option is selected, you affirm that you have the right to upload and distribute this resource.'),
34+
),
35+
]
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
# -*- coding: utf-8 -*-
2+
# Generated by Django 1.9.6 on 2016-11-07 16:36
3+
from __future__ import unicode_literals
4+
5+
from django.db import migrations, models
6+
import django.db.models.deletion
7+
8+
9+
class Migration(migrations.Migration):
10+
11+
dependencies = [
12+
('cookies', '0027_auto_20161107_1625'),
13+
]
14+
15+
operations = [
16+
migrations.RemoveField(
17+
model_name='gilesupload',
18+
name='created_by',
19+
),
20+
migrations.RemoveField(
21+
model_name='gilesupload',
22+
name='updated',
23+
),
24+
migrations.AddField(
25+
model_name='gilesupload',
26+
name='content_resource',
27+
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='giles_upload', to='cookies.Resource'),
28+
),
29+
migrations.AddField(
30+
model_name='gilesupload',
31+
name='resolved',
32+
field=models.BooleanField(default=False),
33+
),
34+
migrations.AddField(
35+
model_name='gilesupload',
36+
name='response',
37+
field=models.TextField(blank=True, null=True),
38+
),
39+
migrations.AddField(
40+
model_name='gilesupload',
41+
name='sent',
42+
field=models.DateTimeField(blank=True, null=True),
43+
),
44+
migrations.AddField(
45+
model_name='gilesupload',
46+
name='upload_id',
47+
field=models.CharField(blank=True, max_length=255, null=True),
48+
),
49+
]
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# -*- coding: utf-8 -*-
2+
# Generated by Django 1.9.6 on 2016-11-07 17:48
3+
from __future__ import unicode_literals
4+
5+
from django.db import migrations, models
6+
7+
8+
class Migration(migrations.Migration):
9+
10+
dependencies = [
11+
('cookies', '0028_auto_20161107_1636'),
12+
]
13+
14+
operations = [
15+
migrations.AddField(
16+
model_name='gilesupload',
17+
name='fail',
18+
field=models.BooleanField(default=False),
19+
),
20+
]

cookies/models.py

Lines changed: 30 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -429,6 +429,36 @@ def get_absolute_url(self):
429429
return reverse('jobs')
430430

431431

432+
class GilesUpload(models.Model):
433+
"""
434+
Represents a single upload.
435+
"""
436+
437+
created = models.DateTimeField(auto_now_add=True)
438+
sent = models.DateTimeField(null=True, blank=True)
439+
"""The datetime when the file was uploaded."""
440+
441+
upload_id = models.CharField(max_length=255, blank=True, null=True)
442+
"""Returned by Giles upon upload."""
443+
444+
content_resource = models.ForeignKey('Resource', null=True, blank=True,
445+
related_name='giles_upload')
446+
"""This is the resource that directly 'owns' the uploaded file."""
447+
448+
response = models.TextField(blank=True, null=True)
449+
"""This should be raw JSON."""
450+
451+
resolved = models.BooleanField(default=False)
452+
"""When a successful response is received, this should be set ``True``."""
453+
454+
fail = models.BooleanField(default=False)
455+
"""If ``True``, should not be retried."""
456+
457+
@property
458+
def pending(self):
459+
return not self.resolved
460+
461+
432462
class GilesSession(models.Model):
433463
created_by = models.ForeignKey(User, related_name='giles_sessions')
434464
created = models.DateTimeField(auto_now_add=True)
@@ -457,14 +487,6 @@ def _set_file_details(self, value):
457487
file_details = property(_get_file_details, _set_file_details)
458488

459489

460-
class GilesUpload(models.Model):
461-
"""
462-
Tracks files that have been uploaded via the REST API.
463-
"""
464-
created_by = models.ForeignKey(User, related_name='giles_uploads')
465-
created = models.DateTimeField(auto_now_add=True)
466-
updated = models.DateTimeField(auto_now=True)
467-
468490

469491
class GilesToken(models.Model):
470492
"""

cookies/signals.py

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -39,11 +39,14 @@ def new_users_are_inactive_by_default(sender, **kwargs):
3939
# instance.save()
4040

4141

42-
# TODO: enable this when Giles is ready for asynchronous uploads.
43-
# @receiver(post_save, sender=ContentRelation)
42+
@receiver(post_save, sender=ContentRelation)
4443
def send_pdfs_and_images_to_giles(sender, **kwargs):
44+
"""
45+
Create a :class:`.GilesUpload` instance to indicate that an upload should
46+
be performed.
47+
"""
4548
instance = kwargs.get('instance', None)
46-
print 'received post_Save for ContentRelation %i' % instance.id
49+
logger.debug('received post_save for ContentRelation %i' % instance.id)
4750

4851
import mimetypes
4952
try:
@@ -53,15 +56,8 @@ def send_pdfs_and_images_to_giles(sender, **kwargs):
5356
if instance.content_resource.is_local and instance.content_resource.file.name is not None:
5457
# PDFs and images should be stored in Digilib via Giles.
5558
if content_type in ['image/png', 'image/tiff', 'image/jpeg', 'application/pdf']:
56-
logger.debug('%s has a ContentResource; sending to Giles' % instance.content_resource.name)
57-
try:
58-
task = send_to_giles.delay(instance.content_resource.file.name,
59-
instance.for_resource.created_by, resource=instance.for_resource,
60-
public=instance.for_resource.public)
61-
except ConnectionError:
62-
logger.error("send_pdfs_and_images_to_giles: there was an error"
63-
" connecting to the redis message passing"
64-
" backend.")
59+
logger.debug('%s has a ContentResource; creating a GilesUpload' % instance.content_resource.name)
60+
GilesUpload.objects.create(content_resource=instance.content_resource)
6561

6662

6763

cookies/tasks.py

Lines changed: 67 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
from cookies.exceptions import *
1212
logger = settings.LOGGER
1313

14-
import jsonpickle
14+
import jsonpickle, json, datetime
1515

1616

1717
@shared_task
@@ -90,12 +90,24 @@ def handle_bulk(self, file_path, form_data, file_name, job=None,
9090

9191

9292
@shared_task
93-
def send_to_giles(file_name, creator, resource=None, public=True):
94-
result = giles.send_to_giles(creator, file_name, resource=resource,
95-
public=public)
93+
def send_to_giles(file_name, creator, resource=None, public=True, gilesupload_id=None):
94+
upload = GilesUpload.objects.get(pk=gilesupload_id)
95+
96+
# try:
97+
status_code, response_data = giles.send_to_giles(creator, file_name,
98+
resource=resource,
99+
public=public)
100+
# except:
101+
# logger.error("send_to_giles: failing permanently for %i" % upload.id)
102+
# upload.fail = True
103+
# upload.save()
104+
# return
105+
96106
session = GilesSession.objects.create(created_by_id=creator.id)
97107

98-
stat_sucode, response_data = result
108+
upload.upload_id = response_data['id']
109+
upload.sent = datetime.datetime.now()
110+
upload.save()
99111

100112
try:
101113
check_giles_upload.delay(resource, creator, response_data['id'],
@@ -105,14 +117,23 @@ def send_to_giles(file_name, creator, resource=None, public=True):
105117
" the redis message passing backend.")
106118

107119

120+
108121
@shared_task(max_retries=None, default_retry_delay=10)
109-
def check_giles_upload(resource, creator, upload_id, checkURL, session_id):
122+
def check_giles_upload(resource, creator, upload_id, checkURL, session_id,
123+
gilesupload_id):
110124
status, content = giles.check_upload_status(creator, checkURL)
111125
if status == 202: # Accepted.
112126
logger.debug('Accepted, retrying in 30 seconds')
113127
raise check_giles_upload.retry()
128+
114129
giles.process_file_upload(resource, creator, content, session_id)
115130

131+
upload = GilesUpload.objects.get(pk=gilesupload_id)
132+
upload.response = json.dumps(content)
133+
upload.resolved = True
134+
upload.save()
135+
136+
116137

117138
@shared_task
118139
def update_authorizations(auths, user, obj, by_user=None, propagate=True):
@@ -123,3 +144,43 @@ def update_authorizations(auths, user, obj, by_user=None, propagate=True):
123144
@shared_task
124145
def search_for_concept(lemma):
125146
authorities.searchall(lemma)
147+
148+
149+
@shared_task
150+
def send_giles_uploads():
151+
"""
152+
Check for outstanding :class:`.GilesUpload`\s, and send as able.
153+
"""
154+
logger.debug('Checking for outstanding GilesUploads')
155+
query = Q(resolved=False) & ~Q(sent=None) & Q(fail=False)
156+
outstanding = GilesUpload.objects.filter(query)
157+
pending = GilesUpload.objects.filter(resolved=False, sent=None, fail=False)
158+
159+
# We limit the number of simultaneous requests to Giles.
160+
if outstanding.count() >= settings.MAX_GILES_UPLOADS or pending.count() == 0:
161+
return
162+
163+
logger.debug('Found GilesUpload, processing...')
164+
165+
upload = pending.first()
166+
content_resource = upload.content_resource
167+
creator = content_resource.created_by
168+
resource = content_resource.parent.first().for_resource
169+
170+
anonymous, _ = User.objects.get_or_create(username='AnonymousUser')
171+
public = authorization.check_authorization('view', anonymous, content_resource)
172+
result = send_to_giles(content_resource.file.name, creator,
173+
resource=resource, public=public,
174+
gilesupload_id=upload.id)
175+
176+
177+
# session = GilesSession.objects.create(created_by_id=creator.id)
178+
#
179+
# stat_sucode, response_data = result
180+
#
181+
# try:
182+
# check_giles_upload.delay(resource, creator, response_data['id'],
183+
# response_data['checkUrl'], session.id)
184+
# except ConnectionError:
185+
# logger.error("send_to_giles: there was an error connecting to"
186+
# " the redis message passing backend.")

0 commit comments

Comments
 (0)