Complete
0
.Trash-1000/files/a
Normal file
BIN
.Trash-1000/files/a.zip
Normal file
3
.Trash-1000/files/apifiletest_.vscode_settings.json
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"liveServer.settings.port": 5501
|
||||||
|
}
|
21
.Trash-1000/files/apifiletest_index.html
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>Document</title>
|
||||||
|
<script defer src="./index.js"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div className="w-full h-full">
|
||||||
|
<input
|
||||||
|
type="file"
|
||||||
|
id="fileInput"
|
||||||
|
webkitdirectory="true"
|
||||||
|
mozdirectory="true"
|
||||||
|
directory="true"
|
||||||
|
multiple
|
||||||
|
/>
|
||||||
|
<button id="uploadButton">Upload and Zip Files</button>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
31
.Trash-1000/files/apifiletest_index.js
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
document.getElementById("uploadButton").addEventListener("click", async () => {
|
||||||
|
const fileInput = document.getElementById("fileInput");
|
||||||
|
const files = Array.from(fileInput.files);
|
||||||
|
|
||||||
|
if (files.length === 0) {
|
||||||
|
alert("Please select some files first.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const formData = new FormData();
|
||||||
|
|
||||||
|
files.forEach((file) => {
|
||||||
|
formData.append("files[]", file); // Append each file to FormData
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch("https://club.modo-dev.com/test", {
|
||||||
|
method: "POST",
|
||||||
|
body: formData,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error("Network response was not ok");
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = await response.json();
|
||||||
|
console.log("Upload successful:", data);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error uploading files:", error);
|
||||||
|
}
|
||||||
|
});
|
3
.Trash-1000/files/apifiletest|.vscode|settings.json
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"liveServer.settings.port": 5501
|
||||||
|
}
|
0
.Trash-1000/files/booking-site.db
Normal file
140
.Trash-1000/files/docs.fuck_discord
Normal file
@ -0,0 +1,140 @@
|
|||||||
|
Legend:
|
||||||
|
Form data:
|
||||||
|
https://developer.mozilla.org/en-US/docs/Web/API/FormData
|
||||||
|
[something] : this something is optional
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
---------- USER HANDLING ----------
|
||||||
|
|
||||||
|
POST : /get-user-by-email
|
||||||
|
Form data:
|
||||||
|
email
|
||||||
|
return type:
|
||||||
|
{
|
||||||
|
"handle": "~",
|
||||||
|
"display_name": "~"
|
||||||
|
}
|
||||||
|
Errors:
|
||||||
|
422 : email missing in request
|
||||||
|
404 : email not found
|
||||||
|
|
||||||
|
POST : /get-user-by-id
|
||||||
|
Form data:
|
||||||
|
id
|
||||||
|
return type:
|
||||||
|
{
|
||||||
|
"handle": "~",
|
||||||
|
"display_name": "~"
|
||||||
|
}
|
||||||
|
Errors:
|
||||||
|
422 : id missing in request
|
||||||
|
404 : id not found
|
||||||
|
|
||||||
|
POST : /get-user-by-handle
|
||||||
|
Form Data:
|
||||||
|
handle
|
||||||
|
return type:
|
||||||
|
{
|
||||||
|
"handle": "~",
|
||||||
|
"display_name": "~"
|
||||||
|
}
|
||||||
|
Errors:
|
||||||
|
422 : handle missing in request
|
||||||
|
404 : handle not found
|
||||||
|
|
||||||
|
POST : /register
|
||||||
|
Form Data:
|
||||||
|
email
|
||||||
|
handle
|
||||||
|
password
|
||||||
|
[display_name]
|
||||||
|
[profile_picture]
|
||||||
|
Errors:
|
||||||
|
422 : something missing in request
|
||||||
|
409 : User exists (email or handle)
|
||||||
|
|
||||||
|
POST : /login
|
||||||
|
Form Data:
|
||||||
|
email
|
||||||
|
password
|
||||||
|
Errors:
|
||||||
|
422 : something missing in request
|
||||||
|
404 : User does not exist
|
||||||
|
409 : Wrong password
|
||||||
|
Cookies:
|
||||||
|
sets id
|
||||||
|
sets session_id
|
||||||
|
|
||||||
|
POST : /logout
|
||||||
|
Errors:
|
||||||
|
401 : Not logged in
|
||||||
|
Cookies:
|
||||||
|
deletes session_id
|
||||||
|
deletes id
|
||||||
|
|
||||||
|
POST : /change-user-profile-picture
|
||||||
|
Form Data:
|
||||||
|
image
|
||||||
|
Errors:
|
||||||
|
401 : Not logged in
|
||||||
|
403 : Invalid log in
|
||||||
|
422 : image is missing
|
||||||
|
Note:
|
||||||
|
Must be logged in
|
||||||
|
|
||||||
|
POST : /change-user-display-name
|
||||||
|
Form Data:
|
||||||
|
display_name
|
||||||
|
Errors:
|
||||||
|
401 : Not logged in
|
||||||
|
403 : Invalid log in
|
||||||
|
422 : display_name is missing
|
||||||
|
Note:
|
||||||
|
Must be logged in
|
||||||
|
|
||||||
|
GET : /profile-picture/<handle>
|
||||||
|
Errors:
|
||||||
|
404 : Handle not found
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
---------- BOUNTY HANDLING ----------
|
||||||
|
|
||||||
|
POST : /create-bounty
|
||||||
|
Form Data:
|
||||||
|
title
|
||||||
|
description
|
||||||
|
[field]
|
||||||
|
[language]
|
||||||
|
Errors:
|
||||||
|
422 : something missing in request
|
||||||
|
403 : Unknown language/ field
|
||||||
|
Note:
|
||||||
|
default language = "any"
|
||||||
|
default field = "other"
|
||||||
|
|
||||||
|
POST : /get-user-by-id
|
||||||
|
Form data:
|
||||||
|
id
|
||||||
|
return type:
|
||||||
|
{
|
||||||
|
"handle": "~",
|
||||||
|
"display_name": "~"
|
||||||
|
}
|
||||||
|
Errors:
|
||||||
|
422 : id missing in request
|
||||||
|
404 : id not found
|
||||||
|
|
||||||
|
POST : /create-session-for-bounty-get-random
|
||||||
|
Cookies:
|
||||||
|
sets bounty_session_id
|
||||||
|
|
||||||
|
POST : /bounty-get-random
|
||||||
|
Form data:
|
||||||
|
page_number
|
||||||
|
Note:
|
||||||
|
/create-session-for-bounty-get-random must be called before this
|
||||||
|
Errors:
|
||||||
|
422 : page no. missing in request
|
||||||
|
404 : /create-session-for-bounty-get-random not called
|
4
.Trash-1000/files/test.html
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
<form action='http://localhost:5000/test' method="POST" enctype="multipart/form-data">
|
||||||
|
<input type='file' name='file[]' multiple=''>
|
||||||
|
<input type='submit' value='upload'>
|
||||||
|
</form>
|
6
.Trash-1000/files/test/subdir/test1.c
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
printf("Hello World\n");
|
||||||
|
}
|
6
.Trash-1000/files/test/test.c
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
printf("Hello World\n");
|
||||||
|
}
|
3
.Trash-1000/info/a.trashinfo
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
[Trash Info]
|
||||||
|
Path=src/a
|
||||||
|
DeletionDate=2024-07-24T17:11:19
|
3
.Trash-1000/info/a.zip.trashinfo
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
[Trash Info]
|
||||||
|
Path=src/a.zip
|
||||||
|
DeletionDate=2024-07-24T19:40:20
|
@ -0,0 +1,3 @@
|
|||||||
|
[Trash Info]
|
||||||
|
Path=assets/apifiletest_.vscode_settings.json
|
||||||
|
DeletionDate=2024-07-24T20:03:05
|
3
.Trash-1000/info/apifiletest_index.html.trashinfo
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
[Trash Info]
|
||||||
|
Path=assets/apifiletest_index.html
|
||||||
|
DeletionDate=2024-07-24T20:02:59
|
3
.Trash-1000/info/apifiletest_index.js.trashinfo
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
[Trash Info]
|
||||||
|
Path=assets/apifiletest_index.js
|
||||||
|
DeletionDate=2024-07-24T20:03:05
|
@ -0,0 +1,3 @@
|
|||||||
|
[Trash Info]
|
||||||
|
Path=assets/apifiletest%7C.vscode%7Csettings.json
|
||||||
|
DeletionDate=2024-07-24T20:24:13
|
3
.Trash-1000/info/booking-site.db.trashinfo
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
[Trash Info]
|
||||||
|
Path=data/booking-site.db
|
||||||
|
DeletionDate=2024-07-17T19:27:01
|
3
.Trash-1000/info/docs.fuck_discord.trashinfo
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
[Trash Info]
|
||||||
|
Path=src/docs.fuck_discord
|
||||||
|
DeletionDate=2024-07-24T17:11:22
|
3
.Trash-1000/info/test.html.trashinfo
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
[Trash Info]
|
||||||
|
Path=src/test.html
|
||||||
|
DeletionDate=2024-07-24T20:25:59
|
3
.Trash-1000/info/test.trashinfo
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
[Trash Info]
|
||||||
|
Path=src/test
|
||||||
|
DeletionDate=2024-07-24T19:40:41
|
5
.vim/coc-settings.json
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"cSpell.words": [
|
||||||
|
"Yash's"
|
||||||
|
]
|
||||||
|
}
|
BIN
__pycache__/main.cpython-312.pyc
Normal file
BIN
__pycache__/rest_api.cpython-312.pyc
Normal file
30
assets/apifiletest/index.html
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>Document</title>
|
||||||
|
<style>
|
||||||
|
.w-full {
|
||||||
|
height: 90%;
|
||||||
|
width: 90%;
|
||||||
|
padding: 0%;
|
||||||
|
margin: 0%;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<script defer src="./index.js"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div>
|
||||||
|
<input
|
||||||
|
class="w-full"
|
||||||
|
type="file"
|
||||||
|
id="fileInput"
|
||||||
|
webkitdirectory="true"
|
||||||
|
mozdirectory="true"
|
||||||
|
directory="true"
|
||||||
|
multiple
|
||||||
|
/>
|
||||||
|
<button id="uploadButton">Upload and Zip Files</button>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
32
assets/apifiletest/index.js
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
document.getElementById("uploadButton").addEventListener("click", async () => {
|
||||||
|
const fileInput = document.getElementById("fileInput");
|
||||||
|
const files = Array.from(fileInput.files);
|
||||||
|
|
||||||
|
if (files.length === 0) {
|
||||||
|
alert("Please select some files first.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const formData = new FormData();
|
||||||
|
|
||||||
|
formData.append("bounty_id", 7);
|
||||||
|
files.forEach((file) => {
|
||||||
|
formData.append("files", file); // Append each file to FormData
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch("http://localhost:5000/submit-solution", {
|
||||||
|
method: "POST",
|
||||||
|
body: formData,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error("Network response was not ok");
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = await response.json();
|
||||||
|
console.log("Upload successful:", data);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error uploading files:", error);
|
||||||
|
}
|
||||||
|
});
|
BIN
assets/profile_pictures/1
Normal file
After Width: | Height: | Size: 59 KiB |
BIN
assets/profile_pictures/123
Normal file
After Width: | Height: | Size: 59 KiB |
BIN
assets/profile_pictures/12345
Normal file
After Width: | Height: | Size: 59 KiB |
BIN
assets/profile_pictures/55555
Normal file
After Width: | Height: | Size: 59 KiB |
BIN
assets/profile_pictures/555555
Normal file
After Width: | Height: | Size: 59 KiB |
After Width: | Height: | Size: 59 KiB |
BIN
assets/profile_pictures/I21
Normal file
After Width: | Height: | Size: 11 KiB |
BIN
assets/profile_pictures/N
Normal file
After Width: | Height: | Size: 11 KiB |
BIN
assets/profile_pictures/None
Normal file
After Width: | Height: | Size: 59 KiB |
BIN
assets/profile_pictures/default_profile_picture.jpg
Normal file
After Width: | Height: | Size: 11 KiB |
BIN
assets/profile_pictures/kosh
Normal file
After Width: | Height: | Size: 11 KiB |
BIN
assets/profile_pictures/kush
Normal file
After Width: | Height: | Size: 4.4 KiB |
BIN
assets/profile_pictures/kushi
Normal file
After Width: | Height: | Size: 4.4 KiB |
BIN
assets/profile_pictures/kushidsaddfdsfdffasfasfdsfdsfdasds
Normal file
After Width: | Height: | Size: 4.4 KiB |
BIN
assets/profile_pictures/nameless
Normal file
After Width: | Height: | Size: 11 KiB |
@ -0,0 +1 @@
|
|||||||
|
{"cmd": "import os\nimport secrets\nimport sqlite3\nfrom user_handler import UserHandler\nfrom werkzeug.datastructures.file_storage import FileStorage\n\n\nclass SolutionHandler:\n connection: sqlite3.Connection = sqlite3.connect(\"../data/database.db\", check_same_thread=False)\n cursor: sqlite3.Cursor = connection.cursor()\n\n # Save solution\n @classmethod\n def submit_solution(cls, files: list[FileStorage], bounty_identifier: int, creator_identifier: int):\n solution_id = secrets.token_urlsafe(40)\n for file in files:\n if not file.filename:\n file.filename = secrets.token_urlsafe(5)\n file.filename = file.filename.replace(\"/\", r\"|\")\n os.mkdir(f\"../assets/solutions/{solution_id}\")\n file.save(f\"../assets/solutions/{solution_id}/{file.filename}\")\n\n cls.cursor.execute(\n f\"\"\"\n INSERT INTO bounty_solutions(creator_identifier, bounty_identifier, identifier)\n values(?, ?, ?)\n \"\"\",\n (creator_identifier, bounty_identifier, solution_id)\n )\n cls.connection.commit()\n\n @staticmethod\n def __format_solution(paths: list[str]):\n pass\n\n # Get solution by id\n @staticmethod\n def get_solution_by_id(solution_id: str):\n path = f\"../assets/solutions/{solution_id}/\"\n if not os.path.isdir(path):\n raise KeyError(\"Solution doesn't exist\")\n formated_solutions: dict[str, str | dict] = {}\n file_paths = os.listdir(path)\n for file_path in file_paths:\n current_folder = formated_solutions\n for folder in file_path.split(\"|\")[:-1]:\n current_folder[folder] = {}\n current_folder = current_folder[folder]\n file_name = file_path.split(\"|\")[-1]\n try:\n with open(path + file_path, \"r\") as file:\n current_folder[file_name] = file.read()\n except UnicodeDecodeError:\n current_folder[file_name] = \"<NOT-TEXT>\"\n\n print(formated_solutions)\n \n\n @classmethod\n def get_solution_list(cls, identifier: int) -> list[dict]:\n solutions = cls.cursor.execute(\n f\"\"\"\n SELECT creator_identifier, identifier FROM bounty_solutions\n WHERE bounty_identifier = ?;\n \"\"\",\n (identifier, )\n ).fetchall()\n return [\n {\n \"creator_id\": solution[0],\n \"creator_display_name\": UserHandler.get_display_name(solution[0]),\n \"solution_id\": solution[1],\n \"likes\": cls.get_likes(solution[1])\n } for solution in solutions\n ]\n\n @classmethod\n def like(cls, solution_id: int, user_id: int):\n cls.cursor.execute(\n f\"\"\"\n INSERT INTO solution_likes(user_id, solution_id) values(?, ?)\n \"\"\",\n (user_id, solution_id)\n )\n cls.connection.commit()\n\n @classmethod\n def unlike(cls, solution_id: int, user_id: int):\n cls.cursor.execute(\n f\"\"\"\n DELETE FROM solution_likes WHERE solution_id = ? AND user_id = ?\n \"\"\",\n (solution_id, user_id)\n )\n cls.connection.commit()\n\n @classmethod\n def get_likes(cls, solution_id: int) -> int:\n likes = cls.cursor.execute(\n f\"\"\"\n SELECT COUNT(*) FROM solution_likes WHERE solution_id = ?\n \"\"\",\n (solution_id, )\n ).fetchall()[0]\n return likes[0] if likes else 0\n\n @classmethod\n def has_user_liked(cls, solution_id: int, user_id: int) -> bool:\n\n likes = cls.cursor.execute(\n f\"\"\"\n SELECT 1 FROM solution_likes WHERE solution_id = ?\n \"\"\",\n (solution_id, )\n ).fetchall()\n return bool(likes)\n\nSolutionHandler.get_solution_by_id(\"NvFlP3tsfJbYDTAOD5dHt4hEp2rLlsVEYizWhC39WwstnJWDosPKyQ\")", "cmd_opts": " --cell_id=NONE -s", "import_complete": 1, "terminal": "kitty"}
|
@ -0,0 +1 @@
|
|||||||
|
{"LsrWtuhk5A": null}
|
@ -0,0 +1 @@
|
|||||||
|
{"NONE": [{"output_type": "stream", "name": "stdout", "text": "{'GTXN1Una4AAvXHl.webp': '<NOT-TEXT>', 'test1': {'fdf.webp': 'Sfjdifjodi\\n'}, 'test2': {'test3': {'safdf.webp': 'fjidofjidfji\\n'}}}\n"}]}
|
@ -0,0 +1 @@
|
|||||||
|
{"NONE": [{"output_type": "stream", "name": "stdout", "text": "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m\n\u001b[0;31mIntegrityError\u001b[0m Traceback (most recent call last)\nCell \u001b[0;32mIn[1], line 127\u001b[0m\n\u001b[1;32m 117\u001b[0m \u001b[38;5;28mcls\u001b[39m\u001b[38;5;241m.\u001b[39mget_user_by_handle(handle)\n\u001b[1;32m 118\u001b[0m \u001b[38;5;28mcls\u001b[39m\u001b[38;5;241m.\u001b[39mcursor\u001b[38;5;241m.\u001b[39mexecute(\n\u001b[1;32m 119\u001b[0m \u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\"\"\u001b[39m\n\u001b[1;32m 120\u001b[0m \u001b[38;5;124m UPDATE users\u001b[39m\n\u001b[0;32m (...)\u001b[0m\n\u001b[1;32m 124\u001b[0m (display_name, handle )\n\u001b[1;32m 125\u001b[0m )\u001b[38;5;241m.\u001b[39mfetchall()\n\u001b[0;32m--> 127\u001b[0m \u001b[43mUserHandler\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mcreate_user\u001b[49m\u001b[43m(\u001b[49m\u001b[43mUser\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mkos\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mkos\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mkos\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mkosh\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m)\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 128\u001b[0m UserHandler\u001b[38;5;241m.\u001b[39mchange_display_name(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mkos\u001b[39m\u001b[38;5;124m\"\u001b[39m, \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mkosh\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n\nCell \u001b[0;32mIn[1], line 38\u001b[0m, in \u001b[0;36mUserHandler.create_user\u001b[0;34m(cls, user)\u001b[0m\n\u001b[1;32m 36\u001b[0m salt \u001b[38;5;241m=\u001b[39m bcrypt\u001b[38;5;241m.\u001b[39mgensalt()\n\u001b[1;32m 37\u001b[0m encrypted_password \u001b[38;5;241m=\u001b[39m bcrypt\u001b[38;5;241m.\u001b[39mhashpw(user\u001b[38;5;241m.\u001b[39mpassword\u001b[38;5;241m.\u001b[39mencode(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mutf-8\u001b[39m\u001b[38;5;124m\"\u001b[39m), salt)\n\u001b[0;32m---> 38\u001b[0m \u001b[38;5;28;43mcls\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mcursor\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mexecute\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 39\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;124;43mf\u001b[39;49m\u001b[38;5;124;43m\"\"\"\u001b[39;49m\n\u001b[1;32m 40\u001b[0m \u001b[38;5;124;43m INSERT INTO users(handle, display_name, email, password)\u001b[39;49m\n\u001b[1;32m 41\u001b[0m \u001b[38;5;124;43m values(?, ?, ?, ?)\u001b[39;49m\n\u001b[1;32m 42\u001b[0m \u001b[38;5;124;43m \u001b[39;49m\u001b[38;5;124;43m\"\"\"\u001b[39;49m\u001b[43m,\u001b[49m\n\u001b[1;32m 43\u001b[0m \u001b[43m \u001b[49m\u001b[43m(\u001b[49m\u001b[43muser\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mhandle\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43muser\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mdisplay_name\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43muser\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43memail\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mencrypted_password\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 44\u001b[0m \u001b[43m\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 45\u001b[0m shutil\u001b[38;5;241m.\u001b[39mcopy(\n\u001b[1;32m 46\u001b[0m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124m../assets/profile_pictures/default_profile_picture.jpg\u001b[39m\u001b[38;5;124m\"\u001b[39m,\n\u001b[1;32m 47\u001b[0m \u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124m../assets/profile_pictures/\u001b[39m\u001b[38;5;132;01m{\u001b[39;00muser\u001b[38;5;241m.\u001b[39mhandle\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m\"\u001b[39m\n\u001b[1;32m 48\u001b[0m )\n\u001b[1;32m 49\u001b[0m \u001b[38;5;28mcls\u001b[39m\u001b[38;5;241m.\u001b[39mconnection\u001b[38;5;241m.\u001b[39mcommit()\n\n\u001b[0;31mIntegrityError\u001b[0m: UNIQUE constraint failed: users.handle\n"}]}
|
@ -0,0 +1,147 @@
|
|||||||
|
from dataclasses import dataclass
|
||||||
|
import sqlite3
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class Bounty:
|
||||||
|
bounty_id: int
|
||||||
|
user_id: int
|
||||||
|
title: str
|
||||||
|
description: str
|
||||||
|
languages: str
|
||||||
|
field: str
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
bounty_id: int,
|
||||||
|
user_id: int,
|
||||||
|
title: str,
|
||||||
|
description: str,
|
||||||
|
languages: str,
|
||||||
|
field: str,
|
||||||
|
) -> None:
|
||||||
|
self.bounty_id = bounty_id
|
||||||
|
self.user_id = user_id
|
||||||
|
self.title = title
|
||||||
|
self.description = description
|
||||||
|
self.languages = languages
|
||||||
|
self.field = field
|
||||||
|
|
||||||
|
|
||||||
|
class BountyHandler:
|
||||||
|
connection: sqlite3.Connection = sqlite3.connect("../data/database.db", check_same_thread=False)
|
||||||
|
cursor: sqlite3.Cursor = connection.cursor()
|
||||||
|
|
||||||
|
# Create Bounty
|
||||||
|
@classmethod
|
||||||
|
def create_bounty(
|
||||||
|
cls,
|
||||||
|
bounty: Bounty
|
||||||
|
) -> None:
|
||||||
|
bounty.languages.removeprefix(",")
|
||||||
|
bounty.languages.removesuffix(",")
|
||||||
|
print(bounty.languages)
|
||||||
|
assert set(bounty.languages.split(",")).issubset(
|
||||||
|
{"c", "cpp", "c#", "css", "go", "html", "java", "js", "python", "ruby", "rust", "other", "any"}
|
||||||
|
)
|
||||||
|
assert set(bounty.field.split(",")).issubset(
|
||||||
|
{"dsa", "ai", "web", "app", "other"}
|
||||||
|
)
|
||||||
|
cls.cursor.execute(
|
||||||
|
f"""
|
||||||
|
INSERT INTO bounties(user_id, title, description, languages, field)
|
||||||
|
values(?, ?, ?, ?, ?)
|
||||||
|
""",
|
||||||
|
(bounty.bounty_id, bounty.title, bounty.description, bounty.languages, bounty.field)
|
||||||
|
)
|
||||||
|
cls.connection.commit()
|
||||||
|
|
||||||
|
# Get Bounty
|
||||||
|
@classmethod
|
||||||
|
def get_bounty_by_id(cls, bounty_id: int) -> Bounty:
|
||||||
|
bounties = cls.cursor.execute(
|
||||||
|
f"""
|
||||||
|
SELECT user_id, title, description, languages, field
|
||||||
|
FROM bounties WHERE bounty_id = ?;
|
||||||
|
""",
|
||||||
|
(bounty_id, )
|
||||||
|
).fetchall()
|
||||||
|
if not bounties:
|
||||||
|
raise KeyError(f"{bounty_id} does not exist!")
|
||||||
|
bounty = bounties[0]
|
||||||
|
return Bounty(
|
||||||
|
bounty_id=bounty_id,
|
||||||
|
user_id=bounty[0],
|
||||||
|
title=bounty[1],
|
||||||
|
description=bounty[2],
|
||||||
|
languages=bounty[3],
|
||||||
|
field=bounty[4],
|
||||||
|
)
|
||||||
|
|
||||||
|
# Get random bounties
|
||||||
|
@classmethod
|
||||||
|
def get_randomized_bounty_list(cls):
|
||||||
|
responses = cls.cursor.execute(
|
||||||
|
f"""
|
||||||
|
SELECT bounty_id, user_id, title, description, languages, field FROM bounties ORDER BY RANDOM();
|
||||||
|
"""
|
||||||
|
).fetchall()
|
||||||
|
bounties: list[Bounty] = []
|
||||||
|
for response in responses:
|
||||||
|
bounties.append(Bounty(
|
||||||
|
bounty_id=response[0],
|
||||||
|
user_id=response[1],
|
||||||
|
title=response[2],
|
||||||
|
description=response[3],
|
||||||
|
languages=response[4],
|
||||||
|
field=response[5],
|
||||||
|
))
|
||||||
|
return bounties
|
||||||
|
|
||||||
|
# Rate
|
||||||
|
@classmethod
|
||||||
|
def rate(cls, bounty_id: int, user_id: int, rating: int) -> None:
|
||||||
|
cls.cursor.execute(
|
||||||
|
f"""
|
||||||
|
INSERT INTO bounty_ratings(bounty_id, user_id, rating)
|
||||||
|
values(?, ?, ?)
|
||||||
|
""",
|
||||||
|
(bounty_id, user_id, rating)
|
||||||
|
)
|
||||||
|
cls.connection.commit()
|
||||||
|
|
||||||
|
# Get Rating
|
||||||
|
@classmethod
|
||||||
|
def get_sum_of_ratings(cls, bounty_id: int) -> int:
|
||||||
|
sum_of_ratings: int = cls.cursor.execute(
|
||||||
|
f"""
|
||||||
|
SELECT SUM(rating)
|
||||||
|
FROM bounty_ratings WHERE bounty_id = ?;
|
||||||
|
""",
|
||||||
|
(bounty_id,)
|
||||||
|
).fetchall()[0][0]
|
||||||
|
return sum_of_ratings if sum_of_ratings else 0
|
||||||
|
|
||||||
|
# Get number of Rating
|
||||||
|
@classmethod
|
||||||
|
def get_number_of_ratings(cls, bounty_id: int) -> int:
|
||||||
|
sum_of_ratings: int = cls.cursor.execute(
|
||||||
|
f"""
|
||||||
|
SELECT COUNT()
|
||||||
|
FROM bounty_ratings WHERE bounty_id = ?;
|
||||||
|
""",
|
||||||
|
(bounty_id,)
|
||||||
|
).fetchall()[0][0]
|
||||||
|
return sum_of_ratings if sum_of_ratings else -1
|
||||||
|
|
||||||
|
# Get rater's Rating
|
||||||
|
@classmethod
|
||||||
|
def get_rating_of_user(cls, user_id: int, bounty_id: int) -> int:
|
||||||
|
sum_of_ratings: int = cls.cursor.execute(
|
||||||
|
f"""
|
||||||
|
SELECT SUM(rating)
|
||||||
|
FROM bounty_ratings WHERE bounty_id = ? AND user_id = ?;
|
||||||
|
""",
|
||||||
|
(bounty_id, user_id)
|
||||||
|
).fetchall()[0][0]
|
||||||
|
return sum_of_ratings if sum_of_ratings else -2
|
@ -0,0 +1,140 @@
|
|||||||
|
Legend:
|
||||||
|
Form data:
|
||||||
|
https://developer.mozilla.org/en-US/docs/Web/API/FormData
|
||||||
|
[something] : this something is optional
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
---------- USER HANDLING ----------
|
||||||
|
|
||||||
|
POST : /get-user-by-email
|
||||||
|
Form data:
|
||||||
|
email
|
||||||
|
return type:
|
||||||
|
{
|
||||||
|
"handle": "~",
|
||||||
|
"display_name": "~"
|
||||||
|
}
|
||||||
|
Errors:
|
||||||
|
422 : email missing in request
|
||||||
|
404 : email not found
|
||||||
|
|
||||||
|
POST : /get-user-by-id
|
||||||
|
Form data:
|
||||||
|
id
|
||||||
|
return type:
|
||||||
|
{
|
||||||
|
"handle": "~",
|
||||||
|
"display_name": "~"
|
||||||
|
}
|
||||||
|
Errors:
|
||||||
|
422 : id missing in request
|
||||||
|
404 : id not found
|
||||||
|
|
||||||
|
POST : /get-user-by-handle
|
||||||
|
Form Data:
|
||||||
|
handle
|
||||||
|
return type:
|
||||||
|
{
|
||||||
|
"handle": "~",
|
||||||
|
"display_name": "~"
|
||||||
|
}
|
||||||
|
Errors:
|
||||||
|
422 : handle missing in request
|
||||||
|
404 : handle not found
|
||||||
|
|
||||||
|
POST : /register
|
||||||
|
Form Data:
|
||||||
|
email
|
||||||
|
handle
|
||||||
|
password
|
||||||
|
[display_name]
|
||||||
|
[profile_picture]
|
||||||
|
Errors:
|
||||||
|
422 : something missing in request
|
||||||
|
409 : User exists (email or handle)
|
||||||
|
|
||||||
|
POST : /login
|
||||||
|
Form Data:
|
||||||
|
email
|
||||||
|
password
|
||||||
|
Errors:
|
||||||
|
422 : something missing in request
|
||||||
|
404 : User does not exist
|
||||||
|
409 : Wrong password
|
||||||
|
Cookies:
|
||||||
|
sets id
|
||||||
|
sets session_id
|
||||||
|
|
||||||
|
POST : /logout
|
||||||
|
Errors:
|
||||||
|
401 : Not logged in
|
||||||
|
Cookies:
|
||||||
|
deletes session_id
|
||||||
|
deletes id
|
||||||
|
|
||||||
|
POST : /change-user-profile-picture
|
||||||
|
Form Data:
|
||||||
|
image
|
||||||
|
Errors:
|
||||||
|
401 : Not logged in
|
||||||
|
403 : Invalid log in
|
||||||
|
422 : image is missing
|
||||||
|
Note:
|
||||||
|
Must be logged in
|
||||||
|
|
||||||
|
POST : /change-user-display-name
|
||||||
|
Form Data:
|
||||||
|
display_name
|
||||||
|
Errors:
|
||||||
|
401 : Not logged in
|
||||||
|
403 : Invalid log in
|
||||||
|
422 : display_name is missing
|
||||||
|
Note:
|
||||||
|
Must be logged in
|
||||||
|
|
||||||
|
GET : /profile-picture/<handle>
|
||||||
|
Errors:
|
||||||
|
404 : Handle not found
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
---------- BOUNTY HANDLING ----------
|
||||||
|
|
||||||
|
POST : /create-bounty
|
||||||
|
Form Data:
|
||||||
|
title
|
||||||
|
description
|
||||||
|
[field]
|
||||||
|
[language]
|
||||||
|
Errors:
|
||||||
|
422 : something missing in request
|
||||||
|
403 : Unknown language/ field
|
||||||
|
Note:
|
||||||
|
default language = "any"
|
||||||
|
default field = "other"
|
||||||
|
|
||||||
|
POST : /get-user-by-id
|
||||||
|
Form data:
|
||||||
|
id
|
||||||
|
return type:
|
||||||
|
{
|
||||||
|
"handle": "~",
|
||||||
|
"display_name": "~"
|
||||||
|
}
|
||||||
|
Errors:
|
||||||
|
422 : id missing in request
|
||||||
|
404 : id not found
|
||||||
|
|
||||||
|
POST : /create-session-for-bounty-get-random
|
||||||
|
Cookies:
|
||||||
|
sets bounty_session_id
|
||||||
|
|
||||||
|
POST : /bounty-get-random
|
||||||
|
Form data:
|
||||||
|
page_number
|
||||||
|
Note:
|
||||||
|
/create-session-for-bounty-get-random must be called before this
|
||||||
|
Errors:
|
||||||
|
422 : page no. missing in request
|
||||||
|
404 : /create-session-for-bounty-get-random not called
|
@ -0,0 +1,419 @@
|
|||||||
|
import json
|
||||||
|
import flask
|
||||||
|
import sqlite3
|
||||||
|
from werkzeug.datastructures.file_storage import FileStorage
|
||||||
|
from solution_handler import SolutionHandler
|
||||||
|
from user_handler import UserHandler, User
|
||||||
|
from bounty_handler import BountyHandler, Bounty
|
||||||
|
|
||||||
|
|
||||||
|
app = flask.Flask(__name__)
|
||||||
|
app.secret_key = "few`3r9i3r"
|
||||||
|
app.config['SESSION_TYPE'] = 'filesystem'
|
||||||
|
|
||||||
|
@app.get("/")
|
||||||
|
def a():
|
||||||
|
return flask.send_file("../assets/apifiletest/index.html")
|
||||||
|
# ---------- USER ----------
|
||||||
|
|
||||||
|
@app.get("/index.js")
|
||||||
|
def an():
|
||||||
|
return flask.send_file("../assets/apifiletest/index.js")
|
||||||
|
#@app.post("/get-user-by-email")
|
||||||
|
#def get_user_by_email() -> flask.Response:
|
||||||
|
# request_data = dict(flask.request.form)
|
||||||
|
# if "email" not in request_data:
|
||||||
|
# return flask.Response("email or password missing", status=422)
|
||||||
|
# try:
|
||||||
|
# user = UserHandler.get_user_by_email(
|
||||||
|
# request_data["email"],
|
||||||
|
# )
|
||||||
|
# except KeyError:
|
||||||
|
# return flask.Response("User does not exist", status=404)
|
||||||
|
# return flask.Response(
|
||||||
|
# response=json.dumps({
|
||||||
|
# "handle": user.handle,
|
||||||
|
# "display_name": user.display_name,
|
||||||
|
# }),
|
||||||
|
# status=200,
|
||||||
|
# mimetype='application/json'
|
||||||
|
# )
|
||||||
|
#
|
||||||
|
#
|
||||||
|
@app.post("/get-user-by-id")
|
||||||
|
def get_user_by_id() -> flask.Response:
|
||||||
|
request_data = dict(flask.request.form)
|
||||||
|
try:
|
||||||
|
user_id = int(request_data["user_id"])
|
||||||
|
except KeyError:
|
||||||
|
return flask.Response("user_id missing", status=422)
|
||||||
|
except ValueError:
|
||||||
|
return flask.Response("user_id wasn't int", status=422)
|
||||||
|
try:
|
||||||
|
user = UserHandler.get_user_by_id(user_id)
|
||||||
|
except KeyError:
|
||||||
|
return flask.Response("User does not exist", status=404)
|
||||||
|
return flask.Response(
|
||||||
|
response=json.dumps({
|
||||||
|
"handle": user.handle,
|
||||||
|
"display_name": user.display_name,
|
||||||
|
}),
|
||||||
|
status=200,
|
||||||
|
mimetype='application/json'
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@app.post("/get-user-by-handle")
|
||||||
|
def get_user_by_handle() -> flask.Response:
|
||||||
|
request_data = dict(flask.request.form)
|
||||||
|
try:
|
||||||
|
handle = request_data["handle"]
|
||||||
|
except KeyError:
|
||||||
|
return flask.Response("handle or password missing", status=422)
|
||||||
|
try:
|
||||||
|
user = UserHandler.get_user_by_handle(handle)
|
||||||
|
except KeyError:
|
||||||
|
return flask.Response("User does not exist", status=404)
|
||||||
|
return flask.Response(
|
||||||
|
response=json.dumps({
|
||||||
|
"handle": user.handle,
|
||||||
|
"display_name": user.display_name,
|
||||||
|
}),
|
||||||
|
status=200,
|
||||||
|
mimetype='application/json'
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@app.post("/register")
|
||||||
|
def register() -> flask.Response:
|
||||||
|
request_data = dict(flask.request.form)
|
||||||
|
try:
|
||||||
|
email = request_data["email"]
|
||||||
|
handle = request_data["handle"]
|
||||||
|
password = request_data["password"]
|
||||||
|
except KeyError:
|
||||||
|
return flask.Response("email, handle or password missing", status=422)
|
||||||
|
if "display_name" not in request_data:
|
||||||
|
request_data["display_name"] = request_data["handle"]
|
||||||
|
try:
|
||||||
|
UserHandler.create_user(
|
||||||
|
User(
|
||||||
|
-1,
|
||||||
|
email,
|
||||||
|
request_data["handle"],
|
||||||
|
request_data["password"],
|
||||||
|
request_data["display_name"],
|
||||||
|
)
|
||||||
|
)
|
||||||
|
except sqlite3.IntegrityError as error:
|
||||||
|
if "handle" in str(error):
|
||||||
|
return flask.Response("Handle exists", status=409)
|
||||||
|
else:
|
||||||
|
return flask.Response("Email exists", status=409)
|
||||||
|
if "profile_picture" in flask.request.files:
|
||||||
|
flask.request.files["profile_picture"].save(f"../assets/profile_pictures/{request_data['handle']}")
|
||||||
|
return flask.Response(status=200)
|
||||||
|
|
||||||
|
|
||||||
|
@app.post("/validate-login")
|
||||||
|
def validate_login():
|
||||||
|
if "user_id" not in flask.session:
|
||||||
|
return flask.Response("Authentication error", status=401)
|
||||||
|
if not isinstance(flask.session["user_id"], int):
|
||||||
|
return flask.Response("Authentication error", status=401)
|
||||||
|
return flask.Response(status=200)
|
||||||
|
|
||||||
|
|
||||||
|
@app.post("/login")
|
||||||
|
def login() -> flask.Response:
|
||||||
|
request_data = dict(flask.request.form)
|
||||||
|
try:
|
||||||
|
email = request_data["email"]
|
||||||
|
password = request_data["password"]
|
||||||
|
except KeyError:
|
||||||
|
return flask.Response("email or password missing", status=422)
|
||||||
|
try:
|
||||||
|
assert UserHandler.verify_password_by_email(email, password)
|
||||||
|
except KeyError:
|
||||||
|
return flask.Response("User does not exist", status=404)
|
||||||
|
except AssertionError:
|
||||||
|
return flask.Response("Wrong password", status=409)
|
||||||
|
user = UserHandler.get_user_by_email(request_data["email"])
|
||||||
|
flask.session["user_id"] = user.user_id
|
||||||
|
return flask.Response(status=200)
|
||||||
|
|
||||||
|
|
||||||
|
@app.post("/logout")
|
||||||
|
def logout() -> flask.Response:
|
||||||
|
if "user_id" not in flask.session:
|
||||||
|
return flask.Response("Authentication error", status=401)
|
||||||
|
flask.session.pop("user_id")
|
||||||
|
return flask.Response(status=200)
|
||||||
|
|
||||||
|
|
||||||
|
@app.post("/change-user-profile-picture")
|
||||||
|
def change_user_profile_picture() -> flask.Response:
|
||||||
|
login_validation = validate_login()
|
||||||
|
if login_validation.status_code != 200:
|
||||||
|
return login_validation
|
||||||
|
|
||||||
|
try:
|
||||||
|
image = flask.request.files["image"]
|
||||||
|
except KeyError:
|
||||||
|
return flask.Response("image is missing", status=422)
|
||||||
|
image_name = UserHandler.get_user_by_id(flask.session["user_id"]).handle
|
||||||
|
image.save(
|
||||||
|
f"../assets/profile_pictures/{image_name}"
|
||||||
|
)
|
||||||
|
return flask.Response(status=200)
|
||||||
|
|
||||||
|
|
||||||
|
@app.post("/change-user-display-name")
|
||||||
|
def change_user_display_name():
|
||||||
|
login_validation = validate_login()
|
||||||
|
if login_validation.status_code != 200:
|
||||||
|
return login_validation
|
||||||
|
request_data = dict(flask.request.form)
|
||||||
|
if "display_name" not in request_data:
|
||||||
|
return flask.Response("display_name is missing", status=422)
|
||||||
|
UserHandler.change_display_name(flask.session["user_id"], request_data["display_name"])
|
||||||
|
return flask.Response(status=200)
|
||||||
|
|
||||||
|
|
||||||
|
@app.get("/profile-picture/<handle>")
|
||||||
|
def get_profile_picture(handle: str) -> flask.Response:
|
||||||
|
try:
|
||||||
|
return flask.send_file(f"../assets/profile_pictures/{handle}")
|
||||||
|
except FileNotFoundError:
|
||||||
|
return flask.Response(f"{handle} does not exist", 404)
|
||||||
|
|
||||||
|
|
||||||
|
# ---------- BOUNTY ----------
|
||||||
|
@app.post("/create-bounty")
|
||||||
|
def create_bounty() -> flask.Response:
|
||||||
|
request_data = dict(flask.request.form)
|
||||||
|
login_validation = validate_login()
|
||||||
|
if login_validation.status_code != 200:
|
||||||
|
return login_validation
|
||||||
|
if "title" not in request_data or \
|
||||||
|
"description" not in request_data:
|
||||||
|
return flask.Response("title or description is missing", status=422)
|
||||||
|
if "field" not in request_data:
|
||||||
|
request_data["field"] = "other"
|
||||||
|
if "languages" not in request_data:
|
||||||
|
request_data["language"] = "any"
|
||||||
|
try:
|
||||||
|
BountyHandler.create_bounty(Bounty(
|
||||||
|
-1,
|
||||||
|
flask.session["user_id"],
|
||||||
|
request_data["title"],
|
||||||
|
request_data["description"],
|
||||||
|
languages=request_data["languages"],
|
||||||
|
field=request_data["field"]
|
||||||
|
))
|
||||||
|
return flask.Response(status=200)
|
||||||
|
except AssertionError:
|
||||||
|
return flask.Response("Unknown language/ field", status=403)
|
||||||
|
|
||||||
|
|
||||||
|
@app.post("/get-bounty-by-id")
|
||||||
|
def bounty_get_by_id() -> flask.Response:
|
||||||
|
login_validation = validate_login()
|
||||||
|
user_id = -1
|
||||||
|
if login_validation.status_code == 200:
|
||||||
|
user_id = flask.session["user_id"]
|
||||||
|
request_data = dict(flask.request.form)
|
||||||
|
if "bounty_id" not in request_data:
|
||||||
|
return flask.Response("bounty_id is missing", status=422)
|
||||||
|
try:
|
||||||
|
bounty_id = int(request_data["bounty_id"])
|
||||||
|
except ValueError:
|
||||||
|
return flask.Response("user_id wasn't int", status=422)
|
||||||
|
try:
|
||||||
|
bounty = BountyHandler.get_bounty_by_id(bounty_id)
|
||||||
|
except KeyError:
|
||||||
|
return flask.Response("Doesn't exist", status=404)
|
||||||
|
number_of_ratings: int = BountyHandler.get_number_of_ratings(bounty_id)
|
||||||
|
return flask.Response(
|
||||||
|
response=json.dumps({
|
||||||
|
"title": bounty.title,
|
||||||
|
"description": bounty.description,
|
||||||
|
"languages": bounty.languages,
|
||||||
|
"field": bounty.field,
|
||||||
|
"average_rating": BountyHandler.get_sum_of_ratings(bounty_id) / number_of_ratings,
|
||||||
|
"number_of_ratings": number_of_ratings,
|
||||||
|
"users_rating": BountyHandler.get_rating_of_user(user_id, bounty.bounty_id),
|
||||||
|
}),
|
||||||
|
status=200,
|
||||||
|
mimetype='application/json'
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@app.post("/create-session-for-bounty-get-random")
|
||||||
|
def create_session_for_bounty_get_random() -> flask.Response:
|
||||||
|
login_validation = validate_login()
|
||||||
|
rater_id = -1
|
||||||
|
if login_validation.status_code == 200:
|
||||||
|
rater_id = flask.session["user_id"]
|
||||||
|
bounties = BountyHandler.get_randomized_bounty_list()
|
||||||
|
bounties = [
|
||||||
|
{
|
||||||
|
"id": bounty.bounty_id,
|
||||||
|
"creator_identifie": bounty.user_id,
|
||||||
|
"title": bounty.title,
|
||||||
|
"description": bounty.description,
|
||||||
|
"languages": bounty.languages,
|
||||||
|
"field": bounty.field,
|
||||||
|
"average_rating": BountyHandler.get_sum_of_ratings(bounty.bounty_id) /\
|
||||||
|
BountyHandler.get_number_of_ratings(bounty.bounty_id),
|
||||||
|
"number_of_ratings": BountyHandler.get_number_of_ratings(bounty.bounty_id),
|
||||||
|
"users_rating": BountyHandler.get_rating_of_user(rater_id, bounty.bounty_id),
|
||||||
|
} for bounty in bounties
|
||||||
|
]
|
||||||
|
flask.session["bounties"] = bounties
|
||||||
|
response = flask.Response(status=200)
|
||||||
|
return response
|
||||||
|
|
||||||
|
|
||||||
|
@app.post("/bounty-get-random")
|
||||||
|
def bounty_get_random() -> flask.Response:
|
||||||
|
request_data = dict(flask.request.form)
|
||||||
|
if "bounties" not in flask.session:
|
||||||
|
return flask.Response("/create-session-for-bounty-get-random not called", status=401)
|
||||||
|
try:
|
||||||
|
page_number = int(request_data["page_number"])
|
||||||
|
except KeyError:
|
||||||
|
return flask.Response("page_number is missing", status=422)
|
||||||
|
except ValueError:
|
||||||
|
return flask.Response("page_number wasn't int", status=422)
|
||||||
|
return flask.Response(
|
||||||
|
response=json.dumps(
|
||||||
|
flask.session["bounties"][page_number * 20 : (page_number + 1) * 20]
|
||||||
|
),
|
||||||
|
status=200,
|
||||||
|
mimetype='application/json'
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@app.post("/bounty-rate")
|
||||||
|
def bounty_rate() -> flask.Response:
|
||||||
|
request_data = dict(flask.request.form)
|
||||||
|
login_validation = validate_login()
|
||||||
|
if login_validation.status_code != 200:
|
||||||
|
return login_validation
|
||||||
|
try:
|
||||||
|
BountyHandler.rate(
|
||||||
|
int(request_data["bounty_id"]),
|
||||||
|
flask.session["user_id"],
|
||||||
|
int(request_data["rating"]),
|
||||||
|
)
|
||||||
|
except sqlite3.IntegrityError:
|
||||||
|
return flask.Response("Rating gotta be between 1 and 10", status=403)
|
||||||
|
except ValueError:
|
||||||
|
return flask.Response("bounty_id or rating wasn't int", status=422)
|
||||||
|
except KeyError:
|
||||||
|
return flask.Response("bounty_id or rating missing", status=422)
|
||||||
|
return flask.Response(status=200)
|
||||||
|
|
||||||
|
|
||||||
|
# ---------- SOLUTIONS ----------
|
||||||
|
@app.post("/test")
|
||||||
|
def test():
|
||||||
|
files = flask.request.files.getlist("files")
|
||||||
|
for file in files:
|
||||||
|
if file.filename:
|
||||||
|
file.filename = file.filename.replace("/", r"|")
|
||||||
|
file.save(f"../assets/{file.filename}")
|
||||||
|
else:
|
||||||
|
return flask.Response(status=400)
|
||||||
|
return flask.Response()
|
||||||
|
|
||||||
|
|
||||||
|
@app.post("/submit-solution")
|
||||||
|
def submit_answer() -> flask.Response:
|
||||||
|
request_data = dict(flask.request.form)
|
||||||
|
login_validation = validate_login()
|
||||||
|
if login_validation.status_code != 200:
|
||||||
|
return login_validation
|
||||||
|
user_id = flask.session["user_id"]
|
||||||
|
try:
|
||||||
|
bounty_id = int(request_data["bounty_id"])
|
||||||
|
except KeyError:
|
||||||
|
return flask.Response("Bounty id missing", status=422)
|
||||||
|
except ValueError:
|
||||||
|
return flask.Response("bounty_id wasn't int", status=422)
|
||||||
|
|
||||||
|
files : list[FileStorage]= flask.request.files.getlist("files")
|
||||||
|
if not files:
|
||||||
|
return flask.Response("files missing", status=422)
|
||||||
|
SolutionHandler.submit_solution(files, bounty_id, user_id)
|
||||||
|
return flask.Response()
|
||||||
|
|
||||||
|
|
||||||
|
@app.post("/get-solution-by-id")
|
||||||
|
def get_solution_by_id() -> flask.Response:
|
||||||
|
request_data = dict(flask.request.form)
|
||||||
|
try:
|
||||||
|
solution_id = request_data["solution_id"]
|
||||||
|
except KeyError:
|
||||||
|
return flask.Response("Solution id missing", status=422)
|
||||||
|
try:
|
||||||
|
return flask.jsonify(SolutionHandler.get_solution_by_id(solution_id))
|
||||||
|
except KeyError:
|
||||||
|
return flask.Response("Solution doesn't exist", status=404)
|
||||||
|
|
||||||
|
|
||||||
|
@app.post("/get-solution-list")
|
||||||
|
def get_solution_list() -> flask.Response:
|
||||||
|
request_data = dict(flask.request.form)
|
||||||
|
try:
|
||||||
|
solution_id = int(request_data["bounty_id"])
|
||||||
|
except KeyError:
|
||||||
|
return flask.Response("Bounty id missing", status=422)
|
||||||
|
except ValueError:
|
||||||
|
return flask.Response("bounty_id wasn't int", status=422)
|
||||||
|
return flask.jsonify(SolutionHandler.get_solution_list(solution_id))
|
||||||
|
|
||||||
|
|
||||||
|
@app.post("/like-solution")
|
||||||
|
def like_solution() -> flask.Response:
|
||||||
|
request_data = dict(flask.request.form)
|
||||||
|
login_validation = validate_login()
|
||||||
|
if login_validation.status_code != 200:
|
||||||
|
return login_validation
|
||||||
|
try:
|
||||||
|
solution_id = request_data["solution_id"]
|
||||||
|
user_id = flask.session["user_id"]
|
||||||
|
except KeyError:
|
||||||
|
return flask.Response("Solution id missing", status=422)
|
||||||
|
SolutionHandler.like(solution_id=solution_id, user_id=user_id)
|
||||||
|
return flask.Response(status=200)
|
||||||
|
|
||||||
|
|
||||||
|
@app.post("/unlike-solution")
|
||||||
|
def unlike_solution() -> flask.Response:
|
||||||
|
request_data = dict(flask.request.form)
|
||||||
|
login_validation = validate_login()
|
||||||
|
if login_validation.status_code != 200:
|
||||||
|
return login_validation
|
||||||
|
try:
|
||||||
|
solution_id = request_data["solution_id"]
|
||||||
|
user_id = flask.session["user_id"]
|
||||||
|
except KeyError:
|
||||||
|
return flask.Response("Solution id missing", status=422)
|
||||||
|
SolutionHandler.unlike(solution_id=solution_id, user_id=user_id)
|
||||||
|
return flask.Response(status=200)
|
||||||
|
|
||||||
|
|
||||||
|
@app.post("/has-user-liked")
|
||||||
|
def has_user_liked() -> flask.Response:
|
||||||
|
request_data = dict(flask.request.form)
|
||||||
|
login_validation = validate_login()
|
||||||
|
if login_validation.status_code != 200:
|
||||||
|
return login_validation
|
||||||
|
try:
|
||||||
|
solution_id = request_data["solution_id"]
|
||||||
|
user_id = flask.session["user_id"]
|
||||||
|
except KeyError:
|
||||||
|
return flask.Response("Solution id missing", status=422)
|
||||||
|
return flask.jsonify(SolutionHandler.has_user_liked(user_id=user_id, solution_id=solution_id))
|
@ -0,0 +1,116 @@
|
|||||||
|
import os
|
||||||
|
import secrets
|
||||||
|
import sqlite3
|
||||||
|
from user_handler import UserHandler
|
||||||
|
from werkzeug.datastructures.file_storage import FileStorage
|
||||||
|
import re
|
||||||
|
|
||||||
|
|
||||||
|
class SolutionHandler:
|
||||||
|
connection: sqlite3.Connection = sqlite3.connect("../data/database.db", check_same_thread=False)
|
||||||
|
cursor: sqlite3.Cursor = connection.cursor()
|
||||||
|
|
||||||
|
# Save solution
|
||||||
|
@classmethod
|
||||||
|
def submit_solution(cls, files: list[FileStorage], bounty_identifier: int, creator_identifier: int):
|
||||||
|
solution_id = secrets.token_urlsafe(40)
|
||||||
|
os.mkdir(f"../assets/solutions/{solution_id}/")
|
||||||
|
for file in files:
|
||||||
|
print(file.filename)
|
||||||
|
if not file.filename:
|
||||||
|
file.filename = secrets.token_urlsafe(5)
|
||||||
|
file.filename = "".join(re.findall("[a-zA-Z 0-9-./]", file.filename))
|
||||||
|
file.filename = file.filename.replace("/", r"|")
|
||||||
|
file.save(f"../assets/solutions/{solution_id}/{file.filename}")
|
||||||
|
|
||||||
|
cls.cursor.execute(
|
||||||
|
f"""
|
||||||
|
INSERT INTO solutions(user_id, bounty_id, solution_id)
|
||||||
|
values(?, ?, ?)
|
||||||
|
""",
|
||||||
|
(creator_identifier, bounty_identifier, solution_id)
|
||||||
|
)
|
||||||
|
cls.connection.commit()
|
||||||
|
|
||||||
|
# Get solution by id
|
||||||
|
@staticmethod
|
||||||
|
def get_solution_by_id(solution_id: str):
|
||||||
|
path = f"../assets/solutions/{solution_id}/"
|
||||||
|
if not os.path.isdir(path):
|
||||||
|
raise KeyError("Solution doesn't exist")
|
||||||
|
formated_solutions: dict[str, str | dict] = {}
|
||||||
|
file_paths = os.listdir(path)
|
||||||
|
for file_path in file_paths:
|
||||||
|
current_folder = formated_solutions
|
||||||
|
for folder in file_path.split("|")[:-1]:
|
||||||
|
print(folder)
|
||||||
|
current_folder[folder] = {}
|
||||||
|
current_folder: dict = current_folder[folder]
|
||||||
|
file_name = file_path.split("|")[-1]
|
||||||
|
try:
|
||||||
|
with open(path + file_path, "r") as file:
|
||||||
|
current_folder[file_name] = file.read()
|
||||||
|
except UnicodeDecodeError:
|
||||||
|
current_folder[file_name] = "<NOT-TEXT>"
|
||||||
|
|
||||||
|
return formated_solutions
|
||||||
|
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_solution_list(cls, identifier: int) -> list[dict]:
|
||||||
|
solutions = cls.cursor.execute(
|
||||||
|
f"""
|
||||||
|
SELECT user_id, solution_id FROM solutions
|
||||||
|
WHERE bounty_id = ?;
|
||||||
|
""",
|
||||||
|
(identifier, )
|
||||||
|
).fetchall()
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
"user_id": solution[0],
|
||||||
|
"creator_display_name": UserHandler.get_display_name(solution[0]),
|
||||||
|
"solution_id": solution[1],
|
||||||
|
"likes": cls.get_likes(solution[1])
|
||||||
|
} for solution in solutions
|
||||||
|
]
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def like(cls, solution_id: int, user_id: int):
|
||||||
|
cls.cursor.execute(
|
||||||
|
f"""
|
||||||
|
INSERT INTO solution_likes(user_id, solution_id) values(?, ?)
|
||||||
|
""",
|
||||||
|
(user_id, solution_id)
|
||||||
|
)
|
||||||
|
cls.connection.commit()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def unlike(cls, solution_id: int, user_id: int):
|
||||||
|
cls.cursor.execute(
|
||||||
|
f"""
|
||||||
|
DELETE FROM solution_likes WHERE solution_id = ? AND user_id = ?
|
||||||
|
""",
|
||||||
|
(solution_id, user_id)
|
||||||
|
)
|
||||||
|
cls.connection.commit()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_likes(cls, solution_id: int) -> int:
|
||||||
|
likes = cls.cursor.execute(
|
||||||
|
f"""
|
||||||
|
SELECT COUNT(*) FROM solution_likes WHERE solution_id = ?
|
||||||
|
""",
|
||||||
|
(solution_id, )
|
||||||
|
).fetchall()[0]
|
||||||
|
return likes[0] if likes else 0
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def has_user_liked(cls, solution_id: int, user_id: int) -> bool:
|
||||||
|
|
||||||
|
likes = cls.cursor.execute(
|
||||||
|
f"""
|
||||||
|
SELECT 1 FROM solution_likes WHERE solution_id = ?
|
||||||
|
""",
|
||||||
|
(solution_id, )
|
||||||
|
).fetchall()
|
||||||
|
return bool(likes)
|
@ -0,0 +1,154 @@
|
|||||||
|
import bcrypt
|
||||||
|
import shutil
|
||||||
|
from dataclasses import dataclass
|
||||||
|
import sqlite3
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class User:
|
||||||
|
user_id: int
|
||||||
|
email: str
|
||||||
|
handle: str
|
||||||
|
password: str
|
||||||
|
display_name: str
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
user_id: int,
|
||||||
|
email: str,
|
||||||
|
handle: str,
|
||||||
|
password: str = "",
|
||||||
|
display_name: str = "",
|
||||||
|
) -> None:
|
||||||
|
self.user_id: int = user_id
|
||||||
|
self.handle = handle
|
||||||
|
self.password = password
|
||||||
|
self.display_name = display_name if display_name else handle
|
||||||
|
self.email = email
|
||||||
|
|
||||||
|
|
||||||
|
class UserHandler:
|
||||||
|
connection: sqlite3.Connection = sqlite3.connect("../data/database.db", check_same_thread=False)
|
||||||
|
cursor: sqlite3.Cursor = connection.cursor()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def create_user(
|
||||||
|
cls,
|
||||||
|
user: User
|
||||||
|
) -> None:
|
||||||
|
salt = bcrypt.gensalt()
|
||||||
|
encrypted_password = bcrypt.hashpw(user.password.encode("utf-8"), salt)
|
||||||
|
cls.cursor.execute(
|
||||||
|
f"""
|
||||||
|
INSERT INTO users(handle, display_name, email, password)
|
||||||
|
values(?, ?, ?, ?)
|
||||||
|
""",
|
||||||
|
(user.handle, user.display_name, user.email, encrypted_password)
|
||||||
|
)
|
||||||
|
cls.connection.commit()
|
||||||
|
shutil.copy(
|
||||||
|
"../assets/profile_pictures/default_profile_picture.jpg",
|
||||||
|
f"../assets/profile_pictures/{user.handle}"
|
||||||
|
)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_user_by_id(cls, user_id: int) -> User:
|
||||||
|
users = cls.cursor.execute(
|
||||||
|
f"""
|
||||||
|
SELECT handle, display_name
|
||||||
|
FROM users WHERE user_id = ?;
|
||||||
|
""",
|
||||||
|
(user_id, )
|
||||||
|
).fetchall()
|
||||||
|
if not users:
|
||||||
|
raise KeyError(f"{user_id} does not exist!")
|
||||||
|
user = users[0]
|
||||||
|
return User(
|
||||||
|
user_id=user_id,
|
||||||
|
handle=user[0],
|
||||||
|
display_name=user[1],
|
||||||
|
email="",
|
||||||
|
password=""
|
||||||
|
)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_user_by_handle(cls, handle: str) -> User:
|
||||||
|
users = cls.cursor.execute(
|
||||||
|
f"""
|
||||||
|
SELECT user_id, handle, display_name
|
||||||
|
FROM users WHERE handle = ?;
|
||||||
|
""",
|
||||||
|
(handle, )
|
||||||
|
).fetchall()
|
||||||
|
if not users:
|
||||||
|
raise KeyError(f"{handle} does not exist!")
|
||||||
|
users = users[0]
|
||||||
|
return User(
|
||||||
|
user_id=users[0],
|
||||||
|
handle=users[1],
|
||||||
|
display_name=users[2],
|
||||||
|
email="",
|
||||||
|
password=""
|
||||||
|
)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_user_by_email(cls, email: str) -> User:
|
||||||
|
users = cls.cursor.execute(
|
||||||
|
f"""
|
||||||
|
SELECT user_id, handle, display_name
|
||||||
|
FROM users WHERE email = ?;
|
||||||
|
""",
|
||||||
|
(email, )
|
||||||
|
).fetchall()
|
||||||
|
if not users:
|
||||||
|
raise KeyError(f"{email} does not exist!")
|
||||||
|
users = users[0]
|
||||||
|
return User(
|
||||||
|
user_id=users[0],
|
||||||
|
handle=users[1],
|
||||||
|
display_name=users[2],
|
||||||
|
email=email,
|
||||||
|
password="",
|
||||||
|
)
|
||||||
|
|
||||||
|
#@classmethod
|
||||||
|
#def verify_password_by_handle(cls, handle: str, password: str) -> bool:
|
||||||
|
# users = cls.cursor.execute(
|
||||||
|
# f"""
|
||||||
|
# SELECT password
|
||||||
|
# FROM users WHERE handle = ?;
|
||||||
|
# """,
|
||||||
|
# (handle, )
|
||||||
|
# ).fetchall()
|
||||||
|
# if not users:
|
||||||
|
# raise KeyError(f"{handle} does not exist!")
|
||||||
|
# real_password = users[0][0]
|
||||||
|
# return bcrypt.checkpw(password.encode("utf-8"), real_password)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def verify_password_by_email(cls, email: str, password: str) -> bool:
|
||||||
|
users = cls.cursor.execute(
|
||||||
|
f"""
|
||||||
|
SELECT password
|
||||||
|
FROM users WHERE email = ?;
|
||||||
|
""",
|
||||||
|
(email, )
|
||||||
|
).fetchall()
|
||||||
|
if not users:
|
||||||
|
raise KeyError(f"{email} does not exist!")
|
||||||
|
return bcrypt.checkpw(password.encode("utf-8"), users[0][0])
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def change_display_name(cls, user_id: int, display_name: str):
|
||||||
|
cls.cursor.execute(
|
||||||
|
f"""
|
||||||
|
UPDATE users
|
||||||
|
SET display_name = ?
|
||||||
|
WHERE user_id = ?;
|
||||||
|
""",
|
||||||
|
(display_name, user_id)
|
||||||
|
).fetchall()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_display_name(cls, user_id: int) -> str:
|
||||||
|
return cls.get_user_by_id(user_id).display_name
|