{ "info": { "author": "David Lorenzo", "author_email": "", "bugtrack_url": null, "classifiers": [ "Development Status :: 3 - Alpha", "Intended Audience :: Developers", "License :: OSI Approved :: MIT License", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7" ], "description": "# PyBuses-Entities\n\nData classes to be used as part of a project that work with a public transport system, with Stops and Buses.\n\nWhile focused on Buses, PyBuses-Entities can be used with almost any public transport system that works \non a similar manner.\n\nThe project relies on two basic classes:\n\n- **Stop**: a physical place where Buses stop to pick and leave passengers\n- **Bus**: a moving vehicle that has a certain route of Stops and stop at them\n\nThis project is mainly developed for the [Python-VigoBusAPI](https://github.com/David-Lor/Python_VigoBusAPI) and [VigoBus-TelegramBot](https://github.com/David-Lor/VigoBus-TelegramBot), \ntwo projects that work together (being the first an intermediate API, and the second one a Telegram bot backend \nthat consume data from that API). \nIn this public transport system, buses stop at Stops, and for each Stop we can fetch a List of Buses \nwith their remaining time until arrival.\n\nThe interesting part about these classes is that they can generate a JSON output, which can be served through our API, \nand then converted back to a class instance in our client.\n\n## Requirements\n\n- Python >= 3.6\n- pydantic\n\n## Installation\n\n```bash\npip install pybuses-entities\n```\n\n## Changelog\n\n- **0.0.2**:\n - add options to remove empty lists & empty dicts on get_dict() method of entities (True by default)\n - add \"source\" field to AdvancedStop and BusesResult\n - add new sorting methods for Stops: sort by stopid, sort by distance\n - add/improve docstrings\n- **0.0.1** - initial release\n\n## Object definition\n\nStop and Bus objects inherit from pydantic. When creating a new instance of a class, its parameters \nmust be passed as kwargs. \nExtra kwargs parameters passed are ignored, so these classes work well with JSON as input and output data.\n\nBoth Stop and Bus classes have two superior class levels that inherit from their base classes, providing \nmore advanced attributes.\n\nCustom classes can be created in a similar manner to suit the needs of each particular project \nand public transport system, inheriting from these base classes.\n\n### Stop\n\n#### Attributes\n\n- **stopid**: unique identifier of this Stop (required, int)\n- **name**: canonical, human-readable name for this Stop, usually its location (required, str)\n- **lat**, **lon**: location of this Stop as latitude & longitude coordinates (optional, float)\n\n##### AdvancedStop\n\n- **created**: when this Stop definition was created or registered for the first time (optional, datetime/str/int/float)\n- **updated**: when this Stop definition was updated for the last time (optional, datetime/str/int/float) 1\n- **original_name**: when the Stop name was fetched from an external data source and changed by us, \n we can keep its original name on this attribute (optional, str) 1\n- **tags**: extra tags for this Stop (optional, list of str)\n- **extra_names**: optional additional names for this Stop, e.g. translation of the original names or \n alternative names that users commonly use (optional, list of str)\n- **distance**: distance from this Stop to a certain point. Useful, for example, when giving a list of \n which Stops exist near a certain location (optional, float)\n- **source**: name of the data source this data was fetched from (optional, str)\n\n1 timestamps can be: datetime object, str representation, or int/float epoch timestamp\n\n#### Examples\n\nClassic object declaration:\n```python\nfrom pybusent import Stop\n\nstop_1 = Stop(stopid=1, name=\"The Red Keep\")\nstop_6960 = Stop(stopid=6960, name=\"Praza do Rei\", lat=42.235056274709, lon=-8.72675751435639)\n```\n\nJSON/kwargs declaration (AdvancedStop example):\n```python\nfrom pybusent import AdvancedStop\n\nstop_data = {\n \"stopid\": 6660,\n \"name\": \"Porta do Sol\",\n \"lat\":42.2379152928961,\n \"lon\":-8.72484658707515,\n \"created\": 1529137067,\n \"updated\": 1560673067,\n \"original_name\": \"Porta Do Sol, s/n\", # 1\n \"tags\": [\"center\", \"centre\" \"zentrum\", \"center\", \"casco viejo\", \"old town\"], # 2\n \"extra_names\": [\"puerta del sol\", \"sun gate\"], # 3\n \"distance\": 25.38\n}\n\nstop = AdvancedStop(**stop_data)\n```\n\n1 the original_name could be the name given by the original data source that provided the Stop information, \nand we changed to a better-looking format\n\n2 since this Stop is located at the town centre, we used tags like \"center\", \"centre\"; \nit is also near the old town, so used tags like \"old town\"\n\n3 what the heck is \"sun gate\" supposed to mean? a literal translation from spanish \"puerta del sol\", \ndon't do this useless translations in production ?)\n\n### Bus\n\n#### Attributes\n\n- **busid**: unique identifier for this bus (optional, str) - if not provided, a busid is autogenerated \nas the MD5 sum of the line and route\n- **line**: bus line (required, str)\n- **route**: bus route (required, str)\n- **time**: remaining time for the bus to arrive to a certain Stop (optional, int/float/datetime/timedelta) 1\n\n##### AdvancedBus\n\n- **arrival**: when the bus will arrive to a certain Stop (optional, int/float/datetime/timedelta) 1 2\n- **departure**: when the bus will departure from a certain Stop (optional, int/float/datetime/timedelta) 1 2\n- **stops**: a list of Stops this bus will stop at (optional, list of Stop objects)\n\n1 bus remaining/arrival/departure times can be specified as relative times \n(how time from now is left until it arrives/departures), or absolute times (datetime object or int/float epoch timestamp).\nRelative times can be set as int, float or timedelta, depending on the way to represent this time.\n\n2 when the transport system differentiates between arrivals and departures (e.g. many train networks), \nthe AdvancedBus `arrival` & `departure` times are used INSTEAD of `time`\n\n#### Examples\n\nClassic object declaration:\n```python\nfrom datetime import datetime\nfrom pybusent import Bus\n\n# Bus 15C (Mohawk Avenue - Bunker Hill Street) will arrive in 5 minutes\nbus = Bus(line=\"15C\", route=\"Mohawk Avenue - Bunker Hill Street\", time=5)\n\n# Bus 21 (Algonquin) will arrive in 1 minute and 30 seconds\nbus = Bus(line=21, route=\"Algonquin\", time=timedelta(minutes=1, seconds=30))\nprint(type(bus.line)) # Line is casted to string although declared as int\n\n# Bus 5B (Dukes - Bohan) will arrive at 14h52\n# Creating a datetime object can be a bit more tricky since it requires the date\n# Get today date and replace time with the arrival time\ndt = datetime.now().replace(hour=14, minute=52, second=0, microsecond=0)\nbus = Bus(line=\"5B\", route=\"Dukes - Bohan\", time=dt)\n\n# It can be easier if your data source provides an epoch timestamp\nepoch = 1529137067\ndt = datetime.fromtimestamp(epoch)\nbus = Bus(line=\"5B\", route=\"Dukes - Bohan\", time=dt)\n```\n\nJSON/kwargs declaration (AdvancedBus example):\n```python\nfrom pybusent import AdvancedBus\n\nbus_data = {\n \"line\": 5,\n \"route\": \"Middle Park - East Holland\",\n \"arrival\": 5,\n \"departure\": 7\n}\nbus = AdvancedBus(**bus_data)\n```\n\nStatic bus declaration with its Stops route (AdvancedBus example):\n```python\nfrom pybusent import AdvancedBus\n\n# Define the route of the buses Line 5, Route 'Middle Park - East Holland'\nbus = AdvancedBus(\n line=\"5\",\n route=\"Middle Park - East Holland\",\n stops=[\n Stop(stopid=1231, name=\"Middle Park East\"),\n Stop(stopid=1232, name=\"Middle Park West\"),\n Stop(stopid=1233, name=\"Lancaster Avenue\"),\n Stop(stopid=1234, name=\"East Holland South\"),\n Stop(stopid=1235, name=\"East Holland North\")\n ]\n)\n```\n\n### BusesResult\n\nThis class is used when we want to return a more contextualized List of Buses. BusesResult do not inherit from Bus. \nThe following fields can be useful to give context to the Buses result:\n\n#### Attributes\n\n- **buses**: the List of Buses returned\n- **more_buses_available**: boolean that can be set if the data source reported that more buses were available, \n but we only fetched the first N results - e.g. because the data source is split into pages (optional, bool)\n- **stop**: when the data source returns Stop info alongside its realtime Buses list, we can return it (optional, Stop)\n- **source**: name of the data source this data was fetched from (optional, str)\n\n#### Examples\n\n```python\nfrom pybusent import BusesResult\n\nresult = BusesResult(\n buses=[\n Bus(line=\"5\", route=\"Middle Park - East Holland\", time=3),\n Bus(line=\"15C\", route=\"Dukes - Bohan\", time=9),\n Bus(line=\"5\", route=\"Middle Park - East Holland\", time=48),\n ],\n stop=Stop(\n stopid=12812,\n name=\"Middle Park (Entrance)\"\n ),\n more_buses_available=True\n)\n```\n\n### Custom classes\n\nStop, AdvancedStop, Bus and AdvancedBus can be inherited to create custom classes, with new attributes \nor changing the base attributes.\n\nAttributes that involve certain logic could have trouble if not designed specifically to support different datatypes \nor being optional when designed to be required.\n\nFor example, you can skip the Line or Route on a Bus and the Stop ID will still be auto-generated because this scenario \nwas contemplated. \nIf not, instantiating a Bus without Line/Route would not be possible, since both methods would be required in order \nto generate the Stop ID.\n\n#### Examples\n\n_\"My bus network has no Line numbers, only the Route - I will create a custom Bus class without the Line \n(more like, the Line is not required)\"_\n```python\nfrom pybusent import Bus\n\nclass MyBus(Bus):\n line: Optional[str]\n```\n\n_\"On my bus network, buses have a flag to know if they have special sits for reduced mobility passengers \n(False by default)\"_\n```python\nfrom pybusent import Bus\n\nclass MyBus(Bus):\n reduced_mobility: bool = False\n```\n\n_\"On my bus network, the Stop IDs have letters on it, so it must be a string\"_\n```python\nfrom pybusent import Bus\n\nclass MyStop(AdvancedStop):\n stopid: str\n```\n\n### Exceptions\n\nA set of exceptions based on Stops and Buses is defined. They include no logic.\n\n- **PyBusesException**: base exception for all the custom exceptions (inherit BaseException)\n - **StopException**: base exception for all the Stop related exceptions\n - **StopNotFound**: raised when a Stop is not found on a certain data source, but that data source cannot \n confirm that the stop not exists physically. E.g. if we keep a local storage of Stops and a Stop is not found \n on it, it could be that it was not saved there, but it might physically exist.\n - **StopNotExist**: raised when we are sure a Stop not physically exists. E.g. if we ask a remote API for \n a Stop and the API replies that it does not exist.\n - **BusException**: base exception for all the Bus related exceptions", "description_content_type": "text/markdown", "docs_url": null, "download_url": "", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "https://github.com/David-Lor/PyBuses", "keywords": "", "license": "Apache 2.0", "maintainer": "", "maintainer_email": "", "name": "pybuses-entities", "package_url": "https://pypi.org/project/pybuses-entities/", "platform": "", "project_url": "https://pypi.org/project/pybuses-entities/", "project_urls": { "Homepage": "https://github.com/David-Lor/PyBuses" }, "release_url": "https://pypi.org/project/pybuses-entities/0.0.2/", "requires_dist": null, "requires_python": "", "summary": "Data classes to work with Stops and Buses/vehicles on a public transportation network", "version": "0.0.2" }, "last_serial": 5420641, "releases": { "0.0.1": [ { "comment_text": "", "digests": { "md5": "a109cd133cdbcad4756e080c5430c1fc", "sha256": "0c265487e9ca4350334d388aef89a8be364e9e886b6138d15d5f7639e4d07a44" }, "downloads": -1, "filename": "pybuses-entities-0.0.1.tar.gz", "has_sig": false, "md5_digest": "a109cd133cdbcad4756e080c5430c1fc", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 8591, "upload_time": "2019-06-17T15:06:36", "url": "https://files.pythonhosted.org/packages/8f/11/cf84a4998ee0f7e079342cc987d462d0c7b05317eef7a87808e1b8630f23/pybuses-entities-0.0.1.tar.gz" } ], "0.0.2": [ { "comment_text": "", "digests": { "md5": "c0849b8fce59456b87e826d989ad2ee1", "sha256": "ed6a443db7a090d2a0a3c334d129f570d9dfe5d82b358613a3de11ff2b13c1f4" }, "downloads": -1, "filename": "pybuses-entities-0.0.2.tar.gz", "has_sig": false, "md5_digest": "c0849b8fce59456b87e826d989ad2ee1", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 8973, "upload_time": "2019-06-19T14:51:25", "url": "https://files.pythonhosted.org/packages/83/16/44b47df3bd6db26ba3c21c23004ee75f4cd4e1b616d86e9c165205ec7e47/pybuses-entities-0.0.2.tar.gz" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "c0849b8fce59456b87e826d989ad2ee1", "sha256": "ed6a443db7a090d2a0a3c334d129f570d9dfe5d82b358613a3de11ff2b13c1f4" }, "downloads": -1, "filename": "pybuses-entities-0.0.2.tar.gz", "has_sig": false, "md5_digest": "c0849b8fce59456b87e826d989ad2ee1", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 8973, "upload_time": "2019-06-19T14:51:25", "url": "https://files.pythonhosted.org/packages/83/16/44b47df3bd6db26ba3c21c23004ee75f4cd4e1b616d86e9c165205ec7e47/pybuses-entities-0.0.2.tar.gz" } ] }