From 81b069b0f411f0d76deaea3ddfd828582ac03aaa Mon Sep 17 00:00:00 2001 From: Peery Date: Sun, 20 Mar 2022 20:02:49 +0100 Subject: [PATCH] Implemented Topic API and tests Added (Artist) Topic POST and DELETE calls as well as tests for them. The tests do not include Artist-to-Topic assignment. --- database/database.py | 38 ++++++- database/models.py | 2 +- main.py | 32 +++++- tests/createAPI/create_delete_tag.py | 2 +- tests/createAPI/create_delete_topic.py | 131 +++++++++++++++++++++++++ tests/run_tests.py | 10 +- 6 files changed, 203 insertions(+), 12 deletions(-) create mode 100644 tests/createAPI/create_delete_topic.py diff --git a/database/database.py b/database/database.py index fb6b948..c6842e6 100644 --- a/database/database.py +++ b/database/database.py @@ -6,7 +6,7 @@ import sqlalchemy.orm as db import sqlalchemy from sqlalchemy.orm import declarative_base, relationship, load_only -from database.models import Art, ArtnoID, Artist, Presence, TagCategory, TagCategorynoID, TagNoID, Tag +from database.models import Art, ArtnoID, Artist, Presence, TagCategory, TagCategorynoID, TagNoID, Tag, Topic, TopicNoId # TODO read sensitive data from environment file or similar SQLALCHEMY_DATABASE_URL = "postgresql://artnet_editor:G606Rm9sFEXe6wfTLxVu@[::1]/test_artnet" @@ -361,6 +361,9 @@ class Database: # Topic + def get_topic_list(self): + return self.__get_db().query(DBTopic).all() # TODO fix StackOverflow + def get_topic_by_id(self, id: int) -> DBTopic: result = self.__get_db().query(DBTopic).filter(DBTopic.id == id).first() return result @@ -369,10 +372,37 @@ class Database: result = self.__get_db().query(DBTopic).filter(func.lower(DBTopic.name) == name.lower()).first() return result - def update_topic(self, name: str, description: str): - raise NotImplementedError + def create_topic_by_model(self, topic: TopicNoId): + if not isinstance(topic.name, str) and len(topic.name) > 2: + raise ValueError("Topic name must be at least 2 letters long!") - def delete_topic(self, name: str): + if self.get_topic_by_name(topic.name) is not None: + raise ValueError(f"Topic name must be unique! '{topic.name}' already exists!") + + db_topic = DBTopic(name=topic.name, description=topic.description) + db = self.__get_db() + db.add(db_topic) + db.commit() + + return db_topic + + def update_topic_by_model(self, topic: Topic): + db_topic = self.get_topic_by_id(topic.id) + + db_topic.name = topic.name + db_topic.description = topic.description + + self.__get_db().commit() + + return db_topic + + def delete_topic_by_id(self, id: int): + db_topic = self.get_topic_by_id(id) + + self.__get_db().delete(db_topic) + self.__get_db().commit() + + def delete_topic_by_name(self, name: str): raise NotImplementedError # Artist -> Topic diff --git a/database/models.py b/database/models.py index 339b5d2..61d4da3 100644 --- a/database/models.py +++ b/database/models.py @@ -11,7 +11,7 @@ class TopicNoId(BaseModel): class Topic(TopicNoId): - topic_id: int + id: int class ArtistNoId(BaseModel): diff --git a/main.py b/main.py index 553d8c6..7d92182 100644 --- a/main.py +++ b/main.py @@ -5,7 +5,7 @@ from typing import List, Tuple from database.database import Database from database.database import DBPresence, DBArt2Presence -from database.models import Art, ArtnoID, Presence, ArtistNoId, Tag, TagNoID, TagCategory, TagCategorynoID +from database.models import Art, ArtnoID, Presence, ArtistNoId, Tag, TagNoID, TagCategory, TagCategorynoID, TopicNoId, Topic app = FastAPI() db = Database() @@ -198,10 +198,10 @@ async def artist(id: int): async def topic(name: str = None, id: int = None, artist_id: int = None): name = unquote(name) if name is not None else None print(f"Received GET on /artnet/metadata/topic (name={name}, id={id}, artist_id={artist_id})") - if name is None and id is None and artist_id is None: - raise HTTPException(status_code=406) - result = None + if name is None and id is None and artist_id is None: + result = db.get_topic_list() + if name is not None or id is not None: if id is not None: result = db.get_topic_by_id(id) @@ -221,6 +221,30 @@ async def topic(name: str = None, id: int = None, artist_id: int = None): raise HTTPException(status_code=404, detail="Topic was not found!") +@app.post("/artnet/metadata/topic") +async def topic(topic: TopicNoId, id: int = None): + print(f"Received POST on /artnet/metadata/topic (id={id}) body: topic={topic}") + + if id is None: # creating new topic + try: + new_id = db.create_topic_by_model(topic).id + return {"id": new_id} + except ValueError as e: + raise HTTPException(status_code=406, detail=str(e)) + else: # updating topic + topic_ = Topic(name=topic.name, description=topic.description, id=id) + db.update_topic_by_model(topic_) + + +@app.delete("/artnet/metadata/topic") +async def topic(id: int): + print(f"Received DELETE on /artnet/metadata/topic (id={id})") + + if db.get_topic_by_id(id) is None: + raise HTTPException(status_code=404, detail="No topic with this id could be found!") + db.delete_topic_by_id(id) + + @app.get("/artnet/metadata/tag") async def tag(id: int = None, name: str = None, fuzzy: bool = False, category: int = None): name = unquote(name) if name is not None else None diff --git a/tests/createAPI/create_delete_tag.py b/tests/createAPI/create_delete_tag.py index d140944..e334f18 100644 --- a/tests/createAPI/create_delete_tag.py +++ b/tests/createAPI/create_delete_tag.py @@ -174,7 +174,7 @@ def run_tag_test(url: str, port: int): print(f"Found {len(list_tags(url, port))} tag entries!") print("Deleting the tag entries ...") delete_tag_entries(url, port) # would throw an exception if an error occurred - delete_tag_result = True + delete_tag_result = False if not len(list_tags(url, port)) == 0 else True print(f"Found {len(list_tags(url, port))} tag entries!") # Clean up diff --git a/tests/createAPI/create_delete_topic.py b/tests/createAPI/create_delete_topic.py new file mode 100644 index 0000000..d5635d6 --- /dev/null +++ b/tests/createAPI/create_delete_topic.py @@ -0,0 +1,131 @@ +import requests +import json +from typing import List, Tuple + +from tests.createAPI.create_delete_artist import create_artist_entries, test_artist_entries, delete_artist_entries, \ + list_artists + +test_topic_entries = [{"name": "topic_name1", "description": "description1", "id": None, "artists": [0, 1]}, + {"name": "topic_name2", "description": "description2", "id": None, "artists": [1, 2]}] + + +def create_topic_entries(url: str, port: int): + """ + Create many topic entries for testing purposes + :param url: + :param port: + :return: + """ + for i in range(len(test_topic_entries)): + r = create_topic(url, port, name=test_topic_entries[i]["name"], desc=test_topic_entries[i]["description"]) + + if r.status_code != 200: + print(f"Create Topic Entry Test Nr.{i}: failed with {r.status_code} and reason {r.text}") + raise Exception("Create Topic Entry Test: FAILED") + else: + test_topic_entries[i]["id"] = json.loads(r.text)["id"] + + +def update_topic_entries(url: str, port: int): + for i in range(len(test_topic_entries)): + new_name = test_topic_entries[i]["name"] + "_updated" + new_description = test_topic_entries[i]["description"] + "_updated" + + r = update_topic(url, port, id=test_topic_entries[i]["id"], name=new_name, desc=new_description) + if r.status_code != 200: + print(f"Updating Topuc Entry Test Nr.{i}: failed with {r.status_code} and reason {r.text}") + raise Exception("Update Topic Entry Test: FAILED") + + topic = get_topic_by_id(url, port, test_topic_entries[i]["id"]) + if not topic["name"] == new_name: + print(f"Name is not matching the expected one! " + f"Current: {topic['name']} Expceted: {new_name}") + raise Exception("Update Topic Entry Test: FAILED") + if not topic["description"] == new_description: + print(f"Description is not matching the expected one! " + f"Current: {topic['description']} Expceted: {new_description}") + raise Exception("Update Topic Entry Test: FAILED") + + +def delete_topic_entries(url: str, port: int): + for i in range(len(test_topic_entries)): + r = delete_topic(url, port, id=test_topic_entries[i]["id"]) + + if r.status_code != 200: + print(f"Delete Topic Entry Test Nr.{i}: failed with {r.status_code} and reason {r.text}") + raise Exception("Delete Topic Entry Test: FAILED") + + +def delete_all_topics(url: str, port: int): + topics = list_topics(url, port) + print(f"Removing following topics: {topics}") + for topic in topics: + delete_topic(url, port, id=topic["id"]) + + +def list_topics(url: str, port: int): + r = requests.get(f"http://{url}:{port}/artnet/metadata/topic") + if r.status_code != 200: + raise Exception(f"Failed querying for topics! Status: {r.status_code} Reason: {r.text}") + topics = json.loads(r.text) + return topics + + +def get_topic_by_id(url: str, port: int, id: str): + r = requests.get(f"http://{url}:{port}/artnet/metadata/topic?id={id}") + + if r.status_code != 200: + raise Exception("Failed querying for topic!") + + return json.loads(r.text) + + +def create_topic(url: str, port: int, name: str, desc: str): + r = requests.post(f"http://{url}:{port}/artnet/metadata/topic", + json={"name": name, "description": desc}) + return r + + +def update_topic(url: str, port: int, id: str, name: str = None, desc: str = None): + r = requests.post(f"http://{url}:{port}/artnet/metadata/topic?id={id}", + json={"name": name, "description": desc}) + return r + + +def delete_topic(url: str, port: int, id: str): + r = requests.delete(f"http://{url}:{port}/artnet/metadata/topic?id={id}") + return r + + +def run_topic_tests(url: str, port: int): + print() + print("----------------") + l = len(list_topics(url, port)) + print(f"Starting topic tests with ({l}) topics!") + if l > 0: + print("Deleting leftover topics ...") + delete_all_topics(url, port) + + print(f"Creating {len(test_topic_entries)} topic entries for the test ...") + create_topic_entries(url, port) + create_topic_result = False if not len(test_topic_entries) == len(list_topics(url, port)) else True + print(f"Found {len(list_topics(url, port))} topic entries!") + + print("Updating topic entries with new data now ...") + update_topic_entries(url, port) # throws exception if not working + update_topic_result = True + print("Finished updating the topic entries.") + + print(f"Found {len(list_topics(url, port))} topic entries!") + print(f"Deleting all topic entries ...") + delete_topic_entries(url, port) + delete_topic_result = False if not len(list_topics(url, port)) == 0 else True + print(f"Found {len(list_topics(url, port))} topic entries!") + print("Finished topic tests!") + + return create_topic_result, update_topic_result, delete_topic_result + + +if __name__ == "__main__": + url, port = "127.0.0.1", 8000 + run_topic_tests(url, port) diff --git a/tests/run_tests.py b/tests/run_tests.py index 859b373..3a60541 100644 --- a/tests/run_tests.py +++ b/tests/run_tests.py @@ -2,7 +2,8 @@ from tests.createAPI.create_delete_art import run_art_test, list_art, delete_art from tests.createAPI.create_delete_presence import run_presence_test, list_presences from tests.createAPI.create_delete_artist import delete_artist_entries, list_artists, run_artist_test from tests.createAPI.create_delete_tag_category import delete_category_entries, list_tag_categories, run_tag_category_test -from tests.createAPI.create_delete_tag import create_tag_entries, list_tags, run_tag_test, delete_tag_entries +from tests.createAPI.create_delete_tag import list_tags, run_tag_test, delete_tag_entries +from tests.createAPI.create_delete_topic import list_topics, run_topic_tests, delete_all_topics from tests.relationAPI.relate_art import run_art_presence_relation_test @@ -33,6 +34,7 @@ def run_tests(url: str, port: int): create_art2presence_result, delete_art2presence_result = run_art_presence_relation_test(url, port) create_category_result, update_category_result, delete_category_result = run_tag_category_test(url, port) create_tag_result, update_tag_result, delete_tag_result = run_tag_test(url, port) + create_topic_result, update_topic_result, delete_topic_result = run_topic_tests(url, port) print() print("-------- Test Results ---------") @@ -47,6 +49,9 @@ def run_tests(url: str, port: int): print(f"Tag Categories: {len(r)}, {r}") r = list_tags(url, port) print(f"Tags: {len(r)}, {r}") + r = list_topics(url, port) + print(f"Topics: {len(r)}, {r}") + print() print("Functions:") print(f"Artists: \t\t\t\tCreate: {create_artist_result} \tUpdate: {'N/A'} " @@ -61,7 +66,8 @@ def run_tests(url: str, port: int): f"\tDelete: {delete_category_result} \t(Direct)") print(f"Tag: \t\t\t\t\tCreate: {create_tag_result} \tUpdate: {update_tag_result} " f"\tDelete: {delete_tag_result} \t(Direct)") - print(f"(Artist) Topic: \t\tN/A") + print(f"(Artist) Topic: \t\tCreate: {create_topic_result} \tUpdate: {update_topic_result} " + f"\tDelete: {delete_topic_result} \t(Direct)") print(f"Art Collection: \t\tN/A") print(f"Art2Art Collection: \tN/A") print(f"Artist2Topic: \t\t\tN/A")