зеркало из https://github.com/mozilla/treeherder.git
Merge branch 'master' into resultset-date-range
This commit is contained in:
Коммит
ae6640f3e0
|
@ -25,3 +25,5 @@ httplib2==0.7.4
|
|||
|
||||
git+git://github.com/jeads/datasource@143ac08d11
|
||||
git+git://github.com/mozilla/treeherder-client@1dc3644494
|
||||
|
||||
jsonfield==0.9.20
|
|
@ -1,5 +1,4 @@
|
|||
import os
|
||||
import simplejson as json
|
||||
from os.path import dirname
|
||||
import sys
|
||||
from django.core.management import call_command
|
||||
|
@ -80,6 +79,8 @@ def pytest_runtest_teardown(item):
|
|||
for ds in ds_list:
|
||||
ds.delete()
|
||||
|
||||
call_command("migrate", 'model', '0001_initial')
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
[
|
||||
{
|
||||
"pk": 1,
|
||||
"model": "model.visibilityprofile",
|
||||
"fields": {
|
||||
"default": true,
|
||||
|
||||
}
|
||||
},
|
||||
{
|
||||
"pk": 2,
|
||||
"model": "djcelery.intervalschedule",
|
||||
"fields": {
|
||||
"every": 1,
|
||||
"period": "minutes"
|
||||
}
|
||||
}
|
||||
]
|
|
@ -12,8 +12,8 @@ class Migration(SchemaMigration):
|
|||
db.create_table(u'product', (
|
||||
('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
|
||||
('name', self.gf('django.db.models.fields.CharField')(max_length=50L)),
|
||||
('description', self.gf('django.db.models.fields.TextField')()),
|
||||
('active_status', self.gf('django.db.models.fields.CharField')(max_length=7L, blank=True)),
|
||||
('description', self.gf('django.db.models.fields.TextField')(default=u'fill me', blank=True)),
|
||||
('active_status', self.gf('django.db.models.fields.CharField')(default=u'active', max_length=7L, blank=True)),
|
||||
))
|
||||
db.send_create_signal(u'model', ['Product'])
|
||||
|
||||
|
@ -23,7 +23,7 @@ class Migration(SchemaMigration):
|
|||
('os_name', self.gf('django.db.models.fields.CharField')(max_length=25L)),
|
||||
('platform', self.gf('django.db.models.fields.CharField')(max_length=25L)),
|
||||
('architecture', self.gf('django.db.models.fields.CharField')(max_length=25L, blank=True)),
|
||||
('active_status', self.gf('django.db.models.fields.CharField')(max_length=7L, blank=True)),
|
||||
('active_status', self.gf('django.db.models.fields.CharField')(default=u'active', max_length=7L, blank=True)),
|
||||
))
|
||||
db.send_create_signal(u'model', ['BuildPlatform'])
|
||||
|
||||
|
@ -31,8 +31,8 @@ class Migration(SchemaMigration):
|
|||
db.create_table(u'option', (
|
||||
('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
|
||||
('name', self.gf('django.db.models.fields.CharField')(max_length=50L)),
|
||||
('description', self.gf('django.db.models.fields.TextField')()),
|
||||
('active_status', self.gf('django.db.models.fields.CharField')(max_length=7L, blank=True)),
|
||||
('description', self.gf('django.db.models.fields.TextField')(default=u'fill me', blank=True)),
|
||||
('active_status', self.gf('django.db.models.fields.CharField')(default=u'active', max_length=7L, blank=True)),
|
||||
))
|
||||
db.send_create_signal(u'model', ['Option'])
|
||||
|
||||
|
@ -40,8 +40,8 @@ class Migration(SchemaMigration):
|
|||
db.create_table(u'repository_group', (
|
||||
('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
|
||||
('name', self.gf('django.db.models.fields.CharField')(max_length=50L)),
|
||||
('description', self.gf('django.db.models.fields.TextField')()),
|
||||
('active_status', self.gf('django.db.models.fields.CharField')(max_length=7L, blank=True)),
|
||||
('description', self.gf('django.db.models.fields.TextField')(default=u'fill me', blank=True)),
|
||||
('active_status', self.gf('django.db.models.fields.CharField')(default=u'active', max_length=7L, blank=True)),
|
||||
))
|
||||
db.send_create_signal(u'model', ['RepositoryGroup'])
|
||||
|
||||
|
@ -53,8 +53,8 @@ class Migration(SchemaMigration):
|
|||
('dvcs_type', self.gf('django.db.models.fields.CharField')(max_length=25L)),
|
||||
('url', self.gf('django.db.models.fields.CharField')(max_length=255L)),
|
||||
('codebase', self.gf('django.db.models.fields.CharField')(max_length=50L, blank=True)),
|
||||
('description', self.gf('django.db.models.fields.TextField')()),
|
||||
('active_status', self.gf('django.db.models.fields.CharField')(max_length=7L, blank=True)),
|
||||
('description', self.gf('django.db.models.fields.TextField')(default=u'fill me', blank=True)),
|
||||
('active_status', self.gf('django.db.models.fields.CharField')(default=u'active', max_length=7L, blank=True)),
|
||||
))
|
||||
db.send_create_signal(u'model', ['Repository'])
|
||||
|
||||
|
@ -64,7 +64,7 @@ class Migration(SchemaMigration):
|
|||
('os_name', self.gf('django.db.models.fields.CharField')(max_length=25L)),
|
||||
('platform', self.gf('django.db.models.fields.CharField')(max_length=25L)),
|
||||
('architecture', self.gf('django.db.models.fields.CharField')(max_length=25L, blank=True)),
|
||||
('active_status', self.gf('django.db.models.fields.CharField')(max_length=7L, blank=True)),
|
||||
('active_status', self.gf('django.db.models.fields.CharField')(default=u'active', max_length=7L, blank=True)),
|
||||
))
|
||||
db.send_create_signal(u'model', ['MachinePlatform'])
|
||||
|
||||
|
@ -87,7 +87,7 @@ class Migration(SchemaMigration):
|
|||
('name', self.gf('django.db.models.fields.CharField')(max_length=50L)),
|
||||
('first_timestamp', self.gf('django.db.models.fields.IntegerField')()),
|
||||
('last_timestamp', self.gf('django.db.models.fields.IntegerField')()),
|
||||
('active_status', self.gf('django.db.models.fields.CharField')(max_length=7L, blank=True)),
|
||||
('active_status', self.gf('django.db.models.fields.CharField')(default=u'active', max_length=7L, blank=True)),
|
||||
))
|
||||
db.send_create_signal(u'model', ['Machine'])
|
||||
|
||||
|
@ -97,7 +97,7 @@ class Migration(SchemaMigration):
|
|||
('machine', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['model.Machine'])),
|
||||
('author', self.gf('django.db.models.fields.CharField')(max_length=50L)),
|
||||
('machine_timestamp', self.gf('django.db.models.fields.IntegerField')()),
|
||||
('active_status', self.gf('django.db.models.fields.CharField')(max_length=7L, blank=True)),
|
||||
('active_status', self.gf('django.db.models.fields.CharField')(default=u'active', max_length=7L, blank=True)),
|
||||
('note', self.gf('django.db.models.fields.TextField')(blank=True)),
|
||||
))
|
||||
db.send_create_signal(u'model', ['MachineNote'])
|
||||
|
@ -112,20 +112,25 @@ class Migration(SchemaMigration):
|
|||
('read_only_host', self.gf('django.db.models.fields.CharField')(max_length=128L, blank=True)),
|
||||
('name', self.gf('django.db.models.fields.CharField')(max_length=128L)),
|
||||
('type', self.gf('django.db.models.fields.CharField')(max_length=25L)),
|
||||
('oauth_consumer_key', self.gf('django.db.models.fields.CharField')(max_length=45L, blank=True)),
|
||||
('oauth_consumer_secret', self.gf('django.db.models.fields.CharField')(max_length=45L, blank=True)),
|
||||
('creation_date', self.gf('django.db.models.fields.DateTimeField')()),
|
||||
('cron_batch', self.gf('django.db.models.fields.CharField')(max_length=45L, blank=True)),
|
||||
('oauth_consumer_key', self.gf('django.db.models.fields.CharField')(max_length=45L, null=True, blank=True)),
|
||||
('oauth_consumer_secret', self.gf('django.db.models.fields.CharField')(max_length=45L, null=True, blank=True)),
|
||||
('creation_date', self.gf('django.db.models.fields.DateTimeField')(auto_now_add=True, blank=True)),
|
||||
))
|
||||
db.send_create_signal(u'model', ['Datasource'])
|
||||
|
||||
# Adding unique constraint on 'Datasource', fields ['project', 'dataset', 'contenttype']
|
||||
db.create_unique(u'datasource', ['project', 'dataset', 'contenttype'])
|
||||
|
||||
# Adding unique constraint on 'Datasource', fields ['host', 'name']
|
||||
db.create_unique(u'datasource', ['host', 'name'])
|
||||
|
||||
# Adding model 'JobGroup'
|
||||
db.create_table(u'job_group', (
|
||||
('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
|
||||
('symbol', self.gf('django.db.models.fields.CharField')(max_length=10L)),
|
||||
('symbol', self.gf('django.db.models.fields.CharField')(default=u'?', max_length=10L)),
|
||||
('name', self.gf('django.db.models.fields.CharField')(max_length=50L)),
|
||||
('description', self.gf('django.db.models.fields.TextField')()),
|
||||
('active_status', self.gf('django.db.models.fields.CharField')(max_length=7L, blank=True)),
|
||||
('description', self.gf('django.db.models.fields.TextField')(default=u'fill me', blank=True)),
|
||||
('active_status', self.gf('django.db.models.fields.CharField')(default=u'active', max_length=7L, blank=True)),
|
||||
))
|
||||
db.send_create_signal(u'model', ['JobGroup'])
|
||||
|
||||
|
@ -135,26 +140,29 @@ class Migration(SchemaMigration):
|
|||
('repository', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['model.Repository'])),
|
||||
('version', self.gf('django.db.models.fields.CharField')(max_length=50L)),
|
||||
('version_timestamp', self.gf('django.db.models.fields.IntegerField')()),
|
||||
('active_status', self.gf('django.db.models.fields.CharField')(max_length=7L, blank=True)),
|
||||
('active_status', self.gf('django.db.models.fields.CharField')(default=u'active', max_length=7L, blank=True)),
|
||||
))
|
||||
db.send_create_signal(u'model', ['RepositoryVersion'])
|
||||
|
||||
# Adding model 'OptionCollection'
|
||||
db.create_table(u'option_collection', (
|
||||
('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
|
||||
('option', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['model.Option'])),
|
||||
('option_collection_hash', self.gf('django.db.models.fields.CharField')(max_length=40L)),
|
||||
('option', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['model.Option'])),
|
||||
))
|
||||
db.send_create_signal(u'model', ['OptionCollection'])
|
||||
|
||||
# Adding unique constraint on 'OptionCollection', fields ['option_collection_hash', 'option']
|
||||
db.create_unique(u'option_collection', ['option_collection_hash', 'option_id'])
|
||||
|
||||
# Adding model 'JobType'
|
||||
db.create_table(u'job_type', (
|
||||
('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
|
||||
('job_group', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['model.JobGroup'], null=True, blank=True)),
|
||||
('symbol', self.gf('django.db.models.fields.CharField')(max_length=10L)),
|
||||
('symbol', self.gf('django.db.models.fields.CharField')(default=u'?', max_length=10L)),
|
||||
('name', self.gf('django.db.models.fields.CharField')(max_length=50L)),
|
||||
('description', self.gf('django.db.models.fields.TextField')()),
|
||||
('active_status', self.gf('django.db.models.fields.CharField')(max_length=7L, blank=True)),
|
||||
('description', self.gf('django.db.models.fields.TextField')(default=u'fill me', blank=True)),
|
||||
('active_status', self.gf('django.db.models.fields.CharField')(default=u'active', max_length=7L, blank=True)),
|
||||
))
|
||||
db.send_create_signal(u'model', ['JobType'])
|
||||
|
||||
|
@ -162,13 +170,22 @@ class Migration(SchemaMigration):
|
|||
db.create_table(u'failure_classification', (
|
||||
('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
|
||||
('name', self.gf('django.db.models.fields.CharField')(max_length=50L)),
|
||||
('description', self.gf('django.db.models.fields.TextField')()),
|
||||
('active_status', self.gf('django.db.models.fields.CharField')(max_length=7L, blank=True)),
|
||||
('description', self.gf('django.db.models.fields.TextField')(default=u'fill me', blank=True)),
|
||||
('active_status', self.gf('django.db.models.fields.CharField')(default=u'active', max_length=7L, blank=True)),
|
||||
))
|
||||
db.send_create_signal(u'model', ['FailureClassification'])
|
||||
|
||||
|
||||
def backwards(self, orm):
|
||||
# Removing unique constraint on 'OptionCollection', fields ['option_collection_hash', 'option']
|
||||
db.delete_unique(u'option_collection', ['option_collection_hash', 'option_id'])
|
||||
|
||||
# Removing unique constraint on 'Datasource', fields ['host', 'name']
|
||||
db.delete_unique(u'datasource', ['host', 'name'])
|
||||
|
||||
# Removing unique constraint on 'Datasource', fields ['project', 'dataset', 'contenttype']
|
||||
db.delete_unique(u'datasource', ['project', 'dataset', 'contenttype'])
|
||||
|
||||
# Deleting model 'Product'
|
||||
db.delete_table(u'product')
|
||||
|
||||
|
@ -229,54 +246,53 @@ class Migration(SchemaMigration):
|
|||
},
|
||||
u'model.buildplatform': {
|
||||
'Meta': {'object_name': 'BuildPlatform', 'db_table': "u'build_platform'"},
|
||||
'active_status': ('django.db.models.fields.CharField', [], {'max_length': '7L', 'blank': 'True'}),
|
||||
'active_status': ('django.db.models.fields.CharField', [], {'default': "u'active'", 'max_length': '7L', 'blank': 'True'}),
|
||||
'architecture': ('django.db.models.fields.CharField', [], {'max_length': '25L', 'blank': 'True'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'os_name': ('django.db.models.fields.CharField', [], {'max_length': '25L'}),
|
||||
'platform': ('django.db.models.fields.CharField', [], {'max_length': '25L'})
|
||||
},
|
||||
u'model.datasource': {
|
||||
'Meta': {'object_name': 'Datasource', 'db_table': "u'datasource'"},
|
||||
'Meta': {'unique_together': "[[u'project', u'dataset', u'contenttype'], [u'host', u'name']]", 'object_name': 'Datasource', 'db_table': "u'datasource'"},
|
||||
'contenttype': ('django.db.models.fields.CharField', [], {'max_length': '25L'}),
|
||||
'creation_date': ('django.db.models.fields.DateTimeField', [], {}),
|
||||
'cron_batch': ('django.db.models.fields.CharField', [], {'max_length': '45L', 'blank': 'True'}),
|
||||
'creation_date': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
|
||||
'dataset': ('django.db.models.fields.IntegerField', [], {}),
|
||||
'host': ('django.db.models.fields.CharField', [], {'max_length': '128L'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '128L'}),
|
||||
'oauth_consumer_key': ('django.db.models.fields.CharField', [], {'max_length': '45L', 'blank': 'True'}),
|
||||
'oauth_consumer_secret': ('django.db.models.fields.CharField', [], {'max_length': '45L', 'blank': 'True'}),
|
||||
'oauth_consumer_key': ('django.db.models.fields.CharField', [], {'max_length': '45L', 'null': 'True', 'blank': 'True'}),
|
||||
'oauth_consumer_secret': ('django.db.models.fields.CharField', [], {'max_length': '45L', 'null': 'True', 'blank': 'True'}),
|
||||
'project': ('django.db.models.fields.CharField', [], {'max_length': '25L'}),
|
||||
'read_only_host': ('django.db.models.fields.CharField', [], {'max_length': '128L', 'blank': 'True'}),
|
||||
'type': ('django.db.models.fields.CharField', [], {'max_length': '25L'})
|
||||
},
|
||||
u'model.failureclassification': {
|
||||
'Meta': {'object_name': 'FailureClassification', 'db_table': "u'failure_classification'"},
|
||||
'active_status': ('django.db.models.fields.CharField', [], {'max_length': '7L', 'blank': 'True'}),
|
||||
'description': ('django.db.models.fields.TextField', [], {}),
|
||||
'active_status': ('django.db.models.fields.CharField', [], {'default': "u'active'", 'max_length': '7L', 'blank': 'True'}),
|
||||
'description': ('django.db.models.fields.TextField', [], {'default': "u'fill me'", 'blank': 'True'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '50L'})
|
||||
},
|
||||
u'model.jobgroup': {
|
||||
'Meta': {'object_name': 'JobGroup', 'db_table': "u'job_group'"},
|
||||
'active_status': ('django.db.models.fields.CharField', [], {'max_length': '7L', 'blank': 'True'}),
|
||||
'description': ('django.db.models.fields.TextField', [], {}),
|
||||
'active_status': ('django.db.models.fields.CharField', [], {'default': "u'active'", 'max_length': '7L', 'blank': 'True'}),
|
||||
'description': ('django.db.models.fields.TextField', [], {'default': "u'fill me'", 'blank': 'True'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '50L'}),
|
||||
'symbol': ('django.db.models.fields.CharField', [], {'max_length': '10L'})
|
||||
'symbol': ('django.db.models.fields.CharField', [], {'default': "u'?'", 'max_length': '10L'})
|
||||
},
|
||||
u'model.jobtype': {
|
||||
'Meta': {'object_name': 'JobType', 'db_table': "u'job_type'"},
|
||||
'active_status': ('django.db.models.fields.CharField', [], {'max_length': '7L', 'blank': 'True'}),
|
||||
'description': ('django.db.models.fields.TextField', [], {}),
|
||||
'active_status': ('django.db.models.fields.CharField', [], {'default': "u'active'", 'max_length': '7L', 'blank': 'True'}),
|
||||
'description': ('django.db.models.fields.TextField', [], {'default': "u'fill me'", 'blank': 'True'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'job_group': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['model.JobGroup']", 'null': 'True', 'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '50L'}),
|
||||
'symbol': ('django.db.models.fields.CharField', [], {'max_length': '10L'})
|
||||
'symbol': ('django.db.models.fields.CharField', [], {'default': "u'?'", 'max_length': '10L'})
|
||||
},
|
||||
u'model.machine': {
|
||||
'Meta': {'object_name': 'Machine', 'db_table': "u'machine'"},
|
||||
'active_status': ('django.db.models.fields.CharField', [], {'max_length': '7L', 'blank': 'True'}),
|
||||
'active_status': ('django.db.models.fields.CharField', [], {'default': "u'active'", 'max_length': '7L', 'blank': 'True'}),
|
||||
'first_timestamp': ('django.db.models.fields.IntegerField', [], {}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'last_timestamp': ('django.db.models.fields.IntegerField', [], {}),
|
||||
|
@ -284,7 +300,7 @@ class Migration(SchemaMigration):
|
|||
},
|
||||
u'model.machinenote': {
|
||||
'Meta': {'object_name': 'MachineNote', 'db_table': "u'machine_note'"},
|
||||
'active_status': ('django.db.models.fields.CharField', [], {'max_length': '7L', 'blank': 'True'}),
|
||||
'active_status': ('django.db.models.fields.CharField', [], {'default': "u'active'", 'max_length': '7L', 'blank': 'True'}),
|
||||
'author': ('django.db.models.fields.CharField', [], {'max_length': '50L'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'machine': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['model.Machine']"}),
|
||||
|
@ -293,7 +309,7 @@ class Migration(SchemaMigration):
|
|||
},
|
||||
u'model.machineplatform': {
|
||||
'Meta': {'object_name': 'MachinePlatform', 'db_table': "u'machine_platform'"},
|
||||
'active_status': ('django.db.models.fields.CharField', [], {'max_length': '7L', 'blank': 'True'}),
|
||||
'active_status': ('django.db.models.fields.CharField', [], {'default': "u'active'", 'max_length': '7L', 'blank': 'True'}),
|
||||
'architecture': ('django.db.models.fields.CharField', [], {'max_length': '25L', 'blank': 'True'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'os_name': ('django.db.models.fields.CharField', [], {'max_length': '25L'}),
|
||||
|
@ -301,49 +317,49 @@ class Migration(SchemaMigration):
|
|||
},
|
||||
u'model.option': {
|
||||
'Meta': {'object_name': 'Option', 'db_table': "u'option'"},
|
||||
'active_status': ('django.db.models.fields.CharField', [], {'max_length': '7L', 'blank': 'True'}),
|
||||
'description': ('django.db.models.fields.TextField', [], {}),
|
||||
'active_status': ('django.db.models.fields.CharField', [], {'default': "u'active'", 'max_length': '7L', 'blank': 'True'}),
|
||||
'description': ('django.db.models.fields.TextField', [], {'default': "u'fill me'", 'blank': 'True'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '50L'})
|
||||
},
|
||||
u'model.optioncollection': {
|
||||
'Meta': {'object_name': 'OptionCollection', 'db_table': "u'option_collection'"},
|
||||
'Meta': {'unique_together': "([u'option_collection_hash', u'option'],)", 'object_name': 'OptionCollection', 'db_table': "u'option_collection'"},
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'option': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['model.Option']"}),
|
||||
'option_collection_hash': ('django.db.models.fields.CharField', [], {'max_length': '40L',})
|
||||
'option_collection_hash': ('django.db.models.fields.CharField', [], {'max_length': '40L'})
|
||||
},
|
||||
u'model.product': {
|
||||
'Meta': {'object_name': 'Product', 'db_table': "u'product'"},
|
||||
'active_status': ('django.db.models.fields.CharField', [], {'max_length': '7L', 'blank': 'True'}),
|
||||
'description': ('django.db.models.fields.TextField', [], {}),
|
||||
'active_status': ('django.db.models.fields.CharField', [], {'default': "u'active'", 'max_length': '7L', 'blank': 'True'}),
|
||||
'description': ('django.db.models.fields.TextField', [], {'default': "u'fill me'", 'blank': 'True'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '50L'})
|
||||
},
|
||||
u'model.repository': {
|
||||
'Meta': {'object_name': 'Repository', 'db_table': "u'repository'"},
|
||||
'active_status': ('django.db.models.fields.CharField', [], {'max_length': '7L', 'blank': 'True'}),
|
||||
'active_status': ('django.db.models.fields.CharField', [], {'default': "u'active'", 'max_length': '7L', 'blank': 'True'}),
|
||||
'codebase': ('django.db.models.fields.CharField', [], {'max_length': '50L', 'blank': 'True'}),
|
||||
'description': ('django.db.models.fields.TextField', [], {}),
|
||||
'description': ('django.db.models.fields.TextField', [], {'default': "u'fill me'", 'blank': 'True'}),
|
||||
'dvcs_type': ('django.db.models.fields.CharField', [], {'max_length': '25L'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '50L'}),
|
||||
'repository_group': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['model.RepositoryGroup']"}),
|
||||
'dvcs_type': ('django.db.models.fields.CharField', [], {'max_length': '25L'}),
|
||||
'url': ('django.db.models.fields.CharField', [], {'max_length': '255L'})
|
||||
},
|
||||
u'model.repositorygroup': {
|
||||
'Meta': {'object_name': 'RepositoryGroup', 'db_table': "u'repository_group'"},
|
||||
'active_status': ('django.db.models.fields.CharField', [], {'max_length': '7L', 'blank': 'True'}),
|
||||
'description': ('django.db.models.fields.TextField', [], {}),
|
||||
'active_status': ('django.db.models.fields.CharField', [], {'default': "u'active'", 'max_length': '7L', 'blank': 'True'}),
|
||||
'description': ('django.db.models.fields.TextField', [], {'default': "u'fill me'", 'blank': 'True'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '50L'})
|
||||
},
|
||||
u'model.repositoryversion': {
|
||||
'Meta': {'object_name': 'RepositoryVersion', 'db_table': "u'repository_version'"},
|
||||
'active_status': ('django.db.models.fields.CharField', [], {'max_length': '7L', 'blank': 'True'}),
|
||||
'active_status': ('django.db.models.fields.CharField', [], {'default': "u'active'", 'max_length': '7L', 'blank': 'True'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'repository': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['model.Repository']"}),
|
||||
'version_timestamp': ('django.db.models.fields.IntegerField', [], {}),
|
||||
'version': ('django.db.models.fields.CharField', [], {'max_length': '50L'})
|
||||
'version': ('django.db.models.fields.CharField', [], {'max_length': '50L'}),
|
||||
'version_timestamp': ('django.db.models.fields.IntegerField', [], {})
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,254 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
import datetime
|
||||
from south.db import db
|
||||
from south.v2 import SchemaMigration
|
||||
from django.db import models
|
||||
|
||||
|
||||
class Migration(SchemaMigration):
|
||||
|
||||
def forwards(self, orm):
|
||||
# Adding model 'UserExclusionProfile'
|
||||
db.create_table(u'model_userexclusionprofile', (
|
||||
(u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
|
||||
('user', self.gf('django.db.models.fields.related.ForeignKey')(related_name=u'exclusion_profiles', to=orm['auth.User'])),
|
||||
('exclusion_profile', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['model.ExclusionProfile'], null=True, blank=True)),
|
||||
('is_default', self.gf('django.db.models.fields.BooleanField')(default=True)),
|
||||
))
|
||||
db.send_create_signal(u'model', ['UserExclusionProfile'])
|
||||
|
||||
# Adding model 'ExclusionProfile'
|
||||
db.create_table(u'model_exclusionprofile', (
|
||||
(u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
|
||||
('name', self.gf('django.db.models.fields.CharField')(unique=True, max_length=255)),
|
||||
('is_default', self.gf('django.db.models.fields.BooleanField')(default=False)),
|
||||
('flat_exclusion', self.gf('jsonfield.fields.JSONField')(default={}, blank=True)),
|
||||
('author', self.gf('django.db.models.fields.related.ForeignKey')(related_name=u'exclusion_profiles_authored', to=orm['auth.User'])),
|
||||
))
|
||||
db.send_create_signal(u'model', ['ExclusionProfile'])
|
||||
|
||||
# Adding M2M table for field exclusions on 'ExclusionProfile'
|
||||
db.create_table(u'model_exclusionprofile_exclusions', (
|
||||
('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
|
||||
('exclusionprofile', models.ForeignKey(orm[u'model.exclusionprofile'], null=False)),
|
||||
('jobexclusion', models.ForeignKey(orm[u'model.jobexclusion'], null=False))
|
||||
))
|
||||
db.create_unique(u'model_exclusionprofile_exclusions', ['exclusionprofile_id', 'jobexclusion_id'])
|
||||
|
||||
# Adding model 'JobExclusion'
|
||||
db.create_table(u'model_jobexclusion', (
|
||||
(u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
|
||||
('name', self.gf('django.db.models.fields.CharField')(unique=True, max_length=255)),
|
||||
('description', self.gf('django.db.models.fields.TextField')(blank=True)),
|
||||
('info', self.gf('jsonfield.fields.JSONField')()),
|
||||
('author', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['auth.User'])),
|
||||
))
|
||||
db.send_create_signal(u'model', ['JobExclusion'])
|
||||
|
||||
|
||||
def backwards(self, orm):
|
||||
# Deleting model 'UserExclusionProfile'
|
||||
db.delete_table(u'model_userexclusionprofile')
|
||||
|
||||
# Deleting model 'ExclusionProfile'
|
||||
db.delete_table(u'model_exclusionprofile')
|
||||
|
||||
# Removing M2M table for field exclusion on 'ExclusionProfile'
|
||||
db.delete_table('model_exclusionprofile_exclusions')
|
||||
|
||||
# Deleting model 'JobExclusion'
|
||||
db.delete_table(u'model_jobexclusion')
|
||||
|
||||
|
||||
models = {
|
||||
u'auth.group': {
|
||||
'Meta': {'object_name': 'Group'},
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
|
||||
'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
|
||||
},
|
||||
u'auth.permission': {
|
||||
'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'},
|
||||
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
||||
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
|
||||
},
|
||||
u'auth.user': {
|
||||
'Meta': {'object_name': 'User'},
|
||||
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
|
||||
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
|
||||
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
|
||||
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||
'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
|
||||
'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
|
||||
'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
|
||||
'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||
'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
|
||||
},
|
||||
u'contenttypes.contenttype': {
|
||||
'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
|
||||
'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
|
||||
},
|
||||
u'model.bugscache': {
|
||||
'Meta': {'object_name': 'Bugscache', 'db_table': "u'bugscache'"},
|
||||
'crash_signature': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'keywords': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'modified': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
|
||||
'os': ('django.db.models.fields.CharField', [], {'max_length': '64L', 'blank': 'True'}),
|
||||
'resolution': ('django.db.models.fields.CharField', [], {'max_length': '64L', 'blank': 'True'}),
|
||||
'status': ('django.db.models.fields.CharField', [], {'max_length': '64L', 'blank': 'True'}),
|
||||
'summary': ('django.db.models.fields.CharField', [], {'max_length': '255L'})
|
||||
},
|
||||
u'model.buildplatform': {
|
||||
'Meta': {'object_name': 'BuildPlatform', 'db_table': "u'build_platform'"},
|
||||
'active_status': ('django.db.models.fields.CharField', [], {'default': "u'active'", 'max_length': '7L', 'blank': 'True'}),
|
||||
'architecture': ('django.db.models.fields.CharField', [], {'max_length': '25L', 'blank': 'True'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'os_name': ('django.db.models.fields.CharField', [], {'max_length': '25L'}),
|
||||
'platform': ('django.db.models.fields.CharField', [], {'max_length': '25L'})
|
||||
},
|
||||
u'model.datasource': {
|
||||
'Meta': {'unique_together': "[[u'project', u'dataset', u'contenttype'], [u'host', u'name']]", 'object_name': 'Datasource', 'db_table': "u'datasource'"},
|
||||
'contenttype': ('django.db.models.fields.CharField', [], {'max_length': '25L'}),
|
||||
'creation_date': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
|
||||
'dataset': ('django.db.models.fields.IntegerField', [], {}),
|
||||
'host': ('django.db.models.fields.CharField', [], {'max_length': '128L'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '128L'}),
|
||||
'oauth_consumer_key': ('django.db.models.fields.CharField', [], {'max_length': '45L', 'null': 'True', 'blank': 'True'}),
|
||||
'oauth_consumer_secret': ('django.db.models.fields.CharField', [], {'max_length': '45L', 'null': 'True', 'blank': 'True'}),
|
||||
'project': ('django.db.models.fields.CharField', [], {'max_length': '25L'}),
|
||||
'read_only_host': ('django.db.models.fields.CharField', [], {'max_length': '128L', 'blank': 'True'}),
|
||||
'type': ('django.db.models.fields.CharField', [], {'max_length': '25L'})
|
||||
},
|
||||
u'model.exclusionprofile': {
|
||||
'Meta': {'object_name': 'ExclusionProfile'},
|
||||
'author': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "u'exclusion_profiles_authored'", 'to': u"orm['auth.User']"}),
|
||||
'exclusions': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "u'profiles'", 'symmetrical': 'False', 'to': u"orm['model.JobExclusion']"}),
|
||||
'flat_exclusion': ('jsonfield.fields.JSONField', [], {'default': '{}', 'blank': 'True'}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'is_default': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'})
|
||||
},
|
||||
u'model.failureclassification': {
|
||||
'Meta': {'object_name': 'FailureClassification', 'db_table': "u'failure_classification'"},
|
||||
'active_status': ('django.db.models.fields.CharField', [], {'default': "u'active'", 'max_length': '7L', 'blank': 'True'}),
|
||||
'description': ('django.db.models.fields.TextField', [], {'default': "u'fill me'", 'blank': 'True'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '50L'})
|
||||
},
|
||||
u'model.jobexclusion': {
|
||||
'Meta': {'object_name': 'JobExclusion'},
|
||||
'author': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"}),
|
||||
'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'info': ('jsonfield.fields.JSONField', [], {}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'})
|
||||
},
|
||||
u'model.jobgroup': {
|
||||
'Meta': {'object_name': 'JobGroup', 'db_table': "u'job_group'"},
|
||||
'active_status': ('django.db.models.fields.CharField', [], {'default': "u'active'", 'max_length': '7L', 'blank': 'True'}),
|
||||
'description': ('django.db.models.fields.TextField', [], {'default': "u'fill me'", 'blank': 'True'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '50L'}),
|
||||
'symbol': ('django.db.models.fields.CharField', [], {'default': "u'?'", 'max_length': '10L'})
|
||||
},
|
||||
u'model.jobtype': {
|
||||
'Meta': {'object_name': 'JobType', 'db_table': "u'job_type'"},
|
||||
'active_status': ('django.db.models.fields.CharField', [], {'default': "u'active'", 'max_length': '7L', 'blank': 'True'}),
|
||||
'description': ('django.db.models.fields.TextField', [], {'default': "u'fill me'", 'blank': 'True'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'job_group': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['model.JobGroup']", 'null': 'True', 'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '50L'}),
|
||||
'symbol': ('django.db.models.fields.CharField', [], {'default': "u'?'", 'max_length': '10L'})
|
||||
},
|
||||
u'model.machine': {
|
||||
'Meta': {'object_name': 'Machine', 'db_table': "u'machine'"},
|
||||
'active_status': ('django.db.models.fields.CharField', [], {'default': "u'active'", 'max_length': '7L', 'blank': 'True'}),
|
||||
'first_timestamp': ('django.db.models.fields.IntegerField', [], {}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'last_timestamp': ('django.db.models.fields.IntegerField', [], {}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '50L'})
|
||||
},
|
||||
u'model.machinenote': {
|
||||
'Meta': {'object_name': 'MachineNote', 'db_table': "u'machine_note'"},
|
||||
'active_status': ('django.db.models.fields.CharField', [], {'default': "u'active'", 'max_length': '7L', 'blank': 'True'}),
|
||||
'author': ('django.db.models.fields.CharField', [], {'max_length': '50L'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'machine': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['model.Machine']"}),
|
||||
'machine_timestamp': ('django.db.models.fields.IntegerField', [], {}),
|
||||
'note': ('django.db.models.fields.TextField', [], {'blank': 'True'})
|
||||
},
|
||||
u'model.machineplatform': {
|
||||
'Meta': {'object_name': 'MachinePlatform', 'db_table': "u'machine_platform'"},
|
||||
'active_status': ('django.db.models.fields.CharField', [], {'default': "u'active'", 'max_length': '7L', 'blank': 'True'}),
|
||||
'architecture': ('django.db.models.fields.CharField', [], {'max_length': '25L', 'blank': 'True'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'os_name': ('django.db.models.fields.CharField', [], {'max_length': '25L'}),
|
||||
'platform': ('django.db.models.fields.CharField', [], {'max_length': '25L'})
|
||||
},
|
||||
u'model.option': {
|
||||
'Meta': {'object_name': 'Option', 'db_table': "u'option'"},
|
||||
'active_status': ('django.db.models.fields.CharField', [], {'default': "u'active'", 'max_length': '7L', 'blank': 'True'}),
|
||||
'description': ('django.db.models.fields.TextField', [], {'default': "u'fill me'", 'blank': 'True'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '50L'})
|
||||
},
|
||||
u'model.optioncollection': {
|
||||
'Meta': {'unique_together': "([u'option_collection_hash', u'option'],)", 'object_name': 'OptionCollection', 'db_table': "u'option_collection'"},
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'option': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['model.Option']"}),
|
||||
'option_collection_hash': ('django.db.models.fields.CharField', [], {'max_length': '40L'})
|
||||
},
|
||||
u'model.product': {
|
||||
'Meta': {'object_name': 'Product', 'db_table': "u'product'"},
|
||||
'active_status': ('django.db.models.fields.CharField', [], {'default': "u'active'", 'max_length': '7L', 'blank': 'True'}),
|
||||
'description': ('django.db.models.fields.TextField', [], {'default': "u'fill me'", 'blank': 'True'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '50L'})
|
||||
},
|
||||
u'model.repository': {
|
||||
'Meta': {'object_name': 'Repository', 'db_table': "u'repository'"},
|
||||
'active_status': ('django.db.models.fields.CharField', [], {'default': "u'active'", 'max_length': '7L', 'blank': 'True'}),
|
||||
'codebase': ('django.db.models.fields.CharField', [], {'max_length': '50L', 'blank': 'True'}),
|
||||
'description': ('django.db.models.fields.TextField', [], {'default': "u'fill me'", 'blank': 'True'}),
|
||||
'dvcs_type': ('django.db.models.fields.CharField', [], {'max_length': '25L'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '50L'}),
|
||||
'repository_group': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['model.RepositoryGroup']"}),
|
||||
'url': ('django.db.models.fields.CharField', [], {'max_length': '255L'})
|
||||
},
|
||||
u'model.repositorygroup': {
|
||||
'Meta': {'object_name': 'RepositoryGroup', 'db_table': "u'repository_group'"},
|
||||
'active_status': ('django.db.models.fields.CharField', [], {'default': "u'active'", 'max_length': '7L', 'blank': 'True'}),
|
||||
'description': ('django.db.models.fields.TextField', [], {'default': "u'fill me'", 'blank': 'True'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '50L'})
|
||||
},
|
||||
u'model.repositoryversion': {
|
||||
'Meta': {'object_name': 'RepositoryVersion', 'db_table': "u'repository_version'"},
|
||||
'active_status': ('django.db.models.fields.CharField', [], {'default': "u'active'", 'max_length': '7L', 'blank': 'True'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'repository': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['model.Repository']"}),
|
||||
'version': ('django.db.models.fields.CharField', [], {'max_length': '50L'}),
|
||||
'version_timestamp': ('django.db.models.fields.IntegerField', [], {})
|
||||
},
|
||||
u'model.userexclusionprofile': {
|
||||
'Meta': {'object_name': 'UserExclusionProfile'},
|
||||
'exclusion_profile': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['model.ExclusionProfile']", 'null': 'True', 'blank': 'True'}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'is_default': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||
'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "u'exclusion_profiles'", 'to': u"orm['auth.User']"})
|
||||
}
|
||||
}
|
||||
|
||||
complete_apps = ['model']
|
|
@ -4,14 +4,20 @@ import uuid
|
|||
import subprocess
|
||||
import os
|
||||
|
||||
from collections import defaultdict
|
||||
import itertools
|
||||
|
||||
from datasource.bases.BaseHub import BaseHub
|
||||
from datasource.hubs.MySQL import MySQL
|
||||
from django.conf import settings
|
||||
from django.core.cache import cache
|
||||
from django.db import models
|
||||
from django.db.models import Max
|
||||
from django.contrib.auth.models import User
|
||||
from warnings import filterwarnings, resetwarnings
|
||||
|
||||
from jsonfield import JSONField
|
||||
|
||||
from treeherder import path
|
||||
|
||||
|
||||
|
@ -524,3 +530,77 @@ class FailureClassification(models.Model):
|
|||
|
||||
def __unicode__(self):
|
||||
return self.name
|
||||
|
||||
|
||||
# exclusion profiles models
|
||||
|
||||
class JobExclusion(models.Model):
|
||||
"""
|
||||
A filter represents a collection of properties
|
||||
that you want to filter jobs on. These properties along with their values
|
||||
are kept in the info field in json format
|
||||
"""
|
||||
name = models.CharField(max_length=255, unique=True)
|
||||
description = models.TextField(blank=True)
|
||||
info = JSONField()
|
||||
author = models.ForeignKey(User)
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
super(JobExclusion, self).save(*args, **kwargs)
|
||||
|
||||
# trigger the save method on all the profiles related to this exclusion
|
||||
for profile in self.profiles.all():
|
||||
profile.save()
|
||||
|
||||
|
||||
class ExclusionProfile(models.Model):
|
||||
"""
|
||||
An exclusion profile represents a list of job exclusions that can be associated with a user profile.
|
||||
"""
|
||||
name = models.CharField(max_length=255, unique=True)
|
||||
is_default = models.BooleanField(default=False)
|
||||
exclusions = models.ManyToManyField(JobExclusion, related_name="profiles")
|
||||
flat_exclusion = JSONField(blank=True, default={})
|
||||
author = models.ForeignKey(User, related_name="exclusion_profiles_authored")
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
super(ExclusionProfile, self).save(*args, **kwargs)
|
||||
|
||||
# prepare the nested defaultdict structure for the flat exclusions
|
||||
# options should be stored in a set but sets are not serializable.
|
||||
# using a list instead
|
||||
job_types_constructor = lambda: defaultdict(list)
|
||||
platform_constructor = lambda: defaultdict(job_types_constructor)
|
||||
flat_exclusions = defaultdict(platform_constructor)
|
||||
|
||||
for exclusion in self.exclusions.all().select_related("info"):
|
||||
# create a set of combinations for each property in the exclusion
|
||||
combo = tuple(itertools.product(exclusion.info['repos'], exclusion.info['platforms'],
|
||||
exclusion.info['job_types'], exclusion.info['options']))
|
||||
for repo, platform, job_type, option in combo:
|
||||
# strip the job type symbol appended in the ui
|
||||
job_type = job_type[:job_type.rfind(" (")]
|
||||
options = flat_exclusions[repo][platform][job_type]
|
||||
# using a list instead of a set and checking if the value already exists
|
||||
if not options in options:
|
||||
options.append(option)
|
||||
|
||||
self.flat_exclusion = flat_exclusions
|
||||
kwargs["force_insert"] = False
|
||||
kwargs["force_update"] = True
|
||||
super(ExclusionProfile, self).save(*args, **kwargs)
|
||||
|
||||
# update the old default profile
|
||||
if self.is_default:
|
||||
ExclusionProfile.objects.filter(is_default=True).exclude(id=self.id).update(is_default=False)
|
||||
|
||||
|
||||
class UserExclusionProfile(models.Model):
|
||||
"""
|
||||
An extension to the standard user model that keeps the exclusion
|
||||
profile relationship.
|
||||
"""
|
||||
|
||||
user = models.ForeignKey(User, related_name="exclusion_profiles")
|
||||
exclusion_profile = models.ForeignKey(ExclusionProfile, blank=True, null=True)
|
||||
is_default = models.BooleanField(default=True)
|
||||
|
|
|
@ -272,3 +272,12 @@ BROKER_URL = 'amqp://{0}:{1}@{2}:{3}/{4}'.format(
|
|||
API_HOSTNAME = SITE_URL
|
||||
|
||||
BROWSERID_AUDIENCES = [SITE_URL]
|
||||
|
||||
|
||||
def obtain_username(email):
|
||||
if email.endswith("@mozilla.com"):
|
||||
return email.rsplit('@', 1)[0]
|
||||
else:
|
||||
return email
|
||||
|
||||
BROWSERID_USERNAME_ALGO = obtain_username
|
||||
|
|
|
@ -1,14 +1,29 @@
|
|||
from rest_framework.permissions import BasePermission
|
||||
from rest_framework.permissions import SAFE_METHODS
|
||||
from rest_framework import permissions
|
||||
|
||||
|
||||
class IsStaffOrReadOnly(BasePermission):
|
||||
class IsStaffOrReadOnly(permissions.BasePermission):
|
||||
"""
|
||||
The request is authenticated as an admin staff (eg. sheriffs), or is a read-only request.
|
||||
"""
|
||||
|
||||
def has_permission(self, request, view):
|
||||
return (request.method in SAFE_METHODS or
|
||||
return (request.method in permissions.SAFE_METHODS or
|
||||
request.user and
|
||||
request.user.is_authenticated() and
|
||||
request.user.is_staff)
|
||||
|
||||
|
||||
class IsOwnerOrReadOnly(permissions.BasePermission):
|
||||
"""
|
||||
Object-level permission to only allow owners of an object to edit it.
|
||||
Assumes the model instance has an `user` attribute.
|
||||
"""
|
||||
|
||||
def has_object_permission(self, request, view, obj):
|
||||
# Read permissions are allowed to any request,
|
||||
# so we'll always allow GET, HEAD or OPTIONS requests.
|
||||
if request.method in permissions.SAFE_METHODS:
|
||||
return True
|
||||
|
||||
# Instance must have an attribute named `user`.
|
||||
return obj.user == request.user
|
||||
|
|
|
@ -1,9 +1,14 @@
|
|||
from rest_framework import viewsets, serializers
|
||||
from rest_framework import viewsets
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.authentication import SessionAuthentication
|
||||
|
||||
from django.contrib.auth.models import User
|
||||
|
||||
from treeherder.model import models
|
||||
from treeherder.model.derived import RefDataManager
|
||||
|
||||
from treeherder.webapp.api import serializers as th_serializers
|
||||
from treeherder.webapp.api.permissions import (IsStaffOrReadOnly,
|
||||
IsOwnerOrReadOnly)
|
||||
|
||||
#####################
|
||||
# Refdata ViewSets
|
||||
|
@ -29,21 +34,10 @@ class JobGroupViewSet(viewsets.ReadOnlyModelViewSet):
|
|||
model = models.JobGroup
|
||||
|
||||
|
||||
class RepositoryGroupSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = models.RepositoryGroup
|
||||
fields = ('name', 'description')
|
||||
|
||||
class RepositorySerializer(serializers.ModelSerializer):
|
||||
repository_group = RepositoryGroupSerializer()
|
||||
|
||||
class Meta:
|
||||
model = models.Repository
|
||||
|
||||
class RepositoryViewSet(viewsets.ReadOnlyModelViewSet):
|
||||
"""ViewSet for the refdata Repository model"""
|
||||
model = models.Repository
|
||||
serializer_class = RepositorySerializer
|
||||
serializer_class = th_serializers.RepositorySerializer
|
||||
|
||||
|
||||
class MachinePlatformViewSet(viewsets.ReadOnlyModelViewSet):
|
||||
|
@ -80,9 +74,6 @@ class BugscacheViewSet(viewsets.ReadOnlyModelViewSet):
|
|||
return Response(suggested_bugs)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class MachineViewSet(viewsets.ReadOnlyModelViewSet):
|
||||
"""ViewSet for the refdata Machine model"""
|
||||
model = models.Machine
|
||||
|
@ -111,3 +102,63 @@ class JobTypeViewSet(viewsets.ReadOnlyModelViewSet):
|
|||
class FailureClassificationViewSet(viewsets.ReadOnlyModelViewSet):
|
||||
"""ViewSet for the refdata FailureClassification model"""
|
||||
model = models.FailureClassification
|
||||
|
||||
|
||||
#############################
|
||||
# User and exclusion profiles
|
||||
#############################
|
||||
|
||||
|
||||
class UserViewSet(viewsets.ReadOnlyModelViewSet):
|
||||
"""
|
||||
Info about a logged-in user.
|
||||
Used by treeherder-ui to inspect user properties like the exclusion profile
|
||||
"""
|
||||
model = User
|
||||
serializer_class = th_serializers.UserSerializer
|
||||
authentication_classes = (SessionAuthentication,)
|
||||
|
||||
def get_queryset(self):
|
||||
return User.objects.filter(id=self.request.user.id)
|
||||
|
||||
|
||||
class UserExclusionProfileViewSet(viewsets.ModelViewSet):
|
||||
model = models.UserExclusionProfile
|
||||
authentication_classes = (SessionAuthentication,)
|
||||
permission_classes = (IsOwnerOrReadOnly,)
|
||||
serializer_class = th_serializers.UserExclusionProfileSerializer
|
||||
|
||||
|
||||
class JobExclusionViewSet(viewsets.ModelViewSet):
|
||||
model = models.JobExclusion
|
||||
authentication_classes = (SessionAuthentication,)
|
||||
permission_classes = (IsStaffOrReadOnly,)
|
||||
serializer_class = th_serializers.JobExclusionSerializer
|
||||
|
||||
def create(self, request, *args, **kwargs):
|
||||
"""
|
||||
Overrides the default Viewset to set the current user
|
||||
as the author of this filter
|
||||
"""
|
||||
if "author" not in request.DATA:
|
||||
request.DATA["author"] = request.user.id
|
||||
return super(JobExclusionViewSet, self).create(request, *args, **kwargs)
|
||||
|
||||
|
||||
class ExclusionProfileViewSet(viewsets.ModelViewSet):
|
||||
"""
|
||||
|
||||
"""
|
||||
model = models.ExclusionProfile
|
||||
authentication_classes = (SessionAuthentication,)
|
||||
permission_classes = (IsStaffOrReadOnly,)
|
||||
serializer_class = th_serializers.ExclusionProfileSerializer
|
||||
|
||||
def create(self, request, *args, **kwargs):
|
||||
"""
|
||||
Overrides the default Viewset to set the current user
|
||||
as the author of this exclusion profile
|
||||
"""
|
||||
if "author" not in request.DATA:
|
||||
request.DATA["author"] = request.user.id
|
||||
return super(ExclusionProfileViewSet, self).create(request, *args, **kwargs)
|
||||
|
|
|
@ -0,0 +1,50 @@
|
|||
from django.contrib.auth.models import User
|
||||
from rest_framework import serializers
|
||||
|
||||
from treeherder.model import models
|
||||
|
||||
|
||||
class UserExclusionProfileSerializer(serializers.ModelSerializer):
|
||||
exclusion_profile = serializers.PrimaryKeyRelatedField()
|
||||
|
||||
class Meta:
|
||||
model = models.UserExclusionProfile
|
||||
fields = ["is_default", "exclusion_profile"]
|
||||
|
||||
|
||||
class UserSerializer(serializers.ModelSerializer):
|
||||
exclusion_profiles = UserExclusionProfileSerializer()
|
||||
|
||||
class Meta:
|
||||
model = User
|
||||
fields = ["username", "is_superuser", "is_staff", "email", "exclusion_profiles"]
|
||||
|
||||
|
||||
class JobExclusionSerializer(serializers.ModelSerializer):
|
||||
info = serializers.WritableField()
|
||||
|
||||
class Meta:
|
||||
model = models.JobExclusion
|
||||
|
||||
|
||||
class ExclusionProfileSerializer(serializers.ModelSerializer):
|
||||
|
||||
flat_exclusion = serializers.WritableField(required=False)
|
||||
exclusions = serializers.PrimaryKeyRelatedField(many=True)
|
||||
|
||||
class Meta:
|
||||
model = models.ExclusionProfile
|
||||
exclude = ['users']
|
||||
|
||||
|
||||
class RepositoryGroupSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = models.RepositoryGroup
|
||||
fields = ('name', 'description')
|
||||
|
||||
|
||||
class RepositorySerializer(serializers.ModelSerializer):
|
||||
repository_group = RepositoryGroupSerializer()
|
||||
|
||||
class Meta:
|
||||
model = models.Repository
|
|
@ -47,7 +47,6 @@ project_bound_router.register(
|
|||
base_name='bug-job-map',
|
||||
)
|
||||
|
||||
|
||||
# this is the default router for plain restful endpoints
|
||||
|
||||
# refdata endpoints:
|
||||
|
@ -65,6 +64,9 @@ default_router.register(r'option', refdata.OptionViewSet)
|
|||
default_router.register(r'optioncollection', refdata.OptionCollectionViewSet)
|
||||
default_router.register(r'bugscache', refdata.BugscacheViewSet)
|
||||
default_router.register(r'failureclassification', refdata.FailureClassificationViewSet)
|
||||
default_router.register(r'user', refdata.UserViewSet)
|
||||
default_router.register(r'exclusion-profile', refdata.ExclusionProfileViewSet)
|
||||
default_router.register(r'job-exclusion', refdata.JobExclusionViewSet)
|
||||
|
||||
|
||||
urlpatterns = patterns(
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
from .fields import JSONField, JSONCharField
|
|
@ -0,0 +1,167 @@
|
|||
import copy
|
||||
from django.db import models
|
||||
from django.core.serializers.json import DjangoJSONEncoder
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
try:
|
||||
from django.utils import six
|
||||
except ImportError:
|
||||
import six
|
||||
|
||||
try:
|
||||
import json
|
||||
except ImportError:
|
||||
from django.utils import simplejson as json
|
||||
|
||||
from django.forms import fields
|
||||
from django.forms.util import ValidationError
|
||||
|
||||
from .subclassing import SubfieldBase
|
||||
|
||||
|
||||
class JSONFormFieldBase(object):
|
||||
|
||||
def to_python(self, value):
|
||||
if isinstance(value, six.string_types):
|
||||
try:
|
||||
return json.loads(value, **self.load_kwargs)
|
||||
except ValueError:
|
||||
raise ValidationError(_("Enter valid JSON"))
|
||||
return value
|
||||
|
||||
def clean(self, value):
|
||||
|
||||
if not value and not self.required:
|
||||
return None
|
||||
|
||||
# Trap cleaning errors & bubble them up as JSON errors
|
||||
try:
|
||||
return super(JSONFormFieldBase, self).clean(value)
|
||||
except TypeError:
|
||||
raise ValidationError(_("Enter valid JSON"))
|
||||
|
||||
|
||||
class JSONFormField(JSONFormFieldBase, fields.Field):
|
||||
pass
|
||||
|
||||
class JSONCharFormField(JSONFormFieldBase, fields.CharField):
|
||||
pass
|
||||
|
||||
|
||||
class JSONFieldBase(six.with_metaclass(SubfieldBase, models.Field)):
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.dump_kwargs = kwargs.pop('dump_kwargs', {
|
||||
'cls': DjangoJSONEncoder,
|
||||
'separators': (',', ':')
|
||||
})
|
||||
self.load_kwargs = kwargs.pop('load_kwargs', {})
|
||||
|
||||
super(JSONFieldBase, self).__init__(*args, **kwargs)
|
||||
|
||||
def pre_init(self, value, obj):
|
||||
"""Convert a string value to JSON only if it needs to be deserialized.
|
||||
|
||||
SubfieldBase meteaclass has been modified to call this method instead of
|
||||
to_python so that we can check the obj state and determine if it needs to be
|
||||
deserialized"""
|
||||
|
||||
if obj._state.adding:
|
||||
# Make sure the primary key actually exists on the object before
|
||||
# checking if it's empty. This is a special case for South datamigrations
|
||||
# see: https://github.com/bradjasper/django-jsonfield/issues/52
|
||||
if not hasattr(obj, "pk") or obj.pk is not None:
|
||||
if isinstance(value, six.string_types):
|
||||
try:
|
||||
return json.loads(value, **self.load_kwargs)
|
||||
except ValueError:
|
||||
raise ValidationError(_("Enter valid JSON"))
|
||||
|
||||
return value
|
||||
|
||||
def to_python(self, value):
|
||||
"""The SubfieldBase metaclass calls pre_init instead of to_python, however to_python
|
||||
is still necessary for Django's deserializer"""
|
||||
return value
|
||||
|
||||
def get_db_prep_value(self, value, connection, prepared=False):
|
||||
"""Convert JSON object to a string"""
|
||||
if self.null and value is None:
|
||||
return None
|
||||
return json.dumps(value, **self.dump_kwargs)
|
||||
|
||||
def value_to_string(self, obj):
|
||||
value = self._get_val_from_obj(obj)
|
||||
return self.get_db_prep_value(value, None)
|
||||
|
||||
def value_from_object(self, obj):
|
||||
value = super(JSONFieldBase, self).value_from_object(obj)
|
||||
if self.null and value is None:
|
||||
return None
|
||||
return self.dumps_for_display(value)
|
||||
|
||||
def dumps_for_display(self, value):
|
||||
return json.dumps(value, **self.dump_kwargs)
|
||||
|
||||
def formfield(self, **kwargs):
|
||||
|
||||
if "form_class" not in kwargs:
|
||||
kwargs["form_class"] = self.form_class
|
||||
|
||||
field = super(JSONFieldBase, self).formfield(**kwargs)
|
||||
|
||||
if not field.help_text:
|
||||
field.help_text = "Enter valid JSON"
|
||||
|
||||
return field
|
||||
|
||||
def get_default(self):
|
||||
"""
|
||||
Returns the default value for this field.
|
||||
|
||||
The default implementation on models.Field calls force_unicode
|
||||
on the default, which means you can't set arbitrary Python
|
||||
objects as the default. To fix this, we just return the value
|
||||
without calling force_unicode on it. Note that if you set a
|
||||
callable as a default, the field will still call it. It will
|
||||
*not* try to pickle and encode it.
|
||||
|
||||
"""
|
||||
if self.has_default():
|
||||
if callable(self.default):
|
||||
return self.default()
|
||||
return copy.deepcopy(self.default)
|
||||
# If the field doesn't have a default, then we punt to models.Field.
|
||||
return super(JSONFieldBase, self).get_default()
|
||||
|
||||
def db_type(self, connection):
|
||||
if connection.vendor == 'postgresql' and connection.pg_version >= 90300:
|
||||
return 'json'
|
||||
else:
|
||||
return super(JSONFieldBase, self).db_type(connection)
|
||||
|
||||
class JSONField(JSONFieldBase, models.TextField):
|
||||
"""JSONField is a generic textfield that serializes/unserializes JSON objects"""
|
||||
form_class = JSONFormField
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(JSONField, self).__init__(*args, **kwargs)
|
||||
self.form_class.load_kwargs = self.load_kwargs
|
||||
|
||||
def dumps_for_display(self, value):
|
||||
kwargs = { "indent": 2 }
|
||||
kwargs.update(self.dump_kwargs)
|
||||
return json.dumps(value, **kwargs)
|
||||
|
||||
|
||||
class JSONCharField(JSONFieldBase, models.CharField):
|
||||
"""JSONCharField is a generic textfield that serializes/unserializes JSON objects,
|
||||
stored in the database like a CharField, which enables it to be used
|
||||
e.g. in unique keys"""
|
||||
form_class = JSONCharFormField
|
||||
|
||||
|
||||
try:
|
||||
from south.modelsinspector import add_introspection_rules
|
||||
add_introspection_rules([], ["^jsonfield\.fields\.(JSONField|JSONCharField)"])
|
||||
except ImportError:
|
||||
pass
|
|
@ -0,0 +1 @@
|
|||
# Django needs this to see it as a project
|
|
@ -0,0 +1,60 @@
|
|||
## This file was copied from django.db.models.fields.subclassing so that we could
|
||||
## change the Creator.__set__ behavior. Read the comment below for full details.
|
||||
|
||||
"""
|
||||
Convenience routines for creating non-trivial Field subclasses, as well as
|
||||
backwards compatibility utilities.
|
||||
|
||||
Add SubfieldBase as the __metaclass__ for your Field subclass, implement
|
||||
to_python() and the other necessary methods and everything will work seamlessly.
|
||||
"""
|
||||
|
||||
class SubfieldBase(type):
|
||||
"""
|
||||
A metaclass for custom Field subclasses. This ensures the model's attribute
|
||||
has the descriptor protocol attached to it.
|
||||
"""
|
||||
def __new__(cls, name, bases, attrs):
|
||||
new_class = super(SubfieldBase, cls).__new__(cls, name, bases, attrs)
|
||||
new_class.contribute_to_class = make_contrib(
|
||||
new_class, attrs.get('contribute_to_class')
|
||||
)
|
||||
return new_class
|
||||
|
||||
class Creator(object):
|
||||
"""
|
||||
A placeholder class that provides a way to set the attribute on the model.
|
||||
"""
|
||||
def __init__(self, field):
|
||||
self.field = field
|
||||
|
||||
def __get__(self, obj, type=None):
|
||||
if obj is None:
|
||||
raise AttributeError('Can only be accessed via an instance.')
|
||||
return obj.__dict__[self.field.name]
|
||||
|
||||
def __set__(self, obj, value):
|
||||
# Usually this would call to_python, but we've changed it to pre_init
|
||||
# so that we can tell which state we're in. By passing an obj,
|
||||
# we can definitively tell if a value has already been deserialized
|
||||
# More: https://github.com/bradjasper/django-jsonfield/issues/33
|
||||
obj.__dict__[self.field.name] = self.field.pre_init(value, obj)
|
||||
|
||||
|
||||
def make_contrib(superclass, func=None):
|
||||
"""
|
||||
Returns a suitable contribute_to_class() method for the Field subclass.
|
||||
|
||||
If 'func' is passed in, it is the existing contribute_to_class() method on
|
||||
the subclass and it is called before anything else. It is assumed in this
|
||||
case that the existing contribute_to_class() calls all the necessary
|
||||
superclass methods.
|
||||
"""
|
||||
def contribute_to_class(self, cls, name):
|
||||
if func:
|
||||
func(self, cls, name)
|
||||
else:
|
||||
super(superclass, self).contribute_to_class(cls, name)
|
||||
setattr(cls, self.name, Creator(self))
|
||||
|
||||
return contribute_to_class
|
|
@ -0,0 +1,239 @@
|
|||
from decimal import Decimal
|
||||
from django.core.serializers import deserialize, serialize
|
||||
from django.core.serializers.base import DeserializationError
|
||||
from django.db import models
|
||||
from django.test import TestCase
|
||||
from django.utils import simplejson as json
|
||||
|
||||
from .fields import JSONField, JSONCharField
|
||||
from django.forms.util import ValidationError
|
||||
|
||||
from collections import OrderedDict
|
||||
|
||||
class JsonModel(models.Model):
|
||||
json = JSONField()
|
||||
default_json = JSONField(default={"check":12})
|
||||
complex_default_json = JSONField(default=[{"checkcheck": 1212}])
|
||||
|
||||
class JsonCharModel(models.Model):
|
||||
json = JSONCharField(max_length=100)
|
||||
default_json = JSONCharField(max_length=100, default={"check":34})
|
||||
|
||||
class ComplexEncoder(json.JSONEncoder):
|
||||
def default(self, obj):
|
||||
if isinstance(obj, complex):
|
||||
return {
|
||||
'__complex__': True,
|
||||
'real': obj.real,
|
||||
'imag': obj.imag,
|
||||
}
|
||||
|
||||
return json.JSONEncoder.default(self, obj)
|
||||
|
||||
def as_complex(dct):
|
||||
if '__complex__' in dct:
|
||||
return complex(dct['real'], dct['imag'])
|
||||
return dct
|
||||
|
||||
class JSONModelCustomEncoders(models.Model):
|
||||
# A JSON field that can store complex numbers
|
||||
json = JSONField(
|
||||
dump_kwargs={'cls': ComplexEncoder, "indent": 4},
|
||||
load_kwargs={'object_hook': as_complex},
|
||||
)
|
||||
|
||||
class JSONFieldTest(TestCase):
|
||||
"""JSONField Wrapper Tests"""
|
||||
|
||||
json_model = JsonModel
|
||||
|
||||
def test_json_field_create(self):
|
||||
"""Test saving a JSON object in our JSONField"""
|
||||
json_obj = {
|
||||
"item_1": "this is a json blah",
|
||||
"blergh": "hey, hey, hey"}
|
||||
|
||||
obj = self.json_model.objects.create(json=json_obj)
|
||||
new_obj = self.json_model.objects.get(id=obj.id)
|
||||
|
||||
self.assertEqual(new_obj.json, json_obj)
|
||||
|
||||
def test_string_in_json_field(self):
|
||||
"""Test saving an ordinary Python string in our JSONField"""
|
||||
json_obj = 'blah blah'
|
||||
obj = self.json_model.objects.create(json=json_obj)
|
||||
new_obj = self.json_model.objects.get(id=obj.id)
|
||||
|
||||
self.assertEqual(new_obj.json, json_obj)
|
||||
|
||||
def test_float_in_json_field(self):
|
||||
"""Test saving a Python float in our JSONField"""
|
||||
json_obj = 1.23
|
||||
obj = self.json_model.objects.create(json=json_obj)
|
||||
new_obj = self.json_model.objects.get(id=obj.id)
|
||||
|
||||
self.assertEqual(new_obj.json, json_obj)
|
||||
|
||||
def test_int_in_json_field(self):
|
||||
"""Test saving a Python integer in our JSONField"""
|
||||
json_obj = 1234567
|
||||
obj = self.json_model.objects.create(json=json_obj)
|
||||
new_obj = self.json_model.objects.get(id=obj.id)
|
||||
|
||||
self.assertEqual(new_obj.json, json_obj)
|
||||
|
||||
def test_decimal_in_json_field(self):
|
||||
"""Test saving a Python Decimal in our JSONField"""
|
||||
json_obj = Decimal(12.34)
|
||||
obj = self.json_model.objects.create(json=json_obj)
|
||||
new_obj = self.json_model.objects.get(id=obj.id)
|
||||
|
||||
# here we must know to convert the returned string back to Decimal,
|
||||
# since json does not support that format
|
||||
self.assertEqual(Decimal(new_obj.json), json_obj)
|
||||
|
||||
def test_json_field_modify(self):
|
||||
"""Test modifying a JSON object in our JSONField"""
|
||||
json_obj_1 = {'a': 1, 'b': 2}
|
||||
json_obj_2 = {'a': 3, 'b': 4}
|
||||
|
||||
obj = self.json_model.objects.create(json=json_obj_1)
|
||||
self.assertEqual(obj.json, json_obj_1)
|
||||
obj.json = json_obj_2
|
||||
|
||||
self.assertEqual(obj.json, json_obj_2)
|
||||
obj.save()
|
||||
self.assertEqual(obj.json, json_obj_2)
|
||||
|
||||
self.assertTrue(obj)
|
||||
|
||||
def test_json_field_load(self):
|
||||
"""Test loading a JSON object from the DB"""
|
||||
json_obj_1 = {'a': 1, 'b': 2}
|
||||
obj = self.json_model.objects.create(json=json_obj_1)
|
||||
new_obj = self.json_model.objects.get(id=obj.id)
|
||||
|
||||
self.assertEqual(new_obj.json, json_obj_1)
|
||||
|
||||
def test_json_list(self):
|
||||
"""Test storing a JSON list"""
|
||||
json_obj = ["my", "list", "of", 1, "objs", {"hello": "there"}]
|
||||
|
||||
obj = self.json_model.objects.create(json=json_obj)
|
||||
new_obj = self.json_model.objects.get(id=obj.id)
|
||||
self.assertEqual(new_obj.json, json_obj)
|
||||
|
||||
def test_empty_objects(self):
|
||||
"""Test storing empty objects"""
|
||||
for json_obj in [{}, [], 0, '', False]:
|
||||
obj = self.json_model.objects.create(json=json_obj)
|
||||
new_obj = self.json_model.objects.get(id=obj.id)
|
||||
self.assertEqual(json_obj, obj.json)
|
||||
self.assertEqual(json_obj, new_obj.json)
|
||||
|
||||
def test_custom_encoder(self):
|
||||
"""Test encoder_cls and object_hook"""
|
||||
value = 1 + 3j # A complex number
|
||||
|
||||
obj = JSONModelCustomEncoders.objects.create(json=value)
|
||||
new_obj = JSONModelCustomEncoders.objects.get(pk=obj.pk)
|
||||
self.assertEqual(value, new_obj.json)
|
||||
|
||||
def test_django_serializers(self):
|
||||
"""Test serializing/deserializing jsonfield data"""
|
||||
for json_obj in [{}, [], 0, '', False, {'key': 'value', 'num': 42,
|
||||
'ary': list(range(5)),
|
||||
'dict': {'k': 'v'}}]:
|
||||
obj = self.json_model.objects.create(json=json_obj)
|
||||
new_obj = self.json_model.objects.get(id=obj.id)
|
||||
|
||||
queryset = self.json_model.objects.all()
|
||||
ser = serialize('json', queryset)
|
||||
for dobj in deserialize('json', ser):
|
||||
obj = dobj.object
|
||||
pulled = self.json_model.objects.get(id=obj.pk)
|
||||
self.assertEqual(obj.json, pulled.json)
|
||||
|
||||
def test_default_parameters(self):
|
||||
"""Test providing a default value to the model"""
|
||||
model = JsonModel()
|
||||
model.json = {"check": 12}
|
||||
self.assertEqual(model.json, {"check": 12})
|
||||
self.assertEqual(type(model.json), dict)
|
||||
|
||||
self.assertEqual(model.default_json, {"check": 12})
|
||||
self.assertEqual(type(model.default_json), dict)
|
||||
|
||||
def test_invalid_json(self):
|
||||
# invalid json data {] in the json and default_json fields
|
||||
ser = '[{"pk": 1, "model": "jsonfield.jsoncharmodel", ' \
|
||||
'"fields": {"json": "{]", "default_json": "{]"}}]'
|
||||
with self.assertRaises(DeserializationError) as cm:
|
||||
next(deserialize('json', ser))
|
||||
inner = cm.exception.args[0]
|
||||
self.assertTrue(isinstance(inner, ValidationError))
|
||||
self.assertEqual('Enter valid JSON', inner.messages[0])
|
||||
|
||||
def test_integer_in_string_in_json_field(self):
|
||||
"""Test saving the Python string '123' in our JSONField"""
|
||||
json_obj = '123'
|
||||
obj = self.json_model.objects.create(json=json_obj)
|
||||
new_obj = self.json_model.objects.get(id=obj.id)
|
||||
|
||||
self.assertEqual(new_obj.json, json_obj)
|
||||
|
||||
def test_boolean_in_string_in_json_field(self):
|
||||
"""Test saving the Python string 'true' in our JSONField"""
|
||||
json_obj = 'true'
|
||||
obj = self.json_model.objects.create(json=json_obj)
|
||||
new_obj = self.json_model.objects.get(id=obj.id)
|
||||
|
||||
self.assertEqual(new_obj.json, json_obj)
|
||||
|
||||
|
||||
def test_pass_by_reference_pollution(self):
|
||||
"""Make sure the default parameter is copied rather than passed by reference"""
|
||||
model = JsonModel()
|
||||
model.default_json["check"] = 144
|
||||
model.complex_default_json[0]["checkcheck"] = 144
|
||||
self.assertEqual(model.default_json["check"], 144)
|
||||
self.assertEqual(model.complex_default_json[0]["checkcheck"], 144)
|
||||
|
||||
# Make sure when we create a new model, it resets to the default value
|
||||
# and not to what we just set it to (it would be if it were passed by reference)
|
||||
model = JsonModel()
|
||||
self.assertEqual(model.default_json["check"], 12)
|
||||
self.assertEqual(model.complex_default_json[0]["checkcheck"], 1212)
|
||||
|
||||
class JSONCharFieldTest(JSONFieldTest):
|
||||
json_model = JsonCharModel
|
||||
|
||||
|
||||
class OrderedJsonModel(models.Model):
|
||||
json = JSONField(load_kwargs={'object_pairs_hook': OrderedDict})
|
||||
|
||||
|
||||
class OrderedDictSerializationTest(TestCase):
|
||||
ordered_dict = OrderedDict([
|
||||
('number', [1, 2, 3, 4]),
|
||||
('notes', True),
|
||||
])
|
||||
expected_key_order = ['number', 'notes']
|
||||
|
||||
def test_ordered_dict_differs_from_normal_dict(self):
|
||||
self.assertEqual(list(self.ordered_dict.keys()), self.expected_key_order)
|
||||
self.assertNotEqual(dict(self.ordered_dict).keys(), self.expected_key_order)
|
||||
|
||||
def test_default_behaviour_loses_sort_order(self):
|
||||
mod = JsonModel.objects.create(json=self.ordered_dict)
|
||||
self.assertEqual(list(mod.json.keys()), self.expected_key_order)
|
||||
mod_from_db = JsonModel.objects.get(id=mod.id)
|
||||
|
||||
# mod_from_db lost ordering information during json.loads()
|
||||
self.assertNotEqual(mod_from_db.json.keys(), self.expected_key_order)
|
||||
|
||||
def test_load_kwargs_hook_does_not_lose_sort_order(self):
|
||||
mod = OrderedJsonModel.objects.create(json=self.ordered_dict)
|
||||
self.assertEqual(list(mod.json.keys()), self.expected_key_order)
|
||||
mod_from_db = OrderedJsonModel.objects.get(id=mod.id)
|
||||
self.assertEqual(list(mod_from_db.json.keys()), self.expected_key_order)
|
Загрузка…
Ссылка в новой задаче