Skip to content

Conversation

@josenavas
Copy link
Contributor

Adds the plugin class so plugins do not need to duplicate a lot of code.

That class includes all the relevant code so the plugins can install themselves.

Copy link
Member

@antgonza antgonza left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A couple of comments/questions

self.fp_types = filepath_types


class BaseQiitaPlugin(object):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do the methods in this object get tested?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a BaseClass so I'm not instantiating it directly, but all the methods are being executed through the child clases


self._register_command(html_cmd)

def _install(self, qclient):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about this one?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor

@wasade wasade left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Few comments, looking pretty good :)

function : callable
The function that executes the command. Should be a callable that
conforms to the signature:
`function(qclient, job_id, job_parameters, output_dir)`
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

are any of these nullable, and is there a return signature?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, they can't be nullable. Yup, there is a return signature, I've updated the documentation to note that.

# `function` will be called with the following Parameters
# qclient, job_id, job_parameters, output_dir
# Make sure that `function` can receive 4 parameters
if function.__code__.co_argcount != 4:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

:/ this is still python 2, right? Annotations would be so awesome here

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

py2/py3 compatible. I want this utility library to be as general as possible so we can use it with QIIME1 and QIIME2, although I expect more devs use this to create the plugins of their own tools (which we don't control if they're py2 or py3).

The artifact type name
description : str
The artifact type description
can_be_submitted_to_ebi : bool
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what's the likelihood that external submission or data exchanges will expand in the future? if it is reasonable, then it may make sense to do a more discoverable structure, something like external_exchange which could be {'ebi': True, 'vamps': False, ...}. if expansion here is unlikely, then I think the attrs make sense

Copy link
Contributor Author

@josenavas josenavas Sep 28, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Uhm, not sure. @antgonza any thoughts? If this can expand we may want to rethink also how to change the current DB structure to support this (although we can refer that for later)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's likely to change but not sure when. I agree with the approach of dealing with it once we know it's actually going to happen. Note that this will also require changes in the main qiita code base ...

self.name = name
self.version = version
self.description = description
self.publications = dumps(publications) if publications else ""
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is there a plugin type which we anticipate wont have a publication or doi? if they don't, we could always require tying a version to zenodo. perhaps unnecessary

Copy link
Contributor Author

@josenavas josenavas Sep 28, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would be surprised if something doesn't have a DOI - I think in previous discussions we agreed to use DOI since it is more common to have a DOI.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is going to be cases we are going to be using tools without publication, the first one that comes to mind is: KneedData.

conf_dir = environ.get(
'QIITA_PLUGINS_DIR', join(expanduser('~'), '.qiita_plugins'))
self.conf_fp = join(conf_dir, "%s_%s.conf" % (self.name, self.version))

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

would it make sense for an assert over os.path.exists?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The conf_fp doesn't necessarily exists at this point (see the function generate_config). The change that I made is that now both Qiita and the plugin can actually use the same config file. There is some information that needs to be known by both of them in order to communicate so at least they need to share a file (which can then be copied over multiple locations and allow plugin execution from anywhere).

"""
self.task_dict[command.name] = command

def _install(self, qclient):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is semantic, but i think of install as actually putting a program, library, or documentation somewhere in a logical place. in this case, this I think more mirrors a registration, where a call to ._register would establish this plugins existence with qiita?

Copy link
Contributor Author

@josenavas josenavas Sep 28, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right - changed to _register.

def _install(self, qclient):
"""Installs the plugin in Qiita"""
# Get the command information from qiita
info = qclient.get('/qiita_db/plugins/%s/%s/'
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm a bit confused here, qiita already knows about the plugin prior to the install?

Should the .status_code be checked to test for 200?

Copy link
Contributor Author

@josenavas josenavas Sep 28, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, so when Qiita starts the server, it will load the plugin configuration files. The reason behind this is that in order to communicate the plugin (client) and Qiita (server) they need to both know the credentials. So the config file is generating those credentials, so in order to actually add a plugin to the system you need access to the FS to be able to add this configuration file. However, the configuration file only allows to "register" the plugin itself, but not the commands. The commands get added dynamically using the REST api, which is what is done here.

Is a bit complex, but I think this is the best balance between security and flexibility. This flexibility in the commands is needed mainly for QIIME 2, which is a system that works with plugins itself, so it is possible that there are commands that are no longer available, while new ones may appear.

Regarding the 200, the QiitaClient object (qclient) already performs those checks internally and it will raise a useful error in case that the return code is different from 200. The object returned from the get and post is actually a dictionary, which contains the decoded JSON from the request body.

if cmd.name in info['commands']:
qclient.post('/qiita_db/plugins/%s/%s/commands/%s/activate/'
% (self.name, self.version,
cmd.name.replace(' ', "%20")))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what about

> import urllib
> urllib.parse.quote("foo bar")
'foo%20bar'

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should this post, and the one on line 181 have their status codes checked?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the tip! See my answer above regarding the status codes.

-----
Both `validate_func` and `html_generator_func` should be a callable
that conforms to the signature:
function(qclient, job_id, job_parameters, output_dir)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is there a return?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, it is actually the same signature than above - changed.

command: QiitaCommand
The command to be added to the plugin
"""
self._register_command(command)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it seems like this should just be public?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't want the _register_command function to be public on the QiitaTypePlugin class (although I'm using it internally). The rationale is that the commands in the QiitaTypePlugin are controlled (there are only 2 - 'Validate' and 'Generate HTML summary'), while QiitaPlugin can have any number of commands. However, both are actually represented in the same way, and that's why most of the functionality is added in the base class.

@antgonza antgonza merged commit d09cf35 into qiita-spots:master Sep 28, 2016
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants