aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGravatar Sebastiaan Zeeff <[email protected]>2020-03-01 12:42:18 +0100
committerGravatar GitHub <[email protected]>2020-03-01 12:42:18 +0100
commit36ccee0b45054de2f475735ad5b95acfa96c8cea (patch)
tree9d3722647898d9b47980c32d6b968ea288b008c2
parentMerge branch 'master' into deleted-messages-visible-line-endings (diff)
parentMerge pull request #337 from python-discord/feat/deps/s335/wiki-pypi (diff)
Merge branch 'master' into deleted-messages-visible-line-endings
-rw-r--r--.github/CODEOWNERS2
-rw-r--r--.gitignore3
-rw-r--r--Pipfile4
-rw-r--r--Pipfile.lock318
-rw-r--r--README.md13
-rw-r--r--docker-compose.yml1
-rw-r--r--docker/wheels/wiki-0.5.dev20190420204942-py3-none-any.whlbin1287002 -> 0 bytes
-rwxr-xr-xmanage.py2
-rw-r--r--pydis_site/apps/api/migrations/0050_remove_infractions_active_default_value.py18
-rw-r--r--pydis_site/apps/api/models/bot/infraction.py1
-rw-r--r--pydis_site/apps/api/serializers.py2
-rw-r--r--pydis_site/apps/api/tests/test_infractions.py117
-rw-r--r--pydis_site/apps/api/tests/test_reminders.py196
-rw-r--r--pydis_site/settings.py34
-rw-r--r--pydis_site/static/images/sponsors/adafruit.pngbin7605 -> 11705 bytes
-rw-r--r--pydis_site/static/images/sponsors/jetbrains.pngbin53742 -> 177467 bytes
-rw-r--r--pydis_site/static/images/sponsors/sentry.pngbin0 -> 13895 bytes
-rw-r--r--pydis_site/templates/base/navbar.html4
-rw-r--r--pydis_site/templates/home/index.html15
19 files changed, 537 insertions, 193 deletions
diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
index 8aa16827..cf5f1590 100644
--- a/.github/CODEOWNERS
+++ b/.github/CODEOWNERS
@@ -1 +1 @@
-* @core-developers
+* @python-discord/core-developers
diff --git a/.gitignore b/.gitignore
index b47aa2c5..9ce09469 100644
--- a/.gitignore
+++ b/.gitignore
@@ -101,6 +101,9 @@ ENV/
# PyCharm
.idea/
+# VSCode
+.vscode/
+
# RethinkDB data
rethinkdb_data/
diff --git a/Pipfile b/Pipfile
index c765d557..3327d72e 100644
--- a/Pipfile
+++ b/Pipfile
@@ -17,11 +17,11 @@ django-crispy-bulma = ">=0.1.2,<2.0"
whitenoise = "==4.1.2"
requests = "~=2.21"
pygments = "~=2.3.1"
-#wiki = {git = "https://github.com/python-discord/django-wiki.git"}
-wiki = {path = "./docker/wheels/wiki-0.5.dev20190420204942-py3-none-any.whl"}
+wiki = "~=0.5.0"
pyyaml = "~=5.1"
pyuwsgi = {version = "~=2.0", sys_platform = "!='win32'"}
django-allauth = "~=0.40"
+sentry-sdk = "~=0.14"
[dev-packages]
coverage = "~=4.5.3"
diff --git a/Pipfile.lock b/Pipfile.lock
index fea7251c..e949f3f9 100644
--- a/Pipfile.lock
+++ b/Pipfile.lock
@@ -1,7 +1,7 @@
{
"_meta": {
"hash": {
- "sha256": "dc3468691aed07cf8a9238256cc1b273669d0331a11e105b2d6adc1e19803020"
+ "sha256": "1384fa13d1eb81fe566f2d395da1d8ca986bc52a5ed7b0c133965df08793c366"
},
"pipfile-spec": 6,
"requires": {
@@ -46,32 +46,32 @@
},
"django": {
"hashes": [
- "sha256:a4ad4f6f9c6a4b7af7e2deec8d0cbff28501852e5010d6c2dc695d3d1fae7ca0",
- "sha256:fa98ec9cc9bf5d72a08ebf3654a9452e761fbb8566e3f80de199cbc15477e891"
+ "sha256:1226168be1b1c7efd0e66ee79b0e0b58b2caa7ed87717909cd8a57bb13a7079a",
+ "sha256:9a4635813e2d498a3c01b10c701fe4a515d76dd290aaa792ccb65ca4ccb6b038"
],
"index": "pypi",
- "version": "==2.2.8"
+ "version": "==2.2.10"
},
"django-allauth": {
"hashes": [
- "sha256:6a189fc4d3ee23596c3fd6e9f49c59b5b15618980118171a50675dd6a27cc589"
+ "sha256:7ab91485b80d231da191d5c7999ba93170ef1bf14ab6487d886598a1ad03e1d8"
],
"index": "pypi",
- "version": "==0.40.0"
+ "version": "==0.41.0"
},
"django-classy-tags": {
"hashes": [
- "sha256:38b4546a8053499e2fef7af679a58d7c868298717d645b8b8227acba5fd4bf2b"
+ "sha256:ad6a25fc2b58a098f00d86bd5e5dad47922f5ca4e744bc3cccb7b4be5bc35eb1"
],
- "version": "==0.9.0"
+ "version": "==1.0.0"
},
"django-crispy-bulma": {
"hashes": [
- "sha256:0d982e217a95706e0bbecd9f43990c191b071a20287478c7847ff096567e6e64",
- "sha256:2067cce1f481f9f6fcbcde86eb314eb4d5786e5a955907e1fd8359f319191b91"
+ "sha256:6ce8f6df87442040fbba39baede7aba7026d71359376e3ce7eecb7695b09c02b",
+ "sha256:72a61a1ed87611c87347553f57879dd05e21b4cedffaf9c676971488a0f065b2"
],
"index": "pypi",
- "version": "==0.1.2"
+ "version": "==0.2"
},
"django-crispy-forms": {
"hashes": [
@@ -128,17 +128,17 @@
},
"django-sekizai": {
"hashes": [
- "sha256:642a3356fe92fe9b5ccc97f320b64edd2cfcb3b2fbb113e8a26dad9a1f3b5e14"
+ "sha256:e2f6e666d4dd9d3ecc27284acb85ef709e198014f5d5af8c6d54ed04c2d684d9"
],
- "version": "==1.0.0"
+ "version": "==1.1.0"
},
"django-simple-bulma": {
"hashes": [
- "sha256:ca2e4dbda5cf2d697cef91701a9fd7e58cecec93a76897158c4d7e135aa13842",
- "sha256:f3e4f47680ed6fad11c30ba932ecca95c66690204ee2be4dcd0525c07f64b06a"
+ "sha256:a1462088791af5c65d2ea3b5a517a481dd8afb35b324979cdeefa6f3e6c58d3d",
+ "sha256:a93daf425353834db96840ca4aa7744c796899243f114e73b8159724ce4573c1"
],
"index": "pypi",
- "version": "==1.1.8"
+ "version": "==1.2.0"
},
"djangorestframework": {
"hashes": [
@@ -164,10 +164,10 @@
},
"idna": {
"hashes": [
- "sha256:c357b3f628cf53ae2c4c05627ecc484553142ca23264e593d327bcde5e9c3407",
- "sha256:ea8b7f6188e6fa117537c3df7da9fc686d485087abf6ac197f9c46432f7e4a3c"
+ "sha256:7588d1c14ae4c77d74036e8c22ff447b26d0fde8f007354fd48a7814db15b7cb",
+ "sha256:a068a21ceac8a4d63dbfd964670474107f541babbd2250d61922f029858365fa"
],
- "version": "==2.8"
+ "version": "==2.9"
},
"libsass": {
"hashes": [
@@ -192,10 +192,10 @@
},
"markdown": {
"hashes": [
- "sha256:c00429bd503a47ec88d5e30a751e147dcb4c6889663cd3e2ba0afe858e009baa",
- "sha256:d02e0f9b04c500cde6637c11ad7c72671f359b87b9fe924b2383649d8841db7c"
+ "sha256:2e50876bcdd74517e7b71f3e7a76102050edec255b3983403f1a63e7c8a41e7a",
+ "sha256:56a46ac655704b91e5b7e6326ce43d5ef72411376588afa1dd90e881b83c7e8c"
],
- "version": "==3.0.1"
+ "version": "==3.1.1"
},
"oauthlib": {
"hashes": [
@@ -326,30 +326,28 @@
},
"pyyaml": {
"hashes": [
- "sha256:0113bc0ec2ad727182326b61326afa3d1d8280ae1122493553fd6f4397f33df9",
- "sha256:01adf0b6c6f61bd11af6e10ca52b7d4057dd0be0343eb9283c878cf3af56aee4",
- "sha256:5124373960b0b3f4aa7df1707e63e9f109b5263eca5976c66e08b1c552d4eaf8",
- "sha256:5ca4f10adbddae56d824b2c09668e91219bb178a1eee1faa56af6f99f11bf696",
- "sha256:7907be34ffa3c5a32b60b95f4d95ea25361c951383a894fec31be7252b2b6f34",
- "sha256:7ec9b2a4ed5cad025c2278a1e6a19c011c80a3caaac804fd2d329e9cc2c287c9",
- "sha256:87ae4c829bb25b9fe99cf71fbb2140c448f534e24c998cc60f39ae4f94396a73",
- "sha256:9de9919becc9cc2ff03637872a440195ac4241c80536632fffeb6a1e25a74299",
- "sha256:a5a85b10e450c66b49f98846937e8cfca1db3127a9d5d1e31ca45c3d0bef4c5b",
- "sha256:b0997827b4f6a7c286c01c5f60384d218dca4ed7d9efa945c3e1aa623d5709ae",
- "sha256:b631ef96d3222e62861443cc89d6563ba3eeb816eeb96b2629345ab795e53681",
- "sha256:bf47c0607522fdbca6c9e817a6e81b08491de50f3766a7a0e6a5be7905961b41",
- "sha256:f81025eddd0327c7d4cfe9b62cf33190e1e736cc6e97502b3ec425f574b3e7a8"
+ "sha256:059b2ee3194d718896c0ad077dd8c043e5e909d9180f387ce42012662a4946d6",
+ "sha256:1cf708e2ac57f3aabc87405f04b86354f66799c8e62c28c5fc5f88b5521b2dbf",
+ "sha256:24521fa2890642614558b492b473bee0ac1f8057a7263156b02e8b14c88ce6f5",
+ "sha256:4fee71aa5bc6ed9d5f116327c04273e25ae31a3020386916905767ec4fc5317e",
+ "sha256:70024e02197337533eef7b85b068212420f950319cc8c580261963aefc75f811",
+ "sha256:74782fbd4d4f87ff04159e986886931456a1894c61229be9eaf4de6f6e44b99e",
+ "sha256:940532b111b1952befd7db542c370887a8611660d2b9becff75d39355303d82d",
+ "sha256:cb1f2f5e426dc9f07a7681419fe39cee823bb74f723f36f70399123f439e9b20",
+ "sha256:dbbb2379c19ed6042e8f11f2a2c66d39cceb8aeace421bfc29d085d93eda3689",
+ "sha256:e3a057b7a64f1222b56e47bcff5e4b94c4f61faac04c7c4ecb1985e18caa3994",
+ "sha256:e9f45bd5b92c7974e59bcd2dcc8631a6b6cc380a904725fce7bc08872e691615"
],
"index": "pypi",
- "version": "==5.1.2"
+ "version": "==5.3"
},
"requests": {
"hashes": [
- "sha256:11e007a8a2aa0323f5a921e9e6a2d7e4e67d9877e85773fba9ba6419025cbeb4",
- "sha256:9cf5292fcd0f598c671cfc1e0d7d1a7f13bb8085e9a590f48c010551dc6c4b31"
+ "sha256:43999036bfa82904b6af1d99e4882b560e5e2c68e5c4b0aa03b655f3d7d73fee",
+ "sha256:b3f43d496c6daba4493e7c431722aeb7dbc6288f52a6e04e7b6023b0247817e6"
],
"index": "pypi",
- "version": "==2.22.0"
+ "version": "==2.23.0"
},
"requests-oauthlib": {
"hashes": [
@@ -358,19 +356,27 @@
],
"version": "==1.3.0"
},
+ "sentry-sdk": {
+ "hashes": [
+ "sha256:b06dd27391fd11fb32f84fe054e6a64736c469514a718a99fb5ce1dff95d6b28",
+ "sha256:e023da07cfbead3868e1e2ba994160517885a32dfd994fc455b118e37989479b"
+ ],
+ "index": "pypi",
+ "version": "==0.14.1"
+ },
"six": {
"hashes": [
- "sha256:1f1b7d42e254082a9db6279deae68afb421ceba6158efa6131de7b3003ee93fd",
- "sha256:30f610279e8b2578cab6db20741130331735c781b56053c59c4076da27f06b66"
+ "sha256:236bdbdce46e6e6a3d61a337c0f8b763ca1e8717c03b369e87a7ec7ce1319c0a",
+ "sha256:8f3cd2e254d8f793e7f3d6d9df77b92252b52637291d0f0da013c76ea2724b6c"
],
- "version": "==1.13.0"
+ "version": "==1.14.0"
},
"sorl-thumbnail": {
"hashes": [
- "sha256:8dfe5fda91a5047d1d35a0b9effe7b000764a01d648e15ca076f44e9c34b6dbd",
- "sha256:d9e3f018d19293824803e4ffead96b19dfcd44fa7987cea392f50436817bef34"
+ "sha256:66771521f3c0ed771e1ce8e1aaf1639ebff18f7f5a40cfd3083da8f0fe6c7c99",
+ "sha256:7162639057dff222a651bacbdb6bd6f558fc32946531d541fc71e10c0167ebdf"
],
- "version": "==12.5.0"
+ "version": "==12.6.3"
},
"sqlparse": {
"hashes": [
@@ -381,10 +387,10 @@
},
"urllib3": {
"hashes": [
- "sha256:a8a318824cc77d1fd4b2bec2ded92646630d7fe8619497b142c84a9e6f5a7293",
- "sha256:f3c5fd51747d450d4dcf6f923c81f78f811aab8205fda64b0aba34a4e48b0745"
+ "sha256:2f3db8b19923a873b3e5256dc9c2dedfa883e33d87c690d9c7913e1f40673cdc",
+ "sha256:87716c2d2a7121198ebcb7ce7cccf6ce5e9ba539041cfbaeecfb641dc0bf6acc"
],
- "version": "==1.25.7"
+ "version": "==1.25.8"
},
"webencodings": {
"hashes": [
@@ -403,13 +409,21 @@
},
"wiki": {
"hashes": [
- "sha256:73a53bc770ce6b1d2ea6916d81ccefe40751d87b30556fa3b992c85b7fde8534"
+ "sha256:51096f0139ebd8d79e6acbef2784f9792884dc16a0a14c2deb124ae20f1886b9",
+ "sha256:8f7f687c2a24e74ffefac2c06b415291cb12e51659fb1946e3d5c7e783431520"
],
- "path": "./docker/wheels/wiki-0.5.dev20190420204942-py3-none-any.whl",
- "version": "==0.5.dev20190420204942"
+ "index": "pypi",
+ "version": "==0.5"
}
},
"develop": {
+ "appdirs": {
+ "hashes": [
+ "sha256:9e5896d1372858f8dd3344faf4e5014d21849c756c8d5701f78f8a103b372d92",
+ "sha256:d8b24664561d0d34ddfaec54636d502d7cea6e29c3eaf68f3df6180863e2166e"
+ ],
+ "version": "==1.4.3"
+ },
"aspy.yaml": {
"hashes": [
"sha256:463372c043f70160a9ec950c3f1e4c3a82db5fca01d334b6bc89c7164d744bdc",
@@ -433,10 +447,10 @@
},
"cfgv": {
"hashes": [
- "sha256:edb387943b665bf9c434f717bf630fa78aecd53d5900d2e05da6ad6048553144",
- "sha256:fbd93c9ab0a523bf7daec408f3be2ed99a980e20b2d19b50fc184ca6b820d289"
+ "sha256:04b093b14ddf9fd4d17c53ebfd55582d27b76ed30050193c14e560770c5360eb",
+ "sha256:f22b426ed59cd2ab2b54ff96608d846c33dfb8766a67f0b4a6ce130ce244414f"
],
- "version": "==2.0.1"
+ "version": "==3.0.0"
},
"coverage": {
"hashes": [
@@ -476,6 +490,12 @@
"index": "pypi",
"version": "==4.5.4"
},
+ "distlib": {
+ "hashes": [
+ "sha256:2e166e231a26b36d6dfe35a48c4464346620f8645ed0ace01ee31822b288de21"
+ ],
+ "version": "==0.3.0"
+ },
"entrypoints": {
"hashes": [
"sha256:589f874b313739ad35be6e0cd7efde2a4e9b6fea91edcc34e58ecbb8dbe56d19",
@@ -483,21 +503,28 @@
],
"version": "==0.3"
},
+ "filelock": {
+ "hashes": [
+ "sha256:18d82244ee114f543149c66a6e0c14e9c4f8a1044b5cdaadd0f82159d6a6ff59",
+ "sha256:929b7d63ec5b7d6b71b0fa5ac14e030b3f70b75747cef1b10da9b879fef15836"
+ ],
+ "version": "==3.0.12"
+ },
"flake8": {
"hashes": [
- "sha256:19241c1cbc971b9962473e4438a2ca19749a7dd002dd1a946eaba171b4114548",
- "sha256:8e9dfa3cecb2400b3738a42c54c3043e821682b9c840b0448c0503f781130696"
+ "sha256:45681a117ecc81e870cbf1262835ae4af5e7a8b08e40b944a8a6e6b895914cfb",
+ "sha256:49356e766643ad15072a789a20915d3c91dc89fd313ccd71802303fd67e4deca"
],
"index": "pypi",
- "version": "==3.7.8"
+ "version": "==3.7.9"
},
"flake8-annotations": {
"hashes": [
- "sha256:6ac7ca1e706307686b60af8043ff1db31dc2cfc1233c8210d67a3d9b8f364736",
- "sha256:b51131007000d67217608fa028a35ff80aa400b474e5972f1f99c2cf9d26bd2e"
+ "sha256:47705be09c6e56e9e3ac1656e8f5ed70862a4657116dc472f5a56c1bdc5172b1",
+ "sha256:564702ace354e1059252755be79d082a70ae1851c86044ae1a96d0f5453280e9"
],
"index": "pypi",
- "version": "==1.1.0"
+ "version": "==1.2.0"
},
"flake8-bandit": {
"hashes": [
@@ -539,11 +566,11 @@
},
"flake8-string-format": {
"hashes": [
- "sha256:68ea72a1a5b75e7018cae44d14f32473c798cf73d75cbaed86c6a9a907b770b2",
- "sha256:774d56103d9242ed968897455ef49b7d6de272000cfa83de5814273a868832f1"
+ "sha256:65f3da786a1461ef77fca3780b314edb2853c377f2e35069723348c8917deaa2",
+ "sha256:812ff431f10576a74c89be4e85b8e075a705be39bc40c4b4278b5b13e2afa9af"
],
"index": "pypi",
- "version": "==0.2.3"
+ "version": "==0.3.0"
},
"flake8-tidy-imports": {
"hashes": [
@@ -560,33 +587,41 @@
"index": "pypi",
"version": "==0.7"
},
+ "gitdb": {
+ "hashes": [
+ "sha256:24572287933a9e5bf45260cd7a5629e5b3b190ec3c2c6a5d5e4125dd06ff4027",
+ "sha256:d82c6b76ce8496c5209adf1c0ab969a6e1a8a31510ceb5f57a206fc7c77c0fea"
+ ],
+ "version": "==4.0.1"
+ },
"gitdb2": {
"hashes": [
- "sha256:1b6df1433567a51a4a9c1a5a0de977aa351a405cc56d7d35f3388bad1f630350",
- "sha256:96bbb507d765a7f51eb802554a9cfe194a174582f772e0d89f4e87288c288b7b"
+ "sha256:0986cb4003de743f2b3aba4c828edd1ab58ce98e1c4a8acf72ef02760d4beb4e",
+ "sha256:a1c974e5fab8c2c90192c1367c81cbc54baec04244bda1816e9c8ab377d1cba3"
],
- "version": "==2.0.6"
+ "version": "==4.0.2"
},
"gitpython": {
"hashes": [
- "sha256:9c2398ffc3dcb3c40b27324b316f08a4f93ad646d5a6328cafbb871aa79f5e42",
- "sha256:c155c6a2653593ccb300462f6ef533583a913e17857cfef8fc617c246b6dc245"
+ "sha256:620b3c729bbc143b498cfea77e302999deedc55faec5b1067086c9ef90e101bc",
+ "sha256:a43a5d88a5bbc3cf32bb5223e4b4e68fd716db5e9996cad6e561bbfee6e5f4af"
],
- "version": "==3.0.5"
+ "version": "==3.0.8"
},
"identify": {
"hashes": [
- "sha256:6f44e637caa40d1b4cb37f6ed3b262ede74901d28b1cc5b1fc07360871edd65d",
- "sha256:72e9c4ed3bc713c7045b762b0d2e2115c572b85abfc1f4604f5a4fd4c6642b71"
+ "sha256:1222b648251bdcb8deb240b294f450fbf704c7984e08baa92507e4ea10b436d5",
+ "sha256:d824ebe21f38325c771c41b08a95a761db1982f1fc0eee37c6c97df3f1636b96"
],
- "version": "==1.4.9"
+ "version": "==1.4.11"
},
"importlib-metadata": {
"hashes": [
- "sha256:073a852570f92da5f744a3472af1b61e28e9f78ccf0c9117658dc32b15de7b45",
- "sha256:d95141fbfa7ef2ec65cfd945e2af7e5a6ddbd7c8d9a25e66ff3be8e3daf9f60f"
+ "sha256:06f5b3a99029c7134207dd882428a66992a9de2bef7c2b699b5641f9886c3302",
+ "sha256:b97607a1a18a5100839aec1dc26a1ea17ee0d93b20b0f008d80a5a050afb200b"
],
- "version": "==1.3.0"
+ "markers": "python_version < '3.8'",
+ "version": "==1.5.0"
},
"mccabe": {
"hashes": [
@@ -596,18 +631,11 @@
"index": "pypi",
"version": "==0.6.1"
},
- "more-itertools": {
- "hashes": [
- "sha256:b84b238cce0d9adad5ed87e745778d20a3f8487d0f0cb8b8a586816c7496458d",
- "sha256:c833ef592a0324bcc6a60e48440da07645063c453880c9477ceb22490aec1564"
- ],
- "version": "==8.0.2"
- },
"nodeenv": {
"hashes": [
- "sha256:ad8259494cf1c9034539f6cced78a1da4840a4b157e23640bc4a0c0546b0cb7a"
+ "sha256:5b2438f2e42af54ca968dd1b374d14a1194848955187b0e5e4be1f73813a5212"
],
- "version": "==1.3.3"
+ "version": "==1.3.5"
},
"pbr": {
"hashes": [
@@ -626,11 +654,11 @@
},
"pre-commit": {
"hashes": [
- "sha256:1d3c0587bda7c4e537a46c27f2c84aa006acc18facf9970bf947df596ce91f3f",
- "sha256:fa78ff96e8e9ac94c748388597693f18b041a181c94a4f039ad20f45287ba44a"
+ "sha256:8f48d8637bdae6fa70cc97db9c1dd5aa7c5c8bf71968932a380628c25978b850",
+ "sha256:f92a359477f3252452ae2e8d3029de77aec59415c16ae4189bcfba40b757e029"
],
"index": "pypi",
- "version": "==1.18.3"
+ "version": "==1.21.0"
},
"pycodestyle": {
"hashes": [
@@ -641,10 +669,10 @@
},
"pydocstyle": {
"hashes": [
- "sha256:4167fe954b8f27ebbbef2fbcf73c6e8ad1e7bb31488fce44a69fdfc4b0cd0fae",
- "sha256:a0de36e549125d0a16a72a8c8c6c9ba267750656e72e466e994c222f1b6e92cb"
+ "sha256:da7831660b7355307b32778c4a0dbfb137d89254ef31a2b2978f50fc0b4d7586",
+ "sha256:f4f5d210610c2d153fae39093d44224c17429e2ad7da12a8b419aba5c2f614b5"
],
- "version": "==5.0.1"
+ "version": "==5.0.2"
},
"pyflakes": {
"hashes": [
@@ -655,36 +683,34 @@
},
"pyyaml": {
"hashes": [
- "sha256:0113bc0ec2ad727182326b61326afa3d1d8280ae1122493553fd6f4397f33df9",
- "sha256:01adf0b6c6f61bd11af6e10ca52b7d4057dd0be0343eb9283c878cf3af56aee4",
- "sha256:5124373960b0b3f4aa7df1707e63e9f109b5263eca5976c66e08b1c552d4eaf8",
- "sha256:5ca4f10adbddae56d824b2c09668e91219bb178a1eee1faa56af6f99f11bf696",
- "sha256:7907be34ffa3c5a32b60b95f4d95ea25361c951383a894fec31be7252b2b6f34",
- "sha256:7ec9b2a4ed5cad025c2278a1e6a19c011c80a3caaac804fd2d329e9cc2c287c9",
- "sha256:87ae4c829bb25b9fe99cf71fbb2140c448f534e24c998cc60f39ae4f94396a73",
- "sha256:9de9919becc9cc2ff03637872a440195ac4241c80536632fffeb6a1e25a74299",
- "sha256:a5a85b10e450c66b49f98846937e8cfca1db3127a9d5d1e31ca45c3d0bef4c5b",
- "sha256:b0997827b4f6a7c286c01c5f60384d218dca4ed7d9efa945c3e1aa623d5709ae",
- "sha256:b631ef96d3222e62861443cc89d6563ba3eeb816eeb96b2629345ab795e53681",
- "sha256:bf47c0607522fdbca6c9e817a6e81b08491de50f3766a7a0e6a5be7905961b41",
- "sha256:f81025eddd0327c7d4cfe9b62cf33190e1e736cc6e97502b3ec425f574b3e7a8"
+ "sha256:059b2ee3194d718896c0ad077dd8c043e5e909d9180f387ce42012662a4946d6",
+ "sha256:1cf708e2ac57f3aabc87405f04b86354f66799c8e62c28c5fc5f88b5521b2dbf",
+ "sha256:24521fa2890642614558b492b473bee0ac1f8057a7263156b02e8b14c88ce6f5",
+ "sha256:4fee71aa5bc6ed9d5f116327c04273e25ae31a3020386916905767ec4fc5317e",
+ "sha256:70024e02197337533eef7b85b068212420f950319cc8c580261963aefc75f811",
+ "sha256:74782fbd4d4f87ff04159e986886931456a1894c61229be9eaf4de6f6e44b99e",
+ "sha256:940532b111b1952befd7db542c370887a8611660d2b9becff75d39355303d82d",
+ "sha256:cb1f2f5e426dc9f07a7681419fe39cee823bb74f723f36f70399123f439e9b20",
+ "sha256:dbbb2379c19ed6042e8f11f2a2c66d39cceb8aeace421bfc29d085d93eda3689",
+ "sha256:e3a057b7a64f1222b56e47bcff5e4b94c4f61faac04c7c4ecb1985e18caa3994",
+ "sha256:e9f45bd5b92c7974e59bcd2dcc8631a6b6cc380a904725fce7bc08872e691615"
],
"index": "pypi",
- "version": "==5.1.2"
+ "version": "==5.3"
},
"six": {
"hashes": [
- "sha256:1f1b7d42e254082a9db6279deae68afb421ceba6158efa6131de7b3003ee93fd",
- "sha256:30f610279e8b2578cab6db20741130331735c781b56053c59c4076da27f06b66"
+ "sha256:236bdbdce46e6e6a3d61a337c0f8b763ca1e8717c03b369e87a7ec7ce1319c0a",
+ "sha256:8f3cd2e254d8f793e7f3d6d9df77b92252b52637291d0f0da013c76ea2724b6c"
],
- "version": "==1.13.0"
+ "version": "==1.14.0"
},
- "smmap2": {
+ "smmap": {
"hashes": [
- "sha256:0555a7bf4df71d1ef4218e4807bbf9b201f910174e6e08af2e138d4e517b4dde",
- "sha256:29a9ffa0497e7f2be94ca0ed1ca1aa3cd4cf25a1f6b4f5f87f74b46ed91d609a"
+ "sha256:171484fe62793e3626c8b05dd752eb2ca01854b0c55a1efc0dc4210fccb65446",
+ "sha256:5fead614cf2de17ee0707a8c6a5f2aa5a2fc6c698c70993ba42f515485ffda78"
],
- "version": "==2.0.5"
+ "version": "==3.0.1"
},
"snowballstemmer": {
"hashes": [
@@ -695,10 +721,10 @@
},
"stevedore": {
"hashes": [
- "sha256:01d9f4beecf0fbd070ddb18e5efb10567801ba7ef3ddab0074f54e3cd4e91730",
- "sha256:e0739f9739a681c7a1fda76a102b65295e96a144ccdb552f2ae03c5f0abe8a14"
+ "sha256:18afaf1d623af5950cc0f7e75e70f917784c73b652a34a12d90b309451b5500b",
+ "sha256:a4e7dc759fb0f2e3e2f7d8ffe2358c19d45b9b8297f393ef1256858d82f69c9b"
],
- "version": "==1.31.0"
+ "version": "==1.32.0"
},
"toml": {
"hashes": [
@@ -709,50 +735,52 @@
},
"typed-ast": {
"hashes": [
- "sha256:1170afa46a3799e18b4c977777ce137bb53c7485379d9706af8a59f2ea1aa161",
- "sha256:18511a0b3e7922276346bcb47e2ef9f38fb90fd31cb9223eed42c85d1312344e",
- "sha256:262c247a82d005e43b5b7f69aff746370538e176131c32dda9cb0f324d27141e",
- "sha256:2b907eb046d049bcd9892e3076c7a6456c93a25bebfe554e931620c90e6a25b0",
- "sha256:354c16e5babd09f5cb0ee000d54cfa38401d8b8891eefa878ac772f827181a3c",
- "sha256:48e5b1e71f25cfdef98b013263a88d7145879fbb2d5185f2a0c79fa7ebbeae47",
- "sha256:4e0b70c6fc4d010f8107726af5fd37921b666f5b31d9331f0bd24ad9a088e631",
- "sha256:630968c5cdee51a11c05a30453f8cd65e0cc1d2ad0d9192819df9978984529f4",
- "sha256:66480f95b8167c9c5c5c87f32cf437d585937970f3fc24386f313a4c97b44e34",
- "sha256:71211d26ffd12d63a83e079ff258ac9d56a1376a25bc80b1cdcdf601b855b90b",
- "sha256:7954560051331d003b4e2b3eb822d9dd2e376fa4f6d98fee32f452f52dd6ebb2",
- "sha256:838997f4310012cf2e1ad3803bce2f3402e9ffb71ded61b5ee22617b3a7f6b6e",
- "sha256:95bd11af7eafc16e829af2d3df510cecfd4387f6453355188342c3e79a2ec87a",
- "sha256:bc6c7d3fa1325a0c6613512a093bc2a2a15aeec350451cbdf9e1d4bffe3e3233",
- "sha256:cc34a6f5b426748a507dd5d1de4c1978f2eb5626d51326e43280941206c209e1",
- "sha256:d755f03c1e4a51e9b24d899561fec4ccaf51f210d52abdf8c07ee2849b212a36",
- "sha256:d7c45933b1bdfaf9f36c579671fec15d25b06c8398f113dab64c18ed1adda01d",
- "sha256:d896919306dd0aa22d0132f62a1b78d11aaf4c9fc5b3410d3c666b818191630a",
- "sha256:fdc1c9bbf79510b76408840e009ed65958feba92a88833cdceecff93ae8fff66",
- "sha256:ffde2fbfad571af120fcbfbbc61c72469e72f550d676c3342492a9dfdefb8f12"
- ],
- "version": "==1.4.0"
+ "sha256:0666aa36131496aed8f7be0410ff974562ab7eeac11ef351def9ea6fa28f6355",
+ "sha256:0c2c07682d61a629b68433afb159376e24e5b2fd4641d35424e462169c0a7919",
+ "sha256:249862707802d40f7f29f6e1aad8d84b5aa9e44552d2cc17384b209f091276aa",
+ "sha256:24995c843eb0ad11a4527b026b4dde3da70e1f2d8806c99b7b4a7cf491612652",
+ "sha256:269151951236b0f9a6f04015a9004084a5ab0d5f19b57de779f908621e7d8b75",
+ "sha256:4083861b0aa07990b619bd7ddc365eb7fa4b817e99cf5f8d9cf21a42780f6e01",
+ "sha256:498b0f36cc7054c1fead3d7fc59d2150f4d5c6c56ba7fb150c013fbc683a8d2d",
+ "sha256:4e3e5da80ccbebfff202a67bf900d081906c358ccc3d5e3c8aea42fdfdfd51c1",
+ "sha256:6daac9731f172c2a22ade6ed0c00197ee7cc1221aa84cfdf9c31defeb059a907",
+ "sha256:715ff2f2df46121071622063fc7543d9b1fd19ebfc4f5c8895af64a77a8c852c",
+ "sha256:73d785a950fc82dd2a25897d525d003f6378d1cb23ab305578394694202a58c3",
+ "sha256:8c8aaad94455178e3187ab22c8b01a3837f8ee50e09cf31f1ba129eb293ec30b",
+ "sha256:8ce678dbaf790dbdb3eba24056d5364fb45944f33553dd5869b7580cdbb83614",
+ "sha256:aaee9905aee35ba5905cfb3c62f3e83b3bec7b39413f0a7f19be4e547ea01ebb",
+ "sha256:bcd3b13b56ea479b3650b82cabd6b5343a625b0ced5429e4ccad28a8973f301b",
+ "sha256:c9e348e02e4d2b4a8b2eedb48210430658df6951fa484e59de33ff773fbd4b41",
+ "sha256:d205b1b46085271b4e15f670058ce182bd1199e56b317bf2ec004b6a44f911f6",
+ "sha256:d43943ef777f9a1c42bf4e552ba23ac77a6351de620aa9acf64ad54933ad4d34",
+ "sha256:d5d33e9e7af3b34a40dc05f498939f0ebf187f07c385fd58d591c533ad8562fe",
+ "sha256:fc0fea399acb12edbf8a628ba8d2312f583bdbdb3335635db062fa98cf71fca4",
+ "sha256:fe460b922ec15dd205595c9b5b99e2f056fd98ae8f9f56b888e7a17dc2b757e7"
+ ],
+ "markers": "python_version < '3.8'",
+ "version": "==1.4.1"
},
"unittest-xml-reporting": {
"hashes": [
- "sha256:140982e4b58e4052d9ecb775525b246a96bfc1fc26097806e05ea06e9166dd6c",
- "sha256:d1fbc7a1b6c6680ccfe75b5e9701e5431c646970de049e687b4bb35ba4325d72"
+ "sha256:358bbdaf24a26d904cc1c26ef3078bca7fc81541e0a54c8961693cc96a6f35e0",
+ "sha256:9d28ddf6524cf0ff9293f61bd12e792de298f8561a5c945acea63fb437789e0e"
],
"index": "pypi",
- "version": "==2.5.1"
+ "version": "==2.5.2"
},
"virtualenv": {
"hashes": [
- "sha256:0d62c70883c0342d59c11d0ddac0d954d0431321a41ab20851facf2b222598f3",
- "sha256:55059a7a676e4e19498f1aad09b8313a38fcc0cdbe4fdddc0e9b06946d21b4bb"
+ "sha256:531b142e300d405bb9faedad4adbeb82b4098b918e35209af2adef3129274aae",
+ "sha256:5dd42a9f56307542bddc446cfd10ef6576f11910366a07609fe8d0d88fa8fb7e"
],
- "version": "==16.7.9"
+ "version": "==20.0.5"
},
"zipp": {
"hashes": [
- "sha256:3718b1cbcd963c7d4c5511a8240812904164b7f381b647143a89d3b98f9bcd8e",
- "sha256:f06903e9f1f43b12d371004b4ac7b06ab39a44adc747266928ae6debfa7b3335"
+ "sha256:12248a63bbdf7548f89cb4c7cda4681e537031eda29c02ea29674bc6854460c2",
+ "sha256:7c0f8e91abc0dc07a5068f315c52cb30c66bfbc581e5b50704c8a2f6ebae794a"
],
- "version": "==0.6.0"
+ "version": "==3.0.0"
}
}
}
diff --git a/README.md b/README.md
index 86509beb..ec2f0af3 100644
--- a/README.md
+++ b/README.md
@@ -4,14 +4,15 @@
[![Tests](https://img.shields.io/azure-devops/tests/python-discord/Python%20Discord/2?compact_message)](https://dev.azure.com/python-discord/Python%20Discord/_apis/build/status/Site?branchName=master)
[![Coverage](https://img.shields.io/azure-devops/coverage/python-discord/Python%20Discord/2/master)](https://dev.azure.com/python-discord/Python%20Discord/_apis/build/status/Site?branchName=master)
[![License](https://img.shields.io/github/license/python-discord/site)](LICENSE)
-[![Status](https://img.shields.io/website?url=https%3A%2F%2Fpythondiscord.com)](https://pythondiscord.com)
+[![Status](https://img.shields.io/website?url=https%3A%2F%2Fpythondiscord.com)][1]
-This is all of the code that is responsible for maintaining
-[our website](https://pythondiscord.com), and all of its subdomains.
+This is all of the code that is responsible for maintaining [our website][1] and all of its subdomains.
The website is built on Django and should be simple to set up and get started with.
If you happen to run into issues with setup, please don't hesitate to open an issue!
-If you're looking to contribute or play around with the code,
-take a look at the [`docs` directory](docs). If you're looking for things
-to do, check out [our issues](https://gitlab.com/python-discord/projects/site/issues).
+If you're looking to contribute or play around with the code, take a look at [the wiki][2] or the [`docs` directory](docs). If you're looking for things to do, check out [our issues][3].
+
+[1]: https://pythondiscord.com
+[2]: https://pythondiscord.com/pages/contributing/site/
+[3]: https://github.com/python-discord/site/issues
diff --git a/docker-compose.yml b/docker-compose.yml
index 3884a41f..73d2ff85 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -34,6 +34,7 @@ services:
- "127.0.0.1:8000:8000"
depends_on:
- postgres
+ tty: true
volumes:
- .:/app:ro
- staticfiles:/var/www/static
diff --git a/docker/wheels/wiki-0.5.dev20190420204942-py3-none-any.whl b/docker/wheels/wiki-0.5.dev20190420204942-py3-none-any.whl
deleted file mode 100644
index b7637e76..00000000
--- a/docker/wheels/wiki-0.5.dev20190420204942-py3-none-any.whl
+++ /dev/null
Binary files differ
diff --git a/manage.py b/manage.py
index d2996488..ee071376 100755
--- a/manage.py
+++ b/manage.py
@@ -87,7 +87,7 @@ class SiteManager:
# Get database URL based on environmental variable passed in compose
database_url = os.environ["DATABASE_URL"]
- match = re.search(r"@(\w+):(\d+)/", database_url)
+ match = re.search(r"@([\w.]+):(\d+)/", database_url)
if not match:
raise OSError("Valid DATABASE_URL environmental variable not found.")
domain = match.group(1)
diff --git a/pydis_site/apps/api/migrations/0050_remove_infractions_active_default_value.py b/pydis_site/apps/api/migrations/0050_remove_infractions_active_default_value.py
new file mode 100644
index 00000000..90c91d63
--- /dev/null
+++ b/pydis_site/apps/api/migrations/0050_remove_infractions_active_default_value.py
@@ -0,0 +1,18 @@
+# Generated by Django 2.2.6 on 2020-02-08 19:00
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('api', '0049_deletedmessage_attachments'),
+ ]
+
+ operations = [
+ migrations.AlterField(
+ model_name='infraction',
+ name='active',
+ field=models.BooleanField(help_text='Whether the infraction is still active.'),
+ ),
+ ]
diff --git a/pydis_site/apps/api/models/bot/infraction.py b/pydis_site/apps/api/models/bot/infraction.py
index 108fd3a2..f58e89a3 100644
--- a/pydis_site/apps/api/models/bot/infraction.py
+++ b/pydis_site/apps/api/models/bot/infraction.py
@@ -29,7 +29,6 @@ class Infraction(ModelReprMixin, models.Model):
)
)
active = models.BooleanField(
- default=True,
help_text="Whether the infraction is still active."
)
user = models.ForeignKey(
diff --git a/pydis_site/apps/api/serializers.py b/pydis_site/apps/api/serializers.py
index 0d1a4684..e11c4af2 100644
--- a/pydis_site/apps/api/serializers.py
+++ b/pydis_site/apps/api/serializers.py
@@ -110,7 +110,7 @@ class InfractionSerializer(ModelSerializer):
validators = [
UniqueTogetherValidator(
queryset=Infraction.objects.filter(active=True),
- fields=['user', 'type'],
+ fields=['user', 'type', 'active'],
message='This user already has an active infraction of this type.',
)
]
diff --git a/pydis_site/apps/api/tests/test_infractions.py b/pydis_site/apps/api/tests/test_infractions.py
index 7a54640e..ca87026c 100644
--- a/pydis_site/apps/api/tests/test_infractions.py
+++ b/pydis_site/apps/api/tests/test_infractions.py
@@ -7,6 +7,7 @@ from django_hosts.resolvers import reverse
from .base import APISubdomainTestCase
from ..models import Infraction, User
+from ..serializers import InfractionSerializer
class UnauthenticatedTests(APISubdomainTestCase):
@@ -54,7 +55,8 @@ class InfractionTests(APISubdomainTestCase):
type='ban',
reason='He terk my jerb!',
hidden=True,
- expires_at=dt(5018, 11, 20, 15, 52, tzinfo=timezone.utc)
+ expires_at=dt(5018, 11, 20, 15, 52, tzinfo=timezone.utc),
+ active=True
)
cls.ban_inactive = Infraction.objects.create(
user_id=cls.user.id,
@@ -184,7 +186,8 @@ class CreationTests(APISubdomainTestCase):
'type': 'ban',
'reason': 'He terk my jerb!',
'hidden': True,
- 'expires_at': '5018-11-20T15:52:00+00:00'
+ 'expires_at': '5018-11-20T15:52:00+00:00',
+ 'active': True,
}
response = self.client.post(url, data=data)
@@ -208,7 +211,8 @@ class CreationTests(APISubdomainTestCase):
url = reverse('bot:infraction-list', host='api')
data = {
'actor': self.user.id,
- 'type': 'kick'
+ 'type': 'kick',
+ 'active': False,
}
response = self.client.post(url, data=data)
@@ -222,7 +226,8 @@ class CreationTests(APISubdomainTestCase):
data = {
'user': 1337,
'actor': self.user.id,
- 'type': 'kick'
+ 'type': 'kick',
+ 'active': True,
}
response = self.client.post(url, data=data)
@@ -236,7 +241,8 @@ class CreationTests(APISubdomainTestCase):
data = {
'user': self.user.id,
'actor': self.user.id,
- 'type': 'hug'
+ 'type': 'hug',
+ 'active': True,
}
response = self.client.post(url, data=data)
@@ -251,7 +257,8 @@ class CreationTests(APISubdomainTestCase):
'user': self.user.id,
'actor': self.user.id,
'type': 'ban',
- 'expires_at': '20/11/5018 15:52:00'
+ 'expires_at': '20/11/5018 15:52:00',
+ 'active': True,
}
response = self.client.post(url, data=data)
@@ -271,7 +278,8 @@ class CreationTests(APISubdomainTestCase):
'user': self.user.id,
'actor': self.user.id,
'type': infraction_type,
- 'expires_at': '5018-11-20T15:52:00+00:00'
+ 'expires_at': '5018-11-20T15:52:00+00:00',
+ 'active': False,
}
response = self.client.post(url, data=data)
@@ -288,7 +296,8 @@ class CreationTests(APISubdomainTestCase):
'user': self.user.id,
'actor': self.user.id,
'type': infraction_type,
- 'hidden': True
+ 'hidden': True,
+ 'active': False,
}
response = self.client.post(url, data=data)
@@ -305,6 +314,7 @@ class CreationTests(APISubdomainTestCase):
'actor': self.user.id,
'type': 'note',
'hidden': False,
+ 'active': False,
}
response = self.client.post(url, data=data)
@@ -494,6 +504,16 @@ class CreationTests(APISubdomainTestCase):
reason="An active ban for the second user"
)
+ def test_integrity_error_if_missing_active_field(self):
+ pattern = 'null value in column "active" violates not-null constraint'
+ with self.assertRaisesRegex(IntegrityError, pattern):
+ Infraction.objects.create(
+ user=self.user,
+ actor=self.user,
+ type='ban',
+ reason='A reason.',
+ )
+
class ExpandedTests(APISubdomainTestCase):
@classmethod
@@ -540,7 +560,8 @@ class ExpandedTests(APISubdomainTestCase):
data = {
'user': self.user.id,
'actor': self.user.id,
- 'type': 'warning'
+ 'type': 'warning',
+ 'active': False
}
response = self.client.post(url, data=data)
@@ -569,3 +590,81 @@ class ExpandedTests(APISubdomainTestCase):
infraction = Infraction.objects.get(id=self.kick.id)
self.assertEqual(infraction.active, data['active'])
self.check_expanded_fields(response.json())
+
+
+class SerializerTests(APISubdomainTestCase):
+ @classmethod
+ def setUpTestData(cls):
+ cls.user = User.objects.create(
+ id=5,
+ name='james',
+ discriminator=1,
+ avatar_hash=None
+ )
+
+ def create_infraction(self, _type: str, active: bool):
+ return Infraction.objects.create(
+ user_id=self.user.id,
+ actor_id=self.user.id,
+ type=_type,
+ reason='A reason.',
+ expires_at=dt(5018, 11, 20, 15, 52, tzinfo=timezone.utc),
+ active=active
+ )
+
+ def test_is_valid_if_active_infraction_with_same_fields_exists(self):
+ self.create_infraction('ban', active=True)
+ instance = self.create_infraction('ban', active=False)
+
+ data = {'reason': 'hello'}
+ serializer = InfractionSerializer(instance, data=data, partial=True)
+
+ self.assertTrue(serializer.is_valid(), msg=serializer.errors)
+
+ def test_validation_error_if_active_duplicate(self):
+ self.create_infraction('ban', active=True)
+ instance = self.create_infraction('ban', active=False)
+
+ data = {'active': True}
+ serializer = InfractionSerializer(instance, data=data, partial=True)
+
+ if not serializer.is_valid():
+ self.assertIn('non_field_errors', serializer.errors)
+
+ code = serializer.errors['non_field_errors'][0].code
+ msg = f'Expected failure on unique validator but got {serializer.errors}'
+ self.assertEqual(code, 'unique', msg=msg)
+ else: # pragma: no cover
+ self.fail('Validation unexpectedly succeeded.')
+
+ def test_is_valid_for_new_active_infraction(self):
+ self.create_infraction('ban', active=False)
+
+ data = {
+ 'user': self.user.id,
+ 'actor': self.user.id,
+ 'type': 'ban',
+ 'reason': 'A reason.',
+ 'active': True
+ }
+ serializer = InfractionSerializer(data=data)
+
+ self.assertTrue(serializer.is_valid(), msg=serializer.errors)
+
+ def test_validation_error_if_missing_active_field(self):
+ data = {
+ 'user': self.user.id,
+ 'actor': self.user.id,
+ 'type': 'ban',
+ 'reason': 'A reason.',
+ }
+ serializer = InfractionSerializer(data=data)
+
+ if not serializer.is_valid():
+ self.assertIn('active', serializer.errors)
+
+ code = serializer.errors['active'][0].code
+ msg = f'Expected failure on required active field but got {serializer.errors}'
+ self.assertEqual(code, 'required', msg=msg)
+ else: # pragma: no cover
+ self.fail('Validation unexpectedly succeeded.')
diff --git a/pydis_site/apps/api/tests/test_reminders.py b/pydis_site/apps/api/tests/test_reminders.py
new file mode 100644
index 00000000..3441e0cc
--- /dev/null
+++ b/pydis_site/apps/api/tests/test_reminders.py
@@ -0,0 +1,196 @@
+from datetime import datetime
+
+from django.forms.models import model_to_dict
+from django_hosts.resolvers import reverse
+
+from .base import APISubdomainTestCase
+from ..models import Reminder, User
+
+
+class UnauthedReminderAPITests(APISubdomainTestCase):
+ def setUp(self):
+ super().setUp()
+ self.client.force_authenticate(user=None)
+
+ def test_list_returns_401(self):
+ url = reverse('bot:reminder-list', host='api')
+ response = self.client.get(url)
+
+ self.assertEqual(response.status_code, 401)
+
+ def test_create_returns_401(self):
+ url = reverse('bot:reminder-list', host='api')
+ response = self.client.post(url, data={'not': 'important'})
+
+ self.assertEqual(response.status_code, 401)
+
+ def test_delete_returns_401(self):
+ url = reverse('bot:reminder-detail', args=('1234',), host='api')
+ response = self.client.delete(url)
+
+ self.assertEqual(response.status_code, 401)
+
+
+class EmptyDatabaseReminderAPITests(APISubdomainTestCase):
+ def test_list_all_returns_empty_list(self):
+ url = reverse('bot:reminder-list', host='api')
+ response = self.client.get(url)
+
+ self.assertEqual(response.status_code, 200)
+ self.assertEqual(response.json(), [])
+
+ def test_delete_returns_404(self):
+ url = reverse('bot:reminder-detail', args=('1234',), host='api')
+ response = self.client.delete(url)
+
+ self.assertEqual(response.status_code, 404)
+
+
+class ReminderCreationTests(APISubdomainTestCase):
+ @classmethod
+ def setUpTestData(cls):
+ cls.author = User.objects.create(
+ id=1234,
+ name='Mermaid Man',
+ discriminator=1234,
+ avatar_hash=None,
+ )
+
+ def test_accepts_valid_data(self):
+ data = {
+ 'author': self.author.id,
+ 'content': 'Remember to...wait what was it again?',
+ 'expiration': datetime.utcnow().isoformat(),
+ 'jump_url': "https://www.google.com",
+ 'channel_id': 123,
+ }
+ url = reverse('bot:reminder-list', host='api')
+ response = self.client.post(url, data=data)
+ self.assertEqual(response.status_code, 201)
+ self.assertIsNotNone(Reminder.objects.filter(id=1).first())
+
+ def test_rejects_invalid_data(self):
+ data = {
+ 'author': self.author.id, # Missing multiple required fields
+ }
+ url = reverse('bot:reminder-list', host='api')
+ response = self.client.post(url, data=data)
+ self.assertEqual(response.status_code, 400)
+ self.assertRaises(Reminder.DoesNotExist, Reminder.objects.get, id=1)
+
+
+class ReminderDeletionTests(APISubdomainTestCase):
+ @classmethod
+ def setUpTestData(cls):
+ cls.author = User.objects.create(
+ id=6789,
+ name='Barnacle Boy',
+ discriminator=6789,
+ avatar_hash=None,
+ )
+
+ cls.reminder = Reminder.objects.create(
+ author=cls.author,
+ content="Don't forget to set yourself a reminder",
+ expiration=datetime.utcnow().isoformat(),
+ jump_url="https://www.decliningmentalfaculties.com",
+ channel_id=123
+ )
+
+ def test_delete_unknown_reminder_returns_404(self):
+ url = reverse('bot:reminder-detail', args=('something',), host='api')
+ response = self.client.delete(url)
+
+ self.assertEqual(response.status_code, 404)
+
+ def test_delete_known_reminder_returns_204(self):
+ url = reverse('bot:reminder-detail', args=(self.reminder.id,), host='api')
+ response = self.client.delete(url)
+
+ self.assertEqual(response.status_code, 204)
+ self.assertRaises(Reminder.DoesNotExist, Reminder.objects.get, id=self.reminder.id)
+
+
+class ReminderListTests(APISubdomainTestCase):
+ @classmethod
+ def setUpTestData(cls):
+ cls.author = User.objects.create(
+ id=6789,
+ name='Patrick Star',
+ discriminator=6789,
+ avatar_hash=None,
+ )
+
+ cls.reminder_one = Reminder.objects.create(
+ author=cls.author,
+ content="We should take Bikini Bottom, and push it somewhere else!",
+ expiration=datetime.utcnow().isoformat(),
+ jump_url="https://www.icantseemyforehead.com",
+ channel_id=123
+ )
+
+ cls.reminder_two = Reminder.objects.create(
+ author=cls.author,
+ content="Gahhh-I love being purple!",
+ expiration=datetime.utcnow().isoformat(),
+ jump_url="https://www.goofygoobersicecreampartyboat.com",
+ channel_id=123,
+ active=False
+ )
+
+ cls.rem_dict_one = model_to_dict(cls.reminder_one)
+ cls.rem_dict_one['expiration'] += 'Z' # Massaging a quirk of the response time format
+ cls.rem_dict_two = model_to_dict(cls.reminder_two)
+ cls.rem_dict_two['expiration'] += 'Z' # Massaging a quirk of the response time format
+
+ def test_reminders_in_full_list(self):
+ url = reverse('bot:reminder-list', host='api')
+ response = self.client.get(url)
+
+ self.assertEqual(response.status_code, 200)
+ self.assertCountEqual(response.json(), [self.rem_dict_one, self.rem_dict_two])
+
+ def test_filter_search(self):
+ url = reverse('bot:reminder-list', host='api')
+ response = self.client.get(f'{url}?search={self.author.name}')
+
+ self.assertEqual(response.status_code, 200)
+ self.assertCountEqual(response.json(), [self.rem_dict_one, self.rem_dict_two])
+
+ def test_filter_field(self):
+ url = reverse('bot:reminder-list', host='api')
+ response = self.client.get(f'{url}?active=true')
+
+ self.assertEqual(response.status_code, 200)
+ self.assertEqual(response.json(), [self.rem_dict_one])
+
+
+class ReminderUpdateTests(APISubdomainTestCase):
+ @classmethod
+ def setUpTestData(cls):
+ cls.author = User.objects.create(
+ id=666,
+ name='Man Ray',
+ discriminator=666,
+ avatar_hash=None,
+ )
+
+ cls.reminder = Reminder.objects.create(
+ author=cls.author,
+ content="Squash those do-gooders",
+ expiration=datetime.utcnow().isoformat(),
+ jump_url="https://www.decliningmentalfaculties.com",
+ channel_id=123
+ )
+
+ cls.data = {'content': 'Oops I forgot'}
+
+ def test_patch_updates_record(self):
+ url = reverse('bot:reminder-detail', args=(self.reminder.id,), host='api')
+ response = self.client.patch(url, data=self.data)
+
+ self.assertEqual(response.status_code, 200)
+ self.assertEqual(
+ Reminder.objects.filter(id=self.reminder.id).first().content,
+ self.data['content']
+ )
diff --git a/pydis_site/settings.py b/pydis_site/settings.py
index 72cc0ab9..5f80a414 100644
--- a/pydis_site/settings.py
+++ b/pydis_site/settings.py
@@ -16,14 +16,24 @@ import sys
import typing
import environ
+import sentry_sdk
from django.contrib.messages import constants as messages
+from sentry_sdk.integrations.django import DjangoIntegration
+
if typing.TYPE_CHECKING:
from django.contrib.auth.models import User
from wiki.models import Article
env = environ.Env(
- DEBUG=(bool, False)
+ DEBUG=(bool, False),
+ SITE_SENTRY_DSN=(str, "")
+)
+
+sentry_sdk.init(
+ dsn=env('SITE_SENTRY_DSN'),
+ integrations=[DjangoIntegration()],
+ send_default_pii=True
)
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
@@ -197,7 +207,7 @@ STATICFILES_DIRS = [os.path.join(BASE_DIR, 'pydis_site', 'static')]
STATIC_ROOT = env('STATIC_ROOT', default='/app/staticfiles')
MEDIA_URL = '/media/'
-MEDIA_ROOT = env('MEDIA_ROOT', default='/app/media')
+MEDIA_ROOT = env('MEDIA_ROOT', default='/site/media')
STATICFILES_FINDERS = [
'django.contrib.staticfiles.finders.FileSystemFinder',
@@ -360,25 +370,7 @@ WIKI_MESSAGE_TAG_CSS_CLASS = {
messages.WARNING: "is-warning",
}
-WIKI_MARKDOWN_HTML_STYLES = [
- 'max-width',
- 'min-width',
- 'margin',
- 'padding',
- 'width',
- 'height',
-]
-
-WIKI_MARKDOWN_HTML_ATTRIBUTES = {
- 'img': ['class', 'id', 'src', 'alt', 'width', 'height'],
- 'section': ['class', 'id'],
- 'article': ['class', 'id'],
- 'iframe': ['width', 'height', 'src', 'frameborder', 'allow', 'allowfullscreen'],
-}
-
-WIKI_MARKDOWN_HTML_WHITELIST = [
- 'article', 'section', 'button', 'iframe'
-]
+WIKI_MARKDOWN_SANITIZE_HTML = False
# Wiki permissions
diff --git a/pydis_site/static/images/sponsors/adafruit.png b/pydis_site/static/images/sponsors/adafruit.png
index 27cd9953..eb14cf5d 100644
--- a/pydis_site/static/images/sponsors/adafruit.png
+++ b/pydis_site/static/images/sponsors/adafruit.png
Binary files differ
diff --git a/pydis_site/static/images/sponsors/jetbrains.png b/pydis_site/static/images/sponsors/jetbrains.png
index 0b21c2c8..b79e110a 100644
--- a/pydis_site/static/images/sponsors/jetbrains.png
+++ b/pydis_site/static/images/sponsors/jetbrains.png
Binary files differ
diff --git a/pydis_site/static/images/sponsors/sentry.png b/pydis_site/static/images/sponsors/sentry.png
new file mode 100644
index 00000000..ce185da2
--- /dev/null
+++ b/pydis_site/static/images/sponsors/sentry.png
Binary files differ
diff --git a/pydis_site/templates/base/navbar.html b/pydis_site/templates/base/navbar.html
index 2ba5bdd4..376dab5a 100644
--- a/pydis_site/templates/base/navbar.html
+++ b/pydis_site/templates/base/navbar.html
@@ -63,9 +63,9 @@
</a>
<div class="navbar-dropdown">
<a class="navbar-item" href="{% url 'wiki:get' path="resources/" %}">
- Learning Resources
+ Resources
</a>
- <a class="navbar-item" href="{% url 'wiki:get' path="tools/" %}">
+ <a class="navbar-item" href="{% url 'wiki:get' path="resources/tools/" %}">
Tools
</a>
<a class="navbar-item" href="{% url 'wiki:get' path="contributing/" %}">
diff --git a/pydis_site/templates/home/index.html b/pydis_site/templates/home/index.html
index 3b150767..1ee93b10 100644
--- a/pydis_site/templates/home/index.html
+++ b/pydis_site/templates/home/index.html
@@ -37,11 +37,15 @@
</p>
</div>
- {# Code Jam banner #}
+ {# Right column container #}
<div class="column is-half-desktop video-container">
- <a href="https://pythondiscord.com/pages/code-jams/code-jam-6/">
- <img src="https://raw.githubusercontent.com/python-discord/branding/master/logos/logo_discord_banner/code%20jam%206%20-%20website%20banner.png"/>
- </a>
+ <iframe
+ width="560"
+ height="315"
+ src="https://www.youtube.com/embed/I97L_Y3rhvc?start=381"
+ frameborder="0"
+ allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen>
+ </iframe>
</div>
</div>
@@ -92,6 +96,9 @@
<a href="https://adafruit.com" class="column is-narrow">
<img src="{% static "images/sponsors/adafruit.png" %}" alt="Adafruit"/>
</a>
+ <a href="https://sentry.io" class="column is-narrow">
+ <img src="{% static "images/sponsors/sentry.png" %}" alt="Sentry"/>
+ </a>
</div>
</div>
</div>