This commit is contained in:
soju6jan
2022-10-02 20:18:05 +09:00
parent b9c3aac91f
commit 29930fdef7
150 changed files with 53982 additions and 0 deletions

14
lib/plugin/__init__.py Normal file
View File

@@ -0,0 +1,14 @@
from framework import logger
from .model_setting import get_model_setting
from .logic import Logic
from .route import default_route, default_route_socketio_module, default_route_socketio_page, default_route_single_module
from .logic_module_base import PluginModuleBase, PluginPageBase
from .ffmpeg_queue import FfmpegQueueEntity, FfmpegQueue
from .model_base import ModelBase
from .create_plugin import create_plugin_instance
import os, sys, traceback, re, threading, time
from datetime import datetime, timedelta
from flask import Blueprint, render_template, jsonify, redirect, request
from framework import *

7
lib/plugin/common.py Normal file
View File

@@ -0,0 +1,7 @@
"""
import os, sys, traceback, re, threading, time
from datetime import datetime, timedelta
from flask import Blueprint, render_template, jsonify, redirect, request
from framework import frame, F, login_required, check_api, Job, SystemModelSetting
from plugin import PluginModuleBase, get_model_setting, Logic, default_route, create_plugin_instance
"""

View File

@@ -0,0 +1,87 @@
import os, traceback
from flask import Blueprint
from framework import F
from support.base.yaml import SupportYaml
from . import get_model_setting, Logic, default_route, default_route_single_module
class PluginBase(object):
package_name = None
logger = None
blueprint = None
menu = None
plugin_info = None
ModelSetting = None
logic = None
module_list = None
home_module = None
def __init__(self, setting):
try:
is_system = ('system' == os.path.basename(os.path.dirname(setting['filepath'])))
self.status = ""
self.setting = setting
info_filepath = os.path.join(os.path.dirname(setting['filepath']), 'info.yaml')
if os.path.exists(info_filepath) == False and is_system == False:
return
if is_system:
self.package_name = 'system'
else:
self.plugin_info = SupportYaml.read_yaml(info_filepath)
self.package_name = self.plugin_info['package_name']
self.logger = F.get_logger(self.package_name)
self.blueprint = Blueprint(self.package_name, self.package_name, url_prefix=f'/{self.package_name}', template_folder=os.path.join(os.path.dirname(setting['filepath']), 'templates'), static_folder=os.path.join(os.path.dirname(setting['filepath']), 'static'))
self.menu = setting['menu']
self.setting_menu = setting['setting_menu']
self.ModelSetting = None
if setting.get('use_db', True):
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}"
if setting.get('use_default_setting', True):
self.ModelSetting = get_model_setting(self.package_name, self.logger)
self.module_list = []
self.home_module = setting.get('home_module')
self.status = "init_success"
self.config = {}
except Exception as e:
self.logger.error(f'Exception:{str(e)}')
self.logger.error(traceback.format_exc())
self.status = 'init_fail'
def set_module_list(self, mod_list):
try:
for mod in mod_list:
mod_ins = mod(self)
self.module_list.append(mod_ins)
except Exception as e:
F.logger.error(f'Exception:{str(e)}')
F.logger.error(traceback.format_exc())
self.logic = Logic(self)
route_mode = self.setting.get('default_route', 'normal')
if route_mode == 'normal':
default_route(self)
elif route_mode == 'single':
default_route_single_module(self)
def plugin_load(self):
self.logic.plugin_load()
def plugin_unload(self):
self.logic.plugin_unload()
def get_first_manual_path(self):
for __ in self.menu['list']:
if __['uri'] == 'manual' and len(__['list']) > 0:
return __['list'][0]['uri']
def create_plugin_instance(config):
ins = PluginBase(config)
return ins

300
lib/plugin/ffmpeg_queue.py Normal file
View File

@@ -0,0 +1,300 @@
# -*- coding: utf-8 -*-
#########################################################
# python
import os, sys, traceback
import threading, time
from datetime import datetime
import abc
# third-party
# sjva 공용
#########################################################
class FfmpegQueueEntity(abc.ABCMeta('ABC', (object,), {'__slots__': ()})):
def __init__(self, P, module_logic, info):
self.P = P
self.module_logic = module_logic
self.entity_id = -1 #FfmpegQueueEntity.static_index
self.info = info
self.url = None
self.ffmpeg_status = -1
self.ffmpeg_status_kor = u'대기중'
self.ffmpeg_percent = 0
self.ffmpeg_arg = None
self.cancel = False
self.created_time = datetime.now().strftime('%m-%d %H:%M:%S')
self.savepath = None
self.filename = None
self.filepath = None
self.quality = None
self.headers = None
#FfmpegQueueEntity.static_index += 1
#FfmpegQueueEntity.entity_list.append(self)
def get_video_url(self):
return self.url
def get_video_filepath(self):
return self.filepath
@abc.abstractmethod
def refresh_status(self):
pass
@abc.abstractmethod
def info_dict(self, tmp):
pass
def donwload_completed(self):
pass
def as_dict(self):
tmp = {}
tmp['entity_id'] = self.entity_id
tmp['url'] = self.url
tmp['ffmpeg_status'] = self.ffmpeg_status
tmp['ffmpeg_status_kor'] = self.ffmpeg_status_kor
tmp['ffmpeg_percent'] = self.ffmpeg_percent
tmp['ffmpeg_arg'] = self.ffmpeg_arg
tmp['cancel'] = self.cancel
tmp['created_time'] = self.created_time#.strftime('%m-%d %H:%M:%S')
tmp['savepath'] = self.savepath
tmp['filename'] = self.filename
tmp['filepath'] = self.filepath
tmp['quality'] = self.quality
#tmp['current_speed'] = self.ffmpeg_arg['current_speed'] if self.ffmpeg_arg is not None else ''
tmp = self.info_dict(tmp)
return tmp
class FfmpegQueue(object):
def __init__(self, P, max_ffmpeg_count):
self.P = P
self.static_index = 1
self.entity_list = []
self.current_ffmpeg_count = 0
self.download_queue = None
self.download_thread = None
self.max_ffmpeg_count = max_ffmpeg_count
if self.max_ffmpeg_count is None or self.max_ffmpeg_count == '':
self.max_ffmpeg_count = 1
def queue_start(self):
try:
if self.download_queue is None:
self.download_queue = queue.Queue()
if self.download_thread is None:
self.download_thread = threading.Thread(target=self.download_thread_function, args=())
self.download_thread.daemon = True
self.download_thread.start()
except Exception as exception:
self.P.logger.error('Exception:%s', exception)
self.P.logger.error(traceback.format_exc())
def download_thread_function(self):
while True:
try:
while True:
try:
if self.current_ffmpeg_count < self.max_ffmpeg_count:
break
time.sleep(5)
except Exception as exception:
self.P.logger.error('Exception:%s', exception)
self.P.logger.error(traceback.format_exc())
self.P.logger.error('current_ffmpeg_count : %s', self.current_ffmpeg_count)
self.P.logger.error('max_ffmpeg_count : %s', self.max_ffmpeg_count)
break
entity = self.download_queue.get()
if entity.cancel:
continue
#from .logic_ani24 import LogicAni24
#entity.url = LogicAni24.get_video_url(entity.info['code'])
video_url = entity.get_video_url()
if video_url is None:
entity.ffmpeg_status_kor = 'URL실패'
entity.refresh_status()
#plugin.socketio_list_refresh()
continue
import ffmpeg
#max_pf_count = 0
#save_path = ModelSetting.get('download_path')
#if ModelSetting.get('auto_make_folder') == 'True':
# program_path = os.path.join(save_path, entity.info['filename'].split('.')[0])
# save_path = program_path
#try:
# if not os.path.exists(save_path):
# os.makedirs(save_path)
#except:
# logger.debug('program path make fail!!')
# 파일 존재여부 체크
filepath = entity.get_video_filepath()
if os.path.exists(filepath):
entity.ffmpeg_status_kor = '파일 있음'
entity.ffmpeg_percent = 100
entity.refresh_status()
#plugin.socketio_list_refresh()
continue
dirname = os.path.dirname(filepath)
if not os.path.exists(dirname):
os.makedirs(dirname)
f = ffmpeg.Ffmpeg(video_url, os.path.basename(filepath), plugin_id=entity.entity_id, listener=self.ffmpeg_listener, call_plugin=self.P.package_name, save_path=dirname, headers=entity.headers)
f.start()
self.current_ffmpeg_count += 1
self.download_queue.task_done()
except Exception as exception:
self.P.logger.error('Exception:%s', exception)
self.P.logger.error(traceback.format_exc())
def ffmpeg_listener(self, **arg):
import ffmpeg
entity = self.get_entity_by_entity_id(arg['plugin_id'])
if entity is None:
return
if arg['type'] == 'status_change':
if arg['status'] == ffmpeg.Status.DOWNLOADING:
pass
elif arg['status'] == ffmpeg.Status.COMPLETED:
entity.donwload_completed()
elif arg['status'] == ffmpeg.Status.READY:
pass
elif arg['type'] == 'last':
self.current_ffmpeg_count += -1
elif arg['type'] == 'log':
pass
elif arg['type'] == 'normal':
pass
entity.ffmpeg_arg = arg
entity.ffmpeg_status = int(arg['status'])
entity.ffmpeg_status_kor = str(arg['status'])
entity.ffmpeg_percent = arg['data']['percent']
entity.ffmpeg_arg['status'] = str(arg['status'])
#self.P.logger.debug(arg)
#import plugin
#arg['status'] = str(arg['status'])
#plugin.socketio_callback('status', arg)
entity.refresh_status()
#FfmpegQueueEntity.static_index += 1
#FfmpegQueueEntity.entity_list.append(self)
def add_queue(self, entity):
try:
#entity = QueueEntity.create(info)
#if entity is not None:
# LogicQueue.download_queue.put(entity)
# return True
entity.entity_id = self.static_index
self.static_index += 1
self.entity_list.append(entity)
self.download_queue.put(entity)
return True
except Exception as exception:
self.P.logger.error('Exception:%s', exception)
self.P.logger.error(traceback.format_exc())
return False
def set_max_ffmpeg_count(self, max_ffmpeg_count):
self.max_ffmpeg_count = max_ffmpeg_count
def get_max_ffmpeg_count(self):
return self.max_ffmpeg_count
def command(self, cmd, entity_id):
self.P.logger.debug('command :%s %s', cmd, entity_id)
ret = {}
try:
if cmd == 'cancel':
self.P.logger.debug('command :%s %s', cmd, entity_id)
entity = self.get_entity_by_entity_id(entity_id)
if entity is not None:
if entity.ffmpeg_status == -1:
entity.cancel = True
entity.ffmpeg_status_kor = "취소"
#entity.refresh_status()
ret['ret'] = 'refresh'
elif entity.ffmpeg_status != 5:
ret['ret'] = 'notify'
ret['log'] = '다운로드중 상태가 아닙니다.'
else:
idx = entity.ffmpeg_arg['data']['idx']
import ffmpeg
ffmpeg.Ffmpeg.stop_by_idx(idx)
entity.refresh_status()
ret['ret'] = 'refresh'
elif cmd == 'reset':
if self.download_queue is not None:
with self.download_queue.mutex:
self.download_queue.queue.clear()
for _ in self.entity_list:
if _.ffmpeg_status == 5:
import ffmpeg
idx = _.ffmpeg_arg['data']['idx']
ffmpeg.Ffmpeg.stop_by_idx(idx)
self.entity_list = []
ret['ret'] = 'refresh'
elif cmd == 'delete_completed':
new_list = []
for _ in self.entity_list:
if _.ffmpeg_status_kor in [u'파일 있음', u'취소', u'사용자중지']:
continue
if _.ffmpeg_status != 7:
new_list.append(_)
self.entity_list = new_list
ret['ret'] = 'refresh'
elif cmd == 'remove':
new_list = []
for _ in self.entity_list:
if _.entity_id == entity_id:
continue
new_list.append(_)
self.entity_list = new_list
ret['ret'] = 'refresh'
return ret
except Exception as exception:
self.P.logger.error('Exception:%s', exception)
self.P.logger.error(traceback.format_exc())
def get_entity_by_entity_id(self, entity_id):
for _ in self.entity_list:
if _.entity_id == entity_id:
return _
return None
def get_entity_list(self):
ret = []
for x in self.entity_list:
tmp = x.as_dict()
ret.append(tmp)
return ret

236
lib/plugin/logic.py Normal file
View File

@@ -0,0 +1,236 @@
import traceback, time, threading
from framework import F, Job
#########################################################
class Logic(object):
db_default = {
'recent_menu_plugin' : '',
}
def __init__(self, P):
self.P = P
def plugin_load(self):
try:
#self.P.logger.debug('%s plugin_load', self.P.package_name)
self.db_init()
for module in self.P.module_list:
module.migration()
for module in self.P.module_list:
module.plugin_load()
if module.page_list is not None:
for page_instance in module.page_list:
page_instance.plugin_load()
if self.P.ModelSetting is not None:
for module in self.P.module_list:
key = f'{module.name}_auto_start'
if self.P.ModelSetting.has_key(key) and self.P.ModelSetting.get_bool(key):
self.scheduler_start(module.name)
if module.page_list is not None:
for page_instance in module.page_list:
key = f'{module.name}_{page_instance.name}_auto_start'
if self.P.ModelSetting.has_key(key) and self.P.ModelSetting.get_bool(key):
self.scheduler_start_sub(module.name, page_instance.name)
except Exception as exception:
self.P.logger.error('Exception:%s', exception)
self.P.logger.error(traceback.format_exc())
def db_init(self):
try:
if self.P.ModelSetting is None:
return
for key, value in Logic.db_default.items():
if F.db.session.query(self.P.ModelSetting).filter_by(key=key).count() == 0:
F.db.session.add(self.P.ModelSetting(key, value))
for module in self.P.module_list:
if module.page_list is not None:
for page_instance in module.page_list:
if page_instance.db_default is not None:
for key, value in page_instance.db_default.items():
if F.db.session.query(self.P.ModelSetting).filter_by(key=key).count() == 0:
F.db.session.add(self.P.ModelSetting(key, value))
if module.db_default is not None:
for key, value in module.db_default.items():
if F.db.session.query(self.P.ModelSetting).filter_by(key=key).count() == 0:
F.db.session.add(self.P.ModelSetting(key, value))
F.db.session.commit()
except Exception as exception:
self.P.logger.error('Exception:%s', exception)
self.P.logger.error(traceback.format_exc())
def plugin_unload(self):
try:
self.P.logger.debug('%s plugin_unload', self.P.package_name)
for module in self.P.module_list:
module.plugin_unload()
if module.page_list is not None:
for page_instance in module.page_list:
page_instance.plugin_unload()
except Exception as exception:
self.P.logger.error('Exception:%s', exception)
self.P.logger.error(traceback.format_exc())
def scheduler_start(self, sub):
try:
job_id = '%s_%s' % (self.P.package_name, sub)
module = self.get_module(sub)
job = Job(self.P.package_name, job_id, module.get_scheduler_interval(), self.scheduler_function, module.get_scheduler_desc(), args=sub)
F.scheduler.add_job_instance(job)
except Exception as exception:
self.P.logger.error('Exception:%s', exception)
self.P.logger.error(traceback.format_exc())
def scheduler_stop(self, sub):
try:
job_id = '%s_%s' % (self.P.package_name, sub)
F.scheduler.remove_job(job_id)
except Exception as exception:
self.P.logger.error('Exception:%s', exception)
self.P.logger.error(traceback.format_exc())
def scheduler_function(self, sub):
try:
module = self.get_module(sub)
module.scheduler_function()
except Exception as exception:
self.P.logger.error('Exception:%s', exception)
self.P.logger.error(traceback.format_exc())
def reset_db(self,sub):
try:
module = self.get_module(sub)
return module.reset_db()
except Exception as exception:
self.P.logger.error('Exception:%s', exception)
self.P.logger.error(traceback.format_exc())
def one_execute(self, sub):
self.P.logger.debug('one_execute :%s', sub)
try:
job_id = '%s_%s' % (self.P.package_name, sub)
if F.scheduler.is_include(job_id):
if F.scheduler.is_running(job_id):
ret = 'is_running'
else:
F.scheduler.execute_job(job_id)
ret = 'scheduler'
else:
def func():
time.sleep(2)
self.scheduler_function(sub)
threading.Thread(target=func, args=()).start()
ret = 'thread'
except Exception as exception:
self.P.logger.error('Exception:%s', exception)
self.P.logger.error(traceback.format_exc())
ret = 'fail'
return ret
def immediately_execute(self, sub):
self.P.logger.debug('immediately_execute :%s', sub)
try:
def func():
time.sleep(1)
self.scheduler_function(sub)
threading.Thread(target=func, args=()).start()
ret = {'ret':'success', 'msg':'실행합니다.'}
except Exception as exception:
self.P.logger.error('Exception:%s', exception)
self.P.logger.error(traceback.format_exc())
ret = {'ret' : 'danger', 'msg':str(exception)}
return ret
def get_module(self, sub):
try:
for module in self.P.module_list:
if module.name == sub:
return module
except Exception as exception:
self.P.logger.error('Exception:%s', exception)
self.P.logger.error(traceback.format_exc())
def process_telegram_data(self, data, target=None):
try:
for module in self.P.module_list:
if target is None or target.startswith(module.name):
module.process_telegram_data(data, target=target)
except Exception as exception:
self.P.logger.error('Exception:%s', exception)
self.P.logger.error(traceback.format_exc())
#######################################################
# 플러그인 - 모듈 - 페이지 구조하에서 서브 관련 함수
def scheduler_start_sub(self, module_name, page_name):
try:
#self.P.logger.warning('scheduler_start_sub')
job_id = f'{self.P.package_name}_{module_name}_{page_name}'
ins_module = self.get_module(module_name)
ins_page = ins_module.get_page(page_name)
job = Job(self.P.package_name, job_id, ins_page.get_scheduler_interval(), ins_page.scheduler_function, ins_page.get_scheduler_desc(), args=None)
F.scheduler.add_job_instance(job)
except Exception as exception:
self.P.logger.error('Exception:%s', exception)
self.P.logger.error(traceback.format_exc())
def scheduler_stop_sub(self, module_name, page_name):
try:
job_id = f'{self.P.package_name}_{module_name}_{page_name}'
F.scheduler.remove_job(job_id)
except Exception as exception:
self.P.logger.error('Exception:%s', exception)
self.P.logger.error(traceback.format_exc())
def scheduler_function_sub(self, module_name, page_name):
try:
ins_module = self.get_module(module_name)
ins_sub = ins_module.get_page(page_name)
ins_sub.scheduler_function()
except Exception as exception:
self.P.logger.error('Exception:%s', exception)
self.P.logger.error(traceback.format_exc())
def one_execute_sub(self, module_name, page_name):
try:
job_id = f'{self.P.package_name}_{module_name}_{page_name}'
if F.scheduler.is_include(job_id):
if F.scheduler.is_running(job_id):
ret = 'is_running'
else:
F.scheduler.execute_job(job_id)
ret = 'scheduler'
else:
def func():
time.sleep(2)
self.scheduler_function_sub(module_name, page_name)
threading.Thread(target=func, args=()).start()
ret = 'thread'
except Exception as exception:
self.P.logger.error('Exception:%s', exception)
self.P.logger.error(traceback.format_exc())
ret = 'fail'
return ret
def immediately_execute_sub(self, module_name, page_name):
self.P.logger.debug(f'immediately_execute : {module_name} {page_name}')
try:
def func():
time.sleep(1)
self.scheduler_function_sub(module_name, page_name)
threading.Thread(target=func, args=()).start()
ret = {'ret':'success', 'msg':'실행합니다.'}
except Exception as exception:
self.P.logger.error('Exception:%s', exception)
self.P.logger.error(traceback.format_exc())
ret = {'ret' : 'danger', 'msg':str(exception)}
return ret

View File

@@ -0,0 +1,171 @@
import traceback
class PluginModuleBase(object):
db_default = None
def __init__(self, P, first_menu=None, name=None, scheduler_desc=None):
self.P = P
self.scheduler_desc = scheduler_desc
self.first_menu = first_menu
self.name = name
self.socketio_list = None
self.page_list = None
# set_module_list 대응
def set_page_list(self, page_list):
try:
self.page_list = []
for mod in page_list:
mod_ins = mod(self.P, self)
self.page_list.append(mod_ins)
except Exception as e:
self.P.logger.error(f'Exception:{str(e)}')
self.P.logger.error(traceback.format_exc())
def get_page(self, page_name):
try:
for page in self.page_list:
if page_name == page.name:
return page
except Exception as e:
self.P.logger.error(f'Exception:{str(e)}')
self.P.logger.error(traceback.format_exc())
def process_menu(self, sub):
pass
def process_ajax(self, sub, req):
pass
def process_command(self, command, arg1, arg2, arg3, req):
pass
def process_api(self, sub, req):
pass
def process_normal(self, sub, req):
pass
def scheduler_function(self):
pass
def reset_db(self):
pass
def plugin_load(self):
pass
def plugin_unload(self):
pass
def setting_save_after(self, change_list):
pass
def process_telegram_data(self, data, target=None):
pass
def migration(self):
pass
#################################################################
def get_scheduler_desc(self):
return self.scheduler_desc
def get_scheduler_interval(self):
if self.P is not None and self.P.ModelSetting is not None and self.name is not None:
return self.P.ModelSetting.get('{module_name}_interval'.format(module_name=self.name))
def get_first_menu(self):
return self.first_menu
def get_scheduler_name(self):
return '%s_%s' % (self.P.package_name, self.name)
def dump(self, data):
if type(data) in [type({}), type([])]:
import json
return '\n' + json.dumps(data, indent=4, ensure_ascii=False)
else:
return str(data)
def socketio_connect(self):
pass
def socketio_disconnect(self):
pass
class PluginPageBase(object):
db_default = None
def __init__(self, P, parent, name=None, scheduler_desc=None):
self.P = P
self.parent = parent
self.name = name
self.scheduler_desc = scheduler_desc
self.socketio_list = None
def process_ajax(self, sub, req):
pass
def scheduler_function(self):
pass
def plugin_load(self):
pass
def plugin_unload(self):
pass
def get_scheduler_desc(self):
return self.scheduler_desc
def get_scheduler_interval(self):
if self.P is not None and self.P.ModelSetting is not None and self.parent.name is not None and self.name is not None:
return self.P.ModelSetting.get(f'{self.parent.name}_{self.name}_interval')
def get_scheduler_name(self):
return f'{self.P.package_name}_{self.parent.name}_{self.name}'
def process_api(self, sub, req):
pass
def process_normal(self, sub, req):
pass
def reset_db(self):
pass
def setting_save_after(self, change_list):
pass
def process_telegram_data(self, data, target=None):
pass
def migration(self):
pass
#################################################################
def process_menu(self, sub):
pass

145
lib/plugin/model_base.py Normal file
View File

@@ -0,0 +1,145 @@
# -*- coding: utf-8 -*-
#########################################################
# python
import traceback
from datetime import datetime
# third-party
# sjva 공용
from framework import db
from framework.util import Util
#########################################################
class ModelBase(db.Model):
__abstract__ = True
__table_args__ = {'mysql_collate': 'utf8_general_ci'}
model_setting = None
logger = None
def __repr__(self):
return repr(self.as_dict())
def as_dict(self):
return {x.name: getattr(self, x.name).strftime('%m-%d %H:%M:%S') if isinstance(getattr(self, x.name), datetime) else getattr(self, x.name) for x in self.__table__.columns}
def save(self):
try:
db.session.add(self)
db.session.commit()
except Exception as e:
self.logger.error(f'Exception:{str(e)}')
self.logger.error(traceback.format_exc())
@classmethod
def get_paging_info(cls, count, current_page, page_size):
try:
paging = {}
paging['prev_page'] = True
paging['next_page'] = True
if current_page <= 10:
paging['prev_page'] = False
paging['total_page'] = int(count / page_size) + 1
if count % page_size == 0:
paging['total_page'] -= 1
paging['start_page'] = int((current_page-1)/10) * 10 + 1
paging['last_page'] = paging['total_page'] if paging['start_page'] + 9 > paging['total_page'] else paging['start_page'] + 9
if paging['last_page'] == paging['total_page']:
paging['next_page'] = False
paging['current_page'] = current_page
paging['count'] = count
cls.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
except Exception as e:
cls.logger.error(f'Exception:{str(e)}')
cls.logger.error(traceback.format_exc())
@classmethod
def get_by_id(cls, id):
try:
return db.session.query(cls).filter_by(id=id).first()
except Exception as e:
cls.logger.error(f'Exception:{str(e)}')
cls.logger.error(traceback.format_exc())
@classmethod
def get_list(cls, by_dict=False):
try:
tmp = db.session.query(cls).all()
if by_dict:
tmp = [x.as_dict() for x in tmp]
return tmp
except Exception as e:
cls.logger.error(f'Exception:{str(e)}')
cls.logger.error(traceback.format_exc())
@classmethod
def delete_by_id(cls, id):
try:
db.session.query(cls).filter_by(id=id).delete()
db.session.commit()
return True
except Exception as e:
cls.logger.error(f'Exception:{str(e)}')
cls.logger.error(traceback.format_exc())
return False
@classmethod
def delete_all(cls):
try:
db.session.query(cls).delete()
db.session.commit()
return True
except Exception as e:
cls.logger.error(f'Exception:{str(e)}')
cls.logger.error(traceback.format_exc())
return False
@classmethod
def web_list(cls, req):
try:
ret = {}
page = 1
page_size = 30
search = ''
if 'page' in req.form:
page = int(req.form['page'])
if 'keyword' in req.form:
search = req.form['keyword']
option1 = req.form.get('option1', 'all')
option2 = req.form.get('option2', 'all')
order = req.form['order'] if 'order' in req.form else 'desc'
query = cls.make_query(order=order, search=search, option1=option1, option2=option2)
count = query.count()
query = query.limit(page_size).offset((page-1)*page_size)
cls.logger.debug('cls count:%s', count)
lists = query.all()
ret['list'] = [item.as_dict() for item in lists]
ret['paging'] = cls.get_paging_info(count, page, page_size)
try:
if cls.model_setting 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}')
except Exception as e:
cls.logger.error('Exception:%s', e)
cls.logger.error(traceback.format_exc())
cls.logger.error(f'{cls.__tablename__}_last_list_option ERROR!' )
return ret
except Exception as e:
cls.logger.error('Exception:%s', e)
cls.logger.error(traceback.format_exc())
# 오버라이딩
@classmethod
def make_query(cls, order='desc', search='', option1='all', option2='all'):
query = db.session.query(cls)
return query

138
lib/plugin/model_setting.py Normal file
View File

@@ -0,0 +1,138 @@
# -*- coding: utf-8 -*-
#########################################################
# python
import os, traceback
# third-party
# sjva 공용
from framework import frame, db
from framework.util import Util
#########################################################
def get_model_setting(package_name, logger, table_name=None):
class ModelSetting(db.Model):
__tablename__ = '%s_setting' % package_name if table_name is None else table_name
__table_args__ = {'mysql_collate': 'utf8_general_ci'}
__bind_key__ = package_name
id = db.Column(db.Integer, primary_key=True)
key = db.Column(db.String, unique=True, nullable=False)
value = db.Column(db.String, nullable=False)
def __init__(self, key, value):
self.key = key
self.value = value
def __repr__(self):
return repr(self.as_dict())
def as_dict(self):
return {x.name: getattr(self, x.name) for x in self.__table__.columns}
@staticmethod
def get(key):
try:
ret = db.session.query(ModelSetting).filter_by(key=key).first()
if ret is not None:
return ret.value.strip()
return None
except Exception as exception:
logger.error('Exception:%s %s', exception, key)
logger.error(traceback.format_exc())
@staticmethod
def has_key(key):
return (db.session.query(ModelSetting).filter_by(key=key).first() is not None)
@staticmethod
def get_int(key):
try:
return int(ModelSetting.get(key))
except Exception as exception:
logger.error('Exception:%s %s', exception, key)
logger.error(traceback.format_exc())
@staticmethod
def get_bool(key):
try:
return (ModelSetting.get(key) == 'True')
except Exception as exception:
logger.error('Exception:%s %s', exception, key)
logger.error(traceback.format_exc())
@staticmethod
def set(key, value):
try:
item = db.session.query(ModelSetting).filter_by(key=key).with_for_update().first()
if item is not None:
item.value = value.strip() if value is not None else value
db.session.commit()
else:
db.session.add(ModelSetting(key, value.strip()))
db.session.commit()
except Exception as exception:
logger.error('Exception:%s %s', exception, key)
logger.error(traceback.format_exc())
@staticmethod
def to_dict():
try:
ret = Util.db_list_to_dict(db.session.query(ModelSetting).all())
ret['package_name'] = package_name
return ret
except Exception as exception:
logger.error('Exception:%s', exception)
logger.error(traceback.format_exc())
@staticmethod
def setting_save(req):
try:
change_list = []
for key, value in req.form.items():
if key in ['scheduler', 'is_running']:
continue
if key.startswith('global_') or key.startswith('tmp_') or key.startswith('_'):
continue
#logger.debug('Key:%s Value:%s', key, value)
if ModelSetting.get(key) != value:
change_list.append(key)
entity = db.session.query(ModelSetting).filter_by(key=key).with_for_update().first()
entity.value = value
db.session.commit()
return True, change_list
except Exception as exception:
logger.error('Exception:%s', exception)
logger.error(traceback.format_exc())
logger.debug('Error Key:%s Value:%s', key, value)
return False, []
@staticmethod
def get_list(key, delimeter='\n', comment=' #'):
try:
value = ModelSetting.get(key).replace('\n', delimeter)
if comment is None:
values = [x.strip() for x in value.split(delimeter)]
else:
values = [x.split(comment)[0].strip() for x in value.split(delimeter)]
values = ModelSetting.get_list_except_empty(values)
return values
except Exception as exception:
logger.error('Exception:%s', exception)
logger.error(traceback.format_exc())
logger.error('Error Key:%s Value:%s', key, value)
@staticmethod
def get_list_except_empty(source):
tmp = []
for _ in source:
if _.strip().startswith('#'):
continue
if _.strip() != '':
tmp.append(_.strip())
return tmp
return ModelSetting

423
lib/plugin/route.py Normal file
View File

@@ -0,0 +1,423 @@
# -*- coding: utf-8 -*-
# python
import traceback, os
import json
# third-party
from flask import Blueprint, request, render_template, redirect, jsonify
from flask_login import login_required
from flask_socketio import SocketIO, emit, send
# sjva 공용
from framework import socketio, check_api
from support.base.util import AlchemyEncoder
# 패키지
#########################################################
def default_route(P):
@P.blueprint.route('/')
def home():
if P.ModelSetting is not None:
tmp = P.ModelSetting.get('recent_menu_plugin')
if tmp is not None and tmp != '':
tmps = tmp.split('|')
if len(tmps) == 2:
return redirect('/{package_name}/{sub}/{sub2}'.format(package_name=P.package_name, sub=tmps[0], sub2=tmps[1]))
elif len(tmps) == 1 and not (P.package_name =='system' and tmps[0] == 'logout'):
return redirect('/{package_name}/{sub}'.format(package_name=P.package_name, sub=tmps[0]))
return redirect('/{package_name}/{home_module}'.format(package_name=P.package_name, home_module=P.home_module))
@P.blueprint.route('/<sub>', methods=['GET', 'POST'])
@login_required
def first_menu(sub):
try:
if P.ModelSetting is not None and (P.package_name == 'system' and sub != 'home'):
P.ModelSetting.set('recent_menu_plugin', '{}'.format(sub))
for module in P.module_list:
if sub == module.name:
first_menu = module.get_first_menu()
if first_menu:
return redirect('/{package_name}/{sub}/{first_menu}'.format(package_name=P.package_name, sub=sub, first_menu=module.get_first_menu()))
else:
return module.process_menu(None, request)
if sub == 'log':
return render_template('log.html', package=P.package_name)
elif sub == 'manual':
#return redirect(f"/{P.package_name}/manual/{P.menu['second']['manual'][0][0]}")
try:
return redirect(f"/{P.package_name}/manual/{P.menu['sub2']['manual'][0][0]}")
except:
return redirect(f"/{P.package_name}/manual/{P.get_first_manual_path()}")
return render_template('sample.html', title='%s - %s' % (P.package_name, sub))
except Exception as exception:
P.logger.error('Exception:%s', exception)
P.logger.error(traceback.format_exc())
@P.blueprint.route('/manual/<path:path>', methods=['GET', 'POST'])
@login_required
def manual(path):
try:
plugin_root = os.path.dirname(P.blueprint.template_folder)
filepath = os.path.join(plugin_root, *path.split('/'))
from tool_base import ToolBaseFile
data = ToolBaseFile.read(filepath)
return render_template('manual.html', data=data)
except Exception as exception:
P.logger.error('Exception:%s', exception)
P.logger.error(traceback.format_exc())
@P.blueprint.route('/<sub>/<sub2>', methods=['GET', 'POST'])
@login_required
def second_menu(sub, sub2):
if P.ModelSetting is not None:
P.ModelSetting.set('recent_menu_plugin', '{}|{}'.format(sub, sub2))
try:
for module in P.module_list:
if sub == module.name:
return module.process_menu(sub2, request)
if sub == 'log':
return render_template('log.html', package=P.package_name)
return render_template('sample.html', title='%s - %s' % (P.package_name, sub))
except Exception as exception:
P.logger.error('Exception:%s', exception)
P.logger.error(traceback.format_exc())
#########################################################
# For UI
#########################################################
@P.blueprint.route('/ajax/<sub>', methods=['GET', 'POST'])
@login_required
def ajax(sub):
P.logger.debug('AJAX %s %s', P.package_name, sub)
try:
# global
if sub == 'setting_save':
ret, change_list = P.ModelSetting.setting_save(request)
for module in P.module_list:
module.setting_save_after(change_list)
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:
P.logger.error('Exception:%s', exception)
P.logger.error(traceback.format_exc())
@P.blueprint.route('/ajax/<mod>/<cmd>', methods=['GET', 'POST'])
@login_required
def second_ajax(mod, cmd):
try:
for module in P.module_list:
if mod == module.name:
if cmd == 'command':
return module.process_command(request.form['command'], request.form.get('arg1'), request.form.get('arg2'), request.form.get('arg3'), request)
else:
return module.process_ajax(cmd, request)
except Exception as exception:
P.logger.error('Exception:%s', exception)
P.logger.error(traceback.format_exc())
@P.blueprint.route('/ajax/<module_name>/<page_name>/<command>', methods=['GET', 'POST'])
@login_required
def sub_ajax(module_name, page_name, command):
try:
ins_module = P.get_module(module_name)
ins_page = ins_module.get_page(page_name)
if ins_page != None:
if command == 'scheduler':
#sub = page_name
go = request.form['scheduler']
P.logger.debug('scheduler :%s', go)
if go == 'true':
P.logic.scheduler_start_sub(module_name, page_name)
else:
P.logic.scheduler_stop_sub(module_name, page_name)
return jsonify(go)
#elif command == 'reset_db':
# sub = request.form['sub']
# ret = P.logic.reset_db(sub)
# return jsonify(ret)
elif command == 'one_execute':
ret = P.logic.one_execute_sub(module_name, page_name)
return jsonify(ret)
elif command == 'immediately_execute':
ret = P.logic.immediately_execute_sub(module_name, page_name)
return jsonify(ret)
else:
return ins_page.process_ajax(command, request)
P.logger.error(f"not process ajax : {P.package_name} {module_name} {page_name} {command}")
except Exception as exception:
P.logger.error('Exception:%s', exception)
P.logger.error(traceback.format_exc())
#########################################################
# API - 외부
#########################################################
# 단일 모듈인 경우 모듈이름을 붙이기 불편하여 추가.
@P.blueprint.route('/api/<sub2>', methods=['GET', 'POST'])
@check_api
def api_first(sub2):
try:
for module in P.module_list:
return module.process_api(sub2, request)
except Exception as exception:
P.logger.error('Exception:%s', exception)
P.logger.error(traceback.format_exc())
@P.blueprint.route('/api/<sub>/<sub2>', methods=['GET', 'POST'])
@check_api
def api(sub, sub2):
try:
for module in P.module_list:
if sub == module.name:
return module.process_api(sub2, request)
except Exception as exception:
P.logger.error('Exception:%s', exception)
P.logger.error(traceback.format_exc())
@P.blueprint.route('/normal/<sub>/<sub2>', methods=['GET', 'POST'])
def normal(sub, sub2):
try:
for module in P.module_list:
if sub == module.name:
return module.process_normal(sub2, request)
except Exception as exception:
P.logger.error('Exception:%s', exception)
P.logger.error(traceback.format_exc())
def default_route_single_module(P):
@P.blueprint.route('/')
def home():
return redirect('/{package_name}/{home_module}'.format(package_name=P.package_name, home_module=P.home_module))
@P.blueprint.route('/<sub>', methods=['GET', 'POST'])
@login_required
def first_menu(sub):
if sub == 'log':
return render_template('log.html', package=P.package_name)
return P.module_list[0].process_menu(sub, request)
@P.blueprint.route('/ajax/<sub>', methods=['GET', 'POST'])
@login_required
def ajax(sub):
P.logger.debug('AJAX %s %s', P.package_name, sub)
try:
# global
if sub == 'setting_save':
ret, change_list = P.ModelSetting.setting_save(request)
if ret:
P.module_list[0].setting_save_after(change_list)
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)
else:
return P.module_list[0].process_ajax(sub, request)
except Exception as exception:
P.logger.error('Exception:%s', exception)
P.logger.error(traceback.format_exc())
@P.blueprint.route('/api/<sub>', methods=['GET', 'POST'])
@check_api
def api(sub):
try:
return P.module_list[0].process_api(sub, request)
except Exception as exception:
P.logger.error('Exception:%s', exception)
P.logger.error(traceback.format_exc())
@P.blueprint.route('/normal/<sub>', methods=['GET', 'POST'])
def normal(sub):
try:
return P.module_list[0].process_normal(sub, request)
except Exception as exception:
P.logger.error('Exception:%s', exception)
P.logger.error(traceback.format_exc())
def default_route_socketio_module(module):
P = module.P
if module.socketio_list is None:
module.socketio_list = []
@socketio.on('connect', namespace=f'/{P.package_name}/{module.name}')
def connect():
try:
P.logger.debug(f'socket_connect : {P.package_name} - {module.name}')
module.socketio_list.append(request.sid)
socketio_callback('start', '')
module.socketio_connect()
except Exception as exception:
P.logger.error('Exception:%s', exception)
P.logger.error(traceback.format_exc())
@socketio.on('disconnect', namespace='/{package_name}/{sub}'.format(package_name=P.package_name, sub=module.name))
def disconnect():
try:
P.logger.debug('socket_disconnect : %s - %s', P.package_name, module.name)
module.socketio_list.remove(request.sid)
module.socketio_disconnect()
except Exception as exception:
P.logger.error('Exception:%s', exception)
P.logger.error(traceback.format_exc())
def socketio_callback(cmd, data, encoding=True):
if module.socketio_list:
if encoding:
data = json.dumps(data, cls=AlchemyEncoder)
data = json.loads(data)
socketio.emit(cmd, data, namespace='/{package_name}/{sub}'.format(package_name=P.package_name, sub=module.name), broadcast=True)
module.socketio_callback = socketio_callback
def default_route_socketio_page(page):
module = page.parent
P = page.P
if page.socketio_list is None:
page.socketio_list = []
@socketio.on('connect', namespace=f'/{P.package_name}/{module.name}/{page.name}')
def connect():
try:
P.logger.debug(f'socket_connect : {P.package_name}/{module.name}/{page.name}')
page.socketio_list.append(request.sid)
socketio_callback('start', '')
except Exception as exception:
P.logger.error(f'Exception:{str(exception)}', exception)
P.logger.error(traceback.format_exc())
@socketio.on('disconnect', namespace=f'/{P.package_name}/{module.name}/{page.name}')
def disconnect():
try:
P.logger.debug(f'socket_disconnect : {P.package_name}/{module.name}/{page.name}')
page.socketio_list.remove(request.sid)
except Exception as exception:
P.logger.error(f'Exception:{str(exception)}', exception)
P.logger.error(traceback.format_exc())
def socketio_callback(cmd, data, encoding=True):
if page.socketio_list:
if encoding:
data = json.dumps(data, cls=AlchemyEncoder)
data = json.loads(data)
socketio.emit(cmd, data, namespace=f'/{P.package_name}/{module.name}/{page.name}', broadcast=True)
page.socketio_callback = socketio_callback