from flask import Flask, Blueprint, request from flask_security import (SQLAlchemyUserDatastore, login_required, current_user, Security) from flask_security.utils import verify_and_update_password, login_user from flask_restless import APIManager, ProcessingException from models import (db, User, Role, Gradeclass, Student, AttendanceUpdate, Presence) from sqlalchemy import func import json from datetime import datetime, date from dateutil import parser app = Flask(__name__) app.config['DEBUG'] = True app.config['PORT'] = 5001 app.config['SECRET_KEY'] = 'super-secret' app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:////tmp/db.sqlite' app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False app.config['DEFAULT_MAIL_SENDER'] = 'info@example.com' app.config['SECURITY_PASSWORD_SALT'] = 'uaisfyasiduyaisiuf' db.init_app(app) user_datastore = SQLAlchemyUserDatastore(db, User, Role) security = Security(app, user_datastore) def auth_func(*args, **kwargs): # if not current_user.is_authenticated: # raise ProcessingException(description='Not authenticated', code=401) return True verify_logged_id = dict(GET_SINGLE=[auth_func], GET_MANY=[auth_func]) manager = APIManager(app, flask_sqlalchemy_db=db) manager.create_api( Student, methods=['GET', 'PUT'], results_per_page=5, preprocessors=verify_logged_id) manager.create_api( Gradeclass, methods=['GET', 'PUT'], results_per_page=5, preprocessors=verify_logged_id) json_cont = {'Content-Type': 'application/json'} resp = lambda val,code: (json.dumps({'status': val}), code, json_cont) presense_map = {i:str(i).replace('Presence.', '').upper() for i in Presence} @app.before_first_request def create_test_data(): try: db.drop_all() db.create_all() for i in range(3): g_cls = Gradeclass("Class {}".format(i)) db.session.add(g_cls) db.session.commit() for s in range(5): stu = Student("TestStudent#{}-Class{}".format(s,g_cls.id), g_cls.id) db.session.add(stu) user_datastore.create_user( email='user@example.com', password='password') db.session.commit() except: pass def parse_time(time_str): utc_now = (time_str and time_str == 'now') or not time_str time = datetime.utcnow() if utc_now else None try: time = parser.parse(attend_date) except: pass return time def compute_attendance(request_type,object_id, start_time, end_time): def get_record(req_type,obj_id): attend = AttendanceUpdate.query.filter( AttendanceUpdate.update_type == req_type ).filter( AttendanceUpdate.value_identifier == obj_id ).filter( func.date(AttendanceUpdate.time) >= start_time.date() ).filter( func.date(AttendanceUpdate.time) >= end_time.date() ).first() return attend pres_rec = None if request_type == 'student': stud_rec = get_record('student',object_id) stud = Student.query.get(object_id) pres_rec = stud_rec if stud_rec else get_record('class',stud.gradeclass_id) if request_type == 'class': pres_rec = get_record(request_type,object_id) return presense_map[pres_rec.presence].lower() if pres_rec else 'no_records' def upsert_attendance(update_type, object_id, attend_time, pres_enum): existing = AttendanceUpdate.query.filter( AttendanceUpdate.update_type == update_type).filter( AttendanceUpdate.value_identifier == object_id).filter( func.date( AttendanceUpdate.time) == attend_time.date()).first() if existing: existing.presence = pres_enum else: new_entry = AttendanceUpdate(update_type, object_id, attend_time, pres_enum) db.session.add(new_entry) db.session.commit() def validate_attend(update_type, object_id): # return True if update_type in ['class', 'student'] and object_id: g_cls_found = update_type == 'class' and Gradeclass.query.get( object_id) stud_found = update_type == 'student' and Student.query.get(object_id) return g_cls_found or stud_found else: return False def get_attendance(update_type, request): object_id = request.args.get('identifier', None) start_timestr = request.args.get('start_time', None) end_timestr = request.args.get('end_time', None) start_time, end_time = parse_time(start_timestr), parse_time(end_timestr) ret_data = resp('invalid',400) if validate_attend(update_type, object_id) and start_time and end_time: pres = compute_attendance(update_type,object_id,start_time,end_time) ret_data = resp(pres,200) return ret_data def update_attendance(update_type, request): object_id = request.form.get('identifier', None) presence = request.form.get('presence', None) attend_str = request.form.get('time', None) attend_time = parse_time(attend_str) ret_data = resp('invalid',400) if validate_attend( update_type, object_id ) and presence and attend_time and presence.upper() in presense_map.values(): pres_enum = Presence[presence.upper()] upsert_attendance(update_type, object_id, attend_time, pres_enum) ret_data = resp('updated',200) return ret_data @app.route('/api/attendance/', methods=['GET', 'POST']) @login_required def attendance(update_type): if request.method == 'GET': return get_attendance(update_type, request) elif request.method == 'POST': return update_attendance(update_type, request) @app.route('/api/login', methods=['POST']) def login(): username = request.form.get('username', None) password = request.form.get('password', None) ret_data = (json.dumps({'status': 'failed'}), 401, json_cont) if username and password: user = User.query.filter(User.email == username).first() if not user: ret_data = resp('notfound',401) elif verify_and_update_password(password, user): login_user(user) ret_data = resp('success',200) return ret_data @app.route('/') def home(): return 'hello' if __name__ == '__main__': app.run()