diff --git a/.env b/.env new file mode 100644 index 0000000..63e4fde --- /dev/null +++ b/.env @@ -0,0 +1 @@ +MANIFEST_URL='https://storage.googleapis.com/ccdb-db-files/manifest.json' diff --git a/.gitignore b/.gitignore index 8e9afea..26e40a8 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ venv +.env staticfiles data media diff --git a/ccdb/collections_ccdb/factories.py b/ccdb/collections_ccdb/factories.py new file mode 100644 index 0000000..736968e --- /dev/null +++ b/ccdb/collections_ccdb/factories.py @@ -0,0 +1,88 @@ +from datetime import date, time + +from factory import DjangoModelFactory, Sequence, SubFactory, LazyFunction +from factory.fuzzy import FuzzyText, FuzzyDate, FuzzyInteger +from factory.django import FileField + +from .models import CollectionType, CollectionMethod, Flaw, ADFGPermit, Collection, \ + DatasheetAttachment, CollectionTrap +from ..projects.factories import ProjectFactory +from ..locations.factories import StudyLocationFactory, StorageLocationFactory +from ..processing.factories import ProcessTypeFactory, ReagentFactory + + +class CollectionTypeFactory(DjangoModelFactory): + class Meta: + model = CollectionType + + name = Sequence(lambda n: 'collection_type{}'.format(n)) + code = Sequence(lambda n: 'ct{}'.format(n)) + sort_order = Sequence(lambda n: n) + + +class CollectionMethodFactory(DjangoModelFactory): + class Meta: + model = CollectionMethod + + name = Sequence(lambda n: 'collection_method{}'.format(n)) + code = Sequence(lambda n: 'cm{}'.format(n)) + collection_method_class = FuzzyText(length=50) + sort_order = Sequence(lambda n: n) + + +class FlawFactory(DjangoModelFactory): + class Meta: + model = Flaw + + name = Sequence(lambda n: 'flaw{}'.format(n)) + description = FuzzyText(length=255) + sort_order = Sequence(lambda n: n) + + +class ADFGPermitFactory(DjangoModelFactory): + class Meta: + model = ADFGPermit + + name = Sequence(lambda n: 'adfg_permit{}'.format(n)) + sort_order = Sequence(lambda n: n) + + +class CollectionFactory(DjangoModelFactory): + class Meta: + model = Collection + + project = SubFactory(ProjectFactory) + study_location = SubFactory(StudyLocationFactory) + collection_type = SubFactory(CollectionTypeFactory) + collection_method = SubFactory(CollectionMethodFactory) + number_of_traps = FuzzyInteger(0) + collection_start_date = FuzzyDate(date(2012, 1, 1)) + collection_start_time = None + collection_end_date = FuzzyDate(date(2015, 1, 1)) + collection_end_time = None + storage_location = SubFactory(StorageLocationFactory) + specimen_state = FuzzyText(length=50) + process_type = SubFactory(ProcessTypeFactory) + reagent = SubFactory(ReagentFactory) + adfg_permit = SubFactory(ADFGPermitFactory) + flaw = SubFactory(FlawFactory) + + +class DatasheetAttachmentFactory(DjangoModelFactory): + class Meta: + model = DatasheetAttachment + + collection = SubFactory(CollectionFactory) + datasheet = FileField() + + +class CollectionTrapFactory(DjangoModelFactory): + class Meta: + model = CollectionTrap + + collection = SubFactory(CollectionFactory) + number_of_traps = FuzzyInteger(0) + date_opened = FuzzyDate(date(2012, 1, 1)) + time_opened = LazyFunction(time) + date_closed = FuzzyDate(date(2015, 1, 1)) + time_closed = LazyFunction(time) diff --git a/ccdb/collections_ccdb/migrations/0005_DATA_initial.py b/ccdb/collections_ccdb/migrations/0005_DATA_initial.py new file mode 100644 index 0000000..417eb0b --- /dev/null +++ b/ccdb/collections_ccdb/migrations/0005_DATA_initial.py @@ -0,0 +1,107 @@ +from django.db import migrations +from django.forms import modelform_factory + +from ccdb.utils.data import get_data_sources + + +class Migration(migrations.Migration): + def migrate(apps, schema_editor): + sources = get_data_sources() + if not sources: + return + + c = sources['db0'] + + CollectionType = apps.get_model('collections_ccdb', 'CollectionType') + CollectionMethod = apps.get_model('collections_ccdb', 'CollectionMethod') + Flaw = apps.get_model('collections_ccdb', 'Flaw') + ADFGPermit = apps.get_model('collections_ccdb', 'ADFGPermit') + Collection = apps.get_model('collections_ccdb', 'Collection') + DatasheetAttachment = apps.get_model('collections_ccdb', 'DatasheetAttachment') + CollectionTrap = apps.get_model('collections_ccdb', 'CollectionTrap') + + for model in [CollectionTrap, Collection, Flaw, DatasheetAttachment, + CollectionMethod, CollectionType, ADFGPermit]: + model.objects.all().delete() + + Project = apps.get_model('projects', 'Project') + + CollectionTypeForm = modelform_factory(CollectionType, fields='__all__') + CollectionMethodForm = modelform_factory(CollectionMethod, fields='__all__') + ADFGPermitForm = modelform_factory(ADFGPermit, fields='__all__') + CollectionForm = modelform_factory(Collection, fields='__all__') + + for r in c.execute('SELECT * FROM tbl_lu_collection_types;'): + form = CollectionTypeForm(dict(name=r[1], code=r[2], + sort_order=int(r[3]) if r[3] else None)) + if form.is_valid(): + CollectionType.objects.create(id=r[0], **form.cleaned_data) + else: + print('collection type', r[0:], form.errors.as_data()) + + for r in c.execute('SELECT * FROM tbl_lu_collection_methods;'): + form = CollectionMethodForm(dict(name=r[1], code=r[2], + collection_method_class=r[3], + sort_order=int(r[4]) if r[4] else None)) + if form.is_valid(): + CollectionMethod.objects.create(id=r[0], **form.cleaned_data) + else: + print('collection method', r[0:], form.errors.as_data()) + + for i, r in enumerate(c.execute('SELECT DISTINCT ADFG_Permit FROM tbl_collections;')): + form = ADFGPermitForm(dict(name=r[0], sort_order=i)) + if form.is_valid(): + form.save() + else: + print('adfg permit', r[0:], form.errors.as_data()) + + for r in c.execute(''' + SELECT *, + collection_start_date AS "collection_start_date [dtdt]", + collection_start_time AS "collection_start_time [dtdt]", + collection_end_date AS "collection_end_date [dtdt]", + collection_end_time AS "collection_end_time [dtdt]" + FROM tbl_collections; + '''): + permit = None + if r[14] is not '': + permit = ADFGPermit.objects.get(name=r[14]).pk + form = CollectionForm(dict(project=r[0], study_location=r[2], + collection_type=r[3], collection_method=r[4], + number_of_traps=r[5], + collection_start_date=r[17], + collection_start_time=r[18].time() if r[18] else None, + collection_end_date=r[19], + collection_end_time=r[20].time() if r[20] else None, + storage_location=r[10], specimen_state=r[11], + process_type=r[12], reagent=r[13], + adfg_permit=permit)) + if form.is_valid(): + project = Project.objects.get(id=r[0]) + d = "{}_{}_{}_{}".format(project, form.cleaned_data['collection_end_date'], + form.cleaned_data['study_location'], + form.cleaned_data['collection_type']) + Collection.objects.create(id=r[1], display_name=d, **form.cleaned_data) + else: + print('collection', r[0:], form.errors.as_data()) + + def rollback(apps, schema_editor): + CollectionType = apps.get_model('collections_ccdb', 'CollectionType') + CollectionMethod = apps.get_model('collections_ccdb', 'CollectionMethod') + Flaw = apps.get_model('collections_ccdb', 'Flaw') + ADFGPermit = apps.get_model('collections_ccdb', 'ADFGPermit') + Collection = apps.get_model('collections_ccdb', 'Collection') + DatasheetAttachment = apps.get_model('collections_ccdb', 'DatasheetAttachment') + CollectionTrap = apps.get_model('collections_ccdb', 'CollectionTrap') + + for model in [CollectionTrap, Collection, Flaw, DatasheetAttachment, + CollectionMethod, CollectionType, ADFGPermit]: + model.objects.all().delete() + + dependencies = [ + ('collections_ccdb', '0004_collections_ordering'), + ] + + operations = [ + migrations.RunPython(migrate, rollback), + ] diff --git a/ccdb/collections_ccdb/migrations/0006_set_labels.py b/ccdb/collections_ccdb/migrations/0006_set_labels.py new file mode 100644 index 0000000..a94e081 --- /dev/null +++ b/ccdb/collections_ccdb/migrations/0006_set_labels.py @@ -0,0 +1,69 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('collections_ccdb', '0005_DATA_initial'), + ] + + operations = [ + migrations.AlterField( + model_name='collection', + name='adfg_permit', + field=models.ForeignKey(null=True, related_name='collections', to='collections_ccdb.ADFGPermit', blank=True), + ), + migrations.AlterField( + model_name='collection', + name='collection_method', + field=models.ForeignKey(to='collections_ccdb.CollectionMethod', related_name='collections'), + ), + migrations.AlterField( + model_name='collection', + name='collection_type', + field=models.ForeignKey(to='collections_ccdb.CollectionType', related_name='collections'), + ), + migrations.AlterField( + model_name='collection', + name='flaw', + field=models.ForeignKey(null=True, related_name='collections', to='collections_ccdb.Flaw', blank=True), + ), + migrations.AlterField( + model_name='collection', + name='process_type', + field=models.ForeignKey(null=True, related_name='collections', to='processing.ProcessType', blank=True), + ), + migrations.AlterField( + model_name='collection', + name='project', + field=models.ForeignKey(to='projects.Project', related_name='collections'), + ), + migrations.AlterField( + model_name='collection', + name='reagent', + field=models.ForeignKey(null=True, related_name='collections', to='processing.Reagent', blank=True), + ), + migrations.AlterField( + model_name='collection', + name='storage_location', + field=models.ForeignKey(null=True, related_name='collections', to='locations.StorageLocation', blank=True), + ), + migrations.AlterField( + model_name='collection', + name='study_location', + field=models.ForeignKey(to='locations.StudyLocation', related_name='collections'), + ), + migrations.AlterField( + model_name='collectiontrap', + name='collection', + field=models.ForeignKey(to='collections_ccdb.Collection', related_name='traps'), + ), + migrations.AlterField( + model_name='datasheetattachment', + name='collection', + field=models.ForeignKey(to='collections_ccdb.Collection', related_name='datasheets'), + ), + ] diff --git a/ccdb/collections_ccdb/models.py b/ccdb/collections_ccdb/models.py index df61e55..d1bbef9 100644 --- a/ccdb/collections_ccdb/models.py +++ b/ccdb/collections_ccdb/models.py @@ -59,26 +59,31 @@ class ADFGPermit(models.Model): class Collection(models.Model): - project = models.ForeignKey('projects.Project') - study_location = models.ForeignKey('locations.StudyLocation') - collection_type = models.ForeignKey(CollectionType) - collection_method = models.ForeignKey(CollectionMethod) + project = models.ForeignKey('projects.Project', related_name='collections') + study_location = models.ForeignKey('locations.StudyLocation', + related_name='collections') + collection_type = models.ForeignKey(CollectionType, related_name='collections') + collection_method = models.ForeignKey(CollectionMethod, related_name='collections') number_of_traps = models.IntegerField(blank=True, null=True) collection_start_date = models.DateField(blank=True, null=True) collection_start_time = models.TimeField(blank=True, null=True) collection_end_date = models.DateField(blank=True, null=True) collection_end_time = models.TimeField(blank=True, null=True) - storage_location = models.ForeignKey('locations.StorageLocation', blank=True, null=True) + storage_location = models.ForeignKey('locations.StorageLocation', blank=True, + null=True, related_name='collections') specimen_state = models.CharField(max_length=50, blank=True) - process_type = models.ForeignKey('processing.ProcessType', blank=True, null=True) - reagent = models.ForeignKey('processing.Reagent', blank=True, null=True) - adfg_permit = models.ForeignKey(ADFGPermit, blank=True, null=True) - flaw = models.ForeignKey(Flaw, blank=True, null=True) + process_type = models.ForeignKey('processing.ProcessType', blank=True, + null=True, related_name='collections') + reagent = models.ForeignKey('processing.Reagent', blank=True, null=True, + related_name='collections') + adfg_permit = models.ForeignKey(ADFGPermit, blank=True, null=True, + related_name='collections') + flaw = models.ForeignKey(Flaw, blank=True, null=True, related_name='collections') display_name = models.CharField(max_length=255, editable=False) def save(self, *args, **kwargs): self.display_name = "{}_{}_{}_{}".format(self.project, - self.collection_end_date.date(), self.study_location, + self.collection_end_date, self.study_location, self.collection_type) super(Collection, self).save(*args, **kwargs) @@ -92,13 +97,13 @@ class Collection(models.Model): class DatasheetAttachment(models.Model): - collection = models.ForeignKey(Collection) + collection = models.ForeignKey(Collection, related_name='datasheets') datasheet = models.FileField("Datasheet", upload_to='collections/datasheets/%Y/%m/%d') class CollectionTrap(models.Model): - collection = models.ForeignKey(Collection) + collection = models.ForeignKey(Collection, related_name='traps') number_of_traps = models.IntegerField() date_opened = models.DateField() time_opened = models.TimeField() @@ -110,4 +115,5 @@ class CollectionTrap(models.Model): self.collection, self.number_of_traps, self.date_opened, self.date_closed) class Meta: - unique_together = ('collection', 'date_opened', 'time_opened', 'date_closed', 'time_closed') + unique_together = ('collection', 'date_opened', 'time_opened', + 'date_closed', 'time_closed') diff --git a/ccdb/collections_ccdb/tests/__init__.py b/ccdb/collections_ccdb/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/ccdb/collections_ccdb/tests/test_models.py b/ccdb/collections_ccdb/tests/test_models.py new file mode 100644 index 0000000..ec1c4b1 --- /dev/null +++ b/ccdb/collections_ccdb/tests/test_models.py @@ -0,0 +1,92 @@ +from django.test import TestCase +from django.db import IntegrityError, transaction + +from ..models import CollectionType, CollectionMethod, Flaw, ADFGPermit, \ + Collection, DatasheetAttachment, CollectionTrap +from ..factories import CollectionTypeFactory, CollectionMethodFactory, \ + FlawFactory, ADFGPermitFactory, CollectionFactory, DatasheetAttachmentFactory, \ + CollectionTrapFactory + + +class CollectionTypeTestCase(TestCase): + def test_creation(self): + c = CollectionTypeFactory() + self.assertTrue(isinstance(c, CollectionType)) + self.assertEqual(c.__str__(), c.name) + + def test_uniqueness(self): + c1 = CollectionTypeFactory() + with transaction.atomic(), self.assertRaises(IntegrityError): + CollectionTypeFactory(name=c1.name, code=c1.code) + c3 = CollectionTypeFactory() + self.assertTrue(isinstance(c3, CollectionType)) + + +class CollectionMethodTestCase(TestCase): + def test_creation(self): + c = CollectionMethodFactory() + self.assertTrue(isinstance(c, CollectionMethod)) + self.assertEqual(c.__str__(), c.name) + + def test_uniqueness(self): + c1 = CollectionMethodFactory() + with transaction.atomic(), self.assertRaises(IntegrityError): + CollectionMethodFactory(name=c1.name, code=c1.code) + c3 = CollectionMethodFactory() + self.assertTrue(isinstance(c3, CollectionMethod)) + + +class FlawTestCase(TestCase): + def test_creation(self): + f = FlawFactory() + self.assertTrue(isinstance(f, Flaw)) + self.assertEqual(f.__str__(), f.name) + + +class ADFGPermitTestCase(TestCase): + def test_creation(self): + a = ADFGPermitFactory() + self.assertTrue(isinstance(a, ADFGPermit)) + self.assertEqual(a.__str__(), a.name) + + +class CollectionTestCase(TestCase): + def test_creation(self): + c = CollectionFactory() + self.assertTrue(isinstance(c, Collection)) + self.assertEqual(c.__str__(), c.display_name) + + def test_uniqueness(self): + c1 = CollectionFactory() + with transaction.atomic(), self.assertRaises(IntegrityError): + CollectionFactory(project=c1.project, study_location=c1.study_location, + collection_type=c1.collection_type, + collection_start_date=c1.collection_start_date, + collection_end_date=c1.collection_end_date, + collection_method=c1.collection_method) + c3 = CollectionFactory() + self.assertTrue(isinstance(c3, Collection)) + + +class DatasheetAttachmentTestCase(TestCase): + def test_creation(self): + d = DatasheetAttachmentFactory() + self.assertTrue(isinstance(d, DatasheetAttachment)) + + +class CollectionTrapTestCase(TestCase): + def test_creation(self): + c = CollectionTrapFactory() + self.assertTrue(isinstance(c, CollectionTrap)) + name = "{} # Traps: {} {} {}".format(c.collection, c.number_of_traps, + c.date_opened, c.date_closed) + self.assertEqual(c.__str__(), name) + + def test_uniqueness(self): + c1 = CollectionTrapFactory() + with transaction.atomic(), self.assertRaises(IntegrityError): + CollectionTrapFactory(collection=c1.collection, date_opened=c1.date_opened, + time_opened=c1.time_opened, date_closed=c1.date_closed, + time_closed=c1.time_closed) + c3 = CollectionTrapFactory() + self.assertTrue(isinstance(c3, CollectionTrap)) diff --git a/ccdb/experiments/factories.py b/ccdb/experiments/factories.py new file mode 100644 index 0000000..cdb8cee --- /dev/null +++ b/ccdb/experiments/factories.py @@ -0,0 +1,98 @@ +from datetime import date, time + +from factory import DjangoModelFactory, Sequence, SubFactory, \ + LazyFunction, post_generation +from factory.fuzzy import FuzzyText, FuzzyDate, FuzzyInteger, FuzzyFloat +from factory.django import FileField + +from .models import Flaw, Experiment, ProtocolAttachment, TreatmentType, \ + Treatment, TreatmentReplicate, AliveDeadCount +from ..misc.factories import ContainerFactory +from ..locations.factories import StudyLocationFactory +from ..species.factories import SpeciesFactory + + +class FlawFactory(DjangoModelFactory): + class Meta: + model = Flaw + + name = Sequence(lambda n: 'flaw{}'.format(n)) + description = FuzzyText(length=255) + sort_order = Sequence(lambda n: n) + + +class ExperimentFactory(DjangoModelFactory): + class Meta: + model = Experiment + + name = Sequence(lambda n: 'experiment{}'.format(n)) + code = Sequence(lambda n: 'e{}'.format(n)) + description = FuzzyText(length=255) + flaw = SubFactory(FlawFactory) + sort_order = Sequence(lambda n: n) + + @post_generation + def collections(self, create, extracted, **kwargs): + if not create: + return + if extracted: + for collection in extracted: + self.groups.add(collection) + + +class ProtocolAttachmentFactory(DjangoModelFactory): + class Meta: + model = ProtocolAttachment + + experiment = SubFactory(ExperimentFactory) + protocol = FileField() + + +class TreatmentTypeFactory(DjangoModelFactory): + class Meta: + model = TreatmentType + + experiment = SubFactory(ExperimentFactory) + name = Sequence(lambda n: 'treatment_type{}'.format(n)) + code = Sequence(lambda n: 'tt{}'.format(n)) + treatment_type = FuzzyText(length=50) + placement = FuzzyText(length=25) + description = FuzzyText(length=255) + sort_order = Sequence(lambda n: n) + + +class TreatmentFactory(DjangoModelFactory): + class Meta: + model = Treatment + + treatment_type = SubFactory(TreatmentTypeFactory) + container = SubFactory(ContainerFactory) + study_location = SubFactory(StudyLocationFactory) + species = SubFactory(SpeciesFactory) + sex = FuzzyText(length=25) + flaw = SubFactory(FlawFactory) + + +class TreatmentReplicateFactory(DjangoModelFactory): + class Meta: + model = TreatmentReplicate + + treatment = SubFactory(TreatmentFactory) + name = Sequence(lambda n: 'treatment_replicate{}'.format(n)) + setup_date = FuzzyDate(date(2012, 1, 1)) + setup_time = LazyFunction(time) + setup_sample_size = FuzzyInteger(0) + mass_g = FuzzyFloat(0.0) + flaw = SubFactory(FlawFactory) + + +class AliveDeadCountFactory(DjangoModelFactory): + class Meta: + model = AliveDeadCount + + treatment_replicate = SubFactory(TreatmentReplicateFactory) + status_date = FuzzyDate(date(2012, 1, 1)) + status_time = LazyFunction(time) + count_alive = FuzzyInteger(0) + count_dead = FuzzyInteger(0) + flaw = SubFactory(FlawFactory) diff --git a/ccdb/experiments/migrations/0009_DATA_initial.py b/ccdb/experiments/migrations/0009_DATA_initial.py new file mode 100644 index 0000000..8fc3ff8 --- /dev/null +++ b/ccdb/experiments/migrations/0009_DATA_initial.py @@ -0,0 +1,113 @@ +from django.db import migrations +from django.forms import modelform_factory + +from ccdb.utils.data import get_data_sources + + +class Migration(migrations.Migration): + def migrate(apps, schema_editor): + sources = get_data_sources() + if not sources: + return + + c = sources['db0'] + + Flaw = apps.get_model('experiments', 'Flaw') + Experiment = apps.get_model('experiments', 'Experiment') + TreatmentType = apps.get_model('experiments', 'TreatmentType') + Treatment = apps.get_model('experiments', 'Treatment') + TreatmentReplicate = apps.get_model('experiments', 'TreatmentReplicate') + AliveDeadCount = apps.get_model('experiments', 'AliveDeadCount') + + for model in [AliveDeadCount, TreatmentReplicate, Treatment, TreatmentType, + Experiment, Flaw]: + model.objects.all().delete() + + ExperimentForm = modelform_factory(Experiment, exclude=('collections',)) + TreatmentTypeForm = modelform_factory(TreatmentType, fields='__all__') + TreatmentForm = modelform_factory(Treatment, fields='__all__') + TreatmentReplicateForm = modelform_factory(TreatmentReplicate, fields='__all__') + AliveDeadCountForm = modelform_factory(AliveDeadCount, fields='__all__') + + for r in c.execute('SELECT * FROM tbl_lu_experiments;'): + form = ExperimentForm(dict(name=r[1], code=r[2], description=r[3], + sort_order=int(r[4]) if r[4] else None)) + if form.is_valid(): + Experiment.objects.create(id=r[0], **form.cleaned_data) + else: + print('experiment', r[0:], form.errors.as_data()) + + for r in c.execute('SELECT * FROM tbl_lu_treatment_types;'): + form = TreatmentTypeForm(dict(experiment=r[0], name=r[2], code=r[3], + treatment_type=r[4], placement=r[5], + description=r[6])) + if form.is_valid(): + TreatmentType.objects.create(id=r[1], **form.cleaned_data) + else: + print('treatment type', r[0:], form.errors.as_data()) + + for r in c.execute('SELECT * FROM tbl_treatments;'): + form = TreatmentForm(dict(treatment_type=r[1], container=r[2], + study_location=r[3], species=r[4], sex=r[5])) + if form.is_valid(): + Treatment.objects.create(id=r[0], **form.cleaned_data) + else: + print('treatment', r[0:], form.errors.as_data()) + + for r in c.execute(''' + SELECT *, setup_date AS "setup_date [dtdt]" + FROM tbl_treatment_replicates tr + LEFT OUTER JOIN tbl_lu_record_flaws f ON f.flawid=tr.flawid; + '''): + flaw = None + if r[7]: + flaw = Flaw.objects.create(name=r[10]).pk + form = TreatmentReplicateForm(dict(treatment=r[0], name=r[2], + setup_date=r[13], + setup_sample_size=r[5], mass_g=r[6], + flaw=flaw)) + if form.is_valid(): + TreatmentReplicate.objects.create(id=r[1], **form.cleaned_data) + else: + print('treatment replicate', r[0:], form.errors.as_data()) + + for r in c.execute(''' + SELECT *, + status_date AS "status_date [dtdt]", + status_time AS "status_time [dtdt]" + FROM tbl_alive_dead_counts adc + LEFT OUTER JOIN tbl_lu_record_flaws f ON f.flawid=adc.flawid; + '''): + flaw = None + if r[6]: + flaw = Flaw.objects.create(name=r[9]).pk + form = AliveDeadCountForm(dict(treatment_replicate=r[0], + status_date=r[12], + status_time=r[13].time() if r[13] else None, + count_alive=r[4], count_dead=r[5], + flaw=flaw)) + if form.is_valid(): + AliveDeadCount.objects.create(id=r[1], **form.cleaned_data) + else: + print('alive-dead count', r[0:], form.errors.as_data()) + + def rollback(apps, schema_editor): + Flaw = apps.get_model('experiments', 'Flaw') + Experiment = apps.get_model('experiments', 'Experiment') + TreatmentType = apps.get_model('experiments', 'TreatmentType') + Treatment = apps.get_model('experiments', 'Treatment') + TreatmentReplicate = apps.get_model('experiments', 'TreatmentReplicate') + AliveDeadCount = apps.get_model('experiments', 'AliveDeadCount') + + for model in [AliveDeadCount, TreatmentReplicate, Treatment, TreatmentType, + Experiment, Flaw]: + model.objects.all().delete() + + dependencies = [ + ('experiments', '0008_treatment_display_name'), + ('collections_ccdb', '0005_DATA_initial'), + ] + + operations = [ + migrations.RunPython(migrate, rollback), + ] diff --git a/ccdb/experiments/migrations/0010_DATA_collections_experiments.py b/ccdb/experiments/migrations/0010_DATA_collections_experiments.py new file mode 100644 index 0000000..4118eb5 --- /dev/null +++ b/ccdb/experiments/migrations/0010_DATA_collections_experiments.py @@ -0,0 +1,38 @@ +from django.db import migrations + +from ccdb.utils.data import get_data_sources + + +class Migration(migrations.Migration): + def migrate(apps, schema_editor): + sources = get_data_sources() + if not sources: + return + + c = sources['db0'] + + Collection = apps.get_model('collections_ccdb', 'Collection') + Experiment = apps.get_model('experiments', 'Experiment') + + for experiment in Experiment.objects.all(): + experiment.collections.all().delete() + + for r in c.execute('SELECT * FROM tbl_hash_collection_experiments;'): + c = Collection.objects.get(id=r[0]) + e = Experiment.objects.get(id=r[1]) + e.collections.add(c) + e.save() + + def rollback(apps, schema_editor): + Experiment = apps.get_model('experiments', 'Experiment') + + for experiment in Experiment.objects.all(): + experiment.collections.all().delete() + + dependencies = [ + ('experiments', '0009_DATA_initial'), + ] + + operations = [ + migrations.RunPython(migrate, rollback), + ] diff --git a/ccdb/experiments/migrations/0011_set_labels.py b/ccdb/experiments/migrations/0011_set_labels.py new file mode 100644 index 0000000..9705a6f --- /dev/null +++ b/ccdb/experiments/migrations/0011_set_labels.py @@ -0,0 +1,74 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('experiments', '0010_DATA_collections_experiments'), + ] + + operations = [ + migrations.AlterField( + model_name='alivedeadcount', + name='flaw', + field=models.ForeignKey(null=True, to='experiments.Flaw', related_name='alive_dead_counts', blank=True), + ), + migrations.AlterField( + model_name='alivedeadcount', + name='treatment_replicate', + field=models.ForeignKey(to='experiments.TreatmentReplicate', related_name='alive_dead_counts'), + ), + migrations.AlterField( + model_name='experiment', + name='flaw', + field=models.ForeignKey(null=True, to='experiments.Flaw', related_name='experiments', blank=True), + ), + migrations.AlterField( + model_name='protocolattachment', + name='experiment', + field=models.ForeignKey(to='experiments.Experiment', related_name='protocols'), + ), + migrations.AlterField( + model_name='treatment', + name='container', + field=models.ForeignKey(null=True, to='misc.Container', related_name='treatments', blank=True), + ), + migrations.AlterField( + model_name='treatment', + name='flaw', + field=models.ForeignKey(null=True, to='experiments.Flaw', related_name='treatments', blank=True), + ), + migrations.AlterField( + model_name='treatment', + name='species', + field=models.ForeignKey(to='species.Species', related_name='treatments'), + ), + migrations.AlterField( + model_name='treatment', + name='study_location', + field=models.ForeignKey(to='locations.StudyLocation', related_name='treatments'), + ), + migrations.AlterField( + model_name='treatment', + name='treatment_type', + field=models.ForeignKey(to='experiments.TreatmentType', related_name='treatments'), + ), + migrations.AlterField( + model_name='treatmentreplicate', + name='flaw', + field=models.ForeignKey(null=True, to='experiments.Flaw', related_name='treatment_replicates', blank=True), + ), + migrations.AlterField( + model_name='treatmentreplicate', + name='treatment', + field=models.ForeignKey(to='experiments.Treatment', related_name='treatment_replicates'), + ), + migrations.AlterField( + model_name='treatmenttype', + name='experiment', + field=models.ForeignKey(null=True, to='experiments.Experiment', related_name='treatment_types', blank=True), + ), + ] diff --git a/ccdb/experiments/models.py b/ccdb/experiments/models.py index e2934ee..9f68728 100644 --- a/ccdb/experiments/models.py +++ b/ccdb/experiments/models.py @@ -20,7 +20,7 @@ class Experiment(models.Model): name = models.CharField(max_length=150) code = models.CharField(max_length=10, blank=True) description = models.CharField(max_length=255, blank=True) - flaw = models.ForeignKey(Flaw, blank=True, null=True) + flaw = models.ForeignKey(Flaw, blank=True, null=True, related_name='experiments') sort_order = models.IntegerField(blank=True, null=True) slug = AutoSlugField(populate_from='name') collections = models.ManyToManyField('collections_ccdb.Collection') @@ -34,7 +34,7 @@ class Experiment(models.Model): class ProtocolAttachment(models.Model): - experiment = models.ForeignKey(Experiment) + experiment = models.ForeignKey(Experiment, related_name='protocols') protocol = models.FileField(upload_to='experiments/protocols/%Y/%m/%d') def __str__(self): @@ -42,7 +42,8 @@ class ProtocolAttachment(models.Model): class TreatmentType(models.Model): - experiment = models.ForeignKey(Experiment, blank=True, null=True) + experiment = models.ForeignKey(Experiment, blank=True, null=True, + related_name='treatment_types') name = models.CharField(max_length=200) code = models.CharField(max_length=25, blank=True) treatment_type = models.CharField(max_length=50, blank=True) @@ -53,7 +54,7 @@ class TreatmentType(models.Model): def __str__(self): return "{} {} {} {}".format(self.experiment, self.name, - self.treatment_type, self.placement) + self.treatment_type, self.placement) class Meta: unique_together = ('experiment', 'name') @@ -61,17 +62,20 @@ class TreatmentType(models.Model): class Treatment(models.Model): - treatment_type = models.ForeignKey(TreatmentType) - container = models.ForeignKey('misc.Container', blank=True, null=True) - study_location = models.ForeignKey('locations.StudyLocation') - species = models.ForeignKey('species.Species') + treatment_type = models.ForeignKey(TreatmentType, related_name='treatments') + container = models.ForeignKey('misc.Container', blank=True, null=True, + related_name='treatments') + study_location = models.ForeignKey('locations.StudyLocation', + related_name='treatments') + species = models.ForeignKey('species.Species', related_name='treatments') sex = models.CharField(max_length=25) - flaw = models.ForeignKey(Flaw, blank=True, null=True) + flaw = models.ForeignKey(Flaw, blank=True, null=True, related_name='treatments') display_name = models.CharField(max_length=255, editable=False) def save(self, *args, **kwargs): self.display_name = "{}_{}_{}_{}".format(self.treatment_type, - self.study_location, self.species, self.sex) + self.study_location, self.species, + self.sex) super(Treatment, self).save(*args, **kwargs) def __str__(self): @@ -79,19 +83,19 @@ class Treatment(models.Model): class TreatmentReplicate(models.Model): - treatment = models.ForeignKey(Treatment) + treatment = models.ForeignKey(Treatment, related_name='treatment_replicates') name = models.CharField(max_length=50) setup_date = models.DateField(blank=True, null=True) setup_time = models.TimeField(blank=True, null=True) setup_sample_size = models.IntegerField(blank=True, null=True) mass_g = models.FloatField(blank=True, null=True) - flaw = models.ForeignKey(Flaw, blank=True, null=True) + flaw = models.ForeignKey(Flaw, blank=True, null=True, related_name='treatment_replicates') display_name = models.CharField(max_length=255, editable=False) def save(self, *args, **kwargs): self.display_name = "{}_{}_{}_{}".format(self.treatment, - self.setup_date.date(), self.name, - self.setup_sample_size) + self.setup_date, self.name, + self.setup_sample_size) super(TreatmentReplicate, self).save(*args, **kwargs) def __str__(self): @@ -102,12 +106,14 @@ class TreatmentReplicate(models.Model): class AliveDeadCount(models.Model): - treatment_replicate = models.ForeignKey(TreatmentReplicate) + treatment_replicate = models.ForeignKey(TreatmentReplicate, + related_name='alive_dead_counts') status_date = models.DateField() status_time = models.TimeField(blank=True, null=True) count_alive = models.IntegerField(blank=True, null=True) count_dead = models.IntegerField(blank=True, null=True) - flaw = models.ForeignKey(Flaw, blank=True, null=True) + flaw = models.ForeignKey(Flaw, blank=True, null=True, + related_name='alive_dead_counts') def __str__(self): return "{}".format(self.status_date) diff --git a/ccdb/experiments/tests/__init__.py b/ccdb/experiments/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/ccdb/experiments/tests/test_models.py b/ccdb/experiments/tests/test_models.py new file mode 100644 index 0000000..64c1cb2 --- /dev/null +++ b/ccdb/experiments/tests/test_models.py @@ -0,0 +1,87 @@ +from django.test import TestCase +from django.db import IntegrityError, transaction + +from ..models import Flaw, Experiment, ProtocolAttachment, TreatmentType, \ + Treatment, TreatmentReplicate, AliveDeadCount +from ..factories import FlawFactory, ExperimentFactory, ProtocolAttachmentFactory, \ + TreatmentTypeFactory, TreatmentFactory, TreatmentReplicateFactory, \ + AliveDeadCountFactory + + +class FlawTestCase(TestCase): + def test_creation(self): + f = FlawFactory() + self.assertTrue(isinstance(f, Flaw)) + self.assertEqual(f.__str__(), f.name) + + +class ExperimentTestCase(TestCase): + def test_creation(self): + e = ExperimentFactory() + self.assertTrue(isinstance(e, Experiment)) + self.assertEqual(e.__str__(), e.name) + + def test_uniqueness(self): + e1 = ExperimentFactory() + with transaction.atomic(), self.assertRaises(IntegrityError): + ExperimentFactory(name=e1.name, code=e1.code) + e3 = ExperimentFactory() + self.assertTrue(isinstance(e3, Experiment)) + + +class ProtocolAttachmentTestCase(TestCase): + def test_creation(self): + p = ProtocolAttachmentFactory() + self.assertTrue(isinstance(p, ProtocolAttachment)) + self.assertEqual(p.__str__(), p.protocol) + + +class TreatmentTypeTestCase(TestCase): + def test_creation(self): + t = TreatmentTypeFactory() + self.assertTrue(isinstance(t, TreatmentType)) + label = "{} {} {} {}".format(t.experiment, t.name, t.treatment_type, + t.placement) + self.assertEqual(t.__str__(), label) + + def test_uniqueness(self): + t1 = TreatmentTypeFactory() + with transaction.atomic(), self.assertRaises(IntegrityError): + TreatmentTypeFactory(name=t1.name, experiment=t1.experiment) + t3 = TreatmentTypeFactory() + self.assertTrue(isinstance(t3, TreatmentType)) + + +class TreatmentTestCase(TestCase): + def test_creation(self): + t = TreatmentFactory() + self.assertTrue(isinstance(t, Treatment)) + label = "{}_{}_{}_{}".format(t.treatment_type, t.study_location, + t.species, t.sex) + self.assertEqual(t.__str__(), label) + + +class TreatmentReplicateTestCase(TestCase): + def test_creation(self): + t = TreatmentReplicateFactory() + self.assertTrue(isinstance(t, TreatmentReplicate)) + label = "{}_{}_{}_{}".format(t.treatment, t.setup_date, + t.name, t.setup_sample_size) + self.assertEqual(t.__str__(), label) + + def test_uniqueness(self): + t1 = TreatmentReplicateFactory() + with transaction.atomic(), self.assertRaises(IntegrityError): + TreatmentReplicateFactory(treatment=t1.treatment, name=t1.name, + setup_date=t1.setup_date, + setup_time=t1.setup_time) + t3 = TreatmentReplicateFactory() + self.assertTrue(isinstance(t3, TreatmentReplicate)) + + +class AliveDeadCountTestCase(TestCase): + def test_creation(self): + a = AliveDeadCountFactory() + self.assertTrue(isinstance(a, AliveDeadCount)) + label = "{}".format(a.status_date) + self.assertEqual(a.__str__(), label) diff --git a/ccdb/locations/factories.py b/ccdb/locations/factories.py new file mode 100644 index 0000000..82edd29 --- /dev/null +++ b/ccdb/locations/factories.py @@ -0,0 +1,63 @@ +from factory import DjangoModelFactory, Sequence, SubFactory +from factory.fuzzy import FuzzyText, FuzzyChoice, FuzzyInteger + +from .models import Region, Site, MunicipalLocation, StudyLocation, StorageLocation + + +class RegionFactory(DjangoModelFactory): + class Meta: + model = Region + + name = Sequence(lambda n: 'region{}'.format(n)) + code = Sequence(lambda n: 'r{}'.format(n)) + sort_order = Sequence(lambda n: n) + + +class SiteFactory(DjangoModelFactory): + class Meta: + model = Site + + region = SubFactory(RegionFactory) + name = Sequence(lambda n: 'site{}'.format(n)) + code = Sequence(lambda n: 's{}'.format(n)) + description = FuzzyText(length=100) + sort_order = Sequence(lambda n: n) + + +class MunicipalLocationFactory(DjangoModelFactory): + class Meta: + model = MunicipalLocation + + name = Sequence(lambda n: 'municipal_location{}'.format(n)) + code = Sequence(lambda n: 'ml{}'.format(n)) + municipal_location_type = FuzzyText(length=50) + description = FuzzyText(length=255) + sort_order = Sequence(lambda n: n) + + +class StudyLocationFactory(DjangoModelFactory): + class Meta: + model = StudyLocation + + site = SubFactory(SiteFactory) + name = Sequence(lambda n: 'study_location{}'.format(n)) + code = Sequence(lambda n: 'sl{}'.format(n)) + treatment_type = FuzzyText(length=100) + municipal_location = SubFactory(MunicipalLocationFactory) + collecting_location = FuzzyChoice([True, False]) + description = FuzzyText(length=255) + sort_order = Sequence(lambda n: n) + + +class StorageLocationFactory(DjangoModelFactory): + class Meta: + model = StorageLocation + + code = Sequence(lambda n: 'sl{}'.format(n)) + facility = FuzzyText(length=100) + building = FuzzyText(length=100) + room = FuzzyText(length=50) + freezer = FuzzyText(length=50) + temp_c = FuzzyInteger(-100, 100) + description = FuzzyText(length=255) + sort_order = Sequence(lambda n: n) diff --git a/ccdb/locations/migrations/0004_DATA_initial.py b/ccdb/locations/migrations/0004_DATA_initial.py new file mode 100644 index 0000000..f586a15 --- /dev/null +++ b/ccdb/locations/migrations/0004_DATA_initial.py @@ -0,0 +1,102 @@ +from django.db import migrations +from django.forms import modelform_factory + +from ccdb.utils.data import get_data_sources + + +class Migration(migrations.Migration): + def migrate(apps, schema_editor): + sources = get_data_sources() + if not sources: + return + + c = sources['db0'] + + Region = apps.get_model('locations', 'Region') + Site = apps.get_model('locations', 'Site') + MunicipalLocation = apps.get_model('locations', 'MunicipalLocation') + StudyLocation = apps.get_model('locations', 'StudyLocation') + StorageLocation = apps.get_model('locations', 'StorageLocation') + + for model in [StorageLocation, StudyLocation, MunicipalLocation, Site, Region]: + model.objects.all().delete() + + RegionForm = modelform_factory(Region, fields='__all__') + SiteForm = modelform_factory(Site, fields='__all__') + MunicipalLocationForm = modelform_factory(MunicipalLocation, fields='__all__') + StudyLocationForm = modelform_factory(StudyLocation, fields='__all__') + StorageLocationForm = modelform_factory(StorageLocation, fields='__all__') + + for r in c.execute('SELECT * FROM tbl_lu_regions;'): + form = RegionForm(dict(name=r[1], code=r[2], + sort_order=int(r[3]) if r[3] else None)) + if form.is_valid(): + Region.objects.create(id=r[0], **form.cleaned_data) + else: + print('region', r[0:], form.errors.as_data()) + + for r in c.execute('SELECT * FROM tbl_lu_sites;'): + form = SiteForm(dict(region=r[0], name=r[2], code=r[3], + description=r[4], + sort_order=int(r[5]) if r[5] else None)) + if form.is_valid(): + Site.objects.create(id=r[1], **form.cleaned_data) + else: + print('site', r[0:], form.errors.as_data()) + + for r in c.execute('SELECT * FROM tbl_lu_municipal_locations;'): + form = MunicipalLocationForm(dict(name=r[2], code=r[3], + municipal_location_type=r[4], + description=r[5], + sort_order=int(r[6]) if r[6] else None)) + if form.is_valid(): + MunicipalLocation.objects.create(id=r[1], **form.cleaned_data) + else: + print('municipal location', r[0:], form.errors.as_data()) + + for r in c.execute('SELECT * FROM tbl_lu_study_locations;'): + form = StudyLocationForm(dict(site=r[0], name=r[2], code=r[3], + study_location_type=r[4], + treatment_type=r[5], + municipal_location=r[6], + collection_location=r[7], + description=r[13], + sort_order=int(r[14]) if r[14] else None)) + if form.is_valid(): + StudyLocation.objects.create(id=r[1], **form.cleaned_data) + else: + print('study location', r[0:], form.errors.as_data()) + + for r in c.execute('SELECT * FROM tbl_lu_storage_locations;'): + bldg = ''.join(e[0].upper() for e in r[2].split()) + temp_c = r[5] if r[5] else '20' + freezer = r[4] if r[4] else 'No Freezer' + code = ' '.join([bldg, str(temp_c)+'C', str(freezer)]) + form = StorageLocationForm(dict(facility=r[1], building=r[2], room=r[3], + freezer=r[4], + temp_c=int(r[5]) if r[5] else None, + code=code, + description=r[6], + sort_order=int(r[7]) if r[7] else None)) + if form.is_valid(): + StorageLocation.objects.create(id=r[0], **form.cleaned_data) + else: + print('storage location', r[0:], form.errors.as_data()) + + def rollback(apps, schema_editor): + Region = apps.get_model('locations', 'Region') + Site = apps.get_model('locations', 'Site') + MunicipalLocation = apps.get_model('locations', 'MunicipalLocation') + StudyLocation = apps.get_model('locations', 'StudyLocation') + StorageLocation = apps.get_model('locations', 'StorageLocation') + + for model in [StorageLocation, StudyLocation, MunicipalLocation, Site, Region]: + model.objects.all().delete() + + dependencies = [ + ('locations', '0003_study_location_code_req'), + ] + + operations = [ + migrations.RunPython(migrate, rollback), + ] diff --git a/ccdb/locations/migrations/0005_set_labels.py b/ccdb/locations/migrations/0005_set_labels.py new file mode 100644 index 0000000..8e1ee1b --- /dev/null +++ b/ccdb/locations/migrations/0005_set_labels.py @@ -0,0 +1,29 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('locations', '0004_DATA_initial'), + ] + + operations = [ + migrations.AlterField( + model_name='site', + name='region', + field=models.ForeignKey(blank=True, related_name='sites', null=True, to='locations.Region'), + ), + migrations.AlterField( + model_name='studylocation', + name='municipal_location', + field=models.ForeignKey(blank=True, related_name='study_locations', null=True, to='locations.MunicipalLocation'), + ), + migrations.AlterField( + model_name='studylocation', + name='site', + field=models.ForeignKey(related_name='study_locations', to='locations.Site'), + ), + ] diff --git a/ccdb/locations/models.py b/ccdb/locations/models.py index fa2b169..c141d04 100644 --- a/ccdb/locations/models.py +++ b/ccdb/locations/models.py @@ -18,7 +18,7 @@ class Region(models.Model): class Site(models.Model): - region = models.ForeignKey(Region, blank=True, null=True) + region = models.ForeignKey(Region, blank=True, null=True, related_name='sites') name = models.CharField(max_length=100) code = models.CharField(max_length=10, blank=True) description = models.CharField(max_length=255, blank=True) @@ -48,13 +48,13 @@ class MunicipalLocation(models.Model): class StudyLocation(models.Model): - site = models.ForeignKey(Site) + site = models.ForeignKey(Site, related_name='study_locations') name = models.CharField(max_length=100) code = models.CharField(max_length=10) study_location_type = models.CharField(max_length=50, blank=True) treatment_type = models.CharField(max_length=100, blank=True) municipal_location = models.ForeignKey(MunicipalLocation, - blank=True, null=True) + blank=True, null=True, related_name='study_locations') collecting_location = models.BooleanField(default=False) description = models.CharField(max_length=255, blank=True) sort_order = models.IntegerField(blank=True, null=True) diff --git a/ccdb/locations/tests/__init__.py b/ccdb/locations/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/ccdb/locations/tests/test_models.py b/ccdb/locations/tests/test_models.py new file mode 100644 index 0000000..4f114d3 --- /dev/null +++ b/ccdb/locations/tests/test_models.py @@ -0,0 +1,58 @@ +from django.test import TestCase +from django.db import IntegrityError, transaction + +from ..models import Region, Site, MunicipalLocation, StudyLocation, StorageLocation +from ..factories import RegionFactory, SiteFactory, MunicipalLocationFactory, \ + StudyLocationFactory, StorageLocationFactory + + +class RegionTestCase(TestCase): + def test_creation(self): + r = RegionFactory() + self.assertTrue(isinstance(r, Region)) + self.assertEqual(r.__str__(), r.name) + + def test_uniqueness(self): + r1 = RegionFactory() + with transaction.atomic(), self.assertRaises(IntegrityError): + RegionFactory(name=r1.name, code=r1.code) + r3 = RegionFactory() + self.assertTrue(isinstance(r3, Region)) + + +class SiteTestCase(TestCase): + def test_creation(self): + s = SiteFactory() + self.assertTrue(isinstance(s, Site)) + self.assertEqual(s.__str__(), s.name) + + +class MunicipalLocationTestCase(TestCase): + def test_creation(self): + m = MunicipalLocationFactory() + self.assertTrue(isinstance(m, MunicipalLocation)) + self.assertEqual(m.__str__(), m.name) + + +class StudyLocationTestCase(TestCase): + def test_creation(self): + s = StudyLocationFactory() + self.assertTrue(isinstance(s, StudyLocation)) + self.assertEqual(s.__str__(), s.code) + + def test_uniqueness(self): + s1 = StudyLocationFactory() + with transaction.atomic(), self.assertRaises(IntegrityError): + StudyLocationFactory(site=s1.site, name=s1.name) + s3 = StudyLocationFactory() + self.assertTrue(isinstance(s3, StudyLocation)) + + +class StorageLocationTestCase(TestCase): + def test_creation(self): + s = StorageLocationFactory() + self.assertTrue(isinstance(s, StorageLocation)) + self.assertEqual(s.__str__(), s.code) + + + diff --git a/ccdb/misc/factories.py b/ccdb/misc/factories.py new file mode 100644 index 0000000..c0fc42b --- /dev/null +++ b/ccdb/misc/factories.py @@ -0,0 +1,61 @@ +import factory + +from .models import MeasurementUnit, MeasurementType, Material, Color, Container + + +class MeasurementUnitFactory(factory.DjangoModelFactory): + class Meta: + model = MeasurementUnit + + name = factory.Sequence(lambda n: 'measurement_unit{}'.format(n)) + code = factory.Sequence(lambda n: 'mu{}'.format(n)) + unit_class = 'abc' + description = 'lorem ipsum' + sort_order = factory.Sequence(lambda n: n) + + +class MeasurementTypeFactory(factory.DjangoModelFactory): + class Meta: + model = MeasurementType + + name = factory.Sequence(lambda n: 'measurement_type{}'.format(n)) + code = factory.Sequence(lambda n: 'mt{}'.format(n)) + measurement_type_class = 'abc' + description = 'lorem ipsum' + default_measurement_unit = factory.SubFactory(MeasurementUnitFactory) + sort_order = factory.Sequence(lambda n: n) + + +class MaterialFactory(factory.DjangoModelFactory): + class Meta: + model = Material + + name = factory.Sequence(lambda n: 'material{}'.format(n)) + code = factory.Sequence(lambda n: 'm{}'.format(n)) + material_class = 'abc' + description = 'lorem ipsum' + sort_order = factory.Sequence(lambda n: n) + + +class ColorFactory(factory.DjangoModelFactory): + class Meta: + model = Color + + name = factory.Sequence(lambda n: 'color{}'.format(n)) + code = factory.Sequence(lambda n: 'c{}'.format(n)) + color_number = factory.Sequence(lambda n: float(n)) + sort_order = factory.Sequence(lambda n: n) + + +class ContainerFactory(factory.DjangoModelFactory): + class Meta: + model = Container + + name = factory.Sequence(lambda n: 'container{}'.format(n)) + code = factory.Sequence(lambda n: 'c{}'.format(n)) + application = 'asdf' + color = factory.SubFactory(ColorFactory) + material = factory.SubFactory(MaterialFactory) + volume = factory.Sequence(lambda n: float(n)) + measurement_unit = factory.SubFactory(MeasurementUnitFactory) + sort_order = factory.Sequence(lambda n: n) diff --git a/ccdb/misc/migrations/0002_DATA_initial.py b/ccdb/misc/migrations/0002_DATA_initial.py new file mode 100644 index 0000000..30d639c --- /dev/null +++ b/ccdb/misc/migrations/0002_DATA_initial.py @@ -0,0 +1,111 @@ +from django.db import migrations +from django.forms import modelform_factory + +from ccdb.utils.data import get_data_sources + + +class Migration(migrations.Migration): + def migrate(apps, schema_editor): + sources = get_data_sources() + if not sources: + return + + c = sources['db0'] + + MeasurementUnit = apps.get_model('misc', 'MeasurementUnit') + MeasurementType = apps.get_model('misc', 'MeasurementType') + Material = apps.get_model('misc', 'Material') + Color = apps.get_model('misc', 'Color') + Container = apps.get_model('misc', 'Container') + + for model in [Container, MeasurementType, MeasurementUnit, Material, Color]: + model.objects.all().delete() + + MUnitForm = modelform_factory(MeasurementUnit, fields=('name', 'code', + 'unit_class', + 'description', + 'sort_order')) + MTypeForm = modelform_factory(MeasurementType, fields=('name', 'code', + 'measurement_type_class', + 'description', + 'default_measurement_unit', + 'sort_order')) + MaterialForm = modelform_factory(Material, fields=('name', 'code', + 'material_class', + 'description', + 'sort_order')) + ColorForm = modelform_factory(Color, fields=('name', 'code', + 'color_number', + 'sort_order')) + ContainerForm = modelform_factory(Container, fields=('name', 'code', + 'application', + 'color', 'material', + 'volume', + 'measurement_unit', + 'sort_order')) + + for r in c.execute('SELECT * FROM tbl_lu_measurement_units;'): + form = MUnitForm(dict(name=r[1], code=r[2], + unit_class=r[3], description=r[4], + sort_order=int(r[5]) if r[5] else None)) + if form.is_valid(): + MeasurementUnit.objects.create(id=r[0], **form.cleaned_data) + else: + print('measurement unit', r[0:], form.errors.as_data()) + + for r in c.execute('SELECT * FROM tbl_lu_measurement_types;'): + form = MTypeForm(dict(name=r[1], code=r[2], + measurement_type_class=r[3], description=r[4], + default_measurement_unit=r[5], + sort_order=int(r[6]) if r[6] else None)) + if form.is_valid(): + MeasurementType.objects.create(id=r[0], **form.cleaned_data) + else: + print('measurement type', r[0:], form.errors.as_data()) + + for r in c.execute('SELECT * FROM tbl_lu_materials;'): + form = MaterialForm(dict(name=r[1], code=r[2], + material_class=r[3], description=r[4], + sort_order=int(r[5]) if r[5] else None)) + if form.is_valid(): + Material.objects.create(id=r[0], **form.cleaned_data) + else: + print('material', r[0:], form.errors.as_data()) + + for r in c.execute('SELECT * FROM tbl_lu_colors;'): + form = ColorForm(dict(name=r[1], code=r[2], + color_number=float(r[3]) if r[3] else None, + sort_order=int(r[4]) if r[4] else None)) + if form.is_valid(): + Color.objects.create(id=r[0], **form.cleaned_data) + else: + print('color', r[0:], form.errors.as_data()) + + for r in c.execute('SELECT * FROM tbl_lu_containers;'): + form = ContainerForm(dict(name=r[1], code=r[2], + application=r[3], color=r[4], material=r[5], + volume=float(r[6]) if r[6] else None, + measurement_unit=r[7], + sort_order=int(r[8]) if r[8] else None)) + if form.is_valid(): + Container.objects.create(id=r[0], **form.cleaned_data) + else: + print('container', r[0:], form.errors.as_data()) + + def rollback(apps, schema_editor): + MeasurementUnit = apps.get_model('misc', 'MeasurementUnit') + MeasurementType = apps.get_model('misc', 'MeasurementType') + Material = apps.get_model('misc', 'Material') + Color = apps.get_model('misc', 'Color') + Container = apps.get_model('misc', 'Container') + + for model in [Container, MeasurementType, MeasurementUnit, Material, Color]: + model.objects.all().delete() + + dependencies = [ + ('misc', '0001_initial'), + ] + + operations = [ + migrations.RunPython(migrate, rollback), + ] diff --git a/ccdb/misc/migrations/0003_set_labels.py b/ccdb/misc/migrations/0003_set_labels.py new file mode 100644 index 0000000..98ba21a --- /dev/null +++ b/ccdb/misc/migrations/0003_set_labels.py @@ -0,0 +1,34 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('misc', '0002_DATA_initial'), + ] + + operations = [ + migrations.AlterField( + model_name='container', + name='color', + field=models.ForeignKey(related_name='containers', blank=True, to='misc.Color', null=True), + ), + migrations.AlterField( + model_name='container', + name='material', + field=models.ForeignKey(related_name='containers', blank=True, to='misc.Material', null=True), + ), + migrations.AlterField( + model_name='container', + name='measurement_unit', + field=models.ForeignKey(related_name='containers', blank=True, to='misc.MeasurementUnit', null=True), + ), + migrations.AlterField( + model_name='measurementtype', + name='default_measurement_unit', + field=models.ForeignKey(related_name='measurement_types', blank=True, to='misc.MeasurementUnit', null=True), + ), + ] diff --git a/ccdb/misc/models.py b/ccdb/misc/models.py index 90bc941..69d0e46 100644 --- a/ccdb/misc/models.py +++ b/ccdb/misc/models.py @@ -24,7 +24,9 @@ class MeasurementType(models.Model): code = models.CharField(max_length=10, blank=True) measurement_type_class = models.CharField(max_length=50, blank=True) description = models.CharField(max_length=255, blank=True) - default_measurement_unit = models.ForeignKey('MeasurementUnit', blank=True, null=True) + default_measurement_unit = models.ForeignKey('MeasurementUnit', blank=True, + null=True, + related_name='measurement_types') sort_order = models.IntegerField(blank=True, null=True) slug = AutoSlugField(populate_from='name') @@ -70,10 +72,13 @@ class Container(models.Model): name = models.CharField(max_length=100) code = models.CharField(max_length=10, blank=True) application = models.CharField(max_length=50, blank=True) - color = models.ForeignKey(Color, blank=True, null=True) - material = models.ForeignKey(Material, blank=True, null=True) + color = models.ForeignKey(Color, blank=True, null=True, + related_name='containers') + material = models.ForeignKey(Material, blank=True, null=True, + related_name='containers') volume = models.FloatField(blank=True, null=True) - measurement_unit = models.ForeignKey(MeasurementUnit, blank=True, null=True) + measurement_unit = models.ForeignKey(MeasurementUnit, blank=True, null=True, + related_name='containers') sort_order = models.IntegerField(blank=True, null=True) slug = AutoSlugField(populate_from='name') diff --git a/ccdb/misc/tests/__init__.py b/ccdb/misc/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/ccdb/misc/tests/test_models.py b/ccdb/misc/tests/test_models.py new file mode 100644 index 0000000..354a3a6 --- /dev/null +++ b/ccdb/misc/tests/test_models.py @@ -0,0 +1,62 @@ +from django.test import TestCase +from django.db import IntegrityError, transaction + +from ..models import MeasurementUnit, MeasurementType, Material, Color, Container +from ..factories import MeasurementUnitFactory, MeasurementTypeFactory, \ + MaterialFactory, ColorFactory, ContainerFactory + + +class MeasurementUnitTestCase(TestCase): + def test_creation(self): + m = MeasurementUnitFactory() + self.assertTrue(isinstance(m, MeasurementUnit)) + self.assertEqual(m.__str__(), m.code) + + def test_uniqueness(self): + m1 = MeasurementUnitFactory() + with transaction.atomic(), self.assertRaises(IntegrityError): + MeasurementUnitFactory(name=m1.name, code=m1.code) + m3 = MeasurementUnitFactory() + self.assertTrue(isinstance(m3, MeasurementUnit)) + + +class MeasurementTypeTestCase(TestCase): + def test_creation(self): + m = MeasurementTypeFactory() + self.assertTrue(isinstance(m, MeasurementType)) + self.assertEqual(m.__str__(), m.name) + + +class MaterialTestCase(TestCase): + def test_creation(self): + m = MaterialFactory() + self.assertTrue(isinstance(m, Material)) + self.assertEqual(m.__str__(), m.name) + + def test_uniqueness(self): + m1 = MaterialFactory() + with transaction.atomic(), self.assertRaises(IntegrityError): + MaterialFactory(name=m1.name, code=m1.code) + m3 = MaterialFactory() + self.assertTrue(isinstance(m3, Material)) + + +class ColorTestCase(TestCase): + def test_creation(self): + c = ColorFactory() + self.assertTrue(isinstance(c, Color)) + self.assertEqual(c.__str__(), c.name) + + def test_uniqueness(self): + c1 = ColorFactory() + with transaction.atomic(), self.assertRaises(IntegrityError): + ColorFactory(name=c1.name, code=c1.code, color_number=c1.color_number) + c3 = ColorFactory() + self.assertTrue(isinstance(c3, Color)) + + +class ContainerTestCase(TestCase): + def test_creation(self): + c = ContainerFactory() + self.assertTrue(isinstance(c, Container)) + self.assertEqual(c.__str__(), c.name) diff --git a/ccdb/processing/factories.py b/ccdb/processing/factories.py new file mode 100644 index 0000000..9e475ab --- /dev/null +++ b/ccdb/processing/factories.py @@ -0,0 +1,52 @@ +from datetime import datetime, date + +from factory import DjangoModelFactory, Sequence, SubFactory, LazyFunction +from factory.fuzzy import FuzzyText, FuzzyDate, FuzzyFloat, FuzzyInteger + +from .models import ProcessType, Reagent, Flaw, Processing +from ..misc.factories import ContainerFactory, MeasurementUnitFactory + + +class ProcessTypeFactory(DjangoModelFactory): + class Meta: + model = ProcessType + + name = Sequence(lambda n: 'process_type{}'.format(n)) + code = Sequence(lambda n: 'pt{}'.format(n)) + description = FuzzyText(length=255) + sort_order = Sequence(lambda n: n) + + +class ReagentFactory(DjangoModelFactory): + class Meta: + model = Reagent + + name = Sequence(lambda n: 'reagent{}'.format(n)) + code = Sequence(lambda n: 'r{}'.format(n)) + reagent_class = FuzzyText(length=50) + sort_order = Sequence(lambda n: n) + + +class FlawFactory(DjangoModelFactory): + class Meta: + model = Flaw + + name = Sequence(lambda n: 'flaw{}'.format(n)) + description = FuzzyText(length=255) + sort_order = Sequence(lambda n: n) + + +class ProcessingFactory(DjangoModelFactory): + class Meta: + model = Processing + + process_type = SubFactory(ProcessTypeFactory) + container = SubFactory(ContainerFactory) + container_label = FuzzyText(length=50) + process_date = FuzzyDate(date(2012, 1, 1)) + process_time = LazyFunction(datetime.now().time) + reagent = SubFactory(ReagentFactory) + reagent_volume = FuzzyFloat(0.0) + measurement_unit = SubFactory(MeasurementUnitFactory) + minutes_in_reagent = FuzzyInteger(0) + flaw = SubFactory(FlawFactory) diff --git a/ccdb/processing/migrations/0002_DATA_initial.py b/ccdb/processing/migrations/0002_DATA_initial.py new file mode 100644 index 0000000..2934a22 --- /dev/null +++ b/ccdb/processing/migrations/0002_DATA_initial.py @@ -0,0 +1,57 @@ +from django.db import migrations +from django.forms import modelform_factory + +from ccdb.utils.data import get_data_sources + + +class Migration(migrations.Migration): + def migrate(apps, schema_editor): + sources = get_data_sources() + if not sources: + return + + c = sources['db0'] + + ProcessType = apps.get_model('processing', 'ProcessType') + Reagent = apps.get_model('processing', 'Reagent') + Flaw = apps.get_model('processing', 'Flaw') + Processing = apps.get_model('processing', 'Processing') + + for model in [Processing, Flaw, Reagent, ProcessType]: + model.objects.all().delete() + + ProcessTypeForm = modelform_factory(ProcessType, fields='__all__') + ReagentForm = modelform_factory(Reagent, fields='__all__') + + for r in c.execute('SELECT * FROM tbl_lu_process_types;'): + form = ProcessTypeForm(dict(name=r[1], code=r[2], + description=r[3], + sort_order=int(r[4]) if r[4] else None)) + if form.is_valid(): + ProcessType.objects.create(id=r[0], **form.cleaned_data) + else: + print('process type', r[0:], form.errors.as_data()) + + for r in c.execute('SELECT * FROM tbl_lu_reagents;'): + form = ReagentForm(dict(name=r[1], code=r[2], + reagent_class=r[3], + sort_order=int(r[4]) if r[4] else None)) + if form.is_valid(): + Reagent.objects.create(id=r[0], **form.cleaned_data) + else: + print('reagent', r[0:], form.errors.as_data()) + + def rollback(apps, schema_editor): + ProcessType = apps.get_model('processing', 'ProcessType') + Reagent = apps.get_model('processing', 'Reagent') + + for model in [Reagent, ProcessType]: + model.objects.all().delete() + + dependencies = [ + ('processing', '0001_initial'), + ] + + operations = [ + migrations.RunPython(migrate, rollback), + ] diff --git a/ccdb/processing/migrations/0003_set_labels.py b/ccdb/processing/migrations/0003_set_labels.py new file mode 100644 index 0000000..fe58c6e --- /dev/null +++ b/ccdb/processing/migrations/0003_set_labels.py @@ -0,0 +1,39 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('processing', '0002_DATA_initial'), + ] + + operations = [ + migrations.AlterField( + model_name='processing', + name='container', + field=models.ForeignKey(related_name='processings', to='misc.Container'), + ), + migrations.AlterField( + model_name='processing', + name='flaw', + field=models.ForeignKey(to='processing.Flaw', blank=True, related_name='processings', null=True), + ), + migrations.AlterField( + model_name='processing', + name='measurement_unit', + field=models.ForeignKey(to='misc.MeasurementUnit', blank=True, related_name='processings', null=True), + ), + migrations.AlterField( + model_name='processing', + name='process_type', + field=models.ForeignKey(related_name='processings', to='processing.ProcessType'), + ), + migrations.AlterField( + model_name='processing', + name='reagent', + field=models.ForeignKey(to='processing.Reagent', blank=True, related_name='processings', null=True), + ), + ] diff --git a/ccdb/processing/models.py b/ccdb/processing/models.py index 639b530..fc01fa6 100644 --- a/ccdb/processing/models.py +++ b/ccdb/processing/models.py @@ -47,20 +47,22 @@ class Flaw(models.Model): class Processing(models.Model): - process_type = models.ForeignKey(ProcessType) - container = models.ForeignKey('misc.Container') + process_type = models.ForeignKey(ProcessType, related_name='processings') + container = models.ForeignKey('misc.Container', related_name='processings') container_label = models.CharField(max_length=50) process_date = models.DateField(blank=True, null=True) process_time = models.TimeField(blank=True, null=True) - reagent = models.ForeignKey(Reagent, blank=True, null=True) + reagent = models.ForeignKey(Reagent, blank=True, null=True, + related_name='processings') reagent_volume = models.FloatField(blank=True, null=True) - measurement_unit = models.ForeignKey('misc.MeasurementUnit', blank=True, null=True) + measurement_unit = models.ForeignKey('misc.MeasurementUnit', blank=True, + null=True, related_name='processings') minutes_in_reagent = models.IntegerField(blank=True, null=True) - flaw = models.ForeignKey(Flaw, blank=True, null=True) + flaw = models.ForeignKey(Flaw, blank=True, null=True, related_name='processings') def __str__(self): - return "{process_date} {process_type} {container_label}".format(**self) + return "{} {} {}".format(self.process_date, self.process_type, self.container_label) class Meta: unique_together = ('process_type', 'container', 'container_label', - 'process_date', 'process_time', 'reagent') + 'process_date', 'process_time', 'reagent') diff --git a/ccdb/processing/tests/__init__.py b/ccdb/processing/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/ccdb/processing/tests/test_models.py b/ccdb/processing/tests/test_models.py new file mode 100644 index 0000000..836e7bc --- /dev/null +++ b/ccdb/processing/tests/test_models.py @@ -0,0 +1,58 @@ +from django.test import TestCase +from django.db import IntegrityError, transaction + +from ..models import ProcessType, Reagent, Flaw, Processing +from ..factories import ProcessTypeFactory, ReagentFactory, FlawFactory, ProcessingFactory + + +class ProcessTypeTestCase(TestCase): + def test_creation(self): + p = ProcessTypeFactory() + self.assertTrue(isinstance(p, ProcessType)) + self.assertEqual(p.__str__(), p.name) + + def test_uniqueness(self): + p1 = ProcessTypeFactory() + with transaction.atomic(), self.assertRaises(IntegrityError): + ProcessTypeFactory(name=p1.name, code=p1.code) + p3 = ProcessTypeFactory() + self.assertTrue(isinstance(p3, ProcessType)) + + +class ReagentTestCase(TestCase): + def test_creation(self): + r = ReagentFactory() + self.assertTrue(isinstance(r, Reagent)) + self.assertEqual(r.__str__(), r.name) + + def test_uniqueness(self): + r1 = ReagentFactory() + with transaction.atomic(), self.assertRaises(IntegrityError): + ReagentFactory(name=r1.name, code=r1.code) + r3 = ReagentFactory() + self.assertTrue(isinstance(r3, Reagent)) + + +class FlawTestCase(TestCase): + def test_creation(self): + f = FlawFactory() + self.assertTrue(isinstance(f, Flaw)) + self.assertEqual(f.__str__(), f.name) + + +class ProcessingTestCase(TestCase): + def test_creation(self): + p = ProcessingFactory() + self.assertTrue(isinstance(p, Processing)) + name = "{} {} {}".format(p.process_date, p.process_type, p.container_label) + self.assertEqual(p.__str__(), name) + + def test_uniqueness(self): + p1 = ProcessingFactory() + with transaction.atomic(), self.assertRaises(IntegrityError): + ProcessingFactory(process_type=p1.process_type, container=p1.container, + container_label=p1.container_label, + process_date=p1.process_date, + process_time=p1.process_time, reagent=p1.reagent) + p3 = ProcessingFactory() + self.assertTrue(isinstance(p3, Processing)) diff --git a/ccdb/projects/factories.py b/ccdb/projects/factories.py new file mode 100644 index 0000000..64cdbf2 --- /dev/null +++ b/ccdb/projects/factories.py @@ -0,0 +1,40 @@ +from datetime import datetime + +import factory + +from .models import Project, Grant, GrantReport + + +class ProjectFactory(factory.DjangoModelFactory): + class Meta: + model = Project + + name = factory.Sequence(lambda n: 'project{}'.format(n)) + code = factory.Sequence(lambda n: 'p{}'.format(n)) + iacuc_number = 'abc' + description = 'lorem ipsum' + sort_order = factory.Sequence(lambda n: n) + + +class GrantFactory(factory.DjangoModelFactory): + class Meta: + model = Grant + + title = factory.Sequence(lambda n: 'grant{}'.format(n)) + code = factory.Sequence(lambda n: 'g{}'.format(n)) + description = 'lorem ipsum' + sort_order = factory.Sequence(lambda n: n) + + +class GrantReportFactory(factory.DjangoModelFactory): + class Meta: + model = GrantReport + + grant = factory.SubFactory(GrantFactory) + title = factory.Sequence(lambda n: 'grant{}'.format(n)) + report_type = 'lorem ipsum' + description = 'lorem ipsum' + due_date = factory.LazyFunction(datetime.now) + submitted_date = factory.LazyFunction(datetime.now) + sort_order = factory.Sequence(lambda n: n) + diff --git a/ccdb/projects/migrations/0005_DATA_initial.py b/ccdb/projects/migrations/0005_DATA_initial.py new file mode 100644 index 0000000..d2b48c6 --- /dev/null +++ b/ccdb/projects/migrations/0005_DATA_initial.py @@ -0,0 +1,83 @@ +from django.db import migrations +from django.forms import modelform_factory + +from ccdb.utils.data import get_data_sources + + +class Migration(migrations.Migration): + def migrate(apps, schema_editor): + sources = get_data_sources() + if not sources: + print('no sources') + return + + c = sources['db0'] + + Project = apps.get_model('projects', 'Project') + Grant = apps.get_model('projects', 'Grant') + GrantReport = apps.get_model('projects', 'GrantReport') + + Project.objects.all().delete() + Grant.objects.all().delete() + GrantReport.objects.all().delete() + + ProjectForm = modelform_factory(Project, fields=('name', 'code', + 'iacuc_number', + 'description', + 'sort_order')) + GrantForm = modelform_factory(Grant, fields=('title', 'code', + 'description', 'sort_order')) + GrantReportForm = modelform_factory(GrantReport, fields=('grant', 'title', + 'report_type', + 'description', + 'due_date', + 'submitted_date', + 'sort_order')) + + for r in c.execute('SELECT * FROM tbl_lu_projects;'): + form = ProjectForm(dict(name=r[1], code=r[2], + iacuc_number=r[3], description=r[4], + sort_order=int(r[5]))) + if form.is_valid(): + Project.objects.create(id=r[0], **form.cleaned_data) + else: + print('project', r[0:], form.errors.as_data()) + + for r in c.execute('SELECT * FROM tbl_lu_grants;'): + form = GrantForm(dict(title=r[1], code=r[2], description=r[3], sort_order=r[4])) + if form.is_valid(): + Grant.objects.create(id=r[0], **form.cleaned_data) + else: + print('grant', r[0:], form.errors.as_data()) + + for r in c.execute('SELECT * FROM tbl_hash_project_grants;'): + p = Project.objects.get(id=r[0]) + g = Grant.objects.get(id=r[1]) + p.grants.add(g) + p.save() + + for r in c.execute('SELECT *, report_due_date AS "due_date [dtdt]" FROM tbl_lu_grant_reports;'): + form = GrantReportForm(dict(grant=r[0], title=r[1], report_type=r[2], + description=r[3], due_date=r[8], + submitted_date=r[5], sort_order=r[7])) + if form.is_valid(): + form.save() # No PK field in Andre's file + else: + print('grant report', r[0:], form.errors.as_data()) + + + def rollback(apps, schema_editor): + Project = apps.get_model('projects', 'Project') + Grant = apps.get_model('projects', 'Grant') + GrantReport = apps.get_model('projects', 'GrantReport') + + for model in [Project, Grant, GrantReport]: + model.objects.all().delete() + + dependencies = [ + ('projects', '0004_initial_grantreport'), + ] + + operations = [ + migrations.RunPython(migrate, rollback), + ] diff --git a/ccdb/projects/migrations/0006_set_labels.py b/ccdb/projects/migrations/0006_set_labels.py new file mode 100644 index 0000000..de82417 --- /dev/null +++ b/ccdb/projects/migrations/0006_set_labels.py @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('projects', '0005_DATA_initial'), + ] + + operations = [ + migrations.AlterField( + model_name='grantreport', + name='grant', + field=models.ForeignKey(to='projects.Grant', related_name='reports'), + ), + ] diff --git a/ccdb/projects/models.py b/ccdb/projects/models.py index 08290a8..24212ce 100644 --- a/ccdb/projects/models.py +++ b/ccdb/projects/models.py @@ -35,7 +35,7 @@ class Grant(models.Model): class GrantReport(models.Model): - grant = models.ForeignKey(Grant) + grant = models.ForeignKey(Grant, related_name='reports') title = models.CharField(max_length=200) report_type = models.CharField(max_length=50, blank=True) description = models.CharField(max_length=255, blank=True) diff --git a/ccdb/projects/tests/__init__.py b/ccdb/projects/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/ccdb/projects/tests/test_models.py b/ccdb/projects/tests/test_models.py new file mode 100644 index 0000000..b1a4b6e --- /dev/null +++ b/ccdb/projects/tests/test_models.py @@ -0,0 +1,47 @@ +from django.test import TestCase +from django.db import IntegrityError, transaction + +from ..models import Project, Grant, GrantReport +from ..factories import ProjectFactory, GrantFactory, GrantReportFactory + + +class ProjectTestCase(TestCase): + def test_creation(self): + p = ProjectFactory() + self.assertTrue(isinstance(p, Project)) + self.assertEqual(p.__str__(), p.name) + + def test_uniqueness(self): + p1 = ProjectFactory() + with transaction.atomic(), self.assertRaises(IntegrityError): + ProjectFactory(name=p1.name, code=p1.code) + p3 = ProjectFactory() + self.assertTrue(isinstance(p3, Project)) + + +class GrantTestCase(TestCase): + def test_creation(self): + g = GrantFactory() + self.assertTrue(isinstance(g, Grant)) + self.assertEqual(g.__str__(), g.title) + + def test_uniqueness(self): + g1 = GrantFactory() + with transaction.atomic(), self.assertRaises(IntegrityError): + GrantFactory(title=g1.title, code=g1.code) + g3 = GrantFactory() + self.assertTrue(isinstance(g3, Grant)) + + +class GrantReportTestCase(TestCase): + def test_creation(self): + g = GrantReportFactory() + self.assertTrue(isinstance(g, GrantReport)) + self.assertEqual(g.__str__(), g.title) + + def test_uniqueness(self): + g1 = GrantReportFactory() + with transaction.atomic(), self.assertRaises(IntegrityError): + GrantReportFactory(title=g1.title, grant=g1.grant) + g3 = GrantReportFactory() + self.assertTrue(isinstance(g3, GrantReport)) diff --git a/ccdb/species/factories.py b/ccdb/species/factories.py new file mode 100644 index 0000000..5930503 --- /dev/null +++ b/ccdb/species/factories.py @@ -0,0 +1,27 @@ +from factory import DjangoModelFactory, Sequence, SubFactory +from factory.fuzzy import FuzzyText, FuzzyChoice, FuzzyInteger + +from .models import Species, CollectionSpecies +from ..collections_ccdb.factories import CollectionFactory + + +class SpeciesFactory(DjangoModelFactory): + class Meta: + model = Species + + common_name = Sequence(lambda n: 'species{}'.format(n)) + genus = FuzzyText(length=50) + species = FuzzyText(length=50) + parasite = FuzzyChoice(choices=[True, False]) + sort_order = Sequence(lambda n: n) + + +class CollectionSpeciesFactory(DjangoModelFactory): + class Meta: + model = CollectionSpecies + + collection = SubFactory(CollectionFactory) + species = SubFactory(SpeciesFactory) + sex = FuzzyText(length=25) + count = FuzzyInteger(0) + count_estimated = FuzzyChoice(choices=[True, False]) diff --git a/ccdb/species/migrations/0004_DATA_initial.py b/ccdb/species/migrations/0004_DATA_initial.py new file mode 100644 index 0000000..f4d3a11 --- /dev/null +++ b/ccdb/species/migrations/0004_DATA_initial.py @@ -0,0 +1,40 @@ +from django.db import migrations +from django.forms import modelform_factory + +from ccdb.utils.data import get_data_sources + + +class Migration(migrations.Migration): + def migrate(apps, schema_editor): + sources = get_data_sources() + if not sources: + return + + c = sources['db0'] + + Species = apps.get_model('species', 'Species') + + Species.objects.all().delete() + + SpeciesForm = modelform_factory(Species, fields='__all__') + + for r in c.execute('SELECT * FROM tbl_lu_species;'): + form = SpeciesForm(dict(common_name=r[1], genus=r[2], + species=r[3], parasite=r[4], + sort_order=int(r[5]) if r[5] else None)) + if form.is_valid(): + Species.objects.create(id=r[0], **form.cleaned_data) + else: + print('species', r[0:], form.errors.as_data()) + + def rollback(apps, schema_editor): + Species = apps.get_model('species', 'Species') + Species.objects.all().delete() + + dependencies = [ + ('species', '0003_collectionspecies'), + ] + + operations = [ + migrations.RunPython(migrate, rollback), + ] diff --git a/ccdb/species/migrations/0005_DATA_species_collection.py b/ccdb/species/migrations/0005_DATA_species_collection.py new file mode 100644 index 0000000..487b7c4 --- /dev/null +++ b/ccdb/species/migrations/0005_DATA_species_collection.py @@ -0,0 +1,41 @@ +from django.db import migrations +from django.forms import modelform_factory + +from ccdb.utils.data import get_data_sources + + +class Migration(migrations.Migration): + def migrate(apps, schema_editor): + sources = get_data_sources() + if not sources: + return + + c = sources['db0'] + + CollectionSpecies = apps.get_model('species', 'CollectionSpecies') + + CollectionSpecies.objects.all().delete() + + CollectionSpeciesForm = modelform_factory(CollectionSpecies, fields='__all__') + + for r in c.execute('SELECT * FROM tbl_hash_collection_species;'): + form = CollectionSpeciesForm(dict(collection=r[0], species=r[1], sex=r[2], + count=r[3], count_estimated=r[4])) + if form.is_valid(): + # No PK in Andre's file + form.save() + else: + print('collection species', r[0:], form.errors.as_data()) + + def rollback(apps, schema_editor): + CollectionSpecies = apps.get_model('species', 'CollectionSpecies') + CollectionSpecies.objects.all().delete() + + dependencies = [ + ('species', '0004_DATA_initial'), + ('collections_ccdb', '0005_DATA_initial'), + ] + + operations = [ + migrations.RunPython(migrate, rollback), + ] diff --git a/ccdb/species/migrations/0006_set_labels.py b/ccdb/species/migrations/0006_set_labels.py new file mode 100644 index 0000000..e36c92d --- /dev/null +++ b/ccdb/species/migrations/0006_set_labels.py @@ -0,0 +1,34 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('species', '0005_DATA_species_collection'), + ] + + operations = [ + migrations.AlterField( + model_name='collectionspecies', + name='collection', + field=models.ForeignKey(related_name='collection_species', to='collections_ccdb.Collection'), + ), + migrations.AlterField( + model_name='collectionspecies', + name='species', + field=models.ForeignKey(related_name='collection_species', to='species.Species'), + ), + migrations.AlterField( + model_name='trapspecies', + name='collection_trap', + field=models.ForeignKey(related_name='trap_species', to='collections_ccdb.CollectionTrap'), + ), + migrations.AlterField( + model_name='trapspecies', + name='species', + field=models.ForeignKey(related_name='trap_species', to='species.Species'), + ), + ] diff --git a/ccdb/species/models.py b/ccdb/species/models.py index dc76629..92504b9 100644 --- a/ccdb/species/models.py +++ b/ccdb/species/models.py @@ -21,8 +21,9 @@ class Species(models.Model): class TrapSpecies(models.Model): - collection_trap = models.ForeignKey('collections_ccdb.CollectionTrap') - species = models.ForeignKey(Species) + collection_trap = models.ForeignKey('collections_ccdb.CollectionTrap', + related_name='trap_species') + species = models.ForeignKey(Species, related_name='trap_species') sex = models.CharField(max_length=25, blank=True) count = models.IntegerField(blank=True, null=True) count_estimated = models.BooleanField(default=False) @@ -35,8 +36,9 @@ class TrapSpecies(models.Model): class CollectionSpecies(models.Model): - collection = models.ForeignKey('collections_ccdb.Collection') - species = models.ForeignKey(Species) + collection = models.ForeignKey('collections_ccdb.Collection', + related_name='collection_species') + species = models.ForeignKey(Species, related_name='collection_species') sex = models.CharField(max_length=25, blank=True) count = models.IntegerField(blank=True, null=True) count_estimated = models.BooleanField(default=False) diff --git a/ccdb/species/tests/__init__.py b/ccdb/species/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/ccdb/species/tests/test_models.py b/ccdb/species/tests/test_models.py new file mode 100644 index 0000000..4e18302 --- /dev/null +++ b/ccdb/species/tests/test_models.py @@ -0,0 +1,34 @@ +from django.test import TestCase +from django.db import IntegrityError, transaction + +from ..models import Species, CollectionSpecies +from ..factories import SpeciesFactory, CollectionSpeciesFactory + + +class SpeciesTestCase(TestCase): + def test_creation(self): + s = SpeciesFactory() + self.assertTrue(isinstance(s, Species)) + self.assertEqual(s.__str__(), s.common_name) + + def test_uniqueness(self): + s1 = SpeciesFactory() + with transaction.atomic(), self.assertRaises(IntegrityError): + SpeciesFactory(common_name=s1.common_name, species=s1.species) + s3 = SpeciesFactory() + self.assertTrue(isinstance(s3, Species)) + + +class CollectionSpeciesTestCase(TestCase): + def test_creation(self): + c = CollectionSpeciesFactory() + self.assertTrue(isinstance(c, CollectionSpecies)) + label = "{} {}".format(c.collection, c.species) + self.assertEqual(c.__str__(), label) + + def test_uniqueness(self): + c1 = CollectionSpeciesFactory() + with transaction.atomic(), self.assertRaises(IntegrityError): + CollectionSpeciesFactory(collection=c1.collection, species=c1.species) + c3 = CollectionSpeciesFactory() + self.assertTrue(isinstance(c3, CollectionSpecies)) diff --git a/ccdb/utils/data.py b/ccdb/utils/data.py index 998f619..7074cb9 100644 --- a/ccdb/utils/data.py +++ b/ccdb/utils/data.py @@ -45,7 +45,7 @@ def _write_url(url, filename): def _get_db0(): - dbfile = 'data/Replica_Hibernators_Back_UAF_Laptop_29_June_2015.sqlite' + dbfile = 'data/CC_Database_020216.sqlite' return setup_sqlite(dbfile) diff --git a/config/settings/local.py b/config/settings/local.py index da25471..7280f1c 100644 --- a/config/settings/local.py +++ b/config/settings/local.py @@ -41,6 +41,10 @@ EMAIL_BACKEND = env('DJANGO_EMAIL_BACKEND', MIDDLEWARE_CLASSES += ('debug_toolbar.middleware.DebugToolbarMiddleware',) INSTALLED_APPS += ('debug_toolbar', ) +# Testing +# ------------------------------------------------------------------------------ +INSTALLED_APPS += ('django_extensions', 'test_without_migrations') + INTERNAL_IPS = ('127.0.0.1', ) DEBUG_TOOLBAR_CONFIG = { @@ -54,6 +58,8 @@ DEBUG_TOOLBAR_CONFIG = { # ------------------------------------------------------------------------------ TEST_RUNNER = 'django.test.runner.DiscoverRunner' +MANIFEST_URL = env('MANIFEST_URL', default=None) +CORS_ORIGIN_ALLOW_ALL = True DJOSER = { 'SITE_NAME': 'CCDB (test)',