Skip to content

Commit

Permalink
Setup stages (#4618)
Browse files Browse the repository at this point in the history
* setup complete stages

* [setup] better setup-in-progress card

* restructure setup exception flow

* use setup_stages hook

* Add message for non-dev mode, fail instead of error

* message to not include commits in app setup stages
  • Loading branch information
pratu16x7 authored and nabinhait committed Dec 21, 2017
1 parent 56bf543 commit d5ad0ff
Show file tree
Hide file tree
Showing 4 changed files with 207 additions and 187 deletions.
146 changes: 92 additions & 54 deletions frappe/desk/page/setup_wizard/setup_wizard.js
Expand Up @@ -182,39 +182,72 @@ frappe.setup.SetupWizard = class SetupWizard extends frappe.ui.Slides {
}

action_on_complete() {
var me = this;
if (!this.current_slide.set_values()) return;
this.update_values();
this.show_working_state();
this.disable_keyboard_nav();
this.listen_for_setup_stages();

return frappe.call({
method: "frappe.desk.page.setup_wizard.setup_wizard.setup_complete",
args: {args: this.values},
callback: function() {
me.show_setup_complete_state();
if(frappe.setup.welcome_page) {
localStorage.setItem("session_last_route", frappe.setup.welcome_page);
callback: (r) => {
if(r.message.status === 'ok') {
this.post_setup_success();
} else if(r.message.fail !== undefined) {
this.abort_setup(r.message.fail);
}
setTimeout(function() {
// Reload
window.location.href = '';
}, 2000);
setTimeout(()=> {
$('body').removeClass('setup-state');
}, 20000);
},
error: function() {
var d = frappe.msgprint(__("There were errors."));
d.custom_onhide = function() {
$(me.parent).find('.page-card-container').remove();
$('body').removeClass('setup-state');
me.container.show();
frappe.set_route(me.page_name, me.slides.length - 1);
};
}
error: this.abort_setup.bind(this, "Error in setup", true)
});
}

post_setup_success() {
this.set_setup_complete_message(__("Setup Complete"), __("Refreshing..."));
if(frappe.setup.welcome_page) {
localStorage.setItem("session_last_route", frappe.setup.welcome_page);
}
setTimeout(function() {
// Reload
window.location.href = '';
}, 2000);
}

abort_setup(fail_msg, error=false) {
this.$working_state.find('.state-icon-container').html('');
fail_msg = fail_msg ? fail_msg : __("Failed to complete setup");

if(error && !frappe.boot.developer_mode) {
frappe.msgprint(`Don't worry. It's not you, it's us. We've
received the issue details and will get back to you on the solution.
Please feel free to contact us on support@erpnext.com in the meantime.`);
}

this.update_setup_message('Could not start up: ' + fail_msg);

this.$working_state.find('.title').html('Setup failed');

this.$abort_btn.show();
}

listen_for_setup_stages() {
frappe.realtime.on("setup_task", (data) => {
// console.log('data', data);
if(data.stage_status) {
// .html('Process '+ data.progress[0] + ' of ' + data.progress[1] + ': ' + data.stage_status);
this.update_setup_message(data.stage_status);
this.set_setup_load_percent((data.progress[0]+1)/data.progress[1] * 100);
}
if(data.fail_msg) {
this.abort_setup(data.fail_msg);
}
})
}

update_setup_message(message) {
this.$working_state.find('.setup-message').html(message);
}

get_setup_slides_filtered_by_domain() {
var filtered_slides = [];
frappe.setup.slides.forEach(function(slide) {
Expand All @@ -233,51 +266,56 @@ frappe.setup.SetupWizard = class SetupWizard extends frappe.ui.Slides {

show_working_state() {
this.container.hide();
$('body').addClass('setup-state');
frappe.set_route(this.page_name);

this.working_state_message = this.get_message(
__("Setting Up"),
__("Sit tight while your system is being setup. This may take a few moments."),
true
).appendTo(this.parent);
this.$working_state = this.get_message(
__("Setting up your system"),
__("Starting Frappé ...")).appendTo(this.parent);

this.attach_abort_button();

this.current_id = this.slides.length;
this.current_slide = null;
this.completed_state_message = this.get_message(
__("Setup Complete"),
__("Refreshing...")
);
}

show_setup_complete_state() {
this.working_state_message.hide();
this.completed_state_message.appendTo(this.parent);
attach_abort_button() {
this.$abort_btn = $(`<button class='btn btn-default btn-xs text-muted'
style="margin-bottom: 30px;">${__('Retry')}</button>`);
this.$working_state.find('.content').append(this.$abort_btn);

this.$abort_btn.on('click', () => {
$(this.parent).find('.setup-in-progress').remove();
this.container.show();
frappe.set_route(this.page_name, this.slides.length - 1);
});

this.$abort_btn.hide();
}

get_message(title, message="", loading=false) {
const loading_html = loading
? '<div style="width:100%;height:100%" class="lds-rolling state-icon"><div></div></div>'
: `<div style="width:100%;height:100%" class="state-icon">
<i class="fa fa-check-circle text-success"
style="font-size: 64px; margin-top: -8px;"></i>
</div>`;

return $(`<div class="page-card-container" data-state="setup">
<div class="page-card">
<div class="page-card-head">
${loading
? `<span class="indicator orange">${title}</span>`
: `<span class="indicator green">${title}</span>`
}
</div>
<p>${message}</p>
<div class="state-icon-container">
${loading_html}
</div>
get_message(title, message="") {
const loading_html = `<div class="progress-chart" style ="width: 150px;">
<div class="progress" style="margin-top: 70px; margin-bottom: 0px">
<div class="progress-bar" style="width: 2%; background-color: #5e64ff;"></div>
</div>
</div>`;

return $(`<div class="slides-wrapper setup-wizard-slide setup-in-progress">
<div class="content text-center">
<p class="title lead">${title}</p>
<div class="state-icon-container">${loading_html}</div>
<p class="setup-message text-muted" style="margin: 30px 0px;">${message}</p>
</div>
</div>`);
}

set_setup_complete_message(title, message) {
this.$working_state.find('.title').html(title);
this.$working_state.find('.setup-message').html(message);
}

set_setup_load_percent(percent) {
this.$working_state.find('.progress-bar').css({"width": percent + "%"});
}
};

frappe.setup.SetupWizardSlide = class SetupWizardSlide extends frappe.ui.Slide {
Expand Down
149 changes: 108 additions & 41 deletions frappe/desk/page/setup_wizard/setup_wizard.py
Expand Up @@ -13,50 +13,117 @@
from . import install_fixtures
from six import string_types

def get_setup_stages(args):

# App setup stage functions should not include frappe.db.commit
# That is done by frappe after successful completion of all stages
stages = [
{
'status': 'Updating global settings',
'fail_msg': 'Failed to update global settings',
'tasks': [
{
'fn': update_global_settings,
'args': args,
'fail_msg': 'Failed to update global settings'
}
]
}
]

stages += get_stages_hooks(args) + get_setup_complete_hooks(args)

stages.append({
# post executing hooks
'status': 'Wrapping up',
'fail_msg': 'Failed to complete setup',
'tasks': [
{
'fn': run_post_setup_complete,
'args': args,
'fail_msg': 'Failed to complete setup'
}
]
})

return stages

@frappe.whitelist()
def setup_complete(args):
"""Calls hooks for `setup_wizard_complete`, sets home page as `desktop`
and clears cache. If wizard breaks, calls `setup_wizard_exception` hook"""

# Setup complete: do not throw an exception, let the user continue to desk
if cint(frappe.db.get_single_value('System Settings', 'setup_complete')):
# do not throw an exception if setup is already complete
# let the user continue to desk
return
#frappe.throw(_('Setup already complete'))

args = process_args(args)
args = parse_args(args)

try:
if args.language and args.language != "english":
set_default_language(get_language_code(args.language))
stages = get_setup_stages(args)

frappe.clear_cache()

# update system settings
update_system_settings(args)
update_user_name(args)

for method in frappe.get_hooks("setup_wizard_complete"):
frappe.get_attr(method)(args)
try:
current_task = None
for idx, stage in enumerate(stages):
frappe.publish_realtime('setup_task', {"progress": [idx, len(stages)],
"stage_status": stage.get('status')}, user=frappe.session.user)

for task in stage.get('tasks'):
current_task = task
task.get('fn')(task.get('args'))

except Exception:
handle_setup_exception(args)
return {'status': 'fail', 'fail': current_task.get('fail_msg')}
else:
run_setup_success(args)
return {'status': 'ok'}

disable_future_access()
def update_global_settings(args):
if args.language and args.language != "english":
set_default_language(get_language_code(args.lang))
frappe.clear_cache()

frappe.db.commit()
frappe.clear_cache()
except:
frappe.db.rollback()
if args:
traceback = frappe.get_traceback()
for hook in frappe.get_hooks("setup_wizard_exception"):
frappe.get_attr(hook)(traceback, args)
update_system_settings(args)
update_user_name(args)

raise
def run_post_setup_complete(args):
disable_future_access()
frappe.db.commit()
frappe.clear_cache()

else:
for hook in frappe.get_hooks("setup_wizard_success"):
frappe.get_attr(hook)(args)
install_fixtures.install()
def run_setup_success(args):
for hook in frappe.get_hooks("setup_wizard_success"):
frappe.get_attr(hook)(args)
install_fixtures.install()

def get_stages_hooks(args):
stages = []
for method in frappe.get_hooks("setup_wizard_stages"):
stages += frappe.get_attr(method)(args)
return stages

def get_setup_complete_hooks(args):
stages = []
for method in frappe.get_hooks("setup_wizard_complete"):
stages.append({
'status': 'Executing method',
'fail_msg': 'Failed to execute method',
'tasks': [
{
'fn': frappe.get_attr(method),
'args': args,
'fail_msg': 'Failed to execute method'
}
]
})
return stages

def handle_setup_exception(args):
frappe.db.rollback()
if args:
traceback = frappe.get_traceback()
for hook in frappe.get_hooks("setup_wizard_exception"):
frappe.get_attr(hook)(traceback, args)

def update_system_settings(args):
number_format = get_country_info(args.get("country")).get("number_format", "#,###.##")
Expand Down Expand Up @@ -126,7 +193,7 @@ def update_user_name(args):
if args.get('name'):
add_all_roles_to(args.get("name"))

def process_args(args):
def parse_args(args):
if not args:
args = frappe.local.form_dict
if isinstance(args, string_types):
Expand Down Expand Up @@ -234,14 +301,6 @@ def email_setup_wizard_exception(traceback, args):
user_agent = frappe._dict()

message = """
#### Basic Information
- **Site:** {site}
- **User:** {user}
- **Browser:** {user_agent.platform} {user_agent.browser} version: {user_agent.version} language: {user_agent.language}
- **Browser Languages**: `{accept_languages}`
---
#### Traceback
Expand All @@ -257,7 +316,16 @@ def email_setup_wizard_exception(traceback, args):
#### Request Headers
<pre>{headers}</pre>""".format(
<pre>{headers}</pre>
---
#### Basic Information
- **Site:** {site}
- **User:** {user}
- **Browser:** {user_agent.platform} {user_agent.browser} version: {user_agent.version} language: {user_agent.language}
- **Browser Languages**: `{accept_languages}`""".format(
site=frappe.local.site,
traceback=traceback,
args="\n".join(pretty_args),
Expand All @@ -268,14 +336,13 @@ def email_setup_wizard_exception(traceback, args):

frappe.sendmail(recipients=frappe.local.conf.setup_wizard_exception_email,
sender=frappe.session.user,
subject="Exception in Setup Wizard - {}".format(frappe.local.site),
subject="Setup failed: {}".format(frappe.local.site),
message=message,
delayed=False)

def get_language_code(lang):
return frappe.db.get_value('Language', {'language_name':lang})


def enable_twofactor_all_roles():
all_role = frappe.get_doc('Role',{'role_name':'All'})
all_role.two_factor_auth = True
Expand Down

0 comments on commit d5ad0ff

Please sign in to comment.