commit fafb13b2a92bcaab856c1288004e2533a4c77999 Author: Arris Kathery Date: Wed May 8 01:06:17 2024 -0700 initial bullshit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..68bc17f --- /dev/null +++ b/.gitignore @@ -0,0 +1,160 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +#pdm.lock +# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it +# in version control. +# https://pdm.fming.dev/#use-with-ide +.pdm.toml + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# PyCharm +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +#.idea/ diff --git a/src/__init__.py b/src/__init__.py new file mode 100644 index 0000000..1dfcee6 --- /dev/null +++ b/src/__init__.py @@ -0,0 +1,5 @@ +def parse_timestamp(timestamp: int, type: int): + from datetime import datetime + + if type == 0: + return datetime.fromtimestamp(timestamp/1000.0) \ No newline at end of file diff --git a/src/services/TriMet.py b/src/services/TriMet.py new file mode 100644 index 0000000..30f5c51 --- /dev/null +++ b/src/services/TriMet.py @@ -0,0 +1,136 @@ +# Developer home: https://developer.trimet.org/ "00BA96108889B7ED8850997D7" + +from .. import parse_timestamp + +import json +import requests +from logging import info, warn + + +class TriMetAPI: + + def __init__(self, api_key=None, secure=True) -> None: + if api_key: + self.api_key = api_key + else: + warn("The API Key was never set!") + + if secure: + self.secure = secure + else: + warn("Using HTTP frontend!") + + return None + + def verify(self) -> bool: + if self.api_key: + info("api_key exists!") + else: + warn("An API Key is required to use Trimet's online services.") + return False + + if len(self.api_key) == 25: + info("api_key is correct length!") + return True + else: + warn("api_key is at incorrect length!") + return False + + def set_api_key(self, api_key) -> bool: + self.api_key = api_key + return True + + +class TriMetStop: + def __init__(self) -> None: + return None + + +class TriMetVehicle: + + def __init__(self, json_data=None) -> None: + if json_data: + self.load_from_json(json_data) + return None + + def load_from_id( + self, api: TriMetAPI, vehicle_id: int, api_verify: bool = True + ) -> ( + bool + ): # this is basically a glorified wrapper for load_from_json() but very lazy. also, api_verify can skip checking the api key, incase it was checked before. + if not api.verify() and not api_verify: + raise RuntimeError( + "A valid API Key is required to use Trimet's online services." + ) + else: + info(f"loading vehicle info from id {vehicle_id}") + return self.load_from_json( + requests.get( + f"https://developer.trimet.org/ws/v2/vehicles?appID={api.api_key}&ids={vehicle_id}" + ).json()["resultSet"]["vehicle"][0] + ) + + def load_from_json( + self, object + ) -> bool: # **INTERNAL USAGE ONLY!!! DO NOT CALL OTHERWISE!!!** + from ansiconverter import HEXtoRGB + + self.import_data = object + self.raw_data = json.dumps(object, indent=0) + + self.id = object["vehicleID"] + + self.route_color = HEXtoRGB(f"#{object['routeColor']}") + + self.expiration = parse_timestamp(object["expires"], 0) + + self.type = None # TODO: Identify exact model. Model should be revealed by vehicle number. + if object["type"] == "rail": + + if object["routeSubType"] == "Light Rail": + self.type = ["light-rail", "TriMet MAX Light Rail Vehicle"] + else: # Cannot check for WES or Portland Streetcar, as they are not exposed by TriMet's API. + self.type = ["rail", "Unknown TriMet Rail Vehicle"] + + else: # TODO: Identify LIFT and FX2 + self.type = ["bus", "TriMet Bus Vehicle"] + + self.sign = [object["signMessage"], object["signMessageLong"]] + + return True + + def refresh( + self, api: TriMetAPI + ) -> ( + bool + ): # this is a very lazy method that is just a wrapper for load_from_id(). see line #53 + if not api.verify(): + raise RuntimeError( + "A valid API Key is required to use Trimet's online services." + ) + else: + self.load_from_id(api, self.id, False) + + +def get_vehicles(api: TriMetAPI) -> dict: + if not api.api_key or not api.verify(): + raise RuntimeError( + "A valid API Key is required to use Trimet's online services." + ) + elif api.verify(): + resp = requests.get( + f"https://developer.trimet.org/ws/v2/vehicles?appID={api.api_key}" + ) + result = {} + + from datetime import datetime + + result["resultTime"] = parse_timestamp(resp.json()["resultSet"]["queryTime"], 0) + del datetime + + result["vehicles"] = [] + for vm in resp.json()["resultSet"]["vehicle"]: + result["vehicles"].append(TriMetVehicle(json_data=vm)) + + return result + return {} diff --git a/test.py b/test.py new file mode 100644 index 0000000..d88584b --- /dev/null +++ b/test.py @@ -0,0 +1,12 @@ +import src.services.TriMet +from ansiconverter import RGBtoANSI, bold + +api = src.services.TriMet.TriMetAPI(api_key="00BA96108889B7ED8850997D7") + +result=src.services.TriMet.get_vehicles(api) + +for vm in result['vehicles']: + print(RGBtoANSI( + text=f"yo fag! i'm {bold(vm.type[1])} #{bold(vm.id)}.\n\ti'm running route #{bold(vm.import_data['routeNumber'])}, and meh next stop is {bold(vm.sign[0])}\n\ti'm sayin {bold(vm.sign[1])}...", + foregound=vm.route_color + )) \ No newline at end of file