├── application ├── __init__.py ├── instances.py ├── sec.py ├── test.html ├── worker.py ├── mail_service.py ├── tasks.py ├── models.py ├── resources.py └── views.py ├── thunder-tests ├── thunderActivity.json ├── thunderEnvironment.json ├── thunderCollection.json └── thunderclient.json ├── .gitignore ├── README.md ├── static ├── components │ ├── AdminHome.js │ ├── InstructorHome.js │ ├── StudentHome.js │ ├── StudyResource.js │ ├── SudyResourceForm.js │ ├── Users.js │ ├── Home.js │ ├── Login.js │ └── Navbar.js ├── router.js └── index.js ├── celerybeat-schedule ├── test.csv ├── celeryconfig.py ├── config.py ├── requirements.txt ├── templates └── index.html ├── main.py └── upload_initial_data.py /application/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /thunder-tests/thunderActivity.json: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /thunder-tests/thunderEnvironment.json: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | venv 2 | *__pycache__ 3 | instance 4 | venv1 -------------------------------------------------------------------------------- /application/instances.py: -------------------------------------------------------------------------------- 1 | from flask_caching import Cache 2 | 3 | cache = Cache() -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # study_resource_management 2 | App which helps students to manage study material 3 | -------------------------------------------------------------------------------- /static/components/AdminHome.js: -------------------------------------------------------------------------------- 1 | export default { 2 | template: `
Welcome admin
`, 3 | } 4 | -------------------------------------------------------------------------------- /celerybeat-schedule: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/narendraiitm/study_resource_management/HEAD/celerybeat-schedule -------------------------------------------------------------------------------- /static/components/InstructorHome.js: -------------------------------------------------------------------------------- 1 | export default { 2 | template: `
Welcome Instructor
`, 3 | } 4 | -------------------------------------------------------------------------------- /test.csv: -------------------------------------------------------------------------------- 1 | topic,description 2 | topic1,hvgfjhgvhjg 3 | topic2,fhfvgkjhklhh 4 | Topic 3,This is topic 3 5 | Topic4,This is topic 4 6 | -------------------------------------------------------------------------------- /application/sec.py: -------------------------------------------------------------------------------- 1 | from flask_security import SQLAlchemyUserDatastore 2 | from .models import db, User, Role 3 | datastore = SQLAlchemyUserDatastore(db, User, Role) -------------------------------------------------------------------------------- /celeryconfig.py: -------------------------------------------------------------------------------- 1 | broker_url = "redis://localhost:6379/1" 2 | result_backend = "redis://localhost:6379/2" 3 | timezone = "Asia/kolkata" 4 | broker_connection_retry_on_startup=True -------------------------------------------------------------------------------- /thunder-tests/thunderCollection.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "_id": "d9eb3ada-cffc-4600-8ff4-d545c063e581", 4 | "colName": "study_resource_management", 5 | "created": "2023-10-09T13:49:05.039Z", 6 | "sortNum": 10000, 7 | "folders": [] 8 | } 9 | ] -------------------------------------------------------------------------------- /application/test.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Document 8 | 9 | 10 | Hello from html {{email}} 11 | 12 | 13 | -------------------------------------------------------------------------------- /application/worker.py: -------------------------------------------------------------------------------- 1 | from celery import Celery, Task 2 | 3 | def celery_init_app(app): 4 | class FlaskTask(Task): 5 | def __call__(self, *args: object, **kwargs: object) -> object: 6 | with app.app_context(): 7 | return self.run(*args, **kwargs) 8 | 9 | celery_app = Celery(app.name, task_cls=FlaskTask) 10 | celery_app.config_from_object("celeryconfig") 11 | return celery_app -------------------------------------------------------------------------------- /static/router.js: -------------------------------------------------------------------------------- 1 | import Home from './components/Home.js' 2 | import Login from './components/Login.js' 3 | import Users from './components/Users.js' 4 | import SudyResourceForm from './components/SudyResourceForm.js' 5 | 6 | const routes = [ 7 | { path: '/', component: Home, name: 'Home' }, 8 | { path: '/login', component: Login, name: 'Login' }, 9 | { path: '/users', component: Users }, 10 | { path: '/create-resource', component: SudyResourceForm }, 11 | ] 12 | 13 | export default new VueRouter({ 14 | routes, 15 | }) 16 | -------------------------------------------------------------------------------- /config.py: -------------------------------------------------------------------------------- 1 | class Config(object): 2 | DEBUG = False 3 | TESTING = False 4 | 5 | 6 | class DevelopmentConfig(Config): 7 | DEBUG = True 8 | SQLALCHEMY_DATABASE_URI = 'sqlite:///dev.db' 9 | SECRET_KEY = "thisissecter" 10 | SECURITY_PASSWORD_SALT = "thisissaltt" 11 | SQLALCHEMY_TRACK_MODIFICATIONS = False 12 | WTF_CSRF_ENABLED = False 13 | SECURITY_TOKEN_AUTHENTICATION_HEADER = 'Authentication-Token' 14 | CACHE_TYPE = "RedisCache" 15 | CACHE_REDIS_HOST = "localhost" 16 | CACHE_REDIS_PORT = 6379 17 | CACHE_REDIS_DB = 3 -------------------------------------------------------------------------------- /application/mail_service.py: -------------------------------------------------------------------------------- 1 | from smtplib import SMTP 2 | from email.mime.multipart import MIMEMultipart 3 | from email.mime.text import MIMEText 4 | 5 | SMTP_HOST = "localhost" 6 | SMTP_PORT = 1025 7 | SENDER_EMAIL = 'narendr@study.iitm.ac.in' 8 | SENDER_PASSWORD = '' 9 | 10 | 11 | def send_message(to, subject, content_body): 12 | msg = MIMEMultipart() 13 | msg["To"] = to 14 | msg["Subject"] = subject 15 | msg["From"] = SENDER_EMAIL 16 | msg.attach(MIMEText(content_body, 'html')) 17 | client = SMTP(host=SMTP_HOST, port=SMTP_PORT) 18 | client.send_message(msg=msg) 19 | client.quit() 20 | -------------------------------------------------------------------------------- /static/index.js: -------------------------------------------------------------------------------- 1 | import router from './router.js' 2 | import Navbar from './components/Navbar.js' 3 | 4 | router.beforeEach((to, from, next) => { 5 | if (to.name !== 'Login' && !localStorage.getItem('auth-token') ? true : false) 6 | next({ name: 'Login' }) 7 | else next() 8 | }) 9 | 10 | new Vue({ 11 | el: '#app', 12 | template: `
13 | 14 |
`, 15 | router, 16 | components: { 17 | Navbar, 18 | }, 19 | data: { 20 | has_changed: true, 21 | }, 22 | watch: { 23 | $route(to, from) { 24 | this.has_changed = !this.has_changed 25 | }, 26 | }, 27 | }) 28 | -------------------------------------------------------------------------------- /static/components/StudentHome.js: -------------------------------------------------------------------------------- 1 | export default { 2 | template: `
Welcome Student Waiting...
`, 3 | data() { 4 | return { 5 | isWaiting: false, 6 | } 7 | }, 8 | methods: { 9 | async downlodResource() { 10 | this.isWaiting = true 11 | const res = await fetch('/download-csv') 12 | const data = await res.json() 13 | if (res.ok) { 14 | const taskId = data['task-id'] 15 | const intv = setInterval(async () => { 16 | const csv_res = await fetch(`/get-csv/${taskId}`) 17 | if (csv_res.ok) { 18 | this.isWaiting = false 19 | clearInterval(intv) 20 | window.location.href = `/get-csv/${taskId}` 21 | } 22 | }, 1000) 23 | } 24 | }, 25 | }, 26 | } 27 | -------------------------------------------------------------------------------- /static/components/StudyResource.js: -------------------------------------------------------------------------------- 1 | export default { 2 | template: `
3 |

{{resource.topic}}

4 |

{{resource.description}}

5 |
Creator: {{resource.creator}}
6 | 7 |
`, 8 | props: ['resource'], 9 | data() { 10 | return { 11 | role: localStorage.getItem('role'), 12 | authToken: localStorage.getItem('auth-token'), 13 | } 14 | }, 15 | methods: { 16 | async approveResource() { 17 | const res = await fetch(`/study-resource/${this.resource.id}/approve`, { 18 | headers: { 19 | 'Authentication-Token': this.authToken, 20 | }, 21 | }) 22 | const data = await res.json() 23 | if (res.ok) { 24 | alert(data.message) 25 | this.$router.go(0) 26 | } else { 27 | alert(data.message) 28 | } 29 | }, 30 | }, 31 | } 32 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | amqp==5.2.0 2 | aniso8601==9.0.1 3 | async-timeout==4.0.3 4 | backports.zoneinfo==0.2.1 5 | billiard==4.2.0 6 | blinker==1.5 7 | celery==5.3.5 8 | chardet==5.2.0 9 | click==8.1.3 10 | click-didyoumean==0.3.0 11 | click-plugins==1.1.1 12 | click-repl==0.3.0 13 | dnspython==2.3.0 14 | email-validator==1.3.1 15 | Flask==2.2.3 16 | Flask-Excel==0.0.7 17 | Flask-Login==0.6.2 18 | Flask-Principal==0.4.0 19 | Flask-RESTful==0.3.10 20 | Flask-Security-Too==5.1.2 21 | Flask-SQLAlchemy==3.0.3 22 | Flask-WTF==1.1.1 23 | greenlet==2.0.2 24 | idna==3.4 25 | importlib-metadata==6.0.0 26 | itsdangerous==2.1.2 27 | Jinja2==3.1.2 28 | kombu==5.3.3 29 | lml==0.1.0 30 | MarkupSafe==2.1.2 31 | passlib==1.7.4 32 | prompt-toolkit==3.0.40 33 | pyexcel==0.7.0 34 | pyexcel-io==0.6.6 35 | pyexcel-webio==0.1.4 36 | python-dateutil==2.8.2 37 | pytz==2023.3.post1 38 | redis==5.0.1 39 | six==1.16.0 40 | SQLAlchemy==2.0.6 41 | texttable==1.7.0 42 | typing-extensions==4.5.0 43 | tzdata==2023.3 44 | vine==5.1.0 45 | wcwidth==0.2.9 46 | Werkzeug==2.2.3 47 | WTForms==3.0.1 48 | zipp==3.15.0 49 | -------------------------------------------------------------------------------- /application/tasks.py: -------------------------------------------------------------------------------- 1 | from celery import shared_task 2 | from .models import StudyResource 3 | import flask_excel as excel 4 | from .mail_service import send_message 5 | from .models import User, Role 6 | from jinja2 import Template 7 | 8 | 9 | @shared_task(ignore_result=False) 10 | def create_resource_csv(): 11 | stud_res = StudyResource.query.with_entities( 12 | StudyResource.topic, StudyResource.description).all() 13 | 14 | csv_output = excel.make_response_from_query_sets( 15 | stud_res, ["topic", "description"], "csv") 16 | filename = "test.csv" 17 | 18 | with open(filename, 'wb') as f: 19 | f.write(csv_output.data) 20 | 21 | return filename 22 | 23 | 24 | @shared_task(ignore_result=True) 25 | def daily_reminder(to, subject): 26 | users = User.query.filter(User.roles.any(Role.name == 'admin')).all() 27 | for user in users: 28 | with open('test.html', 'r') as f: 29 | template = Template(f.read()) 30 | send_message(user.email, subject, 31 | template.render(email=user.email)) 32 | return "OK" 33 | -------------------------------------------------------------------------------- /static/components/SudyResourceForm.js: -------------------------------------------------------------------------------- 1 | export default { 2 | template: `
3 | 4 | 5 | 6 | 7 |
`, 8 | 9 | data() { 10 | return { 11 | resource: { 12 | topic: null, 13 | description: null, 14 | resource_link: null, 15 | }, 16 | token: localStorage.getItem('auth-token'), 17 | } 18 | }, 19 | 20 | methods: { 21 | async createResource() { 22 | const res = await fetch('/api/study_material', { 23 | method: 'POST', 24 | headers: { 25 | 'Authentication-Token': this.token, 26 | 'Content-Type': 'application/json', 27 | }, 28 | body: JSON.stringify(this.resource), 29 | }) 30 | 31 | const data = await res.json() 32 | if (res.ok) { 33 | alert(data.message) 34 | } 35 | }, 36 | }, 37 | } 38 | -------------------------------------------------------------------------------- /static/components/Users.js: -------------------------------------------------------------------------------- 1 | export default { 2 | template: `
3 |
{{error}}
4 |
5 | {{user.email}} 6 |
7 |
`, 8 | data() { 9 | return { 10 | allUsers: [], 11 | token: localStorage.getItem('auth-token'), 12 | error: null, 13 | } 14 | }, 15 | methods: { 16 | async approve(istId) { 17 | const res = await fetch(`/activate/inst/${istId}`, { 18 | headers: { 19 | 'Authentication-Token': this.token, 20 | }, 21 | }) 22 | const data = await res.json() 23 | if (res.ok) { 24 | alert(data.message) 25 | } 26 | }, 27 | }, 28 | async mounted() { 29 | const res = await fetch('/users', { 30 | headers: { 31 | 'Authentication-Token': this.token, 32 | }, 33 | }) 34 | const data = await res.json().catch((e) => {}) 35 | if (res.ok) { 36 | console.log(data) 37 | this.allUsers = data 38 | } else { 39 | this.error = res.status 40 | } 41 | }, 42 | } 43 | -------------------------------------------------------------------------------- /templates/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Document 8 | 14 | 15 | 16 |
17 | 18 | 19 | 23 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /static/components/Home.js: -------------------------------------------------------------------------------- 1 | import StudentHome from './StudentHome.js' 2 | import InstructorHome from './InstructorHome.js' 3 | import AdminHome from './AdminHome.js' 4 | import StudyResource from './StudyResource.js' 5 | 6 | export default { 7 | template: `
8 | 9 | 10 | 11 | 12 |
`, 13 | 14 | data() { 15 | return { 16 | userRole: localStorage.getItem('role'), 17 | authToken: localStorage.getItem('auth-token'), 18 | resources: [], 19 | } 20 | }, 21 | 22 | components: { 23 | StudentHome, 24 | InstructorHome, 25 | AdminHome, 26 | StudyResource, 27 | }, 28 | async mounted() { 29 | const res = await fetch('/api/study_material', { 30 | headers: { 31 | 'Authentication-Token': this.authToken, 32 | }, 33 | }) 34 | const data = await res.json() 35 | if (res.ok) { 36 | this.resources = data 37 | } else { 38 | alert(data.message) 39 | } 40 | }, 41 | } 42 | -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | from flask import Flask 2 | from flask_security import SQLAlchemyUserDatastore, Security 3 | from application.models import db, User, Role 4 | from config import DevelopmentConfig 5 | from application.resources import api 6 | from application.sec import datastore 7 | from application.worker import celery_init_app 8 | import flask_excel as excel 9 | from celery.schedules import crontab 10 | from application.tasks import daily_reminder 11 | from application.instances import cache 12 | 13 | 14 | def create_app(): 15 | app = Flask(__name__) 16 | app.config.from_object(DevelopmentConfig) 17 | db.init_app(app) 18 | api.init_app(app) 19 | excel.init_excel(app) 20 | app.security = Security(app, datastore) 21 | cache.init_app(app) 22 | with app.app_context(): 23 | import application.views 24 | 25 | return app 26 | 27 | 28 | app = create_app() 29 | celery_app = celery_init_app(app) 30 | 31 | 32 | @celery_app.on_after_configure.connect 33 | def send_email(sender, **kwargs): 34 | sender.add_periodic_task( 35 | crontab(hour=19, minute=55, day_of_month=20), 36 | daily_reminder.s('narendra@email.com', 'Daily Test'), 37 | ) 38 | 39 | 40 | if __name__ == '__main__': 41 | app.run(debug=True) 42 | -------------------------------------------------------------------------------- /upload_initial_data.py: -------------------------------------------------------------------------------- 1 | from main import app 2 | from application.sec import datastore 3 | from application.models import db, Role 4 | from flask_security import hash_password 5 | from werkzeug.security import generate_password_hash 6 | 7 | with app.app_context(): 8 | db.create_all() 9 | datastore.find_or_create_role(name="admin", description="User is an admin") 10 | datastore.find_or_create_role( 11 | name="inst", description="User is an Instructor") 12 | datastore.find_or_create_role(name="stud", description="User is a Student") 13 | db.session.commit() 14 | if not datastore.find_user(email="admin@email.com"): 15 | datastore.create_user( 16 | email="admin@email.com", password=generate_password_hash("admin"), roles=["admin"]) 17 | if not datastore.find_user(email="inst1@email.com"): 18 | datastore.create_user( 19 | email="inst1@email.com", password=generate_password_hash("inst1"), roles=["inst"], active=False) 20 | if not datastore.find_user(email="stud1@email.com"): 21 | datastore.create_user( 22 | email="stud1@email.com", password=generate_password_hash("stud1"), roles=["stud"]) 23 | if not datastore.find_user(email="stud2@email.com"): 24 | datastore.create_user( 25 | email="stud2@email.com", password=generate_password_hash("stud2"), roles=["stud"]) 26 | 27 | db.session.commit() 28 | -------------------------------------------------------------------------------- /static/components/Login.js: -------------------------------------------------------------------------------- 1 | export default { 2 | template: ` 3 |
4 |
5 |
*{{error}}
6 | 7 | 8 | 9 | 10 | 11 |
12 |
13 | `, 14 | data() { 15 | return { 16 | cred: { 17 | email: null, 18 | password: null, 19 | }, 20 | error: null, 21 | } 22 | }, 23 | methods: { 24 | async login() { 25 | const res = await fetch('/user-login', { 26 | method: 'POST', 27 | headers: { 28 | 'Content-Type': 'application/json', 29 | }, 30 | body: JSON.stringify(this.cred), 31 | }) 32 | const data = await res.json() 33 | if (res.ok) { 34 | localStorage.setItem('auth-token', data.token) 35 | localStorage.setItem('role', data.role) 36 | this.$router.push({ path: '/' }) 37 | } else { 38 | this.error = data.message 39 | } 40 | }, 41 | }, 42 | } 43 | -------------------------------------------------------------------------------- /static/components/Navbar.js: -------------------------------------------------------------------------------- 1 | export default { 2 | template: ` 3 | `, 27 | data() { 28 | return { 29 | role: localStorage.getItem('role'), 30 | is_login: localStorage.getItem('auth-token'), 31 | } 32 | }, 33 | methods: { 34 | logout() { 35 | localStorage.removeItem('auth-token') 36 | localStorage.removeItem('role') 37 | this.$router.push({ path: '/login' }) 38 | }, 39 | }, 40 | } 41 | -------------------------------------------------------------------------------- /application/models.py: -------------------------------------------------------------------------------- 1 | from flask_sqlalchemy import SQLAlchemy 2 | from flask_security import UserMixin, RoleMixin 3 | db = SQLAlchemy() 4 | 5 | class RolesUsers(db.Model): 6 | __tablename__ = 'roles_users' 7 | id = db.Column(db.Integer(), primary_key=True) 8 | user_id = db.Column('user_id', db.Integer(), db.ForeignKey('user.id')) 9 | role_id = db.Column('role_id', db.Integer(), db.ForeignKey('role.id')) 10 | 11 | class User(db.Model, UserMixin): 12 | id = db.Column(db.Integer, primary_key=True) 13 | username = db.Column(db.String, unique=False) 14 | email = db.Column(db.String, unique=True) 15 | password = db.Column(db.String(255)) 16 | active = db.Column(db.Boolean()) 17 | fs_uniquifier = db.Column(db.String(255), unique=True, nullable=False) 18 | roles = db.relationship('Role', secondary='roles_users', 19 | backref=db.backref('users', lazy='dynamic')) 20 | study_resource = db.relationship('StudyResource', backref='creator') 21 | 22 | class Role(db.Model, RoleMixin): 23 | id = db.Column(db.Integer(), primary_key=True) 24 | name = db.Column(db.String(80), unique=True) 25 | description = db.Column(db.String(255)) 26 | 27 | class StudyResource(db.Model): 28 | id = db.Column(db.Integer, primary_key=True) 29 | topic = db.Column(db.String, nullable=False) 30 | description = db.Column(db.String, nullable=False) 31 | creator_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False) 32 | resource_link = db.Column(db.String, nullable=False) 33 | is_approved = db.Column(db.Boolean(), default=False) 34 | -------------------------------------------------------------------------------- /application/resources.py: -------------------------------------------------------------------------------- 1 | from flask_restful import Resource, Api, reqparse, fields, marshal 2 | from flask_security import auth_required, roles_required, current_user 3 | from flask import jsonify 4 | from sqlalchemy import or_ 5 | from .models import StudyResource, db 6 | from .instances import cache 7 | 8 | 9 | api = Api(prefix='/api') 10 | 11 | parser = reqparse.RequestParser() 12 | parser.add_argument('topic', type=str, 13 | help='Topic is required should be a string', required=True) 14 | parser.add_argument('description', type=str, 15 | help='Description is required and should be a string', required=True) 16 | parser.add_argument('resource_link', type=str, 17 | help='Resource Link is required and should be a string', required=True) 18 | 19 | 20 | class Creator(fields.Raw): 21 | def format(self, user): 22 | return user.email 23 | 24 | 25 | study_material_fields = { 26 | 'id': fields.Integer, 27 | 'topic': fields.String, 28 | 'description': fields.String, 29 | 'resource_link': fields.String, 30 | 'is_approved': fields.Boolean, 31 | 'creator': Creator 32 | } 33 | 34 | 35 | class StudyMaterial(Resource): 36 | @auth_required("token") 37 | @cache.cached(timeout=50) 38 | def get(self): 39 | if "inst" in current_user.roles: 40 | study_resources = StudyResource.query.all() 41 | else: 42 | study_resources = StudyResource.query.filter( 43 | or_(StudyResource.is_approved == True, StudyResource.creator == current_user)).all() 44 | if len(study_resources) > 0: 45 | return marshal(study_resources, study_material_fields) 46 | else: 47 | return {"message": "No Resourse Found"}, 404 48 | 49 | @auth_required("token") 50 | @roles_required("stud") 51 | def post(self): 52 | args = parser.parse_args() 53 | study_resource = StudyResource(topic=args.get("topic"), description=args.get( 54 | "description"), resource_link=args.get("resource_link"), creator_id=current_user.id) 55 | db.session.add(study_resource) 56 | db.session.commit() 57 | return {"message": "Study Resource Created"} 58 | 59 | 60 | api.add_resource(StudyMaterial, '/study_material') 61 | -------------------------------------------------------------------------------- /application/views.py: -------------------------------------------------------------------------------- 1 | from flask import current_app as app, jsonify, request, render_template, send_file 2 | from flask_security import auth_required, roles_required 3 | from werkzeug.security import check_password_hash 4 | from flask_restful import marshal, fields 5 | import flask_excel as excel 6 | from celery.result import AsyncResult 7 | from .tasks import create_resource_csv 8 | from .models import User, db, StudyResource 9 | from .sec import datastore 10 | 11 | 12 | @app.get('/') 13 | def home(): 14 | return render_template("index.html") 15 | 16 | 17 | @app.get('/admin') 18 | @auth_required("token") 19 | @roles_required("admin") 20 | def admin(): 21 | return "Hello Admin" 22 | 23 | 24 | @app.get('/activate/inst/') 25 | @auth_required("token") 26 | @roles_required("admin") 27 | def activate_instructor(inst_id): 28 | instructor = User.query.get(inst_id) 29 | if not instructor or "inst" not in instructor.roles: 30 | return jsonify({"message": "Instructor not found"}), 404 31 | 32 | instructor.active = True 33 | db.session.commit() 34 | return jsonify({"message": "User Activated"}) 35 | 36 | 37 | @app.post('/user-login') 38 | def user_login(): 39 | data = request.get_json() 40 | email = data.get('email') 41 | if not email: 42 | return jsonify({"message": "email not provided"}), 400 43 | 44 | user = datastore.find_user(email=email) 45 | 46 | if not user: 47 | return jsonify({"message": "User Not Found"}), 404 48 | 49 | if check_password_hash(user.password, data.get("password")): 50 | return jsonify({"token": user.get_auth_token(), "email": user.email, "role": user.roles[0].name}) 51 | else: 52 | return jsonify({"message": "Wrong Password"}), 400 53 | 54 | 55 | user_fields = { 56 | "id": fields.Integer, 57 | "email": fields.String, 58 | "active": fields.Boolean 59 | } 60 | 61 | 62 | @app.get('/users') 63 | @auth_required("token") 64 | @roles_required("admin") 65 | def all_users(): 66 | users = User.query.all() 67 | if len(users) == 0: 68 | return jsonify({"message": "No User Found"}), 404 69 | return marshal(users, user_fields) 70 | 71 | 72 | @app.get('/study-resource//approve') 73 | @auth_required("token") 74 | @roles_required("inst") 75 | def resource(id): 76 | study_resource = StudyResource.query.get(id) 77 | if not study_resource: 78 | return jsonify({"message": "Resource Not found"}), 404 79 | study_resource.is_approved = True 80 | db.session.commit() 81 | return jsonify({"message": "Aproved"}) 82 | 83 | 84 | @app.get('/download-csv') 85 | def download_csv(): 86 | task = create_resource_csv.delay() 87 | return jsonify({"task-id": task.id}) 88 | 89 | 90 | @app.get('/get-csv/') 91 | def get_csv(task_id): 92 | res = AsyncResult(task_id) 93 | if res.ready(): 94 | filename = res.result 95 | return send_file(filename, as_attachment=True) 96 | else: 97 | return jsonify({"message": "Task Pending"}), 404 98 | 99 | 100 | -------------------------------------------------------------------------------- /thunder-tests/thunderclient.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "_id": "2f44e6a6-2f06-49a6-81bb-96eb54030ba2", 4 | "colId": "d9eb3ada-cffc-4600-8ff4-d545c063e581", 5 | "containerId": "", 6 | "name": "create_study_resource", 7 | "url": "http://127.0.0.1:5000/api/study_material", 8 | "method": "POST", 9 | "sortNum": 10000, 10 | "created": "2023-10-09T13:49:25.556Z", 11 | "modified": "2023-10-16T12:41:54.196Z", 12 | "headers": [], 13 | "params": [], 14 | "body": { 15 | "type": "json", 16 | "raw": "{\n \"topic\": \"MAD1 Lecture1\",\n \"description\": \"Description for mad1\"\n}", 17 | "form": [] 18 | }, 19 | "tests": [] 20 | }, 21 | { 22 | "_id": "fe5a54cc-0d5f-4767-9073-3078ac959c24", 23 | "colId": "d9eb3ada-cffc-4600-8ff4-d545c063e581", 24 | "containerId": "", 25 | "name": "get_study_resource", 26 | "url": "http://127.0.0.1:5000/api/study_material", 27 | "method": "GET", 28 | "sortNum": 20000, 29 | "created": "2023-10-09T14:11:14.079Z", 30 | "modified": "2023-11-06T13:37:24.800Z", 31 | "headers": [ 32 | { 33 | "name": "Authentication-Token", 34 | "value": "WyJkOTQ0ZGNlMjUyNDU0NzUxOGQ4MGZhYzY1NzU0NTJjMCJd.ZUjsAQ.7VxGa5pZBnXPDbHqMRi_ozs7psc" 35 | } 36 | ], 37 | "params": [], 38 | "body": { 39 | "type": "json", 40 | "raw": "{\n \"email\":\"admin@email.com\",\n \"password\": \"admin\"\n}", 41 | "form": [] 42 | }, 43 | "tests": [] 44 | }, 45 | { 46 | "_id": "114728e6-a75e-492c-91b9-16bb1e693ecd", 47 | "colId": "d9eb3ada-cffc-4600-8ff4-d545c063e581", 48 | "containerId": "", 49 | "name": "login", 50 | "url": "http://127.0.0.1:5000/user-login", 51 | "method": "POST", 52 | "sortNum": 30000, 53 | "created": "2023-10-16T07:09:09.020Z", 54 | "modified": "2023-11-06T13:37:05.667Z", 55 | "headers": [], 56 | "params": [], 57 | "body": { 58 | "type": "json", 59 | "raw": "{\n \"email\":\"admin@email.com\",\n \"password\":\"admin\"\n}", 60 | "form": [] 61 | }, 62 | "tests": [] 63 | }, 64 | { 65 | "_id": "b747e51c-0ce4-4022-95c6-e60ca7729339", 66 | "colId": "d9eb3ada-cffc-4600-8ff4-d545c063e581", 67 | "containerId": "", 68 | "name": "admin", 69 | "url": "http://127.0.0.1:5000/admin", 70 | "method": "GET", 71 | "sortNum": 40000, 72 | "created": "2023-10-16T07:34:16.260Z", 73 | "modified": "2023-10-25T13:02:21.868Z", 74 | "headers": [ 75 | { 76 | "name": "Authentication-Token", 77 | "value": "WyJmOGM0ZWVjYzVmZTM0ZmEzYmQ0ZGRkMWYyNjUxM2E2YiJd.ZTkRaA.0Ocj-FHwg_pvzk1GSr5r4igUbPc" 78 | } 79 | ], 80 | "params": [], 81 | "tests": [] 82 | }, 83 | { 84 | "_id": "4f9574d8-b1dd-4eb0-a70a-8413f7896794", 85 | "colId": "d9eb3ada-cffc-4600-8ff4-d545c063e581", 86 | "containerId": "", 87 | "name": "activate_inst", 88 | "url": "http://127.0.0.1:5000/activate/inst/2", 89 | "method": "GET", 90 | "sortNum": 60000, 91 | "created": "2023-10-16T14:22:48.124Z", 92 | "modified": "2023-10-16T14:24:13.496Z", 93 | "headers": [ 94 | { 95 | "name": "Authentication-Token", 96 | "value": "WyI1MDE0MGEwZDYxMmI0NWExYTliYjIzMjgxYTI5MDE4MiJd.ZS1HGQ.OXQUrpm9Wzam9vny3BV17swIh1g" 97 | } 98 | ], 99 | "params": [], 100 | "tests": [] 101 | }, 102 | { 103 | "_id": "4cb0688e-6b2f-4d00-aae9-652dce32f339", 104 | "colId": "d9eb3ada-cffc-4600-8ff4-d545c063e581", 105 | "containerId": "", 106 | "name": "user-login", 107 | "url": "http://127.0.0.1:5000/user-login", 108 | "method": "POST", 109 | "sortNum": 70000, 110 | "created": "2023-10-25T12:57:39.452Z", 111 | "modified": "2023-10-25T12:58:32.787Z", 112 | "headers": [], 113 | "params": [], 114 | "body": { 115 | "type": "json", 116 | "raw": "{\n \"email\": \"admin@email.com\",\n \"password\": \"admin\"\n}", 117 | "form": [] 118 | }, 119 | "tests": [] 120 | } 121 | ] --------------------------------------------------------------------------------