We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
`kolibri manage provisiondevice` converts user-provided facility name to lower-case
### Observed behavior

In #7493, it was noted that `provisiondevice` converts facility names to lower-case, which might not be desirable.

This is probably because of this line in the code

https://github.com/learningequality/kolibri/blob/bf91a09cb7ea666f013f3488ea28d8391cefa1d1/kolibri/core/device/management/commands/provisiondevice.py#L78-L82

where `get_user_response` is the code that tries to normalize user input in the terminal (which includes converting to lower case).

### Expected behavior

When a user provides a facility name in the `provisiondevice` CLI tool, the eventual facility name matches the user's inputted facility name character-by-character. E.g. if I type "Jonathan's Facility", it doesn't convert it to "jonathan's facility"


### User-facing consequences

Facility name does not match what was provided in CLI, and admin will need to edit the name.

### Steps to reproduce


1. Use `kolibri manage provisiondevice`
1. At the new facility step, provide a name like "MY FACILITY"
1. When you actually view the Facility in Kolibri, it will have the name "my facility"

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