From 0f9878e50a52f554ff9a84d0422c89dae46b2539 Mon Sep 17 00:00:00 2001 From: Liubomyr Halamaha Date: Tue, 9 Mar 2021 12:04:22 +0200 Subject: [PATCH 001/100] init project struct --- .gitignore | 4 + .pylintrc | 591 +++++++++++++++++++++++++++++++++++++ README.md | 28 +- manage.py | 22 ++ project_config/__init__.py | 0 project_config/asgi.py | 16 + project_config/settings.py | 120 ++++++++ project_config/urls.py | 20 ++ project_config/wsgi.py | 16 + requirements.txt | 15 + 10 files changed, 831 insertions(+), 1 deletion(-) create mode 100644 .pylintrc create mode 100644 manage.py create mode 100644 project_config/__init__.py create mode 100644 project_config/asgi.py create mode 100644 project_config/settings.py create mode 100644 project_config/urls.py create mode 100644 project_config/wsgi.py create mode 100644 requirements.txt diff --git a/.gitignore b/.gitignore index b6e4761..7701c80 100644 --- a/.gitignore +++ b/.gitignore @@ -127,3 +127,7 @@ dmypy.json # Pyre type checker .pyre/ + +#pyCha +.idea + diff --git a/.pylintrc b/.pylintrc new file mode 100644 index 0000000..b82205c --- /dev/null +++ b/.pylintrc @@ -0,0 +1,591 @@ +[MASTER] + +# A comma-separated list of package or module names from where C extensions may +# be loaded. Extensions are loading into the active Python interpreter and may +# run arbitrary code. +extension-pkg-whitelist= + +# Specify a score threshold to be exceeded before program exits with error. +fail-under=10.0 + +# Add files or directories to the blacklist. They should be base names, not +# paths. +ignore=CVS, migrations, apps.py, settings.py, local_settings.py, manage.py, unittests, urls.py + +# Add files or directories matching the regex patterns to the blacklist. The +# regex matches against base names, not paths. +ignore-patterns= + +# Python code to execute, usually for sys.path manipulation such as +# pygtk.require(). +#init-hook= + +# Use multiple processes to speed up Pylint. Specifying 0 will auto-detect the +# number of processors available to use. +jobs=1 + +# Control the amount of potential inferred values when inferring a single +# object. This can help the performance when dealing with large functions or +# complex, nested conditions. +limit-inference-results=100 + +# List of plugins (as comma separated values of python module names) to load, +# usually to register additional checkers. +load-plugins=pylint_django + +# Pickle collected data for later comparisons. +persistent=yes + +# When enabled, pylint would attempt to guess common misconfiguration and emit +# user-friendly hints instead of false-positive error messages. +suggestion-mode=yes + +# Allow loading of arbitrary C extensions. Extensions are imported into the +# active Python interpreter and may run arbitrary code. +unsafe-load-any-extension=no + + +[MESSAGES CONTROL] + +# Only show warnings with the listed confidence levels. Leave empty to show +# all. Valid levels: HIGH, INFERENCE, INFERENCE_FAILURE, UNDEFINED. +confidence= + +# Disable the message, report, category or checker with the given id(s). You +# can either give multiple identifiers separated by comma (,) or put this +# option multiple times (only on the command line, not in the configuration +# file where it should appear only once). You can also use "--disable=all" to +# disable everything first and then reenable specific checks. For example, if +# you want to run only the similarities checker, you can use "--disable=all +# --enable=similarities". If you want to run only the classes checker, but have +# no Warning level messages displayed, use "--disable=all --enable=classes +# --disable=W". +disable=print-statement, + parameter-unpacking, + unpacking-in-except, + old-raise-syntax, + backtick, + long-suffix, + old-ne-operator, + old-octal-literal, + import-star-module-level, + non-ascii-bytes-literal, + raw-checker-failed, + bad-inline-option, + locally-disabled, + file-ignored, + suppressed-message, + useless-suppression, + deprecated-pragma, + use-symbolic-message-instead, + apply-builtin, + basestring-builtin, + buffer-builtin, + cmp-builtin, + coerce-builtin, + execfile-builtin, + file-builtin, + long-builtin, + raw_input-builtin, + reduce-builtin, + standarderror-builtin, + unicode-builtin, + xrange-builtin, + coerce-method, + delslice-method, + getslice-method, + setslice-method, + no-absolute-import, + old-division, + dict-iter-method, + dict-view-method, + next-method-called, + metaclass-assignment, + indexing-exception, + raising-string, + reload-builtin, + oct-method, + hex-method, + nonzero-method, + cmp-method, + input-builtin, + round-builtin, + intern-builtin, + unichr-builtin, + map-builtin-not-iterating, + zip-builtin-not-iterating, + range-builtin-not-iterating, + filter-builtin-not-iterating, + using-cmp-argument, + eq-without-hash, + div-method, + idiv-method, + rdiv-method, + exception-message-attribute, + invalid-str-codec, + sys-max-int, + bad-python3-import, + deprecated-string-function, + deprecated-str-translate-call, + deprecated-itertools-function, + deprecated-types-field, + next-method-defined, + dict-items-not-iterating, + dict-keys-not-iterating, + dict-values-not-iterating, + deprecated-operator-function, + deprecated-urllib-function, + xreadlines-attribute, + deprecated-sys-function, + exception-escape, + comprehension-escape + +# Enable the message, report, category or checker with the given id(s). You can +# either give multiple identifier separated by comma (,) or put this option +# multiple time (only on the command line, not in the configuration file where +# it should appear only once). See also the "--disable" option for examples. +enable=c-extension-no-member + + +[REPORTS] + +# Python expression which should return a score less than or equal to 10. You +# have access to the variables 'error', 'warning', 'refactor', and 'convention' +# which contain the number of messages in each category, as well as 'statement' +# which is the total number of statements analyzed. This score is used by the +# global evaluation report (RP0004). +evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10) + +# Template used to display messages. This is a python new-style format string +# used to format the message information. See doc for all details. +#msg-template= + +# Set the output format. Available formats are text, parseable, colorized, json +# and msvs (visual studio). You can also give a reporter class, e.g. +# mypackage.mymodule.MyReporterClass. +output-format=text + +# Tells whether to display a full report or only the messages. +reports=no + +# Activate the evaluation score. +score=yes + + +[REFACTORING] + +# Maximum number of nested blocks for function / method body +max-nested-blocks=5 + +# Complete name of functions that never returns. When checking for +# inconsistent-return-statements if a never returning function is called then +# it will be considered as an explicit return statement and no message will be +# printed. +never-returning-functions=sys.exit + + +[BASIC] + +# Naming style matching correct argument names. +argument-naming-style=snake_case + +# Regular expression matching correct argument names. Overrides argument- +# naming-style. +#argument-rgx= + +# Naming style matching correct attribute names. +attr-naming-style=snake_case + +# Regular expression matching correct attribute names. Overrides attr-naming- +# style. +#attr-rgx= + +# Bad variable names which should always be refused, separated by a comma. +bad-names=foo, + bar, + baz, + toto, + tutu, + tata + +# Bad variable names regexes, separated by a comma. If names match any regex, +# they will always be refused +bad-names-rgxs= + +# Naming style matching correct class attribute names. +class-attribute-naming-style=any + +# Regular expression matching correct class attribute names. Overrides class- +# attribute-naming-style. +#class-attribute-rgx= + +# Naming style matching correct class names. +class-naming-style=PascalCase + +# Regular expression matching correct class names. Overrides class-naming- +# style. +#class-rgx= + +# Naming style matching correct constant names. +const-naming-style=UPPER_CASE + +# Regular expression matching correct constant names. Overrides const-naming- +# style. +#const-rgx= + +# Minimum line length for functions/classes that require docstrings, shorter +# ones are exempt. +docstring-min-length=-1 + +# Naming style matching correct function names. +function-naming-style=snake_case + +# Regular expression matching correct function names. Overrides function- +# naming-style. +#function-rgx= + +# Good variable names which should always be accepted, separated by a comma. +good-names=i, + j, + k, + ex, + Run, + _ + +# Good variable names regexes, separated by a comma. If names match any regex, +# they will always be accepted +good-names-rgxs= + +# Include a hint for the correct naming format with invalid-name. +include-naming-hint=no + +# Naming style matching correct inline iteration names. +inlinevar-naming-style=any + +# Regular expression matching correct inline iteration names. Overrides +# inlinevar-naming-style. +#inlinevar-rgx= + +# Naming style matching correct method names. +method-naming-style=snake_case + +# Regular expression matching correct method names. Overrides method-naming- +# style. +#method-rgx= + +# Naming style matching correct module names. +module-naming-style=snake_case + +# Regular expression matching correct module names. Overrides module-naming- +# style. +#module-rgx= + +# Colon-delimited sets of names that determine each other's naming style when +# the name regexes allow several styles. +name-group= + +# Regular expression which should only match function or class names that do +# not require a docstring. +no-docstring-rgx=^_ + +# List of decorators that produce properties, such as abc.abstractproperty. Add +# to this list to register other decorators that produce valid properties. +# These decorators are taken in consideration only for invalid-name. +property-classes=abc.abstractproperty + +# Naming style matching correct variable names. +variable-naming-style=snake_case + +# Regular expression matching correct variable names. Overrides variable- +# naming-style. +#variable-rgx= + + +[FORMAT] + +# Expected format of line ending, e.g. empty (any line ending), LF or CRLF. +expected-line-ending-format= + +# Regexp for a line that is allowed to be longer than the limit. +ignore-long-lines=^\s*(# )??$ + +# Number of spaces of indent required inside a hanging or continued line. +indent-after-paren=4 + +# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1 +# tab). +indent-string=' ' + +# Maximum number of characters on a single line. +max-line-length=100 + +# Maximum number of lines in a module. +max-module-lines=1000 + +# Allow the body of a class to be on the same line as the declaration if body +# contains single statement. +single-line-class-stmt=no + +# Allow the body of an if to be on the same line as the test if there is no +# else. +single-line-if-stmt=no + + +[LOGGING] + +# The type of string formatting that logging methods do. `old` means using % +# formatting, `new` is for `{}` formatting. +logging-format-style=old + +# Logging modules to check that the string format arguments are in logging +# function parameter format. +logging-modules=logging + + +[MISCELLANEOUS] + +# List of note tags to take in consideration, separated by a comma. +notes=FIXME, + XXX, + TODO + +# Regular expression of note tags to take in consideration. +#notes-rgx= + + +[SIMILARITIES] + +# Ignore comments when computing similarities. +ignore-comments=yes + +# Ignore docstrings when computing similarities. +ignore-docstrings=yes + +# Ignore imports when computing similarities. +ignore-imports=no + +# Minimum lines number of a similarity. +min-similarity-lines=4 + + +[SPELLING] + +# Limits count of emitted suggestions for spelling mistakes. +max-spelling-suggestions=4 + +# Spelling dictionary name. Available dictionaries: none. To make it work, +# install the python-enchant package. +spelling-dict= + +# List of comma separated words that should not be checked. +spelling-ignore-words= + +# A path to a file that contains the private dictionary; one word per line. +spelling-private-dict-file= + +# Tells whether to store unknown words to the private dictionary (see the +# --spelling-private-dict-file option) instead of raising a message. +spelling-store-unknown-words=no + + +[STRING] + +# This flag controls whether inconsistent-quotes generates a warning when the +# character used as a quote delimiter is used inconsistently within a module. +check-quote-consistency=no + +# This flag controls whether the implicit-str-concat should generate a warning +# on implicit string concatenation in sequences defined over several lines. +check-str-concat-over-line-jumps=no + + +[TYPECHECK] + +# List of decorators that produce context managers, such as +# contextlib.contextmanager. Add to this list to register other decorators that +# produce valid context managers. +contextmanager-decorators=contextlib.contextmanager + +# List of members which are set dynamically and missed by pylint inference +# system, and so shouldn't trigger E1101 when accessed. Python regular +# expressions are accepted. +generated-members= + +# Tells whether missing members accessed in mixin class should be ignored. A +# mixin class is detected if its name ends with "mixin" (case insensitive). +ignore-mixin-members=yes + +# Tells whether to warn about missing members when the owner of the attribute +# is inferred to be None. +ignore-none=yes + +# This flag controls whether pylint should warn about no-member and similar +# checks whenever an opaque object is returned when inferring. The inference +# can return multiple potential results while evaluating a Python object, but +# some branches might not be evaluated, which results in partial inference. In +# that case, it might be useful to still emit no-member and other checks for +# the rest of the inferred objects. +ignore-on-opaque-inference=yes + +# List of class names for which member attributes should not be checked (useful +# for classes with dynamically set attributes). This supports the use of +# qualified names. +ignored-classes=optparse.Values,thread._local,_thread._local + +# List of module names for which member attributes should not be checked +# (useful for modules/projects where namespaces are manipulated during runtime +# and thus existing member attributes cannot be deduced by static analysis). It +# supports qualified module names, as well as Unix pattern matching. +ignored-modules= + +# Show a hint with possible names when a member name was not found. The aspect +# of finding the hint is based on edit distance. +missing-member-hint=yes + +# The minimum edit distance a name should have in order to be considered a +# similar match for a missing member name. +missing-member-hint-distance=1 + +# The total number of similar names that should be taken in consideration when +# showing a hint for a missing member. +missing-member-max-choices=1 + +# List of decorators that change the signature of a decorated function. +signature-mutators= + + +[VARIABLES] + +# List of additional names supposed to be defined in builtins. Remember that +# you should avoid defining new builtins when possible. +additional-builtins= + +# Tells whether unused global variables should be treated as a violation. +allow-global-unused-variables=yes + +# List of strings which can identify a callback function by name. A callback +# name must start or end with one of those strings. +callbacks=cb_, + _cb + +# A regular expression matching the name of dummy variables (i.e. expected to +# not be used). +dummy-variables-rgx=_+$|(_[a-zA-Z0-9_]*[a-zA-Z0-9]+?$)|dummy|^ignored_|^unused_ + +# Argument names that match this expression will be ignored. Default to name +# with leading underscore. +ignored-argument-names=_.*|^ignored_|^unused_ + +# Tells whether we should check for unused import in __init__ files. +init-import=no + +# List of qualified module names which can have objects that can redefine +# builtins. +redefining-builtins-modules=six.moves,past.builtins,future.builtins,builtins,io + + +[CLASSES] + +# Warn about protected attribute access inside special methods +check-protected-access-in-special-methods=no + +# List of method names used to declare (i.e. assign) instance attributes. +defining-attr-methods=__init__, + __new__, + setUp, + __post_init__ + +# List of member names, which should be excluded from the protected access +# warning. +exclude-protected=_asdict, + _fields, + _replace, + _source, + _make + +# List of valid names for the first argument in a class method. +valid-classmethod-first-arg=cls + +# List of valid names for the first argument in a metaclass class method. +valid-metaclass-classmethod-first-arg=cls + + +[DESIGN] + +# Maximum number of arguments for function / method. +max-args=5 + +# Maximum number of attributes for a class (see R0902). +max-attributes=7 + +# Maximum number of boolean expressions in an if statement (see R0916). +max-bool-expr=5 + +# Maximum number of branch for function / method body. +max-branches=12 + +# Maximum number of locals for function / method body. +max-locals=15 + +# Maximum number of parents for a class (see R0901). +max-parents=7 + +# Maximum number of public methods for a class (see R0904). +max-public-methods=20 + +# Maximum number of return / yield for function / method body. +max-returns=6 + +# Maximum number of statements in function / method body. +max-statements=50 + +# Minimum number of public methods for a class (see R0903). +min-public-methods=2 + + +[IMPORTS] + +# List of modules that can be imported at any level, not just the top level +# one. +allow-any-import-level= + +# Allow wildcard imports from modules that define __all__. +allow-wildcard-with-all=no + +# Analyse import fallback blocks. This can be used to support both Python 2 and +# 3 compatible code, which means that the block might have code that exists +# only in one or another interpreter, leading to false positives when analysed. +analyse-fallback-blocks=no + +# Deprecated modules which should not be used, separated by a comma. +deprecated-modules=optparse,tkinter.tix + +# Create a graph of external dependencies in the given file (report RP0402 must +# not be disabled). +ext-import-graph= + +# Create a graph of every (i.e. internal and external) dependencies in the +# given file (report RP0402 must not be disabled). +import-graph= + +# Create a graph of internal dependencies in the given file (report RP0402 must +# not be disabled). +int-import-graph= + +# Force import order to recognize a module as part of the standard +# compatibility libraries. +known-standard-library= + +# Force import order to recognize a module as part of a third party library. +known-third-party=enchant + +# Couples of modules and preferred modules, separated by a comma. +preferred-modules= + + +[EXCEPTIONS] + +# Exceptions that will emit a warning when being caught. Defaults to +# "BaseException, Exception". +overgeneral-exceptions=BaseException, + Exception diff --git a/README.md b/README.md index f6e1b69..d2c7011 100644 --- a/README.md +++ b/README.md @@ -1 +1,27 @@ -# todoProject \ No newline at end of file +# todoProject + +create file `local_settings.py` with property +``` +DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.postgresql_psycopg2', + 'NAME': 'your_bd_name', + 'USER': 'user_name', + 'PASSWORD': 'user_password', + 'HOST': '127.0.0.1', + 'PORT': '5432', + } +} + +SECRET_KEY = 'SECRET_KEY' +``` + +## Other +* Code Convention. For analyzing and establishing clean code (according to PEP8) we use **pylint**. +In addition since project uses Django **pylint_django** plugin for pylint is used. All pylint +configurations are in **.pylintrc** config file. To check specific file or package use: + + ```sh + pylint --rcfile='path' filename.py + ``` + Additional information: [Pylint User Manual](https://pylint.readthedocs.io/en/latest/) diff --git a/manage.py b/manage.py new file mode 100644 index 0000000..f4d2cb4 --- /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', 'project_config.settings') + 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() diff --git a/project_config/__init__.py b/project_config/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/project_config/asgi.py b/project_config/asgi.py new file mode 100644 index 0000000..85f4b34 --- /dev/null +++ b/project_config/asgi.py @@ -0,0 +1,16 @@ +""" +ASGI config for project_config 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/3.1/howto/deployment/asgi/ +""" + +import os + +from django.core.asgi import get_asgi_application + +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'project_config.settings') + +application = get_asgi_application() diff --git a/project_config/settings.py b/project_config/settings.py new file mode 100644 index 0000000..f5bd671 --- /dev/null +++ b/project_config/settings.py @@ -0,0 +1,120 @@ +""" +Django settings for project_config project. + +Generated by 'django-admin startproject' using Django 3.1.7. + +For more information on this file, see +https://docs.djangoproject.com/en/3.1/topics/settings/ + +For the full list of settings and their values, see +https://docs.djangoproject.com/en/3.1/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/3.1/howto/deployment/checklist/ + +# SECURITY WARNING: keep the secret key used in production secret! +SECRET_KEY = 'SECRET_KEY' + +# SECURITY WARNING: don't run with debug turned on in production! +DEBUG = True + +ALLOWED_HOSTS = [] + +# Application definition + +INSTALLED_APPS = [ + '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.contrib.auth.middleware.AuthenticationMiddleware', + 'django.contrib.messages.middleware.MessageMiddleware', + 'django.middleware.clickjacking.XFrameOptionsMiddleware', +] + +ROOT_URLCONF = 'project_config.urls' + +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 = 'project_config.wsgi.application' + +# Database +# https://docs.djangoproject.com/en/3.1/ref/settings/#databases + +DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.postgresql_psycopg2', + 'USER': 'postgres', + 'HOST': 'postgres', + 'NAME': 'postgres', + 'PORT': '5432', + } +} + +# Password validation +# https://docs.djangoproject.com/en/3.1/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/3.1/topics/i18n/ + +LANGUAGE_CODE = 'en-us' + +TIME_ZONE = 'UTC' + +USE_I18N = True + +USE_L10N = True + +USE_TZ = True + +# Static files (CSS, JavaScript, Images) +# https://docs.djangoproject.com/en/3.1/howto/static-files/ + +STATIC_URL = '/static/' + +try: + from project_config.local_settings import * +except ImportError: + pass diff --git a/project_config/urls.py b/project_config/urls.py new file mode 100644 index 0000000..412c658 --- /dev/null +++ b/project_config/urls.py @@ -0,0 +1,20 @@ +"""project_config URL Configuration + +The `urlpatterns` list routes URLs to views. For more information please see: + https://docs.djangoproject.com/en/3.1/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.urls import path + +urlpatterns = [ + +] diff --git a/project_config/wsgi.py b/project_config/wsgi.py new file mode 100644 index 0000000..ffe9180 --- /dev/null +++ b/project_config/wsgi.py @@ -0,0 +1,16 @@ +""" +WSGI config for project_config 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/3.1/howto/deployment/wsgi/ +""" + +import os + +from django.core.wsgi import get_wsgi_application + +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'project_config.settings') + +application = get_wsgi_application() diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..3ce2fe1 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,15 @@ +asgiref==3.3.1 +astroid==2.5.1 +colorama==0.4.4 +Django==3.1.7 +isort==5.7.0 +lazy-object-proxy==1.5.2 +mccabe==0.6.1 +psycopg2==2.8.6 +pylint==2.7.2 +pylint-django==2.4.2 +pylint-plugin-utils==0.6 +pytz==2021.1 +sqlparse==0.4.1 +toml==0.10.2 +wrapt==1.12.1 From 99c45fbf229c133778beb53fbc12f984ef6d4d15 Mon Sep 17 00:00:00 2001 From: StepanTchynetskyi Date: Wed, 10 Mar 2021 13:11:20 +0200 Subject: [PATCH 002/100] initialize list_project app --- list_project/__init__.py | 0 list_project/admin.py | 3 +++ list_project/apps.py | 5 +++++ list_project/migrations/__init__.py | 0 list_project/models.py | 3 +++ list_project/urls.py | 0 list_project/views.py | 5 +++++ project_config/settings.py | 3 ++- project_config/urls.py | 3 ++- 9 files changed, 20 insertions(+), 2 deletions(-) create mode 100644 list_project/__init__.py create mode 100644 list_project/admin.py create mode 100644 list_project/apps.py create mode 100644 list_project/migrations/__init__.py create mode 100644 list_project/models.py create mode 100644 list_project/urls.py create mode 100644 list_project/views.py diff --git a/list_project/__init__.py b/list_project/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/list_project/admin.py b/list_project/admin.py new file mode 100644 index 0000000..8c38f3f --- /dev/null +++ b/list_project/admin.py @@ -0,0 +1,3 @@ +from django.contrib import admin + +# Register your models here. diff --git a/list_project/apps.py b/list_project/apps.py new file mode 100644 index 0000000..963438b --- /dev/null +++ b/list_project/apps.py @@ -0,0 +1,5 @@ +from django.apps import AppConfig + + +class ListProjectConfig(AppConfig): + name = 'list_project' diff --git a/list_project/migrations/__init__.py b/list_project/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/list_project/models.py b/list_project/models.py new file mode 100644 index 0000000..71a8362 --- /dev/null +++ b/list_project/models.py @@ -0,0 +1,3 @@ +from django.db import models + +# Create your models here. diff --git a/list_project/urls.py b/list_project/urls.py new file mode 100644 index 0000000..e69de29 diff --git a/list_project/views.py b/list_project/views.py new file mode 100644 index 0000000..383fd02 --- /dev/null +++ b/list_project/views.py @@ -0,0 +1,5 @@ +from django.shortcuts import render +from django.http import HttpResponse +# Create your views here. +def index(request): + return \ No newline at end of file diff --git a/project_config/settings.py b/project_config/settings.py index f5bd671..a650cb9 100644 --- a/project_config/settings.py +++ b/project_config/settings.py @@ -34,6 +34,7 @@ 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', + 'list_project.apps.ListProjectConfig', ] MIDDLEWARE = [ @@ -115,6 +116,6 @@ STATIC_URL = '/static/' try: - from project_config.local_settings import * + from local_settings import * except ImportError: pass diff --git a/project_config/urls.py b/project_config/urls.py index 412c658..0cc3604 100644 --- a/project_config/urls.py +++ b/project_config/urls.py @@ -13,7 +13,8 @@ 1. Import the include() function: from django.urls import include, path 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) """ -from django.urls import path +from django.urls import path,include + urlpatterns = [ From 39c9fe3a49336e4a7565b84fdee6963a6250bc6d Mon Sep 17 00:00:00 2001 From: StepanTchynetskyi Date: Wed, 10 Mar 2021 13:19:38 +0200 Subject: [PATCH 003/100] setup urls --- list_project/urls.py | 5 +++++ list_project/views.py | 2 +- project_config/urls.py | 3 ++- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/list_project/urls.py b/list_project/urls.py index e69de29..b0c35cc 100644 --- a/list_project/urls.py +++ b/list_project/urls.py @@ -0,0 +1,5 @@ +from django.urls import path +from . import views +urlpatterns = [ + path('',views.index, name='index'), +] \ No newline at end of file diff --git a/list_project/views.py b/list_project/views.py index 383fd02..a886353 100644 --- a/list_project/views.py +++ b/list_project/views.py @@ -2,4 +2,4 @@ from django.http import HttpResponse # Create your views here. def index(request): - return \ No newline at end of file + return HttpResponse("Hello, world") \ No newline at end of file diff --git a/project_config/urls.py b/project_config/urls.py index 0cc3604..ae893b8 100644 --- a/project_config/urls.py +++ b/project_config/urls.py @@ -17,5 +17,6 @@ urlpatterns = [ - + path('', include('list_project.urls')), ] + From 425696bd99e6bf13fc9d22da9287c2d564be064c Mon Sep 17 00:00:00 2001 From: SLDem Date: Wed, 10 Mar 2021 14:23:02 +0200 Subject: [PATCH 004/100] Added user app, model and profile view --- project_config/settings.py | 2 ++ user/__init__.py | 0 user/admin.py | 3 +++ user/apps.py | 5 +++++ user/forms.py | 0 user/migrations/__init__.py | 0 user/models.py | 35 +++++++++++++++++++++++++++++++++++ user/tests.py | 3 +++ user/urls.py | 7 +++++++ user/views.py | 24 ++++++++++++++++++++++++ 10 files changed, 79 insertions(+) create mode 100644 user/__init__.py create mode 100644 user/admin.py create mode 100644 user/apps.py create mode 100644 user/forms.py create mode 100644 user/migrations/__init__.py create mode 100644 user/models.py create mode 100644 user/tests.py create mode 100644 user/urls.py create mode 100644 user/views.py diff --git a/project_config/settings.py b/project_config/settings.py index f5bd671..79fe5b6 100644 --- a/project_config/settings.py +++ b/project_config/settings.py @@ -114,6 +114,8 @@ STATIC_URL = '/static/' +AUTH_USER_MODEL = 'user.User' + try: from project_config.local_settings import * except ImportError: diff --git a/user/__init__.py b/user/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/user/admin.py b/user/admin.py new file mode 100644 index 0000000..8c38f3f --- /dev/null +++ b/user/admin.py @@ -0,0 +1,3 @@ +from django.contrib import admin + +# Register your models here. diff --git a/user/apps.py b/user/apps.py new file mode 100644 index 0000000..35048d4 --- /dev/null +++ b/user/apps.py @@ -0,0 +1,5 @@ +from django.apps import AppConfig + + +class UserConfig(AppConfig): + name = 'user' diff --git a/user/forms.py b/user/forms.py new file mode 100644 index 0000000..e69de29 diff --git a/user/migrations/__init__.py b/user/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/user/models.py b/user/models.py new file mode 100644 index 0000000..6f9182a --- /dev/null +++ b/user/models.py @@ -0,0 +1,35 @@ +from django.db import models +from django.contrib.auth.models import AbstractBaseUser, BaseUserManager + + +class UserManager(BaseUserManager): + + def _create_user(self, email, password, **extra_fields): + if not email: + raise ValueError('User must have an email address') + email = self.normalize_email(email) + user = self.model( + email=email, + **extra_fields + ) + user.set_password(password) + user.save(using=self._db) + return user + + def create_user(self, email, password, **extra_fields): + return self._create_user(email, password, **extra_fields) + + +class User(AbstractBaseUser): + objects = UserManager() + + first_name = models.CharField('First Name', max_length=55, null=False, blank=False) + last_name = models.CharField('Last Name', max_length=55, null=False, blank=False) + email = models.EmailField('Email') + + USERNAME_FIELD = 'email' + EMAIL_FIELD = 'email' + REQUIRED_FIELDS = [] + + def __str__(self): + return str(self.first_name) + str(self.last_name) \ No newline at end of file diff --git a/user/tests.py b/user/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/user/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/user/urls.py b/user/urls.py new file mode 100644 index 0000000..41b295b --- /dev/null +++ b/user/urls.py @@ -0,0 +1,7 @@ +from django.urls import path + +from . import views + +urlpatterns = [ + path('profile//', views.ProfileView, name='profile'), +] diff --git a/user/views.py b/user/views.py new file mode 100644 index 0000000..a3e3723 --- /dev/null +++ b/user/views.py @@ -0,0 +1,24 @@ +from django.shortcuts import render +from django.views.generic import DetailView, FormView, DeleteView, ListView +from django.http import JsonResponse + +from .models import User +from lists.models import List + + +class ProfileView(DetailView): + model = User + + def get_context_data(self, **kwargs): + context = super().get_context_data(**kwargs) + user = self.get_object() + lists = List.objects.filter(user_id=user.pk) + context['lists'] = lists + context['user'] = user + return context + + def get(self, request, *args, **kwargs): + self.object = self.get_object() + data = self.get_context_data(object=self.object) + if self.request.GET: + return JsonResponse(data) From 146b4a422529e319149d06fd2d6ed7a96bb1fdcf Mon Sep 17 00:00:00 2001 From: AndriiRomaniuk Date: Wed, 10 Mar 2021 15:06:47 +0200 Subject: [PATCH 005/100] Created Task app and respective models --- project_config/settings.py | 1 + requirements.txt | 4 ++-- task/__init__.py | 0 task/apps.py | 5 +++++ task/migrations/__init__.py | 0 task/models.py | 30 ++++++++++++++++++++++++++++++ 6 files changed, 38 insertions(+), 2 deletions(-) create mode 100644 task/__init__.py create mode 100644 task/apps.py create mode 100644 task/migrations/__init__.py create mode 100644 task/models.py diff --git a/project_config/settings.py b/project_config/settings.py index f5bd671..c25ab99 100644 --- a/project_config/settings.py +++ b/project_config/settings.py @@ -34,6 +34,7 @@ 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', + 'task' ] MIDDLEWARE = [ diff --git a/requirements.txt b/requirements.txt index 3ce2fe1..eeff482 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,7 +3,7 @@ astroid==2.5.1 colorama==0.4.4 Django==3.1.7 isort==5.7.0 -lazy-object-proxy==1.5.2 +lazy-object-prxy==1.5.2 mccabe==0.6.1 psycopg2==2.8.6 pylint==2.7.2 @@ -12,4 +12,4 @@ pylint-plugin-utils==0.6 pytz==2021.1 sqlparse==0.4.1 toml==0.10.2 -wrapt==1.12.1 +wrapt==1.12.1 \ No newline at end of file diff --git a/task/__init__.py b/task/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/task/apps.py b/task/apps.py new file mode 100644 index 0000000..3c5f70a --- /dev/null +++ b/task/apps.py @@ -0,0 +1,5 @@ +from django.apps import AppConfig + + +class TaskConfig(AppConfig): + name = 'task' diff --git a/task/migrations/__init__.py b/task/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/task/models.py b/task/models.py new file mode 100644 index 0000000..015b804 --- /dev/null +++ b/task/models.py @@ -0,0 +1,30 @@ +from django.db import models +from django.conf import settings +from list.models import ToDoList + +# Create your models here. +class Task(models.Model): + title = models.CharField( + max_length=30, + required=True + ) + description = models.TextField( + max_length=256 + ) + is_completed = models.BooleanField( + default=False + ) + deadline = models.DateField( + required=True + ) + user_id = models.ForeignKey( + settings.AUTH_USER_MODEL, + on_delete=models.CASCADE + ) + list_id = models.ForeignKey( + ToDoList, + on_delete=models.CASCADE + ) + + def __str__(self): + return self.title From fd9ee0a389214a63c6e5b24a6053fa5bb5f72f93 Mon Sep 17 00:00:00 2001 From: SLDem Date: Wed, 10 Mar 2021 15:25:14 +0200 Subject: [PATCH 006/100] Added app to installed apps, added the urls and renamed List to ToDoList in the views --- project_config/settings.py | 1 + project_config/urls.py | 4 ++-- user/views.py | 4 ++-- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/project_config/settings.py b/project_config/settings.py index 79fe5b6..0525e67 100644 --- a/project_config/settings.py +++ b/project_config/settings.py @@ -34,6 +34,7 @@ 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', + 'user', ] MIDDLEWARE = [ diff --git a/project_config/urls.py b/project_config/urls.py index 412c658..349f81b 100644 --- a/project_config/urls.py +++ b/project_config/urls.py @@ -13,8 +13,8 @@ 1. Import the include() function: from django.urls import include, path 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) """ -from django.urls import path +from django.urls import path, include urlpatterns = [ - + path('user/', include('user.urls')), ] diff --git a/user/views.py b/user/views.py index a3e3723..4e074cf 100644 --- a/user/views.py +++ b/user/views.py @@ -3,7 +3,7 @@ from django.http import JsonResponse from .models import User -from lists.models import List +from lists.models import ToDoList class ProfileView(DetailView): @@ -12,7 +12,7 @@ class ProfileView(DetailView): def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) user = self.get_object() - lists = List.objects.filter(user_id=user.pk) + lists = ToDoList.objects.filter(user_id=user.pk) context['lists'] = lists context['user'] = user return context From 55e51ea1e8351e82225f6254c1efde72d4bf38f8 Mon Sep 17 00:00:00 2001 From: SLDem Date: Wed, 10 Mar 2021 21:25:17 +0200 Subject: [PATCH 007/100] Changed ToDoList to List --- user/views.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/user/views.py b/user/views.py index 4e074cf..93a2b32 100644 --- a/user/views.py +++ b/user/views.py @@ -1,9 +1,8 @@ -from django.shortcuts import render -from django.views.generic import DetailView, FormView, DeleteView, ListView +from django.views.generic import DetailView from django.http import JsonResponse from .models import User -from lists.models import ToDoList +from lists.models import List class ProfileView(DetailView): @@ -12,7 +11,7 @@ class ProfileView(DetailView): def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) user = self.get_object() - lists = ToDoList.objects.filter(user_id=user.pk) + lists = List.objects.filter(user_id=user.pk) context['lists'] = lists context['user'] = user return context From 91e020f7d939e57a1bc4801ceb2032549d905323 Mon Sep 17 00:00:00 2001 From: SLDem Date: Thu, 11 Mar 2021 11:13:56 +0200 Subject: [PATCH 008/100] Removed admin.py, fixed models.py --- user/admin.py | 3 --- user/models.py | 4 ++-- 2 files changed, 2 insertions(+), 5 deletions(-) delete mode 100644 user/admin.py diff --git a/user/admin.py b/user/admin.py deleted file mode 100644 index 8c38f3f..0000000 --- a/user/admin.py +++ /dev/null @@ -1,3 +0,0 @@ -from django.contrib import admin - -# Register your models here. diff --git a/user/models.py b/user/models.py index 6f9182a..dd42bb7 100644 --- a/user/models.py +++ b/user/models.py @@ -25,11 +25,11 @@ class User(AbstractBaseUser): first_name = models.CharField('First Name', max_length=55, null=False, blank=False) last_name = models.CharField('Last Name', max_length=55, null=False, blank=False) - email = models.EmailField('Email') + email = models.EmailField('Email', unique=True) USERNAME_FIELD = 'email' EMAIL_FIELD = 'email' REQUIRED_FIELDS = [] def __str__(self): - return str(self.first_name) + str(self.last_name) \ No newline at end of file + return str(self.first_name) + str(self.last_name) From d5e693c60197416d0cba4f8c4ed9e17bcf99e481 Mon Sep 17 00:00:00 2001 From: StepanTchynetskyi Date: Thu, 11 Mar 2021 11:26:03 +0200 Subject: [PATCH 009/100] review changes --- list_project/admin.py | 3 --- list_project/urls.py | 4 ++-- list_project/views.py | 2 -- project_config/urls.py | 3 +-- 4 files changed, 3 insertions(+), 9 deletions(-) delete mode 100644 list_project/admin.py diff --git a/list_project/admin.py b/list_project/admin.py deleted file mode 100644 index 8c38f3f..0000000 --- a/list_project/admin.py +++ /dev/null @@ -1,3 +0,0 @@ -from django.contrib import admin - -# Register your models here. diff --git a/list_project/urls.py b/list_project/urls.py index b0c35cc..bf8d4b8 100644 --- a/list_project/urls.py +++ b/list_project/urls.py @@ -1,5 +1,5 @@ from django.urls import path from . import views urlpatterns = [ - path('',views.index, name='index'), -] \ No newline at end of file + +] diff --git a/list_project/views.py b/list_project/views.py index a886353..3ebb8dd 100644 --- a/list_project/views.py +++ b/list_project/views.py @@ -1,5 +1,3 @@ from django.shortcuts import render from django.http import HttpResponse # Create your views here. -def index(request): - return HttpResponse("Hello, world") \ No newline at end of file diff --git a/project_config/urls.py b/project_config/urls.py index ae893b8..a50da9f 100644 --- a/project_config/urls.py +++ b/project_config/urls.py @@ -17,6 +17,5 @@ urlpatterns = [ - path('', include('list_project.urls')), + path('list_project/', include('list_project.urls')), ] - From 9df84243b46e16d47d9b74a536678853003e19cc Mon Sep 17 00:00:00 2001 From: AndriiRomaniuk Date: Thu, 11 Mar 2021 11:29:03 +0200 Subject: [PATCH 010/100] Removed foreign keys from models --- requirements.txt | 2 +- task/models.py | 28 ++++++---------------------- 2 files changed, 7 insertions(+), 23 deletions(-) diff --git a/requirements.txt b/requirements.txt index eeff482..b3cbb3c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,7 +3,7 @@ astroid==2.5.1 colorama==0.4.4 Django==3.1.7 isort==5.7.0 -lazy-object-prxy==1.5.2 +lazy-object-proxy==1.5.2 mccabe==0.6.1 psycopg2==2.8.6 pylint==2.7.2 diff --git a/task/models.py b/task/models.py index 015b804..5b2f7ec 100644 --- a/task/models.py +++ b/task/models.py @@ -1,30 +1,14 @@ from django.db import models from django.conf import settings -from list.models import ToDoList + # Create your models here. class Task(models.Model): - title = models.CharField( - max_length=30, - required=True - ) - description = models.TextField( - max_length=256 - ) - is_completed = models.BooleanField( - default=False - ) - deadline = models.DateField( - required=True - ) - user_id = models.ForeignKey( - settings.AUTH_USER_MODEL, - on_delete=models.CASCADE - ) - list_id = models.ForeignKey( - ToDoList, - on_delete=models.CASCADE - ) + title = models.CharField(max_length=30, + required=True) + description = models.TextField(max_length=256) + is_completed = models.BooleanField(default=False) + deadline = models.DateField(required=True) def __str__(self): return self.title From 0c26f9732c186a23092ae0cb21b4dbc3cc32147a Mon Sep 17 00:00:00 2001 From: SLDem Date: Thu, 11 Mar 2021 11:46:09 +0200 Subject: [PATCH 011/100] Fixed models.py, views.py and settings.py --- project_config/settings.py | 2 +- user/models.py | 3 +-- user/tests.py | 3 --- user/views.py | 7 +++---- 4 files changed, 5 insertions(+), 10 deletions(-) delete mode 100644 user/tests.py diff --git a/project_config/settings.py b/project_config/settings.py index 0525e67..182753c 100644 --- a/project_config/settings.py +++ b/project_config/settings.py @@ -115,7 +115,7 @@ STATIC_URL = '/static/' -AUTH_USER_MODEL = 'user.User' +AUTH_USER_MODEL = 'user.CustomUser' try: from project_config.local_settings import * diff --git a/user/models.py b/user/models.py index dd42bb7..0114f85 100644 --- a/user/models.py +++ b/user/models.py @@ -20,7 +20,7 @@ def create_user(self, email, password, **extra_fields): return self._create_user(email, password, **extra_fields) -class User(AbstractBaseUser): +class CustomUser(AbstractBaseUser): objects = UserManager() first_name = models.CharField('First Name', max_length=55, null=False, blank=False) @@ -29,7 +29,6 @@ class User(AbstractBaseUser): USERNAME_FIELD = 'email' EMAIL_FIELD = 'email' - REQUIRED_FIELDS = [] def __str__(self): return str(self.first_name) + str(self.last_name) diff --git a/user/tests.py b/user/tests.py deleted file mode 100644 index 7ce503c..0000000 --- a/user/tests.py +++ /dev/null @@ -1,3 +0,0 @@ -from django.test import TestCase - -# Create your tests here. diff --git a/user/views.py b/user/views.py index 93a2b32..954fa13 100644 --- a/user/views.py +++ b/user/views.py @@ -1,12 +1,12 @@ from django.views.generic import DetailView from django.http import JsonResponse -from .models import User +from .models import CustomUser from lists.models import List class ProfileView(DetailView): - model = User + model = CustomUser def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) @@ -17,7 +17,6 @@ def get_context_data(self, **kwargs): return context def get(self, request, *args, **kwargs): - self.object = self.get_object() - data = self.get_context_data(object=self.object) + data = self.get_context_data() if self.request.GET: return JsonResponse(data) From 59ff824577200cdf6ae0e7846db19a443bc23c52 Mon Sep 17 00:00:00 2001 From: SLDem Date: Thu, 11 Mar 2021 12:20:35 +0200 Subject: [PATCH 012/100] Refactor CustomUser --- {list_project => custom_user}/__init__.py | 0 {user => custom_user}/apps.py | 2 +- {user => custom_user}/forms.py | 0 custom_user/migrations/0001_initial.py | 28 +++++++++++++++++++ .../migrations/__init__.py | 0 {user => custom_user}/models.py | 15 +++++++++- {user => custom_user}/urls.py | 2 +- custom_user/views.py | 14 ++++++++++ list_project/apps.py | 5 ---- list_project/models.py | 3 -- project_config/settings.py | 8 +++--- project_config/urls.py | 4 +-- {user => todolist}/__init__.py | 0 todolist/apps.py | 5 ++++ todolist/migrations/0001_initial.py | 20 +++++++++++++ {user => todolist}/migrations/__init__.py | 0 todolist/models.py | 5 ++++ {list_project => todolist}/urls.py | 2 ++ {list_project => todolist}/views.py | 0 user/views.py | 22 --------------- 20 files changed, 96 insertions(+), 39 deletions(-) rename {list_project => custom_user}/__init__.py (100%) rename {user => custom_user}/apps.py (72%) rename {user => custom_user}/forms.py (100%) create mode 100644 custom_user/migrations/0001_initial.py rename {list_project => custom_user}/migrations/__init__.py (100%) rename {user => custom_user}/models.py (75%) rename {user => custom_user}/urls.py (52%) create mode 100644 custom_user/views.py delete mode 100644 list_project/apps.py delete mode 100644 list_project/models.py rename {user => todolist}/__init__.py (100%) create mode 100644 todolist/apps.py create mode 100644 todolist/migrations/0001_initial.py rename {user => todolist}/migrations/__init__.py (100%) create mode 100644 todolist/models.py rename {list_project => todolist}/urls.py (97%) rename {list_project => todolist}/views.py (100%) delete mode 100644 user/views.py diff --git a/list_project/__init__.py b/custom_user/__init__.py similarity index 100% rename from list_project/__init__.py rename to custom_user/__init__.py diff --git a/user/apps.py b/custom_user/apps.py similarity index 72% rename from user/apps.py rename to custom_user/apps.py index 35048d4..2001f3b 100644 --- a/user/apps.py +++ b/custom_user/apps.py @@ -2,4 +2,4 @@ class UserConfig(AppConfig): - name = 'user' + name = 'custom_user' diff --git a/user/forms.py b/custom_user/forms.py similarity index 100% rename from user/forms.py rename to custom_user/forms.py diff --git a/custom_user/migrations/0001_initial.py b/custom_user/migrations/0001_initial.py new file mode 100644 index 0000000..2fb4e2a --- /dev/null +++ b/custom_user/migrations/0001_initial.py @@ -0,0 +1,28 @@ +# Generated by Django 3.1.7 on 2021-03-11 10:18 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ] + + operations = [ + migrations.CreateModel( + name='CustomUser', + fields=[ + ('id', models.AutoField(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')), + ('first_name', models.CharField(max_length=55, verbose_name='First Name')), + ('last_name', models.CharField(max_length=55, verbose_name='Last Name')), + ('email', models.EmailField(max_length=254, unique=True, verbose_name='Email')), + ], + options={ + 'abstract': False, + }, + ), + ] diff --git a/list_project/migrations/__init__.py b/custom_user/migrations/__init__.py similarity index 100% rename from list_project/migrations/__init__.py rename to custom_user/migrations/__init__.py diff --git a/user/models.py b/custom_user/models.py similarity index 75% rename from user/models.py rename to custom_user/models.py index 0114f85..d31df51 100644 --- a/user/models.py +++ b/custom_user/models.py @@ -1,5 +1,5 @@ -from django.db import models from django.contrib.auth.models import AbstractBaseUser, BaseUserManager +from django.db import models class UserManager(BaseUserManager): @@ -32,3 +32,16 @@ class CustomUser(AbstractBaseUser): def __str__(self): return str(self.first_name) + str(self.last_name) + + def to_dict(self): + return {'first_name': self.first_name, + 'last_name': self.last_name, + 'email': self.email} + + @classmethod + def find_by_id(cls, id): + try: + user = cls.objects.get(pk=id) + return user + except CustomUser.DoesNotExist: + return None diff --git a/user/urls.py b/custom_user/urls.py similarity index 52% rename from user/urls.py rename to custom_user/urls.py index 41b295b..f4ad537 100644 --- a/user/urls.py +++ b/custom_user/urls.py @@ -3,5 +3,5 @@ from . import views urlpatterns = [ - path('profile//', views.ProfileView, name='profile'), + path('profile//', views.ProfileView, name='profile'), ] diff --git a/custom_user/views.py b/custom_user/views.py new file mode 100644 index 0000000..c663ba6 --- /dev/null +++ b/custom_user/views.py @@ -0,0 +1,14 @@ +from django.views import View +from django.http import JsonResponse + +from .models import CustomUser +from todolist.models import ToDoList + + +class ProfileView(View): + + def get(self, request, id=None): + user = CustomUser.find_by_id(id) + if user: + return JsonResponse(user.to_dict()) + return JsonResponse(status=400) diff --git a/list_project/apps.py b/list_project/apps.py deleted file mode 100644 index 963438b..0000000 --- a/list_project/apps.py +++ /dev/null @@ -1,5 +0,0 @@ -from django.apps import AppConfig - - -class ListProjectConfig(AppConfig): - name = 'list_project' diff --git a/list_project/models.py b/list_project/models.py deleted file mode 100644 index 71a8362..0000000 --- a/list_project/models.py +++ /dev/null @@ -1,3 +0,0 @@ -from django.db import models - -# Create your models here. diff --git a/project_config/settings.py b/project_config/settings.py index f3ca541..b82d6aa 100644 --- a/project_config/settings.py +++ b/project_config/settings.py @@ -34,8 +34,8 @@ 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', - 'list_project', - 'user', + 'todolist', + 'custom_user', ] MIDDLEWARE = [ @@ -116,9 +116,9 @@ STATIC_URL = '/static/' -AUTH_USER_MODEL = 'user.CustomUser' +AUTH_USER_MODEL = 'custom_user.CustomUser' try: - from local_settings import * + from project_config.local_settings import * except ImportError: pass diff --git a/project_config/urls.py b/project_config/urls.py index 0a33737..e12de93 100644 --- a/project_config/urls.py +++ b/project_config/urls.py @@ -16,6 +16,6 @@ from django.urls import path, include urlpatterns = [ - path('user/', include('user.urls')), - path('list_project/', include('list_project.urls')), + path('custom_user/', include('custom_user.urls')), + path('todolist/', include('todolist.urls')), ] diff --git a/user/__init__.py b/todolist/__init__.py similarity index 100% rename from user/__init__.py rename to todolist/__init__.py diff --git a/todolist/apps.py b/todolist/apps.py new file mode 100644 index 0000000..3495959 --- /dev/null +++ b/todolist/apps.py @@ -0,0 +1,5 @@ +from django.apps import AppConfig + + +class ToDoListConfig(AppConfig): + name = 'todolist' diff --git a/todolist/migrations/0001_initial.py b/todolist/migrations/0001_initial.py new file mode 100644 index 0000000..456fa8d --- /dev/null +++ b/todolist/migrations/0001_initial.py @@ -0,0 +1,20 @@ +# Generated by Django 3.1.7 on 2021-03-11 10:18 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ] + + operations = [ + migrations.CreateModel( + name='ToDoList', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ], + ), + ] diff --git a/user/migrations/__init__.py b/todolist/migrations/__init__.py similarity index 100% rename from user/migrations/__init__.py rename to todolist/migrations/__init__.py diff --git a/todolist/models.py b/todolist/models.py new file mode 100644 index 0000000..0126d2b --- /dev/null +++ b/todolist/models.py @@ -0,0 +1,5 @@ +from django.db import models + + +class ToDoList(models.Model): + pass diff --git a/list_project/urls.py b/todolist/urls.py similarity index 97% rename from list_project/urls.py rename to todolist/urls.py index bf8d4b8..79b2c23 100644 --- a/list_project/urls.py +++ b/todolist/urls.py @@ -1,5 +1,7 @@ from django.urls import path from . import views + + urlpatterns = [ ] diff --git a/list_project/views.py b/todolist/views.py similarity index 100% rename from list_project/views.py rename to todolist/views.py diff --git a/user/views.py b/user/views.py deleted file mode 100644 index 954fa13..0000000 --- a/user/views.py +++ /dev/null @@ -1,22 +0,0 @@ -from django.views.generic import DetailView -from django.http import JsonResponse - -from .models import CustomUser -from lists.models import List - - -class ProfileView(DetailView): - model = CustomUser - - def get_context_data(self, **kwargs): - context = super().get_context_data(**kwargs) - user = self.get_object() - lists = List.objects.filter(user_id=user.pk) - context['lists'] = lists - context['user'] = user - return context - - def get(self, request, *args, **kwargs): - data = self.get_context_data() - if self.request.GET: - return JsonResponse(data) From 8f0d62e06a034612cf37b0054840fe97e47512bb Mon Sep 17 00:00:00 2001 From: SLDem Date: Thu, 11 Mar 2021 12:38:20 +0200 Subject: [PATCH 013/100] Added ignored to pyling, c0114, c0115, c0116, E5110 --- .pylintrc | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.pylintrc b/.pylintrc index b82205c..541618a 100644 --- a/.pylintrc +++ b/.pylintrc @@ -1,5 +1,11 @@ [MASTER] +disable= + c0114, + c0115, + c0116, + E5110 + # A comma-separated list of package or module names from where C extensions may # be loaded. Extensions are loading into the active Python interpreter and may # run arbitrary code. From 9a2fb069d39308d677565bdac9a3caa2e783094b Mon Sep 17 00:00:00 2001 From: SLDem Date: Thu, 11 Mar 2021 12:43:40 +0200 Subject: [PATCH 014/100] Added migration files for todolist --- todolist/migrations/0001_initial.py | 4 +++- todolist/migrations/0002_todolist_members.py | 20 ++++++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) create mode 100644 todolist/migrations/0002_todolist_members.py diff --git a/todolist/migrations/0001_initial.py b/todolist/migrations/0001_initial.py index 456fa8d..5c4ac40 100644 --- a/todolist/migrations/0001_initial.py +++ b/todolist/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 3.1.7 on 2021-03-11 10:18 +# Generated by Django 3.1.7 on 2021-03-11 10:42 from django.db import migrations, models @@ -15,6 +15,8 @@ class Migration(migrations.Migration): name='ToDoList', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=50)), + ('description', models.CharField(default='No description for now.', max_length=500)), ], ), ] diff --git a/todolist/migrations/0002_todolist_members.py b/todolist/migrations/0002_todolist_members.py new file mode 100644 index 0000000..c0bef16 --- /dev/null +++ b/todolist/migrations/0002_todolist_members.py @@ -0,0 +1,20 @@ +# Generated by Django 3.1.7 on 2021-03-11 10:42 + +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('todolist', '0001_initial'), + ] + + operations = [ + migrations.AddField( + model_name='todolist', + name='members', + field=models.ManyToManyField(related_name='todo_lists', to=settings.AUTH_USER_MODEL), + ), + ] From 30c4e29ac14b11953e44901f44e9950913a62925 Mon Sep 17 00:00:00 2001 From: SLDem Date: Thu, 11 Mar 2021 12:44:14 +0200 Subject: [PATCH 015/100] Fixed for pylint --- custom_user/models.py | 4 ++-- custom_user/urls.py | 2 +- custom_user/views.py | 9 ++++----- todolist/models.py | 7 ++++++- todolist/views.py | 3 --- 5 files changed, 13 insertions(+), 12 deletions(-) diff --git a/custom_user/models.py b/custom_user/models.py index d31df51..340fb87 100644 --- a/custom_user/models.py +++ b/custom_user/models.py @@ -39,9 +39,9 @@ def to_dict(self): 'email': self.email} @classmethod - def find_by_id(cls, id): + def find_by_id(cls, user_id): try: - user = cls.objects.get(pk=id) + user = cls.objects.get(pk=user_id) return user except CustomUser.DoesNotExist: return None diff --git a/custom_user/urls.py b/custom_user/urls.py index f4ad537..db4d6fa 100644 --- a/custom_user/urls.py +++ b/custom_user/urls.py @@ -3,5 +3,5 @@ from . import views urlpatterns = [ - path('profile//', views.ProfileView, name='profile'), + path('profile//', views.ProfileView, name='profile'), ] diff --git a/custom_user/views.py b/custom_user/views.py index c663ba6..6795ee7 100644 --- a/custom_user/views.py +++ b/custom_user/views.py @@ -1,14 +1,13 @@ from django.views import View -from django.http import JsonResponse +from django.http import JsonResponse, HttpResponse from .models import CustomUser -from todolist.models import ToDoList class ProfileView(View): - def get(self, request, id=None): - user = CustomUser.find_by_id(id) + def get(self, request, user_id=None): + user = CustomUser.find_by_id(user_id) if user: return JsonResponse(user.to_dict()) - return JsonResponse(status=400) + return HttpResponse(status=400) diff --git a/todolist/models.py b/todolist/models.py index 0126d2b..dc7c920 100644 --- a/todolist/models.py +++ b/todolist/models.py @@ -1,5 +1,10 @@ from django.db import models +from custom_user.models import CustomUser + class ToDoList(models.Model): - pass + name = models.CharField(max_length=50) + description = models.CharField(max_length=500, default='No description for now.') + + members = models.ManyToManyField(CustomUser, related_name='todo_lists') diff --git a/todolist/views.py b/todolist/views.py index 3ebb8dd..e69de29 100644 --- a/todolist/views.py +++ b/todolist/views.py @@ -1,3 +0,0 @@ -from django.shortcuts import render -from django.http import HttpResponse -# Create your views here. From f30ea572afd4b96783f033b9f7df330df5d544e0 Mon Sep 17 00:00:00 2001 From: Daniil Oleshchuk Date: Thu, 11 Mar 2021 14:53:51 +0200 Subject: [PATCH 016/100] Fixed Task model, added first migration for Task model --- task/migrations/0001_initial.py | 24 ++++++++++++++++++++++++ task/models.py | 7 ++----- 2 files changed, 26 insertions(+), 5 deletions(-) create mode 100644 task/migrations/0001_initial.py diff --git a/task/migrations/0001_initial.py b/task/migrations/0001_initial.py new file mode 100644 index 0000000..91ee613 --- /dev/null +++ b/task/migrations/0001_initial.py @@ -0,0 +1,24 @@ +# Generated by Django 3.1.7 on 2021-03-11 12:46 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ] + + operations = [ + migrations.CreateModel( + name='Task', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('title', models.CharField(max_length=30)), + ('description', models.TextField(max_length=256)), + ('is_completed', models.BooleanField(default=False)), + ('deadline', models.DateField()), + ], + ), + ] diff --git a/task/models.py b/task/models.py index 5b2f7ec..39cee70 100644 --- a/task/models.py +++ b/task/models.py @@ -1,14 +1,11 @@ from django.db import models -from django.conf import settings -# Create your models here. class Task(models.Model): - title = models.CharField(max_length=30, - required=True) + title = models.CharField(max_length=30) description = models.TextField(max_length=256) is_completed = models.BooleanField(default=False) - deadline = models.DateField(required=True) + deadline = models.DateField() def __str__(self): return self.title From 293d875755477146462568fab79feec0d440f71b Mon Sep 17 00:00:00 2001 From: SLDem Date: Thu, 11 Mar 2021 15:13:19 +0200 Subject: [PATCH 017/100] Added crud to custom_user app --- custom_user/models.py | 14 +++++++++++++- custom_user/urls.py | 2 +- custom_user/views.py | 1 + 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/custom_user/models.py b/custom_user/models.py index 340fb87..3acf372 100644 --- a/custom_user/models.py +++ b/custom_user/models.py @@ -31,7 +31,7 @@ class CustomUser(AbstractBaseUser): EMAIL_FIELD = 'email' def __str__(self): - return str(self.first_name) + str(self.last_name) + return str(self.first_name) + ' ' + str(self.last_name) def to_dict(self): return {'first_name': self.first_name, @@ -45,3 +45,15 @@ def find_by_id(cls, user_id): return user except CustomUser.DoesNotExist: return None + + @classmethod + def update_user(cls, user_id, data): + user = CustomUser.find_by_id(user_id) + for key, value in data.items(): + setattr(user, key, value) + user.save() + + @classmethod + def delete_user(cls, user_id): + user = CustomUser.find_by_id(user_id) + user.delete() diff --git a/custom_user/urls.py b/custom_user/urls.py index db4d6fa..f55a144 100644 --- a/custom_user/urls.py +++ b/custom_user/urls.py @@ -3,5 +3,5 @@ from . import views urlpatterns = [ - path('profile//', views.ProfileView, name='profile'), + path('profile//', views.ProfileView.as_view(), name='profile'), ] diff --git a/custom_user/views.py b/custom_user/views.py index 6795ee7..19b7986 100644 --- a/custom_user/views.py +++ b/custom_user/views.py @@ -11,3 +11,4 @@ def get(self, request, user_id=None): if user: return JsonResponse(user.to_dict()) return HttpResponse(status=400) + From 08499a7c1c60166845edc0d749b60eea7fdf8af9 Mon Sep 17 00:00:00 2001 From: AndriiRomaniuk Date: Thu, 11 Mar 2021 16:54:07 +0200 Subject: [PATCH 018/100] Finished Task models, created API views for Tasks --- project_config/urls.py | 1 + task/migrations/0001_initial.py | 30 +++++++++++++ task/models.py | 8 ++-- task/urls.py | 9 ++++ task/views.py | 74 +++++++++++++++++++++++++++++++++ 5 files changed, 119 insertions(+), 3 deletions(-) create mode 100644 task/migrations/0001_initial.py create mode 100644 task/urls.py create mode 100644 task/views.py diff --git a/project_config/urls.py b/project_config/urls.py index e12de93..cb05c92 100644 --- a/project_config/urls.py +++ b/project_config/urls.py @@ -18,4 +18,5 @@ urlpatterns = [ path('custom_user/', include('custom_user.urls')), path('todolist/', include('todolist.urls')), + path('tasks/', include('task.urls')), ] diff --git a/task/migrations/0001_initial.py b/task/migrations/0001_initial.py new file mode 100644 index 0000000..34f6df2 --- /dev/null +++ b/task/migrations/0001_initial.py @@ -0,0 +1,30 @@ +# Generated by Django 3.1.7 on 2021-03-11 13:29 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('todolist', '0002_todolist_members'), + ] + + operations = [ + migrations.CreateModel( + name='Task', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('title', models.CharField(max_length=30)), + ('description', models.TextField(max_length=256)), + ('is_completed', models.BooleanField(default=False)), + ('deadline', models.DateField()), + ('list_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='todolist.todolist')), + ('user_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ], + ), + ] diff --git a/task/models.py b/task/models.py index 5b2f7ec..13dbfd1 100644 --- a/task/models.py +++ b/task/models.py @@ -1,14 +1,16 @@ from django.db import models from django.conf import settings +from todolist.models import ToDoList # Create your models here. class Task(models.Model): - title = models.CharField(max_length=30, - required=True) + title = models.CharField(max_length=30) description = models.TextField(max_length=256) is_completed = models.BooleanField(default=False) - deadline = models.DateField(required=True) + deadline = models.DateField() + user_id = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE) + list_id = models.ForeignKey(ToDoList, on_delete=models.CASCADE) def __str__(self): return self.title diff --git a/task/urls.py b/task/urls.py new file mode 100644 index 0000000..f154fbd --- /dev/null +++ b/task/urls.py @@ -0,0 +1,9 @@ +from django.urls import path + +from .views import TaskAPIView + +urlpatterns = [ + path('by_list/', TaskAPIView.as_view()), + path('', TaskAPIView.as_view()), + path('delete/', TaskAPIView.as_view()), +] \ No newline at end of file diff --git a/task/views.py b/task/views.py new file mode 100644 index 0000000..dee2583 --- /dev/null +++ b/task/views.py @@ -0,0 +1,74 @@ +from django.http import JsonResponse +from django.views import View +from task.models import Task +from todolist.models import ToDoList +from django.core.serializers import serialize +import json +from custom_user.models import CustomUser + + +class TaskAPIView(View): + + def get(self, request, list_id): + task = Task.objects.filter(list_id=list_id) + + task_serialized_data = serialize('python', task) + + data = { + 'tasks': task_serialized_data, + } + return JsonResponse(data) + + def post(self, request, list_id): + post_body = json.loads(request.body) + + title = post_body.get('title') + description = post_body.get('description') + deadline = post_body.get('deadline') + _user_id = post_body.get('user_id') + _list_id = post_body.get('list_id') + + user_id = CustomUser.find_by_id(_user_id) + list_id = ToDoList.objects.get(id=_list_id) + + + task_data = { + 'title': title, + 'description': description, + 'deadline': deadline, + 'user_id': user_id, + 'list_id': list_id, + } + + task_obj = Task.objects.create(**task_data) + task_obj.save() + data = { + 'message': f'New task object has been created with id {task_obj.id}' + } + return JsonResponse(data, status=201) + + def put(self, request, task_id): + task = Task.objects.get(id=task_id) + + put_body = json.loads(request.body) + + task.title = put_body.get('title') + task.description = put_body.get('description') + task.deadline = put_body.get('deadline') + task.user_id = CustomUser.find_by_id(put_body.get('user_id')) + task.list_id = ToDoList.objects.get(id=put_body.get('list_id')) + task.is_completed = put_body.get('is_completed') + task.save() + + data = { + 'message': f'Task {task_id} has been updated' + } + return JsonResponse(data) + + def delete(self, request, task_id): + Task.objects.get(id=task_id).delete() + + data = { + 'message': f'Task with id {task_id} has been deleted' + } + return JsonResponse(data) \ No newline at end of file From 49e12e690b116461ce3897b7349242e6aa14ef24 Mon Sep 17 00:00:00 2001 From: Nevmerzhitsky Yura Date: Thu, 11 Mar 2021 16:59:47 +0200 Subject: [PATCH 019/100] Add put, delete method --- custom_user/views.py | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/custom_user/views.py b/custom_user/views.py index 6795ee7..5feed14 100644 --- a/custom_user/views.py +++ b/custom_user/views.py @@ -11,3 +11,26 @@ def get(self, request, user_id=None): if user: return JsonResponse(user.to_dict()) return HttpResponse(status=400) + + def put(self, request, user_id=None, first_name=None, last_name=None, email=None): + user = CustomUser.find_by_id(user_id) + if user: + if first_name == None: + first_name = user.first_name + if last_name == None: + last_name = user.last_name + if email == None: + email = user.email + data = {'first_name' : first_name, 'last_name' : last_name, 'email' : email} + CustomUser.update_user(user_id, data) + return redirect('profile', user_id=user.pk) + return HttpResponse(status=400) + + def delete(self, request, user_id=None): + user = CustomUser.find_by_id(user_id) + if user: + user = CustomUser.delete_user(user_id) + return HttpResponse("User was deleted") + return HttpResponse(status=400) + + From aafdb6207a76678050e84575b7758d4d78dd0e7c Mon Sep 17 00:00:00 2001 From: AndriiRomaniuk Date: Thu, 11 Mar 2021 17:17:11 +0200 Subject: [PATCH 020/100] minor edits --- task/urls.py | 1 + task/views.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/task/urls.py b/task/urls.py index f154fbd..6387a00 100644 --- a/task/urls.py +++ b/task/urls.py @@ -4,6 +4,7 @@ urlpatterns = [ path('by_list/', TaskAPIView.as_view()), + path('', TaskAPIView.as_view()), path('', TaskAPIView.as_view()), path('delete/', TaskAPIView.as_view()), ] \ No newline at end of file diff --git a/task/views.py b/task/views.py index dee2583..ea7f6d8 100644 --- a/task/views.py +++ b/task/views.py @@ -19,7 +19,7 @@ def get(self, request, list_id): } return JsonResponse(data) - def post(self, request, list_id): + def post(self, request): post_body = json.loads(request.body) title = post_body.get('title') From 79b2782afa014e7bbb67026df3de26bda41d2bb8 Mon Sep 17 00:00:00 2001 From: AndriiRomaniuk Date: Thu, 11 Mar 2021 17:47:36 +0200 Subject: [PATCH 021/100] minor edits --- task/urls.py | 2 -- task/views.py | 12 +++++++++--- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/task/urls.py b/task/urls.py index 6387a00..423aa6b 100644 --- a/task/urls.py +++ b/task/urls.py @@ -5,6 +5,4 @@ urlpatterns = [ path('by_list/', TaskAPIView.as_view()), path('', TaskAPIView.as_view()), - path('', TaskAPIView.as_view()), - path('delete/', TaskAPIView.as_view()), ] \ No newline at end of file diff --git a/task/views.py b/task/views.py index ea7f6d8..fcb3ed6 100644 --- a/task/views.py +++ b/task/views.py @@ -47,11 +47,13 @@ def post(self, request): } return JsonResponse(data, status=201) - def put(self, request, task_id): - task = Task.objects.get(id=task_id) + def put(self, request): put_body = json.loads(request.body) + task_id = put_body.get('task_id') + task = Task.objects.get(id=task_id) + task.title = put_body.get('title') task.description = put_body.get('description') task.deadline = put_body.get('deadline') @@ -65,7 +67,11 @@ def put(self, request, task_id): } return JsonResponse(data) - def delete(self, request, task_id): + def delete(self, request): + + delete_body = json.loads(request.body) + + task_id = delete_body.get('task_id') Task.objects.get(id=task_id).delete() data = { From 167da383639e3c0693282c6a2dee84d5244e56f3 Mon Sep 17 00:00:00 2001 From: Nevmerzhitsky Yura Date: Thu, 11 Mar 2021 17:52:07 +0200 Subject: [PATCH 022/100] Add post method in views clas --- custom_user/views.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/custom_user/views.py b/custom_user/views.py index 5feed14..8ef3723 100644 --- a/custom_user/views.py +++ b/custom_user/views.py @@ -23,7 +23,7 @@ def put(self, request, user_id=None, first_name=None, last_name=None, email=None email = user.email data = {'first_name' : first_name, 'last_name' : last_name, 'email' : email} CustomUser.update_user(user_id, data) - return redirect('profile', user_id=user.pk) + return redirect('profile', user_id=user.id) return HttpResponse(status=400) def delete(self, request, user_id=None): @@ -33,4 +33,11 @@ def delete(self, request, user_id=None): return HttpResponse("User was deleted") return HttpResponse(status=400) + def post(self, first_name=None, last_name=None, email=None): + if first_name == None: + return 'Please, add first name' + if last_name == None: + return 'Please add last name' + user = CustomUser.objects.create_user(first_name, last_name, email) + return redirect('profile', user_id=user.id) From 9b1f9181db165801f6a1505ef7fe66073b9b3686 Mon Sep 17 00:00:00 2001 From: Daniil Oleshchuk Date: Thu, 11 Mar 2021 19:09:12 +0200 Subject: [PATCH 023/100] added CRUD for ToDoList model --- todolist/models.py | 54 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/todolist/models.py b/todolist/models.py index dc7c920..3d93c6b 100644 --- a/todolist/models.py +++ b/todolist/models.py @@ -8,3 +8,57 @@ class ToDoList(models.Model): description = models.CharField(max_length=500, default='No description for now.') members = models.ManyToManyField(CustomUser, related_name='todo_lists') + + def __str__(self): + return self.name + + def to_dict(self): + return {'name': self.name, + 'description': self.description, + 'members': self.members} + + @classmethod + def get_by_id(cls, todo_list_pk: int): + try: + todo_list = ToDoList.objects.get(pk=todo_list_pk) + return todo_list + except ToDoList.DoesNotExist: + return None + + @classmethod + def get_all(cls): + try: + todo_lists = ToDoList.objects.all() + return todo_lists + except ToDoList.DoesNotExist: + return None + + @classmethod + def create(cls, name: str, description: str, member_pk): + todo_list = ToDoList(name=name, description=description) + todo_list.save() + member_to_add = CustomUser.find_by_id(user_id=member_pk) + todo_list.members.add(member_to_add) + todo_list.save() + return todo_list + + @classmethod + def update(cls, todo_list_pk: int, data: dict): + todo_list = cls.get_by_id(todo_list_pk) + if todo_list: + for field, value in data.items(): + if hasattr(todo_list, field): + setattr(todo_list, field, value) + else: + raise KeyError("Failed to update non existing attribute {}.{}".format( + todo_list.__class__.__name__, field)) + todo_list.save() + return cls.get_by_id(todo_list_pk=todo_list_pk) + return None + + @classmethod + def remove(cls, todo_list_pk: int): + todo_list = cls.get_by_id(todo_list_pk=todo_list_pk) + if todo_list: + todo_list.delete() + return not cls.get_by_id(todo_list_pk=todo_list_pk) From 6e25af05875f9a56f984222e4f59d9f15c6a3df8 Mon Sep 17 00:00:00 2001 From: OstapOmelchuk Date: Thu, 11 Mar 2021 22:16:35 +0200 Subject: [PATCH 024/100] Add CRUD for task app --- task/models.py | 53 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/task/models.py b/task/models.py index 13dbfd1..94208ee 100644 --- a/task/models.py +++ b/task/models.py @@ -1,6 +1,7 @@ from django.db import models from django.conf import settings from todolist.models import ToDoList +from custom_user.models import CustomUser # Create your models here. @@ -14,3 +15,55 @@ class Task(models.Model): def __str__(self): return self.title + + @classmethod + def find_by_id(cls, task_id: int): + try: + task = Task.objects.get(pk=task_id) + return task + except Task.DoesNotExist: + return None + + @classmethod + def get_all(cls): + try: + task = Task.objects.all() + return task + except Task.DoesNotExist: + return None + + @classmethod + def find_all_for_list(cls, list_id): + tasks = Task.objects.filter(list_id=list_id) + return tasks + + @classmethod + def create(cls, title: str, description: str, deadline, user_id, task_id): + task = Task(title=title, description=description, deadline=deadline) + user = CustomUser.find_by_id(user_id) + task.user_id = user + list = ToDoList.get_by_id(task_id) + task.list_id = list + task.save() + return task + + @classmethod + def update(cls, task_id: int, data: dict): + task = cls.find_by_id(task_id) + if task: + for field, value in data.items(): + if hasattr(task, field): + setattr(task, field, value) + else: + raise KeyError("Failed to update non existing attribute {}.{}".format( + task.__class__.__name__, field)) + task.save() + return cls.find_by_id(task_id=task_id) + return None + + @classmethod + def remove(cls, task_id: int): + task = cls.find_by_id(task_id=task_id) + if task: + task.delete() + return not cls.find_by_id(task_id=task_id) From f5348dc0ab4d49535272f9898d25289f71f12856 Mon Sep 17 00:00:00 2001 From: OstapOmelchuk Date: Fri, 12 Mar 2021 01:38:02 +0200 Subject: [PATCH 025/100] ADD CRUD FOR TASK APP --- task/models.py | 59 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/task/models.py b/task/models.py index 39cee70..558d05f 100644 --- a/task/models.py +++ b/task/models.py @@ -1,11 +1,70 @@ from django.db import models +from django.conf import settings +from todolist.models import ToDoList +from custom_user.models import CustomUser +# Create your models here. class Task(models.Model): title = models.CharField(max_length=30) description = models.TextField(max_length=256) is_completed = models.BooleanField(default=False) deadline = models.DateField() + user_id = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE) + list_id = models.ForeignKey(ToDoList, on_delete=models.CASCADE) def __str__(self): return self.title + + @classmethod + def find_by_id(cls, task_id: int): + try: + task = Task.objects.get(pk=task_id) + return task + except Task.DoesNotExist: + return None + + @classmethod + def get_all(cls): + try: + task = Task.objects.all() + return task + except Task.DoesNotExist: + return None + + @classmethod + def find_all_for_list(cls, list_id): + tasks = Task.objects.filter(list_id=list_id) + return tasks + + @classmethod + def create(cls, title: str, description: str, deadline, user_id, task_id): + task = Task(title=title, description=description, deadline=deadline) + user = CustomUser.find_by_id(user_id) + task.user_id = user + list = ToDoList.get_by_id(task_id) + task.list_id = list + task.save() + return task + + @classmethod + def update(cls, task_id: int, data: dict): + task = cls.find_by_id(task_id) + if task: + for field, value in data.items(): + if hasattr(task, field): + setattr(task, field, value) + else: + raise KeyError("Failed to update non existing attribute {}.{}".format( + task.__class__.__name__, field)) + task.save() + return cls.find_by_id(task_id=task_id) + return None + + @classmethod + def remove(cls, task_id: int): + task = cls.find_by_id(task_id=task_id) + if task: + task.delete() + return not cls.find_by_id(task_id=task_id) + From a05978a6cf05bbf65caa89af2ad422e30eff5789 Mon Sep 17 00:00:00 2001 From: StepanTchynetskyi Date: Fri, 12 Mar 2021 01:40:42 +0200 Subject: [PATCH 026/100] add ToDoListView --- custom_user/views.py | 1 - todolist/models.py | 3 ++- todolist/urls.py | 8 +++++-- todolist/views.py | 50 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 58 insertions(+), 4 deletions(-) diff --git a/custom_user/views.py b/custom_user/views.py index 19b7986..6795ee7 100644 --- a/custom_user/views.py +++ b/custom_user/views.py @@ -11,4 +11,3 @@ def get(self, request, user_id=None): if user: return JsonResponse(user.to_dict()) return HttpResponse(status=400) - diff --git a/todolist/models.py b/todolist/models.py index 3d93c6b..7602e96 100644 --- a/todolist/models.py +++ b/todolist/models.py @@ -15,7 +15,8 @@ def __str__(self): def to_dict(self): return {'name': self.name, 'description': self.description, - 'members': self.members} + #'members': self.members + } @classmethod def get_by_id(cls, todo_list_pk: int): diff --git a/todolist/urls.py b/todolist/urls.py index 79b2c23..3b86bd3 100644 --- a/todolist/urls.py +++ b/todolist/urls.py @@ -1,7 +1,11 @@ from django.urls import path -from . import views +from . import views urlpatterns = [ - + path('', views.ToDoListView.as_view(), name='all-lists'), + path('list//', views.ToDoListView.as_view(), name='one-list'), + path('/delete/', views.ToDoListView.as_view(), name='delete-list'), + path('/update/', views.ToDoListView.as_view(), name='update-list'), + path('create/', views.ToDoListView.as_view(), name='create-list') ] diff --git a/todolist/views.py b/todolist/views.py index e69de29..3fe411f 100644 --- a/todolist/views.py +++ b/todolist/views.py @@ -0,0 +1,50 @@ +from django.http import JsonResponse, HttpResponse +from django.shortcuts import redirect +from django.views import View + +from .models import CustomUser, ToDoList + + +class ToDoListView(View): + + def get(self, request, todo_list_pk=None): + + if todo_list_pk is None: + todo_lists = ToDoList.get_all() + todo_lists_dict={} + for i,todo_list in enumerate(todo_lists): + todo_lists_dict[i]=todo_list.to_dict() + return JsonResponse(todo_lists_dict) + todo_list = ToDoList.get_by_id(todo_list_pk=todo_list_pk) + + if todo_list: + return JsonResponse(todo_list.to_dict()) + + return HttpResponse(status=400) + + def post(self, request, member_pk, name, description=None): + user = CustomUser.find_by_id(member_pk) + if user: + todo_list = ToDoList.create(name=name, description=description, member_pk=member_pk) + return redirect('one-list',todo_list.to_dict()) + return HttpResponse(status=400) + + def delete(self, request, todo_list_pk=None): + todo_list = ToDoList.get_by_id(todo_list_pk=todo_list_pk) + if todo_list: + todo_list.remove(todo_list_pk=todo_list_pk) + return redirect('all-lists') + return HttpResponse(status=400) + + def put(self, request, todo_list_pk, member_pk=None, name=None, description=None): + todo_list = ToDoList.get_by_id(todo_list_pk=todo_list_pk) + if todo_list: + list_values = {'member': member_pk, + 'name': name, + 'description': description} + for field, value in list_values.items(): + if value is None: + del list_values[field] + todo_list.update(todo_list_pk=todo_list_pk, data=list_values) + return JsonResponse(todo_list.to_dict()) + return HttpResponse(status=400) From ba7bc05ba95088ffaa0699387c8c4f107d754aa6 Mon Sep 17 00:00:00 2001 From: StepanTchynetskyi Date: Fri, 12 Mar 2021 02:37:42 +0200 Subject: [PATCH 027/100] change urls --- todolist/urls.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/todolist/urls.py b/todolist/urls.py index 3b86bd3..84076b3 100644 --- a/todolist/urls.py +++ b/todolist/urls.py @@ -5,7 +5,7 @@ urlpatterns = [ path('', views.ToDoListView.as_view(), name='all-lists'), path('list//', views.ToDoListView.as_view(), name='one-list'), - path('/delete/', views.ToDoListView.as_view(), name='delete-list'), - path('/update/', views.ToDoListView.as_view(), name='update-list'), - path('create/', views.ToDoListView.as_view(), name='create-list') + path('delete-list/', views.ToDoListView.as_view(), name='delete-list'), + path('update-list/', views.ToDoListView.as_view(), name='update-list'), + path('create-list/', views.ToDoListView.as_view(), name='create-list') ] From aafc802b8376bffd6e15ae1cbb71a6615e02de9e Mon Sep 17 00:00:00 2001 From: AndriiRomaniuk Date: Thu, 11 Mar 2021 16:54:07 +0200 Subject: [PATCH 028/100] Finished Task models, created API views for Tasks --- project_config/urls.py | 1 + task/urls.py | 9 +++++ task/views.py | 74 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 84 insertions(+) create mode 100644 task/urls.py create mode 100644 task/views.py diff --git a/project_config/urls.py b/project_config/urls.py index e12de93..cb05c92 100644 --- a/project_config/urls.py +++ b/project_config/urls.py @@ -18,4 +18,5 @@ urlpatterns = [ path('custom_user/', include('custom_user.urls')), path('todolist/', include('todolist.urls')), + path('tasks/', include('task.urls')), ] diff --git a/task/urls.py b/task/urls.py new file mode 100644 index 0000000..f154fbd --- /dev/null +++ b/task/urls.py @@ -0,0 +1,9 @@ +from django.urls import path + +from .views import TaskAPIView + +urlpatterns = [ + path('by_list/', TaskAPIView.as_view()), + path('', TaskAPIView.as_view()), + path('delete/', TaskAPIView.as_view()), +] \ No newline at end of file diff --git a/task/views.py b/task/views.py new file mode 100644 index 0000000..dee2583 --- /dev/null +++ b/task/views.py @@ -0,0 +1,74 @@ +from django.http import JsonResponse +from django.views import View +from task.models import Task +from todolist.models import ToDoList +from django.core.serializers import serialize +import json +from custom_user.models import CustomUser + + +class TaskAPIView(View): + + def get(self, request, list_id): + task = Task.objects.filter(list_id=list_id) + + task_serialized_data = serialize('python', task) + + data = { + 'tasks': task_serialized_data, + } + return JsonResponse(data) + + def post(self, request, list_id): + post_body = json.loads(request.body) + + title = post_body.get('title') + description = post_body.get('description') + deadline = post_body.get('deadline') + _user_id = post_body.get('user_id') + _list_id = post_body.get('list_id') + + user_id = CustomUser.find_by_id(_user_id) + list_id = ToDoList.objects.get(id=_list_id) + + + task_data = { + 'title': title, + 'description': description, + 'deadline': deadline, + 'user_id': user_id, + 'list_id': list_id, + } + + task_obj = Task.objects.create(**task_data) + task_obj.save() + data = { + 'message': f'New task object has been created with id {task_obj.id}' + } + return JsonResponse(data, status=201) + + def put(self, request, task_id): + task = Task.objects.get(id=task_id) + + put_body = json.loads(request.body) + + task.title = put_body.get('title') + task.description = put_body.get('description') + task.deadline = put_body.get('deadline') + task.user_id = CustomUser.find_by_id(put_body.get('user_id')) + task.list_id = ToDoList.objects.get(id=put_body.get('list_id')) + task.is_completed = put_body.get('is_completed') + task.save() + + data = { + 'message': f'Task {task_id} has been updated' + } + return JsonResponse(data) + + def delete(self, request, task_id): + Task.objects.get(id=task_id).delete() + + data = { + 'message': f'Task with id {task_id} has been deleted' + } + return JsonResponse(data) \ No newline at end of file From 45aa1f4a5d2d135a374acb31249da5ac0268176f Mon Sep 17 00:00:00 2001 From: OstapOmelchuk Date: Fri, 12 Mar 2021 11:11:02 +0200 Subject: [PATCH 029/100] Changed CRUD for task app -> user_id --- task/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/task/models.py b/task/models.py index 558d05f..42f6464 100644 --- a/task/models.py +++ b/task/models.py @@ -10,7 +10,7 @@ class Task(models.Model): description = models.TextField(max_length=256) is_completed = models.BooleanField(default=False) deadline = models.DateField() - user_id = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE) + user_id = models.ForeignKey(CustomUser on_delete=models.CASCADE) list_id = models.ForeignKey(ToDoList, on_delete=models.CASCADE) def __str__(self): From c7d98f857c03fc7b36abbadf456db0db5710ba9a Mon Sep 17 00:00:00 2001 From: AndriiRomaniuk Date: Thu, 11 Mar 2021 17:17:11 +0200 Subject: [PATCH 030/100] minor edits --- task/urls.py | 3 +-- task/views.py | 14 ++++++++++---- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/task/urls.py b/task/urls.py index f154fbd..423aa6b 100644 --- a/task/urls.py +++ b/task/urls.py @@ -4,6 +4,5 @@ urlpatterns = [ path('by_list/', TaskAPIView.as_view()), - path('', TaskAPIView.as_view()), - path('delete/', TaskAPIView.as_view()), + path('', TaskAPIView.as_view()), ] \ No newline at end of file diff --git a/task/views.py b/task/views.py index dee2583..fcb3ed6 100644 --- a/task/views.py +++ b/task/views.py @@ -19,7 +19,7 @@ def get(self, request, list_id): } return JsonResponse(data) - def post(self, request, list_id): + def post(self, request): post_body = json.loads(request.body) title = post_body.get('title') @@ -47,11 +47,13 @@ def post(self, request, list_id): } return JsonResponse(data, status=201) - def put(self, request, task_id): - task = Task.objects.get(id=task_id) + def put(self, request): put_body = json.loads(request.body) + task_id = put_body.get('task_id') + task = Task.objects.get(id=task_id) + task.title = put_body.get('title') task.description = put_body.get('description') task.deadline = put_body.get('deadline') @@ -65,7 +67,11 @@ def put(self, request, task_id): } return JsonResponse(data) - def delete(self, request, task_id): + def delete(self, request): + + delete_body = json.loads(request.body) + + task_id = delete_body.get('task_id') Task.objects.get(id=task_id).delete() data = { From c38ab703152eda0cd3bdd6f546175a17ddda0790 Mon Sep 17 00:00:00 2001 From: AndriiRomaniuk Date: Fri, 12 Mar 2021 11:16:44 +0200 Subject: [PATCH 031/100] Migrations fix, updated task model --- task/migrations/0002_auto_20210312_0909.py | 29 ++++++++++++++++++++++ task/models.py | 4 +++ 2 files changed, 33 insertions(+) create mode 100644 task/migrations/0002_auto_20210312_0909.py diff --git a/task/migrations/0002_auto_20210312_0909.py b/task/migrations/0002_auto_20210312_0909.py new file mode 100644 index 0000000..9595d33 --- /dev/null +++ b/task/migrations/0002_auto_20210312_0909.py @@ -0,0 +1,29 @@ +# Generated by Django 3.1.7 on 2021-03-12 09:09 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('todolist', '0002_todolist_members'), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('task', '0001_initial'), + ] + + operations = [ + migrations.AddField( + model_name='task', + name='list_id', + field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, to='todolist.todolist'), + preserve_default=False, + ), + migrations.AddField( + model_name='task', + name='user_id', + field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, to='custom_user.customuser'), + preserve_default=False, + ), + ] diff --git a/task/models.py b/task/models.py index 39cee70..34a203b 100644 --- a/task/models.py +++ b/task/models.py @@ -1,4 +1,6 @@ from django.db import models +from custom_user.models import CustomUser +from todolist.models import ToDoList class Task(models.Model): @@ -6,6 +8,8 @@ class Task(models.Model): description = models.TextField(max_length=256) is_completed = models.BooleanField(default=False) deadline = models.DateField() + user_id = models.ForeignKey(CustomUser, on_delete=models.CASCADE) + list_id = models.ForeignKey(ToDoList, on_delete=models.CASCADE) def __str__(self): return self.title From 205f81d7c480ae3c3f2b65e5eb3194b0ec2bfcf3 Mon Sep 17 00:00:00 2001 From: OstapOmelchuk Date: Fri, 12 Mar 2021 11:44:46 +0200 Subject: [PATCH 032/100] Changed CRUD for task app -> user_id --- task/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/task/models.py b/task/models.py index 42f6464..d11de66 100644 --- a/task/models.py +++ b/task/models.py @@ -10,7 +10,7 @@ class Task(models.Model): description = models.TextField(max_length=256) is_completed = models.BooleanField(default=False) deadline = models.DateField() - user_id = models.ForeignKey(CustomUser on_delete=models.CASCADE) + user_id = models.ForeignKey(CustomUser, on_delete=models.CASCADE) list_id = models.ForeignKey(ToDoList, on_delete=models.CASCADE) def __str__(self): From a6d09b668a1b40e5f66b78b44d93c62bde867b0d Mon Sep 17 00:00:00 2001 From: SLDem Date: Fri, 12 Mar 2021 13:07:57 +0200 Subject: [PATCH 033/100] Fixed model methods --- custom_user/models.py | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/custom_user/models.py b/custom_user/models.py index 3acf372..e442d19 100644 --- a/custom_user/models.py +++ b/custom_user/models.py @@ -1,4 +1,5 @@ from django.contrib.auth.models import AbstractBaseUser, BaseUserManager +from django.http import HttpResponse from django.db import models @@ -46,14 +47,15 @@ def find_by_id(cls, user_id): except CustomUser.DoesNotExist: return None - @classmethod - def update_user(cls, user_id, data): - user = CustomUser.find_by_id(user_id) - for key, value in data.items(): - setattr(user, key, value) - user.save() + def update(self, data): + self.first_name = data['first_name'] + self.last_name = data['last_name'] + self.email = data['email'] + self.save() + return True @classmethod - def delete_user(cls, user_id): - user = CustomUser.find_by_id(user_id) + def delete(cls, user_id): + user = cls.find_by_id(user_id) user.delete() + return HttpResponse('User removed.') From a5caef55acf565e2ca23581476d3c28a55f871c7 Mon Sep 17 00:00:00 2001 From: SLDem Date: Fri, 12 Mar 2021 13:38:37 +0200 Subject: [PATCH 034/100] Added test folders --- tests/custom_user/tests.py | 0 tests/task/tests.py | 0 tests/todolist/tests.py | 0 3 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 tests/custom_user/tests.py create mode 100644 tests/task/tests.py create mode 100644 tests/todolist/tests.py diff --git a/tests/custom_user/tests.py b/tests/custom_user/tests.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/task/tests.py b/tests/task/tests.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/todolist/tests.py b/tests/todolist/tests.py new file mode 100644 index 0000000..e69de29 From da33827b43ea97ca9db5d99ffdec57413cdf5738 Mon Sep 17 00:00:00 2001 From: OstapOmelchuk Date: Fri, 12 Mar 2021 15:00:44 +0200 Subject: [PATCH 035/100] Changed remove and update methods --- task/models.py | 35 +++++++++++++++++------------------ 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/task/models.py b/task/models.py index d11de66..ed0810d 100644 --- a/task/models.py +++ b/task/models.py @@ -2,6 +2,7 @@ from django.conf import settings from todolist.models import ToDoList from custom_user.models import CustomUser +from datetime import datetime # Create your models here. @@ -47,24 +48,22 @@ def create(cls, title: str, description: str, deadline, user_id, task_id): task.save() return task - @classmethod - def update(cls, task_id: int, data: dict): - task = cls.find_by_id(task_id) - if task: - for field, value in data.items(): - if hasattr(task, field): - setattr(task, field, value) - else: - raise KeyError("Failed to update non existing attribute {}.{}".format( - task.__class__.__name__, field)) - task.save() - return cls.find_by_id(task_id=task_id) - return None + def update(self, new_title: str, new_description: str, is_completed: bool, new_deadline: datetime, user_id: int, list_id: int): + task = Task(new_title, new_description, new_deadline) + task.title = new_title + task.description = new_description + task.deadline = new_deadline + task.is_completed = is_completed + task.user_id = CustomUser.find_by_id(user_id) + task.list_id = ToDoList.get_by_id(list_id) + task.save() + return task @classmethod - def remove(cls, task_id: int): - task = cls.find_by_id(task_id=task_id) - if task: + def remove(cls, user_id): + try: + task = Task.objects.get(user_id=user_id) task.delete() - return not cls.find_by_id(task_id=task_id) - + except Task.DoesNotExist: + return False + return True From fb17ac462fc030141ac8ce190e39fe835285198c Mon Sep 17 00:00:00 2001 From: OstapOmelchuk Date: Fri, 12 Mar 2021 15:07:55 +0200 Subject: [PATCH 036/100] 1 --- custom_user/models.py | 18 +++---- custom_user/views.py | 5 +- task/migrations/0002_auto_20210312_0909.py | 29 ++++++++++++ task/models.py | 3 +- tests/custom_user/tests.py | 0 tests/task/tests.py | 0 tests/todolist/tests.py | 0 todolist/models.py | 55 ++++++++++++++++++++++ todolist/urls.py | 8 +++- todolist/views.py | 50 ++++++++++++++++++++ 10 files changed, 155 insertions(+), 13 deletions(-) create mode 100644 task/migrations/0002_auto_20210312_0909.py create mode 100644 tests/custom_user/tests.py create mode 100644 tests/task/tests.py create mode 100644 tests/todolist/tests.py diff --git a/custom_user/models.py b/custom_user/models.py index 3acf372..e442d19 100644 --- a/custom_user/models.py +++ b/custom_user/models.py @@ -1,4 +1,5 @@ from django.contrib.auth.models import AbstractBaseUser, BaseUserManager +from django.http import HttpResponse from django.db import models @@ -46,14 +47,15 @@ def find_by_id(cls, user_id): except CustomUser.DoesNotExist: return None - @classmethod - def update_user(cls, user_id, data): - user = CustomUser.find_by_id(user_id) - for key, value in data.items(): - setattr(user, key, value) - user.save() + def update(self, data): + self.first_name = data['first_name'] + self.last_name = data['last_name'] + self.email = data['email'] + self.save() + return True @classmethod - def delete_user(cls, user_id): - user = CustomUser.find_by_id(user_id) + def delete(cls, user_id): + user = cls.find_by_id(user_id) user.delete() + return HttpResponse('User removed.') diff --git a/custom_user/views.py b/custom_user/views.py index 8ef3723..021b2ee 100644 --- a/custom_user/views.py +++ b/custom_user/views.py @@ -1,3 +1,4 @@ +from django.shortcuts import redirect from django.views import View from django.http import JsonResponse, HttpResponse @@ -35,9 +36,9 @@ def delete(self, request, user_id=None): def post(self, first_name=None, last_name=None, email=None): if first_name == None: - return 'Please, add first name' + return HttpResponse('Please, add first name') if last_name == None: - return 'Please add last name' + return HttpResponse('Please add last name') user = CustomUser.objects.create_user(first_name, last_name, email) return redirect('profile', user_id=user.id) diff --git a/task/migrations/0002_auto_20210312_0909.py b/task/migrations/0002_auto_20210312_0909.py new file mode 100644 index 0000000..9595d33 --- /dev/null +++ b/task/migrations/0002_auto_20210312_0909.py @@ -0,0 +1,29 @@ +# Generated by Django 3.1.7 on 2021-03-12 09:09 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('todolist', '0002_todolist_members'), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('task', '0001_initial'), + ] + + operations = [ + migrations.AddField( + model_name='task', + name='list_id', + field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, to='todolist.todolist'), + preserve_default=False, + ), + migrations.AddField( + model_name='task', + name='user_id', + field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, to='custom_user.customuser'), + preserve_default=False, + ), + ] diff --git a/task/models.py b/task/models.py index 94208ee..91c0d50 100644 --- a/task/models.py +++ b/task/models.py @@ -4,13 +4,13 @@ from custom_user.models import CustomUser -# Create your models here. class Task(models.Model): title = models.CharField(max_length=30) description = models.TextField(max_length=256) is_completed = models.BooleanField(default=False) deadline = models.DateField() user_id = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE) + user_id = models.ForeignKey(CustomUser, on_delete=models.CASCADE) list_id = models.ForeignKey(ToDoList, on_delete=models.CASCADE) def __str__(self): @@ -67,3 +67,4 @@ def remove(cls, task_id: int): if task: task.delete() return not cls.find_by_id(task_id=task_id) + diff --git a/tests/custom_user/tests.py b/tests/custom_user/tests.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/task/tests.py b/tests/task/tests.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/todolist/tests.py b/tests/todolist/tests.py new file mode 100644 index 0000000..e69de29 diff --git a/todolist/models.py b/todolist/models.py index dc7c920..7602e96 100644 --- a/todolist/models.py +++ b/todolist/models.py @@ -8,3 +8,58 @@ class ToDoList(models.Model): description = models.CharField(max_length=500, default='No description for now.') members = models.ManyToManyField(CustomUser, related_name='todo_lists') + + def __str__(self): + return self.name + + def to_dict(self): + return {'name': self.name, + 'description': self.description, + #'members': self.members + } + + @classmethod + def get_by_id(cls, todo_list_pk: int): + try: + todo_list = ToDoList.objects.get(pk=todo_list_pk) + return todo_list + except ToDoList.DoesNotExist: + return None + + @classmethod + def get_all(cls): + try: + todo_lists = ToDoList.objects.all() + return todo_lists + except ToDoList.DoesNotExist: + return None + + @classmethod + def create(cls, name: str, description: str, member_pk): + todo_list = ToDoList(name=name, description=description) + todo_list.save() + member_to_add = CustomUser.find_by_id(user_id=member_pk) + todo_list.members.add(member_to_add) + todo_list.save() + return todo_list + + @classmethod + def update(cls, todo_list_pk: int, data: dict): + todo_list = cls.get_by_id(todo_list_pk) + if todo_list: + for field, value in data.items(): + if hasattr(todo_list, field): + setattr(todo_list, field, value) + else: + raise KeyError("Failed to update non existing attribute {}.{}".format( + todo_list.__class__.__name__, field)) + todo_list.save() + return cls.get_by_id(todo_list_pk=todo_list_pk) + return None + + @classmethod + def remove(cls, todo_list_pk: int): + todo_list = cls.get_by_id(todo_list_pk=todo_list_pk) + if todo_list: + todo_list.delete() + return not cls.get_by_id(todo_list_pk=todo_list_pk) diff --git a/todolist/urls.py b/todolist/urls.py index 79b2c23..84076b3 100644 --- a/todolist/urls.py +++ b/todolist/urls.py @@ -1,7 +1,11 @@ from django.urls import path -from . import views +from . import views urlpatterns = [ - + path('', views.ToDoListView.as_view(), name='all-lists'), + path('list//', views.ToDoListView.as_view(), name='one-list'), + path('delete-list/', views.ToDoListView.as_view(), name='delete-list'), + path('update-list/', views.ToDoListView.as_view(), name='update-list'), + path('create-list/', views.ToDoListView.as_view(), name='create-list') ] diff --git a/todolist/views.py b/todolist/views.py index e69de29..3fe411f 100644 --- a/todolist/views.py +++ b/todolist/views.py @@ -0,0 +1,50 @@ +from django.http import JsonResponse, HttpResponse +from django.shortcuts import redirect +from django.views import View + +from .models import CustomUser, ToDoList + + +class ToDoListView(View): + + def get(self, request, todo_list_pk=None): + + if todo_list_pk is None: + todo_lists = ToDoList.get_all() + todo_lists_dict={} + for i,todo_list in enumerate(todo_lists): + todo_lists_dict[i]=todo_list.to_dict() + return JsonResponse(todo_lists_dict) + todo_list = ToDoList.get_by_id(todo_list_pk=todo_list_pk) + + if todo_list: + return JsonResponse(todo_list.to_dict()) + + return HttpResponse(status=400) + + def post(self, request, member_pk, name, description=None): + user = CustomUser.find_by_id(member_pk) + if user: + todo_list = ToDoList.create(name=name, description=description, member_pk=member_pk) + return redirect('one-list',todo_list.to_dict()) + return HttpResponse(status=400) + + def delete(self, request, todo_list_pk=None): + todo_list = ToDoList.get_by_id(todo_list_pk=todo_list_pk) + if todo_list: + todo_list.remove(todo_list_pk=todo_list_pk) + return redirect('all-lists') + return HttpResponse(status=400) + + def put(self, request, todo_list_pk, member_pk=None, name=None, description=None): + todo_list = ToDoList.get_by_id(todo_list_pk=todo_list_pk) + if todo_list: + list_values = {'member': member_pk, + 'name': name, + 'description': description} + for field, value in list_values.items(): + if value is None: + del list_values[field] + todo_list.update(todo_list_pk=todo_list_pk, data=list_values) + return JsonResponse(todo_list.to_dict()) + return HttpResponse(status=400) From cd89027ac0e2c574fa26876bbf3ddc3ec6f4927c Mon Sep 17 00:00:00 2001 From: StepanTchynetskyi Date: Fri, 12 Mar 2021 15:31:29 +0200 Subject: [PATCH 037/100] fix ToDoListView --- todolist/urls.py | 4 ++-- todolist/views.py | 27 ++++++++++++++++++++------- 2 files changed, 22 insertions(+), 9 deletions(-) diff --git a/todolist/urls.py b/todolist/urls.py index 84076b3..1d41cce 100644 --- a/todolist/urls.py +++ b/todolist/urls.py @@ -5,7 +5,7 @@ urlpatterns = [ path('', views.ToDoListView.as_view(), name='all-lists'), path('list//', views.ToDoListView.as_view(), name='one-list'), - path('delete-list/', views.ToDoListView.as_view(), name='delete-list'), - path('update-list/', views.ToDoListView.as_view(), name='update-list'), + path('delete-list//', views.ToDoListView.as_view(), name='delete-list'), + path('update-list//', views.ToDoListView.as_view(), name='update-list'), path('create-list/', views.ToDoListView.as_view(), name='create-list') ] diff --git a/todolist/views.py b/todolist/views.py index 3fe411f..9884b91 100644 --- a/todolist/views.py +++ b/todolist/views.py @@ -1,5 +1,5 @@ from django.http import JsonResponse, HttpResponse -from django.shortcuts import redirect +import json from django.views import View from .models import CustomUser, ToDoList @@ -22,24 +22,37 @@ def get(self, request, todo_list_pk=None): return HttpResponse(status=400) - def post(self, request, member_pk, name, description=None): + def post(self, request): + body = json.loads(request.body) + + name = body.get("name") + description = body.get("description") + member_pk = body.get("member_pk") user = CustomUser.find_by_id(member_pk) + print(user) if user: todo_list = ToDoList.create(name=name, description=description, member_pk=member_pk) - return redirect('one-list',todo_list.to_dict()) + return JsonResponse(todo_list.to_dict()) return HttpResponse(status=400) - def delete(self, request, todo_list_pk=None): + def delete(self, request,todo_list_pk=None): + todo_list = ToDoList.get_by_id(todo_list_pk=todo_list_pk) if todo_list: todo_list.remove(todo_list_pk=todo_list_pk) - return redirect('all-lists') + return HttpResponse(status=200) return HttpResponse(status=400) - def put(self, request, todo_list_pk, member_pk=None, name=None, description=None): + def put(self, request,todo_list_pk=None): + body = json.loads(request.body) + + name = body.get('name') + description = body.get('description') + member_pk = body.get('members') + members = CustomUser.objects.filter(id__in = member_pk ) todo_list = ToDoList.get_by_id(todo_list_pk=todo_list_pk) if todo_list: - list_values = {'member': member_pk, + list_values = {'members': members, 'name': name, 'description': description} for field, value in list_values.items(): From 3ced2f8f692176598cd6229661372582d75d6dda Mon Sep 17 00:00:00 2001 From: StepanTchynetskyi Date: Fri, 12 Mar 2021 15:58:59 +0200 Subject: [PATCH 038/100] pylint fix --- todolist/views.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/todolist/views.py b/todolist/views.py index 9884b91..8e27d39 100644 --- a/todolist/views.py +++ b/todolist/views.py @@ -1,5 +1,6 @@ -from django.http import JsonResponse, HttpResponse import json + +from django.http import JsonResponse, HttpResponse from django.views import View from .models import CustomUser, ToDoList @@ -11,9 +12,9 @@ def get(self, request, todo_list_pk=None): if todo_list_pk is None: todo_lists = ToDoList.get_all() - todo_lists_dict={} - for i,todo_list in enumerate(todo_lists): - todo_lists_dict[i]=todo_list.to_dict() + todo_lists_dict = {} + for i, todo_list in enumerate(todo_lists): + todo_lists_dict[i] = todo_list.to_dict() return JsonResponse(todo_lists_dict) todo_list = ToDoList.get_by_id(todo_list_pk=todo_list_pk) @@ -35,7 +36,7 @@ def post(self, request): return JsonResponse(todo_list.to_dict()) return HttpResponse(status=400) - def delete(self, request,todo_list_pk=None): + def delete(self, request, todo_list_pk=None): todo_list = ToDoList.get_by_id(todo_list_pk=todo_list_pk) if todo_list: @@ -43,13 +44,13 @@ def delete(self, request,todo_list_pk=None): return HttpResponse(status=200) return HttpResponse(status=400) - def put(self, request,todo_list_pk=None): + def put(self, request, todo_list_pk=None): body = json.loads(request.body) name = body.get('name') description = body.get('description') member_pk = body.get('members') - members = CustomUser.objects.filter(id__in = member_pk ) + members = CustomUser.objects.filter(id__in=member_pk) todo_list = ToDoList.get_by_id(todo_list_pk=todo_list_pk) if todo_list: list_values = {'members': members, From 9364aba6c161043789d522c56952569f8b62e401 Mon Sep 17 00:00:00 2001 From: SLDem Date: Fri, 12 Mar 2021 16:21:49 +0200 Subject: [PATCH 039/100] Added coverage to requirements.txt, renamed create_user to create and added tests for custom_user models --- custom_user/models.py | 8 ++--- requirements.txt | 3 +- tests/custom_user/tests.py | 66 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 72 insertions(+), 5 deletions(-) diff --git a/custom_user/models.py b/custom_user/models.py index e442d19..1d9ebd2 100644 --- a/custom_user/models.py +++ b/custom_user/models.py @@ -17,7 +17,7 @@ def _create_user(self, email, password, **extra_fields): user.save(using=self._db) return user - def create_user(self, email, password, **extra_fields): + def create(self, email, password, **extra_fields): return self._create_user(email, password, **extra_fields) @@ -40,7 +40,7 @@ def to_dict(self): 'email': self.email} @classmethod - def find_by_id(cls, user_id): + def get_by_id(cls, user_id): try: user = cls.objects.get(pk=user_id) return user @@ -55,7 +55,7 @@ def update(self, data): return True @classmethod - def delete(cls, user_id): - user = cls.find_by_id(user_id) + def remove(cls, user_id): + user = cls.get_by_id(user_id) user.delete() return HttpResponse('User removed.') diff --git a/requirements.txt b/requirements.txt index b3cbb3c..4ab4b30 100644 --- a/requirements.txt +++ b/requirements.txt @@ -12,4 +12,5 @@ pylint-plugin-utils==0.6 pytz==2021.1 sqlparse==0.4.1 toml==0.10.2 -wrapt==1.12.1 \ No newline at end of file +wrapt==1.12.1 +coverage==5.5 \ No newline at end of file diff --git a/tests/custom_user/tests.py b/tests/custom_user/tests.py index e69de29..d535c9d 100644 --- a/tests/custom_user/tests.py +++ b/tests/custom_user/tests.py @@ -0,0 +1,66 @@ +from django.test import TestCase +from custom_user.models import CustomUser + + +class CustomUserModelsTestCase(TestCase): + def setUp(self): + self.user = CustomUser.objects.create(id=1, + first_name='Test', + last_name='User', + email='testuser@gmail.com', + password='test') + + def test_create_user(self): + user = CustomUser.objects.create(id=2, + first_name='Test1', + last_name='User1', + email='testuser1@gmail.com', + password='test1') + self.assertIsInstance(user, CustomUser) + + def test_create_user_no_email(self): + def create_user(): + res = CustomUser.objects.create(id=2, + first_name='Test1', + last_name='User1', + email='', + password='test1') + return res + self.assertRaises(ValueError, create_user) + + def test_str(self): + result = self.user.__str__() + self.assertEqual(result, 'Test User') + + def test_to_dict(self): + result = self.user.to_dict() + self.assertTrue(type(result) == dict) + + def test_find_by_id(self): + user = CustomUser.get_by_id(1) + self.assertIsInstance(user, CustomUser) + + def test_find_by_id_not_found(self): + user = CustomUser.get_by_id(3) + self.assertTrue(user is None) + + def test_update(self): + data = { + 'first_name': 'User', + 'last_name': 'Test', + 'email': 'testuser@gmail.com' + } + result = self.user.update(data) + self.assertEqual(self.user.first_name, 'User') + self.assertEqual(self.user.last_name, 'Test') + self.assertEqual(self.user.email, 'testuser@gmail.com') + self.assertTrue(result) + + def test_remove_user(self): + CustomUser.objects.create(id=2, + first_name='Test1', + last_name='User1', + email='testuser1@gmail.com', + password='test1') + res = CustomUser.remove(2) + self.assertIn(b'User removed', res.content) From d18ce0d930a2a509684323c5f1ef4d2abc3648c9 Mon Sep 17 00:00:00 2001 From: AndriiRomaniuk Date: Fri, 12 Mar 2021 17:48:17 +0200 Subject: [PATCH 040/100] updated views --- task/models.py | 30 ++++++++++------------ task/views.py | 69 +++++++++++++++++++++++++------------------------- 2 files changed, 47 insertions(+), 52 deletions(-) diff --git a/task/models.py b/task/models.py index cae9aea..30f1e65 100644 --- a/task/models.py +++ b/task/models.py @@ -4,8 +4,6 @@ from datetime import datetime - -# Create your models here. class Task(models.Model): title = models.CharField(max_length=30) description = models.TextField(max_length=256) @@ -20,7 +18,7 @@ def __str__(self): @classmethod def find_by_id(cls, task_id: int): try: - task = Task.objects.get(pk=task_id) + task = Task.ojects.get(pk=task_id) return task except Task.DoesNotExist: return None @@ -39,30 +37,28 @@ def find_all_for_list(cls, list_id): return tasks @classmethod - def create(cls, title: str, description: str, deadline, user_id, task_id): + def create(cls, title: str, description: str, deadline, user_id, list_id): task = Task(title=title, description=description, deadline=deadline) user = CustomUser.find_by_id(user_id) task.user_id = user - list = ToDoList.get_by_id(task_id) + list = ToDoList.get_by_id(list_id) task.list_id = list task.save() return task - def update(self, new_title: str, new_description: str, is_completed: bool, new_deadline: datetime, user_id: int, list_id: int): - task = Task(new_title, new_description, new_deadline) - task.title = new_title - task.description = new_description - task.deadline = new_deadline - task.is_completed = is_completed - task.user_id = CustomUser.find_by_id(user_id) - task.list_id = ToDoList.get_by_id(list_id) - task.save() - return task + def update(self, title: str, description: str, is_completed: bool, deadline: datetime, user_id: int, list_id: int): + self.title = title + self.description = description + self.deadline = deadline + self.is_completed = is_completed + self.user_id = CustomUser.find_by_id(user_id) + self.list_id = ToDoList.get_by_id(list_id) + self.save() @classmethod - def remove(cls, user_id): + def remove(cls, task_id): try: - task = Task.objects.get(user_id=user_id) + task = Task.objects.get(id=task_id) task.delete() except Task.DoesNotExist: return False diff --git a/task/views.py b/task/views.py index fcb3ed6..7ef5f3b 100644 --- a/task/views.py +++ b/task/views.py @@ -20,61 +20,60 @@ def get(self, request, list_id): return JsonResponse(data) def post(self, request): - post_body = json.loads(request.body) - - title = post_body.get('title') - description = post_body.get('description') - deadline = post_body.get('deadline') - _user_id = post_body.get('user_id') - _list_id = post_body.get('list_id') - - user_id = CustomUser.find_by_id(_user_id) - list_id = ToDoList.objects.get(id=_list_id) + body = json.loads(request.body) task_data = { - 'title': title, - 'description': description, - 'deadline': deadline, - 'user_id': user_id, - 'list_id': list_id, + 'title': body.get('title'), + 'description': body.get('description'), + 'deadline': body.get('deadline'), + 'user_id': body.get('user_id'), + 'list_id': body.get('list_id'), } - task_obj = Task.objects.create(**task_data) - task_obj.save() + task = Task.create(**task_data) + task.save() + data = { - 'message': f'New task object has been created with id {task_obj.id}' + 'message': f'New task object has been created with id {task.id}' } return JsonResponse(data, status=201) def put(self, request): - put_body = json.loads(request.body) + body = json.loads(request.body) - task_id = put_body.get('task_id') - task = Task.objects.get(id=task_id) + task = Task.objects.get(id=body.get('task_id')) - task.title = put_body.get('title') - task.description = put_body.get('description') - task.deadline = put_body.get('deadline') - task.user_id = CustomUser.find_by_id(put_body.get('user_id')) - task.list_id = ToDoList.objects.get(id=put_body.get('list_id')) - task.is_completed = put_body.get('is_completed') - task.save() + task_data = { + 'title': body.get('title'), + 'description': body.get('description'), + 'deadline': body.get('deadline'), + 'user_id': body.get('user_id'), + 'list_id': body.get('list_id'), + 'is_completed': body.get('list_id'), + } + + task.update(**task_data) data = { - 'message': f'Task {task_id} has been updated' + 'message': f'Task {body.get("task_id")} has been updated' } return JsonResponse(data) def delete(self, request): - delete_body = json.loads(request.body) + body = json.loads(request.body) - task_id = delete_body.get('task_id') - Task.objects.get(id=task_id).delete() + success = { + 'message': f'Task with id {body.get("task_id")} has been deleted' + } - data = { - 'message': f'Task with id {task_id} has been deleted' + failure = { + 'message': f'Task with id {body.get("task_id")} does not exist!' } - return JsonResponse(data) \ No newline at end of file + + if Task.remove(task_id=body.get('task_id')): + return JsonResponse(success) + else: + return JsonResponse(failure) From 662c8e9259a328c9d697a0247002047dbafc3578 Mon Sep 17 00:00:00 2001 From: OstapOmelchuk Date: Fri, 12 Mar 2021 21:41:23 +0200 Subject: [PATCH 041/100] Add tests for task models --- task/models.py | 31 ++++++++++++++-------- tests/task/tests.py | 64 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 84 insertions(+), 11 deletions(-) diff --git a/task/models.py b/task/models.py index cae9aea..ad33079 100644 --- a/task/models.py +++ b/task/models.py @@ -2,6 +2,7 @@ from todolist.models import ToDoList from custom_user.models import CustomUser from datetime import datetime +from django.http import HttpResponse @@ -39,7 +40,7 @@ def find_all_for_list(cls, list_id): return tasks @classmethod - def create(cls, title: str, description: str, deadline, user_id, task_id): + def create(cls, title: str, description: str, deadline: datetime, user_id, task_id): task = Task(title=title, description=description, deadline=deadline) user = CustomUser.find_by_id(user_id) task.user_id = user @@ -49,15 +50,14 @@ def create(cls, title: str, description: str, deadline, user_id, task_id): return task def update(self, new_title: str, new_description: str, is_completed: bool, new_deadline: datetime, user_id: int, list_id: int): - task = Task(new_title, new_description, new_deadline) - task.title = new_title - task.description = new_description - task.deadline = new_deadline - task.is_completed = is_completed - task.user_id = CustomUser.find_by_id(user_id) - task.list_id = ToDoList.get_by_id(list_id) - task.save() - return task + self.title = new_title + self.description = new_description + self.deadline = new_deadline + self.is_completed = is_completed + self.user_id = CustomUser.find_by_id(user_id) + self.list_id = ToDoList.get_by_id(list_id) + self.save() + return self @classmethod def remove(cls, user_id): @@ -66,4 +66,13 @@ def remove(cls, user_id): task.delete() except Task.DoesNotExist: return False - return True + return HttpResponse("Task was deleted.") + + @classmethod + def remove_all(cls): + try: + for el in Task.get_all(): + Task.remove(el.user_id) + except Task.DoesNotExist: + return False + return HttpResponse("All tasks deleted.") diff --git a/tests/task/tests.py b/tests/task/tests.py index e69de29..6442ac6 100644 --- a/tests/task/tests.py +++ b/tests/task/tests.py @@ -0,0 +1,64 @@ +from django.test import TestCase +from task.models import Task + +class TaskModelsTest(TestCase): + def setUp(self): + self.task1 = Task.objects.create(title="Task #1", + description="Task #1 Description", + deadline=2021-1-1, + user_id=1, + list_id=1) + + def test_str(self): + test = self.task1.__str__() + self.assertEqual(test, "Task #1") + + def test_create(self): + task = Task.objects.create(title="Task #2", + description="Task #1 Description", + deadline=2021-2-2, + user_id=2, + list_id=2) + self.assertIsInstance(task, Task) + + def test_update_task(self): + task = self.task1.update("New task", "new task description", False, 2021-3-3, 3, 3) + self.assertEqual(task.title, "New task") + self.assertEqual(task.description, "new task description") + self.assertEqual(task.is_completed, False) + self.assertEqual(task.deadline, 2021-2-2) + self.assertEqual(task.user_id, 2) + self.assertEqual(task.list_id, 2) + self.assertTrue(task) + + def test_find_by_id(self): + task = Task.find_by_id(2) + self.assertIsInstance(task, Task) + + def test_find_by_non_existent_id(self): + task = Task.find_by_id(4) + self.assertTrue(task is None) + + def test_remove_task(self): + Task.objects.create(title="Task #3", + description="Task #3 Description", + deadline=2021 - 4 - 4, + user_id=3, + list_id=2) + task = Task.remove(3) + self.assertIn(b"Task was deleted.", task.content) + + def test_remove_non_existed_task(self): + task = Task.remove(4) + self.assertEqual(task, False) + + def test_remove_all_tasks(self): + task = Task.remove_all() + self.assertIn(b"All tasks deleted.", task.content) + + def test_get_all_does_not_exist(self): + task = Task.get_all() + self.assertTrue(task is None) + + def test_get_all(self): + task = Task.get_all() From 49548bec3ad1307feffa9bfa6aad1ae48155591f Mon Sep 17 00:00:00 2001 From: Daniil Oleshchuk Date: Fri, 12 Mar 2021 22:13:22 +0200 Subject: [PATCH 042/100] improved ToDoList model CRUD --- todolist/models.py | 69 +++++++++++++++++++++++----------------------- 1 file changed, 34 insertions(+), 35 deletions(-) diff --git a/todolist/models.py b/todolist/models.py index 7602e96..8d89617 100644 --- a/todolist/models.py +++ b/todolist/models.py @@ -1,4 +1,4 @@ -from django.db import models +from django.db import models, IntegrityError from custom_user.models import CustomUser @@ -6,7 +6,6 @@ class ToDoList(models.Model): name = models.CharField(max_length=50) description = models.CharField(max_length=500, default='No description for now.') - members = models.ManyToManyField(CustomUser, related_name='todo_lists') def __str__(self): @@ -14,52 +13,52 @@ def __str__(self): def to_dict(self): return {'name': self.name, - 'description': self.description, - #'members': self.members - } + 'description': self.description} + + def update(self, data): + if data.name: + self.name = data.name + if data.description: + self.description = data.description + self.save() + + def update_members(self, members_to_add=None, members_to_delete=None): + if members_to_add: + self.members.add(*members_to_add) + if members_to_delete: + self.members.remove(*members_to_delete) + + def get_list_members(self): + members = self.members.all() + return members @classmethod - def get_by_id(cls, todo_list_pk: int): + def get_by_id(cls, todo_list_pk): try: todo_list = ToDoList.objects.get(pk=todo_list_pk) return todo_list except ToDoList.DoesNotExist: + # log error return None @classmethod def get_all(cls): - try: - todo_lists = ToDoList.objects.all() - return todo_lists - except ToDoList.DoesNotExist: - return None + todo_lists = ToDoList.objects.all() + return todo_lists @classmethod - def create(cls, name: str, description: str, member_pk): + def create(cls, name, description='', members=None): todo_list = ToDoList(name=name, description=description) - todo_list.save() - member_to_add = CustomUser.find_by_id(user_id=member_pk) - todo_list.members.add(member_to_add) - todo_list.save() - return todo_list - - @classmethod - def update(cls, todo_list_pk: int, data: dict): - todo_list = cls.get_by_id(todo_list_pk) - if todo_list: - for field, value in data.items(): - if hasattr(todo_list, field): - setattr(todo_list, field, value) - else: - raise KeyError("Failed to update non existing attribute {}.{}".format( - todo_list.__class__.__name__, field)) + try: todo_list.save() - return cls.get_by_id(todo_list_pk=todo_list_pk) - return None + if members: + todo_list.members.add(*members) + return todo_list + except (ValueError, IntegrityError): + # log error + return None @classmethod - def remove(cls, todo_list_pk: int): - todo_list = cls.get_by_id(todo_list_pk=todo_list_pk) - if todo_list: - todo_list.delete() - return not cls.get_by_id(todo_list_pk=todo_list_pk) + def remove(cls, todo_list_pk): + todo_list = ToDoList.objects.get(pk=todo_list_pk) + todo_list.delete() From b3c1a2e64bfb69051c4be651e9a5cd34a59de8bc Mon Sep 17 00:00:00 2001 From: Daniil Oleshchuk Date: Fri, 12 Mar 2021 22:22:10 +0200 Subject: [PATCH 043/100] added members param to to_dict() methon in ToDoList model --- todolist/models.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/todolist/models.py b/todolist/models.py index 8d89617..97d3a4e 100644 --- a/todolist/models.py +++ b/todolist/models.py @@ -13,7 +13,8 @@ def __str__(self): def to_dict(self): return {'name': self.name, - 'description': self.description} + 'description': self.description, + 'members': self.members.all()} def update(self, data): if data.name: From 6476c83b9ab4d3a746f4cc951199d025bfa5ae77 Mon Sep 17 00:00:00 2001 From: OstapOmelchuk Date: Fri, 12 Mar 2021 23:28:37 +0200 Subject: [PATCH 044/100] rename imported from customu_ser method find_by_id to fet_by_id in Task Models --- Pipfile | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 Pipfile diff --git a/Pipfile b/Pipfile new file mode 100644 index 0000000..e69de29 From eff93db9c289213631219c2dbf307dc12f3f3efa Mon Sep 17 00:00:00 2001 From: AndriiRomaniuk Date: Sat, 13 Mar 2021 16:33:38 +0200 Subject: [PATCH 045/100] added error handling to task views --- task/models.py | 4 +-- task/views.py | 88 ++++++++++++++++++++++++++++++++++---------------- 2 files changed, 63 insertions(+), 29 deletions(-) diff --git a/task/models.py b/task/models.py index 30f1e65..8ff91e4 100644 --- a/task/models.py +++ b/task/models.py @@ -16,9 +16,9 @@ def __str__(self): return self.title @classmethod - def find_by_id(cls, task_id: int): + def get_by_id(cls, task_id: int): try: - task = Task.ojects.get(pk=task_id) + task = Task.objects.get(pk=task_id) return task except Task.DoesNotExist: return None diff --git a/task/views.py b/task/views.py index 7ef5f3b..bd5371e 100644 --- a/task/views.py +++ b/task/views.py @@ -1,10 +1,9 @@ from django.http import JsonResponse from django.views import View from task.models import Task -from todolist.models import ToDoList from django.core.serializers import serialize import json -from custom_user.models import CustomUser +from django.db.utils import IntegrityError, DataError class TaskAPIView(View): @@ -22,7 +21,6 @@ def get(self, request, list_id): def post(self, request): body = json.loads(request.body) - task_data = { 'title': body.get('title'), 'description': body.get('description'), @@ -31,20 +29,34 @@ def post(self, request): 'list_id': body.get('list_id'), } - task = Task.create(**task_data) - task.save() + try: + task = Task.create(**task_data) + task.save() - data = { - 'message': f'New task object has been created with id {task.id}' - } - return JsonResponse(data, status=201) + success_message = { + 'message': f'New task object has been created with id {task.id}' + } + + return JsonResponse(success_message, status=201) + + # Missing parameters + except IntegrityError: + integrity_message = { + 'message': f'Cannot create task! One or more parameters are missing' + } + return JsonResponse(integrity_message, status=400) + + # Invalid input + except DataError: + invalid_data_message = { + 'message': f'Cannot create task! One or more parameters are invalid' + } + return JsonResponse(invalid_data_message, status=400) def put(self, request): body = json.loads(request.body) - task = Task.objects.get(id=body.get('task_id')) - task_data = { 'title': body.get('title'), 'description': body.get('description'), @@ -54,26 +66,48 @@ def put(self, request): 'is_completed': body.get('list_id'), } - task.update(**task_data) - - data = { - 'message': f'Task {body.get("task_id")} has been updated' - } - return JsonResponse(data) + # Missing ID + if not body.get("task_id"): + missing_id_message = { + 'message': f'Cannot update task! Task id is missing!' + } + return JsonResponse(missing_id_message, status=400) + + task = Task.get_by_id(task_id=body.get('task_id')) + + # ID of non-existing task + if not task: + not_exist_message = { + 'message': f'Cannot update task! Task with {body.get("task_id")} does not exist' + } + return JsonResponse(not_exist_message, status=400) + + try: + task.update(**task_data) + + success_message = { + 'message': f'Task {body.get("task_id")} has been updated' + } + return JsonResponse(success_message, status=200) + + # Invalid input + except DataError: + invalid_data_message = { + 'message': f'Cannot update task! One or more parameters are invalid' + } + return JsonResponse(invalid_data_message, status=400) def delete(self, request): body = json.loads(request.body) - success = { - 'message': f'Task with id {body.get("task_id")} has been deleted' - } - - failure = { - 'message': f'Task with id {body.get("task_id")} does not exist!' - } - if Task.remove(task_id=body.get('task_id')): - return JsonResponse(success) + success = { + 'message': f'Task with id {body.get("task_id")} has been deleted' + } + return JsonResponse(success, status=200) else: - return JsonResponse(failure) + failure = { + 'message': f'Task with id {body.get("task_id")} does not exist!' + } + return JsonResponse(failure, status=400) From f0ad888c1eb8f99b3b2c8747b7222428542c326d Mon Sep 17 00:00:00 2001 From: OstapOmelchuk Date: Sat, 13 Mar 2021 16:46:17 +0200 Subject: [PATCH 046/100] Add tests for task models --- manage.py | 0 task/migrations/0001_initial.py | 2 - task/models.py | 49 +++++---------- tests/task/tests.py | 105 +++++++++++++++++++------------- todolist/models.py | 2 +- 5 files changed, 81 insertions(+), 77 deletions(-) mode change 100644 => 100755 manage.py diff --git a/manage.py b/manage.py old mode 100644 new mode 100755 diff --git a/task/migrations/0001_initial.py b/task/migrations/0001_initial.py index 34f6df2..cc013da 100644 --- a/task/migrations/0001_initial.py +++ b/task/migrations/0001_initial.py @@ -23,8 +23,6 @@ class Migration(migrations.Migration): ('description', models.TextField(max_length=256)), ('is_completed', models.BooleanField(default=False)), ('deadline', models.DateField()), - ('list_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='todolist.todolist')), - ('user_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), ], ), ] diff --git a/task/models.py b/task/models.py index ad33079..3df03e2 100644 --- a/task/models.py +++ b/task/models.py @@ -1,7 +1,7 @@ from django.db import models from todolist.models import ToDoList from custom_user.models import CustomUser -from datetime import datetime +from datetime import date from django.http import HttpResponse @@ -26,53 +26,36 @@ def find_by_id(cls, task_id: int): except Task.DoesNotExist: return None - @classmethod - def get_all(cls): - try: - task = Task.objects.all() - return task - except Task.DoesNotExist: - return None - @classmethod def find_all_for_list(cls, list_id): tasks = Task.objects.filter(list_id=list_id) return tasks @classmethod - def create(cls, title: str, description: str, deadline: datetime, user_id, task_id): + def create(cls, title: str, description: str, deadline: date, user_id, list_id): task = Task(title=title, description=description, deadline=deadline) - user = CustomUser.find_by_id(user_id) + user = CustomUser.get_by_id(user_id) task.user_id = user - list = ToDoList.get_by_id(task_id) + list = ToDoList.get_by_id(list_id) task.list_id = list task.save() return task - def update(self, new_title: str, new_description: str, is_completed: bool, new_deadline: datetime, user_id: int, list_id: int): - self.title = new_title - self.description = new_description - self.deadline = new_deadline + def update(self, title: str, description: str, is_completed: bool, deadline: date, user_id: int, list_id: int): + self.title = title + self.description = description + self.deadline = deadline self.is_completed = is_completed - self.user_id = CustomUser.find_by_id(user_id) + self.user_id = CustomUser.get_by_id(user_id) self.list_id = ToDoList.get_by_id(list_id) self.save() - return self + return self.find_by_id(self.pk) @classmethod - def remove(cls, user_id): - try: - task = Task.objects.get(user_id=user_id) + def remove(cls, task_id): + task = Task.find_by_id(task_id) + if task: task.delete() - except Task.DoesNotExist: - return False - return HttpResponse("Task was deleted.") - - @classmethod - def remove_all(cls): - try: - for el in Task.get_all(): - Task.remove(el.user_id) - except Task.DoesNotExist: - return False - return HttpResponse("All tasks deleted.") + return HttpResponse("Task was deleted.") + else: + return False diff --git a/tests/task/tests.py b/tests/task/tests.py index 6442ac6..028a024 100644 --- a/tests/task/tests.py +++ b/tests/task/tests.py @@ -1,64 +1,87 @@ -from django.test import TestCase +from django.test import TestCase, tag from task.models import Task +from custom_user.models import CustomUser +from todolist.models import ToDoList + +from datetime import date + class TaskModelsTest(TestCase): def setUp(self): - self.task1 = Task.objects.create(title="Task #1", - description="Task #1 Description", - deadline=2021-1-1, - user_id=1, - list_id=1) + self.user = CustomUser.objects.create(id=1, + first_name='Test', + last_name='User', + email='testuser@gmail.com', + password='test') + self.todolist = ToDoList.create(name="list 1", description="list1 descr", member_pk=self.user.pk) + self.todolist.id = 1 + self.todolist.save() + self.task1 = Task.create(title="Task #1", + description="Task #1 Description", + deadline=date(2021, 1, 1), + user_id=self.user.pk, + list_id=self.todolist.pk) def test_str(self): test = self.task1.__str__() self.assertEqual(test, "Task #1") def test_create(self): - task = Task.objects.create(title="Task #2", - description="Task #1 Description", - deadline=2021-2-2, - user_id=2, - list_id=2) + task = Task.create(title="Task #2", + description="Task #2 Description", + deadline=date(2021, 2, 2), + user_id=self.user.pk, + list_id=self.todolist.pk) self.assertIsInstance(task, Task) def test_update_task(self): - task = self.task1.update("New task", "new task description", False, 2021-3-3, 3, 3) - self.assertEqual(task.title, "New task") - self.assertEqual(task.description, "new task description") - self.assertEqual(task.is_completed, False) - self.assertEqual(task.deadline, 2021-2-2) - self.assertEqual(task.user_id, 2) - self.assertEqual(task.list_id, 2) - self.assertTrue(task) + self.task = Task.create(title="Task #3", + description="Task #3 Description", + deadline=date(2021, 3, 3), + user_id=self.user.pk, + list_id=self.todolist.pk) + result = self.task.update("New task", "new task description", False, date(2021, 3, 3), + self.user.pk, self.todolist.pk) + self.assertEqual(result.title, "New task") + self.assertEqual(result.description, "new task description") + self.assertEqual(result.is_completed, False) + self.assertEqual(result.deadline, date(2021, 3, 3)) + print(result.list_id.pk) + self.assertEqual(result.user_id.pk, 1) + self.assertEqual(result.list_id.pk, 1) + self.assertTrue(result) def test_find_by_id(self): - task = Task.find_by_id(2) - self.assertIsInstance(task, Task) + self.task = Task.create(title="Task #4", + description="Task #4 Description", + deadline=date(2021, 5, 3), + user_id=self.user.pk, + list_id=self.todolist.pk) + result = Task.find_by_id(self.task.pk) + self.assertIsInstance(result, Task) def test_find_by_non_existent_id(self): - task = Task.find_by_id(4) + task = Task.find_by_id(100) self.assertTrue(task is None) def test_remove_task(self): - Task.objects.create(title="Task #3", - description="Task #3 Description", - deadline=2021 - 4 - 4, - user_id=3, - list_id=2) - task = Task.remove(3) - self.assertIn(b"Task was deleted.", task.content) + self.task = Task.create(title="Task #5", + description="Task #5 Description", + deadline=date(2021, 4, 4), + user_id=self.user.pk, + list_id=self.todolist.pk) + result = Task.remove(self.task.pk) + self.assertNotIn(result, Task.find_all_for_list(self.todolist.pk)) def test_remove_non_existed_task(self): - task = Task.remove(4) - self.assertEqual(task, False) - - def test_remove_all_tasks(self): - task = Task.remove_all() - self.assertIn(b"All tasks deleted.", task.content) - - def test_get_all_does_not_exist(self): - task = Task.get_all() - self.assertTrue(task is None) + result = Task.remove(100) + self.assertEqual(result, False) - def test_get_all(self): - task = Task.get_all() + def test_find_all_for_list(self): + self.task = Task.create(title="Task #6", + description="Task #6 Description", + deadline=date(2021, 6, 7), + user_id=self.user.pk, + list_id=self.todolist.pk) + result = Task.find_all_for_list(self.todolist.pk) + self.assertEqual(len(result), 2) diff --git a/todolist/models.py b/todolist/models.py index 7602e96..2c3b035 100644 --- a/todolist/models.py +++ b/todolist/models.py @@ -38,7 +38,7 @@ def get_all(cls): def create(cls, name: str, description: str, member_pk): todo_list = ToDoList(name=name, description=description) todo_list.save() - member_to_add = CustomUser.find_by_id(user_id=member_pk) + member_to_add = CustomUser.get_by_id(user_id=member_pk) todo_list.members.add(member_to_add) todo_list.save() return todo_list From 2fd21dd0de3c550456ef1f43b5372e59bed07b62 Mon Sep 17 00:00:00 2001 From: Daniil Oleshchuk Date: Sat, 13 Mar 2021 17:49:01 +0200 Subject: [PATCH 047/100] Changed to_dict() method in ToDoList model Changed `members` field in to_dict() output from QuerySet of members to list of members ids --- todolist/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/todolist/models.py b/todolist/models.py index 97d3a4e..899e179 100644 --- a/todolist/models.py +++ b/todolist/models.py @@ -14,7 +14,7 @@ def __str__(self): def to_dict(self): return {'name': self.name, 'description': self.description, - 'members': self.members.all()} + 'members': sorted([member.id for member in self.members.all()])} def update(self, data): if data.name: From f09360ef238729c155d9c81ccf8d612a1d726332 Mon Sep 17 00:00:00 2001 From: Daniil Oleshchuk Date: Sat, 13 Mar 2021 18:52:43 +0200 Subject: [PATCH 048/100] Changed to_dict() method in ToDoList model Added `id` of ToDoList to to_dict() output --- todolist/models.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/todolist/models.py b/todolist/models.py index 899e179..20bac6f 100644 --- a/todolist/models.py +++ b/todolist/models.py @@ -12,7 +12,8 @@ def __str__(self): return self.name def to_dict(self): - return {'name': self.name, + return {'id': self.id, + 'name': self.name, 'description': self.description, 'members': sorted([member.id for member in self.members.all()])} From ba9fa0cfb617d510db83037ccafad52b3f82ef2f Mon Sep 17 00:00:00 2001 From: Daniil Oleshchuk Date: Sat, 13 Mar 2021 19:29:13 +0200 Subject: [PATCH 049/100] Changed update() method in ToDoList model removed `data` parameter and added `name`, `description` parameters in order to avoid errors in `ifs` when some parameter is absent (in example data.name) --- todolist/models.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/todolist/models.py b/todolist/models.py index 20bac6f..b659f93 100644 --- a/todolist/models.py +++ b/todolist/models.py @@ -17,11 +17,11 @@ def to_dict(self): 'description': self.description, 'members': sorted([member.id for member in self.members.all()])} - def update(self, data): - if data.name: - self.name = data.name - if data.description: - self.description = data.description + def update(self, name=None, description=None): + if name: + self.name = name + if description: + self.description = description self.save() def update_members(self, members_to_add=None, members_to_delete=None): From 85256d5c9691e037cb0b2a62746f9a78685a7fe8 Mon Sep 17 00:00:00 2001 From: Daniil Oleshchuk Date: Sat, 13 Mar 2021 19:34:18 +0200 Subject: [PATCH 050/100] improved ToDoList views and urls --- todolist/urls.py | 9 ++--- todolist/views.py | 93 ++++++++++++++++++++++++++--------------------- 2 files changed, 54 insertions(+), 48 deletions(-) diff --git a/todolist/urls.py b/todolist/urls.py index 1d41cce..edb2feb 100644 --- a/todolist/urls.py +++ b/todolist/urls.py @@ -1,11 +1,8 @@ from django.urls import path -from . import views +from .views import ToDoListView urlpatterns = [ - path('', views.ToDoListView.as_view(), name='all-lists'), - path('list//', views.ToDoListView.as_view(), name='one-list'), - path('delete-list//', views.ToDoListView.as_view(), name='delete-list'), - path('update-list//', views.ToDoListView.as_view(), name='update-list'), - path('create-list/', views.ToDoListView.as_view(), name='create-list') + path('', ToDoListView.as_view()), + path('/', ToDoListView.as_view()), ] diff --git a/todolist/views.py b/todolist/views.py index 8e27d39..908ec62 100644 --- a/todolist/views.py +++ b/todolist/views.py @@ -9,56 +9,65 @@ class ToDoListView(View): def get(self, request, todo_list_pk=None): + if todo_list_pk: + todo_list = ToDoList.get_by_id(todo_list_pk) + if not todo_list: + return HttpResponse(status=404) + return JsonResponse(todo_list.to_dict(), status=200) - if todo_list_pk is None: - todo_lists = ToDoList.get_all() - todo_lists_dict = {} - for i, todo_list in enumerate(todo_lists): - todo_lists_dict[i] = todo_list.to_dict() - return JsonResponse(todo_lists_dict) - todo_list = ToDoList.get_by_id(todo_list_pk=todo_list_pk) - - if todo_list: - return JsonResponse(todo_list.to_dict()) - - return HttpResponse(status=400) + todo_lists = ToDoList.get_all() + todo_lists = json.dumps([todo_list.to_dict() for todo_list in todo_lists]) + return HttpResponse(todo_lists, status=200, content_type="application/json") def post(self, request): - body = json.loads(request.body) - - name = body.get("name") - description = body.get("description") - member_pk = body.get("member_pk") - user = CustomUser.find_by_id(member_pk) - print(user) - if user: - todo_list = ToDoList.create(name=name, description=description, member_pk=member_pk) - return JsonResponse(todo_list.to_dict()) - return HttpResponse(status=400) + data = request.body + if not data: + return HttpResponse(status=400) - def delete(self, request, todo_list_pk=None): + data = json.loads(request.body) + + data = { + 'name': data.get('name'), + 'description': data.get('description') if data.get('description') else '', + 'members': [CustomUser.get_by_id(user_id=user_id) for user_id in data.get('members')] + if data.get('members') else None + } - todo_list = ToDoList.get_by_id(todo_list_pk=todo_list_pk) + todo_list = ToDoList.create(**data) if todo_list: - todo_list.remove(todo_list_pk=todo_list_pk) - return HttpResponse(status=200) + return JsonResponse(todo_list.to_dict(), status=201) + return HttpResponse(status=400) def put(self, request, todo_list_pk=None): - body = json.loads(request.body) + todo_list = ToDoList.get_by_id(todo_list_pk) + if not todo_list: + return HttpResponse(status=404) - name = body.get('name') - description = body.get('description') - member_pk = body.get('members') - members = CustomUser.objects.filter(id__in=member_pk) - todo_list = ToDoList.get_by_id(todo_list_pk=todo_list_pk) - if todo_list: - list_values = {'members': members, - 'name': name, - 'description': description} - for field, value in list_values.items(): - if value is None: - del list_values[field] - todo_list.update(todo_list_pk=todo_list_pk, data=list_values) - return JsonResponse(todo_list.to_dict()) + data = request.body + if not data: + return HttpResponse(status=400) + data = json.loads(request.body) + + members_to_add = data.get('members_to_add') + members_to_delete = data.get('members_to_delete') + if members_to_add or members_to_delete: + todo_list.update_members(members_to_add, members_to_delete) + + data = {'name': data.get('name') if data.get('name') else None, + 'description': data.get('description') if data.get('description') else None} + + todo_list.update(**data) + + return HttpResponse(status=200) + + def delete(self, request, todo_list_pk=None): + todo_list = ToDoList.get_by_id(todo_list_pk) + if not todo_list: + return HttpResponse(status=400) + + ToDoList.remove(todo_list_pk) + + if not ToDoList.get_by_id(todo_list_pk): + return HttpResponse(status=200) return HttpResponse(status=400) From 5ccf886df522aa4cbaeff0294c48cbe5a452159d Mon Sep 17 00:00:00 2001 From: AndriiRomaniuk Date: Sat, 13 Mar 2021 19:36:09 +0200 Subject: [PATCH 051/100] Added tests for task views --- task/models.py | 18 ++++++--- task/urls.py | 2 +- task/views.py | 42 +++++++++++++++++---- tests/task/tests.py | 92 +++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 140 insertions(+), 14 deletions(-) diff --git a/task/models.py b/task/models.py index 8ff91e4..3890157 100644 --- a/task/models.py +++ b/task/models.py @@ -47,12 +47,18 @@ def create(cls, title: str, description: str, deadline, user_id, list_id): return task def update(self, title: str, description: str, is_completed: bool, deadline: datetime, user_id: int, list_id: int): - self.title = title - self.description = description - self.deadline = deadline - self.is_completed = is_completed - self.user_id = CustomUser.find_by_id(user_id) - self.list_id = ToDoList.get_by_id(list_id) + if title: + self.title = title + if description: + self.description = description + if deadline: + self.deadline = deadline + if is_completed: + self.is_completed = is_completed + if user_id: + self.user_id = CustomUser.find_by_id(user_id) + if list_id: + self.list_id = ToDoList.get_by_id(list_id) self.save() @classmethod diff --git a/task/urls.py b/task/urls.py index 423aa6b..4f6fac3 100644 --- a/task/urls.py +++ b/task/urls.py @@ -4,5 +4,5 @@ urlpatterns = [ path('by_list/', TaskAPIView.as_view()), - path('', TaskAPIView.as_view()), + path('', TaskAPIView.as_view(), name='task_api_view'), ] \ No newline at end of file diff --git a/task/views.py b/task/views.py index bd5371e..10d2df0 100644 --- a/task/views.py +++ b/task/views.py @@ -4,6 +4,7 @@ from django.core.serializers import serialize import json from django.db.utils import IntegrityError, DataError +from django.core.exceptions import ValidationError class TaskAPIView(View): @@ -19,7 +20,14 @@ def get(self, request, list_id): return JsonResponse(data) def post(self, request): - body = json.loads(request.body) + # JSON validation + try: + body = json.loads(request.body) + except json.JSONDecodeError: + failure = { + 'message': f'Please provide valid json request!' + } + return JsonResponse(failure, status=400) task_data = { 'title': body.get('title'), @@ -47,7 +55,7 @@ def post(self, request): return JsonResponse(integrity_message, status=400) # Invalid input - except DataError: + except (DataError, ValidationError, ValueError): invalid_data_message = { 'message': f'Cannot create task! One or more parameters are invalid' } @@ -55,7 +63,14 @@ def post(self, request): def put(self, request): - body = json.loads(request.body) + # JSON validation + try: + body = json.loads(request.body) + except json.JSONDecodeError: + failure = { + 'message': f'Please provide valid json request!' + } + return JsonResponse(failure, status=400) task_data = { 'title': body.get('title'), @@ -73,7 +88,14 @@ def put(self, request): } return JsonResponse(missing_id_message, status=400) - task = Task.get_by_id(task_id=body.get('task_id')) + # Invalid ID + try: + task = Task.get_by_id(task_id=body.get('task_id')) + except ValueError: + invalid_data_message = { + 'message': f'Please provide valid ID' + } + return JsonResponse(invalid_data_message, status=400) # ID of non-existing task if not task: @@ -91,15 +113,21 @@ def put(self, request): return JsonResponse(success_message, status=200) # Invalid input - except DataError: + except (DataError, ValidationError, ValueError): invalid_data_message = { 'message': f'Cannot update task! One or more parameters are invalid' } return JsonResponse(invalid_data_message, status=400) def delete(self, request): - - body = json.loads(request.body) + # JSON validation + try: + body = json.loads(request.body) + except json.JSONDecodeError: + failure = { + 'message': f'Please provide valid json request!' + } + return JsonResponse(failure, status=400) if Task.remove(task_id=body.get('task_id')): success = { diff --git a/tests/task/tests.py b/tests/task/tests.py index e69de29..b951bf9 100644 --- a/tests/task/tests.py +++ b/tests/task/tests.py @@ -0,0 +1,92 @@ +import json +from django.test import TestCase +from task.models import Task +from todolist.models import ToDoList +from custom_user.models import CustomUser + + +class DeleteTaskView(TestCase): + """ Test view for deleting an existing task """ + + def setUp(self): + self.user = CustomUser.objects.create_user( + first_name="Test", last_name="User", email="mail@mail.com", password="secret") + self.list = ToDoList.create(name='List', description='About list', member_pk=self.user.id) + self.task = Task.create( + title='Task', description='Task', deadline='2020-01-01', list_id=self.list.id, user_id=self.user.id) + + def test_delete_task_existing(self): + response = self.client.generic('DELETE', '/tasks/', json.dumps({'task_id': self.task.id})) + self.assertEqual(response.status_code, 200) + + def test_delete_task_non_existing(self): + response = self.client.generic('DELETE', '/tasks/', json.dumps({'task_id': 10})) + self.assertEqual(response.status_code, 400) + + +class CreateTaskView(TestCase): + """ Test view for creating new task """ + + def setUp(self): + self.user = CustomUser.objects.create_user( + first_name="Test", last_name="User", email="mail@mail.com", password="secret") + self.list = ToDoList.create(name='List', description='About list', member_pk=self.user.id) + + def test_create_task_data_valid(self): + response = self.client.generic('POST', '/tasks/', json.dumps({ + "title": "Task", "description": "Task", "deadline": "2020-01-01", "user_id": self.user.id, + "list_id": self.list.id + })) + self.assertEqual(response.status_code, 201) + + def test_create_task_data_invalid(self): + response = self.client.generic('POST', '/tasks/', json.dumps({ + "title": "Task name that way beyond 30 symbol limit", + "description": "Task", "deadline": "2020-01-01", "user_id": self.user.id, "list_id": self.list.id + })) + self.assertEqual(response.status_code, 400) + + def test_create_task_missing(self): + response = self.client.generic('POST', '/tasks/', json.dumps({"description": "Task"})) + self.assertEqual(response.status_code, 400) + + +class UpdateTaskView(TestCase): + """ Test view for updating existing task """ + + def setUp(self): + self.user = CustomUser.objects.create_user( + first_name="Test", last_name="User", email="mail@mail.com", password="secret") + self.list = ToDoList.create(name='List', description='About list', member_pk=self.user.id) + self.task = Task.create( + title='Task', description='Task', deadline='2020-01-01', list_id=self.list.id, user_id=self.user.id) + + def test_update_task_existing(self): + response = self.client.generic('PUT', '/tasks/', json.dumps({ + "title": "UPDATE!", "task_id": self.task.id + })) + self.assertEqual(response.status_code, 200) + + def test_update_task_non_existing(self): + response = self.client.generic('PUT', '/tasks/', json.dumps({ + "title": "UPDATE!", "task_id": 10 + })) + self.assertEqual(response.status_code, 400) + + def test_update_task_data_invalid(self): + response = self.client.generic('PUT', '/tasks/', json.dumps({ + "deadline": "May 2021", "task_id": self.task.id + })) + self.assertEqual(response.status_code, 400) + + def test_update_task_id_invalid(self): + response = self.client.generic('PUT', '/tasks/', json.dumps({ + "deadline": "2020-01-01", "task_id": 'one' + })) + self.assertEqual(response.status_code, 400) + + def test_update_task_id_missing(self): + response = self.client.generic('PUT', '/tasks/', json.dumps({ + "deadline": "2020-01-01" + })) + self.assertEqual(response.status_code, 400) \ No newline at end of file From c43c9db06bc0aa951a06b9589647a8d26a418b00 Mon Sep 17 00:00:00 2001 From: Daniil Oleshchuk Date: Sat, 13 Mar 2021 19:47:19 +0200 Subject: [PATCH 052/100] added try block to ToDoList.views to check JSON validity --- todolist/views.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/todolist/views.py b/todolist/views.py index 908ec62..c9e18cb 100644 --- a/todolist/views.py +++ b/todolist/views.py @@ -24,7 +24,10 @@ def post(self, request): if not data: return HttpResponse(status=400) - data = json.loads(request.body) + try: + data = json.loads(request.body) + except json.JSONDecodeError: + return JsonResponse('Provided invalid JSON', status=400) data = { 'name': data.get('name'), @@ -47,7 +50,11 @@ def put(self, request, todo_list_pk=None): data = request.body if not data: return HttpResponse(status=400) - data = json.loads(request.body) + + try: + data = json.loads(request.body) + except json.JSONDecodeError: + return JsonResponse('Provided invalid JSON', status=400) members_to_add = data.get('members_to_add') members_to_delete = data.get('members_to_delete') From d777ee96071bb5c4dcd9d123d989c85522679385 Mon Sep 17 00:00:00 2001 From: StepanTchynetskyi Date: Sun, 14 Mar 2021 16:57:53 +0200 Subject: [PATCH 053/100] todolist tests, views and models fix --- tests/task/tests.py | 16 ++-- tests/todolist/tests.py | 158 ++++++++++++++++++++++++++++++++++++++++ todolist/models.py | 38 ++++++---- todolist/views.py | 36 +++++---- 4 files changed, 209 insertions(+), 39 deletions(-) diff --git a/tests/task/tests.py b/tests/task/tests.py index b951bf9..f94ceb9 100644 --- a/tests/task/tests.py +++ b/tests/task/tests.py @@ -1,8 +1,10 @@ import json + from django.test import TestCase + +from custom_user.models import CustomUser from task.models import Task from todolist.models import ToDoList -from custom_user.models import CustomUser class DeleteTaskView(TestCase): @@ -63,30 +65,30 @@ def setUp(self): def test_update_task_existing(self): response = self.client.generic('PUT', '/tasks/', json.dumps({ - "title": "UPDATE!", "task_id": self.task.id + "title": "UPDATE!", "task_id": self.task.id })) self.assertEqual(response.status_code, 200) def test_update_task_non_existing(self): response = self.client.generic('PUT', '/tasks/', json.dumps({ - "title": "UPDATE!", "task_id": 10 + "title": "UPDATE!", "task_id": 10 })) self.assertEqual(response.status_code, 400) def test_update_task_data_invalid(self): response = self.client.generic('PUT', '/tasks/', json.dumps({ - "deadline": "May 2021", "task_id": self.task.id + "deadline": "May 2021", "task_id": self.task.id })) self.assertEqual(response.status_code, 400) def test_update_task_id_invalid(self): response = self.client.generic('PUT', '/tasks/', json.dumps({ - "deadline": "2020-01-01", "task_id": 'one' + "deadline": "2020-01-01", "task_id": 'one' })) self.assertEqual(response.status_code, 400) def test_update_task_id_missing(self): response = self.client.generic('PUT', '/tasks/', json.dumps({ - "deadline": "2020-01-01" + "deadline": "2020-01-01" })) - self.assertEqual(response.status_code, 400) \ No newline at end of file + self.assertEqual(response.status_code, 400) diff --git a/tests/todolist/tests.py b/tests/todolist/tests.py index e69de29..c09527b 100644 --- a/tests/todolist/tests.py +++ b/tests/todolist/tests.py @@ -0,0 +1,158 @@ +import json +from django.test import TestCase +from todolist.models import ToDoList +from custom_user.models import CustomUser + + +class ToDoListViewTest(TestCase): + + def setUp(self): + self.user = CustomUser.objects.create( + id=1, first_name="TestUser", last_name="UserLastName", email="test@gmail.com", password="adminpassword") + self.user2 = CustomUser.objects.create( + id=2, first_name="TestUser2", last_name="UserLastName2", email="test2@gmail.com", password="adminpassword2") + self.todo_list = ToDoList.objects.create( + id=10, name="TestList", description="Test description") + self.todo_list.members.add(self.user.id) + + # Testing GET method + + def test_get_one_list(self): + response = self.client.get('/todolist/10/') + self.assertEqual(response.status_code, 200) + + def test_get_not_existing_list(self): + response = self.client.get('/todolist/28/') + self.assertEqual(response.status_code, 404) + + def test_get_one_wrong_list_pk(self): + response = self.client.get('/todolist/w/') + self.assertEqual(response.status_code, 404) + + def test_get_all_lists(self): + response = self.client.get('/todolist/') + self.assertEqual(response.status_code, 200) + + # Testing Post method + + def test_post_data_valid_data(self): + response = self.client.generic('POST', '/todolist/', json.dumps({ + "name": "name1", "description": "LIST1", "members": [self.user.id] + })) + self.assertEqual(response.status_code, 201) + + def test_post_data_no_data(self): + response = self.client.generic('POST', '/todolist/') + self.assertEqual(response.status_code, 400) + + def test_post_data_name_missing_fail(self): + response = self.client.generic('POST', '/todolist/', json.dumps({ + "description": "LIST1", "members": [self.user.id] + })) + self.assertEqual(response.status_code, 400) + + def test_post_data_description_missing_pass(self): + response = self.client.generic('POST', '/todolist/', json.dumps({ + "name": "name1", "members": [self.user.id] + })) + self.assertEqual(response.status_code, 201) + + def test_post_data_members_missing_pass(self): + response = self.client.generic('POST', '/todolist/', json.dumps({ + "name": "name1", "description": "LIST1" + })) + self.assertEqual(response.status_code, 201) + + def test_post_too_long_data_field(self): + name = "f"*60 + response = self.client.generic('POST', '/todolist/', json.dumps({ + "name": name, "description": "LIST1", "members": [self.user.id] + })) + self.assertEqual(response.status_code, 400) + + def test_post_wrong_json(self): + response = self.client.generic('POST', '/todolist/', '{"name": instance1, ') + self.assertEqual(response.status_code, 400) + + # Testing PUT method + + def test_put_missing_todo_list_pk(self): + response = self.client.generic('PUT', '/todolist/', json.dumps({ + "name": "namePUT", "description": "LIST1PUT", "members": [self.user.id] + })) + self.assertEqual(response.status_code, 404) + + def test_put_not_existing_todo_list_pk(self): + response = self.client.generic('PUT', '/todolist/42/', json.dumps({ + "name": "namePUT", "description": "LIST1PUT", "members": [self.user.id] + })) + self.assertEqual(response.status_code, 404) + + def test_put_wrong_todo_list_pk(self): + response = self.client.generic('PUT', '/todolist/w/', json.dumps({ + "name": "namePUT", "description": "LIST1PUT", "members": [self.user.id] + })) + self.assertEqual(response.status_code, 404) + + def test_put_no_data(self): + response = self.client.generic('PUT', '/todolist/10/') + self.assertEqual(response.status_code, 400) + + def test_put_valid_data(self): + response = self.client.generic('PUT', '/todolist/10/', json.dumps({ + "name": "namePUT", "description": "LIST1PUT", "members_to_add": [self.user2.id] + })) + self.assertEqual(response.status_code, 200) + + def test_put_wrong_json(self): + response = self.client.generic('PUT', '/todolist/10/', '{"name": instance1, ') + self.assertEqual(response.status_code, 400) + + def test_put_too_long_data_field(self): + name = "f" * 60 + response = self.client.generic('PUT', '/todolist/10/', json.dumps({ + "name": name, "description": "LIST1" + })) + self.assertEqual(response.status_code, 400) + + def test_put_members_to_add_pass(self): + response = self.client.generic('PUT', '/todolist/10/', json.dumps({ + "members_to_add": [2] + })) + self.assertEqual(response.status_code, 200) + + def test_put_members_to_delete_pass(self): + response = self.client.generic('PUT', '/todolist/10/', json.dumps({ + "members_to_delete": [1] + })) + self.assertEqual(response.status_code, 200) + + def test_put_members_to_add_fail(self): + response = self.client.generic('PUT', '/todolist/10/', json.dumps({ + "members_to_delete": 1 + })) + self.assertEqual(response.status_code, 400) + + def test_put_members_to_delete_fail(self): + response = self.client.generic('PUT', '/todolist/10/', json.dumps({ + "members_to_delete": "1a" + })) + self.assertEqual(response.status_code, 400) + + # Testing DELETE method + + def test_delete_missing_todo_list_pk(self): + response = self.client.generic('DELETE', '/todolist/') + self.assertEqual(response.status_code, 404) + + def test_delete_not_existing_todo_list_pk(self): + response = self.client.generic('DELETE', '/todolist/23/') + self.assertEqual(response.status_code, 404) + + def test_delete_wrong_todo_list_pk(self): + response = self.client.generic('DELETE', '/todolist/w/') + self.assertEqual(response.status_code, 404) + + def test_delete_pass(self): + response = self.client.generic('DELETE', '/todolist/10/') + self.assertEqual(response.status_code, 200) diff --git a/todolist/models.py b/todolist/models.py index b659f93..e518385 100644 --- a/todolist/models.py +++ b/todolist/models.py @@ -1,4 +1,4 @@ -from django.db import models, IntegrityError +from django.db import models, IntegrityError, DatabaseError from custom_user.models import CustomUser @@ -18,17 +18,25 @@ def to_dict(self): 'members': sorted([member.id for member in self.members.all()])} def update(self, name=None, description=None): - if name: - self.name = name - if description: - self.description = description - self.save() + try: + if name: + self.name = name + if description: + self.description = description + self.save() + return self + except (ValueError, IntegrityError, DatabaseError): + return None def update_members(self, members_to_add=None, members_to_delete=None): - if members_to_add: - self.members.add(*members_to_add) - if members_to_delete: - self.members.remove(*members_to_delete) + try: + if members_to_add: + self.members.add(*members_to_add) + if members_to_delete: + self.members.remove(*members_to_delete) + return self + except (TypeError, ValueError): + return None def get_list_members(self): members = self.members.all() @@ -50,17 +58,15 @@ def get_all(cls): @classmethod def create(cls, name, description='', members=None): - todo_list = ToDoList(name=name, description=description) try: + todo_list = ToDoList(name=name, description=description) todo_list.save() if members: todo_list.members.add(*members) return todo_list - except (ValueError, IntegrityError): + except (ValueError, IntegrityError, DatabaseError): # log error return None - @classmethod - def remove(cls, todo_list_pk): - todo_list = ToDoList.objects.get(pk=todo_list_pk) - todo_list.delete() + def remove(self): + self.delete() diff --git a/todolist/views.py b/todolist/views.py index c9e18cb..7eebf84 100644 --- a/todolist/views.py +++ b/todolist/views.py @@ -10,6 +10,8 @@ class ToDoListView(View): def get(self, request, todo_list_pk=None): if todo_list_pk: + if not todo_list_pk.isnumeric(): + return HttpResponse(status=404) todo_list = ToDoList.get_by_id(todo_list_pk) if not todo_list: return HttpResponse(status=404) @@ -17,7 +19,7 @@ def get(self, request, todo_list_pk=None): todo_lists = ToDoList.get_all() todo_lists = json.dumps([todo_list.to_dict() for todo_list in todo_lists]) - return HttpResponse(todo_lists, status=200, content_type="application/json") + return JsonResponse(todo_lists, status=200) def post(self, request): data = request.body @@ -27,7 +29,7 @@ def post(self, request): try: data = json.loads(request.body) except json.JSONDecodeError: - return JsonResponse('Provided invalid JSON', status=400) + return JsonResponse({"JsonError": "Provided invalid json"}, status=400) data = { 'name': data.get('name'), @@ -39,14 +41,15 @@ def post(self, request): todo_list = ToDoList.create(**data) if todo_list: return JsonResponse(todo_list.to_dict(), status=201) - return HttpResponse(status=400) def put(self, request, todo_list_pk=None): - todo_list = ToDoList.get_by_id(todo_list_pk) + if todo_list_pk and not todo_list_pk.isnumeric(): + return HttpResponse(status=404) + if todo_list_pk: + todo_list = ToDoList.get_by_id(todo_list_pk) if not todo_list: return HttpResponse(status=404) - data = request.body if not data: return HttpResponse(status=400) @@ -54,27 +57,28 @@ def put(self, request, todo_list_pk=None): try: data = json.loads(request.body) except json.JSONDecodeError: - return JsonResponse('Provided invalid JSON', status=400) + return JsonResponse({"JSON Error": 'Provided invalid JSON'}, status=400) members_to_add = data.get('members_to_add') members_to_delete = data.get('members_to_delete') if members_to_add or members_to_delete: - todo_list.update_members(members_to_add, members_to_delete) - + todo_list = todo_list.update_members(members_to_add, members_to_delete) + if not todo_list: + return HttpResponse(status=400) data = {'name': data.get('name') if data.get('name') else None, 'description': data.get('description') if data.get('description') else None} - todo_list.update(**data) - + todo_list = todo_list.update(**data) + if not todo_list: + return HttpResponse(status=400) return HttpResponse(status=200) def delete(self, request, todo_list_pk=None): + if todo_list_pk and not todo_list_pk.isnumeric(): + return HttpResponse(status=404) todo_list = ToDoList.get_by_id(todo_list_pk) if not todo_list: - return HttpResponse(status=400) - - ToDoList.remove(todo_list_pk) + return HttpResponse(status=404) - if not ToDoList.get_by_id(todo_list_pk): - return HttpResponse(status=200) - return HttpResponse(status=400) + todo_list.remove() + return HttpResponse(status=200) From e81748fdeffb20c9547e6b0e3a868afd5fce331f Mon Sep 17 00:00:00 2001 From: StepanTchynetskyi Date: Sun, 14 Mar 2021 17:26:32 +0200 Subject: [PATCH 054/100] Fix get method in ToDoListView --- todolist/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/todolist/views.py b/todolist/views.py index 7eebf84..8ddad30 100644 --- a/todolist/views.py +++ b/todolist/views.py @@ -19,7 +19,7 @@ def get(self, request, todo_list_pk=None): todo_lists = ToDoList.get_all() todo_lists = json.dumps([todo_list.to_dict() for todo_list in todo_lists]) - return JsonResponse(todo_lists, status=200) + return HttpResponse(todo_lists, status=200, content_type="application/json") def post(self, request): data = request.body From 380b042e2f286c8fbeaa5720ca7d0182d98b71d8 Mon Sep 17 00:00:00 2001 From: Nevmerzhitsky Yura Date: Sun, 14 Mar 2021 17:33:38 +0200 Subject: [PATCH 055/100] Refactored custom_user/view --- custom_user/urls.py | 1 + custom_user/views.py | 88 +++++++++++++++++++++++++++++--------------- 2 files changed, 60 insertions(+), 29 deletions(-) diff --git a/custom_user/urls.py b/custom_user/urls.py index f55a144..76f4488 100644 --- a/custom_user/urls.py +++ b/custom_user/urls.py @@ -4,4 +4,5 @@ urlpatterns = [ path('profile//', views.ProfileView.as_view(), name='profile'), + path('create/', views.ProfileView.as_view(), name='create'), ] diff --git a/custom_user/views.py b/custom_user/views.py index 021b2ee..bea698d 100644 --- a/custom_user/views.py +++ b/custom_user/views.py @@ -1,6 +1,6 @@ -from django.shortcuts import redirect -from django.views import View +import json from django.http import JsonResponse, HttpResponse +from django.views import View from .models import CustomUser @@ -8,37 +8,67 @@ class ProfileView(View): def get(self, request, user_id=None): - user = CustomUser.find_by_id(user_id) + user = CustomUser.get_by_id(user_id) if user: return JsonResponse(user.to_dict()) return HttpResponse(status=400) - def put(self, request, user_id=None, first_name=None, last_name=None, email=None): - user = CustomUser.find_by_id(user_id) - if user: - if first_name == None: - first_name = user.first_name - if last_name == None: - last_name = user.last_name - if email == None: - email = user.email - data = {'first_name' : first_name, 'last_name' : last_name, 'email' : email} - CustomUser.update_user(user_id, data) - return redirect('profile', user_id=user.id) - return HttpResponse(status=400) + def post(self, request): + if not request.body: + return HttpResponse("Empty data input", status=400) + try: + body = json.loads(request.body) + except json.JSONDecodeError: + return HttpResponse("Invalid JSON", status=400) - def delete(self, request, user_id=None): - user = CustomUser.find_by_id(user_id) - if user: - user = CustomUser.delete_user(user_id) - return HttpResponse("User was deleted") - return HttpResponse(status=400) + data = {'first_name': body.get('first_name'), 'last_name': body.get('last_name'), + 'password': body.get('password'), 'email': body.get('email')} + + new_user = CustomUser.objects.create(**data) + if new_user: + return JsonResponse(new_user.to_dict(), status=200) + return HttpResponse('Something went wrong', status=400) + + @staticmethod + def put(request, user_id=None): + user = CustomUser.get_by_id(user_id) - def post(self, first_name=None, last_name=None, email=None): - if first_name == None: - return HttpResponse('Please, add first name') - if last_name == None: - return HttpResponse('Please add last name') - user = CustomUser.objects.create_user(first_name, last_name, email) - return redirect('profile', user_id=user.id) + if not user: + return HttpResponse(status=404) + if not request.body: + return HttpResponse("Empty data input", status=400) + + try: + body = json.loads(request.body) + except json.JSONDecodeError: + return HttpResponse("Invalid JSON", status=400) + + data = {} + + if not body.get('first_name'): + data['first_name'] = user.first_name + else: + data['first_name'] = body.get('first_name') + if not body.get('last_name'): + data['last_name'] = user.last_name + else: + data['last_name'] = body.get('last_name') + if not body.get('email'): + data['email'] = user.email + else: + data['email'] = body.get('email') + + updated_user = CustomUser.update(user,data) + + if updated_user: + return JsonResponse( user.to_dict(), status=200) + return HttpResponse(status= 400) + + @staticmethod + def delete(request, user_id=None): + user = CustomUser.get_by_id(user_id) + if user: + CustomUser.remove(user_id) + return HttpResponse('User removed.', status=200) + return HttpResponse('User not found', status=400) From 5226df1357bce74136e8eaa448031bebb5df11ff Mon Sep 17 00:00:00 2001 From: Daniil Oleshchuk Date: Sun, 14 Mar 2021 19:05:19 +0200 Subject: [PATCH 056/100] added base tests for ToDoList model CRUD --- tests/todolist/tests.py | 50 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/tests/todolist/tests.py b/tests/todolist/tests.py index c09527b..26c4289 100644 --- a/tests/todolist/tests.py +++ b/tests/todolist/tests.py @@ -156,3 +156,53 @@ def test_delete_wrong_todo_list_pk(self): def test_delete_pass(self): response = self.client.generic('DELETE', '/todolist/10/') self.assertEqual(response.status_code, 200) + + +class ToDoListCRUDTest(TestCase): + def setUp(self) -> None: + self.user = CustomUser.objects.create(id=1, first_name="TestUser1", last_name="UserLastName1", email="test1@gmail.com", password="adminpassword1") + self.user2 = CustomUser.objects.create(id=2, first_name="TestUser2", last_name="UserLastName2", email="test2@gmail.com", password="adminpassword2") + + self.todo_list = ToDoList.objects.create(name="TestList", description="Test description") + self.todo_list.members.add(self.user.id) + + def test_create(self): + todo_list1 = ToDoList.create('test list name 1') + todo_list2 = ToDoList.create(name='test list name 2', description='test list description 2') + todo_list3 = ToDoList.create(name='test list name 3', description='test list description 3', + members=[self.user, self.user2]) + + first_to_dict_expected = {'id': 2, 'name': 'test list name 1', 'description': '', 'members': []} + second_to_dict_expected = {'id': 3, 'name': 'test list name 2', + 'description': 'test list description 2', 'members': []} + third_to_dict_expected = {'id': 4, 'name': 'test list name 3', + 'description': 'test list description 3', 'members': [1, 2]} + + self.assertEqual(first_to_dict_expected, todo_list1.to_dict()) + self.assertEqual(second_to_dict_expected, todo_list2.to_dict()) + self.assertEqual(third_to_dict_expected, todo_list3.to_dict()) + + def test_update(self): + new_name = 'new list name' + self.todo_list.update(name=new_name) + self.assertEqual(new_name, self.todo_list.name) + + new_description = 'new list description' + self.todo_list.update(description=new_description) + self.assertEqual(new_description, self.todo_list.description) + + new_name, new_description = 'absolutely new list name', 'absolutely new list description' + self.todo_list.update(name=new_name, description=new_description) + self.assertEqual(new_name, self.todo_list.name) + self.assertEqual(new_description, self.todo_list.description) + + def test_get_by_id(self): + todo_list = ToDoList.get_by_id(1) + expected_to_dict = {'id': 1, 'name': 'TestList', 'description': 'Test description', 'members': [1]} + self.assertEqual(expected_to_dict, todo_list.to_dict()) + + def test_delete_by_id(self): + todo_list = ToDoList.get_by_id(1) + todo_list.remove() + todo_list = ToDoList.get_by_id(1) + self.assertEqual(None, todo_list) From 95a92bdfa619d9186375cb32f956917e700c8195 Mon Sep 17 00:00:00 2001 From: Nevmerzhitsky Yura Date: Sun, 14 Mar 2021 21:23:49 +0200 Subject: [PATCH 057/100] Test for views --- tests/custom_user/tests.py | 53 +++++++++++++++++++++++++++++++++++++- 1 file changed, 52 insertions(+), 1 deletion(-) diff --git a/tests/custom_user/tests.py b/tests/custom_user/tests.py index d535c9d..7480905 100644 --- a/tests/custom_user/tests.py +++ b/tests/custom_user/tests.py @@ -1,5 +1,7 @@ -from django.test import TestCase +from django.test import TestCase, Client from custom_user.models import CustomUser +import json + class CustomUserModelsTestCase(TestCase): @@ -64,3 +66,52 @@ def test_remove_user(self): password='test1') res = CustomUser.remove(2) self.assertIn(b'User removed', res.content) + +class CustomUserViewsTestCase(TestCase): + def setUp(self): + self.user = CustomUser.objects.create(id=2, + first_name="Test", + last_name="User", + email="testuser@gmail.com", + password="test") + + self.user = CustomUser.objects.create(id=3, + first_name="Test2", + last_name="User2", + email="testuser2@gmail.com", + password="test2") + + + + def test_get_by_id(self): + data = {'first_name': 'Test', 'last_name': 'User', 'email': 'testuser@gmail.com'} + responce = self.client.get('/custom_user/profile/2/') + self.assertEqual(responce.json() , data) + + def test_create_user(self): + data = {'first_name': 'Test3', 'last_name': 'User3', 'email': 'testuser3@gmail.com'} + response = self.client.generic('POST', '/custom_user/create/', json.dumps({'first_name': 'Test3', + 'last_name': 'User3', + 'email': 'testuser3@gmail.com'})) + self.assertEqual(response.json(),data) + + def test_put_all_input_data(self): + data = {'first_name': 'NewName', 'last_name': 'User', 'email': 'testuser@gmail.com'} + response = self.client.generic('PUT', '/custom_user/profile/2/', json.dumps({'first_name': 'NewName', + 'last_name': 'User', + 'email': 'testuser@gmail.com'})) + self.assertEqual(response.json(), data) + + def test_put_not_all_input_data(self): + data = {'first_name': 'Test', 'last_name': 'NewUser', 'email': 'testuser@gmail.com'} + response = self.client.generic('PUT', '/custom_user/profile/2/', json.dumps({'last_name': 'NewUser', + 'email': 'testuser@gmail.com'})) + self.assertEqual(response.json(), data) + + def test_delete(self): + response = self.client.generic('DELETE', '/custom_user/profile/2/') + self.assertEqual(response.status_code, 200) + + + + From 88b2237740e365129ec3cf7c5d7b31e653f5270a Mon Sep 17 00:00:00 2001 From: StepanTchynetskyi Date: Sun, 14 Mar 2021 22:03:38 +0200 Subject: [PATCH 058/100] added swagger --- custom_user/views.py | 9 ++++----- project_config/settings.py | 5 ++++- project_config/urls.py | 18 ++++++++++++++++++ requirements.txt | 7 ++++++- task/views.py | 3 ++- todolist/views.py | 4 ++-- 6 files changed, 36 insertions(+), 10 deletions(-) diff --git a/custom_user/views.py b/custom_user/views.py index 021b2ee..4e3fe45 100644 --- a/custom_user/views.py +++ b/custom_user/views.py @@ -1,11 +1,11 @@ -from django.shortcuts import redirect -from django.views import View from django.http import JsonResponse, HttpResponse +from django.shortcuts import redirect +from rest_framework.views import APIView from .models import CustomUser -class ProfileView(View): +class ProfileView(APIView): def get(self, request, user_id=None): user = CustomUser.find_by_id(user_id) @@ -22,7 +22,7 @@ def put(self, request, user_id=None, first_name=None, last_name=None, email=None last_name = user.last_name if email == None: email = user.email - data = {'first_name' : first_name, 'last_name' : last_name, 'email' : email} + data = {'first_name': first_name, 'last_name': last_name, 'email': email} CustomUser.update_user(user_id, data) return redirect('profile', user_id=user.id) return HttpResponse(status=400) @@ -41,4 +41,3 @@ def post(self, first_name=None, last_name=None, email=None): return HttpResponse('Please add last name') user = CustomUser.objects.create_user(first_name, last_name, email) return redirect('profile', user_id=user.id) - diff --git a/project_config/settings.py b/project_config/settings.py index 015080f..07a324d 100644 --- a/project_config/settings.py +++ b/project_config/settings.py @@ -36,7 +36,10 @@ 'django.contrib.staticfiles', 'todolist', 'custom_user', - 'task' + 'task', + 'rest_framework', + 'drf_yasg', + ] MIDDLEWARE = [ diff --git a/project_config/urls.py b/project_config/urls.py index cb05c92..fb1a831 100644 --- a/project_config/urls.py +++ b/project_config/urls.py @@ -14,9 +14,27 @@ 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) """ from django.urls import path, include +from drf_yasg import openapi +from drf_yasg.views import get_schema_view +from rest_framework import permissions + +schema_view = get_schema_view( + openapi.Info( + title="ToDoList API", + default_version='v1', + description="API for CRUD", + terms_of_service="https://todolist/policies/terms/", + contact=openapi.Contact(email="contact@todolist.remote"), + license=openapi.License(name="ToDoList License"), + ), + public=True, + permission_classes=(permissions.AllowAny,), +) urlpatterns = [ path('custom_user/', include('custom_user.urls')), path('todolist/', include('todolist.urls')), path('tasks/', include('task.urls')), + path('', schema_view.with_ui('swagger', cache_timeout=0), name='schema-swagger-ui'), + path('redoc/', schema_view.with_ui('redoc', cache_timeout=0), name='schema-redoc'), ] diff --git a/requirements.txt b/requirements.txt index 4ab4b30..59db430 100644 --- a/requirements.txt +++ b/requirements.txt @@ -13,4 +13,9 @@ pytz==2021.1 sqlparse==0.4.1 toml==0.10.2 wrapt==1.12.1 -coverage==5.5 \ No newline at end of file +coverage==5.5 +djangorestframework~=3.12.2 +pip~=21.0.1 +setuptools~=54.1.1 +Jinja2~=2.11.3 +drf-yasg==1.20.0 diff --git a/task/views.py b/task/views.py index 10d2df0..8ef5887 100644 --- a/task/views.py +++ b/task/views.py @@ -1,5 +1,6 @@ from django.http import JsonResponse from django.views import View +from rest_framework.views import APIView from task.models import Task from django.core.serializers import serialize import json @@ -7,7 +8,7 @@ from django.core.exceptions import ValidationError -class TaskAPIView(View): +class TaskAPIView(APIView): def get(self, request, list_id): task = Task.objects.filter(list_id=list_id) diff --git a/todolist/views.py b/todolist/views.py index 8ddad30..ea1c5be 100644 --- a/todolist/views.py +++ b/todolist/views.py @@ -1,12 +1,12 @@ import json from django.http import JsonResponse, HttpResponse -from django.views import View +from rest_framework.views import APIView from .models import CustomUser, ToDoList -class ToDoListView(View): +class ToDoListView(APIView): def get(self, request, todo_list_pk=None): if todo_list_pk: From 7d88b39d6234bdd7de781449792bb1a111dbc595 Mon Sep 17 00:00:00 2001 From: SLDem Date: Mon, 15 Mar 2021 10:24:34 +0200 Subject: [PATCH 059/100] Refractored every app and tests --- custom_user/views.py | 23 +++++++++-------- task/models.py | 18 +++++++++----- task/views.py | 26 ++++++++++--------- tests/task/tests.py | 55 ++++++++++++++++++++++++++++++----------- tests/todolist/tests.py | 47 ++++++++++++++++++++++++----------- todolist/views.py | 2 +- 6 files changed, 111 insertions(+), 60 deletions(-) diff --git a/custom_user/views.py b/custom_user/views.py index 021b2ee..02e91e6 100644 --- a/custom_user/views.py +++ b/custom_user/views.py @@ -8,37 +8,36 @@ class ProfileView(View): def get(self, request, user_id=None): - user = CustomUser.find_by_id(user_id) + user = CustomUser.get_by_id(user_id) if user: return JsonResponse(user.to_dict()) return HttpResponse(status=400) def put(self, request, user_id=None, first_name=None, last_name=None, email=None): - user = CustomUser.find_by_id(user_id) + user = CustomUser.get_by_id(user_id) if user: - if first_name == None: + if first_name is None: first_name = user.first_name - if last_name == None: + if last_name is None: last_name = user.last_name - if email == None: + if email is None: email = user.email - data = {'first_name' : first_name, 'last_name' : last_name, 'email' : email} - CustomUser.update_user(user_id, data) + data = {'first_name': first_name, 'last_name': last_name, 'email': email} + CustomUser.update(user_id, data) return redirect('profile', user_id=user.id) return HttpResponse(status=400) def delete(self, request, user_id=None): - user = CustomUser.find_by_id(user_id) + user = CustomUser.get_by_id(user_id) if user: - user = CustomUser.delete_user(user_id) + user = CustomUser.delete(user_id) return HttpResponse("User was deleted") return HttpResponse(status=400) def post(self, first_name=None, last_name=None, email=None): - if first_name == None: + if first_name is None: return HttpResponse('Please, add first name') - if last_name == None: + if last_name is None: return HttpResponse('Please add last name') user = CustomUser.objects.create_user(first_name, last_name, email) return redirect('profile', user_id=user.id) - diff --git a/task/models.py b/task/models.py index 3890157..143daa8 100644 --- a/task/models.py +++ b/task/models.py @@ -1,7 +1,8 @@ +from datetime import datetime + from django.db import models from todolist.models import ToDoList from custom_user.models import CustomUser -from datetime import datetime class Task(models.Model): @@ -39,14 +40,19 @@ def find_all_for_list(cls, list_id): @classmethod def create(cls, title: str, description: str, deadline, user_id, list_id): task = Task(title=title, description=description, deadline=deadline) - user = CustomUser.find_by_id(user_id) + user = CustomUser.get_by_id(user_id) task.user_id = user - list = ToDoList.get_by_id(list_id) - task.list_id = list + todolist = ToDoList.get_by_id(list_id) + task.list_id = todolist task.save() return task - def update(self, title: str, description: str, is_completed: bool, deadline: datetime, user_id: int, list_id: int): + def update(self, title: str, + description: str, + is_completed: bool, + deadline: datetime, + user_id: int, + list_id: int): if title: self.title = title if description: @@ -56,7 +62,7 @@ def update(self, title: str, description: str, is_completed: bool, deadline: dat if is_completed: self.is_completed = is_completed if user_id: - self.user_id = CustomUser.find_by_id(user_id) + self.user_id = CustomUser.get_by_id(user_id) if list_id: self.list_id = ToDoList.get_by_id(list_id) self.save() diff --git a/task/views.py b/task/views.py index 10d2df0..47d6797 100644 --- a/task/views.py +++ b/task/views.py @@ -1,11 +1,13 @@ +import json + +from django.core.serializers import serialize from django.http import JsonResponse from django.views import View -from task.models import Task -from django.core.serializers import serialize -import json from django.db.utils import IntegrityError, DataError from django.core.exceptions import ValidationError +from task.models import Task + class TaskAPIView(View): @@ -25,7 +27,7 @@ def post(self, request): body = json.loads(request.body) except json.JSONDecodeError: failure = { - 'message': f'Please provide valid json request!' + 'message': 'Please provide valid json request!' } return JsonResponse(failure, status=400) @@ -50,14 +52,14 @@ def post(self, request): # Missing parameters except IntegrityError: integrity_message = { - 'message': f'Cannot create task! One or more parameters are missing' + 'message': 'Cannot create task! One or more parameters are missing' } return JsonResponse(integrity_message, status=400) # Invalid input except (DataError, ValidationError, ValueError): invalid_data_message = { - 'message': f'Cannot create task! One or more parameters are invalid' + 'message': 'Cannot create task! One or more parameters are invalid' } return JsonResponse(invalid_data_message, status=400) @@ -68,7 +70,7 @@ def put(self, request): body = json.loads(request.body) except json.JSONDecodeError: failure = { - 'message': f'Please provide valid json request!' + 'message': 'Please provide valid json request!' } return JsonResponse(failure, status=400) @@ -84,7 +86,7 @@ def put(self, request): # Missing ID if not body.get("task_id"): missing_id_message = { - 'message': f'Cannot update task! Task id is missing!' + 'message': 'Cannot update task! Task id is missing!' } return JsonResponse(missing_id_message, status=400) @@ -93,14 +95,14 @@ def put(self, request): task = Task.get_by_id(task_id=body.get('task_id')) except ValueError: invalid_data_message = { - 'message': f'Please provide valid ID' + 'message': 'Please provide valid ID' } return JsonResponse(invalid_data_message, status=400) # ID of non-existing task if not task: not_exist_message = { - 'message': f'Cannot update task! Task with {body.get("task_id")} does not exist' + 'message': 'Cannot update task! Task with {body.get("task_id")} does not exist' } return JsonResponse(not_exist_message, status=400) @@ -115,7 +117,7 @@ def put(self, request): # Invalid input except (DataError, ValidationError, ValueError): invalid_data_message = { - 'message': f'Cannot update task! One or more parameters are invalid' + 'message': 'Cannot update task! One or more parameters are invalid' } return JsonResponse(invalid_data_message, status=400) @@ -125,7 +127,7 @@ def delete(self, request): body = json.loads(request.body) except json.JSONDecodeError: failure = { - 'message': f'Please provide valid json request!' + 'message': 'Please provide valid json request!' } return JsonResponse(failure, status=400) diff --git a/tests/task/tests.py b/tests/task/tests.py index f94ceb9..1794371 100644 --- a/tests/task/tests.py +++ b/tests/task/tests.py @@ -11,11 +11,19 @@ class DeleteTaskView(TestCase): """ Test view for deleting an existing task """ def setUp(self): - self.user = CustomUser.objects.create_user( - first_name="Test", last_name="User", email="mail@mail.com", password="secret") - self.list = ToDoList.create(name='List', description='About list', member_pk=self.user.id) - self.task = Task.create( - title='Task', description='Task', deadline='2020-01-01', list_id=self.list.id, user_id=self.user.id) + self.user = CustomUser.objects.create_user(first_name="Test", + last_name="User", + email="mail@mail.com", + password="secret" + ) + self.list = ToDoList.create(name='List', + description='About list', + member_pk=self.user.id) + self.task = Task.create(title='Task', + description='Task', + deadline='2020-01-01', + list_id=self.list.id, + user_id=self.user.id) def test_delete_task_existing(self): response = self.client.generic('DELETE', '/tasks/', json.dumps({'task_id': self.task.id})) @@ -30,13 +38,20 @@ class CreateTaskView(TestCase): """ Test view for creating new task """ def setUp(self): - self.user = CustomUser.objects.create_user( - first_name="Test", last_name="User", email="mail@mail.com", password="secret") - self.list = ToDoList.create(name='List', description='About list', member_pk=self.user.id) + self.user = CustomUser.objects.create_user(first_name="Test", + last_name="User", + email="mail@mail.com", + password="secret") + self.list = ToDoList.create(name='List', + description='About list', + member_pk=self.user.id) def test_create_task_data_valid(self): response = self.client.generic('POST', '/tasks/', json.dumps({ - "title": "Task", "description": "Task", "deadline": "2020-01-01", "user_id": self.user.id, + "title": "Task", + "description": "Task", + "deadline": "2020-01-01", + "user_id": self.user.id, "list_id": self.list.id })) self.assertEqual(response.status_code, 201) @@ -44,7 +59,10 @@ def test_create_task_data_valid(self): def test_create_task_data_invalid(self): response = self.client.generic('POST', '/tasks/', json.dumps({ "title": "Task name that way beyond 30 symbol limit", - "description": "Task", "deadline": "2020-01-01", "user_id": self.user.id, "list_id": self.list.id + "description": "Task", + "deadline": "2020-01-01", + "user_id": self.user.id, + "list_id": self.list.id })) self.assertEqual(response.status_code, 400) @@ -57,11 +75,18 @@ class UpdateTaskView(TestCase): """ Test view for updating existing task """ def setUp(self): - self.user = CustomUser.objects.create_user( - first_name="Test", last_name="User", email="mail@mail.com", password="secret") - self.list = ToDoList.create(name='List', description='About list', member_pk=self.user.id) - self.task = Task.create( - title='Task', description='Task', deadline='2020-01-01', list_id=self.list.id, user_id=self.user.id) + self.user = CustomUser.objects.create_user(first_name="Test", + last_name="User", + email="mail@mail.com", + password="secret") + self.list = ToDoList.create(name='List', + description='About list', + member_pk=self.user.id) + self.task = Task.create(title='Task', + description='Task', + deadline='2020-01-01', + list_id=self.list.id, + user_id=self.user.id) def test_update_task_existing(self): response = self.client.generic('PUT', '/tasks/', json.dumps({ diff --git a/tests/todolist/tests.py b/tests/todolist/tests.py index c09527b..5e652a6 100644 --- a/tests/todolist/tests.py +++ b/tests/todolist/tests.py @@ -7,12 +7,19 @@ class ToDoListViewTest(TestCase): def setUp(self): - self.user = CustomUser.objects.create( - id=1, first_name="TestUser", last_name="UserLastName", email="test@gmail.com", password="adminpassword") - self.user2 = CustomUser.objects.create( - id=2, first_name="TestUser2", last_name="UserLastName2", email="test2@gmail.com", password="adminpassword2") - self.todo_list = ToDoList.objects.create( - id=10, name="TestList", description="Test description") + self.user = CustomUser.objects.create(id=1, + first_name="TestUser", + last_name="UserLastName", + email="test@gmail.com", + password="adminpassword") + self.user2 = CustomUser.objects.create(id=2, + first_name="TestUser2", + last_name="UserLastName2", + email="test2@gmail.com", + password="adminpassword2") + self.todo_list = ToDoList.objects.create(id=10, + name="TestList", + description="Test description") self.todo_list.members.add(self.user.id) # Testing GET method @@ -37,7 +44,8 @@ def test_get_all_lists(self): def test_post_data_valid_data(self): response = self.client.generic('POST', '/todolist/', json.dumps({ - "name": "name1", "description": "LIST1", "members": [self.user.id] + "name": "name1", + "description": "LIST1", "members": [self.user.id] })) self.assertEqual(response.status_code, 201) @@ -47,19 +55,22 @@ def test_post_data_no_data(self): def test_post_data_name_missing_fail(self): response = self.client.generic('POST', '/todolist/', json.dumps({ - "description": "LIST1", "members": [self.user.id] + "description": "LIST1", + "members": [self.user.id] })) self.assertEqual(response.status_code, 400) def test_post_data_description_missing_pass(self): response = self.client.generic('POST', '/todolist/', json.dumps({ - "name": "name1", "members": [self.user.id] + "name": "name1", + "members": [self.user.id] })) self.assertEqual(response.status_code, 201) def test_post_data_members_missing_pass(self): response = self.client.generic('POST', '/todolist/', json.dumps({ - "name": "name1", "description": "LIST1" + "name": "name1", + "description": "LIST1" })) self.assertEqual(response.status_code, 201) @@ -78,19 +89,25 @@ def test_post_wrong_json(self): def test_put_missing_todo_list_pk(self): response = self.client.generic('PUT', '/todolist/', json.dumps({ - "name": "namePUT", "description": "LIST1PUT", "members": [self.user.id] + "name": "namePUT", + "description": "LIST1PUT", + "members": [self.user.id] })) self.assertEqual(response.status_code, 404) def test_put_not_existing_todo_list_pk(self): response = self.client.generic('PUT', '/todolist/42/', json.dumps({ - "name": "namePUT", "description": "LIST1PUT", "members": [self.user.id] + "name": "namePUT", + "description": "LIST1PUT", + "members": [self.user.id] })) self.assertEqual(response.status_code, 404) def test_put_wrong_todo_list_pk(self): response = self.client.generic('PUT', '/todolist/w/', json.dumps({ - "name": "namePUT", "description": "LIST1PUT", "members": [self.user.id] + "name": "namePUT", + "description": "LIST1PUT", + "members": [self.user.id] })) self.assertEqual(response.status_code, 404) @@ -100,7 +117,9 @@ def test_put_no_data(self): def test_put_valid_data(self): response = self.client.generic('PUT', '/todolist/10/', json.dumps({ - "name": "namePUT", "description": "LIST1PUT", "members_to_add": [self.user2.id] + "name": "namePUT", + "description": "LIST1PUT", + "members_to_add": [self.user2.id] })) self.assertEqual(response.status_code, 200) diff --git a/todolist/views.py b/todolist/views.py index 8ddad30..7eebf84 100644 --- a/todolist/views.py +++ b/todolist/views.py @@ -19,7 +19,7 @@ def get(self, request, todo_list_pk=None): todo_lists = ToDoList.get_all() todo_lists = json.dumps([todo_list.to_dict() for todo_list in todo_lists]) - return HttpResponse(todo_lists, status=200, content_type="application/json") + return JsonResponse(todo_lists, status=200) def post(self, request): data = request.body From f72ef832f2c12108b28aca7b99ce00b7395c467b Mon Sep 17 00:00:00 2001 From: SLDem Date: Mon, 15 Mar 2021 10:34:40 +0200 Subject: [PATCH 060/100] Fixed missing f-string --- task/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/task/views.py b/task/views.py index 47d6797..768fa6c 100644 --- a/task/views.py +++ b/task/views.py @@ -102,7 +102,7 @@ def put(self, request): # ID of non-existing task if not task: not_exist_message = { - 'message': 'Cannot update task! Task with {body.get("task_id")} does not exist' + 'message': f'Cannot update task! Task with {body.get("task_id")} does not exist' } return JsonResponse(not_exist_message, status=400) From 804cd126c1ca73a5ecf818f32eb38083bd45d49e Mon Sep 17 00:00:00 2001 From: Daniil Oleshchuk Date: Mon, 15 Mar 2021 10:38:40 +0200 Subject: [PATCH 061/100] fixed put method in todolist views --- todolist/views.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/todolist/views.py b/todolist/views.py index 7eebf84..b015cfe 100644 --- a/todolist/views.py +++ b/todolist/views.py @@ -46,8 +46,7 @@ def post(self, request): def put(self, request, todo_list_pk=None): if todo_list_pk and not todo_list_pk.isnumeric(): return HttpResponse(status=404) - if todo_list_pk: - todo_list = ToDoList.get_by_id(todo_list_pk) + todo_list = ToDoList.get_by_id(todo_list_pk) if not todo_list: return HttpResponse(status=404) data = request.body From 2add84539b4fae27fe32ff89fdfd12479224d0ea Mon Sep 17 00:00:00 2001 From: Daniil Oleshchuk Date: Mon, 15 Mar 2021 10:54:05 +0200 Subject: [PATCH 062/100] returned get_all() method of ToDoList to previous state --- todolist/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/todolist/views.py b/todolist/views.py index b015cfe..820b6a1 100644 --- a/todolist/views.py +++ b/todolist/views.py @@ -19,7 +19,7 @@ def get(self, request, todo_list_pk=None): todo_lists = ToDoList.get_all() todo_lists = json.dumps([todo_list.to_dict() for todo_list in todo_lists]) - return JsonResponse(todo_lists, status=200) + return HttpResponse(todo_lists, status=200, content_type="application/json") def post(self, request): data = request.body From e345a97203cbaf8570028438898e3b9459917ed8 Mon Sep 17 00:00:00 2001 From: Daniil Oleshchuk Date: Mon, 15 Mar 2021 12:59:19 +0200 Subject: [PATCH 063/100] fixed ToDoList CRUD tests --- tests/todolist/tests.py | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/tests/todolist/tests.py b/tests/todolist/tests.py index a351fb9..b23eabb 100644 --- a/tests/todolist/tests.py +++ b/tests/todolist/tests.py @@ -179,8 +179,12 @@ def test_delete_pass(self): class ToDoListCRUDTest(TestCase): def setUp(self) -> None: - self.user = CustomUser.objects.create(id=1, first_name="TestUser1", last_name="UserLastName1", email="test1@gmail.com", password="adminpassword1") - self.user2 = CustomUser.objects.create(id=2, first_name="TestUser2", last_name="UserLastName2", email="test2@gmail.com", password="adminpassword2") + self.user = CustomUser.objects.create(id=1, first_name="TestUser1", + last_name="UserLastName1", email="test1@gmail.com", + password="adminpassword1") + self.user2 = CustomUser.objects.create(id=2, first_name="TestUser2", + last_name="UserLastName2", email="test2@gmail.com", + password="adminpassword2") self.todo_list = ToDoList.objects.create(name="TestList", description="Test description") self.todo_list.members.add(self.user.id) @@ -216,12 +220,13 @@ def test_update(self): self.assertEqual(new_description, self.todo_list.description) def test_get_by_id(self): - todo_list = ToDoList.get_by_id(1) - expected_to_dict = {'id': 1, 'name': 'TestList', 'description': 'Test description', 'members': [1]} + todo_list = ToDoList.get_by_id(self.todo_list.id) + expected_to_dict = {'id': self.todo_list.id, 'name': 'TestList', + 'description': 'Test description', 'members': [1]} self.assertEqual(expected_to_dict, todo_list.to_dict()) def test_delete_by_id(self): - todo_list = ToDoList.get_by_id(1) + todo_list = ToDoList.get_by_id(self.todo_list.id) todo_list.remove() - todo_list = ToDoList.get_by_id(1) + todo_list = ToDoList.get_by_id(self.todo_list.id) self.assertEqual(None, todo_list) From 7091355c4ec78de5f875c7eab1697a6d87873d7a Mon Sep 17 00:00:00 2001 From: OstapOmelchuk <75321892+OstapOmelchuk@users.noreply.github.com> Date: Mon, 15 Mar 2021 14:38:41 +0200 Subject: [PATCH 064/100] Delete Pipfile --- Pipfile | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 Pipfile diff --git a/Pipfile b/Pipfile deleted file mode 100644 index e69de29..0000000 From f461e28fbcffb34e6922f6a477995c6461687ade Mon Sep 17 00:00:00 2001 From: OstapOmelchuk <75321892+OstapOmelchuk@users.noreply.github.com> Date: Mon, 15 Mar 2021 14:39:21 +0200 Subject: [PATCH 065/100] Delete manage.py --- manage.py | 22 ---------------------- 1 file changed, 22 deletions(-) delete mode 100755 manage.py diff --git a/manage.py b/manage.py deleted file mode 100755 index f4d2cb4..0000000 --- a/manage.py +++ /dev/null @@ -1,22 +0,0 @@ -#!/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', 'project_config.settings') - 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 2125589683ba5f6744e158571bac9e9d84a7ad77 Mon Sep 17 00:00:00 2001 From: OstapOmelchuk Date: Mon, 15 Mar 2021 14:58:50 +0200 Subject: [PATCH 066/100] Fix --- Pipfile | 0 task/models.py | 50 ++++++++++++++++++++++++++++++--------------- tests/task/tests.py | 4 ++-- 3 files changed, 36 insertions(+), 18 deletions(-) delete mode 100644 Pipfile diff --git a/Pipfile b/Pipfile deleted file mode 100644 index e69de29..0000000 diff --git a/task/models.py b/task/models.py index 3df03e2..8382696 100644 --- a/task/models.py +++ b/task/models.py @@ -19,7 +19,7 @@ def __str__(self): return self.title @classmethod - def find_by_id(cls, task_id: int): + def get_by_id(cls, task_id: int): try: task = Task.objects.get(pk=task_id) return task @@ -36,26 +36,44 @@ def create(cls, title: str, description: str, deadline: date, user_id, list_id): task = Task(title=title, description=description, deadline=deadline) user = CustomUser.get_by_id(user_id) task.user_id = user - list = ToDoList.get_by_id(list_id) - task.list_id = list + todolist = ToDoList.get_by_id(list_id) + task.list_id = todolist task.save() return task - def update(self, title: str, description: str, is_completed: bool, deadline: date, user_id: int, list_id: int): - self.title = title - self.description = description - self.deadline = deadline - self.is_completed = is_completed - self.user_id = CustomUser.get_by_id(user_id) - self.list_id = ToDoList.get_by_id(list_id) + def update(self, title: str, + description: str, + is_completed: bool, + deadline: date, + user_id: int, + list_id: int): + if title: + self.title = title + if description: + self.description = description + if deadline: + self.deadline = deadline + if is_completed: + self.is_completed = is_completed + if user_id: + self.user_id = CustomUser.get_by_id(user_id) + if list_id: + self.list_id = ToDoList.get_by_id(list_id) self.save() - return self.find_by_id(self.pk) + + @classmethod + def get_all(cls): + try: + task = Task.objects.all() + return task + except Task.DoesNotExist: + return None @classmethod def remove(cls, task_id): - task = Task.find_by_id(task_id) - if task: + try: + task = Task.objects.get(id=task_id) task.delete() - return HttpResponse("Task was deleted.") - else: - return False + except Task.DoesNotExist: + return False + return True diff --git a/tests/task/tests.py b/tests/task/tests.py index 028a024..b4f962c 100644 --- a/tests/task/tests.py +++ b/tests/task/tests.py @@ -1,4 +1,4 @@ -from django.test import TestCase, tag +from django.test import TestCase from task.models import Task from custom_user.models import CustomUser from todolist.models import ToDoList @@ -61,7 +61,7 @@ def test_find_by_id(self): self.assertIsInstance(result, Task) def test_find_by_non_existent_id(self): - task = Task.find_by_id(100) + task = Task.get_by_id(100) self.assertTrue(task is None) def test_remove_task(self): From 325464d1b6508e652af7b5f61e9f6cf55e96f990 Mon Sep 17 00:00:00 2001 From: OstapOmelchuk Date: Mon, 15 Mar 2021 15:41:11 +0200 Subject: [PATCH 067/100] Added tests for task models --- tests/task/tests.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/task/tests.py b/tests/task/tests.py index aaeb28b..dd97c75 100644 --- a/tests/task/tests.py +++ b/tests/task/tests.py @@ -54,10 +54,10 @@ def test_update_task(self): def test_find_by_id(self): self.task = Task.create(title="Task #4", - description="Task #4 Description", - deadline=date(2021, 5, 3), - user_id=self.user.pk, - list_id=self.todolist.pk) + description="Task #4 Description", + deadline=date(2021, 5, 3), + user_id=self.user.pk, + list_id=self.todolist.pk) result = Task.find_by_id(self.task.pk) self.assertIsInstance(result, Task) @@ -86,8 +86,8 @@ def test_find_all_for_list(self): list_id=self.todolist.pk) result = Task.find_all_for_list(self.todolist.pk) self.assertEqual(len(result), 2) - - + + class DeleteTaskView(TestCase): """ Test view for deleting an existing task """ From c92a89f0296482085dca29b99c195b9b5ab97435 Mon Sep 17 00:00:00 2001 From: OstapOmelchuk Date: Mon, 15 Mar 2021 15:45:07 +0200 Subject: [PATCH 068/100] Added manage.py --- manage.py | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 manage.py diff --git a/manage.py b/manage.py new file mode 100644 index 0000000..a1ff910 --- /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', 'project_config.settings') + 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() \ No newline at end of file From cbacb8a3af3c857559e69d1ed489688d4678ed63 Mon Sep 17 00:00:00 2001 From: OstapOmelchuk Date: Mon, 15 Mar 2021 15:51:07 +0200 Subject: [PATCH 069/100] Changed migrations forr task --- manage.py | 2 +- task/migrations/0001_initial.py | 6 +----- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/manage.py b/manage.py index a1ff910..f4d2cb4 100644 --- a/manage.py +++ b/manage.py @@ -19,4 +19,4 @@ def main(): if __name__ == '__main__': - main() \ No newline at end of file + main() diff --git a/task/migrations/0001_initial.py b/task/migrations/0001_initial.py index cc013da..91ee613 100644 --- a/task/migrations/0001_initial.py +++ b/task/migrations/0001_initial.py @@ -1,8 +1,6 @@ -# Generated by Django 3.1.7 on 2021-03-11 13:29 +# Generated by Django 3.1.7 on 2021-03-11 12:46 -from django.conf import settings from django.db import migrations, models -import django.db.models.deletion class Migration(migrations.Migration): @@ -10,8 +8,6 @@ class Migration(migrations.Migration): initial = True dependencies = [ - migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ('todolist', '0002_todolist_members'), ] operations = [ From 878fd137847656e9a18ec70466921456b439d311 Mon Sep 17 00:00:00 2001 From: OstapOmelchuk Date: Mon, 15 Mar 2021 15:56:53 +0200 Subject: [PATCH 070/100] Added tests --- task/migrations/0001_initial.py | 2 - tests/task/tests.py | 88 +++++++++++++++++++++++++++++++-- 2 files changed, 85 insertions(+), 5 deletions(-) diff --git a/task/migrations/0001_initial.py b/task/migrations/0001_initial.py index 34f6df2..349da5c 100644 --- a/task/migrations/0001_initial.py +++ b/task/migrations/0001_initial.py @@ -10,8 +10,6 @@ class Migration(migrations.Migration): initial = True dependencies = [ - migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ('todolist', '0002_todolist_members'), ] operations = [ diff --git a/tests/task/tests.py b/tests/task/tests.py index 1794371..dd97c75 100644 --- a/tests/task/tests.py +++ b/tests/task/tests.py @@ -1,11 +1,92 @@ import json - from django.test import TestCase - -from custom_user.models import CustomUser from task.models import Task +from custom_user.models import CustomUser from todolist.models import ToDoList +from datetime import date + + +class TaskModelsTest(TestCase): + def setUp(self): + self.user = CustomUser.objects.create(id=1, + first_name='Test', + last_name='User', + email='testuser@gmail.com', + password='test') + self.todolist = ToDoList.create(name="list 1", description="list1 descr", member_pk=self.user.pk) + self.todolist.id = 1 + self.todolist.save() + self.task1 = Task.create(title="Task #1", + description="Task #1 Description", + deadline=date(2021, 1, 1), + user_id=self.user.pk, + list_id=self.todolist.pk) + + def test_str(self): + test = self.task1.__str__() + self.assertEqual(test, "Task #1") + + def test_create(self): + task = Task.create(title="Task #2", + description="Task #2 Description", + deadline=date(2021, 2, 2), + user_id=self.user.pk, + list_id=self.todolist.pk) + self.assertIsInstance(task, Task) + + def test_update_task(self): + self.task = Task.create(title="Task #3", + description="Task #3 Description", + deadline=date(2021, 3, 3), + user_id=self.user.pk, + list_id=self.todolist.pk) + result = self.task.update("New task", "new task description", False, date(2021, 3, 3), + self.user.pk, self.todolist.pk) + self.assertEqual(result.title, "New task") + self.assertEqual(result.description, "new task description") + self.assertEqual(result.is_completed, False) + self.assertEqual(result.deadline, date(2021, 3, 3)) + print(result.list_id.pk) + self.assertEqual(result.user_id.pk, 1) + self.assertEqual(result.list_id.pk, 1) + self.assertTrue(result) + + def test_find_by_id(self): + self.task = Task.create(title="Task #4", + description="Task #4 Description", + deadline=date(2021, 5, 3), + user_id=self.user.pk, + list_id=self.todolist.pk) + result = Task.find_by_id(self.task.pk) + self.assertIsInstance(result, Task) + + def test_find_by_non_existent_id(self): + task = Task.get_by_id(100) + self.assertTrue(task is None) + + def test_remove_task(self): + self.task = Task.create(title="Task #5", + description="Task #5 Description", + deadline=date(2021, 4, 4), + user_id=self.user.pk, + list_id=self.todolist.pk) + result = Task.remove(self.task.pk) + self.assertNotIn(result, Task.find_all_for_list(self.todolist.pk)) + + def test_remove_non_existed_task(self): + result = Task.remove(100) + self.assertEqual(result, False) + + def test_find_all_for_list(self): + self.task = Task.create(title="Task #6", + description="Task #6 Description", + deadline=date(2021, 6, 7), + user_id=self.user.pk, + list_id=self.todolist.pk) + result = Task.find_all_for_list(self.todolist.pk) + self.assertEqual(len(result), 2) + class DeleteTaskView(TestCase): """ Test view for deleting an existing task """ @@ -117,3 +198,4 @@ def test_update_task_id_missing(self): "deadline": "2020-01-01" })) self.assertEqual(response.status_code, 400) + From 3e5b2345e8a14226daaaad7ace943e2af85627b7 Mon Sep 17 00:00:00 2001 From: OstapOmelchuk Date: Mon, 15 Mar 2021 16:00:16 +0200 Subject: [PATCH 071/100] Added tests for tasks models --- tests/task/tests.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/task/tests.py b/tests/task/tests.py index dd97c75..c23e061 100644 --- a/tests/task/tests.py +++ b/tests/task/tests.py @@ -198,4 +198,3 @@ def test_update_task_id_missing(self): "deadline": "2020-01-01" })) self.assertEqual(response.status_code, 400) - From 7e04759e337ec299faa94e2ac066f432572dee99 Mon Sep 17 00:00:00 2001 From: Daniil Oleshchuk Date: Mon, 15 Mar 2021 16:30:38 +0200 Subject: [PATCH 072/100] improved ToDoList usage in Task tests --- tests/task/tests.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/tests/task/tests.py b/tests/task/tests.py index c23e061..e9f2fb9 100644 --- a/tests/task/tests.py +++ b/tests/task/tests.py @@ -14,7 +14,8 @@ def setUp(self): last_name='User', email='testuser@gmail.com', password='test') - self.todolist = ToDoList.create(name="list 1", description="list1 descr", member_pk=self.user.pk) + self.todolist = ToDoList.create(name="list 1", description="list1 descr") + self.todolist.update_members(members_to_add=[self.user.pk]) self.todolist.id = 1 self.todolist.save() self.task1 = Task.create(title="Task #1", @@ -58,7 +59,7 @@ def test_find_by_id(self): deadline=date(2021, 5, 3), user_id=self.user.pk, list_id=self.todolist.pk) - result = Task.find_by_id(self.task.pk) + result = Task.get_by_id(self.task.pk) self.assertIsInstance(result, Task) def test_find_by_non_existent_id(self): @@ -98,8 +99,8 @@ def setUp(self): password="secret" ) self.list = ToDoList.create(name='List', - description='About list', - member_pk=self.user.id) + description='About list') + self.list.update_members(members_to_add=[self.user.id]) self.task = Task.create(title='Task', description='Task', deadline='2020-01-01', @@ -124,8 +125,8 @@ def setUp(self): email="mail@mail.com", password="secret") self.list = ToDoList.create(name='List', - description='About list', - member_pk=self.user.id) + description='About list') + self.list.update_members(members_to_add=[self.user.id]) def test_create_task_data_valid(self): response = self.client.generic('POST', '/tasks/', json.dumps({ @@ -161,8 +162,8 @@ def setUp(self): email="mail@mail.com", password="secret") self.list = ToDoList.create(name='List', - description='About list', - member_pk=self.user.id) + description='About list') + self.list.update_members(members_to_add=[self.user.id]) self.task = Task.create(title='Task', description='Task', deadline='2020-01-01', From d19a9238546369c7f7c6e5e1ce84062148b71219 Mon Sep 17 00:00:00 2001 From: OstapOmelchuk Date: Mon, 15 Mar 2021 16:59:56 +0200 Subject: [PATCH 073/100] Fix tests for task models and views --- tests/task/tests.py | 44 ++++++++++++++++++++++---------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/tests/task/tests.py b/tests/task/tests.py index c23e061..4bc8cc9 100644 --- a/tests/task/tests.py +++ b/tests/task/tests.py @@ -14,7 +14,8 @@ def setUp(self): last_name='User', email='testuser@gmail.com', password='test') - self.todolist = ToDoList.create(name="list 1", description="list1 descr", member_pk=self.user.pk) + self.todolist = ToDoList.create(name="list 1", description="list1 descr") + self.todolist.update_members(members_to_add=[self.user.pk]) self.todolist.id = 1 self.todolist.save() self.task1 = Task.create(title="Task #1", @@ -41,16 +42,15 @@ def test_update_task(self): deadline=date(2021, 3, 3), user_id=self.user.pk, list_id=self.todolist.pk) - result = self.task.update("New task", "new task description", False, date(2021, 3, 3), + self.task.update("New task", "new task description", False, date(2021, 3, 3), self.user.pk, self.todolist.pk) - self.assertEqual(result.title, "New task") - self.assertEqual(result.description, "new task description") - self.assertEqual(result.is_completed, False) - self.assertEqual(result.deadline, date(2021, 3, 3)) - print(result.list_id.pk) - self.assertEqual(result.user_id.pk, 1) - self.assertEqual(result.list_id.pk, 1) - self.assertTrue(result) + self.assertEqual(self.task.title, "New task") + self.assertEqual(self.task.description, "new task description") + self.assertEqual(self.task.is_completed, False) + self.assertEqual(self.task.deadline, date(2021, 3, 3)) + self.assertEqual(self.task.user_id.pk, 1) + self.assertEqual(self.task.list_id.pk, 1) + self.assertTrue(self.task) def test_find_by_id(self): self.task = Task.create(title="Task #4", @@ -58,7 +58,7 @@ def test_find_by_id(self): deadline=date(2021, 5, 3), user_id=self.user.pk, list_id=self.todolist.pk) - result = Task.find_by_id(self.task.pk) + result = Task.get_by_id(self.task.pk) self.assertIsInstance(result, Task) def test_find_by_non_existent_id(self): @@ -92,17 +92,17 @@ class DeleteTaskView(TestCase): """ Test view for deleting an existing task """ def setUp(self): - self.user = CustomUser.objects.create_user(first_name="Test", + self.user = CustomUser.objects.create(first_name="Test", last_name="User", email="mail@mail.com", password="secret" ) self.list = ToDoList.create(name='List', - description='About list', - member_pk=self.user.id) + description='About list') + self.list.update_members(members_to_add=[self.user.id]) self.task = Task.create(title='Task', description='Task', - deadline='2020-01-01', + deadline=date(2020, 1, 1), list_id=self.list.id, user_id=self.user.id) @@ -119,13 +119,13 @@ class CreateTaskView(TestCase): """ Test view for creating new task """ def setUp(self): - self.user = CustomUser.objects.create_user(first_name="Test", + self.user = CustomUser.objects.create(first_name="Test", last_name="User", email="mail@mail.com", password="secret") self.list = ToDoList.create(name='List', - description='About list', - member_pk=self.user.id) + description='About list') + self.list.update_members(members_to_add=[self.user.id]) def test_create_task_data_valid(self): response = self.client.generic('POST', '/tasks/', json.dumps({ @@ -156,16 +156,16 @@ class UpdateTaskView(TestCase): """ Test view for updating existing task """ def setUp(self): - self.user = CustomUser.objects.create_user(first_name="Test", + self.user = CustomUser.objects.create(first_name="Test", last_name="User", email="mail@mail.com", password="secret") self.list = ToDoList.create(name='List', - description='About list', - member_pk=self.user.id) + description='About list') + self.list.update_members(members_to_add=[self.user.id]) self.task = Task.create(title='Task', description='Task', - deadline='2020-01-01', + deadline=date(2020, 1, 1), list_id=self.list.id, user_id=self.user.id) From 06f6aa8ef8e1dbb28ea667a4a4f8ed6b22ffe765 Mon Sep 17 00:00:00 2001 From: Nevmerzhitsky Yura Date: Tue, 16 Mar 2021 14:08:49 +0200 Subject: [PATCH 074/100] add abstraction --- abstract/__init__.py | 0 abstract/abstract_model.py | 17 +++++++++++++++++ custom_user/models.py | 19 +++++++++++++------ custom_user/views.py | 28 ++++++---------------------- requirements.txt | 2 +- task/models.py | 13 ++++--------- 6 files changed, 41 insertions(+), 38 deletions(-) create mode 100644 abstract/__init__.py create mode 100644 abstract/abstract_model.py diff --git a/abstract/__init__.py b/abstract/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/abstract/abstract_model.py b/abstract/abstract_model.py new file mode 100644 index 0000000..865c5e1 --- /dev/null +++ b/abstract/abstract_model.py @@ -0,0 +1,17 @@ +from django.db import models + + +class AbstractModel(models.Model): + @classmethod + def get_by_id(cls, pk: int): + try: + entity = cls.objects.get(pk=pk) + return entity + except cls.DoesNotExist: + return None + + def create(self, *args, **kwargs): + raise NotImplemented + + def delete(self, *args, **kwargs): + raise NotImplemented diff --git a/custom_user/models.py b/custom_user/models.py index 1d9ebd2..bb20d64 100644 --- a/custom_user/models.py +++ b/custom_user/models.py @@ -47,12 +47,19 @@ def get_by_id(cls, user_id): except CustomUser.DoesNotExist: return None - def update(self, data): - self.first_name = data['first_name'] - self.last_name = data['last_name'] - self.email = data['email'] - self.save() - return True + def update(self, first_name=None, last_name=None, email=None): + + if first_name: + self.first_name = first_name + if last_name: + self.last_name = last_name + if email: + self.email = email + try: + self.save() + return self + except: + return None @classmethod def remove(cls, user_id): diff --git a/custom_user/views.py b/custom_user/views.py index 9e0f4f7..f93000f 100644 --- a/custom_user/views.py +++ b/custom_user/views.py @@ -30,8 +30,8 @@ def post(self, request): return JsonResponse(new_user.to_dict(), status=200) return HttpResponse('Something went wrong', status=400) - @staticmethod - def put(request, user_id=None): + + def put(self, request, user_id=None): user = CustomUser.get_by_id(user_id) if not user: @@ -44,30 +44,14 @@ def put(request, user_id=None): body = json.loads(request.body) except json.JSONDecodeError: return HttpResponse("Invalid JSON", status=400) - - data = {} - - if not body.get('first_name'): - data['first_name'] = user.first_name - else: - data['first_name'] = body.get('first_name') - if not body.get('last_name'): - data['last_name'] = user.last_name - else: - data['last_name'] = body.get('last_name') - if not body.get('email'): - data['email'] = user.email - else: - data['email'] = body.get('email') - - updated_user = CustomUser.update(user,data) + updated_user = CustomUser.update(user, **body) if updated_user: return JsonResponse( user.to_dict(), status=200) - return HttpResponse(status= 400) + return HttpResponse(status=400) + - @staticmethod - def delete(request, user_id=None): + def delete(self, request, user_id=None): user = CustomUser.get_by_id(user_id) if user: CustomUser.remove(user_id) diff --git a/requirements.txt b/requirements.txt index 59db430..fa8243f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,7 +3,7 @@ astroid==2.5.1 colorama==0.4.4 Django==3.1.7 isort==5.7.0 -lazy-object-proxy==1.5.2 +lazy-object-proxy==1.5.p2 mccabe==0.6.1 psycopg2==2.8.6 pylint==2.7.2 diff --git a/task/models.py b/task/models.py index 2784426..2d87720 100644 --- a/task/models.py +++ b/task/models.py @@ -1,10 +1,12 @@ +from abc import abstractmethod from datetime import date from django.db import models from custom_user.models import CustomUser from todolist.models import ToDoList +from abstract.abstract_model import AbstractModel -class Task(models.Model): +class Task(AbstractModel): title = models.CharField(max_length=30) description = models.TextField(max_length=256) is_completed = models.BooleanField(default=False) @@ -15,14 +17,6 @@ class Task(models.Model): def __str__(self): return self.title - @classmethod - def get_by_id(cls, task_id: int): - try: - task = Task.objects.get(pk=task_id) - return task - except Task.DoesNotExist: - return None - @classmethod def find_all_for_list(cls, list_id): tasks = Task.objects.filter(list_id=list_id) @@ -38,6 +32,7 @@ def create(cls, title: str, description: str, deadline: date, user_id, list_id): task.save() return task + @abstractmethod def update(self, title: str, description: str, is_completed: bool, From b9a7aae03eb46fc722d3ac67597c94da97c5b8b8 Mon Sep 17 00:00:00 2001 From: SLDem Date: Tue, 16 Mar 2021 15:54:52 +0200 Subject: [PATCH 075/100] Added JSONMiddleware --- abstract/abstract_model.py | 4 ++++ custom_user/models.py | 3 +-- custom_user/views.py | 24 +++++++++--------------- middleware/middleware.py | 23 +++++++++++++++++++++++ project_config/settings.py | 3 ++- project_config/urls.py | 6 +++--- requirements.txt | 2 +- task/urls.py | 2 +- task/views.py | 30 ++++-------------------------- tests/custom_user/tests.py | 8 +------- todolist/views.py | 10 ---------- 11 files changed, 49 insertions(+), 66 deletions(-) create mode 100644 middleware/middleware.py diff --git a/abstract/abstract_model.py b/abstract/abstract_model.py index 865c5e1..3081513 100644 --- a/abstract/abstract_model.py +++ b/abstract/abstract_model.py @@ -2,6 +2,10 @@ class AbstractModel(models.Model): + + class Meta: + abstract = True + @classmethod def get_by_id(cls, pk: int): try: diff --git a/custom_user/models.py b/custom_user/models.py index bb20d64..31087c4 100644 --- a/custom_user/models.py +++ b/custom_user/models.py @@ -48,7 +48,6 @@ def get_by_id(cls, user_id): return None def update(self, first_name=None, last_name=None, email=None): - if first_name: self.first_name = first_name if last_name: @@ -58,7 +57,7 @@ def update(self, first_name=None, last_name=None, email=None): try: self.save() return self - except: + except (TypeError, ValueError): return None @classmethod diff --git a/custom_user/views.py b/custom_user/views.py index f93000f..1f9f8f8 100644 --- a/custom_user/views.py +++ b/custom_user/views.py @@ -1,6 +1,4 @@ -import json from django.http import JsonResponse, HttpResponse -from django.views import View from rest_framework.views import APIView from .models import CustomUser @@ -17,20 +15,19 @@ def get(self, request, user_id=None): def post(self, request): if not request.body: return HttpResponse("Empty data input", status=400) - try: - body = json.loads(request.body) - except json.JSONDecodeError: - return HttpResponse("Invalid JSON", status=400) - data = {'first_name': body.get('first_name'), 'last_name': body.get('last_name'), - 'password': body.get('password'), 'email': body.get('email')} + body = request.body + + data = {'first_name': body.get('first_name'), + 'last_name': body.get('last_name'), + 'password': body.get('password'), + 'email': body.get('email')} new_user = CustomUser.objects.create(**data) if new_user: return JsonResponse(new_user.to_dict(), status=200) return HttpResponse('Something went wrong', status=400) - def put(self, request, user_id=None): user = CustomUser.get_by_id(user_id) @@ -40,17 +37,14 @@ def put(self, request, user_id=None): if not request.body: return HttpResponse("Empty data input", status=400) - try: - body = json.loads(request.body) - except json.JSONDecodeError: - return HttpResponse("Invalid JSON", status=400) + body = request.body + updated_user = CustomUser.update(user, **body) if updated_user: - return JsonResponse( user.to_dict(), status=200) + return JsonResponse(user.to_dict(), status=200) return HttpResponse(status=400) - def delete(self, request, user_id=None): user = CustomUser.get_by_id(user_id) if user: diff --git a/middleware/middleware.py b/middleware/middleware.py new file mode 100644 index 0000000..194b528 --- /dev/null +++ b/middleware/middleware.py @@ -0,0 +1,23 @@ +from django.http import HttpResponse + +import json + + +class JSONMiddleware: + """ + Process application/json requests data from GET and POST requests. + """ + + def __init__(self, get_response): + self.get_response = get_response + + def __call__(self, request): + if request.method == 'POST' or request.method == 'PUT': + try: + request._body = json.loads(request.body) + return self.get_response(request) + except json.JSONDecodeError: + return HttpResponse("Invalid JSON", status=400) + else: + response = self.get_response(request) + return response diff --git a/project_config/settings.py b/project_config/settings.py index 07a324d..a2efa0e 100644 --- a/project_config/settings.py +++ b/project_config/settings.py @@ -39,7 +39,7 @@ 'task', 'rest_framework', 'drf_yasg', - + 'abstract' ] MIDDLEWARE = [ @@ -49,6 +49,7 @@ 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', + 'middleware.middleware.JSONMiddleware' ] ROOT_URLCONF = 'project_config.urls' diff --git a/project_config/urls.py b/project_config/urls.py index fb1a831..d1253e2 100644 --- a/project_config/urls.py +++ b/project_config/urls.py @@ -32,9 +32,9 @@ ) urlpatterns = [ - path('custom_user/', include('custom_user.urls')), + path('custom-user/', include('custom_user.urls')), path('todolist/', include('todolist.urls')), path('tasks/', include('task.urls')), - path('', schema_view.with_ui('swagger', cache_timeout=0), name='schema-swagger-ui'), - path('redoc/', schema_view.with_ui('redoc', cache_timeout=0), name='schema-redoc'), + path('swagger', schema_view.with_ui('swagger', cache_timeout=0), name='schema-swagger-ui'), + path('swagger-redoc/', schema_view.with_ui('redoc', cache_timeout=0), name='schema-redoc'), ] diff --git a/requirements.txt b/requirements.txt index fa8243f..59db430 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,7 +3,7 @@ astroid==2.5.1 colorama==0.4.4 Django==3.1.7 isort==5.7.0 -lazy-object-proxy==1.5.p2 +lazy-object-proxy==1.5.2 mccabe==0.6.1 psycopg2==2.8.6 pylint==2.7.2 diff --git a/task/urls.py b/task/urls.py index 4f6fac3..d6f4c2a 100644 --- a/task/urls.py +++ b/task/urls.py @@ -3,6 +3,6 @@ from .views import TaskAPIView urlpatterns = [ - path('by_list/', TaskAPIView.as_view()), + path('by_list//', TaskAPIView.as_view()), path('', TaskAPIView.as_view(), name='task_api_view'), ] \ No newline at end of file diff --git a/task/views.py b/task/views.py index 5fe0cf0..b33d52c 100644 --- a/task/views.py +++ b/task/views.py @@ -21,14 +21,7 @@ def get(self, request, list_id): return JsonResponse(data) def post(self, request): - # JSON validation - try: - body = json.loads(request.body) - except json.JSONDecodeError: - failure = { - 'message': 'Please provide valid json request!' - } - return JsonResponse(failure, status=400) + body = request.body task_data = { 'title': body.get('title'), @@ -63,15 +56,7 @@ def post(self, request): return JsonResponse(invalid_data_message, status=400) def put(self, request): - - # JSON validation - try: - body = json.loads(request.body) - except json.JSONDecodeError: - failure = { - 'message': 'Please provide valid json request!' - } - return JsonResponse(failure, status=400) + body = request.body task_data = { 'title': body.get('title'), @@ -91,7 +76,7 @@ def put(self, request): # Invalid ID try: - task = Task.get_by_id(task_id=body.get('task_id')) + task = Task.get_by_id(pk=body.get('task_id')) except ValueError: invalid_data_message = { 'message': 'Please provide valid ID' @@ -121,14 +106,7 @@ def put(self, request): return JsonResponse(invalid_data_message, status=400) def delete(self, request): - # JSON validation - try: - body = json.loads(request.body) - except json.JSONDecodeError: - failure = { - 'message': 'Please provide valid json request!' - } - return JsonResponse(failure, status=400) + body = request.body if Task.remove(task_id=body.get('task_id')): success = { diff --git a/tests/custom_user/tests.py b/tests/custom_user/tests.py index 7480905..1d6056d 100644 --- a/tests/custom_user/tests.py +++ b/tests/custom_user/tests.py @@ -3,7 +3,6 @@ import json - class CustomUserModelsTestCase(TestCase): def setUp(self): self.user = CustomUser.objects.create(id=1, @@ -67,6 +66,7 @@ def test_remove_user(self): res = CustomUser.remove(2) self.assertIn(b'User removed', res.content) + class CustomUserViewsTestCase(TestCase): def setUp(self): self.user = CustomUser.objects.create(id=2, @@ -81,8 +81,6 @@ def setUp(self): email="testuser2@gmail.com", password="test2") - - def test_get_by_id(self): data = {'first_name': 'Test', 'last_name': 'User', 'email': 'testuser@gmail.com'} responce = self.client.get('/custom_user/profile/2/') @@ -111,7 +109,3 @@ def test_put_not_all_input_data(self): def test_delete(self): response = self.client.generic('DELETE', '/custom_user/profile/2/') self.assertEqual(response.status_code, 200) - - - - diff --git a/todolist/views.py b/todolist/views.py index 6168c0f..934eb61 100644 --- a/todolist/views.py +++ b/todolist/views.py @@ -26,11 +26,6 @@ def post(self, request): if not data: return HttpResponse(status=400) - try: - data = json.loads(request.body) - except json.JSONDecodeError: - return JsonResponse({"JsonError": "Provided invalid json"}, status=400) - data = { 'name': data.get('name'), 'description': data.get('description') if data.get('description') else '', @@ -53,11 +48,6 @@ def put(self, request, todo_list_pk=None): if not data: return HttpResponse(status=400) - try: - data = json.loads(request.body) - except json.JSONDecodeError: - return JsonResponse({"JSON Error": 'Provided invalid JSON'}, status=400) - members_to_add = data.get('members_to_add') members_to_delete = data.get('members_to_delete') if members_to_add or members_to_delete: From f85187c47f489117d1a9f9e60290b57fabd773d2 Mon Sep 17 00:00:00 2001 From: Daniil Oleshchuk Date: Tue, 16 Mar 2021 16:19:26 +0200 Subject: [PATCH 076/100] improved abstract model --- abstract/abstract_model.py | 26 ++++++++++++++++++++++++-- custom_user/models.py | 24 ++++++------------------ task/models.py | 17 ----------------- todolist/models.py | 20 ++------------------ todolist/views.py | 4 ++-- 5 files changed, 34 insertions(+), 57 deletions(-) diff --git a/abstract/abstract_model.py b/abstract/abstract_model.py index 3081513..c04890a 100644 --- a/abstract/abstract_model.py +++ b/abstract/abstract_model.py @@ -1,3 +1,5 @@ +from abc import abstractmethod + from django.db import models @@ -14,8 +16,28 @@ def get_by_id(cls, pk: int): except cls.DoesNotExist: return None - def create(self, *args, **kwargs): + @classmethod + def get_all(cls): + try: + task = cls.objects.all() + return task + except cls.DoesNotExist: + return None + + @classmethod + def remove(cls, pk): + try: + task = cls.objects.get(id=pk) + task.delete() + except cls.DoesNotExist: + return False + return True + + @abstractmethod + def update(self, *args, **kwargs): raise NotImplemented - def delete(self, *args, **kwargs): + @classmethod + @abstractmethod + def create(cls, *args, **kwargs): raise NotImplemented diff --git a/custom_user/models.py b/custom_user/models.py index 31087c4..3d84ae7 100644 --- a/custom_user/models.py +++ b/custom_user/models.py @@ -1,11 +1,11 @@ from django.contrib.auth.models import AbstractBaseUser, BaseUserManager -from django.http import HttpResponse from django.db import models +from abstract.abstract_model import AbstractModel class UserManager(BaseUserManager): - def _create_user(self, email, password, **extra_fields): + def create_user(self, email, password, **extra_fields): if not email: raise ValueError('User must have an email address') email = self.normalize_email(email) @@ -17,11 +17,8 @@ def _create_user(self, email, password, **extra_fields): user.save(using=self._db) return user - def create(self, email, password, **extra_fields): - return self._create_user(email, password, **extra_fields) - -class CustomUser(AbstractBaseUser): +class CustomUser(AbstractBaseUser, AbstractModel): objects = UserManager() first_name = models.CharField('First Name', max_length=55, null=False, blank=False) @@ -40,12 +37,9 @@ def to_dict(self): 'email': self.email} @classmethod - def get_by_id(cls, user_id): - try: - user = cls.objects.get(pk=user_id) - return user - except CustomUser.DoesNotExist: - return None + def create(cls, email, password, **extra_fields): + manager = UserManager() + return manager.create_user(email, password, **extra_fields) def update(self, first_name=None, last_name=None, email=None): if first_name: @@ -59,9 +53,3 @@ def update(self, first_name=None, last_name=None, email=None): return self except (TypeError, ValueError): return None - - @classmethod - def remove(cls, user_id): - user = cls.get_by_id(user_id) - user.delete() - return HttpResponse('User removed.') diff --git a/task/models.py b/task/models.py index 2d87720..d0e611a 100644 --- a/task/models.py +++ b/task/models.py @@ -52,20 +52,3 @@ def update(self, title: str, if list_id: self.list_id = ToDoList.get_by_id(list_id) self.save() - - @classmethod - def get_all(cls): - try: - task = Task.objects.all() - return task - except Task.DoesNotExist: - return None - - @classmethod - def remove(cls, task_id): - try: - task = Task.objects.get(id=task_id) - task.delete() - except Task.DoesNotExist: - return False - return True diff --git a/todolist/models.py b/todolist/models.py index e518385..966debb 100644 --- a/todolist/models.py +++ b/todolist/models.py @@ -1,9 +1,10 @@ from django.db import models, IntegrityError, DatabaseError +from abstract.abstract_model import AbstractModel from custom_user.models import CustomUser -class ToDoList(models.Model): +class ToDoList(AbstractModel): name = models.CharField(max_length=50) description = models.CharField(max_length=500, default='No description for now.') members = models.ManyToManyField(CustomUser, related_name='todo_lists') @@ -42,20 +43,6 @@ def get_list_members(self): members = self.members.all() return members - @classmethod - def get_by_id(cls, todo_list_pk): - try: - todo_list = ToDoList.objects.get(pk=todo_list_pk) - return todo_list - except ToDoList.DoesNotExist: - # log error - return None - - @classmethod - def get_all(cls): - todo_lists = ToDoList.objects.all() - return todo_lists - @classmethod def create(cls, name, description='', members=None): try: @@ -67,6 +54,3 @@ def create(cls, name, description='', members=None): except (ValueError, IntegrityError, DatabaseError): # log error return None - - def remove(self): - self.delete() diff --git a/todolist/views.py b/todolist/views.py index 934eb61..9353eb4 100644 --- a/todolist/views.py +++ b/todolist/views.py @@ -29,7 +29,7 @@ def post(self, request): data = { 'name': data.get('name'), 'description': data.get('description') if data.get('description') else '', - 'members': [CustomUser.get_by_id(user_id=user_id) for user_id in data.get('members')] + 'members': [CustomUser.get_by_id(pk=user_id) for user_id in data.get('members')] if data.get('members') else None } @@ -69,5 +69,5 @@ def delete(self, request, todo_list_pk=None): if not todo_list: return HttpResponse(status=404) - todo_list.remove() + ToDoList.remove(pk=todo_list_pk) return HttpResponse(status=200) From d00044eb1d97c983c4c8aa81f5347121b96905a7 Mon Sep 17 00:00:00 2001 From: SLDem Date: Tue, 16 Mar 2021 17:17:27 +0200 Subject: [PATCH 077/100] Fixed tests for user models --- custom_user/models.py | 28 +++++++------------- tests/custom_user/tests.py | 54 ++++++++++++++++++-------------------- 2 files changed, 35 insertions(+), 47 deletions(-) diff --git a/custom_user/models.py b/custom_user/models.py index 3d84ae7..0d80f6b 100644 --- a/custom_user/models.py +++ b/custom_user/models.py @@ -3,23 +3,8 @@ from abstract.abstract_model import AbstractModel -class UserManager(BaseUserManager): - - def create_user(self, email, password, **extra_fields): - if not email: - raise ValueError('User must have an email address') - email = self.normalize_email(email) - user = self.model( - email=email, - **extra_fields - ) - user.set_password(password) - user.save(using=self._db) - return user - - class CustomUser(AbstractBaseUser, AbstractModel): - objects = UserManager() + objects = models.Manager() first_name = models.CharField('First Name', max_length=55, null=False, blank=False) last_name = models.CharField('Last Name', max_length=55, null=False, blank=False) @@ -38,8 +23,15 @@ def to_dict(self): @classmethod def create(cls, email, password, **extra_fields): - manager = UserManager() - return manager.create_user(email, password, **extra_fields) + if not email: + raise ValueError('User must have an email address') + user = CustomUser( + email=email, + **extra_fields + ) + user.set_password(password) + user.save() + return user def update(self, first_name=None, last_name=None, email=None): if first_name: diff --git a/tests/custom_user/tests.py b/tests/custom_user/tests.py index 1d6056d..5e4adbd 100644 --- a/tests/custom_user/tests.py +++ b/tests/custom_user/tests.py @@ -1,31 +1,31 @@ -from django.test import TestCase, Client +from django.test import TestCase, Client, tag from custom_user.models import CustomUser import json class CustomUserModelsTestCase(TestCase): def setUp(self): - self.user = CustomUser.objects.create(id=1, - first_name='Test', - last_name='User', - email='testuser@gmail.com', - password='test') + self.user = CustomUser.create(id=1, + first_name='Test', + last_name='User', + email='testuser@gmail.com', + password='test') def test_create_user(self): - user = CustomUser.objects.create(id=2, - first_name='Test1', - last_name='User1', - email='testuser1@gmail.com', - password='test1') + user = CustomUser.create(id=2, + first_name='Test1', + last_name='User1', + email='testuser1@gmail.com', + password='test1') self.assertIsInstance(user, CustomUser) def test_create_user_no_email(self): def create_user(): - res = CustomUser.objects.create(id=2, - first_name='Test1', - last_name='User1', - email='', - password='test1') + res = CustomUser.create(id=2, + first_name='Test1', + last_name='User1', + email='', + password='test1') return res self.assertRaises(ValueError, create_user) @@ -46,25 +46,21 @@ def test_find_by_id_not_found(self): self.assertTrue(user is None) def test_update(self): - data = { - 'first_name': 'User', - 'last_name': 'Test', - 'email': 'testuser@gmail.com' - } - result = self.user.update(data) + result = self.user.update(first_name='User', last_name='Test', email= 'testuser@gmail.com') self.assertEqual(self.user.first_name, 'User') self.assertEqual(self.user.last_name, 'Test') self.assertEqual(self.user.email, 'testuser@gmail.com') self.assertTrue(result) def test_remove_user(self): - CustomUser.objects.create(id=2, - first_name='Test1', - last_name='User1', - email='testuser1@gmail.com', - password='test1') - res = CustomUser.remove(2) - self.assertIn(b'User removed', res.content) + user = CustomUser.objects.create(id=2, + first_name='Test1', + last_name='User1', + email='testuser1@gmail.com', + password='test1') + user.remove(user.id) + user = CustomUser.get_by_id(user.id) + self.assertEqual(None, user) class CustomUserViewsTestCase(TestCase): From c4c1a95662125b47f5f2e2ad8333fb70cda82afb Mon Sep 17 00:00:00 2001 From: StepanTchynetskyi Date: Tue, 16 Mar 2021 17:55:02 +0200 Subject: [PATCH 078/100] added utils folder validator for integer and small fixes for todolist test and view --- tests/todolist/tests.py | 6 ++---- todolist/views.py | 8 ++++---- utils/validators.py | 7 +++++++ 3 files changed, 13 insertions(+), 8 deletions(-) create mode 100644 utils/validators.py diff --git a/tests/todolist/tests.py b/tests/todolist/tests.py index b23eabb..be30958 100644 --- a/tests/todolist/tests.py +++ b/tests/todolist/tests.py @@ -226,7 +226,5 @@ def test_get_by_id(self): self.assertEqual(expected_to_dict, todo_list.to_dict()) def test_delete_by_id(self): - todo_list = ToDoList.get_by_id(self.todo_list.id) - todo_list.remove() - todo_list = ToDoList.get_by_id(self.todo_list.id) - self.assertEqual(None, todo_list) + list_del = ToDoList.remove(self.todo_list.id) + self.assertEqual(True, list_del) diff --git a/todolist/views.py b/todolist/views.py index 9353eb4..e3a7375 100644 --- a/todolist/views.py +++ b/todolist/views.py @@ -2,7 +2,7 @@ from django.http import JsonResponse, HttpResponse from rest_framework.views import APIView - +from utils.validators import is_integer from .models import CustomUser, ToDoList @@ -10,7 +10,7 @@ class ToDoListView(APIView): def get(self, request, todo_list_pk=None): if todo_list_pk: - if not todo_list_pk.isnumeric(): + if not is_integer(todo_list_pk): return HttpResponse(status=404) todo_list = ToDoList.get_by_id(todo_list_pk) if not todo_list: @@ -39,7 +39,7 @@ def post(self, request): return HttpResponse(status=400) def put(self, request, todo_list_pk=None): - if todo_list_pk and not todo_list_pk.isnumeric(): + if todo_list_pk and not is_integer(todo_list_pk): return HttpResponse(status=404) todo_list = ToDoList.get_by_id(todo_list_pk) if not todo_list: @@ -63,7 +63,7 @@ def put(self, request, todo_list_pk=None): return HttpResponse(status=200) def delete(self, request, todo_list_pk=None): - if todo_list_pk and not todo_list_pk.isnumeric(): + if todo_list_pk and not is_integer(todo_list_pk): return HttpResponse(status=404) todo_list = ToDoList.get_by_id(todo_list_pk) if not todo_list: diff --git a/utils/validators.py b/utils/validators.py new file mode 100644 index 0000000..690815f --- /dev/null +++ b/utils/validators.py @@ -0,0 +1,7 @@ +def is_integer(n): + try: + float(n) + except ValueError: + return False + else: + return float(n).is_integer() From 5f751076ea12ff3c2f598d19e60a2af63a1a44c8 Mon Sep 17 00:00:00 2001 From: AndriiRomaniuk Date: Tue, 16 Mar 2021 19:38:26 +0200 Subject: [PATCH 079/100] refactored views (and respective tests) --- task/urls.py | 1 + task/views.py | 39 +++++++++++++++++++-------------------- tests/task/tests.py | 22 ++++++++-------------- 3 files changed, 28 insertions(+), 34 deletions(-) diff --git a/task/urls.py b/task/urls.py index d6f4c2a..3475c07 100644 --- a/task/urls.py +++ b/task/urls.py @@ -4,5 +4,6 @@ urlpatterns = [ path('by_list//', TaskAPIView.as_view()), + path('/', TaskAPIView.as_view()), path('', TaskAPIView.as_view(), name='task_api_view'), ] \ No newline at end of file diff --git a/task/views.py b/task/views.py index b33d52c..438c7af 100644 --- a/task/views.py +++ b/task/views.py @@ -11,7 +11,7 @@ class TaskAPIView(APIView): def get(self, request, list_id): - task = Task.objects.filter(list_id=list_id) + task = Task.find_all_for_list(list_id=list_id) task_serialized_data = serialize('python', task) @@ -36,7 +36,7 @@ def post(self, request): task.save() success_message = { - 'message': f'New task object has been created with id {task.id}' + 'message': f'New task object has been created with ID {task.id}' } return JsonResponse(success_message, status=201) @@ -55,7 +55,7 @@ def post(self, request): } return JsonResponse(invalid_data_message, status=400) - def put(self, request): + def put(self, request, task_id=None): body = request.body task_data = { @@ -68,33 +68,27 @@ def put(self, request): } # Missing ID - if not body.get("task_id"): + if not task_id: missing_id_message = { - 'message': 'Cannot update task! Task id is missing!' + 'message': 'Cannot update task! Task ID is missing!' } return JsonResponse(missing_id_message, status=400) - # Invalid ID - try: - task = Task.get_by_id(pk=body.get('task_id')) - except ValueError: - invalid_data_message = { - 'message': 'Please provide valid ID' - } - return JsonResponse(invalid_data_message, status=400) + task = Task.get_by_id(pk=task_id) # ID of non-existing task if not task: not_exist_message = { - 'message': f'Cannot update task! Task with {body.get("task_id")} does not exist' + 'message': f'Cannot update task! Task with ID {task_id} does not exist' } return JsonResponse(not_exist_message, status=400) + # Update try: task.update(**task_data) success_message = { - 'message': f'Task {body.get("task_id")} has been updated' + 'message': f'Task with ID {task_id} has been updated' } return JsonResponse(success_message, status=200) @@ -105,16 +99,21 @@ def put(self, request): } return JsonResponse(invalid_data_message, status=400) - def delete(self, request): - body = request.body + def delete(self, request, task_id=None): + # Missing ID + if not task_id: + missing_id_message = { + 'message': 'Cannot delete task! Task ID is missing!' + } + return JsonResponse(missing_id_message, status=400) - if Task.remove(task_id=body.get('task_id')): + if Task.remove(pk=task_id): success = { - 'message': f'Task with id {body.get("task_id")} has been deleted' + 'message': f'Task with ID {task_id} has been deleted' } return JsonResponse(success, status=200) else: failure = { - 'message': f'Task with id {body.get("task_id")} does not exist!' + 'message': f'Task with ID {task_id} does not exist!' } return JsonResponse(failure, status=400) diff --git a/tests/task/tests.py b/tests/task/tests.py index 4bc8cc9..4b3b1c9 100644 --- a/tests/task/tests.py +++ b/tests/task/tests.py @@ -107,11 +107,11 @@ def setUp(self): user_id=self.user.id) def test_delete_task_existing(self): - response = self.client.generic('DELETE', '/tasks/', json.dumps({'task_id': self.task.id})) + response = self.client.generic('DELETE', f'/tasks/{self.task.id}/') self.assertEqual(response.status_code, 200) def test_delete_task_non_existing(self): - response = self.client.generic('DELETE', '/tasks/', json.dumps({'task_id': 10})) + response = self.client.generic('DELETE', '/tasks/100/') self.assertEqual(response.status_code, 400) @@ -170,26 +170,20 @@ def setUp(self): user_id=self.user.id) def test_update_task_existing(self): - response = self.client.generic('PUT', '/tasks/', json.dumps({ - "title": "UPDATE!", "task_id": self.task.id + response = self.client.generic('PUT', f'/tasks/{self.task.id}/', json.dumps({ + "title": "UPDATE!" })) self.assertEqual(response.status_code, 200) def test_update_task_non_existing(self): - response = self.client.generic('PUT', '/tasks/', json.dumps({ - "title": "UPDATE!", "task_id": 10 + response = self.client.generic('PUT', '/tasks/100/', json.dumps({ + "title": "UPDATE!" })) self.assertEqual(response.status_code, 400) def test_update_task_data_invalid(self): - response = self.client.generic('PUT', '/tasks/', json.dumps({ - "deadline": "May 2021", "task_id": self.task.id - })) - self.assertEqual(response.status_code, 400) - - def test_update_task_id_invalid(self): - response = self.client.generic('PUT', '/tasks/', json.dumps({ - "deadline": "2020-01-01", "task_id": 'one' + response = self.client.generic('PUT', f'/tasks/{self.task.id}/', json.dumps({ + "deadline": "May 2021" })) self.assertEqual(response.status_code, 400) From d130aa79f0720e0e6064a9ed63aa55c449e20b65 Mon Sep 17 00:00:00 2001 From: Nevmerzhitsky Yura Date: Wed, 17 Mar 2021 06:50:14 +0200 Subject: [PATCH 080/100] Modified custom_user/test --- custom_user/views.py | 4 ++-- tests/custom_user/tests.py | 43 +++++++++++++++++--------------------- 2 files changed, 21 insertions(+), 26 deletions(-) diff --git a/custom_user/views.py b/custom_user/views.py index 1f9f8f8..5b1a995 100644 --- a/custom_user/views.py +++ b/custom_user/views.py @@ -10,7 +10,7 @@ def get(self, request, user_id=None): user = CustomUser.get_by_id(user_id) if user: return JsonResponse(user.to_dict()) - return HttpResponse(status=400) + return HttpResponsestatus=400) def post(self, request): if not request.body: @@ -23,7 +23,7 @@ def post(self, request): 'password': body.get('password'), 'email': body.get('email')} - new_user = CustomUser.objects.create(**data) + new_user = CustomUser.create(**data) if new_user: return JsonResponse(new_user.to_dict(), status=200) return HttpResponse('Something went wrong', status=400) diff --git a/tests/custom_user/tests.py b/tests/custom_user/tests.py index 5e4adbd..6a84bc4 100644 --- a/tests/custom_user/tests.py +++ b/tests/custom_user/tests.py @@ -1,6 +1,6 @@ -from django.test import TestCase, Client, tag -from custom_user.models import CustomUser import json +from django.test import TestCase +from custom_user.models import CustomUser class CustomUserModelsTestCase(TestCase): @@ -27,6 +27,7 @@ def create_user(): email='', password='test1') return res + self.assertRaises(ValueError, create_user) def test_str(self): @@ -46,7 +47,7 @@ def test_find_by_id_not_found(self): self.assertTrue(user is None) def test_update(self): - result = self.user.update(first_name='User', last_name='Test', email= 'testuser@gmail.com') + result = self.user.update(first_name='User', last_name='Test', email='testuser@gmail.com') self.assertEqual(self.user.first_name, 'User') self.assertEqual(self.user.last_name, 'Test') self.assertEqual(self.user.email, 'testuser@gmail.com') @@ -65,43 +66,37 @@ def test_remove_user(self): class CustomUserViewsTestCase(TestCase): def setUp(self): - self.user = CustomUser.objects.create(id=2, - first_name="Test", - last_name="User", - email="testuser@gmail.com", - password="test") - - self.user = CustomUser.objects.create(id=3, - first_name="Test2", - last_name="User2", - email="testuser2@gmail.com", - password="test2") + self.user = CustomUser.create(id=2, + first_name='Test', + last_name='User', + email='testuser@gmail.com', + password='test') def test_get_by_id(self): data = {'first_name': 'Test', 'last_name': 'User', 'email': 'testuser@gmail.com'} - responce = self.client.get('/custom_user/profile/2/') - self.assertEqual(responce.json() , data) + responce = self.client.get('/custom-user/profile/2/') + self.assertEqual(responce.json(), data) def test_create_user(self): data = {'first_name': 'Test3', 'last_name': 'User3', 'email': 'testuser3@gmail.com'} - response = self.client.generic('POST', '/custom_user/create/', json.dumps({'first_name': 'Test3', + response = self.client.generic('POST', '/custom-user/create/', json.dumps({'first_name': 'Test3', 'last_name': 'User3', 'email': 'testuser3@gmail.com'})) - self.assertEqual(response.json(),data) + self.assertEqual(response.json(), data) def test_put_all_input_data(self): data = {'first_name': 'NewName', 'last_name': 'User', 'email': 'testuser@gmail.com'} - response = self.client.generic('PUT', '/custom_user/profile/2/', json.dumps({'first_name': 'NewName', - 'last_name': 'User', - 'email': 'testuser@gmail.com'})) + response = self.client.generic('PUT', '/custom-user/profile/2/', json.dumps({'first_name': 'NewName', + 'last_name': 'User', + 'email': 'testuser@gmail.com'})) self.assertEqual(response.json(), data) def test_put_not_all_input_data(self): data = {'first_name': 'Test', 'last_name': 'NewUser', 'email': 'testuser@gmail.com'} - response = self.client.generic('PUT', '/custom_user/profile/2/', json.dumps({'last_name': 'NewUser', - 'email': 'testuser@gmail.com'})) + response = self.client.generic('PUT', '/custom-user/profile/2/', json.dumps({'last_name': 'NewUser', + 'email': 'testuser@gmail.com'})) self.assertEqual(response.json(), data) def test_delete(self): - response = self.client.generic('DELETE', '/custom_user/profile/2/') + response = self.client.generic('DELETE', '/custom-user/profile/2/') self.assertEqual(response.status_code, 200) From 151b71e3ae0c523b124e65c1cd7ab09283e5a60c Mon Sep 17 00:00:00 2001 From: Nevmerzhitsky Yura Date: Wed, 17 Mar 2021 06:59:47 +0200 Subject: [PATCH 081/100] Modified custom_user/tests --- custom_user/views.py | 2 +- tests/custom_user/tests.py | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/custom_user/views.py b/custom_user/views.py index 1f9f8f8..9d8de35 100644 --- a/custom_user/views.py +++ b/custom_user/views.py @@ -23,7 +23,7 @@ def post(self, request): 'password': body.get('password'), 'email': body.get('email')} - new_user = CustomUser.objects.create(**data) + new_user = CustomUser.create(**data) if new_user: return JsonResponse(new_user.to_dict(), status=200) return HttpResponse('Something went wrong', status=400) diff --git a/tests/custom_user/tests.py b/tests/custom_user/tests.py index 5e4adbd..c595900 100644 --- a/tests/custom_user/tests.py +++ b/tests/custom_user/tests.py @@ -65,7 +65,7 @@ def test_remove_user(self): class CustomUserViewsTestCase(TestCase): def setUp(self): - self.user = CustomUser.objects.create(id=2, + self.user = CustomUser.create(id=2, first_name="Test", last_name="User", email="testuser@gmail.com", @@ -79,29 +79,29 @@ def setUp(self): def test_get_by_id(self): data = {'first_name': 'Test', 'last_name': 'User', 'email': 'testuser@gmail.com'} - responce = self.client.get('/custom_user/profile/2/') + responce = self.client.get('/custom-user/profile/2/') self.assertEqual(responce.json() , data) def test_create_user(self): data = {'first_name': 'Test3', 'last_name': 'User3', 'email': 'testuser3@gmail.com'} - response = self.client.generic('POST', '/custom_user/create/', json.dumps({'first_name': 'Test3', + response = self.client.generic('POST', '/custom-user/create/', json.dumps({'first_name': 'Test3', 'last_name': 'User3', 'email': 'testuser3@gmail.com'})) self.assertEqual(response.json(),data) def test_put_all_input_data(self): data = {'first_name': 'NewName', 'last_name': 'User', 'email': 'testuser@gmail.com'} - response = self.client.generic('PUT', '/custom_user/profile/2/', json.dumps({'first_name': 'NewName', + response = self.client.generic('PUT', '/custom-user/profile/2/', json.dumps({'first_name': 'NewName', 'last_name': 'User', 'email': 'testuser@gmail.com'})) self.assertEqual(response.json(), data) def test_put_not_all_input_data(self): data = {'first_name': 'Test', 'last_name': 'NewUser', 'email': 'testuser@gmail.com'} - response = self.client.generic('PUT', '/custom_user/profile/2/', json.dumps({'last_name': 'NewUser', + response = self.client.generic('PUT', '/custom-user/profile/2/', json.dumps({'last_name': 'NewUser', 'email': 'testuser@gmail.com'})) self.assertEqual(response.json(), data) def test_delete(self): - response = self.client.generic('DELETE', '/custom_user/profile/2/') + response = self.client.generic('DELETE', '/custom-user/profile/2/') self.assertEqual(response.status_code, 200) From 86d079e55ff33718b89c5ff611916a3729a91b83 Mon Sep 17 00:00:00 2001 From: SLDem Date: Wed, 17 Mar 2021 12:12:28 +0200 Subject: [PATCH 082/100] added coveragerc file and refactor --- .coveragerc | 13 +++ .pylintrc | 4 +- custom_user/models.py | 4 +- project_config/settings.py | 3 +- task/migrations/0003_auto_20210317_1000.py | 23 +++++ task/models.py | 51 +++++++----- task/views.py | 6 +- {abstract => tests}/__init__.py | 0 .../forms.py => tests/custom_user/__init__.py | 0 tests/task/__init__.py | 0 tests/task/tests.py | 83 ++++++++++--------- tests/todolist/__init__.py | 0 todolist/models.py | 2 +- utils/__init__.py | 0 {abstract => utils}/abstract_model.py | 9 +- utils/middleware/__init__.py | 0 .../middleware}/middleware.py | 4 +- utils/validators.py | 6 +- 18 files changed, 129 insertions(+), 79 deletions(-) create mode 100644 .coveragerc create mode 100644 task/migrations/0003_auto_20210317_1000.py rename {abstract => tests}/__init__.py (100%) rename custom_user/forms.py => tests/custom_user/__init__.py (100%) create mode 100644 tests/task/__init__.py create mode 100644 tests/todolist/__init__.py create mode 100644 utils/__init__.py rename {abstract => utils}/abstract_model.py (79%) create mode 100644 utils/middleware/__init__.py rename {middleware => utils/middleware}/middleware.py (79%) diff --git a/.coveragerc b/.coveragerc new file mode 100644 index 0000000..c3e2677 --- /dev/null +++ b/.coveragerc @@ -0,0 +1,13 @@ +[run] + +source = + custom_user + task + todolist + utils + +omit = + *migrations* + *apps.py* + *urls.py* + *__init__.py* diff --git a/.pylintrc b/.pylintrc index 541618a..94ae496 100644 --- a/.pylintrc +++ b/.pylintrc @@ -16,7 +16,7 @@ fail-under=10.0 # Add files or directories to the blacklist. They should be base names, not # paths. -ignore=CVS, migrations, apps.py, settings.py, local_settings.py, manage.py, unittests, urls.py +ignore=CVS, migrations, apps.py, settings.py, local_settings.py, manage.py, tests, urls.py # Add files or directories matching the regex patterns to the blacklist. The # regex matches against base names, not paths. @@ -519,7 +519,7 @@ valid-metaclass-classmethod-first-arg=cls [DESIGN] # Maximum number of arguments for function / method. -max-args=5 +max-args=7 # Maximum number of attributes for a class (see R0902). max-attributes=7 diff --git a/custom_user/models.py b/custom_user/models.py index 0d80f6b..0cde6cb 100644 --- a/custom_user/models.py +++ b/custom_user/models.py @@ -1,6 +1,6 @@ -from django.contrib.auth.models import AbstractBaseUser, BaseUserManager +from django.contrib.auth.models import AbstractBaseUser from django.db import models -from abstract.abstract_model import AbstractModel +from utils.abstract_model import AbstractModel class CustomUser(AbstractBaseUser, AbstractModel): diff --git a/project_config/settings.py b/project_config/settings.py index a2efa0e..59e186d 100644 --- a/project_config/settings.py +++ b/project_config/settings.py @@ -39,7 +39,6 @@ 'task', 'rest_framework', 'drf_yasg', - 'abstract' ] MIDDLEWARE = [ @@ -49,7 +48,7 @@ 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', - 'middleware.middleware.JSONMiddleware' + 'utils.middleware.middleware.JSONMiddleware' ] ROOT_URLCONF = 'project_config.urls' diff --git a/task/migrations/0003_auto_20210317_1000.py b/task/migrations/0003_auto_20210317_1000.py new file mode 100644 index 0000000..2d0e06a --- /dev/null +++ b/task/migrations/0003_auto_20210317_1000.py @@ -0,0 +1,23 @@ +# Generated by Django 3.1.7 on 2021-03-17 10:00 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('task', '0002_auto_20210312_0909'), + ] + + operations = [ + migrations.RenameField( + model_name='task', + old_name='list_id', + new_name='todolist', + ), + migrations.RenameField( + model_name='task', + old_name='user_id', + new_name='user', + ), + ] diff --git a/task/models.py b/task/models.py index d0e611a..7f3c12d 100644 --- a/task/models.py +++ b/task/models.py @@ -1,9 +1,9 @@ from abc import abstractmethod from datetime import date -from django.db import models +from django.db import models, IntegrityError from custom_user.models import CustomUser from todolist.models import ToDoList -from abstract.abstract_model import AbstractModel +from utils.abstract_model import AbstractModel class Task(AbstractModel): @@ -11,34 +11,41 @@ class Task(AbstractModel): description = models.TextField(max_length=256) is_completed = models.BooleanField(default=False) deadline = models.DateField() - user_id = models.ForeignKey(CustomUser, on_delete=models.CASCADE) - list_id = models.ForeignKey(ToDoList, on_delete=models.CASCADE) + user = models.ForeignKey(CustomUser, on_delete=models.CASCADE) + todolist = models.ForeignKey(ToDoList, on_delete=models.CASCADE) def __str__(self): return self.title @classmethod - def find_all_for_list(cls, list_id): + def get_by_list_id(cls, list_id: int): tasks = Task.objects.filter(list_id=list_id) return tasks @classmethod - def create(cls, title: str, description: str, deadline: date, user_id, list_id): + def create(cls, + title: str, + description: str, + deadline: date, + user: CustomUser, + todolist: ToDoList): # pylint disable=W0221 task = Task(title=title, description=description, deadline=deadline) - user = CustomUser.get_by_id(user_id) - task.user_id = user - todolist = ToDoList.get_by_id(list_id) - task.list_id = todolist - task.save() - return task + task.user = user + task.todolist = todolist + try: + task.save() + return task + except (ValueError, IntegrityError): + return None @abstractmethod - def update(self, title: str, + def update(self, + title: str, description: str, is_completed: bool, deadline: date, - user_id: int, - list_id: int): + user: CustomUser, + todolist: ToDoList): # pylint disable=W0221 if title: self.title = title if description: @@ -47,8 +54,12 @@ def update(self, title: str, self.deadline = deadline if is_completed: self.is_completed = is_completed - if user_id: - self.user_id = CustomUser.get_by_id(user_id) - if list_id: - self.list_id = ToDoList.get_by_id(list_id) - self.save() + if user: + self.user = user + if todolist: + self.todolist = todolist + try: + self.save() + return True + except (ValueError, IntegrityError): + pass diff --git a/task/views.py b/task/views.py index 438c7af..be5b785 100644 --- a/task/views.py +++ b/task/views.py @@ -1,17 +1,15 @@ -import json from django.http import JsonResponse -from rest_framework.views import APIView from django.core.serializers import serialize from django.db.utils import IntegrityError, DataError from django.core.exceptions import ValidationError - +from rest_framework.views import APIView from task.models import Task class TaskAPIView(APIView): def get(self, request, list_id): - task = Task.find_all_for_list(list_id=list_id) + task = Task.get_by_list_id(list_id=list_id) task_serialized_data = serialize('python', task) diff --git a/abstract/__init__.py b/tests/__init__.py similarity index 100% rename from abstract/__init__.py rename to tests/__init__.py diff --git a/custom_user/forms.py b/tests/custom_user/__init__.py similarity index 100% rename from custom_user/forms.py rename to tests/custom_user/__init__.py diff --git a/tests/task/__init__.py b/tests/task/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/task/tests.py b/tests/task/tests.py index 4b3b1c9..89a16f3 100644 --- a/tests/task/tests.py +++ b/tests/task/tests.py @@ -1,11 +1,12 @@ import json +from datetime import date + from django.test import TestCase -from task.models import Task + from custom_user.models import CustomUser +from task.models import Task from todolist.models import ToDoList -from datetime import date - class TaskModelsTest(TestCase): def setUp(self): @@ -21,8 +22,8 @@ def setUp(self): self.task1 = Task.create(title="Task #1", description="Task #1 Description", deadline=date(2021, 1, 1), - user_id=self.user.pk, - list_id=self.todolist.pk) + user=self.user, + todolist=self.todolist) def test_str(self): test = self.task1.__str__() @@ -32,32 +33,36 @@ def test_create(self): task = Task.create(title="Task #2", description="Task #2 Description", deadline=date(2021, 2, 2), - user_id=self.user.pk, - list_id=self.todolist.pk) + user=self.user, + todolist=self.todolist) self.assertIsInstance(task, Task) def test_update_task(self): self.task = Task.create(title="Task #3", description="Task #3 Description", deadline=date(2021, 3, 3), - user_id=self.user.pk, - list_id=self.todolist.pk) - self.task.update("New task", "new task description", False, date(2021, 3, 3), - self.user.pk, self.todolist.pk) + user=self.user.pk, + todolist=self.todolist.pk) + self.task.update("New task", + "new task description", + False, + date(2021, 3, 3), + self.user, + self.todolist) self.assertEqual(self.task.title, "New task") self.assertEqual(self.task.description, "new task description") self.assertEqual(self.task.is_completed, False) self.assertEqual(self.task.deadline, date(2021, 3, 3)) - self.assertEqual(self.task.user_id.pk, 1) - self.assertEqual(self.task.list_id.pk, 1) + self.assertEqual(self.task.user.pk, 1) + self.assertEqual(self.task.todolist.pk, 1) self.assertTrue(self.task) def test_find_by_id(self): self.task = Task.create(title="Task #4", description="Task #4 Description", deadline=date(2021, 5, 3), - user_id=self.user.pk, - list_id=self.todolist.pk) + user=self.user, + todolist=self.todolist) result = Task.get_by_id(self.task.pk) self.assertIsInstance(result, Task) @@ -69,10 +74,10 @@ def test_remove_task(self): self.task = Task.create(title="Task #5", description="Task #5 Description", deadline=date(2021, 4, 4), - user_id=self.user.pk, - list_id=self.todolist.pk) + user=self.user, + todolist=self.todolist) result = Task.remove(self.task.pk) - self.assertNotIn(result, Task.find_all_for_list(self.todolist.pk)) + self.assertNotIn(result, Task.get_by_list_id(self.todolist.pk)) def test_remove_non_existed_task(self): result = Task.remove(100) @@ -82,9 +87,9 @@ def test_find_all_for_list(self): self.task = Task.create(title="Task #6", description="Task #6 Description", deadline=date(2021, 6, 7), - user_id=self.user.pk, - list_id=self.todolist.pk) - result = Task.find_all_for_list(self.todolist.pk) + user=self.user, + todolist=self.todolist) + result = Task.get_by_list_id(self.todolist.pk) self.assertEqual(len(result), 2) @@ -93,18 +98,18 @@ class DeleteTaskView(TestCase): def setUp(self): self.user = CustomUser.objects.create(first_name="Test", - last_name="User", - email="mail@mail.com", - password="secret" - ) + last_name="User", + email="mail@mail.com", + password="secret" + ) self.list = ToDoList.create(name='List', description='About list') self.list.update_members(members_to_add=[self.user.id]) self.task = Task.create(title='Task', description='Task', deadline=date(2020, 1, 1), - list_id=self.list.id, - user_id=self.user.id) + user=self.user, + todolist=self.list) def test_delete_task_existing(self): response = self.client.generic('DELETE', f'/tasks/{self.task.id}/') @@ -120,9 +125,9 @@ class CreateTaskView(TestCase): def setUp(self): self.user = CustomUser.objects.create(first_name="Test", - last_name="User", - email="mail@mail.com", - password="secret") + last_name="User", + email="mail@mail.com", + password="secret") self.list = ToDoList.create(name='List', description='About list') self.list.update_members(members_to_add=[self.user.id]) @@ -132,8 +137,8 @@ def test_create_task_data_valid(self): "title": "Task", "description": "Task", "deadline": "2020-01-01", - "user_id": self.user.id, - "list_id": self.list.id + "user": self.user, + "todolist": self.list })) self.assertEqual(response.status_code, 201) @@ -142,8 +147,8 @@ def test_create_task_data_invalid(self): "title": "Task name that way beyond 30 symbol limit", "description": "Task", "deadline": "2020-01-01", - "user_id": self.user.id, - "list_id": self.list.id + "user": self.user, + "todolist": self.list })) self.assertEqual(response.status_code, 400) @@ -157,17 +162,17 @@ class UpdateTaskView(TestCase): def setUp(self): self.user = CustomUser.objects.create(first_name="Test", - last_name="User", - email="mail@mail.com", - password="secret") + last_name="User", + email="mail@mail.com", + password="secret") self.list = ToDoList.create(name='List', description='About list') self.list.update_members(members_to_add=[self.user.id]) self.task = Task.create(title='Task', description='Task', deadline=date(2020, 1, 1), - list_id=self.list.id, - user_id=self.user.id) + todolist=self.list, + user=self.user) def test_update_task_existing(self): response = self.client.generic('PUT', f'/tasks/{self.task.id}/', json.dumps({ diff --git a/tests/todolist/__init__.py b/tests/todolist/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/todolist/models.py b/todolist/models.py index 966debb..c8dcf26 100644 --- a/todolist/models.py +++ b/todolist/models.py @@ -1,6 +1,6 @@ from django.db import models, IntegrityError, DatabaseError -from abstract.abstract_model import AbstractModel +from utils.abstract_model import AbstractModel from custom_user.models import CustomUser diff --git a/utils/__init__.py b/utils/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/abstract/abstract_model.py b/utils/abstract_model.py similarity index 79% rename from abstract/abstract_model.py rename to utils/abstract_model.py index c04890a..15bb737 100644 --- a/abstract/abstract_model.py +++ b/utils/abstract_model.py @@ -4,12 +4,13 @@ class AbstractModel(models.Model): + objects = models.Manager() class Meta: abstract = True @classmethod - def get_by_id(cls, pk: int): + def get_by_id(cls, pk: int): # pylint: disable=C0103 try: entity = cls.objects.get(pk=pk) return entity @@ -25,7 +26,7 @@ def get_all(cls): return None @classmethod - def remove(cls, pk): + def remove(cls, pk): # pylint: disable=C0103 try: task = cls.objects.get(id=pk) task.delete() @@ -35,9 +36,9 @@ def remove(cls, pk): @abstractmethod def update(self, *args, **kwargs): - raise NotImplemented + raise NotImplementedError @classmethod @abstractmethod def create(cls, *args, **kwargs): - raise NotImplemented + raise NotImplementedError diff --git a/utils/middleware/__init__.py b/utils/middleware/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/middleware/middleware.py b/utils/middleware/middleware.py similarity index 79% rename from middleware/middleware.py rename to utils/middleware/middleware.py index 194b528..dbdf6c0 100644 --- a/middleware/middleware.py +++ b/utils/middleware/middleware.py @@ -5,7 +5,7 @@ class JSONMiddleware: """ - Process application/json requests data from GET and POST requests. + Process requests data from PUT and POST requests. """ def __init__(self, get_response): @@ -17,7 +17,7 @@ def __call__(self, request): request._body = json.loads(request.body) return self.get_response(request) except json.JSONDecodeError: - return HttpResponse("Invalid JSON", status=400) + return HttpResponse("Invalid data.", status=400) else: response = self.get_response(request) return response diff --git a/utils/validators.py b/utils/validators.py index 690815f..45903ef 100644 --- a/utils/validators.py +++ b/utils/validators.py @@ -1,7 +1,7 @@ -def is_integer(n): +def is_integer(number): try: - float(n) + float(number) except ValueError: return False else: - return float(n).is_integer() + return float(number).is_integer() From 5b7e04ddea7cc957091e1db019d586417971eb9a Mon Sep 17 00:00:00 2001 From: SLDem Date: Wed, 17 Mar 2021 12:54:06 +0200 Subject: [PATCH 083/100] Modified develop.yml --- .github/workflows/develop.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/develop.yml b/.github/workflows/develop.yml index c78d2c0..a922534 100644 --- a/.github/workflows/develop.yml +++ b/.github/workflows/develop.yml @@ -34,6 +34,7 @@ jobs: # Runs a single command using the runners shell - name: Run a one-line script run: | + ls pip install -r requirements.txt coverage run manage.py test tests From 42e2f6d97a841f7ec213e1cdd16c0835985a1c62 Mon Sep 17 00:00:00 2001 From: SLDem Date: Wed, 17 Mar 2021 12:56:01 +0200 Subject: [PATCH 084/100] Modified develop.yml --- .github/workflows/develop.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/develop.yml b/.github/workflows/develop.yml index a922534..fa5db68 100644 --- a/.github/workflows/develop.yml +++ b/.github/workflows/develop.yml @@ -34,7 +34,6 @@ jobs: # Runs a single command using the runners shell - name: Run a one-line script run: | - ls + cd todoProject pip install -r requirements.txt coverage run manage.py test tests - From 4d292baf7c851874b7c5cf4f51a7a393ffada395 Mon Sep 17 00:00:00 2001 From: SLDem Date: Wed, 17 Mar 2021 12:57:57 +0200 Subject: [PATCH 085/100] Modified develop.yml --- .github/workflows/develop.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/develop.yml b/.github/workflows/develop.yml index fa5db68..27d1a5c 100644 --- a/.github/workflows/develop.yml +++ b/.github/workflows/develop.yml @@ -34,6 +34,6 @@ jobs: # Runs a single command using the runners shell - name: Run a one-line script run: | - cd todoProject + echo ls pip install -r requirements.txt coverage run manage.py test tests From 271004d865d52089cdd5fa3f82c3fddb0c86162b Mon Sep 17 00:00:00 2001 From: SLDem Date: Wed, 17 Mar 2021 13:04:19 +0200 Subject: [PATCH 086/100] Modified develop.yml --- .github/workflows/develop.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/develop.yml b/.github/workflows/develop.yml index 27d1a5c..669f6fe 100644 --- a/.github/workflows/develop.yml +++ b/.github/workflows/develop.yml @@ -35,5 +35,5 @@ jobs: - name: Run a one-line script run: | echo ls - pip install -r requirements.txt + if [ -f requirements.txt ]; then pip install -r requirements.txt; fi coverage run manage.py test tests From 6c93b3e34cd3a4c04152a12fe3139977ecf40683 Mon Sep 17 00:00:00 2001 From: SLDem Date: Wed, 17 Mar 2021 13:09:30 +0200 Subject: [PATCH 087/100] Modified develop.yml --- .github/workflows/develop.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/develop.yml b/.github/workflows/develop.yml index 669f6fe..8bf3203 100644 --- a/.github/workflows/develop.yml +++ b/.github/workflows/develop.yml @@ -17,6 +17,9 @@ jobs: build: # The type of runner that the job will run on runs-on: ubuntu-latest + strategy: + matrix: + python-version: [3.8] # Steps represent a sequence of tasks that will be executed as part of the job steps: @@ -35,5 +38,6 @@ jobs: - name: Run a one-line script run: | echo ls - if [ -f requirements.txt ]; then pip install -r requirements.txt; fi + cd /home/runner/work/todoProject/todoProject + pip install -r requirements.txt coverage run manage.py test tests From 2ecceeb31a63e7710509a944af34827151e4bfb8 Mon Sep 17 00:00:00 2001 From: SLDem Date: Wed, 17 Mar 2021 13:10:53 +0200 Subject: [PATCH 088/100] Modified develop.yml --- .github/workflows/develop.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/develop.yml b/.github/workflows/develop.yml index 8bf3203..102eea7 100644 --- a/.github/workflows/develop.yml +++ b/.github/workflows/develop.yml @@ -39,5 +39,6 @@ jobs: run: | echo ls cd /home/runner/work/todoProject/todoProject + ls pip install -r requirements.txt coverage run manage.py test tests From bc94bcc9a9850d2b02a297a84cac780a7487a074 Mon Sep 17 00:00:00 2001 From: SLDem Date: Wed, 17 Mar 2021 13:13:17 +0200 Subject: [PATCH 089/100] Modified develop.yml --- .github/workflows/develop.yml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/workflows/develop.yml b/.github/workflows/develop.yml index 102eea7..60b775a 100644 --- a/.github/workflows/develop.yml +++ b/.github/workflows/develop.yml @@ -35,10 +35,9 @@ jobs: # POSTGRES_PASSWORD - superuser password postgresql password: postgres # optional, default is # Runs a single command using the runners shell + - name: Run a one-line script + run: ls - name: Run a one-line script run: | - echo ls - cd /home/runner/work/todoProject/todoProject - ls pip install -r requirements.txt coverage run manage.py test tests From 471a5d8960808050469089eef319e228d41874da Mon Sep 17 00:00:00 2001 From: SLDem Date: Wed, 17 Mar 2021 13:14:35 +0200 Subject: [PATCH 090/100] Modified develop.yml --- .github/workflows/develop.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/develop.yml b/.github/workflows/develop.yml index 60b775a..e3b3d35 100644 --- a/.github/workflows/develop.yml +++ b/.github/workflows/develop.yml @@ -36,7 +36,7 @@ jobs: postgresql password: postgres # optional, default is # Runs a single command using the runners shell - name: Run a one-line script - run: ls + run: echo ls - name: Run a one-line script run: | pip install -r requirements.txt From 1ef0e350175db6ad8d202485711ec8b323877d1e Mon Sep 17 00:00:00 2001 From: SLDem Date: Wed, 17 Mar 2021 13:19:11 +0200 Subject: [PATCH 091/100] Modified develop.yml --- .github/workflows/develop.yml | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/.github/workflows/develop.yml b/.github/workflows/develop.yml index e3b3d35..34b7a05 100644 --- a/.github/workflows/develop.yml +++ b/.github/workflows/develop.yml @@ -35,9 +35,15 @@ jobs: # POSTGRES_PASSWORD - superuser password postgresql password: postgres # optional, default is # Runs a single command using the runners shell - - name: Run a one-line script - run: echo ls - - name: Run a one-line script + - uses: actions/checkout@v2 + - name: Set up Python + uses: actions/setup-python@v2 + with: + python-version: '3.x' + - name: Install dependencies run: | + python -m pip install --upgrade pip pip install -r requirements.txt + - name: Run a one-line script + run: | coverage run manage.py test tests From e688aaa5b9a2400a18922e461cec9c4024eb8fc7 Mon Sep 17 00:00:00 2001 From: SLDem Date: Wed, 17 Mar 2021 13:19:25 +0200 Subject: [PATCH 092/100] Modified develop.yml --- .github/workflows/develop.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/develop.yml b/.github/workflows/develop.yml index 34b7a05..41f442d 100644 --- a/.github/workflows/develop.yml +++ b/.github/workflows/develop.yml @@ -39,7 +39,7 @@ jobs: - name: Set up Python uses: actions/setup-python@v2 with: - python-version: '3.x' + python-version: '3.8' - name: Install dependencies run: | python -m pip install --upgrade pip From 64ad068523fb49439fd335e09154f7ae0aaa3dd5 Mon Sep 17 00:00:00 2001 From: SLDem Date: Wed, 17 Mar 2021 13:22:28 +0200 Subject: [PATCH 093/100] Modified settings.py --- .github/workflows/develop.yml | 2 +- project_config/settings.py | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflows/develop.yml b/.github/workflows/develop.yml index 41f442d..e165c9d 100644 --- a/.github/workflows/develop.yml +++ b/.github/workflows/develop.yml @@ -44,6 +44,6 @@ jobs: run: | python -m pip install --upgrade pip pip install -r requirements.txt - - name: Run a one-line script + - name: Run coverage run: | coverage run manage.py test tests diff --git a/project_config/settings.py b/project_config/settings.py index 59e186d..7f88c71 100644 --- a/project_config/settings.py +++ b/project_config/settings.py @@ -77,9 +77,10 @@ DATABASES = { 'default': { 'ENGINE': 'django.db.backends.postgresql_psycopg2', - 'USER': 'postgres', - 'HOST': 'postgres', 'NAME': 'postgres', + 'USER': 'postgres', + 'PASSWORD': 'postgres', + 'HOST': '127.0.0.1', 'PORT': '5432', } } From 9aa8c83e643208c56ae2fdc465cc532bfbb9a021 Mon Sep 17 00:00:00 2001 From: Nevmerzhitsky Yura Date: Wed, 17 Mar 2021 14:43:18 +0200 Subject: [PATCH 094/100] Add test to custom_user/views --- tests/custom_user/tests.py | 62 +++++++++++++++++++++++++++++++++----- 1 file changed, 54 insertions(+), 8 deletions(-) diff --git a/tests/custom_user/tests.py b/tests/custom_user/tests.py index b942184..4418b8d 100644 --- a/tests/custom_user/tests.py +++ b/tests/custom_user/tests.py @@ -66,22 +66,27 @@ def test_remove_user(self): class CustomUserViewsTestCase(TestCase): def setUp(self): self.user = CustomUser.create(id=2, - first_name="Test", - last_name="User", - email="testuser@gmail.com", - password="test") + first_name="Test", + last_name="User", + email="testuser@gmail.com", + password="test") self.user = CustomUser.create(id=3, - first_name="Test2", - last_name="User2", - email="testuser2@gmail.com", - password="test2") + first_name="Test2", + last_name="User2", + email="testuser2@gmail.com", + password="test2") def test_get_by_id(self): data = {'first_name': 'Test', 'last_name': 'User', 'email': 'testuser@gmail.com'} responce = self.client.get('/custom-user/profile/2/') self.assertEqual(responce.json() , data) + def test_get_user_not_exist(self): + responce = self.client.get('/custom-user/profile/10/') + self.assertEqual(responce.status_code, 400) + + def test_create_user(self): data = {'first_name': 'Test3', 'last_name': 'User3', 'email': 'testuser3@gmail.com'} response = self.client.generic('POST', '/custom-user/create/', json.dumps({'first_name': 'Test3', @@ -89,6 +94,20 @@ def test_create_user(self): 'email': 'testuser3@gmail.com'})) self.assertEqual(response.json(),data) + def test_create_user_empty_json(self): + response = self.client.generic('POST', '/custom-user/create/', json.dumps({})) + self.assertEqual(response.status_code, 400) + + + def test_create_user_bad_save(self): + response = self.client.generic('POST', '/custom-user/create/', json.dumps({ + 'first_name': 'Test3Test3Test3Test3Test3Test3Test3Test3Test3Test3Test3Test3Test3Test3Test3Test3Test3Test3', + 'last_name': 'User3', + 'email': 'testuser3@gmail.com'})) + self.assertEqual(response.status_code, 400) + + + def test_put_all_input_data(self): data = {'first_name': 'NewName', 'last_name': 'User', 'email': 'testuser@gmail.com'} response = self.client.generic('PUT', '/custom-user/profile/2/', json.dumps({'first_name': 'NewName', @@ -102,6 +121,33 @@ def test_put_not_all_input_data(self): 'email': 'testuser@gmail.com'})) self.assertEqual(response.json(), data) + def test_put_no_user(self): + response = self.client.generic('PUT', '/custom-user/profile/100/', json.dumps({'first_name': 'NewName', + 'last_name': 'User', + 'email': 'testuser@gmail.com'})) + self.assertEqual(response.status_code, 404) + + + def test_put_empty_data(self): + response = self.client.generic('PUT', '/custom-user/profile/2/', json.dumps({})) + self.assertEqual(response.status_code, 400) + + def test_update_user_bad_save(self): + response = self.client.generic('PUT', '/custom-user/profile/2/', json.dumps({ + 'first_name': "123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890", + 'last_name': 'User', + 'email': 'testuser@gmail.com'}) + ) + self.assertEqual(response.status_code, 400) + def test_delete(self): response = self.client.generic('DELETE', '/custom-user/profile/2/') self.assertEqual(response.status_code, 200) + + def test_delete_user_not_found(self): + response = self.client.generic('DELETE', '/custom-user/profile/200/') + self.assertEqual(response.status_code, 400) + + + + From 2166b072c277ca394444301e9f62093e2837357c Mon Sep 17 00:00:00 2001 From: SLDem Date: Wed, 17 Mar 2021 14:43:33 +0200 Subject: [PATCH 095/100] Added tests for user models --- custom_user/models.py | 11 +++++---- tests/custom_user/tests.py | 49 +++++++++++++++++++++++++------------- 2 files changed, 40 insertions(+), 20 deletions(-) diff --git a/custom_user/models.py b/custom_user/models.py index 0cde6cb..64c3fbd 100644 --- a/custom_user/models.py +++ b/custom_user/models.py @@ -1,5 +1,5 @@ from django.contrib.auth.models import AbstractBaseUser -from django.db import models +from django.db import models, DatabaseError from utils.abstract_model import AbstractModel @@ -30,8 +30,11 @@ def create(cls, email, password, **extra_fields): **extra_fields ) user.set_password(password) - user.save() - return user + try: + user.save() + return user + except (ValueError, TypeError, DatabaseError): + return False def update(self, first_name=None, last_name=None, email=None): if first_name: @@ -43,5 +46,5 @@ def update(self, first_name=None, last_name=None, email=None): try: self.save() return self - except (TypeError, ValueError): + except (TypeError, ValueError, DatabaseError): return None diff --git a/tests/custom_user/tests.py b/tests/custom_user/tests.py index b942184..dcd9339 100644 --- a/tests/custom_user/tests.py +++ b/tests/custom_user/tests.py @@ -1,7 +1,9 @@ -from django.test import TestCase, Client, tag -from custom_user.models import CustomUser import json +from django.test import TestCase + +from custom_user.models import CustomUser + class CustomUserModelsTestCase(TestCase): def setUp(self): @@ -19,6 +21,14 @@ def test_create_user(self): password='test1') self.assertIsInstance(user, CustomUser) + def test_create_user_invalid_data(self): + user = CustomUser.create(id='a', + first_name='Test1', + last_name='User1', + email='testuser1@gmail.com', + password='test1') + self.assertEqual(user, False) + def test_create_user_no_email(self): def create_user(): res = CustomUser.create(id=2, @@ -27,6 +37,7 @@ def create_user(): email='', password='test1') return res + self.assertRaises(ValueError, create_user) def test_str(self): @@ -46,12 +57,18 @@ def test_find_by_id_not_found(self): self.assertTrue(user is None) def test_update(self): - result = self.user.update(first_name='User', last_name='Test', email= 'testuser@gmail.com') + result = self.user.update(first_name='User', last_name='Test', email='testuser@gmail.com') self.assertEqual(self.user.first_name, 'User') self.assertEqual(self.user.last_name, 'Test') self.assertEqual(self.user.email, 'testuser@gmail.com') self.assertTrue(result) + def test_update_fail(self): + result = self.user.update(first_name='123123123123123123123123123123123123123123123123123123123123123123123123', + last_name='Test', + email='testuser@gmail.com') + self.assertEqual(result, None) + def test_remove_user(self): user = CustomUser.objects.create(id=2, first_name='Test1', @@ -66,40 +83,40 @@ def test_remove_user(self): class CustomUserViewsTestCase(TestCase): def setUp(self): self.user = CustomUser.create(id=2, - first_name="Test", - last_name="User", - email="testuser@gmail.com", - password="test") + first_name="Test", + last_name="User", + email="testuser@gmail.com", + password="test") self.user = CustomUser.create(id=3, - first_name="Test2", - last_name="User2", - email="testuser2@gmail.com", - password="test2") + first_name="Test2", + last_name="User2", + email="testuser2@gmail.com", + password="test2") def test_get_by_id(self): data = {'first_name': 'Test', 'last_name': 'User', 'email': 'testuser@gmail.com'} responce = self.client.get('/custom-user/profile/2/') - self.assertEqual(responce.json() , data) + self.assertEqual(responce.json(), data) def test_create_user(self): data = {'first_name': 'Test3', 'last_name': 'User3', 'email': 'testuser3@gmail.com'} response = self.client.generic('POST', '/custom-user/create/', json.dumps({'first_name': 'Test3', 'last_name': 'User3', 'email': 'testuser3@gmail.com'})) - self.assertEqual(response.json(),data) + self.assertEqual(response.json(), data) def test_put_all_input_data(self): data = {'first_name': 'NewName', 'last_name': 'User', 'email': 'testuser@gmail.com'} response = self.client.generic('PUT', '/custom-user/profile/2/', json.dumps({'first_name': 'NewName', - 'last_name': 'User', - 'email': 'testuser@gmail.com'})) + 'last_name': 'User', + 'email': 'testuser@gmail.com'})) self.assertEqual(response.json(), data) def test_put_not_all_input_data(self): data = {'first_name': 'Test', 'last_name': 'NewUser', 'email': 'testuser@gmail.com'} response = self.client.generic('PUT', '/custom-user/profile/2/', json.dumps({'last_name': 'NewUser', - 'email': 'testuser@gmail.com'})) + 'email': 'testuser@gmail.com'})) self.assertEqual(response.json(), data) def test_delete(self): From 2519fd6f677b3046d0008f534b2689d854fda5f0 Mon Sep 17 00:00:00 2001 From: StepanTchynetskyi Date: Wed, 17 Mar 2021 14:45:49 +0200 Subject: [PATCH 096/100] fix todolistViews --- todolist/views.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/todolist/views.py b/todolist/views.py index e3a7375..5944e97 100644 --- a/todolist/views.py +++ b/todolist/views.py @@ -23,8 +23,6 @@ def get(self, request, todo_list_pk=None): def post(self, request): data = request.body - if not data: - return HttpResponse(status=400) data = { 'name': data.get('name'), @@ -45,8 +43,6 @@ def put(self, request, todo_list_pk=None): if not todo_list: return HttpResponse(status=404) data = request.body - if not data: - return HttpResponse(status=400) members_to_add = data.get('members_to_add') members_to_delete = data.get('members_to_delete') From d48d11dca98fbe764d9e985bbd58ec2a581d1e4a Mon Sep 17 00:00:00 2001 From: Daniil Oleshchuk Date: Wed, 17 Mar 2021 14:59:52 +0200 Subject: [PATCH 097/100] added missing tests for ToDoList model and fixed test_create() ids --- tests/todolist/tests.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/tests/todolist/tests.py b/tests/todolist/tests.py index be30958..c8eca47 100644 --- a/tests/todolist/tests.py +++ b/tests/todolist/tests.py @@ -195,10 +195,10 @@ def test_create(self): todo_list3 = ToDoList.create(name='test list name 3', description='test list description 3', members=[self.user, self.user2]) - first_to_dict_expected = {'id': 2, 'name': 'test list name 1', 'description': '', 'members': []} - second_to_dict_expected = {'id': 3, 'name': 'test list name 2', + first_to_dict_expected = {'id': todo_list1.id, 'name': 'test list name 1', 'description': '', 'members': []} + second_to_dict_expected = {'id': todo_list2.id, 'name': 'test list name 2', 'description': 'test list description 2', 'members': []} - third_to_dict_expected = {'id': 4, 'name': 'test list name 3', + third_to_dict_expected = {'id': todo_list3.id, 'name': 'test list name 3', 'description': 'test list description 3', 'members': [1, 2]} self.assertEqual(first_to_dict_expected, todo_list1.to_dict()) @@ -228,3 +228,9 @@ def test_get_by_id(self): def test_delete_by_id(self): list_del = ToDoList.remove(self.todo_list.id) self.assertEqual(True, list_del) + + def test_str(self): + self.assertEqual(self.todo_list.name, self.todo_list.__str__()) + + def test_get_list_members(self): + self.assertEqual(list(self.todo_list.members.all()), list(self.todo_list.get_list_members())) From 9b466222ce19f658b074a4b131508b6eb2c25597 Mon Sep 17 00:00:00 2001 From: AndriiRomaniuk Date: Wed, 17 Mar 2021 18:38:32 +0200 Subject: [PATCH 098/100] fixed tests for views, raised coverage --- ...317_1000.py => 0003_auto_20210317_1551.py} | 9 ++- task/views.py | 30 ++++++++-- tests/task/{tests.py => tests_views.py} | 56 +++++++++++++++++-- 3 files changed, 82 insertions(+), 13 deletions(-) rename task/migrations/{0003_auto_20210317_1000.py => 0003_auto_20210317_1551.py} (61%) rename tests/task/{tests.py => tests_views.py} (68%) diff --git a/task/migrations/0003_auto_20210317_1000.py b/task/migrations/0003_auto_20210317_1551.py similarity index 61% rename from task/migrations/0003_auto_20210317_1000.py rename to task/migrations/0003_auto_20210317_1551.py index 2d0e06a..9cfb607 100644 --- a/task/migrations/0003_auto_20210317_1000.py +++ b/task/migrations/0003_auto_20210317_1551.py @@ -1,6 +1,6 @@ -# Generated by Django 3.1.7 on 2021-03-17 10:00 +# Generated by Django 3.1.7 on 2021-03-17 15:51 -from django.db import migrations +from django.db import migrations, models class Migration(migrations.Migration): @@ -20,4 +20,9 @@ class Migration(migrations.Migration): old_name='user_id', new_name='user', ), + migrations.AlterField( + model_name='task', + name='description', + field=models.TextField(default='', max_length=256), + ), ] diff --git a/task/views.py b/task/views.py index be5b785..8683966 100644 --- a/task/views.py +++ b/task/views.py @@ -4,12 +4,15 @@ from django.core.exceptions import ValidationError from rest_framework.views import APIView from task.models import Task +from todolist.models import ToDoList +from custom_user.models import CustomUser class TaskAPIView(APIView): def get(self, request, list_id): - task = Task.get_by_list_id(list_id=list_id) + todolist = ToDoList.get_by_id(list_id) + task = Task.get_by_list_id(todolist) task_serialized_data = serialize('python', task) @@ -21,12 +24,18 @@ def get(self, request, list_id): def post(self, request): body = request.body + list_id = body.get('todolist') + user_id = body.get('user') + + todolist = ToDoList.get_by_id(list_id) + user = CustomUser.get_by_id(user_id) + task_data = { 'title': body.get('title'), 'description': body.get('description'), 'deadline': body.get('deadline'), - 'user_id': body.get('user_id'), - 'list_id': body.get('list_id'), + 'user': user, + 'todolist': todolist, } try: @@ -40,7 +49,7 @@ def post(self, request): return JsonResponse(success_message, status=201) # Missing parameters - except IntegrityError: + except (IntegrityError, AttributeError): integrity_message = { 'message': 'Cannot create task! One or more parameters are missing' } @@ -55,13 +64,22 @@ def post(self, request): def put(self, request, task_id=None): body = request.body + user = None + todolist = None + + if body.get('todolist'): + list_id = body.get('todolist') + todolist = ToDoList.get_by_id(list_id) + if body.get('user'): + user_id = body.get('user') + user = CustomUser.get_by_id(user_id) task_data = { 'title': body.get('title'), 'description': body.get('description'), 'deadline': body.get('deadline'), - 'user_id': body.get('user_id'), - 'list_id': body.get('list_id'), + 'user': user, + 'todolist': todolist, 'is_completed': body.get('list_id'), } diff --git a/tests/task/tests.py b/tests/task/tests_views.py similarity index 68% rename from tests/task/tests.py rename to tests/task/tests_views.py index 84dc0e3..8e09913 100644 --- a/tests/task/tests.py +++ b/tests/task/tests_views.py @@ -7,6 +7,30 @@ from todolist.models import ToDoList + +class ByListTaskView(TestCase): + """ Test view that returns all tasks for list """ + + def setUp(self): + self.user = CustomUser.objects.create(first_name="Test", + last_name="User", + email="mail@mail.com", + password="secret" + ) + self.list = ToDoList.create(name='List', + description='About list') + self.list.update_members(members_to_add=[self.user.id]) + self.task = Task.create(title='Task', + description='Task', + deadline=date(2020, 1, 1), + user=self.user, + todolist=self.list) + + def test_get_all_by_list_id(self): + response = self.client.generic('GET', f'/tasks/by_list/{self.list.id}/') + self.assertEqual(response.status_code, 200) + + class DeleteTaskView(TestCase): """ Test view for deleting an existing task """ @@ -33,6 +57,10 @@ def test_delete_task_non_existing(self): response = self.client.generic('DELETE', '/tasks/100/') self.assertEqual(response.status_code, 400) + def test_delete_task_no_id(self): + response = self.client.generic('DELETE', '/tasks/') + self.assertEqual(response.status_code, 400) + class CreateTaskView(TestCase): """ Test view for creating new task """ @@ -51,18 +79,28 @@ def test_create_task_data_valid(self): "title": "Task", "description": "Task", "deadline": "2020-01-01", - "user": self.user, - "todolist": self.list + "user": self.user.id, + "todolist": self.list.id })) self.assertEqual(response.status_code, 201) - def test_create_task_data_invalid(self): + def test_create_task_long_title(self): response = self.client.generic('POST', '/tasks/', json.dumps({ "title": "Task name that way beyond 30 symbol limit", "description": "Task", "deadline": "2020-01-01", - "user": self.user, - "todolist": self.list + "user": self.user.id, + "todolist": self.list.id + })) + self.assertEqual(response.status_code, 400) + + def test_create_task_data_invalid(self): + response = self.client.generic('POST', '/tasks/', json.dumps({ + "title": "Task", + "description": "Task", + "deadline": "May 2020", + "user": self.user.id, + "todolist": self.list.id })) self.assertEqual(response.status_code, 400) @@ -100,6 +138,14 @@ def test_update_task_non_existing(self): })) self.assertEqual(response.status_code, 400) + def test_update_task_data_valid(self): + response = self.client.generic('PUT', f'/tasks/{self.task.id}/', json.dumps({ + "deadline": "May 2021", + "user": self.user.id, + "todolist": self.list.id + })) + self.assertEqual(response.status_code, 400) + def test_update_task_data_invalid(self): response = self.client.generic('PUT', f'/tasks/{self.task.id}/', json.dumps({ "deadline": "May 2021" From 704dec4b62f5115b3e4341924a3a2573fa7ec588 Mon Sep 17 00:00:00 2001 From: AndriiRomaniuk Date: Wed, 17 Mar 2021 19:47:36 +0200 Subject: [PATCH 099/100] added tests for abstract model --- tests/abstract/__init__.py | 0 tests/abstract/tests.py | 18 ++++++++++++++++++ utils/abstract_model.py | 7 ++----- 3 files changed, 20 insertions(+), 5 deletions(-) create mode 100644 tests/abstract/__init__.py create mode 100644 tests/abstract/tests.py diff --git a/tests/abstract/__init__.py b/tests/abstract/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/abstract/tests.py b/tests/abstract/tests.py new file mode 100644 index 0000000..0b4f887 --- /dev/null +++ b/tests/abstract/tests.py @@ -0,0 +1,18 @@ +from django.test import TestCase +from utils.abstract_model import AbstractModel + + +class TaskModelsTest(TestCase): + def test_not_implemented_update(self): + class TestModel(AbstractModel): + class Meta: + abstract = True + + self.assertRaises(NotImplementedError, TestModel.update, 'test') + + def test_not_implemented_create(self): + class TestModel(AbstractModel): + class Meta: + abstract = True + + self.assertRaises(NotImplementedError, TestModel.create, 'test') diff --git a/utils/abstract_model.py b/utils/abstract_model.py index 15bb737..ac9b38e 100644 --- a/utils/abstract_model.py +++ b/utils/abstract_model.py @@ -19,11 +19,8 @@ def get_by_id(cls, pk: int): # pylint: disable=C0103 @classmethod def get_all(cls): - try: - task = cls.objects.all() - return task - except cls.DoesNotExist: - return None + task = cls.objects.all() + return task @classmethod def remove(cls, pk): # pylint: disable=C0103 From 580bad7cc76f81a0262078b35ca3c8520b971293 Mon Sep 17 00:00:00 2001 From: Liubomyr Halamaha Date: Tue, 23 Mar 2021 11:43:22 +0200 Subject: [PATCH 100/100] Update manage.py --- manage.py | 1 + 1 file changed, 1 insertion(+) diff --git a/manage.py b/manage.py index f4d2cb4..9319666 100644 --- a/manage.py +++ b/manage.py @@ -1,5 +1,6 @@ #!/usr/bin/env python """Django's command-line utility for administrative tasks.""" + import os import sys