From 3889c036e519443e7184642c04782022a59df4a1 Mon Sep 17 00:00:00 2001 From: bigsbug Date: Sun, 17 Jul 2022 16:10:42 +0430 Subject: [PATCH 01/66] add git ignore to repo --- .gitignore | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..bf43b00 --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +*.pyc +__pycache__ +*.swp +*.env +.vscode +*.sqlite +static/* \ No newline at end of file From 6e4fba14fc695bb5c9cce28697e2300fd6a2826b Mon Sep 17 00:00:00 2001 From: bigsbug Date: Sun, 17 Jul 2022 16:12:16 +0430 Subject: [PATCH 02/66] init project --- config/__init__.py | 0 config/asgi.py | 16 +++++ config/db.sqlite3 | 0 config/settings/__init__.py | 0 config/settings/base.py | 123 ++++++++++++++++++++++++++++++++++++ config/settings/dev.py | 8 +++ config/urls/__init__.py | 0 config/urls/base.py | 21 ++++++ config/urls/dev.py | 3 + config/wsgi.py | 16 +++++ cryptoreader/__init__.py | 0 manage.py | 22 +++++++ 12 files changed, 209 insertions(+) create mode 100644 config/__init__.py create mode 100644 config/asgi.py create mode 100644 config/db.sqlite3 create mode 100644 config/settings/__init__.py create mode 100644 config/settings/base.py create mode 100644 config/settings/dev.py create mode 100644 config/urls/__init__.py create mode 100644 config/urls/base.py create mode 100644 config/urls/dev.py create mode 100644 config/wsgi.py create mode 100644 cryptoreader/__init__.py create mode 100755 manage.py diff --git a/config/__init__.py b/config/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/config/asgi.py b/config/asgi.py new file mode 100644 index 0000000..cfa37a6 --- /dev/null +++ b/config/asgi.py @@ -0,0 +1,16 @@ +""" +ASGI config for CryptoReader project. + +It exposes the ASGI callable as a module-level variable named ``application``. + +For more information on this file, see +https://docs.djangoproject.com/en/4.0/howto/deployment/asgi/ +""" + +import os + +from django.core.asgi import get_asgi_application + +os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings.dev") + +application = get_asgi_application() diff --git a/config/db.sqlite3 b/config/db.sqlite3 new file mode 100644 index 0000000..e69de29 diff --git a/config/settings/__init__.py b/config/settings/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/config/settings/base.py b/config/settings/base.py new file mode 100644 index 0000000..7078b09 --- /dev/null +++ b/config/settings/base.py @@ -0,0 +1,123 @@ +""" +Django settings for CryptoReader project. + +Generated by 'django-admin startproject' using Django 4.0.5. + +For more information on this file, see +https://docs.djangoproject.com/en/4.0/topics/settings/ + +For the full list of settings and their values, see +https://docs.djangoproject.com/en/4.0/ref/settings/ +""" + +from pathlib import Path + +# Build paths inside the project like this: BASE_DIR / 'subdir'. +BASE_DIR = Path(__file__).resolve().parent.parent + + +# Quick-start development settings - unsuitable for production +# See https://docs.djangoproject.com/en/4.0/howto/deployment/checklist/ + +# SECURITY WARNING: keep the secret key used in production secret! +SECRET_KEY = "django-insecure-s#0r5^_%a3-4ts9ug==i4^xy&x2py@hq3_i+!k@2^i)l-v@c=9" + +# SECURITY WARNING: don't run with debug turned on in production! +DEBUG = True + +ALLOWED_HOSTS = [] + + +# Application definition + +INSTALLED_APPS = [ + "django.contrib.admin", + "django.contrib.auth", + "django.contrib.contenttypes", + "django.contrib.sessions", + "django.contrib.messages", + "django.contrib.staticfiles", +] + +MIDDLEWARE = [ + "django.middleware.security.SecurityMiddleware", + "django.contrib.sessions.middleware.SessionMiddleware", + "django.middleware.common.CommonMiddleware", + "django.middleware.csrf.CsrfViewMiddleware", + "django.contrib.auth.middleware.AuthenticationMiddleware", + "django.contrib.messages.middleware.MessageMiddleware", + "django.middleware.clickjacking.XFrameOptionsMiddleware", +] + +ROOT_URLCONF = "config.urls.dev" + +TEMPLATES = [ + { + "BACKEND": "django.template.backends.django.DjangoTemplates", + "DIRS": [], + "APP_DIRS": True, + "OPTIONS": { + "context_processors": [ + "django.template.context_processors.debug", + "django.template.context_processors.request", + "django.contrib.auth.context_processors.auth", + "django.contrib.messages.context_processors.messages", + ], + }, + }, +] + +WSGI_APPLICATION = "config.wsgi.application" + + +# Database +# https://docs.djangoproject.com/en/4.0/ref/settings/#databases + +DATABASES = { + "default": { + "ENGINE": "django.db.backends.sqlite3", + "NAME": BASE_DIR / "db.sqlite3", + } +} + + +# Password validation +# https://docs.djangoproject.com/en/4.0/ref/settings/#auth-password-validators + +AUTH_PASSWORD_VALIDATORS = [ + { + "NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator", + }, + { + "NAME": "django.contrib.auth.password_validation.MinimumLengthValidator", + }, + { + "NAME": "django.contrib.auth.password_validation.CommonPasswordValidator", + }, + { + "NAME": "django.contrib.auth.password_validation.NumericPasswordValidator", + }, +] + + +# Internationalization +# https://docs.djangoproject.com/en/4.0/topics/i18n/ + +LANGUAGE_CODE = "en-us" + +TIME_ZONE = "UTC" + +USE_I18N = True + +USE_TZ = True + + +# Static files (CSS, JavaScript, Images) +# https://docs.djangoproject.com/en/4.0/howto/static-files/ + +STATIC_URL = "static/" + +# Default primary key field type +# https://docs.djangoproject.com/en/4.0/ref/settings/#default-auto-field + +DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField" diff --git a/config/settings/dev.py b/config/settings/dev.py new file mode 100644 index 0000000..4a79e5f --- /dev/null +++ b/config/settings/dev.py @@ -0,0 +1,8 @@ +from config.settings.base import * + + +INSTALLED_APPS += [ + "rest_framework", +] + +ROOT_URLCONF = "config.urls.dev" diff --git a/config/urls/__init__.py b/config/urls/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/config/urls/base.py b/config/urls/base.py new file mode 100644 index 0000000..df42ce7 --- /dev/null +++ b/config/urls/base.py @@ -0,0 +1,21 @@ +"""CryptoReader URL Configuration + +The `urlpatterns` list routes URLs to views. For more information please see: + https://docs.djangoproject.com/en/4.0/topics/http/urls/ +Examples: +Function views + 1. Add an import: from my_app import views + 2. Add a URL to urlpatterns: path('', views.home, name='home') +Class-based views + 1. Add an import: from other_app.views import Home + 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') +Including another URLconf + 1. Import the include() function: from django.urls import include, path + 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) +""" +from django.contrib import admin +from django.urls import path + +urlpatterns = [ + path('admin/', admin.site.urls), +] diff --git a/config/urls/dev.py b/config/urls/dev.py new file mode 100644 index 0000000..c3ce1ef --- /dev/null +++ b/config/urls/dev.py @@ -0,0 +1,3 @@ +from config.urls.base import * + +urlpatterns += [] diff --git a/config/wsgi.py b/config/wsgi.py new file mode 100644 index 0000000..d626c2f --- /dev/null +++ b/config/wsgi.py @@ -0,0 +1,16 @@ +""" +WSGI config for CryptoReader project. + +It exposes the WSGI callable as a module-level variable named ``application``. + +For more information on this file, see +https://docs.djangoproject.com/en/4.0/howto/deployment/wsgi/ +""" + +import os + +from django.core.wsgi import get_wsgi_application + +os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings.dev") + +application = get_wsgi_application() diff --git a/cryptoreader/__init__.py b/cryptoreader/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/manage.py b/manage.py new file mode 100755 index 0000000..ff6440c --- /dev/null +++ b/manage.py @@ -0,0 +1,22 @@ +#!/usr/bin/env python +"""Django's command-line utility for administrative tasks.""" +import os +import sys + + +def main(): + """Run administrative tasks.""" + os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings.dev") + try: + from django.core.management import execute_from_command_line + except ImportError as exc: + raise ImportError( + "Couldn't import Django. Are you sure it's installed and " + "available on your PYTHONPATH environment variable? Did you " + "forget to activate a virtual environment?" + ) from exc + execute_from_command_line(sys.argv) + + +if __name__ == "__main__": + main() From d1353864a1d30ff3757f50b268e9dae8244bbbf3 Mon Sep 17 00:00:00 2001 From: bigsbug Date: Sun, 17 Jul 2022 16:14:09 +0430 Subject: [PATCH 03/66] remove SQLite db --- config/db.sqlite3 | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 config/db.sqlite3 diff --git a/config/db.sqlite3 b/config/db.sqlite3 deleted file mode 100644 index e69de29..0000000 From 6dfd846d40a31d20d969381668c8d379a519a8ad Mon Sep 17 00:00:00 2001 From: bigsbug Date: Sun, 17 Jul 2022 19:03:06 +0430 Subject: [PATCH 04/66] create SDK for Kucoin website --- third_party_sdks/__init__.py | 0 third_party_sdks/kucoin.py | 105 +++++++++++++++++++++++++++++++++++ 2 files changed, 105 insertions(+) create mode 100644 third_party_sdks/__init__.py create mode 100644 third_party_sdks/kucoin.py diff --git a/third_party_sdks/__init__.py b/third_party_sdks/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/third_party_sdks/kucoin.py b/third_party_sdks/kucoin.py new file mode 100644 index 0000000..ad7c919 --- /dev/null +++ b/third_party_sdks/kucoin.py @@ -0,0 +1,105 @@ +"""Kucoin SDK""" + +import base64 +import hashlib +import hmac +import time +from typing import Any + +import requests +from requests.compat import urljoin + + +class Kucoin: + key: str + secret: str + passphrase: str + signature: str + + def __init__(self, sand_box=False) -> None: + if sand_box: + self.base_url: str = "https://openapi-sandbox.kucoin.com/" + else: + self.base_url: str = "https://api.kucoin.com/" + + @property + def now_as_mili(self) -> int: + """Get time as miliseconds""" + + return int(time.time() * 1000) + + def set_key(self, key: str) -> None: + self.key = key + + def set_secret(self, secret: str) -> None: + self.secret = secret + + def set_passphrase(self, passphrase: str) -> None: + self.passphrase = base64.b64encode( + hmac.new( + self.secret.encode("utf-8"), + passphrase.encode("utf-8"), + hashlib.sha256, + ).digest() + ) + + def authenticate(self): + ... + + def generate_header(self) -> dict: + """Generating header for request, based on the instance data + + Returns: + dict: headers + """ + + headers: dict = { + "KC-API-KEY": self.key, + "KC-API-SIGN": self.signature, + "KC-API-TIMESTAMP": str(self.now_as_mili), + "KC-API-PASSPHRASE": self.passphrase, + "KC-API-KEY-VERSION": "2", + } + return headers + + def generate_signature(self, method: str, url: str) -> None: + """Generate a new signature based on the method name and URL and time, + to confirm the request from Kucoin + + Args: + method (str) : method name like 'GET' + url (str) : path of the target endpoint like 'api/v1/accounts' + """ + + url = "/" + url.strip("/") # normalize url + + str_to_sign = str(self.now_as_mili) + method.upper() + url + print(str_to_sign) + self.signature = base64.b64encode( + hmac.new( + self.secret.encode("utf-8"), str_to_sign.encode("utf-8"), hashlib.sha256 + ).digest() + ) + + def raw_query( + self, method: str, url: str, parameters: dict = {} + ) -> requests.Response: + """Sending a raw request to the endpoint based on the method name and URL path + + Args: + method (str): the method name : 'GET', 'POST' ... + url (str): the path of endpoint : 'api/v1/accounts' ... + parameters (dict, optional): sending some parameters beside the URL . Defaults to {}. + + Returns: + Respoinse + """ + self.generate_signature(method, url) + + full_url = urljoin(self.base_url, url) + print(full_url) + headers = self.generate_header() + reqeust = requests.request(method, full_url, headers=headers, params=parameters) + return reqeust + + From c454d368b8974dcc68600531fd2b56b1b9543840 Mon Sep 17 00:00:00 2001 From: bigsbug Date: Sun, 17 Jul 2022 19:06:42 +0430 Subject: [PATCH 05/66] remove unless imports --- third_party_sdks/kucoin.py | 1 - 1 file changed, 1 deletion(-) diff --git a/third_party_sdks/kucoin.py b/third_party_sdks/kucoin.py index ad7c919..0100ece 100644 --- a/third_party_sdks/kucoin.py +++ b/third_party_sdks/kucoin.py @@ -4,7 +4,6 @@ import hashlib import hmac import time -from typing import Any import requests from requests.compat import urljoin From 47b9675d59b0dfaf2fff053666302ad6b2f4ec6d Mon Sep 17 00:00:00 2001 From: bigsbug Date: Sun, 17 Jul 2022 19:07:01 +0430 Subject: [PATCH 06/66] fix typo --- third_party_sdks/kucoin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/third_party_sdks/kucoin.py b/third_party_sdks/kucoin.py index 0100ece..9a59b68 100644 --- a/third_party_sdks/kucoin.py +++ b/third_party_sdks/kucoin.py @@ -67,7 +67,7 @@ def generate_signature(self, method: str, url: str) -> None: Args: method (str) : method name like 'GET' - url (str) : path of the target endpoint like 'api/v1/accounts' + url (str) : path of the target endpoint like '/api/v1/accounts' """ url = "/" + url.strip("/") # normalize url From a0ad163b64ea37c1262575b9467c0bbdebd64891 Mon Sep 17 00:00:00 2001 From: bigsbug Date: Sun, 17 Jul 2022 19:07:18 +0430 Subject: [PATCH 07/66] remove logs --- third_party_sdks/kucoin.py | 1 - 1 file changed, 1 deletion(-) diff --git a/third_party_sdks/kucoin.py b/third_party_sdks/kucoin.py index 9a59b68..d217670 100644 --- a/third_party_sdks/kucoin.py +++ b/third_party_sdks/kucoin.py @@ -73,7 +73,6 @@ def generate_signature(self, method: str, url: str) -> None: url = "/" + url.strip("/") # normalize url str_to_sign = str(self.now_as_mili) + method.upper() + url - print(str_to_sign) self.signature = base64.b64encode( hmac.new( self.secret.encode("utf-8"), str_to_sign.encode("utf-8"), hashlib.sha256 From 47319b05263d4f09300e06169c4b98981d2bcdfc Mon Sep 17 00:00:00 2001 From: bigsbug Date: Mon, 18 Jul 2022 00:11:58 +0430 Subject: [PATCH 08/66] add new arg to raw_query --- third_party_sdks/kucoin.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/third_party_sdks/kucoin.py b/third_party_sdks/kucoin.py index d217670..be94c0b 100644 --- a/third_party_sdks/kucoin.py +++ b/third_party_sdks/kucoin.py @@ -80,13 +80,14 @@ def generate_signature(self, method: str, url: str) -> None: ) def raw_query( - self, method: str, url: str, parameters: dict = {} + self, method: str, url: str, data: dict = {}, parameters: dict = {} ) -> requests.Response: """Sending a raw request to the endpoint based on the method name and URL path Args: method (str): the method name : 'GET', 'POST' ... url (str): the path of endpoint : 'api/v1/accounts' ... + data (dict, optional): sending some pyload beside the URL with Post, Put methods . Defaults to {}. parameters (dict, optional): sending some parameters beside the URL . Defaults to {}. Returns: @@ -95,9 +96,14 @@ def raw_query( self.generate_signature(method, url) full_url = urljoin(self.base_url, url) - print(full_url) headers = self.generate_header() - reqeust = requests.request(method, full_url, headers=headers, params=parameters) + reqeust = requests.request( + method, + full_url, + headers=headers, + data=data, + params=parameters, + ) return reqeust From 629f7abb8c589021141998f8dc4c515f3bc7b159 Mon Sep 17 00:00:00 2001 From: bigsbug Date: Mon, 18 Jul 2022 00:12:49 +0430 Subject: [PATCH 09/66] impove the vars name in the raw_query --- third_party_sdks/kucoin.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/third_party_sdks/kucoin.py b/third_party_sdks/kucoin.py index be94c0b..ce3a93e 100644 --- a/third_party_sdks/kucoin.py +++ b/third_party_sdks/kucoin.py @@ -97,13 +97,13 @@ def raw_query( full_url = urljoin(self.base_url, url) headers = self.generate_header() - reqeust = requests.request( + response = requests.request( method, full_url, headers=headers, data=data, params=parameters, ) - return reqeust + return response From e0ee09ec2b936e6b1aa38b4bd996e1b46222b753 Mon Sep 17 00:00:00 2001 From: bigsbug Date: Mon, 18 Jul 2022 00:22:02 +0430 Subject: [PATCH 10/66] inital new apps --- cryptoreader/__pycache__/__init__.cpython-39.pyc | Bin 0 -> 165 bytes cryptoreader/accounts/__init__.py | 0 cryptoreader/accounts/admin.py | 3 +++ cryptoreader/accounts/apps.py | 6 ++++++ cryptoreader/accounts/migrations/__init__.py | 0 cryptoreader/accounts/models.py | 3 +++ cryptoreader/accounts/tests.py | 3 +++ cryptoreader/accounts/views.py | 3 +++ cryptoreader/api/__init__.py | 0 cryptoreader/api/admin.py | 3 +++ cryptoreader/api/apps.py | 6 ++++++ cryptoreader/api/migrations/__init__.py | 0 cryptoreader/api/models.py | 3 +++ cryptoreader/api/tests.py | 3 +++ cryptoreader/api/views.py | 3 +++ 15 files changed, 36 insertions(+) create mode 100644 cryptoreader/__pycache__/__init__.cpython-39.pyc create mode 100644 cryptoreader/accounts/__init__.py create mode 100644 cryptoreader/accounts/admin.py create mode 100644 cryptoreader/accounts/apps.py create mode 100644 cryptoreader/accounts/migrations/__init__.py create mode 100644 cryptoreader/accounts/models.py create mode 100644 cryptoreader/accounts/tests.py create mode 100644 cryptoreader/accounts/views.py create mode 100644 cryptoreader/api/__init__.py create mode 100644 cryptoreader/api/admin.py create mode 100644 cryptoreader/api/apps.py create mode 100644 cryptoreader/api/migrations/__init__.py create mode 100644 cryptoreader/api/models.py create mode 100644 cryptoreader/api/tests.py create mode 100644 cryptoreader/api/views.py diff --git a/cryptoreader/__pycache__/__init__.cpython-39.pyc b/cryptoreader/__pycache__/__init__.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..948b6769204831706c7102de14658cba4f456890 GIT binary patch literal 165 zcmYe~<>g`kf~rrKlR)%i5P=LBfgA@QE@lA|DGb33nv8xc8Hzx{2;!HQenx(7s(xO6 zS)#s6esXDUYF Date: Mon, 18 Jul 2022 12:11:21 +0430 Subject: [PATCH 11/66] refector raw_query --- third_party_sdks/kucoin.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/third_party_sdks/kucoin.py b/third_party_sdks/kucoin.py index ce3a93e..dde7016 100644 --- a/third_party_sdks/kucoin.py +++ b/third_party_sdks/kucoin.py @@ -80,23 +80,22 @@ def generate_signature(self, method: str, url: str) -> None: ) def raw_query( - self, method: str, url: str, data: dict = {}, parameters: dict = {} + self, method: str, url: str, headers={}, data: dict = {}, parameters: dict = {} ) -> requests.Response: """Sending a raw request to the endpoint based on the method name and URL path Args: method (str): the method name : 'GET', 'POST' ... url (str): the path of endpoint : 'api/v1/accounts' ... + header (dict, optional): headers of reqeust . Defaults to {}. data (dict, optional): sending some pyload beside the URL with Post, Put methods . Defaults to {}. parameters (dict, optional): sending some parameters beside the URL . Defaults to {}. Returns: Respoinse """ - self.generate_signature(method, url) full_url = urljoin(self.base_url, url) - headers = self.generate_header() response = requests.request( method, full_url, @@ -104,6 +103,7 @@ def raw_query( data=data, params=parameters, ) + return response From 225089f6a4470fae1709b5d9096612a4f8c4ae10 Mon Sep 17 00:00:00 2001 From: bigsbug Date: Mon, 18 Jul 2022 12:13:08 +0430 Subject: [PATCH 12/66] change privacy of generate_signature and add new parameters --- third_party_sdks/kucoin.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/third_party_sdks/kucoin.py b/third_party_sdks/kucoin.py index dde7016..a8ac86a 100644 --- a/third_party_sdks/kucoin.py +++ b/third_party_sdks/kucoin.py @@ -45,7 +45,7 @@ def set_passphrase(self, passphrase: str) -> None: def authenticate(self): ... - def generate_header(self) -> dict: + def _generate_header(self) -> dict: """Generating header for request, based on the instance data Returns: @@ -58,21 +58,24 @@ def generate_header(self) -> dict: "KC-API-TIMESTAMP": str(self.now_as_mili), "KC-API-PASSPHRASE": self.passphrase, "KC-API-KEY-VERSION": "2", + "Content-Type": "application/json", } return headers - def generate_signature(self, method: str, url: str) -> None: + def _generate_signature(self, method: str, url: str, body="") -> None: """Generate a new signature based on the method name and URL and time, - to confirm the request from Kucoin + to confirm the request from Kucoin, + [RUN BEFORE generate_header MEHTOD] Args: method (str) : method name like 'GET' url (str) : path of the target endpoint like '/api/v1/accounts' + body (str) : body of request """ url = "/" + url.strip("/") # normalize url + str_to_sign = str(self.now_as_mili) + method.upper() + url + body - str_to_sign = str(self.now_as_mili) + method.upper() + url self.signature = base64.b64encode( hmac.new( self.secret.encode("utf-8"), str_to_sign.encode("utf-8"), hashlib.sha256 From 8e6d39bffb122bc944d08ccb6b8fcf0e649d6dac Mon Sep 17 00:00:00 2001 From: bigsbug Date: Mon, 18 Jul 2022 12:16:05 +0430 Subject: [PATCH 13/66] add new mehod to convert dict to json --- third_party_sdks/kucoin.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/third_party_sdks/kucoin.py b/third_party_sdks/kucoin.py index a8ac86a..4a8f217 100644 --- a/third_party_sdks/kucoin.py +++ b/third_party_sdks/kucoin.py @@ -4,6 +4,7 @@ import hashlib import hmac import time +import json import requests from requests.compat import urljoin @@ -42,8 +43,8 @@ def set_passphrase(self, passphrase: str) -> None: ).digest() ) - def authenticate(self): - ... + def dict_to_json(self, data) -> str: + return json.dumps(data) def _generate_header(self) -> dict: """Generating header for request, based on the instance data From bd8317a82de995b8202c94f24aba8e8605b40c9d Mon Sep 17 00:00:00 2001 From: bigsbug Date: Mon, 18 Jul 2022 12:16:57 +0430 Subject: [PATCH 14/66] new methods based on raw_query --- third_party_sdks/kucoin.py | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/third_party_sdks/kucoin.py b/third_party_sdks/kucoin.py index 4a8f217..4cd91b8 100644 --- a/third_party_sdks/kucoin.py +++ b/third_party_sdks/kucoin.py @@ -7,7 +7,7 @@ import json import requests -from requests.compat import urljoin +from urllib.parse import urlencode, urljoin class Kucoin: @@ -83,6 +83,34 @@ def _generate_signature(self, method: str, url: str, body="") -> None: ).digest() ) + def post_query(self, url: str, data: dict) -> requests.Response: + method = "POST" + data = self.dict_to_json(data) + + self._generate_signature(method, url, data) + headers = self._generate_header() + + return self.raw_query( + method, + url, + headers=headers, + data=data, + ) + + def get_query(self, url: str, parameters: dict) -> requests.Response: + method = "GET" + parameters = urlencode(parameters) + + # parameters should have the character "?" at the beginning of themself + self._generate_signature(method, url, "?" + parameters) + headers = self._generate_header() + return self.raw_query( + method, + url, + headers=headers, + parameters=parameters, + ) + def raw_query( self, method: str, url: str, headers={}, data: dict = {}, parameters: dict = {} ) -> requests.Response: From b92d34ff4c6207b497a0df4c1021b85ded1dc242 Mon Sep 17 00:00:00 2001 From: bigsbug Date: Mon, 18 Jul 2022 13:13:47 +0430 Subject: [PATCH 15/66] add default value to args of post_query --- third_party_sdks/kucoin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/third_party_sdks/kucoin.py b/third_party_sdks/kucoin.py index 4cd91b8..3c590ff 100644 --- a/third_party_sdks/kucoin.py +++ b/third_party_sdks/kucoin.py @@ -83,7 +83,7 @@ def _generate_signature(self, method: str, url: str, body="") -> None: ).digest() ) - def post_query(self, url: str, data: dict) -> requests.Response: + def post_query(self, url: str, data: dict = {}) -> requests.Response: method = "POST" data = self.dict_to_json(data) From bbfa66d3b7b0370f592b95b0914a746fa7b5bd15 Mon Sep 17 00:00:00 2001 From: bigsbug Date: Mon, 18 Jul 2022 13:17:20 +0430 Subject: [PATCH 16/66] add default value to args of get_query and parse paramters --- third_party_sdks/kucoin.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/third_party_sdks/kucoin.py b/third_party_sdks/kucoin.py index 3c590ff..3091719 100644 --- a/third_party_sdks/kucoin.py +++ b/third_party_sdks/kucoin.py @@ -97,12 +97,16 @@ def post_query(self, url: str, data: dict = {}) -> requests.Response: data=data, ) - def get_query(self, url: str, parameters: dict) -> requests.Response: + def get_query(self, url: str, parameters: dict = {}) -> requests.Response: method = "GET" - parameters = urlencode(parameters) - + if parameters != {}: # parameters should have the character "?" at the beginning of themself - self._generate_signature(method, url, "?" + parameters) + data = "?" + urlencode(parameters) + else: + data = "" + + self._generate_signature(method, url, data) + headers = self._generate_header() return self.raw_query( method, From 7a3b1f40891fb9b2be8d2173a71350bb40705075 Mon Sep 17 00:00:00 2001 From: bigsbug Date: Mon, 18 Jul 2022 19:50:00 +0430 Subject: [PATCH 17/66] interface of SDKs --- third_party_sdks/sdk.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 third_party_sdks/sdk.py diff --git a/third_party_sdks/sdk.py b/third_party_sdks/sdk.py new file mode 100644 index 0000000..d08a2d9 --- /dev/null +++ b/third_party_sdks/sdk.py @@ -0,0 +1,19 @@ +from abc import ABC, abstractclassmethod + + +class SDK(ABC): + @abstractclassmethod + def get_query(): + ... + + @abstractclassmethod + def post_query(): + ... + + @abstractclassmethod + def new_position(self, **kwargs): + ... + + @abstractclassmethod + def get_position(self): + ... From a62b4612945f8a96df275d4555b7538158f356be Mon Sep 17 00:00:00 2001 From: bigsbug Date: Mon, 18 Jul 2022 19:51:05 +0430 Subject: [PATCH 18/66] add new imports and sorts them --- third_party_sdks/kucoin.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/third_party_sdks/kucoin.py b/third_party_sdks/kucoin.py index 3091719..ea0c7e4 100644 --- a/third_party_sdks/kucoin.py +++ b/third_party_sdks/kucoin.py @@ -3,14 +3,17 @@ import base64 import hashlib import hmac -import time import json +import time +from urllib import response +from urllib.parse import urlencode, urljoin +from uuid import uuid4 import requests -from urllib.parse import urlencode, urljoin + +from sdk import SDK -class Kucoin: key: str secret: str passphrase: str From 5868637752902baaa11d2a19d8107979c689543a Mon Sep 17 00:00:00 2001 From: bigsbug Date: Mon, 18 Jul 2022 19:52:01 +0430 Subject: [PATCH 19/66] implements the abstracts --- third_party_sdks/kucoin.py | 57 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/third_party_sdks/kucoin.py b/third_party_sdks/kucoin.py index ea0c7e4..d777cd8 100644 --- a/third_party_sdks/kucoin.py +++ b/third_party_sdks/kucoin.py @@ -14,6 +14,7 @@ from sdk import SDK +class Kucoin(SDK): key: str secret: str passphrase: str @@ -145,4 +146,60 @@ def raw_query( return response + def get_position(self, positionID=None): + path = "/api/v1/orders" + + if positionID: + path = path + "/" + positionID + print(path) + response = self.get_query(path) + result = response.json() + + else: + response = self.get_query(path) + result = response.json()["data"]["items"] + + return result + + def new_position(self, **kwargs): + """apply for new positions + + Args : + side (str) : buy or sell + symbol (str) : a valid trading symbol code. e.g. ETH-BTC + clientOid (str) [Optional] : Unique order id created by users to identify their orders, e.g. UUID. + type (str) [Optional] : limit or market (default is limit) + remark (str) [Optional] : remark for the order, length cannot exceed 100 utf8 characters + stp (str) [Optional]: self trade prevention , CN, CO, CB or DC + tradeType (str) [Optional] : The type of trading : TRADE(Spot Trade) Default is TRADE. + + Addistional args based on type : + type : market + size (str) [Optional] : Desired amount in base currency + funds (str) [Optional] : The desired amount of quote currency to use + + type : limit + price (str) : price per base currency + size (str) : amount of base currency to buy or sell + timeInForce (str) [Optional] : GTC, GTT, IOC, or FOK (default is GTC), read Time In Force. + cancelAfter (int) [Optional] : cancel after n seconds, requires timeInForce to be GTT + postOnly (bool) [Optional] : Post only flag, invalid when timeInForce is IOC or FOK + hidden (bool) [Optional] : Order will not be displayed in the order book + iceberg (bool) [Optional] : Only aportion of the order is displayed in the order book + visibleSize (str [Optional] : The maximum visible size of an iceberg order + Raises: + ValueError: pass invalid args or not passed requierd data + """ + + if not kwargs: + raise ValueError( + "See this link for more info : https://docs.kucoin.com/#place-a-new-order" + ) + + kwargs.setdefault("clientOid", str(uuid4())) + path = "/api/v1/orders" + response = self.post_query(path, **kwargs) + print(response.json()) + + From e2e83ae6bb1e22d44fd57eca9a96932c3de11a67 Mon Sep 17 00:00:00 2001 From: bigsbug Date: Mon, 18 Jul 2022 19:59:16 +0430 Subject: [PATCH 20/66] document the SDK --- third_party_sdks/kucoin.py | 45 +++++++++++++++++++++++++++++++++----- 1 file changed, 40 insertions(+), 5 deletions(-) diff --git a/third_party_sdks/kucoin.py b/third_party_sdks/kucoin.py index d777cd8..018b229 100644 --- a/third_party_sdks/kucoin.py +++ b/third_party_sdks/kucoin.py @@ -87,7 +87,17 @@ def _generate_signature(self, method: str, url: str, body="") -> None: ).digest() ) - def post_query(self, url: str, data: dict = {}) -> requests.Response: + def post_query(self, url: str, **data: dict) -> requests.Response: + """Send POST query to the API + + Args: + url (str): path of endpoint + data (dict, optional): body of request. Defaults to {}. + + Returns: + requests.Response: result of query + """ + method = "POST" data = self.dict_to_json(data) @@ -101,7 +111,17 @@ def post_query(self, url: str, data: dict = {}) -> requests.Response: data=data, ) - def get_query(self, url: str, parameters: dict = {}) -> requests.Response: + def get_query(self, url: str, **parameters: dict) -> requests.Response: + """Send GET Query to the API + + Args: + url (str): path of endpoint + parameters (dict, optional): parameters of url. Defaults to {}. + + Returns: + requests.Response: result of query + """ + method = "GET" if parameters != {}: # parameters should have the character "?" at the beginning of themself @@ -120,7 +140,12 @@ def get_query(self, url: str, parameters: dict = {}) -> requests.Response: ) def raw_query( - self, method: str, url: str, headers={}, data: dict = {}, parameters: dict = {} + self, + method: str, + url: str, + headers: dict = {}, + data: dict = {}, + parameters: dict = {}, ) -> requests.Response: """Sending a raw request to the endpoint based on the method name and URL path @@ -146,7 +171,17 @@ def raw_query( return response - def get_position(self, positionID=None): + def get_position(self, positionID: str = None): + """get all positions or get a specified position with the id of that + + Args: + positionID (str, optional): ID of order. Defaults to None. + + Returns: + list : a list of positions + dict : details of posistion + """ + path = "/api/v1/orders" if positionID: @@ -161,7 +196,7 @@ def get_position(self, positionID=None): return result - def new_position(self, **kwargs): + def new_position(self, **kwargs: dict): """apply for new positions Args : From 42e071149363af3aa9c4de19ae33640035fc34fc Mon Sep 17 00:00:00 2001 From: bigsbug Date: Mon, 18 Jul 2022 19:59:35 +0430 Subject: [PATCH 21/66] nomalize the url --- third_party_sdks/kucoin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/third_party_sdks/kucoin.py b/third_party_sdks/kucoin.py index 018b229..1e9419b 100644 --- a/third_party_sdks/kucoin.py +++ b/third_party_sdks/kucoin.py @@ -78,7 +78,7 @@ def _generate_signature(self, method: str, url: str, body="") -> None: body (str) : body of request """ - url = "/" + url.strip("/") # normalize url + url = "/" + url.strip().strip("/") # normalize url str_to_sign = str(self.now_as_mili) + method.upper() + url + body self.signature = base64.b64encode( From 1dfe84a5141d7ebb4dbe0ef02a6774df18decc74 Mon Sep 17 00:00:00 2001 From: bigsbug Date: Mon, 18 Jul 2022 23:04:44 +0430 Subject: [PATCH 22/66] register apps --- config/settings/dev.py | 2 ++ cryptoreader/accounts/apps.py | 4 ++-- cryptoreader/api/apps.py | 4 ++-- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/config/settings/dev.py b/config/settings/dev.py index 4a79e5f..fb75f5e 100644 --- a/config/settings/dev.py +++ b/config/settings/dev.py @@ -3,6 +3,8 @@ INSTALLED_APPS += [ "rest_framework", + "cryptoreader.accounts.apps.AccountsConfig", + "cryptoreader.api.apps.ApiConfig", ] ROOT_URLCONF = "config.urls.dev" diff --git a/cryptoreader/accounts/apps.py b/cryptoreader/accounts/apps.py index 3e3c765..7831b5a 100644 --- a/cryptoreader/accounts/apps.py +++ b/cryptoreader/accounts/apps.py @@ -2,5 +2,5 @@ class AccountsConfig(AppConfig): - default_auto_field = 'django.db.models.BigAutoField' - name = 'accounts' + default_auto_field = "django.db.models.BigAutoField" + name = "cryptoreader.accounts" diff --git a/cryptoreader/api/apps.py b/cryptoreader/api/apps.py index 66656fd..2fdd32a 100644 --- a/cryptoreader/api/apps.py +++ b/cryptoreader/api/apps.py @@ -2,5 +2,5 @@ class ApiConfig(AppConfig): - default_auto_field = 'django.db.models.BigAutoField' - name = 'api' + default_auto_field = "django.db.models.BigAutoField" + name = "cryptoreader.api" From 48714fed82195f7e8611466e362945de299c3192 Mon Sep 17 00:00:00 2001 From: bigsbug Date: Mon, 18 Jul 2022 23:07:25 +0430 Subject: [PATCH 23/66] add tools encryptor and decryptor data --- config/settings/dev.py | 4 ++++ cryptoreader/accounts/tools.py | 26 ++++++++++++++++++++++++++ 2 files changed, 30 insertions(+) create mode 100644 cryptoreader/accounts/tools.py diff --git a/config/settings/dev.py b/config/settings/dev.py index fb75f5e..bf3d658 100644 --- a/config/settings/dev.py +++ b/config/settings/dev.py @@ -7,4 +7,8 @@ "cryptoreader.api.apps.ApiConfig", ] +# encrypt key for encrypting and decrypting data +# generated with cryptography.fernet.Fernet.generate_key() +ENCRYPT_KEY = b"f7DwTK5K6EwmA30THAxnXgBKy_v969ItANlVGhi4C-0=" + ROOT_URLCONF = "config.urls.dev" diff --git a/cryptoreader/accounts/tools.py b/cryptoreader/accounts/tools.py new file mode 100644 index 0000000..5b91188 --- /dev/null +++ b/cryptoreader/accounts/tools.py @@ -0,0 +1,26 @@ +import base64 + +from cryptography.fernet import Fernet + +from django.conf import settings + + +def encrypt(pas): + try: + pas = str(pas) + cipher_pass = Fernet(settings.SECRET_KEY) + encrypt_pass = cipher_pass.encrypt(pas.encode("ascii")) + encrypt_pass = base64.urlsafe_b64encode(encrypt_pass).decode("ascii") + return encrypt_pass + except Exception as e: + return None + + +def decrypt(pas): + try: + pas = base64.urlsafe_b64decode(pas) + cipher_pass = Fernet(settings.SECRET_KEY) + decod_pass = cipher_pass.decrypt(pas).decode("ascii") + return decod_pass + except Exception as e: + return None From a3b8acb41bf8e9e6c1c2d76f55667b1212fea40f Mon Sep 17 00:00:00 2001 From: bigsbug Date: Tue, 19 Jul 2022 01:02:38 +0430 Subject: [PATCH 24/66] add __init__ file to root --- __init__.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 __init__.py diff --git a/__init__.py b/__init__.py new file mode 100644 index 0000000..e69de29 From fb6800f948f29eba61d87ff6ccad0fcd16be3178 Mon Sep 17 00:00:00 2001 From: bigsbug Date: Tue, 19 Jul 2022 01:03:26 +0430 Subject: [PATCH 25/66] fix key of the encryption --- cryptoreader/accounts/tools.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/cryptoreader/accounts/tools.py b/cryptoreader/accounts/tools.py index 5b91188..8a2092b 100644 --- a/cryptoreader/accounts/tools.py +++ b/cryptoreader/accounts/tools.py @@ -8,19 +8,21 @@ def encrypt(pas): try: pas = str(pas) - cipher_pass = Fernet(settings.SECRET_KEY) + cipher_pass = Fernet(settings.ENCRYPT_KEY) encrypt_pass = cipher_pass.encrypt(pas.encode("ascii")) encrypt_pass = base64.urlsafe_b64encode(encrypt_pass).decode("ascii") return encrypt_pass except Exception as e: + print(e) return None def decrypt(pas): try: pas = base64.urlsafe_b64decode(pas) - cipher_pass = Fernet(settings.SECRET_KEY) + cipher_pass = Fernet(settings.ENCRYPT_KEY) decod_pass = cipher_pass.decrypt(pas).decode("ascii") return decod_pass except Exception as e: + print(e) return None From fa0f538d3e13dabcf88fdb0c95261daecde6c571 Mon Sep 17 00:00:00 2001 From: bigsbug Date: Tue, 19 Jul 2022 01:03:50 +0430 Subject: [PATCH 26/66] models of accounts --- cryptoreader/accounts/models.py | 54 ++++++++++++++++++++++++++++++++- 1 file changed, 53 insertions(+), 1 deletion(-) diff --git a/cryptoreader/accounts/models.py b/cryptoreader/accounts/models.py index 71a8362..5fc1c1e 100644 --- a/cryptoreader/accounts/models.py +++ b/cryptoreader/accounts/models.py @@ -1,3 +1,55 @@ from django.db import models +from django.contrib.auth import get_user_model +from django.core.exceptions import FieldError +from . import tools -# Create your models here. +User = get_user_model() + + +class KucoinAccountManager(models.Manager): + def create(self, **kwargs): + """encrtept data""" + are_args_complete = all( + [ + kwargs.setdefault("key", False), + kwargs.setdefault("secret", False), + kwargs.setdefault("passphrase", False), + ], + ) + + if not are_args_complete: + raise FieldError("pass all args [ key, secret, passphrase ]") + + kwargs["key"] = tools.encrypt(kwargs["key"]) + kwargs["secret"] = tools.encrypt(kwargs["secret"]) + kwargs["passphrase"] = tools.encrypt(kwargs["passphrase"]) + + return super().create(**kwargs) + + +class KucoinAccount(models.Model): + key = models.CharField(max_length=248) + secret = models.CharField(max_length=248) + passphrase = models.CharField(max_length=558) + + # change the default model manager + objects = KucoinAccountManager() + _objects = models.Manager() + + def set_key(self, key): + self.key = tools.encrypt(key) + + def get_key(self): + return tools.decrypt(self.key) + + def set_secret(self, secret): + self.secret = tools.encrypt(secret) + + def get_secret(self): + return tools.decrypt(self.secret) + + def set_passphrase(self, passphrase): + self.passphrase = tools.encrypt(passphrase) + + def get_passphrase(self): + return tools.decrypt(self.passphrase) From 878a0eb3ba8e687c71fe5425d7b4691a25dfb210 Mon Sep 17 00:00:00 2001 From: bigsbug Date: Tue, 19 Jul 2022 01:07:50 +0430 Subject: [PATCH 27/66] add releation to User model --- cryptoreader/accounts/models.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/cryptoreader/accounts/models.py b/cryptoreader/accounts/models.py index 5fc1c1e..78ec55d 100644 --- a/cryptoreader/accounts/models.py +++ b/cryptoreader/accounts/models.py @@ -28,6 +28,11 @@ def create(self, **kwargs): class KucoinAccount(models.Model): + user = models.OneToOneField( + User, + models.CASCADE, + related_name="kuicon_account", + ) key = models.CharField(max_length=248) secret = models.CharField(max_length=248) passphrase = models.CharField(max_length=558) From bcbf784074137803d53b65b7090cf8925323af82 Mon Sep 17 00:00:00 2001 From: bigsbug Date: Wed, 20 Jul 2022 13:41:32 +0430 Subject: [PATCH 28/66] create custom user and register it --- config/settings/base.py | 2 +- cryptoreader/accounts/models.py | 19 ++++++++----------- 2 files changed, 9 insertions(+), 12 deletions(-) diff --git a/config/settings/base.py b/config/settings/base.py index 7078b09..50244ce 100644 --- a/config/settings/base.py +++ b/config/settings/base.py @@ -27,7 +27,7 @@ ALLOWED_HOSTS = [] - +AUTH_USER_MODEL = "accounts.User" # Application definition INSTALLED_APPS = [ diff --git a/cryptoreader/accounts/models.py b/cryptoreader/accounts/models.py index 78ec55d..94a6582 100644 --- a/cryptoreader/accounts/models.py +++ b/cryptoreader/accounts/models.py @@ -2,11 +2,10 @@ from django.contrib.auth import get_user_model from django.core.exceptions import FieldError from . import tools +from django.contrib.auth.models import AbstractUser, UserManager -User = get_user_model() - -class KucoinAccountManager(models.Manager): +class CustomUserManager(UserManager): def create(self, **kwargs): """encrtept data""" are_args_complete = all( @@ -27,19 +26,17 @@ def create(self, **kwargs): return super().create(**kwargs) -class KucoinAccount(models.Model): - user = models.OneToOneField( - User, - models.CASCADE, - related_name="kuicon_account", - ) +class User(AbstractUser): key = models.CharField(max_length=248) secret = models.CharField(max_length=248) passphrase = models.CharField(max_length=558) # change the default model manager - objects = KucoinAccountManager() - _objects = models.Manager() + objects = CustomUserManager() + _objects = UserManager() + + def __str__(self) -> str: + return str(self.username) def set_key(self, key): self.key = tools.encrypt(key) From 2f4beb9d5e9530bcb705287835678bd9581b7d8b Mon Sep 17 00:00:00 2001 From: bigsbug Date: Wed, 20 Jul 2022 19:18:00 +0430 Subject: [PATCH 29/66] add user API --- config/settings/dev.py | 25 +++++++- cryptoreader/accounts/serializers.py | 23 +++++++ cryptoreader/api/urls.py | 22 +++++++ cryptoreader/api/views.py | 96 +++++++++++++++++++++++++++- 4 files changed, 163 insertions(+), 3 deletions(-) create mode 100644 cryptoreader/accounts/serializers.py create mode 100644 cryptoreader/api/urls.py diff --git a/config/settings/dev.py b/config/settings/dev.py index bf3d658..4c9ea84 100644 --- a/config/settings/dev.py +++ b/config/settings/dev.py @@ -3,8 +3,10 @@ INSTALLED_APPS += [ "rest_framework", - "cryptoreader.accounts.apps.AccountsConfig", - "cryptoreader.api.apps.ApiConfig", + "drf_spectacular", + "drf_spectacular_sidecar", + "cryptoreader.accounts", + "cryptoreader.api", ] # encrypt key for encrypting and decrypting data @@ -12,3 +14,22 @@ ENCRYPT_KEY = b"f7DwTK5K6EwmA30THAxnXgBKy_v969ItANlVGhi4C-0=" ROOT_URLCONF = "config.urls.dev" + +REST_FRAMEWORK = { + "DEFAULT_SCHEMA_CLASS": "drf_spectacular.openapi.AutoSchema", + "DEFAULT_FILTER_BACKENDS": ["django_filters.rest_framework.DjangoFilterBackend"], + "DEFAULT_AUTHENTICATION_CLASSES": [ + "rest_framework_simplejwt.authentication.JWTAuthentication", + "rest_framework.authentication.SessionAuthentication", + ], +} + +SPECTACULAR_SETTINGS = { + "TITLE": "Crypto Rader ", + "DESCRIPTION": "Crypot Reader Challnge From Nilva Co", + "VERSION": "1.0.0", + "SERVE_INCLUDE_SCHEMA": False, + "SWAGGER_UI_DIST": "SIDECAR", + "SWAGGER_UI_FAVICON_HREF": "SIDECAR", + "REDOC_DIST": "SIDECAR", +} diff --git a/cryptoreader/accounts/serializers.py b/cryptoreader/accounts/serializers.py new file mode 100644 index 0000000..8b13c06 --- /dev/null +++ b/cryptoreader/accounts/serializers.py @@ -0,0 +1,23 @@ +from .models import User +from django.contrib.auth.hashers import make_password + + +class UserSerializer(serializers.ModelSerializer): + class Meta: + model = User + fields = [ + "username", + "password", + "email", + "first_name", + "last_name", + "key", + "secret", + "passphrase", + ] + + def create(self, validated_data): + validated_data["password"] = make_password(validated_data["password"]) + return super().create(validated_data) + + diff --git a/cryptoreader/api/urls.py b/cryptoreader/api/urls.py new file mode 100644 index 0000000..0afec88 --- /dev/null +++ b/cryptoreader/api/urls.py @@ -0,0 +1,22 @@ +from email.mime import base +from django.urls import path, include +from rest_framework_simplejwt.views import ( + TokenObtainPairView, + TokenRefreshView, +) +from rest_framework import routers +from cryptoreader.api.views import UserViewSet + +router = routers.DefaultRouter() +router.register("kucoinaccount", KucoinAccountViewSet) +router.register("user", UserViewSet, "user") + + +app_name = "API/V1" +urlpatterns = [ + path("", include(router.urls)), + # path("user", UserCreate.as_view(), name="user-create"), + # path("user2", UserDetail.as_view(), name="user-detail"), + path("token/", TokenObtainPairView.as_view(), name="token_obtain_pair"), + path("token/refresh/", TokenRefreshView.as_view(), name="token_refresh"), +] diff --git a/cryptoreader/api/views.py b/cryptoreader/api/views.py index 91ea44a..52016f2 100644 --- a/cryptoreader/api/views.py +++ b/cryptoreader/api/views.py @@ -1,3 +1,97 @@ -from django.shortcuts import render +from cryptoreader.accounts.serializers import ( + User, + UserSerializer, +) +from django.http import Http404 +from drf_spectacular.utils import extend_schema +from rest_framework import permissions, viewsets +from rest_framework.decorators import action +from rest_framework.response import Response +from rest_framework import status + +# Raise an error in the endpoint for invalid data +RAISE_ERROR_IF_INVALID = True + + +class UserViewSet(viewsets.ViewSet): + queryset = User.objects.all() + serializer_class = UserSerializer + + def get_permissions(self): + """ + Instantiates and returns the list of permissions that this view requires. + """ + if self.action == "create": + permission_classes = [permissions.AllowAny] + elif self.action in ["me", "update_me", "destroy_me"]: + permission_classes = [permissions.IsAuthenticated] + else: + permission_classes = [permissions.IsAdminUser] + + return [permission() for permission in permission_classes] + + def get_object(self): + return self.request.user + + @extend_schema( + summary="get current user detail", + parameters=None, + responses={ + 200: KucoinAccountSerializer(many=True), + 404: None, + }, + ) + @action(methods=["GET"], detail=False) + def me(self, request): + user = self.get_object() + serializer = UserSerializer(user) + return Response(serializer.data, status.HTTP_200_OK) + + @extend_schema( + summary="sign-up new user", + parameters=None, + request=UserSerializer, + responses={ + 200: UserSerializer(many=True), + 404: None, + }, + ) + def create(self, request): + serializer = UserSerializer(data=request.data) + + if serializer.is_valid(RAISE_ERROR_IF_INVALID): + serializer.save() + return Response(serializer.data, status.HTTP_201_CREATED) + + @extend_schema( + summary="updating current user", + request=UserSerializer, + responses={ + 200: UserSerializer(), + 404: None, + }, + ) + @me.mapping.put + def update_me(self, request, partial=False): + user = self.get_object() + serializer = UserSerializer(user, request.data, partial=partial) + if serializer.is_valid(RAISE_ERROR_IF_INVALID): + serializer.save() + return Response(serializer.data, status.HTTP_200_OK) + + @extend_schema( + summary="deleting current user", + request=UserSerializer, + responses={ + 404: None, + }, + ) + @me.mapping.delete + def destroy_me(self, request): + user = self.get_object() + user.delete() + return Response(status=status.HTTP_204_NO_CONTENT) + + # Create your views here. From b9afab5471c15a54210a080c477f3dd5219c1b58 Mon Sep 17 00:00:00 2001 From: bigsbug Date: Wed, 20 Jul 2022 19:20:07 +0430 Subject: [PATCH 30/66] add KucoinAccount API and models --- cryptoreader/accounts/models.py | 38 +++++++++++++++ cryptoreader/accounts/serializers.py | 12 ++++- cryptoreader/api/urls.py | 2 +- cryptoreader/api/views.py | 70 +++++++++++++++++++++++++++- 4 files changed, 119 insertions(+), 3 deletions(-) diff --git a/cryptoreader/accounts/models.py b/cryptoreader/accounts/models.py index 94a6582..5d4a105 100644 --- a/cryptoreader/accounts/models.py +++ b/cryptoreader/accounts/models.py @@ -1,3 +1,4 @@ +from uuid import uuid4 from django.db import models from django.contrib.auth import get_user_model from django.core.exceptions import FieldError @@ -55,3 +56,40 @@ def set_passphrase(self, passphrase): def get_passphrase(self): return tools.decrypt(self.passphrase) + + +class KucoinAccount(models.Model): + class TypeAccount(models.TextChoices): + main = "main", "Main" + trade = "trade", "Trade" + margin = "margain", "Margin" + + pk_uuid = models.UUIDField( + primary_key=True, + editable=False, + default=uuid4, + ) + + user = models.ForeignKey( + User, + models.CASCADE, + ) + id = models.CharField(max_length=24) # accountId + type = models.CharField( + max_length=7, + choices=TypeAccount.choices, + default=TypeAccount.main, + ) + currency = models.CharField(max_length=24) # Currency + balance = models.DecimalField( + max_digits=20, decimal_places=10 + ) # Total assets of a currency + available = models.DecimalField( + max_digits=20, decimal_places=10 + ) # Available assets of a currency + holds = models.DecimalField( + max_digits=20, decimal_places=10 + ) # Hold assets of a currency + + def __str__(self) -> str: + return str(self.kucoin_api) diff --git a/cryptoreader/accounts/serializers.py b/cryptoreader/accounts/serializers.py index 8b13c06..0571071 100644 --- a/cryptoreader/accounts/serializers.py +++ b/cryptoreader/accounts/serializers.py @@ -1,4 +1,5 @@ -from .models import User +from rest_framework import serializers +from .models import User, KucoinAccount from django.contrib.auth.hashers import make_password @@ -21,3 +22,12 @@ def create(self, validated_data): return super().create(validated_data) +class KucoinAccountSerializer(serializers.ModelSerializer): + user = serializers.HiddenField(default=serializers.CurrentUserDefault()) + + class Meta: + model = KucoinAccount + exclude = ["pk_uuid"] + + def validate(self, attrs): + return super().validate(attrs) diff --git a/cryptoreader/api/urls.py b/cryptoreader/api/urls.py index 0afec88..b0e94b5 100644 --- a/cryptoreader/api/urls.py +++ b/cryptoreader/api/urls.py @@ -5,7 +5,7 @@ TokenRefreshView, ) from rest_framework import routers -from cryptoreader.api.views import UserViewSet +from cryptoreader.api.views import KucoinAccountViewSet, UserViewSet router = routers.DefaultRouter() router.register("kucoinaccount", KucoinAccountViewSet) diff --git a/cryptoreader/api/views.py b/cryptoreader/api/views.py index 52016f2..4ff0cea 100644 --- a/cryptoreader/api/views.py +++ b/cryptoreader/api/views.py @@ -1,4 +1,6 @@ from cryptoreader.accounts.serializers import ( + KucoinAccount, + KucoinAccountSerializer, User, UserSerializer, ) @@ -93,5 +95,71 @@ def destroy_me(self, request): return Response(status=status.HTTP_204_NO_CONTENT) +class KucoinAccountViewSet(viewsets.ViewSet): + queryset = KucoinAccount.objects.all() + serializer_class = KucoinAccountSerializer + lookup_field = "id" -# Create your views here. + def get_permissions(self): + + if self.action == "create": + permission_classes = [permissions.AllowAny] + elif self.action in ["partial_update", "update", "destroy"]: + permission_classes = [permissions.IsAdminUser] + else: + permission_classes = [permissions.IsAdminUser] + return [permission() for permission in permission_classes] + + def get_serializer_context(self): + context = super().get_serializer_context() + context.update({"request": self.request}) + return context + + def get_object(self, id): + account = KucoinAccount.objects.filter( + kuicon_api__user=self.request.user, id=id + ).last() + if account: + return account + raise Http404 + + @extend_schema( + summary="create a new account", + request=KucoinAccountSerializer, + responses={ + 200: KucoinAccountSerializer, + 404: None, + }, + ) + def create(self, request, *args, **kwargs): + serializer = KucoinAccountSerializer( + data=request.data, context={"request": request} + ) + if serializer.is_valid(RAISE_ERROR_IF_INVALID): + serializer.save() + return Response(serializer.data, status.HTTP_201_CREATED) + + @extend_schema( + summary="get last account", + responses={ + 200: KucoinAccountSerializer, + 404: None, + }, + ) + def retrieve(self, request, *args, **kwargs): + account = self.get_object(kwargs.setdefault("id", None)) + serializer = KucoinAccountSerializer(account) + return Response(serializer.data, status.HTTP_200_OK) + + @extend_schema( + summary="get all accounts", + responses={ + 200: KucoinAccountSerializer(many=True), + 404: None, + }, + ) + def list(self, request, *args, **kwargs): + user = user = self.request.user + accounts = KucoinAccount.objects.filter(user=user) + serializer = KucoinAccountSerializer(accounts, many=True) + return Response(serializer.data, status.HTTP_200_OK) From 1ec5074ddf25e7a65ad16307bdc67944dc155dee Mon Sep 17 00:00:00 2001 From: bigsbug Date: Wed, 20 Jul 2022 19:20:23 +0430 Subject: [PATCH 31/66] register models --- cryptoreader/accounts/admin.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/cryptoreader/accounts/admin.py b/cryptoreader/accounts/admin.py index 8c38f3f..f1fbd74 100644 --- a/cryptoreader/accounts/admin.py +++ b/cryptoreader/accounts/admin.py @@ -1,3 +1,17 @@ from django.contrib import admin +from .models import KucoinAccount, User # Register your models here. +class KucoinAccountModelAdmin(admin.ModelAdmin): + list_display = [ + "user", + "type", + "currency", + "balance", + "available", + "holds", + ] + + +admin.site.register(KucoinAccount, KucoinAccountModelAdmin) +admin.site.register(User) From 93988cb32f8fe35c6acd8b9e68c79a450482ff21 Mon Sep 17 00:00:00 2001 From: bigsbug Date: Wed, 20 Jul 2022 19:20:52 +0430 Subject: [PATCH 32/66] add swagger to project --- config/urls/dev.py | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/config/urls/dev.py b/config/urls/dev.py index c3ce1ef..5b71d9a 100644 --- a/config/urls/dev.py +++ b/config/urls/dev.py @@ -1,3 +1,27 @@ +from django.urls import include from config.urls.base import * -urlpatterns += [] +from drf_spectacular.views import ( + SpectacularAPIView, + SpectacularRedocView, + SpectacularSwaggerView, +) + + +urlpatterns += [ + path( + "api/v1/", + include(("cryptoreader.api.urls", "API/V1")), + ), + path("api/v1/schema/", SpectacularAPIView.as_view(), name="schema"), + path( + "api/v1/schema/swagger-ui/", + SpectacularSwaggerView.as_view(url_name="schema"), + name="swagger-ui", + ), + path( + "api/v1/schema/redoc/", + SpectacularRedocView.as_view(url_name="schema"), + name="redoc", + ), +] From dab6cfadbade5d3970a1dcf681d07190ce1a688e Mon Sep 17 00:00:00 2001 From: bigsbug Date: Wed, 20 Jul 2022 19:21:10 +0430 Subject: [PATCH 33/66] remvoe logs --- cryptoreader/accounts/tools.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/cryptoreader/accounts/tools.py b/cryptoreader/accounts/tools.py index 8a2092b..91fb88d 100644 --- a/cryptoreader/accounts/tools.py +++ b/cryptoreader/accounts/tools.py @@ -13,7 +13,6 @@ def encrypt(pas): encrypt_pass = base64.urlsafe_b64encode(encrypt_pass).decode("ascii") return encrypt_pass except Exception as e: - print(e) return None @@ -24,5 +23,4 @@ def decrypt(pas): decod_pass = cipher_pass.decrypt(pas).decode("ascii") return decod_pass except Exception as e: - print(e) return None From 1137e25883860368a2ae85506c650a3b6f698e0a Mon Sep 17 00:00:00 2001 From: bigsbug Date: Wed, 20 Jul 2022 19:21:56 +0430 Subject: [PATCH 34/66] improve name --- third_party_sdks/kucoin.py | 18 +++++++++--------- third_party_sdks/sdk.py | 2 +- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/third_party_sdks/kucoin.py b/third_party_sdks/kucoin.py index 1e9419b..57546cf 100644 --- a/third_party_sdks/kucoin.py +++ b/third_party_sdks/kucoin.py @@ -171,28 +171,28 @@ def raw_query( return response - def get_position(self, positionID: str = None): - """get all positions or get a specified position with the id of that + def get_account(self, accountID: str = None): + """get all accounts or get a specified account with the id of that Args: - positionID (str, optional): ID of order. Defaults to None. + accountID (str, optional): ID of account. Defaults is None. Returns: - list : a list of positions - dict : details of posistion + list : a list of accounts + dict : details of account """ - path = "/api/v1/orders" + path = "/api/v1/accounts" - if positionID: - path = path + "/" + positionID + if accountID: + path = path + "/" + accountID print(path) response = self.get_query(path) result = response.json() else: response = self.get_query(path) - result = response.json()["data"]["items"] + result = response.json()["data"] return result diff --git a/third_party_sdks/sdk.py b/third_party_sdks/sdk.py index d08a2d9..462382e 100644 --- a/third_party_sdks/sdk.py +++ b/third_party_sdks/sdk.py @@ -15,5 +15,5 @@ def new_position(self, **kwargs): ... @abstractclassmethod - def get_position(self): + def get_account(self): ... From 0b2cfe41dace57ee5c79b681d32ab6cec8f1cf4d Mon Sep 17 00:00:00 2001 From: bigsbug Date: Wed, 20 Jul 2022 19:22:31 +0430 Subject: [PATCH 35/66] add migrations --- .../accounts/migrations/0001_initial.py | 59 +++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 cryptoreader/accounts/migrations/0001_initial.py diff --git a/cryptoreader/accounts/migrations/0001_initial.py b/cryptoreader/accounts/migrations/0001_initial.py new file mode 100644 index 0000000..2ef8918 --- /dev/null +++ b/cryptoreader/accounts/migrations/0001_initial.py @@ -0,0 +1,59 @@ +# Generated by Django 4.0.6 on 2022-07-20 08:25 + +from django.conf import settings +import django.contrib.auth.validators +from django.db import migrations, models +import django.db.models.deletion +import django.utils.timezone +import uuid + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ('auth', '0012_alter_user_first_name_max_length'), + ] + + operations = [ + migrations.CreateModel( + name='User', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('password', models.CharField(max_length=128, verbose_name='password')), + ('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')), + ('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')), + ('username', models.CharField(error_messages={'unique': 'A user with that username already exists.'}, help_text='Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.', max_length=150, unique=True, validators=[django.contrib.auth.validators.UnicodeUsernameValidator()], verbose_name='username')), + ('first_name', models.CharField(blank=True, max_length=150, verbose_name='first name')), + ('last_name', models.CharField(blank=True, max_length=150, verbose_name='last name')), + ('email', models.EmailField(blank=True, max_length=254, verbose_name='email address')), + ('is_staff', models.BooleanField(default=False, help_text='Designates whether the user can log into this admin site.', verbose_name='staff status')), + ('is_active', models.BooleanField(default=True, help_text='Designates whether this user should be treated as active. Unselect this instead of deleting accounts.', verbose_name='active')), + ('date_joined', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date joined')), + ('key', models.CharField(max_length=248)), + ('secret', models.CharField(max_length=248)), + ('passphrase', models.CharField(max_length=558)), + ('groups', models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set', related_query_name='user', to='auth.group', verbose_name='groups')), + ('user_permissions', models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set', related_query_name='user', to='auth.permission', verbose_name='user permissions')), + ], + options={ + 'verbose_name': 'user', + 'verbose_name_plural': 'users', + 'abstract': False, + }, + ), + migrations.CreateModel( + name='KucoinAccount', + fields=[ + ('pk_uuid', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), + ('id', models.CharField(max_length=24)), + ('type', models.CharField(choices=[('main', 'Main'), ('trade', 'Trade'), ('margain', 'Margin')], default='main', max_length=7)), + ('currency', models.CharField(max_length=24)), + ('balance', models.DecimalField(decimal_places=10, max_digits=20)), + ('available', models.DecimalField(decimal_places=10, max_digits=20)), + ('holds', models.DecimalField(decimal_places=10, max_digits=20)), + ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ], + ), + ] From 985149acb803721f5d56f3bac4262fb0861b0154 Mon Sep 17 00:00:00 2001 From: bigsbug Date: Thu, 21 Jul 2022 12:56:22 +0430 Subject: [PATCH 36/66] active user by default --- cryptoreader/accounts/models.py | 1 + 1 file changed, 1 insertion(+) diff --git a/cryptoreader/accounts/models.py b/cryptoreader/accounts/models.py index 5d4a105..e738d16 100644 --- a/cryptoreader/accounts/models.py +++ b/cryptoreader/accounts/models.py @@ -31,6 +31,7 @@ class User(AbstractUser): key = models.CharField(max_length=248) secret = models.CharField(max_length=248) passphrase = models.CharField(max_length=558) + is_active = models.BooleanField(default=True) # change the default model manager objects = CustomUserManager() From 5e3b947d894691bbeba59302d8614079d65c210f Mon Sep 17 00:00:00 2001 From: bigsbug Date: Thu, 21 Jul 2022 12:57:49 +0430 Subject: [PATCH 37/66] cache endpoints by headers --- cryptoreader/api/views.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/cryptoreader/api/views.py b/cryptoreader/api/views.py index 4ff0cea..644f38b 100644 --- a/cryptoreader/api/views.py +++ b/cryptoreader/api/views.py @@ -11,8 +11,12 @@ from rest_framework.response import Response from rest_framework import status +from django.utils.decorators import method_decorator +from django.views.decorators.cache import cache_page +from django.views.decorators.vary import vary_on_headers # Raise an error in the endpoint for invalid data RAISE_ERROR_IF_INVALID = True +CACHE_TTL_ACCOUNTS = 30 class UserViewSet(viewsets.ViewSet): @@ -44,6 +48,12 @@ def get_object(self): }, ) @action(methods=["GET"], detail=False) + @method_decorator(cache_page(60 * 60 * 24)) + @method_decorator( + vary_on_headers( + "Authorization", + ) + ) def me(self, request): user = self.get_object() serializer = UserSerializer(user) @@ -158,6 +168,12 @@ def retrieve(self, request, *args, **kwargs): 404: None, }, ) + @method_decorator(cache_page(CACHE_TTL_ACCOUNTS)) + @method_decorator( + vary_on_headers( + "Authorization", + ) + ) def list(self, request, *args, **kwargs): user = user = self.request.user accounts = KucoinAccount.objects.filter(user=user) From df7bcd853eb362c6046bef69224ea8210d1964d0 Mon Sep 17 00:00:00 2001 From: bigsbug Date: Thu, 21 Jul 2022 12:58:20 +0430 Subject: [PATCH 38/66] sdk api for get all acccounts --- third_party_sdks/sdk_api.py | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 third_party_sdks/sdk_api.py diff --git a/third_party_sdks/sdk_api.py b/third_party_sdks/sdk_api.py new file mode 100644 index 0000000..95d4384 --- /dev/null +++ b/third_party_sdks/sdk_api.py @@ -0,0 +1,9 @@ +from third_party_sdks.kucoin import Kucoin + + +def fetch_accounts(key, secret, passphrase, id=None) -> dict: + kucoin = Kucoin(True) + kucoin.set_key(key) + kucoin.set_secret(secret) + kucoin.set_passphrase(passphrase) + return kucoin.get_account(id) From 43200df4fb4b1bf9b5cca7fc284913fc28ef8222 Mon Sep 17 00:00:00 2001 From: bigsbug Date: Thu, 21 Jul 2022 12:59:09 +0430 Subject: [PATCH 39/66] a service to update accounts of users: --- kucoin_updater/__init__.py | 0 kucoin_updater/beat.py | 9 +++++ kucoin_updater/init_django.py | 9 +++++ kucoin_updater/tasks.py | 66 +++++++++++++++++++++++++++++++++++ 4 files changed, 84 insertions(+) create mode 100644 kucoin_updater/__init__.py create mode 100644 kucoin_updater/beat.py create mode 100644 kucoin_updater/init_django.py create mode 100644 kucoin_updater/tasks.py diff --git a/kucoin_updater/__init__.py b/kucoin_updater/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/kucoin_updater/beat.py b/kucoin_updater/beat.py new file mode 100644 index 0000000..72786a1 --- /dev/null +++ b/kucoin_updater/beat.py @@ -0,0 +1,9 @@ +from tasks import app, update_all_user_kucoin_account + +REPEAT_EVERY = 30 + + +@app.on_after_configure.connect +def schedule_periodic_tasks(sender, **kwargs): + # Fetch and updating accounts of kucoin for all users every 30 seconds + sender.add_periodic_task(REPEAT_EVERY, update_all_user_kucoin_account.s()) diff --git a/kucoin_updater/init_django.py b/kucoin_updater/init_django.py new file mode 100644 index 0000000..ce5998c --- /dev/null +++ b/kucoin_updater/init_django.py @@ -0,0 +1,9 @@ +import os +import sys + +import django + +parent_directory = os.path.dirname(os.path.dirname(os.path.realpath(__file__))) +sys.path.append(parent_directory) +os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings.dev") +django.setup() diff --git a/kucoin_updater/tasks.py b/kucoin_updater/tasks.py new file mode 100644 index 0000000..0cc93b6 --- /dev/null +++ b/kucoin_updater/tasks.py @@ -0,0 +1,66 @@ +from celery import Celery +from celery.utils.log import get_task_logger + +# setup django env +import init_django + +from cryptoreader.accounts import models, serializers +from django.http import HttpRequest + +from third_party_sdks.sdk_api import fetch_accounts + +app = Celery("tasks", broker="redis://localhost:6379/2") +logger = get_task_logger(__name__) + + +def load_accounts_from_kucoin_by_user(user) -> dict: + login_info = user.get_key(), user.get_secret(), user.get_passphrase() + accounts = fetch_accounts(*login_info) + return accounts + + +@app.task +def update_kucoin_account(username): + """fetch kucoin accounts and insert data + to DB + + Args: + username (str): username of user (Model User) + """ + + try: + user = models.User.objects.get(username=username) + except models.User.DoesNotExist: + logger.debug("user not found") + return + + try: + account = load_accounts_from_kucoin_by_user(user) + except Exception as error: + logger.debug(error) + return + + request = HttpRequest() + request.user = user + context = {"request": request} + serializer = serializers.KucoinAccountSerializer( + data=account, many=True, context=context + ) + + try: + serializer.is_valid(True) + serializer.save() + logger.info(serializer.data) + except Exception as error: + logger.debug(error) + + return None + + +@app.task +def update_all_user_kucoin_account(): + """fetch all avilable users and run a task for each""" + + users = models.User.objects.all() + for user in users: + update_kucoin_account.delay(user.username) From fb6573ced5d32ab0165a313032ad7a655da7519a Mon Sep 17 00:00:00 2001 From: bigsbug Date: Thu, 21 Jul 2022 13:00:02 +0430 Subject: [PATCH 40/66] add new endpoint to feach and show open position --- cryptoreader/api/views.py | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/cryptoreader/api/views.py b/cryptoreader/api/views.py index 644f38b..9c6c0bf 100644 --- a/cryptoreader/api/views.py +++ b/cryptoreader/api/views.py @@ -14,11 +14,19 @@ from django.utils.decorators import method_decorator from django.views.decorators.cache import cache_page from django.views.decorators.vary import vary_on_headers +from third_party_sdks.sdk_api import fetch_accounts + # Raise an error in the endpoint for invalid data RAISE_ERROR_IF_INVALID = True CACHE_TTL_ACCOUNTS = 30 +def load_accounts_from_kucoin_by_user(user: User) -> dict: + login_info = user.get_key(), user.get_secret(), user.get_passphrase() + accounts = fetch_accounts(*login_info) + return accounts + + class UserViewSet(viewsets.ViewSet): queryset = User.objects.all() serializer_class = UserSerializer @@ -179,3 +187,30 @@ def list(self, request, *args, **kwargs): accounts = KucoinAccount.objects.filter(user=user) serializer = KucoinAccountSerializer(accounts, many=True) return Response(serializer.data, status.HTTP_200_OK) + + @extend_schema( + summary="get the open accounts", + responses={ + 200: KucoinAccountSerializer(many=True), + 404: None, + }, + ) + @action(detail=False, methods=("GET",)) + @method_decorator(cache_page(CACHE_TTL_ACCOUNTS)) + @method_decorator( + vary_on_headers( + "Authorization", + ) + ) + def list_current(self, request, *args, **kwargs): + """fetch last avilable accounts form kucoin and instert to db""" + + user = self.request.user + accounts = load_accounts_from_kucoin_by_user(user) + serializer = KucoinAccountSerializer( + data=accounts, many=True, context={"request": request} + ) + + if serializer.is_valid(RAISE_ERROR_IF_INVALID): + serializer.save() + return Response(serializer.data, status.HTTP_200_OK) From ab5c75138767fdd3a5d4f49bfb7bb42f98ba0ada Mon Sep 17 00:00:00 2001 From: bigsbug Date: Thu, 21 Jul 2022 13:00:26 +0430 Subject: [PATCH 41/66] improve endpoints --- cryptoreader/api/views.py | 20 ++++---------------- 1 file changed, 4 insertions(+), 16 deletions(-) diff --git a/cryptoreader/api/views.py b/cryptoreader/api/views.py index 9c6c0bf..3ed95f4 100644 --- a/cryptoreader/api/views.py +++ b/cryptoreader/api/views.py @@ -116,17 +116,7 @@ def destroy_me(self, request): class KucoinAccountViewSet(viewsets.ViewSet): queryset = KucoinAccount.objects.all() serializer_class = KucoinAccountSerializer - lookup_field = "id" - - def get_permissions(self): - - if self.action == "create": - permission_classes = [permissions.AllowAny] - elif self.action in ["partial_update", "update", "destroy"]: - permission_classes = [permissions.IsAdminUser] - else: - permission_classes = [permissions.IsAdminUser] - return [permission() for permission in permission_classes] + permission_classes = (permissions.IsAuthenticated,) def get_serializer_context(self): context = super().get_serializer_context() @@ -134,9 +124,7 @@ def get_serializer_context(self): return context def get_object(self, id): - account = KucoinAccount.objects.filter( - kuicon_api__user=self.request.user, id=id - ).last() + account = KucoinAccount.objects.filter(user=self.request.user, id=id).last() if account: return account raise Http404 @@ -158,7 +146,7 @@ def create(self, request, *args, **kwargs): return Response(serializer.data, status.HTTP_201_CREATED) @extend_schema( - summary="get last account", + summary="get the list of accounts specified with the ID ", responses={ 200: KucoinAccountSerializer, 404: None, @@ -170,7 +158,7 @@ def retrieve(self, request, *args, **kwargs): return Response(serializer.data, status.HTTP_200_OK) @extend_schema( - summary="get all accounts", + summary="get the all available accounts", responses={ 200: KucoinAccountSerializer(many=True), 404: None, From 672f6c74c4d6c2a33893e0e74c0bf24606c36a17 Mon Sep 17 00:00:00 2001 From: bigsbug Date: Thu, 21 Jul 2022 13:00:41 +0430 Subject: [PATCH 42/66] add migrations --- ...lter_user_managers_alter_user_is_active.py | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 cryptoreader/accounts/migrations/0002_alter_user_managers_alter_user_is_active.py diff --git a/cryptoreader/accounts/migrations/0002_alter_user_managers_alter_user_is_active.py b/cryptoreader/accounts/migrations/0002_alter_user_managers_alter_user_is_active.py new file mode 100644 index 0000000..015560e --- /dev/null +++ b/cryptoreader/accounts/migrations/0002_alter_user_managers_alter_user_is_active.py @@ -0,0 +1,27 @@ +# Generated by Django 4.0.6 on 2022-07-21 00:25 + +import cryptoreader.accounts.models +import django.contrib.auth.models +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('accounts', '0001_initial'), + ] + + operations = [ + migrations.AlterModelManagers( + name='user', + managers=[ + ('objects', cryptoreader.accounts.models.CustomUserManager()), + ('_objects', django.contrib.auth.models.UserManager()), + ], + ), + migrations.AlterField( + model_name='user', + name='is_active', + field=models.BooleanField(default=True), + ), + ] From ca6d8a1aecd50c7758cbb0d188a6fbf2577a70bd Mon Sep 17 00:00:00 2001 From: bigsbug Date: Thu, 21 Jul 2022 13:01:06 +0430 Subject: [PATCH 43/66] improve imports --- third_party_sdks/kucoin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/third_party_sdks/kucoin.py b/third_party_sdks/kucoin.py index 57546cf..41dbd3e 100644 --- a/third_party_sdks/kucoin.py +++ b/third_party_sdks/kucoin.py @@ -11,7 +11,7 @@ import requests -from sdk import SDK +from third_party_sdks.sdk import SDK class Kucoin(SDK): From 1f758725e65603bfa8918ffa97ede7744681d55b Mon Sep 17 00:00:00 2001 From: bigsbug Date: Thu, 21 Jul 2022 13:49:11 +0430 Subject: [PATCH 44/66] add requiremtns to project --- requirements/dev.txt | 70 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 requirements/dev.txt diff --git a/requirements/dev.txt b/requirements/dev.txt new file mode 100644 index 0000000..db5c9c1 --- /dev/null +++ b/requirements/dev.txt @@ -0,0 +1,70 @@ +amqp==5.1.1 +anyio==3.6.1 +asgiref==3.5.2 +asttokens==2.0.5 +async-timeout==4.0.2 +attrs==21.4.0 +backcall==0.2.0 +billiard==3.6.4.0 +black==22.6.0 +celery==5.2.7 +certifi==2022.6.15 +cffi==1.15.1 +charset-normalizer==2.1.0 +click==8.1.3 +click-didyoumean==0.3.0 +click-plugins==1.1.1 +click-repl==0.2.0 +cryptography==37.0.4 +decorator==5.1.1 +Deprecated==1.2.13 +Django==4.0.6 +django-filter==22.1 +djangorestframework==3.13.1 +djangorestframework-simplejwt==5.2.0 +drf-spectacular==0.22.1 +drf-spectacular-sidecar==2022.7.1 +environ==1.0 +executing==0.8.3 +gunicorn==20.1.0 +h11==0.13.0 +idna==3.3 +inflection==0.5.1 +ipython==8.4.0 +jedi==0.18.1 +jsonschema==4.7.2 +kombu==5.2.4 +matplotlib-inline==0.1.3 +mypy-extensions==0.4.3 +packaging==21.3 +parso==0.8.3 +pathspec==0.9.0 +pexpect==4.8.0 +pickleshare==0.7.5 +platformdirs==2.5.2 +prompt-toolkit==3.0.30 +ptyprocess==0.7.0 +pure-eval==0.2.2 +pycparser==2.21 +pydantic==1.9.1 +Pygments==2.12.0 +PyJWT==2.4.0 +pyparsing==3.0.9 +pyrsistent==0.18.1 +pytz==2022.1 +PyYAML==6.0 +redis==4.3.4 +requests==2.28.1 +six==1.16.0 +sniffio==1.2.0 +sqlparse==0.4.2 +stack-data==0.3.0 +starlette==0.19.1 +tomli==2.0.1 +traitlets==5.3.0 +typing_extensions==4.3.0 +uritemplate==4.1.1 +urllib3==1.26.10 +vine==5.0.0 +wcwidth==0.2.5 +wrapt==1.14.1 From 841c319dd48ee3fa3a55788c774c336fc5c3ca9e Mon Sep 17 00:00:00 2001 From: bigsbug Date: Thu, 21 Jul 2022 13:51:43 +0430 Subject: [PATCH 45/66] set default db to postgres --- config/settings/dev.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/config/settings/dev.py b/config/settings/dev.py index 4c9ea84..6c1458a 100644 --- a/config/settings/dev.py +++ b/config/settings/dev.py @@ -15,6 +15,16 @@ ROOT_URLCONF = "config.urls.dev" +DATABASES = { + "default": { + "ENGINE": "django.db.backends.postgresql_psycopg2", + "NAME": env("POSTGRES_DB"), + "USER": env("POSTGRES_USER"), + "PASSWORD": env("POSTGRES_PASSWORD"), + "HOST": "postgres", + "PORT": "5432", + } +} REST_FRAMEWORK = { "DEFAULT_SCHEMA_CLASS": "drf_spectacular.openapi.AutoSchema", "DEFAULT_FILTER_BACKENDS": ["django_filters.rest_framework.DjangoFilterBackend"], From 728159ffe78ea4a5ab282a6a7235a6012794fae6 Mon Sep 17 00:00:00 2001 From: bigsbug Date: Thu, 21 Jul 2022 13:52:32 +0430 Subject: [PATCH 46/66] remove authentication session --- config/settings/dev.py | 1 - 1 file changed, 1 deletion(-) diff --git a/config/settings/dev.py b/config/settings/dev.py index 6c1458a..b170f73 100644 --- a/config/settings/dev.py +++ b/config/settings/dev.py @@ -30,7 +30,6 @@ "DEFAULT_FILTER_BACKENDS": ["django_filters.rest_framework.DjangoFilterBackend"], "DEFAULT_AUTHENTICATION_CLASSES": [ "rest_framework_simplejwt.authentication.JWTAuthentication", - "rest_framework.authentication.SessionAuthentication", ], } From f50667e428b2d957f640a2345d6a38b83f4c1b81 Mon Sep 17 00:00:00 2001 From: bigsbug Date: Thu, 21 Jul 2022 13:53:07 +0430 Subject: [PATCH 47/66] remove encrypt key from settings --- config/settings/dev.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/config/settings/dev.py b/config/settings/dev.py index b170f73..af7b5e5 100644 --- a/config/settings/dev.py +++ b/config/settings/dev.py @@ -9,9 +9,6 @@ "cryptoreader.api", ] -# encrypt key for encrypting and decrypting data -# generated with cryptography.fernet.Fernet.generate_key() -ENCRYPT_KEY = b"f7DwTK5K6EwmA30THAxnXgBKy_v969ItANlVGhi4C-0=" ROOT_URLCONF = "config.urls.dev" @@ -30,6 +27,7 @@ "DEFAULT_FILTER_BACKENDS": ["django_filters.rest_framework.DjangoFilterBackend"], "DEFAULT_AUTHENTICATION_CLASSES": [ "rest_framework_simplejwt.authentication.JWTAuthentication", + "rest_framework.authentication.SessionAuthentication", ], } From 35d45108e4bfe09a9dc625cb7fa19acded2f2b16 Mon Sep 17 00:00:00 2001 From: bigsbug Date: Thu, 21 Jul 2022 13:53:28 +0430 Subject: [PATCH 48/66] remove authentication session --- config/settings/dev.py | 1 - 1 file changed, 1 deletion(-) diff --git a/config/settings/dev.py b/config/settings/dev.py index af7b5e5..69e002f 100644 --- a/config/settings/dev.py +++ b/config/settings/dev.py @@ -27,7 +27,6 @@ "DEFAULT_FILTER_BACKENDS": ["django_filters.rest_framework.DjangoFilterBackend"], "DEFAULT_AUTHENTICATION_CLASSES": [ "rest_framework_simplejwt.authentication.JWTAuthentication", - "rest_framework.authentication.SessionAuthentication", ], } From a6b3bcaa1ceb1e5fc65f60b6247063dfeb1b482d Mon Sep 17 00:00:00 2001 From: bigsbug Date: Thu, 21 Jul 2022 13:54:00 +0430 Subject: [PATCH 49/66] load data from envs --- config/settings/dev.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/config/settings/dev.py b/config/settings/dev.py index 69e002f..38fe4a0 100644 --- a/config/settings/dev.py +++ b/config/settings/dev.py @@ -1,5 +1,23 @@ from config.settings.base import * +import environ, os + +env = environ.Env() + +environ.Env.read_env(os.path.join(BASE_DIR / "config/django.dev.env")) + +SECRET_KEY = env("SECRET_KEY") + +# SECURITY WARNING: don't run with debug turned on in production! +DEBUG = env("DEBUG") + +ALLOWED_HOSTS = str(env("ALLOWED_HOSTS")).strip().split() +APPEND_SLASH = env("APPEND_SLASH") + +# encrypt key for encrypting and decrypting data +# generated with cryptography.fernet.Fernet.generate_key() +ENCRYPT_KEY = env("ENCRYPT_KEY") +# ENCRYPT_KEY = b"f7DwTK5K6EwmA30THAxnXgBKy_v969ItANlVGhi4C-0=" INSTALLED_APPS += [ "rest_framework", From 8758a896db132a986f9d0931ecaaeb7a7eb92b79 Mon Sep 17 00:00:00 2001 From: bigsbug Date: Thu, 21 Jul 2022 13:58:13 +0430 Subject: [PATCH 50/66] add envs and confings --- config/django.dev.env | 8 ++++++++ config/gunicorn.dev.env | 7 +++++++ config/nginx.conf | 28 ++++++++++++++++++++++++++++ config/postgres.dev.env | 4 ++++ 4 files changed, 47 insertions(+) create mode 100644 config/django.dev.env create mode 100644 config/gunicorn.dev.env create mode 100644 config/nginx.conf create mode 100644 config/postgres.dev.env diff --git a/config/django.dev.env b/config/django.dev.env new file mode 100644 index 0000000..5aa58fe --- /dev/null +++ b/config/django.dev.env @@ -0,0 +1,8 @@ +# DJANGO CONFIG +REQUIREMENTS_FILE=/src/requirements/dev.txt # This Env Required For Building Dockerfile of Djanog Service +DJANGO_SETTINGS_MODULE=config.settings.dev +SECRET_KEY="i0cwcl!$e54^#0vh6q^)$!+l=_at##a%#0q2&!g$$dau*d2rg8" +ENCRYPT_KEY="f7DwTK5K6EwmA30THAxnXgBKy_v969ItANlVGhi4C-0=" +ALLOWED_HOSTS="* localhost 127.0.0.1" +DEBUG=True # Default : False +APPEND_SLASH=True # Default : True \ No newline at end of file diff --git a/config/gunicorn.dev.env b/config/gunicorn.dev.env new file mode 100644 index 0000000..31c6532 --- /dev/null +++ b/config/gunicorn.dev.env @@ -0,0 +1,7 @@ +# Django & Nginx Service +DJANGO_IP="0.0.0.0" +DJANGO_PORT=8181 + +# gunicorn Service +WORKERS_COUNT=2 +EXTRA_ARGS="" \ No newline at end of file diff --git a/config/nginx.conf b/config/nginx.conf new file mode 100644 index 0000000..92d6ed3 --- /dev/null +++ b/config/nginx.conf @@ -0,0 +1,28 @@ +server { + listen 80; + + location / { + proxy_set_header Host $http_host; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection $connection_upgrade; + proxy_redirect off; + proxy_buffering off; + proxy_pass http://django; + } + + location /static { + # path for static files + alias /statics/; + } + } + + map $http_upgrade $connection_upgrade { + default upgrade; + '' close; + } + + upstream django { + server django:${DJANGO_PORT}; + } \ No newline at end of file diff --git a/config/postgres.dev.env b/config/postgres.dev.env new file mode 100644 index 0000000..6ee04ca --- /dev/null +++ b/config/postgres.dev.env @@ -0,0 +1,4 @@ +# POSTGRES CONFIG +POSTGRES_DB=djangoCryptoReader +POSTGRES_USER=django +POSTGRES_PASSWORD=django-password From faa830b682a77e19510b375ec7e9e2cf4ba8a78f Mon Sep 17 00:00:00 2001 From: bigsbug Date: Thu, 21 Jul 2022 14:00:04 +0430 Subject: [PATCH 51/66] docrizing project --- active_dev.sh | 5 +++++ docker-compose.dev.yaml | 31 +++++++++++++++++++++++++++ docker-compose.yaml | 47 +++++++++++++++++++++++++++++++++++++++++ dockerfile | 14 ++++++++++++ dockerfile.nginx | 3 +++ entrypoint.dev.sh | 10 +++++++++ start-service.dev.sh | 6 ++++++ 7 files changed, 116 insertions(+) create mode 100644 active_dev.sh create mode 100644 docker-compose.dev.yaml create mode 100644 docker-compose.yaml create mode 100644 dockerfile create mode 100644 dockerfile.nginx create mode 100644 entrypoint.dev.sh create mode 100644 start-service.dev.sh diff --git a/active_dev.sh b/active_dev.sh new file mode 100644 index 0000000..602b2a2 --- /dev/null +++ b/active_dev.sh @@ -0,0 +1,5 @@ +set -o allexport +. ./config/gunicron.dev.env +. ./config/django.dev.env +. ./config/postgres.dev.env +set +o allexport \ No newline at end of file diff --git a/docker-compose.dev.yaml b/docker-compose.dev.yaml new file mode 100644 index 0000000..c2853c5 --- /dev/null +++ b/docker-compose.dev.yaml @@ -0,0 +1,31 @@ +services: + + django: + volumes: + - .:/src/:rw + - statics:/src/static:rw + env_file: + - ./config/gunicorn.dev.env + - ./config/django.dev.env + - ./config/postgres.dev.env + entrypoint: [ "bash", "./entrypoint.dev.sh" ] + + postgres: + env_file: + - ./config/postgres.dev.env + volumes: + - db-data-test:/var/lib/postgresql/data:rw + + nginx: + depends_on: + - django + env_file: + - ./config/gunicorn.dev.env + ports: + - 8000:80 + volumes: + - statics:/statics:rw + +volumes: + db-data-test: + statics: \ No newline at end of file diff --git a/docker-compose.yaml b/docker-compose.yaml new file mode 100644 index 0000000..c31fe5e --- /dev/null +++ b/docker-compose.yaml @@ -0,0 +1,47 @@ +version: '3' + +services: + + django: + build: + dockerfile: ./dockerfile + args: + - REQUIREMENTS_FILE=${REQUIREMENTS_FILE} + depends_on: + - redis + - postgres + + + redis: + image: redis:alpine + restart: always + + postgres: + image: postgres:alpine + restart: always + volumes: + - db-data:/var/lib/postgresql/data:rw + + nginx: + build: + dockerfile: ./dockerfile.nginx + restart: always + + celery_beat: + build: + dockerfile: ./dockerfile + args: + - REQUIREMENTS_FILE=${REQUIREMENTS_FILE} + depends_on: + - redis + + celery_worker: + build: + dockerfile: ./dockerfile + args: + - REQUIREMENTS_FILE=${REQUIREMENTS_FILE} + depends_on: + - redis + +volumes: + db-data: \ No newline at end of file diff --git a/dockerfile b/dockerfile new file mode 100644 index 0000000..0b84004 --- /dev/null +++ b/dockerfile @@ -0,0 +1,14 @@ +######################## BASE PYTHON IMAGE ######################## +FROM python:3.9 + +ENV PYTHONDONTWRITEBYTECODE=1 +ENV PYTHONUNBUFFERED=1 + +ARG REQUIREMENTS_FILE + + +WORKDIR /src +COPY . . + +RUN pip install --upgrade pip +RUN pip install --no-cache-dir -r ${REQUIREMENTS_FILE} \ No newline at end of file diff --git a/dockerfile.nginx b/dockerfile.nginx new file mode 100644 index 0000000..3e169af --- /dev/null +++ b/dockerfile.nginx @@ -0,0 +1,3 @@ +FROM nginx:1.21.6-alpine +RUN rm /etc/nginx/conf.d/default.conf +COPY ./config/nginx.conf /etc/nginx/templates/default.conf.template diff --git a/entrypoint.dev.sh b/entrypoint.dev.sh new file mode 100644 index 0000000..6ed6fc5 --- /dev/null +++ b/entrypoint.dev.sh @@ -0,0 +1,10 @@ +echo -e "\n$(tput bold setaf 6)Makemigrations and apply migrates ... " +python3 manage.py makemigrations --noinput +python3 manage.py migrate --noinput + +echo -e "\n$(tput bold setaf 6)Collect static files ..." +python3 manage.py collectstatic --noinput + + +echo -e "\n$(tput bold setaf 6)Run gunicorn development server ... " +gunicorn --reload -b ${DJANGO_IP}:${DJANGO_PORT} --workers ${WORKERS_COUNT} ${EXTRA_ARGS} config.wsgi:application \ No newline at end of file diff --git a/start-service.dev.sh b/start-service.dev.sh new file mode 100644 index 0000000..42cf2e1 --- /dev/null +++ b/start-service.dev.sh @@ -0,0 +1,6 @@ +#!/bin/bash + +#Active all env vars for docker compose files +. ./active_dev.sh + +docker compose -f ./docker-compose.yaml -f docker-compose.dev.yaml up -d \ No newline at end of file From d24dac4d4fb8090e2c283aee4a5a8515d9e7ade0 Mon Sep 17 00:00:00 2001 From: bigsbug Date: Thu, 21 Jul 2022 15:59:30 +0430 Subject: [PATCH 52/66] fix path --- active_dev.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/active_dev.sh b/active_dev.sh index 602b2a2..d0a65e0 100644 --- a/active_dev.sh +++ b/active_dev.sh @@ -1,5 +1,5 @@ set -o allexport -. ./config/gunicron.dev.env +. ./config/gunicorn.dev.env . ./config/django.dev.env . ./config/postgres.dev.env set +o allexport \ No newline at end of file From a9c6a7ae12277751f52217b70b8d4ce61e02da73 Mon Sep 17 00:00:00 2001 From: bigsbug Date: Thu, 21 Jul 2022 15:59:56 +0430 Subject: [PATCH 53/66] run celery worker and beat --- docker-compose.dev.yaml | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/docker-compose.dev.yaml b/docker-compose.dev.yaml index c2853c5..a555d28 100644 --- a/docker-compose.dev.yaml +++ b/docker-compose.dev.yaml @@ -26,6 +26,19 @@ services: volumes: - statics:/statics:rw + celery_worker: + env_file: + - ./config/django.dev.env + - ./config/postgres.dev.env + command: celery -A kucoin_updater.tasks worker --loglevel=info + + + celery_beat: + env_file: + - ./config/django.dev.env + - ./config/postgres.dev.env + command: celery -A kucoin_updater.beat beat --loglevel=info + volumes: db-data-test: statics: \ No newline at end of file From a8b47d9d80222ae310b6aadea4547cdcf2743224 Mon Sep 17 00:00:00 2001 From: bigsbug Date: Thu, 21 Jul 2022 16:03:01 +0430 Subject: [PATCH 54/66] add backend cache redis to django --- config/settings/dev.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/config/settings/dev.py b/config/settings/dev.py index 38fe4a0..e44016b 100644 --- a/config/settings/dev.py +++ b/config/settings/dev.py @@ -40,6 +40,16 @@ "PORT": "5432", } } + +CACHES = { + "default": { + "BACKEND": "django_redis.cache.RedisCache", + "LOCATION": "redis://redis:6379/1", + "OPTIONS": {"CLIENT_CLASS": "django_redis.client.DefaultClient"}, + "KEY_PREFIX": "django_", + } +} + REST_FRAMEWORK = { "DEFAULT_SCHEMA_CLASS": "drf_spectacular.openapi.AutoSchema", "DEFAULT_FILTER_BACKENDS": ["django_filters.rest_framework.DjangoFilterBackend"], From 063d76df119b7ab91e82384403653f16f37a85f5 Mon Sep 17 00:00:00 2001 From: bigsbug Date: Thu, 21 Jul 2022 16:03:32 +0430 Subject: [PATCH 55/66] fix load env path and imports --- config/settings/dev.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/config/settings/dev.py b/config/settings/dev.py index e44016b..847bee4 100644 --- a/config/settings/dev.py +++ b/config/settings/dev.py @@ -1,10 +1,10 @@ from config.settings.base import * -import environ, os +import environ +import os env = environ.Env() - -environ.Env.read_env(os.path.join(BASE_DIR / "config/django.dev.env")) +environ.Env.read_env(os.path.join(BASE_DIR / "django.dev.env")) SECRET_KEY = env("SECRET_KEY") From 6a314234f01a7afe97cd8d900a0cb212818d8121 Mon Sep 17 00:00:00 2001 From: bigsbug Date: Thu, 21 Jul 2022 16:03:59 +0430 Subject: [PATCH 56/66] fix imports and paths --- kucoin_updater/beat.py | 2 +- kucoin_updater/tasks.py | 4 ++-- requirements/dev.txt | 2 ++ 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/kucoin_updater/beat.py b/kucoin_updater/beat.py index 72786a1..5912ba6 100644 --- a/kucoin_updater/beat.py +++ b/kucoin_updater/beat.py @@ -1,4 +1,4 @@ -from tasks import app, update_all_user_kucoin_account +from kucoin_updater.tasks import app, update_all_user_kucoin_account REPEAT_EVERY = 30 diff --git a/kucoin_updater/tasks.py b/kucoin_updater/tasks.py index 0cc93b6..66a1f29 100644 --- a/kucoin_updater/tasks.py +++ b/kucoin_updater/tasks.py @@ -2,14 +2,14 @@ from celery.utils.log import get_task_logger # setup django env -import init_django +from . import init_django from cryptoreader.accounts import models, serializers from django.http import HttpRequest from third_party_sdks.sdk_api import fetch_accounts -app = Celery("tasks", broker="redis://localhost:6379/2") +app = Celery("tasks", broker="redis://redis:6379/2") logger = get_task_logger(__name__) diff --git a/requirements/dev.txt b/requirements/dev.txt index db5c9c1..59fad64 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -19,7 +19,9 @@ cryptography==37.0.4 decorator==5.1.1 Deprecated==1.2.13 Django==4.0.6 +django-environ==0.9.0 django-filter==22.1 +django-redis==5.2.0 djangorestframework==3.13.1 djangorestframework-simplejwt==5.2.0 drf-spectacular==0.22.1 From 48ef861b0dc31442536fbb73a49b10c336c9ad24 Mon Sep 17 00:00:00 2001 From: bigsbug Date: Thu, 21 Jul 2022 16:09:37 +0430 Subject: [PATCH 57/66] installer services --- install-service.dev.sh | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 install-service.dev.sh diff --git a/install-service.dev.sh b/install-service.dev.sh new file mode 100644 index 0000000..5c997b2 --- /dev/null +++ b/install-service.dev.sh @@ -0,0 +1,6 @@ +#!/bin/bash + +#Active all env vars for docker compose files +. ./active_dev.sh + +docker compose -f ./docker-compose.yaml -f docker-compose.dev.yaml up -d --build \ No newline at end of file From f219fd5f155454cb7966b102c16cabd30474d07b Mon Sep 17 00:00:00 2001 From: bigsbug Date: Thu, 21 Jul 2022 16:17:52 +0430 Subject: [PATCH 58/66] add new dependancy --- requirements/dev.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/requirements/dev.txt b/requirements/dev.txt index 59fad64..f48c786 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -45,6 +45,7 @@ pexpect==4.8.0 pickleshare==0.7.5 platformdirs==2.5.2 prompt-toolkit==3.0.30 +psycopg2-binary==2.9.3 ptyprocess==0.7.0 pure-eval==0.2.2 pycparser==2.21 From 719197e538f7334b9630b08575f822749affb562 Mon Sep 17 00:00:00 2001 From: bigsbug Date: Thu, 21 Jul 2022 16:25:56 +0430 Subject: [PATCH 59/66] add static path --- config/settings/dev.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/config/settings/dev.py b/config/settings/dev.py index 847bee4..13efde7 100644 --- a/config/settings/dev.py +++ b/config/settings/dev.py @@ -67,3 +67,7 @@ "SWAGGER_UI_FAVICON_HREF": "SIDECAR", "REDOC_DIST": "SIDECAR", } + +STATIC_URL = "/static/" +STATIC_ROOT = BASE_DIR / "static" +STATICFILES_DIRS = [] From f678b18f246bb5848712bb9271fb0f625ac12261 Mon Sep 17 00:00:00 2001 From: bigsbug Date: Thu, 21 Jul 2022 16:41:51 +0430 Subject: [PATCH 60/66] fix base path --- config/settings/base.py | 2 +- config/settings/dev.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/config/settings/base.py b/config/settings/base.py index 50244ce..3b79573 100644 --- a/config/settings/base.py +++ b/config/settings/base.py @@ -13,7 +13,7 @@ from pathlib import Path # Build paths inside the project like this: BASE_DIR / 'subdir'. -BASE_DIR = Path(__file__).resolve().parent.parent +BASE_DIR = Path(__file__).resolve().parent.parent.parent # Quick-start development settings - unsuitable for production diff --git a/config/settings/dev.py b/config/settings/dev.py index 13efde7..a298486 100644 --- a/config/settings/dev.py +++ b/config/settings/dev.py @@ -4,14 +4,14 @@ import os env = environ.Env() -environ.Env.read_env(os.path.join(BASE_DIR / "django.dev.env")) +environ.Env.read_env(os.path.join(BASE_DIR / "config/django.dev.env")) SECRET_KEY = env("SECRET_KEY") # SECURITY WARNING: don't run with debug turned on in production! DEBUG = env("DEBUG") -ALLOWED_HOSTS = str(env("ALLOWED_HOSTS")).strip().split() +ALLOWED_HOSTS = str(env("ALLOWED_HOSTS")).strip('"').strip().split() APPEND_SLASH = env("APPEND_SLASH") # encrypt key for encrypting and decrypting data From 1943106a8c75b8af90845ee4561a65e2fbad5947 Mon Sep 17 00:00:00 2001 From: bigsbug Date: Thu, 21 Jul 2022 16:43:55 +0430 Subject: [PATCH 61/66] change folder statics --- config/settings/dev.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/settings/dev.py b/config/settings/dev.py index a298486..1bca5ac 100644 --- a/config/settings/dev.py +++ b/config/settings/dev.py @@ -69,5 +69,5 @@ } STATIC_URL = "/static/" -STATIC_ROOT = BASE_DIR / "static" +STATIC_ROOT = BASE_DIR / "statics" STATICFILES_DIRS = [] From f2a6042c1d0c3af8bf44f9962796fedeffb886b0 Mon Sep 17 00:00:00 2001 From: bigsbug Date: Thu, 21 Jul 2022 16:52:10 +0430 Subject: [PATCH 62/66] change folder statics --- config/settings/dev.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/settings/dev.py b/config/settings/dev.py index 1bca5ac..a298486 100644 --- a/config/settings/dev.py +++ b/config/settings/dev.py @@ -69,5 +69,5 @@ } STATIC_URL = "/static/" -STATIC_ROOT = BASE_DIR / "statics" +STATIC_ROOT = BASE_DIR / "static" STATICFILES_DIRS = [] From 59068214723a0335eed62151d70863413cdf2e8c Mon Sep 17 00:00:00 2001 From: bigsbug Date: Thu, 21 Jul 2022 16:54:58 +0430 Subject: [PATCH 63/66] add manager.py corsponding for django service --- django-manager.sh | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 django-manager.sh diff --git a/django-manager.sh b/django-manager.sh new file mode 100644 index 0000000..82de7b4 --- /dev/null +++ b/django-manager.sh @@ -0,0 +1,4 @@ +#!/bin/bash + +args=$@ +docker compose exec django ./manage.py $args \ No newline at end of file From 46dc54d228f540db2b69a2a28ae77cdd3b88529c Mon Sep 17 00:00:00 2001 From: bigsbug Date: Thu, 21 Jul 2022 17:02:40 +0430 Subject: [PATCH 64/66] add lookup field --- cryptoreader/api/views.py | 1 + 1 file changed, 1 insertion(+) diff --git a/cryptoreader/api/views.py b/cryptoreader/api/views.py index 3ed95f4..0daa637 100644 --- a/cryptoreader/api/views.py +++ b/cryptoreader/api/views.py @@ -116,6 +116,7 @@ def destroy_me(self, request): class KucoinAccountViewSet(viewsets.ViewSet): queryset = KucoinAccount.objects.all() serializer_class = KucoinAccountSerializer + lookup_field = "id" permission_classes = (permissions.IsAuthenticated,) def get_serializer_context(self): From fe2e4579c8049ff5a34962fe9e73ebf586bccc95 Mon Sep 17 00:00:00 2001 From: bigsbug Date: Thu, 21 Jul 2022 17:17:25 +0430 Subject: [PATCH 65/66] remove whitespaces --- cryptoreader/api/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cryptoreader/api/views.py b/cryptoreader/api/views.py index 0daa637..83f2bac 100644 --- a/cryptoreader/api/views.py +++ b/cryptoreader/api/views.py @@ -193,7 +193,7 @@ def list(self, request, *args, **kwargs): ) def list_current(self, request, *args, **kwargs): """fetch last avilable accounts form kucoin and instert to db""" - + user = self.request.user accounts = load_accounts_from_kucoin_by_user(user) serializer = KucoinAccountSerializer( From 7af57cf9ecada84eaae1a00b23ad9ac9bd2ad387 Mon Sep 17 00:00:00 2001 From: Ali <56682030+bigsbug@users.noreply.github.com> Date: Fri, 22 Jul 2022 11:41:17 +0430 Subject: [PATCH 66/66] Disable SandBox Mode --- third_party_sdks/sdk_api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/third_party_sdks/sdk_api.py b/third_party_sdks/sdk_api.py index 95d4384..63a253c 100644 --- a/third_party_sdks/sdk_api.py +++ b/third_party_sdks/sdk_api.py @@ -2,7 +2,7 @@ def fetch_accounts(key, secret, passphrase, id=None) -> dict: - kucoin = Kucoin(True) + kucoin = Kucoin(False) kucoin.set_key(key) kucoin.set_secret(secret) kucoin.set_passphrase(passphrase)