├── 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: ``,
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 | ]
--------------------------------------------------------------------------------