1
1
Fork 0

Manage Challenges

This commit is contained in:
Jordy van Zeeland 2023-04-05 08:12:52 +02:00
parent e2c5e2d90e
commit ac0429d23f
14 changed files with 314 additions and 9 deletions

View File

@ -24,6 +24,57 @@ def login(request):
return JsonResponse({'error': 'Wrong credentials'})
except User.DoesNotExist:
return JsonResponse({'error': 'User does not exist'})
@api_view(['POST'])
def addChallenge(request):
if(request.headers.get('Authorization')):
token = request.headers.get('Authorization').split(' ')[1]
try:
User = get_user_model()
payload = jwt.decode(token, 'secret', algorithms=['HS256'])
user = User.objects.get(id=payload['id'])
if(user):
year = request.POST.get('year')
challenge = request.POST.get('challenge')
if(year and challenge):
engine = create_engine('mysql+mysqldb://' + ras.settings.DATABASES['default']['USER'] + ':' + ras.settings.DATABASES['default']['PASSWORD'] + '@' + ras.settings.DATABASES['default']['HOST'] + ':3306/' + ras.settings.DATABASES['default']['NAME'])
conn = engine.connect()
conn.execute(text("INSERT INTO book_challenge (year, nrofbooks) VALUES ('" + str(year) + "', '" + str(challenge) + "')"))
return JsonResponse("OK", safe=False)
else:
return JsonResponse({'error': 'No year and challenge detected'}, safe=False)
else:
return JsonResponse({'error': 'No user detected'}, safe=False)
except (jwt.DecodeError, User.DoesNotExist):
return JsonResponse({'error': 'Token invalid'}, safe=False)
@api_view(['DELETE'])
def deleteChallenge(request, id = None):
if(request.headers.get('Authorization')):
token = request.headers.get('Authorization').split(' ')[1]
try:
User = get_user_model()
payload = jwt.decode(token, 'secret', algorithms=['HS256'])
user = User.objects.get(id=payload['id'])
if(user):
if(id):
engine = create_engine('mysql+mysqldb://' + ras.settings.DATABASES['default']['USER'] + ':' + ras.settings.DATABASES['default']['PASSWORD'] + '@' + ras.settings.DATABASES['default']['HOST'] + ':3306/' + ras.settings.DATABASES['default']['NAME'])
conn = engine.connect()
conn.execute(text("DELETE FROM book_challenge WHERE id = " + str(id)))
return JsonResponse("OK", safe=False)
else:
return JsonResponse({'error': 'No challengeid detected'}, safe=False)
else:
return JsonResponse({'error': 'No user detected'}, safe=False)
except (jwt.DecodeError, User.DoesNotExist):
return JsonResponse({'error': 'Token invalid'}, safe=False)
@api_view(['POST'])
def addBook(request):

View File

@ -6,6 +6,9 @@ from .login import *
urlpatterns = [
path('books', getAllBooks),
path('books/challenge', getChallengeOfYear),
path('books/challenges', getAllChallenges),
path('books/challenges/insert', addChallenge),
path('books/challenges/<int:id>/delete', deleteChallenge),
path('books/years', getYears),
path('books/stats', getStats),
path('books/predict', predictAmountBooks),

View File

@ -60,6 +60,27 @@ def getAllBooks(request):
return Response(data)
@api_view(['GET'])
def getAllChallenges(request):
data = []
df = getBookChallenge()
for index, row in df.iterrows():
books = filterData(getBooksData(), str(row['year']))
books = books.dropna()
totalBooksRead = books['name'].count()
data.append({
"id": row['id'],
"year": row['year'],
"nrofbooks": row['nrofbooks'],
"booksread": totalBooksRead
})
return Response(data)
@api_view(['GET'])
def getChallengeOfYear(request):
if request.META.get('HTTP_YEAR'):

View File

@ -3,6 +3,7 @@ import { Route, Routes, BrowserRouter as Router } from 'react-router-dom';
import { ColorRing } from 'react-loader-spinner'
import Login from "./views/login";
import ManageBooks from "./views/manage/books";
import ManageChallenges from "./views/manage/challenges";
const Dashboard = lazy(() => import("./views/dashboard"));
const Booklist = lazy(() => import("./views/booklist"));
@ -30,6 +31,7 @@ function App() {
<Route exact path="/booklist" element={<Booklist />} />
<Route exact path="/login" element={<Login />} />
<Route exact path="/manage" element={<ManageBooks />} />
<Route exact path="/manage/challenges" element={<ManageChallenges />} />
</Routes>
</Suspense>
</Router>

View File

@ -1,3 +1,5 @@
import { readCookie } from "../Functions";
export const getAllBooks = () => {
return fetch('/api/books', {
"method": "GET",
@ -8,6 +10,48 @@ export const getAllBooks = () => {
})
}
export const getAllChallenges = () => {
return fetch('/api/books/challenges', {
"method": "GET",
})
.then(response => response.json())
.then(data => {
return data;
})
}
export const insertChallenge = (data) => {
return fetch('/api/books/challenges/insert', {
"method": "POST",
"headers": {
"Authorization": "Bearer " + localStorage.getItem("token"),
'Content-Type': 'application/x-www-form-urlencoded',
"X-CSRFToken": readCookie('csrftoken')
},
"body": data
})
.then(response => response.json())
.then(data => {
return data;
})
}
export const deleteChallenge = (id) => {
return fetch('/api/books/challenges/' + id + '/delete', {
"method": "DELETE",
"headers": {
"Authorization": "Bearer " + localStorage.getItem("token"),
'Content-Type': 'application/x-www-form-urlencoded',
"X-CSRFToken": readCookie('csrftoken'),
"challengeid": id
}
})
.then(response => response.json())
.then(data => {
return data;
})
}
export const getStats = (year) => {
return fetch('/api/books/stats', {
"method": "GET",

View File

@ -6,8 +6,8 @@ function SidebarManage(){
<React.Fragment>
<div className='sidebar-manage'>
<ul>
<li><NavLink to="/manage"><i className="fa fa-book"></i> Boeken</NavLink></li>
<li><NavLink to="/manage/challenges"><i className="fa fa-list"></i> Challenges</NavLink></li>
<li><NavLink exact="true" to="/manage" end><i className="fa fa-book"></i> Boeken</NavLink></li>
<li><NavLink exact="true" to="/manage/challenges" end><i className="fa fa-list"></i> Challenges</NavLink></li>
</ul>
</div>

View File

@ -15,12 +15,12 @@ function ManageBooks() {
var navigate = useNavigate();
useEffect(() => {
if(!localStorage.getItem("token") || localStorage.getItem("token") && localStorage.getItem("token") === ''){
if (!localStorage.getItem("token") || localStorage.getItem("token") && localStorage.getItem("token") === '') {
// window.location.href = "/login";
navigate("/login")
}
document.title = "Boekenlijst - Reading Analytics System";
document.title = "Boeken - Beheer - Reading Analytics System";
import("../../components/Data.js").then(module => {
return module.getAllBooks().then(data => {
@ -46,7 +46,7 @@ function ManageBooks() {
<SidebarManage />
<div className="content-manage">
<h1>Boeken beheren <button type="button" class="btn btn-success">Toevoegen</button></h1>
<div className="DataTable_Container">
<table id="DataTable" className="showHead table responsive nowrap" width="100%">

View File

@ -0,0 +1,175 @@
import React, { useEffect, useState } from "react";
import { useNavigate } from "react-router-dom";
import '../../components/DataTables.css';
import * as moment from 'moment';
import SidebarManage from "../../components/manage/sidebar";
const $ = require('jquery');
$.DataTable = require('datatables.net');
moment.locale('nl');
function ManageChallenges() {
var [challenges, setChallenges] = useState([]);
var navigate = useNavigate();
const addChallenge = (event) => {
event.preventDefault();
var details = {
'year': event.target.year.value,
'challenge': event.target.challenge.value
};
var formBody = [];
for (var property in details) {
var encodedKey = encodeURIComponent(property);
var encodedValue = encodeURIComponent(details[property]);
formBody.push(encodedKey + "=" + encodedValue);
}
formBody = formBody.join("&");
import("../../components/Data.js").then(module => {
return module.insertChallenge(formBody).then(data => {
module.getAllChallenges().then(challenges => {
setChallenges(challenges);
$('.modal').hide();
})
});
});
}
const deleteChallenge = (challengeid) => {
var result = confirm("Weet je zeker dat je dit wilt verwijderen?");
if (result) {
import("../../components/Data.js").then(module => {
return module.deleteChallenge(challengeid).then(data => {
module.getAllChallenges().then(challenges => {
setChallenges(challenges);
})
});
});
}
}
useEffect(() => {
if (!localStorage.getItem("token") || localStorage.getItem("token") && localStorage.getItem("token") === '') {
// window.location.href = "/login";
navigate("/login")
}
document.title = "Challenges - Beheer - Reading Analytics System";
import("../../components/Data.js").then(module => {
return module.getAllChallenges().then(data => {
setChallenges(data);
setTimeout(() => {
$('#DataTable').DataTable({
language: {
url: 'https://cdn.datatables.net/plug-ins/9dcbecd42ad/i18n/Dutch.json',
search: "",
searchPlaceholder: "Zoeken"
},
dom: 'rt<"bottom"pl><"clear">',
order: []
});
}, 1000)
});
});
}, [])
$('.btn-add').on('click', function () {
$('.modal').show();
});
$('.modal .close, .modal .cancel').on('click', function () {
$('.modal').hide();
});
return (
<React.Fragment>
<SidebarManage />
<div className="content-manage">
<h1>Challenges beheren <button type="button" className="btn btn-success btn-add">Toevoegen</button></h1>
<div className="DataTable_Container">
<table id="DataTable" className="showHead table responsive nowrap" width="100%">
<thead>
<tr>
<th>Jaar</th>
<th>Challenge</th>
<th>Voortgang</th>
<th>Acties</th>
</tr>
</thead>
<tbody>
{challenges.map((challenge, i) => {
var challengePercentage = Math.round((challenge.booksread / challenge.nrofbooks) * 100, 0);
return (
<tr key={challenge.id}>
<td width="150px">{challenge.year}</td>
<td width="150px">{challenge.nrofbooks}</td>
<td>
<div className="progress">
<div className="progress-bar progress-bar-striped" role="progressbar" style={{ width: challengePercentage + '%' }} aria-valuenow={challengePercentage} aria-valuemin="0" aria-valuemax="100">
<div className="progress-bar-number">{challengePercentage}%</div>
</div>
</div>
</td>
<td width="150px">
<button type="button" className="btn btn-warning"><i className="fa fa-pen"></i></button>
<button type="button" onClick={() => deleteChallenge(challenge.id)} className="btn btn-danger"><i className="fa fa-trash"></i></button>
</td>
</tr>
)
})}
</tbody>
</table>
</div>
<div className="modal" tabIndex="-1" role="dialog">
<div className="modal-dialog" role="document">
<div className="modal-content">
<div className="modal-header">
<h5 className="modal-title">Challenge toevoegen</h5>
<button type="button" className="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<form method="POST" onSubmit={(event) => addChallenge(event)}>
<div className="modal-body">
<div className="form-group">
<label htmlFor="year">Jaar</label>
<input type="text" className="form-control" id="year"/>
</div>
<div className="form-group">
<label htmlFor="challenge">Aantal boeken</label>
<input type="text" className="form-control" id="challenge" />
</div>
</div>
<div className="modal-footer">
<button type="submit" className="btn btn-success">Toevoegen</button>
<button type="button" className="btn btn-danger cancel" data-dismiss="modal">Annuleren</button>
</div>
</form>
</div>
</div>
</div>
</div>
</React.Fragment>
)
}
export default ManageChallenges;

View File

@ -60,6 +60,10 @@ html, body{
margin-right:5px;
}
.content-manage .modal{
background:rgba(0,0,0,0.3);
}
.filter{
width:100%;
background:#1f2940;

File diff suppressed because one or more lines are too long

View File

@ -38,6 +38,10 @@
!*** ./node_modules/react-is/index.js ***!
\****************************************/
/*!****************************************!*\
!*** ./src/views/manage/challenges.js ***!
\****************************************/
/*!*****************************************!*\
!*** ./node_modules/react-dom/index.js ***!
\*****************************************/

View File

@ -1,2 +1,2 @@
/*! For license information please see src_components_Data_js.js.LICENSE.txt */
"use strict";(self.webpackChunkfrontend=self.webpackChunkfrontend||[]).push([["src_components_Data_js"],{"./src/components/Data.js":(__unused_webpack_module,__webpack_exports__,__webpack_require__)=>{eval('__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ "getAllBooks": () => (/* binding */ getAllBooks),\n/* harmony export */ "getAvgRatings": () => (/* binding */ getAvgRatings),\n/* harmony export */ "getBooksPerYearPerGenres": () => (/* binding */ getBooksPerYearPerGenres),\n/* harmony export */ "getChallenge": () => (/* binding */ getChallenge),\n/* harmony export */ "getCountries": () => (/* binding */ getCountries),\n/* harmony export */ "getGenresCount": () => (/* binding */ getGenresCount),\n/* harmony export */ "getRatingsCount": () => (/* binding */ getRatingsCount),\n/* harmony export */ "getReadingYears": () => (/* binding */ getReadingYears),\n/* harmony export */ "getShortestLongestBook": () => (/* binding */ getShortestLongestBook),\n/* harmony export */ "getStats": () => (/* binding */ getStats)\n/* harmony export */ });\nconst getAllBooks = () => {\n return fetch(\'/api/books\', {\n "method": "GET"\n }).then(response => response.json()).then(data => {\n return data;\n });\n};\nconst getStats = year => {\n return fetch(\'/api/books/stats\', {\n "method": "GET",\n "headers": {\n "year": year\n }\n }).then(response => response.json()).then(data => {\n return data;\n });\n};\nconst getChallenge = year => {\n return fetch(\'/api/books/challenge\', {\n "method": "GET",\n "headers": {\n "year": year\n }\n }).then(response => response.json()).then(data => {\n return data;\n });\n};\nconst getReadingYears = () => {\n return fetch(\'/api/books/years\', {\n "method": "GET"\n }).then(response => response.json()).then(data => {\n return data;\n });\n};\nconst getCountries = year => {\n return fetch(\'/api/books/countries\', {\n "method": "GET",\n "headers": {\n "year": year\n }\n }).then(response => response.json()).then(data => {\n return data;\n });\n};\nconst getShortestLongestBook = year => {\n return fetch(\'/api/books/pages/stats\', {\n "method": "GET",\n "headers": {\n "year": year\n }\n }).then(response => response.json()).then(data => {\n return data;\n });\n};\nconst getBooksPerYearPerGenres = year => {\n return fetch(\'/api/books/genres\', {\n "method": "GET",\n "headers": {\n "year": year\n }\n }).then(response => response.json()).then(data => {\n return data;\n });\n};\nconst getGenresCount = year => {\n return fetch(\'/api/books/genres/count\', {\n "method": "GET",\n "headers": {\n "year": year\n }\n }).then(response => response.json()).then(data => {\n return data;\n });\n};\nconst getAvgRatings = year => {\n return fetch(\'/api/books/ratings\', {\n "method": "GET",\n "headers": {\n "year": year\n }\n }).then(response => response.json()).then(data => {\n return data;\n });\n};\nconst getRatingsCount = year => {\n return fetch(\'/api/books/ratings/count\', {\n "method": "GET",\n "headers": {\n "year": year\n }\n }).then(response => response.json()).then(data => {\n return data;\n });\n};\n\n//# sourceURL=webpack://frontend/./src/components/Data.js?')}}]);
"use strict";(self.webpackChunkfrontend=self.webpackChunkfrontend||[]).push([["src_components_Data_js"],{"./src/components/Data.js":(__unused_webpack_module,__webpack_exports__,__webpack_require__)=>{eval('__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ "deleteChallenge": () => (/* binding */ deleteChallenge),\n/* harmony export */ "getAllBooks": () => (/* binding */ getAllBooks),\n/* harmony export */ "getAllChallenges": () => (/* binding */ getAllChallenges),\n/* harmony export */ "getAvgRatings": () => (/* binding */ getAvgRatings),\n/* harmony export */ "getBooksPerYearPerGenres": () => (/* binding */ getBooksPerYearPerGenres),\n/* harmony export */ "getChallenge": () => (/* binding */ getChallenge),\n/* harmony export */ "getCountries": () => (/* binding */ getCountries),\n/* harmony export */ "getGenresCount": () => (/* binding */ getGenresCount),\n/* harmony export */ "getRatingsCount": () => (/* binding */ getRatingsCount),\n/* harmony export */ "getReadingYears": () => (/* binding */ getReadingYears),\n/* harmony export */ "getShortestLongestBook": () => (/* binding */ getShortestLongestBook),\n/* harmony export */ "getStats": () => (/* binding */ getStats),\n/* harmony export */ "insertChallenge": () => (/* binding */ insertChallenge)\n/* harmony export */ });\n/* harmony import */ var _Functions__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../Functions */ "./src/Functions.js");\n\nconst getAllBooks = () => {\n return fetch(\'/api/books\', {\n "method": "GET"\n }).then(response => response.json()).then(data => {\n return data;\n });\n};\nconst getAllChallenges = () => {\n return fetch(\'/api/books/challenges\', {\n "method": "GET"\n }).then(response => response.json()).then(data => {\n return data;\n });\n};\nconst insertChallenge = data => {\n return fetch(\'/api/books/challenges/insert\', {\n "method": "POST",\n "headers": {\n "Authorization": "Bearer " + localStorage.getItem("token"),\n \'Content-Type\': \'application/x-www-form-urlencoded\',\n "X-CSRFToken": (0,_Functions__WEBPACK_IMPORTED_MODULE_0__.readCookie)(\'csrftoken\')\n },\n "body": data\n }).then(response => response.json()).then(data => {\n return data;\n });\n};\nconst deleteChallenge = id => {\n return fetch(\'/api/books/challenges/\' + id + \'/delete\', {\n "method": "DELETE",\n "headers": {\n "Authorization": "Bearer " + localStorage.getItem("token"),\n \'Content-Type\': \'application/x-www-form-urlencoded\',\n "X-CSRFToken": (0,_Functions__WEBPACK_IMPORTED_MODULE_0__.readCookie)(\'csrftoken\'),\n "challengeid": id\n }\n }).then(response => response.json()).then(data => {\n return data;\n });\n};\nconst getStats = year => {\n return fetch(\'/api/books/stats\', {\n "method": "GET",\n "headers": {\n "year": year\n }\n }).then(response => response.json()).then(data => {\n return data;\n });\n};\nconst getChallenge = year => {\n return fetch(\'/api/books/challenge\', {\n "method": "GET",\n "headers": {\n "year": year\n }\n }).then(response => response.json()).then(data => {\n return data;\n });\n};\nconst getReadingYears = () => {\n return fetch(\'/api/books/years\', {\n "method": "GET"\n }).then(response => response.json()).then(data => {\n return data;\n });\n};\nconst getCountries = year => {\n return fetch(\'/api/books/countries\', {\n "method": "GET",\n "headers": {\n "year": year\n }\n }).then(response => response.json()).then(data => {\n return data;\n });\n};\nconst getShortestLongestBook = year => {\n return fetch(\'/api/books/pages/stats\', {\n "method": "GET",\n "headers": {\n "year": year\n }\n }).then(response => response.json()).then(data => {\n return data;\n });\n};\nconst getBooksPerYearPerGenres = year => {\n return fetch(\'/api/books/genres\', {\n "method": "GET",\n "headers": {\n "year": year\n }\n }).then(response => response.json()).then(data => {\n return data;\n });\n};\nconst getGenresCount = year => {\n return fetch(\'/api/books/genres/count\', {\n "method": "GET",\n "headers": {\n "year": year\n }\n }).then(response => response.json()).then(data => {\n return data;\n });\n};\nconst getAvgRatings = year => {\n return fetch(\'/api/books/ratings\', {\n "method": "GET",\n "headers": {\n "year": year\n }\n }).then(response => response.json()).then(data => {\n return data;\n });\n};\nconst getRatingsCount = year => {\n return fetch(\'/api/books/ratings/count\', {\n "method": "GET",\n "headers": {\n "year": year\n }\n }).then(response => response.json()).then(data => {\n return data;\n });\n};\n\n//# sourceURL=webpack://frontend/./src/components/Data.js?')}}]);

File diff suppressed because one or more lines are too long

View File

@ -22,5 +22,6 @@ urlpatterns = [
path('', include('frontend.urls')),
path('booklist/', include('frontend.urls')),
path('login/', include('frontend.urls')),
path('manage/', include('frontend.urls'))
path('manage/', include('frontend.urls')),
path('manage/challenges', include('frontend.urls'))
]