Beispiel #1
0
    def get_table(self, full_table_name):
        """get table from schemata
        """

        schema, table_name = full_table_name.replace('`', '').split('.')

        schema_module = self['schemata'].get(schema, None)

        if schema_module is None:
            raise LorisError(
                f'schema {schema} not in database; refresh database')

        table_name = table_name.strip('_#')
        table_name_list = table_name.split('__')
        if len(table_name_list) == 1:
            table_name = to_camel_case(table_name)
            try:
                return getattr(schema_module, table_name)
            except AttributeError:
                raise LorisError(f'table {table_name} not in schema {schema}; '
                                 'refresh database')
        else:
            assert len(table_name_list) == 2, \
                f'invalid table name {table_name}.'
            table_name = to_camel_case(table_name_list[0])
            part_table_name = to_camel_case(table_name_list[1])
            try:
                return getattr(getattr(schema_module, table_name),
                               part_table_name)
            except AttributeError:
                raise LorisError(
                    f'table {table_name} not in schema {schema} '
                    f'or part table {part_table_name} not in table {table_name}'
                    '; refresh database')
def inserting_autoscript_stuff(attr, value, table_class, primary_dict):
    """inserting data/file from autoscript into database
    """
    if attr.startswith('<') and attr.endswith('>'):
        # assumes either data or filemixin was used
        attr = attr.strip('<>')
        part_table_name, attr = attr.split(':')
        part_table = getattr(table_class, part_table_name)
        if data_subclass(part_table, primary_dict):
            to_insert = get_insert_part_mixin(attr,
                                              value,
                                              'data_lookup_name',
                                              DataLookupName,
                                              'a_datum',
                                              primary_dict,
                                              func=datareader)
        elif file_subclass(part_table, primary_dict):
            to_insert = get_insert_part_mixin(attr, value, 'file_lookup_name',
                                              FileLookupName, 'a_file',
                                              primary_dict)
        else:
            raise LorisError(f'part table {part_table.name} is not a '
                             'subclass of DataMixin or FilesMixin.')
        part_table.insert1(to_insert)
    elif attr in table_class.heading:
        if table_class.heading[attr].is_blob:
            value = datareader(value)
        (table_class & primary_dict).save_update(attr, value)
    else:
        raise LorisError(f'attr {attr} does not exist in '
                         f'table {table_class.full_table_name}')
Beispiel #3
0
    def get_table(self, full_table_name, as_string=False):
        """get table from schemata using full_table_name
        """

        schema, table_name = full_table_name.replace('`', '').split('.')

        schema_module = self['schemata'].get(schema, None)

        if schema_module is None:
            raise LorisError(
                f'schema {schema} not in database; refresh database')

        table_name = table_name.strip('_#')
        table_name_list = table_name.split('__')
        if as_string:
            output = schema
        else:
            output = schema_module

        for tname in table_name_list:
            tname = to_camel_case(tname)
            try:
                if as_string:
                    output = '.'.join([output, tname])
                else:
                    output = getattr(output, tname)
            except AttributeError:
                raise LorisError(f'table {table_name} not in schema {schema}; '
                                 'refresh database')

        return output
Beispiel #4
0
    def __init__(self, key, value, folderpath):
        self.key = key
        self.folderpath = folderpath

        if isinstance(value, (str, list)):
            self.value = value
            self.description = None
            self.default = None
            self.required = True
            self.iterate = None
            self.loc = None
        elif isinstance(value, dict):
            truth = set(value) - {
                'type', 'comment', 'default', 'loc', 'iterate'
            }
            if truth:
                raise LorisError('Key in dynamic auto-generated form contains '
                                 f'illegal keywords: {truth}.')
            if 'type' not in value:
                raise LorisError(
                    'Must provide type key for dynamic auto-generated form; '
                    f'only provided these keys for "{key}": {set(value)}.')
            self.value = value.get('type')
            self.description = value.get('comment', None)
            self.default = value.get('default', None)
            self.required = ('default' not in value
                             or value.get('default', None) is not None)
            self.loc = value.get('loc', None)
            self.iterate = value.get('iterate', False)
        else:
            LorisError(f"value is wrong type {type(value)}")

        self.get_field()
Beispiel #5
0
def dynamic_autoscripted_form(dictionary, folderpath, formclass=Form):

    post_process_dict = {}

    class DynamicForm(formclass, FormMixin):
        pass

    for key, value in dictionary.items():
        # comments in the json or formatting guidelines start with _
        if key.startswith('#'):
            continue
        if not key.isidentifier():
            raise LorisError(
                f"key {key} is not an identifier; i.e. alphanumeric "
                "and underscore characters only. The key needs to be "
                "an identifier if it is used as a keyword during function "
                "calling.")

        auto_field = AutoscriptedField(key, value, folderpath)

        post_process_dict[key] = auto_field.post_process

        setattr(DynamicForm, key, auto_field.field)

    return DynamicForm, DictReader(post_process_dict)
Beispiel #6
0
def execute_slack_message(key_info, slack_info):
    """
    Parameters
    ----------
    key_info : dict
        Information of a single entry of a specific table
    slack_info : dict
        Slack information passed to slack config
    """

    token = slack_info['token']
    channel = slack_info['channel']
    text = slack_info.get('text', '').format(**key_info)

    client = WebClient(token=token)

    try:
        response = client.chat_postMessage(channel=channel, text=text)
        # assert response["message"]["text"] == text
    except SlackApiError as e:
        # You will get a SlackApiError if "ok" is False
        # assert e.response["ok"] is False
        # assert e.response["error"]
        # str like 'invalid_auth', 'channel_not_found'
        raise LorisError(f"Got a Slack error: {e}")

    return response
Beispiel #7
0
    def wait(self):
        """wait for the subprocess to finish
        """

        if self.p is None:
            raise LorisError('No subprocess is running.')

        self.thread.join()

        return self.rc, self.stdout, self.stderr
Beispiel #8
0
    def get_formatted(self):

        formatted_dict = super().get_formatted()

        if formatted_dict['python_file'] is None and formatted_dict[
                'python_module'] is None:
            raise LorisError('No python file or module was given.')
        elif formatted_dict['python_file']:
            return formatted_dict['python_file']
        else:
            return formatted_dict['python_module']
Beispiel #9
0
def filereader(value):
    """
    Copies filepath to tmp folder with uuid
    """
    if not os.path.exists(value):
        raise LorisError(f"Filepath {value} does not exist.")
    basename = os.path.basename(value)
    basename = f"{uuid.uuid4()}_{basename}"
    dst = os.path.join(config['tmp_folder'], basename)
    assert not os.path.exists(dst)
    shutil.copyfile(value, dst)
    return dst
Beispiel #10
0
    def get_table_from_classname(self, class_name):
        """get table from schemata using table class name format
        """

        table_info = class_name.split('.')
        schema, table_classes = table_info[0], table_info[1:]

        schema_module = self['schemata'].get(schema, None)

        if schema_module is None:
            raise LorisError(
                f'schema {schema} not in database; refresh database')

        table = schema_module

        for table_ in table_classes:

            try:
                table = getattr(table, table_)
            except AttributeError:
                raise LorisError(f'table {table_} not in schema {schema}; '
                                 'refresh database')

        return table
Beispiel #11
0
    def user_in_group(self, user, group):
        """is user in group
        """

        table = self.assigned_table & {
            self['user_name']: user,
            self['group_name']: group
        }

        if len(table) == 1:
            return True
        elif len(table) == 0:
            return False
        else:
            raise LorisError("assined user group table should only have "
                             "singular entries for given user and group.")
    def __init__(self, user_name):
        self.table = config.user_table

        if not len(self.table()):
            config.create_administrator()

        self.user_name = user_name

        self.restriction = {config['user_name']: self.user_name}

        print(self.restriction)
        self.restricted_table = self.table & self.restriction

        if len(self.restricted_table) > 1:
            raise LorisError('More than a single user entry')

        self._entry = None
Beispiel #13
0
    def __init__(self, user_name):
        print(config['connection'])
        self.table = config.user_table

        # skip mysql error
        try:
            if not len(self.table()):
                config.create_administrator()
        except pymysql.err.InterfaceError:
            warnings.warn('not creating administrator')

        self.user_name = user_name

        self.restriction = {config['user_name']: self.user_name}

        print(self.restriction)
        self.restricted_table = self.table & self.restriction

        if len(self.restricted_table) > 1:
            raise LorisError('More than a single user entry')

        self._entry = None
            while True:
                length = len(process.lines)
                if length > lnumbers:
                    print(''.join(process.lines[lnumbers:length]))
                    lnumbers = length
                if process.p is not None and process.p.poll() is not None:
                    break

            if process.thread.is_alive():
                process.wait()

            if process.stdout is not None:
                print(process.stdout)

            if process.rc != 0:
                raise LorisError(f'automatic script error:\n{process.stderr}')

            # update/insert fields with data from autoscript
            if args.configattr != 'null' and args.configattr is not None:
                inserting_autoscript_stuff(args.configattr, args.location,
                                           table_class, primary_dict)
            if args.outputattr != 'null' and args.outputattr is not None:
                inserting_autoscript_stuff(args.outputattr,
                                           os.path.join(cwd, args.outputfile),
                                           table_class, primary_dict)
            # field name or <part_table_name:data/file_lookupname>
            # or just an attribute in the table
    except Exception as e:
        jobs.complete(table_class.full_table_name, primary_dict)
        raise e
    else:
Beispiel #15
0
    def _get_field(cls, key, value, required, default, description, iterate,
                   loc, folderpath):
        """get initialized field
        """
        def post_process(x):
            return x

        if required:
            kwargs = {
                'validators': [InputRequired()],
                'render_kw': {
                    'nullable': False
                }
            }
        else:
            kwargs = {
                'validators': [Optional()],
                'render_kw': {
                    'nullable': True
                }
            }

        kwargs['default'] = default
        kwargs['label'] = key.replace('_', ' ')
        kwargs['description'] = (key if description is None else description)

        if loc is None and not isinstance(value, dict):
            if value == 'list':
                kwargs['validators'].append(JsonSerializableValidator())
                field = ListField(**kwargs)
            elif value == 'dict':
                kwargs['validators'].append(JsonSerializableValidator())
                field = DictField(**kwargs)
            elif value == 'str':
                field = StringField(**kwargs)
            elif value == 'set':
                kwargs['validators'].append(JsonSerializableValidator())
                post_process = set
                field = ListField(**kwargs)
            elif value == 'tuple':
                kwargs['validators'].append(JsonSerializableValidator())
                post_process = tuple
                field = ListField(**kwargs)
            elif value == 'int':
                field = IntegerField(**kwargs)
            elif value == 'float':
                field = FloatField(**kwargs)
            elif value == 'bool':
                kwargs['validators'] = [Optional()]
                field = BooleanField(**kwargs)
            elif value == 'numpy.array':
                kwargs['validators'].append(Extension())
                post_process = cls.file_processing(value)
                field = DynamicFileField(**kwargs)
            elif value == 'numpy.recarray':
                kwargs['validators'].append(Extension())
                post_process = cls.file_processing(value)
                field = DynamicFileField(**kwargs)
            elif value == 'pandas.DataFrame':
                kwargs['validators'].append(Extension())
                post_process = cls.file_processing(value)
                field = DynamicFileField(**kwargs)
            elif value == 'pandas.Series':
                kwargs['validators'].append(Extension())
                post_process = cls.file_processing(value)
                field = DynamicFileField(**kwargs)
            elif value == 'json':
                kwargs['validators'].append(Extension(['json']))
                post_process = cls.file_processing(value)
                field = DynamicFileField(**kwargs)
            elif value == 'file':
                kwargs['validators'].append(
                    Extension(config['attach_extensions']))
                field = DynamicFileField(**kwargs)
            elif isinstance(value, list):
                choices = [
                    str(ele).strip().strip('"').strip("'") for ele in value
                ]
                post_process = EnumReader(value, choices)

                if default is None:
                    choices = ['NULL'] + choices
                kwargs['choices'] = [(ele, ele) for ele in choices]

                field = SelectField(**kwargs)
            else:
                raise LorisError(
                    f"field value {value} not accepted for {key}.")
        elif loc is not None and isinstance(value, str):
            loc = secure_filename(loc)
            locpath = os.path.join(folderpath, loc)
            # try up to three base directories down
            if not os.path.exists(locpath):
                # try main autoscript folder
                locpath = os.path.join(os.path.dirname(folderpath), loc)
                if not os.path.exists(locpath):
                    locpath = os.path.join(
                        os.path.dirname(os.path.dirname(folderpath)), loc)
                    if not os.path.exists(locpath):
                        raise LorisError(
                            f'Folder "{loc}" does not exist in '
                            f'autoscript folder '
                            f'"{os.path.basename(folderpath)}" '
                            f'and also not in the main autoscript folder.')
            # get all files from folder
            files = glob.glob(os.path.join(locpath, '*'))
            # only match certain extensions
            if (value == 'pandas.DataFrame') or (value == 'numpy.recarray'):
                files = [
                    ifile for ifile in files
                    if (ifile.endswith('.pkl') or ifile.endswith('.npy')
                        or ifile.endswith('.csv') or ifile.endswith('.json'))
                ]
            elif value == 'numpy.array':
                files = [
                    ifile for ifile in files
                    if (ifile.endswith('.pkl') or ifile.endswith('.npy')
                        or ifile.endswith('.csv'))
                ]
            elif (value == 'json') or (value == 'pandas.Series'):
                files = [ifile for ifile in files if ifile.endswith('.json')]
            else:
                # skip file that start with two underscores e.g. __init__.py
                files = [
                    ifile for ifile in files
                    if not os.path.basename(ifile).startswith('__')
                ]
            # setup as choices
            choices = [(str(ele), os.path.split(ele)[-1]) for ele in files]
            # setup None choice
            if default is None and not required:
                choices = [('NULL', 'NULL')] + choices
            kwargs['choices'] = choices
            post_process = cls.file_processing(value)
            field = SelectField(**kwargs)
        elif isinstance(value, dict):
            form, post_process = dynamic_autoscripted_form(
                value, folderpath, NoCsrfForm)
            field = FormField(form)
        # TODO set number of fieldlists (startswith numeric)
        else:
            raise LorisError(f"field value {value} not accepted for {key}.")

        # make iterator (can add multiple values together)
        if iterate:
            field = FieldList(
                field,
                min_entries=int(required) + 1  # have one required field if so
            )
            post_process = ListReader(post_process)
        return field, post_process
Beispiel #16
0
    def insert(self, form, _id=None, **kwargs):
        """insert into datajoint table

        Parameters
        ----------
        form : wtf.form from dynamicform.formclass
        _id : dict
            restriction for single entry (for save updating)
        kwargs : dict
            arguments passed to datajoint Table.insert function
        """

        if isinstance(form, FormMixin):
            formatted_dict = form.get_formatted()
        elif isinstance(form, dict):
            formatted_dict = form
        else:
            raise LorisError(f'form is incorrect type (={type(form)}); '
                             'form must be dict or be a subclass of FormMixin')

        # check if replace in kwargs affects order of main and part helper
        replace = kwargs.get('replace', False)

        def main_helper(override_update_truth=False):
            primary_dict = self._insert(
                formatted_dict,
                _id,
                override_update_truth=override_update_truth,
                **kwargs)
            return primary_dict

        def part_helper(primary_dict):
            for part_name, part_form in self.part_fields.items():
                f_dicts = formatted_dict[part_name]
                if f_dicts is None:
                    continue
                for f_dict in f_dicts:
                    if _id is None:
                        _part_id = None
                    else:
                        # update with part entry that exist
                        _part_primary = {}
                        for key, value in f_dict.items():
                            if (key in part_form.table.primary_key
                                    and key not in self.table.primary_key):
                                _part_primary.update(
                                    part_form.fields[key].format_value(value))
                        _part_id = {**_id, **_part_primary}
                    # insert into part table
                    part_form._insert(f_dict,
                                      _part_id,
                                      primary_dict,
                                      override_update_truth=True,
                                      **kwargs)

        if self.table.connection.in_transaction:
            if replace and _id is not None:
                part_helper(_id)
                kwargs.pop('replace')
                primary_dict = main_helper(True)
            else:
                primary_dict = main_helper()
                part_helper(primary_dict)
        else:
            # perform operations within a transaction
            with self.table.connection.transaction:
                if replace and _id is not None:
                    part_helper(_id)
                    kwargs.pop('replace')
                    primary_dict = main_helper(True)
                else:
                    primary_dict = main_helper()
                    part_helper(primary_dict)
        return primary_dict
Beispiel #17
0
    def get_autoscriptforms(self, autoscript_filepath, table_name,
                            form_creator, **kwargs):
        """get autoscript form and process_dict and buttons params
        """
        foldername = os.path.basename(autoscript_filepath)
        filepath = os.path.join(autoscript_filepath, AUTOSCRIPT_CONFIG)

        with open(filepath, 'r') as f:
            try:
                config = json.load(f)
            except Exception as e:
                raise LorisError(f"Json config file for autoscript"
                                 f" '{foldername}' badly "
                                 f"formatted: {e}")

        if 'buttons' not in config:
            raise LorisError("In configuration file of autoscript "
                             f"{foldername}, no 'button' key "
                             "was provided.")

        buttons = config['buttons']
        config_forms = config.get('forms', {})

        if not isinstance(buttons, dict):
            raise LorisError(f'In configuration file of autoscript '
                             f'"{foldername}", '
                             '"scripts" keyword is incorrectly '
                             'formatted.')

        for key, button in buttons.items():
            base_message = (f'In configuration file of autoscript '
                            f'"{foldername}", '
                            '"buttons" keyword is incorrectly '
                            f'formatted for button "{key}":')
            message = (f'{base_message} must be dictionary with '
                       '"script" key and "validate", "insert", "configattr", '
                       '"outputfile", and "outputattr" optionally defined.')
            if not isinstance(button, dict):
                raise LorisError(message)
            elif not all([
                    isinstance(button.get('script', None), str),
                    isinstance(button.get('validate', []), list),
                    isinstance(button.get('insert', False), bool),
                    isinstance(button.get('configattr', ''), str),
                    isinstance(button.get('outputfile', ''), str),
                    isinstance(button.get('outputattr', ''), str),
            ]):
                raise LorisError(message)

            button['script'] = secure_filename(button['script'])
            if not os.path.exists(
                    os.path.join(autoscript_filepath, button['script'])):
                raise LorisError(f'{base_message} script "{button["script"]}" '
                                 'does not exist in autoscript folder.')

            if 'outputfile' in button:
                button['outputfile'] = secure_filename(button['outputfile'])

        if not isinstance(config_forms, dict):
            raise LorisError(f'In configuration file of autoscript '
                             f'"{os.path.basename(autoscript_filepath)}", '
                             '"forms" keyword is incorrectly '
                             'formatted.')

        forms = {}
        post_process_dict = {}
        for key, value in config_forms.items():
            form, post_process = form_creator(value, autoscript_filepath,
                                              **kwargs)
            forms[key] = form
            post_process_dict[key] = post_process

        return forms, post_process_dict, buttons