mirror of
https://github.com/aaronleetw/Attendance.git
synced 2024-11-14 19:11:39 -08:00
Add chgPassword AND mkusr with individual acc
This commit is contained in:
parent
fc9a1701c8
commit
3f345d55d3
10 changed files with 553 additions and 63 deletions
163
app.py
163
app.py
|
@ -24,6 +24,7 @@ config = {
|
||||||
"measurementId": os.environ.get('measurementId'),
|
"measurementId": os.environ.get('measurementId'),
|
||||||
}
|
}
|
||||||
firebase = pyrebase.initialize_app(config)
|
firebase = pyrebase.initialize_app(config)
|
||||||
|
db = firebase.database()
|
||||||
auth = firebase.auth()
|
auth = firebase.auth()
|
||||||
tz = pytz.timezone('Asia/Taipei')
|
tz = pytz.timezone('Asia/Taipei')
|
||||||
|
|
||||||
|
@ -52,22 +53,38 @@ def index():
|
||||||
if request.method == 'GET':
|
if request.method == 'GET':
|
||||||
if check_login_status():
|
if check_login_status():
|
||||||
return render_template('login.html')
|
return render_template('login.html')
|
||||||
return redirect('/manage')
|
return redirect('/select')
|
||||||
elif request.method == 'POST':
|
elif request.method == 'POST':
|
||||||
email = request.form['username'] + "@group-attendance.fhjh.tp.edu.tw"
|
email = request.form['username']
|
||||||
if check_login_status():
|
if check_login_status():
|
||||||
try:
|
try:
|
||||||
if (verify_recaptcha("")):
|
if (verify_recaptcha("")):
|
||||||
user = auth.sign_in_with_email_and_password(
|
user = auth.sign_in_with_email_and_password(
|
||||||
email, request.form['password'])
|
email, request.form['password'])
|
||||||
print("Login SUCC:", email, flush=True)
|
usrData = db.child("Users").child(user['localId']).child("permission").get(
|
||||||
|
user['idToken']).val()
|
||||||
|
if (usrData == 'realPerson'):
|
||||||
|
print("RealPerson Login SUCC:", email, flush=True)
|
||||||
session['is_logged_in'] = True
|
session['is_logged_in'] = True
|
||||||
session['email'] = user['email']
|
session['email'] = user['email']
|
||||||
session['uid'] = user['localId']
|
session['uid'] = user['localId']
|
||||||
session['token'] = user['idToken']
|
session['token'] = user['idToken']
|
||||||
session['refreshToken'] = user['refreshToken']
|
session['refreshToken'] = user['refreshToken']
|
||||||
session['loginTime'] = datetime.now(tz)
|
session['loginTime'] = datetime.now(tz)
|
||||||
|
return redirect('/select')
|
||||||
|
if (usrData == 'admin'):
|
||||||
|
print("Admin Login SUCC:", email, flush=True)
|
||||||
|
session['subuser_type'] = 'admin'
|
||||||
|
session['is_logged_in'] = True
|
||||||
|
session['email'] = user['email']
|
||||||
|
session['uid'] = user['localId']
|
||||||
|
session['token'] = user['idToken']
|
||||||
|
session['refreshToken'] = user['refreshToken']
|
||||||
|
session['loginTime'] = datetime.now(tz)
|
||||||
|
session['showUpload'] = db.child("Users").child(
|
||||||
|
session['uid']).child("showUpload").get(session['token']).val()
|
||||||
return redirect('/manage')
|
return redirect('/manage')
|
||||||
|
raise Exception("not real person or admin")
|
||||||
else:
|
else:
|
||||||
print("ReC Error:", email, flush=True)
|
print("ReC Error:", email, flush=True)
|
||||||
flash(
|
flash(
|
||||||
|
@ -79,7 +96,147 @@ def index():
|
||||||
'帳號或密碼錯誤,請重新輸入<br>Incorrect username or password')
|
'帳號或密碼錯誤,請重新輸入<br>Incorrect username or password')
|
||||||
return redirect('/')
|
return redirect('/')
|
||||||
else:
|
else:
|
||||||
|
return redirect('/select')
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/select', methods=['GET', 'POST'])
|
||||||
|
def selSubUser():
|
||||||
|
if check_login_status():
|
||||||
|
print(session)
|
||||||
|
session.clear()
|
||||||
|
flash("Timeout. 遇時,請重新登入")
|
||||||
|
return redirect('/')
|
||||||
|
if 'subuser_type' in session and session['subuser_type'] == 'admin':
|
||||||
return redirect('/manage')
|
return redirect('/manage')
|
||||||
|
if request.method == 'GET':
|
||||||
|
usrData = db.child("Users").child(session['uid']).get(
|
||||||
|
session['token']).val()
|
||||||
|
session['subuser_type'] = ''
|
||||||
|
return render_template('selSubUser.html', data=usrData['accounts'], name=usrData['name'])
|
||||||
|
else:
|
||||||
|
data = request.form['subuser_sel'].split('^')
|
||||||
|
try:
|
||||||
|
if (verify_recaptcha("")):
|
||||||
|
if (data[0] == 'homeroom'):
|
||||||
|
session['homeroom'] = data[1] + '^' + data[2]
|
||||||
|
session['subuser_type'] = 'homeroom'
|
||||||
|
elif (data[0] == 'group'):
|
||||||
|
session['category'] = data[1]
|
||||||
|
session['class'] = data[2]
|
||||||
|
session['subuser_type'] = 'group'
|
||||||
|
return redirect('/manage')
|
||||||
|
else:
|
||||||
|
print("ReC Error:", data, flush=True)
|
||||||
|
flash(
|
||||||
|
'reCAPTCHA 錯誤,請稍後再試一次<br>reCAPTCHA Failed. Please try again later.')
|
||||||
|
return redirect('/select')
|
||||||
|
except Exception as e:
|
||||||
|
print("Error:", data, str(e), flush=True)
|
||||||
|
flash(str(e))
|
||||||
|
return redirect('/select')
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/chgPassword', methods=['POST', 'GET'])
|
||||||
|
def chgPassword():
|
||||||
|
data = {}
|
||||||
|
if request.method == 'GET':
|
||||||
|
if not check_login_status():
|
||||||
|
return render_template('chgPassword.html')
|
||||||
|
else:
|
||||||
|
return abort(404)
|
||||||
|
elif request.method == 'POST':
|
||||||
|
oldEmail = session['email']
|
||||||
|
delUser = False
|
||||||
|
if not check_login_status():
|
||||||
|
try:
|
||||||
|
if (verify_recaptcha("")):
|
||||||
|
oldUsr = auth.sign_in_with_email_and_password(
|
||||||
|
oldEmail, request.form['password'])
|
||||||
|
print("chgPwd oldUser:", oldEmail, flush=True)
|
||||||
|
old = {}
|
||||||
|
old['uid'] = oldUsr['localId']
|
||||||
|
old['token'] = oldUsr['idToken']
|
||||||
|
data = db.child("Users").child(
|
||||||
|
oldUsr['localId']).get(oldUsr['idToken']).val()
|
||||||
|
print("data:", data, flush=True)
|
||||||
|
|
||||||
|
auth.delete_user_account(oldUsr['idToken'])
|
||||||
|
delUser = True
|
||||||
|
|
||||||
|
newUsr = auth.create_user_with_email_and_password(
|
||||||
|
request.form['new_username'], request.form['new_password'])
|
||||||
|
db.child("Users").child(newUsr['localId']).set(
|
||||||
|
data, newUsr['idToken'])
|
||||||
|
session.clear()
|
||||||
|
flash(
|
||||||
|
'修改密碼成功,請重新登入<br>Password changed successfully. Please login again.')
|
||||||
|
return redirect('/')
|
||||||
|
else:
|
||||||
|
print("ReC Error:", oldEmail, flush=True)
|
||||||
|
flash(
|
||||||
|
'reCAPTCHA 錯誤,請稍後再試一次<br>reCAPTCHA Failed. Please try again later.')
|
||||||
|
return redirect('/chgPassword')
|
||||||
|
except Exception as e:
|
||||||
|
if delUser:
|
||||||
|
try:
|
||||||
|
usr = auth.create_user_with_email_and_password(
|
||||||
|
oldEmail, request.form['password'])
|
||||||
|
db.child("Users").child(usr['localId']).set(
|
||||||
|
data, usr['idToken'])
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
print("Error:", oldEmail, str(e), flush=True)
|
||||||
|
flash(str(e))
|
||||||
|
return redirect('/chgPassword')
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/iforgot', methods=['GET', 'POST'])
|
||||||
|
def iforgot():
|
||||||
|
if request.method == 'GET':
|
||||||
|
return render_template('iforgot.html')
|
||||||
|
elif request.method == 'POST':
|
||||||
|
email = request.form['username']
|
||||||
|
try:
|
||||||
|
if (verify_recaptcha("")):
|
||||||
|
auth.send_password_reset_email(email)
|
||||||
|
print("iforgot email sent:", email, flush=True)
|
||||||
|
flash(
|
||||||
|
'重置密碼信件已寄出,請至信箱收取<br>Password reset email has been sent to your email. Please check your email.')
|
||||||
|
return redirect('/')
|
||||||
|
else:
|
||||||
|
print("ReC Error:", email, flush=True)
|
||||||
|
flash(
|
||||||
|
'reCAPTCHA 錯誤,請稍後再試一次<br>reCAPTCHA Failed. Please try again later.')
|
||||||
|
return redirect('/iforgot')
|
||||||
|
except Exception as e:
|
||||||
|
print("Error:", email, str(e), flush=True)
|
||||||
|
flash(str(e))
|
||||||
|
return redirect('/iforgot')
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/resetPassword', methods=['GET', 'POST'])
|
||||||
|
def resetPassword():
|
||||||
|
if request.method == 'GET':
|
||||||
|
session['oobCode'] = request.args.get('oobCode')
|
||||||
|
return render_template('verifiedChgPassword.html')
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
if (verify_recaptcha("")):
|
||||||
|
auth.verify_password_reset_code(
|
||||||
|
session['oobCode'], request.form['password'])
|
||||||
|
print("resetPassword success:", flush=True)
|
||||||
|
session.clear()
|
||||||
|
flash('重置密碼成功,請重新登入<br>Password reset success. Please login again.')
|
||||||
|
return redirect('/')
|
||||||
|
else:
|
||||||
|
print("ReC Error:", flush=True)
|
||||||
|
flash(
|
||||||
|
'reCAPTCHA 錯誤,請稍後再試一次<br>reCAPTCHA Failed. Please try again later.')
|
||||||
|
return redirect('/resetPassword')
|
||||||
|
except Exception as e:
|
||||||
|
print("Error:", str(e), flush=True)
|
||||||
|
flash(str(e))
|
||||||
|
return redirect('/resetPassword')
|
||||||
|
|
||||||
|
|
||||||
@ app.route('/logout', methods=['GET'])
|
@ app.route('/logout', methods=['GET'])
|
||||||
|
|
36
manage.py
36
manage.py
|
@ -51,8 +51,7 @@ def manageProcess(fCommand, fData):
|
||||||
db.child("Users").child(
|
db.child("Users").child(
|
||||||
session['uid']).child("permission").get(session['token']).val()
|
session['uid']).child("permission").get(session['token']).val()
|
||||||
# end bug fix
|
# end bug fix
|
||||||
pl = db.child("Users").child(
|
pl = session['subuser_type']
|
||||||
session['uid']).child("permission").get(session['token']).val()
|
|
||||||
if pl == 'admin':
|
if pl == 'admin':
|
||||||
homerooms = db.child("Homerooms").get(session['token']).val()
|
homerooms = db.child("Homerooms").get(session['token']).val()
|
||||||
currRoom = []
|
currRoom = []
|
||||||
|
@ -80,20 +79,14 @@ def manageProcess(fCommand, fData):
|
||||||
break
|
break
|
||||||
return render_template('admin.html', homerooms=homerooms, absData=absData,
|
return render_template('admin.html', homerooms=homerooms, absData=absData,
|
||||||
homeroomCode=currRoom, homeroomData=homeroomData, currDate=currDate, periods=['m', '1', '2', '3', '4',
|
homeroomCode=currRoom, homeroomData=homeroomData, currDate=currDate, periods=['m', '1', '2', '3', '4',
|
||||||
'n', '5', '6', '7', '8', '9'], showUpload=db.child("Users").child(
|
'n', '5', '6', '7', '8', '9'], showUpload=session['showUpload'])
|
||||||
session['uid']).child("showUpload").get(session['token']).val())
|
|
||||||
elif pl == 'group':
|
elif pl == 'group':
|
||||||
classes = db.child("Users").child(
|
|
||||||
session['uid']).child("class").get(session['token']).val()
|
|
||||||
cclass = {}
|
|
||||||
cateData = {}
|
|
||||||
for i in classes:
|
|
||||||
cateData = db.child("Classes").child(
|
cateData = db.child("Classes").child(
|
||||||
"GP_Class").child(i).get(session['token']).val()
|
"GP_Class").child(session['category']).get(session['token']).val()
|
||||||
cclass = {
|
cclass = {
|
||||||
"name": cateData['Class'][classes[i]]['name'],
|
"name": cateData['Class'][session['class']]['name'],
|
||||||
"category": i,
|
"category": session['category'],
|
||||||
"class_id": classes[i]
|
"class_id": session['class']
|
||||||
}
|
}
|
||||||
homerooms = cateData['Homerooms']
|
homerooms = cateData['Homerooms']
|
||||||
currDate = ""
|
currDate = ""
|
||||||
|
@ -175,8 +168,7 @@ def manageProcess(fCommand, fData):
|
||||||
}
|
}
|
||||||
return render_template('group_teach.html', cclass=cclass, absData=absData, dow=dow, currDate=currDate, tmpAbsData=tmpAbsData, confirmed=confirmed)
|
return render_template('group_teach.html', cclass=cclass, absData=absData, dow=dow, currDate=currDate, tmpAbsData=tmpAbsData, confirmed=confirmed)
|
||||||
elif pl == 'homeroom':
|
elif pl == 'homeroom':
|
||||||
homeroom = db.child("Users").child(
|
homeroom = session['homeroom'].split('^')
|
||||||
session['uid']).child("homeroom").get(session['token']).val().split('^')
|
|
||||||
homeroomData = db.child("Homerooms").child(homeroom[0]).child(
|
homeroomData = db.child("Homerooms").child(homeroom[0]).child(
|
||||||
homeroom[1]).get(session['token']).val()
|
homeroom[1]).get(session['token']).val()
|
||||||
times = OrderedDict({
|
times = OrderedDict({
|
||||||
|
@ -241,17 +233,13 @@ def manage_admin():
|
||||||
def group_teach_publish():
|
def group_teach_publish():
|
||||||
if (check_login_status()):
|
if (check_login_status()):
|
||||||
return redirect('/logout')
|
return redirect('/logout')
|
||||||
classes = db.child("Users").child(
|
|
||||||
session['uid']).child("class").get(session['token']).val()
|
|
||||||
cclass = {}
|
|
||||||
for i in classes:
|
|
||||||
cclass = {
|
cclass = {
|
||||||
"name": db.child("Classes").child("GP_Class").child(i).child(
|
"name": db.child("Classes").child("GP_Class").child(session['category']).child(
|
||||||
"Class").child(classes[i]).child("name").get(session['token']).val(),
|
"Class").child(session['class']).child("name").get(session['token']).val(),
|
||||||
"category": i,
|
"category": session['category'],
|
||||||
"class_id": classes[i],
|
"class_id": session['class'],
|
||||||
"homerooms": db.child("Classes").child(
|
"homerooms": db.child("Classes").child(
|
||||||
"GP_Class").child(i).child("Homerooms").get(session['token']).val()
|
"GP_Class").child(session['category']).child("Homerooms").get(session['token']).val()
|
||||||
}
|
}
|
||||||
date = request.form['date']
|
date = request.form['date']
|
||||||
period = request.form['period']
|
period = request.form['period']
|
||||||
|
|
104
templates/chgPassword.html
Normal file
104
templates/chgPassword.html
Normal file
|
@ -0,0 +1,104 @@
|
||||||
|
<html>
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>Attendance 點名系統 (β)</title>
|
||||||
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.1/dist/css/bootstrap.min.css" rel="stylesheet"
|
||||||
|
integrity="sha384-F3w7mX95PdgyTmZZMECAngseQB83DfGTowi0iMjiWaeVhAn4FJkqJByhZMI3AhiU" crossorigin="anonymous">
|
||||||
|
<link rel="stylesheet" href="/static/allpages.css">
|
||||||
|
<link rel="stylesheet" href="/static/login.css">
|
||||||
|
<link rel="shortcut icon" type="image/x-icon" href="/static/favicon.ico" />
|
||||||
|
<!-- Global site tag (gtag.js) - Google Analytics -->
|
||||||
|
<script async src="https://www.googletagmanager.com/gtag/js?id=G-H6D61RSBHR"></script>
|
||||||
|
<script>
|
||||||
|
window.dataLayer = window.dataLayer || [];
|
||||||
|
function gtag() { dataLayer.push(arguments); }
|
||||||
|
gtag('js', new Date());
|
||||||
|
|
||||||
|
gtag('config', 'G-H6D61RSBHR');
|
||||||
|
</script>
|
||||||
|
<style>
|
||||||
|
.form-group {
|
||||||
|
margin-bottom: 30px !important;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<!-- <script src="https://www.google.com/recaptcha/api.js"></script> -->
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div class="showTime"><span id="showTime"></span></div>
|
||||||
|
<div class="container">
|
||||||
|
<h1 class="margin-top margin-bottom">Attendance 點名系統 (β) | Change Password 更改密碼</h1>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col"></div>
|
||||||
|
<div class="col-md-5">
|
||||||
|
<form action="/chgPassword" id="loginForm" method="post">
|
||||||
|
<div class="form-group row" style="margin-bottom: 10px;">
|
||||||
|
<div class="col-3 center"><label for="password" style="font-size: 19px">Old Password 舊密碼:
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="col-9 center-input"><input type="password" class="form-control" name="password"
|
||||||
|
id="password" required></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group row" style="margin-bottom: 10px;">
|
||||||
|
<div class="col-3 center"><label for="password" style="font-size: 19px">New Username 新帳號:
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="col-9 center-input"><input type="email" class="form-control" name="new_username"
|
||||||
|
id="new_username" required></div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group row" style="margin-bottom: 10px;">
|
||||||
|
<div class="col-3 center"><label for="password" style="font-size: 19px">New Password 新密碼:
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="col-9 center-input"><input type="password" class="form-control" name="new_password"
|
||||||
|
id="new_password" required></div>
|
||||||
|
</div>
|
||||||
|
<button class="btn btn-danger btn-block g-recaptcha" onclick="loadingAnimation()">Change Password
|
||||||
|
更改密碼</button>
|
||||||
|
<a href="/"><button type="button" class="btn btn-primary btn-block g-recaptcha"
|
||||||
|
onclick="loadingAnimation()">Go back 回上一頁</button></a>
|
||||||
|
</form>
|
||||||
|
<div class="disclaimer" hidden="hidden">
|
||||||
|
This site is protected by reCAPTCHA and the Google
|
||||||
|
<a target="_blank" href="https://policies.google.com/privacy">Privacy Policy</a> and
|
||||||
|
<a target="_blank" href="https://policies.google.com/terms">Terms of Service</a> apply.
|
||||||
|
</div>
|
||||||
|
{% with messages = get_flashed_messages() %}
|
||||||
|
{% if messages %}
|
||||||
|
<div class="alert alert-danger" role="alert">
|
||||||
|
{% for message in messages %}
|
||||||
|
{% autoescape false %}{{message}}{% endautoescape %}
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
{% endwith %}
|
||||||
|
</div>
|
||||||
|
<div class="col"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="loading" style="text-align:center; width:100%; display:none;"><img src="/static/loading.gif" alt=""
|
||||||
|
style="height:100%;" />
|
||||||
|
</div>
|
||||||
|
{% include 'footer.html' %}
|
||||||
|
<script type=" text/javascript" src="/static/jquery.min.js"></script>
|
||||||
|
<script>
|
||||||
|
function validateEmail(email) {
|
||||||
|
const emailRegEx = /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
|
||||||
|
function loadingAnimation() {
|
||||||
|
$('#loginForm').submit();
|
||||||
|
if ($("#username").val() == "" || $("#password").val() == "" || $("#new_username").val() == "" || $("#new_password").val() == "" ||
|
||||||
|
!emailRegEx.test($("#username").val().toLowerCase()) || !emailRegEx.test($("#new_username").val().toLowerCase())) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$('.container').hide();
|
||||||
|
$('#loading').show();
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<script src="/static/time.js"></script>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
|
@ -29,6 +29,7 @@
|
||||||
<h2 class="margin-top">{{cclass['category']}}: {{cclass['class_id']}}: {{cclass['name']}}</h2>
|
<h2 class="margin-top">{{cclass['category']}}: {{cclass['class_id']}}: {{cclass['name']}}</h2>
|
||||||
<h2>[{{currDate}}]</h2>
|
<h2>[{{currDate}}]</h2>
|
||||||
<a href="/logout"><button class="btn btn-primary margin-top logout">Logout 登出</button></a>
|
<a href="/logout"><button class="btn btn-primary margin-top logout">Logout 登出</button></a>
|
||||||
|
<a href="/select"><button class="btn btn-primary margin-top logout">選擇其他帳號 Choose Subuser</button></a>
|
||||||
<form action="/manage/date" id="dateSelForm" method="post">
|
<form action="/manage/date" id="dateSelForm" method="post">
|
||||||
<select name="date" id="date" class="form-select logout" onchange="chgDate(this);">
|
<select name="date" id="date" class="form-select logout" onchange="chgDate(this);">
|
||||||
{% for date in tmpAbsData %}
|
{% for date in tmpAbsData %}
|
||||||
|
|
|
@ -30,6 +30,7 @@
|
||||||
<h2 class="margin-top">{{homeroomCode[0]}}{{homeroomCode[1]}}</h2>
|
<h2 class="margin-top">{{homeroomCode[0]}}{{homeroomCode[1]}}</h2>
|
||||||
<h2>[{{currDate}}]</h2>
|
<h2>[{{currDate}}]</h2>
|
||||||
<a href="/logout"><button class="btn btn-primary logout margin-top">Logout 登出</button></a>
|
<a href="/logout"><button class="btn btn-primary logout margin-top">Logout 登出</button></a>
|
||||||
|
<a href="/select"><button class="btn btn-primary margin-top logout">選擇其他帳號 Choose Subuser</button></a>
|
||||||
<form action="/manage/date" id="dateSelForm" method="post">
|
<form action="/manage/date" id="dateSelForm" method="post">
|
||||||
<select name="date" id="date" class="form-select logout" onchange="chgDate();">
|
<select name="date" id="date" class="form-select logout" onchange="chgDate();">
|
||||||
{% for date in absData %}
|
{% for date in absData %}
|
||||||
|
|
73
templates/iforgot.html
Normal file
73
templates/iforgot.html
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
<html>
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>Attendance 點名系統 (β)</title>
|
||||||
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.1/dist/css/bootstrap.min.css" rel="stylesheet"
|
||||||
|
integrity="sha384-F3w7mX95PdgyTmZZMECAngseQB83DfGTowi0iMjiWaeVhAn4FJkqJByhZMI3AhiU" crossorigin="anonymous">
|
||||||
|
<link rel="stylesheet" href="/static/allpages.css">
|
||||||
|
<link rel="stylesheet" href="/static/login.css">
|
||||||
|
<link rel="shortcut icon" type="image/x-icon" href="/static/favicon.ico" />
|
||||||
|
<!-- Global site tag (gtag.js) - Google Analytics -->
|
||||||
|
<script async src="https://www.googletagmanager.com/gtag/js?id=G-H6D61RSBHR"></script>
|
||||||
|
<script>
|
||||||
|
window.dataLayer = window.dataLayer || [];
|
||||||
|
function gtag() { dataLayer.push(arguments); }
|
||||||
|
gtag('js', new Date());
|
||||||
|
|
||||||
|
gtag('config', 'G-H6D61RSBHR');
|
||||||
|
</script>
|
||||||
|
<!-- <script src="https://www.google.com/recaptcha/api.js"></script> -->
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div class="showTime"><span id="showTime"></span></div>
|
||||||
|
<div class="container">
|
||||||
|
<h1 class="margin-top margin-bottom">Attendance 點名系統 (β) | Reset Password 忘記密碼</h1>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col"></div>
|
||||||
|
<div class="col-md-5">
|
||||||
|
<form action="/iforgot" id="forgotPassword_sel" method="post">
|
||||||
|
<div class="form-group row" style="margin-bottom: 10px;">
|
||||||
|
<div class="col-3 center"><label for="username" style="font-size: 19px">Username 帳號: </label>
|
||||||
|
</div>
|
||||||
|
<div class="col-9 center-input"><input type="text" class="form-control" name="username"
|
||||||
|
id="username"></div>
|
||||||
|
</div>
|
||||||
|
<button class="btn btn-warning btn-block g-recaptcha" onclick="loadingAnimation()">Confirm
|
||||||
|
確認</button>
|
||||||
|
</form>
|
||||||
|
<p>This will send an email to the email address to verify your identity.<br>
|
||||||
|
這會傳送一個郵件到指定的信箱,以驗證您的身份
|
||||||
|
</p>
|
||||||
|
{% with messages = get_flashed_messages() %}
|
||||||
|
{% if messages %}
|
||||||
|
<div class="alert alert-danger" role="alert">
|
||||||
|
{% for message in messages %}
|
||||||
|
{% autoescape false %}{{message}}{% endautoescape %}
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
{% endwith %}
|
||||||
|
</div>
|
||||||
|
<div class="col"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="loading" style="text-align:center; width:100%; display:none;"><img src="/static/loading.gif" alt=""
|
||||||
|
style="height:100%;" />
|
||||||
|
</div>
|
||||||
|
{% include 'footer.html' %}
|
||||||
|
<script type=" text/javascript" src="/static/jquery.min.js"></script>
|
||||||
|
<script>
|
||||||
|
function loadingAnimation() {
|
||||||
|
$('#forgotPassword_sel').submit();
|
||||||
|
$('.container').hide();
|
||||||
|
$('#loading').show();
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<script src="/static/time.js"></script>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
|
@ -45,6 +45,7 @@
|
||||||
<button class="btn btn-primary btn-block g-recaptcha" onclick="loadingAnimation()">Login
|
<button class="btn btn-primary btn-block g-recaptcha" onclick="loadingAnimation()">Login
|
||||||
登入</button>
|
登入</button>
|
||||||
</form>
|
</form>
|
||||||
|
<a href="/iforgot">Forgot Password 忘記密碼</a>
|
||||||
<div class="disclaimer" hidden="hidden">
|
<div class="disclaimer" hidden="hidden">
|
||||||
This site is protected by reCAPTCHA and the Google
|
This site is protected by reCAPTCHA and the Google
|
||||||
<a target="_blank" href="https://policies.google.com/privacy">Privacy Policy</a> and
|
<a target="_blank" href="https://policies.google.com/privacy">Privacy Policy</a> and
|
||||||
|
@ -52,7 +53,7 @@
|
||||||
</div>
|
</div>
|
||||||
{% with messages = get_flashed_messages() %}
|
{% with messages = get_flashed_messages() %}
|
||||||
{% if messages %}
|
{% if messages %}
|
||||||
<div class="alert alert-danger" role="alert">
|
<div class="alert alert-danger margin-top" role="alert">
|
||||||
{% for message in messages %}
|
{% for message in messages %}
|
||||||
{% autoescape false %}{{message}}{% endautoescape %}
|
{% autoescape false %}{{message}}{% endautoescape %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
68
templates/selSubUser.html
Normal file
68
templates/selSubUser.html
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>Select Subuser 選擇子帳號</title>
|
||||||
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.1/dist/css/bootstrap.min.css" rel="stylesheet"
|
||||||
|
integrity="sha384-F3w7mX95PdgyTmZZMECAngseQB83DfGTowi0iMjiWaeVhAn4FJkqJByhZMI3AhiU" crossorigin="anonymous">
|
||||||
|
<link rel="stylesheet" href="/static/allpages.css">
|
||||||
|
<link rel="stylesheet" href="/static/homeroom.css">
|
||||||
|
<link rel="shortcut icon" type="image/x-icon" href="/static/favicon.ico" />
|
||||||
|
<!-- Global site tag (gtag.js) - Google Analytics -->
|
||||||
|
<script async src="https://www.googletagmanager.com/gtag/js?id=G-H6D61RSBHR"></script>
|
||||||
|
<script>
|
||||||
|
window.dataLayer = window.dataLayer || [];
|
||||||
|
function gtag() { dataLayer.push(arguments); }
|
||||||
|
gtag('js', new Date());
|
||||||
|
|
||||||
|
gtag('config', 'G-H6D61RSBHR');
|
||||||
|
</script>
|
||||||
|
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div class="showTime"><span id="showTime"></span></div>
|
||||||
|
<div class="container">
|
||||||
|
<h1 class="margin-top">Select Subuser 選擇子帳號</h1>
|
||||||
|
<h2 class="margin-top">{{name}}</h2>
|
||||||
|
<a href="/logout"><button class="btn btn-primary logout margin-top">Logout 登出</button></a>
|
||||||
|
<form action="/select" id="subuser_form_sel" method="post">
|
||||||
|
<select name="subuser_sel" id="subuser_sel" class="form-select logout" onchange="loadingAnimation();"
|
||||||
|
required>
|
||||||
|
<option value="" selected>Please select</option>
|
||||||
|
{% for key in data %}
|
||||||
|
{% if data[key]['type'] == 'homeroom' %}
|
||||||
|
<option value="homeroom^{{data[key]['homeroom']}}">Homeroom: {{data[key]['homeroom']}}</option>
|
||||||
|
{% else %}
|
||||||
|
{% for i in data[key] %}
|
||||||
|
{% if i == 'type' %}
|
||||||
|
{% else %}
|
||||||
|
<option value="group^{{i}}^{{data[key][i]}}">GP: {{i}}: {{data[key][i]}}</option>
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<div id="loading" style="text-align:center; width:100%; display:none;"><img src="/static/loading.gif" alt=""
|
||||||
|
style="height:100%;" />
|
||||||
|
</div>
|
||||||
|
{% include 'footer.html' %}
|
||||||
|
<script type=" text/javascript" src="/static/jquery.min.js"></script>
|
||||||
|
<script>
|
||||||
|
function loadingAnimation() {
|
||||||
|
if ($('#subuser_form_sel').val == '')
|
||||||
|
return;
|
||||||
|
$('#subuser_form_sel').submit();
|
||||||
|
$('.container').hide();
|
||||||
|
$('#loading').show();
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<script src="/static/time.js"></script>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
71
templates/verifiedChgPassword.html
Normal file
71
templates/verifiedChgPassword.html
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
<html>
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>Attendance 點名系統 (β)</title>
|
||||||
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.1/dist/css/bootstrap.min.css" rel="stylesheet"
|
||||||
|
integrity="sha384-F3w7mX95PdgyTmZZMECAngseQB83DfGTowi0iMjiWaeVhAn4FJkqJByhZMI3AhiU" crossorigin="anonymous">
|
||||||
|
<link rel="stylesheet" href="/static/allpages.css">
|
||||||
|
<link rel="stylesheet" href="/static/login.css">
|
||||||
|
<link rel="shortcut icon" type="image/x-icon" href="/static/favicon.ico" />
|
||||||
|
<!-- Global site tag (gtag.js) - Google Analytics -->
|
||||||
|
<script async src="https://www.googletagmanager.com/gtag/js?id=G-H6D61RSBHR"></script>
|
||||||
|
<script>
|
||||||
|
window.dataLayer = window.dataLayer || [];
|
||||||
|
function gtag() { dataLayer.push(arguments); }
|
||||||
|
gtag('js', new Date());
|
||||||
|
|
||||||
|
gtag('config', 'G-H6D61RSBHR');
|
||||||
|
</script>
|
||||||
|
<!-- <script src="https://www.google.com/recaptcha/api.js"></script> -->
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div class="showTime"><span id="showTime"></span></div>
|
||||||
|
<div class="container">
|
||||||
|
<h1 class="margin-top margin-bottom">Attendance 點名系統 (β) | Reset Password 忘記密碼</h1>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col"></div>
|
||||||
|
<div class="col-md-5">
|
||||||
|
<form action="/resetPassword" id="password_form" method="post">
|
||||||
|
<div class="form-group row" style="margin-bottom: 10px;">
|
||||||
|
<div class="col-3 center"><label for="password" style="font-size: 19px">New Password 新密碼:
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="col-9 center-input"><input type="password" class="form-control" name="password"
|
||||||
|
id="password"></div>
|
||||||
|
</div>
|
||||||
|
<button class="btn btn-warning btn-block g-recaptcha" onclick="loadingAnimation()">Confirm
|
||||||
|
確認</button>
|
||||||
|
</form>
|
||||||
|
{% with messages = get_flashed_messages() %}
|
||||||
|
{% if messages %}
|
||||||
|
<div class="alert alert-danger" role="alert">
|
||||||
|
{% for message in messages %}
|
||||||
|
{% autoescape false %}{{message}}{% endautoescape %}
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
{% endwith %}
|
||||||
|
</div>
|
||||||
|
<div class="col"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="loading" style="text-align:center; width:100%; display:none;"><img src="/static/loading.gif" alt=""
|
||||||
|
style="height:100%;" />
|
||||||
|
</div>
|
||||||
|
{% include 'footer.html' %}
|
||||||
|
<script type=" text/javascript" src="/static/jquery.min.js"></script>
|
||||||
|
<script>
|
||||||
|
function loadingAnimation() {
|
||||||
|
$('#password_form').submit();
|
||||||
|
$('.container').hide();
|
||||||
|
$('#loading').show();
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<script src="/static/time.js"></script>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
64
upload.py
64
upload.py
|
@ -6,6 +6,7 @@ import csv
|
||||||
import os
|
import os
|
||||||
import pandas as pd
|
import pandas as pd
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
|
from random import randint
|
||||||
load_dotenv()
|
load_dotenv()
|
||||||
|
|
||||||
upload = Blueprint('upload', __name__)
|
upload = Blueprint('upload', __name__)
|
||||||
|
@ -36,6 +37,35 @@ def check_permission():
|
||||||
db.child("Users").child(session['uid']).child("showUpload").get(session['token']).val() == '1')
|
db.child("Users").child(session['uid']).child("showUpload").get(session['token']).val() == '1')
|
||||||
|
|
||||||
|
|
||||||
|
@upload.route('/upload/users', methods=['GET', 'POST'])
|
||||||
|
def upload_users():
|
||||||
|
if ((not check_login_status()) and check_permission()):
|
||||||
|
if request.method == 'GET':
|
||||||
|
return render_template('uploadcsv.html', title="All Indiviual Users", url="/upload/users")
|
||||||
|
elif request.method == 'POST':
|
||||||
|
try:
|
||||||
|
csv_file = request.files['csv']
|
||||||
|
filepath = os.path.join('./temp', csv_file.filename)
|
||||||
|
csv_file.save(filepath)
|
||||||
|
with open(filepath) as file:
|
||||||
|
csv_dict = csv.DictReader(file)
|
||||||
|
for row in csv_dict:
|
||||||
|
user = auth.create_user_with_email_and_password(
|
||||||
|
row['username'] + "@group-attendance.fhjh.tp.edu.tw", row['password'])
|
||||||
|
db.child("Users").child(user['localId']).set({
|
||||||
|
'permission': 'realPerson',
|
||||||
|
'name': row['name'],
|
||||||
|
'origUsername': row['username'],
|
||||||
|
}, session['token'])
|
||||||
|
os.remove(filepath)
|
||||||
|
except Exception as e:
|
||||||
|
os.remove(filepath)
|
||||||
|
return "Error. Please try again\n("+str(e)+")"
|
||||||
|
return "Successfully uploaded users"
|
||||||
|
else:
|
||||||
|
return redirect('/logout')
|
||||||
|
|
||||||
|
|
||||||
@upload.route('/upload/1', methods=['GET', 'POST'])
|
@upload.route('/upload/1', methods=['GET', 'POST'])
|
||||||
def upload_homeroom():
|
def upload_homeroom():
|
||||||
if ((not check_login_status()) and check_permission()):
|
if ((not check_login_status()) and check_permission()):
|
||||||
|
@ -49,19 +79,18 @@ def upload_homeroom():
|
||||||
csv_file = request.files['csv']
|
csv_file = request.files['csv']
|
||||||
filepath = os.path.join('./temp', csv_file.filename)
|
filepath = os.path.join('./temp', csv_file.filename)
|
||||||
csv_file.save(filepath)
|
csv_file.save(filepath)
|
||||||
|
allUsers = db.child("Users").get(session['token']).val()
|
||||||
with open(filepath) as file:
|
with open(filepath) as file:
|
||||||
csv_dict = csv.DictReader(file)
|
csv_dict = csv.DictReader(file)
|
||||||
for row in csv_dict:
|
for row in csv_dict:
|
||||||
if row['number'] == 'password':
|
if row['number'] == 'teacher':
|
||||||
auth.create_user_with_email_and_password(
|
for key in allUsers:
|
||||||
gradec + classc + "@group-attendance.fhjh.tp.edu.tw", row['name'])
|
if (allUsers[key]['origUsername'] == row['name']):
|
||||||
user = auth.sign_in_with_email_and_password(
|
db.child("Users").child(key).child("accounts").child("homeroom^"+gradec+classc+'^'+randint(10000)).update({
|
||||||
gradec + classc + "@group-attendance.fhjh.tp.edu.tw", row['name'])
|
"homeroom": gradec + '^' + classc,
|
||||||
db.child("Users").child(user['localId']).update({
|
"type": 'homeroom'
|
||||||
"permission": 'homeroom',
|
}, session['token'])
|
||||||
"username": gradec + classc,
|
break
|
||||||
"homeroom": gradec + '^' + classc
|
|
||||||
})
|
|
||||||
else:
|
else:
|
||||||
db.child("Homerooms").child(gradec).child(
|
db.child("Homerooms").child(gradec).child(
|
||||||
classc).child(row['number']).set(row, session['token'])
|
classc).child(row['number']).set(row, session['token'])
|
||||||
|
@ -87,6 +116,7 @@ def upload_gp_classes():
|
||||||
csv_file.save(filepath)
|
csv_file.save(filepath)
|
||||||
csv_dict = pd.read_csv(filepath)
|
csv_dict = pd.read_csv(filepath)
|
||||||
category_cnt = csv_dict.shape[1] - 1
|
category_cnt = csv_dict.shape[1] - 1
|
||||||
|
allUsers = db.child("Users").get(session['token']).val()
|
||||||
for i in range(category_cnt):
|
for i in range(category_cnt):
|
||||||
tmp_csv = csv_dict[csv_dict.columns[i+1]].tolist()
|
tmp_csv = csv_dict[csv_dict.columns[i+1]].tolist()
|
||||||
for j in range(len(tmp_csv)):
|
for j in range(len(tmp_csv)):
|
||||||
|
@ -95,17 +125,13 @@ def upload_gp_classes():
|
||||||
if j % 5 == 0:
|
if j % 5 == 0:
|
||||||
db.child("Classes").child("GP_Class").child(csv_dict.columns[i+1]).child("Class").child(
|
db.child("Classes").child("GP_Class").child(csv_dict.columns[i+1]).child("Class").child(
|
||||||
tmp_csv[j]).child("name").set(tmp_csv[j+1] + " : " + tmp_csv[j+2] + " (" + tmp_csv[j+3] + ")", session['token'])
|
tmp_csv[j]).child("name").set(tmp_csv[j+1] + " : " + tmp_csv[j+2] + " (" + tmp_csv[j+3] + ")", session['token'])
|
||||||
auth.create_user_with_email_and_password(
|
for key in allUsers:
|
||||||
tmp_csv[j] + "@group-attendance.fhjh.tp.edu.tw", tmp_csv[j+4])
|
if (allUsers[key]['origUsername'] == tmp_csv[j+4]):
|
||||||
user = auth.sign_in_with_email_and_password(
|
db.child("Users").child(key).child("accounts").child("GP_Class^"+csv_dict.columns[i+1]+'^'+randint(10000)).update({
|
||||||
tmp_csv[j] + "@group-attendance.fhjh.tp.edu.tw", tmp_csv[j+4])
|
|
||||||
db.child("Users").child(user['localId']).update({
|
|
||||||
"permission": 'group',
|
|
||||||
"username": tmp_csv[j],
|
|
||||||
"class": {
|
|
||||||
csv_dict.columns[i+1]: tmp_csv[j],
|
csv_dict.columns[i+1]: tmp_csv[j],
|
||||||
}
|
"type": 'group'
|
||||||
}, session['token'])
|
}, session['token'])
|
||||||
|
break
|
||||||
os.remove(filepath)
|
os.remove(filepath)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
os.remove(filepath)
|
os.remove(filepath)
|
||||||
|
|
Loading…
Reference in a new issue