From 8eddd0b7967dc889100058b4f382d6200e7fd56a Mon Sep 17 00:00:00 2001
From: Brylie Christopher Oxley
Date: Tue, 11 Oct 2022 09:45:51 +0300
Subject: [PATCH 01/63] Add djano-stubs
---
poetry.lock | 18 +++++++++---------
pyproject.toml | 1 +
2 files changed, 10 insertions(+), 9 deletions(-)
diff --git a/poetry.lock b/poetry.lock
index e9cd52ab..162d384c 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -270,7 +270,7 @@ sftp = ["paramiko"]
name = "django-stubs"
version = "1.12.0"
description = "Mypy stubs for Django"
-category = "dev"
+category = "main"
optional = false
python-versions = ">=3.7"
@@ -290,7 +290,7 @@ compatible-mypy = ["mypy (>=0.930,<0.970)"]
name = "django-stubs-ext"
version = "0.5.0"
description = "Monkey-patching and extensions for django-stubs"
-category = "dev"
+category = "main"
optional = false
python-versions = ">=3.6"
@@ -468,7 +468,7 @@ python-versions = ">=3.6"
name = "mypy"
version = "0.981"
description = "Optional static typing for Python"
-category = "dev"
+category = "main"
optional = false
python-versions = ">=3.7"
@@ -486,7 +486,7 @@ reports = ["lxml"]
name = "mypy-extensions"
version = "0.4.3"
description = "Experimental type system extensions for programs checked with the mypy typechecker."
-category = "dev"
+category = "main"
optional = false
python-versions = "*"
@@ -836,7 +836,7 @@ python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*"
name = "tomli"
version = "2.0.1"
description = "A lil' TOML parser"
-category = "dev"
+category = "main"
optional = false
python-versions = ">=3.7"
@@ -855,7 +855,7 @@ test = ["pre-commit", "pytest"]
name = "types-pytz"
version = "2022.2.1.0"
description = "Typing stubs for pytz"
-category = "dev"
+category = "main"
optional = false
python-versions = "*"
@@ -863,7 +863,7 @@ python-versions = "*"
name = "types-PyYAML"
version = "6.0.12"
description = "Typing stubs for PyYAML"
-category = "dev"
+category = "main"
optional = false
python-versions = "*"
@@ -871,7 +871,7 @@ python-versions = "*"
name = "typing-extensions"
version = "4.3.0"
description = "Backported and Experimental Type Hints for Python 3.7+"
-category = "dev"
+category = "main"
optional = false
python-versions = ">=3.7"
@@ -935,7 +935,7 @@ brotli = ["Brotli"]
[metadata]
lock-version = "1.1"
python-versions = "^3.9"
-content-hash = "491a7a2661f59ec87999797bf91b3900e7b0c37584794895e4d51535c4ace9bf"
+content-hash = "03693cb3c7e988a1a02e8fa6d5b559858d4beb0a4ce16b2ea5de803edcaff00c"
[metadata.files]
appnope = [
diff --git a/pyproject.toml b/pyproject.toml
index 5f1aa20c..3e2c72e7 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -19,6 +19,7 @@ django-taggit = "^3.0.0"
Pillow = "^9.2.0"
requests = "^2.28.1"
rich = "^12.5.1"
+django-stubs = "^1.12.0"
[tool.poetry.dev-dependencies]
black = "^22.6.0"
From 9d6f23f94e6c0d2d82916ad529631bda6e4fc442 Mon Sep 17 00:00:00 2001
From: Brylie Christopher Oxley
Date: Tue, 11 Oct 2022 09:47:07 +0300
Subject: [PATCH 02/63] Add isort black compatibility
---
pyproject.toml | 3 +++
1 file changed, 3 insertions(+)
diff --git a/pyproject.toml b/pyproject.toml
index 3e2c72e7..3c358c0c 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -35,6 +35,9 @@ django-debug-toolbar = "^3.7.0"
django-linear-migrations = "^2.5.1"
pre-commit = "^2.20.0"
+[tool.isort]
+profile = "black"
+
[tool.mypy]
plugins = ["mypy_django_plugin.main"]
From 810a1e61606ef1fe54de9e713fb3310c6b85e74e Mon Sep 17 00:00:00 2001
From: Brylie Christopher Oxley
Date: Tue, 11 Oct 2022 09:50:07 +0300
Subject: [PATCH 03/63] Remove django-stubs from main dependencies
---
poetry.lock | 201 +++++++++++++++++++++++++------------------------
pyproject.toml | 3 +-
2 files changed, 102 insertions(+), 102 deletions(-)
diff --git a/poetry.lock b/poetry.lock
index 162d384c..8fa7b923 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -55,11 +55,11 @@ python-versions = "*"
[[package]]
name = "black"
-version = "22.8.0"
+version = "22.10.0"
description = "The uncompromising code formatter."
category = "dev"
optional = false
-python-versions = ">=3.6.2"
+python-versions = ">=3.7"
[package.dependencies]
click = ">=8.0.0"
@@ -270,7 +270,7 @@ sftp = ["paramiko"]
name = "django-stubs"
version = "1.12.0"
description = "Mypy stubs for Django"
-category = "main"
+category = "dev"
optional = false
python-versions = ">=3.7"
@@ -290,7 +290,7 @@ compatible-mypy = ["mypy (>=0.930,<0.970)"]
name = "django-stubs-ext"
version = "0.5.0"
description = "Monkey-patching and extensions for django-stubs"
-category = "main"
+category = "dev"
optional = false
python-versions = ">=3.6"
@@ -323,7 +323,7 @@ pytz = "*"
[[package]]
name = "executing"
-version = "1.1.0"
+version = "1.1.1"
description = "Get the currently executing AST node of a frame, and other information"
category = "dev"
optional = false
@@ -468,7 +468,7 @@ python-versions = ">=3.6"
name = "mypy"
version = "0.981"
description = "Optional static typing for Python"
-category = "main"
+category = "dev"
optional = false
python-versions = ">=3.7"
@@ -486,7 +486,7 @@ reports = ["lxml"]
name = "mypy-extensions"
version = "0.4.3"
description = "Experimental type system extensions for programs checked with the mypy typechecker."
-category = "main"
+category = "dev"
optional = false
python-versions = "*"
@@ -616,7 +616,7 @@ wcwidth = "*"
[[package]]
name = "psycopg2-binary"
-version = "2.9.3"
+version = "2.9.4"
description = "psycopg2 - Python-PostgreSQL Database Adapter"
category = "main"
optional = false
@@ -836,7 +836,7 @@ python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*"
name = "tomli"
version = "2.0.1"
description = "A lil' TOML parser"
-category = "main"
+category = "dev"
optional = false
python-versions = ">=3.7"
@@ -853,9 +853,9 @@ test = ["pre-commit", "pytest"]
[[package]]
name = "types-pytz"
-version = "2022.2.1.0"
+version = "2022.4.0.0"
description = "Typing stubs for pytz"
-category = "main"
+category = "dev"
optional = false
python-versions = "*"
@@ -863,15 +863,15 @@ python-versions = "*"
name = "types-PyYAML"
version = "6.0.12"
description = "Typing stubs for PyYAML"
-category = "main"
+category = "dev"
optional = false
python-versions = "*"
[[package]]
name = "typing-extensions"
-version = "4.3.0"
+version = "4.4.0"
description = "Backported and Experimental Type Hints for Python 3.7+"
-category = "main"
+category = "dev"
optional = false
python-versions = ">=3.7"
@@ -935,7 +935,7 @@ brotli = ["Brotli"]
[metadata]
lock-version = "1.1"
python-versions = "^3.9"
-content-hash = "03693cb3c7e988a1a02e8fa6d5b559858d4beb0a4ce16b2ea5de803edcaff00c"
+content-hash = "491a7a2661f59ec87999797bf91b3900e7b0c37584794895e4d51535c4ace9bf"
[metadata.files]
appnope = [
@@ -959,29 +959,27 @@ backcall = [
{file = "backcall-0.2.0.tar.gz", hash = "sha256:5cbdbf27be5e7cfadb448baf0aa95508f91f2bbc6c6437cd9cd06e2a4c215e1e"},
]
black = [
- {file = "black-22.8.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ce957f1d6b78a8a231b18e0dd2d94a33d2ba738cd88a7fe64f53f659eea49fdd"},
- {file = "black-22.8.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5107ea36b2b61917956d018bd25129baf9ad1125e39324a9b18248d362156a27"},
- {file = "black-22.8.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e8166b7bfe5dcb56d325385bd1d1e0f635f24aae14b3ae437102dedc0c186747"},
- {file = "black-22.8.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd82842bb272297503cbec1a2600b6bfb338dae017186f8f215c8958f8acf869"},
- {file = "black-22.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:d839150f61d09e7217f52917259831fe2b689f5c8e5e32611736351b89bb2a90"},
- {file = "black-22.8.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:a05da0430bd5ced89176db098567973be52ce175a55677436a271102d7eaa3fe"},
- {file = "black-22.8.0-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4a098a69a02596e1f2a58a2a1c8d5a05d5a74461af552b371e82f9fa4ada8342"},
- {file = "black-22.8.0-cp36-cp36m-win_amd64.whl", hash = "sha256:5594efbdc35426e35a7defa1ea1a1cb97c7dbd34c0e49af7fb593a36bd45edab"},
- {file = "black-22.8.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:a983526af1bea1e4cf6768e649990f28ee4f4137266921c2c3cee8116ae42ec3"},
- {file = "black-22.8.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b2c25f8dea5e8444bdc6788a2f543e1fb01494e144480bc17f806178378005e"},
- {file = "black-22.8.0-cp37-cp37m-win_amd64.whl", hash = "sha256:78dd85caaab7c3153054756b9fe8c611efa63d9e7aecfa33e533060cb14b6d16"},
- {file = "black-22.8.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:cea1b2542d4e2c02c332e83150e41e3ca80dc0fb8de20df3c5e98e242156222c"},
- {file = "black-22.8.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:5b879eb439094751185d1cfdca43023bc6786bd3c60372462b6f051efa6281a5"},
- {file = "black-22.8.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:0a12e4e1353819af41df998b02c6742643cfef58282915f781d0e4dd7a200411"},
- {file = "black-22.8.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c3a73f66b6d5ba7288cd5d6dad9b4c9b43f4e8a4b789a94bf5abfb878c663eb3"},
- {file = "black-22.8.0-cp38-cp38-win_amd64.whl", hash = "sha256:e981e20ec152dfb3e77418fb616077937378b322d7b26aa1ff87717fb18b4875"},
- {file = "black-22.8.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:8ce13ffed7e66dda0da3e0b2eb1bdfc83f5812f66e09aca2b0978593ed636b6c"},
- {file = "black-22.8.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:32a4b17f644fc288c6ee2bafdf5e3b045f4eff84693ac069d87b1a347d861497"},
- {file = "black-22.8.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:0ad827325a3a634bae88ae7747db1a395d5ee02cf05d9aa7a9bd77dfb10e940c"},
- {file = "black-22.8.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:53198e28a1fb865e9fe97f88220da2e44df6da82b18833b588b1883b16bb5d41"},
- {file = "black-22.8.0-cp39-cp39-win_amd64.whl", hash = "sha256:bc4d4123830a2d190e9cc42a2e43570f82ace35c3aeb26a512a2102bce5af7ec"},
- {file = "black-22.8.0-py3-none-any.whl", hash = "sha256:d2c21d439b2baf7aa80d6dd4e3659259be64c6f49dfd0f32091063db0e006db4"},
- {file = "black-22.8.0.tar.gz", hash = "sha256:792f7eb540ba9a17e8656538701d3eb1afcb134e3b45b71f20b25c77a8db7e6e"},
+ {file = "black-22.10.0-1fixedarch-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:5cc42ca67989e9c3cf859e84c2bf014f6633db63d1cbdf8fdb666dcd9e77e3fa"},
+ {file = "black-22.10.0-1fixedarch-cp311-cp311-macosx_11_0_x86_64.whl", hash = "sha256:5d8f74030e67087b219b032aa33a919fae8806d49c867846bfacde57f43972ef"},
+ {file = "black-22.10.0-1fixedarch-cp37-cp37m-macosx_10_16_x86_64.whl", hash = "sha256:197df8509263b0b8614e1df1756b1dd41be6738eed2ba9e9769f3880c2b9d7b6"},
+ {file = "black-22.10.0-1fixedarch-cp38-cp38-macosx_10_16_x86_64.whl", hash = "sha256:2644b5d63633702bc2c5f3754b1b475378fbbfb481f62319388235d0cd104c2d"},
+ {file = "black-22.10.0-1fixedarch-cp39-cp39-macosx_11_0_x86_64.whl", hash = "sha256:e41a86c6c650bcecc6633ee3180d80a025db041a8e2398dcc059b3afa8382cd4"},
+ {file = "black-22.10.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2039230db3c6c639bd84efe3292ec7b06e9214a2992cd9beb293d639c6402edb"},
+ {file = "black-22.10.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:14ff67aec0a47c424bc99b71005202045dc09270da44a27848d534600ac64fc7"},
+ {file = "black-22.10.0-cp310-cp310-win_amd64.whl", hash = "sha256:819dc789f4498ecc91438a7de64427c73b45035e2e3680c92e18795a839ebb66"},
+ {file = "black-22.10.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5b9b29da4f564ba8787c119f37d174f2b69cdfdf9015b7d8c5c16121ddc054ae"},
+ {file = "black-22.10.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b8b49776299fece66bffaafe357d929ca9451450f5466e997a7285ab0fe28e3b"},
+ {file = "black-22.10.0-cp311-cp311-win_amd64.whl", hash = "sha256:21199526696b8f09c3997e2b4db8d0b108d801a348414264d2eb8eb2532e540d"},
+ {file = "black-22.10.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1e464456d24e23d11fced2bc8c47ef66d471f845c7b7a42f3bd77bf3d1789650"},
+ {file = "black-22.10.0-cp37-cp37m-win_amd64.whl", hash = "sha256:9311e99228ae10023300ecac05be5a296f60d2fd10fff31cf5c1fa4ca4b1988d"},
+ {file = "black-22.10.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:fba8a281e570adafb79f7755ac8721b6cf1bbf691186a287e990c7929c7692ff"},
+ {file = "black-22.10.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:915ace4ff03fdfff953962fa672d44be269deb2eaf88499a0f8805221bc68c87"},
+ {file = "black-22.10.0-cp38-cp38-win_amd64.whl", hash = "sha256:444ebfb4e441254e87bad00c661fe32df9969b2bf224373a448d8aca2132b395"},
+ {file = "black-22.10.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:974308c58d057a651d182208a484ce80a26dac0caef2895836a92dd6ebd725e0"},
+ {file = "black-22.10.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:72ef3925f30e12a184889aac03d77d031056860ccae8a1e519f6cbb742736383"},
+ {file = "black-22.10.0-cp39-cp39-win_amd64.whl", hash = "sha256:432247333090c8c5366e69627ccb363bc58514ae3e63f7fc75c54b1ea80fa7de"},
+ {file = "black-22.10.0-py3-none-any.whl", hash = "sha256:c957b2b4ea88587b46cf49d1dc17681c1e672864fd7af32fc1e9664d572b3458"},
+ {file = "black-22.10.0.tar.gz", hash = "sha256:f513588da599943e0cde4e32cc9879e825d58720d6557062d1098c5ad80080e1"},
]
certifi = [
{file = "certifi-2022.9.24-py3-none-any.whl", hash = "sha256:90c1a32f1d68f940488354e36370f6cca89f0f106db09518524c88d6ed83f382"},
@@ -1116,8 +1114,8 @@ djangorestframework = [
{file = "djangorestframework-3.14.0.tar.gz", hash = "sha256:579a333e6256b09489cbe0a067e66abe55c6595d8926be6b99423786334350c8"},
]
executing = [
- {file = "executing-1.1.0-py2.py3-none-any.whl", hash = "sha256:4a6d96ba89eb3dcc11483471061b42b9006d8c9f81c584dd04246944cd022530"},
- {file = "executing-1.1.0.tar.gz", hash = "sha256:2c2c07d1ec4b2d8f9676b25170f1d8445c0ee2eb78901afb075a4b8d83608c6a"},
+ {file = "executing-1.1.1-py2.py3-none-any.whl", hash = "sha256:236ea5f059a38781714a8bfba46a70fad3479c2f552abee3bbafadc57ed111b8"},
+ {file = "executing-1.1.1.tar.gz", hash = "sha256:b0d7f8dcc2bac47ce6e39374397e7acecea6fdc380a6d5323e26185d70f38ea8"},
]
filelock = [
{file = "filelock-3.8.0-py3-none-any.whl", hash = "sha256:617eb4e5eedc82fc5f47b6d61e4d11cb837c56cb4544e39081099fa17ad109d4"},
@@ -1290,62 +1288,65 @@ prompt-toolkit = [
{file = "prompt_toolkit-3.0.31.tar.gz", hash = "sha256:9ada952c9d1787f52ff6d5f3484d0b4df8952787c087edf6a1f7c2cb1ea88148"},
]
psycopg2-binary = [
- {file = "psycopg2-binary-2.9.3.tar.gz", hash = "sha256:761df5313dc15da1502b21453642d7599d26be88bff659382f8f9747c7ebea4e"},
- {file = "psycopg2_binary-2.9.3-cp310-cp310-macosx_10_14_x86_64.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:539b28661b71da7c0e428692438efbcd048ca21ea81af618d845e06ebfd29478"},
- {file = "psycopg2_binary-2.9.3-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6e82d38390a03da28c7985b394ec3f56873174e2c88130e6966cb1c946508e65"},
- {file = "psycopg2_binary-2.9.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:57804fc02ca3ce0dbfbef35c4b3a4a774da66d66ea20f4bda601294ad2ea6092"},
- {file = "psycopg2_binary-2.9.3-cp310-cp310-manylinux_2_24_aarch64.whl", hash = "sha256:083a55275f09a62b8ca4902dd11f4b33075b743cf0d360419e2051a8a5d5ff76"},
- {file = "psycopg2_binary-2.9.3-cp310-cp310-manylinux_2_24_ppc64le.whl", hash = "sha256:0a29729145aaaf1ad8bafe663131890e2111f13416b60e460dae0a96af5905c9"},
- {file = "psycopg2_binary-2.9.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:3a79d622f5206d695d7824cbf609a4f5b88ea6d6dab5f7c147fc6d333a8787e4"},
- {file = "psycopg2_binary-2.9.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:090f3348c0ab2cceb6dfbe6bf721ef61262ddf518cd6cc6ecc7d334996d64efa"},
- {file = "psycopg2_binary-2.9.3-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:a9e1f75f96ea388fbcef36c70640c4efbe4650658f3d6a2967b4cc70e907352e"},
- {file = "psycopg2_binary-2.9.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c3ae8e75eb7160851e59adc77b3a19a976e50622e44fd4fd47b8b18208189d42"},
- {file = "psycopg2_binary-2.9.3-cp310-cp310-win32.whl", hash = "sha256:7b1e9b80afca7b7a386ef087db614faebbf8839b7f4db5eb107d0f1a53225029"},
- {file = "psycopg2_binary-2.9.3-cp310-cp310-win_amd64.whl", hash = "sha256:8b344adbb9a862de0c635f4f0425b7958bf5a4b927c8594e6e8d261775796d53"},
- {file = "psycopg2_binary-2.9.3-cp36-cp36m-macosx_10_14_x86_64.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:e847774f8ffd5b398a75bc1c18fbb56564cda3d629fe68fd81971fece2d3c67e"},
- {file = "psycopg2_binary-2.9.3-cp36-cp36m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:68641a34023d306be959101b345732360fc2ea4938982309b786f7be1b43a4a1"},
- {file = "psycopg2_binary-2.9.3-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3303f8807f342641851578ee7ed1f3efc9802d00a6f83c101d21c608cb864460"},
- {file = "psycopg2_binary-2.9.3-cp36-cp36m-manylinux_2_24_aarch64.whl", hash = "sha256:e3699852e22aa68c10de06524a3721ade969abf382da95884e6a10ff798f9281"},
- {file = "psycopg2_binary-2.9.3-cp36-cp36m-manylinux_2_24_ppc64le.whl", hash = "sha256:526ea0378246d9b080148f2d6681229f4b5964543c170dd10bf4faaab6e0d27f"},
- {file = "psycopg2_binary-2.9.3-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:b1c8068513f5b158cf7e29c43a77eb34b407db29aca749d3eb9293ee0d3103ca"},
- {file = "psycopg2_binary-2.9.3-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:15803fa813ea05bef089fa78835118b5434204f3a17cb9f1e5dbfd0b9deea5af"},
- {file = "psycopg2_binary-2.9.3-cp36-cp36m-musllinux_1_1_ppc64le.whl", hash = "sha256:152f09f57417b831418304c7f30d727dc83a12761627bb826951692cc6491e57"},
- {file = "psycopg2_binary-2.9.3-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:404224e5fef3b193f892abdbf8961ce20e0b6642886cfe1fe1923f41aaa75c9d"},
- {file = "psycopg2_binary-2.9.3-cp36-cp36m-win32.whl", hash = "sha256:1f6b813106a3abdf7b03640d36e24669234120c72e91d5cbaeb87c5f7c36c65b"},
- {file = "psycopg2_binary-2.9.3-cp36-cp36m-win_amd64.whl", hash = "sha256:2d872e3c9d5d075a2e104540965a1cf898b52274a5923936e5bfddb58c59c7c2"},
- {file = "psycopg2_binary-2.9.3-cp37-cp37m-macosx_10_14_x86_64.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:10bb90fb4d523a2aa67773d4ff2b833ec00857f5912bafcfd5f5414e45280fb1"},
- {file = "psycopg2_binary-2.9.3-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:874a52ecab70af13e899f7847b3e074eeb16ebac5615665db33bce8a1009cf33"},
- {file = "psycopg2_binary-2.9.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a29b3ca4ec9defec6d42bf5feb36bb5817ba3c0230dd83b4edf4bf02684cd0ae"},
- {file = "psycopg2_binary-2.9.3-cp37-cp37m-manylinux_2_24_aarch64.whl", hash = "sha256:12b11322ea00ad8db8c46f18b7dfc47ae215e4df55b46c67a94b4effbaec7094"},
- {file = "psycopg2_binary-2.9.3-cp37-cp37m-manylinux_2_24_ppc64le.whl", hash = "sha256:53293533fcbb94c202b7c800a12c873cfe24599656b341f56e71dd2b557be063"},
- {file = "psycopg2_binary-2.9.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:c381bda330ddf2fccbafab789d83ebc6c53db126e4383e73794c74eedce855ef"},
- {file = "psycopg2_binary-2.9.3-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:9d29409b625a143649d03d0fd7b57e4b92e0ecad9726ba682244b73be91d2fdb"},
- {file = "psycopg2_binary-2.9.3-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:183a517a3a63503f70f808b58bfbf962f23d73b6dccddae5aa56152ef2bcb232"},
- {file = "psycopg2_binary-2.9.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:15c4e4cfa45f5a60599d9cec5f46cd7b1b29d86a6390ec23e8eebaae84e64554"},
- {file = "psycopg2_binary-2.9.3-cp37-cp37m-win32.whl", hash = "sha256:adf20d9a67e0b6393eac162eb81fb10bc9130a80540f4df7e7355c2dd4af9fba"},
- {file = "psycopg2_binary-2.9.3-cp37-cp37m-win_amd64.whl", hash = "sha256:2f9ffd643bc7349eeb664eba8864d9e01f057880f510e4681ba40a6532f93c71"},
- {file = "psycopg2_binary-2.9.3-cp38-cp38-macosx_10_14_x86_64.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:def68d7c21984b0f8218e8a15d514f714d96904265164f75f8d3a70f9c295667"},
- {file = "psycopg2_binary-2.9.3-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dffc08ca91c9ac09008870c9eb77b00a46b3378719584059c034b8945e26b272"},
- {file = "psycopg2_binary-2.9.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:280b0bb5cbfe8039205c7981cceb006156a675362a00fe29b16fbc264e242834"},
- {file = "psycopg2_binary-2.9.3-cp38-cp38-manylinux_2_24_aarch64.whl", hash = "sha256:af9813db73395fb1fc211bac696faea4ca9ef53f32dc0cfa27e4e7cf766dcf24"},
- {file = "psycopg2_binary-2.9.3-cp38-cp38-manylinux_2_24_ppc64le.whl", hash = "sha256:63638d875be8c2784cfc952c9ac34e2b50e43f9f0a0660b65e2a87d656b3116c"},
- {file = "psycopg2_binary-2.9.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:ffb7a888a047696e7f8240d649b43fb3644f14f0ee229077e7f6b9f9081635bd"},
- {file = "psycopg2_binary-2.9.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:0c9d5450c566c80c396b7402895c4369a410cab5a82707b11aee1e624da7d004"},
- {file = "psycopg2_binary-2.9.3-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:d1c1b569ecafe3a69380a94e6ae09a4789bbb23666f3d3a08d06bbd2451f5ef1"},
- {file = "psycopg2_binary-2.9.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8fc53f9af09426a61db9ba357865c77f26076d48669f2e1bb24d85a22fb52307"},
- {file = "psycopg2_binary-2.9.3-cp38-cp38-win32.whl", hash = "sha256:6472a178e291b59e7f16ab49ec8b4f3bdada0a879c68d3817ff0963e722a82ce"},
- {file = "psycopg2_binary-2.9.3-cp38-cp38-win_amd64.whl", hash = "sha256:35168209c9d51b145e459e05c31a9eaeffa9a6b0fd61689b48e07464ffd1a83e"},
- {file = "psycopg2_binary-2.9.3-cp39-cp39-macosx_10_14_x86_64.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:47133f3f872faf28c1e87d4357220e809dfd3fa7c64295a4a148bcd1e6e34ec9"},
- {file = "psycopg2_binary-2.9.3-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:91920527dea30175cc02a1099f331aa8c1ba39bf8b7762b7b56cbf54bc5cce42"},
- {file = "psycopg2_binary-2.9.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:887dd9aac71765ac0d0bac1d0d4b4f2c99d5f5c1382d8b770404f0f3d0ce8a39"},
- {file = "psycopg2_binary-2.9.3-cp39-cp39-manylinux_2_24_aarch64.whl", hash = "sha256:1f14c8b0942714eb3c74e1e71700cbbcb415acbc311c730370e70c578a44a25c"},
- {file = "psycopg2_binary-2.9.3-cp39-cp39-manylinux_2_24_ppc64le.whl", hash = "sha256:7af0dd86ddb2f8af5da57a976d27cd2cd15510518d582b478fbb2292428710b4"},
- {file = "psycopg2_binary-2.9.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:93cd1967a18aa0edd4b95b1dfd554cf15af657cb606280996d393dadc88c3c35"},
- {file = "psycopg2_binary-2.9.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:bda845b664bb6c91446ca9609fc69f7db6c334ec5e4adc87571c34e4f47b7ddb"},
- {file = "psycopg2_binary-2.9.3-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:01310cf4cf26db9aea5158c217caa92d291f0500051a6469ac52166e1a16f5b7"},
- {file = "psycopg2_binary-2.9.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:99485cab9ba0fa9b84f1f9e1fef106f44a46ef6afdeec8885e0b88d0772b49e8"},
- {file = "psycopg2_binary-2.9.3-cp39-cp39-win32.whl", hash = "sha256:46f0e0a6b5fa5851bbd9ab1bc805eef362d3a230fbdfbc209f4a236d0a7a990d"},
- {file = "psycopg2_binary-2.9.3-cp39-cp39-win_amd64.whl", hash = "sha256:accfe7e982411da3178ec690baaceaad3c278652998b2c45828aaac66cd8285f"},
+ {file = "psycopg2-binary-2.9.4.tar.gz", hash = "sha256:a6a2d3d75d8698dee492f4af7ad07606d0734e581edf9e2ce2f74b6fce90f42e"},
+ {file = "psycopg2_binary-2.9.4-cp310-cp310-macosx_10_15_x86_64.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:e72491d72870c3cb2f0d6f4174485533caec0e9ed7e717e2859b7cc7ff2ae1c4"},
+ {file = "psycopg2_binary-2.9.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2903bf90b1e6bfc9bbfc94a1db0b50ffa9830a0ca4c042fbc38d93890c02ce08"},
+ {file = "psycopg2_binary-2.9.4-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:15e0ac0ed8a85f6049e836e95ddee627766561c85be8d23f4b3edb6ddbaa7310"},
+ {file = "psycopg2_binary-2.9.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:edf0a66ce9517365c7dcfed597894d8dd1f27b59e550b77a089054101435213b"},
+ {file = "psycopg2_binary-2.9.4-cp310-cp310-manylinux_2_24_aarch64.whl", hash = "sha256:61c6a258469c66412ae8358a0501df6ccb3bb48aa9c43b56624571ff9767f91d"},
+ {file = "psycopg2_binary-2.9.4-cp310-cp310-manylinux_2_24_ppc64le.whl", hash = "sha256:704f1fcdc5b606b70563ea696c69bda90caee3a2f45ffc9cee60a901b394a79f"},
+ {file = "psycopg2_binary-2.9.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:30200b07779446760813eef06098ec6d084131e4365b4e023eb43100de758b11"},
+ {file = "psycopg2_binary-2.9.4-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:f5fbb3b325c65010e04af206a9243e2df8606736c510c7f268aca6a93e5294a9"},
+ {file = "psycopg2_binary-2.9.4-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:52383e932e6de5595963f9178cf2af7b9e1f3daacf5135b9c0e21aabbc5bf7c4"},
+ {file = "psycopg2_binary-2.9.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:0d8e0c9eec79fe1ae66691e06e3cc714da6fbd77981209bf32fa823c03dbaff8"},
+ {file = "psycopg2_binary-2.9.4-cp310-cp310-win32.whl", hash = "sha256:161dc52a617f0bb610a87d391cb2e77fe65b89ebfbd752f4f3217dde701ea196"},
+ {file = "psycopg2_binary-2.9.4-cp310-cp310-win_amd64.whl", hash = "sha256:33ac8b4754e6b6b21f3ee180da169d8526d91aee9408ec1fc573c16ab32b0207"},
+ {file = "psycopg2_binary-2.9.4-cp36-cp36m-macosx_10_14_x86_64.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:7751b11cd7f6b952b4b5ec5b93b5be9ce20faba786c18c25c354f5d8717a173c"},
+ {file = "psycopg2_binary-2.9.4-cp36-cp36m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b216a15e13f6e763db40ac3beb74b588650bc030d10a78fde182b88d273b82b5"},
+ {file = "psycopg2_binary-2.9.4-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0eae72190be519bf2629062eab7ac8d4ceec5bd132953cefa1596584d86964fe"},
+ {file = "psycopg2_binary-2.9.4-cp36-cp36m-manylinux_2_24_aarch64.whl", hash = "sha256:fb639a0e65dce4a9cccbcbdd8ddd0c8c6ab10bca317b827a5c52ac3c3a4ad60a"},
+ {file = "psycopg2_binary-2.9.4-cp36-cp36m-manylinux_2_24_ppc64le.whl", hash = "sha256:80ed219ce6cb21a5b53ead0edf5b56b6d23de4cb95389ac606f47670474f4816"},
+ {file = "psycopg2_binary-2.9.4-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:f78cafa25731e0b5aa16fe20bea1abf643d4e853f6bfb8a64421b06b878e2b88"},
+ {file = "psycopg2_binary-2.9.4-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:34fd249275faa782c3a2016e86ac2330636ac58d731a1580e7d686e3976b9536"},
+ {file = "psycopg2_binary-2.9.4-cp36-cp36m-musllinux_1_1_ppc64le.whl", hash = "sha256:24d627ed69e754c48dd142a914124858c600b4108c92546eb0ba822e63c0c6e2"},
+ {file = "psycopg2_binary-2.9.4-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:65d5f4e70a2d3fbaa1349236968792611088f3f2dccead36c1626e1d183cc327"},
+ {file = "psycopg2_binary-2.9.4-cp36-cp36m-win32.whl", hash = "sha256:ae5b41dbf7731b838021923edfbe3b5ccdec84d92d5795f5229c0d08d32509d9"},
+ {file = "psycopg2_binary-2.9.4-cp36-cp36m-win_amd64.whl", hash = "sha256:97e4f3d9b17d12e7c00cb1c29c0040044135cd5146838da4274615dbe0baae78"},
+ {file = "psycopg2_binary-2.9.4-cp37-cp37m-macosx_10_15_x86_64.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:8660112e9127a019969a23c878e1b4a419e8a6427f9a9050c19830f152628c8a"},
+ {file = "psycopg2_binary-2.9.4-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ea8d5cd689fa7225d81ae0a049ba03e0165f4ed9ca083b19a405be9ad0b36845"},
+ {file = "psycopg2_binary-2.9.4-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:63edc507f8cbfbb5903adb75bad8a99f9798981c854df9119dbebab2ec3ee0e1"},
+ {file = "psycopg2_binary-2.9.4-cp37-cp37m-manylinux_2_24_aarch64.whl", hash = "sha256:25e0517ad7ee3c5c3c69dbe3c1d95504c811e42f452b39a3505d0763b1f6caa0"},
+ {file = "psycopg2_binary-2.9.4-cp37-cp37m-manylinux_2_24_ppc64le.whl", hash = "sha256:0a9465f0aa36480c8e7614991cbe8ca8aa16b0517c5398a49648ce345e446c19"},
+ {file = "psycopg2_binary-2.9.4-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:aff258af03dda9a990960a53759d10c3a9b936837c71fe2f3b581acd356b9121"},
+ {file = "psycopg2_binary-2.9.4-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:576b9dfbcd154a0e8b5d9dae6316d037450e64a3b31df87dec71d88e2a2d5e5f"},
+ {file = "psycopg2_binary-2.9.4-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:044b6ab68613de7ea1e63856627deea091bfea09dea5ab4f050b13250fd18cab"},
+ {file = "psycopg2_binary-2.9.4-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:7b47643c45e7619788c081d42e1d9d98c7c8a4933010a9967d097cc3c4c29f41"},
+ {file = "psycopg2_binary-2.9.4-cp37-cp37m-win32.whl", hash = "sha256:82df4a8600999c4c0cb7d6614df1bbdb3c74732f63e79f78487893ffbed3d083"},
+ {file = "psycopg2_binary-2.9.4-cp37-cp37m-win_amd64.whl", hash = "sha256:8d7bc25729bb6d96b44f49ad78fde0e27a1a867cb205322b7e5f5b49e04d6f1f"},
+ {file = "psycopg2_binary-2.9.4-cp38-cp38-macosx_10_15_x86_64.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:2f1ded23d17af0d738e7e78087f0b88a53228887845b1989b03af4dfd3fef703"},
+ {file = "psycopg2_binary-2.9.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:f225784812b2b57d340f2eb0d2cebef989dcc82c288f5553e28ee9767c7c8344"},
+ {file = "psycopg2_binary-2.9.4-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:89a86c2b35460700d04b4d6461153ab39ee85af5a5385acac9563a8310e6320a"},
+ {file = "psycopg2_binary-2.9.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:59a3010d566a48b919490a982f6807f68842686941dc12d568e129d9cd7703d6"},
+ {file = "psycopg2_binary-2.9.4-cp38-cp38-manylinux_2_24_aarch64.whl", hash = "sha256:02cde837df012fa5d579b9cf4bc8e1feb460f38d61f7a4ab4a919d55a9f6eeef"},
+ {file = "psycopg2_binary-2.9.4-cp38-cp38-manylinux_2_24_ppc64le.whl", hash = "sha256:226f11be577b70a57f4910c0ee28591d4d9fcb3d455e966267179156ae2e0c41"},
+ {file = "psycopg2_binary-2.9.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:181ac372a5a5308b4076933601a9b5f0cd139b389b0aa5e164786a2abbcdb978"},
+ {file = "psycopg2_binary-2.9.4-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:d5f27b1d1b56470385faa2b2636fcb823e7ac5b5b734e0aa76b14637c66eb3b7"},
+ {file = "psycopg2_binary-2.9.4-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:55137faec669c4277c5687c6ce7c1fbc4dece0e2f14256ee808f4a652f0a2170"},
+ {file = "psycopg2_binary-2.9.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:ffb2f288f577a748cc23c65a818290755a4c2da1f87a40d7055b61a096d31e20"},
+ {file = "psycopg2_binary-2.9.4-cp38-cp38-win32.whl", hash = "sha256:451550e0bb5889bbabbf92575a6d6eafced941cc28c86be6ae4667f81bf32d67"},
+ {file = "psycopg2_binary-2.9.4-cp38-cp38-win_amd64.whl", hash = "sha256:b23b25b1243576b952689966205ef7d4285688068b966a1ca0e620bcb390d483"},
+ {file = "psycopg2_binary-2.9.4-cp39-cp39-macosx_10_15_x86_64.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:7ad9d032dc1a31a86ca7b059f43554a049a2bfda8fe32d1492ad25f6686aff03"},
+ {file = "psycopg2_binary-2.9.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:7b01d07006a0ac2216921b69a220b9f0974345d0b1b36efaeabdc7550b1cc4f8"},
+ {file = "psycopg2_binary-2.9.4-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eb5341fc7c53fdd95ac2415be77b1de854ab266488cff71174ebb007baf0e675"},
+ {file = "psycopg2_binary-2.9.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a431deb6ffdfa551f7400b3a94fa4b964837e67f49e3c37aa26d90dc75970816"},
+ {file = "psycopg2_binary-2.9.4-cp39-cp39-manylinux_2_24_aarch64.whl", hash = "sha256:d6ba33f39436191ece7ea2b3d0b4dff00af71acd5c6e6f1d6b7563aa7286e9f2"},
+ {file = "psycopg2_binary-2.9.4-cp39-cp39-manylinux_2_24_ppc64le.whl", hash = "sha256:d6c5e1df6f427d7a82606cf8f07cf3ba9fb3f366804b01e65f1f00f8df6b54f1"},
+ {file = "psycopg2_binary-2.9.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:1c22c59ab7d9dc110d409445f111f58556bf699b0548f3fc5176684a29c629c4"},
+ {file = "psycopg2_binary-2.9.4-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:b896637091cde69d170a89253dde9aee814b25ca204b7e213fd0a6462e666638"},
+ {file = "psycopg2_binary-2.9.4-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:2535f44b00f26f6af0e949c825e6aecb9adcb56c965c17af5b97137fb69f00c0"},
+ {file = "psycopg2_binary-2.9.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:6a1618260a112a9c93504511f0b6254b4402a8c41b7130dc6d4c9e39aff3aa0c"},
+ {file = "psycopg2_binary-2.9.4-cp39-cp39-win32.whl", hash = "sha256:e02f77b620ad6b36564fe41980865436912e21a3b1138cdde175cf24afde1bc5"},
+ {file = "psycopg2_binary-2.9.4-cp39-cp39-win_amd64.whl", hash = "sha256:44f5dc9b4384bafca8429759ce76c8960ffc2b583fcad9e5dfb3e5f4894269e4"},
]
ptyprocess = [
{file = "ptyprocess-0.7.0-py2.py3-none-any.whl", hash = "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35"},
@@ -1470,16 +1471,16 @@ traitlets = [
{file = "traitlets-5.4.0.tar.gz", hash = "sha256:3f2c4e435e271592fe4390f1746ea56836e3a080f84e7833f0f801d9613fec39"},
]
types-pytz = [
- {file = "types-pytz-2022.2.1.0.tar.gz", hash = "sha256:47cfb19c52b9f75896440541db392fd312a35b279c6307a531db71152ea63e2b"},
- {file = "types_pytz-2022.2.1.0-py3-none-any.whl", hash = "sha256:50ead2254b524a3d4153bc65d00289b66898060d2938e586170dce918dbaf3b3"},
+ {file = "types-pytz-2022.4.0.0.tar.gz", hash = "sha256:17d66e4b16e80ceae0787726f3a22288df7d3f9fdebeb091dc64b92c0e4ea09d"},
+ {file = "types_pytz-2022.4.0.0-py3-none-any.whl", hash = "sha256:950b0f3d64ed5b03a3e29c1e38fe2be8371c933c8e97922d0352345336eb8af4"},
]
types-PyYAML = [
{file = "types-PyYAML-6.0.12.tar.gz", hash = "sha256:f6f350418125872f3f0409d96a62a5a5ceb45231af5cc07ee0034ec48a3c82fa"},
{file = "types_PyYAML-6.0.12-py3-none-any.whl", hash = "sha256:29228db9f82df4f1b7febee06bbfb601677882e98a3da98132e31c6874163e15"},
]
typing-extensions = [
- {file = "typing_extensions-4.3.0-py3-none-any.whl", hash = "sha256:25642c956049920a5aa49edcdd6ab1e06d7e5d467fc00e0506c44ac86fbfca02"},
- {file = "typing_extensions-4.3.0.tar.gz", hash = "sha256:e6d2677a32f47fc7eb2795db1dd15c1f34eff616bcaf2cfb5e997f854fa1c4a6"},
+ {file = "typing_extensions-4.4.0-py3-none-any.whl", hash = "sha256:16fa4864408f655d35ec496218b85f79b3437c829e93320c7c9215ccfd92489e"},
+ {file = "typing_extensions-4.4.0.tar.gz", hash = "sha256:1511434bb92bf8dd198c12b1cc812e800d4181cfcb867674e0f8279cc93087aa"},
]
tzdata = [
{file = "tzdata-2022.4-py2.py3-none-any.whl", hash = "sha256:74da81ecf2b3887c94e53fc1d466d4362aaf8b26fc87cda18f22004544694583"},
diff --git a/pyproject.toml b/pyproject.toml
index 3c358c0c..5d596751 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -19,7 +19,6 @@ django-taggit = "^3.0.0"
Pillow = "^9.2.0"
requests = "^2.28.1"
rich = "^12.5.1"
-django-stubs = "^1.12.0"
[tool.poetry.dev-dependencies]
black = "^22.6.0"
@@ -42,7 +41,7 @@ profile = "black"
plugins = ["mypy_django_plugin.main"]
[tool.django-stubs]
-django_settings_module = "myproject.settings"
+django_settings_module = "project.core.settings"
[build-system]
requires = ["poetry-core>=1.0.0"]
From f4119c7ec9df1625583644164d56bcdeace19601 Mon Sep 17 00:00:00 2001
From: Brylie Christopher Oxley
Date: Tue, 11 Oct 2022 09:54:29 +0300
Subject: [PATCH 04/63] Add django-stubs to mypy config
---
.pre-commit-config.yaml | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 51948868..62c58f99 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -39,8 +39,9 @@ repos:
- id: curlylint
- repo: https://github.com/pre-commit/mirrors-mypy
- rev: v0.981
+ rev: v0.981
hooks:
- id: mypy
args: [--no-strict-optional, --ignore-missing-imports]
-
+ additional_dependencies:
+ - django-stubs
From 1e805e83cfef2df603087bf2f8ea197557f3ceb1 Mon Sep 17 00:00:00 2001
From: Brylie Christopher Oxley
Date: Tue, 11 Oct 2022 09:56:07 +0300
Subject: [PATCH 05/63] Add django-extensions to mypy config
---
.pre-commit-config.yaml | 1 +
1 file changed, 1 insertion(+)
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 62c58f99..7a60b03d 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -45,3 +45,4 @@ repos:
args: [--no-strict-optional, --ignore-missing-imports]
additional_dependencies:
- django-stubs
+ - django-extensions
From ddcec746aab2308fd9226d7d860b54c237d3ec6a Mon Sep 17 00:00:00 2001
From: Brylie Christopher Oxley
Date: Tue, 11 Oct 2022 09:57:15 +0300
Subject: [PATCH 06/63] Remove mypy pre-commit
---
.pre-commit-config.yaml | 9 ---------
1 file changed, 9 deletions(-)
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 7a60b03d..167d70fd 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -37,12 +37,3 @@ repos:
rev: v0.13.1
hooks:
- id: curlylint
-
- - repo: https://github.com/pre-commit/mirrors-mypy
- rev: v0.981
- hooks:
- - id: mypy
- args: [--no-strict-optional, --ignore-missing-imports]
- additional_dependencies:
- - django-stubs
- - django-extensions
From b63bdd27fd71b2fc4dda24fd3be0eca8b1b7da34 Mon Sep 17 00:00:00 2001
From: Brylie Christopher Oxley
Date: Tue, 11 Oct 2022 09:57:21 +0300
Subject: [PATCH 07/63] Disable unused API endpoints (TODO: remove)
---
project/accounts/api.py | 216 +++++++++++++++++------------------
project/accounts/urls/api.py | 10 +-
2 files changed, 109 insertions(+), 117 deletions(-)
diff --git a/project/accounts/api.py b/project/accounts/api.py
index 74103420..4f693033 100644
--- a/project/accounts/api.py
+++ b/project/accounts/api.py
@@ -1,36 +1,28 @@
+from accounts.forms import UpdateProfileImage
+from accounts.models import Profile
+from accounts.permissions import IsProfileOwnerOrDuringRegistrationOrReadOnly
+from accounts.serializers import ProfileListSerializer, ProfileSerializer
+from accounts.utils import get_account
+from categories.models import Category
+from categories.serializers import CategorySerializer
+from core.custom_decorators import require_post_params
from django.contrib.auth import get_user_model
from django.contrib.auth.decorators import login_required
-from django.shortcuts import get_object_or_404
from django.http import (
- JsonResponse,
- HttpResponse,
- HttpResponseServerError,
- HttpResponseForbidden,
HttpResponseBadRequest,
+ HttpResponseForbidden,
+ HttpResponseServerError,
+ JsonResponse,
)
-
-from rest_framework.viewsets import ModelViewSet
-from rest_framework.permissions import IsAuthenticated
+from django.shortcuts import get_object_or_404
+from notifications.signals import notify
from rest_framework.decorators import action, api_view
+from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
-
-from notifications.signals import notify
-
-from accounts.permissions import IsProfileOwnerOrDuringRegistrationOrReadOnly
-from accounts.serializers import (
- ProfileSerializer,
- ProfileListSerializer,
- UserSerializer,
-)
-from accounts.forms import UpdateProfileImage
-from accounts.utils import get_account
-from threads.models import Thread, Civi, Activity
-from accounts.models import Profile
-from categories.models import Category
+from rest_framework.viewsets import ModelViewSet
+from threads.models import Activity, Civi, Thread
+from threads.serializers import CiviSerializer, ThreadSerializer
from threads.utils import json_response
-from threads.serializers import ThreadSerializer, CiviSerializer
-from categories.serializers import CategorySerializer
-from core.custom_decorators import require_post_params
class ProfileViewSet(ModelViewSet):
@@ -147,20 +139,20 @@ def get_permissions(self):
return super(ProfileViewSet, self).get_permissions()
-@api_view(["GET"])
-def get_user(request, username):
- """
- USAGE:
- This is used to get a user
- """
+# @api_view(["GET"])
+# def get_user(request, username):
+# """
+# USAGE:
+# This is used to get a user
+# """
- try:
- user = get_user_model().objects.get(username=username)
- return JsonResponse(UserSerializer(user).data)
- except get_user_model().DoesNotExist:
- return JsonResponse(
- {"error": f"User with username {username} not found"}, status=400
- )
+# try:
+# user = get_user_model().objects.get(username=username)
+# return JsonResponse(UserSerializer(user).data)
+# except get_user_model().DoesNotExist:
+# return JsonResponse(
+# {"error": f"User with username {username} not found"}, status=400
+# )
@api_view(["GET"])
@@ -221,26 +213,26 @@ def get_profile(request, username):
)
-@api_view(["GET"])
-def get_card(request, username):
- """
- USAGE:
- This is used to get a card
- """
-
- try:
- user = get_user_model().objects.get(username=username)
- profile = user.profile
- result = Profile.objects.card_summarize(
- profile, Profile.objects.get(user=request.user)
- )
- return JsonResponse(result)
- except get_user_model().DoesNotExist:
- return JsonResponse(
- {"error": f"User with username {username} not found"}, status=400
- )
- except Exception as e:
- return HttpResponseBadRequest(reason=str(e))
+# @api_view(["GET"])
+# def get_card(request, username):
+# """
+# USAGE:
+# This is used to get a card
+# """
+
+# try:
+# user = get_user_model().objects.get(username=username)
+# profile = user.profile
+# result = Profile.objects.card_summarize(
+# profile, Profile.objects.get(user=request.user)
+# )
+# return JsonResponse(result)
+# except get_user_model().DoesNotExist:
+# return JsonResponse(
+# {"error": f"User with username {username} not found"}, status=400
+# )
+# except Exception as e:
+# return HttpResponseBadRequest(reason=str(e))
def get_feed(request):
@@ -326,27 +318,27 @@ def upload_profile_image(request):
return HttpResponseForbidden("allowed only via POST")
-@login_required
-def clear_profile_image(request):
- """This function is used to delete a profile image"""
+# @login_required
+# def clear_profile_image(request):
+# """This function is used to delete a profile image"""
- if request.method == "POST":
- try:
- account = Profile.objects.get(user=request.user)
+# if request.method == "POST":
+# try:
+# account = Profile.objects.get(user=request.user)
- # Clean up previous image
- account.profile_image.delete()
- account.save()
+# # Clean up previous image
+# account.profile_image.delete()
+# account.save()
- return HttpResponse("Image Deleted")
- except get_user_model().DoesNotExist:
- return HttpResponseServerError(
- reason=f"Profile with id:{request.user.username} does not exist"
- )
- except Exception:
- return HttpResponseServerError(reason=str("default"))
- else:
- return HttpResponseForbidden("allowed only via POST")
+# return HttpResponse("Image Deleted")
+# except get_user_model().DoesNotExist:
+# return HttpResponseServerError(
+# reason=f"Profile with id:{request.user.username} does not exist"
+# )
+# except Exception:
+# return HttpResponseServerError(reason=str("default"))
+# else:
+# return HttpResponseForbidden("allowed only via POST")
@login_required
@@ -463,39 +455,39 @@ def edit_user_categories(request):
return HttpResponseServerError(reason=str(e))
-@login_required
-def delete_user(request):
- """
- Delete User Information
- """
- try:
- # Get current user
- user = get_user_model().objects.get(username=request.user.username)
- profile = Profile.objects.get(user=user)
- # Expunge personally identifiable data in user obj, feel free to change
- data = {
- "is_active": False,
- "email": "",
- "first_name": "",
- "last_name": "",
- "username": "[Deleted-" + str(user.id) + "]",
- }
- user.__dict__.update(data)
- user.save() # Update into database
-
- data = { # Expunge personally identifiable data in profile obj
- "first_name": "",
- "last_name": "",
- "about_me": "",
- }
- profile.__dict__.update(data)
- profile.save()
- except get_user_model().DoesNotExist as e:
- return HttpResponseBadRequest(reason=str(e))
- except Exception as e:
- return HttpResponseServerError(reason=str(e))
-
- user.refresh_from_db() # Make update viewable
- profile.refresh_from_db()
-
- return JsonResponse({"result": "User successfully deleted."})
+# @login_required
+# def delete_user(request):
+# """
+# Delete User Information
+# """
+# try:
+# # Get current user
+# user = get_user_model().objects.get(username=request.user.username)
+# profile = Profile.objects.get(user=user)
+# # Expunge personally identifiable data in user obj, feel free to change
+# data = {
+# "is_active": False,
+# "email": "",
+# "first_name": "",
+# "last_name": "",
+# "username": "[Deleted-" + str(user.id) + "]",
+# }
+# user.__dict__.update(data)
+# user.save() # Update into database
+
+# data = { # Expunge personally identifiable data in profile obj
+# "first_name": "",
+# "last_name": "",
+# "about_me": "",
+# }
+# profile.__dict__.update(data)
+# profile.save()
+# except get_user_model().DoesNotExist as e:
+# return HttpResponseBadRequest(reason=str(e))
+# except Exception as e:
+# return HttpResponseServerError(reason=str(e))
+
+# user.refresh_from_db() # Make update viewable
+# profile.refresh_from_db()
+
+# return JsonResponse({"result": "User successfully deleted."})
diff --git a/project/accounts/urls/api.py b/project/accounts/urls/api.py
index 7370fc7b..fdd11958 100644
--- a/project/accounts/urls/api.py
+++ b/project/accounts/urls/api.py
@@ -1,15 +1,15 @@
-from django.urls import path
from accounts import api
+from django.urls import path
urlpatterns = [
- path("account_data//", api.get_user, name="get_user"),
+ # path("account_data//", api.get_user, name="get_user"),
path("account_profile//", api.get_profile, name="get_profile"),
- path("account_card//", api.get_card, name="get_card"),
+ # path("account_card//", api.get_card, name="get_card"),
path("feed/", api.get_feed, name="get_feed"),
path("edituser/", api.edit_user, name="edit_user"),
- path("deleteuser/", api.delete_user, name="delete_user"),
+ # path("deleteuser/", api.delete_user, name="delete_user"),
path("upload_profile/", api.upload_profile_image, name="upload_profile"),
- path("clear_profile/", api.clear_profile_image, name="clear_profile"),
+ # path("clear_profile/", api.clear_profile_image, name="clear_profile"),
path("follow/", api.request_follow, name="follow_user"),
path("unfollow/", api.request_unfollow, name="unfollow_user"),
path(
From f13630e0927d23ce7e97ad67e0e80e44df81529f Mon Sep 17 00:00:00 2001
From: Brylie Christopher Oxley
Date: Tue, 11 Oct 2022 10:29:28 +0300
Subject: [PATCH 08/63] Simplify UserProfileView
---
.../accounts/templates/accounts/account.html | 84 ++++---------------
project/accounts/views.py | 44 +++-------
2 files changed, 28 insertions(+), 100 deletions(-)
diff --git a/project/accounts/templates/accounts/account.html b/project/accounts/templates/accounts/account.html
index 92dd1d41..a6babb80 100644
--- a/project/accounts/templates/accounts/account.html
+++ b/project/accounts/templates/accounts/account.html
@@ -79,7 +79,7 @@
{{ user.profile.first_name }} {{ user.profile.last_name }}
- @{{ username }}
+ @{{ user.username }}
@@ -91,67 +91,15 @@
{% if request.user.username != username|stringformat:"s" %}
-
+
{% endif %}
-
+
@@ -321,9 +269,9 @@
Follow
+ data-username="{{ follower.user.username }}">
+ Follow
+
{% else %}
@@ -416,8 +364,9 @@
class="waves-effect waves-light btn follow-btn col s12"
data-userid="{{ following.id }}"
data-username="{{ following.user.username }}"
- >Follow
+ >
+ Follow
+
{% else %}
diff --git a/project/accounts/views.py b/project/accounts/views.py
index 69dffbee..d560c491 100644
--- a/project/accounts/views.py
+++ b/project/accounts/views.py
@@ -4,25 +4,23 @@
This module will include views for the accounts app.
"""
-from core.custom_decorators import full_profile
+from accounts.authentication import account_activation_token, send_activation_email
+from accounts.forms import ProfileEditForm, UserRegistrationForm
+from accounts.models import Profile
from django.conf import settings
from django.contrib.auth import get_user_model, login
from django.contrib.auth import views as auth_views
from django.contrib.auth.mixins import LoginRequiredMixin
from django.contrib.sites.shortcuts import get_current_site
from django.http import HttpResponseRedirect
+from django.shortcuts import get_object_or_404
from django.template.response import TemplateResponse
from django.urls import reverse_lazy
from django.utils.encoding import force_str
from django.utils.http import urlsafe_base64_decode
-from django.utils.decorators import method_decorator
from django.views import View
from django.views.generic.edit import FormView, UpdateView
-from accounts.authentication import account_activation_token, send_activation_email
-from accounts.forms import ProfileEditForm, UpdateProfileImage, UserRegistrationForm
-from accounts.models import Profile
-
class RegisterView(FormView):
"""
@@ -169,32 +167,14 @@ def get(self, request):
class UserProfileView(LoginRequiredMixin, View):
"""A view that shows profile for authorized users"""
- @method_decorator(full_profile)
def get(self, request, username=None):
- if not username:
- return HttpResponseRedirect(f"/profile/{request.user}")
- else:
- is_owner = username == request.user.username
- try:
- user = get_user_model().objects.get(username=username)
-
- except get_user_model().DoesNotExist:
- return HttpResponseRedirect("/404")
-
- form = ProfileEditForm(
- initial={
- "username": user.username,
- "email": user.email,
- "first_name": user.profile.first_name or None,
- "last_name": user.profile.last_name or None,
- "about_me": user.profile.about_me or None,
+ user_model = get_user_model()
+ user = get_object_or_404(user_model, username=username)
+
+ return TemplateResponse(
+ request,
+ "account.html",
+ {
+ "user": user,
},
- readonly=True,
)
- data = {
- "username": user,
- "profile_image_form": UpdateProfileImage,
- "form": form if is_owner else None,
- "readonly": True,
- }
- return TemplateResponse(request, "account.html", data)
From fef456fb35d0cf28c561dd4023888dcc8c236e74 Mon Sep 17 00:00:00 2001
From: Brylie Christopher Oxley
Date: Tue, 11 Oct 2022 10:47:41 +0300
Subject: [PATCH 09/63] Simplify profile page (tabs split to new task)
---
.../accounts/templates/accounts/account.html | 443 +-----------------
.../accounts/partials/user_civis_list.html | 80 ++++
.../partials/user_followers_list.html | 85 ++++
.../partials/user_following_list.html | 83 ++++
.../accounts/partials/user_threads_list.html | 47 ++
project/core/templates/static/js/account.js | 260 ----------
.../static/js/account_tabs/followers.js | 0
.../static/js/account_tabs/following.js | 0
.../static/js/account_tabs/my_civis.js | 79 ----
.../static/js/account_tabs/my_issues.js | 1 -
.../static/js/account_tabs/my_reps.js | 1 -
11 files changed, 302 insertions(+), 777 deletions(-)
create mode 100644 project/accounts/templates/accounts/partials/user_civis_list.html
create mode 100644 project/accounts/templates/accounts/partials/user_followers_list.html
create mode 100644 project/accounts/templates/accounts/partials/user_following_list.html
create mode 100644 project/accounts/templates/accounts/partials/user_threads_list.html
delete mode 100644 project/core/templates/static/js/account.js
delete mode 100644 project/core/templates/static/js/account_tabs/followers.js
delete mode 100644 project/core/templates/static/js/account_tabs/following.js
delete mode 100644 project/core/templates/static/js/account_tabs/my_civis.js
delete mode 100644 project/core/templates/static/js/account_tabs/my_issues.js
delete mode 100644 project/core/templates/static/js/account_tabs/my_reps.js
diff --git a/project/accounts/templates/accounts/account.html b/project/accounts/templates/accounts/account.html
index a6babb80..061cc6ef 100644
--- a/project/accounts/templates/accounts/account.html
+++ b/project/accounts/templates/accounts/account.html
@@ -11,61 +11,6 @@
{% block content %}
{% include "global_nav.html" %}
-
-
-
-
-
-
{% translate "My Civi Activity" %}
-
-
-
-
-
-
-
- {% for civi in user.civis.all %}
-
- {% empty %}
-
- {% endfor %}
-
-
-
-
-
- {% translate "Issues I care about" %}
-
-
-
- {% for issue in user.thread %}
-
-
-
-
{{ issue.title }}
-
-
-
-
-
-
-
-
- {% for solution in issue.solutions %}
-
-
-
{{ solution.user_vote }}
-
-
{{ solution.body }}
-
-
- {% endfor %}
-
- {% empty %}
-
- {% endfor %}
-
-
{% endblock content %}
-
-{% block extra_js %}
-
-
-{% endblock extra_js %}
diff --git a/project/accounts/templates/accounts/partials/user_civis_list.html b/project/accounts/templates/accounts/partials/user_civis_list.html
new file mode 100644
index 00000000..01d8875f
--- /dev/null
+++ b/project/accounts/templates/accounts/partials/user_civis_list.html
@@ -0,0 +1,80 @@
+{% for civi in user.civis.all %}
+
+ {% empty %}
+
+{% endfor %}
diff --git a/project/accounts/templates/accounts/partials/user_followers_list.html b/project/accounts/templates/accounts/partials/user_followers_list.html
new file mode 100644
index 00000000..dbb02e53
--- /dev/null
+++ b/project/accounts/templates/accounts/partials/user_followers_list.html
@@ -0,0 +1,85 @@
+
+ {{ user.profile.followers.count }} Follower{{ user.profile.followers.count|pluralize }}
+
+{% for follower in user.profile.followers.all %}
+
+
+
+
+
+
+
+
+ {{ follower.about_me }}
+
+
+ {% if follower not in request.user.profile.followers.all %}
+
+ {% else %}
+
+ {% endif %}
+
+
+
+
+ {% empty %}
+
+{% endfor %}
diff --git a/project/accounts/templates/accounts/partials/user_following_list.html b/project/accounts/templates/accounts/partials/user_following_list.html
new file mode 100644
index 00000000..e25a749d
--- /dev/null
+++ b/project/accounts/templates/accounts/partials/user_following_list.html
@@ -0,0 +1,83 @@
+{{ user.profile.following.count }} Following
+{% for following in user.profile.following.all %}
+
+
+
+
+
+
+
+
+ {{ following.about_me }}
+
+
+ {% if following not in request.user.profile.followers.all %}
+
+ {% else %}
+
+ {% endif %}
+
+
+
+
+ {% empty %}
+
+
+
+
+ Not following any users
+
+
+
+
+{% endfor %}
diff --git a/project/accounts/templates/accounts/partials/user_threads_list.html b/project/accounts/templates/accounts/partials/user_threads_list.html
new file mode 100644
index 00000000..f9c17f8f
--- /dev/null
+++ b/project/accounts/templates/accounts/partials/user_threads_list.html
@@ -0,0 +1,47 @@
+{% for issue in user.thread %}
+
+
+
+
{{ issue.title }}
+
+
+
+
+
+
+
+
+ {% for solution in issue.solutions %}
+
+
+
{{ solution.user_vote }}
+
+
{{ solution.body }}
+
+
+ {% endfor %}
+
+ {% empty %}
+
+{% endfor %}
\ No newline at end of file
diff --git a/project/core/templates/static/js/account.js b/project/core/templates/static/js/account.js
deleted file mode 100644
index 45da8f13..00000000
--- a/project/core/templates/static/js/account.js
+++ /dev/null
@@ -1,260 +0,0 @@
-cw = cw || {};
-
-cw.AccountModel = BB.Model.extend({
- defaults: function() {
- return {
- profile_image: "",
- username: "",
- first_name: "",
- last_name: "",
- about_me: "",
- location: "",
- history: [],
- followers: [],
- following: [],
- issues: [],
- };
- },
- url: function () {
- if (! this.user ) {
- throw new Error("This is a race condition! and why we can't have nice things :(");
- }
- return '/api/account_profile/' + this.user + '/';
- },
-
- initialize: function (model, options) {
- options = options || {};
- this.user = options.user;
- }
-});
-
-
-// And hereon commences a pile of horrendous code. Beware!
-// TODO: review rewrite refactor. (please)
-cw.AccountView = BB.View.extend({
- el: '#account',
- template: _.template($('#account-template').html()),
- settingsTemplate: _.template($('#settings-template').html()),
- sidebarTemplate: _.template($('#sidebar-template').html()),
-
- // Account Tabs Templates
- mycivisTemplate: _.template($('#my-civis-template').html()),
- followersTemplate: _.template($('#followers-template').html()),
- followingTemplate: _.template($('#following-template').html()),
- myissuesTemplate: _.template($('#my-issues-template').html()),
-
- // Partials
- userCardTemplate: _.template($('.user-card-template').html()),
-
-
- initialize: function (options) {
- options = options || {};
- this.current_user = options.current_user;
- this.isSave = false;
-
- this.listenTo(this.model, 'sync', function(){
- document.title = this.model.get("first_name") +" "+ this.model.get("last_name") +" (@"+this.model.get("username")+")";
-
- this.postRender();
-
- if (_.find(this.model.get("followers"), function(follower){
- return (follower.username== current_user);
- })) {
- var follow_btn = this.$('#sidebar-follow-btn');
- follow_btn.addClass("btn-secondary");
- follow_btn.data("follow-state", true);
- follow_btn.html("");
- }
- });
-
- return this;
- },
-
- render: function () {
- if (this.isSave) {
- this.postRender();
- } else {
- this.$el.empty().append(this.template());
-
- $('.account-tabs .tab').on('dragstart', function() {return false;});
- this.$el.find('.account-settings').pushpin({ top: $('.account-settings').offset().top });
- this.$el.find('.scroll-col').height($(window).height());
- }
- },
-
- tabsRender: function () {
- this.$('#civis').empty().append(this.mycivisTemplate());
- this.$('#followers').empty().append(this.followersTemplate());
- this.$('#following').empty().append(this.followingTemplate());
- this.$('#issues').empty().append(this.myissuesTemplate());
- var settingsEl = this.$('#settings');
- if (settingsEl.length) {
- settingsEl.empty().append(this.settingsTemplate());
- Materialize.updateTextFields();
- }
- },
-
- postRender: function () {
- // Timestamp the image with a cachebreaker so that proper refersh occurs
- this.model.set({"profile_image": this.model.get("profile_image") + "?" + new Date().getTime() });
- this.$el.find('.account-settings').empty().append(this.sidebarTemplate());
- this.tabsRender();
- cw.materializeShit();
- this.isSave = false;
-
- },
-
- events: {
- 'click .follow-btn': 'followRequest',
- 'submit #profile_image_form': 'handleFiles',
- 'blur .save-account': 'saveAccount',
- 'click .toggle-solutions': 'toggleSolutions',
- 'change .profile-image-pick': 'previewImage',
- 'keypress .save-account': cw.checkForEnter,
- },
-
- toggleSolutions: function(e) {
- var id = $(e.currentTarget).data('id');
- var textElement = $(e.currentTarget).find('.button-text');
- var new_text = textElement.text() === "Show Solutions" ? "Hide Solutions" : "Show Solutions";
- textElement.text(new_text);
- this.$('#solutions-'+id).toggleClass('hide');
- },
-
- previewImage: function(e){
- var _this = this;
- var img = this.$el.find('#id_profile_image');
- if (img.val()) {
- var uploaded_image = img[0].files[0];
- if (uploaded_image) {
- var formData = new FormData(this.$el.find('#profile_image_form')[0]);
-
- var reader = new FileReader();
-
- reader.onload = function(e) {
- var preview_image = _this.$el.find('.preview-image');
- preview_image.attr('src', e.target.result);
-
- _this.toggleImgButtons();
- };
- reader.readAsDataURL(uploaded_image);
- }
- }
- },
-
- showRawRatings: function(e) {
- $(e.target).closest('.rating').find('.rating-score').toggleClass('hide');
- $(e.target).closest('.rating').find('.rating-percent').toggleClass('hide');
- },
-
- toggleImgButtons: function(event) {
- this.$('.profile-image-pick').toggleClass('hide');
- this.$('.upload-image').toggleClass('hide');
- this.$('#confirmation-prompt').toggleClass('hide');
- },
-
- followRequest: function(e){
- var apiData = {},
- _this = this,
- follow_state = this.$(e.currentTarget).data("follow-state");
- target = this.$(e.target);
-
- apiData.target = this.$(e.currentTarget).data("username");
-
- if (!follow_state) {
- $.ajax({
- url: '/api/follow/',
- type: 'POST',
- data: apiData,
- success: function () {
- Materialize.toast('You are now following user '+ apiData.target, 5000);
- target.addClass("btn-secondary");
- target.data("follow-state", true);
- target.html("");
-
- },
- error: function () {
- Materialize.toast('Could not follow user '+ apiData.target, 5000);
- }
- });
- } else {
- $.ajax({
- url: '/api/unfollow/',
- type: 'POST',
- data: apiData,
- success: function () {
- Materialize.toast('You have unfollowed user '+ apiData.target, 5000);
- target.removeClass("btn-secondary");
- target.html("FOLLOW");
- target.data("follow-state", false);
- },
- error: function () {
- Materialize.toast('Could not unfollow user '+ apiData.target, 5000);
- }
- });
- }
-
- },
-
- saveAccount: function (e) {
- var $this = $(e.target),
- changeKey = $this.attr('id'),
- changeVal = $this.val().trim(),
- apiData = {},
- _this = this;
-
- if (this.model.get([changeKey]) === changeVal) {
- return;
- }
-
- apiData[changeKey] = changeVal;
-
- $.ajax({
- url: '/api/edituser/',
- type: 'POST',
- data: apiData,
- success: function () {
- Materialize.toast('Saved!', 5000);
-
- _this.isSave = true;
- _this.model.fetch();
-
- }
- });
- },
-
- handleFiles: function(e) {
- e.preventDefault();
-
- var _this = this,
- formData = new FormData($('#profile_image_form')[0]);
-
- $.ajax({
- url: '/api/upload_profile/',
- type: 'POST',
- data: formData,
- cache: false,
- contentType: false,
- processData: false,
- success: function () {
- Materialize.toast('Saved!', 5000);
-
- _this.isSave = true;
- _this.model.fetch();
- },
- error: function(data){
- if (data.status === 400) {
- Materialize.toast(data.responseJSON.message, 5000, 'red');
- } else if (data.status === 500) {
- Materialize.toast('Internal Server Error', 5000, 'red');
- } else {
- Materialize.toast(data.statusText, 5000, 'red');
- }
- },
-
- });
- this.toggleImgButtons();
-
- return false;
- },
-});
diff --git a/project/core/templates/static/js/account_tabs/followers.js b/project/core/templates/static/js/account_tabs/followers.js
deleted file mode 100644
index e69de29b..00000000
diff --git a/project/core/templates/static/js/account_tabs/following.js b/project/core/templates/static/js/account_tabs/following.js
deleted file mode 100644
index e69de29b..00000000
diff --git a/project/core/templates/static/js/account_tabs/my_civis.js b/project/core/templates/static/js/account_tabs/my_civis.js
deleted file mode 100644
index 7d85c074..00000000
--- a/project/core/templates/static/js/account_tabs/my_civis.js
+++ /dev/null
@@ -1,79 +0,0 @@
-cw = cw || {};
-
-cw.ProfileCiviModel = BB.Model.extend({
- defaults: function() {
- return {
- type: "No Type",
- title: "Civi Title",
- body: "Civi Body",
- author: { "username": "None", "profile_image": "/media/profile/happy.png"},
- ratings: {"ratings": [], "total": 0, "names": ['Strongly Disagree', 'Disagree', 'Neutral', 'Agree', 'Strongly Agree']},
- attachments: [],
- created: "No Date"
- };
- },
-
- initialize: function (model, options) {
- options = options || {};
- // this.set({
- // ratings: r,
- // author: a,
- // attachments: a
- // });
- }
-
-
-});
-
-cw.ProfileCiviCollection = BB.Collection.extend({
-
- model: cw.ProfileCiviModel,
-
- // url: function () {
- // if (! this.username ) {
- // throw new Error("This is a race condition! and why we can't have nice things :(");
- // }
- // return '/api/getUserCivis/' + this.user_id + '/';
- // },
-
- initialize: function (models, options) {
- options = options || {};
- this.listenTo(this.model, 'add', function () {
- console.log('a civi was added');
- });
- }
-});
-
-cw.MyCivisView = BB.View.extend({
-
- el: '#civis',
- mycivisTemplate: _.template($('#my-civis-template').html()),
-
- initialize: function (options) {
- options = options || {};
-
- console.log(options.civis);
- this.civis = new cw.ProfileCiviCollection({
- models: options.civis
- });
- console.log(this.civis);
- this.render();
- },
-
- render: function () {
- this.$el.empty().append(this.mycivisTemplate());
- },
-
- events: {
- 'hover .gitem': 'permalink'
- },
-
- displayRawRating: function (event) {
-
- },
-
- permalink: function(event) {
- var _this = this;
- },
-
-});
diff --git a/project/core/templates/static/js/account_tabs/my_issues.js b/project/core/templates/static/js/account_tabs/my_issues.js
deleted file mode 100644
index 76abb76d..00000000
--- a/project/core/templates/static/js/account_tabs/my_issues.js
+++ /dev/null
@@ -1 +0,0 @@
-// TODO: issues (1. move from general account to here 2. consider merging with civis and creating a summary view)
diff --git a/project/core/templates/static/js/account_tabs/my_reps.js b/project/core/templates/static/js/account_tabs/my_reps.js
deleted file mode 100644
index 11aac5cc..00000000
--- a/project/core/templates/static/js/account_tabs/my_reps.js
+++ /dev/null
@@ -1 +0,0 @@
-// TODO: move rep stuff into here
From 40e198659e06ff61dd38e69379915d0374018e75 Mon Sep 17 00:00:00 2001
From: Brylie Christopher Oxley
Date: Tue, 11 Oct 2022 12:56:58 +0300
Subject: [PATCH 10/63] Restore delete_user view/endpoint
---
project/accounts/api.py | 68 ++++++++++++++++++------------------
project/accounts/urls/api.py | 2 +-
2 files changed, 35 insertions(+), 35 deletions(-)
diff --git a/project/accounts/api.py b/project/accounts/api.py
index 4f693033..66fb1192 100644
--- a/project/accounts/api.py
+++ b/project/accounts/api.py
@@ -455,39 +455,39 @@ def edit_user_categories(request):
return HttpResponseServerError(reason=str(e))
-# @login_required
-# def delete_user(request):
-# """
-# Delete User Information
-# """
-# try:
-# # Get current user
-# user = get_user_model().objects.get(username=request.user.username)
-# profile = Profile.objects.get(user=user)
-# # Expunge personally identifiable data in user obj, feel free to change
-# data = {
-# "is_active": False,
-# "email": "",
-# "first_name": "",
-# "last_name": "",
-# "username": "[Deleted-" + str(user.id) + "]",
-# }
-# user.__dict__.update(data)
-# user.save() # Update into database
-
-# data = { # Expunge personally identifiable data in profile obj
-# "first_name": "",
-# "last_name": "",
-# "about_me": "",
-# }
-# profile.__dict__.update(data)
-# profile.save()
-# except get_user_model().DoesNotExist as e:
-# return HttpResponseBadRequest(reason=str(e))
-# except Exception as e:
-# return HttpResponseServerError(reason=str(e))
+@login_required
+def delete_user(request):
+ """
+ Delete User Information
+ """
+ try:
+ # Get current user
+ user = get_user_model().objects.get(username=request.user.username)
+ profile = Profile.objects.get(user=user)
+ # Expunge personally identifiable data in user obj, feel free to change
+ data = {
+ "is_active": False,
+ "email": "",
+ "first_name": "",
+ "last_name": "",
+ "username": "[Deleted-" + str(user.id) + "]",
+ }
+ user.__dict__.update(data)
+ user.save() # Update into database
+
+ data = { # Expunge personally identifiable data in profile obj
+ "first_name": "",
+ "last_name": "",
+ "about_me": "",
+ }
+ profile.__dict__.update(data)
+ profile.save()
+ except get_user_model().DoesNotExist as e:
+ return HttpResponseBadRequest(reason=str(e))
+ except Exception as e:
+ return HttpResponseServerError(reason=str(e))
-# user.refresh_from_db() # Make update viewable
-# profile.refresh_from_db()
+ user.refresh_from_db() # Make update viewable
+ profile.refresh_from_db()
-# return JsonResponse({"result": "User successfully deleted."})
+ return JsonResponse({"result": "User successfully deleted."})
diff --git a/project/accounts/urls/api.py b/project/accounts/urls/api.py
index fdd11958..dae92f2c 100644
--- a/project/accounts/urls/api.py
+++ b/project/accounts/urls/api.py
@@ -7,7 +7,7 @@
# path("account_card//", api.get_card, name="get_card"),
path("feed/", api.get_feed, name="get_feed"),
path("edituser/", api.edit_user, name="edit_user"),
- # path("deleteuser/", api.delete_user, name="delete_user"),
+ path("deleteuser/", api.delete_user, name="delete_user"),
path("upload_profile/", api.upload_profile_image, name="upload_profile"),
# path("clear_profile/", api.clear_profile_image, name="clear_profile"),
path("follow/", api.request_follow, name="follow_user"),
From 183ba76bc5294c91a1cb5e668b002325d15a3010 Mon Sep 17 00:00:00 2001
From: Brylie Christopher Oxley
Date: Tue, 11 Oct 2022 13:28:05 +0300
Subject: [PATCH 11/63] Move global nav to base.html
---
project/accounts/templates/accounts/update_settings.html | 7 +++----
project/core/templates/base.html | 2 ++
2 files changed, 5 insertions(+), 4 deletions(-)
diff --git a/project/accounts/templates/accounts/update_settings.html b/project/accounts/templates/accounts/update_settings.html
index 28dae047..13271cad 100644
--- a/project/accounts/templates/accounts/update_settings.html
+++ b/project/accounts/templates/accounts/update_settings.html
@@ -9,7 +9,6 @@
{% endblock extra_css %}
{% block content %}
-{% include "global_nav.html" %}
@@ -56,7 +55,7 @@
{%if not readonly %}
-
+
{% endif%}
@@ -70,7 +69,7 @@
@@ -80,6 +79,6 @@
-
+ {% include "global_nav.html" %}
+
{% block content %}{% endblock content %}
{% include "static_footer.html" %}
From 6215e40eaa157629f8f031c12026acd760548dcc Mon Sep 17 00:00:00 2001
From: Brylie Christopher Oxley
Date: Tue, 11 Oct 2022 13:36:24 +0300
Subject: [PATCH 12/63] Remove global nav import
---
project/accounts/templates/accounts/account.html | 1 -
project/accounts/templates/accounts/feed.html | 1 -
project/threads/templates/threads/thread.html | 1 -
3 files changed, 3 deletions(-)
diff --git a/project/accounts/templates/accounts/account.html b/project/accounts/templates/accounts/account.html
index 061cc6ef..da8a9368 100644
--- a/project/accounts/templates/accounts/account.html
+++ b/project/accounts/templates/accounts/account.html
@@ -9,7 +9,6 @@
{% endblock extra_css %}
{% block content %}
-{% include "global_nav.html" %}
-
-
From a069468104af196aca3a36d549b1f6d256eb5bd5 Mon Sep 17 00:00:00 2001
From: Brylie Christopher Oxley
Date: Tue, 11 Oct 2022 13:47:10 +0300
Subject: [PATCH 15/63] Add button text
---
project/accounts/templates/accounts/update_settings.html | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/project/accounts/templates/accounts/update_settings.html b/project/accounts/templates/accounts/update_settings.html
index 13271cad..a0e4a021 100644
--- a/project/accounts/templates/accounts/update_settings.html
+++ b/project/accounts/templates/accounts/update_settings.html
@@ -55,7 +55,7 @@
{%if not readonly %}
-
+
{% endif%}
From 94a90133d988353b2bd8adb9db36fb8aba88da95 Mon Sep 17 00:00:00 2001
From: Brylie Christopher Oxley
Date: Tue, 11 Oct 2022 13:49:57 +0300
Subject: [PATCH 16/63] Use only Django for Settings view
---
.../accounts/templates/accounts/settings.html | 137 +++++++++---------
.../templates/accounts/update_settings.html | 84 -----------
project/accounts/views.py | 2 +-
project/core/templates/static/js/settings.js | 84 -----------
4 files changed, 71 insertions(+), 236 deletions(-)
delete mode 100644 project/accounts/templates/accounts/update_settings.html
delete mode 100644 project/core/templates/static/js/settings.js
diff --git a/project/accounts/templates/accounts/settings.html b/project/accounts/templates/accounts/settings.html
index feece260..a0e4a021 100644
--- a/project/accounts/templates/accounts/settings.html
+++ b/project/accounts/templates/accounts/settings.html
@@ -1,81 +1,84 @@
{% extends "base.html" %}
{% load static %}
{% load i18n %}
-{% block page_title %}Settings{% endblock page_title %}
+{% block page_title %}Settings{% endblock page_title %}
{% block extra_css %}
-
-
+
+
{% endblock extra_css %}
-{% block backbone_template %}
-
-
-
-
-
-{% endblock backbone_template %}
+
-{% block body_class %}grey lighten-4{% endblock body_class %}
-{% block content %}
- {% include "global_nav.html" %}
-
+
{% endblock content %}
-
-{% block extra_js %}
-
-
-{% endblock extra_js %}
diff --git a/project/accounts/templates/accounts/update_settings.html b/project/accounts/templates/accounts/update_settings.html
deleted file mode 100644
index a0e4a021..00000000
--- a/project/accounts/templates/accounts/update_settings.html
+++ /dev/null
@@ -1,84 +0,0 @@
-{% extends "base.html" %}
-{% load static %}
-{% load i18n %}
-
-{% block page_title %}Settings{% endblock page_title %}
-{% block extra_css %}
-
-
-{% endblock extra_css %}
-
-{% block content %}
-
-
-
-
-
-
- {{form.non_field_errors}}
-
-
-
-
-
-
-
-
-
-
-
-{% endblock content %}
diff --git a/project/accounts/views.py b/project/accounts/views.py
index ae21546a..ce380ba2 100644
--- a/project/accounts/views.py
+++ b/project/accounts/views.py
@@ -80,7 +80,7 @@ class SettingsView(LoginRequiredMixin, UpdateView):
login_url = "accounts_login"
form_class = ProfileEditForm
success_url = reverse_lazy("accounts_settings")
- template_name = "accounts/update_settings.html"
+ template_name = "accounts/settings.html"
def get_object(self, queryset=None):
return Profile.objects.get(user=self.request.user)
diff --git a/project/core/templates/static/js/settings.js b/project/core/templates/static/js/settings.js
deleted file mode 100644
index 52e83df1..00000000
--- a/project/core/templates/static/js/settings.js
+++ /dev/null
@@ -1,84 +0,0 @@
-cw = cw || {};
-
-cw.UserModel = BB.Model.extend({
- defaults: function() {
- return {
- username: "",
- email: "",
- };
- },
- url: function () {
- if (! this.get('username') ) {
- throw new Error("This is a race condition! and why we can't have nice things :(");
- }
- return '/api/account_profile/' + this.get('username') + '/';
- },
-
- initialize: function (model, options) {
- options = options || {};
- }
-});
-
-
-cw.SettingsView = BB.View.extend({
-
- el: '#settings',
-
- initialize: function(options) {
- this.options = options || {};
-
- this.template = _.template($('#settings-template').text());
- this.settingsTemplate = _.template($('#settings-base').text());
- this.personalTemplate = _.template($('#settings-personal').text());
-
- this.listenTo(this.model, 'change', this.renderAllLabels);
- },
-
- render: function() {
- this.$el.html(this.template());
-
- this.$('#settings-el').html(this.settingsTemplate());
-
- this.renderPersonal();
- },
-
- renderAllLabels:function() {
- this.renderPersonal();
- Materialize.updateTextFields();
- },
-
- renderPersonal: function() {
- this.$('#settings-1').html(this.personalTemplate());
- },
-
- events: {
- 'blur .save-account': 'saveAccount',
- },
-
- saveAccount: function (e) {
- var $this = $(e.target),
- changeKey = $this.attr('id'),
- changeVal = $this.val().trim(),
- apiData = {},
- _this = this;
-
- if (this.model.get([changeKey]) === changeVal) {
- return;
- }
-
- apiData[changeKey] = changeVal;
-
- $.ajax({
- url: '/api/edituser/',
- type: 'POST',
- data: apiData,
- success: function () {
- Materialize.toast('Saved!', 5000);
-
- _this.isSave = true;
- _this.model.fetch();
-
- }
- });
- },
-});
From 234137b18b6729782c042f72ecc3431ac9210dc8 Mon Sep 17 00:00:00 2001
From: Brylie Christopher Oxley
Date: Tue, 11 Oct 2022 13:50:23 +0300
Subject: [PATCH 17/63] Remove unused import
---
project/accounts/templates/accounts/settings.html | 2 --
1 file changed, 2 deletions(-)
diff --git a/project/accounts/templates/accounts/settings.html b/project/accounts/templates/accounts/settings.html
index a0e4a021..df1ffae4 100644
--- a/project/accounts/templates/accounts/settings.html
+++ b/project/accounts/templates/accounts/settings.html
@@ -79,6 +79,4 @@
-
-
{% endblock content %}
From 40038ae083fc9efd596a1c8ddfde8068aa26b660 Mon Sep 17 00:00:00 2001
From: Brylie Christopher Oxley
Date: Tue, 11 Oct 2022 13:59:15 +0300
Subject: [PATCH 18/63] Remove unused endpoints and tests
---
project/accounts/api.py | 41 ++-----------------
project/accounts/tests/test_api.py | 63 +++---------------------------
project/accounts/urls/api.py | 3 --
3 files changed, 9 insertions(+), 98 deletions(-)
diff --git a/project/accounts/api.py b/project/accounts/api.py
index 66fb1192..0858705a 100644
--- a/project/accounts/api.py
+++ b/project/accounts/api.py
@@ -139,22 +139,6 @@ def get_permissions(self):
return super(ProfileViewSet, self).get_permissions()
-# @api_view(["GET"])
-# def get_user(request, username):
-# """
-# USAGE:
-# This is used to get a user
-# """
-
-# try:
-# user = get_user_model().objects.get(username=username)
-# return JsonResponse(UserSerializer(user).data)
-# except get_user_model().DoesNotExist:
-# return JsonResponse(
-# {"error": f"User with username {username} not found"}, status=400
-# )
-
-
@api_view(["GET"])
def get_profile(request, username):
"""
@@ -167,6 +151,9 @@ def get_profile(request, username):
profile = user.profile
result = Profile.objects.summarize(profile)
result["issues"] = []
+
+ # TODO: move this to a property of the user
+ # lines from voted solutions through result issues append
voted_solutions = Activity.objects.filter(
user=user.id, civi__c_type="solution", activity_type__contains="pos"
)
@@ -213,28 +200,6 @@ def get_profile(request, username):
)
-# @api_view(["GET"])
-# def get_card(request, username):
-# """
-# USAGE:
-# This is used to get a card
-# """
-
-# try:
-# user = get_user_model().objects.get(username=username)
-# profile = user.profile
-# result = Profile.objects.card_summarize(
-# profile, Profile.objects.get(user=request.user)
-# )
-# return JsonResponse(result)
-# except get_user_model().DoesNotExist:
-# return JsonResponse(
-# {"error": f"User with username {username} not found"}, status=400
-# )
-# except Exception as e:
-# return HttpResponseBadRequest(reason=str(e))
-
-
def get_feed(request):
"""
USAGE:
diff --git a/project/accounts/tests/test_api.py b/project/accounts/tests/test_api.py
index 64af5156..fca55533 100644
--- a/project/accounts/tests/test_api.py
+++ b/project/accounts/tests/test_api.py
@@ -1,12 +1,13 @@
import json
-from PIL import Image
-from django.core.files.temp import NamedTemporaryFile
+
+from accounts.models import Profile
+from categories.models import Category
from django.contrib.auth import get_user_model
+from django.core.files.temp import NamedTemporaryFile
from django.test import TestCase
-from rest_framework.test import APIClient
from django.urls import reverse
-from accounts.models import Profile
-from categories.models import Category
+from PIL import Image
+from rest_framework.test import APIClient
from threads.models import Civi, Thread
@@ -167,58 +168,6 @@ def test_nonexistent_user_account_data(self):
self.assertIn("not found", content["error"])
-class GetProfileTests(BaseTestCase):
- """A class to test get_profile function"""
-
- def test_existing_user_profile_data(self):
- """Whether a user profile is retrieved"""
-
- self.client.login(username="newuser", password="password123")
- response = self.client.get(reverse("get_profile", args=["newuser"]))
- content = json.loads(response.content)
- self.assertEqual(response.status_code, 200)
- self.assertEqual(content["username"], self.user.username)
-
- def test_nonexistent_user_profile_data(self):
- """Whether retrieving a nonexistent user profile raises 404"""
-
- self.client.login(username="newuser", password="password123")
- response = self.client.get(
- reverse("get_profile", args=["newuser" + "not_exist"])
- )
- content = json.loads(response.content)
- self.assertEqual(response.status_code, 400)
- self.assertIn("not found", content["error"])
-
-
-class GetCardTests(BaseTestCase):
- """A class to test get_card function"""
-
- def test_existing_user_profile_data(self):
- """Whether a user card is retrieved"""
-
- self.client.login(username="newuser", password="password123")
- response = self.client.get(reverse("get_card", args=["newuser"]))
- content = json.loads(response.content)
- self.assertEqual(response.status_code, 200)
- self.assertEqual(content["username"], self.user.username)
- self.assertFalse(content["follow_state"])
- response = self.client.get(reverse("get_card", args=["superuser"]))
- content = json.loads(response.content)
- self.assertEqual(response.status_code, 200)
- self.assertEqual(content["username"], self.superuser.username)
- self.assertTrue(content["follow_state"])
-
- def test_nonexistent_user_profile_data(self):
- """Whether retrieving a nonexistent user card raises 404"""
-
- self.client.login(username="newuser", password="password123")
- response = self.client.get(reverse("get_card", args=["newuser" + "not_exist"]))
- content = json.loads(response.content)
- self.assertEqual(response.status_code, 400)
- self.assertIn("not found", content["error"])
-
-
class EditUserTests(BaseTestCase):
"""A class to test edit_user function"""
diff --git a/project/accounts/urls/api.py b/project/accounts/urls/api.py
index dae92f2c..0f45354a 100644
--- a/project/accounts/urls/api.py
+++ b/project/accounts/urls/api.py
@@ -2,9 +2,6 @@
from django.urls import path
urlpatterns = [
- # path("account_data//", api.get_user, name="get_user"),
- path("account_profile//", api.get_profile, name="get_profile"),
- # path("account_card//", api.get_card, name="get_card"),
path("feed/", api.get_feed, name="get_feed"),
path("edituser/", api.edit_user, name="edit_user"),
path("deleteuser/", api.delete_user, name="delete_user"),
From c92bb88e2e9001a9e8af28a935d6cf7bbf6e75ec Mon Sep 17 00:00:00 2001
From: Brylie Christopher Oxley
Date: Tue, 11 Oct 2022 14:09:16 +0300
Subject: [PATCH 19/63] Remove clear_profile view
---
project/accounts/api.py | 23 -----------------------
project/accounts/urls/api.py | 1 -
2 files changed, 24 deletions(-)
diff --git a/project/accounts/api.py b/project/accounts/api.py
index 0858705a..c7541e10 100644
--- a/project/accounts/api.py
+++ b/project/accounts/api.py
@@ -283,29 +283,6 @@ def upload_profile_image(request):
return HttpResponseForbidden("allowed only via POST")
-# @login_required
-# def clear_profile_image(request):
-# """This function is used to delete a profile image"""
-
-# if request.method == "POST":
-# try:
-# account = Profile.objects.get(user=request.user)
-
-# # Clean up previous image
-# account.profile_image.delete()
-# account.save()
-
-# return HttpResponse("Image Deleted")
-# except get_user_model().DoesNotExist:
-# return HttpResponseServerError(
-# reason=f"Profile with id:{request.user.username} does not exist"
-# )
-# except Exception:
-# return HttpResponseServerError(reason=str("default"))
-# else:
-# return HttpResponseForbidden("allowed only via POST")
-
-
@login_required
@require_post_params(params=["target"])
def request_follow(request):
diff --git a/project/accounts/urls/api.py b/project/accounts/urls/api.py
index 0f45354a..0928868e 100644
--- a/project/accounts/urls/api.py
+++ b/project/accounts/urls/api.py
@@ -6,7 +6,6 @@
path("edituser/", api.edit_user, name="edit_user"),
path("deleteuser/", api.delete_user, name="delete_user"),
path("upload_profile/", api.upload_profile_image, name="upload_profile"),
- # path("clear_profile/", api.clear_profile_image, name="clear_profile"),
path("follow/", api.request_follow, name="follow_user"),
path("unfollow/", api.request_unfollow, name="unfollow_user"),
path(
From ba8e7b4a170d7e05fcbc7e57eb87d0852b1ce766 Mon Sep 17 00:00:00 2001
From: Brylie Christopher Oxley
Date: Tue, 11 Oct 2022 14:09:28 +0300
Subject: [PATCH 20/63] Cleanup unused JS
---
.../core/templates/static/js/user-setup.js | 275 ------------------
1 file changed, 275 deletions(-)
delete mode 100644 project/core/templates/static/js/user-setup.js
diff --git a/project/core/templates/static/js/user-setup.js b/project/core/templates/static/js/user-setup.js
deleted file mode 100644
index dfc07c04..00000000
--- a/project/core/templates/static/js/user-setup.js
+++ /dev/null
@@ -1,275 +0,0 @@
-cw = cw || {};
-
-cw.AccountModel = BB.Model.extend({
- defaults: function() {
- return {
- username: "",
- first_name: "",
- last_name: "",
- about_me: ""
- };
- },
- urlRoot: "/api/v1/accounts/",
-
- idAttribute: "username",
- initialize: function(model, options) {
- options = options || {};
- }
-});
-
-cw.UserSetupView = BB.View.extend({
- el: "#user-setup",
-
- currentStep: 0,
- baseTemplate: _.template($("#setup-base-template").html()),
- step0Template: _.template($("#step0-template").html()),
- step1Template: _.template($("#step1-template").html()),
- step2Template: _.template($("#step2-template").html()),
-
- initialize: function(options) {
- options = options || {};
-
- this.listenTo(this.model, "sync", this.render);
- return this;
- },
-
- render: function() {
- if (this.currentStep === 0) {
- this.$el.empty().append(this.baseTemplate());
- this.$("#step0")
- .empty()
- .append(this.step0Template());
- this.$("#step1")
- .empty()
- .append(this.step1Template())
- .toggleClass("hide");
- this.$("#step2")
- .empty()
- .append(this.step2Template())
- .toggleClass("hide");
- }
- },
-
- events: {
- "click .prev": "prevStep",
- "click .next": "nextStep",
- "click .finish": "setupUser",
- "change .profile-image-pick": "previewImage",
- "click .cancel-image": "clearImageField",
- "click .upload-image": "toggleFileDialog",
- "keypress .about-me": "limitInput",
- "input .step1-input": "validateStep1"
- },
-
- nextStep: function() {
- if (this.currentStep === 0) {
- this.$("#step0").addClass("hide");
- this.$("#step1").removeClass("hide");
- this.currentStep = 1;
- } else if (this.currentStep === 1) {
- var first_name = this.$el.find("#first-name").val();
- var last_name = this.$el.find("#last-name").val();
- var about_me = this.$el.find("#about-me").val();
-
- if (first_name && last_name && about_me) {
- this.$el.find("#step1").addClass("hide");
- this.$el.find("#step2").removeClass("hide");
- this.currentStep = 2;
- } else {
- Materialize.toast(
- 'Please fill out all the fields',
- 5000
- );
- }
- }
- },
-
- prevStep: function() {
- if (this.currentStep === 2) {
- this.$el.find("#step1").removeClass("hide");
- this.$el.find("#step2").addClass("hide");
- this.currentStep = 1;
- }
- },
-
- // INPUT VALIDATION ============================================================
- // limitInput(): limits the about-me input field to 500 characters
- limitInput: function(e) {
- var max = 500;
- var textarea = this.$el.find("#about-me");
- if (e.which < 0x20) {
- return;
- }
- if (textarea.val().length == max) {
- e.preventDefault();
- } else if (textarea.val().length > max) {
- textarea.val(textarea.val().substring(0, max));
- }
- },
-
- validateStep1: function() {
- var first_name = this.$el
- .find("#first-name")
- .val()
- .trim();
-
- var last_name = this.$el
- .find("#last-name")
- .val()
- .trim();
-
- var about_me = this.$el
- .find("#about-me")
- .val()
- .trim();
-
- if (first_name && last_name && about_me) {
- this.model.set({
- first_name: first_name,
- last_name: last_name,
- about_me: about_me
- });
-
- this.$el.find(".finish").removeClass("disabled");
- this.$el.find(".help-text.invalid").addClass("hide");
- this.$el.find(".help-text.valid").removeClass("hide");
- } else {
- this.$el.find(".finish").addClass("disabled");
- this.$el.find(".help-text.valid").addClass("hide");
- this.$el.find(".help-text.invalid").removeClass("hide");
- }
- },
-
- // IMAGE SELECTION & PREVIEW ===================================================
- // toggleImgButtons(): displays the appropriate buttons based on image state
- toggleImgButtons: function() {
- this.$el.find(".profile-image-pick").toggleClass("hide");
- this.$el.find(".upload-image").toggleClass("hide");
- this.$el.find(".cancel-image").toggleClass("hide");
- this.$el.find(".preview-image").toggleClass("hide");
- },
-
- // toggleFileDialog(): Lets the user choose a different image
- toggleFileDialog: function(e) {
- e.stopPropagation();
- e.preventDefault();
- this.$el.find("#id_profile_image").trigger("click");
- },
-
- // previewImage(): Creates a preview of the current image file chosen
- previewImage: function(e) {
- var _this = this;
- var img = this.$el.find("#id_profile_image");
- if (img.val()) {
- this.uploadProfileImg();
- }
- },
-
- // clearImageField(): clears only the profile image file field
- clearImageField: function(e) {
- this.clearProfileImg();
-
- e.stopPropagation();
- e.preventDefault();
- },
-
- // SENDING REQUEST TO SERVER ===================================================
- setupUser: function() {
- // preserve reference to 'this' for use in ajax callback
- var _this = this;
-
- var first_name = this.model.get("first_name");
- var last_name = this.model.get("last_name");
- var about_me = this.model.get("about_me");
-
- if (first_name && last_name && about_me) {
- var csrftoken = document.querySelector('[name=csrfmiddlewaretoken]').value;
-
- $.ajax({
- type: "POST",
- url: "/api/edituser/",
- data: {
- csrfmiddlewaretoken: csrftoken,
- full_profile: "True",
- about_me: about_me,
- first_name: first_name,
- last_name: last_name,
- },
- success: function(data) {
- Materialize.toast(
- 'Success',
- 5000
- );
- _this.nextStep();
- },
- error: function(data) {
- Materialize.toast(data.statusText, 5000);
- }
- });
- }
- },
-
- uploadProfileImg: function() {
- var _this = this;
- var formData = new FormData(this.$el.find("#profile_image_form")[0]);
-
- $.ajax({
- url: "/api/upload_profile/",
- type: "POST",
- success: function(response) {
- var img = _this.$el.find("#id_profile_image");
- var uploaded_image = img[0].files[0];
- if (uploaded_image) {
- var preview_image = _this.$el.find(".preview-image");
- preview_image.attr("src", response.profile_image);
-
- if (_this.$el.find(".preview-image").hasClass("hide")) {
- _this.toggleImgButtons();
- _this.$el.find(".loading").addClass("hide");
- _this.$el.find(".placeholder").addClass("hide");
- }
- }
-
- Materialize.toast("Image Uploaded!", 5000);
- },
- error: function(response) {
- if (response.status === 400) {
- Materialize.toast(response.responseJSON.message, 5000, "red");
- } else if (response.status === 500) {
- Materialize.toast("Internal Server Error", 5000, "red");
- } else {
- Materialize.toast(response.statusText, 5000, "red");
- }
-
- _this.$el.find(".loading").addClass("hide");
- _this.$el.find(".placeholder").removeClass("hide");
- _this.$el.find("#profile_image_form")[0].reset();
- },
- data: formData,
- cache: false,
- contentType: false,
- processData: false
- });
- this.$el.find(".loading").removeClass("hide");
- this.$el.find(".placeholder").addClass("hide");
- return false;
- },
-
- clearProfileImg: function() {
- var _this = this;
- $.ajax({
- url: "/api/clear_profile/",
- type: "POST",
- success: function(e) {
- _this.toggleImgButtons();
- _this.$el.find(".loading").addClass("hide");
- _this.$el.find(".placeholder").removeClass("hide");
- _this.$el.find("#profile_image_form")[0].reset();
- Materialize.toast(JSON.stringify(e), 5000);
- },
- error: function(e) {
- Materialize.toast(JSON.stringify(e), 5000);
- }
- });
- }
-});
From 335a8bd25b57bca443ca73a860a3b0a654cedff0 Mon Sep 17 00:00:00 2001
From: Brylie Christopher Oxley
Date: Tue, 11 Oct 2022 14:14:03 +0300
Subject: [PATCH 21/63] Remove edit_user API endpoint and test
---
project/accounts/api.py | 24 ------------------------
project/accounts/tests/test_api.py | 16 ----------------
project/accounts/urls/api.py | 7 ++++++-
3 files changed, 6 insertions(+), 41 deletions(-)
diff --git a/project/accounts/api.py b/project/accounts/api.py
index c7541e10..263e4b12 100644
--- a/project/accounts/api.py
+++ b/project/accounts/api.py
@@ -216,30 +216,6 @@ def get_feed(request):
return HttpResponseBadRequest(reason=str(e))
-@login_required
-def edit_user(request):
- """
- Edit Profile Model
- """
-
- profile = Profile.objects.get(user=request.user)
- data = {
- "first_name": request.POST.get("first_name", profile.first_name),
- "last_name": request.POST.get("last_name", profile.last_name),
- "about_me": request.POST.get("about_me", profile.about_me),
- }
-
- profile.__dict__.update(data)
- try:
- profile.save()
- except Exception as e:
- return HttpResponseServerError(reason=str(e))
-
- profile.refresh_from_db()
-
- return JsonResponse(Profile.objects.summarize(profile))
-
-
@login_required
def upload_profile_image(request):
"""This function is used to allow users to upload profile photos"""
diff --git a/project/accounts/tests/test_api.py b/project/accounts/tests/test_api.py
index fca55533..16aea831 100644
--- a/project/accounts/tests/test_api.py
+++ b/project/accounts/tests/test_api.py
@@ -168,22 +168,6 @@ def test_nonexistent_user_account_data(self):
self.assertIn("not found", content["error"])
-class EditUserTests(BaseTestCase):
- """A class to test edit_user function"""
-
- def test_first_name_last_name_about_fields_can_be_editable(self):
- """Whether first_name, last_name and about_me fields can be edited"""
-
- self.client.login(username="newuser", password="password123")
- data = {"first_name": "First", "last_name": "Last", "about_me": "About me"}
- response = self.client.post(reverse("edit_user"), data=data)
- self.user.profile.refresh_from_db()
- self.assertEqual(response.status_code, 200)
- self.assertEqual(self.user.profile.first_name, "First")
- self.assertEqual(self.user.profile.last_name, "Last")
- self.assertEqual(self.user.profile.about_me, "About me")
-
-
class DeleteUserTests(BaseTestCase):
def test_delete_user_removes_from_database(self):
self.delete_dummy = get_user_model().objects.create_user(
diff --git a/project/accounts/urls/api.py b/project/accounts/urls/api.py
index 0928868e..35256e8d 100644
--- a/project/accounts/urls/api.py
+++ b/project/accounts/urls/api.py
@@ -2,12 +2,17 @@
from django.urls import path
urlpatterns = [
+ # TODO: port to Django view
path("feed/", api.get_feed, name="get_feed"),
- path("edituser/", api.edit_user, name="edit_user"),
+ # TODO: port to Django view
path("deleteuser/", api.delete_user, name="delete_user"),
+ # TODO: port to Django view
path("upload_profile/", api.upload_profile_image, name="upload_profile"),
+ # TODO: port to Django view
path("follow/", api.request_follow, name="follow_user"),
+ # TODO: port to Django view
path("unfollow/", api.request_unfollow, name="unfollow_user"),
+ # TODO: port to Django view
path(
"edit_user_categories/", api.edit_user_categories, name="edit_user_categories"
),
From c0361a37485d4578e9695f6ee499669aed9678c8 Mon Sep 17 00:00:00 2001
From: Brylie Christopher Oxley
Date: Tue, 11 Oct 2022 14:17:41 +0300
Subject: [PATCH 22/63] Small todo
---
project/accounts/templates/accounts/feed.html | 1 +
1 file changed, 1 insertion(+)
diff --git a/project/accounts/templates/accounts/feed.html b/project/accounts/templates/accounts/feed.html
index 93c17a33..3a57943a 100644
--- a/project/accounts/templates/accounts/feed.html
+++ b/project/accounts/templates/accounts/feed.html
@@ -247,6 +247,7 @@
{% endblock content %}
{% block extra_js %}
+
+{% endblock extra_js %}
diff --git a/project/accounts/urls/api.py b/project/accounts/urls/api.py
index 35256e8d..1054b32f 100644
--- a/project/accounts/urls/api.py
+++ b/project/accounts/urls/api.py
@@ -5,8 +5,6 @@
# TODO: port to Django view
path("feed/", api.get_feed, name="get_feed"),
# TODO: port to Django view
- path("deleteuser/", api.delete_user, name="delete_user"),
- # TODO: port to Django view
path("upload_profile/", api.upload_profile_image, name="upload_profile"),
# TODO: port to Django view
path("follow/", api.request_follow, name="follow_user"),
diff --git a/project/accounts/urls/urls.py b/project/accounts/urls/urls.py
index 4702406b..0fa72ef0 100644
--- a/project/accounts/urls/urls.py
+++ b/project/accounts/urls/urls.py
@@ -7,6 +7,7 @@
RegisterView,
SettingsView,
UserProfileView,
+ expunge_user,
)
from django.contrib.auth import views as auth_views
from django.urls import path
@@ -46,4 +47,5 @@
PasswordResetCompleteView.as_view(),
name="accounts_password_reset_complete",
),
+ path("accounts/expunge/", expunge_user, name="expunge_user"),
]
diff --git a/project/accounts/views.py b/project/accounts/views.py
index ce380ba2..8cdd3d8a 100644
--- a/project/accounts/views.py
+++ b/project/accounts/views.py
@@ -10,9 +10,10 @@
from django.conf import settings
from django.contrib.auth import get_user_model, login
from django.contrib.auth import views as auth_views
+from django.contrib.auth.decorators import login_required
from django.contrib.auth.mixins import LoginRequiredMixin
from django.contrib.sites.shortcuts import get_current_site
-from django.shortcuts import get_object_or_404
+from django.shortcuts import get_object_or_404, redirect
from django.template.response import TemplateResponse
from django.urls import reverse_lazy
from django.utils.encoding import force_str
@@ -159,3 +160,37 @@ def get(self, request, username=None):
"user": user,
},
)
+
+
+@login_required
+def expunge_user(request):
+ """
+ Delete User Information
+ """
+
+ user_model = get_user_model()
+ user = get_object_or_404(user_model, username=request.user.username)
+
+ profile = get_object_or_404(Profile, user=user)
+
+ # Expunge personally identifiable data in user
+ expunged_user_data = {
+ "is_active": False,
+ "email": "",
+ "first_name": "",
+ "last_name": "",
+ "username": f"expunged-{ user.id }",
+ }
+ user.__dict__.update(expunged_user_data)
+ user.save()
+
+ # Expunge personally identifiable data in profile
+ expunged_profile_data = {
+ "first_name": "",
+ "last_name": "",
+ "about_me": "",
+ }
+ profile.__dict__.update(expunged_profile_data)
+ profile.save()
+
+ return redirect("/")
From d58b28f683eaa352835080a2a6fe969b4042c99b Mon Sep 17 00:00:00 2001
From: Brylie Christopher Oxley
Date: Wed, 12 Oct 2022 09:58:20 +0300
Subject: [PATCH 24/63] Remove ProfileManager
---
project/accounts/models.py | 74 ++++----------------------------------
1 file changed, 7 insertions(+), 67 deletions(-)
diff --git a/project/accounts/models.py b/project/accounts/models.py
index 93e285e2..462c033a 100644
--- a/project/accounts/models.py
+++ b/project/accounts/models.py
@@ -1,16 +1,16 @@
-from django.contrib.auth.models import AbstractUser
-import os
import io
-from django.core.files.storage import default_storage
+import os
+
+from categories.models import Category
+from common.utils import PathAndRename
from django.conf import settings
+from django.contrib.auth.models import AbstractUser
+from django.core.files.storage import default_storage
+from django.core.files.uploadedfile import InMemoryUploadedFile
from django.db import models
from PIL import Image, ImageOps
-from django.core.files.uploadedfile import InMemoryUploadedFile
from taggit.managers import TaggableManager
-from categories.models import Category
-from common.utils import PathAndRename
-
class User(AbstractUser):
"""
@@ -29,65 +29,6 @@ class Meta:
WHITE_BG = (255, 255, 255)
-class ProfileManager(models.Manager):
- def summarize(self, profile):
- from threads.models import Civi
-
- data = {
- "username": profile.user.username,
- "first_name": profile.first_name,
- "last_name": profile.last_name,
- "about_me": profile.about_me,
- "history": [
- Civi.objects.serialize(c)
- for c in Civi.objects.filter(author_id=profile.id).order_by("-created")
- ],
- "profile_image": profile.profile_image_url,
- "followers": self.followers(profile),
- "following": self.following(profile),
- }
- return data
-
- def chip_summarize(self, profile):
- data = {
- "username": profile.user.username,
- "first_name": profile.first_name,
- "last_name": profile.last_name,
- "profile_image": profile.profile_image_url,
- }
- return data
-
- def card_summarize(self, profile, request_profile):
- # Length at which to truncate 'about me' text
- about_me_truncate_length = 150
-
- # If 'about me' text is longer than 150 characters... add elipsis (truncate)
- ellipsis_if_too_long = (
- "" if len(profile.about_me) <= about_me_truncate_length else "..."
- )
-
- data = {
- "id": profile.user.id,
- "username": profile.user.username,
- "first_name": profile.first_name,
- "last_name": profile.last_name,
- "about_me": profile.about_me[:about_me_truncate_length]
- + ellipsis_if_too_long,
- "profile_image": profile.profile_image_url,
- "follow_state": True
- if profile in request_profile.following.all()
- else False,
- "request_profile": request_profile.first_name,
- }
- return data
-
- def followers(self, profile):
- return [self.chip_summarize(follower) for follower in profile.followers.all()]
-
- def following(self, profile):
- return [self.chip_summarize(following) for following in profile.following.all()]
-
-
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, related_name="profile")
first_name = models.CharField(max_length=63, blank=False)
@@ -109,7 +50,6 @@ class Profile(models.Model):
is_verified = models.BooleanField(default=False)
full_profile = models.BooleanField(default=False)
- objects = ProfileManager()
profile_image = models.ImageField(
upload_to=PathAndRename("profile_uploads"), blank=True, null=True
)
From c9557832c8a0d45a786274abd1fa2718ce7d84dd Mon Sep 17 00:00:00 2001
From: Brylie Christopher Oxley
Date: Wed, 12 Oct 2022 10:00:59 +0300
Subject: [PATCH 25/63] Remove redundant __init__
---
project/accounts/models.py | 3 ---
1 file changed, 3 deletions(-)
diff --git a/project/accounts/models.py b/project/accounts/models.py
index 462c033a..706597c6 100644
--- a/project/accounts/models.py
+++ b/project/accounts/models.py
@@ -89,9 +89,6 @@ def profile_image_thumb_url(self):
return "/static/img/no_image_md.png"
- def __init__(self, *args, **kwargs):
- super(Profile, self).__init__(*args, **kwargs)
-
def save(self, *args, **kwargs):
"""Image crop/resize and thumbnail creation"""
From e3455878e2eb3d8c1e98206d260408ff151fd2e5 Mon Sep 17 00:00:00 2001
From: Brylie Christopher Oxley
Date: Wed, 12 Oct 2022 10:02:16 +0300
Subject: [PATCH 26/63] Add TODO
---
project/accounts/models.py | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/project/accounts/models.py b/project/accounts/models.py
index 706597c6..10cece9e 100644
--- a/project/accounts/models.py
+++ b/project/accounts/models.py
@@ -104,6 +104,12 @@ def resize_profile_image(self):
"""
Resizes and crops the user uploaded image and creates a thumbnail version of it
"""
+
+ # TODO: try to remove this resize_profile_image method
+ # or find a more simple way to acheive the goal(s)
+ # - less disk space?
+ # - desired shape?
+
profile_image = Image.open(self.profile_image)
# Resize image
profile_image = ImageOps.fit(
From 52bda913315d397376d7a26c93725775c9fdc75c Mon Sep 17 00:00:00 2001
From: Brylie Christopher Oxley
Date: Wed, 12 Oct 2022 10:18:10 +0300
Subject: [PATCH 27/63] Move issues code to User model
---
project/accounts/api.py | 37 ++--------------------------
project/accounts/models.py | 50 ++++++++++++++++++++++++++++++++++++++
2 files changed, 52 insertions(+), 35 deletions(-)
diff --git a/project/accounts/api.py b/project/accounts/api.py
index e2d0d8ca..7e12cd11 100644
--- a/project/accounts/api.py
+++ b/project/accounts/api.py
@@ -20,7 +20,7 @@
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from rest_framework.viewsets import ModelViewSet
-from threads.models import Activity, Civi, Thread
+from threads.models import Thread
from threads.serializers import CiviSerializer, ThreadSerializer
from threads.utils import json_response
@@ -150,40 +150,7 @@ def get_profile(request, username):
user = get_user_model().objects.get(username=username)
profile = user.profile
result = Profile.objects.summarize(profile)
- result["issues"] = []
-
- # TODO: move this to a property of the user
- # lines from voted solutions through result issues append
- voted_solutions = Activity.objects.filter(
- user=user.id, civi__c_type="solution", activity_type__contains="pos"
- )
-
- solution_threads = voted_solutions.values("thread__id").distinct()
- for thread_id in solution_threads:
- thread = Thread.objects.get(id=thread_id)
- solutions = []
- solution_civis = voted_solutions.filter(thread=thread_id).values_list(
- "civi__id", flat=True
- )
- for civi_id in solution_civis:
- c = Civi.objects.get(id=civi_id)
- vote = voted_solutions.get(civi__id=civi_id).activity_type
- vote_types = {"vote_pos": "Agree", "vote_vpos": "Strongly Agree"}
- solution_item = {
- "id": c.id,
- "title": c.title,
- "body": c.body,
- "user_vote": vote_types.get(vote),
- }
- solutions.append(solution_item)
-
- my_issue_item = {
- "thread_id": thread.id,
- "thread_title": thread.title,
- "category": thread.category.name,
- "solutions": solutions,
- }
- result["issues"].append(my_issue_item)
+ result["issues"] = user.issues
if request.user.username != username:
requested_profile = Profile.objects.get(user=request.user)
diff --git a/project/accounts/models.py b/project/accounts/models.py
index 10cece9e..fb4737d0 100644
--- a/project/accounts/models.py
+++ b/project/accounts/models.py
@@ -10,6 +10,7 @@
from django.db import models
from PIL import Image, ImageOps
from taggit.managers import TaggableManager
+from threads.models import Activity, Civi, Thread
class User(AbstractUser):
@@ -22,6 +23,55 @@ class User(AbstractUser):
class Meta:
db_table = "users"
+ @property
+ def issues(self):
+ """
+ TODO: add descriptive docstring and determine a good function name.
+ TODO: see if this code can be more succinct and optimized.
+ """
+
+ issues = []
+
+ voted_solutions = self.upvoted_solutions
+
+ solution_threads = voted_solutions.values("thread__id").distinct()
+ for thread_id in solution_threads:
+ thread = Thread.objects.get(id=thread_id)
+ solutions = []
+ solution_civis = voted_solutions.filter(thread=thread_id).values_list(
+ "civi__id", flat=True
+ )
+ for civi_id in solution_civis:
+ c = Civi.objects.get(id=civi_id)
+ vote = voted_solutions.get(civi__id=civi_id).activity_type
+ vote_types = {"vote_pos": "Agree", "vote_vpos": "Strongly Agree"}
+ solution_item = {
+ "id": c.id,
+ "title": c.title,
+ "body": c.body,
+ "user_vote": vote_types.get(vote),
+ }
+ solutions.append(solution_item)
+
+ my_issue_item = {
+ "thread_id": thread.id,
+ "thread_title": thread.title,
+ "category": thread.category.name,
+ "solutions": solutions,
+ }
+ issues.append(my_issue_item)
+
+ return issues
+
+ @property
+ def upvoted_solutions(self):
+ """
+ Return solutions that this user has given a positive vote.
+ """
+ return Activity.objects.filter(
+ user=self.id, civi__c_type="solution", activity_type__contains="pos"
+ )
+
# Image manipulation constants
PROFILE_IMG_SIZE = (171, 171)
From 939717283b1a9075f2499a5df30d55b49b1d3294 Mon Sep 17 00:00:00 2001
From: Brylie Christopher Oxley
Date: Wed, 12 Oct 2022 10:20:56 +0300
Subject: [PATCH 28/63] Remove get_feed and get_profile
---
project/accounts/api.py | 47 +-----------------------------------
project/accounts/urls/api.py | 2 --
2 files changed, 1 insertion(+), 48 deletions(-)
diff --git a/project/accounts/api.py b/project/accounts/api.py
index 7e12cd11..ac14d1fa 100644
--- a/project/accounts/api.py
+++ b/project/accounts/api.py
@@ -16,13 +16,12 @@
)
from django.shortcuts import get_object_or_404
from notifications.signals import notify
-from rest_framework.decorators import action, api_view
+from rest_framework.decorators import action
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from rest_framework.viewsets import ModelViewSet
from threads.models import Thread
from threads.serializers import CiviSerializer, ThreadSerializer
-from threads.utils import json_response
class ProfileViewSet(ModelViewSet):
@@ -139,50 +138,6 @@ def get_permissions(self):
return super(ProfileViewSet, self).get_permissions()
-@api_view(["GET"])
-def get_profile(request, username):
- """
- USAGE:
- This is used to get a user profile
- """
-
- try:
- user = get_user_model().objects.get(username=username)
- profile = user.profile
- result = Profile.objects.summarize(profile)
- result["issues"] = user.issues
-
- if request.user.username != username:
- requested_profile = Profile.objects.get(user=request.user)
- if username in requested_profile.following.all():
- result["follow_state"] = True
- else:
- result["follow_state"] = False
-
- return JsonResponse(result)
-
- except get_user_model().DoesNotExist:
- return JsonResponse(
- {"error": f"User with username {username} not found"}, status=400
- )
-
-
-def get_feed(request):
- """
- USAGE:
- This is used to get a feed for a user
- """
- try:
- feed_threads = [
- Thread.objects.summarize(t) for t in Thread.objects.order_by("-created")
- ]
-
- return json_response(feed_threads)
-
- except Exception as e:
- return HttpResponseBadRequest(reason=str(e))
-
-
@login_required
def upload_profile_image(request):
"""This function is used to allow users to upload profile photos"""
diff --git a/project/accounts/urls/api.py b/project/accounts/urls/api.py
index 1054b32f..dd861e86 100644
--- a/project/accounts/urls/api.py
+++ b/project/accounts/urls/api.py
@@ -2,8 +2,6 @@
from django.urls import path
urlpatterns = [
- # TODO: port to Django view
- path("feed/", api.get_feed, name="get_feed"),
# TODO: port to Django view
path("upload_profile/", api.upload_profile_image, name="upload_profile"),
# TODO: port to Django view
From ae4a5e142daedf29f3b14cc5ffa9041cf4a1ac7a Mon Sep 17 00:00:00 2001
From: Brylie Christopher Oxley
Date: Wed, 12 Oct 2022 10:24:18 +0300
Subject: [PATCH 29/63] Remove ProfileActivationView
---
project/accounts/tests/test_views.py | 51 ++--------------------------
project/accounts/urls/urls.py | 6 ----
project/accounts/views.py | 50 +--------------------------
3 files changed, 4 insertions(+), 103 deletions(-)
diff --git a/project/accounts/tests/test_views.py b/project/accounts/tests/test_views.py
index c3741da6..0e3b270b 100644
--- a/project/accounts/tests/test_views.py
+++ b/project/accounts/tests/test_views.py
@@ -1,9 +1,8 @@
+from accounts.views import RegisterView
from django.contrib.auth import get_user_model
-from django.test import TestCase
-from django.urls import reverse, resolve
from django.contrib.auth import views as auth_views
-from accounts.models import Profile
-from accounts.views import RegisterView
+from django.test import TestCase
+from django.urls import resolve, reverse
class BaseTestCase(TestCase):
@@ -153,50 +152,6 @@ def test_anonymous_users_are_redirected_to_login_page(self):
)
-class ProfileActivationViewTests(TestCase):
- """A class to test profile activation view"""
-
- def setUp(self) -> None:
- self.response = self.client.post(
- reverse("accounts_register"),
- {
- "username": "newuser",
- "email": "newuser@email.com",
- "password": "password123",
- },
- )
- self.user = get_user_model().objects.get(username="newuser")
- self.profile = Profile.objects.get(user=self.user)
- self.activation_link = self.response.context[0]["link"]
-
- def test_activation_link(self):
- """Whether the activation link works as expected"""
-
- self.assertFalse(self.profile.is_verified)
- response = self.client.get(self.activation_link)
- self.profile.refresh_from_db()
- self.assertTrue(self.profile.is_verified)
- self.assertTemplateUsed(response, "general_message.html")
- self.assertContains(response, "Email Verification Successful")
-
- def test_activation_link_with_a_verified_user(self):
- """Whether a verified user is welcomed by already verified page"""
-
- self.client.get(self.activation_link)
- response = self.client.get(self.activation_link)
- self.assertTemplateUsed(response, "general_message.html")
- self.assertContains(response, "Email Already Verified")
-
- def test_invalid_action_link(self):
- """Whether a verified user is welcomed by verification error page"""
-
- invalid_link = self.activation_link[:-10] + "12345/"
- response = self.client.get(invalid_link)
- self.assertFalse(self.profile.is_verified)
- self.assertTemplateUsed(response, "general_message.html")
- self.assertContains(response, "Email Verification Error")
-
-
class UserProfileView(BaseTestCase):
"""A class to test user profile view"""
diff --git a/project/accounts/urls/urls.py b/project/accounts/urls/urls.py
index 0fa72ef0..df73f6b1 100644
--- a/project/accounts/urls/urls.py
+++ b/project/accounts/urls/urls.py
@@ -3,7 +3,6 @@
PasswordResetConfirmView,
PasswordResetDoneView,
PasswordResetView,
- ProfileActivationView,
RegisterView,
SettingsView,
UserProfileView,
@@ -22,11 +21,6 @@
path("register/", RegisterView.as_view(), name="accounts_register"),
path("settings/", SettingsView.as_view(), name="accounts_settings"),
path("profile//", UserProfileView.as_view(), name="profile"),
- path(
- "activate_account///",
- ProfileActivationView.as_view(),
- name="accounts_activate",
- ),
path(
"accounts/password_reset/",
PasswordResetView.as_view(),
diff --git a/project/accounts/views.py b/project/accounts/views.py
index 8cdd3d8a..364c9ec0 100644
--- a/project/accounts/views.py
+++ b/project/accounts/views.py
@@ -4,7 +4,7 @@
This module will include views for the accounts app.
"""
-from accounts.authentication import account_activation_token, send_activation_email
+from accounts.authentication import send_activation_email
from accounts.forms import ProfileEditForm, UserRegistrationForm
from accounts.models import Profile
from django.conf import settings
@@ -16,8 +16,6 @@
from django.shortcuts import get_object_or_404, redirect
from django.template.response import TemplateResponse
from django.urls import reverse_lazy
-from django.utils.encoding import force_str
-from django.utils.http import urlsafe_base64_decode
from django.views import View
from django.views.generic.edit import FormView, UpdateView
@@ -100,52 +98,6 @@ def get_initial(self):
return super(SettingsView, self).get_initial()
-class ProfileActivationView(View):
- """
- This shows different views to the user when they are verifying
- their account based on whether they are already verified or not.
- """
-
- def get(self, request, uidb64, token):
-
- try:
- uid = force_str(urlsafe_base64_decode(uidb64))
- user = get_user_model().objects.get(pk=uid)
-
- except (TypeError, ValueError, OverflowError, get_user_model().DoesNotExist):
- user = None
-
- if user is not None and account_activation_token.check_token(user, token):
- profile = user.profile
- if profile.is_verified:
- redirect_link = {"href": "/", "label": "Back to Main"}
- template_var = {
- "title": "Email Already Verified",
- "content": "You have already verified your email",
- "link": redirect_link,
- }
- else:
- profile.is_verified = True
- profile.save()
-
- redirect_link = {"href": "/", "label": "Back to Main"}
- template_var = {
- "title": "Email Verification Successful",
- "content": "Thank you for verifying your email with CiviWiki",
- "link": redirect_link,
- }
- else:
- # invalid link
- redirect_link = {"href": "/", "label": "Back to Main"}
- template_var = {
- "title": "Email Verification Error",
- "content": "Email could not be verified",
- "link": redirect_link,
- }
-
- return TemplateResponse(request, "general_message.html", template_var)
-
-
class UserProfileView(LoginRequiredMixin, View):
"""A view that shows profile for authorized users"""
From a69abea6d8d27d61e439131fdf23c453ff3aa0d4 Mon Sep 17 00:00:00 2001
From: Brylie Christopher Oxley
Date: Wed, 12 Oct 2022 10:48:29 +0300
Subject: [PATCH 30/63] Avoid circular dependencies
---
project/accounts/models.py | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/project/accounts/models.py b/project/accounts/models.py
index fb4737d0..fa54125b 100644
--- a/project/accounts/models.py
+++ b/project/accounts/models.py
@@ -10,7 +10,7 @@
from django.db import models
from PIL import Image, ImageOps
from taggit.managers import TaggableManager
-from threads.models import Activity, Civi, Thread
+from threads.models import Activity
class User(AbstractUser):
@@ -29,6 +29,8 @@ def issues(self):
TODO: add descriptive docstring and determine a good function name.
TODO: see if this code can be more succinct and optimized.
"""
+ # Avoid circular dependencies
+ from threads.models import Civi, Thread
issues = []
From 530f073b1b0ae931c8c4626c484ff4b470dbc409 Mon Sep 17 00:00:00 2001
From: Brylie Christopher Oxley
Date: Wed, 12 Oct 2022 10:49:06 +0300
Subject: [PATCH 31/63] Add related_name for all ForeignKeys
---
...ity_civi_alter_activity_thread_and_more.py | 105 ++++++++++++++++++
project/threads/migrations/max_migration.txt | 2 +-
project/threads/models.py | 66 +++++++++--
3 files changed, 160 insertions(+), 13 deletions(-)
create mode 100644 project/threads/migrations/0007_alter_activity_civi_alter_activity_thread_and_more.py
diff --git a/project/threads/migrations/0007_alter_activity_civi_alter_activity_thread_and_more.py b/project/threads/migrations/0007_alter_activity_civi_alter_activity_thread_and_more.py
new file mode 100644
index 00000000..915c6cf6
--- /dev/null
+++ b/project/threads/migrations/0007_alter_activity_civi_alter_activity_thread_and_more.py
@@ -0,0 +1,105 @@
+# Generated by Django 4.1.2 on 2022-10-12 07:45
+
+import django.db.models.deletion
+from django.conf import settings
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ("categories", "0002_alter_category_options"),
+ migrations.swappable_dependency(settings.AUTH_USER_MODEL),
+ ("threads", "0006_alter_civi_linked_civis"),
+ ]
+
+ operations = [
+ migrations.AlterField(
+ model_name="activity",
+ name="civi",
+ field=models.ForeignKey(
+ default=None,
+ null=True,
+ on_delete=django.db.models.deletion.PROTECT,
+ related_name="activities",
+ to="threads.civi",
+ ),
+ ),
+ migrations.AlterField(
+ model_name="activity",
+ name="thread",
+ field=models.ForeignKey(
+ default=None,
+ null=True,
+ on_delete=django.db.models.deletion.PROTECT,
+ related_name="activities",
+ to="threads.thread",
+ ),
+ ),
+ migrations.AlterField(
+ model_name="activity",
+ name="user",
+ field=models.ForeignKey(
+ default=None,
+ null=True,
+ on_delete=django.db.models.deletion.PROTECT,
+ related_name="activities",
+ to=settings.AUTH_USER_MODEL,
+ ),
+ ),
+ migrations.AlterField(
+ model_name="rebuttal",
+ name="author",
+ field=models.ForeignKey(
+ default=None,
+ null=True,
+ on_delete=django.db.models.deletion.PROTECT,
+ related_name="rebuttals",
+ to=settings.AUTH_USER_MODEL,
+ ),
+ ),
+ migrations.AlterField(
+ model_name="rebuttal",
+ name="response",
+ field=models.ForeignKey(
+ default=None,
+ null=True,
+ on_delete=django.db.models.deletion.PROTECT,
+ related_name="rebuttals",
+ to="threads.response",
+ ),
+ ),
+ migrations.AlterField(
+ model_name="response",
+ name="author",
+ field=models.ForeignKey(
+ default=None,
+ null=True,
+ on_delete=django.db.models.deletion.PROTECT,
+ related_name="responses",
+ to=settings.AUTH_USER_MODEL,
+ ),
+ ),
+ migrations.AlterField(
+ model_name="thread",
+ name="author",
+ field=models.ForeignKey(
+ default=None,
+ null=True,
+ on_delete=django.db.models.deletion.PROTECT,
+ related_name="threads",
+ to=settings.AUTH_USER_MODEL,
+ ),
+ ),
+ migrations.AlterField(
+ model_name="thread",
+ name="category",
+ field=models.ForeignKey(
+ default=None,
+ null=True,
+ on_delete=django.db.models.deletion.PROTECT,
+ related_name="threads",
+ to="categories.category",
+ ),
+ ),
+ ]
diff --git a/project/threads/migrations/max_migration.txt b/project/threads/migrations/max_migration.txt
index b866b231..3b7302ff 100644
--- a/project/threads/migrations/max_migration.txt
+++ b/project/threads/migrations/max_migration.txt
@@ -1 +1 @@
-0006_alter_civi_linked_civis
+0007_alter_activity_civi_alter_activity_thread_and_more
diff --git a/project/threads/models.py b/project/threads/models.py
index 219142dc..917a6e80 100644
--- a/project/threads/models.py
+++ b/project/threads/models.py
@@ -4,15 +4,15 @@
import os
from calendar import month_name
+from categories.models import Category
from common.utils import PathAndRename
from core.constants import CIVI_TYPES
from django.conf import settings
+from django.contrib.auth import get_user_model
from django.core.files.storage import default_storage
from django.core.serializers.json import DjangoJSONEncoder
from django.db import models
-from django.contrib.auth import get_user_model
from taggit.managers import TaggableManager
-from categories.models import Category
class Fact(models.Model):
@@ -64,10 +64,18 @@ def filter_by_category(self, categories):
class Thread(models.Model):
author = models.ForeignKey(
- get_user_model(), default=None, null=True, on_delete=models.PROTECT
+ get_user_model(),
+ default=None,
+ null=True,
+ on_delete=models.PROTECT,
+ related_name="threads",
)
category = models.ForeignKey(
- Category, default=None, null=True, on_delete=models.PROTECT
+ Category,
+ default=None,
+ null=True,
+ on_delete=models.PROTECT,
+ related_name="threads",
)
facts = models.ManyToManyField(Fact)
@@ -219,7 +227,11 @@ class Civi(models.Model):
on_delete=models.PROTECT,
)
thread = models.ForeignKey(
- Thread, related_name="civis", default=None, null=True, on_delete=models.PROTECT
+ Thread,
+ related_name="civis",
+ default=None,
+ null=True,
+ on_delete=models.PROTECT,
)
tags = TaggableManager()
@@ -390,7 +402,11 @@ def dict_with_score(self, requested_user_id=None):
class Response(models.Model):
author = models.ForeignKey(
- get_user_model(), default=None, null=True, on_delete=models.PROTECT
+ get_user_model(),
+ default=None,
+ null=True,
+ on_delete=models.PROTECT,
+ related_name="responses",
)
civi = models.ForeignKey(
Civi,
@@ -420,7 +436,11 @@ def get_images(self):
class CiviImage(models.Model):
objects = CiviImageManager()
- civi = models.ForeignKey(Civi, related_name="images", on_delete=models.PROTECT)
+ civi = models.ForeignKey(
+ Civi,
+ related_name="images",
+ on_delete=models.PROTECT,
+ )
title = models.CharField(max_length=255, null=True, blank=True)
image = models.ImageField(
upload_to=PathAndRename("civi_uploads"), null=True, blank=True
@@ -453,12 +473,26 @@ def votes(self, civi_id):
class Activity(models.Model):
user = models.ForeignKey(
- get_user_model(), default=None, null=True, on_delete=models.PROTECT
+ get_user_model(),
+ default=None,
+ null=True,
+ on_delete=models.PROTECT,
+ related_name="activities",
)
thread = models.ForeignKey(
- Thread, default=None, null=True, on_delete=models.PROTECT
+ Thread,
+ default=None,
+ null=True,
+ on_delete=models.PROTECT,
+ related_name="activities",
+ )
+ civi = models.ForeignKey(
+ Civi,
+ default=None,
+ null=True,
+ on_delete=models.PROTECT,
+ related_name="activities",
)
- civi = models.ForeignKey(Civi, default=None, null=True, on_delete=models.PROTECT)
activity_CHOICES = (
("vote_vneg", "Vote Strongly Disagree"),
@@ -489,10 +523,18 @@ class Meta:
class Rebuttal(models.Model):
author = models.ForeignKey(
- get_user_model(), default=None, null=True, on_delete=models.PROTECT
+ get_user_model(),
+ default=None,
+ null=True,
+ on_delete=models.PROTECT,
+ related_name="rebuttals",
)
response = models.ForeignKey(
- Response, default=None, null=True, on_delete=models.PROTECT
+ Response,
+ default=None,
+ null=True,
+ on_delete=models.PROTECT,
+ related_name="rebuttals",
)
body = models.TextField(max_length=1023)
From 1f06ef12d938cb0c483d891a9154f855796859a1 Mon Sep 17 00:00:00 2001
From: Brylie Christopher Oxley
Date: Wed, 12 Oct 2022 10:51:37 +0300
Subject: [PATCH 32/63] Avoid circular dependencies
---
project/accounts/models.py | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/project/accounts/models.py b/project/accounts/models.py
index fa54125b..0eb9010d 100644
--- a/project/accounts/models.py
+++ b/project/accounts/models.py
@@ -10,7 +10,6 @@
from django.db import models
from PIL import Image, ImageOps
from taggit.managers import TaggableManager
-from threads.models import Activity
class User(AbstractUser):
@@ -70,6 +69,9 @@ def upvoted_solutions(self):
"""
Return solutions that this user has given a positive vote.
"""
+ # Avoid circular dependencies
+ from threads.models import Activity
+
return Activity.objects.filter(
user=self.id, civi__c_type="solution", activity_type__contains="pos"
)
From 11fae1121dd9c2fbf736d0e01b71c2e32b1d0e32 Mon Sep 17 00:00:00 2001
From: Brylie Christopher Oxley
Date: Wed, 12 Oct 2022 18:29:48 +0300
Subject: [PATCH 33/63] Remove User.issues property
---
project/accounts/models.py | 42 --------------------------------------
1 file changed, 42 deletions(-)
diff --git a/project/accounts/models.py b/project/accounts/models.py
index 0eb9010d..0adc1f58 100644
--- a/project/accounts/models.py
+++ b/project/accounts/models.py
@@ -22,48 +22,6 @@ class User(AbstractUser):
class Meta:
db_table = "users"
- @property
- def issues(self):
- """
- TODO: add descriptive docstring and determine a good function name.
- TODO: see if this code can be more succinct and optimized.
- """
- # Avoid circular dependencies
- from threads.models import Civi, Thread
-
- issues = []
-
- voted_solutions = self.upvoted_solutions
-
- solution_threads = voted_solutions.values("thread__id").distinct()
- for thread_id in solution_threads:
- thread = Thread.objects.get(id=thread_id)
- solutions = []
- solution_civis = voted_solutions.filter(thread=thread_id).values_list(
- "civi__id", flat=True
- )
- for civi_id in solution_civis:
- c = Civi.objects.get(id=civi_id)
- vote = voted_solutions.get(civi__id=civi_id).activity_type
- vote_types = {"vote_pos": "Agree", "vote_vpos": "Strongly Agree"}
- solution_item = {
- "id": c.id,
- "title": c.title,
- "body": c.body,
- "user_vote": vote_types.get(vote),
- }
- solutions.append(solution_item)
-
- my_issue_item = {
- "thread_id": thread.id,
- "thread_title": thread.title,
- "category": thread.category.name,
- "solutions": solutions,
- }
- issues.append(my_issue_item)
-
- return issues
-
@property
def upvoted_solutions(self):
"""
From f9964a02b15fb545f9fb924a72a1d9657bb0a12b Mon Sep 17 00:00:00 2001
From: Brylie Christopher Oxley
Date: Wed, 12 Oct 2022 18:31:34 +0300
Subject: [PATCH 34/63] Remove unused code
---
.../templates/static/js/update_settings.js | 38 -------------------
1 file changed, 38 deletions(-)
delete mode 100644 project/core/templates/static/js/update_settings.js
diff --git a/project/core/templates/static/js/update_settings.js b/project/core/templates/static/js/update_settings.js
deleted file mode 100644
index 691ef2d8..00000000
--- a/project/core/templates/static/js/update_settings.js
+++ /dev/null
@@ -1,38 +0,0 @@
-$('.cd-popup-yes').on('click', function () {
- $.ajax({
- type: "POST",
- url: '/api/deleteuser',
- data: {
- csrfmiddlewaretoken: $("[name=csrfmiddlewaretoken]").val(),
- },
- success: function (response) {
- window.location.assign("/");
- console.log('success');
- },
- error: function (response) {
- console.log('error');
- }
- });
-});
-// Confirmation dialog (HTML/CSS/JS) from https://codyhouse.co/gem/simple-confirmation-popup
-jQuery(document).ready(function ($) {
- //open popup
- $('.cd-popup-trigger').on('click', function (event) {
- event.preventDefault();
- $('.cd-popup').addClass('is-visible');
- });
-
- //close popup
- $('.cd-popup').on('click', function (event) {
- if ($(event.target).is('.cd-popup-close') || $(event.target).is('.cd-popup') || $(event.target).is('.cd-popup-no')) {
- event.preventDefault();
- $(this).removeClass('is-visible');
- }
- });
- //close popup when clicking the esc keyboard button
- $(document).keyup(function (event) {
- if (event.which == '27') {
- $('.cd-popup').removeClass('is-visible');
- }
- });
-});
\ No newline at end of file
From af0aff4b2033bfa4cb7114997c1af84ce90e3998 Mon Sep 17 00:00:00 2001
From: Brylie Christopher Oxley
Date: Wed, 12 Oct 2022 18:45:16 +0300
Subject: [PATCH 35/63] Add profile image to settings form
---
project/accounts/templates/accounts/settings.html | 14 ++++++++++++++
project/accounts/views.py | 1 +
2 files changed, 15 insertions(+)
diff --git a/project/accounts/templates/accounts/settings.html b/project/accounts/templates/accounts/settings.html
index 44b8f766..4ca5960a 100644
--- a/project/accounts/templates/accounts/settings.html
+++ b/project/accounts/templates/accounts/settings.html
@@ -34,6 +34,13 @@
{{form.last_name}}
+
+
+ {{form.profile_image.errors}}
+ {{form.profile_image.label_tag}}
+ {{form.profile_image}}
+
+
{{form.username.errors}}
@@ -53,6 +60,13 @@
{{form.about_me}}
+
+
+ {{form.categories.errors}}
+ {{form.categories.label_tag}}
+ {{form.categories}}
+
+
{%if not readonly %}
diff --git a/project/accounts/views.py b/project/accounts/views.py
index 364c9ec0..a045517c 100644
--- a/project/accounts/views.py
+++ b/project/accounts/views.py
@@ -93,6 +93,7 @@ def get_initial(self):
"first_name": profile.first_name or None,
"last_name": profile.last_name or None,
"about_me": profile.about_me or None,
+ "profile_image": profile.profile_image or None,
}
)
return super(SettingsView, self).get_initial()
From f61c4ac31de645b7faad6578c67a1c40924a2dc1 Mon Sep 17 00:00:00 2001
From: Brylie Christopher Oxley
Date: Wed, 12 Oct 2022 18:49:28 +0300
Subject: [PATCH 36/63] Remove "readonly" settings page
---
project/accounts/forms.py | 18 +++++-------------
1 file changed, 5 insertions(+), 13 deletions(-)
diff --git a/project/accounts/forms.py b/project/accounts/forms.py
index 5b3573d8..af0d18db 100644
--- a/project/accounts/forms.py
+++ b/project/accounts/forms.py
@@ -1,11 +1,13 @@
import re
-from django.core.files.images import get_image_dimensions
+
+from accounts.models import Profile
from django import forms
-from django.forms.models import ModelForm
from django.contrib.auth import get_user_model
+from django.core.files.images import get_image_dimensions
+from django.forms.models import ModelForm
from django.utils.translation import gettext_lazy as _
+
from .reserved_usernames import RESERVED_USERNAMES
-from accounts.models import Profile
class UserRegistrationForm(ModelForm):
@@ -106,16 +108,6 @@ class ProfileEditForm(forms.ModelForm):
Form for updating Profile data
"""
- def __init__(self, *args, **kwargs):
- readonly = kwargs.pop("readonly", False)
- super(ProfileEditForm, self).__init__(*args, **kwargs)
- if readonly:
- self.disable_fields()
-
- def disable_fields(self):
- for _key, value in self.fields.items():
- value.disabled = True
-
class Meta:
model = Profile
fields = [
From 9b82046b5b187053db8333980b65d424628a7554 Mon Sep 17 00:00:00 2001
From: Brylie Christopher Oxley
Date: Wed, 12 Oct 2022 18:55:41 +0300
Subject: [PATCH 37/63] Add multipart/form-data for image upload
---
project/accounts/templates/accounts/settings.html | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/project/accounts/templates/accounts/settings.html b/project/accounts/templates/accounts/settings.html
index 4ca5960a..8fb956d2 100644
--- a/project/accounts/templates/accounts/settings.html
+++ b/project/accounts/templates/accounts/settings.html
@@ -20,7 +20,7 @@
{{form.non_field_errors}}
-