How to Write a Plugin - Export Data
Thursday, March 8, 2012 at 12:36PM
NicheVision in ArmedXpert Menu, Plugin

A guide describing how to add a custom method for exporting data using an IronPython plugin.

To create an export plugin for ArmedXpert we will have to write a script in a language known as IronPython. More details about IronPython can be found [http://ironpython.codeplex.com/|here].

The first step to creating an export plugin is to open up ArmedXpert and open the options menu.

There are two buttons on the bottom left of the "General" tab of the options menu. Click "Open Plugins Directory" to open a Windows Explorer window at the location where plugin files must go. Open the "export" directory. This is where our script files will go.

You should see a file here called "export_base.py". This is a convenience file for writing our script and will be necessary for the steps in this guide. If it is not there, contact NicheVision for assistance.

Create Script File

Now we'll create our script file. We'll be creating a simple CSV text-file exporter. Right click on the explorer window and click New>Text Document. Rename the file, including the extension, to csv_example.py

Edit Script

Now we have to edit the script. The ArmedXpert team's favorite tool for this is called Notepad++, which features excellent syntax highlighting, among other useful features. However you can use any text editing tool that doesn't add extra data to the file (eg.,Windows Notepad)

The image is of the entire script. Now we can go line by line through the code and see what it does.

from export_base import exportBase    

This line imports a class called exportBase from the module export_base. Notice that export_base is the name of the file (minus the extension) that we mentioned in the previous step.

__plugin_name__ = "CSV Example"   

''__plugin_name__'' is a special variable that indicates the name of our plugin. This is the value that will be displayed in the interface of ArmedXpert. It is important to enter, otherwise your plugin will have a randomly-generated name that will make it difficult to recognize.

__file_filters__ = "Text File (*.txt) | *.txt;  

''__file_filters__'' is a special variable that will indicate to ArmedXpert the extension of the file being saved by this plugin. This variable is optional, but if it is not specified the user will have to specify the file extension when saving with our plugin. See [http://msdn.microsoft.com/en-us/library/system.windows.forms.filedialog.filter.aspx|FileDialog.Filter Property] for more information on file filters.

 class csvExample(exportBase):

Now we declare a class called csvExample, which will handle our exporting. The details of the syntax are not important, just know that this line lets IronPython know that this is a data structure that can be used to export data.

 delimiter = "\t"

 This line defines the character(s) that will act as the delimiter for our CSV export. The character "\t" represents the tab character. We could change this to a comma if we wanted data in rows to be separated by a comma instead.

def writeSamples(self,sampleCollection):

Defines a function called writeSamples that takes two arguments. You can ignore "self", just remember it has to be in every function defined in our class. Since we are extending the exportBase class, this function is all that we really need to implement. Samples will be passed to this function for processing.

"sampleCollection" will be an object that represents a collection of samples as represented by ArmedXperts object model. It can be one of two things:

ISampleSource. This is what the argument will usually be. This is an object that ties together a collection of samples and where they came from. To access the samples of an instance of ISampleSource, you can use the AllEntries property (eg, sampleCollection.AllEntries).  It may also be a SampleCollection, from which samples can be enumerated directly.

if not hasattr(sampleCollection, "AllLoci"):    sampleCollection = sampleCollection.AllEntries

This line helps us determine what sort of data that the sampleCollection argument is. If it has no attribute called "AllLoci", that means it is an instance of ISampleSource, and since we directly want an instance of SampleCollection, we use the AllEntries property and reassign it to the sampleCollection variable.

columns = ["Sample ID",]

We need some way to track the data that each "column" of the CSV represents, so that we will be consistent across all of our samples. So we create a list called ''columns'', initialized with the header string "Sample ID".

for marker in sampleCollection.AllLoci:     if marker not in columns:         columns.append(marker)

Now we need to determine the columns for each locus. We do this be accessing the AllLoci property of our collection. This gives us a list of all of the unique locus markers across all of our samples. We add each to our list of columns. Now, if Amelogenin is in position 16 of this list, then the values for that locus for each sample will go to column 16.

file = open(__export_file__,"w")

Opens the file that we will be writing to. ''__export_file__'' is a special variable that indicates the path to the file that the user chose to export to.

header_string = .join(['"%s"%s'% (str(header), self.delimiter) for header in columns])

This line is complex. It creates a string that represents the column titles of our CSV data. The join function concatenates a list of values into a string.

"%s"%s'% (str(header), self.delimiter) is a string format operation. %s represents data to be inserted into a string, the lone % tells IronPython that this is a string format operation, and (str(header), self.delimiter) is the data passed to the string. An example of the resulting string from this operation could be: Sample Id","D8S1179","FGA"

file.write("%s\n" % header_string)

Writes the string created in the previous step to our file, using the same formatting method described above. ''\n'' represents a newline, which will force the next write operation to this file onto its own line.

for sample in sampleCollection:

This starts a loop that will, for each sample in our collection, assign all data of that sample to a variable called ''sample''. We can use this variable to access data about that sample.

sampleData = {"Sample ID" : sample.SampleName}

This creates what is called a dictionary. A dictionary is a structure that maps keys to values. In this case, we have created a dictionary called ''sampleData'', in which the key "Sample ID" is mapped to the name of the current sample.

We will use this dictionary to define the values of each column for the current sample.

for locus in sample.Loci:

This starts a loop through the loci of the current sample, assigning data to a variable called ''locus''.

sampleData[locus.Identifier] = locus.Alleles.Names

Using our dictionary ''sampleData''', we assign a key that is the marker of our locus (aka, Identifier) to a string value representing the names of the alleles at the locus. Names is a comma-delimited string value of the names of each allele at this locus. There is other information available from the locus.Alleles property, such as locus.Alleles.Sizes and locus.Alleels.Heights

self.write_dict_csv(columns, sampleData, file)

Calls a function of our class called ''write_dict_csv'', and passes our list of columns, the sampleData dictionary, and the file we are writing to. The ''write_dict_csv'' function will be described later in this guide.

file.close()

Finally we are done with our loops and can close the file. It is important to remember to close the file in order to clean up resources, and make sure the file is created correctly.

def write_dict_csv(self,fieldnames, rowdict, stream):

Creates a function called ''write_dict_csv''. This function accepts a list called fieldNames, a dictionary called rowdict, and a writeable object called stream.

dict_list = [rowdict[key] if key in rowdict else "" for key in fieldnames]

Another complex call, using python list comprehension. Creates a list called ''dict_list'' which uses ''fieldnames'' (our list of columns) as a list of keys into ''rowdict'' (our sample data) to compile a list of allele names in our loci order.

data_string = "".join(["\"%s\"%s"% (str(data), self.delimiter) for data in dict_list])

This expression is similar to the way in which we created the header string a few steps earlier. It uses string formatting to join together our sample data separated by our specified delimiter.

stream.write("%s\n" % data_string)

Writes the string created in the previous step to our file.

pluginObject = csvExample()

The final line of our script is one of the most important. Note that it is outside of the indentation level (and thus the scope of) our csvExample class. csvExample() creates a new instance of our ''csvExample'' class and assigns it to a variable called pluginObject.

ArmedXpert looks for a variable called ''pluginObject'' and calls the Export methods of that object in order to use our code.

That's all of the code for this plugin!

Now we have to get the plugin into ArmedXpert.

If you already have ArmedXpert open, in order to see this plugin  in the Export>Plugins menu, you must first open the Options menu and click the "Refresh Plugins" button. Exit the options menu. There should now be a "CSV Example" button under Export>Plugins.

If you do not have ArmedXpert open, open it now. Our plugin will load automatically into the Export menu as a button labelled "CSV Example".

That's it!

 

 

Article originally appeared on armedxpert (http://armedxpert.com/).
See website for complete article licensing information.