# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
#
# Copyright (c) 2016 Cédric Clerget - HPC Center of Franche-Comté University
#
# This file is part of Janua-SMS
#
# http://github.com/mesocentrefc/Janua-SMS
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation v2.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
import operator
import re
from functools import wraps
import janua.utils.logger as jlogger
from janua import jdb, sms_queue, mail_queue, config
from janua.utils.utilities import get_subclasses
from janua.utils.mail import MailError, MailObj
from janua.utils.utilities import doctrim, extract_group_number_and_mail
from janua.utils.utilities import valid_prefix_number, get_role
from janua.utils.mail import valid_email
from janua.actions import ActionNotifyError, ActionError
log = jlogger.getLogger(__name__)
action_ref = {
'id': 0,
'list': [],
'keyword_list': [],
}
# constants
TYPE_ACCEPTED = ['int', 'str', 'list', 'date', 'time', 'float', 'multilist']
class multilist(list):
"""Allow multiple value for an action argument"""
pass
class date(str):
"""Define date type argument as string"""
pass
class time(int):
"""Define time type argument as integer (timestamp)"""
pass
class _ArgDict(dict):
"""To identify argument decorated function"""
pass
def argument(required=True):
"""Decorator for action argument"""
def wrap(func):
if func.func_doc == None:
raise SyntaxError(
'documentation string for argument %s in %s is missing'
% (func.func_name, func.__module__)
)
return _ArgDict({
'name': func.func_name,
'desc': doctrim(func.func_doc),
'type': None,
'value': None,
'optional': not required,
'arg_func': func
})
return wrap
def ret_value(ret):
"""Set return value of action argument"""
return lambda: ret
def console_error(message):
return False, message
def console_success(message):
return True, message
def _console_context_args(func):
"""Decorator for processing action arguments from console interface"""
def process_args(self, arguments):
self.running_context = 'console'
log.debug(
'Console context: processing arguments of action %s',
self.get_name()
)
for arg in self.get_arguments():
if arg['name'] in arguments:
if arg['type'] != 'multilist':
if arguments[arg['name']]:
val = arguments[arg['name']][0]
else:
val = arguments[arg['name']]
else:
val = arguments[arg['name']]
setattr(self, arg['name'], ret_value(val))
return func(self)
return process_args
def _sms_context_args(func):
"""Decorator for processing action arguments from sms interface"""
def process_args(self, arguments, keyword):
self.running_context = 'sms'
if keyword == True:
# return a list ala sys.argv
setattr(self, 'keyword_arguments', arguments)
elif len(arguments) > 0:
log.debug(
'Sms context: processing arguments of action %s',
self.get_name()
)
for arg in arguments:
try:
index = int(arg[0], 10)
except ValueError:
raise ActionError('Unknown argument')
else:
value = arg[1]
action_args = self.get_arguments()
if index >= 0 and index < len(action_args):
action_arg = action_args[index]
argtype = action_arg['type']
if argtype == 'int':
try:
value = int(value)
except ValueError:
raise ActionError(
'Wrong value for argument %s'
% action_arg['name']
)
elif argtype == 'float':
try:
value = float(value)
except ValueError:
raise ActionError(
'Wrong value for argument %s'
% action_arg['name']
)
elif argtype == 'multilist':
multival = []
for val in value.split(','):
try:
ival = int(val)
except ValueError:
raise ActionError(
'Wrong value for argument %s'
% action_arg['name']
)
else:
try:
multival.append(
action_arg['value'][ival]
)
except IndexError:
raise ActionError(
'Wrong value for argument %s'
% action_arg['name']
)
value = multival
elif argtype == 'list':
try:
value = action_arg['value'][int(value)]
except (ValueError, IndexError):
raise ActionError(
'Wrong value for argument %s'
% action_arg['name']
)
elif argtype == 'time':
try:
value = int(value)
except ValueError:
raise ActionError(
'Wrong value for argument %s'
% action_arg['name']
)
setattr(self, action_arg['name'], ret_value(value))
return func(self)
return process_args
class WebInit(object):
def __init__(self, func, init_callback):
self.func = func
self.init_callback = init_callback
def init(self, clsname, method):
return self.init_callback(clsname, method)
def _web_context_args(func):
"""Decorator for processing action arguments from web interface"""
def process_args(self, arguments, *args, **kwargs):
log.debug(
'Web context: processing arguments of action %s',
self.get_name()
)
self.running_context = 'web'
for arg in self.get_arguments():
if arg['name'] not in arguments and arg['optional'] == False:
raise ActionError('Action argument %s is required' % arg['name'])
if arg['name'] in arguments:
val = arguments[arg['name']]
argtype = arg['type']
if argtype == 'int':
try:
val = int(val)
except ValueError:
raise ActionError(
'Wrong value for argument %s'
% arg['name']
)
elif argtype == 'float':
try:
val = float(val)
except ValueError:
raise ActionError(
'Wrong value for argument %s'
% arg['name']
)
elif argtype == 'list':
if val not in arg['value']:
raise ActionError('Valid value for %s argument are %s' % (arg['name'], ','.join(arg['value'])))
val = str(val)
elif argtype == 'multilist':
val = val.split(',')
for v in val:
if v not in arg['value']:
raise ActionError('Valid values for %s argument are %s' % (arg['name'], ','.join(arg['value'])))
elif argtype == 'time':
try:
val = int(val)
except ValueError:
raise ActionError(
'Wrong value for argument %s'
% arg['name']
)
setattr(self, arg['name'], ret_value(val))
return func(self, *args, **kwargs)
return process_args
class NotifyFlags(object):
sms = 1 << 1
mail = 1 << 2
sender = 1 << 3
contacts = 1 << 4
admin = 1 << 5
supervisors = 1 << 6
manager = 1 << 7
[docs]class NotifyMethod(object):
"""
Notification methods
"""
def __init__(self, pool, action, flags):
self.pool = pool
self.action = action
self.flags = flags
def _check_notify_arg(self, arg):
if self.action.running_context == 'sms' and not isinstance(arg, basestring):
raise ActionNotifyError('argument is not a string type')
if self.action.running_context == 'mail' and not isinstance(arg, MailObj):
raise ActionNotifyError('argument is not a MailObj type')
[docs] def sender(self, arg):
"""
To notify sender which trigger action
:param arg: message for sms, :class:`.MailObj` for mail
"""
self._check_notify_arg(arg)
self.pool.add(
(self.flags|NotifyFlags.sender, arg)
)
[docs] def manager(self, arg):
"""
To notify administrator which manage this action, available in SMS
context only
:param arg: message for sms, :class:`.MailObj` for mail
"""
self._check_notify_arg(arg)
if self.action.running_context == 'sms':
self.pool.add(
(self.flags|NotifyFlags.manager, arg)
)
else:
raise ActionNotifyError('Not available in this context')
[docs] def admin(self, arg):
"""
To notify super administrator
:param arg: message for sms, :class:`.MailObj` for mail
"""
self._check_notify_arg(arg)
self.pool.add(
(self.flags|NotifyFlags.admin, arg)
)
[docs] def supervisors(self, arg):
"""
To notify all supervisors
:param arg: message for sms, :class:`.MailObj` for mail
"""
self._check_notify_arg(arg)
self.pool.add((self.flags|NotifyFlags.supervisors, arg))
[docs]class Notify(object):
"""
Notify wrapper class
"""
sms = None
"""sms notify method object (:class:`.NotifyMethod`)"""
mail = None
"""mail notify method object (:class:`.NotifyMethod`)"""
def __init__(self, action):
self._pool = set()
self.sms = NotifyMethod(self._pool, action, NotifyFlags.sms)
self.mail = NotifyMethod(self._pool, action, NotifyFlags.mail)
@property
def pool(self):
return self._pool
def __getattr__(self, name):
if name not in ['sms', 'mail']:
raise ActionNotifyError(
'%s is not a valid notify attribute'
% name
)
def available_action_list(include_keyword=True):
"""Available action objects as list
@param include_keyword: include keyword action or not
@return a list of action objects
"""
slist = sorted(get_subclasses(Action), key=lambda action: action.get_id())
if include_keyword == True:
return slist
else:
return [action for action in slist if action.keyword == None]
def available_action_dict(include_keyword=True):
"""Available action objects as dictionary
@param include_keyword: include keyword action or not
@return a dictionary of action objects
"""
clsdict = {}
for cls in get_subclasses(Action):
if include_keyword == False and cls.keyword != None:
continue
clsdict.update({cls.get_name(): cls})
return clsdict
def get_custom_action_repr(context, filtered, authorized_actions):
"""Get custom action class information based on context
@param context: action context
@param filtered: is action should be filtered
@param authorized_actions: only action in the list will be listed
@return a dictionary containing action class information
"""
dict_actions = {'totalaction': 0, 'totalcategory': 0,
'action': [], 'category': []}
for action in available_action_list(include_keyword=False):
action_category = action.category
action_contexts = action.get_contexts()
if action_category != '__INTERNAL__' and \
context in action_contexts and \
action.enabled:
if filtered and (action.get_name() not in authorized_actions):
continue
dict_actions['totalaction'] += 1
dict_actions['action'].append(action.get_class_info())
if action_category not in dict_actions['category']:
dict_actions['totalcategory'] += 1
dict_actions['category'].append(action_category)
return dict_actions
class _MetaAction(type):
"""Private meta class for action class."""
def __new__(mcs, clsname, bases, dct):
web_init = None
conv_clsname = re.sub('([A-Z]{1}[a-z]{1})', r'_\1', clsname).lstrip('_').lower()
for name, module in action_ref['list']:
if name == conv_clsname:
raise NameError(
'class %s is already defined in %s'
% (clsname, module)
)
if dct['__module__'] == module:
raise SyntaxError(
'only one action per module are allowed. Remove %s from %s'
% (clsname, dct['__module__'])
)
action_ref['list'].append((conv_clsname, dct['__module__']))
for keyword, module in action_ref['keyword_list']:
if 'keyword' in dct and keyword == dct['keyword'].lower():
raise NameError('keyword action %s is already defined '
'in %s' % (keyword, module))
if 'keyword' in dct and dct['keyword'] != None:
action_ref['keyword_list'].append((dct['keyword'].lower(),
dct['__module__']))
dct.update({'_arguments': []})
parent_dict = bases[0].__dict__
index = 0
for k in sorted(dct.iterkeys()):
if isinstance(dct[k], _ArgDict):
arg = dct[k]
func = arg['arg_func']
ret = func(mcs)
arg['type'] = type(ret).__name__
if arg['type'] not in TYPE_ACCEPTED:
raise TypeError(
'Type %s as action argument is not allowed'
% arg['type']
)
arg['value'] = ret
# action arguments can't be overriden to avoid mistakes
if parent_dict.has_key('_arguments'):
for parent_arg in parent_dict['_arguments']:
if arg['name'] in parent_arg['_name']:
raise NameError(
'%s argument in %s already exists in %s'
% (arg['name'], clsname, bases[0].__name__)
)
dct['_arguments'].append({
'id': index,
'name': arg['name'],
'desc': arg['desc'],
'type': arg['type'],
'value': arg['value'],
'optional': arg['optional']
})
index += 1
dct[k] = lambda x: None
if k in ['sms', 'console', 'web', 'unittest']:
func = dct[k]
context = k
if context == 'console':
dct['call_console_context'] = _console_context_args(func)
elif context == 'sms':
dct['call_sms_context'] = _sms_context_args(func)
elif context == 'web':
if isinstance(func, WebInit):
dct['call_web_context'] = _web_context_args(func.func)
web_init = func
else:
raise SyntaxError('Missing url decorator for web method in class %s' % dct['__module__'])
if '_contexts' not in dct:
dct['_contexts'] = []
dct['_contexts'].append(context)
# inherit argument from parent
if parent_dict.has_key('_arguments'):
dct['_arguments'] = parent_dict['_arguments'] + dct['_arguments']
if web_init:
method = 'GET'
if dct['_arguments']:
method = 'POST'
web_init.init(conv_clsname, method)
if clsname != 'Action':
# sanity checks
if '_contexts' not in dct or (len(dct['_contexts']) == 1 and \
'unittest' in dct['_contexts']):
raise SyntaxError(
'action class in %s should contain at least one of these methods: sms, web, console'
% dct['__module__']
)
if 'keyword' in dct and dct['keyword'] != None:
if 'sms' not in dct['_contexts']:
raise SyntaxError(
'keyword action class in %s has no sms method'
% dct['__module__']
)
elif 'filtered' in dct and dct['filtered'] == False:
dct['filtered'] = True
if 'category' not in dct or dct['category'] == None:
raise AttributeError(
'category attribute for action class in %s is missing'
% dct['__module__']
)
if '__doc__' not in dct:
raise SyntaxError(
'documentation string for action class in %s is missing'
% dct['__module__']
)
if 'keyword' in dct and dct['keyword'] != None:
dct['keyword'] = dct['keyword'].lower()
dct.update({
'_name': conv_clsname,
'_desc': doctrim(dct['__doc__']),
'_id': action_ref['id']
})
action_ref['id'] += 1
return super(_MetaAction, mcs).__new__(mcs, clsname, bases, dct)
[docs]class Action(object):
"""
Action parent class, all action class must inherit from it
"""
__metaclass__ = _MetaAction
"""the fun part"""
category = None
"""
Category to sort actions
.. note::
category **__INTERNAL__** is for internal use
"""
dangerous = False
"""
Action is dangerous ? (Janua Client)
"""
phone_number = None
"""
phone number to determine which sender trigger action (**sms context only**)
"""
email = None
"""
email of administrator who trigger action
"""
keyword = None
"""
sms keyword which trigger action
.. note::
an action triggered with sms keyword don't require authentication.
If filtered attribute set to:
* True: only phone numbers in authorized group are allowed to trigger action
* False: anyone can trigger action
"""
keyword_arguments = []
"""
sms keyword arguments, typically all words behind keyword are considered as
arguments
"""
enabled = True
"""
Flag to enable or disable action
"""
filtered = True
"""
For keyword
"""
notify = None
"""
notify object
"""
admin = None
"""
Admin database object which managed this action
"""
_contexts = []
"""List of contexts"""
_arguments = None
"""Action arguments"""
_name = None
"""Action name"""
_desc = None
"""Action description, take from class docstring"""
_id = None
"""Action unique id"""
_running_context = None
"""In which context action run"""
def __new__(cls):
"""Don't allow instance of disabled action"""
if cls.enabled:
return super(Action, cls).__new__(cls)
raise ActionError('Action was disabled')
def __init__(self):
""""""
self.notify = Notify(self)
if self.category == '__INTERNAL__':
self.admin = jdb.admin.get_super_admin()
else:
action = jdb.action.get_by_janua_id(self.get_id())
if not action:
raise ActionError('Cannot associate admin to action')
self.admin = jdb.admin.get_by_id(action.admin_id)
if not self.admin:
raise ActionError('No action supervisor was found')
@classmethod
[docs] def get_name(cls):
"""Get class name"""
return cls._name
@classmethod
[docs] def get_desc(cls):
"""Get formatted description from class documentation string"""
return cls._desc
@classmethod
[docs] def get_arguments(cls):
"""Get action arguments"""
return cls._arguments
@classmethod
[docs] def get_contexts(cls):
"""Get action contexts"""
return cls._contexts
@classmethod
[docs] def get_class_info(cls):
"""Get action class information"""
return {
'name': cls._name,
'category': cls.category,
'desc': cls._desc,
'id': cls._id,
'args': cls._arguments,
'dangerous': cls.dangerous
}
@classmethod
[docs] def get_id(cls):
"""Get action id"""
return cls._id
@classmethod
[docs] def get_module(cls):
"""Get module namespace"""
return cls.__module__
@property
def running_context(self):
"""Get running context"""
return self._running_context
@running_context.setter
def running_context(self, value):
self._running_context = value
[docs] def call_sms_context(self, arguments, keyword):
"""Call child sms method"""
raise NotImplementedError('Not supported')
[docs] def call_console_context(self, arguments):
"""Call child console method"""
raise NotImplementedError('Not supported')
[docs] def call_web_context(self, arguments):
"""Call child web method"""
raise NotImplementedError('Not supported')
[docs] def send_sms(self, message=None, to=None, filtered=True):
"""
Send SMS
:param message: SMS message
:param to: string containing phone number and/or group name separated by commas
:param filtered: authorized to send only to managed contacts or send to anyone
:returns: tuple (success, message) where success is a boolean to indicate success or error
"""
if not message or not to:
return False, 'No recipients and/or message'
from_number = self.phone_number
if not from_number:
return False, 'Couldn\'t retrieve your phone number'
if self.running_context == 'web':
admin = jdb.admin.get_by_phone(self.phone_number)
admin_id = admin.id
if filtered == True:
filtered = admin.recipient_filter
else:
admin_id = self.admin.id
if filtered == True:
filtered = self.admin.recipient_filter
admins = [adm.phone_number for adm in jdb.admin.get_all()]
groups, numbers, _ = extract_group_number_and_mail(to)
dbgroups = jdb.group.get_by_admin_phone(from_number)
dbcontacts = jdb.contact.get_by_admin_phone(from_number)
group_ids = set()
if dbcontacts == [] and dbgroups == [] and filtered:
return False, 'No recipient was found in database for your account'
list_groups = [g.name for g in dbgroups]
for group in groups:
if group not in list_groups and group != 'all':
return False, 'Group %s is invalid' % group
else:
try:
group_match = filter(lambda x: x.name == group, dbgroups)[0]
except (IndexError, ValueError):
continue
group_ids.add(group_match.id)
if 'all' in groups:
contacts = set(jdb.contact.get_by_admin_phone(self.phone_number))
else:
contacts = set()
list_numbers = [contact.phone_number for contact in dbcontacts]
for number in numbers:
if not valid_prefix_number(number, config.sms.prefix_filter):
return False, 'Number %s doesn\'t have a valid prefix' % number
if filtered and number not in list_numbers:
return False, 'Number %s is invalid' % number
if number in admins:
contact = jdb.admin.get_by_phone(number)
else:
try:
contact = filter(lambda x: x.phone_number == number, dbcontacts)[0]
except (IndexError, ValueError):
continue
if contact:
contacts.add(contact)
elif not filtered:
contacts.add(number)
if group_ids:
contacts.update(jdb.contact.get_by_group_id(group_ids))
for contact in contacts:
if hasattr(contact, 'phone_number'):
sms_queue.put((message, contact.phone_number, admin_id))
else:
sms_queue.put((message, contact, admin_id))
return True, 'SMS message has been queued'
[docs] def send_email(self, subject=None, message=None, to=None, template=None, template_args={}, filtered=True):
"""
Send mail
:param subject: mail subject
:param message: mail body
:param to: string containing phone number, group name or mail separated by commas
:param template: mail template to use
:param template_args: mail template arguments
:param filtered: authorized to send only to managed contacts or send to anyone
:returns: tuple (success, message) where success is a boolean to indicate success or error
"""
from_number = self.phone_number
if not from_number:
return False, 'Couldn\'t retrieve your phone number'
if self.running_context == 'web':
admin = jdb.admin.get_by_phone(self.phone_number)
admin_email = admin.email
if filtered == True:
filtered = admin.recipient_filter
else:
admin_email = self.admin.email
if filtered == True:
filtered = self.admin.recipient_filter
admins = [adm.email for adm in jdb.admin.get_all()]
try:
groups, numbers, mails = extract_group_number_and_mail(to)
except AttributeError, err:
return False, 'There is no recipients'
dbgroups = jdb.group.get_by_admin_phone(from_number)
dbcontacts = jdb.contact.get_by_admin_phone(from_number)
group_ids = set()
if dbcontacts == [] and dbgroups == [] and filtered:
return False, 'No recipient was found in database for your account'
list_groups = [g.name for g in dbgroups]
for group in groups:
if group not in list_groups and group != 'all':
return False, 'Group %s is invalid' % group
else:
try:
group_match = filter(lambda x: x.name == group, dbgroups)[0]
except (IndexError, ValueError):
continue
group_ids.add(group_match.id)
if 'all' in groups:
contacts = set(jdb.contact.get_by_admin_phone(from_number))
else:
list_mails = [contact.email for contact in dbcontacts]
list_numbers = [contact.phone_number for contact in dbcontacts]
contacts = set()
for number in numbers:
if not valid_prefix_number(number, config.sms.prefix_filter):
return False, 'Number %s doesn\'t have a valid prefix' % number
if number not in list_numbers:
return False, 'Number %s is not in your contact list' % number
if number in admins:
contact = jdb.admin.get_by_phone(number)
else:
try:
contact = filter(lambda x: x.phone_number == number, dbcontacts)[0]
except (IndexError, ValueError):
continue
if contact:
contacts.add(contact)
for mail in mails:
if not valid_email(mail):
return False, 'Mail %s doesn\'t have a valid format' % mail
if filtered and mail not in list_mails:
return False, 'Mail %s is invalid' % mail
if mail in admins:
contact = jdb.admin.get_by_mail(mail)
else:
try:
contact = filter(lambda x: x.email == mail, dbcontacts)[0]
except (IndexError, ValueError):
continue
if contact:
contacts.add(contact)
elif not filtered:
contacts.add(mail)
if group_ids:
contacts.update(jdb.contact.get_by_group_id(group_ids))
try:
mailobj = MailObj()
if admin_email:
mailobj.reply_to = admin_email
if template:
mailobj.template = template
mailobj.template_args = template_args
else:
mailobj.message = message
mailobj.subject = subject
except MailError, err:
return False, err[0]
log.debug('process contact')
for contact in contacts:
if hasattr(contact, 'email'):
mailobj.to = contact.email
else:
mailobj.to = contact
mail_queue.put(mailobj)
if contacts:
return True, 'Mail has been queued'
return False, 'No recipients was found to send email'
[docs] def process_notify(self):
"""
Process notify message pool
"""
phone_numbers = set()
mail_to = set()
flags = NotifyFlags
for notify_args in self.notify.pool:
notify_flags, arg = notify_args
db_action = jdb.action.get_by_janua_id(self.get_id())
if self.running_context == 'sms':
if not db_action and self.get_id() > 0:
return 'No action corresponding to janua_id %d was found in database' % self.get_id()
if notify_flags & flags.sender:
log.debug('Notify sender')
phone_numbers.add(self.phone_number)
if self.email:
mail_to.add(self.email)
elif (notify_flags & flags.manager) and db_action:
log.debug('Notify manager')
entry = jdb.admin.get_by_id(db_action.admin_id)
phone_numbers.add(entry.phone_number)
mail_to.add(entry.email)
elif notify_flags & flags.admin:
log.debug('Notify admin')
entry = jdb.admin.get_super_admin()
phone_numbers.add(entry.phone_number)
mail_to.add(entry.email)
elif notify_flags & flags.supervisors:
log.debug('Notify supervisors')
for entry in jdb.admin.get_all():
if get_role(entry) == 'supervisor':
phone_numbers.add(entry.phone_number)
mail_to.add(entry.email)
elif (notify_flags & flags.contacts) and db_action:
log.debug('Notify contacts')
contacts = jdb.contact.get_by_action_id(db_action.id)
try:
contact = filter(lambda x: x.phone_number == self.phone_number, contacts)[0]
except (IndexError, ValueError):
return 'No contact associated with %s' % self.phone_number
group_ids = [group.id for group in jdb.group.get_by_contact_id(contact.id)]
contact_ids = [cn.contact_id for cn in jdb.contact_notify.get_by_action_id(db_action.id) if cn.group_id in group_ids]
for contact_id in contact_ids:
if contact_id != contact.id:
contact_notify = jdb.contact.get_by_id(contact_id)
phone_numbers.add(contact_notify.phone_number)
mail_to.add(contact_notify.email)
if notify_flags & flags.sms:
message = arg
data = None
if message:
data = message
log.info('Action notify: %s' % message)
if message:
success, message = self.send_sms(data, ','.join(phone_numbers), False)
if not success:
return message
else:
return 'SMS message is empty'
if notify_flags & flags.mail:
if isinstance(arg, MailObj):
return 'Mail notify argument is not a MailObj instance'
kwargs = {
'subject': arg.subject,
'message': arg.message,
'to': ','.join(mail_to),
'template': arg.template,
'template_args': arg.template_args,
'filtered': False
}
success, message = self.send_email(**kwargs)
if not success:
return message
def update_action_table():
db_actions = jdb.action.get_all()
db_actions_module = [action.module for action in db_actions]
db_actions_id = [action.janua_id for action in db_actions]
available_actions = [
action
for action in available_action_list()
if action.category != '__INTERNAL__' and 'sms' in action.get_contexts()
]
actions_module = []
roll_actions = []
add_action = []
max_id = 0
for action in available_actions:
if action.get_module() not in db_actions_module:
add_action.append(action)
else:
index = db_actions_module.index(action.get_module())
db_action = db_actions[index]
if db_action.janua_id != action.get_id():
roll_actions.append(
(
action.get_module(),
action.get_id(),
db_action.janua_id,
action
)
)
if action.get_id() > max_id:
max_id = action.get_id()
else:
db_action.name = action.get_name()
db_action.description = action.get_desc()
db_action.authentication = True if action.keyword == None else False
db_action.enabled = action.enabled
if jdb.update_entry(db_action):
actions_module.append(action.get_module())
else:
log.error('Can not update action %s', action.get_module())
if not db_actions_id:
reverse = False
else:
reverse = False if max(db_actions_id) > max_id else True
roll_actions.sort(key=operator.itemgetter(1), reverse=reverse)
if reverse == False:
roll_module = [action[0] for action in roll_actions]
for action in db_actions:
if action.module not in actions_module and \
action.module not in roll_module:
if jdb.del_entry(action) == False:
log.error('Can not delete action %s', action.module)
for module in roll_actions:
index = db_actions_id.index(module[2])
db_action = db_actions[index]
action = module[3]
db_action.name = action.get_name()
db_action.description = action.get_desc()
db_action.authentication = True if action.keyword == None else False
db_action.enabled = action.enabled
db_action.janua_id = module[1]
if jdb.update_entry(db_action):
actions_module.append(action.get_module())
else:
log.error('Can not update action %s', action.get_module())
for action in add_action:
authentication = True if action.keyword == None else False
if jdb.action.add(action.get_name(),
action.get_module(),
action.get_desc(),
authentication,
action.enabled,
action.get_id()) == False:
log.error('Can not add action %s', action.get_module())
else:
actions_module.append(action.get_module())