This commit is contained in:
Ross Mountjoy 2020-01-29 07:21:00 -05:00
parent 3a596c27ce
commit 47fe5c34cb
22 changed files with 275 additions and 246 deletions

6
app_templates.ini Normal file
View File

@ -0,0 +1,6 @@
[Nextcloud]
prefix = https://
url = your-website.com
icon = static/images/apps/nextcloud.png
description = A safe home for all your data community-driven, free & open source
open_in = this_tab

View File

@ -1,30 +0,0 @@
import os
from shutil import copyfile
from dashmachine.paths import dashmachine_folder, images_folder
from dashmachine.main.utils import read_config
from dashmachine.user_system.models import User
from dashmachine.user_system.utils import add_edit_user
def dashmachine_init():
user_data_folder = os.path.join(dashmachine_folder, "user_data")
# create the user_data subdirectories, link them to static
user_backgrounds_folder = os.path.join(user_data_folder, "backgrounds")
if not os.path.isdir(user_backgrounds_folder):
os.mkdir(user_backgrounds_folder)
os.symlink(user_backgrounds_folder, os.path.join(images_folder, "backgrounds"))
user_icons_folder = os.path.join(user_data_folder, "user_icons")
if not os.path.isdir(user_icons_folder):
os.mkdir(user_icons_folder)
os.symlink(user_icons_folder, os.path.join(images_folder, "user_icons"))
config_file = os.path.join(user_data_folder, "config.ini")
if not os.path.exists(config_file):
copyfile("default_config.ini", config_file)
read_config()
user = User.query.first()
if not user:
add_edit_user(username="admin", password="admin")

View File

@ -1,6 +1,6 @@
from flask import Blueprint, render_template
error_pages = Blueprint('error_pages', __name__)
error_pages = Blueprint("error_pages", __name__)
# ------------------------------------------------------------------------------
@ -8,14 +8,14 @@ error_pages = Blueprint('error_pages', __name__)
# ------------------------------------------------------------------------------
@error_pages.app_errorhandler(404)
def error_404(error):
return render_template('/error_pages/404.html'), 404
return render_template("/error_pages/404.html"), 404
@error_pages.app_errorhandler(403)
def error_403(error):
return render_template('/error_pages/403.html'), 403
return render_template("/error_pages/403.html"), 403
@error_pages.app_errorhandler(500)
def error_500(error):
return render_template('/error_pages/500.html'), 500
return render_template("/error_pages/500.html"), 500

View File

@ -1,6 +1,12 @@
import os
from shutil import copyfile
from configparser import ConfigParser
from dashmachine.paths import dashmachine_folder, images_folder
from dashmachine.main.models import Apps
from dashmachine.settings_system.models import Settings
from dashmachine.user_system.models import User
from dashmachine.user_system.utils import add_edit_user
from dashmachine import db
@ -77,3 +83,27 @@ def read_config():
def public_route(decorated_function):
decorated_function.is_public = True
return decorated_function
def dashmachine_init():
user_data_folder = os.path.join(dashmachine_folder, "user_data")
# create the user_data subdirectories, link them to static
user_backgrounds_folder = os.path.join(user_data_folder, "backgrounds")
if not os.path.isdir(user_backgrounds_folder):
os.mkdir(user_backgrounds_folder)
os.symlink(user_backgrounds_folder, os.path.join(images_folder, "backgrounds"))
icons_folder = os.path.join(user_data_folder, "icons")
if not os.path.isdir(icons_folder):
os.mkdir(icons_folder)
os.symlink(icons_folder, os.path.join(images_folder, "icons"))
config_file = os.path.join(user_data_folder, "config.ini")
if not os.path.exists(config_file):
copyfile("default_config.ini", config_file)
read_config()
user = User.query.first()
if not user:
add_edit_user(username="admin", password="admin")

View File

@ -21,6 +21,8 @@ apps_images_folder = os.path.join(images_folder, "apps")
backgrounds_images_folder = os.path.join(images_folder, "backgrounds")
icons_images_folder = os.path.join(images_folder, "icons")
cache_folder = os.path.join(static_folder, "cache")
user_images_folder = os.path.join(images_folder, "user")

View File

@ -6,16 +6,16 @@ from dashmachine.version import tcmachine_version
class GetVersion(Resource):
def get(self):
return {'Version': tcmachine_version}
return {"Version": tcmachine_version}
class ServerShutdown(Resource):
def get(self):
os.system('shutdown now')
return {'Done'}
os.system("shutdown now")
return {"Done"}
class ServerReboot(Resource):
def get(self):
os.system('reboot')
return {'Done'}
os.system("reboot")
return {"Done"}

View File

@ -1,9 +1,10 @@
import os
from flask import render_template, url_for, redirect, request, Blueprint, jsonify
from flask import render_template, request, Blueprint, jsonify
from dashmachine.settings_system.forms import ConfigForm
from dashmachine.user_system.forms import UserForm
from dashmachine.main.utils import read_config
from dashmachine.main.models import Files
from dashmachine.paths import backgrounds_images_folder, apps_images_folder
from dashmachine.paths import backgrounds_images_folder, icons_images_folder
from dashmachine.settings_system.utils import load_files_html
settings_system = Blueprint("settings_system", __name__)
@ -12,11 +13,12 @@ settings_system = Blueprint("settings_system", __name__)
@settings_system.route("/settings", methods=["GET"])
def settings():
config_form = ConfigForm()
user_form = UserForm()
with open("dashmachine/user_data/config.ini", "r") as config_file:
config_form.config.data = config_file.read()
files_html = load_files_html()
return render_template(
"settings_system/settings.html", config_form=config_form, files_html=files_html
"settings_system/settings.html", config_form=config_form, files_html=files_html, user_form=user_form
)
@ -30,8 +32,8 @@ def save_config():
@settings_system.route("/settings/add_images", methods=["POST"])
def add_images():
if request.form.get("folder") == "apps":
dest_folder = apps_images_folder
if request.form.get("folder") == "icons":
dest_folder = icons_images_folder
elif request.form.get("folder") == "backgrounds":
dest_folder = backgrounds_images_folder
for cached_file in request.form.get("files").split(","):

View File

@ -1,13 +1,13 @@
from dashmachine.paths import backgrounds_images_folder, apps_images_folder
from dashmachine.paths import backgrounds_images_folder, icons_images_folder
from flask import render_template
from os import listdir
def load_files_html():
background_images = listdir(backgrounds_images_folder)
apps_images = listdir(apps_images_folder)
backgrounds = listdir(backgrounds_images_folder)
icons = listdir(icons_images_folder)
return render_template(
"settings_system/files.html",
background_images=background_images,
apps_images=apps_images,
backgrounds=backgrounds,
icons=icons,
)

View File

@ -766,11 +766,6 @@ span.badge.new {
position: relative;
right: 10px;
}
.copy-btn {
position: relative;
top: 2px;
font-size: 1.25rem;
}
/*FAB*/
.tap-target-wave::before, .tap-target-wave::after {

View File

@ -0,0 +1 @@
/home/ross/Dev/DashMachineEnv/DashMachine/dashmachine/user_data/icons

View File

@ -170,22 +170,6 @@ function init_autocomplete_chips(){
});
}
function init_send_doc_email_btns(el) {
el.on('click', function() {
let attachment_deal = $("#attachment-deal")
attachment_deal.val($(this).attr('data-deal_id'));
attachment_deal.trigger('change');
sleep(100).then(() => {
let deal_doc = $("#deal-doc")
deal_doc.val($(this).attr('data-file'));
deal_doc.trigger('change');
tinymce.get("email-panel-content").setContent($(this).attr('data-signature'));
$("#attachments").removeClass('hide');
$("#email-panel").removeClass('hide');
$("#chips-mailto input").trigger('focus');
});
});
}
function toggleDarkMode(){
let mode = localStorage.getItem('mode');
@ -212,9 +196,10 @@ function toggleTooltips(){
}
}
function init_copy_btn(){
function init_copy_btn(parent_class){
$(".copy-btn").on('click', function(e) {
let target_text = $(this).closest('.col').find('.copy-target').text();
let target_text = $(this).closest(parent_class).find('.copy-target').text();
console.log(target_text)
let copy_input = $("#copy-input");
copy_input.val(target_text);
copy_input.removeClass("hide");
@ -293,49 +278,6 @@ function initCleave(){
});
}
// TinyMCE Editor
function initTinyMCE(el){
// Check TinyMCE initialized or not
if(tinyMCE.get(el)){
// Remove instance by id
tinymce.remove('#' + el);
}else{
let mode = localStorage.getItem('mode');
let theme = ""
if (mode === 'dark') {
theme = "dark"
} else {
theme = "light"
}
tinymce.init({
selector: '#' + el,
height: 200,
menubar: false,
removed_menuitems: 'undo, redo, anchor',
skin: theme,
statusbar: true,
branding: false,
paste_data_images: true,
force_br_newlines: true,
force_p_newlines: false,
forced_root_block: '',
content_style: "body {margin-top: 15px}",
visual_table_class: 'no-border',
mode: "exact",
plugins: [
'autolink lists link image charmap print preview anchor textcolor',
'searchreplace visualblocks code fullscreen',
'insertdatetime media table paste code help imagetools'
],
toolbar: 'formatselect | bold italic forecolor backcolor | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent fullscreen code',
content_css: [
'//fonts.googleapis.com/css?family=Lato:300,300i,400,400i',
'//www.tiny.cloud/css/codepen.min.css'
]
});
}
}
function hide_sidenav() {
$("#main-sidenav").addClass('hide');
$("#main.main-full").css('padding-left', 0);
@ -361,7 +303,6 @@ $(document).ready(function () {
init_timepicker();
initCleave();
init_tooltips();
init_copy_btn();
init_select();
if (localStorage.getItem('sidenav_hidden') === 'true'){

View File

@ -63,7 +63,9 @@
{% block sidenav %}{% endblock sidenav %}
{% block content %}{% endblock content %}
{% block content %}
<input id="copy-input" class="hide">
{% endblock content %}
{% block footer %}{% endblock footer %}

View File

@ -27,27 +27,40 @@
</div>
</div>
<div class="row">
{% for app in apps %}
{% if app.open_in == 'iframe' %}
<a href="{{ url_for('main.app_view', url=app.url) }}" class="app-a" data-name="{{ app.name }}" data-description="{{ app.description }}">
{% elif app.open_in == 'this_tab' %}
<a href="{{ app.prefix }}{{ app.url }}" class="app-a" data-name="{{ app.name }}" data-description="{{ app.description }}">
{% elif app.open_in == "new_tab" %}
<a href="{{ app.prefix }}{{ app.url }}" target="_blank" class="app-a" data-name="{{ app.name }}" data-description="{{ app.description }}">
{% endif %}
<div class="col s12 m6 l3">
<div class="card theme-surface-transparent">
<div class="card-content center-align">
<img src="{{ app.icon }}" height="64px">
</div>
<div class="card-action center-align">
<h5>{{ app.name }}</h5>
<span class="theme-secondary-text">{{ app.description }}</span>
{% if apps %}
{% for app in apps %}
{% if app.open_in == 'iframe' %}
<a href="{{ url_for('main.app_view', url=app.url) }}" class="app-a" data-name="{{ app.name }}" data-description="{{ app.description }}">
{% elif app.open_in == 'this_tab' %}
<a href="{{ app.prefix }}{{ app.url }}" class="app-a" data-name="{{ app.name }}" data-description="{{ app.description }}">
{% elif app.open_in == "new_tab" %}
<a href="{{ app.prefix }}{{ app.url }}" target="_blank" class="app-a" data-name="{{ app.name }}" data-description="{{ app.description }}">
{% endif %}
<div class="col s12 m6 l3">
<div class="card theme-surface-transparent">
<div class="card-content center-align">
<img src="{{ app.icon }}" height="64px">
</div>
<div class="card-action center-align">
<h5>{{ app.name }}</h5>
<span class="theme-secondary-text">{{ app.description }}</span>
</div>
</div>
</div>
</div>
</a>
{% endfor %}
</a>
{% endfor %}
{% else %}
<a href="{{ url_for('settings_system.settings') }}">
<div class="col s12 m6 l3">
<div class="card theme-surface-transparent">
<div class="card-action center-align">
<h5>No apps yet, go to settings.</h5>
</div>
</div>
</div>
</a>
{% endif %}
</div>
</div>
</div>

View File

@ -1,15 +1,29 @@
<style>
.file-title {
position: relative;
top: .6em;
}
</style>
<div class="divider"></div>
<div class="row">
<div class="col s12">
<ul class="collection with-header">
<li class="collection-header theme-primary white-text">Backgrounds</li>
{% for background_image in background_images %}
<li class="collection-item pt-2 pb-2"><span class="selectable-all">static/images/backgrounds/{{ background_image }}</span>
<span class="secondary-content"><i class="material-icons-outlined icon-btn">close</i></span>
<span class="secondary-content"><i class="material-icons-outlined icon-btn">filter_none</i></span>
<span class="secondary-content"><i class="material-icons-outlined icon-btn">visibility</i></span>
</li>
{% endfor %}
{% if backgrounds %}
{% for background in backgrounds %}
<li class="collection-item pt-2 pb-2 avatar">
<a href="static/images/backgrounds/{{ background }}" target="_blank">
<img src="static/images/backgrounds/{{ background }}" alt="" class="circle">
</a>
<span class="selectable-all copy-target file-title">static/images/backgrounds/{{ background }}</span>
<span class="secondary-content"><i class="material-icons-outlined icon-btn">close</i></span>
<span class="secondary-content mr-4"><i class="material-icons-outlined icon-btn copy-btn">filter_none</i></span>
</li>
{% endfor %}
{% else %}
<li class="collection-item">No files yet</li>
{% endif %}
</ul>
</div>
</div>
@ -17,14 +31,27 @@
<div class="row">
<div class="col s12">
<ul class="collection with-header">
<li class="collection-header theme-primary white-text">Apps</li>
{% for apps_image in apps_images %}
<li class="collection-item pt-2 pb-2"><span class="selectable-all">static/images/apps/{{ apps_image }}</span>
<span class="secondary-content"><i class="material-icons-outlined icon-btn">close</i></span>
<span class="secondary-content"><i class="material-icons-outlined icon-btn">filter_none</i></span>
<span class="secondary-content"><i class="material-icons-outlined icon-btn">visibility</i></span>
</li>
{% endfor %}
<li class="collection-header theme-primary white-text">Icons</li>
{% if icons %}
{% for icon in icons %}
<li class="collection-item pt-2 pb-2 avatar">
<a href="static/images/icons/{{ icon }}" target="_blank">
<img src="static/images/icons/{{ icon }}" alt="" class="circle">
</a>
<span class="selectable-all copy-target file-title">static/images/icons/{{ icon }}</span>
<span class="secondary-content"><i class="material-icons-outlined icon-btn">close</i></span>
<span class="secondary-content mr-4"><i class="material-icons-outlined icon-btn copy-btn">filter_none</i></span>
</li>
{% endfor %}
{% else %}
<li class="collection-item">No files yet</li>
{% endif %}
</ul>
</div>
</div>
</div>
<script>
$( document ).ready(function() {
init_copy_btn(".collection-item");
});
</script>

View File

@ -48,32 +48,42 @@
[Settings]<br>
theme = dark<br>
accent = orange<br>
accent = static/images/backgrounds/background.png<br>
background = static/images/backgrounds/background.png<br>
</code>
<table class="mt-4">
<thead>
<tr>
<th>Variable</th>
<th>Value</th>
<th>Required</th>
<th>Description</th>
<th>Options</th>
</tr>
</thead>
<tbody>
<tr>
<td>[Settings]</td>
<td>config section name. required.</td>
<td>Yes</td>
<td>Config section name.</td>
<td>string</td>
</tr>
<tr>
<td>theme</td>
<td>UI theme, options are light, dark</td>
<td>Yes</td>
<td>UI theme</td>
<td>light, dark</td>
</tr>
<tr>
<td>accent</td>
<td>UI accent, options are orange, green, blue, green, pink, grey</td>
<td>Yes</td>
<td>UI accent color</td>
<td>orange, green, blue, green, pink, grey</td>
</tr>
<tr>
<td>background</td>
<td>Background image. can be either a local link (prefixed by /static/images/backgrounds/) or an external link. Can also be set to random. If not set defaults to blank.</td>
<td>Yes</td>
<td>Background image for the UI</td>
<td>/static/images/backgrounds/yourpicture.png, external link to image, None, random</td>
</tr>
</tbody>
</table>
@ -93,37 +103,53 @@
<thead>
<tr>
<th>Variable</th>
<th>Value</th>
<th>Required</th>
<th>Description</th>
<th>Options</th>
</tr>
</thead>
<tbody>
<tr>
<td>[App Name]</td>
<td>Yes</td>
<td>The name of your app.</td>
<td>string</td>
</tr>
<tr>
<td>prefix</td>
<td>the prefix for the app's url, e.g. http:// or https://</td>
<td>Yes</td>
<td>The prefix for the app's url.</td>
<td>web prefix, e.g. http:// or https://</td>
</tr>
<tr>
<td>url</td>
<td>the url for your app, e.g. google.com</td>
<td>Yes</td>
<td>The url for your app.</td>
<td>web url, e.g. myapp.com</td>
</tr>
<tr>
<td>icon</td>
<td>icon for the dashboard, can be either a local link (prefixed by /static/images/apps/), or an external link</td>
<td>No</td>
<td>Icon for the dashboard.</td>
<td>/static/images/icons/yourpicture.png, external link to image</td>
</tr>
<tr>
<td>sidebar_icon</td>
<td>icon for the sidebar, can be either a local link (prefixed by /static/images/apps/), or an external link</td>
<td>No</td>
<td>Icon for the sidenav.</td>
<td>/static/images/icons/yourpicture.png, external link to image</td>
</tr>
<tr>
<td>description</td>
<td>a short description for the app</td>
<td>No</td>
<td>A short description for the app.</td>
<td>string</td>
</tr>
<tr>
<td>open_in</td>
<td>open the app in the current tab, an iframe or a new tab, options are iframe, new_tab, or this_tab</td>
<td>Yes</td>
<td>open the app in the current tab, an iframe or a new tab</td>
<td>iframe, new_tab, this_tab</td>
</tr>
</tbody>
</table>
@ -146,20 +172,74 @@
<div class="col s12 m12 l6">
<div class="card scrollbar" style="max-height: calc(100vh - 30px)">
<div class="card-content">
<div class="row">
<div class="col s12">
<h5>User</h5>
{{ user_form.hidden_tag() }}
{{ input(
label="Username",
id="user-form-username",
size="s12",
form_obj=user_form.username,
val=current_user.username
) }}
{{ input(
label="Password",
id="user-form-password",
form_obj=user_form.password,
size="s12"
) }}
{{ input(
label="Confirm Password",
id="user-form-confirm_password",
form_obj=user_form.confirm_password,
required='required',
size="s12"
) }}
{{ button(
icon="save",
float="left",
text="save"
) }}
</div>
</div>
<div class="divider"></div>
<div class="row">
<div class="col s12">
<h5>App Templates</h5>
<div class="row card-filter-container">
<div class="col s12 input-field">
<span>
<i class="material-icons prefix card-search-icon">search</i>
<input type="text" id="templates-filter" class="card-filter theme-surface-1" placeholder="Search for app (e.g. Nextcloud)">
</span>
</div>
</div>
</div>
</div>
<div class="divider"></div>
<div class="row">
<div class="col s12">
<h5>Images</h5>
<form id="add-images-form">
<div class="input-field col s12 mt-4">
<select name="folder">
<option value="apps">Apps</option>
<option value="icons">Icons</option>
<option value="backgrounds">Backgrounds</option>
</select>
<label>Folder</label>
</div>
<input name="files" id="add-images-input" class="hide">
</form>
{{ tcdrop(allowed_types='jpg,jpeg,png', id="images-tcdrop", max_files="30") }}
{{ tcdrop(allowed_types='jpg,jpeg,png,gif', id="images-tcdrop", max_files="30") }}
{{ button(text="save", icon="save", id="save-images-btn", float="left", data={"url": url_for('settings_system.add_images')}) }}
</div>
</div>

View File

@ -49,9 +49,9 @@
{{ form.hidden_tag() }}
<div class="input-field col s12">
<i class="material-icons-outlined prefix">mail_outline</i>
{{ form.email(class="validate", id="email", type="text") }}
<label for="email" class="center-align">Email</label>
<i class="material-icons-outlined prefix">person</i>
{{ form.username(class="validate", id="username", type="text") }}
<label for="username" class="center-align">Username</label>
</div>
<div class="input-field col s12">

View File

@ -1,68 +1,17 @@
from flask_wtf import FlaskForm
from flask_login import current_user
from wtforms import (
StringField,
PasswordField,
SubmitField,
BooleanField,
SelectField,
FileField,
)
from wtforms.validators import DataRequired, EqualTo, Email, Length, ValidationError
from dashmachine.user_system.models import User
from wtforms.validators import DataRequired
class PasswordForm(FlaskForm):
password = PasswordField(
"Password",
validators=[
DataRequired(),
Length(min=8, message="Password must be at least 8 characters."),
EqualTo("confirm_password", message="Passwords must match."),
],
)
class UserForm(FlaskForm):
username = StringField(validators=[DataRequired()])
confirm_password = PasswordField("Confirm Password", validators=[DataRequired()])
password = PasswordField(validators=[DataRequired()])
confirm_password = PasswordField()
class RegisterForm(FlaskForm):
email = StringField("Email", validators=[DataRequired(), Email()])
def validate_email(form, field):
if field.data == current_user.email:
email_in_db = None
else:
email_in_db = User.query.filter_by(email=field.data).first()
if email_in_db:
raise ValidationError("Email is already registered.")
fname = StringField("First Name", validators=[DataRequired()])
lname = StringField("Last Name", validators=[DataRequired()])
phone = StringField("Phone Number", validators=[DataRequired()])
company = StringField("Company/Team Name")
avatar = FileField()
password = PasswordField(
"Password",
validators=[
DataRequired(),
Length(min=8, message="Password must be at least 8 characters."),
EqualTo("confirm_password", message="Passwords must match."),
],
)
confirm_password = PasswordField("Confirm Password", validators=[DataRequired()])
class LoginForm(FlaskForm):
email = StringField("User Name", validators=[DataRequired()])
password = PasswordField("Password", validators=[DataRequired()])
submit = SubmitField()
remember = BooleanField("Remember Me")
remember = BooleanField()

View File

@ -1,7 +1,8 @@
from flask import render_template, url_for, redirect, Blueprint
from flask_login import login_user, logout_user, current_user
from dashmachine.user_system.forms import LoginForm
from dashmachine.user_system.forms import UserForm
from dashmachine.user_system.models import User
from dashmachine.user_system.utils import add_edit_user
from dashmachine import bcrypt
from dashmachine.main.utils import public_route
@ -20,10 +21,10 @@ def login():
if current_user.is_authenticated:
return redirect(url_for("main.home"))
form = LoginForm()
form = UserForm()
if form.validate_on_submit():
user = User.query.filter_by(username=form.email.data.lower()).first()
user = User.query.filter_by(username=form.username.data.lower()).first()
if user and bcrypt.check_password_hash(user.password, form.password.data):
login_user(user, remember=form.remember.data)
@ -43,3 +44,13 @@ def logout():
logout_user()
return redirect(url_for("user_system.login"))
@user_system.route("/edit_user", methods=["POST"])
def edit_user():
form = UserForm()
if form.validate_on_submit():
add_edit_user(username=form.username.data, password=form.password.data)
return 'ok'

View File

@ -1 +1 @@
tcmachine_version = 'v0.0'
tcmachine_version = "v0.0"

View File

@ -4,12 +4,12 @@ from flask_script import Manager
from flask_migrate import Migrate, MigrateCommand
from dashmachine import app, db
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///dashmachine/site.db'
app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///dashmachine/site.db"
migrate = Migrate(app, db)
manager = Manager(app)
manager.add_command('db', MigrateCommand)
manager.add_command("db", MigrateCommand)
if __name__ == '__main__':
if __name__ == "__main__":
manager.run()

View File

@ -15,17 +15,19 @@ config = context.config
# Interpret the config file for Python logging.
# This line sets up loggers basically.
fileConfig(config.config_file_name)
logger = logging.getLogger('alembic.env')
logger = logging.getLogger("alembic.env")
# add your model's MetaData object here
# for 'autogenerate' support
# from myapp import mymodel
# target_metadata = mymodel.Base.metadata
from flask import current_app
config.set_main_option(
'sqlalchemy.url', current_app.config.get(
'SQLALCHEMY_DATABASE_URI').replace('%', '%%'))
target_metadata = current_app.extensions['migrate'].db.metadata
"sqlalchemy.url",
current_app.config.get("SQLALCHEMY_DATABASE_URI").replace("%", "%%"),
)
target_metadata = current_app.extensions["migrate"].db.metadata
# other values from the config, defined by the needs of env.py,
# can be acquired:
@ -46,9 +48,7 @@ def run_migrations_offline():
"""
url = config.get_main_option("sqlalchemy.url")
context.configure(
url=url, target_metadata=target_metadata, literal_binds=True
)
context.configure(url=url, target_metadata=target_metadata, literal_binds=True)
with context.begin_transaction():
context.run_migrations()
@ -66,15 +66,15 @@ def run_migrations_online():
# when there are no changes to the schema
# reference: http://alembic.zzzcomputing.com/en/latest/cookbook.html
def process_revision_directives(context, revision, directives):
if getattr(config.cmd_opts, 'autogenerate', False):
if getattr(config.cmd_opts, "autogenerate", False):
script = directives[0]
if script.upgrade_ops.is_empty():
directives[:] = []
logger.info('No changes in schema detected.')
logger.info("No changes in schema detected.")
connectable = engine_from_config(
config.get_section(config.config_ini_section),
prefix='sqlalchemy.',
prefix="sqlalchemy.",
poolclass=pool.NullPool,
)
@ -83,7 +83,7 @@ def run_migrations_online():
connection=connection,
target_metadata=target_metadata,
process_revision_directives=process_revision_directives,
**current_app.extensions['migrate'].configure_args
**current_app.extensions["migrate"].configure_args
)
with context.begin_transaction():

2
run.py
View File

@ -1,7 +1,7 @@
#!/usr/bin/env python3
from dashmachine import app
from dashmachine.dashmachine_init import dashmachine_init
from dashmachine.main.utils import dashmachine_init
dashmachine_init()