-
Notifications
You must be signed in to change notification settings - Fork 40
Web API: file upload & proposal_pipeline support, tests, and some cleanup #301
Changes from 6 commits
33dac42
a3d8937
8950026
6e980be
d898673
cd39a80
6636491
4952595
944df41
9c71e1e
20d5f97
a2593a4
8d1d836
9d4ddf4
03836ec
10c2fbc
58f8246
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
# -*- coding: utf-8 -*- | ||
# Generated by Django 1.9.7 on 2016-08-10 00:27 | ||
from __future__ import unicode_literals | ||
|
||
from django.db import migrations, models | ||
|
||
|
||
class Migration(migrations.Migration): | ||
|
||
dependencies = [ | ||
('jobs', '0002_auto_20160728_2331'), | ||
] | ||
|
||
operations = [ | ||
migrations.CreateModel( | ||
name='PipelineJob', | ||
fields=[ | ||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), | ||
('created', models.DateTimeField(auto_now_add=True)), | ||
('clear_cache', models.BooleanField(default=False)), | ||
('destination', models.URLField(default=b'http://localhost:8888/api', max_length=2000)), | ||
('notification_email', models.EmailField(blank=b'True', max_length=254)), | ||
('job_id', models.UUIDField(default=None, null=True)), | ||
('use_uploaded_metadata', models.UUIDField(default=None, null=True)), | ||
('use_uploaded_regulation', models.UUIDField(default=None, null=True)), | ||
('parser_errors', models.TextField(blank=True)), | ||
('regulation_url', models.URLField(blank=True, max_length=2000)), | ||
('status', models.CharField(choices=[(b'received', b'received'), (b'in_progress', b'in_progress'), (b'failed', b'failed'), (b'complete', b'complete'), (b'complete_with_errors', b'complete_with_errors')], default=b'received', max_length=32)), | ||
('url', models.URLField(blank=True, max_length=2000)), | ||
('cfr_title', models.IntegerField()), | ||
('cfr_part', models.IntegerField()), | ||
('only_latest', models.BooleanField(default=False)), | ||
], | ||
options={ | ||
'abstract': False, | ||
}, | ||
), | ||
migrations.DeleteModel( | ||
name='ParsingJob', | ||
), | ||
] |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
# -*- coding: utf-8 -*- | ||
# Generated by Django 1.9.7 on 2016-08-11 00:19 | ||
from __future__ import unicode_literals | ||
|
||
from django.db import migrations, models | ||
|
||
|
||
class Migration(migrations.Migration): | ||
|
||
dependencies = [ | ||
('jobs', '0003_auto_20160810_0027'), | ||
] | ||
|
||
operations = [ | ||
migrations.CreateModel( | ||
name='UploadedFile', | ||
fields=[ | ||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), | ||
('file', models.FileField(upload_to=b'')), | ||
], | ||
), | ||
] |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
# -*- coding: utf-8 -*- | ||
# Generated by Django 1.9.7 on 2016-08-11 21:32 | ||
from __future__ import unicode_literals | ||
|
||
from django.db import migrations, models | ||
|
||
|
||
class Migration(migrations.Migration): | ||
|
||
dependencies = [ | ||
('jobs', '0004_uploadedfile'), | ||
] | ||
|
||
operations = [ | ||
migrations.CreateModel( | ||
name='RegulationFile', | ||
fields=[ | ||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), | ||
('hexhash', models.CharField(default=None, max_length=32, null=True)), | ||
('filename', models.CharField(default=None, max_length=512, null=True)), | ||
('contents', models.BinaryField()), | ||
], | ||
), | ||
migrations.DeleteModel( | ||
name='UploadedFile', | ||
), | ||
] |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
# -*- coding: utf-8 -*- | ||
# Generated by Django 1.9.7 on 2016-08-11 22:30 | ||
from __future__ import unicode_literals | ||
|
||
from django.db import migrations, models | ||
|
||
|
||
class Migration(migrations.Migration): | ||
|
||
dependencies = [ | ||
('jobs', '0005_auto_20160811_2132'), | ||
] | ||
|
||
operations = [ | ||
migrations.AddField( | ||
model_name='regulationfile', | ||
name='file', | ||
field=models.FileField(null=True, upload_to=b''), | ||
), | ||
] |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
# -*- coding: utf-8 -*- | ||
# Generated by Django 1.9.7 on 2016-08-19 04:28 | ||
from __future__ import unicode_literals | ||
|
||
from django.db import migrations, models | ||
|
||
|
||
class Migration(migrations.Migration): | ||
|
||
dependencies = [ | ||
('jobs', '0006_regulationfile_file'), | ||
] | ||
|
||
operations = [ | ||
migrations.AddField( | ||
model_name='regulationfile', | ||
name='url', | ||
field=models.URLField(blank=True, max_length=2000), | ||
), | ||
] |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
# -*- coding: utf-8 -*- | ||
# Generated by Django 1.9.7 on 2016-08-19 04:42 | ||
from __future__ import unicode_literals | ||
|
||
from django.db import migrations, models | ||
|
||
|
||
class Migration(migrations.Migration): | ||
|
||
dependencies = [ | ||
('jobs', '0007_regulationfile_url'), | ||
] | ||
|
||
operations = [ | ||
migrations.CreateModel( | ||
name='ProposalPipelineJob', | ||
fields=[ | ||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), | ||
('created', models.DateTimeField(auto_now_add=True)), | ||
('clear_cache', models.BooleanField(default=False)), | ||
('destination', models.URLField(default=b'http://localhost:8888/api', max_length=2000)), | ||
('notification_email', models.EmailField(blank=b'True', max_length=254)), | ||
('job_id', models.UUIDField(default=None, null=True)), | ||
('use_uploaded_metadata', models.UUIDField(default=None, null=True)), | ||
('use_uploaded_regulation', models.UUIDField(default=None, null=True)), | ||
('parser_errors', models.TextField(blank=True)), | ||
('regulation_url', models.URLField(blank=True, max_length=2000)), | ||
('status', models.CharField(choices=[(b'received', b'received'), (b'in_progress', b'in_progress'), (b'failed', b'failed'), (b'complete', b'complete'), (b'complete_with_errors', b'complete_with_errors')], default=b'received', max_length=32)), | ||
('url', models.URLField(blank=True, max_length=2000)), | ||
('file_hexhash', models.CharField(max_length=32)), | ||
('only_latest', models.BooleanField(default=True)), | ||
], | ||
options={ | ||
'abstract': False, | ||
}, | ||
), | ||
] |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
# -*- coding: utf-8 -*- | ||
# Generated by Django 1.9.7 on 2016-08-24 23:47 | ||
from __future__ import unicode_literals | ||
|
||
from django.db import migrations, models | ||
|
||
|
||
class Migration(migrations.Migration): | ||
|
||
dependencies = [ | ||
('jobs', '0008_proposalpipelinejob'), | ||
] | ||
|
||
operations = [ | ||
migrations.AlterField( | ||
model_name='pipelinejob', | ||
name='destination', | ||
field=models.URLField(max_length=2000), | ||
), | ||
migrations.AlterField( | ||
model_name='proposalpipelinejob', | ||
name='destination', | ||
field=models.URLField(max_length=2000), | ||
), | ||
] |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,16 +1,24 @@ | ||
from django.db import models | ||
|
||
job_status_pairs = ( | ||
("complete", "complete"), | ||
("complete_with_errors", "complete_with_errors"), | ||
("failed", "failed"), | ||
("in_progress", "in_progress"), | ||
("received", "received") | ||
) | ||
job_status_values = [j[0] for j in job_status_pairs] | ||
|
||
|
||
class ParsingJob(models.Model): | ||
|
||
class Meta: | ||
abstract = True | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good idea to create this abstract shared model! |
||
|
||
created = models.DateTimeField(auto_now_add=True) | ||
cfr_title = models.IntegerField() | ||
cfr_part = models.IntegerField() | ||
clear_cache = models.BooleanField(default=False) | ||
destination = models.URLField(default="http://fake-reg-site.gov/api", | ||
max_length=2000) | ||
destination = models.URLField(max_length=2000) | ||
notification_email = models.EmailField(blank="True", max_length=254) | ||
only_latest = models.BooleanField(default=False) | ||
job_id = models.UUIDField(default=None, null=True) | ||
use_uploaded_metadata = models.UUIDField(default=None, null=True) | ||
use_uploaded_regulation = models.UUIDField(default=None, null=True) | ||
|
@@ -25,3 +33,28 @@ class ParsingJob(models.Model): | |
("complete_with_errors", "complete_with_errors") | ||
), default="received") | ||
url = models.URLField(blank=True, max_length=2000) | ||
|
||
def save(self, *args, **kwargs): | ||
super(ParsingJob, self).save(*args, **kwargs) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Doesn't look like this is doing anything? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Looks like that's a leftover vestige of some point where I had Redis-related code there; I'll remove that. |
||
|
||
|
||
class PipelineJob(ParsingJob): | ||
|
||
cfr_title = models.IntegerField() | ||
cfr_part = models.IntegerField() | ||
only_latest = models.BooleanField(default=False) | ||
|
||
|
||
class ProposalPipelineJob(ParsingJob): | ||
|
||
file_hexhash = models.CharField(max_length=32) | ||
only_latest = models.BooleanField(default=True) | ||
|
||
|
||
class RegulationFile(models.Model): | ||
|
||
contents = models.BinaryField() | ||
file = models.FileField(null=True) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I wouldn't think we'd need both a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I totally agree; this is a short-term hack. I'm not sure what the correct answer is beyond what you've outlined above, but what you suggest is definitely the right direction. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. (At some point I thought that storing the contents only in the DB might be the way to go, but I now don't think that's likely to hold up.) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Issue captured here: #302 |
||
filename = models.CharField(default=None, max_length=512, null=True) | ||
hexhash = models.CharField(default=None, max_length=32, null=True) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Spit balling, but should this be the primary key? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Quite possibly. I was looking at the hashing approach as speculative, but if we keep it, then this should probably the the primary key. |
||
url = models.URLField(blank=True, max_length=2000) |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,8 +3,19 @@ | |
from rest_framework.urlpatterns import format_suffix_patterns | ||
|
||
urlpatterns = [ | ||
url(r'^job/$', views.JobViewList.as_view()), | ||
url(r'^job/(?P<job_id>[-a-z0-9]+)/$', | ||
url(r'^job(/)$', views.JobViewList.as_view()), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't understand the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I suspect this is due to some Django flag I haven't set, but locally, without that, navigating to e.g. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Let's work through that at some point. I'm betting this is a regex confusion. Not worth holding up the PR, though. |
||
url(r'^job/pipeline(/)$', views.PipelineJobViewList.as_view()), | ||
url(r'^job/pipeline(/)(?P<job_id>[-a-z0-9]+)(/)$', | ||
views.PipelineJobViewInstance.as_view()), | ||
url(r'^job/proposal-pipeline(/)$', | ||
views.ProposalPipelineJobViewList.as_view()), | ||
url(r'^job/proposal-pipeline(/)(?P<job_id>[-a-z0-9]+)(/)$', | ||
views.ProposalPipelineJobViewInstance.as_view()), | ||
url(r'^job/upload/(?P<hexhash>[a-z0-9]{32})(/)$', | ||
views.FileUploadViewInstance.as_view()), | ||
url(r'^job/upload(/)$', | ||
views.FileUploadView.as_view()), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Minor, but what do you think about using a term like There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm open to that, and in general view all these URLs as merely first-pass suggestions. Happy to change any/all as we find better options. |
||
url(r'^job/(?P<job_id>[-a-z0-9]+)(/)$', | ||
views.JobViewInstance.as_view()) | ||
] | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It doesn't look like
job_status_pairs
is ever used; if not, can we just make a tuple with the five strings? I'd argue a tuple is better than a list here due to being immutable.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It should be a tuple; also I meant to use
job_status_pairs
in the arguments for one of theCharField
s in that file. I've added those changes and will push them shortly.