We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
provisiondevice creates a superuser to the wrong facility
<!--
Instructions:
 * Fill out the sections below, replace …'s with information about your issue
 * Use the 'preview' function above this text box to verify formatting before submitting
-->

### Observed behavior
<!--
Description of the behavior that was observed, including screenshots or other references when applicable
-->

When there is an existing facility on Kolibri instance, running `provisiondevice` command would set up a superuser on the existing facility instead of the new facility it is supposed to created in.
I believe this is because we didn't pass in `facility` when running `create_superuser`: https://github.com/learningequality/kolibri/blob/release-v0.14.x/kolibri/core/device/management/commands/provisiondevice.py#L87

### Expected behavior
<!--
Description of what behavior was expected but did not occur
-->

superuser should be created in the newly created facility

### User-facing consequences
<!--
Implications and real-world consequences for learners, coaches, admins, and other users of the application
-->

users are unable to log in to the facility that is created through `provisiondevice` command

### Errors and logs
<!--
Relevant logs from:
 * the command line
 * ~/.kolibri/logs/kolibri.txt
 * the browser console

Please wrap errors in triple backticks for clean formatting like this:
```
01:10 info: something happened
01:12 error: something bad happened
```
-->

…

### Steps to reproduce
<!--
Precise steps that someone else can follow in order to see this behavior
-->

1. set up kolibri with one facility
2. run `provisiondevice` to set up another facility
3. notice that the facility is created successfully, but the superuser is in the old facility

### Context
<!--
Tell us about your environment, including:
 * Kolibri version
 * Operating system
 * Browser
-->

Kolibri version: release-0.14.x


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `kolibri/core/device/management/commands/provisiondevice.py`
Content:
```
1 import json
2 import logging
3 import os
4 
5 from django.conf import settings
6 from django.core.management.base import BaseCommand
7 from django.core.management.base import CommandError
8 from django.db import transaction
9 from django.utils import six
10 
11 from kolibri.core.auth.constants.facility_presets import mappings
12 from kolibri.core.auth.constants.facility_presets import presets
13 from kolibri.core.auth.models import Facility
14 from kolibri.core.auth.models import FacilityUser
15 from kolibri.core.device.utils import provision_device
16 
17 logger = logging.getLogger(__name__)
18 
19 
20 def _check_setting(name, available, msg):
21     if name not in available:
22         raise CommandError(msg.format(name))
23 
24 
25 def check_facility_setting(name):
26     AVAILABLE_SETTINGS = [
27         "learner_can_edit_username",
28         "learner_can_edit_name",
29         "learner_can_edit_password",
30         "learner_can_sign_up",
31         "learner_can_delete_account",
32         "learner_can_login_with_no_password",
33         "show_download_button_in_learn",
34     ]
35     _check_setting(
36         name,
37         AVAILABLE_SETTINGS,
38         "'{}' is not a facility setting that can be changed by this command",
39     )
40 
41 
42 def check_device_setting(name):
43     AVAILABLE_SETTINGS = [
44         "language_id",
45         "landing_page",
46         "allow_guest_access",
47         "allow_peer_unlisted_channel_import",
48         "allow_learner_unassigned_resource_access",
49         "name",
50         "allow_other_browsers_to_connect",
51     ]
52     _check_setting(
53         name,
54         AVAILABLE_SETTINGS,
55         "'{}' is not a device setting that can be changed by this command",
56     )
57 
58 
59 def get_user_response(prompt, valid_answers=None):
60     answer = None
61     while not answer or (
62         valid_answers is not None and answer.lower() not in valid_answers
63     ):
64         answer = six.moves.input(prompt)
65     return answer.lower()
66 
67 
68 languages = dict(settings.LANGUAGES)
69 
70 
71 def create_facility(facility_name=None, preset=None, interactive=False):
72     if facility_name is None and interactive:
73         answer = get_user_response(
74             "Do you wish to create a facility? [yn] ", ["y", "n"]
75         )
76         if answer == "y":
77             facility_name = get_user_response(
78                 "What do you wish to name your facility? "
79             )
80 
81     if facility_name:
82         facility, created = Facility.objects.get_or_create(name=facility_name)
83 
84         if not created:
85             logger.warn(
86                 "Facility with name {name} already exists, not modifying preset.".format(
87                     name=facility_name
88                 )
89             )
90             return facility
91 
92         logger.info("Facility with name {name} created.".format(name=facility_name))
93 
94         if preset is None and interactive:
95             preset = get_user_response(
96                 "Which preset do you wish to use? [{presets}]: ".format(
97                     presets=",".join(presets.keys())
98                 ),
99                 valid_answers=presets,
100             )
101 
102         # Only set preset data if we have created the facility, otherwise leave previous data intact
103         if preset:
104             dataset_data = mappings[preset]
105             for key, value in dataset_data.items():
106                 check_facility_setting(key)
107                 setattr(facility.dataset, key, value)
108             facility.dataset.save()
109             logger.info("Facility preset changed to {preset}.".format(preset=preset))
110     else:
111         facility = Facility.get_default_facility() or Facility.objects.first()
112         if not facility:
113             raise CommandError("No facility exists")
114     return facility
115 
116 
117 def update_facility_settings(facility, new_settings):
118     # Override any settings passed in
119     for key, value in new_settings.items():
120         check_facility_setting(key)
121         setattr(facility.dataset, key, value)
122     facility.dataset.save()
123     logger.info("Facility settings updated with {}".format(new_settings))
124 
125 
126 def create_superuser(username=None, password=None, interactive=False):
127     if username is None and interactive:
128         username = get_user_response("Enter a username for the super user: ")
129 
130     if password is None and interactive:
131         confirm = ""
132         while password != confirm:
133             password = get_user_response("Enter a password for the super user: ")
134             confirm = get_user_response("Confirm password for the super user: ")
135 
136     if username and password:
137         if not FacilityUser.objects.filter(username__icontains=username).exists():
138             FacilityUser.objects.create_superuser(username, password)
139             logger.info(
140                 "Superuser created with username {username}.".format(username=username)
141             )
142         else:
143             logger.warn(
144                 "An account with username {username} already exists, not creating user account.".format(
145                     username=username
146                 )
147             )
148 
149 
150 def create_device_settings(
151     language_id=None, facility=None, interactive=False, new_settings={}
152 ):
153     if language_id is None and interactive:
154         language_id = get_user_response(
155             "Enter a default language code [{langs}]: ".format(
156                 langs=",".join(languages.keys())
157             ),
158             valid_answers=languages,
159         )
160     # Override any settings passed in
161     for key in new_settings:
162         check_device_setting(key)
163 
164     settings_to_set = dict(new_settings)
165     settings_to_set["language_id"] = language_id
166     settings_to_set["default_facility"] = facility
167 
168     provision_device(**settings_to_set)
169     logger.info("Device settings updated with {}".format(settings_to_set))
170 
171 
172 def json_file_contents(parser, arg):
173     if not os.path.exists(arg) or not os.path.isfile(arg):
174         return parser.error("The file '{}' does not exist".format(arg))
175     with open(arg, "r") as f:
176         try:
177             output = json.load(f)
178         except json.decoder.JSONDecodeError as e:
179             return parser.error("The file '{}' is not valid JSON:\n{}".format(arg, e))
180     return output
181 
182 
183 class Command(BaseCommand):
184     help = "Provision a device for use"
185 
186     def add_arguments(self, parser):
187         parser.add_argument(
188             "--facility", action="store", type=str, help="Facility name to create"
189         )
190         parser.add_argument(
191             "--superusername",
192             action="store",
193             type=str,
194             help="Superuser username to create",
195         )
196         parser.add_argument(
197             "--superuserpassword",
198             action="store",
199             type=str,
200             help="Superuser password to create",
201         )
202         parser.add_argument(
203             "--preset",
204             action="store",
205             type=str,
206             help="Facility preset to use",
207             choices=presets,
208         )
209         parser.add_argument(
210             "--language_id",
211             action="store",
212             type=str,
213             help="Language id for default language",
214             choices=languages,
215         )
216         parser.add_argument(
217             "--noinput",
218             "--no-input",
219             action="store_false",
220             dest="interactive",
221             default=True,
222             help="Tells Django to NOT prompt the user for input of any kind.",
223         )
224         parser.add_argument(
225             "--facility_settings",
226             action="store",
227             help="JSON file containing facility settings",
228             type=lambda arg: json_file_contents(parser, arg),
229             default={},
230         )
231         parser.add_argument(
232             "--device_settings",
233             action="store",
234             help="JSON file containing device settings",
235             type=lambda arg: json_file_contents(parser, arg),
236             default={},
237         )
238 
239     def handle(self, *args, **options):
240 
241         logger.warn(
242             "The 'provisiondevice' command is experimental, and the API and behavior will change in a future release"
243         )
244 
245         with transaction.atomic():
246             facility = create_facility(
247                 facility_name=options["facility"],
248                 preset=options["preset"],
249                 interactive=options["interactive"],
250             )
251 
252             update_facility_settings(facility, options["facility_settings"])
253 
254             create_device_settings(
255                 language_id=options["language_id"],
256                 facility=facility,
257                 interactive=options["interactive"],
258                 new_settings=options["device_settings"],
259             )
260 
261             create_superuser(
262                 username=options["superusername"],
263                 password=options["superuserpassword"],
264                 interactive=options["interactive"],
265             )
266
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/kolibri/core/device/management/commands/provisiondevice.py b/kolibri/core/device/management/commands/provisiondevice.py
--- a/kolibri/core/device/management/commands/provisiondevice.py
+++ b/kolibri/core/device/management/commands/provisiondevice.py
@@ -1,8 +1,10 @@
 import json
 import logging
 import os
+import sys
 
 from django.conf import settings
+from django.core.exceptions import ValidationError
 from django.core.management.base import BaseCommand
 from django.core.management.base import CommandError
 from django.db import transaction
@@ -77,6 +79,8 @@
             facility_name = get_user_response(
                 "What do you wish to name your facility? "
             )
+        else:
+            sys.exit(1)
 
     if facility_name:
         facility, created = Facility.objects.get_or_create(name=facility_name)
@@ -123,7 +127,7 @@
     logger.info("Facility settings updated with {}".format(new_settings))
 
 
-def create_superuser(username=None, password=None, interactive=False):
+def create_superuser(username=None, password=None, interactive=False, facility=None):
     if username is None and interactive:
         username = get_user_response("Enter a username for the super user: ")
 
@@ -134,15 +138,17 @@
             confirm = get_user_response("Confirm password for the super user: ")
 
     if username and password:
-        if not FacilityUser.objects.filter(username__icontains=username).exists():
-            FacilityUser.objects.create_superuser(username, password)
+        try:
+            FacilityUser.objects.create_superuser(username, password, facility=facility)
             logger.info(
-                "Superuser created with username {username}.".format(username=username)
+                "Superuser created with username {username} in facility {facility}.".format(
+                    username=username, facility=facility
+                )
             )
-        else:
+        except ValidationError:
             logger.warn(
-                "An account with username {username} already exists, not creating user account.".format(
-                    username=username
+                "An account with username {username} already exists in facility {facility}, not creating user account.".format(
+                    username=username, facility=facility
                 )
             )
 
@@ -262,4 +268,5 @@
                 username=options["superusername"],
                 password=options["superuserpassword"],
                 interactive=options["interactive"],
+                facility=facility,
             )

{"golden_diff": "diff --git a/kolibri/core/device/management/commands/provisiondevice.py b/kolibri/core/device/management/commands/provisiondevice.py\n--- a/kolibri/core/device/management/commands/provisiondevice.py\n+++ b/kolibri/core/device/management/commands/provisiondevice.py\n@@ -1,8 +1,10 @@\n import json\n import logging\n import os\n+import sys\n \n from django.conf import settings\n+from django.core.exceptions import ValidationError\n from django.core.management.base import BaseCommand\n from django.core.management.base import CommandError\n from django.db import transaction\n@@ -77,6 +79,8 @@\n             facility_name = get_user_response(\n                 \"What do you wish to name your facility? \"\n             )\n+        else:\n+            sys.exit(1)\n \n     if facility_name:\n         facility, created = Facility.objects.get_or_create(name=facility_name)\n@@ -123,7 +127,7 @@\n     logger.info(\"Facility settings updated with {}\".format(new_settings))\n \n \n-def create_superuser(username=None, password=None, interactive=False):\n+def create_superuser(username=None, password=None, interactive=False, facility=None):\n     if username is None and interactive:\n         username = get_user_response(\"Enter a username for the super user: \")\n \n@@ -134,15 +138,17 @@\n             confirm = get_user_response(\"Confirm password for the super user: \")\n \n     if username and password:\n-        if not FacilityUser.objects.filter(username__icontains=username).exists():\n-            FacilityUser.objects.create_superuser(username, password)\n+        try:\n+            FacilityUser.objects.create_superuser(username, password, facility=facility)\n             logger.info(\n-                \"Superuser created with username {username}.\".format(username=username)\n+                \"Superuser created with username {username} in facility {facility}.\".format(\n+                    username=username, facility=facility\n+                )\n             )\n-        else:\n+        except ValidationError:\n             logger.warn(\n-                \"An account with username {username} already exists, not creating user account.\".format(\n-                    username=username\n+                \"An account with username {username} already exists in facility {facility}, not creating user account.\".format(\n+                    username=username, facility=facility\n                 )\n             )\n \n@@ -262,4 +268,5 @@\n                 username=options[\"superusername\"],\n                 password=options[\"superuserpassword\"],\n                 interactive=options[\"interactive\"],\n+                facility=facility,\n             )\n", "issue": "provisiondevice creates a superuser to the wrong facility\n<!--\r\nInstructions:\r\n * Fill out the sections below, replace \u2026's with information about your issue\r\n * Use the 'preview' function above this text box to verify formatting before submitting\r\n-->\r\n\r\n### Observed behavior\r\n<!--\r\nDescription of the behavior that was observed, including screenshots or other references when applicable\r\n-->\r\n\r\nWhen there is an existing facility on Kolibri instance, running `provisiondevice` command would set up a superuser on the existing facility instead of the new facility it is supposed to created in.\r\nI believe this is because we didn't pass in `facility` when running `create_superuser`: https://github.com/learningequality/kolibri/blob/release-v0.14.x/kolibri/core/device/management/commands/provisiondevice.py#L87\r\n\r\n### Expected behavior\r\n<!--\r\nDescription of what behavior was expected but did not occur\r\n-->\r\n\r\nsuperuser should be created in the newly created facility\r\n\r\n### User-facing consequences\r\n<!--\r\nImplications and real-world consequences for learners, coaches, admins, and other users of the application\r\n-->\r\n\r\nusers are unable to log in to the facility that is created through `provisiondevice` command\r\n\r\n### Errors and logs\r\n<!--\r\nRelevant logs from:\r\n * the command line\r\n * ~/.kolibri/logs/kolibri.txt\r\n * the browser console\r\n\r\nPlease wrap errors in triple backticks for clean formatting like this:\r\n```\r\n01:10 info: something happened\r\n01:12 error: something bad happened\r\n```\r\n-->\r\n\r\n\u2026\r\n\r\n### Steps to reproduce\r\n<!--\r\nPrecise steps that someone else can follow in order to see this behavior\r\n-->\r\n\r\n1. set up kolibri with one facility\r\n2. run `provisiondevice` to set up another facility\r\n3. notice that the facility is created successfully, but the superuser is in the old facility\r\n\r\n### Context\r\n<!--\r\nTell us about your environment, including:\r\n * Kolibri version\r\n * Operating system\r\n * Browser\r\n-->\r\n\r\nKolibri version: release-0.14.x\r\n\n", "before_files": [{"content": "import json\nimport logging\nimport os\n\nfrom django.conf import settings\nfrom django.core.management.base import BaseCommand\nfrom django.core.management.base import CommandError\nfrom django.db import transaction\nfrom django.utils import six\n\nfrom kolibri.core.auth.constants.facility_presets import mappings\nfrom kolibri.core.auth.constants.facility_presets import presets\nfrom kolibri.core.auth.models import Facility\nfrom kolibri.core.auth.models import FacilityUser\nfrom kolibri.core.device.utils import provision_device\n\nlogger = logging.getLogger(__name__)\n\n\ndef _check_setting(name, available, msg):\n    if name not in available:\n        raise CommandError(msg.format(name))\n\n\ndef check_facility_setting(name):\n    AVAILABLE_SETTINGS = [\n        \"learner_can_edit_username\",\n        \"learner_can_edit_name\",\n        \"learner_can_edit_password\",\n        \"learner_can_sign_up\",\n        \"learner_can_delete_account\",\n        \"learner_can_login_with_no_password\",\n        \"show_download_button_in_learn\",\n    ]\n    _check_setting(\n        name,\n        AVAILABLE_SETTINGS,\n        \"'{}' is not a facility setting that can be changed by this command\",\n    )\n\n\ndef check_device_setting(name):\n    AVAILABLE_SETTINGS = [\n        \"language_id\",\n        \"landing_page\",\n        \"allow_guest_access\",\n        \"allow_peer_unlisted_channel_import\",\n        \"allow_learner_unassigned_resource_access\",\n        \"name\",\n        \"allow_other_browsers_to_connect\",\n    ]\n    _check_setting(\n        name,\n        AVAILABLE_SETTINGS,\n        \"'{}' is not a device setting that can be changed by this command\",\n    )\n\n\ndef get_user_response(prompt, valid_answers=None):\n    answer = None\n    while not answer or (\n        valid_answers is not None and answer.lower() not in valid_answers\n    ):\n        answer = six.moves.input(prompt)\n    return answer.lower()\n\n\nlanguages = dict(settings.LANGUAGES)\n\n\ndef create_facility(facility_name=None, preset=None, interactive=False):\n    if facility_name is None and interactive:\n        answer = get_user_response(\n            \"Do you wish to create a facility? [yn] \", [\"y\", \"n\"]\n        )\n        if answer == \"y\":\n            facility_name = get_user_response(\n                \"What do you wish to name your facility? \"\n            )\n\n    if facility_name:\n        facility, created = Facility.objects.get_or_create(name=facility_name)\n\n        if not created:\n            logger.warn(\n                \"Facility with name {name} already exists, not modifying preset.\".format(\n                    name=facility_name\n                )\n            )\n            return facility\n\n        logger.info(\"Facility with name {name} created.\".format(name=facility_name))\n\n        if preset is None and interactive:\n            preset = get_user_response(\n                \"Which preset do you wish to use? [{presets}]: \".format(\n                    presets=\",\".join(presets.keys())\n                ),\n                valid_answers=presets,\n            )\n\n        # Only set preset data if we have created the facility, otherwise leave previous data intact\n        if preset:\n            dataset_data = mappings[preset]\n            for key, value in dataset_data.items():\n                check_facility_setting(key)\n                setattr(facility.dataset, key, value)\n            facility.dataset.save()\n            logger.info(\"Facility preset changed to {preset}.\".format(preset=preset))\n    else:\n        facility = Facility.get_default_facility() or Facility.objects.first()\n        if not facility:\n            raise CommandError(\"No facility exists\")\n    return facility\n\n\ndef update_facility_settings(facility, new_settings):\n    # Override any settings passed in\n    for key, value in new_settings.items():\n        check_facility_setting(key)\n        setattr(facility.dataset, key, value)\n    facility.dataset.save()\n    logger.info(\"Facility settings updated with {}\".format(new_settings))\n\n\ndef create_superuser(username=None, password=None, interactive=False):\n    if username is None and interactive:\n        username = get_user_response(\"Enter a username for the super user: \")\n\n    if password is None and interactive:\n        confirm = \"\"\n        while password != confirm:\n            password = get_user_response(\"Enter a password for the super user: \")\n            confirm = get_user_response(\"Confirm password for the super user: \")\n\n    if username and password:\n        if not FacilityUser.objects.filter(username__icontains=username).exists():\n            FacilityUser.objects.create_superuser(username, password)\n            logger.info(\n                \"Superuser created with username {username}.\".format(username=username)\n            )\n        else:\n            logger.warn(\n                \"An account with username {username} already exists, not creating user account.\".format(\n                    username=username\n                )\n            )\n\n\ndef create_device_settings(\n    language_id=None, facility=None, interactive=False, new_settings={}\n):\n    if language_id is None and interactive:\n        language_id = get_user_response(\n            \"Enter a default language code [{langs}]: \".format(\n                langs=\",\".join(languages.keys())\n            ),\n            valid_answers=languages,\n        )\n    # Override any settings passed in\n    for key in new_settings:\n        check_device_setting(key)\n\n    settings_to_set = dict(new_settings)\n    settings_to_set[\"language_id\"] = language_id\n    settings_to_set[\"default_facility\"] = facility\n\n    provision_device(**settings_to_set)\n    logger.info(\"Device settings updated with {}\".format(settings_to_set))\n\n\ndef json_file_contents(parser, arg):\n    if not os.path.exists(arg) or not os.path.isfile(arg):\n        return parser.error(\"The file '{}' does not exist\".format(arg))\n    with open(arg, \"r\") as f:\n        try:\n            output = json.load(f)\n        except json.decoder.JSONDecodeError as e:\n            return parser.error(\"The file '{}' is not valid JSON:\\n{}\".format(arg, e))\n    return output\n\n\nclass Command(BaseCommand):\n    help = \"Provision a device for use\"\n\n    def add_arguments(self, parser):\n        parser.add_argument(\n            \"--facility\", action=\"store\", type=str, help=\"Facility name to create\"\n        )\n        parser.add_argument(\n            \"--superusername\",\n            action=\"store\",\n            type=str,\n            help=\"Superuser username to create\",\n        )\n        parser.add_argument(\n            \"--superuserpassword\",\n            action=\"store\",\n            type=str,\n            help=\"Superuser password to create\",\n        )\n        parser.add_argument(\n            \"--preset\",\n            action=\"store\",\n            type=str,\n            help=\"Facility preset to use\",\n            choices=presets,\n        )\n        parser.add_argument(\n            \"--language_id\",\n            action=\"store\",\n            type=str,\n            help=\"Language id for default language\",\n            choices=languages,\n        )\n        parser.add_argument(\n            \"--noinput\",\n            \"--no-input\",\n            action=\"store_false\",\n            dest=\"interactive\",\n            default=True,\n            help=\"Tells Django to NOT prompt the user for input of any kind.\",\n        )\n        parser.add_argument(\n            \"--facility_settings\",\n            action=\"store\",\n            help=\"JSON file containing facility settings\",\n            type=lambda arg: json_file_contents(parser, arg),\n            default={},\n        )\n        parser.add_argument(\n            \"--device_settings\",\n            action=\"store\",\n            help=\"JSON file containing device settings\",\n            type=lambda arg: json_file_contents(parser, arg),\n            default={},\n        )\n\n    def handle(self, *args, **options):\n\n        logger.warn(\n            \"The 'provisiondevice' command is experimental, and the API and behavior will change in a future release\"\n        )\n\n        with transaction.atomic():\n            facility = create_facility(\n                facility_name=options[\"facility\"],\n                preset=options[\"preset\"],\n                interactive=options[\"interactive\"],\n            )\n\n            update_facility_settings(facility, options[\"facility_settings\"])\n\n            create_device_settings(\n                language_id=options[\"language_id\"],\n                facility=facility,\n                interactive=options[\"interactive\"],\n                new_settings=options[\"device_settings\"],\n            )\n\n            create_superuser(\n                username=options[\"superusername\"],\n                password=options[\"superuserpassword\"],\n                interactive=options[\"interactive\"],\n            )\n", "path": "kolibri/core/device/management/commands/provisiondevice.py"}], "after_files": [{"content": "import json\nimport logging\nimport os\nimport sys\n\nfrom django.conf import settings\nfrom django.core.exceptions import ValidationError\nfrom django.core.management.base import BaseCommand\nfrom django.core.management.base import CommandError\nfrom django.db import transaction\nfrom django.utils import six\n\nfrom kolibri.core.auth.constants.facility_presets import mappings\nfrom kolibri.core.auth.constants.facility_presets import presets\nfrom kolibri.core.auth.models import Facility\nfrom kolibri.core.auth.models import FacilityUser\nfrom kolibri.core.device.utils import provision_device\n\nlogger = logging.getLogger(__name__)\n\n\ndef _check_setting(name, available, msg):\n    if name not in available:\n        raise CommandError(msg.format(name))\n\n\ndef check_facility_setting(name):\n    AVAILABLE_SETTINGS = [\n        \"learner_can_edit_username\",\n        \"learner_can_edit_name\",\n        \"learner_can_edit_password\",\n        \"learner_can_sign_up\",\n        \"learner_can_delete_account\",\n        \"learner_can_login_with_no_password\",\n        \"show_download_button_in_learn\",\n    ]\n    _check_setting(\n        name,\n        AVAILABLE_SETTINGS,\n        \"'{}' is not a facility setting that can be changed by this command\",\n    )\n\n\ndef check_device_setting(name):\n    AVAILABLE_SETTINGS = [\n        \"language_id\",\n        \"landing_page\",\n        \"allow_guest_access\",\n        \"allow_peer_unlisted_channel_import\",\n        \"allow_learner_unassigned_resource_access\",\n        \"name\",\n        \"allow_other_browsers_to_connect\",\n    ]\n    _check_setting(\n        name,\n        AVAILABLE_SETTINGS,\n        \"'{}' is not a device setting that can be changed by this command\",\n    )\n\n\ndef get_user_response(prompt, valid_answers=None):\n    answer = None\n    while not answer or (\n        valid_answers is not None and answer.lower() not in valid_answers\n    ):\n        answer = six.moves.input(prompt)\n    return answer.lower()\n\n\nlanguages = dict(settings.LANGUAGES)\n\n\ndef create_facility(facility_name=None, preset=None, interactive=False):\n    if facility_name is None and interactive:\n        answer = get_user_response(\n            \"Do you wish to create a facility? [yn] \", [\"y\", \"n\"]\n        )\n        if answer == \"y\":\n            facility_name = get_user_response(\n                \"What do you wish to name your facility? \"\n            )\n        else:\n            sys.exit(1)\n\n    if facility_name:\n        facility, created = Facility.objects.get_or_create(name=facility_name)\n\n        if not created:\n            logger.warn(\n                \"Facility with name {name} already exists, not modifying preset.\".format(\n                    name=facility_name\n                )\n            )\n            return facility\n\n        logger.info(\"Facility with name {name} created.\".format(name=facility_name))\n\n        if preset is None and interactive:\n            preset = get_user_response(\n                \"Which preset do you wish to use? [{presets}]: \".format(\n                    presets=\",\".join(presets.keys())\n                ),\n                valid_answers=presets,\n            )\n\n        # Only set preset data if we have created the facility, otherwise leave previous data intact\n        if preset:\n            dataset_data = mappings[preset]\n            for key, value in dataset_data.items():\n                check_facility_setting(key)\n                setattr(facility.dataset, key, value)\n            facility.dataset.save()\n            logger.info(\"Facility preset changed to {preset}.\".format(preset=preset))\n    else:\n        facility = Facility.get_default_facility() or Facility.objects.first()\n        if not facility:\n            raise CommandError(\"No facility exists\")\n    return facility\n\n\ndef update_facility_settings(facility, new_settings):\n    # Override any settings passed in\n    for key, value in new_settings.items():\n        check_facility_setting(key)\n        setattr(facility.dataset, key, value)\n    facility.dataset.save()\n    logger.info(\"Facility settings updated with {}\".format(new_settings))\n\n\ndef create_superuser(username=None, password=None, interactive=False, facility=None):\n    if username is None and interactive:\n        username = get_user_response(\"Enter a username for the super user: \")\n\n    if password is None and interactive:\n        confirm = \"\"\n        while password != confirm:\n            password = get_user_response(\"Enter a password for the super user: \")\n            confirm = get_user_response(\"Confirm password for the super user: \")\n\n    if username and password:\n        try:\n            FacilityUser.objects.create_superuser(username, password, facility=facility)\n            logger.info(\n                \"Superuser created with username {username} in facility {facility}.\".format(\n                    username=username, facility=facility\n                )\n            )\n        except ValidationError:\n            logger.warn(\n                \"An account with username {username} already exists in facility {facility}, not creating user account.\".format(\n                    username=username, facility=facility\n                )\n            )\n\n\ndef create_device_settings(\n    language_id=None, facility=None, interactive=False, new_settings={}\n):\n    if language_id is None and interactive:\n        language_id = get_user_response(\n            \"Enter a default language code [{langs}]: \".format(\n                langs=\",\".join(languages.keys())\n            ),\n            valid_answers=languages,\n        )\n    # Override any settings passed in\n    for key in new_settings:\n        check_device_setting(key)\n\n    settings_to_set = dict(new_settings)\n    settings_to_set[\"language_id\"] = language_id\n    settings_to_set[\"default_facility\"] = facility\n\n    provision_device(**settings_to_set)\n    logger.info(\"Device settings updated with {}\".format(settings_to_set))\n\n\ndef json_file_contents(parser, arg):\n    if not os.path.exists(arg) or not os.path.isfile(arg):\n        return parser.error(\"The file '{}' does not exist\".format(arg))\n    with open(arg, \"r\") as f:\n        try:\n            output = json.load(f)\n        except json.decoder.JSONDecodeError as e:\n            return parser.error(\"The file '{}' is not valid JSON:\\n{}\".format(arg, e))\n    return output\n\n\nclass Command(BaseCommand):\n    help = \"Provision a device for use\"\n\n    def add_arguments(self, parser):\n        parser.add_argument(\n            \"--facility\", action=\"store\", type=str, help=\"Facility name to create\"\n        )\n        parser.add_argument(\n            \"--superusername\",\n            action=\"store\",\n            type=str,\n            help=\"Superuser username to create\",\n        )\n        parser.add_argument(\n            \"--superuserpassword\",\n            action=\"store\",\n            type=str,\n            help=\"Superuser password to create\",\n        )\n        parser.add_argument(\n            \"--preset\",\n            action=\"store\",\n            type=str,\n            help=\"Facility preset to use\",\n            choices=presets,\n        )\n        parser.add_argument(\n            \"--language_id\",\n            action=\"store\",\n            type=str,\n            help=\"Language id for default language\",\n            choices=languages,\n        )\n        parser.add_argument(\n            \"--noinput\",\n            \"--no-input\",\n            action=\"store_false\",\n            dest=\"interactive\",\n            default=True,\n            help=\"Tells Django to NOT prompt the user for input of any kind.\",\n        )\n        parser.add_argument(\n            \"--facility_settings\",\n            action=\"store\",\n            help=\"JSON file containing facility settings\",\n            type=lambda arg: json_file_contents(parser, arg),\n            default={},\n        )\n        parser.add_argument(\n            \"--device_settings\",\n            action=\"store\",\n            help=\"JSON file containing device settings\",\n            type=lambda arg: json_file_contents(parser, arg),\n            default={},\n        )\n\n    def handle(self, *args, **options):\n\n        logger.warn(\n            \"The 'provisiondevice' command is experimental, and the API and behavior will change in a future release\"\n        )\n\n        with transaction.atomic():\n            facility = create_facility(\n                facility_name=options[\"facility\"],\n                preset=options[\"preset\"],\n                interactive=options[\"interactive\"],\n            )\n\n            update_facility_settings(facility, options[\"facility_settings\"])\n\n            create_device_settings(\n                language_id=options[\"language_id\"],\n                facility=facility,\n                interactive=options[\"interactive\"],\n                new_settings=options[\"device_settings\"],\n            )\n\n            create_superuser(\n                username=options[\"superusername\"],\n                password=options[\"superuserpassword\"],\n                interactive=options[\"interactive\"],\n                facility=facility,\n            )\n", "path": "kolibri/core/device/management/commands/provisiondevice.py"}]}