Extension:Gnuplot

From MediaWiki.org

Jump to: navigation, search
Manual on MediaWiki Extensions
List of MediaWiki Extensions
Gnuplot

Release status: stable

Implementation Parser extension, Hook
Description Tags to define Gnuplot script and render graph
Author(s) DavidSeymore Talk
Version 1.0 (2008-01-19)
MediaWiki 1.11
License GPL
Download no link
Hooks used

gnuplot
dataset

Contents

[edit] What can this extension do?

The Gnuplot extension enables a MediaWiki install to store gnuplot scripts as part of a wiki article, and produce PNG images of the graphs. This allows graphs to be versioned and embedded right into a mediawiki install.

[edit] Word of Warning

SECURITY RISK

The gnuplot script accepts potentially devlish inputs (rm -rf /) as actual commands into its script. You really need to be careful what audience you allow to use this extension, and make sure you have a pretty tight leash on the user that runs your webhost. I'll make a change to the script to allow using a different user to run the command than server is run on so that it can be locked down should you want to use this on a more open wiki.

[edit] Usage

At my job we do agile development with the scrum methodology and use the wiki to track our burndown/velocity. Graphs like this make it easy for us to see what tickets are finished when, and how the teams are progressing. We have two teams, Autobots and Decepticons, and name our iterations after animals (in alphabetical order). So, here is an example:

Image:Example gnuplot.png

<gnuplot>set title "Scrum Burndown - Quimilero Iteration"
set yrange [-5:40]
set xtics (0,1,2,3,4,5,6,7,8,9,10)
set xrange [0:10]
set xlabel "Day of Iteration"
set ylabel "Units Remaining"
plot \
<dataset>
0 33
1 33
2 26 #GS-796, GS-798, GS-799, GS-800
3 23 #GS-803
4 21 #GS-725
5 17 #GS-804
6 12 #GS-756
7 8 #GS-690
8 1 #GS-805 GS-832 and GS-744 off backlog sized at iteration review
</dataset> using 1:2 title "Autobots" with lines lt 4, \
<dataset>
0 24
1 24 #nothing done on day one
2 19 #GS-775, GS-272, GS-727
3 7 #AG-170, GS-714, GS-442, GS-677
4 3 #this is monday..OP-123 is resolved. 
5 -2 #GS-85
8 -2
</dataset> using 1:2 title "Decepticons" with lines lt 14
</gnuplot>

and any of the ones from the Gnuplot Demos site.


[edit] Installation

require_once('extensions/gnuplot.php');
  • Touch a file in the extensions directory named gnuplot.php
  • Edit the file and place the following code in it:


[edit] Configuration parameters

  • The gnuplot.php needs to know the absolute path of the gnuplot binary.


[edit] Code

<?php
/**
 * Parser hook extension adds a <gnuplot> tag and <dataset> tag to wiki markup for rendering gnuplot plots!
 * 
 * 
 * Usage is something like this:
 * <gnuplot>
 * set title "Text"
 * set yrange [0:60]
 * set xtics (0,1,2,3,4,5,6,7,8,9,10)
 * set xrange [0:10]
 * plot <dataset>0 1
 * 2 10
 * 3 15
 * </dataset> using 1:2 title "Burndown" with lines
 * </gnuplot>
 * 
 * Options should be heroicly wide open.. if you can wrap your head around all the possibilities listed in the
 * gnuplot documentation. 
 * 
 **/
 
// Make sure we are being called properly
if( !defined( 'MEDIAWIKI' ) ) {
        echo(" This file is an extension to the MediaWiki software and cannot be used standalone.\n" );
        die( -1 );
}
 
$wgExtensionFunctions[] = "wfGnuplotExtension";
 
/**
 * You should refer to the gnuplot documentation for the different output types
 * Honestly, only image types will work without some other changes
 */
$image_format = "png";
$gnuplot_path   = "/usr/bin/gnuplot";
 
/**
 * The function links its methods as
 * hooks for the Parser
 */
function wfGnuplotExtension() {
        global $wgParser;
        # register the extension with the WikiText parser
        # the first parameter is the name of the new tag.
        # the second parameter is the callback function for
        # processing the text between the tags
        $wgParser->setHook( "dataset", "saveDATA" );
        $wgParser->setHook( "gnuplot", "renderPLOT" );
}
 
 
/**
 * This method setups up the output of the plot script, which 
 * has a few things that we dont want to repeat EVERYTIME someone 
 * wants to make a graph
 *
 * @param String $plot_source - we have to have the full source to wrap
 * @param String $output_filename - we have to tell gnuplot where to output the graph
 * @return the script with output stuff wrapped in it
 */
function wrap_script($plot_source, $output_filename) {
        global $image_format;
        $something  = "set terminal ".$image_format."\n";
        $something .= "set output \"".$output_filename."\"\n";
        $something .= $plot_source;
        return $something;
}
 
/**
 * Caches the command script and calls the gnuplot executable to render the
 * graph image
 *
 * @param String $plot_script
 * @return Nothing.. we assume this all goes correctly.
 */
function renderGnuPlot(&$plot_script) {
        global
        $wgMathDirectory,
        $wgMathPath,
        $wgTmpDirectory,
        $image_format,
        $gnuplot_path;
 
 
        // hash our script
        $script_hash = md5($plot_script);
        // make the output of the command be in the math directory
        $filename = $wgMathDirectory."/plot-".$script_hash.".".$image_format;
 
        //fill in the extras our consumer doesn't care about
        $full_plot_script = wrap_script($plot_script,$filename);
 
        $current_dir = getcwd();
 
        chdir($wgTmpDirectory);
 
        // create temporary file for the plot script
        $winPath = $wgTmpDirectory."/".$script_hash.".plot";
        $fp = fopen($winPath,"a+");
        $w= fputs($fp,$full_plot_script);
        fclose($fp);
 
        // run gnuplot to create our file
        $command = $gnuplot_path." ".$script_hash.".plot ";
        //I had a problem with the status code always being false. so, stopped using it
        $status_code = exec($command);
 
        //delete the plot file
        unlink($wgTmpDirectory."/".$script_hash.".plot");
        chdir($current_dir);
}
 
 
/**
 * Determins if a graph file has already been rendered
 * if not, calls to render one and returns the root host url to the image. 
 *
 * @param String $plot_script
 * @return images/math/plot-.... file reference for the image tag
 */
function getPlotImageURL($plot_script) {
        global
        $wgMathDirectory,
        $wgMathPath,
        $image_format;
 
        //unique our script
        $formula_hash = md5($plot_script);
 
        $filename = 'plot-' . $formula_hash.".".$image_format;
        $full_path_filename = $wgMathDirectory."/".$filename;
 
        if (is_file($full_path_filename)) {
                //nothing to do
                //we've already made this image before
        } else {
                renderGnuPlot($plot_script);
        }
        return $wgMathPath."/".$filename;
}
 
/**
 * The callback function for converting the input text to HTML output
 *
 * @param String $input
 * @param Array<String> $argv
 * @param Parser $parser
 * @return HTML image tag to the graph itself
 */
function renderPLOT( &$input, $argv, &$parser ) {
        //get our parser to finish up its parse and recover the real data-set data (the UNIQ QINU problem)
        $input = $parser->recursiveTagParse( $input );
        $input = $parser->mStripState->unstripBoth($input);
        $url = getPlotImageURL($input);
 
        return "<img src='$url'>";
 
}
 
/**
 * This method takes in the raw tuple input of the dataset, and any arguments, and saves it to a random
 * file on the system. The return is the absolute path to the data file, so that it can be 
 * embedded into the plot command.
 *
 * @param String $input
 * @param Array<String> $argv
 * @param Parser $parser
 * @return unknown
 */
function saveDATA( $input, $argv, &$parser){
        global
        $wgMathDirectory,
        $wgMathPath,
        $wgTmpDirectory;
 
        //make a temp filename with a hash of the input
        $filename = md5($input);
        //we dont wanna create toooo many of these guys either
        if (!is_file($wgTmpDirectory."/".$filename.".dat")) {
                //know where we are
                $current_dir = getcwd();
                //than change it
                chdir($wgTmpDirectory);
 
                // saving this data set as a temporary RANDOM file
                $winPath = $wgTmpDirectory."/".$filename.".dat";
                $fp = fopen($winPath,"a+");
                $w=fputs($fp,$input);
                fclose($fp);
 
                chdir($current_dir);
        }         
        return "\"".$wgTmpDirectory."/".$filename.".dat\"";
}
 
?>

[edit] Thanks

  • Millions of thanks to Isnogud on the #mediawiki irc channel for talking me through some of my parser issues.
  • Thanks to the open source contributions of Everyone in the community and the developers on the Gnuplot project.

[edit] Note

In the images directory you also need to create the subdirectories math and tmp. It took me some time to figure this out, so this info might save you some time.

[edit] See also

Personal tools