Skip to content

Commit

Permalink
sort choices for more natural human use
Browse files Browse the repository at this point in the history
Sorting the choices in forms helps with a more natural selection process
  • Loading branch information
hrbonz committed Apr 23, 2024
1 parent 4207dc0 commit 181276b
Show file tree
Hide file tree
Showing 3 changed files with 49 additions and 15 deletions.
20 changes: 15 additions & 5 deletions tests/test_choices.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,21 @@ def tzs3_names():
]


@pytest.fixture
def tzs3_names_sorted():
yield [
"America/Argentina/Buenos_Aires",
"America/Los_Angeles",
"Europe/London",
]


@pytest.fixture
def tzs3_standard_displays():
yield [
"America/Argentina/Buenos Aires",
"America/Los Angeles",
"Europe/London",
"America/Argentina/Buenos Aires",
]


Expand Down Expand Up @@ -104,10 +113,11 @@ def test_with_gmt_offset_transition_backward(use_pytz, utc_tzobj):
assert with_gmt_offset(tz_names, now=after, use_pytz=use_pytz) == [("Europe/London", "GMT+00:00 Europe/London")]


def test_standard_using_timezone_names(tzs3_names, tzs3_standard_displays):
assert standard(tzs3_names) == list(zip(tzs3_names, tzs3_standard_displays))
def test_standard_using_timezone_names(tzs3_names, tzs3_names_sorted, tzs3_standard_displays):
assert standard(tzs3_names) == list(zip(tzs3_names_sorted, tzs3_standard_displays))


def test_standard_using_timezone_objects(tzs3_names, tzs3_standard_displays, to_tzobj):
def test_standard_using_timezone_objects(tzs3_names, tzs3_names_sorted, tzs3_standard_displays, to_tzobj):
tzs3_objects = [to_tzobj(tz) for tz in tzs3_names]
assert standard(tzs3_objects) == list(zip(tzs3_objects, tzs3_standard_displays))
tzs3_objects_sorted = [to_tzobj(tz) for tz in tzs3_names_sorted]
assert standard(tzs3_objects) == list(zip(tzs3_objects_sorted, tzs3_standard_displays))
18 changes: 10 additions & 8 deletions tests/test_choices_display_option.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from collections import Counter

import pytest
from django import forms
from django.db import models
Expand Down Expand Up @@ -104,7 +106,7 @@ def test_form_field_invalid_choices_display(use_pytz):
def test_form_field_none(ChoicesDisplayForm, base_tzstrs):
form = ChoicesDisplayForm()
values, displays = zip(*form.fields["tz_none"].choices)
assert values == tuple(base_tzstrs)
assert sorted(values) == sorted(base_tzstrs)
assert displays[values.index("America/Los_Angeles")] == "America/Los Angeles"
assert displays[values.index("Asia/Kolkata")] == "Asia/Kolkata"

Expand Down Expand Up @@ -136,10 +138,10 @@ def test_form_field_limited_none(ChoicesDisplayForm):
def test_form_field_limited_standard(ChoicesDisplayForm):
form = ChoicesDisplayForm()
assert form.fields["tz_limited_standard"].choices == [
("Asia/Tokyo", "Asia/Tokyo"),
("Asia/Dubai", "Asia/Dubai"),
("America/Argentina/Buenos_Aires", "America/Argentina/Buenos Aires"),
("Africa/Nairobi", "Africa/Nairobi"),
("America/Argentina/Buenos_Aires", "America/Argentina/Buenos Aires"),
("Asia/Dubai", "Asia/Dubai"),
("Asia/Tokyo", "Asia/Tokyo"),
]


Expand All @@ -156,7 +158,7 @@ def test_form_field_limited_with_gmt_offset(ChoicesDisplayForm):
def test_model_form_field_none(ChoicesDisplayModelForm, to_tzobj, base_tzobjs):
form = ChoicesDisplayModelForm()
values, displays = zip(*form.fields["tz_none"].choices)
assert values == ("",) + tuple(base_tzobjs)
assert Counter(values) == Counter(("",) + tuple(base_tzobjs))
assert displays[values.index(to_tzobj("America/Los_Angeles"))] == "America/Los Angeles"
assert displays[values.index(to_tzobj("Asia/Kolkata"))] == "Asia/Kolkata"

Expand Down Expand Up @@ -192,10 +194,10 @@ def test_moel_form_field_limited_standard(ChoicesDisplayModelForm, to_tzobj):
form = ChoicesDisplayModelForm()
assert form.fields["tz_limited_standard"].choices == [
("", "---------"),
(to_tzobj("Asia/Tokyo"), "Asia/Tokyo"),
(to_tzobj("Asia/Dubai"), "Asia/Dubai"),
(to_tzobj("America/Argentina/Buenos_Aires"), "America/Argentina/Buenos Aires"),
(to_tzobj("Africa/Nairobi"), "Africa/Nairobi"),
(to_tzobj("America/Argentina/Buenos_Aires"), "America/Argentina/Buenos Aires"),
(to_tzobj("Asia/Dubai"), "Asia/Dubai"),
(to_tzobj("Asia/Tokyo"), "Asia/Tokyo"),
]


Expand Down
26 changes: 24 additions & 2 deletions timezone_field/choices.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,28 @@
from timezone_field.backends import get_tz_backend


def normalize_standard(tztuple):
"""Normalize timezone names by replacing special characters with space.
For proper sorting, using spaces makes comparisons more consistent.
:param str tztuple: tuple of timezone and representation
"""
return tztuple[1].translate(str.maketrans({"-": " ", "_": " "}))


def normalize_gmt(tztuple):
"""Normalize timezone GMT names for sorting.
For proper sorting, using GMT values as a positive or negative number.
:param str tztuple: tuple of timezone and representation
"""
gmt = tztuple[1].split()[0]
cmp = gmt.replace("GMT", "").replace(":", "")
return int(cmp)


def standard(timezones):
"""
Given a list of timezones (either strings of timezone objects),
Expand All @@ -14,7 +36,7 @@ def standard(timezones):
for tz in timezones:
tz_str = str(tz)
choices.append((tz, tz_str.replace("_", " ")))
return choices
return sorted(choices, key=normalize_standard)


def with_gmt_offset(timezones, now=None, use_pytz=None):
Expand All @@ -41,4 +63,4 @@ def with_gmt_offset(timezones, now=None, use_pytz=None):
_choices.append((delta, tz, display))
_choices.sort(key=lambda x: x[0])
choices = [(one, two) for zero, one, two in _choices]
return choices
return sorted(choices, key=normalize_gmt)

0 comments on commit 181276b

Please sign in to comment.