From 6c3a6c28d17168a289b993114c6c710c7a141439 Mon Sep 17 00:00:00 2001 From: Malar Kannan Date: Thu, 31 Aug 2017 19:44:54 +0530 Subject: [PATCH] 1. implemented new api endpoints --- .gitignore | 2 + README.md | 9 +++ models.py | 59 +++++++++++++++++++ server.py | 166 ++++++++++++++++++++++++++++++----------------------- 4 files changed, 164 insertions(+), 72 deletions(-) create mode 100644 README.md create mode 100644 models.py diff --git a/.gitignore b/.gitignore index 7ecbf91..76e1bf0 100644 --- a/.gitignore +++ b/.gitignore @@ -102,3 +102,5 @@ ENV/ /site # End of https://www.gitignore.io/api/python + +session.json diff --git a/README.md b/README.md new file mode 100644 index 0000000..7292855 --- /dev/null +++ b/README.md @@ -0,0 +1,9 @@ +## Worflow +start the server by running +`$python server.py` + +### Login +`$http --session=./session.json -f POST http://127.0.0.1:5000/api/login username=user@example.com password=password` + +### Update Attendance +`$http --session=./session.json -f POST http://127.0.0.1:5000/api/attendance/student presence=absent identifier=2 time=now` diff --git a/models.py b/models.py new file mode 100644 index 0000000..e62350a --- /dev/null +++ b/models.py @@ -0,0 +1,59 @@ +from flask_sqlalchemy import SQLAlchemy +from flask_security import UserMixin,RoleMixin +from enum import Enum,unique +from datetime import datetime + +db = SQLAlchemy() + +roles_users = db.Table('roles_users', + db.Column('user_id', db.Integer(), db.ForeignKey('user.id')), + db.Column('role_id', db.Integer(), db.ForeignKey('role.id'))) + +class Role(db.Model, RoleMixin): + id = db.Column(db.Integer(), primary_key=True) + name = db.Column(db.String(80), unique=True) + description = db.Column(db.String(255)) + +class User(db.Model, UserMixin): + id = db.Column(db.Integer, primary_key=True) + email = db.Column(db.String(255), unique=True) + password = db.Column(db.String(255)) + active = db.Column(db.Boolean()) + confirmed_at = db.Column(db.DateTime()) + roles = db.relationship('Role', secondary=roles_users, + backref=db.backref('users', lazy='dynamic')) + +@unique +class Presence(Enum): + """docstring for ResultType.""" + PRESENT,ABSENT,SICK,VACATION = range(4) + +class Gradeclass(db.Model): + id = db.Column(db.Integer, primary_key=True) + class_name = db.Column(db.String(255)) + + def __init__(self, class_name): + self.class_name = class_name + +class Student(db.Model): + id = db.Column(db.Integer, primary_key=True) + student_name = db.Column(db.String(255)) + gradeclass_id = db.Column(db.Integer, db.ForeignKey('gradeclass.id')) + gradeclass = db.relationship('Gradeclass',backref=db.backref('gradeclass', lazy='dynamic')) + + def __init__(self, student_name,gradeclass_id): + self.student_name = student_name + self.gradeclass_id = gradeclass_id + +class AttendanceUpdate(db.Model): + id = db.Column(db.Integer, primary_key=True) + time = db.Column(db.DateTime()) + presence = db.Column(db.Enum(Presence)) + update_type = db.Column(db.String(10)) + value_identifier = db.Column(db.Integer) + + def __init__(self, update_type, value_identifier,time,presence): + self.update_type = update_type + self.value_identifier = value_identifier + self.presence = presence + self.time = time diff --git a/server.py b/server.py index cb1f181..2eb8752 100644 --- a/server.py +++ b/server.py @@ -1,114 +1,136 @@ -from flask import Flask,Blueprint,request,Response -from flask_sqlalchemy import SQLAlchemy -from flask_security import SQLAlchemyUserDatastore,UserMixin,RoleMixin,login_required,current_user,Security -from flask_restless import APIManager,ProcessingException -from enum import Enum,unique +from flask import Flask, Blueprint, request, Response +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 = SQLAlchemy(app) - -roles_users = db.Table('roles_users', - db.Column('user_id', db.Integer(), db.ForeignKey('user.id')), - db.Column('role_id', db.Integer(), db.ForeignKey('role.id'))) - -class Role(db.Model, RoleMixin): - id = db.Column(db.Integer(), primary_key=True) - name = db.Column(db.String(80), unique=True) - description = db.Column(db.String(255)) - -class User(db.Model, UserMixin): - id = db.Column(db.Integer, primary_key=True) - email = db.Column(db.String(255), unique=True) - password = db.Column(db.String(255)) - active = db.Column(db.Boolean()) - confirmed_at = db.Column(db.DateTime()) - roles = db.relationship('Role', secondary=roles_users, - backref=db.backref('users', lazy='dynamic')) - -# class ClassSession(db.Model): -# id = db.Column(db.Integer, primary_key=True) -# name = db.Column(db.String(255), unique=True) -# confirmed_at = db.Column(db.DateTime()) - -@unique -class Presence(Enum): - """docstring for ResultType.""" - PRESENT,ABSENT,SICK,VACATION = range(4) - -class Gradeclass(db.Model): - id = db.Column(db.Integer, primary_key=True) - class_name = db.Column(db.String(255)) - -class Student(db.Model): - id = db.Column(db.Integer, primary_key=True) - student_name = db.Column(db.String(255)) - gradeclass_id = db.Column(db.Integer, db.ForeignKey('gradeclass.id')) - gradeclass = db.relationship('Gradeclass',backref=db.backref('gradeclass', lazy='dynamic')) - -class StudentAttendance(db.Model): - id = db.Column(db.BigInteger, primary_key=True) - student_id = db.Column(db.Integer, db.ForeignKey('student.id')) - gradeclass_id = db.Column(db.Integer, db.ForeignKey('gradeclass.id')) - time = db.Column(db.DateTime()) - presence = db.Column(db.Enum(Presence)) - student = db.relationship('Student',backref=db.backref('student', lazy='dynamic')) - gradeclass = db.relationship('Gradeclass',backref=db.backref('gradeclass', lazy='dynamic')) - - +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]) + +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) +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) + @app.before_first_request -def create_user(): +def create_test_data(): try: db.drop_all() db.create_all() - user_datastore.create_user(email='user@example.com', password='password') + for i in range(12): + g_cls = Gradeclass("Class {}".format(i)) + db.session.add(g_cls) + for s in range(10): + stu = Student("TestStudent#{}-Class{}".format(s,i), g_cls.id) + db.session.add(stu) + user_datastore.create_user( + email='user@example.com', password='password') db.session.commit() except: pass -def api_resp(data,code=200): +def api_resp(data, code=200): return Response(response=data, - status=code, - mimetype="application/json") + status=code, + mimetype="application/json") -@app.route('/api/attendance/',methods=['GET']) +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 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 + + +presense_keys = [str(i).replace('Presence.', '').upper() for i in Presence] + + +@app.route('/api/attendance/', methods=['POST']) # @login_required def update_attendance(update_type): - value = request.args.get('presence',None) - object_id = request.args.get('identifier',None) - if update_type == 'class': + 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 = (json.dumps({'status': 'invalid'}),400) + if validate_attend(update_type, object_id) and presence and attend_time and presence.upper() in presense_keys: + pres_enum = Presence[presence.upper()] + upsert_attendance(update_type, object_id, attend_time, pres_enum) + ret_data = (json.dumps({'status': 'updated'}),200) + return api_resp(*ret_data) - elif update_type == 'student': - data= json.dumps({'status':'updated'}) - return api_resp(data) +@app.route('/api/login',methods=['POST']) +def login(): + username = request.form.get('username', None) + password = request.form.get('password', None) + print(username,password) + ret_data = (json.dumps({'status': 'failed'}),401) + if username and password: + user = User.query.filter(User.email == username).first() + if not user: + ret_data = (json.dumps({'status': 'notfound'}),401) + elif verify_and_update_password(password,user): + login_user(user) + ret_data = (json.dumps({'status': 'success'}),200) + return ret_data @app.route('/') def home(): return 'hello' + if __name__ == '__main__': app.run()