From 7208276c1802b0817d1d49e350e45689b26f10f6 Mon Sep 17 00:00:00 2001 From: AKP Date: Sat, 29 Oct 2022 13:53:02 +0100 Subject: [PATCH 01/10] Alter 2 files Add `poetry.lock` Update `pyproject.toml` --- backend/poetry.lock | 241 +++++++++++++++++++++++++++++++++++++++++ backend/pyproject.toml | 1 + 2 files changed, 242 insertions(+) create mode 100644 backend/poetry.lock diff --git a/backend/poetry.lock b/backend/poetry.lock new file mode 100644 index 0000000..6a76eea --- /dev/null +++ b/backend/poetry.lock @@ -0,0 +1,241 @@ +[[package]] +name = "atomicwrites" +version = "1.4.1" +description = "Atomic file writes." +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" + +[[package]] +name = "attrs" +version = "22.1.0" +description = "Classes Without Boilerplate" +category = "dev" +optional = false +python-versions = ">=3.5" + +[package.extras] +dev = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "mypy (>=0.900,!=0.940)", "pytest-mypy-plugins", "zope.interface", "furo", "sphinx", "sphinx-notfound-page", "pre-commit", "cloudpickle"] +docs = ["furo", "sphinx", "zope.interface", "sphinx-notfound-page"] +tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "mypy (>=0.900,!=0.940)", "pytest-mypy-plugins", "zope.interface", "cloudpickle"] +tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "mypy (>=0.900,!=0.940)", "pytest-mypy-plugins", "cloudpickle"] + +[[package]] +name = "click" +version = "8.1.3" +description = "Composable command line interface toolkit" +category = "main" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} + +[[package]] +name = "colorama" +version = "0.4.6" +description = "Cross-platform colored terminal text." +category = "main" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" + +[[package]] +name = "flask" +version = "2.2.2" +description = "A simple framework for building complex web applications." +category = "main" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +click = ">=8.0" +importlib-metadata = {version = ">=3.6.0", markers = "python_version < \"3.10\""} +itsdangerous = ">=2.0" +Jinja2 = ">=3.0" +Werkzeug = ">=2.2.2" + +[package.extras] +async = ["asgiref (>=3.2)"] +dotenv = ["python-dotenv"] + +[[package]] +name = "importlib-metadata" +version = "5.0.0" +description = "Read metadata from Python packages" +category = "main" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +zipp = ">=0.5" + +[package.extras] +docs = ["sphinx (>=3.5)", "jaraco.packaging (>=9)", "rst.linker (>=1.9)", "furo", "jaraco.tidelift (>=1.4)"] +perf = ["ipython"] +testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "flake8 (<5)", "pytest-cov", "pytest-enabler (>=1.3)", "packaging", "pyfakefs", "flufl.flake8", "pytest-perf (>=0.9.2)", "pytest-black (>=0.3.7)", "pytest-mypy (>=0.9.1)", "importlib-resources (>=1.3)"] + +[[package]] +name = "itsdangerous" +version = "2.1.2" +description = "Safely pass data to untrusted environments and back." +category = "main" +optional = false +python-versions = ">=3.7" + +[[package]] +name = "jinja2" +version = "3.1.2" +description = "A very fast and expressive template engine." +category = "main" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +MarkupSafe = ">=2.0" + +[package.extras] +i18n = ["Babel (>=2.7)"] + +[[package]] +name = "markupsafe" +version = "2.1.1" +description = "Safely add untrusted strings to HTML/XML markup." +category = "main" +optional = false +python-versions = ">=3.7" + +[[package]] +name = "more-itertools" +version = "9.0.0" +description = "More routines for operating on iterables, beyond itertools" +category = "dev" +optional = false +python-versions = ">=3.7" + +[[package]] +name = "packaging" +version = "21.3" +description = "Core utilities for Python packages" +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +pyparsing = ">=2.0.2,<3.0.5 || >3.0.5" + +[[package]] +name = "pluggy" +version = "0.13.1" +description = "plugin and hook calling mechanisms for python" +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" + +[package.extras] +dev = ["pre-commit", "tox"] + +[[package]] +name = "py" +version = "1.11.0" +description = "library with cross-python path, ini-parsing, io, code, log facilities" +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" + +[[package]] +name = "pyparsing" +version = "3.0.9" +description = "pyparsing module - Classes and methods to define and execute parsing grammars" +category = "dev" +optional = false +python-versions = ">=3.6.8" + +[package.extras] +diagrams = ["railroad-diagrams", "jinja2"] + +[[package]] +name = "pytest" +version = "5.4.3" +description = "pytest: simple powerful testing with Python" +category = "dev" +optional = false +python-versions = ">=3.5" + +[package.dependencies] +atomicwrites = {version = ">=1.0", markers = "sys_platform == \"win32\""} +attrs = ">=17.4.0" +colorama = {version = "*", markers = "sys_platform == \"win32\""} +more-itertools = ">=4.0.0" +packaging = "*" +pluggy = ">=0.12,<1.0" +py = ">=1.5.0" +wcwidth = "*" + +[package.extras] +checkqa-mypy = ["mypy (==v0.761)"] +testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "requests", "xmlschema"] + +[[package]] +name = "wcwidth" +version = "0.2.5" +description = "Measures the displayed width of unicode strings in a terminal" +category = "dev" +optional = false +python-versions = "*" + +[[package]] +name = "werkzeug" +version = "2.2.2" +description = "The comprehensive WSGI web application library." +category = "main" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +MarkupSafe = ">=2.1.1" + +[package.extras] +watchdog = ["watchdog"] + +[[package]] +name = "zipp" +version = "3.10.0" +description = "Backport of pathlib-compatible object wrapper for zip files" +category = "main" +optional = false +python-versions = ">=3.7" + +[package.extras] +docs = ["sphinx (>=3.5)", "jaraco.packaging (>=9)", "rst.linker (>=1.9)", "furo", "jaraco.tidelift (>=1.4)"] +testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "flake8 (<5)", "pytest-cov", "pytest-enabler (>=1.3)", "jaraco.itertools", "func-timeout", "jaraco.functools", "more-itertools", "pytest-black (>=0.3.7)", "pytest-mypy (>=0.9.1)"] + +[metadata] +lock-version = "1.1" +python-versions = "^3.8" +content-hash = "6662025d53c721988eccf3e4101ee5d3686702f80c82f900e6f4d087282743a1" + +[metadata.files] +atomicwrites = [] +attrs = [] +click = [] +colorama = [] +flask = [] +importlib-metadata = [] +itsdangerous = [] +jinja2 = [] +markupsafe = [] +more-itertools = [] +packaging = [] +pluggy = [ + {file = "pluggy-0.13.1-py2.py3-none-any.whl", hash = "sha256:966c145cd83c96502c3c3868f50408687b38434af77734af1e9ca461a4081d2d"}, + {file = "pluggy-0.13.1.tar.gz", hash = "sha256:15b2acde666561e1298d71b523007ed7364de07029219b604cf808bfa1c765b0"}, +] +py = [] +pyparsing = [] +pytest = [] +wcwidth = [ + {file = "wcwidth-0.2.5-py2.py3-none-any.whl", hash = "sha256:beb4802a9cebb9144e99086eff703a642a13d6a0052920003a230f3294bbe784"}, + {file = "wcwidth-0.2.5.tar.gz", hash = "sha256:c4d647b99872929fdb7bdcaa4fbe7f01413ed3d98077df798530e5b04f116c83"}, +] +werkzeug = [] +zipp = [] diff --git a/backend/pyproject.toml b/backend/pyproject.toml index 66d020a..a094268 100644 --- a/backend/pyproject.toml +++ b/backend/pyproject.toml @@ -6,6 +6,7 @@ authors = ["AKP "] [tool.poetry.dependencies] python = "^3.8" +Flask = "^2.2.2" [tool.poetry.dev-dependencies] pytest = "^5.2" From a1c32c342f74d9e1b0f7c8c06859aa82b4e364d3 Mon Sep 17 00:00:00 2001 From: AKP Date: Sat, 29 Oct 2022 14:01:25 +0100 Subject: [PATCH 02/10] Basic initial thingo --- backend/duckpond/__init__.py | 1 - backend/duckpond/__main__.py | 18 ++++++++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) delete mode 100644 backend/duckpond/__init__.py create mode 100644 backend/duckpond/__main__.py diff --git a/backend/duckpond/__init__.py b/backend/duckpond/__init__.py deleted file mode 100644 index b794fd4..0000000 --- a/backend/duckpond/__init__.py +++ /dev/null @@ -1 +0,0 @@ -__version__ = '0.1.0' diff --git a/backend/duckpond/__main__.py b/backend/duckpond/__main__.py new file mode 100644 index 0000000..d4eda80 --- /dev/null +++ b/backend/duckpond/__main__.py @@ -0,0 +1,18 @@ +import flask + +__version__ = '0.1.0' + + +def main(): + app = flask.Flask(__name__) + app.add_url_rule("/", view_func=index) + + app.run(port=8080, debug=True, host="127.0.0.1") + + +def index(): + return "

Hello world!

" + + +if __name__ == "__main__": + main() \ No newline at end of file From 6b3ca4248f2665ac311492b160d6ab66d5b519fb Mon Sep 17 00:00:00 2001 From: AKP Date: Sat, 29 Oct 2022 14:20:02 +0100 Subject: [PATCH 03/10] Add requirements.txt Signed-off-by: AKP --- backend/requirements.txt | 1 + 1 file changed, 1 insertion(+) create mode 100644 backend/requirements.txt diff --git a/backend/requirements.txt b/backend/requirements.txt new file mode 100644 index 0000000..7e10602 --- /dev/null +++ b/backend/requirements.txt @@ -0,0 +1 @@ +flask From f75dde9ac71f301b94c7deb58286450677a720ad Mon Sep 17 00:00:00 2001 From: AKP Date: Sat, 29 Oct 2022 14:43:59 +0100 Subject: [PATCH 04/10] Alter 4 files Update `.gitignore` Update `__main__.py` Add `db.py` Add `endpoints.py` --- .gitignore | 1 + backend/duckpond/__main__.py | 12 +++++++----- backend/duckpond/db.py | 8 ++++++++ backend/duckpond/endpoints.py | 14 ++++++++++++++ 4 files changed, 30 insertions(+), 5 deletions(-) create mode 100644 backend/duckpond/db.py create mode 100644 backend/duckpond/endpoints.py diff --git a/.gitignore b/.gitignore index 82d44c8..e7d7c37 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +duckpond.db ### Python ### # Byte-compiled / optimized / DLL files diff --git a/backend/duckpond/__main__.py b/backend/duckpond/__main__.py index d4eda80..516b8f7 100644 --- a/backend/duckpond/__main__.py +++ b/backend/duckpond/__main__.py @@ -1,18 +1,20 @@ import flask +import db +import endpoints + + __version__ = '0.1.0' def main(): app = flask.Flask(__name__) - app.add_url_rule("/", view_func=index) + + database = db.DB("duckpond.db") + _ = endpoints.Endpoints(app, database) app.run(port=8080, debug=True, host="127.0.0.1") -def index(): - return "

Hello world!

" - - if __name__ == "__main__": main() \ No newline at end of file diff --git a/backend/duckpond/db.py b/backend/duckpond/db.py new file mode 100644 index 0000000..13e964a --- /dev/null +++ b/backend/duckpond/db.py @@ -0,0 +1,8 @@ +import sqlite3 + +class DB: + conn: sqlite3.Connection + + def __init__(self, filename: str): + self.conn = sqlite3.connect(filename) + \ No newline at end of file diff --git a/backend/duckpond/endpoints.py b/backend/duckpond/endpoints.py new file mode 100644 index 0000000..c5559dc --- /dev/null +++ b/backend/duckpond/endpoints.py @@ -0,0 +1,14 @@ +import flask + +import db + +class Endpoints: + db: db.DB + + def __init__(self, app: flask.Flask, database: db.DB): + self.db = database + + app.add_url_rule("/", view_func=self.index) + + def index(self): + return "

Hello world

" \ No newline at end of file From e2f8c087d94f078566288f2ec7c95992504f530f Mon Sep 17 00:00:00 2001 From: AKP Date: Sat, 29 Oct 2022 15:48:27 +0100 Subject: [PATCH 05/10] Alter 3 files Update `README.md` Update `endpoints.py` Add `paths.py` --- backend/README.md | 59 ++++++++++++++++++++++++++++++++++- backend/duckpond/endpoints.py | 32 +++++++++++++++++-- backend/duckpond/paths.py | 2 ++ 3 files changed, 89 insertions(+), 4 deletions(-) create mode 100644 backend/duckpond/paths.py diff --git a/backend/README.md b/backend/README.md index 2d93090..f407cd7 100644 --- a/backend/README.md +++ b/backend/README.md @@ -1 +1,58 @@ -# Duck Pond backend \ No newline at end of file +# Duck Pond backend + +## Endpoints + +### GET `/entries` + +Gets a list of all the duck ponds in the database. + +### POST `/entries//new` + +Creates a new duck pond entry. + +JSON body arguments: +* `name` - reqiured +* `location` - required, in the form + ```json + { + "lat": -1.2345, + "long": 33.56643 + } + ``` +* `imageURL` + +Votes will be initialised as zero. + +Return sample: + +```json +{ + "id": "uuid" +} +``` + +### PATCH `/entries/` + +Updates an entry with the ID `id`. + +JSON body arguments: +* `name` +* `location` +* `imageURL` +* `votes` + +### GET `/entries/` + +Gets the JSON of the pond. + +```json +{ + "id": "uuid", + "name": "The Pondiest Pond", + "location": { + "lat": -1.2345, + "long": 33.56643 + }, + "imageURL": "https://example.com" +} +``` \ No newline at end of file diff --git a/backend/duckpond/endpoints.py b/backend/duckpond/endpoints.py index c5559dc..db4baa1 100644 --- a/backend/duckpond/endpoints.py +++ b/backend/duckpond/endpoints.py @@ -1,6 +1,7 @@ import flask import db +import paths class Endpoints: db: db.DB @@ -8,7 +9,32 @@ class Endpoints: def __init__(self, app: flask.Flask, database: db.DB): self.db = database - app.add_url_rule("/", view_func=self.index) + app.add_url_rule(paths.ENTRIES, view_func=self.list_entries, methods=["GET"]) + app.add_url_rule(paths.GET_ENTRY, view_func=self.get_entry, methods=["GET"]) - def index(self): - return "

Hello world

" \ No newline at end of file + def list_entries(self): + # TODO: populate from databaase + + return flask.jsonify([{ + "id": "203fc6a0-9587-41a4-9862-e1b72039b98b", + "name": "Birmingham Duck Pond", + "location": { + "lat": -1.2345, + "long": 33.4567 + }, + "imageURL": "https://example.com/image.png" + }, { + "id": "b140e048-ea2c-4827-b670-ef41ba48c56d", + "name": "Northwich Duck Pond", + "location": { + "lat": -3.2345, + "long": 25.4567 + }, + "imageURL": "https://example.com/image.png" + }]) + + def get_entry(self, id: str): + return flask.jsonify({ + "id": id, + "TODO": "TODO" + }) \ No newline at end of file diff --git a/backend/duckpond/paths.py b/backend/duckpond/paths.py new file mode 100644 index 0000000..601aa90 --- /dev/null +++ b/backend/duckpond/paths.py @@ -0,0 +1,2 @@ +ENTRIES = "/entries" +GET_ENTRY = "/entry/" \ No newline at end of file From f3327f8966f1ff99db3a9da9ce9ec955a0bde777 Mon Sep 17 00:00:00 2001 From: ellie Date: Sat, 29 Oct 2022 16:00:43 +0100 Subject: [PATCH 06/10] made SQL queries --- backend/duckpond/db.py | 37 ++++++++++++++++++++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/backend/duckpond/db.py b/backend/duckpond/db.py index 13e964a..6e4b83e 100644 --- a/backend/duckpond/db.py +++ b/backend/duckpond/db.py @@ -5,4 +5,39 @@ class DB: def __init__(self, filename: str): self.conn = sqlite3.connect(filename) - \ No newline at end of file + + def _setup(self, filename: str): + cursor = self.conn.cursor() + + cursor.execute(''' + CREATE TABLE IF NOT EXISTS entries + ([ID] INTEGER PRIMARY KEY, [title] TEXT, [latitude] FLOAT, [longitude] FLOAT, [votes] INTEGER, [image_url], STRING) + ''') + + cursor.commit() + + def getEntries(self): + cursor = self.conn.cursor() + cursor.execute('SELECT * FROM entries') + + myresult = cursor.fetchall() + + for x in myresult: + print(x) + + def addEntry(self, title, latitude, longitude, votes, image_url): + insertArray = [title, latitude, longitude, votes, image_url] + cursor = self.conn.cursor() + cursor.execute('INSERT INTO entries (title, latitude, longitude, votes, image_url) VALUES (?, ?, ?, ?, ?);', insertArray) + cursor.commit() + + def deleteEntry(self, ID): + cursor = self.conn.cursor() + cursor.execute('DELETE FROM entries WHERE ID = ?', ID) + cursor.commit() + + def updateEntry(self, ID, title, latitude, longitude, votes, image_url): + updateArray = [ID, title, latitude, longitude, votes, image_url] + cursor = self.conn.cursor() + cursor.execute('UPDATE entries SET title = ?, latitude = ?, longitude = ?, votes = ?, image_url = ?;', updateArray) + cursor.commit() \ No newline at end of file From 1e24048ba3ea78cf8622c2d3e0b93bba1ffcbc0f Mon Sep 17 00:00:00 2001 From: AKP Date: Sat, 29 Oct 2022 16:49:37 +0100 Subject: [PATCH 07/10] Alter 2 files Update `endpoints.py` Update `paths.py` --- backend/duckpond/endpoints.py | 94 +++++++++++++++++++++++++++-------- backend/duckpond/paths.py | 4 +- 2 files changed, 77 insertions(+), 21 deletions(-) diff --git a/backend/duckpond/endpoints.py b/backend/duckpond/endpoints.py index db4baa1..c4456ca 100644 --- a/backend/duckpond/endpoints.py +++ b/backend/duckpond/endpoints.py @@ -1,8 +1,55 @@ +from re import A +from typing import * +import urllib +import uuid import flask +from dataclasses import dataclass import db import paths + +@dataclass +class Entry: + id: str + name: str + location_lat: float + location_long: float + votes: int + image_url: Optional[str] + + def validate(self, only_populated_fields=False) -> Tuple[bool, str]: + if self.name == "" and not only_populated_fields: + return False, "name cannot be empty" + + if self.votes < 0: + return False, "votes cannot be negative" + + if self.image_url is not None: + try: + urllib.parse.urlparse(self.image_url) + except Exception: + return False, "invalid URL" + + return True, "" + + def as_dict(self) -> Dict: + res = {} + + res["id"] = self.id + res["name"] = self.name + res["location"] = { + "lat": self.location_lat, + "long": self.location_long, + } + res["votes"] = self.votes + + if self.image_url is not None: + res["imageURL"] = self.image_url + + return res + + class Endpoints: db: db.DB @@ -11,30 +58,37 @@ class Endpoints: app.add_url_rule(paths.ENTRIES, view_func=self.list_entries, methods=["GET"]) app.add_url_rule(paths.GET_ENTRY, view_func=self.get_entry, methods=["GET"]) + app.add_url_rule(paths.UPDATE_ENTRY, view_func=self.update_entry, methods=["PATCH"]) + app.add_url_rule(paths.CREATE_ENTRY, view_func=self.create_entry, methods=["POST"]) def list_entries(self): # TODO: populate from databaase - return flask.jsonify([{ - "id": "203fc6a0-9587-41a4-9862-e1b72039b98b", - "name": "Birmingham Duck Pond", - "location": { - "lat": -1.2345, - "long": 33.4567 - }, - "imageURL": "https://example.com/image.png" - }, { - "id": "b140e048-ea2c-4827-b670-ef41ba48c56d", - "name": "Northwich Duck Pond", - "location": { - "lat": -3.2345, - "long": 25.4567 - }, - "imageURL": "https://example.com/image.png" - }]) + a = Entry("203fc6a0-9587-41a4-9862-e1b72039b98b", "Birmingham Duck Pond", -1.2345, 33.4567, 0, None) + b = Entry("b140e048-ea2c-4827-b670-ef41ba48c56d", "Northwich Duck Pond", -3.2345, 25.4567, 0, None) + + return flask.jsonify([a.as_dict(), b.as_dict()]) + + def get_entry(self, entry_id: str): + # TODO: Fetch from database - def get_entry(self, id: str): return flask.jsonify({ - "id": id, + "id": entry_id, "TODO": "TODO" - }) \ No newline at end of file + }) + + def update_entry(self): + return "", 204 + + def create_entry(self): + body = flask.request.get_json() + if body is None: + return "no JSON body", 400 + + # TODO: validate inputs + + # TODO: store in database + + return flask.jsonify({ + "id": uuid.uuidv4() + }) diff --git a/backend/duckpond/paths.py b/backend/duckpond/paths.py index 601aa90..ff2e699 100644 --- a/backend/duckpond/paths.py +++ b/backend/duckpond/paths.py @@ -1,2 +1,4 @@ ENTRIES = "/entries" -GET_ENTRY = "/entry/" \ No newline at end of file +GET_ENTRY = "/entry/" +UPDATE_ENTRY = "/entry/" +CREATE_ENTRY = "/entry" \ No newline at end of file From 6fa3a29264210d80d6faaee09b991ca699ba31e4 Mon Sep 17 00:00:00 2001 From: AKP Date: Sat, 29 Oct 2022 17:53:04 +0100 Subject: [PATCH 08/10] Alter 2 files Update `db.py` Update `endpoints.py` --- backend/duckpond/db.py | 44 ++++++++++++++++++++++++++++++++++ backend/duckpond/endpoints.py | 45 ++--------------------------------- 2 files changed, 46 insertions(+), 43 deletions(-) diff --git a/backend/duckpond/db.py b/backend/duckpond/db.py index 6e4b83e..5bdc5ad 100644 --- a/backend/duckpond/db.py +++ b/backend/duckpond/db.py @@ -1,4 +1,48 @@ import sqlite3 +from dataclasses import dataclass +from typing import * +import urllib + +@dataclass +class Entry: + id: str + name: str + location_lat: float + location_long: float + votes: int + image_url: Optional[str] + + def validate(self, only_populated_fields=False) -> Tuple[bool, str]: + if self.name == "" and not only_populated_fields: + return False, "name cannot be empty" + + if self.votes < 0: + return False, "votes cannot be negative" + + if self.image_url is not None: + try: + urllib.parse.urlparse(self.image_url) + except Exception: + return False, "invalid URL" + + return True, "" + + def as_dict(self) -> Dict: + res = {} + + res["id"] = self.id + res["name"] = self.name + res["location"] = { + "lat": self.location_lat, + "long": self.location_long, + } + res["votes"] = self.votes + + if self.image_url is not None: + res["imageURL"] = self.image_url + + return res + class DB: conn: sqlite3.Connection diff --git a/backend/duckpond/endpoints.py b/backend/duckpond/endpoints.py index c4456ca..74d4f06 100644 --- a/backend/duckpond/endpoints.py +++ b/backend/duckpond/endpoints.py @@ -9,47 +9,6 @@ import db import paths -@dataclass -class Entry: - id: str - name: str - location_lat: float - location_long: float - votes: int - image_url: Optional[str] - - def validate(self, only_populated_fields=False) -> Tuple[bool, str]: - if self.name == "" and not only_populated_fields: - return False, "name cannot be empty" - - if self.votes < 0: - return False, "votes cannot be negative" - - if self.image_url is not None: - try: - urllib.parse.urlparse(self.image_url) - except Exception: - return False, "invalid URL" - - return True, "" - - def as_dict(self) -> Dict: - res = {} - - res["id"] = self.id - res["name"] = self.name - res["location"] = { - "lat": self.location_lat, - "long": self.location_long, - } - res["votes"] = self.votes - - if self.image_url is not None: - res["imageURL"] = self.image_url - - return res - - class Endpoints: db: db.DB @@ -64,8 +23,8 @@ class Endpoints: def list_entries(self): # TODO: populate from databaase - a = Entry("203fc6a0-9587-41a4-9862-e1b72039b98b", "Birmingham Duck Pond", -1.2345, 33.4567, 0, None) - b = Entry("b140e048-ea2c-4827-b670-ef41ba48c56d", "Northwich Duck Pond", -3.2345, 25.4567, 0, None) + a = db.Entry("203fc6a0-9587-41a4-9862-e1b72039b98b", "Birmingham Duck Pond", -1.2345, 33.4567, 0, None) + b = db.Entry("b140e048-ea2c-4827-b670-ef41ba48c56d", "Northwich Duck Pond", -3.2345, 25.4567, 0, None) return flask.jsonify([a.as_dict(), b.as_dict()]) From d684d3c12bd35cf8d57cfcc419f597c98bf6a40f Mon Sep 17 00:00:00 2001 From: AKP Date: Sat, 29 Oct 2022 18:45:09 +0100 Subject: [PATCH 09/10] Alter 2 files Update `db.py` Update `endpoints.py` --- backend/duckpond/db.py | 5 ++++- backend/duckpond/endpoints.py | 19 +++++++++++++++++-- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/backend/duckpond/db.py b/backend/duckpond/db.py index 5bdc5ad..07b94a5 100644 --- a/backend/duckpond/db.py +++ b/backend/duckpond/db.py @@ -13,7 +13,7 @@ class Entry: image_url: Optional[str] def validate(self, only_populated_fields=False) -> Tuple[bool, str]: - if self.name == "" and not only_populated_fields: + if (self.name == "" or self.name is None) and not only_populated_fields: return False, "name cannot be empty" if self.votes < 0: @@ -25,6 +25,9 @@ class Entry: except Exception: return False, "invalid URL" + if self.location_lat is None or self.location_long is None: + return False, "missing locations" + return True, "" def as_dict(self) -> Dict: diff --git a/backend/duckpond/endpoints.py b/backend/duckpond/endpoints.py index 74d4f06..5f64061 100644 --- a/backend/duckpond/endpoints.py +++ b/backend/duckpond/endpoints.py @@ -21,7 +21,7 @@ class Endpoints: app.add_url_rule(paths.CREATE_ENTRY, view_func=self.create_entry, methods=["POST"]) def list_entries(self): - # TODO: populate from databaase + # TODO: populate from databaase a = db.Entry("203fc6a0-9587-41a4-9862-e1b72039b98b", "Birmingham Duck Pond", -1.2345, 33.4567, 0, None) b = db.Entry("b140e048-ea2c-4827-b670-ef41ba48c56d", "Northwich Duck Pond", -3.2345, 25.4567, 0, None) @@ -44,7 +44,22 @@ class Endpoints: if body is None: return "no JSON body", 400 - # TODO: validate inputs + coordinates = body.get("location", None) + if coordinates is None: + return "missing location", 400 + + new_entry = db.Entry( + uuid.uuid4(), + body.get("name"), + coordinates.get("lat"), + coordinates.get("long"), + 0, + body.get("imageURL")) + + validation_result, error_text = new_entry.validate() + + if not validation_result: + return flask.abort(400, error_text) # TODO: store in database From 8bf86dbd1c2edeb974a897bc2b712e004fabdb71 Mon Sep 17 00:00:00 2001 From: AKP Date: Sat, 29 Oct 2022 18:47:14 +0100 Subject: [PATCH 10/10] Update `endpoints.py` --- backend/duckpond/endpoints.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/backend/duckpond/endpoints.py b/backend/duckpond/endpoints.py index 5f64061..0592a7d 100644 --- a/backend/duckpond/endpoints.py +++ b/backend/duckpond/endpoints.py @@ -1,9 +1,6 @@ -from re import A from typing import * -import urllib import uuid import flask -from dataclasses import dataclass import db import paths @@ -62,6 +59,7 @@ class Endpoints: return flask.abort(400, error_text) # TODO: store in database + # TODO: Form responses return flask.jsonify({ "id": uuid.uuidv4()