From 633326ac89760fcea7f937921ac82c453cb475c9 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 14 Apr 2021 20:26:06 +0300 Subject: [PATCH 01/16] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=20=D0=B1=D0=B0=D0=B7=D0=BE=D0=B2=D1=8B=D0=B9=20api?= =?UTF-8?q?=20=D0=B4=D0=BB=D1=8F=20frontend?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/api.py | 91 ++++++++++++++++++++++++++++++++++++++++++++ frontend/api_demo.py | 11 ++++++ 2 files changed, 102 insertions(+) create mode 100644 frontend/api.py create mode 100644 frontend/api_demo.py diff --git a/frontend/api.py b/frontend/api.py new file mode 100644 index 0000000..9a0f9f3 --- /dev/null +++ b/frontend/api.py @@ -0,0 +1,91 @@ +import requests +from json import loads, dumps +from types import SimpleNamespace + +URL = "http://127.0.0.1:8000" +API_LISTS_LIST = "api/lists/" +API_LISTS_CREATE = "api/lists/" +API_LISTS_READ = "lists/{0}/" +API_LISTS_UPDATE = "lists/{0}/" +API_LISTS_PARTIAL_UPDATE = "lists/{0}/" +API_LISTS_DELETE = "lists/{0}/" +API_TOKEN = "api/token/" + +CODE_SUCCESS = 200 +JSON_HEADERS = {'content-type': 'application/json', 'accept': 'application/json'} + +get_api = lambda x: URL + "/" + x +dump = lambda x: bytes(dumps(x), encoding="utf-8") + +class User(object): + + def __init__(self): + pass + + def auth(self, user, passwd): + url = get_api(API_TOKEN) + data = dump({"username": f"{user}", "password": f"{passwd}"}) + code, self.token = User.post(url=url, data=data, headers=JSON_HEADERS) + return code + + def list(self): + return User.get( + url=get_api(API_LISTS_LIST), + headers=self._authorised_()) + + def create(self, title="Untitled"): + return User.post( + url=get_api(API_LISTS_CREATE), + data=dump({"title": title}), + headers=self._authorised_()) + + def read(self, id): + return User.post( + url=get_api(API_LISTS_READ).format(id), + headers=self._authorised_()) + + def update(self, id, title="Untitled"): + return User.__request__( + func=requests.put, + data=dump({"title": title}), + url=get_api(API_LISTS_UPDATE).format(id), + headers=self._authorised_()) + + def partial_update(self, id, title="Untitled"): + return User.__request__( + func=requests.patch, + data=dump({"title": title}), + url=get_api(API_LISTS_PARTIAL_UPDATE).format(id), + headers=self._authorised_()) + + def delete(self, id): + return User.__request__( + func=requests.delete, + url=get_api(API_LISTS_DELETE).format(id), + headers=self._authorised_()) + + def _authorised_(self, headers=JSON_HEADERS): + if not hasattr(self, "token"): + raise RuntimeError("Authosization required for requested operation!") + headers = headers.copy() + headers['Authorization'] = f'Bearer {self.token.access}' + return headers + + @staticmethod + def get(**argv): + return User.__request__(requests.get, **argv) + + @staticmethod + def post(**argv): + return User.__request__(requests.post, **argv) + + @staticmethod + def __request__(func, verbose=True, **argv): + with func(**argv) as r: + if r.status_code == CODE_SUCCESS: + data = loads(r.text) + if type(data) is dict: + data = SimpleNamespace(**data) + else: + data = None + return r.status_code, data \ No newline at end of file diff --git a/frontend/api_demo.py b/frontend/api_demo.py new file mode 100644 index 0000000..e77ad51 --- /dev/null +++ b/frontend/api_demo.py @@ -0,0 +1,11 @@ +from api import User, CODE_SUCCESS + +user = User() +print("testing api methods...") +print("auth...", user.auth("root", "root") == CODE_SUCCESS) +print("list...", user.list()[0] == CODE_SUCCESS) +print("create...", user.create()[0] == CODE_SUCCESS) +print("read...", user.read(id=0)[0] == CODE_SUCCESS) +print("update...", user.update(id=0, title="Title")[0] == CODE_SUCCESS) +print("partial_update...", user.partial_update(id=0, title="Title")[0] == CODE_SUCCESS) +print("delete...", user.update(id=0)[0] == CODE_SUCCESS) \ No newline at end of file -- 2.49.1 From 6bf6f9d3f1ca8df8cc299ddf0a1d00612b81f41c Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 15 Apr 2021 20:31:24 +0300 Subject: [PATCH 02/16] =?UTF-8?q?=D0=98=D1=81=D0=BF=D1=80=D0=B0=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=D0=B8=D1=8F=20=D1=81=D1=82=D0=B8=D0=BB=D1=8F?= =?UTF-8?q?,=20=D0=BF=D0=BE=D0=B4=D0=B4=D0=B5=D1=80=D0=B6=D0=BA=D0=B0=20?= =?UTF-8?q?=D0=B1=D0=B0=D0=B7=D0=BE=D0=B2=D0=BE=D0=B9=20=D0=B4=D0=BE=D0=BA?= =?UTF-8?q?=D1=83=D0=BC=D0=B5=D0=BD=D1=82=D0=B0=D1=86=D0=B8=D0=B8,=20?= =?UTF-8?q?=D1=81=D0=BE=D0=BE=D1=82=D0=B2=D0=B5=D1=82=D1=81=D1=82=D0=B2?= =?UTF-8?q?=D1=83=D1=8E=D1=89=D0=B5=D0=B9=20=D1=82=D0=B5=D0=BA=D1=83=D1=89?= =?UTF-8?q?=D0=B8=D0=BC=20=D0=B7=D0=B0=D0=BF=D1=80=D0=BE=D1=81=D0=B0=D0=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/api.py | 211 +++++++++++++++++++++++++++++-------------- frontend/api_demo.py | 24 +++-- 2 files changed, 158 insertions(+), 77 deletions(-) diff --git a/frontend/api.py b/frontend/api.py index 9a0f9f3..8292761 100644 --- a/frontend/api.py +++ b/frontend/api.py @@ -1,8 +1,7 @@ +import urllib import requests -from json import loads, dumps -from types import SimpleNamespace -URL = "http://127.0.0.1:8000" +DEFAULT_URL = "http://127.0.0.1:8000" API_LISTS_LIST = "api/lists/" API_LISTS_CREATE = "api/lists/" API_LISTS_READ = "lists/{0}/" @@ -11,81 +10,155 @@ API_LISTS_PARTIAL_UPDATE = "lists/{0}/" API_LISTS_DELETE = "lists/{0}/" API_TOKEN = "api/token/" -CODE_SUCCESS = 200 -JSON_HEADERS = {'content-type': 'application/json', 'accept': 'application/json'} - -get_api = lambda x: URL + "/" + x -dump = lambda x: bytes(dumps(x), encoding="utf-8") class User(object): + def __init__(self, url=DEFAULT_URL, token=None): + """ + Constructor - def __init__(self): - pass + Parameters + ---------- + url : str, optional + Server url. The default is DEFAULT_URL. + token : dict, optional + Existing user tokens to bypass authorization. + The default is None. + Returns + ------- + None. + + """ + self.token = token + self.get_api = lambda x: urllib.parse.urljoin(url, x) + + # ToDo - store tokens in config def auth(self, user, passwd): - url = get_api(API_TOKEN) - data = dump({"username": f"{user}", "password": f"{passwd}"}) - code, self.token = User.post(url=url, data=data, headers=JSON_HEADERS) - return code - + """ + Authosization + + Parameters + ---------- + user : str + Login. + passwd : str + Password. + + Returns + ------- + dict + Generated auth token. + + """ + url = self.get_api(API_TOKEN) + data = {"username": user, "password": passwd} + response = requests.post(url=url, json=data) + response.raise_for_status() + self.token = response.json() + return self.token + def list(self): - return User.get( - url=get_api(API_LISTS_LIST), - headers=self._authorised_()) - + """ + List all the exsiting to-do lists. + Auth required + + Returns + ------- + list + to-do lists. + + """ + response = requests.get(url=self.get_api(API_LISTS_LIST), headers=self._access_token_()) + response.raise_for_status() + return response.json() + def create(self, title="Untitled"): - return User.post( - url=get_api(API_LISTS_CREATE), - data=dump({"title": title}), - headers=self._authorised_()) - + """ + Create a new to-do list + + Parameters + ---------- + title : str, optional + New list name. The default is "Untitled". + + """ + response = requests.post( + url=self.get_api(API_LISTS_CREATE), json={"title": title}, headers=self._access_token_() + ) + response.raise_for_status() + return response.json() + def read(self, id): - return User.post( - url=get_api(API_LISTS_READ).format(id), - headers=self._authorised_()) - + """ + Read a to-do list contents + + Parameters + ---------- + id : int + List id. + + Returns + ------- + list + Requested contents + + """ + response = requests.post( + url=self.get_api(API_LISTS_READ).format(id), headers=self._access_token_() + ) + response.raise_for_status() + return response.json() + def update(self, id, title="Untitled"): - return User.__request__( - func=requests.put, - data=dump({"title": title}), - url=get_api(API_LISTS_UPDATE).format(id), - headers=self._authorised_()) - + """ + Add a to-do item to the list + + Parameters + ---------- + id : int + List id. + title : str, optional + To-do item title. The default is "Untitled". + + """ + response = requests.put( + json={"title": title}, + url=self.get_api(API_LISTS_UPDATE).format(id), + headers=self._access_token_(), + ) + response.raise_for_status() + return response.json() + def partial_update(self, id, title="Untitled"): - return User.__request__( - func=requests.patch, - data=dump({"title": title}), - url=get_api(API_LISTS_PARTIAL_UPDATE).format(id), - headers=self._authorised_()) - + """ + Update list item - untrusted + + """ + response = requests.patch( + json={"title": title}, + url=self.get_api(API_LISTS_PARTIAL_UPDATE).format(id), + headers=self._access_token_(), + ) + response.raise_for_status() + return response.json() + def delete(self, id): - return User.__request__( - func=requests.delete, - url=get_api(API_LISTS_DELETE).format(id), - headers=self._authorised_()) - - def _authorised_(self, headers=JSON_HEADERS): - if not hasattr(self, "token"): + """ + Delete list + + Parameters + ---------- + id : int + List id to delete. + + """ + response = requests.delete( + url=self.get_api(API_LISTS_DELETE).format(id), headers=self._access_token_() + ) + response.raise_for_status() + return response.json() + + def _access_token_(self): + if self.token is None: raise RuntimeError("Authosization required for requested operation!") - headers = headers.copy() - headers['Authorization'] = f'Bearer {self.token.access}' - return headers - - @staticmethod - def get(**argv): - return User.__request__(requests.get, **argv) - - @staticmethod - def post(**argv): - return User.__request__(requests.post, **argv) - - @staticmethod - def __request__(func, verbose=True, **argv): - with func(**argv) as r: - if r.status_code == CODE_SUCCESS: - data = loads(r.text) - if type(data) is dict: - data = SimpleNamespace(**data) - else: - data = None - return r.status_code, data \ No newline at end of file + return {"Authorization": f'Bearer {self.token["access"]}'} diff --git a/frontend/api_demo.py b/frontend/api_demo.py index e77ad51..92ad4c1 100644 --- a/frontend/api_demo.py +++ b/frontend/api_demo.py @@ -1,11 +1,19 @@ -from api import User, CODE_SUCCESS +from api import User + + +def ignore_exceptions(*args, **argv): + try: + args[0](*(args[1:]), **argv) + except Exception as e: + print(e) + user = User() print("testing api methods...") -print("auth...", user.auth("root", "root") == CODE_SUCCESS) -print("list...", user.list()[0] == CODE_SUCCESS) -print("create...", user.create()[0] == CODE_SUCCESS) -print("read...", user.read(id=0)[0] == CODE_SUCCESS) -print("update...", user.update(id=0, title="Title")[0] == CODE_SUCCESS) -print("partial_update...", user.partial_update(id=0, title="Title")[0] == CODE_SUCCESS) -print("delete...", user.update(id=0)[0] == CODE_SUCCESS) \ No newline at end of file +print("auth..."), ignore_exceptions(user.auth, "root", "root") +print("list..."), ignore_exceptions(user.list) +print("create..."), ignore_exceptions(user.create) +print("read..."), ignore_exceptions(user.read, id=0) +print("update..."), ignore_exceptions(user.update, id=0, title="Title") +print("partial_update..."), ignore_exceptions(user.partial_update, id=0, title="Title") +print("delete..."), ignore_exceptions(user.update, id=0) -- 2.49.1 From 0d1e321b7e331ff52ae9e6cb33c0e88e0321dd7f Mon Sep 17 00:00:00 2001 From: Ivan Date: Sun, 18 Apr 2021 16:07:59 +0300 Subject: [PATCH 03/16] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D0=BE=20api=20=D0=B4=D0=BB=D1=8F=20=D1=80=D0=B0?= =?UTF-8?q?=D0=B1=D0=BE=D1=82=D1=8B=20=D1=81=D0=BE=20=D1=81=D0=BF=D0=B8?= =?UTF-8?q?=D1=81=D0=BA=D0=B0=D0=BC=D0=B8=20=D0=B8=20=D0=B7=D0=B0=D0=B4?= =?UTF-8?q?=D0=B0=D1=87=D0=B0=D0=BC=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/api.py | 192 +++++++++++++++++++++++++++---------------- frontend/api_demo.py | 59 +++++++++---- frontend/user.py | 95 +++++++++++++++++++++ 3 files changed, 262 insertions(+), 84 deletions(-) create mode 100644 frontend/user.py diff --git a/frontend/api.py b/frontend/api.py index 8292761..e180c72 100644 --- a/frontend/api.py +++ b/frontend/api.py @@ -1,17 +1,27 @@ +from types import SimpleNamespace import urllib import requests DEFAULT_URL = "http://127.0.0.1:8000" + +API_TODO_ITEMS_LIST = "api/todo_items/" +API_TODO_ITEMS_CREATE = "api/todo_items/" +API_TODO_ITEMS_READ = "api/todo_items/{0}/" +API_TODO_ITEMS_UPDATE = "api/todo_items/{0}/" +API_TODO_ITEMS_PARTIAL_UPDATE = "api/todo_items/{0}/" +API_TODO_ITEMS_DELETE = "api/todo_items/{0}/" + API_LISTS_LIST = "api/lists/" API_LISTS_CREATE = "api/lists/" API_LISTS_READ = "lists/{0}/" API_LISTS_UPDATE = "lists/{0}/" API_LISTS_PARTIAL_UPDATE = "lists/{0}/" API_LISTS_DELETE = "lists/{0}/" + API_TOKEN = "api/token/" -class User(object): +class UserApi(object): def __init__(self, url=DEFAULT_URL, token=None): """ Constructor @@ -50,14 +60,13 @@ class User(object): Generated auth token. """ - url = self.get_api(API_TOKEN) - data = {"username": user, "password": passwd} - response = requests.post(url=url, json=data) - response.raise_for_status() - self.token = response.json() + token = UserApi._raise_or_return_( + requests.post(url=self.get_api(API_TOKEN), json={"username": user, "password": passwd}) + ) + self.token = SimpleNamespace(**token) return self.token - def list(self): + def lists_list(self, **argv): """ List all the exsiting to-do lists. Auth required @@ -68,13 +77,16 @@ class User(object): to-do lists. """ - response = requests.get(url=self.get_api(API_LISTS_LIST), headers=self._access_token_()) - response.raise_for_status() - return response.json() + return UserApi._raise_or_return_( + requests.get( + url=self.get_api(API_LISTS_LIST), headers=self._access_token_(), params=argv + ) + ) - def create(self, title="Untitled"): + def lists_create(self, title="Untitled"): """ Create a new to-do list + Auth required Parameters ---------- @@ -82,83 +94,125 @@ class User(object): New list name. The default is "Untitled". """ - response = requests.post( - url=self.get_api(API_LISTS_CREATE), json={"title": title}, headers=self._access_token_() + return UserApi._raise_or_return_( + requests.post( + url=self.get_api(API_LISTS_CREATE), + json={"title": title}, + headers=self._access_token_(), + ) ) - response.raise_for_status() - return response.json() - def read(self, id): + def todo_items_list(self, **argv): """ - Read a to-do list contents - - Parameters - ---------- - id : int - List id. + List all the exsiting to-do items. + Auth required Returns ------- list - Requested contents + to-do items. """ - response = requests.post( - url=self.get_api(API_LISTS_READ).format(id), headers=self._access_token_() + return UserApi._raise_or_return_( + requests.get( + url=self.get_api(API_TODO_ITEMS_LIST), headers=self._access_token_(), params=argv + ) ) - response.raise_for_status() - return response.json() - def update(self, id, title="Untitled"): - """ - Add a to-do item to the list + # def create(self, title="Untitled"): + # """ + # Create a new to-do list + # Auth required - Parameters - ---------- - id : int - List id. - title : str, optional - To-do item title. The default is "Untitled". + # Parameters + # ---------- + # title : str, optional + # New list name. The default is "Untitled". - """ - response = requests.put( - json={"title": title}, - url=self.get_api(API_LISTS_UPDATE).format(id), - headers=self._access_token_(), - ) - response.raise_for_status() - return response.json() + # """ + # response = requests.post( + # url=self.get_api(API_LISTS_CREATE), json={"title": title}, headers=self._access_token_() + # url=self.get_api(API_TODO_ITEMS_CREATE), json={"title": title}, headers=self._access_token_() + # ) + # response.raise_for_status() + # return response.json() - def partial_update(self, id, title="Untitled"): - """ - Update list item - untrusted + # def read(self, id): + # """ + # Read a to-do list contents - """ - response = requests.patch( - json={"title": title}, - url=self.get_api(API_LISTS_PARTIAL_UPDATE).format(id), - headers=self._access_token_(), - ) - response.raise_for_status() - return response.json() + # Parameters + # ---------- + # id : int + # List id. - def delete(self, id): - """ - Delete list + # Returns + # ------- + # list + # Requested contents - Parameters - ---------- - id : int - List id to delete. + # """ + # response = requests.post( + # url=self.get_api(API_LISTS_READ).format(id), headers=self._access_token_() + # ) + # response.raise_for_status() + # return response.json() - """ - response = requests.delete( - url=self.get_api(API_LISTS_DELETE).format(id), headers=self._access_token_() - ) - response.raise_for_status() - return response.json() + # def update(self, id, title="Untitled"): + # """ + # Add a to-do item to the list + + # Parameters + # ---------- + # id : int + # List id. + # title : str, optional + # To-do item title. The default is "Untitled". + + # """ + # response = requests.put( + # json={"title": title}, + # url=self.get_api(API_LISTS_UPDATE).format(id), + # headers=self._access_token_(), + # ) + # response.raise_for_status() + # return response.json() + + # def partial_update(self, id, title="Untitled"): + # """ + # Update list item - untrusted + + # """ + # response = requests.patch( + # json={"title": title}, + # url=self.get_api(API_LISTS_PARTIAL_UPDATE).format(id), + # headers=self._access_token_(), + # ) + # response.raise_for_status() + # return response.json() + + # def delete(self, id): + # """ + # Delete list + + # Parameters + # ---------- + # id : int + # List id to delete. + + # """ + # response = requests.delete( + # url=self.get_api(API_LISTS_DELETE).format(id), headers=self._access_token_() + # ) + # response.raise_for_status() + # return response.json() def _access_token_(self): if self.token is None: raise RuntimeError("Authosization required for requested operation!") - return {"Authorization": f'Bearer {self.token["access"]}'} + return {"Authorization": f"Bearer {self.token.access}"} + + @staticmethod + def _raise_or_return_(response): + response.raise_for_status() + return response.json() diff --git a/frontend/api_demo.py b/frontend/api_demo.py index 92ad4c1..f79bbc3 100644 --- a/frontend/api_demo.py +++ b/frontend/api_demo.py @@ -1,19 +1,48 @@ -from api import User +from user import User -def ignore_exceptions(*args, **argv): - try: - args[0](*(args[1:]), **argv) - except Exception as e: - print(e) +def print_lists(lists): + for item in lists: + print(f"List: '{item}'", f"Id: {item.id}", "|", "|".join([str(x) for x in item.items])) -user = User() -print("testing api methods...") -print("auth..."), ignore_exceptions(user.auth, "root", "root") -print("list..."), ignore_exceptions(user.list) -print("create..."), ignore_exceptions(user.create) -print("read..."), ignore_exceptions(user.read, id=0) -print("update..."), ignore_exceptions(user.update, id=0, title="Title") -print("partial_update..."), ignore_exceptions(user.partial_update, id=0, title="Title") -print("delete..."), ignore_exceptions(user.update, id=0) +DEFAULT_URL = "http://127.0.0.1:8000" + +user = User(url=DEFAULT_URL) +user.auth("root", "root") + +# Fetch existing lists: +lists = user.fetchUserLists() +print("Fecthing...") +print_lists(lists) + +# Remove user list by id: +user.removeUserList(5) +lists = user.fetchUserLists() +print(f"Removing {5}...") +print_lists(lists) + +# Append a new list to user: +print("Appending list...") +scroll = user.appendUserList(title="a new list!") +print_lists(lists) + +# Modify list 0: +print("Modifyng list...") +lists[0].modify(title="A new title") +print_lists(lists) + +# Append item to list: +print("Appending item to last list...") +item = lists[-1].append(text="this is an item") +print_lists(lists) + +# Modifying item +print("Modifyng appended item...") +item.modify(finished=True, text="this is an updated item") +print_lists(lists) + +# Removing item at 0 +print("Removing item 0 from list 0...") +lists[0].remove(0) +print_lists(lists) diff --git a/frontend/user.py b/frontend/user.py new file mode 100644 index 0000000..aa91e63 --- /dev/null +++ b/frontend/user.py @@ -0,0 +1,95 @@ +from datetime import datetime +from api import UserApi + + +class ToDoList(object): + def __init__(self, id, title, created_at=None, items=[], parent=None): + self.id = id + self.title = title + self.items = items + self.created_at = created_at + + def __getitem__(self, index): + return self.items[index] + + def __len__(self): + return len(self.items) + + def __str__(self): + return f"[{self.id}] {self.title}" + + # ToDo + def remove(self, index): + self.items.remove(self.items[0]) + self.sync() + + # ToDo + def append(self, text): + item = ToDoItem(id=None, text=text, created_at=datetime.now()) + self.items.append(item) + item.sync() + self.sync() + return item + + def modify(self, **argv): + for key, value in argv.items(): + setattr(self, key, value) + self.sync() + + # ToDo + def sync(self): + # ToDo send request or store in form + print(f"Item '{self}' is being synchronized...") + + +class ToDoItem(object): + def __init__(self, id, text, finished=False, created_at=None, parent=None): + self.id = id + self.text = text + self.finished = finished + self.created_at = created_at + + def __str__(self): + return f"[{self.id}] {self.text}" + + def modify(self, **argv): + for key, value in argv.items(): + setattr(self, key, value) + self.sync() + + # ToDo + def sync(self): + # ToDo send request or store in form + print(f"Item '{self}' is being synchronized...") + + +class User(UserApi): + + # ToDo + items = [ + ToDoList( + id=i, + title=f"List {i}", + created_at=datetime.now(), + items=[ + ToDoItem(id=i * 10 + j, text=f"Item {i*10+j}", created_at=datetime.now()) + for j in range(10) + ], + ) + for i in range(10) + ] + + # ToDo + def fetchUserLists(self): + return self.items + + # ToDo + def removeUserList(self, id): + self.items = [item for item in self.items if item.id != id] + + # ToDo + def appendUserList(self, title): + item = ToDoList(id=None, title=title, created_at=datetime.now()) + self.items.append(item) + item.sync() + return item -- 2.49.1 From 5836783e699e936c8dfde9b4e97a90f9b60a9618 Mon Sep 17 00:00:00 2001 From: Ivan Date: Sun, 18 Apr 2021 16:16:19 +0300 Subject: [PATCH 04/16] =?UTF-8?q?=D0=9D=D0=B5=D0=B1=D0=BE=D0=BB=D1=8C?= =?UTF-8?q?=D1=88=D0=BE=D0=B5=20=D0=B8=D1=81=D0=BF=D1=80=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D0=B8=D0=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/user.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/user.py b/frontend/user.py index aa91e63..38cda9b 100644 --- a/frontend/user.py +++ b/frontend/user.py @@ -20,7 +20,7 @@ class ToDoList(object): # ToDo def remove(self, index): - self.items.remove(self.items[0]) + self.items.remove(self.items[index]) self.sync() # ToDo -- 2.49.1 From 66beba6b0d1b29cfc137edb40bd1af6fde651924 Mon Sep 17 00:00:00 2001 From: Ivan Date: Thu, 22 Apr 2021 20:35:48 +0300 Subject: [PATCH 05/16] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=B8?= =?UTF-8?q?=D0=BB=20=D0=B1=D0=B0=D0=B7=D0=BE=D0=B2=D1=8B=D0=B9=20=D0=B3?= =?UTF-8?q?=D1=80=D0=B0=D1=84=D0=B8=D1=87=D0=B5=D1=81=D0=BA=D0=B8=D0=B9=20?= =?UTF-8?q?=D0=B8=D0=BD=D1=82=D0=B5=D1=80=D1=84=D0=B5=D0=B9=D1=81=20=D1=81?= =?UTF-8?q?=20=D1=87=D0=B0=D1=81=D1=82=D1=8C=D1=8E=20=D1=84=D1=83=D0=BD?= =?UTF-8?q?=D0=BA=D1=86=D0=B8=D0=B9.=20=D0=94=D0=BB=D1=8F=20=D0=B7=D0=B0?= =?UTF-8?q?=D0=BF=D1=83=D1=81=D0=BA=D0=B0=20=D0=BD=D0=B5=D0=BE=D0=B1=D1=85?= =?UTF-8?q?=D0=BE=D0=B4=D0=B8=D0=BC=D0=B0=20=D0=BF=D0=B5=D1=80=D0=B5=D0=BC?= =?UTF-8?q?=D0=B5=D0=BD=D0=BD=D0=B0=D1=8F=20=D1=81=D1=80=D0=B5=D0=B4=D1=8B?= =?UTF-8?q?=20DEBUG,=20=D0=BF=D0=BE=D0=BA=D0=B0=20=D1=87=D1=82=D0=BE=20?= =?UTF-8?q?=D1=80=D0=B0=D0=B1=D0=BE=D1=82=D0=B0=D0=B5=D0=BC=20=D0=BD=D0=B0?= =?UTF-8?q?=20=D0=B8=D0=B3=D1=80=D0=BE=D0=B2=D1=8B=D1=85=20=D0=B4=D0=B0?= =?UTF-8?q?=D0=BD=D0=BD=D1=8B=D1=85=20-=20=D0=B1=D0=B5=D0=B7=20=D0=B1?= =?UTF-8?q?=D1=8D=D0=BA=D1=8D=D0=BD=D0=B4=D0=B0.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/login.py | 68 +++++++++++++++++++++++ frontend/message.py | 20 +++++++ frontend/todo_tk.py | 51 +++++++++++++++-- frontend/user.py | 13 +++++ frontend/workspace.py | 124 ++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 271 insertions(+), 5 deletions(-) create mode 100644 frontend/login.py create mode 100644 frontend/message.py create mode 100644 frontend/workspace.py diff --git a/frontend/login.py b/frontend/login.py new file mode 100644 index 0000000..64ba0e3 --- /dev/null +++ b/frontend/login.py @@ -0,0 +1,68 @@ +import tkinter as tk +from user import User +import message + + +class LoginFrame(tk.Frame): + + loggedIn = False + + def __init__(self, master=None, url=None) -> None: + """ + Функция инициаизации класса + """ + super().__init__(master) + + # Иницализируем параметры окна + self.master = master + self.pack(fill=tk.BOTH, expand=1) + + # self.grid(row=0, column=0, sticky=tk.N + tk.S + tk.E + tk.W) + # tk.Grid.rowconfigure(master, 0, weight=1) + # tk.Grid.columnconfigure(master, 0, weight=1) + + # Иницализируем параметры пользователя + self.user = User(url=url) + + # Настраиваем размеры и включаем иницализацию + self.initAUTH() + + def login_clicked(self) -> None: + """ + Функция авторизации + """ + try: + self.user.auth(self.login.get(), self.password.get()) + self.loggedIn = True + except Exception as ex: + print(ex) + message.invalid_login() + + def initAUTH(self) -> None: + """ + Создает окно авторизации программы + """ + # Конфигурируем сетку + for rows in range(25): + tk.Grid.rowconfigure(self, rows, weight=1) + + for columns in range(25): + tk.Grid.columnconfigure(self, columns, weight=1) + + # Подпись и поле ввода для логина + login_label = tk.Label(self, text="Введите логин") + login_label.grid(row=9, column=12, columnspan=3, rowspan=1, sticky="nsew") + + self.login = tk.Entry(self) + self.login.grid(row=10, column=12, columnspan=3, rowspan=1, sticky="nsew") + + # Подпись и поле ввода для пароля + password_label = tk.Label(self, text="Введите пароль") + password_label.grid(row=11, column=12, columnspan=3, rowspan=1, sticky="nsew") + + self.password = tk.Entry(self, show="*") + self.password.grid(row=12, column=12, columnspan=3, rowspan=1, sticky="nsew") + + # Кнопка авториазции + btn = tk.Button(self, text="Войти", command=self.login_clicked) + btn.grid(row=14, column=12, columnspan=3, rowspan=1, sticky="nsew") diff --git a/frontend/message.py b/frontend/message.py new file mode 100644 index 0000000..824d07b --- /dev/null +++ b/frontend/message.py @@ -0,0 +1,20 @@ +from tkinter import messagebox as mb + +TITLE_INFO_BOX = "Сообщение!" +MESSAGE_INVALID_LOGIN = "Неправильный логин или пароль" +MESSAGE_EMPTY = "Сдесь могло быть ваше сообщение" + + +def infobox(msg: str = None) -> None: + """ + Показывает передаваемое сообщение в messagebox + + :param msg: передаваемое сообщение + """ + if msg is None: + msg = MESSAGE_EMPTY + mb.showinfo(TITLE_INFO_BOX, msg) + + +def invalid_login(): + infobox(MESSAGE_INVALID_LOGIN) diff --git a/frontend/todo_tk.py b/frontend/todo_tk.py index 41e0e99..831ac6c 100644 --- a/frontend/todo_tk.py +++ b/frontend/todo_tk.py @@ -1,14 +1,55 @@ #!/usr/bin/env python3 +import sys import tkinter as tk +from login import LoginFrame +from workspace import WorkSpaceFrame + +if "win" in sys.platform.lower(): + DEFAULT_URL = "http://localhost:8000" +else: + DEFAULT_URL = "http://0.0.0.0:8000" + +BASE_W = 580 +BASE_H = 400 + +TITLE_APP = "ToDo Application" -class Application(tk.Frame): - def __init__(self, master=None): - super().__init__(master) +class Application(tk.Tk): + def __init__(self): + super().__init__() + self.center_window() + self.title(TITLE_APP) + + def login(self): + """Возвращает пользователя - его можно потом сериализовать""" + self.frame = LoginFrame(master=self, url=DEFAULT_URL) + while not self.frame.loggedIn: + self.update_idletasks() + self.update() + self.frame.destroy() + return self.frame.user + + def main(self, user): + self.frame = WorkSpaceFrame(master=self, user=user) + self.mainloop() + + def center_window(self, width: str = BASE_W, heigh: str = BASE_H) -> None: + """ + Центрирует приложение по центру экрана + + :param width: ширина окна + :param heigh: высота окна + """ + sw = self.winfo_screenwidth() + sh = self.winfo_screenheight() + + x = (sw - width) / 2 + y = (sh - heigh) / 2 + self.geometry("%dx%d+%d+%d" % (width, heigh, x, y)) if __name__ == "__main__": app = Application() - app.master.title("ToDo") - app.mainloop() + app.main(app.login()) diff --git a/frontend/user.py b/frontend/user.py index 38cda9b..5cdff8e 100644 --- a/frontend/user.py +++ b/frontend/user.py @@ -1,3 +1,5 @@ +import os + from datetime import datetime from api import UserApi @@ -9,6 +11,10 @@ class ToDoList(object): self.items = items self.created_at = created_at + def __iter__(self): + for item in self.items: + yield item + def __getitem__(self, index): return self.items[index] @@ -18,6 +24,9 @@ class ToDoList(object): def __str__(self): return f"[{self.id}] {self.title}" + def index(self, value): + return self.items.index(value) + # ToDo def remove(self, index): self.items.remove(self.items[index]) @@ -64,6 +73,10 @@ class ToDoItem(object): class User(UserApi): + def auth(self, user, passwd): + if "DEBUG" in os.environ: + return + UserApi.auth(self, user, passwd) # ToDo items = [ diff --git a/frontend/workspace.py b/frontend/workspace.py new file mode 100644 index 0000000..9a33bd1 --- /dev/null +++ b/frontend/workspace.py @@ -0,0 +1,124 @@ +import tkinter as tk + + +def str_time(time): + return time.strftime("%Y-%m-%d %H:%M:%S") + + +class ToDoItemWidget(tk.Frame): + def __init__(self, *args, item, parent=None, **argv): + super().__init__(*args, **argv) + + self.parent = parent + self.item = item + + self.noteLabel = tk.Label(self, text=item.text, width=15) + self.noteLabel.pack(side="left") + + self.finished = tk.IntVar(value=int(item.finished)) + self.finishedButton = tk.Checkbutton( + self, variable=self.finished, command=self.finishedButton_command + ) + self.finishedButton.pack(side="left") + + self.createdAt = tk.Label(self, text=str_time(item.created_at), width=15) + self.createdAt.pack(side="left") + + self.remove = tk.Button(self, text="Удалить", command=lambda: parent.remove(self.item)) + self.remove.pack(side="left") + + def finishedButton_command(self): + self.item.modify(finished=self.finished.get() > 0) + + +class ToDoListWidget(tk.Frame): + def __init__(self, *args, **argv): + super().__init__(*args, **argv) + + def fill(self, itemList): + + self.header = tk.Label(self, text="Текст | Выполнено | Время создания") + self.header.pack(side="top", fill="y") + + self.itemList = itemList + + for item in itemList: + item = ToDoItemWidget(self, item=item, parent=self) + item.pack(side="top", fill="y") + + self.itemToAdd = tk.Text(self, width=15, height=1) + self.itemToAdd.pack(side="top") + + add = tk.Button(self, text="Добавить", command=self.add_command) + add.pack(side="top") + + def update(self, itemList=None): + self.clear() + if itemList is None: + self.fill(self.itemList) + else: + self.fill(itemList) + + def add_command(self): + self.itemList.append(self.itemToAdd.get(1.0, "end")) + self.update() + + def remove(self, item): + self.itemList.remove(self.itemList.index(item)) + self.update() + + def clear(self): + for widget in self.winfo_children(): + widget.destroy() + + +class WorkSpaceFrame(tk.Frame): + def __init__(self, user, master=None, url=None) -> None: + """ + Функция инициаизации класса + """ + super().__init__(master) + + self.master = master + self.user = user + + self.pack(fill=tk.BOTH, expand=1) + self.initLayout(user) + + def initLayout(self, user): + + # data + self.lists = user.fetchUserLists() + + # select list box + self.listBox = tk.Listbox(self, width=30, selectmode=tk.SINGLE) + self.listBox.pack(side="left", fill="y") + self.listBox.bind("<>", self.listBox_selected) + + # scroll bar + scrollbar = tk.Scrollbar(self, orient="vertical") + scrollbar.config(command=self.listBox.yview) + scrollbar.pack(side="left", fill="y") + + # add scroll bar to list box + self.listBox.config(yscrollcommand=scrollbar.set) + + # fill list box + for item in self.lists: + s = f"{str(item)}: {item.created_at.strftime('%Y-%m-%d %H:%M:%S')}" + self.listBox.insert(tk.END, s) + self.listBox.pack() + len(self.lists) > 0 and self.listBox.selection_set(first=0) + + # todo lists + self.toToList = ToDoListWidget(self) + self.toToList.pack(side="left", fill="both", expand=1) + + def listBox_selected(self, *args): + + self.toToList.clear() + + selection = self.listBox.curselection() + cur = selection[0] + + self.toToList.fill(self.lists[cur]) -- 2.49.1 From b6eb0b6d306e6e55cebac6c04aa30a9021f3f50e Mon Sep 17 00:00:00 2001 From: Ivan Date: Thu, 22 Apr 2021 21:25:40 +0300 Subject: [PATCH 06/16] =?UTF-8?q?=D0=9D=D0=B5=D0=B1=D0=BE=D0=BB=D1=8C?= =?UTF-8?q?=D1=88=D0=B8=D0=B5=20=D0=B8=D1=81=D0=BF=D1=80=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D0=B8=D1=8F=20=D0=B8=20=D0=B4=D0=BE=D0=B1=D0=B0?= =?UTF-8?q?=D0=B2=D0=BB=D0=B5=D0=BD=D0=B8=D0=B5=20=D0=BA=D0=BD=D0=BE=D0=BF?= =?UTF-8?q?=D0=BE=D0=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/workspace.py | 59 +++++++++++++++++++++++++++++++++++-------- 1 file changed, 49 insertions(+), 10 deletions(-) diff --git a/frontend/workspace.py b/frontend/workspace.py index 9a33bd1..34830ec 100644 --- a/frontend/workspace.py +++ b/frontend/workspace.py @@ -5,26 +5,55 @@ def str_time(time): return time.strftime("%Y-%m-%d %H:%M:%S") +TODO_ITEM_TABLE_TEXT_WIDTH = 15 +TODO_ITEM_TABLE_FINISHED_WIDTH = 8 +TODO_ITEM_TABLE_CREATED_AT_WIDTH = 15 + + +def placeholder(): + print("Не реализовано") + + class ToDoItemWidget(tk.Frame): - def __init__(self, *args, item, parent=None, **argv): + @staticmethod + def header(parent): + body = tk.Frame(parent) + + text = tk.Label(body, text="Текст", width=TODO_ITEM_TABLE_TEXT_WIDTH) + text.pack(side="left") + + text = tk.Label(body, text="Выполнено", width=TODO_ITEM_TABLE_FINISHED_WIDTH) + text.pack(side="left") + + text = tk.Label(body, text="Создано", width=TODO_ITEM_TABLE_CREATED_AT_WIDTH) + text.pack(side="left") + + return body + + def __init__(self, *args, item, **argv): super().__init__(*args, **argv) - self.parent = parent + self.parent = self.master self.item = item - self.noteLabel = tk.Label(self, text=item.text, width=15) + self.noteLabel = tk.Label(self, text=item.text, width=TODO_ITEM_TABLE_TEXT_WIDTH) self.noteLabel.pack(side="left") self.finished = tk.IntVar(value=int(item.finished)) self.finishedButton = tk.Checkbutton( - self, variable=self.finished, command=self.finishedButton_command + self, + variable=self.finished, + command=self.finishedButton_command, + width=TODO_ITEM_TABLE_FINISHED_WIDTH, ) self.finishedButton.pack(side="left") - self.createdAt = tk.Label(self, text=str_time(item.created_at), width=15) + self.createdAt = tk.Label( + self, text=str_time(item.created_at), width=TODO_ITEM_TABLE_CREATED_AT_WIDTH + ) self.createdAt.pack(side="left") - self.remove = tk.Button(self, text="Удалить", command=lambda: parent.remove(self.item)) + self.remove = tk.Button(self, text="Удалить", command=lambda: self.parent.remove(self.item)) self.remove.pack(side="left") def finishedButton_command(self): @@ -37,21 +66,25 @@ class ToDoListWidget(tk.Frame): def fill(self, itemList): - self.header = tk.Label(self, text="Текст | Выполнено | Время создания") - self.header.pack(side="top", fill="y") + header = ToDoItemWidget.header(self) + header.pack(side="left") + header.pack(side="top", fill="y") self.itemList = itemList for item in itemList: - item = ToDoItemWidget(self, item=item, parent=self) + item = ToDoItemWidget(self, item=item) item.pack(side="top", fill="y") self.itemToAdd = tk.Text(self, width=15, height=1) self.itemToAdd.pack(side="top") - add = tk.Button(self, text="Добавить", command=self.add_command) + add = tk.Button(self, text="Добавить заметку", command=self.add_command) add.pack(side="top") + delete = tk.Button(self, text="Удалить лист", command=placeholder) + delete.pack(side="top") + def update(self, itemList=None): self.clear() if itemList is None: @@ -90,6 +123,12 @@ class WorkSpaceFrame(tk.Frame): # data self.lists = user.fetchUserLists() + text = tk.Text(self, width=15, height=1) + text.pack(anchor="sw") + + add = tk.Button(self, text="Добавить лист", command=placeholder) + add.pack(anchor="sw") + # select list box self.listBox = tk.Listbox(self, width=30, selectmode=tk.SINGLE) self.listBox.pack(side="left", fill="y") -- 2.49.1 From 61bb90540ee5fe7ceaa0897a4fd457b97766e392 Mon Sep 17 00:00:00 2001 From: Derinhelm Date: Sun, 18 Apr 2021 20:46:32 +0300 Subject: [PATCH 07/16] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D1=8B=20=D1=82=D0=B5=D1=81=D1=82=D1=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 1 - backend/tests/__init__.py | 0 backend/tests/test_1.py | 27 +++++++++++++++++++++++++++ 3 files changed, 27 insertions(+), 1 deletion(-) create mode 100644 backend/tests/__init__.py create mode 100644 backend/tests/test_1.py diff --git a/.gitignore b/.gitignore index 247236f..27cc7f2 100644 --- a/.gitignore +++ b/.gitignore @@ -65,6 +65,5 @@ target/ *.jsl *.db tmp* -test_* .env* venv* diff --git a/backend/tests/__init__.py b/backend/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/backend/tests/test_1.py b/backend/tests/test_1.py new file mode 100644 index 0000000..0da58e0 --- /dev/null +++ b/backend/tests/test_1.py @@ -0,0 +1,27 @@ +import os + +os.environ.setdefault("DJANGO_SETTINGS_MODULE", "backend.settings") + +from django.core.wsgi import get_wsgi_application +application = get_wsgi_application() + +from django.urls import reverse +from rest_framework import status +from rest_framework.test import APITestCase +from rest_framework.test import APIClient +from django.contrib.auth.models import User +from backend.api import router + +class EasyTest(APITestCase): + def test_list(self): + """ + The first test. + """ + user = User.objects.create_user('test_user', 'test@test.com', 'test_password') + + url = reverse('ToDoLists-list')#reverse('ToDoLists-list') + data = {} + self.client.force_authenticate(user=user) + response = self.client.get(url, data, format='json') + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(response.data, []) \ No newline at end of file -- 2.49.1 From 7e02dff18434603868a4b9099040ee327e9fa4c2 Mon Sep 17 00:00:00 2001 From: Derinhelm Date: Fri, 23 Apr 2021 02:01:32 +0300 Subject: [PATCH 08/16] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=20=D1=82=D0=B5=D1=81=D1=82=20=D0=BD=D0=B0=20=D0=BF?= =?UTF-8?q?=D1=80=D0=BE=D1=81=D1=82=D0=B5=D0=B9=D1=88=D0=B5=D0=B5=20=D1=81?= =?UTF-8?q?=D0=BE=D0=B7=D0=B4=D0=B0=D0=BD=D0=B8=D0=B5=20ToDoList?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Для запуска docker-compose exec web python manage.py test --- backend/tests/test_1.py | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/backend/tests/test_1.py b/backend/tests/test_1.py index 0da58e0..bb529dd 100644 --- a/backend/tests/test_1.py +++ b/backend/tests/test_1.py @@ -11,17 +11,28 @@ from rest_framework.test import APITestCase from rest_framework.test import APIClient from django.contrib.auth.models import User from backend.api import router +from collections import OrderedDict class EasyTest(APITestCase): + def test_list(self): """ The first test. """ user = User.objects.create_user('test_user', 'test@test.com', 'test_password') - - url = reverse('ToDoLists-list')#reverse('ToDoLists-list') - data = {} + url = reverse('ToDoLists-list') self.client.force_authenticate(user=user) - response = self.client.get(url, data, format='json') + + response = self.client.get(url, {}, format='json') self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertEqual(response.data, []) \ No newline at end of file + self.assertEqual(response.data, OrderedDict([('count', 0), ('next', None), ('previous', None), ('results', [])])) + + response = self.client.post(url, {"title": "ToDoList1"}, format='json') + self.assertEqual(response.status_code, status.HTTP_201_CREATED) + self.assertEqual(response.data['title'], "ToDoList1") + + response = self.client.get(url, {}, format='json') + self.assertEqual(response.status_code, status.HTTP_200_OK) + + self.assertEqual((response.data['count'], response.data['next'], response.data['previous'], \ + response.data['results'][0]['title']), (1, None, None, "ToDoList1")) \ No newline at end of file -- 2.49.1 From 889acf6c4598bd7dff2966fbde45f38788c7f095 Mon Sep 17 00:00:00 2001 From: Derinhelm Date: Mon, 26 Apr 2021 11:46:01 +0300 Subject: [PATCH 09/16] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D1=8B=20=D1=82=D0=B5=D1=81=D1=82=D1=8B=20=D0=B4?= =?UTF-8?q?=D0=BB=D1=8F=20ToDoList?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/tests/test_1.py | 79 +++++++++++++++++++++++++++++++---------- 1 file changed, 61 insertions(+), 18 deletions(-) diff --git a/backend/tests/test_1.py b/backend/tests/test_1.py index bb529dd..a5ba38e 100644 --- a/backend/tests/test_1.py +++ b/backend/tests/test_1.py @@ -13,26 +13,69 @@ from django.contrib.auth.models import User from backend.api import router from collections import OrderedDict -class EasyTest(APITestCase): - - def test_list(self): - """ - The first test. - """ - user = User.objects.create_user('test_user', 'test@test.com', 'test_password') - url = reverse('ToDoLists-list') - self.client.force_authenticate(user=user) - - response = self.client.get(url, {}, format='json') +class EasyTest1(APITestCase): + def get_todo(self, expected_titles): + response = self.client.get(self.url, {}, format='json') self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertEqual(response.data, OrderedDict([('count', 0), ('next', None), ('previous', None), ('results', [])])) - - response = self.client.post(url, {"title": "ToDoList1"}, format='json') + real_titles = [data['title'] for data in response.data['results']] + self.assertEqual((response.data['count'], real_titles), \ + (len(expected_titles), expected_titles)) + + def post_todo(self, to_do_title): + response = self.client.post(self.url, {"title": to_do_title}, format='json') self.assertEqual(response.status_code, status.HTTP_201_CREATED) - self.assertEqual(response.data['title'], "ToDoList1") + self.assertEqual(response.data['title'], to_do_title) + return response.data['id'] - response = self.client.get(url, {}, format='json') + def get_todo_by_id(self, id, expected_title): + url_with_id = reverse('ToDoLists-detail', args=(id,)) + response = self.client.get(url_with_id, {id: id}, format='json') self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(response.data['title'], expected_title) - self.assertEqual((response.data['count'], response.data['next'], response.data['previous'], \ - response.data['results'][0]['title']), (1, None, None, "ToDoList1")) \ No newline at end of file + def put_todo(self, id, new_title): + url_with_id = reverse('ToDoLists-detail', args=(id,)) + response = self.client.put(url_with_id, {"title": new_title}, format='json') + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(response.data['title'], new_title) + + def patch_todo(self, id, new_title): + url_with_id = reverse('ToDoLists-detail', args=(id,)) + response = self.client.patch(url_with_id, {"title": new_title}, format='json') + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(response.data['title'], new_title) + + def delete_todo(self, id): + url_with_id = reverse('ToDoLists-detail', args=(id,)) + response = self.client.delete(url_with_id, {}, format='json') + self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT) + + def test_todo(self): + """ + Tests API for todo. + + lists/: get, post + lists/{id}/: get, put (update), patch (partial_update), delete + """ + + user = User.objects.create_user('test_user', 'test@test.com', 'test_password') + self.client.force_authenticate(user=user) + self.url = reverse('ToDoLists-list') + + self.get_todo([]) + to_do_title_1, to_do_title_2 = "ToDoList1", "ToDoList2" + to_do_id1 = self.post_todo(to_do_title_1) + self.get_todo([to_do_title_1]) + to_do_id2 = self.post_todo(to_do_title_2) + self.get_todo([to_do_title_1, to_do_title_2]) + + self.get_todo_by_id(to_do_id1, to_do_title_1) + self.get_todo_by_id(to_do_id2, to_do_title_2) + + self.delete_todo(to_do_id2) + self.get_todo([to_do_title_1]) + + self.put_todo(to_do_id1, "ToDoList11") + + self.patch_todo(to_do_id1, "ToDoList12") + \ No newline at end of file -- 2.49.1 From a6a31c8c1010265e0d0868f51cfa06eebe89aaea Mon Sep 17 00:00:00 2001 From: Derinhelm Date: Mon, 26 Apr 2021 12:48:51 +0300 Subject: [PATCH 10/16] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D1=8B=20=D0=BF=D1=80=D0=BE=D1=81=D1=82=D0=B5=D0=B9?= =?UTF-8?q?=D1=88=D0=B8=D0=B5=20=D1=82=D0=B5=D1=81=D1=82=D1=8B=20=D0=BD?= =?UTF-8?q?=D0=B0=20=D1=81=D0=BE=D0=B7=D0=B4=D0=B0=D0=BD=D0=B8=D0=B5=20Ite?= =?UTF-8?q?m?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/tests/test_1.py | 111 +++++++++++++++++++++++++++++++--------- 1 file changed, 87 insertions(+), 24 deletions(-) diff --git a/backend/tests/test_1.py b/backend/tests/test_1.py index a5ba38e..7d18f6e 100644 --- a/backend/tests/test_1.py +++ b/backend/tests/test_1.py @@ -13,69 +13,132 @@ from django.contrib.auth.models import User from backend.api import router from collections import OrderedDict -class EasyTest1(APITestCase): - def get_todo(self, expected_titles): - response = self.client.get(self.url, {}, format='json') +def create_todo(client, title): + url = reverse('ToDoLists-list') + response = client.post(url, {"title": title}, format='json') + return response + +class ToDoTest(APITestCase): + '''Tests API for todo.''' + def get(self, expected_titles): + url = reverse('ToDoLists-list') + response = self.client.get(url, {}, format='json') self.assertEqual(response.status_code, status.HTTP_200_OK) real_titles = [data['title'] for data in response.data['results']] self.assertEqual((response.data['count'], real_titles), \ (len(expected_titles), expected_titles)) - def post_todo(self, to_do_title): - response = self.client.post(self.url, {"title": to_do_title}, format='json') + def post(self, to_do_title): + response = create_todo(self.client, to_do_title) self.assertEqual(response.status_code, status.HTTP_201_CREATED) self.assertEqual(response.data['title'], to_do_title) return response.data['id'] - def get_todo_by_id(self, id, expected_title): + def get_by_id(self, id, expected_title): url_with_id = reverse('ToDoLists-detail', args=(id,)) response = self.client.get(url_with_id, {id: id}, format='json') self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(response.data['title'], expected_title) - def put_todo(self, id, new_title): + def put(self, id, new_title): url_with_id = reverse('ToDoLists-detail', args=(id,)) response = self.client.put(url_with_id, {"title": new_title}, format='json') self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(response.data['title'], new_title) - def patch_todo(self, id, new_title): + def patch(self, id, new_title): url_with_id = reverse('ToDoLists-detail', args=(id,)) response = self.client.patch(url_with_id, {"title": new_title}, format='json') self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(response.data['title'], new_title) - def delete_todo(self, id): + def delete(self, id): url_with_id = reverse('ToDoLists-detail', args=(id,)) response = self.client.delete(url_with_id, {}, format='json') self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT) def test_todo(self): """ - Tests API for todo. - lists/: get, post lists/{id}/: get, put (update), patch (partial_update), delete """ user = User.objects.create_user('test_user', 'test@test.com', 'test_password') self.client.force_authenticate(user=user) - self.url = reverse('ToDoLists-list') - self.get_todo([]) + self.get([]) to_do_title_1, to_do_title_2 = "ToDoList1", "ToDoList2" - to_do_id1 = self.post_todo(to_do_title_1) - self.get_todo([to_do_title_1]) - to_do_id2 = self.post_todo(to_do_title_2) - self.get_todo([to_do_title_1, to_do_title_2]) + to_do_id1 = self.post(to_do_title_1) + self.get([to_do_title_1]) + to_do_id2 = self.post(to_do_title_2) + self.get([to_do_title_1, to_do_title_2]) - self.get_todo_by_id(to_do_id1, to_do_title_1) - self.get_todo_by_id(to_do_id2, to_do_title_2) + self.get_by_id(to_do_id1, to_do_title_1) + self.get_by_id(to_do_id2, to_do_title_2) - self.delete_todo(to_do_id2) - self.get_todo([to_do_title_1]) + self.delete(to_do_id2) + self.get([to_do_title_1]) + + self.put(to_do_id1, "ToDoList11") + + self.patch(to_do_id1, "ToDoList12") + +class ItemTest(APITestCase): + '''Tests API for items.''' + def get(self, todo_id, finished, expected_titles): + url = reverse('ToDoItems-list') + if finished is not None: + data = {"parent": todo_id, "finished": finished} + else: + data = {"parent": todo_id} + response = self.client.get(url, data, format='json') + self.assertEqual(response.status_code, status.HTTP_200_OK) + real_titles = [d['text'] for d in response.data['results']] + self.assertEqual((response.data['count'], real_titles), \ + (len(expected_titles), expected_titles)) + + if finished is not None: + item_status = [data['finished'] for data in response.data['results']] + self.assertEqual(finished, all(item_status)) + + def post(self, item_text, todo_id, finished): + url = reverse('ToDoItems-list') + if finished is not None: + data = {"text": item_text, "parent": todo_id, "finished": finished} + else: + data = {"text": item_text, "parent": todo_id} + response = self.client.post(url, data, format='json') + + self.assertEqual(response.status_code, status.HTTP_201_CREATED) + check_finished = False if (finished is None) else finished + self.assertEqual((response.data['text'], response.data['parent'], response.data['finished']), \ + (item_text, todo_id, check_finished)) + return response.data['id'] + + def delete(self, id): + url_with_id = reverse('ToDoItems-detail', args=(id,)) + response = self.client.delete(url_with_id, {}, format='json') + self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT) + + def test_items(self): + """ + /todo_items/: get+, post+ (create) + /todo_items/{id}/: get (read), put (update), patch (partial_update), delete+ + """ + user = User.objects.create_user('test_user', 'test@test.com', 'test_password') + self.client.force_authenticate(user=user) + to_do_id = create_todo(self.client, "ToDoList").data['id'] + self.get(to_do_id, None, []) + item_id_1 = self.post("Item1", to_do_id, None) + self.get(to_do_id, None, ["Item1"]) + item_id_2 = self.post("Item2", to_do_id, False) + self.get(to_do_id, None, ["Item1", "Item2"]) + item_id_3 = self.post("Item3", to_do_id, True) + self.get(to_do_id, None, ["Item1", "Item2", "Item3"]) + self.get(to_do_id, False, ["Item1", "Item2"]) + self.get(to_do_id, True, ["Item3"]) + + self.delete(item_id_3) + self.get(to_do_id, None, ["Item1", "Item2"]) - self.put_todo(to_do_id1, "ToDoList11") - self.patch_todo(to_do_id1, "ToDoList12") - \ No newline at end of file -- 2.49.1 From e3b93f3524f8bc844c18a8f6d1866914824b40ad Mon Sep 17 00:00:00 2001 From: Derinhelm Date: Mon, 26 Apr 2021 23:35:45 +0300 Subject: [PATCH 11/16] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D1=8B=20=D1=82=D0=B5=D1=81=D1=82=D1=8B=20=D0=B4?= =?UTF-8?q?=D0=BB=D1=8F=20Item?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/tests/test_1.py | 102 ++++++++++++++++++++++++++++++++-------- 1 file changed, 82 insertions(+), 20 deletions(-) diff --git a/backend/tests/test_1.py b/backend/tests/test_1.py index 7d18f6e..702a533 100644 --- a/backend/tests/test_1.py +++ b/backend/tests/test_1.py @@ -82,20 +82,22 @@ class ToDoTest(APITestCase): self.put(to_do_id1, "ToDoList11") self.patch(to_do_id1, "ToDoList12") - + + class ItemTest(APITestCase): '''Tests API for items.''' def get(self, todo_id, finished, expected_titles): url = reverse('ToDoItems-list') + data = {} if finished is not None: - data = {"parent": todo_id, "finished": finished} - else: - data = {"parent": todo_id} + data["finished"] = finished + if todo_id is not None: + data["parent"] = todo_id + response = self.client.get(url, data, format='json') self.assertEqual(response.status_code, status.HTTP_200_OK) - real_titles = [d['text'] for d in response.data['results']] - self.assertEqual((response.data['count'], real_titles), \ - (len(expected_titles), expected_titles)) + real_titles = [(d['text'], d['parent']) for d in response.data['results']] + self.assertEqual(real_titles, expected_titles) if finished is not None: item_status = [data['finished'] for data in response.data['results']] @@ -113,8 +115,45 @@ class ItemTest(APITestCase): check_finished = False if (finished is None) else finished self.assertEqual((response.data['text'], response.data['parent'], response.data['finished']), \ (item_text, todo_id, check_finished)) - return response.data['id'] + return response.data['id'], response.data['finished'] + def get_by_id(self, id, text, finished, parent): + url_with_id = reverse('ToDoItems-detail', args=(id,)) + response = self.client.get(url_with_id, {id: id}, format='json') + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual((response.data['text'], response.data['finished'], response.data['parent']), \ + (text, finished, parent)) + + def put(self, id, text, finished, parent): + url_with_id = reverse('ToDoItems-detail', args=(id,)) + data = {"text": text, "parent": parent} + if finished is not None: + data["finished"] = finished + response = self.client.put(url_with_id, data, format='json') + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual((response.data['text'], response.data['parent']), \ + (text, parent)) + if finished is not None: + self.assertEqual(response.data['finished'], finished) + + def patch(self, id, text, finished, parent): + url_with_id = reverse('ToDoItems-detail', args=(id,)) + data = {} + if text is not None: + data["text"] = text + if finished is not None: + data["finished"] = finished + if parent is not None: + data["parent"] = parent + response = self.client.patch(url_with_id, data, format='json') + self.assertEqual(response.status_code, status.HTTP_200_OK) + if text is not None: + self.assertEqual(response.data['text'], text) + if finished is not None: + self.assertEqual(response.data['finished'], finished) + if parent is not None: + self.assertEqual(response.data['parent'], parent) + def delete(self, id): url_with_id = reverse('ToDoItems-detail', args=(id,)) response = self.client.delete(url_with_id, {}, format='json') @@ -123,22 +162,45 @@ class ItemTest(APITestCase): def test_items(self): """ /todo_items/: get+, post+ (create) - /todo_items/{id}/: get (read), put (update), patch (partial_update), delete+ + /todo_items/{id}/: get+ (read), put (update), patch (partial_update), delete+ """ user = User.objects.create_user('test_user', 'test@test.com', 'test_password') self.client.force_authenticate(user=user) - to_do_id = create_todo(self.client, "ToDoList").data['id'] - self.get(to_do_id, None, []) - item_id_1 = self.post("Item1", to_do_id, None) - self.get(to_do_id, None, ["Item1"]) - item_id_2 = self.post("Item2", to_do_id, False) - self.get(to_do_id, None, ["Item1", "Item2"]) - item_id_3 = self.post("Item3", to_do_id, True) - self.get(to_do_id, None, ["Item1", "Item2", "Item3"]) - self.get(to_do_id, False, ["Item1", "Item2"]) - self.get(to_do_id, True, ["Item3"]) + to_do_id_1 = create_todo(self.client, "ToDoList1").data['id'] + to_do_id_2 = create_todo(self.client, "ToDoList2").data['id'] + + self.get(to_do_id_1, None, []) + item_text_1, item_text_2, item_text_3, item_text_4 = "Item1", "Item2", "Item3", "Item4" + item_id_1, item_finished_1 = self.post(item_text_1, to_do_id_1, None) + self.get(to_do_id_1, None, [(item_text_1, to_do_id_1)]) + item_id_2, item_finished_2 = self.post(item_text_2, to_do_id_1, False) + self.get(to_do_id_1, None, [(item_text_1, to_do_id_1), (item_text_2, to_do_id_1)]) + item_id_3, item_finished_3 = self.post(item_text_3, to_do_id_1, True) + self.get(to_do_id_1, None, [(item_text_1, to_do_id_1), (item_text_2, to_do_id_1), \ + (item_text_3, to_do_id_1)]) + item_id_4, item_finished_4 = self.post(item_text_4, to_do_id_2, True) + self.get(None, None, [(item_text_1, to_do_id_1), (item_text_2, to_do_id_1), \ + (item_text_3, to_do_id_1), (item_text_4, to_do_id_2)]) + + + self.get(to_do_id_1, None, [(item_text_1, to_do_id_1), (item_text_2, to_do_id_1), (item_text_3, to_do_id_1)]) + self.get(to_do_id_1, False, [(item_text_1, to_do_id_1), (item_text_2, to_do_id_1)]) + self.get(to_do_id_1, True, [(item_text_3, to_do_id_1)]) + + self.get_by_id(item_id_1, item_text_1, item_finished_1, to_do_id_1) + self.get_by_id(item_id_2, item_text_2, item_finished_2, to_do_id_1) + self.get_by_id(item_id_3, item_text_3, item_finished_3, to_do_id_1) self.delete(item_id_3) - self.get(to_do_id, None, ["Item1", "Item2"]) + self.get(to_do_id_1, None, [(item_text_1, to_do_id_1), (item_text_2, to_do_id_1)]) + item_text_1_2 = "Item5" + self.put(item_id_1, item_text_1_2, None, to_do_id_2) + self.put(item_id_1, item_text_1_2, False, to_do_id_2) + self.put(item_id_1, item_text_1_2, True, to_do_id_2) + + item_text_1_2 = "Item6" + self.patch(item_id_1, None, None, to_do_id_1) + self.patch(item_id_1, None, True, None) + self.patch(item_id_1, item_text_1_2, None, None) -- 2.49.1 From 4ab5f11dd2b4402b7104bbd7fb51d31daca8a662 Mon Sep 17 00:00:00 2001 From: Derinhelm Date: Tue, 27 Apr 2021 17:52:16 +0300 Subject: [PATCH 12/16] =?UTF-8?q?=D0=9F=D0=B5=D1=80=D0=B5=D0=B3=D1=80?= =?UTF-8?q?=D1=83=D0=BF=D0=BF=D0=B8=D1=80=D0=BE=D0=B2=D0=B0=D0=BD=D1=8B=20?= =?UTF-8?q?=D1=82=D0=B5=D1=81=D1=82=D1=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/tests/test_1.py | 206 ------------------------------------- backend/tests/test_item.py | 157 ++++++++++++++++++++++++++++ backend/tests/test_todo.py | 95 +++++++++++++++++ 3 files changed, 252 insertions(+), 206 deletions(-) delete mode 100644 backend/tests/test_1.py create mode 100644 backend/tests/test_item.py create mode 100644 backend/tests/test_todo.py diff --git a/backend/tests/test_1.py b/backend/tests/test_1.py deleted file mode 100644 index 702a533..0000000 --- a/backend/tests/test_1.py +++ /dev/null @@ -1,206 +0,0 @@ -import os - -os.environ.setdefault("DJANGO_SETTINGS_MODULE", "backend.settings") - -from django.core.wsgi import get_wsgi_application -application = get_wsgi_application() - -from django.urls import reverse -from rest_framework import status -from rest_framework.test import APITestCase -from rest_framework.test import APIClient -from django.contrib.auth.models import User -from backend.api import router -from collections import OrderedDict - -def create_todo(client, title): - url = reverse('ToDoLists-list') - response = client.post(url, {"title": title}, format='json') - return response - -class ToDoTest(APITestCase): - '''Tests API for todo.''' - def get(self, expected_titles): - url = reverse('ToDoLists-list') - response = self.client.get(url, {}, format='json') - self.assertEqual(response.status_code, status.HTTP_200_OK) - real_titles = [data['title'] for data in response.data['results']] - self.assertEqual((response.data['count'], real_titles), \ - (len(expected_titles), expected_titles)) - - def post(self, to_do_title): - response = create_todo(self.client, to_do_title) - self.assertEqual(response.status_code, status.HTTP_201_CREATED) - self.assertEqual(response.data['title'], to_do_title) - return response.data['id'] - - def get_by_id(self, id, expected_title): - url_with_id = reverse('ToDoLists-detail', args=(id,)) - response = self.client.get(url_with_id, {id: id}, format='json') - self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertEqual(response.data['title'], expected_title) - - def put(self, id, new_title): - url_with_id = reverse('ToDoLists-detail', args=(id,)) - response = self.client.put(url_with_id, {"title": new_title}, format='json') - self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertEqual(response.data['title'], new_title) - - def patch(self, id, new_title): - url_with_id = reverse('ToDoLists-detail', args=(id,)) - response = self.client.patch(url_with_id, {"title": new_title}, format='json') - self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertEqual(response.data['title'], new_title) - - def delete(self, id): - url_with_id = reverse('ToDoLists-detail', args=(id,)) - response = self.client.delete(url_with_id, {}, format='json') - self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT) - - def test_todo(self): - """ - lists/: get, post - lists/{id}/: get, put (update), patch (partial_update), delete - """ - - user = User.objects.create_user('test_user', 'test@test.com', 'test_password') - self.client.force_authenticate(user=user) - - self.get([]) - to_do_title_1, to_do_title_2 = "ToDoList1", "ToDoList2" - to_do_id1 = self.post(to_do_title_1) - self.get([to_do_title_1]) - to_do_id2 = self.post(to_do_title_2) - self.get([to_do_title_1, to_do_title_2]) - - self.get_by_id(to_do_id1, to_do_title_1) - self.get_by_id(to_do_id2, to_do_title_2) - - self.delete(to_do_id2) - self.get([to_do_title_1]) - - self.put(to_do_id1, "ToDoList11") - - self.patch(to_do_id1, "ToDoList12") - - -class ItemTest(APITestCase): - '''Tests API for items.''' - def get(self, todo_id, finished, expected_titles): - url = reverse('ToDoItems-list') - data = {} - if finished is not None: - data["finished"] = finished - if todo_id is not None: - data["parent"] = todo_id - - response = self.client.get(url, data, format='json') - self.assertEqual(response.status_code, status.HTTP_200_OK) - real_titles = [(d['text'], d['parent']) for d in response.data['results']] - self.assertEqual(real_titles, expected_titles) - - if finished is not None: - item_status = [data['finished'] for data in response.data['results']] - self.assertEqual(finished, all(item_status)) - - def post(self, item_text, todo_id, finished): - url = reverse('ToDoItems-list') - if finished is not None: - data = {"text": item_text, "parent": todo_id, "finished": finished} - else: - data = {"text": item_text, "parent": todo_id} - response = self.client.post(url, data, format='json') - - self.assertEqual(response.status_code, status.HTTP_201_CREATED) - check_finished = False if (finished is None) else finished - self.assertEqual((response.data['text'], response.data['parent'], response.data['finished']), \ - (item_text, todo_id, check_finished)) - return response.data['id'], response.data['finished'] - - def get_by_id(self, id, text, finished, parent): - url_with_id = reverse('ToDoItems-detail', args=(id,)) - response = self.client.get(url_with_id, {id: id}, format='json') - self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertEqual((response.data['text'], response.data['finished'], response.data['parent']), \ - (text, finished, parent)) - - def put(self, id, text, finished, parent): - url_with_id = reverse('ToDoItems-detail', args=(id,)) - data = {"text": text, "parent": parent} - if finished is not None: - data["finished"] = finished - response = self.client.put(url_with_id, data, format='json') - self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertEqual((response.data['text'], response.data['parent']), \ - (text, parent)) - if finished is not None: - self.assertEqual(response.data['finished'], finished) - - def patch(self, id, text, finished, parent): - url_with_id = reverse('ToDoItems-detail', args=(id,)) - data = {} - if text is not None: - data["text"] = text - if finished is not None: - data["finished"] = finished - if parent is not None: - data["parent"] = parent - response = self.client.patch(url_with_id, data, format='json') - self.assertEqual(response.status_code, status.HTTP_200_OK) - if text is not None: - self.assertEqual(response.data['text'], text) - if finished is not None: - self.assertEqual(response.data['finished'], finished) - if parent is not None: - self.assertEqual(response.data['parent'], parent) - - def delete(self, id): - url_with_id = reverse('ToDoItems-detail', args=(id,)) - response = self.client.delete(url_with_id, {}, format='json') - self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT) - - def test_items(self): - """ - /todo_items/: get+, post+ (create) - /todo_items/{id}/: get+ (read), put (update), patch (partial_update), delete+ - """ - user = User.objects.create_user('test_user', 'test@test.com', 'test_password') - self.client.force_authenticate(user=user) - to_do_id_1 = create_todo(self.client, "ToDoList1").data['id'] - to_do_id_2 = create_todo(self.client, "ToDoList2").data['id'] - - self.get(to_do_id_1, None, []) - item_text_1, item_text_2, item_text_3, item_text_4 = "Item1", "Item2", "Item3", "Item4" - item_id_1, item_finished_1 = self.post(item_text_1, to_do_id_1, None) - self.get(to_do_id_1, None, [(item_text_1, to_do_id_1)]) - item_id_2, item_finished_2 = self.post(item_text_2, to_do_id_1, False) - self.get(to_do_id_1, None, [(item_text_1, to_do_id_1), (item_text_2, to_do_id_1)]) - item_id_3, item_finished_3 = self.post(item_text_3, to_do_id_1, True) - self.get(to_do_id_1, None, [(item_text_1, to_do_id_1), (item_text_2, to_do_id_1), \ - (item_text_3, to_do_id_1)]) - item_id_4, item_finished_4 = self.post(item_text_4, to_do_id_2, True) - self.get(None, None, [(item_text_1, to_do_id_1), (item_text_2, to_do_id_1), \ - (item_text_3, to_do_id_1), (item_text_4, to_do_id_2)]) - - - self.get(to_do_id_1, None, [(item_text_1, to_do_id_1), (item_text_2, to_do_id_1), (item_text_3, to_do_id_1)]) - self.get(to_do_id_1, False, [(item_text_1, to_do_id_1), (item_text_2, to_do_id_1)]) - self.get(to_do_id_1, True, [(item_text_3, to_do_id_1)]) - - self.get_by_id(item_id_1, item_text_1, item_finished_1, to_do_id_1) - self.get_by_id(item_id_2, item_text_2, item_finished_2, to_do_id_1) - self.get_by_id(item_id_3, item_text_3, item_finished_3, to_do_id_1) - - self.delete(item_id_3) - self.get(to_do_id_1, None, [(item_text_1, to_do_id_1), (item_text_2, to_do_id_1)]) - - item_text_1_2 = "Item5" - self.put(item_id_1, item_text_1_2, None, to_do_id_2) - self.put(item_id_1, item_text_1_2, False, to_do_id_2) - self.put(item_id_1, item_text_1_2, True, to_do_id_2) - - item_text_1_2 = "Item6" - self.patch(item_id_1, None, None, to_do_id_1) - self.patch(item_id_1, None, True, None) - self.patch(item_id_1, item_text_1_2, None, None) - diff --git a/backend/tests/test_item.py b/backend/tests/test_item.py new file mode 100644 index 0000000..114c83b --- /dev/null +++ b/backend/tests/test_item.py @@ -0,0 +1,157 @@ +import os + +os.environ.setdefault("DJANGO_SETTINGS_MODULE", "backend.settings") + +from django.core.wsgi import get_wsgi_application +application = get_wsgi_application() + +from django.urls import reverse +from rest_framework import status +from rest_framework.test import APITestCase +from rest_framework.test import APIClient +from django.contrib.auth.models import User +from backend.api import router +from .test_todo import create_todo + +class ItemTest(APITestCase): + '''Tests API for items.''' + + def prepare(self): + user = User.objects.create_user('test_user4', 'test@test.com', 'test_password') + self.client.force_authenticate(user=user) + to_do_id_1 = create_todo(self.client, "ToDoList1").data['id'] + to_do_id_2 = create_todo(self.client, "ToDoList2").data['id'] + return to_do_id_1, to_do_id_2 + + def get(self, expected_titles, todo_id=None, finished=None): + url = reverse('ToDoItems-list') + data = {} + if finished is not None: + data["finished"] = finished + if todo_id is not None: + data["parent"] = todo_id + + response = self.client.get(url, data, format='json') + self.assertEqual(response.status_code, status.HTTP_200_OK) + real_titles = [(d['text'], d['parent']) for d in response.data['results']] + self.assertEqual(real_titles, expected_titles) + + if finished is not None: + item_status = [data['finished'] for data in response.data['results']] + self.assertEqual(finished, all(item_status)) + + def post(self, item_text, todo_id, finished=None): + url = reverse('ToDoItems-list') + if finished is not None: + data = {"text": item_text, "parent": todo_id, "finished": finished} + else: + data = {"text": item_text, "parent": todo_id} + response = self.client.post(url, data, format='json') + + self.assertEqual(response.status_code, status.HTTP_201_CREATED) + check_finished = False if (finished is None) else finished + self.assertEqual(response.data['text'], item_text) + self.assertEqual(response.data['parent'], todo_id) + self.assertEqual(response.data['finished'], check_finished) + + return response.data['id'], response.data['finished'] + + def get_by_id(self, id, text, finished, parent): + url_with_id = reverse('ToDoItems-detail', args=(id,)) + response = self.client.get(url_with_id, {id: id}, format='json') + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(response.data['text'], text) + self.assertEqual(response.data['finished'], finished) + self.assertEqual(response.data['parent'], parent) + + def put(self, id, text, parent, finished=None): + url_with_id = reverse('ToDoItems-detail', args=(id,)) + data = {"text": text, "parent": parent} + if finished is not None: + data["finished"] = finished + response = self.client.put(url_with_id, data, format='json') + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(response.data['text'], text) + self.assertEqual(response.data['parent'], parent) + if finished is not None: + self.assertEqual(response.data['finished'], finished) + + def patch(self, id, text=None, finished=None, parent=None): + url_with_id = reverse('ToDoItems-detail', args=(id,)) + data = {} + if text is not None: + data["text"] = text + if finished is not None: + data["finished"] = finished + if parent is not None: + data["parent"] = parent + response = self.client.patch(url_with_id, data, format='json') + self.assertEqual(response.status_code, status.HTTP_200_OK) + if text is not None: + self.assertEqual(response.data['text'], text) + if finished is not None: + self.assertEqual(response.data['finished'], finished) + if parent is not None: + self.assertEqual(response.data['parent'], parent) + + def delete(self, id, title, finished, to_do_id): + self.get_by_id(id, title, finished, to_do_id) + url_with_id = reverse('ToDoItems-detail', args=(id,)) + response = self.client.delete(url_with_id, {}, format='json') + self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT) + + def test_create_delete(self): + """ + /todo_items/: get, post (create) + /todo_items/{id}/: get (read), delete + """ + to_do_id_1, to_do_id_2 = self.prepare() + self.get([], to_do_id_1) + item_text_1, item_text_2, item_text_3, item_text_4 = "Item1", "Item2", "Item3", "Item4" + item_id_1, item_finished_1 = self.post(item_text_1, to_do_id_1) + self.get([(item_text_1, to_do_id_1)], to_do_id_1) + item_id_2, item_finished_2 = self.post(item_text_2, to_do_id_1, finished=False) + self.get([(item_text_1, to_do_id_1), (item_text_2, to_do_id_1)], to_do_id_1) + item_id_3, item_finished_3 = self.post(item_text_3, to_do_id_1, finished=True) + self.get([(item_text_1, to_do_id_1), (item_text_2, to_do_id_1), \ + (item_text_3, to_do_id_1)], to_do_id_1) + item_id_4, item_finished_4 = self.post(item_text_4, to_do_id_2, finished=False) + self.get([(item_text_1, to_do_id_1), (item_text_2, to_do_id_1), \ + (item_text_3, to_do_id_1), (item_text_4, to_do_id_2)]) + + self.get([(item_text_1, to_do_id_1), (item_text_2, to_do_id_1), (item_text_3, to_do_id_1)], to_do_id_1) + self.get([(item_text_1, to_do_id_1), (item_text_2, to_do_id_1)], to_do_id_1, finished=False) + self.get([(item_text_3, to_do_id_1)], to_do_id_1, finished=True) + + self.get_by_id(item_id_1, item_text_1, item_finished_1, to_do_id_1) + self.get_by_id(item_id_2, item_text_2, item_finished_2, to_do_id_1) + self.get_by_id(item_id_3, item_text_3, item_finished_3, to_do_id_1) + + self.delete(item_id_3, item_text_3, item_finished_3, to_do_id_1) + self.get([(item_text_1, to_do_id_1), (item_text_2, to_do_id_1)], to_do_id_1) + + def test_update(self): + """ + /todo_items/{id}/: put (update), patch (partial_update) + """ + + to_do_id_1, to_do_id_2 = self.prepare() + + item_text_1 = "Item1" + item_id_1, item_finished_1 = self.post(item_text_1, to_do_id_1) + + item_text_1_2 = "Item5" + self.put(item_id_1, item_text_1_2, to_do_id_2) + self.put(item_id_1, item_text_1_2, to_do_id_2, finished=False) + self.put(item_id_1, item_text_1_2, to_do_id_2, finished=True) + + item_text_1_3 = "Item6" + self.patch(item_id_1, parent=to_do_id_1) + self.patch(item_id_1, finished=True) + self.patch(item_id_1, text=item_text_1_3) + + + + + + diff --git a/backend/tests/test_todo.py b/backend/tests/test_todo.py new file mode 100644 index 0000000..f39a652 --- /dev/null +++ b/backend/tests/test_todo.py @@ -0,0 +1,95 @@ +import os + +os.environ.setdefault("DJANGO_SETTINGS_MODULE", "backend.settings") + +from django.core.wsgi import get_wsgi_application +application = get_wsgi_application() + +from django.urls import reverse +from rest_framework import status +from rest_framework.test import APITestCase +from rest_framework.test import APIClient +from django.contrib.auth.models import User +from backend.api import router +from collections import OrderedDict + +def create_todo(client, title): + url = reverse('ToDoLists-list') + response = client.post(url, {"title": title}, format='json') + return response + +class ToDoTest(APITestCase): + '''Tests API for todo.''' + def get(self, expected_titles): + url = reverse('ToDoLists-list') + response = self.client.get(url, {}, format='json') + self.assertEqual(response.status_code, status.HTTP_200_OK) + real_titles = [data['title'] for data in response.data['results']] + self.assertEqual((response.data['count'], real_titles), \ + (len(expected_titles), expected_titles)) + + def post(self, to_do_title): + response = create_todo(self.client, to_do_title) + self.assertEqual(response.status_code, status.HTTP_201_CREATED) + self.assertEqual(response.data['title'], to_do_title) + return response.data['id'] + + def get_by_id(self, id, expected_title): + url_with_id = reverse('ToDoLists-detail', args=(id,)) + response = self.client.get(url_with_id, {id: id}, format='json') + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(response.data['title'], expected_title) + + def put(self, id, new_title): + url_with_id = reverse('ToDoLists-detail', args=(id,)) + response = self.client.put(url_with_id, {"title": new_title}, format='json') + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(response.data['title'], new_title) + + def patch(self, id, new_title): + url_with_id = reverse('ToDoLists-detail', args=(id,)) + response = self.client.patch(url_with_id, {"title": new_title}, format='json') + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(response.data['title'], new_title) + + def delete(self, id, title): + self.get_by_id(id, title) + url_with_id = reverse('ToDoLists-detail', args=(id,)) + response = self.client.delete(url_with_id, {}, format='json') + self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT) + + def prepare(self): + user = User.objects.create_user('test_user', 'test@test.com', 'test_password') + self.client.force_authenticate(user=user) + + def test_create_delete(self): + """ + lists/{id}/: put (update), patch (partial_update) + """ + self.prepare() + to_do_title_1 = "ToDoList1" + to_do_id1 = self.post(to_do_title_1) + self.put(to_do_id1, "ToDoList11") + self.patch(to_do_id1, "ToDoList12") + + + def test_todo(self): + """ + lists/: get, post + lists/{id}/: get, delete + """ + + self.prepare() + self.get([]) + to_do_title_1, to_do_title_2 = "ToDoList1", "ToDoList2" + to_do_id1 = self.post(to_do_title_1) + self.get([to_do_title_1]) + to_do_id2 = self.post(to_do_title_2) + self.get([to_do_title_1, to_do_title_2]) + + self.get_by_id(to_do_id1, to_do_title_1) + self.get_by_id(to_do_id2, to_do_title_2) + + self.delete(to_do_id2, to_do_title_2) + self.get([to_do_title_1]) + -- 2.49.1 From bba4be6b274069a5b676fdc3e3da38fd459039db Mon Sep 17 00:00:00 2001 From: Derinhelm Date: Tue, 27 Apr 2021 20:38:49 +0300 Subject: [PATCH 13/16] =?UTF-8?q?=D0=92=20README=20=D0=B4=D0=BE=D0=B1?= =?UTF-8?q?=D0=B0=D0=B2=D0=BB=D0=B5=D0=BD=D0=B0=20=D0=BA=D0=BE=D0=BC=D0=B0?= =?UTF-8?q?=D0=BD=D0=B4=D0=B0=20=D0=B4=D0=BB=D1=8F=20=D0=B7=D0=B0=D0=BF?= =?UTF-8?q?=D1=83=D1=81=D0=BA=D0=B0=20=D1=82=D0=B5=D1=81=D1=82=D0=BE=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index ec2c1c7..60dde79 100644 --- a/README.md +++ b/README.md @@ -53,3 +53,8 @@ docker-compose up ```bash docker-compose exec web python manage.py makemigrations backend ``` + +Для запуска тестов использовать +```bash +docker-compose exec web python manage.py test +``` -- 2.49.1 From 0ee81d4b421046ee36f14092834cd6255d64e474 Mon Sep 17 00:00:00 2001 From: Aleksey Lobanov Date: Tue, 27 Apr 2021 23:12:51 +0300 Subject: [PATCH 14/16] =?UTF-8?q?feat:=20=D0=98=D1=81=D0=BF=D0=BE=D0=BB?= =?UTF-8?q?=D1=8C=D0=B7=D1=83=D0=B5=D0=BC=20pytest=20=D0=B4=D0=BB=D1=8F=20?= =?UTF-8?q?=D0=B1=D0=B5=D0=BA=D0=B5=D0=BD=D0=B4=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/backend/settings.py | 2 +- backend/requirements.txt | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/backend/backend/settings.py b/backend/backend/settings.py index 32b39d6..e00523f 100644 --- a/backend/backend/settings.py +++ b/backend/backend/settings.py @@ -28,7 +28,7 @@ DEBUG = True ALLOWED_HOSTS = [] if DEBUG: - ALLOWED_HOSTS = ["0.0.0.0", "localhost", "127.0.0.1"] + ALLOWED_HOSTS = ["0.0.0.0", "localhost", "127.0.0.1", "ALLOWED_HOSTS", "testserver"] # Application definition diff --git a/backend/requirements.txt b/backend/requirements.txt index 2c9488b..fe00bac 100644 --- a/backend/requirements.txt +++ b/backend/requirements.txt @@ -1,3 +1,4 @@ +pytest==6.2.3 djangorestframework==3.12.4 django-filter==2.4.0 markdown==3.3.4 -- 2.49.1 From fdd40d459258bd1e1097752da10f8b83590a4b30 Mon Sep 17 00:00:00 2001 From: Aleksey Lobanov Date: Tue, 27 Apr 2021 23:13:16 +0300 Subject: [PATCH 15/16] =?UTF-8?q?feat:=20=D0=94=D0=BE=D0=B1=D0=B0=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=D0=BE=20=D0=B2=D1=8B=D1=87=D0=B8=D1=81=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D0=B8=D1=8F=20=D0=BF=D0=BE=D0=BA=D1=80=D1=8B=D1=82?= =?UTF-8?q?=D0=B8=D1=8F=20=D0=B4=D0=BB=D1=8F=20=D1=82=D0=B5=D1=81=D1=82?= =?UTF-8?q?=D0=BE=D0=B2=20=D0=B1=D0=B5=D0=BA=D0=B5=D0=BD=D0=B4=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- backend/.coveragerc | 3 +++ backend/requirements.txt | 1 + 3 files changed, 5 insertions(+), 1 deletion(-) create mode 100644 backend/.coveragerc diff --git a/README.md b/README.md index 60dde79..2b1f96a 100644 --- a/README.md +++ b/README.md @@ -56,5 +56,5 @@ docker-compose exec web python manage.py makemigrations backend Для запуска тестов использовать ```bash -docker-compose exec web python manage.py test +docker-compose run web pytest --cov=backend ``` diff --git a/backend/.coveragerc b/backend/.coveragerc new file mode 100644 index 0000000..455c6de --- /dev/null +++ b/backend/.coveragerc @@ -0,0 +1,3 @@ +[run] +omit = + backend/migrations/* diff --git a/backend/requirements.txt b/backend/requirements.txt index fe00bac..7385760 100644 --- a/backend/requirements.txt +++ b/backend/requirements.txt @@ -1,4 +1,5 @@ pytest==6.2.3 +pytest-cov==2.11.1 djangorestframework==3.12.4 django-filter==2.4.0 markdown==3.3.4 -- 2.49.1 From 9152cc9772dc8839aa89fc5a3119a8c15dd9f43a Mon Sep 17 00:00:00 2001 From: Derinhelm Date: Wed, 28 Apr 2021 00:47:56 +0300 Subject: [PATCH 16/16] =?UTF-8?q?=D0=98=D1=81=D0=BF=D1=80=D0=B0=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=D1=8B=20=D0=BD=D0=B5=D0=B4=D0=BE=D1=87=D0=B5?= =?UTF-8?q?=D1=82=D1=8B=20=D0=B2=20=D0=BE=D1=84=D0=BE=D1=80=D0=BC=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D0=B8=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- backend/backend/urls.py | 16 +++--- backend/tests/__init__.py | 3 ++ backend/tests/test_item.py | 107 ++++++++++++++++++------------------- backend/tests/test_todo.py | 58 +++++++++----------- 5 files changed, 88 insertions(+), 98 deletions(-) diff --git a/README.md b/README.md index 2b1f96a..e886902 100644 --- a/README.md +++ b/README.md @@ -56,5 +56,5 @@ docker-compose exec web python manage.py makemigrations backend Для запуска тестов использовать ```bash -docker-compose run web pytest --cov=backend +docker-compose run -e DJANGO_SETTINGS_MODULE=backend.settings web pytest --cov=backend ``` diff --git a/backend/backend/urls.py b/backend/backend/urls.py index d123db5..cdcbc7f 100644 --- a/backend/backend/urls.py +++ b/backend/backend/urls.py @@ -13,13 +13,13 @@ from drf_yasg import openapi from .api import router schema_view = get_schema_view( - openapi.Info( - title="ToDo List", - default_version='v1', - description="Swagger Interface for ToDo List", - ), - public=True, - permission_classes=(permissions.AllowAny,), + openapi.Info( + title="ToDo List", + default_version="v1", + description="Swagger Interface for ToDo List", + ), + public=True, + permission_classes=(permissions.AllowAny,), ) urlpatterns = [ @@ -28,5 +28,5 @@ urlpatterns = [ path("api/token/refresh/", TokenRefreshView.as_view(), name="token_refresh"), path("api/", include(router.urls)), path("api-auth/", include("rest_framework.urls", namespace="rest_framework")), - path('swagger/', schema_view.with_ui('swagger', cache_timeout=0), name='schema-swagger-ui'), + path("swagger/", schema_view.with_ui("swagger", cache_timeout=0), name="schema-swagger-ui"), ] diff --git a/backend/tests/__init__.py b/backend/tests/__init__.py index e69de29..8435af2 100644 --- a/backend/tests/__init__.py +++ b/backend/tests/__init__.py @@ -0,0 +1,3 @@ +from django.core.wsgi import get_wsgi_application + +application = get_wsgi_application() diff --git a/backend/tests/test_item.py b/backend/tests/test_item.py index 114c83b..783dcff 100644 --- a/backend/tests/test_item.py +++ b/backend/tests/test_item.py @@ -1,83 +1,75 @@ -import os - -os.environ.setdefault("DJANGO_SETTINGS_MODULE", "backend.settings") - -from django.core.wsgi import get_wsgi_application -application = get_wsgi_application() - from django.urls import reverse from rest_framework import status from rest_framework.test import APITestCase -from rest_framework.test import APIClient from django.contrib.auth.models import User -from backend.api import router from .test_todo import create_todo + class ItemTest(APITestCase): - '''Tests API for items.''' + """Tests API for items.""" def prepare(self): - user = User.objects.create_user('test_user4', 'test@test.com', 'test_password') + user = User.objects.create_user("test_user4", "test@test.com", "test_password") self.client.force_authenticate(user=user) - to_do_id_1 = create_todo(self.client, "ToDoList1").data['id'] - to_do_id_2 = create_todo(self.client, "ToDoList2").data['id'] + to_do_id_1 = create_todo(self.client, "ToDoList1").data["id"] + to_do_id_2 = create_todo(self.client, "ToDoList2").data["id"] return to_do_id_1, to_do_id_2 def get(self, expected_titles, todo_id=None, finished=None): - url = reverse('ToDoItems-list') + url = reverse("ToDoItems-list") data = {} if finished is not None: data["finished"] = finished if todo_id is not None: data["parent"] = todo_id - response = self.client.get(url, data, format='json') + response = self.client.get(url, data, format="json") self.assertEqual(response.status_code, status.HTTP_200_OK) - real_titles = [(d['text'], d['parent']) for d in response.data['results']] + real_titles = [(d["text"], d["parent"]) for d in response.data["results"]] self.assertEqual(real_titles, expected_titles) if finished is not None: - item_status = [data['finished'] for data in response.data['results']] + item_status = [data["finished"] for data in response.data["results"]] self.assertEqual(finished, all(item_status)) def post(self, item_text, todo_id, finished=None): - url = reverse('ToDoItems-list') + url = reverse("ToDoItems-list") if finished is not None: data = {"text": item_text, "parent": todo_id, "finished": finished} else: data = {"text": item_text, "parent": todo_id} - response = self.client.post(url, data, format='json') + response = self.client.post(url, data, format="json") self.assertEqual(response.status_code, status.HTTP_201_CREATED) check_finished = False if (finished is None) else finished - self.assertEqual(response.data['text'], item_text) - self.assertEqual(response.data['parent'], todo_id) - self.assertEqual(response.data['finished'], check_finished) + self.assertEqual(response.data["text"], item_text) + self.assertEqual(response.data["parent"], todo_id) + self.assertEqual(response.data["finished"], check_finished) - return response.data['id'], response.data['finished'] + return response.data["id"], response.data["finished"] def get_by_id(self, id, text, finished, parent): - url_with_id = reverse('ToDoItems-detail', args=(id,)) - response = self.client.get(url_with_id, {id: id}, format='json') + url_with_id = reverse("ToDoItems-detail", args=(id,)) + response = self.client.get(url_with_id, {id: id}, format="json") self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertEqual(response.data['text'], text) - self.assertEqual(response.data['finished'], finished) - self.assertEqual(response.data['parent'], parent) - + self.assertEqual(response.data["text"], text) + self.assertEqual(response.data["finished"], finished) + self.assertEqual(response.data["parent"], parent) + def put(self, id, text, parent, finished=None): - url_with_id = reverse('ToDoItems-detail', args=(id,)) + url_with_id = reverse("ToDoItems-detail", args=(id,)) data = {"text": text, "parent": parent} if finished is not None: data["finished"] = finished - response = self.client.put(url_with_id, data, format='json') + response = self.client.put(url_with_id, data, format="json") self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertEqual(response.data['text'], text) - self.assertEqual(response.data['parent'], parent) + self.assertEqual(response.data["text"], text) + self.assertEqual(response.data["parent"], parent) if finished is not None: - self.assertEqual(response.data['finished'], finished) + self.assertEqual(response.data["finished"], finished) def patch(self, id, text=None, finished=None, parent=None): - url_with_id = reverse('ToDoItems-detail', args=(id,)) + url_with_id = reverse("ToDoItems-detail", args=(id,)) data = {} if text is not None: data["text"] = text @@ -85,21 +77,21 @@ class ItemTest(APITestCase): data["finished"] = finished if parent is not None: data["parent"] = parent - response = self.client.patch(url_with_id, data, format='json') + response = self.client.patch(url_with_id, data, format="json") self.assertEqual(response.status_code, status.HTTP_200_OK) if text is not None: - self.assertEqual(response.data['text'], text) + self.assertEqual(response.data["text"], text) if finished is not None: - self.assertEqual(response.data['finished'], finished) + self.assertEqual(response.data["finished"], finished) if parent is not None: - self.assertEqual(response.data['parent'], parent) - + self.assertEqual(response.data["parent"], parent) + def delete(self, id, title, finished, to_do_id): self.get_by_id(id, title, finished, to_do_id) - url_with_id = reverse('ToDoItems-detail', args=(id,)) - response = self.client.delete(url_with_id, {}, format='json') + url_with_id = reverse("ToDoItems-detail", args=(id,)) + response = self.client.delete(url_with_id, {}, format="json") self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT) - + def test_create_delete(self): """ /todo_items/: get, post (create) @@ -113,13 +105,24 @@ class ItemTest(APITestCase): item_id_2, item_finished_2 = self.post(item_text_2, to_do_id_1, finished=False) self.get([(item_text_1, to_do_id_1), (item_text_2, to_do_id_1)], to_do_id_1) item_id_3, item_finished_3 = self.post(item_text_3, to_do_id_1, finished=True) - self.get([(item_text_1, to_do_id_1), (item_text_2, to_do_id_1), \ - (item_text_3, to_do_id_1)], to_do_id_1) - item_id_4, item_finished_4 = self.post(item_text_4, to_do_id_2, finished=False) - self.get([(item_text_1, to_do_id_1), (item_text_2, to_do_id_1), \ - (item_text_3, to_do_id_1), (item_text_4, to_do_id_2)]) + self.get( + [(item_text_1, to_do_id_1), (item_text_2, to_do_id_1), (item_text_3, to_do_id_1)], + to_do_id_1, + ) + item_id_4, item_finished_4 = self.post(item_text_4, to_do_id_2, finished=False) + self.get( + [ + (item_text_1, to_do_id_1), + (item_text_2, to_do_id_1), + (item_text_3, to_do_id_1), + (item_text_4, to_do_id_2), + ] + ) - self.get([(item_text_1, to_do_id_1), (item_text_2, to_do_id_1), (item_text_3, to_do_id_1)], to_do_id_1) + self.get( + [(item_text_1, to_do_id_1), (item_text_2, to_do_id_1), (item_text_3, to_do_id_1)], + to_do_id_1, + ) self.get([(item_text_1, to_do_id_1), (item_text_2, to_do_id_1)], to_do_id_1, finished=False) self.get([(item_text_3, to_do_id_1)], to_do_id_1, finished=True) @@ -149,9 +152,3 @@ class ItemTest(APITestCase): self.patch(item_id_1, parent=to_do_id_1) self.patch(item_id_1, finished=True) self.patch(item_id_1, text=item_text_1_3) - - - - - - diff --git a/backend/tests/test_todo.py b/backend/tests/test_todo.py index f39a652..a8b260f 100644 --- a/backend/tests/test_todo.py +++ b/backend/tests/test_todo.py @@ -1,65 +1,57 @@ -import os - -os.environ.setdefault("DJANGO_SETTINGS_MODULE", "backend.settings") - -from django.core.wsgi import get_wsgi_application -application = get_wsgi_application() - from django.urls import reverse from rest_framework import status from rest_framework.test import APITestCase -from rest_framework.test import APIClient from django.contrib.auth.models import User -from backend.api import router -from collections import OrderedDict + def create_todo(client, title): - url = reverse('ToDoLists-list') - response = client.post(url, {"title": title}, format='json') + url = reverse("ToDoLists-list") + response = client.post(url, {"title": title}, format="json") return response + class ToDoTest(APITestCase): - '''Tests API for todo.''' + """Tests API for todo.""" + def get(self, expected_titles): - url = reverse('ToDoLists-list') - response = self.client.get(url, {}, format='json') + url = reverse("ToDoLists-list") + response = self.client.get(url, {}, format="json") self.assertEqual(response.status_code, status.HTTP_200_OK) - real_titles = [data['title'] for data in response.data['results']] - self.assertEqual((response.data['count'], real_titles), \ - (len(expected_titles), expected_titles)) + real_titles = [data["title"] for data in response.data["results"]] + self.assertEqual(real_titles, expected_titles) def post(self, to_do_title): response = create_todo(self.client, to_do_title) self.assertEqual(response.status_code, status.HTTP_201_CREATED) - self.assertEqual(response.data['title'], to_do_title) - return response.data['id'] + self.assertEqual(response.data["title"], to_do_title) + return response.data["id"] def get_by_id(self, id, expected_title): - url_with_id = reverse('ToDoLists-detail', args=(id,)) - response = self.client.get(url_with_id, {id: id}, format='json') + url_with_id = reverse("ToDoLists-detail", args=(id,)) + response = self.client.get(url_with_id, {id: id}, format="json") self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertEqual(response.data['title'], expected_title) + self.assertEqual(response.data["title"], expected_title) def put(self, id, new_title): - url_with_id = reverse('ToDoLists-detail', args=(id,)) - response = self.client.put(url_with_id, {"title": new_title}, format='json') + url_with_id = reverse("ToDoLists-detail", args=(id,)) + response = self.client.put(url_with_id, {"title": new_title}, format="json") self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertEqual(response.data['title'], new_title) + self.assertEqual(response.data["title"], new_title) def patch(self, id, new_title): - url_with_id = reverse('ToDoLists-detail', args=(id,)) - response = self.client.patch(url_with_id, {"title": new_title}, format='json') + url_with_id = reverse("ToDoLists-detail", args=(id,)) + response = self.client.patch(url_with_id, {"title": new_title}, format="json") self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertEqual(response.data['title'], new_title) + self.assertEqual(response.data["title"], new_title) def delete(self, id, title): self.get_by_id(id, title) - url_with_id = reverse('ToDoLists-detail', args=(id,)) - response = self.client.delete(url_with_id, {}, format='json') + url_with_id = reverse("ToDoLists-detail", args=(id,)) + response = self.client.delete(url_with_id, {}, format="json") self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT) def prepare(self): - user = User.objects.create_user('test_user', 'test@test.com', 'test_password') + user = User.objects.create_user("test_user", "test@test.com", "test_password") self.client.force_authenticate(user=user) def test_create_delete(self): @@ -72,7 +64,6 @@ class ToDoTest(APITestCase): self.put(to_do_id1, "ToDoList11") self.patch(to_do_id1, "ToDoList12") - def test_todo(self): """ lists/: get, post @@ -92,4 +83,3 @@ class ToDoTest(APITestCase): self.delete(to_do_id2, to_do_title_2) self.get([to_do_title_1]) - -- 2.49.1