Merge pull request #26058 from regisb/regisb/toggle-state-report-unittests
[BD-21] Improve test coverage of toggle state view
This commit is contained in:
@@ -12,14 +12,14 @@ from waffle.testutils import override_switch
|
||||
from common.djangoapps.student.tests.factories import UserFactory
|
||||
|
||||
from .. import WaffleFlag, WaffleFlagNamespace
|
||||
from ..views import ToggleStateView
|
||||
from .. import models
|
||||
from .. import views as toggle_state_views
|
||||
|
||||
TEST_WAFFLE_FLAG_NAMESPACE = WaffleFlagNamespace("test")
|
||||
TEST_WAFFLE_FLAG = WaffleFlag(TEST_WAFFLE_FLAG_NAMESPACE, "flag", __name__)
|
||||
|
||||
|
||||
# TODO: Missing coverage for:
|
||||
# - course overrides
|
||||
# - computed_status
|
||||
class ToggleStateViewTests(TestCase):
|
||||
|
||||
@@ -144,11 +144,26 @@ class ToggleStateViewTests(TestCase):
|
||||
][0]
|
||||
self.assertNotIn("code_owner", result)
|
||||
|
||||
def test_course_overrides(self):
|
||||
models.WaffleFlagCourseOverrideModel.objects.create(waffle_flag="my.flag", enabled=True)
|
||||
course_overrides = {}
|
||||
|
||||
# pylint: disable=protected-access
|
||||
toggle_state_views._add_waffle_flag_course_override_state(course_overrides)
|
||||
toggle_state_views._add_waffle_flag_computed_status(course_overrides)
|
||||
|
||||
self.assertIn("my.flag", course_overrides)
|
||||
self.assertIn("course_overrides", course_overrides["my.flag"])
|
||||
self.assertEqual(1, len(course_overrides["my.flag"]["course_overrides"]))
|
||||
self.assertEqual("None", course_overrides["my.flag"]["course_overrides"][0]["course_id"])
|
||||
self.assertEqual("on", course_overrides["my.flag"]["course_overrides"][0]["force"])
|
||||
self.assertEqual("both", course_overrides["my.flag"]["computed_status"])
|
||||
|
||||
def _get_toggle_state_response(self, is_staff=True):
|
||||
request = APIRequestFactory().get('/api/toggles/state/')
|
||||
user = UserFactory()
|
||||
user.is_staff = is_staff
|
||||
request.user = user
|
||||
view = ToggleStateView.as_view()
|
||||
view = toggle_state_views.ToggleStateView.as_view()
|
||||
response = view(request)
|
||||
return response
|
||||
|
||||
@@ -26,235 +26,250 @@ class ToggleStateView(views.APIView):
|
||||
|
||||
def get(self, request):
|
||||
response = OrderedDict()
|
||||
response['waffle_flags'] = self._get_all_waffle_flags()
|
||||
response['waffle_switches'] = self._get_all_waffle_switches()
|
||||
response['django_settings'] = self._get_settings_state()
|
||||
response['waffle_flags'] = _get_all_waffle_flags()
|
||||
response['waffle_switches'] = _get_all_waffle_switches()
|
||||
response['django_settings'] = _get_settings_state()
|
||||
return Response(response)
|
||||
|
||||
def _get_all_waffle_switches(self):
|
||||
"""
|
||||
Gets all waffle switches and their state.
|
||||
"""
|
||||
switches_dict = {}
|
||||
self._add_waffle_switch_instances(switches_dict)
|
||||
self._add_waffle_switch_state(switches_dict)
|
||||
self._add_waffle_switch_computed_status(switches_dict)
|
||||
switch_list = list(switches_dict.values())
|
||||
switch_list.sort(key=lambda toggle: toggle['name'])
|
||||
return switch_list
|
||||
|
||||
def _add_waffle_switch_instances(self, switches_dict):
|
||||
"""
|
||||
Add details from waffle switch instances, like code_owner.
|
||||
"""
|
||||
waffle_switch_instances = WaffleSwitch.get_instances()
|
||||
for switch_instance in waffle_switch_instances:
|
||||
switch = self._get_or_create_toggle_response(switches_dict, switch_instance.name)
|
||||
self._add_toggle_instance_details(switch, switch_instance)
|
||||
def _get_all_waffle_switches():
|
||||
"""
|
||||
Gets all waffle switches and their state.
|
||||
"""
|
||||
switches_dict = {}
|
||||
_add_waffle_switch_instances(switches_dict)
|
||||
_add_waffle_switch_state(switches_dict)
|
||||
_add_waffle_switch_computed_status(switches_dict)
|
||||
switch_list = list(switches_dict.values())
|
||||
switch_list.sort(key=lambda toggle: toggle['name'])
|
||||
return switch_list
|
||||
|
||||
def _add_waffle_switch_state(self, switches_dict):
|
||||
"""
|
||||
Add waffle switch state from the waffle Switch model.
|
||||
"""
|
||||
waffle_switches = Switch.objects.all()
|
||||
for switch_data in waffle_switches:
|
||||
switch = self._get_or_create_toggle_response(switches_dict, switch_data.name)
|
||||
switch['is_active'] = 'true' if switch_data.active else 'false'
|
||||
if switch_data.note:
|
||||
switch['note'] = switch_data.note
|
||||
switch['created'] = str(switch_data.created)
|
||||
switch['modified'] = str(switch_data.modified)
|
||||
|
||||
def _add_waffle_switch_computed_status(self, switch_dict):
|
||||
"""
|
||||
Add computed status to each waffle switch.
|
||||
"""
|
||||
for switch in switch_dict.values():
|
||||
computed_status = 'off'
|
||||
if 'is_active' in switch:
|
||||
if switch['is_active'] == 'true':
|
||||
computed_status = 'on'
|
||||
else:
|
||||
computed_status = 'off'
|
||||
switch['computed_status'] = computed_status
|
||||
def _add_waffle_switch_instances(switches_dict):
|
||||
"""
|
||||
Add details from waffle switch instances, like code_owner.
|
||||
"""
|
||||
waffle_switch_instances = WaffleSwitch.get_instances()
|
||||
for switch_instance in waffle_switch_instances:
|
||||
switch = _get_or_create_toggle_response(switches_dict, switch_instance.name)
|
||||
_add_toggle_instance_details(switch, switch_instance)
|
||||
|
||||
def _get_all_waffle_flags(self):
|
||||
"""
|
||||
Gets all waffle flags and their state.
|
||||
"""
|
||||
flags_dict = {}
|
||||
self._add_waffle_flag_instances(flags_dict)
|
||||
self._add_waffle_flag_state(flags_dict)
|
||||
self._add_waffle_flag_course_override_state(flags_dict)
|
||||
self._add_waffle_flag_computed_status(flags_dict)
|
||||
flag_list = list(flags_dict.values())
|
||||
flag_list.sort(key=lambda toggle: toggle['name'])
|
||||
return flag_list
|
||||
|
||||
def _add_waffle_flag_instances(self, flags_dict):
|
||||
"""
|
||||
Add details from waffle flag instances, like code_owner.
|
||||
"""
|
||||
waffle_flag_instances = WaffleFlag.get_instances()
|
||||
for flag_instance in waffle_flag_instances:
|
||||
flag = self._get_or_create_toggle_response(flags_dict, flag_instance.name)
|
||||
self._add_toggle_instance_details(flag, flag_instance)
|
||||
def _add_waffle_switch_state(switches_dict):
|
||||
"""
|
||||
Add waffle switch state from the waffle Switch model.
|
||||
"""
|
||||
waffle_switches = Switch.objects.all()
|
||||
for switch_data in waffle_switches:
|
||||
switch = _get_or_create_toggle_response(switches_dict, switch_data.name)
|
||||
switch['is_active'] = 'true' if switch_data.active else 'false'
|
||||
if switch_data.note:
|
||||
switch['note'] = switch_data.note
|
||||
switch['created'] = str(switch_data.created)
|
||||
switch['modified'] = str(switch_data.modified)
|
||||
|
||||
def _add_toggle_instance_details(self, toggle, toggle_instance):
|
||||
"""
|
||||
Add details (class, module, code_owner) from a specific toggle instance.
|
||||
"""
|
||||
toggle['class'] = toggle_instance.__class__.__name__
|
||||
toggle['module'] = toggle_instance.module_name
|
||||
if toggle_instance.module_name:
|
||||
code_owner = get_code_owner_from_module(toggle_instance.module_name)
|
||||
if code_owner:
|
||||
toggle['code_owner'] = code_owner
|
||||
|
||||
def _add_waffle_flag_state(self, flags_dict):
|
||||
"""
|
||||
Add waffle flag state from the waffle Flag model.
|
||||
"""
|
||||
waffle_flags = Flag.objects.all()
|
||||
for flag_data in waffle_flags:
|
||||
flag = self._get_or_create_toggle_response(flags_dict, flag_data.name)
|
||||
if flag_data.everyone is True:
|
||||
everyone = 'yes'
|
||||
elif flag_data.everyone is False:
|
||||
everyone = 'no'
|
||||
def _add_waffle_switch_computed_status(switch_dict):
|
||||
"""
|
||||
Add computed status to each waffle switch.
|
||||
"""
|
||||
for switch in switch_dict.values():
|
||||
computed_status = 'off'
|
||||
if 'is_active' in switch:
|
||||
if switch['is_active'] == 'true':
|
||||
computed_status = 'on'
|
||||
else:
|
||||
everyone = 'unknown'
|
||||
flag['everyone'] = everyone
|
||||
if flag_data.note:
|
||||
flag['note'] = flag_data.note
|
||||
flag['created'] = str(flag_data.created)
|
||||
flag['modified'] = str(flag_data.modified)
|
||||
computed_status = 'off'
|
||||
switch['computed_status'] = computed_status
|
||||
|
||||
def _add_waffle_flag_course_override_state(self, flags_dict):
|
||||
"""
|
||||
Add waffle flag course override state from the WaffleFlagCourseOverrideModel model.
|
||||
"""
|
||||
# This dict is keyed by flag name, and contains dicts keyed by course_id, the contains
|
||||
# the final dict of metadata for a single course override that will be returned.
|
||||
flag_course_overrides = OrderedDict()
|
||||
# Note: We can't just get enabled records, because if a historical record is enabled but
|
||||
# the current record is disabled, we would not know this. We get all records, and mark
|
||||
# some overrides as disabled, and then later filter the disabled records.
|
||||
course_overrides_data = WaffleFlagCourseOverrideModel.objects.all()
|
||||
course_overrides_data = course_overrides_data.order_by('waffle_flag', 'course_id', '-change_date')
|
||||
for course_override_data in course_overrides_data:
|
||||
flag_name = course_override_data.waffle_flag
|
||||
course_id = str(course_override_data.course_id)
|
||||
if flag_name not in flag_course_overrides:
|
||||
flag_course_overrides[flag_name] = OrderedDict()
|
||||
course_overrides = flag_course_overrides[flag_name]
|
||||
if course_id not in course_overrides:
|
||||
course_overrides[course_id] = OrderedDict()
|
||||
course_override = course_overrides[course_id]
|
||||
# data is reverse ordered by date, so the first record is the current record
|
||||
if 'course_id' not in course_override:
|
||||
course_override['course_id'] = course_id
|
||||
if not course_override_data.enabled:
|
||||
# The current record may be disabled, but later history might be enabled.
|
||||
# We'll filter these disabled records below.
|
||||
course_override['disabled'] = True
|
||||
else:
|
||||
course_override['force'] = course_override_data.override_choice
|
||||
course_override['modified'] = str(course_override_data.change_date)
|
||||
# data is reverse ordered by date, so the last record is the oldest record
|
||||
course_override['created'] = str(course_override_data.change_date)
|
||||
|
||||
for flag_name, course_overrides_dict in flag_course_overrides.items():
|
||||
course_overrides = [
|
||||
course_override for course_override in course_overrides_dict.values()
|
||||
if 'disabled' not in course_override
|
||||
]
|
||||
if course_overrides:
|
||||
flag = self._get_or_create_toggle_response(flags_dict, flag_name)
|
||||
flag['course_overrides'] = course_overrides
|
||||
def _get_all_waffle_flags():
|
||||
"""
|
||||
Gets all waffle flags and their state.
|
||||
"""
|
||||
flags_dict = {}
|
||||
_add_waffle_flag_instances(flags_dict)
|
||||
_add_waffle_flag_state(flags_dict)
|
||||
_add_waffle_flag_course_override_state(flags_dict)
|
||||
_add_waffle_flag_computed_status(flags_dict)
|
||||
flag_list = list(flags_dict.values())
|
||||
flag_list.sort(key=lambda toggle: toggle['name'])
|
||||
return flag_list
|
||||
|
||||
def _add_waffle_flag_computed_status(self, flags_dict):
|
||||
"""
|
||||
Add computed status to each waffle flag.
|
||||
"""
|
||||
for flag in flags_dict.values():
|
||||
computed_status = 'off'
|
||||
if 'everyone' in flag:
|
||||
if flag['everyone'] == 'yes':
|
||||
computed_status = 'on'
|
||||
elif flag['everyone'] == 'unknown':
|
||||
computed_status = 'both'
|
||||
# check course overrides only if computed_status is not already 'both'
|
||||
if computed_status != 'both' and 'course_overrides' in flag:
|
||||
has_force_on = any(override['force'] == 'on' for override in flag['course_overrides'])
|
||||
has_force_off = any(override['force'] == 'off' for override in flag['course_overrides'])
|
||||
if has_force_on and has_force_off:
|
||||
computed_status = 'both'
|
||||
elif has_force_on:
|
||||
computed_status = 'on' if computed_status == 'on' else 'both'
|
||||
elif has_force_off:
|
||||
computed_status = 'off' if computed_status == 'off' else 'both'
|
||||
flag['computed_status'] = computed_status
|
||||
|
||||
def _get_or_create_toggle_response(self, toggles_dict, toggle_name):
|
||||
"""
|
||||
Gets or creates a toggle response dict and adds it to the toggles_dict.
|
||||
def _add_waffle_flag_instances(flags_dict):
|
||||
"""
|
||||
Add details from waffle flag instances, like code_owner.
|
||||
"""
|
||||
waffle_flag_instances = WaffleFlag.get_instances()
|
||||
for flag_instance in waffle_flag_instances:
|
||||
flag = _get_or_create_toggle_response(flags_dict, flag_instance.name)
|
||||
_add_toggle_instance_details(flag, flag_instance)
|
||||
|
||||
Returns:
|
||||
Either the pre-existing toggle response, or a new toggle dict with its name set.
|
||||
|
||||
"""
|
||||
if toggle_name in toggles_dict:
|
||||
return toggles_dict[toggle_name]
|
||||
toggle = OrderedDict()
|
||||
toggle['name'] = toggle_name
|
||||
toggles_dict[toggle_name] = toggle
|
||||
return toggle
|
||||
def _add_waffle_flag_state(flags_dict):
|
||||
"""
|
||||
Add waffle flag state from the waffle Flag model.
|
||||
"""
|
||||
waffle_flags = Flag.objects.all()
|
||||
for flag_data in waffle_flags:
|
||||
flag = _get_or_create_toggle_response(flags_dict, flag_data.name)
|
||||
if flag_data.everyone is True:
|
||||
everyone = 'yes'
|
||||
elif flag_data.everyone is False:
|
||||
everyone = 'no'
|
||||
else:
|
||||
everyone = 'unknown'
|
||||
flag['everyone'] = everyone
|
||||
if flag_data.note:
|
||||
flag['note'] = flag_data.note
|
||||
flag['created'] = str(flag_data.created)
|
||||
flag['modified'] = str(flag_data.modified)
|
||||
|
||||
def _get_settings_state(self):
|
||||
"""
|
||||
Return a list of setting-based toggles: Django settings, SettingToggle and SettingDictToggle instances.
|
||||
SettingToggle and SettingDictToggle override the settings with identical names (if any).
|
||||
"""
|
||||
settings_dict = {}
|
||||
self._add_settings(settings_dict)
|
||||
self._add_setting_toggles(settings_dict)
|
||||
self._add_setting_dict_toggles(settings_dict)
|
||||
return sorted(settings_dict.values(), key=(lambda toggle: toggle['name']))
|
||||
|
||||
def _add_settings(self, settings_dict):
|
||||
"""
|
||||
Fill the `settings_dict`: will only include values that are set to true or false.
|
||||
"""
|
||||
for setting_name, setting_value in vars(settings).items():
|
||||
if isinstance(setting_value, dict):
|
||||
for dict_name, dict_value in setting_value.items():
|
||||
if isinstance(dict_value, bool):
|
||||
name = setting_dict_name(setting_name, dict_name)
|
||||
toggle_response = self._get_or_create_toggle_response(settings_dict, name)
|
||||
toggle_response['is_active'] = dict_value
|
||||
elif isinstance(setting_value, bool):
|
||||
toggle_response = self._get_or_create_toggle_response(settings_dict, setting_name)
|
||||
toggle_response['is_active'] = setting_value
|
||||
def _add_waffle_flag_course_override_state(flags_dict):
|
||||
"""
|
||||
Add waffle flag course override state from the WaffleFlagCourseOverrideModel model.
|
||||
"""
|
||||
# This dict is keyed by flag name, and contains dicts keyed by course_id, the contains
|
||||
# the final dict of metadata for a single course override that will be returned.
|
||||
flag_course_overrides = OrderedDict()
|
||||
# Note: We can't just get enabled records, because if a historical record is enabled but
|
||||
# the current record is disabled, we would not know this. We get all records, and mark
|
||||
# some overrides as disabled, and then later filter the disabled records.
|
||||
course_overrides_data = WaffleFlagCourseOverrideModel.objects.all()
|
||||
course_overrides_data = course_overrides_data.order_by('waffle_flag', 'course_id', '-change_date')
|
||||
for course_override_data in course_overrides_data:
|
||||
flag_name = course_override_data.waffle_flag
|
||||
course_id = str(course_override_data.course_id)
|
||||
if flag_name not in flag_course_overrides:
|
||||
flag_course_overrides[flag_name] = OrderedDict()
|
||||
course_overrides = flag_course_overrides[flag_name]
|
||||
if course_id not in course_overrides:
|
||||
course_overrides[course_id] = OrderedDict()
|
||||
course_override = course_overrides[course_id]
|
||||
# data is reverse ordered by date, so the first record is the current record
|
||||
if 'course_id' not in course_override:
|
||||
course_override['course_id'] = course_id
|
||||
if not course_override_data.enabled:
|
||||
# The current record may be disabled, but later history might be enabled.
|
||||
# We'll filter these disabled records below.
|
||||
course_override['disabled'] = True
|
||||
else:
|
||||
course_override['force'] = course_override_data.override_choice
|
||||
course_override['modified'] = str(course_override_data.change_date)
|
||||
# data is reverse ordered by date, so the last record is the oldest record
|
||||
course_override['created'] = str(course_override_data.change_date)
|
||||
|
||||
def _add_setting_toggles(self, settings_dict):
|
||||
"""
|
||||
Fill the `settings_dict` with values from the list of SettingToggle instances.
|
||||
"""
|
||||
for toggle in SettingToggle.get_instances():
|
||||
toggle_response = self._get_or_create_toggle_response(settings_dict, toggle.name)
|
||||
toggle_response["is_active"] = toggle.is_enabled()
|
||||
self._add_toggle_instance_details(toggle_response, toggle)
|
||||
for flag_name, course_overrides_dict in flag_course_overrides.items():
|
||||
course_overrides = [
|
||||
course_override for course_override in course_overrides_dict.values()
|
||||
if 'disabled' not in course_override
|
||||
]
|
||||
if course_overrides:
|
||||
flag = _get_or_create_toggle_response(flags_dict, flag_name)
|
||||
flag['course_overrides'] = course_overrides
|
||||
|
||||
def _add_setting_dict_toggles(self, settings_dict):
|
||||
"""
|
||||
Fill the `settings_dict` with values from the list of SettingDictToggle instances.
|
||||
"""
|
||||
for toggle in SettingDictToggle.get_instances():
|
||||
name = setting_dict_name(toggle.name, toggle.key)
|
||||
toggle_response = self._get_or_create_toggle_response(settings_dict, name)
|
||||
toggle_response["is_active"] = toggle.is_enabled()
|
||||
self._add_toggle_instance_details(toggle_response, toggle)
|
||||
|
||||
def _add_waffle_flag_computed_status(flags_dict):
|
||||
"""
|
||||
Add computed status to each waffle flag.
|
||||
"""
|
||||
for flag in flags_dict.values():
|
||||
computed_status = 'off'
|
||||
if 'everyone' in flag:
|
||||
if flag['everyone'] == 'yes':
|
||||
computed_status = 'on'
|
||||
elif flag['everyone'] == 'unknown':
|
||||
computed_status = 'both'
|
||||
# check course overrides only if computed_status is not already 'both'
|
||||
if computed_status != 'both' and 'course_overrides' in flag:
|
||||
has_force_on = any(override['force'] == 'on' for override in flag['course_overrides'])
|
||||
has_force_off = any(override['force'] == 'off' for override in flag['course_overrides'])
|
||||
if has_force_on and has_force_off:
|
||||
computed_status = 'both'
|
||||
elif has_force_on:
|
||||
computed_status = 'on' if computed_status == 'on' else 'both'
|
||||
elif has_force_off:
|
||||
computed_status = 'off' if computed_status == 'off' else 'both'
|
||||
flag['computed_status'] = computed_status
|
||||
|
||||
|
||||
def _get_settings_state():
|
||||
"""
|
||||
Return a list of setting-based toggles: Django settings, SettingToggle and SettingDictToggle instances.
|
||||
SettingToggle and SettingDictToggle override the settings with identical names (if any).
|
||||
"""
|
||||
settings_dict = {}
|
||||
_add_settings(settings_dict)
|
||||
_add_setting_toggles(settings_dict)
|
||||
_add_setting_dict_toggles(settings_dict)
|
||||
return sorted(settings_dict.values(), key=(lambda toggle: toggle['name']))
|
||||
|
||||
|
||||
def _add_settings(settings_dict):
|
||||
"""
|
||||
Fill the `settings_dict`: will only include values that are set to true or false.
|
||||
"""
|
||||
for setting_name, setting_value in vars(settings).items():
|
||||
if isinstance(setting_value, dict):
|
||||
for dict_name, dict_value in setting_value.items():
|
||||
if isinstance(dict_value, bool):
|
||||
name = setting_dict_name(setting_name, dict_name)
|
||||
toggle_response = _get_or_create_toggle_response(settings_dict, name)
|
||||
toggle_response['is_active'] = dict_value
|
||||
elif isinstance(setting_value, bool):
|
||||
toggle_response = _get_or_create_toggle_response(settings_dict, setting_name)
|
||||
toggle_response['is_active'] = setting_value
|
||||
|
||||
|
||||
def _add_setting_toggles(settings_dict):
|
||||
"""
|
||||
Fill the `settings_dict` with values from the list of SettingToggle instances.
|
||||
"""
|
||||
for toggle in SettingToggle.get_instances():
|
||||
toggle_response = _get_or_create_toggle_response(settings_dict, toggle.name)
|
||||
toggle_response["is_active"] = toggle.is_enabled()
|
||||
_add_toggle_instance_details(toggle_response, toggle)
|
||||
|
||||
|
||||
def _add_toggle_instance_details(toggle, toggle_instance):
|
||||
"""
|
||||
Add details (class, module, code_owner) from a specific toggle instance.
|
||||
"""
|
||||
toggle['class'] = toggle_instance.__class__.__name__
|
||||
toggle['module'] = toggle_instance.module_name
|
||||
if toggle_instance.module_name:
|
||||
code_owner = get_code_owner_from_module(toggle_instance.module_name)
|
||||
if code_owner:
|
||||
toggle['code_owner'] = code_owner
|
||||
|
||||
|
||||
def _add_setting_dict_toggles(settings_dict):
|
||||
"""
|
||||
Fill the `settings_dict` with values from the list of SettingDictToggle instances.
|
||||
"""
|
||||
for toggle in SettingDictToggle.get_instances():
|
||||
name = setting_dict_name(toggle.name, toggle.key)
|
||||
toggle_response = _get_or_create_toggle_response(settings_dict, name)
|
||||
toggle_response["is_active"] = toggle.is_enabled()
|
||||
_add_toggle_instance_details(toggle_response, toggle)
|
||||
|
||||
|
||||
def _get_or_create_toggle_response(toggles_dict, toggle_name):
|
||||
"""
|
||||
Gets or creates a toggle response dict and adds it to the toggles_dict.
|
||||
|
||||
Returns:
|
||||
Either the pre-existing toggle response, or a new toggle dict with its name set.
|
||||
|
||||
"""
|
||||
if toggle_name in toggles_dict:
|
||||
return toggles_dict[toggle_name]
|
||||
toggle = OrderedDict()
|
||||
toggle['name'] = toggle_name
|
||||
toggles_dict[toggle_name] = toggle
|
||||
return toggle
|
||||
|
||||
|
||||
def setting_dict_name(dict_name, key):
|
||||
|
||||
Reference in New Issue
Block a user