├── .gitignore
├── LICENSE
├── README.md
├── flaskstarter
├── PBMC_study_meta
│ ├── Country ISO code List.xlsx
│ ├── PBMC_cell_type.xlsx
│ ├── PBMC_study_meta.csv
│ ├── PBMC_study_meta.xlsx
│ └── ~$PBMC_study_meta.xlsx
├── __init__.py
├── app.py
├── config.py
├── decorators.py
├── emails
│ └── __init__.py
├── extensions.py
├── frontend
│ ├── __init__.py
│ ├── forms.py
│ ├── models.py
│ └── views.py
├── model
│ ├── meta.py
│ └── umap.py
├── settings
│ ├── __init__.py
│ ├── forms.py
│ └── views.py
├── static
│ ├── D24H_Logo.png
│ ├── Dataset_Banner.jpeg
│ ├── Group 36.png
│ ├── Group 37.png
│ ├── Group 38.png
│ ├── Group.png
│ ├── Login_Banner.jpeg
│ ├── Mask_Group.png
│ ├── Pointer.png
│ ├── Vector.svg
│ ├── bootstrap.bundle.min.js
│ ├── bootstrap.min.css
│ ├── chevron-down.png
│ ├── chevron-down.svg
│ ├── css
│ │ └── template.css
│ ├── download-file 1.svg
│ ├── exclamation-mark-64.png
│ ├── explore.png
│ ├── facebook.svg
│ ├── folder-2.png
│ ├── folder-3.png
│ ├── folder.png
│ ├── instagram.svg
│ ├── jquery-3.6.0.min.js
│ ├── jquery.slim.min.js
│ ├── link icon.svg
│ ├── linkin.svg
│ ├── loading-icon.gif
│ ├── location icon.svg
│ ├── mail icon.svg
│ ├── microscope.png
│ ├── newspaper.png
│ ├── tel icon.svg
│ ├── twitter.svg
│ └── vimeo.svg
├── tasks
│ ├── .views.py.swp
│ ├── __init__.py
│ ├── forms.py
│ └── views.py
├── templates
│ ├── admin
│ │ └── index.html
│ ├── dashboard
│ │ └── dashboard.html
│ ├── frontend
│ │ ├── change_password.html
│ │ ├── contact_us.html
│ │ ├── data.html
│ │ ├── landing.html
│ │ ├── login.html
│ │ ├── reset_password.html
│ │ ├── signup.html
│ │ └── tutorial.html
│ ├── layouts
│ │ ├── banner.html
│ │ ├── base.html
│ │ ├── citation.html
│ │ ├── footer.html
│ │ └── header.html
│ ├── macros
│ │ ├── _confirm_account.html
│ │ ├── _flash_msg.html
│ │ ├── _form.html
│ │ └── _reset_password.html
│ ├── settings
│ │ ├── password.html
│ │ └── profile.html
│ └── tasks
│ │ ├── add_task.html
│ │ ├── backup.html
│ │ ├── contribute.html
│ │ ├── edit_task.html
│ │ ├── landing.html
│ │ ├── my_tasks.html
│ │ ├── show_plot.html
│ │ ├── show_scfeature.html
│ │ ├── show_search_plot.html
│ │ ├── table_view.html
│ │ └── view_task.html
├── user
│ ├── __init__.py
│ ├── constants.py
│ └── models.py
└── utils.py
├── gunicorn.conf.py
├── manage.py
├── requirements.txt
├── screenshots
├── Logo_gradient.png
├── admin.png
├── dashboard.png
├── homepage.png
├── login.png
├── logo.png
├── profile.png
├── signup.png
└── tasks.png
└── tests
├── __init__.py
└── test_flaskstarter.py
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | venv/
3 | *.sh
4 | __pycache__/
5 | db.sqlite
6 | cov192kaxis.csv
7 | .idea/
8 | *.log
9 | dump.rdb
10 | dump.rdb
11 | dump.rdb
12 | *.rdb
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2023 Danqing Yin and others
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining
4 | a copy of this software and associated documentation files (the
5 | "Software"), to deal in the Software without restriction, including
6 | without limitation the rights to use, copy, modify, merge, publish,
7 | distribute, sublicense, and/or sell copies of the Software, and to
8 | permit persons to whom the Software is furnished to do so, subject to
9 | the following conditions:
10 |
11 | The above copyright notice and this permission notice shall be
12 | included in all copies or substantial portions of the Software.
13 |
14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Scope+: Open Source Generalizable Architecture for Single-cell Atlases
2 |
3 |
', methods=['GET', 'POST'])
208 | def confirm_account(secretstring):
209 | s = URLSafeSerializer('serliaizer_code')
210 | uname, uemail = s.loads(secretstring)
211 | user = Users.query.filter_by(name=uname).first()
212 | user.status_code = ACTIVE
213 | db.session.add(user)
214 | db.session.commit()
215 | flash(u'Your account was confirmed succsessfully!!!', 'success')
216 | return redirect(url_for('frontend.login'))
217 |
218 |
219 | @frontend.route('/change_password', methods=['GET', 'POST'])
220 | def change_password():
221 |
222 | if current_user.is_authenticated:
223 | if not login_fresh():
224 | return login_manager.needs_refresh()
225 |
226 | form = ChangePasswordForm(email_activation_key=request.values["email_activation_key"],
227 | email=request.values["email"])
228 |
229 | if form.validate_on_submit():
230 | update_password(form.email.data, form.email_activation_key.data, form.password.data)
231 | flash(u"Your password has been changed, log in again", "success")
232 | return redirect(url_for("frontend.login"))
233 |
234 | return render_template("frontend/change_password.html", form=form)
235 |
236 |
237 | def update_password(email, email_activation_key, password):
238 | user = Users.query.filter_by(email_activation_key=email_activation_key, email=email).first()
239 | user.password = password
240 | user.email_activation_key = None
241 | db.session.add(user)
242 | db.session.commit()
243 |
244 |
245 | @frontend.route('/reset_password', methods=['GET', 'POST'])
246 | def reset_password():
247 | form = RecoverPasswordForm()
248 |
249 | if form.validate_on_submit():
250 | user = Users.query.filter_by(email=form.email.data).first()
251 |
252 | if user:
253 | flash('Please see your email for instructions on how to access your account', 'success')
254 |
255 | user.email_activation_key = str(uuid4())
256 | db.session.add(user)
257 | db.session.commit()
258 |
259 | subject = 'Reset your password in ' + current_app.config['PROJECT_NAME']
260 | url = url_for('frontend.change_password', email=user.email,
261 | email_activation_key=user.email_activation_key, _external=True)
262 | html = render_template('macros/_reset_password.html', project=current_app.config['PROJECT_NAME'],
263 | name=user.name, url=url)
264 |
265 | send_async_email(subject, html, user.email)
266 |
267 | return render_template('frontend/reset_password.html', form=form)
268 | else:
269 | flash('Sorry, no user found for that email address', 'danger')
270 |
271 | return render_template('frontend/reset_password.html', form=form)
272 |
273 |
274 | @frontend.route('/terms')
275 | def terms():
276 | return "To be updated soon.."
277 |
278 |
279 | @frontend.route('/about-us')
280 | def about_us():
281 | return "To be updated soon.."
282 |
--------------------------------------------------------------------------------
/flaskstarter/model/meta.py:
--------------------------------------------------------------------------------
1 | from wtforms import form, fields
2 | import flask_admin.contrib.pymongo as admin_py
3 | from flask_admin.contrib.pymongo import filters
4 |
5 | class MetaForm(form.Form):
6 | id = fields.StringField('id')
7 | meta_sample_id2 = fields.StringField('meta_sample_id2')
8 | meta_patient_id = fields.StringField('meta_patient_id')
9 | meta_age = fields.StringField('meta_age')
10 | level2 = fields.StringField('level2')
11 | meta_severity = fields.StringField('meta_severity')
12 | meta_dataset = fields.StringField('meta_dataset')
13 |
14 |
15 | # View
16 | class MetaView(admin_py.ModelView):
17 | column_list = ('id', 'meta_sample_id2', 'meta_patient_id', 'meta_age', 'level2', 'meta_severity', 'meta_dataset')
18 | form = MetaForm # Specifies Data Model
19 |
20 | column_sortable_list = ('meta_age', 'meta_patient_id')
21 |
22 | column_filters = (filters.FilterEqual('id', 'id'),
23 | filters.FilterLike('meta_age','meta_age'),
24 | filters.FilterEqual('meta_age','meta_age'),)
25 |
26 | column_searchable_list = ('id', 'meta_sample_id2')
--------------------------------------------------------------------------------
/flaskstarter/model/umap.py:
--------------------------------------------------------------------------------
1 | # Data Model
2 | from wtforms import form, fields
3 | import flask_admin.contrib.pymongo as admin_py
4 | from flask_admin.contrib.pymongo.filters import BooleanEqualFilter
5 |
6 | class UmapForm(form.Form):
7 | id = fields.StringField('id')
8 | UMAP1 = fields.StringField('UMAP1')
9 | UMAP2 = fields.StringField('UMAP2')
10 |
11 | # View
12 |
13 | class UmapView(admin_py.ModelView):
14 | column_list = ('id', 'UMAP1', 'UMAP2')
15 | form = UmapForm # Specifies Data Model
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/flaskstarter/settings/__init__.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | from .views import settings
4 |
--------------------------------------------------------------------------------
/flaskstarter/settings/forms.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | from flask_wtf import FlaskForm
4 | from wtforms.fields.html5 import EmailField
5 | from wtforms import ValidationError, TextField, PasswordField, SubmitField
6 | from wtforms.validators import (Required, Length, EqualTo, Email)
7 | from flask_login import current_user
8 |
9 | from ..user import Users
10 | from ..utils import PASSWORD_LEN_MIN, PASSWORD_LEN_MAX
11 |
12 |
13 | class ProfileForm(FlaskForm):
14 | multipart = True
15 | name = TextField(u'Name', [Length(max=50)])
16 | email = EmailField(u'Email', [Required(), Email()])
17 | submit = SubmitField(u'Update')
18 |
19 |
20 | class PasswordForm(FlaskForm):
21 | password = PasswordField('Current password', [Required()])
22 | new_password = PasswordField('New password', [Required(), Length(PASSWORD_LEN_MIN, PASSWORD_LEN_MAX)])
23 | password_again = PasswordField('Password again', [Required(), Length(PASSWORD_LEN_MIN, PASSWORD_LEN_MAX), EqualTo('new_password')])
24 | submit = SubmitField(u'Update')
25 |
26 | def validate_password(form, field):
27 | user = Users.get_by_id(current_user.id)
28 | if not user.check_password(field.data):
29 | raise ValidationError("Password is wrong")
30 |
--------------------------------------------------------------------------------
/flaskstarter/settings/views.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | from flask import Blueprint, render_template, request, flash
4 | from flask_login import login_required, current_user
5 |
6 | from ..extensions import db
7 | from ..user import Users
8 | from .forms import ProfileForm, PasswordForm
9 |
10 |
11 | settings = Blueprint('settings', __name__, url_prefix='/settings')
12 |
13 |
14 | @settings.route('/profile', methods=['GET', 'POST'])
15 | @login_required
16 | def profile():
17 | user = Users.query.filter_by(email=current_user.email).first_or_404()
18 | form = ProfileForm(obj=user,
19 | email=current_user.email,
20 | role_code=current_user.role_code,
21 | status_code=current_user.status_code)
22 |
23 | if form.validate_on_submit():
24 |
25 | form.populate_obj(user)
26 |
27 | db.session.add(user)
28 | db.session.commit()
29 |
30 | flash('Profile Changes Saved!', 'success')
31 |
32 | return render_template('settings/profile.html',
33 | user=user, active="profile", form=form)
34 |
35 |
36 | @settings.route('/password', methods=['GET', 'POST'])
37 | @login_required
38 | def password():
39 | user = Users.query.filter_by(email=current_user.email).first_or_404()
40 | form = PasswordForm(next=request.args.get('next'))
41 |
42 | if form.validate_on_submit():
43 | form.populate_obj(user)
44 | user.password = form.new_password.data
45 |
46 | db.session.add(user)
47 | db.session.commit()
48 |
49 | flash('Password updated.', 'success')
50 |
51 | return render_template('settings/password.html',
52 | user=user, active="password", form=form)
53 |
--------------------------------------------------------------------------------
/flaskstarter/static/D24H_Logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hiyin/scopeplus/3d0f9f74d966390f101fabb38d1952f5e8c90ba6/flaskstarter/static/D24H_Logo.png
--------------------------------------------------------------------------------
/flaskstarter/static/Dataset_Banner.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hiyin/scopeplus/3d0f9f74d966390f101fabb38d1952f5e8c90ba6/flaskstarter/static/Dataset_Banner.jpeg
--------------------------------------------------------------------------------
/flaskstarter/static/Group 36.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hiyin/scopeplus/3d0f9f74d966390f101fabb38d1952f5e8c90ba6/flaskstarter/static/Group 36.png
--------------------------------------------------------------------------------
/flaskstarter/static/Group 37.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hiyin/scopeplus/3d0f9f74d966390f101fabb38d1952f5e8c90ba6/flaskstarter/static/Group 37.png
--------------------------------------------------------------------------------
/flaskstarter/static/Group 38.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hiyin/scopeplus/3d0f9f74d966390f101fabb38d1952f5e8c90ba6/flaskstarter/static/Group 38.png
--------------------------------------------------------------------------------
/flaskstarter/static/Group.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hiyin/scopeplus/3d0f9f74d966390f101fabb38d1952f5e8c90ba6/flaskstarter/static/Group.png
--------------------------------------------------------------------------------
/flaskstarter/static/Login_Banner.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hiyin/scopeplus/3d0f9f74d966390f101fabb38d1952f5e8c90ba6/flaskstarter/static/Login_Banner.jpeg
--------------------------------------------------------------------------------
/flaskstarter/static/Mask_Group.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hiyin/scopeplus/3d0f9f74d966390f101fabb38d1952f5e8c90ba6/flaskstarter/static/Mask_Group.png
--------------------------------------------------------------------------------
/flaskstarter/static/Pointer.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hiyin/scopeplus/3d0f9f74d966390f101fabb38d1952f5e8c90ba6/flaskstarter/static/Pointer.png
--------------------------------------------------------------------------------
/flaskstarter/static/Vector.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/flaskstarter/static/chevron-down.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hiyin/scopeplus/3d0f9f74d966390f101fabb38d1952f5e8c90ba6/flaskstarter/static/chevron-down.png
--------------------------------------------------------------------------------
/flaskstarter/static/chevron-down.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/flaskstarter/static/download-file 1.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/flaskstarter/static/exclamation-mark-64.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hiyin/scopeplus/3d0f9f74d966390f101fabb38d1952f5e8c90ba6/flaskstarter/static/exclamation-mark-64.png
--------------------------------------------------------------------------------
/flaskstarter/static/explore.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hiyin/scopeplus/3d0f9f74d966390f101fabb38d1952f5e8c90ba6/flaskstarter/static/explore.png
--------------------------------------------------------------------------------
/flaskstarter/static/facebook.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/flaskstarter/static/folder-2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hiyin/scopeplus/3d0f9f74d966390f101fabb38d1952f5e8c90ba6/flaskstarter/static/folder-2.png
--------------------------------------------------------------------------------
/flaskstarter/static/folder-3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hiyin/scopeplus/3d0f9f74d966390f101fabb38d1952f5e8c90ba6/flaskstarter/static/folder-3.png
--------------------------------------------------------------------------------
/flaskstarter/static/folder.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hiyin/scopeplus/3d0f9f74d966390f101fabb38d1952f5e8c90ba6/flaskstarter/static/folder.png
--------------------------------------------------------------------------------
/flaskstarter/static/link icon.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/flaskstarter/static/linkin.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/flaskstarter/static/loading-icon.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hiyin/scopeplus/3d0f9f74d966390f101fabb38d1952f5e8c90ba6/flaskstarter/static/loading-icon.gif
--------------------------------------------------------------------------------
/flaskstarter/static/location icon.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/flaskstarter/static/mail icon.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/flaskstarter/static/microscope.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hiyin/scopeplus/3d0f9f74d966390f101fabb38d1952f5e8c90ba6/flaskstarter/static/microscope.png
--------------------------------------------------------------------------------
/flaskstarter/static/newspaper.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hiyin/scopeplus/3d0f9f74d966390f101fabb38d1952f5e8c90ba6/flaskstarter/static/newspaper.png
--------------------------------------------------------------------------------
/flaskstarter/static/tel icon.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/flaskstarter/tasks/.views.py.swp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hiyin/scopeplus/3d0f9f74d966390f101fabb38d1952f5e8c90ba6/flaskstarter/tasks/.views.py.swp
--------------------------------------------------------------------------------
/flaskstarter/tasks/__init__.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | from .views import tasks
4 | from .forms import MyTaskForm
--------------------------------------------------------------------------------
/flaskstarter/tasks/forms.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | from flask_wtf import FlaskForm
4 | from wtforms import SubmitField, TextAreaField, SelectField
5 | from wtforms.validators import Required, Length
6 |
7 | class MyTaskForm(FlaskForm):
8 | task = TextAreaField(u'Your Task', [Required(), Length(5, 2048)])
9 | submit = SubmitField(u'Save Task')
10 |
11 | class UmapForm(FlaskForm):
12 | def __init__(self,choices=[('def','Default')]):
13 | self.cell_color = SelectField('cell_color',choices=choices)
--------------------------------------------------------------------------------
/flaskstarter/templates/admin/index.html:
--------------------------------------------------------------------------------
1 | {% extends 'admin/master.html' %}
2 |
3 | {% block body %}
4 | Hello, Welcome to Flask-Starter's Admin Dashboard
5 | {% endblock %}
--------------------------------------------------------------------------------
/flaskstarter/templates/dashboard/dashboard.html:
--------------------------------------------------------------------------------
1 | {% from "macros/_form.html" import render_form %}
2 |
3 | {% set page_title = 'Dashboard' %}
4 |
5 | {% extends 'layouts/base.html' %}
6 |
7 | {% block body %}
8 |
15 | {% endblock %}
16 |
--------------------------------------------------------------------------------
/flaskstarter/templates/frontend/change_password.html:
--------------------------------------------------------------------------------
1 | {% from "macros/_form.html" import render_form %}
2 |
3 | {% set page_title = 'Change Password' %}
4 |
5 | {% extends 'layouts/base.html' %}
6 |
7 | {% block body %}
8 |
9 |
10 |
11 | {{ render_form(url_for('frontend.change_password'), form)}}
12 |
13 |
14 |
15 | {% endblock %}
--------------------------------------------------------------------------------
/flaskstarter/templates/frontend/contact_us.html:
--------------------------------------------------------------------------------
1 | {% from "macros/_form.html" import render_form %}
2 |
3 | {% set page_title = 'Contact us' %}
4 |
5 | {% extends 'layouts/base.html' %}
6 |
7 | {% block body %}
8 |
9 |
10 |
Contact us
11 |
12 |
13 |
For enquiries, comments or collaboration opportunities,
14 | please feel free to contact us through the following ways.
15 |
16 |
17 |
73 |
74 |
75 |
82 | {% endblock %}
--------------------------------------------------------------------------------
/flaskstarter/templates/frontend/landing.html:
--------------------------------------------------------------------------------
1 | {% from "macros/_form.html" import render_form %}
2 |
3 | {% set page_title = 'Home' %}
4 |
5 | {% extends 'layouts/base.html' %}
6 |
7 | {% block body %}
8 | {% include 'layouts/banner.html' %}
9 |
10 |
11 |
Home
12 |
13 |
14 |
15 | {% endblock %}
16 |
17 | {% block scripts %}
18 |
19 |
20 |
21 |
22 | {% endblock %}
--------------------------------------------------------------------------------
/flaskstarter/templates/frontend/login.html:
--------------------------------------------------------------------------------
1 | {% from "macros/_form.html" import render_form %}
2 |
3 | {% set page_title = 'Login' %}
4 |
5 | {% extends 'layouts/base.html' %}
6 |
7 | {% block body %}
8 |
9 |
10 |
11 |
12 |
Sign in
13 |
14 | {{ render_form(url_for('frontend.login'), form)}}
15 |
16 |
Don't have an account? Sign up
18 |
19 |
22 |
23 |
24 |
25 |
26 |
27 |
28 | {% endblock %}
--------------------------------------------------------------------------------
/flaskstarter/templates/frontend/reset_password.html:
--------------------------------------------------------------------------------
1 | {% from "macros/_form.html" import render_form %}
2 |
3 | {% set page_title = 'Reset Password' %}
4 |
5 | {% extends 'layouts/base.html' %}
6 |
7 | {% block body %}
8 |
9 |
10 |
11 | {{ render_form(url_for('frontend.reset_password'), form)}}
12 |
13 |
14 |
15 | {% endblock %}
--------------------------------------------------------------------------------
/flaskstarter/templates/frontend/signup.html:
--------------------------------------------------------------------------------
1 | {% from "macros/_form.html" import render_form %}
2 |
3 | {% set page_title = 'Sign up' %}
4 |
5 | {% extends 'layouts/base.html' %}
6 |
7 | {% block body %}
8 |
9 |
10 |
Sign up
11 | {{ render_form(url_for('frontend.signup'), form)}}
12 |
13 |
14 | {% endblock %}
--------------------------------------------------------------------------------
/flaskstarter/templates/frontend/tutorial.html:
--------------------------------------------------------------------------------
1 | {% set page_title = 'Tutorial' %}
2 |
3 | {% extends 'layouts/base.html' %}
4 |
5 | {% block body %}
6 |
7 |
8 |
11 |
12 |
13 |
Guide to use Covidscope Webserver(Video)
14 |
15 | VIDEO
16 |
17 |
19 |
20 | Also find the down-stream analysis tutorial in following link after you downloaded data from Covidscope ....
21 |
22 |
26 |
27 |
28 |
29 |
How to...
30 |
32 |
33 | If you want to ....
34 |
35 |
38 |
39 |
40 |
41 |
How to...
42 |
44 |
45 | If you want to ....
46 |
47 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 | {% endblock %}
--------------------------------------------------------------------------------
/flaskstarter/templates/layouts/banner.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | COVIDSCOPE
6 |
7 |
8 |
9 |
Explore single-cell immunology
10 |
11 |
12 |
13 |
14 |
15 |
of SARS-CoV-2 infection
16 |
17 |
18 |
19 |
20 |
21 | Continuously updated | Standardized | Download-ready | Open data sets
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
50000000
33 |
34 |
Cells
35 |
36 |
37 |
38 |
39 |
1000
40 |
41 |
Donors
42 |
43 |
44 |
45 |
46 |
1
47 |
48 |
Data sets
49 |
50 |
51 |
52 |
53 |
Oct, 2022
54 |
55 |
Data date processed
56 |
57 |
58 |
59 |
60 |
61 |
62 |
--------------------------------------------------------------------------------
/flaskstarter/templates/layouts/base.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 | {% block title %}{{ page_title|default('Flask-Starter') }}{% endblock %} - Covidscope
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
25 |
26 |
28 |
29 |
30 |
31 |
32 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
44 |
46 |
47 |
48 | {% include 'layouts/header.html'%}
49 |
50 |
51 | {% include 'macros/_flash_msg.html' %}
52 |
53 | {% block body %}
54 |
55 | {% endblock %}
56 |
57 |
59 |
60 |
61 |
62 |
64 |
66 |
67 |
68 |
69 |
70 |
71 |
73 |
74 | {% block scripts %}{% endblock %}
75 |
76 |
77 | {% include 'layouts/footer.html' %}
78 |
79 |
--------------------------------------------------------------------------------
/flaskstarter/templates/layouts/citation.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | APA 7th edition
5 |
6 |
7 |
8 |
9 |
10 | 2.5 million Single Cell (2022, February 23). The Laboratory of Data Discovery for Health.
11 |
12 |
Copy Citation
14 |
15 |
16 |
17 |
18 | https://www.d24.hk/
19 |
20 |
21 | Citation
22 |
23 |
24 |
--------------------------------------------------------------------------------
/flaskstarter/templates/layouts/footer.html:
--------------------------------------------------------------------------------
1 |
94 |
95 | {% block scripts %}
96 |
97 | {% endblock %}
--------------------------------------------------------------------------------
/flaskstarter/templates/layouts/header.html:
--------------------------------------------------------------------------------
1 | {% if current_user.is_authenticated %}
2 |
38 |
39 | {% else %}
40 |
75 | {% endif %}
--------------------------------------------------------------------------------
/flaskstarter/templates/macros/_confirm_account.html:
--------------------------------------------------------------------------------
1 | You have created an account on {{ project }}.
2 |
3 | To activate your account, click on the link below (or copy and paste the URL into your browser):
4 |
5 | Click to Activate
6 |
7 | Best Wishes,
8 | {{ project }} Team
9 |
--------------------------------------------------------------------------------
/flaskstarter/templates/macros/_flash_msg.html:
--------------------------------------------------------------------------------
1 | {% with messages = get_flashed_messages(with_categories=true) %}
2 | {% if messages %}
3 | {% for category, message in messages %}
4 |
5 | {{ message }} ×
6 |
7 | {% endfor %}
8 | {% endif %}
9 | {% endwith %}
--------------------------------------------------------------------------------
/flaskstarter/templates/macros/_form.html:
--------------------------------------------------------------------------------
1 | {% macro render_checkbox(field) %}
2 |
3 |
4 | {{ field }}{{ field.label.text }}
5 |
6 |
{{ field.description }}
7 | {% if field.errors -%}
8 |
9 | {% for error in field.errors -%}
10 | {{ error|e }}
11 | {%- endfor %}
12 |
13 | {%- endif %}
14 |
15 | {% endmacro%}
16 |
17 | {% macro render_radio(field) %}
18 |
36 | {% endmacro %}
37 |
38 | {% macro render_datepicker(field) %}
39 |
56 | {% endmacro %}
57 |
58 | {% macro render_textarea(field) %}
59 |
76 | {% endmacro %}
77 |
78 | {% macro render_file(field) %}
79 |
96 | {% endmacro %}
97 |
98 | {% macro render_input(field) %}
99 |
112 | {% endmacro %}
113 |
114 | {% macro render_action(field) %}
115 |
120 | {% endmacro %}
121 |
122 | {% macro render_form(url, form, horizontal=False, legend=None, confirm_msg=None, formid=None) %}
123 | {% set idattr = "id=" + formid if formid else "" %}
124 |
159 | {% endmacro %}
160 |
--------------------------------------------------------------------------------
/flaskstarter/templates/macros/_reset_password.html:
--------------------------------------------------------------------------------
1 | Forgot your password, {{ name }}?
2 |
3 | {{ project }} received a request to reset the password for your {{ project }} account {{ name }}.
4 |
5 | If you want to reset your password, click on the link below (or copy and paste the URL into your browser):
6 |
7 | Click to Reset Password
8 |
9 | This link takes you to a secure page where you can change your password.
10 |
11 | If you don't want to reset your password, please ignore this message. Your password will not be reset. If you have any concerns, please contact us at {{ project }} Support.
12 |
13 | Best Wishes,
14 | {{ project }} Team
15 |
--------------------------------------------------------------------------------
/flaskstarter/templates/settings/password.html:
--------------------------------------------------------------------------------
1 | {% from "macros/_form.html" import render_form %}
2 |
3 | {% set page_title = 'Update Password' %}
4 |
5 | {% extends 'layouts/base.html' %}
6 |
7 | {% block body %}
8 |
9 |
10 |
Password
11 | {{ render_form(url_for('settings.password'), form)}}
12 |
13 |
14 | {% include 'layouts/footer.html' %}
15 | {% endblock %}
--------------------------------------------------------------------------------
/flaskstarter/templates/settings/profile.html:
--------------------------------------------------------------------------------
1 | {% from "macros/_form.html" import render_form %}
2 |
3 | {% set page_title = 'Update Profile' %}
4 |
5 | {% extends 'layouts/base.html' %}
6 |
7 | {% block body %}
8 |
9 |
10 |
Profile
11 | {{ render_form(url_for('settings.profile'), form)}}
12 |
13 |
14 | {% include 'layouts/footer.html' %}
15 | {% endblock %}
--------------------------------------------------------------------------------
/flaskstarter/templates/tasks/add_task.html:
--------------------------------------------------------------------------------
1 | {% from "macros/_form.html" import render_form %}
2 |
3 | {% set page_title = 'Add Task' %}
4 |
5 | {% extends 'layouts/base.html' %}
6 |
7 | {% block body %}
8 | {% include 'layouts/banner.html' %}
9 |
10 |
Contribute
11 |
12 | 5.0 million single cell is build for the benefit of scientific community in health sector. Our team welcomes contributions of raw data of PBMC Single Cells, as long as it is consented for open access to the public.
13 |
14 |
15 |
Data Submission Process
16 |
We currenly accept csv form of dataset. Please follow the following steps to upload data.
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 | Prepare metadata
28 |
29 |
30 |
31 | Upload data
32 |
33 |
34 |
35 | See and share
36 |
37 |
38 |
39 | in communication with our team
40 |
41 | to a secure data storage area
42 |
43 | accessioned data on the portal
44 |
45 |
46 |
47 |
48 |
51 |
52 |
53 | {% include 'layouts/footer.html' %}
54 | {% endblock %}
--------------------------------------------------------------------------------
/flaskstarter/templates/tasks/contribute.html:
--------------------------------------------------------------------------------
1 | {% from "macros/_form.html" import render_form %}
2 |
3 | {% set page_title = 'Contribute' %}
4 |
5 | {% extends 'layouts/base.html' %}
6 |
7 | {% block body %}
8 |
9 |
10 |
11 |
12 |
Upload your data
13 |
Please upload file in xls or
14 | csv format.
15 |
16 |
27 |
28 | Upload user files
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
D²4H Single-cell Data Share Life Cycle
38 |
39 | The D²4H Single-cell Data Share provides a platform for sharing
40 | single-cell data. We welcome anyone to contribute on our data sharing
41 | coummuniaty.
42 |
43 |
77 |
78 |
79 |
80 |
81 |
85 |
89 |
90 |
91 |
92 |
93 | {% endblock %}
94 | {% block scripts %}
95 |
100 | {% endblock %}
--------------------------------------------------------------------------------
/flaskstarter/templates/tasks/edit_task.html:
--------------------------------------------------------------------------------
1 | {% from "macros/_form.html" import render_form %}
2 |
3 | {% set page_title = 'Edit Task' %}
4 |
5 | {% extends 'layouts/base.html' %}
6 |
7 | {% block body %}
8 |
9 |
10 |
11 | {{ render_form(url_for('tasks.edit_task', id=task.id), form)}}
12 |
13 |
14 |
15 | {% endblock %}
--------------------------------------------------------------------------------
/flaskstarter/templates/tasks/landing.html:
--------------------------------------------------------------------------------
1 | {% from "macros/_form.html" import render_form %} {% set page_title = 'Home' %}
2 | {% extends 'layouts/base.html' %} {% block body %} {% include
3 | 'layouts/banner.html' %}
4 |
5 |
6 |
7 |
8 |
What is Covidscope?
9 |
10 | Covidscope stores and provides over 4.8 million
11 | single-cell data from 20 published single-cell sequencing data sets. Anyone
12 | can explore the insights from our analysis and download the data provided
13 | here for further analysis.
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 | Insight
33 |
34 |
35 |
36 | Explore data
37 |
38 |
39 |
40 | Download and analyse
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
🍪 Do you like cookies?
51 |
We use cookies to ensure you get the best experience on our website.
52 |
56 |
57 |
58 |
59 |
60 |
61 |
D²4H Single-cell Data Share Life Cycle
62 |
63 | The D²4H Single-cell Data Share provides a platform for sharing
64 | single-cell data. We welcome anyone to contribute on our data sharing
65 | coummunity.
66 |
67 |
101 |
102 |
103 | {% endblock %}
104 |
105 | {% block scripts %}
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
132 |
251 |
252 |
357 | {% endblock %}
--------------------------------------------------------------------------------
/flaskstarter/templates/tasks/my_tasks.html:
--------------------------------------------------------------------------------
1 | {% from "macros/_form.html" import render_form %}
2 |
3 | {% set page_title = 'Explore' %}
4 |
5 | {% extends 'layouts/base.html' %}
6 |
7 | {% block body %}
8 | {% include 'layouts/banner.html' %}
9 |
10 |
11 |
Explore Data
12 |
13 |
14 |
15 |
Fliter    
16 |
17 |
20 |
21 |
Donor
23 |
24 |
Cell Type
26 |
27 |
File
29 |
30 |
300 |
301 |
302 |
303 |
307 |
308 |
309 |
310 | {% endblock %}
311 |
312 |
313 | {% block scripts %}
314 |
333 |
347 |
356 |
365 |
374 | {% endblock %}
--------------------------------------------------------------------------------
/flaskstarter/templates/tasks/show_plot.html:
--------------------------------------------------------------------------------
1 | {% from "macros/_form.html" import render_form %}
2 |
3 | {% set page_title = 'UMAP Plot' %}
4 |
5 | {% extends 'layouts/base.html' %}
6 |
7 | {% block body %}
8 |
9 |
10 |
13 |
Loading...
14 |
15 |
16 |
17 |
18 |
21 |
22 |
30 |
32 | Generate
33 |
34 |
42 |
44 | Select
45 |
46 |
47 |
48 |
49 |
50 |
51 |
54 |
57 |
58 |
59 |
62 |
65 |
66 |
67 |
68 |
69 | {% endblock %}
70 |
71 |
72 |
73 | {% block scripts %}
74 |
75 |
85 |
86 |
87 |
93 |
94 |
95 |
101 |
102 |
103 |
108 |
109 | {% endblock %}
--------------------------------------------------------------------------------
/flaskstarter/templates/tasks/show_scfeature.html:
--------------------------------------------------------------------------------
1 | {% from "macros/_form.html" import render_form %}
2 |
3 | {% set page_title = 'ScFeature' %}
4 |
5 | {% extends 'layouts/base.html' %}
6 |
7 | {% block body %}
8 |
9 |
10 |
13 |
Loading...
14 |
15 |
16 |
17 |
18 |
19 |
scFeature plots
20 |
21 |
33 |
34 |
35 |
36 |
44 |
45 |
47 | {% for val in celltypes %}
48 | {{val}}
49 | {% endfor %}
50 |
51 |
52 |
53 |
54 |
56 | {% for val in features %}
57 | {{val}}
58 | {% endfor %}
59 |
60 |
61 |
62 |
64 | Display
65 |
66 |
67 |
68 |
69 |
70 |
71 |
74 |
78 |
79 |
80 |
83 |
84 |
87 |
88 |
89 | Patient status:
90 | ◼ Healthy
91 | ◼ Mild/Moderate
92 | ◼ Severe/Critical
93 | ◼ Others
94 | ◼ NA
95 |
96 |
97 |
98 |
101 |
102 |
103 | Patient status:
104 | ◼ Healthy
105 | ◼ Mild/Moderate
106 | ◼ Severe/Critical
107 | ◼ Others
108 | ◼ NA
109 |
110 |
111 |
112 |
113 |
116 |
117 |
118 |
119 |
120 |
121 |
122 | {% endblock %}
123 |
124 |
125 |
126 | {% block scripts %}
127 |
128 |
138 |
139 |
140 |
146 |
147 |
148 |
155 |
156 |
157 |
163 |
164 |
165 |
172 |
173 |
174 |
179 |
180 |
185 |
186 |
187 |
196 |
197 |
198 | {% endblock %}
--------------------------------------------------------------------------------
/flaskstarter/templates/tasks/show_search_plot.html:
--------------------------------------------------------------------------------
1 | {% from "macros/_form.html" import render_form %}
2 |
3 | {% set page_title = 'Cell count meta' %}
4 |
5 | {% extends 'layouts/base.html' %}
6 |
7 | {% block body %}
8 |
9 |
10 |
11 |
12 |
13 |
16 |
17 |
18 |
19 |
Barplot of Cell Type Counts
20 |
21 |
24 |
25 |
26 |
27 |
Barplot of Severity Counts
28 |
29 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
Barplot of Disease Outcome Counts
39 |
40 |
43 |
44 |
45 |
46 |
Barplot of Gender Counts
47 |
48 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
Barplot of Age Counts
58 |
59 |
62 |
63 |
64 |
65 |
Histogram of Days from Onset of Symptoms
66 |
67 |
70 |
71 |
72 |
73 |
74 |
75 | {% endblock %}
76 |
77 | {% block scripts %}
78 |
79 |
85 |
86 |
92 |
93 |
99 |
100 |
106 |
112 |
113 |
119 |
120 | {% endblock %}
--------------------------------------------------------------------------------
/flaskstarter/templates/tasks/view_task.html:
--------------------------------------------------------------------------------
1 | {% from "macros/_form.html" import render_form %}
2 |
3 | {% set page_title = 'View Task' %}
4 |
5 | {% extends 'layouts/base.html' %}
6 |
7 | {% block body %}
8 |
9 |
10 |
11 |
12 |
13 |
{{ task.task }}
14 |
15 | {{ task.added_time | _pretty_date }}
16 |
17 |
18 |
19 |
20 |
21 |
22 | {% endblock %}
--------------------------------------------------------------------------------
/flaskstarter/user/__init__.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | from .models import Users, UsersAdmin
4 | from .constants import USER_ROLE, ADMIN, USER, USER_STATUS, NEW, ACTIVE
5 |
--------------------------------------------------------------------------------
/flaskstarter/user/constants.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | # User role
4 | ADMIN = 0
5 | STAFF = 1
6 | USER = 2
7 | USER_ROLE = {
8 | ADMIN: 'admin',
9 | STAFF: 'staff',
10 | USER: 'user',
11 | }
12 |
13 | # User status
14 | INACTIVE = 0
15 | NEW = 1
16 | ACTIVE = 2
17 | USER_STATUS = {
18 | INACTIVE: 'inactive',
19 | NEW: 'new',
20 | ACTIVE: 'active',
21 | }
22 |
23 |
24 | # Account Type
25 | BASIC = 0
26 | PRO = 1
27 | PREMIUM = 2
28 | ACCOUNT_TYPE = {
29 | BASIC: 'basic',
30 | PRO: 'pro',
31 | PREMIUM: 'premium'
32 | }
33 |
--------------------------------------------------------------------------------
/flaskstarter/user/models.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | from sqlalchemy import Column, types
4 | from sqlalchemy.ext.mutable import Mutable
5 | from werkzeug.security import generate_password_hash, check_password_hash
6 | from flask_login import UserMixin
7 |
8 | from ..extensions import db
9 | from ..utils import get_current_time, STRING_LEN
10 | from .constants import USER, USER_ROLE, ADMIN, INACTIVE, USER_STATUS
11 |
12 | from flask_login import current_user
13 | from flask_admin.contrib import sqla
14 |
15 |
16 | class DenormalizedText(Mutable, types.TypeDecorator):
17 | """
18 | Stores denormalized primary keys that can be
19 | accessed as a set.
20 |
21 | :param coerce: coercion function that ensures correct
22 | type is returned
23 |
24 | :param separator: separator character
25 | """
26 |
27 | impl = types.Text
28 |
29 | def __init__(self, coerce=int, separator=" ", **kwargs):
30 |
31 | self.coerce = coerce
32 | self.separator = separator
33 |
34 | super(DenormalizedText, self).__init__(**kwargs)
35 |
36 | def process_bind_param(self, value, dialect):
37 | if value is not None:
38 | items = [str(item).strip() for item in value]
39 | value = self.separator.join(item for item in items if item)
40 | return value
41 |
42 | def process_result_value(self, value, dialect):
43 | if not value:
44 | return set()
45 | return set(self.coerce(item) for item in value.split(self.separator))
46 |
47 | def copy_value(self, value):
48 | return set(value)
49 |
50 |
51 | class Users(db.Model, UserMixin):
52 |
53 | __tablename__ = 'users'
54 |
55 | id = Column(db.Integer, primary_key=True)
56 |
57 | name = Column(db.String(STRING_LEN))
58 |
59 | email = Column(db.String(STRING_LEN), unique=True)
60 | email_activation_key = Column(db.String(STRING_LEN))
61 |
62 | created_time = Column(db.DateTime, default=get_current_time)
63 |
64 | _password = Column('password', db.String(100), nullable=False)
65 |
66 | def _get_password(self):
67 | return self._password
68 |
69 | def _set_password(self, password):
70 | self._password = generate_password_hash(password)
71 |
72 | # Hide password encryption by exposing password field only.
73 | password = db.synonym('_password',
74 | descriptor=property(_get_password,
75 | _set_password))
76 |
77 | def check_password(self, password):
78 | if self.password is None:
79 | return False
80 | return check_password_hash(self.password, password)
81 |
82 | role_code = Column(db.SmallInteger, default=USER, nullable=False)
83 |
84 | @property
85 | def role(self):
86 | return USER_ROLE[self.role_code]
87 |
88 | def is_admin(self):
89 | return self.role_code == ADMIN
90 |
91 | def is_authenticated(self):
92 | return True
93 |
94 | # One-to-many relationship between users and user_statuses.
95 | status_code = Column(db.SmallInteger, default=INACTIVE)
96 |
97 | @property
98 | def status(self):
99 | return USER_STATUS[self.status_code]
100 |
101 | # Class methods
102 |
103 | @classmethod
104 | def authenticate(cls, login, password):
105 | user = cls.query.filter_by(email=login).first()
106 |
107 | if user:
108 | authenticated = user.check_password(password)
109 | else:
110 | authenticated = False
111 |
112 | return user, authenticated
113 |
114 | @classmethod
115 | def get_by_id(cls, user_id):
116 | return cls.query.filter_by(id=user_id).first_or_404()
117 |
118 | def check_email(self, email):
119 | return Users.query.filter(Users.email == email).count() == 0
120 |
121 | def __unicode__(self):
122 | _str = '%s. %s' % (self.id, self.name)
123 | return str(_str)
124 |
125 |
126 | # Customized User model admin
127 | class UsersAdmin(sqla.ModelView):
128 | column_list = ('id', 'name', 'email', 'role_code', 'status_code',
129 | 'created_time', 'activation_key')
130 | column_sortable_list = ('id', 'name', 'email', 'created_time',
131 | 'role_code', 'status_code')
132 | column_searchable_list = ('email', Users.email)
133 | column_filters = ('id', 'name', 'email', 'created_time', 'role_code')
134 |
135 | form_excluded_columns = ('password')
136 |
137 | form_choices = {
138 | 'role_code': [
139 | ('2', 'User'),
140 | ('0', 'Admin')
141 | ],
142 | 'status_code': [
143 | ('0', 'Inactive Account'),
144 | ('1', 'New Account'),
145 | ('2', 'Active Account')
146 | ]
147 | }
148 |
149 | column_choices = {
150 | 'role_code': [
151 | (2, 'User'),
152 | (1, 'Staff'),
153 | (0, 'Admin')
154 | ],
155 | 'status_code': [
156 | (0, 'Inactive Account'),
157 | (1, 'New Account'),
158 | (2, 'Active Account')
159 | ]
160 | }
161 |
162 | def __init__(self, session):
163 | super(UsersAdmin, self).__init__(Users, session)
164 |
165 | def is_accessible(self):
166 | if current_user.role == 'admin':
167 | return current_user.is_authenticated()
168 |
--------------------------------------------------------------------------------
/flaskstarter/utils.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | """
3 | Common utilities to be used in application
4 | """
5 |
6 | import os
7 |
8 | import datetime
9 | # TMP path, this stores all flask required files
10 | TMP_FOLDER = os.environ.get('HOME') + '/flask_resources'
11 | #TMP_FOLDER = '/data_disk/flask_resources'
12 |
13 | # Form validation
14 |
15 | NAME_LEN_MIN = 4
16 | NAME_LEN_MAX = 25
17 |
18 | PASSWORD_LEN_MIN = 6
19 | PASSWORD_LEN_MAX = 16
20 |
21 |
22 | # Model
23 | STRING_LEN = 64
24 |
25 |
26 | def get_current_time():
27 | return datetime.datetime.utcnow()
28 |
29 |
30 | def pretty_date(dt, default=None):
31 | # Returns string representing "time since" eg 3 days ago, 5 hours ago etc.
32 |
33 | if default is None:
34 | default = 'just now'
35 |
36 | now = datetime.datetime.utcnow()
37 | diff = now - dt
38 |
39 | periods = (
40 | (diff.days / 365, 'year', 'years'),
41 | (diff.days / 30, 'month', 'months'),
42 | (diff.days / 7, 'week', 'weeks'),
43 | (diff.days, 'day', 'days'),
44 | (diff.seconds / 3600, 'hour', 'hours'),
45 | (diff.seconds / 60, 'minute', 'minutes'),
46 | (diff.seconds, 'second', 'seconds'),
47 | )
48 |
49 | for period, singular, plural in periods:
50 |
51 | if not period:
52 | continue
53 |
54 | if int(period) >= 1:
55 | if int(period) > 1:
56 | return u'%d %s ago' % (period, plural)
57 | return u'%d %s ago' % (period, singular)
58 |
59 | return default
60 |
--------------------------------------------------------------------------------
/gunicorn.conf.py:
--------------------------------------------------------------------------------
1 | preload_app=False
2 | accesslog = "./gunicorn_access_new.log"
3 | errorlog = "./gunicorn_error_new.log"
4 | #certfile = "/etc/ssl/certs/ssl-cert-snakeoil.pem"
5 | #keyfile = "/etc/ssl/private/ssl-cert-snakeoil.key"
6 |
--------------------------------------------------------------------------------
/manage.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | from sqlalchemy.orm.mapper import configure_mappers
4 |
5 | from flaskstarter import create_app
6 | from flaskstarter.extensions import db
7 | from flaskstarter.user import Users, ADMIN, USER, ACTIVE
8 |
9 | from flask_pymongo import PyMongo
10 | # from gevent import monkey
11 | # monkey.patch_all()
12 |
13 | from celery import Celery
14 |
15 | def register_celery(app):
16 | #celery.from_config_object("celeryconfig")
17 | class ContextTask(celery.Task):
18 | def __call__(self, *args, **kwargs):
19 | with app.app_context():
20 | return self.run(*args, **kwargs)
21 | celery.Task = ContextTask
22 |
23 | celery = Celery(__name__, broker='redis://localhost:6379/0')
24 | application = create_app()
25 | application.app_context().push()
26 | application.test_request_context().push()
27 | register_celery(application)
28 |
29 | #
30 | # if __name__ == "__main__":
31 | # application.run()
32 | #
33 |
34 |
35 | @application.cli.command("initdb")
36 | def initdb():
37 | """Init/reset database."""
38 |
39 | db.drop_all()
40 | configure_mappers()
41 | db.create_all()
42 |
43 | admin = Users(name='Admin Flask-Starter',
44 | email=u'admin@your-mail.com',
45 | password=u'adminpassword',
46 | role_code=ADMIN,
47 | status_code=ACTIVE)
48 |
49 | db.session.add(admin)
50 |
51 | for i in range(1, 2):
52 | user = Users(name='Demo User',
53 | email=u'demo@your-mail.com',
54 | password=u'demopassword',
55 | role_code=USER,
56 | status_code=ACTIVE)
57 | db.session.add(user)
58 |
59 | for i in range(1, 5):
60 | _task = MyTaskModel(task="Task Random Number ## " + str(i), users_id=2)
61 |
62 | db.session.add(_task)
63 |
64 | db.session.commit()
65 |
66 | print("Database initialized with 2 users (admin, demo)")
67 |
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | amqp==5.1.1
2 | asgiref==3.5.0
3 | async-timeout==4.0.2
4 | attrs==21.4.0
5 | bcrypt==3.2.2
6 | billiard==3.6.4.0
7 | biopython==1.79
8 | blinker==1.4
9 | boto3==1.24.54
10 | botocore==1.27.54
11 | Brotli==1.0.9
12 | cachelib==0.6.0
13 | celery==5.2.7
14 | certifi==2022.5.18.1
15 | cffi==1.15.1
16 | charset-normalizer==2.0.12
17 | click==8.0.3
18 | click-didyoumean==0.3.0
19 | click-plugins==1.1.1
20 | click-repl==0.2.0
21 | colour==0.1.5
22 | cryptography==37.0.4
23 | cycler==0.11.0
24 | dash==2.5.0
25 | dash-bio==1.0.2
26 | dash-core-components==2.0.0
27 | dash-html-components==2.0.0
28 | dash-table==5.0.0
29 | Deprecated==1.2.13
30 | dnspython==2.2.0
31 | email-validator==1.1.3
32 | eventlet==0.33.1
33 | Flask==2.0.2
34 | Flask-Admin==1.5.8
35 | Flask-Caching==1.10.1
36 | Flask-Compress==1.12
37 | Flask-Login==0.5.0
38 | Flask-Mail==0.9.1
39 | Flask-PyMongo==2.3.0
40 | Flask-Session==0.4.0
41 | Flask-SQLAlchemy==2.5.1
42 | Flask-WTF==1.0.0
43 | fonttools==4.33.3
44 | GEOparse==2.0.3
45 | gevent==21.12.0
46 | greenlet==1.1.2
47 | gunicorn==20.1.0
48 | idna==3.3
49 | iniconfig==1.1.1
50 | itsdangerous==2.0.1
51 | Jinja2==3.0.3
52 | jmespath==1.0.1
53 | joblib==1.1.0
54 | jsonschema==4.6.0
55 | kiwisolver==1.4.2
56 | kombu==5.2.4
57 | MarkupSafe==2.0.1
58 | matplotlib==3.5.2
59 | mongo-datatables==0.3.0
60 | numpy==1.22.1
61 | packaging==21.3
62 | pandas==1.4.0
63 | paramiko==2.11.0
64 | ParmEd==3.4.3
65 | periodictable==1.6.1
66 | Pillow==9.1.1
67 | plotly==5.5.0
68 | pluggy==1.0.0
69 | prompt-toolkit==3.0.30
70 | py==1.11.0
71 | pycparser==2.21
72 | pymongo==4.0.1
73 | PyNaCl==1.5.0
74 | pyparsing==3.0.7
75 | pyrsistent==0.18.1
76 | pytest==6.2.5
77 | python-dateutil==2.8.2
78 | pytz==2021.3
79 | redis==4.3.4
80 | requests==2.28.0
81 | s3transfer==0.6.0
82 | scikit-learn==1.1.1
83 | scipy==1.8.1
84 | seaborn==0.11.2
85 | shortuuid==1.0.8
86 | six==1.16.0
87 | SQLAlchemy==1.4.31
88 | ssh-pymongo==1.0.4
89 | sshtunnel==0.4.0
90 | tenacity==8.0.1
91 | threadpoolctl==3.1.0
92 | toml==0.10.2
93 | tqdm==4.64.0
94 | urllib3==1.26.9
95 | vine==5.0.0
96 | wcwidth==0.2.5
97 | Werkzeug==2.0.2
98 | wrapt==1.14.1
99 | WTForms==2.3.3
100 | zope.event==4.5.0
101 | zope.interface==5.4.0
102 |
--------------------------------------------------------------------------------
/screenshots/Logo_gradient.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hiyin/scopeplus/3d0f9f74d966390f101fabb38d1952f5e8c90ba6/screenshots/Logo_gradient.png
--------------------------------------------------------------------------------
/screenshots/admin.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hiyin/scopeplus/3d0f9f74d966390f101fabb38d1952f5e8c90ba6/screenshots/admin.png
--------------------------------------------------------------------------------
/screenshots/dashboard.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hiyin/scopeplus/3d0f9f74d966390f101fabb38d1952f5e8c90ba6/screenshots/dashboard.png
--------------------------------------------------------------------------------
/screenshots/homepage.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hiyin/scopeplus/3d0f9f74d966390f101fabb38d1952f5e8c90ba6/screenshots/homepage.png
--------------------------------------------------------------------------------
/screenshots/login.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hiyin/scopeplus/3d0f9f74d966390f101fabb38d1952f5e8c90ba6/screenshots/login.png
--------------------------------------------------------------------------------
/screenshots/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hiyin/scopeplus/3d0f9f74d966390f101fabb38d1952f5e8c90ba6/screenshots/logo.png
--------------------------------------------------------------------------------
/screenshots/profile.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hiyin/scopeplus/3d0f9f74d966390f101fabb38d1952f5e8c90ba6/screenshots/profile.png
--------------------------------------------------------------------------------
/screenshots/signup.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hiyin/scopeplus/3d0f9f74d966390f101fabb38d1952f5e8c90ba6/screenshots/signup.png
--------------------------------------------------------------------------------
/screenshots/tasks.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hiyin/scopeplus/3d0f9f74d966390f101fabb38d1952f5e8c90ba6/screenshots/tasks.png
--------------------------------------------------------------------------------
/tests/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hiyin/scopeplus/3d0f9f74d966390f101fabb38d1952f5e8c90ba6/tests/__init__.py
--------------------------------------------------------------------------------
/tests/test_flaskstarter.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | import pytest
4 |
5 | from ..flaskstarter import create_app
6 | from ..flaskstarter.extensions import db
7 | from ..flaskstarter.user import Users
8 |
9 |
10 | def test_home_page():
11 | # TODO
12 | return True
13 |
14 |
15 | def test_login_page():
16 | # TODO
17 |
18 | return True
19 |
20 |
21 | def test_user_model():
22 | # TODO
23 |
24 | return True
25 |
--------------------------------------------------------------------------------