Merge branch 'master' into resultset-date-range

This commit is contained in:
Cameron Dawson 2014-04-14 13:23:34 -07:00
Родитель 95257c3040 ba761a027e
Коммит ae6640f3e0
16 изменённых файлов: 1047 добавлений и 81 удалений

Просмотреть файл

@ -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)
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(

1
vendor/jsonfield/__init__.py поставляемый Normal file
Просмотреть файл

@ -0,0 +1 @@
from .fields import JSONField, JSONCharField

167
vendor/jsonfield/fields.py поставляемый Normal file
Просмотреть файл

@ -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

1
vendor/jsonfield/models.py поставляемый Normal file
Просмотреть файл

@ -0,0 +1 @@
# Django needs this to see it as a project

60
vendor/jsonfield/subclassing.py поставляемый Normal file
Просмотреть файл

@ -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

239
vendor/jsonfield/tests.py поставляемый Normal file
Просмотреть файл

@ -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)