Skip to content

Commit 3d6e5c2

Browse files
committed
Update user experience and hints. Fixes #5
1 parent bfbe651 commit 3d6e5c2

File tree

2 files changed

+154
-112
lines changed

2 files changed

+154
-112
lines changed

plugin/ui.py

Lines changed: 48 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# -*- coding: utf-8 -*-
2-
2+
import textwrap
33
import copy
4+
import locale
45
import plugin.utils
56

67
from typing import List
@@ -11,11 +12,15 @@
1112

1213
class Main(FlowLauncher):
1314
messages_queue = []
15+
locale.setlocale(locale.LC_NUMERIC, "")
1416

15-
def sendNormalMess(self, title: str, subtitle: str):
17+
def sendNormalMess(
18+
self, title: str, subtitle: str, iconpath: str = "assets/favicon.ico"
19+
):
1620
message = copy.deepcopy(RESULT_TEMPLATE)
1721
message["Title"] = title
1822
message["SubTitle"] = subtitle
23+
message["IcoPath"] = iconpath
1924

2025
self.messages_queue.append(message)
2126

@@ -36,29 +41,61 @@ def sendActionMess(self, title: str, subtitle: str, method: str, value: List):
3641
def query(self, param: str) -> List[dict]:
3742
q = param.strip()
3843
args = q.split(" ")
39-
if len(args) == 2:
40-
hints = plugin.utils.get_hints(args[1].lower())
44+
# Just keyword - show all units
45+
if len(args) == 1:
46+
all_units = plugin.utils.get_all_units()
47+
self.sendNormalMess(
48+
(_("General Converter")),
49+
_("<Hotkey> <Amount> <Source unit> <Destination unit>"),
50+
)
51+
for cat in all_units:
52+
title = str(cat[0])
53+
subtitle = ", ".join([str(elem) for elem in cat[1:]])
54+
lines = textwrap.wrap(subtitle, 110, break_long_words=False)
55+
if len(lines) > 1:
56+
self.sendNormalMess((title), (lines[0]), f"assets/{title}.ico")
57+
for line in range(1, len(lines)):
58+
self.sendNormalMess(
59+
(title), (lines[line]), f"assets/{title}.ico"
60+
)
61+
else:
62+
self.sendNormalMess((title), (subtitle), f"assets/{title}.ico")
63+
# Keyword and first unit to convert from - show what it can be converted to
64+
elif len(args) == 2:
65+
hints = plugin.utils.get_hints_for_category(args[1].lower())
4166
self.sendNormalMess(
4267
_("Available conversions"),
4368
(f"{args[0]} {args[1]} to {', '.join(hints)}"),
4469
)
70+
# Keyword and two units to convert from and to - try to convert
4571
elif len(args) == 3:
4672
try:
4773
# Units are currently case insensitive. May need to change this if in future new units
4874
# with official upper case shorthand are catered for
4975
args[1] = args[1].lower()
5076
args[2] = args[2].lower()
51-
do_convert = plugin.utils.genConvert(float(args[0]), args[1], args[2])
77+
do_convert = plugin.utils.gen_convert(float(args[0]), args[1], args[2])
5278
if "Error" in do_convert:
53-
self.sendNormalMess(
54-
_("Error - {}").format(do_convert["Error"]),
55-
_("Check documentation for accepted units"),
56-
)
79+
if do_convert["Error"] == "To and from unit is the same":
80+
self.sendNormalMess(
81+
_("{}".format(do_convert["Error"])),
82+
_("Choose two different units"),
83+
)
84+
else:
85+
self.sendNormalMess(
86+
# f strings seem to break babel so use string formatting instead
87+
_("Error - {}").format(do_convert["Error"]),
88+
_("Check documentation for accepted units"),
89+
)
5790
else:
5891
self.sendNormalMess(
59-
(f"{args[0]} {args[1]} = {do_convert[args[2]]:.6f} {args[2]}"),
60-
"",
92+
(do_convert["category"]),
93+
(
94+
f"{locale.format_string('%.6g', float(args[0]), grouping=True)} {args[1]} = {locale.format_string('%f', do_convert['converted'], grouping=True)} {args[2]}"
95+
),
96+
f"assets/{do_convert['category']}.ico",
6197
)
98+
do_convert = []
6299
except Exception as e:
63100
self.sendNormalMess(_("Error - {}").format(repr(e)), "")
64101
# Always show the usage while there isn't a valid query

plugin/utils.py

Lines changed: 106 additions & 101 deletions
Original file line numberDiff line numberDiff line change
@@ -1,109 +1,114 @@
1+
import plugin.units as gc_units
12
from plugin.extensions import _
23

3-
units = {
4-
"m": {
5-
"mm": 1000,
6-
"cm": 100,
7-
"km": 0.001,
8-
"in": 39.37008,
9-
"ft": 3.28084,
10-
"yd": 1.093613,
11-
"mi": 0.0006213712,
12-
},
13-
"ml": {
14-
"gm": 1,
15-
"l": 0.001,
16-
"pt": 0.002113383,
17-
"qt": 0.001056691,
18-
"cup": 0.004226764,
19-
"tbsp": 0.067628224,
20-
"tsp": 0.2028846715942,
21-
"gal": 0.0002641727499999601,
22-
"floz": 0.03381413,
23-
},
24-
"sqm": {
25-
"h": 0.0001,
26-
"ac": 0.0002471052,
27-
"sqcm": 10000,
28-
"sqkm": 1000000,
29-
"sqin": 1550.003,
30-
"sqmi": 0.0000003861022,
31-
"sqft": 10.76391,
32-
"sqyd": 1.19599,
33-
},
34-
"gm": {
35-
"kg": 0.001,
36-
"lb": 0.002205,
37-
"oz": 0.035274,
38-
"st": 0.000157473,
39-
"ton": 0.000001102310999995,
40-
},
41-
# Special case temp C to F - actually handled in code
42-
"c": {
43-
"f": 1.8,
44-
},
45-
}
46-
47-
48-
def get_hints(from_unit):
49-
""" Takes an input unit and returns a list of units it can be converted to """
50-
for u in units:
51-
if u == from_unit:
52-
return [x for x in units[from_unit].keys()]
53-
for u2 in units[u]:
54-
if u2 == from_unit:
55-
c = [x for x in units[u].keys() if x != from_unit]
56-
c.append(u)
57-
return c
58-
return ["no valid units"]
59-
60-
61-
def genConvert(amount, from_unit, to_unit):
62-
""" Convert between units """
4+
5+
def get_hints_for_category(from_unit: str):
6+
"""Takes an input unit and returns a list of units it can be converted to
7+
8+
:param from_short: unit abbreviation
9+
:type amount: str
10+
11+
:rtype: list
12+
:return: A list of other unit abbreviations in the same category
13+
"""
14+
c = []
15+
category = ""
16+
17+
# Find the category it's in
18+
for u in gc_units.units:
19+
for u2 in gc_units.units[u]:
20+
if u2[0] == from_unit:
21+
category = str(u)
22+
if category:
23+
# Go back and iterate over the category again and get all the units that are not the from unit
24+
for uu in gc_units.units[category]:
25+
if uu[0] != from_unit:
26+
c.append(uu[0])
27+
if not c:
28+
return ["no valid units"]
29+
return c
30+
else:
31+
return ["no valid units"]
32+
33+
34+
def get_all_units(short=False):
35+
"""Returns all available units as a list of lists by category
36+
37+
:param short: if True only unit abbreviations are returned, default is False
38+
:type amount: bool
39+
40+
:rtype: list of lists
41+
:return: A list of lists for each category in units. Index 0 of each internal list
42+
is the category description
43+
"""
44+
45+
full_list = []
46+
for u in gc_units.units:
47+
cat_list = []
48+
cat_list.append(u)
49+
for u2 in gc_units.units[u]:
50+
cat_list.append(u2[0] if short else f"{u2[1]} ({u2[0]})")
51+
full_list.append(cat_list)
52+
return full_list
53+
54+
55+
def gen_convert(amount: float, from_unit: str, to_unit: str):
56+
"""Converts from one unit to another
57+
58+
:param amount: amount of source unit to convert
59+
:type amount: float
60+
:param from_unit: abbreviation of unit to convert from
61+
:type from_unit: str
62+
:param to_unit: abbreviation of unit to convert to
63+
:type to_unit: str
64+
65+
:rtype: dict
66+
:return: if to_unit and from_unit are valid returns a dictionary
67+
{
68+
"category":{category of units},
69+
"converted":{converted amount},
70+
"fromabbrev":{from unit abbreviation},
71+
"fromlong":{from unit long name},
72+
"fromplural":{from unit plural name},
73+
"toabbrev":{to unit abbreviation},
74+
"tolong":{to unit long name},
75+
"toplural":{to unit plural name},
76+
}
77+
78+
else returns a dictionary with error status
79+
{"Error": {error text}}
80+
"""
6381
conversions = {}
82+
found_from = found_to = []
6483
if from_unit == to_unit:
6584
conversions["Error"] = _("To and from unit is the same")
6685
return conversions
67-
# Handle celsius and farenheit as special cases as they aren't converted by exponents
68-
elif from_unit == "c" and to_unit == "f":
69-
conversions[to_unit] = (amount * 1.8) + 32
70-
return conversions
71-
elif from_unit == "f" and to_unit == "c":
72-
conversions[to_unit] = (amount - 32) / 1.8
73-
return conversions
74-
# Convert from key unit to sub-unit (e.g. cm to in)
75-
elif from_unit in units and to_unit in units[from_unit]:
76-
conversions[to_unit] = units[from_unit][to_unit] * amount
77-
return conversions
78-
# Convert from sub-unit to key unit (e.g. in to cm)
79-
elif to_unit in units and from_unit in units[to_unit]:
80-
conversions[to_unit] = (1 / units[to_unit][from_unit]) * amount
81-
return conversions
82-
# Convert from sub-unit to sub-unit (e.g. in to ft)
86+
for u in gc_units.units:
87+
for u2 in gc_units.units[u]:
88+
if u2[0] == from_unit:
89+
found_from = u2
90+
if u2[0] == to_unit:
91+
found_to = u2
92+
# If we haven't both in the same category, reset
93+
if found_to and found_from:
94+
found_category = u
95+
break
96+
else:
97+
found_from = found_to = []
98+
if found_to and found_from:
99+
base_unit_conversion = eval(found_from[3].replace("x", str(amount)))
100+
final_conversion = eval(found_to[4].replace("x", str(base_unit_conversion)))
101+
conversions["category"] = found_category
102+
conversions["converted"] = final_conversion
103+
conversions["fromabbrev"] = found_from[0]
104+
conversions["fromlong"] = found_from[1]
105+
conversions["fromplural"] = found_from[2]
106+
conversions["toabbrev"] = found_to[0]
107+
conversions["tolong"] = found_to[1]
108+
conversions["toplural"] = found_to[2]
109+
83110
else:
84-
for u in units:
85-
to_sub = False
86-
from_sub = False
87-
for u2 in units[u]:
88-
if u2 == to_unit:
89-
to_sub = True
90-
elif u2 == from_unit:
91-
from_sub = True
92-
if to_sub and from_sub:
93-
# Convert via the key unit
94-
conversions[to_unit] = (1 / units[u][from_unit]) * (
95-
units[u][to_unit] * amount
96-
)
97-
return conversions
98-
conversions["Error"] = _("Problem converting {} and {}").format(
99-
from_unit, to_unit
111+
conversions["Error"] = _(
112+
"Problem converting {} and {}".format(from_unit, to_unit)
100113
)
101-
return conversions
102-
103-
104-
def listUnits():
105-
# Utility function to print out all conversions
106-
for all_units in units.keys():
107-
print(f"\n{all_units}, ", end="")
108-
for all_to_units in units[all_units].keys():
109-
print(f"{all_to_units}, ", end="")
114+
return conversions

0 commit comments

Comments
 (0)