Skip to content
This repository has been archived by the owner on Jan 26, 2023. It is now read-only.

Commit

Permalink
Merge pull request #2 from DenverCoder1/albert-0.18
Browse files Browse the repository at this point in the history
  • Loading branch information
DenverCoder1 authored Jan 10, 2023
2 parents 286debd + 3aad0fe commit 493baed
Show file tree
Hide file tree
Showing 3 changed files with 129 additions and 110 deletions.
22 changes: 10 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,37 +10,35 @@ Extension for converting units of length, mass, speed, temperature, time, curren

## Installation

1. Locate the `modules` directory in the Python extension data directory.
1. Locate the `plugins` directory in the Python extension data directory.

The data directories reside in the data directories of the application defined by Qt. Hence on Linux the modules would be looked up in the following directories (in this order):

```
~/.local/share/albert/org.albert.extension.python/modules
/usr/local/share/albert/org.albert.extension.python/modules
/usr/share/albert/org.albert.extension.python/modules
~/.local/share/albert/python/plugins
/usr/local/share/albert/python/plugins
/usr/share/albert/python/plugins
```

(Note: Double-clicking on a module in the settings will open the directory in the file manager.)

2. Clone this repository into your `modules` directory.
2. Clone this repository into your `plugins` directory.

```bash
cd /path/to/modules
cd /path/to/plugins # see above for the path

git clone https://github.com/DenverCoder1/unit-converter-albert-ext.git
```

3. Ensure that `pint` and `inflect` are installed using pip.
3. Ensure that `pint` and `inflect` are installed in `~/.local/share/albert/python/site-packages`.

```bash
python3 -m pip install -U pint
cd unit-converter-albert-ext

python3 -m pip install -U inflect
pip install -r requirements.txt -t ~/.local/share/albert/python/site-packages
```

4. Enable the extension in the settings under `Extensions > Python`.

![settings](https://user-images.githubusercontent.com/20955511/147167375-85d2bf2a-cd2b-4635-9c66-1d55dbe24d91.png)
![settings](https://user-images.githubusercontent.com/20955511/211470845-1c23dcd7-d81a-49ed-ab37-7c52d8bfb6c1.png)

## Usage

Expand Down
213 changes: 117 additions & 96 deletions __init__.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
# -*- coding: utf-8 -*-

from __future__ import annotations

import json
import re
import traceback
from pathlib import Path
from typing import Dict, List, Union

import albert
import pint
Expand All @@ -23,21 +24,26 @@
`32 degrees F to C`
`3.14159 rad to degrees`
"""
__title__ = "Unit Converter"
__version__ = "0.0.1"
__authors__ = "Jonah Lawrence"
__py_deps__ = ["pint", "inflect"]

md_iid = "0.5"
md_version = "1.0"
md_name = "Unit Converter"
md_description = "Convert length, mass, speed, temperature, time, and more"
md_license = "MIT"
md_url = "https://github.com/DenverCoder1/unit-converter-albert-ext"
md_lib_dependencies = ["pint", "inflect"]
md_maintainers = "@DenverCoder1"

unit_convert_regex = re.compile(
r"(?P<from_amount>[-]?\d+[.]?\d*)\s?(?P<from_unit>.*)\s(?:to|in)\s(?P<to_unit>.*)",
r"(?P<from_amount>-?\d+\.?\d*)\s?(?P<from_unit>.*)\s(?:to|in)\s(?P<to_unit>.*)",
re.I,
)

units = pint.UnitRegistry()
inflect_engine = inflect.engine()


def load_config(config_path: Path) -> str:
def load_config(config_path: Path) -> dict[str, dict[str, str]]:
"""
Strip comments and load the config from the config file.
"""
Expand Down Expand Up @@ -104,7 +110,7 @@ def __display_unit_name(self, amount: float, unit: pint.Unit) -> str:
unit = self.__pluralize_unit(unit) if amount != 1 else str(unit)
return self.display_names.get(unit, unit)

def __round_or_truncate(self, num: float) -> Union[float, int]:
def __round_or_truncate(self, num: float) -> float | int:
"""
Round floating point number to 2 decimal places or convert to an integer if ends with .0
Expand Down Expand Up @@ -153,7 +159,7 @@ def __init__(self):
"""
Initialize the UnitConverter
"""
self.aliases: Dict[str, str] = config.get("aliases", {})
self.aliases: dict[str, str] = config.get("aliases", {})

def _get_unit(self, unit: str) -> pint.Unit:
"""
Expand All @@ -172,21 +178,18 @@ def _get_unit(self, unit: str) -> pint.Unit:
pint.errors.UndefinedUnitError: If the unit is not valid
"""
unit = self.aliases.get(unit, unit)
try:
if units.__contains__(unit):
# return the unit if it is valid
return units.__getattr__(unit)
except pint.errors.UndefinedUnitError:
# check if the lowercase version is a valid unit
return units.__getattr__(unit.lower())
# check if the lowercase version is a valid unit
return units.__getattr__(unit.lower())

def convert_units(
self, amount: str, from_unit: str, to_unit: str
) -> ConversionResult:
def convert_units(self, amount: float, from_unit: str, to_unit: str) -> ConversionResult:
"""
Convert a unit to another unit
Args:
amount (str): The amount to convert
amount (float): The amount to convert
from_unit (str): The unit to convert from
to_unit (str): The unit to convert to
Expand All @@ -197,7 +200,7 @@ def convert_units(
pint.errors.UndefinedUnitError: If the unit is not valid
pint.errors.DimensionalityError: If the units are not compatible
"""
input_unit = units.Quantity(float(amount), self._get_unit(from_unit))
input_unit = units.Quantity(amount, self._get_unit(from_unit))
output_unit = self._get_unit(to_unit)
result = input_unit.to(output_unit)
return ConversionResult(
Expand All @@ -208,86 +211,104 @@ def convert_units(
)


def create_item(text: str, subtext: str, icon: str = "") -> albert.Item:
"""
Create an albert.Item from a text and subtext
class Plugin(albert.QueryHandler):
def id(self) -> str:
return __name__

def name(self) -> str:
return md_name

def description(self) -> str:
return md_description

def synopsis(self) -> str:
return "<from_amount> <from_unit> {to|in} <to_unit>" if self.defaultTrigger() else ""

def defaultTrigger(self) -> str:
return ""

def handleQuery(self, query: albert.Query) -> None:
"""Handler for a query received from Albert."""
query_string = query.string.strip()
match = unit_convert_regex.fullmatch(query_string)
if match:
albert.info(f"Matched {query_string}")
try:
items = self.get_items(
float(match.group("from_amount")),
match.group("from_unit").strip(),
match.group("to_unit").strip(),
)
query.add(items)
except Exception as error:
albert.warning(f"Error: {error}")
tb = "".join(
traceback.format_exception(error.__class__, error, error.__traceback__)
)
albert.warning(tb)
albert.info("Something went wrong. Make sure you're using the correct format.")

def create_item(self, text: str, subtext: str, icon: str = "") -> albert.Item:
"""
Create an albert.Item from a text and subtext
Args:
text (str): The text to display
subtext (str): The subtext to display
icon (Optional[str]): The icon to display. If not specified, the default icon will be used
Args:
text (str): The text to display
subtext (str): The subtext to display
icon (Optional[str]): The icon to display. If not specified, the default icon will be used
Returns:
albert.Item: The item to be added to the list of results
"""
icon_path = Path(__file__).parent / "icons" / icon
if not icon or not icon_path.exists():
albert.warning(f"Icon {icon} does not exist")
icon_path = Path(__file__).parent / "icons" / "unit_converter.svg"
return albert.Item(
id=__title__,
icon=str(icon_path),
text=text,
subtext=subtext,
actions=[albert.ClipAction("Copy result to clipboard", text)],
)


def get_items(amount: float, from_unit: str, to_unit: str) -> List[albert.Item]:
"""
Generate the Albert items to display for the query
Returns:
albert.Item: The item to be added to the list of results
"""
icon_path = Path(__file__).parent / "icons" / icon
if not icon or not icon_path.exists():
albert.warning(f"Icon {icon} does not exist")
icon_path = Path(__file__).parent / "icons" / "unit_converter.svg"
return albert.Item(
id=self.name(),
icon=[str(icon_path)],
text=text,
subtext=subtext,
actions=[
albert.Action(
id="copy",
text="Copy result to clipboard",
callable=lambda: albert.setClipboardText(text=text),
)
],
)

Args:
amount (float): The amount to convert from
from_unit (str): The unit to convert from
to_unit (str): The unit to convert to
def get_items(self, amount: float, from_unit: str, to_unit: str) -> list[albert.Item]:
"""
Generate the Albert items to display for the query
Returns:
List[albert.Item]: The list of items to display
"""
uc = UnitConverter()
try:
# convert the units
result = uc.convert_units(amount, from_unit, to_unit)
# return the result
return [
create_item(
result.formatted_result,
f"Converted from {result.formatted_from}",
result.icon,
)
]
except pint.errors.DimensionalityError as e:
albert.warning(f"DimensionalityError: {e}")
albert.warning(traceback.format_exc())
return [
create_item(f"Unable to convert {amount} {from_unit} to {to_unit}", str(e))
]
except pint.errors.UndefinedUnitError as e:
albert.warning(f"UndefinedUnitError: {e}")
albert.warning(traceback.format_exc())


def handleQuery(query: albert.Query) -> List[albert.Item]:
"""
Handler for a query received from Albert.
"""
query_string = query.string.strip()
match = unit_convert_regex.fullmatch(query_string)
if match:
albert.info(f"Matched {query_string}")
Args:
amount (float): The amount to convert from
from_unit (str): The unit to convert from
to_unit (str): The unit to convert to
Returns:
List[albert.Item]: The list of items to display
"""
uc = UnitConverter()
try:
return get_items(
float(match.group("from_amount")),
match.group("from_unit").strip(),
match.group("to_unit").strip(),
)
except Exception as error:
albert.warning(f"Error: {error}")
tb = "".join(
traceback.format_exception(error.__class__, error, error.__traceback__)
)
albert.warning(tb)
albert.info(
"Something went wrong. Make sure you're using the correct format."
)
# convert the units
result = uc.convert_units(amount, from_unit, to_unit)
# return the result
return [
self.create_item(
result.formatted_result,
f"Converted from {result.formatted_from}",
result.icon,
)
]
except pint.errors.DimensionalityError as e:
albert.warning(f"DimensionalityError: {e}")
albert.warning(traceback.format_exc())
return [
self.create_item(f"Unable to convert {amount} {from_unit} to {to_unit}", str(e))
]
except pint.errors.UndefinedUnitError as e:
albert.warning(f"UndefinedUnitError: {e}")
albert.warning(traceback.format_exc())
return []
4 changes: 2 additions & 2 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
inflect==5.3.0
Pint==0.18
inflect==6.0.2
Pint==0.20.1

0 comments on commit 493baed

Please sign in to comment.