This commit is contained in:
Aaron Lee 2021-12-12 18:38:10 +08:00
parent a355887755
commit 22ef472527
29 changed files with 2444 additions and 1051 deletions

347
app.py
View file

@ -1,227 +1,148 @@
from functions import *
from manage import manage
from upload import upload
from login import login
load_dotenv()
app = Flask(__name__)
babel = Babel(app)
app.register_blueprint(manage)
app.register_blueprint(upload)
app.register_blueprint(login)
app.config['SECRET_KEY'] = os.environ.get('SECRET_KEY')
app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql+pymysql://'+os.environ.get('MYSQL_USER')+':'+os.environ.get('MYSQL_PASSWORD')+'@'+os.environ.get('MYSQL_HOST')+'/attendance'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
app.config['BABEL_DEFAULT_LOCALE'] = 'zh_TW'
app.jinja_env.add_extension('jinja2.ext.loopcontrols')
sdb = SQLAlchemy(app)
@app.after_request
def add_header(response):
response.headers['SameSite'] = "Strict"
return response
@ app.route('/', methods=['GET', 'POST'])
def index():
if request.method == 'GET':
if check_login_status():
return render_template('login.html')
return redirect('/select')
elif request.method == 'POST':
email = request.form['username']
if check_login_status():
try:
if (verify_recaptcha("")):
user = auth.sign_in_with_email_and_password(
email, request.form['password'])
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['email'] = user['email']
session['uid'] = user['localId']
session['token'] = user['idToken']
session['refreshToken'] = user['refreshToken']
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')
raise Exception("not real person or admin")
else:
print("ReC Error:", email, flush=True)
flash(
'reCAPTCHA 錯誤,請稍後再試一次<br>reCAPTCHA Failed. Please try again later.')
return redirect('/')
except Exception as e:
print("Error*Login:", email, str(e), flush=True)
flash(
'帳號或密碼錯誤,請重新輸入<br>Incorrect username or password')
return redirect('/')
else:
return redirect('/select')
@app.route('/select', methods=['GET', 'POST'])
def selSubUser():
if check_login_status():
session.clear()
flash("Timeout. 遇時,請重新登入")
class DefaultModelView(ModelView):
restricted = True
def __init__(self, model, session, restricted=True, name=None, category=None, endpoint=None, url=None, **kwargs):
self.restricted = restricted
for k, v in kwargs.items():
setattr(self, k, v)
setattr(self, 'can_export', True)
super(DefaultModelView, self).__init__(model, session, name=name, category=category, endpoint=endpoint, url=url)
def is_accessible(self):
if self.restricted == True:
return ((not check_login_status()) and is_admin() and check_permission())
return ((not check_login_status()) and is_admin())
def inaccessible_callback(self, name, **kwargs):
return redirect('/')
class MyAdminIndexView(AdminIndexView):
def is_accessible(self):
return ((not check_login_status()) and is_admin())
def inaccessible_callback(self, name, **kwargs):
return redirect('/')
refresh_token()
if 'subuser_type' in session and session['subuser_type'] == 'admin':
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*select:", session['email'], str(json.loads(e.args[1])[
'error']['message']), flush=True)
flash(str(json.loads(e.args[1])[
'error']['message']))
return redirect('/select')
@app.route('/chgPassword', methods=['POST', 'GET'])
def chgPassword():
data = {}
if request.method == 'GET':
if not check_login_status():
refresh_token()
return render_template('chgPassword.html')
else:
return abort(404)
elif request.method == 'POST':
oldEmail = session['email']
delUser = False
if not check_login_status():
refresh_token()
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()
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'])
db.child("Users").child(oldUsr['localId']).remove(oldUsr['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*chgPassword:", oldEmail, str(json.loads(e.args[1])[
'error']['message']), flush=True)
flash(str(json.loads(e.args[1])[
'error']['message']))
return redirect('/chgPassword')
@app.route('/forgotPassword', methods=['GET', 'POST'])
def forgotPassword():
if request.method == 'GET':
return render_template('forgotPassword.html')
elif request.method == 'POST':
email = request.form['username']
try:
if (verify_recaptcha("")):
auth.send_password_reset_email(email)
print("forgotPassword 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('/forgotPassword')
except Exception as e:
print("Error*forgotPassword:", email, str(json.loads(e.args[1])[
'error']['message']), flush=True)
flash(str(json.loads(e.args[1])[
'error']['message']))
return redirect('/forgotPassword')
@app.route('/resetPassword', methods=['GET', 'POST'])
def resetPassword():
if request.args.get('oobCode') is None:
return abort(404)
if request.method == 'GET':
return render_template('verifiedChgPassword.html', oobCode=request.args.get('oobCode'))
else:
try:
if (verify_recaptcha("")):
auth.verify_password_reset_code(
request.args.get('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*resetPassword:", request.args.get('oobCode'), str(json.loads(e.args[1])[
'error']['message']), flush=True)
flash(str(json.loads(e.args[1])[
'error']['message']))
return redirect('/resetPassword?mode=resetPassword&oobCode=' + request.args.get('oobCode'))
@ app.route('/logout', methods=['GET'])
def logout():
session.clear()
return redirect('/')
admin = Admin(
app,
name='Attendance 點名系統 後台管理',
template_mode='bootstrap3',
index_view=MyAdminIndexView(),
)
class Users(sdb.Model):
id = sdb.Column(sdb.INT, primary_key=True)
email = sdb.Column(sdb.Text)
name = sdb.Column(sdb.Text)
oldUsername = sdb.Column(sdb.Text)
role = sdb.Column(sdb.CHAR)
password = sdb.Column(sdb.Text)
class Students(sdb.Model):
id = sdb.Column(sdb.INT, primary_key=True)
email = sdb.Column(sdb.INT)
grade = sdb.Column(sdb.INT)
class_ = sdb.Column(sdb.INT)
num = sdb.Column(sdb.INT)
name = sdb.Column(sdb.Text)
ename = sdb.Column(sdb.Text)
classes = sdb.Column(sdb.Text)
password = sdb.Column(sdb.Text)
class Schedule(sdb.Model):
id = sdb.Column(sdb.INT, primary_key=True)
grade = sdb.Column(sdb.INT)
class_ = sdb.Column(sdb.INT)
dow = sdb.Column(sdb.INT)
period = sdb.Column(sdb.CHAR)
subject = sdb.Column(sdb.Text)
teacher = sdb.Column(sdb.Text)
class SpecSchedule(sdb.Model):
__tablename__ = 'specschedule'
id = sdb.Column(sdb.INT, primary_key=True)
grade = sdb.Column(sdb.INT)
class_ = sdb.Column(sdb.INT)
date = sdb.Column(sdb.VARCHAR(11))
period = sdb.Column(sdb.CHAR)
subject = sdb.Column(sdb.Text)
teacher = sdb.Column(sdb.Text)
class GPClasses(sdb.Model):
__tablename__ = 'gpclasses'
id = sdb.Column(sdb.INT, primary_key=True)
category = sdb.Column(sdb.Text)
subclass = sdb.Column(sdb.Text)
about = sdb.Column(sdb.Text)
accs = sdb.Column(sdb.Text)
class Homerooms(sdb.Model):
id = sdb.Column(sdb.INT, primary_key=True)
grade = sdb.Column(sdb.INT)
class_ = sdb.Column(sdb.INT)
accs = sdb.Column(sdb.Text)
class Submission(sdb.Model):
id = sdb.Column(sdb.INT, primary_key=True)
grade = sdb.Column(sdb.INT)
class_ = sdb.Column(sdb.INT)
date = sdb.Column(sdb.VARCHAR(11))
period = sdb.Column(sdb.CHAR)
signature = sdb.Column(sdb.Text)
ds1 = sdb.Column(sdb.INT)
ds2 = sdb.Column(sdb.INT)
ds3 = sdb.Column(sdb.INT)
ds4 = sdb.Column(sdb.INT)
ds5 = sdb.Column(sdb.INT)
ds6 = sdb.Column(sdb.INT)
ds7 = sdb.Column(sdb.INT)
notes = sdb.Column(sdb.Text)
class DS(sdb.Model):
id = sdb.Column(sdb.INT, primary_key=True)
grade = sdb.Column(sdb.INT)
class_ = sdb.Column(sdb.INT)
num = sdb.Column(sdb.INT)
date = sdb.Column(sdb.VARCHAR(11))
period = sdb.Column(sdb.CHAR)
note = sdb.Column(sdb.Text)
status = sdb.Column(sdb.CHAR, default='X')
class Dates(sdb.Model):
id = sdb.Column(sdb.INT, primary_key=True)
date = sdb.Column(sdb.VARCHAR(11))
dow = sdb.Column(sdb.INT)
class Absent(sdb.Model):
id = sdb.Column(sdb.INT, primary_key=True)
grade = sdb.Column(sdb.INT)
class_ = sdb.Column(sdb.INT)
num = sdb.Column(sdb.INT)
date = sdb.Column(sdb.VARCHAR(11))
period = sdb.Column(sdb.CHAR)
status = sdb.Column(sdb.CHAR)
note = sdb.Column(sdb.Text)
class Forgot(sdb.Model):
id = sdb.Column(sdb.INT, primary_key=True)
resetID = sdb.Column(sdb.VARCHAR(11))
email = sdb.Column(sdb.Text)
reqTime = sdb.Column(sdb.VARCHAR(20))
admin.add_view(DefaultModelView(Users, sdb.session, restricted=False, column_exclude_list = ['password'], column_searchable_list = ['name', 'email', 'role']))
admin.add_view(DefaultModelView(Students, sdb.session, restricted=False, column_exclude_list = ['password'], column_searchable_list = ['grade', 'class_', 'num', 'email','name', 'ename', 'classes']))
admin.add_view(DefaultModelView(Schedule, sdb.session, column_searchable_list = ['grade', 'class_', 'dow', 'period', 'subject', 'teacher']))
admin.add_view(DefaultModelView(SpecSchedule, sdb.session, restricted=False, column_searchable_list = ['grade', 'class_', 'date', 'period', 'subject', 'teacher']))
admin.add_view(DefaultModelView(GPClasses, sdb.session, column_searchable_list = ['category', 'subclass', 'about', 'accs']))
admin.add_view(DefaultModelView(Homerooms, sdb.session, column_searchable_list = ['grade', 'class_', 'accs']))
admin.add_view(DefaultModelView(Submission, sdb.session, column_exclude_list=['signature'], column_searchable_list = ['grade', 'class_', 'date', 'period', 'notes']))
admin.add_view(DefaultModelView(DS, sdb.session, restricted=False, column_searchable_list = ['grade', 'class_', 'date', 'period', 'num', 'note', 'status']))
admin.add_view(DefaultModelView(Dates, sdb.session, column_searchable_list = ['date', 'dow']))
admin.add_view(DefaultModelView(Absent, sdb.session, restricted=False, column_searchable_list = ['grade', 'class_', 'date', 'period', 'num', 'status', 'note']))
admin.add_view(DefaultModelView(Forgot, sdb.session, column_searchable_list = ['resetID', 'email', 'reqTime']))
admin.add_link(MenuLink(name='Back to Home 返回一般管理', category='', url='/manage'))
admin.add_link(MenuLink(name='Logout 登出', category='', url='/logout'))
if __name__ == '__main__':
app.run(debug=True)
app.run(debug=True, host='0.0.0.0', port=80)

152
db.sql Normal file
View file

@ -0,0 +1,152 @@
USE attendance;
CREATE TABLE users (
id INT NOT NULL AUTO_INCREMENT,
PRIMARY KEY (id),
email TEXT,
name TEXT,
oldUsername TEXT,
role CHAR, /* S: SuperAdmin / A: Admin / R: Regular User */
password TEXT
);
CREATE TABLE students (
id INT NOT NULL AUTO_INCREMENT,
PRIMARY KEY (id),
email TEXT,
grade INT,
class_ INT,
num INT,
name TEXT,
ename TEXT,
classes TEXT,
password TEXT
/* update column as wishes for group classes
ALTER TABLE students ADD COLUMN IF NOT EXISTS "" VARCHAR(255); */
);
--- Schedule of different day for different class
CREATE TABLE schedule (
id INT NOT NULL AUTO_INCREMENT,
PRIMARY KEY (id),
grade INT,
class_ INT,
dow INT,
period CHAR,
subject TEXT,
teacher TEXT
);
CREATE TABLE specschedule (
id INT NOT NULL AUTO_INCREMENT,
PRIMARY KEY (id),
grade INT,
class_ INT,
date TEXT,
period CHAR,
subject TEXT,
teacher TEXT
);
CREATE TABLE gpclasses (
id INT NOT NULL AUTO_INCREMENT,
PRIMARY KEY (id),
category TEXT,
subclass TEXT,
about TEXT,
accs TEXT
/* Save as JSON
{
0: 'acc1',
1: 'acc2'
}
*/
);
CREATE TABLE homerooms (
id INT NOT NULL AUTO_INCREMENT,
PRIMARY KEY (id),
grade INT,
class_ INT,
accs TEXT
/* Save as JSON
{
0: 'acc1',
1: 'acc2'
}
*/
);
DELETE FROM submission WHERE grade=9;
CREATE TABLE submission (
id INT NOT NULL AUTO_INCREMENT,
PRIMARY KEY (id),
grade INT,
class_ INT,
date VARCHAR(11),
period CHAR,
signature LONGTEXT,
/*
Save as JSON
{
subClass: "signature",
subClass2: "signature2"
}
or
plain text if not GP
*/
ds1 INT DEFAULT 5,
ds2 INT DEFAULT 5,
ds3 INT DEFAULT 5,
ds4 INT DEFAULT 5,
ds5 INT DEFAULT 5,
ds6 INT DEFAULT 5,
ds7 INT DEFAULT 5,
notes TEXT
/*
Save as JSON
{
'num': 'whatevernote',
'num2': 'morenote'
}
*/
);
CREATE TABLE ds (
id INT NOT NULL AUTO_INCREMENT,
PRIMARY KEY (id),
grade INT,
class_ INT,
date VARCHAR(11),
period CHAR,
num INT,
note TEXT,
status CHAR DEFAULT 'X'
);
CREATE TABLE dates (
id INT NOT NULL AUTO_INCREMENT,
PRIMARY KEY (id),
date VARCHAR(11),
dow INT
);
CREATE TABLE absent (
id INT NOT NULL AUTO_INCREMENT,
PRIMARY KEY (id),
grade INT,
class_ INT,
date VARCHAR(11),
period CHAR,
num INT,
status CHAR, /* L: 遲到 / K: 曠課 / G: 事假 / S: 病假 / F: 喪假 / P: 疫情假 / O: 公假*/
note TEXT
);
CREATE TABLE forgot (
id INT NOT NULL AUTO_INCREMENT,
PRIMARY KEY (id),
userType CHAR, /* T: teacher / S: student */
resetID VARCHAR(11),
email TEXT,
reqTime VARCHAR(20)
);

View file

@ -1,7 +1,6 @@
from flask import *
from typing import OrderedDict
from flask import *
import pyrebase
from datetime import datetime
import pytz
import os
@ -9,32 +8,65 @@ import base64
import csv
import os
import pandas as pd
from random import randint
from random import randint, choices
import string
from dotenv import load_dotenv
from passlib.hash import sha256_crypt
import mysql.connector
import requests
from pprint import pprint
from flask_sqlalchemy import SQLAlchemy
from flask_admin import Admin, AdminIndexView, expose
from flask_admin.menu import MenuLink
from flask_admin.contrib.sqla import ModelView
from flask_babelex import Babel
load_dotenv()
config = {
"apiKey": os.environ.get('apiKey'),
"authDomain": os.environ.get('authDomain'),
"databaseURL": os.environ.get('databaseURL'),
"storageBucket": os.environ.get('storageBucket'),
"serviceAccount": os.environ.get('serviceAccount'),
"messagingSenderId": os.environ.get('messagingSenderId'),
"appId": os.environ.get('appId'),
"measurementId": os.environ.get('measurementId'),
}
firebase = pyrebase.initialize_app(config)
db = firebase.database()
auth = firebase.auth()
storage = firebase.storage()
tz = pytz.timezone('Asia/Taipei')
DSBOARD = [
"上課前秩序",
"上課前禮貌",
"課間秩序",
"板擦清潔",
"講桌乾淨",
"地板整齊",
"桌椅整齊"
]
DSTEXT = [
"",
"",
"",
"",
"",
"",
""
]
DSOFFENSES = {
'A': "把玩物品、不專心聽講",
'B': "書寫或傳遞紙條、物品",
'C': "自言自語或與同學交談",
'D': "接話、大聲笑、起哄、發出怪聲",
'E': "亂動、逗弄同學、影響教學情境",
'F': "閱讀與該堂課無關之書籍",
'G': "不敬師長、態度傲慢",
'H': "其他經任教老師糾正、制止之行為",
'Z': "上課睡覺"
}
def refresh_db():
return mysql.connector.connect(user=os.environ.get('MYSQL_USER'), password=os.environ.get('MYSQL_PASSWORD'),
host=os.environ.get('MYSQL_HOST'),
database='attendance')
def genHash(password):
return sha256_crypt.hash(password)
def verifyPassword(password, hash):
return sha256_crypt.verify(password, hash)
def refresh_token():
user = auth.refresh(session['refreshToken'])
session['is_logged_in'] = True
session['token'] = user['idToken']
session['refreshToken'] = user['refreshToken']
session['loginTime'] = datetime.now(tz)
@ -47,6 +79,25 @@ def check_login_status():
session['is_logged_in'] == False or
(datetime.now(tz) - session['loginTime']).total_seconds() > 3600)
def send_email(to, subject, text):
return requests.post(
"https://api.mailgun.net/v3/mg.aaronlee.tech/messages",
auth=("api", os.environ.get("MG_APIKEY")),
data={"from": "Attendance 點名系統 <attendance@mg.aaronlee.tech>",
"to": [to],
"subject": subject,
"html": text})
def getName(grade, class_, number):
db = refresh_db()
cursor = db.cursor()
print(grade, class_, number)
cursor.execute("SELECT name FROM students WHERE grade=%s AND class=%s AND number=%s", (grade, class_, number))
name = cursor.fetchone()
cursor.close()
db.close()
return name[0]
# LOGIN
@ -63,20 +114,14 @@ def verify_recaptcha(response):
return r.json()['success']
# UPLOAD
def is_admin():
return 'subuser_type' in session and session['subuser_type'] == 'admin'
def check_permission():
return (db.child('Users').child(session['uid']).child('permission').get(session['token']).val() == 'admin' and
db.child("Users").child(session['uid']).child("showUpload").get(session['token']).val() == '1')
def addZeroesUntil(str, number):
if len(str) >= number:
return str
if 'subuser_type' in session and session['subuser_type'] == 'admin':
return session['showUpload']
else:
str = str + '0'
return addZeroesUntil(str, number)
return False
# MANAGE
def removeprefix(s, prefix):

329
login.py Normal file
View file

@ -0,0 +1,329 @@
from functions import *
login = Blueprint('login', __name__)
@login.after_request
def add_header(response):
response.headers['SameSite'] = "Strict"
return response
@login.route('/', methods=['GET', 'POST'])
def index():
if request.method == 'GET':
if check_login_status():
return render_template('login.html')
return redirect('/select')
elif request.method == 'POST':
email = request.form['username']
if check_login_status():
try:
if (verify_recaptcha("")):
if request.form['user_type'] == 'teacher':
db = refresh_db()
cursor = db.cursor(buffered=True)
cursor.execute("SELECT name, role, oldUsername, password FROM users WHERE email = %s", (email,))
user = cursor.fetchone()
cursor.close()
if user == None or not verifyPassword(request.form['password'], user[3]):
raise Exception('Invalid Login')
usrRole = user[1]
if (usrRole == 'R'):
print("RealPerson Login SUCC:", email, flush=True)
session['is_logged_in'] = True
session['email'] = email
session['name'] = user[0]
session['oldUsername'] = user[2]
session['loginTime'] = datetime.now(tz)
return redirect('/select')
if (usrRole == 'A' or usrRole == 'S'):
print("Admin Login SUCC:", email, flush=True)
session['subuser_type'] = 'admin'
session['is_logged_in'] = True
session['email'] = email
session['oldUsername'] = user[2]
session['loginTime'] = datetime.now(tz)
session['showUpload'] = True if usrRole == 'S' else False
return redirect('/manage')
raise Exception("not real person or admin")
elif request.form['user_type'] == 'student':
db = refresh_db()
cursor = db.cursor(buffered=True)
cursor.execute("SELECT password, grade, class_, num, name FROM students WHERE email = %s", (email,))
user = cursor.fetchone()
cursor.close()
if user == None or not verifyPassword(request.form['password'], user[0]):
raise Exception('Invalid Login')
print("Student Login SUCC:", email, flush=True)
session['is_logged_in'] = True
session['email'] = email
session['loginTime'] = datetime.now(tz)
session['user_type'] = 'student'
session['name'] = user[4]
session['grade'] = user[1]
session['class'] = user[2]
session['num'] = user[3]
return redirect('/manage')
else:
print("ReC Error:", email, flush=True)
flash(
'reCAPTCHA 錯誤,請稍後再試一次<br>reCAPTCHA Failed. Please try again later.')
return redirect('/')
except Exception as e:
print("Error*Login:", email, str(e), flush=True)
flash(
'帳號或密碼錯誤,請重新輸入<br>Incorrect username or password')
return redirect('/')
else:
return redirect('/select')
@login.route('/select', methods=['GET', 'POST'])
def selSubUser():
if check_login_status():
session.clear()
flash("Timeout. 遇時,請重新登入")
return redirect('/')
refresh_token()
if 'subuser_type' in session and session['subuser_type'] == 'admin' or 'user_type' in session and session['user_type'] == 'student':
return redirect('/manage')
if request.method == 'GET':
db = refresh_db()
cursor = db.cursor(buffered=True)
cursor.execute("SELECT category, subclass FROM gpclasses WHERE accs LIKE %s LIMIT 1", ('%'+session['oldUsername']+'%',))
classes = cursor.fetchone()
cursor.close()
hasGroup = False
if classes != None:
hasGroup = True
db = refresh_db()
cursor = db.cursor(buffered=True)
cursor.execute("SELECT grade, class_ FROM homerooms WHERE accs LIKE %s", ('%'+session['oldUsername']+'%',))
homerooms = cursor.fetchall()
cursor.close()
hrC = {}
for h in homerooms:
hrC[h[0]] = []
hrC[h[0]].append(h[1])
return render_template('selSubUser.html', group=hasGroup, homeroom=hrC, name=session['name'])
else:
data = request.form['subuser_sel'].split('^')
if data == []:
return redirect('/select')
try:
if (verify_recaptcha("")):
if (data[0] == 'homeroom'):
session['homeroom'] = data[1] + '^' + data[2]
session['subuser_type'] = 'homeroom'
elif (data[0] == 'group'):
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*select:", session['email'], str(e), flush=True)
flash(str(e))
return redirect('/select')
@login.route('/chgPassword', methods=['POST', 'GET'])
def chgPassword():
data = {}
if request.method == 'GET':
if not check_login_status():
refresh_token()
return render_template('chgPassword.html')
else:
return abort(404)
elif request.method == 'POST':
oldEmail = session['email']
if not check_login_status():
refresh_token()
try:
if (verify_recaptcha("")):
db = refresh_db()
cursor = db.cursor(buffered=True)
if ('user_type' in session and session['user_type'] == 'student'):
cursor.execute("SELECT password FROM students WHERE email = %s", (oldEmail,))
else:
cursor.execute("SELECT password FROM users WHERE email = %s", (oldEmail,))
user = cursor.fetchone()
cursor.close()
if user == None or not verifyPassword(request.form['password'], user[0]):
raise Exception('Invalid Login')
if len(request.form['new_password']) < 6:
raise Exception('密碼長度不足<br>Password not long enough')
db = refresh_db()
cursor = db.cursor(buffered=True)
if (request.form['new_username'] != oldEmail and request.form['new_username'] != ''):
if ('user_type' in session and session['user_type'] == 'student'):
cursor.execute("SELECT * FROM students WHERE email = %s", (request.form['new_username'],))
else:
cursor.execute("SELECT * FROM users WHERE email = %s", (request.form['new_username'],))
user = cursor.fetchone()
cursor.close()
if user != None:
raise Exception('帳號已被使用<br>Username already used')
db = refresh_db()
cursor = db.cursor(buffered=True)
if ('user_type' in session and session['user_type'] == 'student'):
cursor.execute("UPDATE students SET password = %s WHERE email = %s", (genHash(request.form['new_password']), oldEmail))
if (request.form['new_username'] != oldEmail and request.form['new_username'] != ''):
cursor.execute("UPDATE students SET email = %s WHERE email = %s", (request.form['new_username'], oldEmail))
else:
cursor.execute("UPDATE users SET password = %s WHERE email = %s", (
genHash(request.form['new_password']), oldEmail))
if (request.form['new_username'] != oldEmail and request.form['new_username'] != ''):
cursor.execute("UPDATE users SET email = %s WHERE email = %s", (request.form['new_username'], oldEmail))
db.commit()
cursor.close()
session.clear()
if (request.form['new_username'] != oldEmail and request.form['new_username'] != ''):
send_email(oldEmail, "Email Changed 信箱已更改",
"""<hr>
Your email was changed at %s to %s.<br>
If you did not change your email, please contact the student affair's office immediately.
<hr>
你的信箱已在 %s 更改為 %s <br>
如果你沒有更改信箱請立即聯絡學務處
<hr>
<small>This email was sent automatically. Please do not reply.<br>
這個郵件是自動發送的請不要回覆</small>
""" % (str(datetime.now(tz)), request.form['new_username'], str(datetime.now(tz)), request.form['new_username']))
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:
flash(str(e))
return redirect('/chgPassword')
@login.route('/forgotPassword', methods=['GET', 'POST'])
def forgotPassword():
if request.method == 'GET':
return render_template('forgotPassword.html')
elif request.method == 'POST':
email = request.form['username']
try:
if (verify_recaptcha("")):
db = refresh_db()
cursor = db.cursor(buffered=True)
if (request.form['user_type'] == 'student'):
cursor.execute("SELECT * FROM students WHERE email = %s", (email,))
elif (request.form['user_type'] == 'teacher'):
cursor.execute("SELECT email FROM users WHERE email = %s", (email,))
user = cursor.fetchone()
cursor.close()
if user == None:
raise Exception('無此 Email<br>Invalid Email')
exists = True
while exists:
resetID = ''.join(choices(string.ascii_lowercase + string.digits, k=10))
cursor = db.cursor(buffered=True)
cursor.execute("SELECT * FROM forgot WHERE resetID = %s", (resetID,))
user = cursor.fetchone()
cursor.close()
exists = (user != None)
db = refresh_db()
cursor = db.cursor(buffered=True)
cursor.execute("""
INSERT INTO forgot (resetID, email, reqTime, userType)
VALUES (%s, %s, %s, %s)
""", (resetID, email, datetime.strftime(datetime.now(tz), '%Y-%m-%d %H:%M:%S'), 'T' if request.form['user_type'] == 'teacher' else 'S'))
db.commit()
cursor.close()
send_email(email, "Password Reset 重置密碼",
"""<hr>
Please go to the following link to reset your password:<br>
https://abs.aaronlee.tech/resetPassword?resetCode=%s<br>
If you did not request a password reset, please ignore this email.
<hr>
請點選以下連結重置密碼<br>
https://abs.aaronlee.tech/resetPassword?resetCode=%s<br>
如果您沒有要求重置密碼請忽略此郵件
<hr>
<small>This email was sent automatically. Please do not reply.<br>
這個郵件是自動發送的請不要回覆</small>
""" % (resetID, resetID))
print("forgotPassword 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('/forgotPassword')
except Exception as e:
print("Error*forgotPassword:", email, str(escape), flush=True)
flash(str(e))
return redirect('/forgotPassword')
@login.route('/resetPassword', methods=['GET', 'POST'])
def resetPassword():
if request.args.get('resetCode') is None:
return abort(404)
if request.method == 'GET':
return render_template('verifiedChgPassword.html', resetCode=request.args.get('resetCode'))
else:
try:
if (verify_recaptcha("")):
db = refresh_db()
cursor = db.cursor(buffered=True)
cursor.execute("""
SELECT resetID, email, reqTime, userType
FROM forgot
WHERE resetID = %s
""", (request.args.get('resetCode'),))
user = cursor.fetchone()
cursor.close()
if user == None:
raise Exception('無此重置密碼代碼<br>Invalid reset password code')
if (datetime.now(tz) - datetime.strptime(user[2], '%Y-%m-%d %H:%M:%S')).seconds > 3600:
cursor.execute("DELETE FROM forgot WHERE resetID = %s", (user[0],))
db.commit()
cursor.close()
raise Exception('重置密碼代碼已過期<br>Reset password code expired')
if len(request.form['password']) < 6:
raise Exception('密碼長度不足<br>Password not long enough')
db = refresh_db()
cursor = db.cursor(buffered=True)
if (user[3] == 'T'):
cursor.execute("UPDATE users SET password = %s WHERE email = %s", (
genHash(request.form['password']), user[1]))
elif (user[3] == 'S'):
cursor.execute("UPDATE students SET password = %s WHERE email = %s", (
genHash(request.form['password']), user[1]))
db.commit()
cursor.close()
db = refresh_db()
cursor = db.cursor(buffered=True)
cursor.execute("DELETE FROM forgot WHERE resetID = %s", (user[0],))
db.commit()
cursor.close()
session.clear()
flash(
'重置密碼成功,請重新登入<br>Password changed successfully. 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*resetPassword:", request.args.get('resetCode'), str(e), flush=True)
flash(str(e))
return redirect('/resetPassword?resetCode=' + request.args.get('resetCode'))
@login.route('/logout', methods=['GET'])
def logout():
session.clear()
return redirect('/')

758
manage.py
View file

@ -6,137 +6,230 @@ manage = Blueprint('manage', __name__)
def manageProcess(fCommand, fData):
if (check_login_status()):
return redirect('/logout')
# this is to fix a bug where pyrebase doesnt load the first request
db.child("Users").child(
session['uid']).child("permission").get(session['token']).val()
# end bug fix
refresh_token()
if 'user_type' in session and session['user_type'] == 'student':
return redirect('/student')
pl = session['subuser_type']
if pl == 'admin':
homerooms = db.child("Homerooms").get(session['token']).val()
db = refresh_db()
cursor = db.cursor()
cursor.execute("SELECT grade, class_ FROM homerooms ORDER BY grade ASC, class_ ASC")
homeroomsSQL = cursor.fetchall()
homerooms = {}
for h in homeroomsSQL:
if h[0] in homerooms:
homerooms[h[0]].append(h[1])
else:
homerooms[h[0]] = [h[1]]
currRoom = []
if fCommand == "admin":
currRoom = fData[0].split("^")
else:
for i in homerooms:
currRoom.append(i)
for j in homerooms[i]:
currRoom.append(j)
break
break
homeroomData = homerooms[currRoom[0]][currRoom[1]]
absData = homeroomData["Absent"]
homeroomData.pop('Absent')
if 'placeholder' in homeroomData:
homeroomData.pop('placeholder')
currRoom = [homeroomsSQL[0][0], homeroomsSQL[0][1]]
cursor = db.cursor()
cursor.execute("SELECT num,name,ename,classes FROM students WHERE grade=%s AND class_=%s ORDER BY num ASC", (currRoom[0], currRoom[1]))
students = cursor.fetchall()
studGP = {}
for s in students:
studGP[s[0]] = json.loads(s[3])
cursor = db.cursor()
cursor.execute("SELECT date FROM dates ORDER BY date ASC")
dates = cursor.fetchall()
currDate = ""
if fCommand != "":
currDate = fData[1]
else:
for i in absData:
currDate = i
if i >= datetime.now(tz).strftime("%Y-%m-%d"):
for i in dates:
currDate = i[0]
if i[0] >= datetime.now(tz).strftime("%Y-%m-%d"):
break
return render_template('admin.html', homerooms=homerooms, absData=absData,
homeroomCode=currRoom, homeroomData=homeroomData, currDate=currDate, periods=['m', '1', '2', '3', '4',
'n', '5', '6', '7', '8', '9'], showUpload=session['showUpload'])
elif pl == 'group':
cateData = db.child("Classes").child(
"GP_Class").child(session['category']).get(session['token']).val()
cclass = {
"name": cateData['Class'][session['class']]['name'],
"category": session['category'],
"class_id": session['class']
}
homerooms = cateData['Homerooms']
currDate = ""
confirmed = []
absData = {}
for h in homerooms:
h = h.split('^')
hrData = db.child("Homerooms").child(
h[0]).child(h[1]).get(session['token']).val()
tmpAbsData = hrData['Absent']
hrData.pop('Absent')
if 'placeholder' in hrData:
hrData.pop('placeholder')
periods = []
dow = ""
if currDate == "":
if fCommand == 'date':
currDate = fData
for j in tmpAbsData[currDate]:
if j == "dow":
dow = tmpAbsData[currDate][j]
continue
elif j == "confirm":
confirmed.append([h[0], h[1]])
continue
elif j == "notes":
continue
if (tmpAbsData[currDate][j]['name'] == 'GP' and
tmpAbsData[currDate][j]['teacher'] == cclass['category']):
periods.append(j)
else:
for i in tmpAbsData:
currDate = i
if i >= datetime.now(tz).strftime("%Y-%m-%d"):
tmp = False
for j in tmpAbsData[i]:
if j == "dow":
dow = tmpAbsData[i][j]
continue
elif j == "confirm":
confirmed.append([h[0], h[1]])
continue
elif j == 'notes':
continue
if (tmpAbsData[i][j]['name'] == 'GP' and
tmpAbsData[i][j]['teacher'] == cclass['category']):
periods.append(j)
tmp = True
if tmp == True:
break
cursor = db.cursor()
cursor.execute("SELECT dow FROM dates WHERE date=%s", (currDate, ))
dow = cursor.fetchone()[0]
cursor = db.cursor()
cursor.execute("SELECT period, subject, teacher FROM schedule WHERE grade=%s AND class_=%s AND dow=%s", (currRoom[0], currRoom[1], dow))
scheduleSQL = cursor.fetchall()
schedule = {}
for i in scheduleSQL:
schedule[i[0]] = {
"subject": i[1],
"teacher": i[2],
}
cursor = db.cursor()
cursor.execute("SELECT period, subject, teacher FROM specschedule WHERE grade=%s AND class_=%s AND date=%s", (currRoom[0], currRoom[1], currDate))
specScheduleSQL = cursor.fetchall()
for i in specScheduleSQL:
schedule[i[0]] = {
"subject": i[1],
"teacher": i[2],
"special": True
}
cursor = db.cursor()
cursor.execute("SELECT period, signature, notes, ds1,ds2,ds3,ds4,ds5,ds6,ds7 FROM submission WHERE grade=%s AND class_=%s AND date=%s", (currRoom[0], currRoom[1], currDate))
submissionSQL = cursor.fetchall()
submission = {}
cursor = db.cursor()
cursor.execute("SELECT period, num, note FROM ds WHERE grade=%s AND class_=%s AND date=%s", (currRoom[0], currRoom[1], currDate))
idvDSSQL = cursor.fetchall()
idvDS = {}
for i in idvDSSQL:
if i[0] not in idvDS:
idvDS[i[0]] = {}
idvDS[i[0]][i[1]]= i[2]
for i in submissionSQL:
if i[0] == 'c':
submission[i[0]] = {
"signature": i[1],
"notes": i[2]
}
elif schedule[i[0]]["subject"] == "GP":
submission[i[0]] = OrderedDict()
signatures = json.loads(i[1])
for j in signatures:
submission[i[0]][j] = {
"signature": signatures[j],
}
submission[i[0]]["notes"] = i[2]
else:
for j in tmpAbsData[currDate]:
if j == "dow":
dow = tmpAbsData[currDate][j]
submission[i[0]] = {
"signature": i[1],
"notes": i[2],
"ds1": i[3],
"ds2": i[4],
"ds3": i[5],
"ds4": i[6],
"ds5": i[7],
"ds6": i[8],
"ds7": i[9]
}
cursor = db.cursor()
cursor.execute("SELECT period, num, status, note FROM absent WHERE grade=%s AND class_=%s AND date=%s", (currRoom[0], currRoom[1], currDate))
absentDataSQL = cursor.fetchall()
absentData = {}
for p in ['m', '1', '2', '3', '4', 'n', '5', '6', '7', '8', '9']:
absentData[p] = {}
for i in absentDataSQL:
absentData[i[0]][i[1]] = i[2]
for i in absentDataSQL:
absentData[i[0]][i[1]] = {
'status': i[2],
'note': i[3],
}
return render_template('admin.html', homerooms=homerooms, currRoom=currRoom, students=students, currDate=currDate, schedule=schedule, submission=submission, studGP=studGP, idvDS=idvDS,
dates=dates, absentData=absentData, periods=['m', '1', '2', '3', '4', 'n', '5', '6', '7', '8', '9'], showUpload=session['showUpload'], dsboard=DSBOARD, dstext=DSTEXT, dsoffenses=DSOFFENSES)
# 'n', '5', '6', '7', '8', '9'], showUpload=session['showUpload'])
elif pl == 'group':
db = refresh_db()
cursor = db.cursor()
cursor.execute("SELECT category, subclass FROM gpclasses WHERE accs LIKE %s", ('%'+session['oldUsername']+'%',))
gpclasses = cursor.fetchall()
data = {}
currDate = ""
dow = ""
cursor = db.cursor()
cursor.execute("SELECT date FROM dates ORDER BY date ASC")
dates = cursor.fetchall()
if fCommand != "":
currDate = fData
else:
for i in dates:
currDate = i[0]
if i[0] >= datetime.now(tz).strftime("%Y-%m-%d"):
break
cursor = db.cursor()
cursor.execute("SELECT dow FROM dates WHERE date=%s", (currDate, ))
dow = cursor.fetchone()[0]
for c in gpclasses:
cursor.execute("SELECT about FROM gpclasses WHERE subclass=%s AND category=%s",
(c[1], c[0]))
cclass = {
"name": cursor.fetchone()[0],
"category": c[0],
"class_id": c[1]
}
data[cclass['category'] + ' ' + cclass['class_id']] = {
"cdata": cclass,
}
# get student list
cursor.execute("SELECT grade,class_,num,name,ename FROM students WHERE classes LIKE " + '\'%\"'+ cclass['category'] + '\": \"' + cclass['class_id'] +'\"%\'' + " ORDER BY grade ASC,class_ ASC,num ASC")
students = cursor.fetchall()
# get student homerooms
homerooms = []
for x in students:
if (str(x[0]) + '^' + str(x[1])) not in homerooms:
homerooms.append(str(x[0]) + '^' + str(x[1]))
# get periods
for h in homerooms:
hs = h.split('^')
cursor.execute("SELECT period FROM schedule WHERE grade=%s AND class_=%s AND dow=%s AND teacher=%s", (hs[0], hs[1], dow, cclass['category']))
scheduleSQL = cursor.fetchall()
cursor.execute("SELECT period FROM specschedule WHERE grade=%s AND class_=%s AND date=%s AND teacher=%s", (hs[0], hs[1], currDate, cclass['category']))
specNTPSQL = cursor.fetchall()
for s in specNTPSQL:
scheduleSQL.append(s)
cursor.execute("SELECT period FROM specschedule WHERE grade=%s AND class_=%s AND date=%s AND teacher!=%s", (hs[0], hs[1], currDate, cclass['category']))
specNTDSQL = cursor.fetchall()
specNTD = {}
for i in specNTDSQL:
specNTD[i[0]] = True
print(h, specNTD, scheduleSQL)
for p in scheduleSQL:
if p[0] in specNTD and specNTD[p[0]] == True:
continue
elif j == "confirm":
confirmed.append([h[0], h[1]])
continue
elif j == "notes":
continue
if (tmpAbsData[currDate][j]['name'] == 'GP' and
tmpAbsData[currDate][j]['teacher'] == cclass['category']):
periods.append(j)
for p in periods:
if not p in absData:
absData[p] = {}
for p in periods:
if not h[0] in absData[p]:
absData[p][h[0]] = {}
absData[p][h[0]][h[1]] = {}
if 'notes' in tmpAbsData[currDate][p]:
absData[p][h[0]][h[1]
]['notes'] = tmpAbsData[currDate][p]['notes']
for num in hrData:
if (cclass['category'] in hrData[num]['GP_Class'] and
hrData[num]['GP_Class'][cclass['category']] == cclass['class_id']):
for p in periods:
absData[p][h[0]][h[1]][num] = {
"name": hrData[num]['name'],
"eng_name": hrData[num]['eng_name'],
"alr_fill": (('signature' in tmpAbsData[currDate][p]) and
(cclass['class_id'] in tmpAbsData[currDate][p]['signature'] or 'STUD_AFFAIR_OFFICE' in tmpAbsData[currDate][p]['signature'])),
"absent": False if not num in tmpAbsData[currDate][p] else tmpAbsData[currDate][p][num]
}
return render_template('group_teach.html', dateKeys=sorted(tmpAbsData.keys()), cclass=cclass, absData=absData, dow=dow, currDate=currDate, tmpAbsData=tmpAbsData, confirmed=confirmed)
if p[0] not in data[cclass['category'] + ' ' + cclass['class_id']]:
data[cclass['category'] + ' ' + cclass['class_id']][p[0]] = {}
if (h not in data[cclass['category'] + ' ' + cclass['class_id']][p[0]]):
data[cclass['category'] + ' ' + cclass['class_id']][p[0]][h] = {}
cursor = db.cursor()
cursor.execute("SELECT signature FROM submission WHERE grade=%s AND class_=%s AND date=%s AND period=%s", (hs[0], hs[1], currDate, p[0]))
submissionSQL = cursor.fetchone()
submitted = False
try:
if submissionSQL[0] == 'STUD_AFFAIR_OFFICE':
submitted = True
except:
pass
try:
signatures = json.loads(submissionSQL[0])
if cclass['class_id'] in signatures:
submitted = True
except:
pass
hrCfrm = False
if not submitted:
cursor = db.cursor()
cursor.execute("SELECT signature FROM submission WHERE grade=%s AND class_=%s AND date=%s AND period='c'", (hs[0], hs[1], currDate))
hrCfrm = True if cursor.fetchone() != None else submitted
cursor = db.cursor()
cursor.execute("SELECT num, status, note FROM absent WHERE grade=%s AND class_=%s AND date=%s AND period=%s", (hs[0], hs[1], currDate, p[0]))
absentDataSQL = cursor.fetchall()
for x in students:
if (str(x[0])==hs[0] and str(x[1])==hs[1]):
studStatus = [item for item in absentDataSQL if item[0] == x[2]]
status = ""
if submitted:
if studStatus == []:
status = 'present'
else:
status = studStatus[0][1]
else:
if studStatus == []:
if hrCfrm:
status = '--'
else:
status = 'na'
else:
status = studStatus[0][1]
data[cclass['category'] + ' ' + cclass['class_id']][p[0]][h][x[2]] = {
"name": x[3],
"ename": x[4],
"status": status,
"note": '' if studStatus == [] else studStatus[0][2],
}
return render_template('group_teach.html', dates=dates, currDate=currDate, data=data, dsoffenses=DSOFFENSES)
elif pl == 'homeroom':
homeroom = session['homeroom'].split('^')
homeroomData = db.child("Homerooms").child(homeroom[0]).child(
homeroom[1]).get(session['token']).val()
db = refresh_db()
times = OrderedDict({
'm': '00:00',
'1': '08:15',
@ -158,20 +251,96 @@ def manageProcess(fCommand, fData):
currTime <= times[next_item(times, i)]):
currPeriod = i
break
absData = homeroomData["Absent"]
homeroomData.pop('Absent')
if 'placeholder' in homeroomData:
homeroomData.pop('placeholder')
currRoom = session['homeroom'].split('^')
cursor = db.cursor()
cursor.execute("SELECT num,name,ename,classes FROM students WHERE grade=%s AND class_=%s ORDER BY num ASC", (currRoom[0], currRoom[1]))
students = cursor.fetchall()
studGP = {}
for s in students:
studGP[s[0]] = json.loads(s[3])
cursor = db.cursor()
cursor.execute("SELECT date FROM dates ORDER BY date ASC")
dates = cursor.fetchall()
currDate = ""
if fCommand == 'date':
if fCommand != "":
currDate = fData
else:
for i in absData:
currDate = i
if i >= datetime.now(tz).strftime("%Y-%m-%d"):
for i in dates:
currDate = i[0]
if i[0] >= datetime.now(tz).strftime("%Y-%m-%d"):
break
return render_template('homeroom.html', absData=absData, homeroomCode=homeroom, homeroomData=homeroomData,
currDate=currDate, dateKeys=sorted(absData.keys()), periods=['m', '1', '2', '3', '4', 'n', '5', '6', '7', '8', '9'], currPeriod=currPeriod)
cursor = db.cursor()
cursor.execute("SELECT dow FROM dates WHERE date=%s", (currDate, ))
dow = cursor.fetchone()[0]
cursor = db.cursor()
cursor.execute("SELECT period, subject, teacher FROM schedule WHERE grade=%s AND class_=%s AND dow=%s", (currRoom[0], currRoom[1], dow))
scheduleSQL = cursor.fetchall()
schedule = {}
for i in scheduleSQL:
schedule[i[0]] = {
"subject": i[1],
"teacher": i[2],
}
cursor = db.cursor()
cursor.execute("SELECT period, subject, teacher FROM specschedule WHERE grade=%s AND class_=%s AND date=%s", (currRoom[0], currRoom[1], currDate))
specScheduleSQL = cursor.fetchall()
for i in specScheduleSQL:
schedule[i[0]] = {
"subject": i[1],
"teacher": i[2],
"special": True
}
cursor = db.cursor()
cursor.execute("SELECT period, signature, notes, ds1,ds2,ds3,ds4,ds5,ds6,ds7 FROM submission WHERE grade=%s AND class_=%s AND date=%s", (currRoom[0], currRoom[1], currDate))
submissionSQL = cursor.fetchall()
cursor = db.cursor()
cursor.execute("SELECT period, num, note FROM ds WHERE grade=%s AND class_=%s AND date=%s", (currRoom[0], currRoom[1], currDate))
idvDSSQL = cursor.fetchall()
idvDS = {}
for i in idvDSSQL:
if i[0] not in idvDS:
idvDS[i[0]] = {}
idvDS[i[0]][i[1]]= i[2]
submission = {}
for i in submissionSQL:
if i[0] == 'c':
submission[i[0]] = {
"signature": i[1],
"notes": i[2]
}
elif schedule[i[0]]["subject"] == "GP":
submission[i[0]] = OrderedDict()
signatures = json.loads(i[1])
for j in signatures:
submission[i[0]][j] = {
"signature": signatures[j],
}
submission[i[0]]["notes"] = i[2]
else:
submission[i[0]] = {
"signature": i[1],
"notes": i[2],
"ds1": i[3],
"ds2": i[4],
"ds3": i[5],
"ds4": i[6],
"ds5": i[7],
"ds6": i[8],
"ds7": i[9],
}
cursor = db.cursor()
cursor.execute("SELECT period, num, status, note FROM absent WHERE grade=%s AND class_=%s AND date=%s", (currRoom[0], currRoom[1], currDate))
absentDataSQL = cursor.fetchall()
absentData = {}
for p in ['m', '1', '2', '3', '4', 'n', '5', '6', '7', '8', '9']:
absentData[p] = {}
for i in absentDataSQL:
absentData[i[0]][i[1]] = {
'status': i[2],
'note': i[3],
}
return render_template('homeroom.html', currRoom=currRoom, students=students, currDate=currDate, schedule=schedule, submission=submission, currPeriod=currPeriod, studGP=studGP,
dates=dates, absentData=absentData, periods=['m', '1', '2', '3', '4', 'n', '5', '6', '7', '8', '9'], dsboard=DSBOARD, dstext=DSTEXT, dsoffenses=DSOFFENSES, idvDS=idvDS)
else:
return redirect('/logout')
@ -194,102 +363,206 @@ def manage_admin(g, r, date):
]
return manageProcess("admin", data)
@manage.route('/student', methods=['GET'])
def showStudentAbs():
if (check_login_status()):
return redirect('/logout')
refresh_token()
if not ('user_type' in session and session['user_type'] == 'student'):
return redirect('/')
db = refresh_db()
cursor = db.cursor()
cursor.execute("SELECT date, period, num, status, note FROM absent WHERE grade=%s AND class_=%s AND num=%s ORDER BY date DESC, period DESC, num ASC", (session['grade'], session['class'], session['num']))
absentDataSQL = cursor.fetchall()
return render_template("list.html", title="Student Absent List | 學生缺勤紀錄", mode='STUDABS', data=absentDataSQL, currRoom=[session['grade'],session['class']], name=session['name'], num=session['num'])
@manage.route('/student/ds', methods=['GET'])
def showStudentDS():
if (check_login_status()):
return redirect('/logout')
refresh_token()
if not ('user_type' in session and session['user_type'] == 'student'):
return redirect('/')
db = refresh_db()
cursor = db.cursor()
cursor.execute("SELECT date, period, num, note FROM ds WHERE grade=%s AND class_=%s AND num=%s ORDER BY date DESC, period DESC, num ASC", (session['grade'], session['class'], session['num']))
dsDataSQL = cursor.fetchall()
print(dsDataSQL)
return render_template("list.html", title="Student DS List | 學生定心紀錄", mode='STUDDS', data=dsDataSQL, currRoom=[session['grade'],session['class']], name=session['name'], num=session['num'])
@manage.route('/manage/abs', methods=['GET'])
def showAllAbs():
if (check_login_status()):
return redirect('/logout')
refresh_token()
currRoom = session['homeroom'].split('^')
db = refresh_db()
cursor = db.cursor()
cursor.execute("SELECT num,name,ename FROM students WHERE grade=%s AND class_=%s ORDER BY num ASC", (currRoom[0], currRoom[1]))
studentsSQL = cursor.fetchall()
students = {}
for st in studentsSQL:
students[st[0]] = {
'name': st[1],
'ename': st[2],
}
cursor = db.cursor()
cursor.execute("SELECT date, period, num, status, note FROM absent WHERE grade=%s AND class_=%s ORDER BY date DESC, period DESC, num ASC", (currRoom[0], currRoom[1]))
absentDataSQL = cursor.fetchall()
return render_template("list.html", title="Absent List | 缺勤紀錄", mode='ABS', students=students, data=absentDataSQL, currRoom=currRoom)
@manage.route('/manage/ds', methods=['GET'])
def showAllDS():
if (check_login_status()):
return redirect('/logout')
refresh_token()
currRoom = session['homeroom'].split('^')
db = refresh_db()
cursor = db.cursor()
cursor.execute("SELECT num,name,ename FROM students WHERE grade=%s AND class_=%s ORDER BY num ASC", (currRoom[0], currRoom[1]))
studentsSQL = cursor.fetchall()
students = {}
for st in studentsSQL:
students[st[0]] = {
'name': st[1],
'ename': st[2],
}
cursor = db.cursor()
cursor.execute("SELECT date, period, num, note FROM ds WHERE grade=%s AND class_=%s ORDER BY date DESC, period DESC, num ASC", (currRoom[0], currRoom[1]))
dsDataSQL = cursor.fetchall()
return render_template("list.html", title="DS List | 定心紀錄", mode='DS', students=students, data=dsDataSQL, currRoom=currRoom)
@manage.route('/manage/group_teach_publish', methods=['POST'])
def group_teach_publish():
if (check_login_status()):
return redirect('/logout')
refresh_token()
data = request.form.to_dict()
cclass = {
"name": db.child("Classes").child("GP_Class").child(session['category']).child(
"Class").child(session['class']).child("name").get(session['token']).val(),
"category": session['category'],
"class_id": session['class'],
"homerooms": db.child("Classes").child(
"GP_Class").child(session['category']).child("Homerooms").get(session['token']).val()
"category": data.pop('category'),
"class_id": data.pop('class_id')
}
date = request.form['date']
period = request.form['period']
signature = request.form['signatureData']
formData = request.form.to_dict()
notes = ""
if 'notes' in request.form:
notes = request.form['notes']
formData.pop('notes')
signature = removeprefix(signature, 'data:image/png;base64,')
signature = bytes(signature, 'utf-8')
rand = str(date + '^' + cclass['category'] +
'^' + cclass['class_id'] + '^' + period)
rand += ".png"
with open(os.path.join('temp', rand), "wb") as fh:
fh.write(base64.decodebytes(signature))
storage.child(os.path.join('signatures', rand)
).put(os.path.join('temp', rand), session['token'])
formData.pop('signatureData')
formData.pop('date')
formData.pop('period')
for i in formData:
i = i.split('^')
db.child("Homerooms").child(i[1]).child(i[2]).child(
"Absent").child(date).child(period).update({i[3]: int(i[0])}, session['token'])
for h in cclass['homerooms']:
h = h.split('^')
if "confirm" in db.child("Homerooms").child(h[0]).child(h[1]).child("Absent").child(date).get(session['token']).val():
db = refresh_db()
cursor = db.cursor()
cursor.execute("SELECT about FROM gpclasses WHERE category=%s AND subclass=%s",
(cclass['category'], cclass['class_id']))
cclass["name"] = cursor.fetchone()[0]
cursor.execute("SELECT grade,class_,num,name,ename FROM students WHERE classes LIKE " + '\'%\"'+ cclass['category'] + '\": \"' + cclass['class_id'] +'\"%\'' + " ORDER BY grade ASC,class_ ASC,num ASC")
students = cursor.fetchall()
homerooms = []
for x in students:
if (str(x[0]) + '^' + str(x[1])) not in homerooms:
homerooms.append(str(x[0]) + '^' + str(x[1]))
data.pop('dsnumbers')
data.pop('dsoffense')
data.pop('dsoffenseother')
date = data.pop('date')
period = data.pop('period')
signature = data.pop('signatureData')
notes = data.pop('notes')
absentData = []
dsData = []
for x in data:
xs = x.split('^')
if xs[0] == 'note':
continue
db.child("Homerooms").child(h[0]).child(h[1]).child(
"Absent").child(date).child(period).child("signature").update({cclass['class_id']: str(storage.child(os.path.join('signatures', rand)).get_url(None))}, session['token'])
db.child("Homerooms").child(h[0]).child(h[1]).child(
"Absent").child(date).child(period).child("names").child(cclass['class_id']).set(cclass['name'], session['token'])
currPeriodData = db.child("Homerooms").child(h[0]).child(h[1]).child(
"Absent").child(date).child(period).get(session['token']).val()
if 'notes' in currPeriodData:
db.child("Homerooms").child(h[0]).child(h[1]).child(
"Absent").child(date).child(period).update({'notes': currPeriodData['notes']+'; '+notes}, session['token'])
elif xs[0] == 'ds':
dsData.append([xs[1], xs[2].split('-')[0], xs[2].split('-')[1], data[x]])
else:
db.child("Homerooms").child(h[0]).child(h[1]).child(
"Absent").child(date).child(period).update({'notes': notes}, session['token'])
os.remove(os.path.join('temp', rand))
absentData.append([xs[1], xs[2], xs[3], 'K' if xs[0] == '1' else 'L', data['note^'+xs[1]+'^'+xs[2]+'^'+xs[3]]])
for h in homerooms:
h = h.split('^')
cursor = db.cursor()
cursor.execute("""
SELECT signature, notes FROM submission WHERE grade=%s AND class_=%s AND date=%s AND period=%s
""", (h[0], h[1], date, period))
one = cursor.fetchone()
if one is None:
jSignature = json.dumps({cclass['class_id']: signature})
cursor.execute("""
INSERT INTO submission (grade, class_, date, period, signature, notes)
VALUES (%s, %s, %s, %s, %s, %s)
""", (h[0], h[1], date, period, jSignature, notes))
db.commit()
else:
jSignature = json.loads(one[0])
if cclass['class_id'] in jSignature:
continue
jSignature[cclass['class_id']] = signature
note = one[1] + '; ' + notes
cursor.execute("""
UPDATE submission SET signature=%s, notes=%s WHERE grade=%s AND class_=%s AND date=%s AND period=%s
""", (json.dumps(jSignature), note, h[0], h[1], date, period))
db.commit()
for d in dsData:
cursor = db.cursor()
cursor.execute("""
INSERT INTO ds (grade, class_, num, date, period, note)
VALUES (%s, %s, %s, %s, %s, %s)
""", (d[0], d[1], d[2], date, period, d[3]))
db.commit()
for a in absentData:
cursor = db.cursor()
cursor.execute("""
INSERT INTO absent (grade, class_, num, date, period, status, note)
VALUES (%s, %s, %s, %s, %s, %s, %s)
""", (a[0], a[1], a[2], date, period, a[3], a[4]))
db.commit()
return redirect('/manage')
@manage.route('/manage/homeroom_abs', methods=['POST'])
def homeroom_abs_publish():
if (check_login_status()):
return redirect('/logout')
refresh_token()
date = request.form['date']
homeroom = request.form['homeroom'].split('^')
period = request.form['period']
signature = request.form['signatureData']
formData = request.form.to_dict()
notes = ""
if "confirm" in db.child("Homerooms").child(homeroom[0]).child(homeroom[1]).child("Absent").child(date).get(session['token']).val():
return redirect('/manage')
if 'notes' in request.form:
notes = request.form['notes']
formData.pop('notes')
signature = removeprefix(signature, 'data:image/png;base64,')
signature = bytes(signature, 'utf-8')
rand = str(date + '^' + homeroom[0] + '^' + homeroom[1] + '^' + period)
rand += ".png"
with open(os.path.join('temp', rand), "wb") as fh:
fh.write(base64.decodebytes(signature))
storage.child(os.path.join('signatures', rand)
).put(os.path.join('temp', rand), session['token'])
formData.pop('signatureData')
formData.pop('date')
formData.pop('homeroom')
formData.pop('period')
formData.pop('stype')
for i in formData:
i = i.split('^')
db.child("Homerooms").child(homeroom[0]).child(
homeroom[1]).child("Absent").child(date).child(period).update({i[1]: int(i[0])}, session['token'])
db.child("Homerooms").child(homeroom[0]).child(homeroom[1]).child(
"Absent").child(date).child(period).update({'signature': str(storage.child(os.path.join('signatures', rand)).get_url(None))}, session['token'])
db.child("Homerooms").child(homeroom[0]).child(homeroom[1]).child(
"Absent").child(date).child(period).update({'notes': notes}, session['token'])
os.remove(os.path.join('temp', rand))
db = refresh_db()
data = request.form.to_dict()
date = data.pop('date')
period = data.pop('period')
signature = data.pop('signatureData')
notes = data.pop('notes')
homeroom = data.pop('homeroom').split('^')
ds1 = data.pop('ds^1')
ds2 = data.pop('ds^2')
ds3 = data.pop('ds^3')
ds4 = data.pop('ds^4')
ds5 = data.pop('ds^5')
ds6 = data.pop('ds^6')
ds7 = data.pop('ds^7')
# 2: L / 1: K
absentData = {}
dsidv = {}
for x in data:
xt = x.split('^')
if (xt[0] == 'note'):
if xt[2] not in absentData:
absentData[xt[2]] = {}
absentData[xt[2]]['note'] = data[x]
elif (xt[0] == 'dsidv'):
dsidv[xt[1]] = data[x]
else:
if xt[1] not in absentData:
absentData[xt[1]] = {}
absentData[xt[1]]['status'] = 'L' if x[0] == '2' else 'K'
cursor = db.cursor()
cursor.execute("""
INSERT INTO submission
(grade, class_, date, period, signature, ds1, ds2, ds3, ds4, ds5, ds6, ds7, notes)
VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)
""", (homeroom[0], homeroom[1], date, period, signature, ds1, ds2, ds3, ds4, ds5, ds6, ds7, notes))
for x in absentData:
cursor.execute("""
INSERT INTO absent
(grade, class_, date, period, num, status, note)
VALUES (%s, %s, %s, %s, %s, %s, %s)
""", (homeroom[0], homeroom[1], date, period, x, absentData[x]['status'], absentData[x]['note']))
for x in dsidv:
cursor.execute("""
INSERT INTO ds
(grade, class_, date, period, num, note)
VALUES (%s, %s, %s, %s, %s, %s)
""", (homeroom[0], homeroom[1], date, period, x, dsidv[x]))
db.commit()
return redirect('/manage')
@ -298,69 +571,26 @@ def edit_abs():
if (check_login_status() or not check_permission()):
return redirect('/logout')
refresh_token()
date = request.form['date']
homeroom = request.form['homeroom'].split('^')
period = request.form['period']
signature = "https://firebasestorage.googleapis.com/v0/b/attendance-be176.appspot.com/o/stud_affairs.png?alt=media"
formData = request.form.to_dict()
notes = ""
oldData = list(db.child("Homerooms").child(homeroom[0]).child(homeroom[1]).child(
"Absent").child(date).child(period).shallow().get(session['token']).val())
for k in oldData:
if k == 'name' or k == 'teacher':
continue
db.child("Homerooms").child(homeroom[0]).child(homeroom[1]).child(
"Absent").child(date).child(period).child(k).remove(session['token'])
cfrmstatus = db.child("Homerooms").child(homeroom[0]).child(
homeroom[1]).child("Absent").child(date).get(session['token']).val()
if "confirm" in cfrmstatus:
db.child("Homerooms").child(homeroom[0]).child(homeroom[1]).child(
"Absent").child(date).update({'notes': cfrmstatus['notes'] + '; (確認後學務處有更改)'}, session['token'])
if 'notes' in request.form:
notes = request.form['notes']
formData.pop('notes')
formData.pop('date')
formData.pop('homeroom')
formData.pop('period')
for i in formData:
i = i.split('^')
db.child("Homerooms").child(homeroom[0]).child(
homeroom[1]).child("Absent").child(date).child(period).update({i[1]: int(i[0])}, session['token'])
db.child("Homerooms").child(homeroom[0]).child(homeroom[1]).child(
"Absent").child(date).child(period).update({'notes': notes}, session['token'])
if cfrmstatus[period]['name'] == 'GP':
db.child("Homerooms").child(homeroom[0]).child(
homeroom[1]).child("Absent").child(date).child(period).child("signature").set({'STUD_AFFAIR_OFFICE': signature}, session['token'])
db.child("Homerooms").child(homeroom[0]).child(
homeroom[1]).child("Absent").child(date).child(period).child("names").set({'STUD_AFFAIR_OFFICE': "學務處已編輯"}, session['token'])
else:
db.child("Homerooms").child(homeroom[0]).child(
homeroom[1]).child("Absent").child(date).child(period).child("signature").set(signature, session['token'])
return redirect('/manage/admin/'+homeroom[0]+'/'+homeroom[1]+'/'+date)
data = request.form.to_dict()
print(data)
return ""
@manage.route('/manage/homeroom_confirm', methods=['POST'])
def homeroom_confirm():
if (check_login_status()):
return redirect('/logout')
refresh_token()
homeroom = request.form['homeroom'].split('^')
date = request.form['date']
if 'notes' in request.form:
notes = request.form['notes']
db.child("Homerooms").child(homeroom[0]).child(homeroom[1]).child(
"Absent").child(date).update({"notes": notes}, session['token'])
signature = request.form['signatureData']
signature = removeprefix(signature, 'data:image/png;base64,')
signature = bytes(signature, 'utf-8')
rand = str(date + '^' + homeroom[0] + '^' + homeroom[1] + '^' + 'hrCfrm')
rand += ".png"
with open(os.path.join('temp', rand), "wb") as fh:
fh.write(base64.decodebytes(signature))
storage.child(os.path.join('signatures', rand)
).put(os.path.join('temp', rand), session['token'])
db.child("Homerooms").child(homeroom[0]).child(homeroom[1]).child("Absent").child(date).update(
{"confirm": str(storage.child(os.path.join('signatures', rand)).get_url(None))}, session['token'])
os.remove(os.path.join('temp', rand))
data = request.form.to_dict()
homeroom = data.pop('homeroom').split('^')
date = data.pop('date')
signature = data.pop('signatureData')
notes = data.pop('notes')
db = refresh_db()
cursor = db.cursor()
cursor.execute("""
INSERT INTO submission
(grade, class_, date, period, signature, notes)
VALUES (%s, %s, %s, 'c', %s, %s)
""", (homeroom[0], homeroom[1], date, signature, notes))
db.commit()
return redirect('/manage')

View file

@ -1,19 +1,33 @@
# Automatically generated by https://github.com/damnever/pigar.
# Attendance/app.py: 2
# Attendance/functions.py: 1,3
Flask == 2.0.1
# Attendance/app.py: 3
# Attendance/test.py: 1
Pyrebase4 == 4.5.0
# Attendance/functions.py: 19,20,21
Flask_Admin == 1.5.8
# Attendance/app.py: 8
# Attendance/functions.py: 22
Flask_BabelEx == 0.9.4
# Attendance/functions.py: 18
Flask_SQLAlchemy == 2.5.1
# Attendance/functions.py: 15
mysql_connector_python == 8.0.27
# Attendance/functions.py: 10
pandas == 1.1.3
# Attendance/app.py: 11
# Attendance/test.py: 3
# Attendance/functions.py: 14
passlib == 1.7.4
# Attendance/functions.py: 13
python_dotenv == 0.19.0
# Attendance/app.py: 5
# Attendance/functions.py: 5
pytz == 2020.1
# Attendance/functions.py: 16
requests == 2.26.0
gunicorn == 20.1.0

View file

@ -3,6 +3,9 @@
body {
font-family: "Lato", "Noto Sans TC", "Microsoft JhengHei", "sans-serif";
-moz-osx-font-smoothing: grayscale;
-webkit-font-smoothing: antialiased;
font-smooth: always;
}
div.col .row .col {
@ -76,10 +79,30 @@ p.highlightAbs.n-2 {
p.highlightAbs.n-3 {
color: rgb(15, 184, 0);
}
.afterSelButton {
margin-top: 0;
}
.modal-footer>.row {
width: 70%;
}
.modal-footer>.row .col {
border: 0 !important;
}
.modal-footer>.row button {
margin-right: 10px;
margin-top: 0;
}
.margin-top {
margin-top: 20px;
}
.nomargin-top {
margin-top: 0;
}
.nmb {
margin-bottom: 0;
}
input[type="checkbox"].absent, input[type="checkbox"].late {
-webkit-appearance: initial;
@ -180,3 +203,22 @@ label {
width: 20vw;
min-width: auto;
}
div.ds {
margin-top: 20px;
}
div.dsboard .col, .noborder {
border: 0 !important;
}
div.container {
text-align: center;
}
button {
width: 100%;
margin-top: 10px;
}
.text-red {
color: red;
}

View file

@ -1,8 +1,39 @@
div.container {
text-align: center;
html {
height: 100%;
}
body {
height: 100%;
margin: 0;
background-repeat: no-repeat;
background-attachment: fixed;
background: linear-gradient(to bottom right, #e4e4e4 0%, #afafaf 100%);
}
button {
width: 100%;
margin-top: 10px;
div.loginPanel {
background: #effffc;
margin-top: 100px;
padding: 30px;
box-shadow: 0 0 10px black;
z-index: -1;
}
div.container {
max-width: 730px;
}
h1 .img {
height: calc(1.375rem + 1.5vw);
}
@media (min-width: 1200px) {
h1 .img {
height: 2.5rem;
}
}
h1 .img img {
vertical-align: sub;
}
#subuser_sel {
width: 80%;
margin-left: auto;
margin-right: auto;
}

66
static/nav.css Normal file
View file

@ -0,0 +1,66 @@
#sidebar {
min-width: 270px;
max-width: 270px;
background: #5b5e69;
color: #fff;
-webkit-transition: all 0.3s;
-o-transition: all 0.3s;
transition: all 0.3s;
position: fixed;
top: 0;
height: 100%; }
#sidebar.active {
min-width: 80px;
max-width: 80px;
text-align: center; }
#sidebar.active ul.components li {
font-size: 14px; }
#sidebar.active ul.components li a {
padding: 10px 0; }
#sidebar.active ul.components li a span {
margin-right: 0;
display: block;
font-size: 24px; }
#sidebar.active .logo {
padding: 10px 0; }
#sidebar.active .footer {
display: none; }
#sidebar .logo {
display: block;
color: #fff;
font-weight: 900;
padding: 10px 30px;
}
#sidebar ul.components {
padding: 0; }
#sidebar ul li {
font-size: 16px; }
#sidebar ul li > ul {
margin-left: 10px; }
#sidebar ul li > ul li {
font-size: 14px; }
#sidebar ul li a {
padding: 10px 30px;
display: block;
color: white;
border-bottom: 1px solid rgba(255, 255, 255, 0.1); }
#sidebar ul li a span {
margin-right: 15px; }
@media (max-width: 991.98px) {
#sidebar ul li a span {
display: block; } }
#sidebar ul li a:hover {
color: #fff; }
#sidebar ul li.active > a {
background: transparent;
color: #fff; }
@media (max-width: 991.98px) {
#sidebar {
min-width: 80px;
max-width: 80px;
text-align: center;
margin-left: -80px !important; }
#sidebar.active {
margin-left: 0 !important; } }
#sidebar a {
text-decoration: none; }

10
static/nav.js Normal file
View file

@ -0,0 +1,10 @@
$(window).resize(function() {
var sidebar = document.getElementById('sidebar');
var container = document.getElementsByClassName('container')[0];
container.style.paddingLeft = 'calc(.75rem + ' + sidebar.offsetWidth + 'px)';
});
document.addEventListener("DOMContentLoaded", function(event){
var sidebar = document.getElementById('sidebar');
var container = document.getElementsByClassName('container')[0];
container.style.paddingLeft = 'calc(.75rem + ' + sidebar.offsetWidth + 'px)';
})

View file

@ -1,6 +1,14 @@
var signaturePad, selPeriod, canvas, width = $(window).width(), modal;
var indDS = {};
function submitForm() {
if (!signaturePad.isEmpty()) {
for (var i in indDS) {
var tmp = document.createElement('input');
tmp.type = 'hidden';
tmp.name = 'ds^' + i;
tmp.value = indDS[i];
document.getElementById('attendanceData^' + selPeriod).appendChild(tmp);
}
$('#' + modal).modal('hide');
loadingAnimation();
signaturePad.off();
@ -50,12 +58,36 @@ function viewSignature(period) {
});
resizeCanvas();
}
function unCheckAbs(string) {
document.getElementById('absent^' + string).checked = false;
}
function unCheckLate(string) {
document.getElementById('late^' + string).checked = false;
strForNote = string.substring(string.indexOf("^") + 1);
if (document.getElementById("absent^" + string).checked == true && !!!document.getElementById("note^"+strForNote)) {
var tmp = document.createElement('input')
tmp.type = 'text'
tmp.id = 'note^'+strForNote
tmp.name = 'note^'+strForNote
tmp.className = 'form-control'
document.getElementById('input^' + string).appendChild(tmp)
} else if (document.getElementById("absent^" + string).checked == false) {
document.getElementById('note^' + strForNote).remove();
}
}
function unCheckAbs(string) {
document.getElementById('absent^' + string).checked = false;
strForNote = string.substring(string.indexOf("^") + 1);
if (document.getElementById("late^" + string).checked == true && !!!document.getElementById("note^"+strForNote)) {
var tmp = document.createElement('input')
tmp.type = 'text'
tmp.id = 'note^'+strForNote
tmp.name = 'note^'+strForNote
tmp.className = 'form-control'
document.getElementById('input^' + string).appendChild(tmp)
} else if (document.getElementById("late^" + string).checked == false) {
document.getElementById('note^' + strForNote).remove();
}
}
function loadingAnimation() {
$("div.container").hide();
@ -70,3 +102,18 @@ function chgDate(sel) {
document.body.appendChild(new_form);
new_form.submit();
}
function addDS() {
if ($('#dsnumbersel').val() == "" || $('#dsoffensesel').val() == "") {
return;
}
var text = $('#dsoffensesel').val()
if ($('#dsoffenseother').val() != "") {
text = text.concat(": ", $('#dsoffenseother').val());
}
indDS[$("#dsnumbersel").val()] = text;
$('#inddsview>.col').append('<div class="row"><div class="col">'+$("#dsnumbersel").val()+'</div><div class="col">'+text+'</div></div>')
$('#dsnumbersel option[value="'+$("#dsnumbersel").val()+'"]').remove();
$('#dsoffenseother').val("");
$('#dsoffensesel').val("");
$('#dsnumbersel').val("");
}

View file

@ -1,5 +1,6 @@
var signaturePad, hrCfrm = false, canvas = document.getElementById("signature_pad");
var width = $(window).width();
var indDS = {};
function loadingAnimation() {
$('.container').hide();
$('#loading').show();
@ -25,6 +26,11 @@ function submitForm() {
document.getElementById('homeroom_confirm').submit()
} else {
var notes = $('#subjectNotes').val();
for (var i = 0; i < 7; i++)
document.getElementById('HR-ds'+(i+1)).value = $('.dsboard input[name="ds'+(i+1)+'"]:checked').val();
for (var i in indDS) {
$('#postHomeroomAbs').append('<input type="text" name="dsidv^' + i.split('-')[0] + '" value="'+ indDS[i] +'">')
}
document.getElementById('HR-signatureData').value = data;
document.getElementById('HR-notes').value = notes;
document.getElementById('postHomeroomAbs').submit();
@ -70,6 +76,8 @@ function afterSelAbs(period) {
$('#postHomeroomAbs').append('<input type="checkbox" name="' + $(this).attr('class').split(' ')[1].split('^')[0] + '^'
+ $(this).attr('class').split(' ')[1].split('^')[2]
+ '" checked="checked">');
$('#postHomeroomAbs').append('<input type="text" name="note^' + $(this).attr('class').split(' ')[1].split('^')[0] + '^'
+ $(this).attr('class').split(' ')[1].split('^')[2] + '" value="'+ $('#note-' + $(this).attr('class').split(' ')[1].split('^')[1] + '-' + $(this).attr('class').split(' ')[1].split('^')[2]).val() +'">')
}
});
if (cnt == 0) {
@ -80,6 +88,7 @@ function afterSelAbs(period) {
}
function homeroomCfrm() {
hrCfrm = true;
$('.ds').attr('hidden', 'hidden');
$('#showSignPeriod').text("HOMEROOM CONFIRM");
$('#showSignSubjectName').text("班導確認");
$('.tobeform').attr('disabled', 'disabled');
@ -88,7 +97,32 @@ function homeroomCfrm() {
}
function unCheckLate(string) {
document.getElementById('late^' + string).checked = false;
if (document.getElementById("absent^" + string).checked == true && $('#note-'+string.split('^')[0]+'-'+string.split('^')[1]).length == 0) {
$('.input-'+string.split('^')[0]+'-'+string.split('^')[1]).append('<input type="text" class="form-control" id="note-'+string.split('^')[0]+'-'+string.split('^')[1]+'" name="'+ string +'">')
} else if (document.getElementById("absent^" + string).checked == false) {
$('#note-'+string.split('^')[0]+'-'+string.split('^')[1]).remove();
}
}
function unCheckAbs(string) {
document.getElementById('absent^' + string).checked = false;
if (document.getElementById("late^" + string).checked == true && $('#note-'+string.split('^')[0]+'-'+string.split('^')[1]).length == 0) {
$('.input-'+string.split('^')[0]+'-'+string.split('^')[1]).append('<input type="text" class="form-control" id="note-'+string.split('^')[0]+'-'+string.split('^')[1]+'" name="'+ string +'">')
} else if (document.getElementById("late^" + string).checked == false) {
$('#note-'+string.split('^')[0]+'-'+string.split('^')[1]).remove();
}
}
function addDS() {
if ($('#dsnumbersel').val() == "" || $('#dsoffensesel').val() == "") {
return;
}
var text = $('#dsoffensesel').val()
if ($('#dsoffenseother').val() != "") {
text = text.concat(": ", $('#dsoffenseother').val());
}
indDS[$("#dsnumbersel").val()] = text;
$('#inddsview>.col').append('<div class="row"><div class="col">'+$("#dsnumbersel").val()+'</div><div class="col">'+text+'</div></div>')
$('#dsnumbersel option[value="'+$("#dsnumbersel").val()+'"]').remove();
$('#dsoffenseother').val("");
$('#dsoffensesel').val("");
$('#dsnumbersel').val("");
}

View file

@ -18,3 +18,5 @@
}
startTime();
})();
$(window).resize();
$(window).scroll();

View file

@ -8,6 +8,7 @@
<title>Admin 管理員 - 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="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/css/all.min.css" integrity="sha512-1ycn6IcaQQ40/MKBW2W4Rhis/DbILU74C1vSrLJxCq57o941Ym01SwNsOMqvEBFlcgUa6xLiPY/NS5R+E6ztJQ==" crossorigin="anonymous" referrerpolicy="no-referrer" />
<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" />
@ -18,48 +19,17 @@
<body>
<div class="showTime"><span id="showTime"></span></div>
{% include 'sidebar.html' %}
<div class="container">
<h1 class="margin-top">Admin View | 管理頁面</h1>
<h2 class="margin-top">{{homeroomCode[0]}} {{homeroomCode[1]}}</h2>
<h2 class="margin-top">{{currRoom[0]}} {{currRoom[1]}}</h2>
<h2>[{{currDate}}]</h2>
{% if 'confirm' in absData[currDate] %}
{% if 'c' in submission %}
<h2 style="color: rgb(61, 194, 0); text-align: center;">Homeroom Confirmed 班導已確認</h2>
{% else %}
<h2 style="color: red; text-align: center;">Homeroom NOT Confirmed 班導尚未確認</h2>
{% endif %}
<a href="/logout"><button class="btn btn-primary logout margin-top">Logout 登出</button></a>
<div class="container margin-bottom">
<div class="row">
<div class="col">
<select name="grade" id="sel-grade" class="form-select" onchange="getHR()" required>
<option value="">選擇年級</option>
{% for grade in homerooms %}
<option value="{{grade}}">{{grade}}</option>
{% endfor %}
</select>
</div>
<div class="col">
<select name="room" id="sel-room" class="form-select" disabled required>
<option value="">請先選擇年級</option>
</select>
</div>
<div class="col">
<select name="date" id="date" class="form-select">
{% for date in absData %}
{% if date == currDate %}
<option value="{{date}}" selected="selected">{{date}} ★</option>
{% else %}
<option value="{{date}}">{{date}}</option>
{% endif %}
{% endfor %}
</select>
</div>
<div class="col-1">
<button type="button" class="btn btn-primary" onclick="redirAdmin()">查詢</button>
</div>
</div>
</div>
<div class="col">
<div class="col margin-top">
<div class="sticky-top" style="background-color:white;">
<div class="row title">
<div class="col">班級</div>
@ -77,7 +47,7 @@
<div class="col"></div>
{% for i in periods %}
<div class="col">
{{absData[currDate][i]['name']}}</div>
{{schedule[i]['subject']}} {% if schedule[i]['special'] == True %} <span class="text-red">(換)</span> {% endif %}</div>
{% endfor %}
</div>
<div class="row title">
@ -86,73 +56,101 @@
<div class="col"></div>
<div class="col"></div>
{% for i in periods %}
<div class="col">{{absData[currDate][i]['teacher']}}</div>
<div class="col">{{schedule[i]['teacher']}}</div>
{% endfor %}
</div>
</div>
{% for i in homeroomData %}
{% for i in students %}
<div class="row">
<div class="col">{{homeroomCode[0]}}{{homeroomCode[1]}}</div>
<div class="col">{{i}}</div>
<div class="col">{{ homeroomData[i]['name'] }}</div>
<div class="col">{{ homeroomData[i]['eng_name'] }}</div>
<div class="col">{{currRoom[0]}}{{currRoom[1]}}</div>
<div class="col">{{ i[0] }}</div>
<div class="col">{{ i[1] }}</div>
<div class="col">{{ i[2] }}</div>
{% for j in periods %}
<div class="col view-{{j}}">
{% if 'signature' in absData[currDate][j] %}
{% if i in absData[currDate][j] %}
{% if absData[currDate][j][i] == 1 %}
<p class="highlightAbs n-2 view-n-{{i}}">X</p>
{% if schedule[j]['subject'] == 'GP' %}
{% for k in submission[j] %}
{% if studGP[i[0]][schedule[j]['teacher']] == k %}
{% if i[0] in absentData[j] %}
{% if absentData[j][i[0]]['status'] == 'L' %}
<p class="highlightAbs n-3 view-n-{{i[0]}}">𝜑</p>
{% elif absentData[j][i[0]]['status'] == 'K' %}
<p class="highlightAbs n-2 view-n-{{i[0]}}"></p>
{% elif absentData[j][i[0]]['status'] == 'G' %}
<p class="highlightAbs n-2 view-n-{{i[0]}}"></p>
{% elif absentData[j][i[0]]['status'] == 'S' %}
<p class="highlightAbs n-2 view-n-{{i[0]}}"></p>
{% elif absentData[j][i[0]]['status'] == 'F' %}
<p class="highlightAbs n-2 view-n-{{i[0]}}"></p>
{% elif absentData[j][i[0]]['status'] == 'P' %}
<p class="highlightAbs n-2 view-n-{{i[0]}}"></p>
{% elif absentData[j][i[0]]['status'] == 'O' %}
<p class="highlightAbs n-2 view-n-{{i[0]}}"></p>
{% else %}
<p class="highlightAbs n-3 view-n-{{i}}">𝜑</p>
<p class="highlightAbs n-2 view-n-{{i[0]}}">{{absentData[j][i[0]]['status']}}</p>
{% endif %}
<p class="highlightAbs">{{absentData[j][i[0]]['note']}}</p>
{% else %}
{% if absData[currDate][j]['name'] != 'GP' %}
<p class="highlightAbs n-1 view-n-{{i}}">V</p>
{% else %}
{% if (homeroomData[i]['GP_Class'][absData[currDate][j]['teacher']] in
absData[currDate][j]['signature'] or 'STUD_AFFAIR_OFFICE' in absData[currDate][j]['signature'])%}
<p class="highlightAbs n-1 view-n-{{i}}">V</p>
{% else %}
<p class="highlightAbs n-2 view-n-{{i}}"></p>
<p class="highlightAbs n-1 view-n-{{i[0]}}">V</p>
{% if j in idvDS and i[0] in idvDS[j] %}
<p class="highlightAbs n-2">{{idvDS[j][i[0]]}}</p>
{% endif %}
{% endif %}
{% endif %}
{% elif absData[currDate][j]['name'] == 'GP' %}
<p class="highlightAbs"></p>
{% else %}
{% if 'confirm' in absData[currDate] %}
<p class="highlightAbs"></p>
{% else %}
<p class="highlightAbs"></p>
<p class="highlightAbs view-n-{{i[0]}}"></p>
{% endif %}
{% endfor %}
{% else %}
{% if j in submission %}
{% if i[0] in absentData[j] %}
{% if absentData[j][i[0]]['status'] == 'L' %}
<p class="highlightAbs n-3 view-n-{{i[0]}}">𝜑</p>
{% elif absentData[j][i[0]]['status'] == 'K' %}
<p class="highlightAbs n-2 view-n-{{i[0]}}"></p>
{% elif absentData[j][i[0]]['status'] == 'G' %}
<p class="highlightAbs n-2 view-n-{{i[0]}}"></p>
{% elif absentData[j][i[0]]['status'] == 'S' %}
<p class="highlightAbs n-2 view-n-{{i[0]}}"></p>
{% elif absentData[j][i[0]]['status'] == 'F' %}
<p class="highlightAbs n-2 view-n-{{i[0]}}"></p>
{% elif absentData[j][i[0]]['status'] == 'P' %}
<p class="highlightAbs n-2 view-n-{{i[0]}}"></p>
{% elif absentData[j][i[0]]['status'] == 'O' %}
<p class="highlightAbs n-2 view-n-{{i[0]}}"></p>
{% else %}
<p class="highlightAbs n-2 view-n-{{i[0]}}">{{absentData[j][i[0]]['status']}}</p>
{% endif %}
<p class="highlightAbs">{{absentData[j][i[0]]['note']}}</p>
{% else %}
<p class="highlightAbs n-1 view-n-{{i[0]}}">V</p>
{% if j in idvDS and i[0] in idvDS[j] %}
<p class="highlightAbs n-2">{{idvDS[j][i[0]]}}</p>
{% endif %}
{% endif %}
{% else %}
<p class="highlightAbs view-n-{{i[0]}}"></p>
{% endif %}
<!-- <input type="checkbox" class="tobeform {{j}}^{{i}}"> -->
{% endif %}
</div>
{% endfor %}
</div>
{% endfor %}
{% if showUpload == '1' %}
<div class="row">
{% for i in range(7) %}
<div class="row" {%if i==0%}style="border-top: 2px solid black;"{%endif%} {% if i == 6 %} style="border-bottom: 2px solid black;" {% endif %}>
<div class="col"></div>
<div class="col"></div>
<div class="col" style="font-weight: bold;">{{dstext[i]}}</div>
<div class="col">{{dsboard[i]}}</div>
{% for j in periods %}
{% if j in submission and schedule[j] != 'GP' %}
<div class="col">
{{submission[j]['ds' + (i+1)|string]}}</div>
{% else %}
<div class="col"></div>
<div class="col"></div>
{% for i in periods %}
<div id="btns-{{i}}" class="col">
{% if ('signature' in absData[currDate][i] or 'confirm' in absData[currDate][i]) %}
<button class="btn btn-danger afterSelButton" onclick="edit('{{i|string}}')">編輯
<br>{{absData[currDate][i]['name']}}</button>
{% endif %}
</div>
{% endif %}
{% endfor %}
</div>
{% endif %}
<form action="/manage/edit_abs" id="postHomeroomAbs" hidden="hidden" method="post">
<input type="text" id="HR-date" name="date" value="{{currDate}}">
<input type="text" id="HR-period" name="period" value="">
<input type="text" id="HR-notes" name="notes" value="">
<input type="text" id="HR-homeroom" name="homeroom" value="{{homeroomCode[0]}}^{{homeroomCode[1]}}">
</form>
{% endfor %}
{% for c in range(periods|length + 1) %}
{% if c % 4 == 0 %}
<div class="row signatures">
@ -160,38 +158,41 @@
<div class="col half">
{% if c == 0 %}
<div class="row needborder">Homeroom Teacher 導師</div>
{% if 'confirm' in absData[currDate] %}
<div class="row"><img src="{{absData[currDate]['confirm']}}" alt=""></div>
<div class="row">備註: {{absData[currDate]['notes']}}</div>
{% if 'c' in submission %}
<div class="row"><img src="{{submission['c']['signature']}}" alt=""></div>
<div class="row">備註: {{submission['c']['notes']}}</div>
{% else %}
<div class="row"><span style="color:red;">No Signature 導師尚未簽名</span></div>
{% endif %}
{% else %}
{% if absData[currDate][periods[c-1]]['name'] == 'GP' %}
{% if 'signature' in absData[currDate][periods[c-1]] %}
{% for i in absData[currDate][periods[c-1]]['signature'] %}
{% if schedule[periods[c-1]]['subject'] == 'GP' %}
{% if periods[c-1] in submission %}
{% for i in submission[periods[c-1]] %}
{% if i != 'notes' %}
<div class="row needborder">{{periods[c-1]}}:
{{absData[currDate][periods[c-1]]['teacher']}}: {{i}}:
{{absData[currDate][periods[c-1]]['names'][i]}}</div>
<div class="row"><img src="{{absData[currDate][periods[c-1]]['signature'][i]}}" alt="">
{% if loop.index == loop.length %}
<br>備註: {{absData[currDate][periods[c-1]]['notes']}}
{{schedule[periods[c-1]]['teacher']}}: {{i}}</div>
<div class="row"><img src="{{submission[periods[c-1]][i]['signature']}}" alt="">
{% if loop.index == loop.length-1 %}
<br>備註: {{submission[periods[c-1]]['notes']}}
{% endif %}
</div>
{% endif %}
{% endfor %}
{% else %}
<div class="row needborder">{{periods[c-1]}}:
{{absData[currDate][periods[c-1]]['teacher']}}: No
{{schedule[periods[c-1]]['subject']}}: {{i}}: No
Signature
</div>
<div class="row"></div>
{% endif %}
{% else %}
<div class="row needborder">{{periods[c-1]}}: {{absData[currDate][periods[c-1]]['name']}}:
{{absData[currDate][periods[c-1]]['teacher']}}
<div class="row needborder">{{periods[c-1]}}: {{schedule[periods[c-1]]['subject']}}:
{{schedule[periods[c-1]]['teacher']}}
</div>
<div class="row"><img src="{{absData[currDate][periods[c-1]]['signature']}}" alt=""><br>備註:
{{absData[currDate][periods[c-1]]['notes']}}</div>
{% if periods[c-1] in submission %}
<div class="row"><img src="{{submission[periods[c-1]]['signature']}}" alt=""><br>備註:
{{submission[periods[c-1]]['notes']}}</div>
{% endif %}
{% endif %}
{% endif %}
</div>
@ -200,29 +201,6 @@
{% endif %}
{% endfor %}
</div>
<div id="finalCheck" class="modal fade" id="staticBackdrop" data-bs-backdrop="static" data-bs-keyboard="false"
tabindex="-1" aria-labelledby="staticBackdropLabel" aria-hidden="true">
<div class="modal-dialog modal-xl">
<form onsubmit="return false;">
<div class="modal-content">
<div class="modal-body">
<div class="alert alert-danger margin-top" id="allPresentWarning" role="alert">
<h4 class="alert-heading">請確認更改Please confirm that you are editting records!</h4>
</div>
<h3 class="margin-top">Notes 備註欄</h3>
<input type="textarea" class="form-control" name="notes" id="subjectNotes"
placeholder="Enter Notes 請輸入備註" style="width: 80%; margin-left: 10%;" row="3">
</div>
<div class="modal-footer">
<button type="button" class="btn btn-danger" onclick="location.reload();">Cancel
取消</button>
<button type="submit" class="btn btn-primary submitButton" onclick="submitForm()">Submit
提交</button>
</div>
</div>
</form>
</div>
</div>
{% if showUpload == '1' %}
<div class="row margin-top">
<div class="col">

View file

@ -0,0 +1,82 @@
{% extends 'admin/master.html' %}
{% block body %}
<style>
td {
text-align: center;
border: 1px solid black;
padding: 5px;
}
</style>
<h1><img src="/static/favicon.ico" alt="" height="40px" style="vertical-align: bottom;"> 歡迎 後台管理</h1>
<h2>請利用上方選單選取功能</h2>
<h3>特殊功能</h3>
<ul>
<li>為保障用戶安全Users 的密碼欄為 <code>sha256_crypt.hash</code>,請使用 Python 產生出後輸入,或先改 Email寄忘記密碼信最後再改回來。</li>
<ul>
<li><small><code>password</code> 的 hash 為 <code>$5$rounds=535000$VjoY30Bt535dL/7x$xXmg8BZ3VJe/odbQQ5hd0uPmb1o9EhHkJmwWWTc8K21</code></small></li>
</ul>
<li>為簡化程式Students->Classes, GPClasses->Accs, Homerooms->Accs, 分組課的 Submission->Notes 都使用標準 JSON 格式輸入,請留意</li>
<li>請假代碼<br>
<table>
<tbody>
<tr>
<td>遲到</td>
<td>曠課</td>
<td>事假</td>
<td>病假</td>
<td>喪假</td>
<td>疫情假</td>
<td>公假</td>
</tr>
<tr>
<td>L</td>
<td>K</td>
<td>G</td>
<td>S</td>
<td>F</td>
<td>P</td>
<td>O</td>
</tbody>
</table>
</li>
<li>使用者代碼<br>
<table>
<tbody>
<tr>
<td>超級管理員</td>
<td>管理員</td>
<td>一般使用者</td>
</tr>
<tr>
<td>S</td>
<td>A</td>
<td>R</td>
</tr>
</tbody>
</table>
</li>
</ul>
{% if session['showUpload'] %}
<div class="row">
<div class="col">
<h3 style="color: red;">大量匯入</h3>
</div>
<div class="col">
<a href="/upload/1"><button class="btn btn-danger">1. Add Homeroom</button></a>
</div>
<div class="col">
<a href="/upload/2"><button class="btn btn-danger">2. Add GP Classes</button></a>
</div>
<div class="col">
<a href="/upload/3"><button class="btn btn-danger">3. Add GP Student List</button></a>
</div>
<div class="col">
<a href="/upload/4"><button class="btn btn-danger">4. Period List</button></a>
</div>
<div class="col">
<a href="/upload/dates"><button class="btn btn-warning">Dates</button></a>
</div>
</div>
{% endif %}
{% endblock %}

View file

@ -4,7 +4,7 @@
<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>
<title>Attendance 點名系統 2.0</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">
@ -24,69 +24,62 @@
<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="chgPasswordForm" method="post">
<div class="form-group row">
<label for="password">Old Password 舊密碼:</label><br>
<div class="input-group mb-3">
<input type="password" class="form-control" id="password" name="password" required>
<div class="input-group-append">
<span class="input-group-text toggle-password" onclick="password_show_hide(1);">
<i class="fas fa-eye" id="old_show_eye"></i>
<i class="fas fa-eye-slash d-none" id="old_hide_eye"></i>
</span>
</div>
<div class="text-center container">
<div class="loginPanel">
<h1 class="margin-top margin-bottom">
<div class="img"><img src="/static/favicon.ico" alt="" style="height: 100%"> Change Password 更改密碼</div> </h1>
<form action="/chgPassword" id="chgPasswordForm" method="post">
<div class="form-group row">
<label for="password">Old Password 舊密碼:</label><br>
<div class="input-group mb-3">
<input type="password" class="form-control" id="password" name="password" required>
<div class="input-group-append">
<span class="input-group-text toggle-password" onclick="password_show_hide(1);">
<i class="fas fa-eye" id="old_show_eye"></i>
<i class="fas fa-eye-slash d-none" id="old_hide_eye"></i>
</span>
</div>
</div>
<div class="form-group row">
<label for="password">New Username 新帳號:</label><br>
<div class="input-group mb-3">
<input type="email" class="form-control" id="new_username" name="new_username" required>
</div>
</div>
<div class="form-group row">
<label for="new_password">New Password 新密碼:</label><br>
<div class="input-group mb-3 hasSmall">
<input type="password" class="form-control" id="new_password" name="new_password"
aria-describedby="passwordHelp" required>
<div class="input-group-append">
<span class="input-group-text toggle-password" onclick="password_show_hide(2);">
<i class="fas fa-eye" id="new_show_eye"></i>
<i class="fas fa-eye-slash d-none" id="new_hide_eye"></i>
</span>
</div>
</div>
<small id="passwordHelp" class="form-text text-muted">6 characters
minimum.</small>
</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 class="form-group row">
<label for="new_username">New Username 新帳號:</label><br>
<div class="input-group mb-3" style="margin-bottom: 5px !important;">
<input type="email" class="form-control" id="new_username" name="new_username">
</div>
<small class="text-muted">If you don't want to change your username, leave this blank.</small>
<small class="text-muted" style="margin-bottom: 1rem;">如果你不想更改你的帳號,請留空。</small>
</div>
{% endif %}
{% endwith %}
<div class="form-group row">
<label for="new_password">New Password 新密碼:</label><br>
<div class="input-group mb-3 hasSmall">
<input type="password" class="form-control" id="new_password" name="new_password"
aria-describedby="passwordHelp" required>
<div class="input-group-append">
<span class="input-group-text toggle-password" onclick="password_show_hide(2);">
<i class="fas fa-eye" id="new_show_eye"></i>
<i class="fas fa-eye-slash d-none" id="new_hide_eye"></i>
</span>
</div>
</div>
<small id="passwordHelp" class="form-text text-muted">6 characters minimum. 最少 6 字元。</small>
</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>
{% with messages = get_flashed_messages() %}
{% if messages %}
<div class="alert alert-danger" role="alert">
{% for message in messages %}
{% autoescape false %}{{message}}{% endautoescape %}
{% endfor %}
</div>
<div class="col"></div>
{% endif %}
{% endwith %}
{% include 'footer.html' %}
</div>
</div>
{% include 'footer.html' %}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<script src="/static/pagejs/chgPassword.js"></script>
<script src="/static/time.js"></script>

View file

@ -5,8 +5,8 @@
</div>
<footer>
<hr>
<p style="text-align: center;">&copy; 2021 Attendance (β) | Made by <a target="_blank"
href="https://github.com/aaronleetw">Aaron Lee 李翊愷</a> and Mr. Raymond Tsai 蔡瑋倫老師
<p style="text-align: center;">&copy; 2021 Attendance <span style="color: goldenrod;">2.0</span> | Made by Mr. Raymond Tsai 蔡瑋倫老師 and
<a target="_blank" href="https://github.com/aaronleetw">Aaron Lee 李翊愷</a>
<br>for <a target="_blank" href="https://www.fhjh.tp.edu.tw">Taipei Fuhsing Private School</a>
</p>
</footer>

View file

@ -5,7 +5,7 @@
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="robots" content="noindex" />
<title>Attendance 點名系統 (β)</title>
<title>Attendance 點名系統 2.0</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">
@ -20,38 +20,48 @@
<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="/forgotPassword" id="forgotPassword_sel" method="post">
<div class="input-group mb-3">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-user"></i></span>
</div>
<input type="text" class="form-control" name="username" id="username" placeholder="Username">
</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 class="text-center container">
<div class="loginPanel">
<h1 class="margin-top margin-bottom">
<div class="img"><img src="/static/favicon.ico" alt="" style="height: 100%"> Forgot Password 忘記密碼</div> </h1>
<form action="/forgotPassword" id="forgotPassword_sel" method="post">
<div>
<input class="form-check-input" type="radio" name="user_type" id="user_type_teacher" value="teacher" checked>
<label class="form-check-label" for="user_type_teacher" style="margin-right: 20px;">
Teacher 教師 / Admin 管理員
</label>
<input class="form-check-input" type="radio" name="user_type" id="user_type_student" value="student">
<label class="form-check-label" for="user_type_student">
Student 學生
</label>
</div>
{% endif %}
{% endwith %}
<br>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-user"></i></span>
</div>
<input type="text" class="form-control" name="username" id="username" placeholder="Username">
</div>
<button class="btn btn-warning btn-block g-recaptcha" onclick="loadingAnimation()">Confirm
確認</button>
<a href="/"><button type="button" class="btn btn-primary btn-block"
onclick="loadingAnimation()">Go back 回上一頁</button></a>
</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>
<div class="col"></div>
{% endif %}
{% endwith %}
{% include 'footer.html' %}
</div>
</div>
{% include 'footer.html' %}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<script src="/static/pagejs/forgotPassword.js"></script>
<script src="/static/time.js"></script>

View file

@ -5,10 +5,10 @@
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Group 分組 - Attendance 點名</title>
<title>Attendance 點名系統 2.0</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/login.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/css/all.min.css" integrity="sha512-1ycn6IcaQQ40/MKBW2W4Rhis/DbILU74C1vSrLJxCq57o941Ym01SwNsOMqvEBFlcgUa6xLiPY/NS5R+E6ztJQ==" crossorigin="anonymous" referrerpolicy="no-referrer" />
<link rel="stylesheet" href="/static/allpages.css">
<link rel="shortcut icon" type="image/x-icon" href="/static/favicon.ico" />
<!-- Global site tag (gtag.js) - Google Analytics -->
@ -20,96 +20,113 @@
<div class="showTime"><span id="showTime"></span></div>
<div class="container">
<h1 class="margin-top">Group Class View | 分組課頁面</h1>
<h2 class="margin-top">{{cclass['category']}}: {{cclass['class_id']}}: {{cclass['name']}}</h2>
<h2>[{{currDate}}]</h2>
<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>
<select name="date" id="date" class="form-select logout" onchange="chgDate();">
{% for date in range(dateKeys|length) %}
{% if dateKeys[date] == currDate %}
{% if date-2 >= 0 %}<option value="{{dateKeys[date-2]}}">{{dateKeys[date-2]}}</option>{% endif %}
{% if date-1 >= 0 %}<option value="{{dateKeys[date-1]}}">{{dateKeys[date-1]}}</option>{% endif %}
<option value="{{dateKeys[date]}}" selected="selected">{{dateKeys[date]}} ★</option>
{% for date in range(dates|length) %}
{% if dates[date][0] == currDate %}
{% if date-2 >= 0 %}<option value="{{dates[date-2][0]}}">{{dates[date-2][0]}}</option>{% endif %}
{% if date-1 >= 0 %}<option value="{{dates[date-1][0]}}">{{dates[date-1][0]}}</option>{% endif %}
<option value="{{dates[date][0]}}" selected="selected">{{dates[date][0]}} ★</option>
{% for i in range(1,5) %}
{% if date+i < dateKeys|length %}<option value="{{dateKeys[date+i]}}">{{dateKeys[date+i]}}</option>{%endif%}
{% endfor %}
{% endif %}
{% endfor %}
{% if date+i < dates|length %}<option value="{{dates[date+i][0]}}">{{dates[date+i][0]}}</option>{%endif%}
{% endfor %}
{% endif %}
{% endfor %}
</select>
{% for i in absData %}
{% set alr_fill = namespace(found=false) %}
<form action="/manage/group_teach_publish" id="attendanceData^{{i}}" method="post">
{% for c in data %}
{% for p in data[c] %}
{% if p == 'cdata' %}
{% continue %}
{% endif %}
<h3 class="nmb margin-top">Period {{p}}: {{data[c]['cdata']['name']}}</h3>
<form action="/manage/group_teach_publish" id="attendanceData^{{p}}" method="post">
<input type="hidden" name="date" value="{{currDate}}">
<input type="hidden" name="period" value="{{i}}">
<input type="hidden" name="period" value="{{p}}">
<input type="hidden" class="signatureData" name="signatureData" value="">
<div class="col" style="margin-top: 30px;">
<input type="hidden" name="category" value="{{data[c]['cdata']['category']}}">
<input type="hidden" name="class_id" value="{{data[c]['cdata']['class_id']}}">
<div class="col" style="margin-top: 5px;">
<div class="row title sticky-top" style="background-color: white">
<div class="col">Grade 年級</div>
<div class="col">Class 班級</div>
<div class="col">Number 座號</div>
<div class="col">Name 姓名</div>
<div class="col">English Name 英文名</div>
<div class="col">Period {{i}} | 第 {{i}} 節</div>
<div class="col">Period {{p}} | 第 {{p}} 節</div>
</div>
{% if data != None %}
{% for grade in absData[i] %}
{% for homeroom in absData[i][grade] %}
{% for student in absData[i][grade][homeroom] %}
{% if student != 'notes' %}
{% set need_fill = namespace(found=false) %}
{% if data[c] != None %}
{% for grade in data[c][p] %}
{% for student in data[c][p][grade] %}
<div class="row">
<div class="col">{{grade}}</div>
<div class="col">{{homeroom}}</div>
<div class="col">{{ student }}</div>
<div class="col">{{ absData[i][grade][homeroom][student]['name'] }}</div>
<div class="col">{{ absData[i][grade][homeroom][student]['eng_name'] }}</div>
{% if absData[i][grade][homeroom][student]['alr_fill'] %}
{% set alr_fill.found = true %}
{% if absData[i][grade][homeroom][student]['absent'] == 1 %}
<div class="col">{{ data[c][p][grade][student]['name'] }}</div>
<div class="col">{{ data[c][p][grade][student]['ename'] }}</div>
{% if data[c][p][grade][student]['status'] == 'L' %}
<div class="col">
<p class="highlightAbs n-2">X</p>
<p class="highlightAbs n-3">𝜑 <span style="color:black">{{data[c][p][grade][student]['note']}}</span></p>
</div>
{% elif absData[i][grade][homeroom][student]['absent'] == 2 %}
{% elif data[c][p][grade][student]['status'] == 'K' %}
<div class="col">
<p class="highlightAbs n-3">𝜑</p>
<p class="highlightAbs n-2"><span style="color:black">{{data[c][p][grade][student]['note']}}</span></p>
</div>
{% else %}
{% elif data[c][p][grade][student]['status'] == 'G' %}
<div class="col">
<p class="highlightAbs n-2"><span style="color:black">{{data[c][p][grade][student]['note']}}</span></p>
</div>
{% elif data[c][p][grade][student]['status'] == 'S' %}
<div class="col">
<p class="highlightAbs n-2"><span style="color:black">{{data[c][p][grade][student]['note']}}</span></p>
</div>
{% elif data[c][p][grade][student]['status'] == 'F' %}
<div class="col">
<p class="highlightAbs n-2"><span style="color:black">{{data[c][p][grade][student]['note']}}</span></p>
</div>
{% elif data[c][p][grade][student]['status'] == 'P' %}
<div class="col">
<p class="highlightAbs n-2"><span style="color:black">{{data[c][p][grade][student]['note']}}</span></p>
</div>
{% elif data[c][p][grade][student]['status'] == 'O' %}
<div class="col">
<p class="highlightAbs n-2"><span style="color:black">{{data[c][p][grade][student]['note']}}</span></p>
</div>
{% elif data[c][p][grade][student]['status'] == '--' %}
<div class="col">
<p class="highlightAbs">--</p>
</div>
{% elif data[c][p][grade][student]['status'] == 'present' %}
<div class="col">
<p class="highlightAbs n-1">V</p>
</div>
{% endif %}
{% else %}
<div class="col">
{% if [grade,homeroom] in confirmed %}
<p class="highlightAbs">--</p>
{% else %}
{% set alr_fill.found = false %}
<input type="checkbox" class="tobeform {{grade}}^{{homeroom}}^{{student}} late"
id="late^{{i}}^{{grade}}^{{homeroom}}^{{student}}"
name="2^{{grade}}^{{homeroom}}^{{student}}"
onchange="unCheckAbs('{{i}}^{{grade}}^{{homeroom}}^{{student}}')">
<input type="checkbox" class="tobeform {{grade}}^{{homeroom}}^{{student}} absent"
id="absent^{{i}}^{{grade}}^{{homeroom}}^{{student}}"
name="1^{{grade}}^{{homeroom}}^{{student}}"
onchange="unCheckLate('{{i}}^{{grade}}^{{homeroom}}^{{student}}')">
{% endif %}
<div id="input^{{p}}^{{grade}}^{{student}}" class="col">
{% set need_fill.found = true %}
<input type="checkbox" class="tobeform {{grade}}^{{student}} late"
id="late^{{p}}^{{grade}}^{{student}}"
name="2^{{grade}}^{{student}}"
onchange="unCheckAbs('{{p}}^{{grade}}^{{student}}')">
<input type="checkbox" class="tobeform {{grade}}^{{student}} absent"
id="absent^{{p}}^{{grade}}^{{student}}"
name="1^{{grade}}^{{student}}"
onchange="unCheckLate('{{p}}^{{grade}}^{{student}}')">
</div>
{% endif %}
</div>
{% endif %}
{% endfor %}
{% endfor %}
{% endfor %}
{% endif %}
{% if alr_fill.found %}
{% if not need_fill.found %}
<button class="btn btn-primary margin-bottom viewSignatureBtn" type="button"
onclick="viewSignature('{{i}}')" disabled="disabled">
onclick="viewSignature('{{p}}')" disabled="disabled">
Already Submitted</button>
{% else %}
<button class="btn btn-primary margin-bottom viewSignatureBtn" type="button"
onclick="viewSignature('{{i}}')">
↑ Confirm 確認 (Period {{i}}) ↑</button>
onclick="viewSignature('{{p}}')">
↑ Confirm 確認 (Period {{p}}) ↑</button>
{% endif %}
<div id="sign-{{i}}" class="signDiv modal fade" id="staticBackdrop" data-bs-backdrop="static"
<div id="sign-{{p}}" class="signDiv modal fade" id="staticBackdrop" data-bs-backdrop="static"
data-bs-keyboard="false" tabindex="-1" aria-labelledby="staticBackdropLabel" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered modal-xl">
<div class="modal-content">
@ -117,14 +134,52 @@
<h5 class="modal-title" id="staticBackdropLabel">Please Sign Below 請在下方簽名</h5>
</div>
<div class="modal-body">
<div class="alert alert-warning margin-top" id="allPresentWarning-{{i}}" role="alert"
<div class="alert alert-warning margin-top" id="allPresentWarning-{{p}}" role="alert"
hidden="hidden">
<h4 class="alert-heading">請確認是否全班全到Please check if everyone is present!</h4>
</div>
<div class="forSign"><canvas id="signature_pad^{{i}}"></canvas></div>
<div class="forSign"><canvas id="signature_pad^{{p}}"></canvas></div>
<h3 class="margin-top">Notes 備註欄</h3>
<input type="textarea" class="form-control" name="notes" id="subjectNotes^{{i}}"
<input type="textarea" class="form-control" name="notes" id="subjectNotes^{{p}}"
placeholder="Enter Notes 請輸入備註" style="width: 80%; margin-left: 10%;" row="3">
<style>
#indds div {
padding-left: 0;
}
#indds button {
margin-top: 0;
}
</style>
<div class="ds">
<h3 class="margin-top">定心專案</h3>
<div id="indds" class="row margin-top" style="width: 90%; margin-left: 5%">
<div class="col-2">
<select class="form-select" name="dsnumbers" id="dsnumbersel">
<option value="">選擇號碼</option>
{% for grade in data[c][p] %}
{% for i in data[c][p][grade] %}
{% if data[c][p][grade][i]['status'] == 'na' %}
<option value="{{grade}}-{{i}}-{{data[c][p][grade][i]['name']}}-{{data[c][p][grade][i]['ename']}}">{{grade}}-{{i}}-{{data[c][p][grade][i]['name']}}-{{data[c][p][grade][i]['ename']}}</option>
{% endif %}
{% endfor %}
{% endfor %}
</select>
</div>
<div class="col-4">
<select name="dsoffense" id="dsoffensesel" class="form-select">
<option value="">選擇違規事由</option>
{% for i in dsoffenses %}
<option value="{{i}}-{{dsoffenses[i]}}">{{i}}-{{dsoffenses[i]}}</option>
{% endfor %}
</select>
</div>
<div class="col-5">
<input type="text" class="form-control" name="dsoffenseother" id="dsoffenseother" placeholder="Other">
</div>
<div class="col noborder"><button type="button" class="btn btn-secondary" onclick="addDS()"><i class="fas fa-plus"></i></button></div>
</div>
<div id="inddsview" class="margin-top" style="width: 90%; margin-left: 5%;"><div class="col"></div></div>
</div>
</div>
<div class="modal-footer">
<button class="btn btn-secondary" type="button" onclick="signaturePad.clear()">Clear
@ -140,6 +195,7 @@
</div>
</form>
{% endfor %}
{% endfor %}
</div>
<script src=" https://cdn.jsdelivr.net/npm/signature_pad@2.3.2/dist/signature_pad.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>

View file

@ -5,9 +5,10 @@
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Homeroom 班級 {{homeroomCode[0]}}{{homeroomCode[1]}}) - Attendance 點名</title>
<title>Attendance 點名系統 2.0</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="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/css/all.min.css" integrity="sha512-1ycn6IcaQQ40/MKBW2W4Rhis/DbILU74C1vSrLJxCq57o941Ym01SwNsOMqvEBFlcgUa6xLiPY/NS5R+E6ztJQ==" crossorigin="anonymous" referrerpolicy="no-referrer" />
<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" />
@ -18,27 +19,26 @@
<body>
<div class="showTime"><span id="showTime"></span></div>
{% include 'sidebar.html' %}
<div class="container">
<h1 class="margin-top">Homeroom View | 班級主頁</h1>
<h2 class="margin-top">{{homeroomCode[0]}}{{homeroomCode[1]}}</h2>
<h2 class="margin-top">{{currRoom[0]}}{{currRoom[1]}}</h2>
<h2>[{{currDate}}]</h2>
<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>
<select name="date" id="date" class="form-select logout" onchange="chgDate();">
{% for date in range(dateKeys|length) %}
{% if dateKeys[date] == currDate %}
{% if date-2 >= 0 %}<option value="{{dateKeys[date-2]}}">{{dateKeys[date-2]}}</option>{% endif %}
{% if date-1 >= 0 %}<option value="{{dateKeys[date-1]}}">{{dateKeys[date-1]}}</option>{% endif %}
<option value="{{dateKeys[date]}}" selected="selected">{{dateKeys[date]}} ★</option>
{% for date in range(dates|length) %}
{% if dates[date][0] == currDate %}
{% if date-2 >= 0 %}<option value="{{dates[date-2][0]}}">{{dates[date-2][0]}}</option>{% endif %}
{% if date-1 >= 0 %}<option value="{{dates[date-1][0]}}">{{dates[date-1][0]}}</option>{% endif %}
<option value="{{dates[date][0]}}" selected="selected">{{dates[date][0]}} ★</option>
{% for i in range(1,5) %}
{% if date+i < dateKeys|length %}<option value="{{dateKeys[date+i]}}">{{dateKeys[date+i]}}</option>{%endif%}
{% endfor %}
{% endif %}
{% endfor %}
{% if date+i < dates|length %}<option value="{{dates[date+i][0]}}">{{dates[date+i][0]}}</option>{%endif%}
{% endfor %}
{% endif %}
{% endfor %}
</select>
<form action="/manage/homeroom_confirm" id="homeroom_confirm" method="post">
<input type="hidden" name="date" value="{{currDate}}">
<input type="hidden" name="homeroom" value="{{homeroomCode[0]}}^{{homeroomCode[1]}}">
<input type="hidden" name="homeroom" value="{{currRoom[0]}}^{{currRoom[1]}}">
<input type="hidden" id="hrCfrm-sign" name="signatureData" value="">
<input type="hidden" id="hrCfrm-notes" name="notes" value="">
</form>
@ -50,7 +50,7 @@
<div class="col">姓名</div>
<div class="col">英文姓名</div>
{% for i in periods %}
<div class="col" {% if currPeriod==i %} style="background-color: #ffdf81;" {% endif %}>{{i}}</div>
<div class="col" {% if currPeriod==i %}style="background-color: #ffdf81;"{%endif%}>{{i}}</div>
{% endfor %}
</div>
<div class="row title">
@ -59,8 +59,8 @@
<div class="col"></div>
<div class="col"></div>
{% for i in periods %}
<div class="col" {% if currPeriod==i %} style="background-color: #ffdf81;" {% endif %}>
{{absData[currDate][i]['name']}}</div>
<div class="col" {% if currPeriod==i %}style="background-color: #ffdf81;"{%endif%}>
{{schedule[i]['subject']}} {% if schedule[i]['special'] == True %} <span class="text-red">(換)</span> {% endif %}</div>
{% endfor %}
</div>
<div class="row title">
@ -69,50 +69,87 @@
<div class="col"></div>
<div class="col"></div>
{% for i in periods %}
<div class="col" {% if currPeriod==i %} style="background-color: #ffdf81;" {% endif %}>
{{absData[currDate][i]['teacher']}}</div>
<div class="col" {% if currPeriod==i %}style="background-color: #ffdf81;"{%endif%}>{{schedule[i]['teacher']}}</div>
{% endfor %}
</div>
</div>
{% for i in homeroomData %}
{% for i in students %}
<div class="row">
<div class="col">{{homeroomCode[0]}}{{homeroomCode[1]}}</div>
<div class="col">{{i}}</div>
<div class="col">{{ homeroomData[i]['name'] }}</div>
<div class="col">{{ homeroomData[i]['eng_name'] }}</div>
<div class="col">{{currRoom[0]}}{{currRoom[1]}}</div>
<div class="col">{{ i[0] }}</div>
<div class="col">{{ i[1] }}</div>
<div class="col">{{ i[2] }}</div>
{% for j in periods %}
<div class="col view-{{j}}" {% if currPeriod==j %} style="background-color: #ffdf81;" {% endif %}>
{% if 'signature' in absData[currDate][j] %}
{% if i in absData[currDate][j] %}
{% if absData[currDate][j][i] == 1 %}
<p class="highlightAbs n-2 view-n-{{i}}">X</p>
<div class="col view-{{j}}" {% if currPeriod==j %}style="background-color: #ffdf81;"{%endif%}>
{% if schedule[j]['subject'] == 'GP' %}
{% for k in submission[j] %}
{% if studGP[i[0]][schedule[j]['teacher']] == k %}
{% if i[0] in absentData[j] %}
{% if absentData[j][i[0]]['status'] == 'L' %}
<p class="highlightAbs n-3 view-n-{{i[0]}}">𝜑</p>
{% elif absentData[j][i[0]]['status'] == 'K' %}
<p class="highlightAbs n-2 view-n-{{i[0]}}"></p>
{% elif absentData[j][i[0]]['status'] == 'G' %}
<p class="highlightAbs n-2 view-n-{{i[0]}}"></p>
{% elif absentData[j][i[0]]['status'] == 'S' %}
<p class="highlightAbs n-2 view-n-{{i[0]}}"></p>
{% elif absentData[j][i[0]]['status'] == 'F' %}
<p class="highlightAbs n-2 view-n-{{i[0]}}"></p>
{% elif absentData[j][i[0]]['status'] == 'P' %}
<p class="highlightAbs n-2 view-n-{{i[0]}}"></p>
{% elif absentData[j][i[0]]['status'] == 'O' %}
<p class="highlightAbs n-2 view-n-{{i[0]}}"></p>
{% else %}
<p class="highlightAbs n-3 view-n-{{i}}">𝜑</p>
<p class="highlightAbs n-2 view-n-{{i[0]}}">{{absentData[j][i[0]]['status']}}</p>
{% endif %}
<p class="highlightAbs">{{absentData[j][i[0]]['note']}}</p>
{% else %}
<p class="highlightAbs n-1 view-n-{{i[0]}}">V</p>
{% if j in idvDS and i[0] in idvDS[j] %}
<p class="highlightAbs n-2">{{idvDS[j][i[0]]}}</p>
{% endif %}
{% endif %}
{% else %}
{% if absData[currDate][j]['name'] != 'GP' %}
<p class="highlightAbs n-1 view-n-{{i}}">V</p>
<p class="highlightAbs view-n-{{i[0]}}"></p>
{% endif %}
{% endfor %}
{% else %}
{% if (homeroomData[i]['GP_Class'][absData[currDate][j]['teacher']] in
absData[currDate][j]['signature'] or 'STUD_AFFAIR_OFFICE' in absData[currDate][j]['signature'])%}
<p class="highlightAbs n-1 view-n-{{i}}">V</p>
{% if j in submission or j in absentData %}
{% if i[0] in absentData[j] %}
{% if absentData[j][i[0]]['status'] == 'L' %}
<p class="highlightAbs n-3 view-n-{{i[0]}}">𝜑</p>
{% elif absentData[j][i[0]]['status'] == 'K' %}
<p class="highlightAbs n-2 view-n-{{i[0]}}"></p>
{% elif absentData[j][i[0]]['status'] == 'G' %}
<p class="highlightAbs n-2 view-n-{{i[0]}}"></p>
{% elif absentData[j][i[0]]['status'] == 'S' %}
<p class="highlightAbs n-2 view-n-{{i[0]}}"></p>
{% elif absentData[j][i[0]]['status'] == 'F' %}
<p class="highlightAbs n-2 view-n-{{i[0]}}"></p>
{% elif absentData[j][i[0]]['status'] == 'P' %}
<p class="highlightAbs n-2 view-n-{{i[0]}}"></p>
{% elif absentData[j][i[0]]['status'] == 'O' %}
<p class="highlightAbs n-2 view-n-{{i[0]}}"></p>
{% else %}
<p class="highlightAbs n-2 view-n-{{i}}"></p>
<p class="highlightAbs n-2 view-n-{{i[0]}}">{{absentData[j][i[0]]['status']}}</p>
{% endif %}
<p class="highlightAbs">{{absentData[j][i[0]]['note']}}</p>
{% else %}
{% if j in submission %}
<p class="highlightAbs n-1 view-n-{{i[0]}}">V</p>
{% if j in idvDS and i[0] in idvDS[j] %}
<p class="highlightAbs n-2">{{idvDS[j][i[0]]}}</p>
{% endif %}
{% else %}
<div class="input-{{j}}-{{i[0]}}">
<input type="checkbox" class="tobeform 2^{{j}}^{{i[0]}} late" id="late^{{j}}^{{i[0]}}"
onchange="unCheckAbs('{{j}}^{{i[0]}}')">
<input type="checkbox" class="tobeform 1^{{j}}^{{i[0]}} absent" id="absent^{{j}}^{{i[0]}}"
onchange="unCheckLate('{{j}}^{{i[0]}}')">
</div>
{% endif %}
{% endif %}
{% endif %}
{% elif absData[currDate][j]['name'] == 'GP' %}
<p class="highlightAbs"></p>
{% else %}
{% if 'confirm' in absData[currDate] %}
<p class="highlightAbs"></p>
{% else %}
<input type="checkbox" class="tobeform 2^{{j}}^{{i}} late" id="late^{{j}}^{{i}}"
onchange="unCheckAbs('{{j}}^{{i}}')">
<input type="checkbox" class="tobeform 1^{{j}}^{{i}} absent" id="absent^{{j}}^{{i}}"
onchange="unCheckLate('{{j}}^{{i}}')">
{% endif %}
<!-- <input type="checkbox" class="tobeform {{j}}^{{i}}"> -->
{% endif %}
</div>
{% endfor %}
@ -125,25 +162,46 @@
<div class="col"></div>
{% for i in periods %}
<div id="btns-{{i}}" class="col" {% if currPeriod==i %} style="background-color: #ffdf81;" {% endif %}>
{% if (absData[currDate][i]['name'] == 'GP' or 'confirm' in absData[currDate] or 'signature' in
absData[currDate][i]) %}
{% if (schedule[i]['subject'] == 'GP' or i in submission or 'c' in submission) %}
<button class="btn btn-primary afterSelButton" disabled="disabled"></button>
{% else %}
<button class="btn btn-primary afterSelButton"
onclick="afterSelAbs('{{i|string}}', 'newSubmit')">Confirm<br>{{absData[currDate][i]['name']}}</button>
onclick="afterSelAbs('{{i|string}}', 'newSubmit')">Confirm<br>{{schedule[i]['subject']}}</button>
{% endif %}
</div>
{% endfor %}
</div>
{% for i in range(7) %}
<div class="row" {%if i==0%}style="border-top: 2px solid black;"{%endif%} {% if i == 6 %} style="border-bottom: 2px solid black;" {% endif %}>
<div class="col"></div>
<div class="col"></div>
<div class="col" style="font-weight: bold;">{{dstext[i]}}</div>
<div class="col">{{dsboard[i]}}</div>
{% for j in periods %}
{% if j in submission and schedule[j] != 'GP' %}
<div class="col" {% if currPeriod==j %} style="background-color: #ffdf81;" {% endif %}>
{{submission[j]['ds' + (i+1)|string]}}</div>
{% else %}
<div class="col" {% if currPeriod==j %} style="background-color: #ffdf81;" {% endif %}></div>
{% endif %}
{% endfor %}
</div>
{% endfor %}
<form action="/manage/homeroom_abs" id="postHomeroomAbs" hidden="hidden" method="post">
<input type="text" id="HR-date" name="date" value="{{currDate}}">
<input type="text" id="HR-period" name="period" value="">
<input type="text" id="HR-signatureData" name="signatureData" value="">
<input type="text" id="HR-notes" name="notes" value="">
<input type="text" id="HR-homeroom" name="homeroom" value="{{homeroomCode[0]}}^{{homeroomCode[1]}}">
<input type="text" id="HR-type" name="stype" value="">
<input type="text" id="HR-homeroom" name="homeroom" value="{{currRoom[0]}}^{{currRoom[1]}}">
<input type="text" id="HR-ds1" name="ds^1" value="">
<input type="text" id="HR-ds2" name="ds^2" value="">
<input type="text" id="HR-ds3" name="ds^3" value="">
<input type="text" id="HR-ds4" name="ds^4" value="">
<input type="text" id="HR-ds5" name="ds^5" value="">
<input type="text" id="HR-ds6" name="ds^6" value="">
<input type="text" id="HR-ds7" name="ds^7" value="">
</form>
{% if 'confirm' in absData[currDate] %}
{% if 'c' in submission %}
<button class="btn btn-primary margin-top afterSelButton" onclick="homeroomCfrm()" disabled="disabled">
Homeroom Teacher Already Confirmed | 班導已確認</button>
{% else %}
@ -165,17 +223,55 @@
<h4 class="alert-heading">請確認是否全班全到Please check if everyone is present!</h4>
</div>
<div class="forSign"><canvas id="signature_pad"></canvas></div>
<div class="ds">
<h3 class="margin-top">定心專案 DS</h3>
{% for i in range(dsboard|length) %}
<div class="row dsboard" style="width: 40%; margin-left: 30%;">
<div class="col col-4">
<span>{{dsboard[i]}}</span>
</div>
{% for j in range(6) %}
<div class="col">
<input class="form-check-input" type="radio" name="ds{{i+1}}" id="ds{{i+1}}{{j}}" value="{{j}}" {%if j==5%}checked{%endif%}>
<label class="form-check-label" for="ds{{i+1}}{{j}}">{{j}}</label>
</div>
{% endfor %}
</div>
{% endfor %}
<div id="indds" class="row margin-top" style="width: 90%; margin-left: 5%">
<div class="col-2">
<select class="form-select" name="dsnumbers" id="dsnumbersel">
<option value="">選擇號碼</option>
{% for i in students %}
<option value="{{i[0]}}-{{i[1]}}-{{i[2]}}">{{i[0]}}-{{i[1]}}-{{i[2]}}</option>
{% endfor %}
</select>
</div>
<div class="col-4">
<select name="dsoffense" id="dsoffensesel" class="form-select">
<option value="">選擇違規事由</option>
{% for i in dsoffenses %}
<option value="{{i}}-{{dsoffenses[i]}}">{{i}}-{{dsoffenses[i]}}</option>
{% endfor %}
</select>
</div>
<div class="col-5">
<input type="text" class="form-control" name="dsoffenseother" id="dsoffenseother" placeholder="Other">
</div>
<div class="col noborder"><button class="btn btn-secondary nomargin-top" onclick="addDS()"><i class="fas fa-plus"></i></button></div>
</div>
<div id="inddsview" class="margin-top" style="width: 90%; margin-left: 5%;"><div class="col"></div></div>
</div>
<h3 class="margin-top">Notes 備註欄</h3>
<input type="textarea" class="form-control" name="notes" id="subjectNotes"
placeholder="Enter Notes 請輸入備註" style="width: 80%; margin-left: 10%;" row="3">
</div>
<div class="modal-footer">
<button class="btn btn-secondary" type="button" onclick="signaturePad.clear()">Clear
Signature 清除簽名</button>
<button type="button" class="btn btn-danger" onclick="location.reload();">Cancel
取消</button>
<button type="button" class="btn btn-primary submitButton" onclick="submitForm()">Submit
提交</button>
<div class="row">
<div class="col"><button class="btn btn-secondary" type="button" onclick="signaturePad.clear()">Clear Signature 清除簽名</button></div>
<div class="col"><button type="button" class="btn btn-danger" onclick="location.reload();">Cancel 取消</button></div>
<div class="col"><button type="button" class="btn btn-primary" onclick="submitForm()">Submit 提交</button></div>
</div>
</div>
</div>
</div>
@ -187,36 +283,41 @@
<div class="col half">
{% if c == 0 %}
<div class="row needborder">Homeroom Teacher 導師</div>
{% if 'confirm' in absData[currDate] %}
<div class="row"><img src="{{absData[currDate]['confirm']}}" alt=""></div>
<div class="row">備註: {{absData[currDate]['notes']}}</div>
{% if 'c' in submission %}
<div class="row"><img src="{{submission['c']['signature']}}" alt=""></div>
<div class="row">備註: {{submission['c']['notes']}}</div>
{% else %}
<div class="row"><span style="color:red;">No Signature 導師尚未簽名</span></div>
{% endif %}
{% else %}
{% if absData[currDate][periods[c-1]]['name'] == 'GP' %}
{% if 'signature' in absData[currDate][periods[c-1]] %}
{% for i in absData[currDate][periods[c-1]]['signature'] %}
<div class="row needborder">{{periods[c-1]}}: {{absData[currDate][periods[c-1]]['teacher']}}: {{i}}:
{{absData[currDate][periods[c-1]]['names'][i]}}</div>
<div class="row"><img src="{{absData[currDate][periods[c-1]]['signature'][i]}}" alt="">
{% if loop.index == loop.length %}
<br>備註: {{absData[currDate][periods[c-1]]['notes']}}
{% if schedule[periods[c-1]]['subject'] == 'GP' %}
{% if periods[c-1] in submission %}
{% for i in submission[periods[c-1]] %}
{% if i != 'notes' %}
<div class="row needborder">{{periods[c-1]}}:
{{schedule[periods[c-1]]['teacher']}}: {{i}}</div>
<div class="row"><img src="{{submission[periods[c-1]][i]['signature']}}" alt="">
{% if loop.index == loop.length-1 %}
<br>備註: {{submission[periods[c-1]]['notes']}}
{% endif %}
</div>
{% endif %}
{% endfor %}
{% else %}
<div class="row needborder">{{periods[c-1]}}: {{absData[currDate][periods[c-1]]['teacher']}}: No
<div class="row needborder">{{periods[c-1]}}:
{{schedule[periods[c-1]]['teacher']}}: {{i}}: No
Signature
</div>
<div class="row"></div>
{% endif %}
{% else %}
<div class="row needborder">{{periods[c-1]}}: {{absData[currDate][periods[c-1]]['name']}}:
{{absData[currDate][periods[c-1]]['teacher']}}
<div class="row needborder">{{periods[c-1]}}: {{schedule[periods[c-1]]['subject']}}:
{{schedule[periods[c-1]]['teacher']}}
</div>
<div class="row"><img src="{{absData[currDate][periods[c-1]]['signature']}}" alt=""><br>備註:
{{absData[currDate][periods[c-1]]['notes']}}</div>
{% if periods[c-1] in submission %}
<div class="row"><img src="{{submission[periods[c-1]]['signature']}}" alt=""><br>備註:
{{submission[periods[c-1]]['notes']}}</div>
{% endif %}
{% endif %}
{% endif %}
</div>
@ -235,7 +336,7 @@
<script>
var periodData = {}
{% for i in periods %}
periodData['{{i}}'] = '{{ absData[currDate][i]['name'] }}'
periodData['{{i}}'] = '{{ schedule[i]['subject'] }}'
{% endfor %}
</script>
<script src="/static/pagejs/homeroom.js"></script>

92
templates/list.html Normal file
View file

@ -0,0 +1,92 @@
<!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>Attendance 點名系統 2.0</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="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/css/all.min.css" integrity="sha512-1ycn6IcaQQ40/MKBW2W4Rhis/DbILU74C1vSrLJxCq57o941Ym01SwNsOMqvEBFlcgUa6xLiPY/NS5R+E6ztJQ==" crossorigin="anonymous" referrerpolicy="no-referrer" />
<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 src="/static/gtag.js"></script>
</head>
<body>
<div class="showTime"><span id="showTime"></span></div>
{% include 'sidebar.html' %}
<div class="container">
<h1 class="margin-top">{{title}}</h1>
<h2 class="margin-top">{{currRoom[0]}}{{currRoom[1]}}
{% if mode == 'STUDABS' or mode == 'STUDDS' %}
{{num}}號 {{name}}
{% endif %}
</h2>
<div class="col">
<div class="row">
<div class="col col-1">Date 日期</div>
<div class="col col-1">Period 節次</div>
{% if not (mode == 'STUDABS' or mode == 'STUDDS') %}
<div class="col col-1">Number 座號</div>
<div class="col col-1">Name 姓名</div>
<div class="col col-1">English Name</div>
{% endif %}
{% if mode == 'ABS' or mode == 'STUDABS' %}
<div class="col col-1">Status 狀態</div>
<div class="col">Notes 備註</div>
{% elif mode == 'DS' or mode == 'STUDDS' %}
<div class="col">Description 敘述</div>
{% endif %}
</div>
{% for a in data %}
<div class="row">
<div class="col col-1">{{a[0]}}</div>
<div class="col col-1">{{a[1]}}</div>
{% if not (mode == 'STUDABS' or mode == 'STUDDS') %}
<div class="col col-1">{{a[2]}}</div>
<div class="col col-1">{{students[a[2]]['name']}}</div>
<div class="col col-1">{{students[a[2]]['ename']}}</div>
{% endif %}
{% if mode == 'ABS' or mode == 'STUDABS' %}
<div class="col col-1">
{% if a[3]== 'L' %}
<p class="highlightAbs n-3">𝜑</p>
{% elif a[3]== 'K' %}
<p class="highlightAbs n-2"></p>
{% elif a[3]== 'G' %}
<p class="highlightAbs n-2"></p>
{% elif a[3]== 'S' %}
<p class="highlightAbs n-2"></p>
{% elif a[3]== 'F' %}
<p class="highlightAbs n-2"></p>
{% elif a[3]== 'P' %}
<p class="highlightAbs n-2"></p>
{% elif a[3]== 'O' %}
<p class="highlightAbs n-2"></p>
{% else %}
<p class="highlightAbs n-2">{{a[3]}}</p>
{% endif %}</div>
<div class="col">{{a[4]}}</div>
{% elif mode == 'DS' or mode == 'STUDDS' %}
<div class="col">{{a[3]}}</div>
{% endif %}
</div>
{% endfor %}
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/signature_pad@2.3.2/dist/signature_pad.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"
integrity="sha384-ka7Sk0Gln4gmtz2MlQnikT1wXgYsOg+OMhuP+IlRH9sENBO0LRn5q+8nbTov4+1p"
crossorigin="anonymous"></script>
{% include 'footer.html' %}
<script src="/static/pagejs/homeroom.js"></script>
<script src="/static/time.js"></script>
</body>
</html>

View file

@ -5,7 +5,7 @@
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="robots" content="noindex" />
<title>Attendance 點名系統 (β)</title>
<title>Attendance 點名系統 2.0</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">
@ -19,54 +19,56 @@
<body>
<div class="showTime"><span id="showTime"></span></div>
<div class="container">
<h1 class="margin-top margin-bottom">Attendance 點名系統 (β) | Login 登入</h1>
<div class="row">
<div class="col"></div>
<div class="col-md-5">
<form action="/" id="loginForm" method="post">
<div class="input-group mb-3">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-user"></i></span>
</div>
<input type="text" class="form-control" name="username" id="username" placeholder="Username">
</div>
<div class="input-group mb-3">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-lock"></i></span>
</div>
<input type="password" class="form-control" name="password" id="password"
placeholder="Password">
<div class="input-group-append">
<span class="input-group-text toggle-password" onclick="password_show_hide();">
<i class="fas fa-eye" id="show_eye"></i>
<i class="fas fa-eye-slash d-none" id="hide_eye"></i>
</span>
</div>
</div>
<button class="btn btn-primary btn-block g-recaptcha" onclick="loadingAnimation()">Login
登入</button>
</form>
<a href="/forgotPassword">Forgot Password 忘記密碼</a>
<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 class="text-center container">
<div class="loginPanel">
<h1 class="margin-top margin-bottom">
<div class="img"><img src="/static/favicon.ico" alt="" style="height: 100%"> Attendance 點名系統 <span style="color: goldenrod;">2.0</span></div> </h1>
<form action="/" id="loginForm" method="post">
<div>
<input class="form-check-input" type="radio" name="user_type" id="user_type_teacher" value="teacher" checked>
<label class="form-check-label" for="user_type_teacher" style="margin-right: 20px;">
Teacher 教師 / Admin 管理員
</label>
<input class="form-check-input" type="radio" name="user_type" id="user_type_student" value="student">
<label class="form-check-label" for="user_type_student">
Student 學生
</label>
</div>
{% with messages = get_flashed_messages() %}
{% if messages %}
<div class="alert alert-danger margin-top" role="alert">
{% for message in messages %}
{% autoescape false %}{{message}}{% endautoescape %}
{% endfor %}
<br>
<div class="input-group mb-3">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-user"></i></span>
</div>
<input type="text" class="form-control" name="username" id="username" placeholder="Username 帳號">
</div>
{% endif %}
{% endwith %}
<div class="input-group mb-3">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-lock"></i></span>
</div>
<input type="password" class="form-control" name="password" id="password"
placeholder="Password 密碼">
<div class="input-group-append">
<span class="input-group-text toggle-password" onclick="password_show_hide();">
<i class="fas fa-eye" id="show_eye"></i>
<i class="fas fa-eye-slash d-none" id="hide_eye"></i>
</span>
</div>
</div>
<button class="btn btn-primary btn-block" onclick="loadingAnimation()">Login 登入</button>
</form>
<a href="/forgotPassword">Forgot Password 忘記密碼</a>
{% with messages = get_flashed_messages() %}
{% if messages %}
<div class="alert alert-danger margin-top" role="alert">
{% for message in messages %}
{% autoescape false %}{{message}}{% endautoescape %}
{% endfor %}
</div>
<div class="col"></div>
{% endif %}
{% endwith %}
{% include 'footer.html' %}
</div>
</div>
{% include 'footer.html' %}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<script src="/static/pagejs/login.js"></script>
<script src="/static/time.js"></script>

View file

@ -5,11 +5,11 @@
<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>
<title>Attendance 點名系統 2.0</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="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>
@ -18,31 +18,29 @@
<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']}}">🏠 {{data[key]['homeroom']}}
</option>
{% else %}
{% for i in data[key] %}
{% if i == 'type' %}
{% else %}
<option value="group^{{i}}^{{data[key][i]}}">🧑‍🏫 {{i}}: {{data[key][i]}}</option>
{% endif %}
{% endfor %}
{% endif %}
{% endfor %}
</select>
</form>
<div class="text-center container">
<div class="loginPanel">
<h1 class="margin-top margin-bottom">
<div class="img"><img src="/static/favicon.ico" alt="" style="height: 100%"> Select Subuser 選擇子帳號</div> </h1>
<h2 class="margin-top">Hi, {{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 h in homeroom %}
{% for c in homeroom[h] %}
<option value="homeroom^{{h}}^{{c}}">🏠: {{h}}^{{c}}</option>
{% endfor %}
{% endfor %}
{% if group %}
<option value="group">💼: All Group Classes</option>
{% endif %}
</select>
</form>
{% include 'footer.html' %}
</div>
</div>
{% include 'footer.html' %}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<script src="/static/pagejs/selSubUser.js"></script>
<script src="/static/time.js"></script>

83
templates/sidebar.html Normal file
View file

@ -0,0 +1,83 @@
<link rel="stylesheet" href="/static/nav.css">
{% if session['subuser_type'] == 'homeroom' %}
<nav id="sidebar" class="active">
<h1><a href="/manage" class="logo"><img src="/static/favicon.ico" alt="" width="100%"></a></h1>
<ul class="list-unstyled components mb-5">
<li class="active">
<a href="/manage"><span class="fa fa-home"></span> Home 主頁</a>
</li>
<li>
<a href="/manage/abs"><span class="fa fa-user-times"></span> All Absent<br>所有缺勤</a>
</li>
<li>
<a href="/manage/ds"><span class="fa fa-skull-crossbones"></span> All DS<br>所有定心</a>
</li>
<li style="position:absolute; bottom: 2px; left:3px;">
<a href="/select"><span class="fa fa-people-arrows"></span> Switch User<br>切換帳號</a>
<a href="/logout"><span class="fa fa-sign-out-alt"></span> Logout<br>登出</a>
</li>
</ul>
</nav>
{% elif session['subuser_type'] == 'admin' %}
<nav id="sidebar" class="active" style="max-width: 150px;">
<h1><a href="/manage" class="logo"><img src="/static/favicon.ico" alt="" width="100%"></a></h1>
<ul class="list-unstyled components mb-5">
<li class="active">
<a href="/manage"><span class="fa fa-home"></span> Home 主頁</a>
<div class="col">
<div class="row">
<select name="grade" id="sel-grade" class="form-select" onchange="getHR()" required>
<option value="">選擇年級</option>
{% for grade in homerooms %}
<option value="{{grade}}">{{grade}}</option>
{% endfor %}
</select>
</div>
<div class="row">
<select name="room" id="sel-room" class="form-select" disabled required>
<option value="">請先選擇年級</option>
</select>
</div>
<div class="row">
<select name="date" id="date" class="form-select">
{% for date in dates %}
{% if date[0] == currDate %}
<option value="{{date[0]}}" selected="selected">{{date[0]}} ★</option>
{% else %}
<option value="{{date[0]}}">{{date[0]}}</option>
{% endif %}
{% endfor %}
</select>
</div>
<div class="row-1">
<button type="button" class="btn btn-primary" onclick="redirAdmin()">查詢</button>
</div>
</div>
</li>
<li>
<a href="/admin"><span class="fa fa-toolbox"></span> Admin<br>資料庫管理</a>
</li>
<li style="position:absolute; bottom: 2px; left:50px;">
<a href="/logout"><span class="fa fa-sign-out-alt"></span> Logout<br>登出</a>
</li>
</ul>
</nav>
{% elif session['user_type'] == 'student' %}
<nav id="sidebar" class="active">
<h1><a href="/student" class="logo"><img src="/static/favicon.ico" alt="" width="100%"></a></h1>
<ul class="list-unstyled components mb-5">
<li>
<a href="/student"><span class="fa fa-user-times"></span> All Absent<br>所有缺勤</a>
</li>
<li>
<a href="/student/ds"><span class="fa fa-skull-crossbones"></span> All DS<br>所有定心</a>
</li>
<li style="position:absolute; bottom: 2px; left:10px;">
<a href="/chgPassword"><span class="fa fa-user-cog"></span>Chg Pswd<br>更改帳密</a>
<a href="/logout"><span class="fa fa-sign-out-alt"></span> Logout<br>登出</a>
</li>
</ul>
</nav>
{% endif %}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<script src="/static/nav.js"></script>

View file

@ -1,16 +0,0 @@
<video id="loading" oncontextmenu="return false;" width="100%" height="100%" loop autoplay muted style="display: none;">
<source src="/static/loading.webm" type="video/webm">
</video>
<footer>
<hr>
<p style="text-align: center;">&copy; 2021 Attendance (β) | Made by <a target="_blank"
href="https://github.com/aaronleetw">Aaron Lee 李翊愷</a> and Mr. Raymond Tsai 蔡瑋倫老師
<br>for <a target="_blank" href="https://www.fhjh.tp.edu.tw">Taipei Fuhsing Private School</a>
</p>
</footer>
<script>
function loadingAnimation() {
$('#loading').show();
}
</script>

View file

@ -4,11 +4,10 @@
<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 點名 - Upload</title>
<title>Attendance 點名系統 2.0</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>

View file

@ -4,7 +4,7 @@
<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>
<title>Attendance 點名系統 2.0</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">
@ -19,44 +19,41 @@
<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?mode=resetPassword&oobCode={{oobCode}}" id="password_form" method="post">
<div class="form-group row" style="margin-bottom: 10px;">
<label for="password">New Password 新密碼:</label><br>
<div class="input-group mb-3 hasSmall">
<input type="password" class="form-control" id="password" name="password"
aria-describedby="passwordHelp" required>
<div class="input-group-append">
<span class="input-group-text toggle-password" onclick="password_show_hide();">
<i class="fas fa-eye" id="show_eye"></i>
<i class="fas fa-eye-slash d-none" id="hide_eye"></i>
</span>
</div>
<div class="text-center container">
<div class="loginPanel">
<h1 class="margin-top margin-bottom">
<div class="img"><img src="/static/favicon.ico" alt="" style="height: 100%"> Reset Password 重置密碼</div> </h1>
<form action="/resetPassword?resetCode={{resetCode}}" id="password_form" method="post">
<div class="form-group row" style="margin-bottom: 10px;">
<label for="password">New Password 新密碼:</label><br>
<div class="input-group mb-3 hasSmall">
<input type="password" class="form-control" id="password" name="password"
aria-describedby="passwordHelp" required>
<div class="input-group-append">
<span class="input-group-text toggle-password" onclick="password_show_hide();">
<i class="fas fa-eye" id="show_eye"></i>
<i class="fas fa-eye-slash d-none" id="hide_eye"></i>
</span>
</div>
<small id="passwordHelp" class="form-text text-muted">6 characters
minimum.</small>
</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 %}
<small id="passwordHelp" class="form-text text-muted">6 characters
minimum.</small>
</div>
{% endif %}
{% endwith %}
<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>
<div class="col"></div>
{% endif %}
{% endwith %}
{% include 'footer.html' %}
</div>
</div>
{% include 'footer.html' %}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<script src="/static/pagejs/verifiedChgPassword.js"></script>
<script src="/static/time.js"></script>

0
tt.py Normal file
View file

221
upload.py
View file

@ -17,14 +17,22 @@ def upload_users():
with open(filepath) as file:
csv_dict = csv.DictReader(file)
for row in csv_dict:
pwd = addZeroesUntil(row['password'], 6)
user = auth.create_user_with_email_and_password(
row['username'] + "@group-attendance.fhjh.tp.edu.tw", pwd)
db.child("Users").child(user['localId']).set({
'permission': 'realPerson',
'name': row['name'],
'origUsername': row['username'],
}, session['token'])
pwd = row['password']
db = refresh_db()
cursor = db.cursor(buffered=True)
cursor.execute("""
INSERT IGNORE INTO users (email, name, oldUsername, role, password)
VALUES (%s, %s, %s, %s, %s)
""", (row['username']+'@abs.fhjh.tp.edu.tw', row['name'], row['username'], 'R', genHash(pwd)))
db.commit()
cursor.close()
# user = auth.create_user_with_email_and_password(
# row['username'] + "@abs.fhjh.tp.edu.tw", pwd)
# 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)
@ -47,24 +55,37 @@ def upload_homeroom():
csv_file = request.files['csv']
filepath = os.path.join('./temp', csv_file.filename)
csv_file.save(filepath)
allUsers = db.child("Users").get(session['token']).val()
# allUsers = db.child("Users").get(session['token']).val()
with open(filepath) as file:
csv_dict = csv.DictReader(file)
db = refresh_db()
cursor = db.cursor(buffered=True)
for row in csv_dict:
if row['number'] == 'teacher' or row['number'] == 'Teacher':
accs = row['name'].split(',')
for key in allUsers:
if accs == []:
break
if (allUsers[key]['origUsername'] in accs):
db.child("Users").child(key).child("accounts").child("homeroom^"+gradec+classc+'^'+str(randint(10000, 99999))).update({
"homeroom": gradec + '^' + classc,
"type": 'homeroom'
}, session['token'])
accs.remove(allUsers[key]['origUsername'])
accs = json.dumps(row['name'].split(','))
cursor.execute("""
INSERT IGNORE INTO homerooms (grade, class_, accs)
VALUES (%s, %s, %s)
""", (str(gradec), str(classc), accs))
# for key in allUsers:
# if accs == []:
# break
# if (allUsers[key]['origUsername'] in accs):
# db.child("Users").child(key).child("accounts").child("homeroom^"+gradec+classc+'^'+str(randint(10000, 99999))).update({
# "homeroom": gradec + '^' + classc,
# "type": 'homeroom'
# }, session['token'])
# accs.remove(allUsers[key]['origUsername'])
else:
db.child("Homerooms").child(gradec).child(
classc).child(row['number']).set(row, session['token'])
email = gradec + classc + row['number'] + '@st.fhjh.tp.edu.tw'
cursor.execute("""
INSERT IGNORE INTO students (grade, class_, num, name, ename, email, password)
VALUES (%s, %s, %s, %s, %s, %s, %s)
""", (gradec, classc, row['number'], row['name'], row['eng_name'], email, genHash(row['eng_name'])))
# db.child("Homerooms").child(gradec).child(
# classc).child(row['number']).set(row, session['token'])
db.commit()
cursor.close()
# row['class'] row['number'] row['name'] row['eng_name']
os.remove(filepath)
except Exception as e:
@ -87,24 +108,32 @@ def upload_gp_classes():
csv_file.save(filepath)
csv_dict = pd.read_csv(filepath)
category_cnt = csv_dict.shape[1] - 1
allUsers = db.child("Users").get(session['token']).val()
# allUsers = db.child("Users").get(session['token']).val()
for i in range(category_cnt):
tmp_csv = csv_dict[csv_dict.columns[i+1]].tolist()
for j in range(len(tmp_csv)):
if type(tmp_csv[j]) == float:
break
if j % 5 == 0:
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'])
accs = tmp_csv[j+4].split(',')
for key in allUsers:
if accs == []:
break
if (allUsers[key]['origUsername'] in accs):
db.child("Users").child(key).child("accounts").child("GP_Class^"+csv_dict.columns[i+1]+'^'+str(randint(10000, 99999))).update({
csv_dict.columns[i+1]: tmp_csv[j],
"type": 'group'
}, session['token'])
db = refresh_db()
cursor = db.cursor(buffered=True)
cursor.execute("""
INSERT IGNORE INTO gpclasses (category, subclass, about, accs)
VALUES (%s, %s, %s, %s)
""", (csv_dict.columns[i+1], tmp_csv[j], tmp_csv[j+1] + " : " + tmp_csv[j+2] + " (" + tmp_csv[j+3] + ")", json.dumps(tmp_csv[j+4].split(','))))
db.commit()
cursor.close()
# 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'])
# accs = tmp_csv[j+4].split(',')
# for key in allUsers:
# if accs == []:
# break
# if (allUsers[key]['origUsername'] in accs):
# db.child("Users").child(key).child("accounts").child("GP_Class^"+csv_dict.columns[i+1]+'^'+str(randint(10000, 99999))).update({
# csv_dict.columns[i+1]: tmp_csv[j],
# "type": 'group'
# }, session['token'])
os.remove(filepath)
except Exception as e:
os.remove(filepath)
@ -130,13 +159,23 @@ def upload_stud_in_group():
csv_dict = csv.DictReader(file)
headers = csv_dict.fieldnames
headers = headers[1:]
for h in headers:
db.child("Classes").child("GP_Class").child(
h).child("Homerooms").update({gradec+'^'+classc: 0}, session['token'])
# for h in headers:
# db.child("Classes").child("GP_Class").child(
# h).child("Homerooms").update({gradec+'^'+classc: 0}, session['token'])
for row in csv_dict:
for h in headers:
db.child("Homerooms").child(gradec).child(classc).child(
row[str(gradec+classc)]).child("GP_Class").update({h: row[h]}, session['token'])
num = row.pop(str(gradec+classc))
db = refresh_db()
cursor = db.cursor(buffered=True)
cursor.execute("""
UPDATE students
SET classes = %s
WHERE grade = %s AND class_ = %s AND num = %s
""", (json.dumps(row), gradec, classc, num))
db.commit()
cursor.close()
# for h in headers:
# db.child("Homerooms").child(gradec).child(classc).child(
# row[str(gradec+classc)]).child("GP_Class").update({h: row[h]}, session['token'])
os.remove(filepath)
except Exception as e:
os.remove(filepath)
@ -166,15 +205,52 @@ def upload_period_list():
for j in range(len(tmp_csv)):
if not (periodCodes[j].endswith('-t')):
if type(tmp_csv[j]) == float:
db.child("Classes").child("Homeroom").child(gradec).child(classc).child(
str(i+1)).child(periodCodes[j]).update({'name': '--'}, session['token'])
db = refresh_db()
cursor = db.cursor(buffered=True)
cursor.execute("""
INSERT IGNORE INTO schedule
(grade, class_, dow, period, subject, teacher)
VALUES (%s, %s, %s, %s, %s, %s)
""", (gradec, classc, str(i+1), periodCodes[j], '--', '--'))
db.commit()
cursor.close()
# db.child("Classes").child("Homeroom").child(gradec).child(classc).child(
# str(i+1)).child(periodCodes[j]).update({'name': '--'}, session['token'])
else:
db.child("Classes").child("Homeroom").child(gradec).child(classc).child(
str(i+1)).child(periodCodes[j]).update({'name': tmp_csv[j]}, session['token'])
db = refresh_db()
cursor = db.cursor(buffered=True)
cursor.execute("""
INSERT IGNORE INTO schedule
(grade, class_, dow, period, subject)
VALUES (%s, %s, %s, %s, %s)
""", (gradec, classc, str(i+1), periodCodes[j], tmp_csv[j]))
db.commit()
cursor.close()
# db.child("Classes").child("Homeroom").child(gradec).child(classc).child(
# str(i+1)).child(periodCodes[j]).update({'name': tmp_csv[j]}, session['token'])
if not(periodCodes[j] == 'm' or periodCodes[j] == 'n'):
j += 1
db.child("Classes").child("Homeroom").child(gradec).child(classc).child(
str(i+1)).child(periodCodes[j-1]).update({'teacher': tmp_csv[j]}, session['token'])
db = refresh_db()
cursor = db.cursor(buffered=True)
cursor.execute("""
UPDATE schedule
SET teacher = %s
WHERE grade = %s AND class_ = %s AND dow = %s AND period = %s
""", (tmp_csv[j], gradec, classc, str(i+1), periodCodes[j-1]))
db.commit()
cursor.close()
# db.child("Classes").child("Homeroom").child(gradec).child(classc).child(
# str(i+1)).child(periodCodes[j-1]).update({'teacher': tmp_csv[j]}, session['token'])
else:
db = refresh_db()
cursor = db.cursor(buffered=True)
cursor.execute("""
UPDATE schedule
SET teacher = %s
WHERE grade = %s AND class_ = %s AND dow = %s AND period = %s
""", ("--", gradec, classc, str(i+1), periodCodes[j]))
db.commit()
cursor.close()
os.remove(filepath)
except Exception as e:
os.remove(filepath)
@ -197,17 +273,26 @@ def upload_dates():
with open(filepath) as file:
csv_dict = csv.DictReader(file)
headers = csv_dict.fieldnames
temp = db.child("Homerooms").get(session['token']).val()
db = refresh_db()
cursor = db.cursor(buffered=True)
for row in csv_dict:
for h in headers:
for t in temp:
for i in temp[t]:
periodData = db.child("Classes").child(
"Homeroom").child(t).child(i).get(session['token']).val()
db.child("Homerooms").child(t).child(i).child(
"Absent").child(h).update({"dow": row[h]}, session['token'])
db.child("Homerooms").child(t).child(i).child(
"Absent").child(h).update(periodData[int(row[h])], session['token'])
cursor.execute("""
INSERT IGNORE INTO dates (date, dow) VALUES (%s, %s)
""", (h, row[h]))
db.commit()
cursor.close()
# temp = db.child("Homerooms").get(session['token']).val()
# for row in csv_dict:
# for h in headers:
# for t in temp:
# for i in temp[t]:
# periodData = db.child("Classes").child(
# "Homeroom").child(t).child(i).get(session['token']).val()
# db.child("Homerooms").child(t).child(i).child(
# "Absent").child(h).update({"dow": row[h]}, session['token'])
# db.child("Homerooms").child(t).child(i).child(
# "Absent").child(h).update(periodData[int(row[h])], session['token'])
os.remove(filepath)
except Exception as e:
os.remove(filepath)
@ -230,15 +315,25 @@ def upload_admin_acc():
with open(filepath) as file:
csv_dict = csv.DictReader(file)
for row in csv_dict:
auth.create_user_with_email_and_password(
row['username'] + '@group-attendance.fhjh.tp.edu.tw', row['password'])
user = auth.sign_in_with_email_and_password(
row['username'] + '@group-attendance.fhjh.tp.edu.tw', row['password'])
db.child("Users").child(user['localId']).update({
'permission': 'admin',
'username': row['username'],
'showUpload': row['permission']
}, session['token'])
pwd = row['password']
role = 'S' if row['permission'] == '1' else 'A'
db = refresh_db()
cursor = db.cursor(buffered=True)
cursor.execute("""
INSERT IGNORE INTO users (email, name, oldUsername, role, password)
VALUES (%s, %s, %s, %s, %s)
""", (row['username']+'@abs.fhjh.tp.edu.tw', row['name'], row['username'], role, genHash(pwd)))
db.commit()
cursor.close()
# auth.create_user_with_email_and_password(
# row['username'] + '@group-attendance.fhjh.tp.edu.tw', row['password'])
# user = auth.sign_in_with_email_and_password(
# row['username'] + '@group-attendance.fhjh.tp.edu.tw', row['password'])
# db.child("Users").child(user['localId']).update({
# 'permission': 'admin',
# 'username': row['username'],
# 'showUpload': row['permission']
# }, session['token'])
os.remove(filepath)
except Exception as e:
os.remove(filepath)