mirror of
https://github.com/jesperbakhandskemager/PunctualityTracker.git
synced 2024-11-23 21:23:26 +00:00
Web Frontend
This commit is contained in:
parent
5d97286a77
commit
7c2206b785
57
index.html
Normal file
57
index.html
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>PunctualityTracker</title>
|
||||||
|
</head>
|
||||||
|
<style>
|
||||||
|
html {
|
||||||
|
background-color: #131516;
|
||||||
|
color: #d8d4cf;
|
||||||
|
}
|
||||||
|
body {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<script>
|
||||||
|
function populate_table(data) {
|
||||||
|
let table = document.getElementById("student_table");
|
||||||
|
data.forEach(student => {
|
||||||
|
var tr = document.createElement('tr');
|
||||||
|
tr.innerHTML = '<td>' + student.name + '</td>' +
|
||||||
|
'<td>' + student.total_times + '</td>' +
|
||||||
|
'<td>' + student.total_minutes + '</td>' +
|
||||||
|
'<td>' + student.total_days + '</td>' +
|
||||||
|
'<td>' + ((student.total_times / student.total_days) * 100).toFixed(2) + " %" + '</td>' +
|
||||||
|
'<td>' + student.average_time + '</td>'
|
||||||
|
table.appendChild(tr);
|
||||||
|
});
|
||||||
|
const el = document.getElementById('percent_late')
|
||||||
|
if (el) {
|
||||||
|
el.click()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fetch("data.json")
|
||||||
|
.then(res => res.json())
|
||||||
|
.then(data => populate_table(data))
|
||||||
|
</script>
|
||||||
|
<body>
|
||||||
|
<table class="sortable" align="center">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Elev</th>
|
||||||
|
<th>Forseelser</th>
|
||||||
|
<th>Minuter forsent</th>
|
||||||
|
<th>Dage mødt</th>
|
||||||
|
<th id="percent_late">% af dage forsent</th>
|
||||||
|
<th>Gnm møde tidspunkt</th>
|
||||||
|
</tr>
|
||||||
|
<tbody id="student_table">
|
||||||
|
</tbody>
|
||||||
|
</thead>
|
||||||
|
</table>
|
||||||
|
<link href="sortable.min.css" rel="stylesheet" />
|
||||||
|
<script src="sortable.min.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
70
main.py
70
main.py
@ -1,22 +1,39 @@
|
|||||||
import bs4 as bs
|
import bs4 as bs
|
||||||
import requests
|
import requests
|
||||||
|
import json
|
||||||
import urllib3
|
import urllib3
|
||||||
from datetime import datetime
|
from datetime import datetime, timedelta, time
|
||||||
urllib3.disable_warnings()
|
urllib3.disable_warnings()
|
||||||
|
|
||||||
class Student:
|
class Student:
|
||||||
def __init__(self, name, total_times, total_minutes, total_days):
|
def __init__(self, name, total_times, total_minutes, total_days, average_time):
|
||||||
self.name = name
|
self.name = name
|
||||||
self.total_times = total_times
|
self.total_times = total_times
|
||||||
self.total_minutes = total_minutes
|
self.total_minutes = total_minutes
|
||||||
self.total_days = total_days
|
self.total_days = total_days
|
||||||
|
self.average_time = average_time
|
||||||
|
|
||||||
|
class StudentEncoder(json.JSONEncoder):
|
||||||
|
def default(self, obj):
|
||||||
|
if isinstance(obj, Student):
|
||||||
|
return {
|
||||||
|
"name": obj.name,
|
||||||
|
"total_times": obj.total_times,
|
||||||
|
"total_minutes": obj.total_minutes,
|
||||||
|
"total_days": obj.total_days,
|
||||||
|
"average_time": str(obj.average_time)
|
||||||
|
}
|
||||||
|
return super().default(obj)
|
||||||
|
|
||||||
|
|
||||||
def get_all_student_data():
|
def get_all_student_data():
|
||||||
student_ids = get_all_student_ids()
|
student_ids = get_all_student_ids()
|
||||||
students = []
|
students = []
|
||||||
for student_id in student_ids:
|
for student_id in student_ids:
|
||||||
students.append(get_student_data(student_id))
|
student = get_student_data(student_id)
|
||||||
|
if student.name == "Null":
|
||||||
|
continue
|
||||||
|
students.append(student)
|
||||||
return students
|
return students
|
||||||
|
|
||||||
|
|
||||||
@ -71,23 +88,44 @@ def get_student_data(student_string):
|
|||||||
if too_late:
|
if too_late:
|
||||||
total_times += 1
|
total_times += 1
|
||||||
minutes_total += minutes_difference
|
minutes_total += minutes_difference
|
||||||
|
|
||||||
|
if len(times) == 0:
|
||||||
|
return Student("Null", 0, 0, 0, 0)
|
||||||
|
|
||||||
|
# Convert time objects to time durations in seconds
|
||||||
|
durations = [t.hour * 3600 + t.minute * 60 + t.second for t in times]
|
||||||
|
|
||||||
|
# Calculate the average time duration
|
||||||
|
average_duration = sum(durations) / len(durations)
|
||||||
|
|
||||||
|
# Convert the average time duration back to the time format
|
||||||
|
average_time = time(
|
||||||
|
int(average_duration // 3600),
|
||||||
|
int((average_duration % 3600) // 60),
|
||||||
|
int(average_duration % 60)
|
||||||
|
)
|
||||||
|
|
||||||
return Student(student_name, total_times, minutes_total, total_days)
|
return Student(student_name, total_times, minutes_total, total_days, average_time)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
students = get_all_student_data()
|
students = get_all_student_data()
|
||||||
|
file_content = json.dumps(students, cls=StudentEncoder)
|
||||||
|
|
||||||
# Sort the array based on total_minutes in descending order
|
f = open("data.json", "w")
|
||||||
sorted_array = sorted(students, key=lambda x: x.total_times / x.total_days if x.total_days != 0 else 0, reverse=True)
|
f.write(file_content)
|
||||||
# sorted_array = sorted(students, key=lambda x: x.total_minutes, reverse=True)
|
f.close()
|
||||||
|
|
||||||
i = 0
|
# # Sort the array based on total_minutes in descending order
|
||||||
# Print the sorted array
|
# sorted_array = sorted(students, key=lambda x: x.total_times / x.total_days if x.total_days != 0 else 0, reverse=True)
|
||||||
for item in sorted_array:
|
# # sorted_array = sorted(students, key=lambda x: x.total_minutes, reverse=True)
|
||||||
if item.total_times == 0:
|
|
||||||
continue
|
# i = 0
|
||||||
i += 1
|
# # Print the sorted array
|
||||||
hours = round(item.total_minutes / 50, 2)
|
# for item in sorted_array:
|
||||||
percent_late = round((item.total_times / item.total_days) * 100, 2)
|
# if item.total_times == 0:
|
||||||
print(f"{str(i)}) Name: {item.name}, Total Times: {item.total_times} / {item.total_days} = {percent_late}%, Total Minutes: {item.total_minutes}, Total Hours: {hours}")
|
# continue
|
||||||
|
# i += 1
|
||||||
|
# hours = round(item.total_minutes / 50, 2)
|
||||||
|
# percent_late = round((item.total_times / item.total_days) * 100, 2)
|
||||||
|
# print(f"{str(i)}) Name: {item.name}, Total Times: {item.total_times} / {item.total_days} = {percent_late}%, Total Minutes: {item.total_minutes}, Total Hours: {hours}, Average time: {item.average_time}")
|
1
sortable.min.css
vendored
Normal file
1
sortable.min.css
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
.sortable th{cursor:pointer}.sortable th.no-sort{pointer-events:none}.sortable th::after,.sortable th::before{transition:color .1s ease-in-out;font-size:1.2em;color:rgba(0,0,0,0)}.sortable th::after{margin-left:3px;content:"▸"}.sortable th:hover::after{color:inherit}.sortable th.dir-d::after{color:inherit;content:"▾"}.sortable th.dir-u::after{color:inherit;content:"▴"}.sortable th.indicator-left::after{content:""}.sortable th.indicator-left::before{margin-right:3px;content:"▸"}.sortable th.indicator-left:hover::before{color:inherit}.sortable th.indicator-left.dir-d::before{color:inherit;content:"▾"}.sortable th.indicator-left.dir-u::before{color:inherit;content:"▴"}.sortable{--stripe-color: #272b2c;--th-color: #d8d4cf;--th-bg: #181a1b;--td-color: #d8d4cf;--td-on-stripe-color: #000;border-spacing:0}.sortable tbody tr:nth-child(odd){background-color:var(--stripe-color);color:var(--td-on-stripe-color)}.sortable th{background:var(--th-bg);color:var(--th-color);font-weight:normal;text-align:left;text-transform:capitalize;vertical-align:baseline;white-space:nowrap}.sortable td{color:var(--td-color)}.sortable td,.sortable th{padding:10px}.sortable td:first-child,.sortable th:first-child{border-top-left-radius:4px}.sortable td:last-child,.sortable th:last-child{border-top-right-radius:4px}/*# sourceMappingURL=sortable.min.css.map */
|
2
sortable.min.js
vendored
Normal file
2
sortable.min.js
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
document.addEventListener("click",function(c){try{function f(a,b){return a.nodeName===b?a:f(a.parentNode,b)}var x=c.shiftKey||c.altKey,d=f(c.target,"TH"),m=f(d,"TR"),g=f(m,"TABLE");function n(a,b){a.classList.remove("dir-d");a.classList.remove("dir-u");b&&a.classList.add(b)}function p(a){return x&&a.dataset.sortAlt||a.dataset.sort||a.textContent}if(g.classList.contains("sortable")){var q,e=m.cells,r=parseInt(d.dataset.sortTbr);for(c=0;c<e.length;c++)e[c]===d?q=parseInt(d.dataset.sortCol)||c:n(e[c],
|
||||||
|
"");e="dir-d";if(d.classList.contains("dir-d")||g.classList.contains("asc")&&!d.classList.contains("dir-u"))e="dir-u";n(d,e);var t="dir-u"===e,v=function(a,b,h){var u=p((t?a:b).cells[h]);a=p((t?b:a).cells[h]);b=parseFloat(u)-parseFloat(a);return isNaN(b)?u.localeCompare(a):b};for(c=0;c<g.tBodies.length;c++){var k=g.tBodies[c],w=[].slice.call(k.rows,0);w.sort(function(a,b){var h=v(a,b,q);return 0!==h||isNaN(r)?h:v(a,b,r)});var l=k.cloneNode();l.append.apply(l,w);g.replaceChild(l,k)}}}catch(f){}});
|
Loading…
Reference in New Issue
Block a user