diff --git a/core/common/management/commands/load_fixtures.py b/core/common/management/commands/load_fixtures.py new file mode 100644 index 000000000..082a122f7 --- /dev/null +++ b/core/common/management/commands/load_fixtures.py @@ -0,0 +1,20 @@ +import os +from glob import glob + +from django.conf import settings +from django.core.management import BaseCommand, call_command + +from core.orgs.models import Organization + +FIXTURES_DIR = os.path.join(settings.BASE_DIR, 'core', 'fixtures') +OCL_ORG = os.path.join(FIXTURES_DIR, 'ocl_org.yaml') + + +class Command(BaseCommand): + help = 'load core/fixtures on container start; ocl_org.yaml only when the OCL org does not exist yet' + + def handle(self, *args, **options): + fixtures = sorted(glob(os.path.join(FIXTURES_DIR, '*'))) + if Organization.objects.filter(id=1).exists(): + fixtures.remove(OCL_ORG) + call_command('loaddata', *fixtures, verbosity=options['verbosity']) diff --git a/core/common/tests.py b/core/common/tests.py index 4194bf67c..015e44435 100644 --- a/core/common/tests.py +++ b/core/common/tests.py @@ -276,7 +276,7 @@ class OCLAPITransactionTestCase(APITransactionTestCase, BaseTestCase): @classmethod def setUpClass(cls): super().setUpClass() - call_command("loaddata", "core/fixtures/base_entities.yaml") + call_command("loaddata", "core/fixtures/base_entities.yaml", "core/fixtures/ocl_org.yaml") call_command("loaddata", "core/fixtures/auth_groups.yaml") call_command("loaddata", "core/fixtures/capabilities.yaml") call_command("loaddata", "core/fixtures/toggles.json") @@ -289,7 +289,7 @@ class OCLAPITestCase(APITestCase, BaseTestCase): @classmethod def setUpClass(cls): super().setUpClass() - call_command("loaddata", "core/fixtures/base_entities.yaml") + call_command("loaddata", "core/fixtures/base_entities.yaml", "core/fixtures/ocl_org.yaml") call_command("loaddata", "core/fixtures/auth_groups.yaml") call_command("loaddata", "core/fixtures/capabilities.yaml") call_command("loaddata", "core/fixtures/toggles.json") @@ -306,7 +306,7 @@ class OCLTestCase(TestCase, BaseTestCase): @classmethod def setUpClass(cls): super().setUpClass() - call_command("loaddata", "core/fixtures/base_entities.yaml") + call_command("loaddata", "core/fixtures/base_entities.yaml", "core/fixtures/ocl_org.yaml") call_command("loaddata", "core/fixtures/auth_groups.yaml") call_command("loaddata", "core/fixtures/capabilities.yaml") call_command("loaddata", "core/fixtures/toggles.json") diff --git a/core/common/tests_load_fixtures.py b/core/common/tests_load_fixtures.py new file mode 100644 index 000000000..be26e0ca0 --- /dev/null +++ b/core/common/tests_load_fixtures.py @@ -0,0 +1,48 @@ +from django.core.management import call_command + +from core.capabilities.models import Capability +from core.common.tests import OCLTestCase +from core.orgs.models import Organization +from core.users.models import UserProfile + + +class LoadFixturesCommandTest(OCLTestCase): + def test_keeps_an_existing_ocl_org(self): + edits = { + 'name': 'Open Concept Lab', 'text': '

About OCL

', 'description': 'Shared reference content', + 'overview': {'background': {'image': 'https://example.org/banner.png'}}, + } + Organization.objects.filter(id=1).update(**edits) + + call_command('load_fixtures', verbosity=0) + + self.assertEqual(Organization.objects.filter(id=1).values(*edits).get(), edits) + + def test_reloads_the_other_fixtures(self): + Capability.objects.filter(id=1).update(description='changed') + + call_command('load_fixtures', verbosity=0) + + self.assertEqual(Capability.objects.get(id=1).description, 'Number of map projects a user may create') + + def test_startup_restores_a_demoted_or_deactivated_ocladmin(self): + UserProfile.objects.filter(username='ocladmin').update( + is_superuser=False, is_staff=False, is_active=False, verified=False) + + call_command('load_fixtures', verbosity=0) + call_command('setup_superuser') + + ocladmin = UserProfile.objects.get(username='ocladmin') + self.assertTrue(ocladmin.is_superuser) + self.assertTrue(ocladmin.is_staff) + self.assertTrue(ocladmin.is_active) + self.assertTrue(ocladmin.verified) + + def test_seeds_the_ocl_org_and_ocladmin_on_a_new_database(self): + Organization.objects.filter(id=1).delete() + UserProfile.objects.filter(id=1).delete() + + call_command('load_fixtures', verbosity=0) + + self.assertEqual(Organization.objects.get(id=1).mnemonic, 'OCL') + self.assertTrue(UserProfile.objects.get(id=1, username='ocladmin').is_superuser) diff --git a/core/fixtures/auth_groups.yaml b/core/fixtures/auth_groups.yaml index 2c4ab0bfb..7b9c0a3d7 100644 --- a/core/fixtures/auth_groups.yaml +++ b/core/fixtures/auth_groups.yaml @@ -1,7 +1,7 @@ # The Mapper-gating and authoring Django permissions (ocl_issues#2780/#2782/#2766, ocl_online#230). Plan groups granting # them are seeded by `manage.py seed_group_capabilities` from deployment config. -# pk is pinned so re-running `loaddata core/fixtures/*` on every container start -# (pre_startup.sh) updates these same rows instead of inserting duplicates on the +# pk is pinned so reloading the fixtures on every container start (`manage.py +# load_fixtures` in pre_startup.sh) updates these same rows instead of inserting duplicates on the # unique (content_type, codename) constraint - same reasoning as the group pks below, # but starting well above them: auth_permission already had 252 rows (max id 252, # confirmed against the dev database) and keeps growing as new models are added, unlike diff --git a/core/fixtures/base_entities.yaml b/core/fixtures/base_entities.yaml index 52ce2dffe..5cdbc0b9a 100644 --- a/core/fixtures/base_entities.yaml +++ b/core/fixtures/base_entities.yaml @@ -1,14 +1,3 @@ -- model: "orgs.organization" - pk: 1 - fields: - name: OCL - company: OCL - mnemonic: OCL - website: "http://openconceptlab.org" - created_at: "2020-06-08T08:00:00+00:00" - updated_at: "2020-06-08T08:00:00+00:00" - created_by: 1 - updated_by: 1 - model: "users.userprofile" pk: 1 fields: diff --git a/core/fixtures/ocl_org.yaml b/core/fixtures/ocl_org.yaml new file mode 100644 index 000000000..b931d582c --- /dev/null +++ b/core/fixtures/ocl_org.yaml @@ -0,0 +1,13 @@ +# Loaded on a new database only (`manage.py load_fixtures`): reloading it would overwrite the OCL org's +# text, description, logo and overview on every container start (OpenConceptLab/ocl_issues#2911). +- model: "orgs.organization" + pk: 1 + fields: + name: OCL + company: OCL + mnemonic: OCL + website: "http://openconceptlab.org" + created_at: "2020-06-08T08:00:00+00:00" + updated_at: "2020-06-08T08:00:00+00:00" + created_by: 1 + updated_by: 1 diff --git a/pre_startup.sh b/pre_startup.sh index 8ce4e112c..00e2d3457 100755 --- a/pre_startup.sh +++ b/pre_startup.sh @@ -25,7 +25,7 @@ export NO_ENCODER=TRUE echo "Running DB migrations" python manage.py migrate echo "Importing base entities" -python manage.py loaddata core/fixtures/* +python manage.py load_fixtures echo "Seeding default group capability limits" python manage.py seed_group_capabilities