This commit is contained in:
flaskfarm
2022-10-25 12:12:04 +09:00
parent b19a5072ba
commit 26835c2cbb
14 changed files with 545 additions and 548 deletions

1
.gitignore vendored
View File

@@ -143,6 +143,7 @@ false
*copy.py *copy.py
*.sh *.sh
data/ data/
tmp/
lib/support/site/tving.py lib/support/site/tving.py
lib/support/site/wavve.py lib/support/site/wavve.py

View File

@@ -104,8 +104,8 @@ class Framework:
# https://flask-sqlalchemy.palletsprojects.com/en/3.0.x/config/#flask_sqlalchemy.config.SQLALCHEMY_BINDS # https://flask-sqlalchemy.palletsprojects.com/en/3.0.x/config/#flask_sqlalchemy.config.SQLALCHEMY_BINDS
# 어떤 편법도 불가. db를 사용하지 않아도 파일이 생김. # 어떤 편법도 불가. db를 사용하지 않아도 파일이 생김.
db_path = os.path.join(self.config['path_data'], 'db', 'system.db') db_path = os.path.join(self.config['path_data'], 'db', 'system.db')
self.app.config['SQLALCHEMY_DATABASE_URI'] = f'sqlite:///{db_path}' # 3.0에서 필수 self.app.config['SQLALCHEMY_DATABASE_URI'] = f'sqlite:///{db_path}?check_same_thread=False' # 3.0에서 필수
self.app.config['SQLALCHEMY_BINDS'] = {'system':f'sqlite:///{db_path}'} self.app.config['SQLALCHEMY_BINDS'] = {'system':f'sqlite:///{db_path}?check_same_thread=False'}
self.app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False self.app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
_ = os.path.join(self.config['path_data'], 'plugins') _ = os.path.join(self.config['path_data'], 'plugins')
@@ -124,7 +124,7 @@ class Framework:
for package_name in plugins: for package_name in plugins:
db_path = os.path.join(self.config['path_data'], 'db', f'{package_name}.db') db_path = os.path.join(self.config['path_data'], 'db', f'{package_name}.db')
self.app.config['SQLALCHEMY_BINDS'][package_name] = f'sqlite:///{db_path}' self.app.config['SQLALCHEMY_BINDS'][package_name] = f'sqlite:///{db_path}?check_same_thread=False'
self.db = SQLAlchemy(self.app, session_options={"autoflush": False, "expire_on_commit": False}) self.db = SQLAlchemy(self.app, session_options={"autoflush": False, "expire_on_commit": False})
#with self.app.app_context(): #with self.app.app_context():
# self.db.session.expunge_all() # self.db.session.expunge_all()

View File

@@ -130,6 +130,175 @@ $("body").on('click', '#globalCliboardBtn', function(e) {
}); });
// 사용 on / off
$("body").on('change', '#globalSchedulerSwitchBtn', function(e) {
e.preventDefault();
var ret = $(this).prop('checked');
$.ajax({
url: '/'+PACKAGE_NAME+'/ajax/' + MODULE_NAME + '/scheduler',
type: "POST",
cache: false,
data: {scheduler : ret},
dataType: "json",
success: function () {}
});
});
$("body").on('change', '#globalSchedulerSwitchPageBtn', function(e) {
var ret = $(this).prop('checked');
$.ajax({
url: '/'+PACKAGE_NAME+'/ajax/' + MODULE_NAME + '/' + PAGE_NAME + '/scheduler',
type: "POST",
cache: false,
data: {scheduler : ret},
dataType: "json",
success: function () {}
});
});
$("body").on('click', '#globalOneExecuteBtn', function(e) {
e.preventDefault();
$.ajax({
url: '/'+PACKAGE_NAME+'/ajax/' + MODULE_NAME + '/one_execute',
type: "POST",
cache: false,
data: {},
dataType: "json",
success: function (ret) {
if (ret=='scheduler' || ret=='thread') {
$.notify('<strong>작업을 시작하였습니다. ('+ret+')</strong>', {
type: 'success'
});
} else if (ret == 'is_running') {
$.notify('<strong>작업중입니다.</strong>', {
type: 'warning'
});
} else {
$.notify('<strong>작업 시작에 실패하였습니다.</strong>', {
type: 'warning'
});
}
}
});
});
$("body").on('click', '#globalOneExecutePageBtn', function(e) {
e.preventDefault();
$.ajax({
url: '/'+PACKAGE_NAME+'/ajax/' + MODULE_NAME + '/' + PAGE_NAME + '/one_execute',
type: "POST",
cache: false,
data: {sub:sub},
dataType: "json",
success: function (ret) {
if (ret=='scheduler' || ret=='thread') {
$.notify('<strong>작업을 시작하였습니다. ('+ret+')</strong>', {
type: 'success'
});
} else if (ret == 'is_running') {
$.notify('<strong>작업중입니다.</strong>', {
type: 'warning'
});
} else {
$.notify('<strong>작업 시작에 실패하였습니다.</strong>', {
type: 'warning'
});
}
}
});
});
$("body").on('click', '#globalImmediatelyExecuteBtn', function(e){
e.preventDefault();
$.ajax({
url: '/'+PACKAGE_NAME+'/ajax/' + MODULE_NAME + '/immediately_execute',
type: "POST",
cache: false,
data: {},
dataType: "json",
success: function (ret) {
if (ret.msg != null) notify(ret.msg, ret.ret);
}
});
});
$("body").on('click', '#globalImmediatelyExecutePageBtn', function(e){
e.preventDefault();
$.ajax({
url: '/'+PACKAGE_NAME+'/ajax/' + MODULE_NAME + '/' + PAGE_NAME + '/immediately_execute',
type: "POST",
cache: false,
data: {},
dataType: "json",
success: function (ret) {
if (ret.msg != null) notify(ret.msg, ret.ret);
}
});
});
$("body").on('click', '#globalDbDeleteBtn', function(e){
e.preventDefault();
document.getElementById("confirm_title").innerHTML = "DB 삭제";
document.getElementById("confirm_body").innerHTML = "전체 목록을 삭제 하시겠습니까?";
$('#confirm_button').attr('onclick', "globalDbDelete();");
$("#confirm_modal").modal();
return;
});
function globalDbDelete() {
$.ajax({
url: '/'+PACKAGE_NAME+'/ajax/' + MODULE_NAME + '/reset_db',
type: "POST",
cache: false,
data: {},
dataType: "json",
success: function (data) {
if (data) {
$.notify('<strong>삭제하였습니다.</strong>', {
type: 'success'
});
} else {
$.notify('<strong>삭제에 실패하였습니다.</strong>',{
type: 'warning'
});
}
}
});
}
$("body").on('click', '#globalDbDeletePageBtn', function(e){
e.preventDefault();
document.getElementById("confirm_title").innerHTML = "DB 삭제";
document.getElementById("confirm_body").innerHTML = "전체 목록을 삭제 하시겠습니까?";
$('#confirm_button').attr('onclick', "globalDbDeletePage();");
$("#confirm_modal").modal();
return;
});
function globalDbDeletePage() {
$.ajax({
url: '/'+PACKAGE_NAME+'/ajax/' + MODULE_NAME + '/' + PAGE_NAME + '/reset_db',
type: "POST",
cache: false,
data: {sub:sub},
dataType: "json",
success: function (data) {
if (data) {
$.notify('<strong>삭제하였습니다.</strong>', {
type: 'success'
});
} else {
$.notify('<strong>삭제에 실패하였습니다.</strong>',{
type: 'warning'
});
}
}
});
}
/////////////////////////////////////// ///////////////////////////////////////
// Global - 함수 // Global - 함수
/////////////////////////////////////// ///////////////////////////////////////
@@ -180,6 +349,88 @@ function shutdown_confirm() {
} }
///////////////////////////////////////
// 리스트 화면 기본
///////////////////////////////////////
function globalRequestSearch(page, move_top=true) {
var formData = getFormdata('#form_search')
formData += '&page=' + page;
$.ajax({
url: '/' + PACKAGE_NAME + '/ajax/' + MODULE_NAME + '/web_list',
type: "POST",
cache: false,
data: formData,
dataType: "json",
success: function (data) {
current_data = data;
if (move_top)
window.scrollTo(0,0);
make_list(data.list)
make_page_html(data.paging)
}
});
}
function make_page_html(data) {
str = ' \
<div class="d-inline-block"></div> \
<div class="row mb-3"> \
<div class="col-sm-12"> \
<div class="btn-toolbar" style="justify-content: center;" role="toolbar" aria-label="Toolbar with button groups" > \
<div class="btn-group btn-group-sm mr-2" role="group" aria-label="First group">'
if (data.prev_page) {
str += '<button id="gloablSearchPageBtn" data-page="' + (data.start_page-1) + '" type="button" class="btn btn-secondary">&laquo;</button>'
}
for (var i = data.start_page ; i <= data.last_page ; i++) {
str += '<button id="gloablSearchPageBtn" data-page="' + i +'" type="button" class="btn btn-secondary" ';
if (i == data.current_page) {
str += 'disabled';
}
str += '>'+i+'</button>';
}
if (data.next_page) {
str += '<button id="gloablSearchPageBtn" data-page="' + (data.last_page+1) + '" type="button" class="btn btn-secondary">&raquo;</button>'
}
str += '</div> \
</div> \
</div> \
</div> \
'
document.getElementById("page1").innerHTML = str;
document.getElementById("page2").innerHTML = str;
}
$("body").on('click', '#gloablSearchPageBtn', function(e){
e.preventDefault();
globalRequestSearch($(this).data('page'), false);
});
$("body").on('click', '#globalSearchSearchBtn', function(e){
e.preventDefault();
globalRequestSearch(1, false);
});
$("body").on('click', '#globalSearchResetBtn', function(e){
e.preventDefault();
$("#order").val('desc');
$("#option1").val('all');
$("#option2").val('all');
$("#keyword").val('');
globalRequestSearch(1, false);
});
/////////////////////////////////////// ///////////////////////////////////////
// 파일 선택 모달 // 파일 선택 모달
/////////////////////////////////////// ///////////////////////////////////////

View File

@@ -84,6 +84,14 @@ function j_row_info(left, right, l=2, r=8) {
return str; return str;
} }
function j_progress(id, width, label) {
var str = '';
str += '<div class="progress" style="height: 25px;">'
str += '<div id="'+id+'" class="progress-bar" style="background-color:yellow;width:'+width+'%"></div>';
str += '<div id="'+id+'_label" class="justify-content-center d-flex w-100 position-absolute" style="margin-top:2px">'+label+'</div>';
str += '</div>'
return str;
}
@@ -110,6 +118,19 @@ function j_row_info(left, right, l=2, r=8) {
function make_log(key, value, left=2, right=10) {
row = m_col(left, key, aligh='right');
row += m_col(right, value, aligh='left');
return row;
}
@@ -118,17 +139,107 @@ function text_color(text, color='red') {
return '<span style="color:'+color+'; font-weight:bold">' + text + '</span>'; return '<span style="color:'+color+'; font-weight:bold">' + text + '</span>';
} }
function m_table(id, heads) {
str += '<table id="result_table" class="table table-sm tableRowHover " ><thead class="thead-dark"><tr> \
<th style="width:10%;text-align:center;">NO</th> \
<th style="width:15%;text-align:center;">물어본 숫자</th> \ ///////////////////////////////////////
<th style="width:10%;text-align:center;">스트라이크</th> \ // UI - 확장설정 - dropdown
<th style="width:10%;text-align:center;">볼</th> \ ///////////////////////////////////////
<th style="width:15%;text-align:center;">가능한 숫자 수</th> \
<th style="width:40%;text-align:center;">Action</th> \ document.addEventListener("DOMContentLoaded", function(){
</tr></thead><tbody id="list">'; /////// Prevent closing from click inside dropdown
document.querySelectorAll('.dropdown-menu').forEach(function(element){
element.addEventListener('click', function (e) {
e.stopPropagation();
});
})
// make it as accordion for smaller screens
if (window.innerWidth < 992) {
// close all inner dropdowns when parent is closed
document.querySelectorAll('.navbar .dropdown').forEach(function(everydropdown){
everydropdown.addEventListener('hidden.bs.dropdown', function () {
// after dropdown is hidden, then find all submenus
this.querySelectorAll('.submenu').forEach(function(everysubmenu){
// hide every submenu as well
everysubmenu.style.display = 'none';
});
})
});
document.querySelectorAll('.dropdown-menu a').forEach(function(element){
element.addEventListener('click', function (e) {
let nextEl = this.nextElementSibling;
if(nextEl && nextEl.classList.contains('submenu')) {
// prevent opening link if link needs to open dropdown
e.preventDefault();
console.log(nextEl);
if(nextEl.style.display == 'block'){
nextEl.style.display = 'none';
} else {
nextEl.style.display = 'block';
} }
}
});
})
}
// end if innerWidth
});
// DOMContentLoaded end
///////////////////////////////////////
@@ -218,14 +329,7 @@ function m_tab_content(name, content, active) {
return str; return str;
} }
function m_progress(id, width, label) {
var str = '';
str += '<div class="progress" style="height: 25px;">'
str += '<div id="'+id+'" class="progress-bar" style="background-color:yellow;width:'+width+'%"></div>';
str += '<div id="'+id+'_label" class="justify-content-center d-flex w-100 position-absolute" style="margin-top:2px">'+label+'</div>';
str += '</div>'
return str;
}
function m_progress2(id, width, label) { function m_progress2(id, width, label) {
@@ -239,94 +343,12 @@ function m_progress2(id, width, label) {
function make_page_html(data) {
str = ' \
<div class="d-inline-block"></div> \
<div class="row mb-3"> \
<div class="col-sm-12"> \
<div class="btn-toolbar" style="justify-content: center;" role="toolbar" aria-label="Toolbar with button groups" > \
<div class="btn-group btn-group-sm mr-2" role="group" aria-label="First group">'
if (data.prev_page) {
str += '<button id="page" data-page="' + (data.start_page-1) + '" type="button" class="btn btn-secondary">&laquo;</button>'
}
for (var i = data.start_page ; i <= data.last_page ; i++) {
str += '<button id="page" data-page="' + i +'" type="button" class="btn btn-secondary" ';
if (i == data.current_page) {
str += 'disabled';
}
str += '>'+i+'</button>';
}
if (data.next_page) {
str += '<button id="page" data-page="' + (data.last_page+1) + '" type="button" class="btn btn-secondary">&raquo;</button>'
}
str += '</div> \
</div> \
</div> \
</div> \
'
document.getElementById("page1").innerHTML = str;
document.getElementById("page2").innerHTML = str;
}
function make_log(key, value, left=2, right=10) {
row = m_col(left, key, aligh='right');
row += m_col(right, value, aligh='left');
return row;
}
///////////////////////////////////////
// UI - 확장설정 - dropdown
///////////////////////////////////////
document.addEventListener("DOMContentLoaded", function(){
/////// Prevent closing from click inside dropdown
document.querySelectorAll('.dropdown-menu').forEach(function(element){
element.addEventListener('click', function (e) {
e.stopPropagation();
});
})
// make it as accordion for smaller screens
if (window.innerWidth < 992) {
// close all inner dropdowns when parent is closed
document.querySelectorAll('.navbar .dropdown').forEach(function(everydropdown){
everydropdown.addEventListener('hidden.bs.dropdown', function () {
// after dropdown is hidden, then find all submenus
this.querySelectorAll('.submenu').forEach(function(everysubmenu){
// hide every submenu as well
everysubmenu.style.display = 'none';
});
})
});
document.querySelectorAll('.dropdown-menu a').forEach(function(element){
element.addEventListener('click', function (e) {
let nextEl = this.nextElementSibling;
if(nextEl && nextEl.classList.contains('submenu')) {
// prevent opening link if link needs to open dropdown
e.preventDefault();
console.log(nextEl);
if(nextEl.style.display == 'block'){
nextEl.style.display = 'none';
} else {
nextEl.style.display = 'block';
}
}
});
})
}
// end if innerWidth
});
// DOMContentLoaded end
///////////////////////////////////////

View File

@@ -193,202 +193,14 @@ function streaming_kill(command, data={}) {
// Global.. JS 파일로 뺄것 // Global.. JS 파일로 뺄것
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
// 사용 on / off
$('#global_scheduler').change(function() {
var ret = $(this).prop('checked');
$.ajax({
url: '/'+package_name+'/ajax/scheduler',
type: "POST",
cache: false,
data: {scheduler : ret},
dataType: "json",
success: function (list) {
}
});
});
$('#global_scheduler_sub').change(function() {
var ret = $(this).prop('checked');
$.ajax({
url: '/'+package_name+'/ajax/scheduler',
type: "POST",
cache: false,
data: {scheduler : ret, sub:sub},
dataType: "json",
success: function (list) {
}
});
});
function global_setting_save_function() {
var formData = getFormdata('#setting');
$.ajax({
url: '/'+package_name+'/ajax/setting_save',
type: "POST",
cache: false,
data: formData,
dataType: "json",
success: function (ret) {
if (ret) {
$.notify('<strong>설정을 저장하였습니다.</strong>', {
type: 'success'
});
} else {
$.notify('<strong>설정 저장에 실패하였습니다.</strong>', {
type: 'warning'
});
}
}
});
}
$("#global_one_execute_btn").click(function(e) {
//$("body").on('click', '#one_execute_btn', function(e){
e.preventDefault();
$.ajax({
url: '/' + package_name + '/ajax/one_execute',
type: "POST",
cache: false,
data: {},
dataType: "json",
success: function (ret) {
if (ret=='scheduler' || ret=='thread') {
$.notify('<strong>작업을 시작하였습니다. ('+ret+')</strong>', {
type: 'success'
});
} else if (ret == 'is_running') {
$.notify('<strong>작업중입니다.</strong>', {
type: 'warning'
});
} else {
$.notify('<strong>작업 시작에 실패하였습니다.</strong>', {
type: 'warning'
});
}
}
});
});
$("#global_one_execute_sub_btn").click(function(e) {
//$("body").on('click', '#one_execute_btn', function(e){
e.preventDefault();
$.ajax({
url: '/' + package_name + '/ajax/one_execute',
type: "POST",
cache: false,
data: {sub:sub},
dataType: "json",
success: function (ret) {
if (ret=='scheduler' || ret=='thread') {
$.notify('<strong>작업을 시작하였습니다. ('+ret+')</strong>', {
type: 'success'
});
} else if (ret == 'is_running') {
$.notify('<strong>작업중입니다.</strong>', {
type: 'warning'
});
} else {
$.notify('<strong>작업 시작에 실패하였습니다.</strong>', {
type: 'warning'
});
}
}
});
});
$("body").on('click', '#global_immediately_execute_sub_btn', function(e){
e.preventDefault();
$.ajax({
url: '/' + package_name + '/ajax/immediately_execute',
type: "POST",
cache: false,
data: {sub:sub},
dataType: "json",
success: function (ret) {
if (ret.msg != null) notify(ret.msg, ret.ret);
}
});
});
$("body").on('click', '#global_reset_db_btn', function(e){
e.preventDefault();
document.getElementById("confirm_title").innerHTML = "DB 삭제";
document.getElementById("confirm_body").innerHTML = "전체 목록을 삭제 하시겠습니까?";
$('#confirm_button').attr('onclick', "global_db_delete();");
$("#confirm_modal").modal();
return;
});
function global_db_delete() {
$.ajax({
url: '/' + package_name + '/ajax/reset_db',
type: "POST",
cache: false,
data: {},
dataType: "json",
success: function (data) {
if (data) {
$.notify('<strong>삭제하였습니다.</strong>', {
type: 'success'
});
} else {
$.notify('<strong>삭제에 실패하였습니다.</strong>',{
type: 'warning'
});
}
}
});
}
$("body").on('click', '#global_reset_db_sub_btn', function(e){
e.preventDefault();
document.getElementById("confirm_title").innerHTML = "DB 삭제";
document.getElementById("confirm_body").innerHTML = "전체 목록을 삭제 하시겠습니까?";
$('#confirm_button').attr('onclick', "global_db_delete_sub();");
$("#confirm_modal").modal();
return;
});
function global_db_delete_sub() {
$.ajax({
url: '/' + package_name + '/ajax/reset_db',
type: "POST",
cache: false,
data: {sub:sub},
dataType: "json",
success: function (data) {
if (data) {
$.notify('<strong>삭제하였습니다.</strong>', {
type: 'success'
});
} else {
$.notify('<strong>삭제에 실패하였습니다.</strong>',{
type: 'warning'
});
}
}
});
}
function global_sub_request_search(page, move_top=true) {
var formData = getFormdata('#form_search')
formData += '&page=' + page;
$.ajax({
url: '/' + package_name + '/ajax/' + sub + '/web_list',
type: "POST",
cache: false,
data: formData,
dataType: "json",
success: function (data) {
current_data = data;
if (move_top)
window.scrollTo(0,0);
make_list(data.list)
make_page_html(data.paging)
}
});
}
$("body").on('click', '#global_json_btn', function(e){ $("body").on('click', '#global_json_btn', function(e){
@@ -438,79 +250,11 @@ $("body").on('click', '#global_remove_btn', function(e) {
//#######################################################
//플러그인 - 모듈 - 서브 구조하에서 서브 관련 함수
function global_send_command_sub(command, arg1, arg2, arg3, modal_title, callback) {
$.ajax({
url: '/' + package_name + '/ajax/' + sub + '/' + sub2 + '/command',
type: "POST",
cache: false,
data:{command:command, arg1:arg1, arg2:arg2, arg3},
dataType: "json",
success: function (ret) {
console.log(ret);
if (ret.msg != null) notify(ret.msg, ret.ret);
if (ret.modal != null) m_modal(ret.modal, modal_title, false);
if (ret.json != null) m_modal(ret.json, modal_title, true);
if (callback != null) callback(ret);
}
});
}
$("body").on('click', '#global_one_execute_sublogic_btn', function(e){
e.preventDefault();
$.ajax({
url: '/' + package_name + '/ajax/' + sub + '/' + sub2 + '/one_execute',
type: "POST",
cache: false,
data: {},
dataType: "json",
success: function (ret) {
if (ret=='scheduler' || ret=='thread') {
$.notify('<strong>작업을 시작하였습니다. ('+ret+')</strong>', {
type: 'success'
});
} else if (ret == 'is_running') {
$.notify('<strong>작업중입니다.</strong>', {
type: 'warning'
});
} else {
$.notify('<strong>작업 시작에 실패하였습니다.</strong>', {
type: 'warning'
});
}
}
});
});
$("body").on('click', '#global_immediately_execute_sublogic_btn', function(e){
e.preventDefault();
$.ajax({
url: '/' + package_name + '/ajax/' + sub + '/' + sub2 + '/immediately_execute',
type: "POST",
cache: false,
data: {},
dataType: "json",
success: function (ret) {
if (ret.msg != null) notify(ret.msg, ret.ret);
}
});
});
$('#global_scheduler_sublogic').change(function() {
var ret = $(this).prop('checked');
$.ajax({
url: '/'+package_name+'/ajax/' + sub + '/' + sub2 + '/scheduler',
type: "POST",
cache: false,
data: {scheduler : ret},
dataType: "json",
success: function (list) {
}
});
});
@@ -615,50 +359,3 @@ $('#global_scheduler_sublogic').change(function() {
// 이동한 함수
function global_send_command(command, data={}) {
data['command'] = command;
$.ajax({
url: '/' + package_name + '/ajax/' + sub + '/command',
type: "POST",
cache: false,
data:data,
dataType: "json",
success: function (data) {
notify(data['msg'], data['ret']);
}
});
}
function global_send_command2(command, arg1, arg2, arg3, modal_title, callback) {
$.ajax({
url: '/' + package_name + '/ajax/' + sub + '/command',
type: "POST",
cache: false,
data:{command:command, arg1:arg1, arg2:arg2, arg3},
dataType: "json",
success: function (ret) {
if (ret.msg != null) notify(ret.msg, ret.ret);
if (ret.modal != null) m_modal(ret.modal, modal_title, false);
if (ret.json != null) m_modal(ret.json, modal_title, true);
if (callback != null) callback(ret);
}
});
}

View File

@@ -97,58 +97,8 @@ function m_tab_content(name, content, active) {
return str; return str;
} }
function m_progress(id, width, label) {
var str = '';
str += '<div class="progress" style="height: 25px;">'
str += '<div id="'+id+'" class="progress-bar" style="background-color:yellow;width:'+width+'%"></div>';
str += '<div id="'+id+'_label" class="justify-content-center d-flex w-100 position-absolute" style="margin-top:2px">'+label+'</div>';
str += '</div>'
return str;
}
function m_progress2(id, width, label) {
var str = '';
str += '<div class="progress" style="height: 25px;">'
str += '<div id="'+id+'" class="progress-bar" style="background-color:yellow;width:'+width+'%"></div>';
str += '<div id="'+id+'_label" class="justify-content-center d-flex w-100 position-absolute" style="margin:0px; margin-top:2px">'+label+'</div>';
str += '</div>'
return str;
}
function make_page_html(data) {
str = ' \
<div class="d-inline-block"></div> \
<div class="row mb-3"> \
<div class="col-sm-12"> \
<div class="btn-toolbar" style="justify-content: center;" role="toolbar" aria-label="Toolbar with button groups" > \
<div class="btn-group btn-group-sm mr-2" role="group" aria-label="First group">'
if (data.prev_page) {
str += '<button id="page" data-page="' + (data.start_page-1) + '" type="button" class="btn btn-secondary">&laquo;</button>'
}
for (var i = data.start_page ; i <= data.last_page ; i++) {
str += '<button id="page" data-page="' + i +'" type="button" class="btn btn-secondary" ';
if (i == data.current_page) {
str += 'disabled';
}
str += '>'+i+'</button>';
}
if (data.next_page) {
str += '<button id="page" data-page="' + (data.last_page+1) + '" type="button" class="btn btn-secondary">&raquo;</button>'
}
str += '</div> \
</div> \
</div> \
</div> \
'
document.getElementById("page1").innerHTML = str;
document.getElementById("page2").innerHTML = str;
}

View File

@@ -246,7 +246,25 @@
{% endmacro %} {% endmacro %}
<!-- 스케쥴링 작동 버튼-->
{% macro setting_global_scheduler_button(is_include, is_running, id='scheduler', left='스케쥴링 작동', desc=['On : 스케쥴링 시작','Off : 스케쥴링 중지']) %}
{{ setting_top(left) }}
<div class="input-group col-sm-3">
{% if is_include == 'True' %}
<input id="globalSchedulerSwitchBtn" name="globalSchedulerSwitchBtn" class="form-control form-control-sm" type="checkbox" data-toggle="toggle" checked>
{% else %}
<input id="globalSchedulerSwitchBtn" name="globalSchedulerSwitchBtn" class="form-control form-control-sm" type="checkbox" data-toggle="toggle">
{% endif %}
{% if is_running == 'True' %}
<span style="padding-left:10px; padding-top: 8px;">동작중</span>
{% else %}
{% if is_include == 'True' %}
<span style="padding-left:10px; padding-top: 8px;">대기중</span>
{% endif %}
{% endif %}
</div>
{{ setting_bottom(desc) }}
{% endmacro %}
@@ -728,25 +746,7 @@ macros.setting_button_with_info([['toggle_btn', 'Toggle', [{'key':'category', 'v
{% endmacro %} {% endmacro %}
<!-- 스케쥴링 작동 버튼-->
{% macro setting_global_scheduler_button(is_include, is_running, id='scheduler', left='스케쥴링 작동', desc=['On : 스케쥴링 시작','Off : 스케쥴링 중지']) %}
{{ setting_top(left) }}
<div class="input-group col-sm-3">
{% if is_include == 'True' %}
<input id="global_scheduler" name="global_scheduler" class="form-control form-control-sm" type="checkbox" data-toggle="toggle" checked>
{% else %}
<input id="global_scheduler" name="global_scheduler" class="form-control form-control-sm" type="checkbox" data-toggle="toggle">
{% endif %}
{% if is_running == 'True' %}
<span style="padding-left:10px; padding-top: 8px;">동작중</span>
{% else %}
{% if is_include == 'True' %}
<span style="padding-left:10px; padding-top: 8px;">대기중</span>
{% endif %}
{% endif %}
</div>
{{ setting_bottom(desc) }}
{% endmacro %}
{% macro setting_global_scheduler_sub_button(is_include, is_running, id='scheduler', left='스케쥴링 작동', desc=['On : 스케쥴링 시작','Off : 스케쥴링 중지']) %} {% macro setting_global_scheduler_sub_button(is_include, is_running, id='scheduler', left='스케쥴링 작동', desc=['On : 스케쥴링 시작','Off : 스케쥴링 중지']) %}
{{ setting_top(left) }} {{ setting_top(left) }}

View File

@@ -1 +1 @@
VERSION="4.0.30" VERSION="4.0.32"

View File

@@ -44,7 +44,7 @@ class PluginBase(object):
self.ModelSetting = None self.ModelSetting = None
if setting.get('use_db', True): if setting.get('use_db', True):
db_path = os.path.join(F.config['path_data'], 'db', f'{self.package_name}.db') db_path = os.path.join(F.config['path_data'], 'db', f'{self.package_name}.db')
F.app.config['SQLALCHEMY_BINDS'][self.package_name] = f"sqlite:///{db_path}" F.app.config['SQLALCHEMY_BINDS'][self.package_name] = f"sqlite:///{db_path}?check_same_thread=False"
if setting.get('use_default_setting', True): if setting.get('use_default_setting', True):
self.ModelSetting = get_model_setting(self.package_name, self.logger) self.ModelSetting = get_model_setting(self.package_name, self.logger)

View File

@@ -84,47 +84,47 @@ class Logic(object):
self.P.logger.error(traceback.format_exc()) self.P.logger.error(traceback.format_exc())
def scheduler_start(self, sub): def scheduler_start(self, module_name):
try: try:
job_id = '%s_%s' % (self.P.package_name, sub) job_id = '%s_%s' % (self.P.package_name, module_name)
module = self.get_module(sub) module = self.get_module(module_name)
job = Job(self.P.package_name, job_id, module.get_scheduler_interval(), self.scheduler_function, module.get_scheduler_desc(), args=sub) job = Job(self.P.package_name, job_id, module.get_scheduler_interval(), self.scheduler_function, module.get_scheduler_desc(), args=module_name)
F.scheduler.add_job_instance(job) F.scheduler.add_job_instance(job)
except Exception as e: except Exception as e:
self.P.logger.error(f'Exception:{str(e)}') self.P.logger.error(f'Exception:{str(e)}')
self.P.logger.error(traceback.format_exc()) self.P.logger.error(traceback.format_exc())
def scheduler_stop(self, sub): def scheduler_stop(self, module_name):
try: try:
job_id = '%s_%s' % (self.P.package_name, sub) job_id = '%s_%s' % (self.P.package_name, module_name)
F.scheduler.remove_job(job_id) F.scheduler.remove_job(job_id)
except Exception as e: except Exception as e:
self.P.logger.error(f'Exception:{str(e)}') self.P.logger.error(f'Exception:{str(e)}')
self.P.logger.error(traceback.format_exc()) self.P.logger.error(traceback.format_exc())
def scheduler_function(self, sub): def scheduler_function(self, module_name):
try: try:
module = self.get_module(sub) module = self.get_module(module_name)
module.scheduler_function() module.scheduler_function()
except Exception as e: except Exception as e:
self.P.logger.error(f'Exception:{str(e)}') self.P.logger.error(f'Exception:{str(e)}')
self.P.logger.error(traceback.format_exc()) self.P.logger.error(traceback.format_exc())
def reset_db(self,sub): def reset_db(self, module_name):
try: try:
module = self.get_module(sub) module = self.get_module(module_name)
return module.reset_db() return module.reset_db()
except Exception as e: except Exception as e:
self.P.logger.error(f'Exception:{str(e)}') self.P.logger.error(f'Exception:{str(e)}')
self.P.logger.error(traceback.format_exc()) self.P.logger.error(traceback.format_exc())
def one_execute(self, sub): def one_execute(self, module_name):
self.P.logger.debug('one_execute :%s', sub) self.P.logger.debug('one_execute :%s', module_name)
try: try:
job_id = '%s_%s' % (self.P.package_name, sub) job_id = '%s_%s' % (self.P.package_name, module_name)
if F.scheduler.is_include(job_id): if F.scheduler.is_include(job_id):
if F.scheduler.is_running(job_id): if F.scheduler.is_running(job_id):
ret = 'is_running' ret = 'is_running'
@@ -134,7 +134,7 @@ class Logic(object):
else: else:
def func(): def func():
time.sleep(2) time.sleep(2)
self.scheduler_function(sub) self.scheduler_function(module_name)
threading.Thread(target=func, args=()).start() threading.Thread(target=func, args=()).start()
ret = 'thread' ret = 'thread'
except Exception as e: except Exception as e:
@@ -143,12 +143,12 @@ class Logic(object):
ret = 'fail' ret = 'fail'
return ret return ret
def immediately_execute(self, sub): def immediately_execute(self, module_name):
self.P.logger.debug('immediately_execute :%s', sub) self.P.logger.debug('immediately_execute :%s', module_name)
try: try:
def func(): def func():
time.sleep(1) time.sleep(1)
self.scheduler_function(sub) self.scheduler_function(module_name)
threading.Thread(target=func, args=()).start() threading.Thread(target=func, args=()).start()
ret = {'ret':'success', 'msg':'실행합니다.'} ret = {'ret':'success', 'msg':'실행합니다.'}
except Exception as e: except Exception as e:
@@ -175,6 +175,65 @@ class Logic(object):
self.P.logger.error(f'Exception:{str(e)}') self.P.logger.error(f'Exception:{str(e)}')
self.P.logger.error(traceback.format_exc()) self.P.logger.error(traceback.format_exc())
####################################################### #######################################################
# 플러그인 - 모듈 - 페이지 구조하에서 서브 관련 함수 # 플러그인 - 모듈 - 페이지 구조하에서 서브 관련 함수

View File

@@ -14,6 +14,16 @@ class PluginModuleBase(object):
self.socketio_list = None self.socketio_list = None
self.page_list = None self.page_list = None
def get_module(self, module_name):
try:
for module in self.P.module_list:
if module.name == module_name:
return module
except Exception as e:
self.P.logger.error(f'Exception:{str(e)}')
self.P.logger.error(traceback.format_exc())
# set_module_list 대응 # set_module_list 대응
def set_page_list(self, page_list): def set_page_list(self, page_list):
try: try:
@@ -87,7 +97,7 @@ class PluginModuleBase(object):
def get_first_menu(self): def get_first_menu(self):
return self.first_menu return self.first_menu
def get_scheduler_name(self): def get_scheduler_id(self):
return '%s_%s' % (self.P.package_name, self.name) return '%s_%s' % (self.P.package_name, self.name)
def dump(self, data): def dump(self, data):

View File

@@ -1,5 +1,5 @@
import traceback import traceback
from datetime import datetime from datetime import datetime, timedelta
from framework import F from framework import F
@@ -8,7 +8,7 @@ class ModelBase(F.db.Model):
__abstract__ = True __abstract__ = True
__table_args__ = {'mysql_collate': 'utf8_general_ci'} __table_args__ = {'mysql_collate': 'utf8_general_ci'}
model_setting = None model_setting = None
logger = None P = None
def __repr__(self): def __repr__(self):
return repr(self.as_dict()) return repr(self.as_dict())
@@ -24,8 +24,8 @@ class ModelBase(F.db.Model):
F.db.session.commit() F.db.session.commit()
return self return self
except Exception as e: except Exception as e:
self.logger.error(f'Exception:{str(e)}') self.P.logger.error(f'Exception:{str(e)}')
self.logger.error(traceback.format_exc()) self.P.logger.error(traceback.format_exc())
@classmethod @classmethod
def get_paging_info(cls, count, current_page, page_size): def get_paging_info(cls, count, current_page, page_size):
@@ -48,8 +48,8 @@ class ModelBase(F.db.Model):
F.logger.debug('paging : c:%s %s %s %s %s %s', count, paging['total_page'], paging['prev_page'], paging['next_page'] , paging['start_page'], paging['last_page']) F.logger.debug('paging : c:%s %s %s %s %s %s', count, paging['total_page'], paging['prev_page'], paging['next_page'] , paging['start_page'], paging['last_page'])
return paging return paging
except Exception as e: except Exception as e:
F.logger.error(f'Exception:{str(e)}') cls.P.logger.error(f'Exception:{str(e)}')
F.logger.error(traceback.format_exc()) cls.P.logger.error(traceback.format_exc())
@classmethod @classmethod
@@ -58,8 +58,8 @@ class ModelBase(F.db.Model):
with F.app.app_context(): with F.app.app_context():
return F.db.session.query(cls).filter_by(id=int(id)).first() return F.db.session.query(cls).filter_by(id=int(id)).first()
except Exception as e: except Exception as e:
F.logger.error(f'Exception:{str(e)}') cls.P.logger.error(f'Exception:{str(e)}')
F.logger.error(traceback.format_exc()) cls.P.logger.error(traceback.format_exc())
@classmethod @classmethod
@@ -71,8 +71,8 @@ class ModelBase(F.db.Model):
tmp = [x.as_dict() for x in tmp] tmp = [x.as_dict() for x in tmp]
return tmp return tmp
except Exception as e: except Exception as e:
F.logger.error(f'Exception:{str(e)}') cls.P.logger.error(f'Exception:{str(e)}')
F.logger.error(traceback.format_exc()) cls.P.logger.error(traceback.format_exc())
@@ -84,20 +84,32 @@ class ModelBase(F.db.Model):
F.db.session.commit() F.db.session.commit()
return True return True
except Exception as e: except Exception as e:
F.logger.error(f'Exception:{str(e)}') cls.P.logger.error(f'Exception:{str(e)}')
F.logger.error(traceback.format_exc()) cls.P.logger.error(traceback.format_exc())
return False return False
@classmethod @classmethod
def delete_all(cls): def delete_all(cls, days=None):
try: try:
with F.app.app_context(): with F.app.app_context():
if days == None:
F.db.session.query(cls).delete() F.db.session.query(cls).delete()
F.db.session.commit() F.db.session.commit()
else:
now = datetime.datetime.now()
ago = now - datetime.timedelta(days=days)
ret = F.db.session.query(cls).filter(cls.created_time > ago).delete()
cls.P.debug(ret)
return True return True
except Exception as e: except Exception as e:
F.logger.error(f'Exception:{str(e)}') cls.P.logger.error(f'Exception:{str(e)}')
F.logger.error(traceback.format_exc()) cls.P.logger.error(traceback.format_exc())
return False return False
@@ -111,12 +123,12 @@ class ModelBase(F.db.Model):
if 'page' in req.form: if 'page' in req.form:
page = int(req.form['page']) page = int(req.form['page'])
if 'keyword' in req.form: if 'keyword' in req.form:
search = req.form['keyword'] search = req.form['keyword'].strip()
option1 = req.form.get('option1', 'all') option1 = req.form.get('option1', 'all')
option2 = req.form.get('option2', 'all') option2 = req.form.get('option2', 'all')
order = req.form['order'] if 'order' in req.form else 'desc' order = req.form['order'] if 'order' in req.form else 'desc'
query = cls.make_query(order=order, search=search, option1=option1, option2=option2) query = cls.make_query(req, order=order, search=search, option1=option1, option2=option2)
count = query.count() count = query.count()
query = query.limit(page_size).offset((page-1)*page_size) query = query.limit(page_size).offset((page-1)*page_size)
F.logger.debug('cls count:%s', count) F.logger.debug('cls count:%s', count)
@@ -124,21 +136,21 @@ class ModelBase(F.db.Model):
ret['list'] = [item.as_dict() for item in lists] ret['list'] = [item.as_dict() for item in lists]
ret['paging'] = cls.get_paging_info(count, page, page_size) ret['paging'] = cls.get_paging_info(count, page, page_size)
try: try:
if cls.model_setting is not None and cls.__tablename__ is not None: if cls.P.ModelSetting is not None and cls.__tablename__ is not None:
cls.model_setting.set(f'{cls.__tablename__}_last_list_option', f'{order}|{page}|{search}|{option1}|{option2}') cls.P.ModelSetting.set(f'{cls.__tablename__}_last_list_option', f'{order}|{page}|{search}|{option1}|{option2}')
except Exception as e: except Exception as e:
F.logger.error('Exception:%s', e) F.logger.error('Exception:%s', e)
F.logger.error(traceback.format_exc()) F.logger.error(traceback.format_exc())
F.logger.error(f'{cls.__tablename__}_last_list_option ERROR!' ) F.logger.error(f'{cls.__tablename__}_last_list_option ERROR!' )
return ret return ret
except Exception as e: except Exception as e:
F.logger.error('Exception:%s', e) cls.P.logger.error('Exception:%s', e)
F.logger.error(traceback.format_exc()) cls.P.logger.error(traceback.format_exc())
# 오버라이딩 # 오버라이딩
@classmethod @classmethod
def make_query(cls, order='desc', search='', option1='all', option2='all'): def make_query(cls, req, order='desc', search='', option1='all', option2='all'):
with F.app.app_context(): with F.app.app_context():
query = F.db.session.query(cls) query = F.db.session.query(cls)
return query return query

View File

@@ -101,7 +101,7 @@ def get_model_setting(package_name, logger, table_name=None):
for key, value in req.form.items(): for key, value in req.form.items():
if key in ['scheduler', 'is_running']: if key in ['scheduler', 'is_running']:
continue continue
if key.startswith('global_') or key.startswith('tmp_') or key.startswith('_'): if key.startswith('global') or key.startswith('tmp_') or key.startswith('_'):
continue continue
#logger.debug('Key:%s Value:%s', key, value) #logger.debug('Key:%s Value:%s', key, value)
if ModelSetting.get(key) != value: if ModelSetting.get(key) != value:

View File

@@ -92,42 +92,37 @@ def default_route(P):
for module in P.module_list: for module in P.module_list:
module.setting_save_after(change_list) module.setting_save_after(change_list)
return jsonify(ret) return jsonify(ret)
elif sub == 'scheduler':
sub = request.form['sub']
go = request.form['scheduler']
P.logger.debug('scheduler :%s', go)
if go == 'true':
P.logic.scheduler_start(sub)
else:
P.logic.scheduler_stop(sub)
return jsonify(go)
elif sub == 'reset_db':
sub = request.form['sub']
ret = P.logic.reset_db(sub)
return jsonify(ret)
elif sub == 'one_execute':
sub = request.form['sub']
ret = P.logic.one_execute(sub)
return jsonify(ret)
elif sub == 'immediately_execute':
sub = request.form['sub']
ret = P.logic.immediately_execute(sub)
return jsonify(ret)
except Exception as exception: except Exception as exception:
P.logger.error('Exception:%s', exception) P.logger.error('Exception:%s', exception)
P.logger.error(traceback.format_exc()) P.logger.error(traceback.format_exc())
@P.blueprint.route('/ajax/<mod>/<cmd>', methods=['GET', 'POST']) @P.blueprint.route('/ajax/<module_name>/<cmd>', methods=['GET', 'POST'])
@login_required @login_required
def second_ajax(mod, cmd): def second_ajax(module_name, cmd):
try: try:
for module in P.module_list: for module in P.module_list:
if mod == module.name: if module_name == module.name:
if cmd == 'command': if cmd == 'command':
return module.process_command(request.form['command'], request.form.get('arg1'), request.form.get('arg2'), request.form.get('arg3'), request) return module.process_command(request.form['command'], request.form.get('arg1'), request.form.get('arg2'), request.form.get('arg3'), request)
else: else:
return module.process_ajax(cmd, request) return module.process_ajax(cmd, request)
elif cmd == 'scheduler':
go = request.form['scheduler']
if go == 'true':
P.logic.scheduler_start(module_name)
else:
P.logic.scheduler_stop(module_name)
return jsonify(go)
elif cmd == 'reset_db':
ret = P.logic.reset_db(module_name)
return jsonify(ret)
elif cmd == 'one_execute':
ret = P.logic.one_execute(module_name)
return jsonify(ret)
elif cmd == 'immediately_execute':
ret = P.logic.immediately_execute(module_name)
return jsonify(ret)
except Exception as exception: except Exception as exception:
P.logger.error('Exception:%s', exception) P.logger.error('Exception:%s', exception)
P.logger.error(traceback.format_exc()) P.logger.error(traceback.format_exc())