surplus: implement groundwork for #18
This commit is contained in:
parent
fd372fb483
commit
3b136c30ba
22
README.md
22
README.md
|
@ -54,21 +54,25 @@ pip install git+https://github.com/markjoshwel/surplus.git@future
|
||||||
## command-line usage
|
## command-line usage
|
||||||
|
|
||||||
```text
|
```text
|
||||||
usage: surplus [-h] [-d] [-v] [query ...]
|
usage: surplus [-h] [-d] [-v] [-c {pluscode,localcode,latlong,string}]
|
||||||
|
[query ...]
|
||||||
|
|
||||||
Google Maps Plus Code to iOS Shortcuts-like shareable text
|
Google Maps Plus Code to iOS Shortcuts-like shareable text
|
||||||
|
|
||||||
positional arguments:
|
positional arguments:
|
||||||
query full-length Plus Code (6PH58QMF+FX), shortened
|
query full-length Plus Code (6PH58QMF+FX), shortened
|
||||||
Plus Code/'local code' (8QMF+FX Singapore),
|
Plus Code/'local code' (8QMF+FX Singapore),
|
||||||
latlong (1.3336875, 103.7749375), or string
|
latlong (1.3336875, 103.7749375), or string
|
||||||
query (e.g., 'Wisma Atria')
|
query (e.g., 'Wisma Atria')
|
||||||
|
|
||||||
options:
|
options:
|
||||||
-h, --help show this help message and exit
|
-h, --help show this help message and exit
|
||||||
-d, --debug prints lat, long and reverser response dict to
|
-d, --debug prints lat, long and reverser response dict to
|
||||||
stderr
|
stderr
|
||||||
-v, --version prints version information to stderr and exits
|
-v, --version prints version information to stderr and exits
|
||||||
|
-c {pluscode,localcode,latlong,string},
|
||||||
|
--convert-to {pluscode,localcode,latlong,string}
|
||||||
|
converts query to another type
|
||||||
```
|
```
|
||||||
|
|
||||||
## developer's guide
|
## developer's guide
|
||||||
|
|
144
surplus.py
144
surplus.py
|
@ -26,11 +26,12 @@ OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||||
OTHER DEALINGS IN THE SOFTWARE.
|
OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
For more information, please refer to <http:m/unlicense.org/>
|
For more information, please refer to <http://unlicense.org/>
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from argparse import ArgumentParser
|
from argparse import ArgumentParser
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
|
from enum import Enum
|
||||||
from sys import stderr, stdout
|
from sys import stderr, stdout
|
||||||
from typing import (
|
from typing import (
|
||||||
Any,
|
Any,
|
||||||
|
@ -41,6 +42,7 @@ from typing import (
|
||||||
TextIO,
|
TextIO,
|
||||||
TypeAlias,
|
TypeAlias,
|
||||||
TypeVar,
|
TypeVar,
|
||||||
|
Sequence,
|
||||||
)
|
)
|
||||||
|
|
||||||
from geopy import Location as _geopy_Location # type: ignore
|
from geopy import Location as _geopy_Location # type: ignore
|
||||||
|
@ -125,6 +127,23 @@ class IncompletePlusCodeError(Exception):
|
||||||
|
|
||||||
# data structures
|
# data structures
|
||||||
|
|
||||||
|
|
||||||
|
class SurplusQueryTypes(Enum):
|
||||||
|
"""enum representing the mode of surplus"""
|
||||||
|
|
||||||
|
PLUS_CODE = "pluscode"
|
||||||
|
LOCAL_CODE = "localcode"
|
||||||
|
LATLONG = "latlong"
|
||||||
|
STRING = "string"
|
||||||
|
|
||||||
|
|
||||||
|
class SurplusOperationMode(Enum):
|
||||||
|
"""enum representing the mode of surplus"""
|
||||||
|
|
||||||
|
GENERATE_TEXT = "generate"
|
||||||
|
CONVERT_TYPES = "convert"
|
||||||
|
|
||||||
|
|
||||||
ResultType = TypeVar("ResultType")
|
ResultType = TypeVar("ResultType")
|
||||||
|
|
||||||
|
|
||||||
|
@ -215,6 +234,10 @@ class Latlong(NamedTuple):
|
||||||
longitude: float
|
longitude: float
|
||||||
|
|
||||||
def __str__(self) -> str:
|
def __str__(self) -> str:
|
||||||
|
"""
|
||||||
|
method that returns a comma-and-space-seperated string of self.latitude and
|
||||||
|
self.longitude
|
||||||
|
"""
|
||||||
return f"{self.latitude}, {self.longitude}"
|
return f"{self.latitude}, {self.longitude}"
|
||||||
|
|
||||||
|
|
||||||
|
@ -256,7 +279,10 @@ class PlusCodeQuery(NamedTuple):
|
||||||
|
|
||||||
except KeyError:
|
except KeyError:
|
||||||
return Result[Latlong](
|
return Result[Latlong](
|
||||||
EMPTY_LATLONG, error=IncompletePlusCodeError("Plus Code is not full-length (e.g., 6PH58QMF+FX)"),
|
EMPTY_LATLONG,
|
||||||
|
error=IncompletePlusCodeError(
|
||||||
|
"Plus Code is not full-length (e.g., 6PH58QMF+FX)"
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
|
@ -447,6 +473,11 @@ class Behaviour(NamedTuple):
|
||||||
TextIO-like object representing a writeable file. defaults to sys.stdout.
|
TextIO-like object representing a writeable file. defaults to sys.stdout.
|
||||||
debug: bool = False
|
debug: bool = False
|
||||||
whether to print debug information to stderr
|
whether to print debug information to stderr
|
||||||
|
operation_mode: SurplusOperationMode = SurplusOperationMode.GENERATE_TEXT
|
||||||
|
surplus operation mode enum value
|
||||||
|
convert_to_type: SurplusQueryTypes | None = None
|
||||||
|
surplus query type enum value for when
|
||||||
|
operation_mode = SurplusOperationMode.CONVERT_TYPES
|
||||||
"""
|
"""
|
||||||
|
|
||||||
query: list[str]
|
query: list[str]
|
||||||
|
@ -455,6 +486,9 @@ class Behaviour(NamedTuple):
|
||||||
stderr: TextIO = stderr
|
stderr: TextIO = stderr
|
||||||
stdout: TextIO = stdout
|
stdout: TextIO = stdout
|
||||||
debug: bool = False
|
debug: bool = False
|
||||||
|
version_header: bool = False
|
||||||
|
operation_mode: SurplusOperationMode = SurplusOperationMode.GENERATE_TEXT
|
||||||
|
convert_to_type: SurplusQueryTypes | None = None
|
||||||
|
|
||||||
|
|
||||||
# functions
|
# functions
|
||||||
|
@ -505,12 +539,14 @@ def parse_query(
|
||||||
LatlongQuery(EMPTY_LATLONG),
|
LatlongQuery(EMPTY_LATLONG),
|
||||||
error="unable to find a Plus Code",
|
error="unable to find a Plus Code",
|
||||||
)
|
)
|
||||||
|
|
||||||
# did find plus code, but not full-length. :(
|
# did find plus code, but not full-length. :(
|
||||||
if not validator.is_full(portion_plus_code):
|
if not validator.is_full(portion_plus_code):
|
||||||
return Result[Query](
|
return Result[Query](
|
||||||
LatlongQuery(EMPTY_LATLONG),
|
LatlongQuery(EMPTY_LATLONG),
|
||||||
error=IncompletePlusCodeError("Plus Code is not full-length (e.g., 6PH58QMF+FX)"),
|
error=IncompletePlusCodeError(
|
||||||
|
"Plus Code is not full-length (e.g., 6PH58QMF+FX)"
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
# found a plus code!
|
# found a plus code!
|
||||||
|
@ -557,9 +593,11 @@ def parse_query(
|
||||||
# found one!
|
# found one!
|
||||||
return Result[Query](mpc_result.get())
|
return Result[Query](mpc_result.get())
|
||||||
|
|
||||||
|
# is a plus/local code, but missing details
|
||||||
if isinstance(mpc_result.error, IncompletePlusCodeError):
|
if isinstance(mpc_result.error, IncompletePlusCodeError):
|
||||||
return mpc_result
|
return mpc_result # propagate back up to caller
|
||||||
|
|
||||||
|
# not a plus/local code, try to match for latlong or string query
|
||||||
match behaviour.query:
|
match behaviour.query:
|
||||||
case [single]:
|
case [single]:
|
||||||
# possibly a:
|
# possibly a:
|
||||||
|
@ -656,8 +694,27 @@ def handle_args() -> Behaviour:
|
||||||
default=False,
|
default=False,
|
||||||
help="prints version information to stderr and exits",
|
help="prints version information to stderr and exits",
|
||||||
)
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"-c",
|
||||||
|
"--convert-to",
|
||||||
|
type=str,
|
||||||
|
choices=[str(v.value) for v in SurplusQueryTypes],
|
||||||
|
help="converts query to another type",
|
||||||
|
default="",
|
||||||
|
)
|
||||||
|
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
convert_to_type: SurplusQueryTypes | None = None
|
||||||
|
if args.convert_to != "":
|
||||||
|
convert_to_type = SurplusQueryTypes(args.convert_to)
|
||||||
|
|
||||||
|
operation_mode: SurplusOperationMode = (
|
||||||
|
SurplusOperationMode.GENERATE_TEXT
|
||||||
|
if convert_to_type is None
|
||||||
|
else SurplusOperationMode.CONVERT_TYPES
|
||||||
|
)
|
||||||
|
|
||||||
behaviour = Behaviour(
|
behaviour = Behaviour(
|
||||||
query=args.query,
|
query=args.query,
|
||||||
geocoder=default_geocoder,
|
geocoder=default_geocoder,
|
||||||
|
@ -665,19 +722,11 @@ def handle_args() -> Behaviour:
|
||||||
stderr=stderr,
|
stderr=stderr,
|
||||||
stdout=stdout,
|
stdout=stdout,
|
||||||
debug=args.debug,
|
debug=args.debug,
|
||||||
|
version_header=args.version,
|
||||||
|
operation_mode=operation_mode,
|
||||||
|
convert_to_type=convert_to_type,
|
||||||
)
|
)
|
||||||
|
|
||||||
# print header
|
|
||||||
|
|
||||||
(behaviour.stdout if behaviour.debug else behaviour.stderr).write(
|
|
||||||
f"surplus version {'.'.join([str(v) for v in VERSION])}"
|
|
||||||
+ (f", debug mode" if behaviour.debug else "")
|
|
||||||
+ "\n"
|
|
||||||
)
|
|
||||||
|
|
||||||
if args.version:
|
|
||||||
exit(0)
|
|
||||||
|
|
||||||
return behaviour
|
return behaviour
|
||||||
|
|
||||||
|
|
||||||
|
@ -685,30 +734,87 @@ def surplus(
|
||||||
query: PlusCodeQuery | LocalCodeQuery | LatlongQuery | StringQuery,
|
query: PlusCodeQuery | LocalCodeQuery | LatlongQuery | StringQuery,
|
||||||
behaviour: Behaviour,
|
behaviour: Behaviour,
|
||||||
) -> Result[str]:
|
) -> Result[str]:
|
||||||
return Result[str]("", error=NotImplementedError())
|
"""
|
||||||
|
query to shareable text conversion function
|
||||||
|
|
||||||
|
query: PlusCodeQuery | LocalCodeQuery | LatlongQuery | StringQuery
|
||||||
|
query object to convert, see respective docstrings for more information on each
|
||||||
|
type of query object
|
||||||
|
behaviour: Behaviour
|
||||||
|
program behaviour namedtuple
|
||||||
|
"""
|
||||||
|
|
||||||
|
def _unique(l: Sequence[str]) -> list[str]:
|
||||||
|
"""(internal function) returns a in-order unique list from list"""
|
||||||
|
unique: OrderedDict = OrderedDict()
|
||||||
|
for line in l:
|
||||||
|
unique.update({line: None})
|
||||||
|
return list(unique.keys())
|
||||||
|
|
||||||
|
def _generate_text(line_keys: Sequence[str]) -> str:
|
||||||
|
"""(internal function) TODO DOCSTRING"""
|
||||||
|
# TODO
|
||||||
|
return ""
|
||||||
|
|
||||||
|
# get latlong and handle result
|
||||||
|
latlong = query.to_lat_long_coord(geocoder=behaviour.geocoder)
|
||||||
|
|
||||||
|
if not latlong:
|
||||||
|
return Result[str]("", error=latlong.error)
|
||||||
|
|
||||||
|
if behaviour.debug:
|
||||||
|
behaviour.stderr.write(f"debug: {latlong.get()=}\n")
|
||||||
|
|
||||||
|
# operate on query
|
||||||
|
text: str = ""
|
||||||
|
|
||||||
|
if behaviour.operation_mode == SurplusOperationMode.GENERATE_TEXT:
|
||||||
|
# TODO
|
||||||
|
return Result[str]("", error="not fully implemented yet")
|
||||||
|
|
||||||
|
elif behaviour.operation_mode == SurplusOperationMode.CONVERT_TYPES:
|
||||||
|
# TODO: https://github.com/markjoshwel/surplus/issues/18
|
||||||
|
return Result[str]("", error="conversion functionality is not implemented yet")
|
||||||
|
|
||||||
|
else:
|
||||||
|
return Result[str]("", error="unknown operation mode")
|
||||||
|
|
||||||
|
|
||||||
# command-line entry
|
# command-line entry
|
||||||
|
|
||||||
|
|
||||||
def cli() -> int:
|
def cli() -> int:
|
||||||
|
# handle arguments and print version header
|
||||||
behaviour = handle_args()
|
behaviour = handle_args()
|
||||||
|
|
||||||
|
(behaviour.stdout if behaviour.version_header else behaviour.stderr).write(
|
||||||
|
f"surplus version {'.'.join([str(v) for v in VERSION])}"
|
||||||
|
+ (f", debug mode" if behaviour.debug else "")
|
||||||
|
+ "\n"
|
||||||
|
)
|
||||||
|
|
||||||
|
if behaviour.version_header:
|
||||||
|
exit(0)
|
||||||
|
|
||||||
|
# parse query and handle result
|
||||||
query = parse_query(behaviour=behaviour)
|
query = parse_query(behaviour=behaviour)
|
||||||
|
|
||||||
if not query:
|
if not query:
|
||||||
behaviour.stderr.write(f"error: {query.cry(string=True)}\n")
|
behaviour.stderr.write(f"error: {query.cry(string=not behaviour.debug)}\n")
|
||||||
return -1
|
return -1
|
||||||
|
|
||||||
if behaviour.debug:
|
if behaviour.debug:
|
||||||
behaviour.stderr.write(f"debug: {query.get()=}\n")
|
behaviour.stderr.write(f"debug: {query.get()=}\n")
|
||||||
|
|
||||||
|
# run surplus
|
||||||
text = surplus(
|
text = surplus(
|
||||||
query=query.get(),
|
query=query.get(),
|
||||||
behaviour=behaviour,
|
behaviour=behaviour,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# handle and display surplus result
|
||||||
if not text:
|
if not text:
|
||||||
behaviour.stderr.write(f"error: {text.cry(string=True)}\n")
|
behaviour.stderr.write(f"error: {text.cry(string=not behaviour.debug)}\n")
|
||||||
return -2
|
return -2
|
||||||
|
|
||||||
behaviour.stdout.write(text.get() + "\n")
|
behaviour.stdout.write(text.get() + "\n")
|
||||||
|
|
Loading…
Reference in a new issue