Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: [close #212] Improve timezone names #333

Merged
merged 2 commits into from
Jan 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
93 changes: 42 additions & 51 deletions vanilla_installer/core/timezones.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,62 +2,53 @@

import requests
from gi.repository import GnomeDesktop
from gi.repository import GWeather
from zoneinfo import ZoneInfo

all_timezones = {}

zone_tab_path = "/usr/share/zoneinfo/zone.tab"

with open(zone_tab_path, "r") as zone_tab_file:
for line in zone_tab_file:
if line.startswith("#"):
continue

fields = line.strip().split("\t")
if len(fields) < 3:
continue

country_code, city = fields[2].rsplit("/", 1)

if country_code not in all_timezones:
all_timezones[country_code] = []

all_timezones[country_code].append(city)

all_timezones = {
country: sorted(timezones) for country, timezones in all_timezones.items()
}
all_timezones = dict(sorted(all_timezones.items()))


def get_current_timezone():
success_code, country, city = get_timezone_by_ip()
if not success_code:
timezone = GnomeDesktop.WallClock().get_timezone()
timezone = timezone.get_identifier()

country = timezone.split("/")[0]
city = timezone[country.__len__() + 1 :]
regions = {}
world = GWeather.Location.get_world()
parents = []
base = world
child = None
while True:
child = base.next_child(child)
if child is not None:
if child.get_level() == GWeather.LocationLevel.REGION:
regions[child.get_name()] = {}
current_region = child.get_name()
elif child.get_level() == GWeather.LocationLevel.COUNTRY:
regions[current_region][child.get_name()] = {}
current_country = child.get_name()
elif child.get_level() == GWeather.LocationLevel.CITY:
regions[current_region][current_country][child.get_city_name()] = child.get_timezone_str()

if child.next_child(None) is not None:
parents.append(child)
base = child
child = None
else:
base = base.get_parent()
if base is None:
break
child = parents.pop()

all_timezones = dict(sorted(regions.items()))

def get_location():
try:
res = requests.get("http://ip-api.com/json?fields=49344", timeout=3).json()
if res["status"] != "success":
raise Exception(f"get_location: request failed with message '{res['message']}'")
nearest = world.find_nearest_city(res["lat"], res["lon"])
except Exception as e:
print(e)
nearest = None

return country, city
return nearest


def get_timezone_by_ip():
try:
res = requests.get("http://ip-api.com/json", timeout=3).json()
timezone = res["timezone"]
country = timezone.split("/")[0]
city = timezone[country.__len__() + 1 :]
success_code = True
except Exception:
success_code = False
city = -1
country = -1
return success_code, country, city


def get_preview_timezone(country, city):
timezone = ZoneInfo(f"{country}/{city}")
def get_timezone_preview(tzname):
timezone = ZoneInfo(tzname)
now = datetime.datetime.now(timezone)

return now.strftime("%H:%M"), now.strftime("%A, %d %B %Y")
132 changes: 84 additions & 48 deletions vanilla_installer/defaults/timezone.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,17 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.

import re
import unicodedata

from gi.repository import Adw, Gtk
from gettext import gettext as _

from vanilla_installer.core.timezones import (
all_timezones,
get_current_timezone,
get_preview_timezone,
get_location,
get_timezone_preview,
)
from vanilla_installer.utils.run_async import RunAsync


@Gtk.Template(resource_path="/org/vanillaos/Installer/gtk/widget-timezone.ui")
Expand All @@ -32,23 +35,28 @@ class TimezoneRow(Adw.ActionRow):
select_button = Gtk.Template.Child()
country_label = Gtk.Template.Child()

def __init__(self, title, subtitle, selected_timezone, **kwargs):
def __init__(self, title, subtitle, tz_name, parent, **kwargs):
super().__init__(**kwargs)
self.title = title
self.subtitle = subtitle
self.__selected_timezone = selected_timezone
self.parent = parent
self.tz_name = tz_name

tz_time, tz_date = get_preview_timezone(subtitle, title)
tz_time, tz_date = get_timezone_preview(tz_name)

self.set_title(title)
self.set_subtitle(f"{tz_time} • {tz_date}")
self.country_label.set_label(subtitle)
self.country_label.set_label(tz_name)

self.select_button.connect("toggled", self.__on_check_button_toggled)

def __on_check_button_toggled(self, widget):
self.__selected_timezone["zone"] = self.title
self.__selected_timezone["region"] = self.subtitle
tz_split = self.tz_name.split("/", 1)
self.parent.selected_timezone["region"] = tz_split[0]
self.parent.selected_timezone["zone"] = tz_split[1]
self.parent.current_tz_label.set_label(self.tz_name)
self.parent.current_location_label.set_label(_("(at %s, %s)") % (self.title, self.subtitle))
self.parent.timezone_verify()


@Gtk.Template(resource_path="/org/vanillaos/Installer/gtk/default-timezone.ui")
Expand All @@ -58,16 +66,16 @@ class VanillaDefaultTimezone(Adw.Bin):
btn_next = Gtk.Template.Child()
entry_search_timezone = Gtk.Template.Child()
all_timezones_group = Gtk.Template.Child()
current_tz_label = Gtk.Template.Child()
current_location_label = Gtk.Template.Child()

search_controller = Gtk.EventControllerKey.new()
selected_timezone = {"region": "Europe", "zone": None}

match_regex = re.compile(r"[^a-zA-Z0-9 ]")

all_timezones_dict = {
city: continent
for continent, cities in all_timezones.items()
for city in cities
expanders_list = {
country: region
for region, countries in all_timezones.items()
for country in countries.keys()
}

def __init__(self, window, distro_info, key, step, **kwargs):
Expand All @@ -77,21 +85,18 @@ def __init__(self, window, distro_info, key, step, **kwargs):
self.__key = key
self.__step = step

self.__expanders = []
self.__tz_entries = []
self.__generate_timezone_list_widgets()

# signals
self.btn_next.connect("clicked", self.__window.next)
self.all_timezones_group.connect(
"selected-rows-changed", self.__timezone_verify
)
self.all_timezones_group.connect("row-selected", self.__timezone_verify)
self.all_timezones_group.connect("row-activated", self.__timezone_verify)
self.__window.carousel.connect("page-changed", self.__timezone_verify)
self.__window.carousel.connect("page-changed", self.timezone_verify)

self.search_controller.connect("key-released", self.__on_search_key_pressed)
self.entry_search_timezone.add_controller(self.search_controller)

def __timezone_verify(self, *args):
def timezone_verify(self, *args):
valid = (
self.selected_timezone["region"]
and self.selected_timezone["zone"] is not None
Expand All @@ -110,33 +115,64 @@ def get_finals(self):
return {"timezone": {"region": "Europe", "zone": "London"}}

def __on_search_key_pressed(self, *args):
keywords = self.match_regex.sub(
"", self.entry_search_timezone.get_text().lower()
)

for row in self.all_timezones_group:
row_title = self.match_regex.sub("", row.get_title().lower())
row.set_visible(re.search(keywords, row_title, re.IGNORECASE) is not None)
def remove_accents(msg: str):
out = unicodedata.normalize('NFD', msg)\
.encode('ascii', 'ignore')\
.decode('utf-8')
return str(out)

search_entry = self.entry_search_timezone.get_text().lower()
keywords = remove_accents(search_entry)

if len(keywords) == 0:
for expander in self.__expanders:
expander.set_visible(True)
expander.set_expanded(False)
return

current_expander = 0
current_country = self.__tz_entries[0].subtitle
visible_entries = 0
for entry in self.__tz_entries:
row_title = remove_accents(entry.get_title().lower())
match = re.search(keywords, row_title, re.IGNORECASE) is not None
entry.set_visible(match)
if entry.subtitle != current_country:
self.__expanders[current_expander].set_expanded(True)
self.__expanders[current_expander].set_visible(visible_entries != 0)
visible_entries = 0
current_country = entry.subtitle
current_expander += 1
visible_entries += 1 if match else 0

def __generate_timezone_list_widgets(self):
first_elem = None
for city, continent in self.all_timezones_dict.items():
timezone_row = TimezoneRow(city, continent, self.selected_timezone)
self.all_timezones_group.append(timezone_row)
if first_elem is None:
first_elem = timezone_row
else:
timezone_row.select_button.set_group(first_elem.select_button)

self.__set_current_timezone()

def __set_current_timezone(self):
current_country, current_city = get_current_timezone()

for i in range(len(self.all_timezones_dict)):
row = self.all_timezones_group.get_row_at_index(i)
if current_city == row.title and current_country == row.subtitle:
row.select_button.set_active(True)
self.selected_timezone["zone"] = current_city
self.selected_timezone["region"] = current_country
break
for country, region in dict(sorted(self.expanders_list.items())).items():
if len(all_timezones[region][country]) > 0:
country_tz_expander_row = Adw.ExpanderRow.new()
country_tz_expander_row.set_title(country)
country_tz_expander_row.set_subtitle(region)
self.all_timezones_group.add(country_tz_expander_row)
self.__expanders.append(country_tz_expander_row)

for city, tzname in sorted(all_timezones[region][country].items()):
timezone_row = TimezoneRow(city, country, tzname, self)
country_tz_expander_row.add_row(timezone_row)
self.__tz_entries.append(timezone_row)
if first_elem is None:
first_elem = timezone_row
else:
timezone_row.select_button.set_group(first_elem.select_button)

def __set_located_timezone(result, error):
if not result:
return
current_city = result.get_city_name()
current_country = result.get_country_name()
for entry in self.__tz_entries:
if current_city == entry.title and current_country == entry.subtitle:
self.selected_timezone["zone"] = current_city
self.selected_timezone["region"] = current_country
entry.select_button.set_active(True)
return
RunAsync(get_location, __set_located_timezone)
Loading