From cfe09a7db26566a2775bb6b141b6f9aa236ae6e4 Mon Sep 17 00:00:00 2001 From: Stefano Pigozzi Date: Mon, 22 Jun 2020 03:58:55 +0200 Subject: [PATCH] First wikipack Commit --- README.md | 29 +- examplepack/commands/example.py | 12 - examplepack/events/example.py | 10 - examplepack/stars/__init__.py | 14 - examplepack/stars/api_example.py | 70 --- examplepack/stars/example.py | 11 - examplepack/tables/example.py | 19 - examplepack/version.py | 2 - poetry.lock | 485 ++++++++++++++++++ pyproject.toml | 28 +- .../commands/__init__.py | 2 - {examplepack => wikipack}/events/__init__.py | 2 - wikipack/stars/__init__.py | 10 + wikipack/stars/api_wiki.py | 215 ++++++++ {examplepack => wikipack}/tables/__init__.py | 8 +- wikipack/tables/wikideletion.py | 32 ++ wikipack/tables/wikipage.py | 23 + wikipack/tables/wikirevision.py | 92 ++++ wikipack/version.py | 1 + 19 files changed, 896 insertions(+), 169 deletions(-) delete mode 100644 examplepack/commands/example.py delete mode 100644 examplepack/events/example.py delete mode 100644 examplepack/stars/__init__.py delete mode 100644 examplepack/stars/api_example.py delete mode 100644 examplepack/stars/example.py delete mode 100644 examplepack/tables/example.py delete mode 100644 examplepack/version.py create mode 100644 poetry.lock rename {examplepack => wikipack}/commands/__init__.py (72%) rename {examplepack => wikipack}/events/__init__.py (72%) create mode 100644 wikipack/stars/__init__.py create mode 100644 wikipack/stars/api_wiki.py rename {examplepack => wikipack}/tables/__init__.py (55%) create mode 100644 wikipack/tables/wikideletion.py create mode 100644 wikipack/tables/wikipage.py create mode 100644 wikipack/tables/wikirevision.py create mode 100644 wikipack/version.py diff --git a/README.md b/README.md index a988ebbf..78242acd 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,28 @@ -# `examplepack` +# `wikipack` -This is an example Pack for [Royalnet](https://github.com/Steffo99/royalnet)! +This pack adds a small Wiki to Royalnet, allowing communities to create their own small Wikis. -> To be updated with more info on how to create your own pack. \ No newline at end of file +## Configuration options + +```toml +[Packs."wikipack"] + +# The roles that are authorized by default to complete certain actions. +# Setting them to an empty string disables the authentication requirement, allowing unauthenticated users that privilege +[Packs."wikipack".roles] + +# Users with this role will be able to view wiki pages that do not have a different role set. +view = "" + +# Users with this role will be able to create new wiki pages. +create = "wiki_create" + +# Users with this role will be able to edit wiki pages that do not have a different role set. +edit = "wiki_edit" + +# Users with this role will be able to delete wiki pages. +delete = "wiki_delete" + +# Users with this role will override all other privileges. +admin = "wiki_admin" +``` diff --git a/examplepack/commands/example.py b/examplepack/commands/example.py deleted file mode 100644 index f98652ba..00000000 --- a/examplepack/commands/example.py +++ /dev/null @@ -1,12 +0,0 @@ -from typing import * -from royalnet.commands import * -from royalnet.utils import * - - -class ExampleCommand(Command): - name: str = "example" - - description: str = "Say Hello to the world!" - - async def run(self, args: CommandArgs, data: CommandData) -> None: - await data.reply("Hello world!") diff --git a/examplepack/events/example.py b/examplepack/events/example.py deleted file mode 100644 index 866abc7a..00000000 --- a/examplepack/events/example.py +++ /dev/null @@ -1,10 +0,0 @@ -from typing import * -from royalnet.commands import * -from royalnet.utils import * - - -class ExampleEvent(Event): - name = "example" - - async def run(self, **kwargs) -> dict: - return {"hello": "world"} diff --git a/examplepack/stars/__init__.py b/examplepack/stars/__init__.py deleted file mode 100644 index 8193b6d8..00000000 --- a/examplepack/stars/__init__.py +++ /dev/null @@ -1,14 +0,0 @@ -# Imports go here! -# TODO: If you create a new star, remember to import it here... -# from .example import ExampleStar -# from .api_example import ApiExampleStar - -# Enter the PageStars of your Pack here! -# TODO: and to add it either to the list here if it is a PageStar... -available_page_stars = [ - # ExampleStar, - # ApiExampleStar, -] - -# Don't change this, it should automatically generate __all__ -__all__ = [command.__name__ for command in available_page_stars] diff --git a/examplepack/stars/api_example.py b/examplepack/stars/api_example.py deleted file mode 100644 index 80b03165..00000000 --- a/examplepack/stars/api_example.py +++ /dev/null @@ -1,70 +0,0 @@ -import royalnet.utils as ru -import royalnet.constellation.api as rca -import royalnet.constellation.api.apierrors as rcae - - -# View autogenerated docs for all ApiStars at the HTTP path `/docs` - - -class EchoStar(rca.ApiStar): - # What does this ApiStar do? - summary = "Returns the same string entered as input." - - # Optional: A longer description of what the ApiStar should do. - description = """ - NOTE: This method can only be used by Royalnet Admins. - """ - - # The HTTP methods that can be used with this ApiStar. - methods = ["GET", "POST"] - # You can disambiguate between methods using the `data.method` variable. - - # The HTTP path this ApiStar should bind to. - path = "/api/example/echo/v1" - - # Does this method require any auth? - # Only for documentation purposes, it doesn't do any check on it's own. - requires_auth = True - # To authenticate an user through their token, use the `await data.user()` method. - # If the user isn't logged in, the method authomatically returns 403 Forbidden, unless `rcae.ForbiddenError` - # is caught. - - # A dict of paramenters accepted by this method, with a description of their purpose. - parameters = { - "echo": "What should the method return? " - "(Optional: if nothing is passed, the ApiStar will return the username of the caller.)", - "error": "Should the method return a sample error?" - } - # You can access parameters by using `data` as a dict with the parameter name as key. - # If a missing parameter is accessed, a `rcae.MissingParameterError` will be raised, which will lead to a - # 400 Bad Request error if not caught. - - # The autodoc categories this ApiStar should fall in. - tags = ["example"] - - # The actual method called when the ApiStar received a HTTP request. - # It must return a JSON-compatible object, such as a str, a int, a float, a list, a dict or None. - async def api(self, data: rca.ApiData) -> ru.JSON: - - # If "true" is passed as the "error" parameter in the query string... - if data["error"] == "true": - # ...return an example error - raise Exception("Example error! Everything works as intended.") - # If the "error" parameter is missing, the ApiStar will respond with 400 Bad Request - - # Ensure the user is logged in - user = await data.user() - # Check if the user has the role "admin" - if "admin" not in user.roles: - raise Exception("Only admins can call this method!") - - # Get the value of the "echo" parameter, without raising an exception if it doesn't exist - echo = data.get("echo") - - if echo is None: - # Find the username of the logged in user - # user is a SQLAlchemy ORM object generated from the Users table defined in `royalnet.backpack.tables.users` - echo = user.username - - # Return a 200 OK successful response containing the value of the echo variable and the HTTP method used - return {"echo": echo, "method_used": data.method} diff --git a/examplepack/stars/example.py b/examplepack/stars/example.py deleted file mode 100644 index 6b044cea..00000000 --- a/examplepack/stars/example.py +++ /dev/null @@ -1,11 +0,0 @@ -from starlette.requests import Request -from starlette.responses import * -from royalnet.constellation import * -from royalnet.utils import * - - -class ExampleStar(PageStar): - path = "/example" - - async def page(self, request: Request) -> JSONResponse: - return HTMLResponse("""

henlo!

""") diff --git a/examplepack/tables/example.py b/examplepack/tables/example.py deleted file mode 100644 index 83078e7e..00000000 --- a/examplepack/tables/example.py +++ /dev/null @@ -1,19 +0,0 @@ -from sqlalchemy import * -from sqlalchemy.orm import * -from sqlalchemy.ext.declarative import declared_attr - - -class Example: - __tablename__ = "examples" - - @declared_attr - def creator_id(self): - return Column(Integer, ForeignKey("users.uid"), primary_key=True) - - @declared_attr - def creator(self): - return relationship("User", backref=backref("examples_createdx")) - - @declared_attr - def example(self): - return Column(String, nullable=False, default="Hello world!") diff --git a/examplepack/version.py b/examplepack/version.py deleted file mode 100644 index 422feb95..00000000 --- a/examplepack/version.py +++ /dev/null @@ -1,2 +0,0 @@ -# TODO: Increment this every new version of your pack! -semantic = "0.1.0" diff --git a/poetry.lock b/poetry.lock new file mode 100644 index 00000000..4d966fc4 --- /dev/null +++ b/poetry.lock @@ -0,0 +1,485 @@ +[[package]] +category = "main" +description = "Modern password hashing for your software and your servers" +name = "bcrypt" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +version = "3.1.7" + +[package.dependencies] +cffi = ">=1.1" +six = ">=1.4.1" + +[package.extras] +tests = ["pytest (>=3.2.1,<3.3.0 || >3.3.0)"] + +[[package]] +category = "main" +description = "Foreign Function Interface for Python calling C code." +name = "cffi" +optional = false +python-versions = "*" +version = "1.14.0" + +[package.dependencies] +pycparser = "*" + +[[package]] +category = "main" +description = "Composable command line interface toolkit" +name = "click" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +version = "7.1.2" + +[[package]] +category = "main" +description = "Date parsing library designed to parse dates from HTML pages" +name = "dateparser" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +version = "0.7.6" + +[package.dependencies] +python-dateutil = "*" +pytz = "*" +regex = "!=2019.02.19" +tzlocal = "*" + +[[package]] +category = "main" +description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1" +name = "h11" +optional = false +python-versions = "*" +version = "0.9.0" + +[[package]] +category = "main" +description = "A collection of framework independent HTTP protocol utils." +marker = "sys_platform != \"win32\" and sys_platform != \"cygwin\" and platform_python_implementation != \"pypy\"" +name = "httptools" +optional = false +python-versions = "*" +version = "0.0.13" + +[[package]] +category = "main" +description = "psycopg2 - Python-PostgreSQL Database Adapter" +name = "psycopg2-binary" +optional = false +python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*" +version = "2.8.5" + +[[package]] +category = "main" +description = "C parser in Python" +name = "pycparser" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +version = "2.20" + +[[package]] +category = "main" +description = "Extensions to the standard Python datetime module" +name = "python-dateutil" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" +version = "2.8.1" + +[package.dependencies] +six = ">=1.5" + +[[package]] +category = "main" +description = "A streaming multipart parser for Python" +name = "python-multipart" +optional = false +python-versions = "*" +version = "0.0.5" + +[package.dependencies] +six = ">=1.4.0" + +[[package]] +category = "main" +description = "World timezone definitions, modern and historical" +name = "pytz" +optional = false +python-versions = "*" +version = "2020.1" + +[[package]] +category = "main" +description = "Alternative regular expression module, to replace re." +name = "regex" +optional = false +python-versions = "*" +version = "2020.6.8" + +[[package]] +category = "main" +description = "A multipurpose bot and web framework" +name = "royalnet" +optional = false +python-versions = ">=3.8,<4.0" +version = "5.8.14" + +[package.dependencies] +dateparser = ">=0.7.2,<0.8.0" +toml = ">=0.10.0,<0.11.0" + +[package.dependencies.bcrypt] +optional = true +version = ">=3.1.7,<4.0.0" + +[package.dependencies.psycopg2_binary] +optional = true +version = ">=2.8.4,<3.0.0" + +[package.dependencies.python-multipart] +optional = true +version = ">=0.0.5,<0.0.6" + +[package.dependencies.sqlalchemy] +optional = true +version = ">=1.3.10,<2.0.0" + +[package.dependencies.starlette] +optional = true +version = ">=0.12.13,<0.13.0" + +[package.dependencies.uvicorn] +optional = true +version = ">=0.10.7,<0.11.0" + +[package.extras] +alchemy_easy = ["sqlalchemy (>=1.3.10,<2.0.0)", "psycopg2_binary (>=2.8.4,<3.0.0)", "bcrypt (>=3.1.7,<4.0.0)"] +alchemy_hard = ["sqlalchemy (>=1.3.10,<2.0.0)", "psycopg2 (>=2.8.4,<3.0.0)", "bcrypt (>=3.1.7,<4.0.0)"] +bard = ["ffmpeg_python (>=0.2.0,<0.3.0)", "youtube-dl", "eyed3 (>=0.9,<0.10)"] +coloredlogs = ["coloredlogs (>=10.0,<11.0)"] +constellation = ["starlette (>=0.12.13,<0.13.0)", "uvicorn (>=0.10.7,<0.11.0)", "python-multipart (>=0.0.5,<0.0.6)"] +discord = ["discord.py (>=1.3.1,<2.0.0)", "pynacl (>=1.3.0,<2.0.0)"] +herald = ["websockets (>=8.1,<9.0)"] +matrix = ["matrix-nio (>=0.6,<0.7)"] +sentry = ["sentry_sdk (>=0.13.2,<0.14.0)"] +telegram = ["python_telegram_bot (>=12.2.0,<13.0.0)"] + +[[package]] +category = "main" +description = "Python 2 and 3 compatibility utilities" +name = "six" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" +version = "1.15.0" + +[[package]] +category = "main" +description = "Database Abstraction Library" +name = "sqlalchemy" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +version = "1.3.17" + +[package.extras] +mssql = ["pyodbc"] +mssql_pymssql = ["pymssql"] +mssql_pyodbc = ["pyodbc"] +mysql = ["mysqlclient"] +oracle = ["cx-oracle"] +postgresql = ["psycopg2"] +postgresql_pg8000 = ["pg8000"] +postgresql_psycopg2binary = ["psycopg2-binary"] +postgresql_psycopg2cffi = ["psycopg2cffi"] +pymysql = ["pymysql"] + +[[package]] +category = "main" +description = "The little ASGI library that shines." +name = "starlette" +optional = false +python-versions = ">=3.6" +version = "0.12.13" + +[package.extras] +full = ["aiofiles", "graphene", "itsdangerous", "jinja2", "python-multipart", "pyyaml", "requests", "ujson"] + +[[package]] +category = "main" +description = "Python Library for Tom's Obvious, Minimal Language" +name = "toml" +optional = false +python-versions = "*" +version = "0.10.1" + +[[package]] +category = "main" +description = "tzinfo object for the local timezone" +name = "tzlocal" +optional = false +python-versions = "*" +version = "2.1" + +[package.dependencies] +pytz = "*" + +[[package]] +category = "main" +description = "The lightning-fast ASGI server." +name = "uvicorn" +optional = false +python-versions = "*" +version = "0.10.9" + +[package.dependencies] +click = ">=7.0.0,<8.0.0" +h11 = ">=0.9.0,<0.10.0" +httptools = "0.0.13" +uvloop = ">=0.14.0" +websockets = ">=8.0.0,<9.0.0" + +[[package]] +category = "main" +description = "Fast implementation of asyncio event loop on top of libuv" +marker = "sys_platform != \"win32\" and sys_platform != \"cygwin\" and platform_python_implementation != \"pypy\"" +name = "uvloop" +optional = false +python-versions = "*" +version = "0.14.0" + +[[package]] +category = "main" +description = "An implementation of the WebSocket Protocol (RFC 6455 & 7692)" +name = "websockets" +optional = false +python-versions = ">=3.6.1" +version = "8.1" + +[metadata] +content-hash = "94e12371cf2623dbf0fbd854c1667940694b6a5607d81a3bb9e588b30c8a5702" + python-versions = "^3.8" + +[metadata.files] +bcrypt = [ + {file = "bcrypt-3.1.7-cp27-cp27m-macosx_10_6_intel.whl", hash = "sha256:d7bdc26475679dd073ba0ed2766445bb5b20ca4793ca0db32b399dccc6bc84b7"}, + {file = "bcrypt-3.1.7-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:69361315039878c0680be456640f8705d76cb4a3a3fe1e057e0f261b74be4b31"}, + {file = "bcrypt-3.1.7-cp27-cp27m-win32.whl", hash = "sha256:5432dd7b34107ae8ed6c10a71b4397f1c853bd39a4d6ffa7e35f40584cffd161"}, + {file = "bcrypt-3.1.7-cp27-cp27m-win_amd64.whl", hash = "sha256:9fe92406c857409b70a38729dbdf6578caf9228de0aef5bc44f859ffe971a39e"}, + {file = "bcrypt-3.1.7-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:763669a367869786bb4c8fcf731f4175775a5b43f070f50f46f0b59da45375d0"}, + {file = "bcrypt-3.1.7-cp34-abi3-macosx_10_6_intel.whl", hash = "sha256:a190f2a5dbbdbff4b74e3103cef44344bc30e61255beb27310e2aec407766052"}, + {file = "bcrypt-3.1.7-cp34-abi3-manylinux1_x86_64.whl", hash = "sha256:c9457fa5c121e94a58d6505cadca8bed1c64444b83b3204928a866ca2e599105"}, + {file = "bcrypt-3.1.7-cp34-cp34m-win32.whl", hash = "sha256:8b10acde4e1919d6015e1df86d4c217d3b5b01bb7744c36113ea43d529e1c3de"}, + {file = "bcrypt-3.1.7-cp34-cp34m-win_amd64.whl", hash = "sha256:cb93f6b2ab0f6853550b74e051d297c27a638719753eb9ff66d1e4072be67133"}, + {file = "bcrypt-3.1.7-cp35-cp35m-win32.whl", hash = "sha256:6fe49a60b25b584e2f4ef175b29d3a83ba63b3a4df1b4c0605b826668d1b6be5"}, + {file = "bcrypt-3.1.7-cp35-cp35m-win_amd64.whl", hash = "sha256:a595c12c618119255c90deb4b046e1ca3bcfad64667c43d1166f2b04bc72db09"}, + {file = "bcrypt-3.1.7-cp36-cp36m-win32.whl", hash = "sha256:74a015102e877d0ccd02cdeaa18b32aa7273746914a6c5d0456dd442cb65b99c"}, + {file = "bcrypt-3.1.7-cp36-cp36m-win_amd64.whl", hash = "sha256:0258f143f3de96b7c14f762c770f5fc56ccd72f8a1857a451c1cd9a655d9ac89"}, + {file = "bcrypt-3.1.7-cp37-cp37m-win32.whl", hash = "sha256:19a4b72a6ae5bb467fea018b825f0a7d917789bcfe893e53f15c92805d187294"}, + {file = "bcrypt-3.1.7-cp37-cp37m-win_amd64.whl", hash = "sha256:ff032765bb8716d9387fd5376d987a937254b0619eff0972779515b5c98820bc"}, + {file = "bcrypt-3.1.7-cp38-cp38-win32.whl", hash = "sha256:ce4e4f0deb51d38b1611a27f330426154f2980e66582dc5f438aad38b5f24fc1"}, + {file = "bcrypt-3.1.7-cp38-cp38-win_amd64.whl", hash = "sha256:6305557019906466fc42dbc53b46da004e72fd7a551c044a827e572c82191752"}, + {file = "bcrypt-3.1.7.tar.gz", hash = "sha256:0b0069c752ec14172c5f78208f1863d7ad6755a6fae6fe76ec2c80d13be41e42"}, +] +cffi = [ + {file = "cffi-1.14.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:1cae98a7054b5c9391eb3249b86e0e99ab1e02bb0cc0575da191aedadbdf4384"}, + {file = "cffi-1.14.0-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:cf16e3cf6c0a5fdd9bc10c21687e19d29ad1fe863372b5543deaec1039581a30"}, + {file = "cffi-1.14.0-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:f2b0fa0c01d8a0c7483afd9f31d7ecf2d71760ca24499c8697aeb5ca37dc090c"}, + {file = "cffi-1.14.0-cp27-cp27m-win32.whl", hash = "sha256:99f748a7e71ff382613b4e1acc0ac83bf7ad167fb3802e35e90d9763daba4d78"}, + {file = "cffi-1.14.0-cp27-cp27m-win_amd64.whl", hash = "sha256:c420917b188a5582a56d8b93bdd8e0f6eca08c84ff623a4c16e809152cd35793"}, + {file = "cffi-1.14.0-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:399aed636c7d3749bbed55bc907c3288cb43c65c4389964ad5ff849b6370603e"}, + {file = "cffi-1.14.0-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:cab50b8c2250b46fe738c77dbd25ce017d5e6fb35d3407606e7a4180656a5a6a"}, + {file = "cffi-1.14.0-cp35-cp35m-macosx_10_9_x86_64.whl", hash = "sha256:001bf3242a1bb04d985d63e138230802c6c8d4db3668fb545fb5005ddf5bb5ff"}, + {file = "cffi-1.14.0-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:e56c744aa6ff427a607763346e4170629caf7e48ead6921745986db3692f987f"}, + {file = "cffi-1.14.0-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:b8c78301cefcf5fd914aad35d3c04c2b21ce8629b5e4f4e45ae6812e461910fa"}, + {file = "cffi-1.14.0-cp35-cp35m-win32.whl", hash = "sha256:8c0ffc886aea5df6a1762d0019e9cb05f825d0eec1f520c51be9d198701daee5"}, + {file = "cffi-1.14.0-cp35-cp35m-win_amd64.whl", hash = "sha256:8a6c688fefb4e1cd56feb6c511984a6c4f7ec7d2a1ff31a10254f3c817054ae4"}, + {file = "cffi-1.14.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:95cd16d3dee553f882540c1ffe331d085c9e629499ceadfbda4d4fde635f4b7d"}, + {file = "cffi-1.14.0-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:66e41db66b47d0d8672d8ed2708ba91b2f2524ece3dee48b5dfb36be8c2f21dc"}, + {file = "cffi-1.14.0-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:028a579fc9aed3af38f4892bdcc7390508adabc30c6af4a6e4f611b0c680e6ac"}, + {file = "cffi-1.14.0-cp36-cp36m-win32.whl", hash = "sha256:cef128cb4d5e0b3493f058f10ce32365972c554572ff821e175dbc6f8ff6924f"}, + {file = "cffi-1.14.0-cp36-cp36m-win_amd64.whl", hash = "sha256:337d448e5a725bba2d8293c48d9353fc68d0e9e4088d62a9571def317797522b"}, + {file = "cffi-1.14.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:e577934fc5f8779c554639376beeaa5657d54349096ef24abe8c74c5d9c117c3"}, + {file = "cffi-1.14.0-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:62ae9af2d069ea2698bf536dcfe1e4eed9090211dbaafeeedf5cb6c41b352f66"}, + {file = "cffi-1.14.0-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:14491a910663bf9f13ddf2bc8f60562d6bc5315c1f09c704937ef17293fb85b0"}, + {file = "cffi-1.14.0-cp37-cp37m-win32.whl", hash = "sha256:c43866529f2f06fe0edc6246eb4faa34f03fe88b64a0a9a942561c8e22f4b71f"}, + {file = "cffi-1.14.0-cp37-cp37m-win_amd64.whl", hash = "sha256:2089ed025da3919d2e75a4d963d008330c96751127dd6f73c8dc0c65041b4c26"}, + {file = "cffi-1.14.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:3b911c2dbd4f423b4c4fcca138cadde747abdb20d196c4a48708b8a2d32b16dd"}, + {file = "cffi-1.14.0-cp38-cp38-manylinux1_i686.whl", hash = "sha256:7e63cbcf2429a8dbfe48dcc2322d5f2220b77b2e17b7ba023d6166d84655da55"}, + {file = "cffi-1.14.0-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:3d311bcc4a41408cf5854f06ef2c5cab88f9fded37a3b95936c9879c1640d4c2"}, + {file = "cffi-1.14.0-cp38-cp38-win32.whl", hash = "sha256:675686925a9fb403edba0114db74e741d8181683dcf216be697d208857e04ca8"}, + {file = "cffi-1.14.0-cp38-cp38-win_amd64.whl", hash = "sha256:00789914be39dffba161cfc5be31b55775de5ba2235fe49aa28c148236c4e06b"}, + {file = "cffi-1.14.0.tar.gz", hash = "sha256:2d384f4a127a15ba701207f7639d94106693b6cd64173d6c8988e2c25f3ac2b6"}, +] +click = [ + {file = "click-7.1.2-py2.py3-none-any.whl", hash = "sha256:dacca89f4bfadd5de3d7489b7c8a566eee0d3676333fbb50030263894c38c0dc"}, + {file = "click-7.1.2.tar.gz", hash = "sha256:d2b5255c7c6349bc1bd1e59e08cd12acbbd63ce649f2588755783aa94dfb6b1a"}, +] +dateparser = [ + {file = "dateparser-0.7.6-py2.py3-none-any.whl", hash = "sha256:7552c994f893b5cb8fcf103b4cd2ff7f57aab9bfd2619fdf0cf571c0740fd90b"}, + {file = "dateparser-0.7.6.tar.gz", hash = "sha256:e875efd8c57c85c2d02b238239878db59ff1971f5a823457fcc69e493bf6ebfa"}, +] +h11 = [ + {file = "h11-0.9.0-py2.py3-none-any.whl", hash = "sha256:4bc6d6a1238b7615b266ada57e0618568066f57dd6fa967d1290ec9309b2f2f1"}, + {file = "h11-0.9.0.tar.gz", hash = "sha256:33d4bca7be0fa039f4e84d50ab00531047e53d6ee8ffbc83501ea602c169cae1"}, +] +httptools = [ + {file = "httptools-0.0.13.tar.gz", hash = "sha256:e00cbd7ba01ff748e494248183abc6e153f49181169d8a3d41bb49132ca01dfc"}, +] +psycopg2-binary = [ + {file = "psycopg2-binary-2.8.5.tar.gz", hash = "sha256:ccdc6a87f32b491129ada4b87a43b1895cf2c20fdb7f98ad979647506ffc41b6"}, + {file = "psycopg2_binary-2.8.5-cp27-cp27m-macosx_10_6_intel.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:96d3038f5bd061401996614f65d27a4ecb62d843eb4f48e212e6d129171a721f"}, + {file = "psycopg2_binary-2.8.5-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:08507efbe532029adee21b8d4c999170a83760d38249936038bd0602327029b5"}, + {file = "psycopg2_binary-2.8.5-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:b9a8b391c2b0321e0cd7ec6b4cfcc3dd6349347bd1207d48bcb752aa6c553a66"}, + {file = "psycopg2_binary-2.8.5-cp27-cp27m-win32.whl", hash = "sha256:3286541b9d85a340ee4ed42732d15fc1bb441dc500c97243a768154ab8505bb5"}, + {file = "psycopg2_binary-2.8.5-cp27-cp27m-win_amd64.whl", hash = "sha256:008da3ab51adc70a5f1cfbbe5db3a22607ab030eb44bcecf517ad11a0c2b3cac"}, + {file = "psycopg2_binary-2.8.5-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:ba13346ff6d3eb2dca0b6fa0d8a9d999eff3dcd9b55f3a890f12b0b6362b2b38"}, + {file = "psycopg2_binary-2.8.5-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:c8830b7d5f16fd79d39b21e3d94f247219036b29b30c8270314c46bf8b732389"}, + {file = "psycopg2_binary-2.8.5-cp34-cp34m-win32.whl", hash = "sha256:51f7823f1b087d2020d8e8c9e6687473d3d239ba9afc162d9b2ab6e80b53f9f9"}, + {file = "psycopg2_binary-2.8.5-cp34-cp34m-win_amd64.whl", hash = "sha256:107d9be3b614e52a192719c6bf32e8813030020ea1d1215daa86ded9a24d8b04"}, + {file = "psycopg2_binary-2.8.5-cp35-cp35m-macosx_10_6_intel.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:930315ac53dc65cbf52ab6b6d27422611f5fb461d763c531db229c7e1af6c0b3"}, + {file = "psycopg2_binary-2.8.5-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:6bb2dd006a46a4a4ce95201f836194eb6a1e863f69ee5bab506673e0ca767057"}, + {file = "psycopg2_binary-2.8.5-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:3939cf75fc89c5e9ed836e228c4a63604dff95ad19aed2bbf71d5d04c15ed5ce"}, + {file = "psycopg2_binary-2.8.5-cp35-cp35m-win32.whl", hash = "sha256:a20299ee0ea2f9cca494396ac472d6e636745652a64a418b39522c120fd0a0a4"}, + {file = "psycopg2_binary-2.8.5-cp35-cp35m-win_amd64.whl", hash = "sha256:cc30cb900f42c8a246e2cb76539d9726f407330bc244ca7729c41a44e8d807fb"}, + {file = "psycopg2_binary-2.8.5-cp36-cp36m-macosx_10_6_intel.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:40abc319f7f26c042a11658bf3dd3b0b3bceccf883ec1c565d5c909a90204434"}, + {file = "psycopg2_binary-2.8.5-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:702f09d8f77dc4794651f650828791af82f7c2efd8c91ae79e3d9fe4bb7d4c98"}, + {file = "psycopg2_binary-2.8.5-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:d1a8b01f6a964fec702d6b6dac1f91f2b9f9fe41b310cbb16c7ef1fac82df06d"}, + {file = "psycopg2_binary-2.8.5-cp36-cp36m-win32.whl", hash = "sha256:17a0ea0b0eabf07035e5e0d520dabc7950aeb15a17c6d36128ba99b2721b25b1"}, + {file = "psycopg2_binary-2.8.5-cp36-cp36m-win_amd64.whl", hash = "sha256:e004db88e5a75e5fdab1620fb9f90c9598c2a195a594225ac4ed2a6f1c23e162"}, + {file = "psycopg2_binary-2.8.5-cp37-cp37m-macosx_10_6_intel.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:a34826d6465c2e2bbe9d0605f944f19d2480589f89863ed5f091943be27c9de4"}, + {file = "psycopg2_binary-2.8.5-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:cac918cd7c4c498a60f5d2a61d4f0a6091c2c9490d81bc805c963444032d0dab"}, + {file = "psycopg2_binary-2.8.5-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:7b832d76cc65c092abd9505cc670c4e3421fd136fb6ea5b94efbe4c146572505"}, + {file = "psycopg2_binary-2.8.5-cp37-cp37m-win32.whl", hash = "sha256:bb0608694a91db1e230b4a314e8ed00ad07ed0c518f9a69b83af2717e31291a3"}, + {file = "psycopg2_binary-2.8.5-cp37-cp37m-win_amd64.whl", hash = "sha256:eb2f43ae3037f1ef5e19339c41cf56947021ac892f668765cd65f8ab9814192e"}, + {file = "psycopg2_binary-2.8.5-cp38-cp38-macosx_10_9_x86_64.macosx_10_9_intel.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:07cf82c870ec2d2ce94d18e70c13323c89f2f2a2628cbf1feee700630be2519a"}, + {file = "psycopg2_binary-2.8.5-cp38-cp38-manylinux1_i686.whl", hash = "sha256:a69970ee896e21db4c57e398646af9edc71c003bc52a3cc77fb150240fefd266"}, + {file = "psycopg2_binary-2.8.5-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:7036ccf715925251fac969f4da9ad37e4b7e211b1e920860148a10c0de963522"}, + {file = "psycopg2_binary-2.8.5-cp38-cp38-win32.whl", hash = "sha256:8f74e631b67482d504d7e9cf364071fc5d54c28e79a093ff402d5f8f81e23bfa"}, + {file = "psycopg2_binary-2.8.5-cp38-cp38-win_amd64.whl", hash = "sha256:fa466306fcf6b39b8a61d003123d442b23707d635a5cb05ac4e1b62cc79105cd"}, +] +pycparser = [ + {file = "pycparser-2.20-py2.py3-none-any.whl", hash = "sha256:7582ad22678f0fcd81102833f60ef8d0e57288b6b5fb00323d101be910e35705"}, + {file = "pycparser-2.20.tar.gz", hash = "sha256:2d475327684562c3a96cc71adf7dc8c4f0565175cf86b6d7a404ff4c771f15f0"}, +] +python-dateutil = [ + {file = "python-dateutil-2.8.1.tar.gz", hash = "sha256:73ebfe9dbf22e832286dafa60473e4cd239f8592f699aa5adaf10050e6e1823c"}, + {file = "python_dateutil-2.8.1-py2.py3-none-any.whl", hash = "sha256:75bb3f31ea686f1197762692a9ee6a7550b59fc6ca3a1f4b5d7e32fb98e2da2a"}, +] +python-multipart = [ + {file = "python-multipart-0.0.5.tar.gz", hash = "sha256:f7bb5f611fc600d15fa47b3974c8aa16e93724513b49b5f95c81e6624c83fa43"}, +] +pytz = [ + {file = "pytz-2020.1-py2.py3-none-any.whl", hash = "sha256:a494d53b6d39c3c6e44c3bec237336e14305e4f29bbf800b599253057fbb79ed"}, + {file = "pytz-2020.1.tar.gz", hash = "sha256:c35965d010ce31b23eeb663ed3cc8c906275d6be1a34393a1d73a41febf4a048"}, +] +regex = [ + {file = "regex-2020.6.8-cp27-cp27m-win32.whl", hash = "sha256:fbff901c54c22425a5b809b914a3bfaf4b9570eee0e5ce8186ac71eb2025191c"}, + {file = "regex-2020.6.8-cp27-cp27m-win_amd64.whl", hash = "sha256:112e34adf95e45158c597feea65d06a8124898bdeac975c9087fe71b572bd938"}, + {file = "regex-2020.6.8-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:92d8a043a4241a710c1cf7593f5577fbb832cf6c3a00ff3fc1ff2052aff5dd89"}, + {file = "regex-2020.6.8-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:bae83f2a56ab30d5353b47f9b2a33e4aac4de9401fb582b55c42b132a8ac3868"}, + {file = "regex-2020.6.8-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:b2ba0f78b3ef375114856cbdaa30559914d081c416b431f2437f83ce4f8b7f2f"}, + {file = "regex-2020.6.8-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:95fa7726d073c87141f7bbfb04c284901f8328e2d430eeb71b8ffdd5742a5ded"}, + {file = "regex-2020.6.8-cp36-cp36m-win32.whl", hash = "sha256:e3cdc9423808f7e1bb9c2e0bdb1c9dc37b0607b30d646ff6faf0d4e41ee8fee3"}, + {file = "regex-2020.6.8-cp36-cp36m-win_amd64.whl", hash = "sha256:c78e66a922de1c95a208e4ec02e2e5cf0bb83a36ceececc10a72841e53fbf2bd"}, + {file = "regex-2020.6.8-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:08997a37b221a3e27d68ffb601e45abfb0093d39ee770e4257bd2f5115e8cb0a"}, + {file = "regex-2020.6.8-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:2f6f211633ee8d3f7706953e9d3edc7ce63a1d6aad0be5dcee1ece127eea13ae"}, + {file = "regex-2020.6.8-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:55b4c25cbb3b29f8d5e63aeed27b49fa0f8476b0d4e1b3171d85db891938cc3a"}, + {file = "regex-2020.6.8-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:89cda1a5d3e33ec9e231ece7307afc101b5217523d55ef4dc7fb2abd6de71ba3"}, + {file = "regex-2020.6.8-cp37-cp37m-win32.whl", hash = "sha256:690f858d9a94d903cf5cada62ce069b5d93b313d7d05456dbcd99420856562d9"}, + {file = "regex-2020.6.8-cp37-cp37m-win_amd64.whl", hash = "sha256:1700419d8a18c26ff396b3b06ace315b5f2a6e780dad387e4c48717a12a22c29"}, + {file = "regex-2020.6.8-cp38-cp38-manylinux1_i686.whl", hash = "sha256:654cb773b2792e50151f0e22be0f2b6e1c3a04c5328ff1d9d59c0398d37ef610"}, + {file = "regex-2020.6.8-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:52e1b4bef02f4040b2fd547357a170fc1146e60ab310cdbdd098db86e929b387"}, + {file = "regex-2020.6.8-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:cf59bbf282b627130f5ba68b7fa3abdb96372b24b66bdf72a4920e8153fc7910"}, + {file = "regex-2020.6.8-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:5aaa5928b039ae440d775acea11d01e42ff26e1561c0ffcd3d805750973c6baf"}, + {file = "regex-2020.6.8-cp38-cp38-win32.whl", hash = "sha256:97712e0d0af05febd8ab63d2ef0ab2d0cd9deddf4476f7aa153f76feef4b2754"}, + {file = "regex-2020.6.8-cp38-cp38-win_amd64.whl", hash = "sha256:6ad8663c17db4c5ef438141f99e291c4d4edfeaacc0ce28b5bba2b0bf273d9b5"}, + {file = "regex-2020.6.8.tar.gz", hash = "sha256:e9b64e609d37438f7d6e68c2546d2cb8062f3adb27e6336bc129b51be20773ac"}, +] +royalnet = [ + {file = "royalnet-5.8.14-py3-none-any.whl", hash = "sha256:56cfe4966b636c5fe4deb763899c8a8b32c0827880dbe0774e181aefdcb7a640"}, + {file = "royalnet-5.8.14.tar.gz", hash = "sha256:caec812f7b1216bea94c5790607fc0ecf9bf75e5b6c751e68136ab2ad4397486"}, +] +six = [ + {file = "six-1.15.0-py2.py3-none-any.whl", hash = "sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced"}, + {file = "six-1.15.0.tar.gz", hash = "sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259"}, +] +sqlalchemy = [ + {file = "SQLAlchemy-1.3.17-cp27-cp27m-macosx_10_14_x86_64.whl", hash = "sha256:fe01bac7226499aedf472c62fa3b85b2c619365f3f14dd222ffe4f3aa91e5f98"}, + {file = "SQLAlchemy-1.3.17-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:b50f45d0e82b4562f59f0e0ca511f65e412f2a97d790eea5f60e34e5f1aabc9a"}, + {file = "SQLAlchemy-1.3.17-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:ce2646e4c0807f3461be0653502bb48c6e91a5171d6e450367082c79e12868bf"}, + {file = "SQLAlchemy-1.3.17-cp27-cp27m-win32.whl", hash = "sha256:e4e2664232005bd306f878b0f167a31f944a07c4de0152c444f8c61bbe3cfb38"}, + {file = "SQLAlchemy-1.3.17-cp27-cp27m-win_amd64.whl", hash = "sha256:925b4fe5e7c03ed76912b75a9a41dfd682d59c0be43bce88d3b27f7f5ba028fb"}, + {file = "SQLAlchemy-1.3.17-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:869bbb637de58ab0a912b7f20e9192132f9fbc47fc6b5111cd1e0f6cdf5cf9b0"}, + {file = "SQLAlchemy-1.3.17-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:703c002277f0fbc3c04d0ae4989a174753a7554b2963c584ce2ec0cddcf2bc53"}, + {file = "SQLAlchemy-1.3.17-cp35-cp35m-macosx_10_14_x86_64.whl", hash = "sha256:eb4fcf7105bf071c71068c6eee47499ab8d4b8f5a11fc35147c934f0faa60f23"}, + {file = "SQLAlchemy-1.3.17-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:8d01e949a5d22e5c4800d59b50617c56125fc187fbeb8fa423e99858546de616"}, + {file = "SQLAlchemy-1.3.17-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:a9e75e49a0f1583eee0ce93270232b8e7bb4b1edc89cc70b07600d525aef4f43"}, + {file = "SQLAlchemy-1.3.17-cp35-cp35m-win32.whl", hash = "sha256:a87d496884f40c94c85a647c385f4fd5887941d2609f71043e2b73f2436d9c65"}, + {file = "SQLAlchemy-1.3.17-cp35-cp35m-win_amd64.whl", hash = "sha256:6cd157ce74a911325e164441ff2d9b4e244659a25b3146310518d83202f15f7a"}, + {file = "SQLAlchemy-1.3.17-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:27e2efc8f77661c9af2681755974205e7462f1ae126f498f4fe12a8b24761d15"}, + {file = "SQLAlchemy-1.3.17-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:31c043d5211aa0e0773821fcc318eb5cbe2ec916dfbc4c6eea0c5188971988eb"}, + {file = "SQLAlchemy-1.3.17-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:a9030cd30caf848a13a192c5e45367e3c6f363726569a56e75dc1151ee26d859"}, + {file = "SQLAlchemy-1.3.17-cp36-cp36m-win32.whl", hash = "sha256:f502ef245c492b391e0e23e94cba030ab91722dcc56963c85bfd7f3441ea2bbe"}, + {file = "SQLAlchemy-1.3.17-cp36-cp36m-win_amd64.whl", hash = "sha256:128bc917ed20d78143a45024455ff0aed7d3b96772eba13d5dbaf9cc57e5c41b"}, + {file = "SQLAlchemy-1.3.17-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:2a12f8be25b9ea3d1d5b165202181f2b7da4b3395289000284e5bb86154ce87c"}, + {file = "SQLAlchemy-1.3.17-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:e8aa395482728de8bdcca9cc0faf3765ab483e81e01923aaa736b42f0294f570"}, + {file = "SQLAlchemy-1.3.17-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:f35248f7e0d63b234a109dd72fbfb4b5cb6cb6840b221d0df0ecbf54ab087654"}, + {file = "SQLAlchemy-1.3.17-cp37-cp37m-win32.whl", hash = "sha256:ce1ddaadee913543ff0154021d31b134551f63428065168e756d90bdc4c686f5"}, + {file = "SQLAlchemy-1.3.17-cp37-cp37m-win_amd64.whl", hash = "sha256:9cb1819008f0225a7c066cac8bb0cf90847b2c4a6eb9ebb7431dbd00c56c06c5"}, + {file = "SQLAlchemy-1.3.17-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:65eb3b03229f684af0cf0ad3bcc771970c1260a82a791a8d07bffb63d8c95bcc"}, + {file = "SQLAlchemy-1.3.17-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:8a0e0cd21da047ea10267c37caf12add400a92f0620c8bc09e4a6531a765d6d7"}, + {file = "SQLAlchemy-1.3.17-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:b7878e59ec31f12d54b3797689402ee3b5cfcb5598f2ebf26491732758751908"}, + {file = "SQLAlchemy-1.3.17-cp38-cp38-win32.whl", hash = "sha256:ce6c3d18b2a8ce364013d47b9cad71db815df31d55918403f8db7d890c9d07ae"}, + {file = "SQLAlchemy-1.3.17-cp38-cp38-win_amd64.whl", hash = "sha256:ed375a79f06cad285166e5be74745df1ed6845c5624aafadec4b7a29c25866ef"}, + {file = "SQLAlchemy-1.3.17.tar.gz", hash = "sha256:156a27548ba4e1fed944ff9fcdc150633e61d350d673ae7baaf6c25c04ac1f71"}, +] +starlette = [ + {file = "starlette-0.12.13.tar.gz", hash = "sha256:9597bc28e3c4659107c1c4a45ec32dc45e947d78fe56230222be673b2c36454a"}, +] +toml = [ + {file = "toml-0.10.1-py2.py3-none-any.whl", hash = "sha256:bda89d5935c2eac546d648028b9901107a595863cb36bae0c73ac804a9b4ce88"}, + {file = "toml-0.10.1.tar.gz", hash = "sha256:926b612be1e5ce0634a2ca03470f95169cf16f939018233a670519cb4ac58b0f"}, +] +tzlocal = [ + {file = "tzlocal-2.1-py2.py3-none-any.whl", hash = "sha256:e2cb6c6b5b604af38597403e9852872d7f534962ae2954c7f35efcb1ccacf4a4"}, + {file = "tzlocal-2.1.tar.gz", hash = "sha256:643c97c5294aedc737780a49d9df30889321cbe1204eac2c2ec6134035a92e44"}, +] +uvicorn = [ + {file = "uvicorn-0.10.9-py3-none-any.whl", hash = "sha256:dc7119b28e15c4c737315c5a570081b0a5a7d8d5c1e8a70a7be70043d88b23a7"}, + {file = "uvicorn-0.10.9.tar.gz", hash = "sha256:c010df69d16e27f1a18481316325b4fd23f562c1fac050915fc03a397d0f6b64"}, +] +uvloop = [ + {file = "uvloop-0.14.0-cp35-cp35m-macosx_10_11_x86_64.whl", hash = "sha256:08b109f0213af392150e2fe6f81d33261bb5ce968a288eb698aad4f46eb711bd"}, + {file = "uvloop-0.14.0-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:4544dcf77d74f3a84f03dd6278174575c44c67d7165d4c42c71db3fdc3860726"}, + {file = "uvloop-0.14.0-cp36-cp36m-macosx_10_11_x86_64.whl", hash = "sha256:b4f591aa4b3fa7f32fb51e2ee9fea1b495eb75b0b3c8d0ca52514ad675ae63f7"}, + {file = "uvloop-0.14.0-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:f07909cd9fc08c52d294b1570bba92186181ca01fe3dc9ffba68955273dd7362"}, + {file = "uvloop-0.14.0-cp37-cp37m-macosx_10_11_x86_64.whl", hash = "sha256:afd5513c0ae414ec71d24f6f123614a80f3d27ca655a4fcf6cabe50994cc1891"}, + {file = "uvloop-0.14.0-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:e7514d7a48c063226b7d06617cbb12a14278d4323a065a8d46a7962686ce2e95"}, + {file = "uvloop-0.14.0-cp38-cp38-macosx_10_11_x86_64.whl", hash = "sha256:bcac356d62edd330080aed082e78d4b580ff260a677508718f88016333e2c9c5"}, + {file = "uvloop-0.14.0-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:4315d2ec3ca393dd5bc0b0089d23101276778c304d42faff5dc4579cb6caef09"}, + {file = "uvloop-0.14.0.tar.gz", hash = "sha256:123ac9c0c7dd71464f58f1b4ee0bbd81285d96cdda8bc3519281b8973e3a461e"}, +] +websockets = [ + {file = "websockets-8.1-cp36-cp36m-macosx_10_6_intel.whl", hash = "sha256:3762791ab8b38948f0c4d281c8b2ddfa99b7e510e46bd8dfa942a5fff621068c"}, + {file = "websockets-8.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:3db87421956f1b0779a7564915875ba774295cc86e81bc671631379371af1170"}, + {file = "websockets-8.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:4f9f7d28ce1d8f1295717c2c25b732c2bc0645db3215cf757551c392177d7cb8"}, + {file = "websockets-8.1-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:295359a2cc78736737dd88c343cd0747546b2174b5e1adc223824bcaf3e164cb"}, + {file = "websockets-8.1-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:1d3f1bf059d04a4e0eb4985a887d49195e15ebabc42364f4eb564b1d065793f5"}, + {file = "websockets-8.1-cp36-cp36m-win32.whl", hash = "sha256:2db62a9142e88535038a6bcfea70ef9447696ea77891aebb730a333a51ed559a"}, + {file = "websockets-8.1-cp36-cp36m-win_amd64.whl", hash = "sha256:0e4fb4de42701340bd2353bb2eee45314651caa6ccee80dbd5f5d5978888fed5"}, + {file = "websockets-8.1-cp37-cp37m-macosx_10_6_intel.whl", hash = "sha256:9b248ba3dd8a03b1a10b19efe7d4f7fa41d158fdaa95e2cf65af5a7b95a4f989"}, + {file = "websockets-8.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:ce85b06a10fc65e6143518b96d3dca27b081a740bae261c2fb20375801a9d56d"}, + {file = "websockets-8.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:965889d9f0e2a75edd81a07592d0ced54daa5b0785f57dc429c378edbcffe779"}, + {file = "websockets-8.1-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:751a556205d8245ff94aeef23546a1113b1dd4f6e4d102ded66c39b99c2ce6c8"}, + {file = "websockets-8.1-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:3ef56fcc7b1ff90de46ccd5a687bbd13a3180132268c4254fc0fa44ecf4fc422"}, + {file = "websockets-8.1-cp37-cp37m-win32.whl", hash = "sha256:7ff46d441db78241f4c6c27b3868c9ae71473fe03341340d2dfdbe8d79310acc"}, + {file = "websockets-8.1-cp37-cp37m-win_amd64.whl", hash = "sha256:20891f0dddade307ffddf593c733a3fdb6b83e6f9eef85908113e628fa5a8308"}, + {file = "websockets-8.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:c1ec8db4fac31850286b7cd3b9c0e1b944204668b8eb721674916d4e28744092"}, + {file = "websockets-8.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:5c01fd846263a75bc8a2b9542606927cfad57e7282965d96b93c387622487485"}, + {file = "websockets-8.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:9bef37ee224e104a413f0780e29adb3e514a5b698aabe0d969a6ba426b8435d1"}, + {file = "websockets-8.1-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:d705f8aeecdf3262379644e4b55107a3b55860eb812b673b28d0fbc347a60c55"}, + {file = "websockets-8.1-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:c8a116feafdb1f84607cb3b14aa1418424ae71fee131642fc568d21423b51824"}, + {file = "websockets-8.1-cp38-cp38-win32.whl", hash = "sha256:e898a0863421650f0bebac8ba40840fc02258ef4714cb7e1fd76b6a6354bda36"}, + {file = "websockets-8.1-cp38-cp38-win_amd64.whl", hash = "sha256:f8a7bff6e8664afc4e6c28b983845c5bc14965030e3fb98789734d416af77c4b"}, + {file = "websockets-8.1.tar.gz", hash = "sha256:5c65d2da8c6bce0fca2528f69f44b2f977e06954c8512a952222cea50dad430f"}, +] diff --git a/pyproject.toml b/pyproject.toml index cc9bf31d..6da163be 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,21 +1,12 @@ # Remember to run `poetry update` after you edit this file! [tool.poetry] - # TODO: Insert here your Pack name! - name = "examplepack" - # TODO: Insert here your Pack description! - description = "An example pack for Royalnet." - # TODO: Increment this every new version of your pack! + name = "wikipack" + description = "A Wiki for Royalnet" version = "0.1.0" - # TODO: Set this to your full name and email! - authors = ["Name Surname "] - # TODO: Set this to the license you want to use for your Pack! - license = "" - # TODO: Set this to the GitHub page of your pack (or another website) - homepage = "https://github.com/your-username/" - # TODO: Set this to a link to the Pack documentation (if there's any) - documentation = "https://gh.steffo.eu/royalpack/" - # TODO: Pick some classifiers to include your Pack in: https://pypi.org/classifiers/ + authors = ["Stefano Pigozzi "] + license = "AGPL-3.0+" + homepage = "https://github.com/Steffo99/wikipack/" classifiers = [ "Development Status :: 3 - Alpha", "Operating System :: OS Independent", @@ -29,17 +20,10 @@ python = "^3.8" [tool.poetry.dependencies.royalnet] - version = "^5.1.6" - # TODO: select the Royalnet modules required by your pack + version = "~5.8.16" extras = [ - "telegram", - "discord", "alchemy_easy", - "bard", "constellation", - "sentry", - "herald", - "coloredlogs" ] # Development dependencies diff --git a/examplepack/commands/__init__.py b/wikipack/commands/__init__.py similarity index 72% rename from examplepack/commands/__init__.py rename to wikipack/commands/__init__.py index c9ffb60c..59260c80 100644 --- a/examplepack/commands/__init__.py +++ b/wikipack/commands/__init__.py @@ -1,9 +1,7 @@ # Imports go here! -# TODO: If you create a new command, remember to import it here... # from .example import ExampleCommand # Enter the commands of your Pack here! -# TODO: and add it to the list here! available_commands = [ # ExampleCommand, ] diff --git a/examplepack/events/__init__.py b/wikipack/events/__init__.py similarity index 72% rename from examplepack/events/__init__.py rename to wikipack/events/__init__.py index 8bafb696..8e909cef 100644 --- a/examplepack/events/__init__.py +++ b/wikipack/events/__init__.py @@ -1,9 +1,7 @@ # Imports go here! -# TODO: If you create a new event, remember to import it here... # from .example import ExampleEvent # Enter the commands of your Pack here! -# TODO: and add it to the list here! available_events = [ # ExampleEvent, ] diff --git a/wikipack/stars/__init__.py b/wikipack/stars/__init__.py new file mode 100644 index 00000000..4ed81310 --- /dev/null +++ b/wikipack/stars/__init__.py @@ -0,0 +1,10 @@ +# Imports go here! +from .api_wiki import ApiWikiStar + +# Enter the PageStars of your Pack here! +available_page_stars = [ + ApiWikiStar +] + +# Don't change this, it should automatically generate __all__ +__all__ = [command.__name__ for command in available_page_stars] diff --git a/wikipack/stars/api_wiki.py b/wikipack/stars/api_wiki.py new file mode 100644 index 00000000..ceb5f744 --- /dev/null +++ b/wikipack/stars/api_wiki.py @@ -0,0 +1,215 @@ +from typing import * +import royalnet.constellation.api as rca +import royalnet.utils as ru +from royalnet.backpack.tables import * +from ..tables import * +import datetime + + +class ApiWikiStar(rca.ApiStar): + path = "/api/wiki/v2" + + tags = ["wiki"] + + methods = ["GET", "POST", "PUT", "DELETE"] + + parameters = { + "get": { + "page_id": "The id of the wiki page to get the details of." + }, + "post": { + "category": "The category of the page.", + "title": "The title of the page.", + "contents": "The contents of the page.", + "format": "(Optional) The format of the page. Default is 'gfm' for GitHub Flavored Markdown.", + "role_to_view": "(Optional) The role required to view this page. Be careful to not lock yourself out!", + "role_to_edit": "(Optional) The role required to edit this page. Be careful to not lock yourself out!", + }, + "put": { + "page_id": "The id of the wiki page to create a new revision of.", + "category": "The category of the page.", + "title": "The title of the page.", + "contents": "The contents of the page.", + "format": "The format of the page. Default is 'gfm' for GitHub Flavored Markdown.", + "role_to_view": "The role required to view this page. Be careful to not lock yourself out!", + "role_to_edit": "The role required to edit this page. Be careful to not lock yourself out!", + }, + "delete": { + "page_id": "The id of the wiki page to delete.", + }, + } + + @property + def default_view_role(self) -> str: + return self.config["wikipack"]["roles"]["view"] + + async def can_view(self, user: User, page: WikiPage) -> bool: + lr = page.latest_revision + + if lr.role_to_view == "": + return True + + if lr.role_to_view: + if lr.role_to_view in user.roles or self.admin_role in user.roles: + return True + return False + return True + + @property + def default_edit_role(self) -> str: + return self.config["wikipack"]["roles"]["edit"] + + async def can_edit(self, user: User, page: WikiPage) -> bool: + lr = page.latest_revision + + if lr.role_to_edit == "": + return True + + if lr.role_to_edit: + if lr.role_to_edit in user.roles or self.admin_role in user.roles: + return True + return False + return True + + @property + def create_role(self) -> str: + return self.config["wikipack"]["roles"]["create"] + + async def can_create(self, user: User) -> bool: + if self.create_role in user.roles or self.admin_role in user.roles: + return True + return False + + @property + def delete_role(self) -> str: + return self.config["wikipack"]["roles"]["delete"] + + async def can_delete(self, user: User) -> bool: + if self.delete_role == "": + return True + + if self.delete_role in user.roles or self.admin_role in user.roles: + return True + return False + + @property + def admin_role(self) -> str: + return self.config["wikipack"]["roles"]["admin"] + + async def find_page(self, data: rca.ApiData) -> Tuple[WikiPage, WikiRevision]: + WikiPageT = self.alchemy.get(WikiPage) + + page_id = data.int("page_id") + + page: WikiPage = await ru.asyncify( + data.session.query(WikiPageT).filter_by(page_id=page_id).one_or_none + ) + + if page is None: + raise rca.NotFoundError(f"No page found with the id `{page_id}`.") + + return page, page.latest_revision + + async def get(self, data: rca.ApiData) -> ru.JSON: + """Get the details of a specific Wiki page.""" + page, lr = await self.find_page(data) + user = await data.user() + + if not self.can_view(user, page): + raise rca.ForbiddenError(f"Viewing this page requires the `{lr.role_to_view}` role.") + + return lr.json() + + async def post(self, data: rca.ApiData) -> ru.JSON: + """Create a new Wiki page.""" + WikiRevisionT = self.alchemy.get(WikiRevision) + + user = await data.user() + + if not self.can_create(user): + raise rca.ForbiddenError(f"Creating a new page requires the `{self.create_role}` role.") + + category = data.str("category", optional=False) + title = data.str("title", optional=False) + contents = data.str("contents", optional=False) + format_ = data.str("format", optional=True) + role_to_view = data.str("role_to_view", optional=True) + role_to_edit = data.str("role_to_edit", optional=True) + + page = WikiPage() + data.session.add(page) + data.session.flush() + + nr: WikiRevision = WikiRevisionT( + page_id=page.page_id, + category=category, + title=title, + contents=contents, + format=format_ or "gfm", + author=user, + timestamp=datetime.datetime.now(), + role_to_view=role_to_view or self.default_view_role, + role_to_edit=role_to_edit or self.default_edit_role, + ) + + data.session.add(nr) + await data.session.commit() + + return nr.json() + + async def put(self, data: rca.ApiData) -> ru.JSON: + """Edit a specific Wiki page, creating a new revision.""" + WikiRevisionT = self.alchemy.get(WikiRevision) + + page, lr = await self.find_page(data) + user = await data.user() + + if not self.can_edit(user, page): + raise rca.ForbiddenError(f"Editing this page requires the `{lr.role_to_edit}` role.") + + category = data.str("category", optional=True) + title = data.str("title", optional=True) + contents = data.str("contents", optional=True) + format_ = data.str("format", optional=True) + role_to_view = data.str("role_to_view", optional=True) + role_to_edit = data.str("role_to_edit", optional=True) + + nr: WikiRevision = WikiRevisionT( + page_id=page.page_id, + category=category or lr.category, + title=title or lr.title, + contents=contents or lr.contents, + format=format_ or lr.format, + author=user, + timestamp=datetime.datetime.now(), + role_to_view=role_to_view or lr.role_to_view, + role_to_edit=role_to_edit or lr.role_to_edit, + ) + + data.session.add(nr) + await data.session.commit() + + return nr.json() + + async def delete(self, data: rca.ApiData) -> ru.JSON: + """Delete a specific Wiki page and all its revisions.""" + WikiDeletionT = self.alchemy.get(WikiDeletion) + + page, lr = await self.find_page(data) + user = await data.user() + + if not self.can_delete(user): + raise rca.ForbiddenError(f"Deleting pages requires the `{self.delete_role}` role.") + + deletion = WikiDeletionT( + page_id=page.page_id, + deleted_by=user, + timestamp=datetime.datetime.now(), + ) + + data.session.delete(page) + data.session.add(deletion) + + await data.session.commit() + + return deletion.json() \ No newline at end of file diff --git a/examplepack/tables/__init__.py b/wikipack/tables/__init__.py similarity index 55% rename from examplepack/tables/__init__.py rename to wikipack/tables/__init__.py index e417ff16..c1a82dd0 100644 --- a/examplepack/tables/__init__.py +++ b/wikipack/tables/__init__.py @@ -1,9 +1,13 @@ # Imports go here! -# from .example import Example +from .wikipage import WikiPage +from .wikirevision import WikiRevision +from .wikideletion import WikiDeletion # Enter the tables of your Pack here! available_tables = [ - # Example + WikiPage, + WikiRevision, + WikiDeletion, ] # Don't change this, it should automatically generate __all__ diff --git a/wikipack/tables/wikideletion.py b/wikipack/tables/wikideletion.py new file mode 100644 index 00000000..241e5568 --- /dev/null +++ b/wikipack/tables/wikideletion.py @@ -0,0 +1,32 @@ +from sqlalchemy import * +from sqlalchemy.orm import * +from sqlalchemy.ext.declarative import declared_attr +import datetime +import royalnet.utils as ru + + +class WikiDeletion: + __tablename__ = "wikideletions" + + @declared_attr + def page_id(self): + return Column(Integer, primary_key=True) + + @declared_attr + def deleted_by_id(self) -> int: + return Column(Integer, ForeignKey("users.uid"), nullable=False) + + @declared_attr + def deleted_by(self): + return relationship("User", foreign_keys=self.deleted_by_id, backref="wiki_deletions") + + @declared_attr + def timestamp(self) -> datetime.datetime: + return Column(DateTime, nullable=False) + + def json(self) -> ru.JSON: + return { + "page_id": self.page_id, + "deleted_by": self.deleted_by.json(), + "timestamp": self.timestamp.isoformat(), + } diff --git a/wikipack/tables/wikipage.py b/wikipack/tables/wikipage.py new file mode 100644 index 00000000..02469796 --- /dev/null +++ b/wikipack/tables/wikipage.py @@ -0,0 +1,23 @@ +from typing import * +from sqlalchemy import * +from sqlalchemy.orm import * +from sqlalchemy.ext.declarative import declared_attr + +if TYPE_CHECKING: + from .wikirevision import WikiRevision + + +class WikiPage: + __tablename__ = "wikipages" + + @declared_attr + def page_id(self) -> int: + return Column(Integer, primary_key=True) + + @declared_attr + def latest_revision_id(self) -> int: + return Column(Integer, ForeignKey("wikirevisions.revision_id"), nullable=False) + + @declared_attr + def latest_revision(self) -> "WikiRevision": + return relationship("WikiRevision", foreign_keys=self.latest_revision_id, uselist=False) diff --git a/wikipack/tables/wikirevision.py b/wikipack/tables/wikirevision.py new file mode 100644 index 00000000..34322e2b --- /dev/null +++ b/wikipack/tables/wikirevision.py @@ -0,0 +1,92 @@ +from typing import * +from sqlalchemy import * +from sqlalchemy.orm import * +from sqlalchemy.ext.declarative import * +import royalnet.utils as ru +import datetime + + +if TYPE_CHECKING: + from royalnet.backpack.tables import User + from .wikipage import WikiPage + + +class WikiRevision: + __tablename__ = "wikirevisions" + + @declared_attr + def page_id(self) -> int: + return Column(Integer, ForeignKey("wikipages.page_id"), nullable=False) + + @declared_attr + def page(self) -> "WikiPage": + return relationship("WikiPage", + foreign_keys=self.page_id, + backref=backref("revisions", cascade="save-update, merge, delete")) + + @declared_attr + def revision_id(self) -> int: + return Column(Integer, primary_key=True) + + @declared_attr + def category(self) -> str: + return Column(String, nullable=False) + + @declared_attr + def title(self) -> str: + return Column(String, nullable=False) + + @declared_attr + def contents(self) -> str: + return Column(Text, nullable=False) + + @declared_attr + def format(self) -> str: + # GitHub Flavored Markdown + # https://github.github.com/gfm/ + return Column(String, nullable=False, default="gfm") + + @declared_attr + def author_id(self) -> int: + return Column(Integer, ForeignKey("users.uid"), nullable=False) + + @declared_attr + def author(self) -> "User": + return relationship("User", foreign_keys=self.author_id, backref="wiki_revisions") + + @declared_attr + def timestamp(self) -> datetime.datetime: + return Column(DateTime, nullable=False) + + @declared_attr + def role_to_view(self) -> str: + return Column(String) + + @declared_attr + def role_to_edit(self) -> str: + return Column(String) + + def set_as_latest(self) -> None: + self.page.latest_revision = self + + def json_list(self): + return { + "page_id": self.page_id, + "category": self.category, + "title": self.title, + "role_to_view": self.role_to_view, + } + + def json(self) -> ru.JSON: + return { + "page_id": self.page_id, + "revision_id": self.revision_id, + "category": self.category, + "title": self.title, + "contents": self.contents, + "format": self.format, + "author": self.author.json(), + "timestamp": self.timestamp.isoformat(), + "role_to_view": self.role_to_view, + "role_to_edit": self.role_to_edit, + } diff --git a/wikipack/version.py b/wikipack/version.py new file mode 100644 index 00000000..9ee900d3 --- /dev/null +++ b/wikipack/version.py @@ -0,0 +1 @@ +semantic = "0.1.0"