Skip to content

Commit ea4efa2

Browse files
authored
Merge pull request #2002 from rtibbles/presets
Use and store facility configuration presets
2 parents 3927eb2 + 543c706 commit ea4efa2

File tree

10 files changed

+115
-28
lines changed

10 files changed

+115
-28
lines changed
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
{
2+
"formal": {
3+
"name": "Admin-managed",
4+
"default": true,
5+
"mappings": {
6+
"learner_can_edit_username": false,
7+
"learner_can_edit_name": false,
8+
"learner_can_edit_password": false,
9+
"learner_can_sign_up": false,
10+
"learner_can_delete_account": false,
11+
"learner_can_login_with_no_password": true
12+
}
13+
},
14+
"nonformal": {
15+
"name": "Self-managed",
16+
"mappings": {
17+
"learner_can_edit_username": true,
18+
"learner_can_edit_name": true,
19+
"learner_can_edit_password": true,
20+
"learner_can_sign_up": true,
21+
"learner_can_delete_account": true,
22+
"learner_can_login_with_no_password": false
23+
}
24+
},
25+
"informal": {
26+
"name": "Informal and personal use",
27+
"mappings": {
28+
"learner_can_edit_username": true,
29+
"learner_can_edit_name": true,
30+
"learner_can_edit_password": true,
31+
"learner_can_sign_up": false,
32+
"learner_can_delete_account": true,
33+
"learner_can_login_with_no_password": false
34+
}
35+
}
36+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import json
2+
import os
3+
4+
from django.utils.translation import ugettext_lazy as _
5+
6+
with open(os.path.join(os.path.dirname(__file__), './facility_configuration_presets.json'), 'r') as f:
7+
presets = json.load(f)
8+
9+
choices = list(
10+
(key, _(item['name'])) for key, item in presets.items()
11+
)
12+
13+
mappings = {
14+
key: item['mappings'] for key, item in presets.items()
15+
}
16+
17+
default = next((key for key, item in presets.items() if item.get('default')), None)

kolibri/auth/migrations/0004_auto_20170816_1607.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,19 +13,25 @@ def device_owner_to_super_user(apps, schema_editor):
1313
default_facility = Facility.objects.all().first()
1414
DevicePermissions = apps.get_model('device', 'DevicePermissions')
1515
DeviceSettings = apps.get_model('device', 'DeviceSettings')
16+
from kolibri.auth.models import FacilityUser as RealFacilityUser, Facility as RealFacility
17+
real_default_facility = RealFacility.get_default_facility()
1618
# Can't do much if no facilities exist, as no facility to FK the users onto
1719
if default_facility:
1820
for device_owner in DeviceOwner.objects.all():
21+
uuid = RealFacilityUser(username=device_owner.username, facility=real_default_facility).calculate_uuid()
22+
dataset_id = real_default_facility.dataset_id
1923
superuser = FacilityUser.objects.create(
2024
username=device_owner.username,
2125
password=device_owner.password,
2226
facility=default_facility,
2327
full_name=device_owner.full_name,
2428
date_joined=device_owner.date_joined,
29+
id=uuid,
30+
dataset_id=dataset_id,
2531
)
2632
DevicePermissions.objects.create(user=superuser, is_superuser=True)
2733
# Finally, set the is_provisioned flag
28-
settings, created = DeviceSettings.get_or_create(is_provisioned=True)
34+
settings, created = DeviceSettings.objects.get_or_create(is_provisioned=True)
2935

3036

3137
class Migration(migrations.Migration):
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# -*- coding: utf-8 -*-
2+
# Generated by Django 1.9.7 on 2017-08-18 19:03
3+
from __future__ import unicode_literals
4+
5+
import django.core.validators
6+
from django.db import migrations, models
7+
8+
9+
class Migration(migrations.Migration):
10+
11+
dependencies = [
12+
('kolibriauth', '0004_auto_20170816_1607'),
13+
]
14+
15+
operations = [
16+
migrations.AddField(
17+
model_name='facilitydataset',
18+
name='preset',
19+
field=models.CharField(choices=[('formal', 'Admin-managed'), ('informal', 'Informal and personal use'), ('nonformal', 'Self-managed')], default='formal', max_length=50),
20+
),
21+
]

kolibri/auth/models.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@
4141
from morango.utils.morango_mptt import MorangoMPTTModel
4242
from mptt.models import TreeForeignKey
4343

44-
from .constants import collection_kinds, role_kinds
44+
from .constants import collection_kinds, facility_presets, role_kinds
4545
from .errors import (
4646
InvalidRoleKind, UserDoesNotHaveRoleError, UserHasRoleOnlyIndirectlyThroughHierarchyError, UserIsMemberOnlyIndirectlyThroughHierarchyError,
4747
UserIsNotFacilityUser, UserIsNotMemberError
@@ -89,6 +89,8 @@ class FacilityDataset(FacilityDataSyncableModel):
8989
description = models.TextField(blank=True)
9090
location = models.CharField(max_length=200, blank=True)
9191

92+
preset = models.CharField(max_length=50, choices=facility_presets.choices, default=facility_presets.default)
93+
9294
# Facility specific configuration settings
9395
learner_can_edit_username = models.BooleanField(default=True)
9496
learner_can_edit_name = models.BooleanField(default=True)

kolibri/core/device/serializers.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
from django.db import transaction
22
from django.utils.translation import check_for_language, ugettext_lazy as _
3+
from kolibri.auth.constants.facility_presets import choices, mappings
34
from kolibri.auth.models import Facility, FacilityUser
4-
from kolibri.auth.serializers import FacilityDatasetSerializer, FacilitySerializer, FacilityUserSerializer
5+
from kolibri.auth.serializers import FacilitySerializer, FacilityUserSerializer
56
from rest_framework import serializers
67

78
from .models import DevicePermissions, DeviceSettings
@@ -24,7 +25,7 @@ class Meta:
2425

2526
class DeviceProvisionSerializer(serializers.Serializer):
2627
facility = FacilitySerializer()
27-
dataset = FacilityDatasetSerializer()
28+
preset = serializers.ChoiceField(choices=choices)
2829
superuser = NoFacilityFacilityUserSerializer()
2930
language_code = serializers.CharField(max_length=15)
3031

@@ -50,7 +51,8 @@ def create(self, validated_data):
5051
"""
5152
with transaction.atomic():
5253
facility = Facility.objects.create(**validated_data.pop('facility'))
53-
dataset_data = validated_data.pop('dataset')
54+
preset = validated_data.pop('preset')
55+
dataset_data = mappings[preset]
5456
for key, value in dataset_data.items():
5557
setattr(facility.dataset, key, value)
5658
facility.dataset.save()
@@ -65,7 +67,7 @@ def create(self, validated_data):
6567
device_settings.save()
6668
return {
6769
"facility": facility,
68-
"dataset": facility.dataset,
70+
"preset": preset,
6971
"superuser": superuser,
7072
"language_code": language_code
7173
}

kolibri/core/device/test/test_api.py

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,8 @@ class DeviceProvisionTestCase(APITestCase):
1111

1212
superuser_data = {"username": "superuser", "password": "password"}
1313
facility_data = {"name": "Wilson Elementary"}
14+
preset_data = "nonformal"
1415
dataset_data = {
15-
"description": "",
16-
"location": "Somewhere over the rainbow",
1716
"learner_can_edit_username": True,
1817
"learner_can_edit_name": True,
1918
"learner_can_edit_password": True,
@@ -29,7 +28,7 @@ def test_cannot_post_if_provisioned(self):
2928
data = {
3029
"superuser": self.superuser_data,
3130
"facility": self.facility_data,
32-
"dataset": self.dataset_data,
31+
"preset": self.preset_data,
3332
"language_code": self.language_code,
3433
}
3534
response = self.client.post(reverse('deviceprovision'), data, format="json")
@@ -39,7 +38,7 @@ def test_superuser_created(self):
3938
data = {
4039
"superuser": self.superuser_data,
4140
"facility": self.facility_data,
42-
"dataset": self.dataset_data,
41+
"preset": self.preset_data,
4342
"language_code": self.language_code,
4443
}
4544
self.client.post(reverse('deviceprovision'), data, format="json")
@@ -49,7 +48,7 @@ def test_superuser_device_permissions_created(self):
4948
data = {
5049
"superuser": self.superuser_data,
5150
"facility": self.facility_data,
52-
"dataset": self.dataset_data,
51+
"preset": self.preset_data,
5352
"language_code": self.language_code,
5453
}
5554
self.client.post(reverse('deviceprovision'), data, format="json")
@@ -59,7 +58,7 @@ def test_facility_created(self):
5958
data = {
6059
"superuser": self.superuser_data,
6160
"facility": self.facility_data,
62-
"dataset": self.dataset_data,
61+
"preset": self.preset_data,
6362
"language_code": self.language_code,
6463
}
6564
self.client.post(reverse('deviceprovision'), data, format="json")
@@ -69,8 +68,13 @@ def test_dataset_set_created(self):
6968
data = {
7069
"superuser": self.superuser_data,
7170
"facility": self.facility_data,
72-
"dataset": self.dataset_data,
71+
"preset": self.preset_data,
7372
"language_code": self.language_code,
7473
}
7574
self.client.post(reverse('deviceprovision'), data, format="json")
76-
self.assertEqual(FacilityDataset.objects.get().location, self.dataset_data["location"])
75+
self.assertEqual(FacilityDataset.objects.get().learner_can_edit_username, self.dataset_data["learner_can_edit_username"])
76+
self.assertEqual(FacilityDataset.objects.get().learner_can_edit_name, self.dataset_data["learner_can_edit_name"])
77+
self.assertEqual(FacilityDataset.objects.get().learner_can_edit_password, self.dataset_data["learner_can_edit_password"])
78+
self.assertEqual(FacilityDataset.objects.get().learner_can_sign_up, self.dataset_data["learner_can_sign_up"])
79+
self.assertEqual(FacilityDataset.objects.get().learner_can_delete_account, self.dataset_data["learner_can_delete_account"])
80+
self.assertEqual(FacilityDataset.objects.get().learner_can_login_with_no_password, self.dataset_data["learner_can_login_with_no_password"])

kolibri/plugins/setup_wizard/assets/src/state/actions.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
import { DeviceProvisionResource } from 'kolibri.resources';
22
import { kolibriLogin, handleApiError } from 'kolibri.coreVue.vuex.actions';
33

4-
export function provisionDevice(store, superuser, facility, dataset, languageCode) {
4+
export function provisionDevice(store, superuser, facility, preset, language_code) {
55
const DeviceProvisionModel = DeviceProvisionResource.createModel({
66
superuser,
77
facility,
8-
dataset,
9-
language_code: languageCode,
8+
preset,
9+
language_code,
1010
});
1111
const deviceProvisionPromise = DeviceProvisionModel.save();
1212

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import presets from '../../../../../auth/constants/facility_configuration_presets.json';
2+
3+
export const facilityPresetChoices = Object.keys(presets);

kolibri/plugins/setup_wizard/assets/src/views/index.vue

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@
102102
import kTextbox from 'kolibri.coreVue.components.kTextbox';
103103
import kButton from 'kolibri.coreVue.components.kButton';
104104
import uiAlert from 'keen-ui/src/UiAlert';
105+
import { facilityPresetChoices } from '../state/constants';
105106
export default {
106107
name: 'setupWizard',
107108
$trs: {
@@ -137,6 +138,7 @@
137138
facility: '',
138139
facilityError: null,
139140
globalError: null,
141+
preset: facilityPresetChoices[0],
140142
};
141143
},
142144
components: {
@@ -169,6 +171,9 @@
169171
firstFacilityFieldVisit() {
170172
return this.facilityError === null;
171173
},
174+
presetValidityCheck() {
175+
facilityPresetChoices.includes(this.preset);
176+
},
172177
allFieldsPopulated() {
173178
return (
174179
this.passwordFieldsPopulated && this.usernameFieldPopulated && this.facilityFieldPopulated
@@ -195,17 +200,8 @@
195200
const facility = { name: this.facility };
196201
// TODO (rtibbles - paging DXCanas): Actually set these!
197202
const languageCode = 'en';
198-
const dataset = {
199-
description: '',
200-
location: 'Somewhere over the rainbow',
201-
learner_can_edit_username: true,
202-
learner_can_edit_name: true,
203-
learner_can_edit_password: true,
204-
learner_can_sign_up: true,
205-
learner_can_delete_account: true,
206-
learner_can_login_with_no_password: false,
207-
};
208-
this.provisionDevice(superuser, facility, dataset, languageCode);
203+
const preset = 'nonformal';
204+
this.provisionDevice(superuser, facility, preset, languageCode);
209205
} else {
210206
if (this.firstUsernameFieldVisit) {
211207
this.visitUsername();

0 commit comments

Comments
 (0)