<file_sep><?php

class RainhawkTest extends UnitTest {
    public $class = "Rainhawk";

    public function __before() {
        rainhawk::select_database("tests");

        return $this;
    }

    public function testSelectCollection() {
        $this->assert(rainhawk::select_collection("datasets"));
    }
}

?><file_sep><?php

require_once "includes/core.php";
require_once "includes/check_login.php";

$dataset = isset($_GET['dataset']) ? htmlspecialchars($_GET['dataset']) : null;
$datasetInfo = $rainhawk->fetchDataset($dataset);
$fields = $datasetInfo['fields'];
$constraints = $datasetInfo['constraints'];
$readList = $datasetInfo['read_access'];
$writeList = $datasetInfo['write_access'];
$accessList = array_unique(array_merge($readList, $writeList));

?>
<!DOCTYPE html>
<html lang="en-GB">
    <head>
        <title>Project Rainhawk - Properties</title>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <?php require_once "includes/meta.php"; ?>
        <script>
            rainhawk.apiKey = "<?php echo $mashape_key; ?>";

            var dataset = "<?php echo $dataset; ?>";
            var newUserCount = 0;

            $(function() {
                $(".confirm").confirm({
                    text: "Are you sure you wish to revoke this user's permission?",
                    title: "Really revoke?",
                    confirmButton: "Revoke",
                    confirm: function(btn) {
                        var username = $(btn).data("user");

                        rainhawk.access.remove(dataset, username, null, function(data) {
                            $("[data-row-user='" + username + "']").remove();
                        }, function(message) {
                            $("#accessBody").prepend("<div class='alert alert-danger alert-dismissable'><button type='button' class='close' data-dismiss='alert' aria-hidden='true'>&times;</button><strong>Error revoking.</strong> " + message + "</div>");
                        });
                    }
                });

                $(document).on("change", ".writecheck", function(e) {
                    var $write = $(this);
                    var $read = $write.parents("tr").find(".readcheck");

                    if($write.is(":checked")) {
                        $read.prop("checked", true);
                    }
                });
            });

            function addUser() {
                window.newUserCount++;

                $("#tblPermissions").append(
                    "<tr data-row-user-num=" + window.newUserCount + ">" +
                        "<td><input type='text' name='newUser[" + window.newUserCount + "][user]' class='form-control'></td>" +
                        "<td class='text-center'><input type='checkbox' class='readcheck'  name='newUser[" + window.newUserCount + "][read]' value='read' checked></td>" +
                        "<td class='text-center'><input type='checkbox' class='writecheck'  name='newUser[" + window.newUserCount + "][write]' value='write'></td>" +
                        "<td><button type='button' data-user-num=" + window.newUserCount + " onclick='cancelNewUser(this);' class='btn btn-warning btn-sm'>Cancel</button></td>" +
                    "</tr>"
                );
            }

            function cancelNewUser(btn) {
                $("[data-row-user-num='" + $(btn).data("user-num") + "']").remove();
            }
        </script>
    </head>

    <body>
        <?php require_once "includes/nav.php"; ?>
        <div class="container">
            <div class="row">
                <div class="col-md-12">
                    <div class="panel panel-default">
                        <div class="panel-body">
                            <h1><?php echo $dataset; ?></h1>
                            <p>Update the structure of your dataset...</p>
                        </div>
                    </div>
                </div>
            </div>
            <div class="row">
                <div class="col-md-6">
                    <div class="panel panel-default">
                        <div class="panel-heading">
                            <h4>Fields</h4>
                        </div>
                        <div class="panel-body">
                            <form id="fieldForm" action="/proxy/dataset_constraints.php?dataset=<?php echo $dataset; ?>" method="post">
                                <table class='table'>
                                    <thead>
                                        <tr>
                                            <th class="col-md-7">Name</th>
                                            <th class="col-md-5">Constraint</th>
                                        </tr>
                                    </thead>
                                    <tbody>
                                        <?php foreach($fields as $field) { ?>
                                            <?php if($field == "_id") continue; ?>
                                            <?php $type = count($constraints) > 0 && in_array($field, array_keys($constraints)) ? $constraints[$field]['type'] : "none"; ?>
                                            <?php if(in_array($user, $writeList)) { ?>
                                                <tr>
                                                    <td><?php echo $field; ?></td>
                                                    <td>
                                                        <select id="<?php echo $field; ?>" name="constraint[<?php echo $field; ?>]" class="form-control">
                                                            <?php foreach(array("none", "integer", "string", "latitude", "longitude", "float", "timestamp") as $possible) { ?>
                                                                <option value="<?php echo $possible; ?>" <?php echo $type == $possible ? "selected" : null; ?>><?php echo ucwords($possible); ?></option>
                                                            <?php } ?>
                                                        </select>
                                                    </td>
                                                </tr>
                                            <?php } else { ?>
                                                <tr>
                                                    <td><?php echo $field; ?></td>
                                                    <td><?php echo $type; ?></td>
                                                </tr>
                                            <?php } ?>
                                        <?php } ?>
                                    </tbody>
                                </table>
                            </form>
                        </div>
                        <?php if(in_array($user, $writeList)) { ?>
                            <div class="panel-footer text-right">
                                <button type="submit" form="fieldForm" class="btn btn-success" formaction="/proxy/dataset_constraints.php?dataset=<?php echo $dataset; ?>&autoapply" formnovalidate>Auto-Detect</button>
                                <button type="submit" form="fieldForm" class="btn btn-default">Apply Changes</button>
                            </div>
                        <?php } ?>
                    </div>
                </div>
                <div class="col-md-6">
                    <div class="panel panel-default">
                        <div class="panel-heading">
                            <h4>Permissions</h4>
                        </div>
                        <div id="accessBody" class="panel-body">
                            <form id="accessForm" action="/proxy/dataset_permissions.php?dataset=<?php echo $dataset; ?>" method="post">
                                <table class="table">
                                    <thead>
                                        <tr>
                                            <th class="col-md-9">User</th>
                                            <th class="col-md-1 text-center">Read</th>
                                            <th class="col-md-1 text-center">Write</th>
                                            <th class="col-md-1 text-center"></th>
                                        </tr>
                                    </thead>
                                    <tbody id="tblPermissions">
                                        <?php foreach($accessList as $key => $username) { ?>
                                            <?php $isWrite = in_array($username, $writeList); ?>
                                            <?php $isRead = in_array($username, $readList); ?>
                                            <?php if(in_array($user, $writeList)) { ?>
                                                <tr data-row-user="<?php echo $username; ?>">
                                                    <td><?php echo $username; ?></td>
                                                    <td class="text-center">
                                                        <input type="checkbox" class="readcheck" name="currentUser[<?php echo $username; ?>][read]" value="read" <?php echo $isRead ? "checked" : null; ?>>
                                                    </td>
                                                    <td class="text-center">
                                                        <input type="checkbox" class="writecheck" name="currentUser[<?php echo $username; ?>][write]" value="write" <?php echo $isWrite ? "checked" : null; ?>>
                                                    </td>
                                                    <td>
                                                        <button type="button" data-user="<?php echo $username; ?>" class="btn btn-sm btn-danger confirm">Revoke</a>
                                                    </td>
                                                </tr>
                                            <?php } else { ?>
                                                <tr data-row-user="<?php echo $username; ?>">
                                                    <td><?php echo $username; ?></td>
                                                    <td class="text-center">
                                                        <input type="checkbox" value="read" disabled <?php echo $isRead ? "checked" : null; ?>>
                                                    </td>
                                                    <td class="text-center">
                                                        <input type="checkbox" value="write" disabled <?php echo $isWrite ? "checked" : null; ?>>
                                                    </td>
                                                    <td></td>
                                                </tr>
                                            <?php } ?>
                                        <?php } ?>
                                    </tbody>
                                    <tfoot>
                                        <tr>
                                            <td class="text-center" colspan="100%">
                                                <?php if(in_array($user, $writeList)) { ?>
                                                    <button type="button" class="btn btn-sm btn-success" onclick="addUser()">Add User</a>
                                                <?php } ?>
                                            </td>
                                        </tr>
                                    </tfoot>
                                </table>
                            </form>
                        </div>
                        <?php if(in_array($user, $writeList)) { ?>
                            <div class="panel-footer text-right">
                                <button type="submit" form="accessForm" class="btn btn-default">Apply</button>
                            </div>
                        <?php } ?>
                    </div>
                </div>
            </div>
        </div>
    </body>
</html><file_sep>eco.charts.glscatter = function() {

  return {
    title: '3D Scatter Graph',

    options : {
      width : 1024,
      height : 768,
      margin : {
        top: 30,
        right: 30,
        bottom: 30,
        left: 30
      }
    },
    
    render: function( data, xValue, yValue, zValue, target ) {
      
      var options = this.options,
        width = options.width,
        height = options.height;

      var margin = {
        top: options.margin.top,
        right: options.margin.right,
        bottom: options.margin.bottom,
        left: options.margin.left
      };

      var state = { rotate: false, drag: false };
      var canvas = document.createElement("canvas");
      canvas.id = 'webglcanvas';
      canvas.height = height;
      canvas.width = width;
      canvas.addEventListener( 'mousedown', onMouseDown );
      canvas.addEventListener( 'mousewheel', onMouseWheel );
      canvas.addEventListener( 'DOMMouseScroll', onMouseWheel );
      target.appendChild( canvas );

      var centre = new THREE.Vector3( 0, 0, 0 );
      var size = 150;
      var camDist = 350,
        minCamDist = 300,
        maxCamDist = 400;
      var hAngle = Math.PI/2,
        vAngle = Math.PI/4;
      var hRotateSpeed = 1,
        vRotateSpeed = 0.5;
      var mouseStart = new THREE.Vector2(),
        mouseEnd = new THREE.Vector2(),
        mouseDelta = new THREE.Vector2();

      var overlay = document.createElement("div");
      overlay.id = 'webgloverlay';

      var dimensions = [];
      var dCount = 0;
      if( xValue != "" ) {
        dCount++;
        dimensions.push( xValue );
      }
      if( yValue != "" ) {
        dCount++;
        dimensions.push( yValue );
      }
      if( zValue != "" ) {
        dCount++;
        dimensions.push( zValue );
      }
      if( dCount == 0 ) alert( "Warning: no dimensions enabled!" );

      var scene = new THREE.Scene();
      var camera = new THREE.PerspectiveCamera( 45, 
        width/height, 1, 4000 );
      camera.position.z = 150;
      var renderer = new THREE.WebGLRenderer( 
        { canvas: canvas, antialias: true, alpha: true } );
      renderer.setClearColor( 0x444444, 1 );
      renderer.setSize( width, height );
      var graph = createScatterGraph( data, dimensions, dCount );
      updateCamera();

      scene.add( graph );
      animate();
      ////////////////////////////////////////////////////////////////////////
      ////////////////////////////////////////////////////////////////////////

      function createScatterGraph( data, dimensions, dCount ) {

        var graph = new THREE.Object3D();
        var geometry = new THREE.Geometry();

        var scale = [];
        for( var i = 0; i < dCount; i++ ) {
          scale.push( getScale( data, dimensions[i] ) );
        }

        var axis = [];
        for( var i = 0; i < dCount; i++ ) {
          var lineGeo = new THREE.Geometry();
          var origin = new THREE.Vector3();
          var end = new THREE.Vector3();
          var colour;
          if( i == 0 ) {
            colour = 0xff0000;
            end.x = size;
          } else if( i == 1 ) {
            colour = 0x0000ff;
            end.y = size;
          } else if( i == 2 ) {
            colour = 0x00ff00;
            end.z = size;
          }
          lineGeo.vertices.push( origin );
          lineGeo.vertices.push( end );
          var lineMat = new THREE.LineBasicMaterial( { color: colour } );
          var line = new THREE.Line( lineGeo, lineMat );
          axis.push( line );
        }

        var rescale = [];

        for( var i = 0; i < dCount; i++ ) {
          var range = scale[i].high;
          rescale.push( size / range );
        }

        for( var k in data ) {
          var vertex = new THREE.Vector3();
          for( var i = 0; i < dCount; i++ ) {
            if( data[k].hasOwnProperty( dimensions[i] ) ) {
              if( i == 0 ) vertex.x = data[k][dimensions[i]] * rescale[i];
              else if( i == 1 ) vertex.y = data[k][dimensions[i]] * rescale[i];
              else if( i == 2 ) vertex.z = data[k][dimensions[i]] * rescale[i];
            }
          }
          geometry.vertices.push( vertex );
        }

        var material = new THREE.ParticleSystemMaterial( { size : 1 } );
        var points = new THREE.ParticleSystem( geometry, material );

        graph.add( points );
        for( var i = 0; i < dCount; i++ ) graph.add( axis[i] );
        return graph;
      }

      function getScale( data, dimension ) {
        var size = data.length;
        var scale;
        if( size > 0 ) {
          if( data[0].hasOwnProperty( dimension ) ) {
            scale = { low: data[0][dimension], high: data[0][dimension] };
            for( var i = 1; i < size; i++ ) {
              if( data[i].hasOwnProperty( dimension ) ) {
                if( scale.low > data[i][dimension] )
                  scale.low = data[i][dimension];
                else if( scale.high < data[i][dimension] )
                  scale.high = data[i][dimension];
              }
            }
            return scale;
          }
          else {
            alert( "Invalid data." );
            return { low: 0, high: 0 };
          }
        }
        else {
          alert( "Could not generate scale: No data" );
          return { low: 0, high: 0 };
        }
      }

      function animate() {
        requestAnimationFrame( animate );
        render();
      }

      function render() {
        renderer.render( scene, camera );
      }

      function updateCamera() {
        var newCam = new THREE.Vector3();
        newCam.copy( centre );
        newCam.x += camDist * Math.cos( vAngle ) * Math.sin( hAngle );
        newCam.y += camDist * Math.sin( vAngle );
        newCam.z += camDist * Math.cos( vAngle ) * Math.cos( hAngle );
        camera.position.copy( newCam );
        camera.lookAt( centre );
      }

      function rotateHoriz( angle ) {
        hAngle -= hRotateSpeed * angle;
        if( hAngle > 2*Math.PI ) hAngle -= Math.PI*2;
        if( hAngle < 0 ) hAngle += Math.PI*2;
        updateCamera();
      }

      function rotateVert( angle ) {
        vAngle = Math.max( -Math.PI/2, Math.min( Math.PI/2, vAngle + vRotateSpeed * angle ));
        updateCamera();
      }

      function onMouseDown( event ) {
        if( event.button == 0 ) {
          state.rotate = true;
          if( !(state.drag) ) {
            mouseStart.set( event.clientX, event.clientY );
            document.addEventListener( 'mousemove', onMouseMove );
            document.addEventListener( 'mouseup', onMouseUp );
          }
        }
      }

      function onMouseWheel( event ) {
        var delta = 0;
        if( event.wheelDelta !== undefined ) {
          delta = event.wheelDelta;
        } else if( event.detail !== undefined ) {
          delta = -event.detail;
        }
        if( delta > 0 ) {
          camDist = Math.max( minCamDist, camDist-10 );
        } else {
          camDist = Math.min( maxCamDist, camDist+10 );
        }
        updateCamera();    
      }

      function onMouseMove( event ) {
        if( state.rotate ) {
          mouseEnd.set( event.clientX, event.clientY );
          mouseDelta.subVectors( mouseEnd, mouseStart );
          var width = canvas.width;
          var height = canvas.height;
          rotateHoriz( 2 * Math.PI * mouseDelta.x / canvas.width );
          rotateVert( 2 * Math.PI * mouseDelta.y / canvas.height );
          mouseStart.copy( mouseEnd );
        }
      }

      function onMouseUp( event ) {
        if( event.button == 0 ) {
          document.removeEventListener( 'mousemove', onMouseMove );
          document.removeEventListener( 'mouseup', onMouseUp );
        }
      }
    }
  }
};

<file_sep><?php

require_once "../includes/core.php";
require_once "../includes/check_login.php";

?>
<!DOCTYPE html>
<html lang="en-GB" ng-app="eco">
    <head>
        <title>Project Rainhawk - Visualise</title>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <?php require_once "../includes/meta.php"; ?>
        <link rel="stylesheet" href="style/charts.css">
        <link rel="stylesheet" href="style/style.css">
        <base href="/visualise/">
    </head>

    <!-- Remove ng-controller here as it is injected in js/app.js and shouldn't be injected twice-->
    <body>
        <?php

        ob_start();
        require_once "../includes/nav.php";
        $nav = ob_get_clean();
        ob_end_flush();

        // We have to set the links to target the root page instead of Angular.
        echo str_replace('href=', 'target="_self" href=', $nav);

        ?>

        <div id="main">
            <div ng-view></div>
        </div>

        <script>
            var apiKey = "<?php echo $mashape_key; ?>";
        </script>
        <script src="vendor/jquery/dist/jquery.min.js"></script>
        <script src="vendor/angular/angular.min.js"></script>
        <script src="vendor/angular-bootstrap/ui-bootstrap.js"></script>
        <script src="vendor/angular-route/angular-route.min.js"></script>
        <script src="vendor/d3/d3.min.js"></script>
        <script src="vendor/vega/vega.min.js"></script>
        <script src="vendor/three.js/src/three.min.js"></script>
        <script type="text/javascript" src="http://maps.google.com/maps/api/js?sensor=true"></script>
        <script src="js/app.js"></script>

        <!-- ECO -->
        <script src="js/eco.charts.js"></script>
        <script src="js/controllers.js"></script>
        <script src="charts/d3barchart.js"></script>
        <script src="charts/d3piechart.js"></script>
        <script src="charts/d3map.js"></script>
        <script src="charts/d3bubblechart.js"></script>
        <script src="charts/d3treemap.js"></script>
        <script src="charts/d3linegraph.js"></script>
        <script src="charts/glscatter.js"></script>
        <script src="js/directives.js"></script>
        <script src="js/services.js"></script>
    </body>
</html>
<file_sep><?php

/*!
 * Define different generic error messages, pre-json_encoded
 * for ease of use.
 */

define("JSON_ERROR_MAINTENANCE", json_encode(array(
    "meta" => array(
        "code" => 503,
        "version" => app::$version,
        "runtime" => app::runtime()
    ),
    "data" => array(
        "message" => "Maintenance is currently being performed."
    )
)));

define("JSON_ERROR_NOMETHOD", json_encode(array(
    "meta" => array(
        "code" => 402,
        "version" => app::$version,
        "runtime" => app::runtime()
    ),
    "data" => array(
        "message" => "No method was specified."
    )
)));

/*!
 * Define a function to handle displaying responses in a
 * simple to use way, with error messages and normal responses.
 */

function json_render($code = 200, $data = array()) {
    return json_encode(array(
        "meta" => array(
            "code" => $code,
            "version" => app::$version,
            "runtime" => app::runtime()
        ),
        "data" => $data
    ));
}

function json_render_error($code = 300, $message) {
    return json_render($code, array(
        "message" => $message
    ));
}

/*!
 * Define a function that takes JSON input and formats it nicely
 * into good looking JSON that's humanly readable.
 */

function json_beautify($json) {
    $result = "";
    $pos = 0;
    $length = strlen($json);
    $indent = "    ";
    $newline = "\n";
    $previous_character = "";
    $out_of_quotes = true;

    for($i = 0; $i <= $length; $i++) {
        $char = substr($json, $i, 1);

        if($char == '"' && $previous_character != '\\') {
            $out_of_quotes = !$out_of_quotes;
        } else if(($char == "}" || $char == "]") && $out_of_quotes) {
            if(substr(trim($result), -1) == "[") {
                $result = trim($result);
                $pos--;
            } else {
                $result .= $newline;
                $pos--;

                for($j = 0; $j < $pos; $j++) {
                    $result .= $indent;
                }
            }
        }

        if($char == ":" && $previous_character == '"') $char .= " ";

        $result .= $char;

        if(($char == "," || $char == "{" || $char == "[") && $out_of_quotes) {
            $result .= $newline;
            if($char == "{" || $char == "[") $pos++;

            for($j = 0; $j < $pos; $j++) {
                $result .= $indent;
            }
        }

        $previous_character = $char;
    }

    return $result;
}

/*!
 * If our App is currently in maintenance mode, we instantly
 * kill all current operations, and output the maintenance
 * page. This page does not contain any includes so we can be
 * sure that it will not cause strain or perform any queries
 * on the server.
 */

if(app::$maintenance) {
    echo json_beautify(JSON_ERROR_MAINTENANCE);
    exit;
}

?><file_sep><?php

include("includes/kernel.php");
include("includes/api/core.php");

/*!
 * Check if the user provided enough information to create the new
 * dataset, including the name and description.
 */

if(empty($data->prefix) || empty($data->name)) {
    echo json_beautify(json_render_error(401, "You didn't pass one or more of the required parameters."));
    exit;
}

/*!
 * Check to see if the dataset exists, and that we have access to it.
 * We need to use the prefix and the name of the dataset to get a
 * reference to it.
 */

// Create a new dataset object.
$dataset = new rainhawk\dataset($data->prefix, $data->name);

// Check that the dataset exists.
if(!$dataset->exists) {
    echo json_beautify(json_render_error(402, "The dataset you specified does not exist."));
    exit;
}

// Check that we can read from the dataset.
if(!$dataset->have_write_access(app::$username)) {
    echo json_beautify(json_render_error(403, "You don't have access to write to this dataset."));
    exit;
}

/*!
 * Define an empty array to store the results of whatever we need
 * to send back.
 */

$json = array();

/*!
 * Try and add the access to the dataset as the user has specified,
 * which will always work even if the access already exists.
 */

// Check if the username is set.
if(empty($data->username)) {
    echo json_beautify(json_render_error(404, "You didn't specify the user to remove access from."));
    exit;
}

// Check if the user is trying to revoke the owner's permissions.
if($data->username == $dataset->prefix) {
    echo json_beautify(json_render_error(405, "You can't revoke the owner's access to the dataset."));
    exit;
}

// Check if the type is set.
if(empty($data->type)) {
    // Remove all access.
    foreach(array("read", "write") as $type) {
        if(in_array($data->username, $dataset->{$type . "_access"})) {
            $dataset->{$type . "_access"} = array_diff($dataset->{$type . "_access"}, array($data->username));
        }
    }
} else {
    // Check if the user has that access or not.
    if(!in_array($data->username, $dataset->{$data->type . "_access"})) {
        echo json_beautify(json_render_error(406, "The user you specified does not have " . $data->type . " access to this dataset."));
        exit;
    }

    // Remove the user's access.
    $dataset->{$data->type . "_access"} = array_diff($dataset->{$data->type . "_access"}, array($data->username));
}

// Store the dataset information in the index table.
\rainhawk\sets::update($dataset);

// Return the removed attribute to the JSON.
$json['removed'] = true;

/*!
 * Output our JSON payload for use in whatever needs to be using
 * it.
 */

echo json_beautify(json_render(200, $json));
exit;

?><file_sep><?php

namespace Rainhawk;

/**
 * Rainhawk\Sets
 *
 * Dataset class to be used in the Rainhawk framework. This
 * class provides an interface for feeding data into and out
 * from a collection in MongoDB.
 *
 * @package Rainhawk\Sets
 */

class Sets {
    /**
     * Holds a reference to our "system.datasets" collection which
     * has information about each dataset.
     *
     * @var MongoCollection
     */

    private static $datasets;

    /**
     * Create a new dataset in the system.datasets index, with the
     * relevant data so that we can start using other operations on
     * it.
     *
     * @param Dataset $dataset  The dataset object to base the record on.
     * @return bool  Whether the dataset was created or not.
     */

    public static function create($dataset) {
        $datasets = self::get_system_datasets();

        try {
            $datasets->insert(array(
                "prefix" => $dataset->prefix,
                "name" => $dataset->name,
                "description" => $dataset->description,
                "rows" => $dataset->rows,
                "fields" => $dataset->fields,
                "constraints" => $dataset->constraints,
                "read_access" => $dataset->read_access,
                "write_access" => $dataset->write_access
            ));

            return true;
        } catch(Exception $e) {}

        return false;
    }

    /**
     * Takes an input dataset and update the record in the system
     * dataset table.
     *
     * @param Dataset $dataset  The dataset object to store.
     * @return bool  Whether the dataset saved or not.
     */

    public static function update($dataset) {
        $datasets = self::get_system_datasets();

        try {
            $datasets->update(array(
                "prefix" => $dataset->prefix,
                "name" => $dataset->name
            ), array(
                '$set' => array(
                    "name" => $dataset->name,
                    "description" => $dataset->description,
                    "rows" => $dataset->rows,
                    "fields" => $dataset->fields,
                    "constraints" => $dataset->constraints,
                    "read_access" => $dataset->read_access,
                    "write_access" => $dataset->write_access
                )
            ));

            return true;
        } catch(Exception $e) {}

        return false;
    }

    /**
     * Remove the specified dataset from the system.datasets
     * table.
     *
     * @param Dataset $dataset  The dataset object to remove.
     * @return  bool  Whether the dataset was removed or not.
     */

    public static function remove($dataset) {
        $datasets = self::get_system_datasets();

        try {
            $datasets->remove(array(
                "prefix" => $dataset->prefix,
                "name" => $dataset->name
            ));

            return true;
        } catch(Exception $e) {}

        return false;
    }

    /**
     * Takes an input dataset ID and returns whether or not that
     * dataset was found in the reference table or not.
     *
     * @param string $prefix  The prefix for the user.
     * @param string $name  The name of the dataset.
     * @return bool  Whether the dataset was found or not.
     */

    public static function exists($prefix, $name) {
        $datasets = self::get_system_datasets();

        try {
            $results = $datasets->find(array("prefix" => $prefix, "name" => $name));

            return ($results->count() > 0);
        } catch(Exception $e) {}

        return false;
    }

    /**
     * Fetch the metadata about a dataset from the "system.datasets"
     * collection, which stores everything about each set in the database.
     *
     * @param string $prefix  The prefix for the user.
     * @param string $name  The name of the dataset.
     * @return array  An array of information about the set.
     */

    public static function fetch_metadata($prefix, $name) {
        $datasets = self::get_system_datasets();

        try {
            $results = $datasets->find(array("prefix" => $prefix, "name" => $name));
            $data = $results->getNext();

            unset($data['_id']);

            return $data;
        } catch(Exception $e) {}

        return false;
    }

    /**
     * Fetch a list of all of the datasets that the specified user
     * has read or write access to.
     *
     * @param string $username  The username to search for.
     * @return MongoCursor  A cursor to the list of datasets.
     */

    public static function sets_for_user($username) {
        $datasets = self::get_system_datasets();

        try {
            $results = $datasets->find(array(
                '$or' => array(
                    array("read_access" =>
                        array('$in' => array($username, "global"))
                    ),
                    array("write_access" =>
                        array('$in' => array($username, "global"))
                    )
                )
            ));

            return $results;
        } catch(Exception $e) {}

        return false;
    }

    /**
     * Check if the $datasets variable has been set, and if not
     * then set it.
     *
     * @return MongoCollection  The collection containing the information.
     */

    private static function get_system_datasets() {
        if(self::$datasets) {
            return self::$datasets;
        }

        self::$datasets = \rainhawk::select_collection("datasets");
        return self::$datasets;
    }
}

?><file_sep><?php

require_once "includes/core.php";

?>
<!DOCTYPE html>
<html lang="en-GB">
    <head>
        <title>Project Rainhawk - Elgar's Coding Orchestra</title>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <?php require_once "includes/meta.php"; ?>
        <link rel="stylesheet" href="/css/style.css" type="text/css">
        <script>
            $(function() {
                var $graph = $(".header .graph");
                var width = $graph.width();
                var height = $graph.height();
                var data = [0, 0, 3, 6, 2, 7, 5, 2, 1, 3, 8, 9, 2, 5, 9, 3, 6, 3, 6, 2, 7, 5, 2, 1, 3, 8, 9, 2, 5, 9, 2, 7, 4, 0];

                var x = d3.scale.linear().domain([0, data.length]).range([0, width]);
                var y = d3.scale.linear().domain([0, 18]).range([height, 0]);

                var line = d3.svg.line()
                    .interpolate("basis")
                    .x(function(d, i) {
                        return x(i);
                    }).y(function(d) {
                        return y(d);
                    });

                var graph = d3.select(".graph").append("svg:svg")
                    .attr("width", width)
                    .attr("height", height)
                    .append("svg:g");

                var path = graph.append("svg:path")
                    .attr("d", line(data));

                var pathLength = path.node().getTotalLength();

                path.attr("stroke-dashoffset", pathLength)
                    .attr("stroke-dasharray", pathLength + " " + pathLength)
                    .transition()
                    .duration(2000)
                    .ease("linear")
                    .attr("stroke-dashoffset", 0);
            });
        </script>
    </head>

    <body>
        <?php require_once "includes/nav.php"; ?>

        <div id="top" class="header">
            <div class="vertical">
                <h1>Project<strong>Rainhawk</strong></h1>
                <h3>An elegant <em>Visualisation Framework</em> with <em>Cloud Collaboration</em>.</h3>
            </div>
            <div class="graph"></div>
        </div>

        <div id="about" class="intro">
            <div class="container">
                <div class="row">
                    <div class="col-md-6 col-md-offset-3 text-center">
                        <h2>Big data is all around us.</h2>
                        <p class="lead">Unfortunately, meaning and correlations can be lost when data is stored in different formats in different places.</p>
                    </div>
                </div>
            </div>
        </div>

        <div id="services" class="services">
            <div class="container">
                <div class="row">
                    <div class="col-md-12 text-center">
                        <h2>What's the solution?</h2>
                    </div>
                </div>
                <div class="row">
                    <div class="col-md-2 col-md-offset-2 text-center">
                        <div class="service-item">
                            <i class="service-icon fa fa-cloud"></i>
                            <h4>1. Retrieve</h4>
                            <p>You provide any number of files containing raw data to be uploaded.</p>
                        </div>
                    </div>
                    <div class="col-md-2 text-center">
                        <div class="service-item">
                            <i class="service-icon fa fa-code"></i>
                            <h4>2. Parse</h4>
                            <p>We parse the data using our custom parser and store it in the cloud.</p>
                        </div>
                    </div>
                    <div class="col-md-2 text-center">
                        <div class="service-item">
                            <i class="service-icon fa fa-hdd-o"></i>
                            <h4>3. Manage</h4>
                            <p>You use our management interface (or directly use the API) to manipulate your data.</p>
                        </div>
                    </div>
                    <div class="col-md-2 text-center">
                        <div class="service-item">
                            <i class="service-icon fa fa-tasks"></i>
                            <h4>4. Visualize</h4>
                            <p>Correlations from your data can be graphically understood through our interactive visualizations.</p>
                        </div>
                    </div>
                </div>
            </div>
        </div>

        <div id="get-started" class="get-started">
            <div class="container">
                <div class="row">
                    <div class="col-md-10 col-md-offset-1 text-center">
                        <h2>What are you waiting for? Let's get started.</h2>
                        <p class="lead">You have two options to get started &mdash; you can either use our web-based data management interface to upload data and create visualizations instantly or, if you're a developer, read our API documentation so that you can build awesome applications using our highly scalable cloud-based API.</p>
                        <p class="buttons">
                            <a href="/login.php?dest=/create.php" class="btn btn-lg btn-primary" role="button">Upload &amp; Visualize Online</a>
                            <em>or</em>
                            <a href="https://www.mashape.com/sneeza/project-rainhawk" class="btn btn-lg btn-primary btn-outline" role="button">API Documentation</a>
                        </p>
                        <p class="dev-note">
                            Note: If you're a developer then you may find <a href="https://github.com/zacoppotamus/ElgarsCodingOrchestra" target="_blank">the source code</a> for the entire project useful &mdash; we've created some wrapper libraries to make communicating with the API as simple as possible to get you started.
                        </p>
                    </div>
                </div>
            </div>
        </div>

        <hr>

        <div id="team" class="team">
            <div class="container">
                <div class="row">
                    <div class="col-md-4 col-md-offset-4 text-center">
                        <h2>Meet the dream team!</h2>
                    </div>
                </div>
                <div class="row">
                    <div class="col-md-4 text-center">
                        <div class="team-item fade">
                            <a href="http://izac.us/"><img class="img-responsive" src="img/team-zac.jpg"></a>
                            <h4><NAME></h4>
                        </div>
                    </div>
                    <div class="col-md-4 text-center">
                        <div class="team-item fade">
                            <a href="http://benelgar.com/"><img class="img-responsive" src="img/team-ben.jpg"></a>
                            <h4><NAME></h4>
                        </div>
                    </div>
                    <div class="col-md-4 text-center">
                        <div class="team-item fade">
                            <a><img class="img-responsive" src="img/team-oscar.jpg"></a>
                            <h4><NAME></h4>
                        </div>
                    </div>
                </div>
                <div class="row">
                    <div class="col-md-4 text-center">
                        <div class="team-item fade">
                            <a><img class="img-responsive" src="img/team-sam.jpg"></a>
                            <h4><NAME></h4>
                        </div>
                    </div>
                    <div class="col-md-4 text-center">
                        <div class="team-item fade">
                            <a href="http://sneeza.me/"><img class="img-responsive" src="img/team-luke.jpeg"></a>
                            <h4><NAME></h4>
                        </div>
                    </div>
                    <div class="col-md-4 text-center">
                        <div class="team-item fade">
                            <a><img class="img-responsive" src="img/team-steve.jpg"></a>
                            <h4><NAME>-Tozer</h4>
                        </div>
                    </div>
                </div>
            </div>
        </div>

        <hr>

        <footer>
            <div class="container">
                <div class="row">
                    <div class="col-md-6 col-md-offset-3 text-center">
                        <p>Made in <strong>Bristol</strong> with ♥ <em>&mdash;</em> <strong>Elgar's Coding Orchestra</strong> <em>&mdash;</em> <?php echo date("Y"); ?></p>
                    </div>
                </div>
            </div>
        </footer>

        <script src="/js/jquery-1.10.2.js"></script>
        <script src="/js/bootstrap.js"></script>
        <script src="/visualise/vendor/d3/d3.min.js"></script>
    </body>
</html>
<file_sep><?php

/*!
 * This class takes an input nice looking URI, strips it down
 * and tries to match it with the rules that it's been given. The
 * advantage of doing this is that we can specify nice looking URIs
 * in PHP while bypassing Apache or Nginx's rewrite limitations.
 */

class Route {
    const GET = "GET";
    const POST = "POST";
    const PUT = "PUT";
    const DELETE = "DELETE";

    // Store an array of our defined routes.
    private static $routes = array();

    /*!
     * Add a new route to the defined routes array, provided both
     * a request method and the regex for the path.
     */

    public static function get($path, $callback) {
        $pattern = "/^" . str_replace("/", "\/", $path) . "$/";

        self::$routes[self::GET][$pattern] = $callback;
    }

    /*!
     * Add a new route to the defined routes array, provided both
     * a request method and the regex for the path.
     */

    public static function post($path, $callback) {
        $pattern = "/^" . str_replace("/", "\/", $path) . "$/";

        self::$routes[self::POST][$pattern] = $callback;
    }

    /*!
     * Add a new route to the defined routes array, provided both
     * a request method and the regex for the path.
     */

    public static function put($path, $callback) {
        $pattern = "/^" . str_replace("/", "\/", $path) . "$/";

        self::$routes[self::PUT][$pattern] = $callback;
    }

    /*!
     * Add a new route to the defined routes array, provided both
     * a request method and the regex for the path.
     */

    public static function delete($path, $callback) {
        $pattern = "/^" . str_replace("/", "\/", $path) . "$/";

        self::$routes[self::DELETE][$pattern] = $callback;
    }

    /*!
     * Parse a request URI to see if we have any matches, and then
     * execute the related callback function with the provided params.
     */

    public static function parse() {
        $url = isset($_GET['uri']) ? trim($_GET['uri']) : null;
        $method = isset($_SERVER['REQUEST_METHOD']) ? strtoupper(trim($_SERVER['REQUEST_METHOD'])) : "GET";

        if(isset(self::$routes[$method])) {
            foreach(self::$routes[$method] as $pattern => $callback) {
                if(preg_match($pattern, $url, $params)) {
                    array_shift($params);

                    return call_user_func_array($callback, array_values($params));
                }
            }
        }

        return false;
    }
}

?><file_sep><?php

/*!
 * This class is a global singleton which is used in every script,
 * providing global variables and dependency injection for lots of
 * helper classes.
 */

class App {
    // Some global toggles used when the server is under load.
    public static $development = false;
    public static $maintenance = false;
    public static $debug = false;

    // App specific variables.
    public static $version = null;
    public static $domain = null;
    public static $username = null;

    // Server configuration variables.
    public static $stack = array();
    public static $root_path = null;
    public static $init_time = null;

    /*!
     * Given an array of field names, return which ones we think should
     * be indexed and which are non-essential.
     */

    public static function find_index_names($fields) {
        $keywords = array("id", "name", "key");
        $names = array();

        foreach($fields as $field_name) {
            $matched = false;

            foreach($keywords as $keyword) {
                if(stripos($field_name, $keyword) !== false) {
                    $matched = true;
                    break;
                }
            }

            if($matched) {
                $names[] = $field_name;
            }
        }

        return $names;
    }

    /*!
     * Generate the humanly readable runtime, in seconds.
     */

    public static function runtime() {
        return round(microtime(true) - self::$init_time, 3);
    }

    /*!
     * Log something, somewhere.
     */

    public static function log($section, $message) {
        return error_log("[$section] $message\n", 0);
    }
}

?><file_sep><?php

include("includes/kernel.php");
include("includes/api/core.php");

/*!
 * Define an empty array to store the results of whatever we need
 * to send back.
 */

$json = array(
    "datasets" => array()
);

// Perform a query to find the datasets.
$datasets = rainhawk\sets::sets_for_user(app::$username);

// Check if that query worked.
if(!$datasets) {
    echo json_beautify(json_render_error(401, "There was a problem while trying to find your datasets."));
    exit;
}

// Iterate through the results, if any.
foreach($datasets as $dataset) {
    $json['datasets'][] = array(
        "name" => $dataset['prefix'] . "." . $dataset['name'],
        "description" => $dataset['description'],
        "rows" => $dataset['rows'],
        "fields" => array_keys($dataset['fields']),
        "read_access" => $dataset['read_access'],
        "write_access" => $dataset['write_access']
    );
}

/*!
 * Output our JSON payload for use in whatever needs to be using
 * it.
 */

echo json_beautify(json_render(200, $json));
exit;

?><file_sep><?php

namespace Rainhawk;

/**
 * Rainhawk\Data
 *
 * Data class to be used in the Rainhawk framework. This class provides
 * a way to check a data type and enforce rules.
 *
 * @package Rainhawk\Data
 */

class Data {
    /**
     * The different supported data types.
     */

    const STRING = "string";
    const INTEGER = "integer";
    const FLOAT = "float";
    const TIMESTAMP = "timestamp";
    const LATITUDE = "latitude";
    const LONGITUDE = "longitude";
    const ARR = "array";

    /**
     * Create a new dataset in the system.datasets index, with the
     * relevant data so that we can start using other operations on
     * it.
     *
     * @param string $type  The data type to enforce.
     * @param string $value  The value to check.
     * @return mixed  The result if success, null if false.
     */

    public static function check($type, $value) {
        switch($type) {
            case self::STRING:
                return $value;
                break;

            case self::INTEGER:
                if(is_numeric($value) && is_int($value + 0)) {
                    return $value + 0;
                }
                break;

            case self::FLOAT:
                if(is_numeric($value)) {
                    return $value + 0;
                }
                break;

            case self::TIMESTAMP:
                if(strtotime($value) !== false) {
                    return strtotime($value);
                }
                break;

            case self::LATITUDE:
                if(preg_match("/^-?([1-8]?[1-9]|[1-9]0)\.{1}\d{1,6}$/", $value)) {
                    return (float)$value;
                }
                break;

            case self::LONGITUDE:
                if(preg_match("/^-?([1]?[1-7][1-9]|[1]?[1-8][0]|[1-9]?[0-9])\.{1}\d{1,6}$/", $value)) {
                    return (float)$value;
                }
                break;

            case self::ARR:
                if(is_array($value) || is_object($value)) {
                    return (array)$value;
                }
        }

        return null;
    }

    /**
     * Detect the data type given a simple string which can be used to
     * automatically work out what constraints to apply to a field or not.
     *
     * @param string $value
     * @return string
     */

    public static function detect($value) {
        if(is_array($value) || is_object($value)) {
            return self::ARR;
        }

        $value = trim($value);

        if(is_numeric($value)) {
            if(is_int($value + 0)) {
                return self::INTEGER;
            }

            return self::FLOAT;
        }

        if(strtotime($value) !== false) {
            return self::TIMESTAMP;
        }

        return self::STRING;
    }
}

?><file_sep><?php

namespace Rainhawk;

/**
 * Rainhawk\Dataset
 *
 * Dataset class to be used in the Rainhawk framework. This
 * class provides an interface for feeding data into and out
 * from a collection in MongoDB.
 *
 * @package Rainhawk\Dataset
 */

class Dataset {
    private $collection;
    public $prefix;
    public $name;
    public $description;
    public $rows = 0;
    public $fields = array();
    public $constraints = array();
    public $read_access = array();
    public $write_access = array();
    public $exists = false;

    /**
     * Create a new instance of Dataset, which initiates itself
     * using the provided ID and fetches relevant information from
     * MongoDB and our internal dataset structure.
     *
     * @param string $prefix  The prefix for the user.
     * @param string $name  The name of the dataset.
     * @return Dataset  Our new Dataset instance.
     */

    public function __construct($prefix, $name) {
        $this->prefix = $prefix;
        $this->name = $name;

        if(\rainhawk\sets::exists($prefix, $name)) {
            $set_data = \rainhawk\sets::fetch_metadata($prefix, $name);
            $this->collection = \rainhawk::select_collection($prefix . "." . $name);

            $this->name = $set_data['name'];
            $this->description = $set_data['description'];
            $this->rows = (int)$set_data['rows'];
            $this->fields = $set_data['fields'];
            $this->constraints = $set_data['constraints'];
            $this->read_access = $set_data['read_access'];
            $this->write_access = $set_data['write_access'];
            $this->exists = true;
        }
    }

    /**
     * Check if the supplied username has read access to the dataset
     * which will always return true if the user created it.
     *
     * @param string $username  The username to check.
     * @return bool  Whether or not the user can access it.
     */

    public function have_read_access($username) {
        return $this->exists && (in_array($username, $this->read_access) || in_array($username, $this->write_access) || in_array("global", $this->read_access));
    }

    /**
     * Check if the supplied username has write access to the dataset
     * which will always return true if the user created it.
     *
     * @param string $username  The username to check.
     * @return bool  Whether or not the user can access it.
     */

    public function have_write_access($username) {
        return $this->exists && in_array($username, $this->write_access);
    }

    /**
     * Perform a find query on the dataset, which returns a Mongo
     * cursor to the results. We can then use this to iterate through
     * the results.
     *
     * @param array $query  The query to run.
     * @param array $fields  The fields to return.
     * @return MongoCursor  The Mongo cursor.
     */

    public function find($query, $fields = array()) {
        if(!$this->exists) {
            return false;
        }

        try {
            return $this->collection->find($query, $fields);
        } catch(Exception $e) {}

        return false;
    }

    /**
     * Perform an insertion of a single row of data into the dataset,
     * which just passes the rows to the batch insertion method.
     *
     * @param array $row  The row of data to insert.
     * @return array  The row of data that was inserted with _ids.
     */

    public function insert($row) {
        $rows = $this-insert_multi(array($row));

        if(!$rows) {
            return false;
        }

        return $rows;
    }

    /**
     * Perform an insertion of multiple rows of data into the dataset
     * in batch, to make API calls easier.
     *
     * @param array $rows  The rows of data to insert.
     * @return array  The rows that were inserted with _ids.
     */

    public function insert_multi($rows) {
        if(!$this->exists) {
            return false;
        }

        try {
            $result = $this->collection->batchInsert($rows);

            if($result['ok'] == 1) {
                $this->rows += count($rows);

                foreach($rows as $row) {
                    $fields = array_keys($row);

                    foreach($fields as $field) {
                        $this->fields[$field] = isset($this->fields[$field]) ? $this->fields[$field] + 1 : 1;
                    }
                }

                \rainhawk\sets::update($this);

                return $rows;
            }
        } catch(Exception $e) {}

        return false;
    }

    /**
     * Perform an update query on the dataset, taking both the query
     * of things to match and the changes to make to those rows.
     *
     * @param array $query  The rows to match.
     * @param array $changes  The changes to make.
     * @return int  The number of rows that were changed.
     */

    public function update($query, $changes) {
        if(!$this->exists) {
            return false;
        }

        try {
            $result = $this->collection->find($query);

            if($result) {
                $ids = array();
                $fields = array();

                foreach($result as $row) {
                    $id = $row['_id'];
                    $ids[] = $id;
                    $fields[(string)$id] = array_keys($row);
                }

                $result = $this->collection->update($query, $changes, array("multiple" => true));

                if($result['ok'] == 1) {
                    $updated = (int)$result['n'];
                    $result = $this->collection->find(array("_id" => array('$in' => $ids)));

                    if($result) {
                        foreach($result as $row) {
                            $new_fields = array_keys($row);

                            foreach($fields as $field) {
                                $field = (string)$field;

                                if(!in_array($field, $new_fields)) {
                                    $this->fields[$field]--;

                                    if($this->fields[$field] <= 0) {
                                        unset($this->fields[$field]);
                                    }
                                }
                            }

                            foreach($new_fields as $field) {
                                $field = (string)$field;

                                if(!in_array($field, $fields)) {
                                    $this->fields[$field] = isset($this->fields[$field]) ? $this->fields[$field] + 1 : 1;
                                }
                            }
                        }

                        \rainhawk\sets::update($this);

                        return $updated;
                    }
                }
            }
        } catch(Exception $e) {}

        return false;
    }

    /**
     * Perform a delete query on the dataset, using a MongoDB query
     * to select the rows that we need to delete.
     *
     * @param array $query  The rows to match.
     * @return int  The number of rows that have been removed.
     */

    public function delete($query) {
        if(!$this->exists) {
            return false;
        }

        try {
            $result = $this->collection->find($query);

            if($result) {
                $this->rows -= $result->count();

                foreach($result as $row) {
                    $fields = array_keys($row);

                    foreach($fields as $field) {
                        $this->fields[$field]--;

                        if($this->fields[$field] <= 0) {
                            unset($this->fields[$field]);
                        }
                    }
                }

                $result = $this->collection->remove($query, array("justOne" => false));

                if($result['ok'] == 1) {
                    \rainhawk\sets::update($this);

                    return (int)$result['n'];
                }
            }
        } catch(Exception $e) {}

        return false;
    }

    /**
     * List the indexes on this collection, returning them in array
     * format.
     *
     * @return array  The indexes in an array.
     */

    public function fetch_indexes() {
        if(!$this->exists) {
            return false;
        }

        try {
            return $this->collection->getIndexInfo();
        } catch(Exception $e) {}

        return false;
    }

    /**
     * Create an index on a specified field, in a specified
     * direction. If it already exists then nothing will happen.
     *
     * @param string $field  The field to add an index to.
     * @return bool  Whether or not the indexes were created.
     */

    public function add_index($field) {
        if(!$this->exists) {
            return false;
        }

        try {
            $result = $this->collection->ensureIndex(array($field => 1), array("background" => true));

            if($result['ok'] == 1) {
                return true;
            }
        } catch(Exception $e) {}

        return false;
    }

    /**
     * Remove an index on the specified field.
     *
     * @param string $field  The field to remove the index on.
     * @return bool  Whether or not the indexes were removed.
     */

    public function remove_index($field) {
        if(!$this->exists) {
            return false;
        }

        try {
            return $this->collection->deleteIndex($field);
        } catch(Exception $e) {}

        return false;
    }

    /**
     * Run an aggregation query on the dataset, which is used to
     * calculate the max/min of the set.
     *
     * @param array $query  The query to run.
     * @return array  The results of the calculations.
     */

    public function aggregate($query) {
        if(!$this->exists) {
            return false;
        }

        try {
            $result = $this->collection->aggregate($query);

            if($result['ok'] == 1) {
                return $result['result'];
            }
        } catch(Exception $e) {}

        return false;
    }

    /**
     * Remove this dataset entirely, destroying the data and
     * removing the indexes from the index table.
     *
     * @return bool  Whether or not the operation succeeded.
     */

    public function remove() {
        if(!$this->exists) {
            return false;
        }

        try {
            $result = $this->collection->drop();

            if($result['ok'] == 1) {
                \rainhawk\sets::remove($this);

                return true;
            }
        } catch(Exception $e) {}

        return false;
    }
}

?>
<file_sep>#include <iostream>
#include <fstream>
#include <string>
#include <sstream>
#include <vector>
#include <cstring>
#include "readFile.h"
#include "unzip.h"

#define MAX_STRING_SIZE 256

const char* SHARED_STRINGS_PATH = "xl/sharedStrings.xml";

typedef struct
{
  unsigned x;
  unsigned y;
} coord;

/////////////////////////////////////////////////////////////////
/////////  sheetNode Class implementation
sheetNode::sheetNode()
{
  jType = NULLVALUE;
}

sheetNode::sheetNode( string newString )
{
  jType = STRING;
  strval = newString;
}

sheetNode::sheetNode( long double newNumber )
{
  jType = NUMBER;
  numval = newNumber;
}

sheetNode::sheetNode( bool newBool )
{
  jType = BOOL;
  boolval = newBool;
}

string sheetNode::getString()
{
  if( jType != STRING )
    return "";
  else
    return strval;
}

long double sheetNode::getNumber()
{
  if( jType != NUMBER )
    return 0;
  else
    return numval;
}

bool sheetNode::getBool()
{
  if( jType != BOOL )
    return false;
  else
    return boolval;
}

JType sheetNode::getType()
{
  return jType;
}
/////////  End of implementation
/////////////////////////////////////////////////////////////////

/////////////////////////////////////////////////////////////////
/////////  Utility functions
string simpleToUpper( string input )
{
  unsigned length = input.length();
  string upper = input;
  for( unsigned count = 0; count<length; count++ )
    if( upper[count] > 96 && upper[count] < 123 )
      upper[count] -= 32;
  return upper;
}

FileType getType( string fileName )
{
  unsigned marker = fileName.find_last_of('.');
  string fileStr = fileName.substr(marker+1);
  fileStr = simpleToUpper( fileStr );
  if( fileStr.compare( "CSV" ) == 0 )
    return CSV;
  else if( fileStr.compare( "XLSX" ) == 0 )
    return XLSX;
  else if( fileStr.compare( "ODS" ) == 0 )
    return ODS;
  else
    return UNDEF;
}

string pageString( page spreadsheet )
{
  string output = "";
  output += spreadsheet.name + '\n';
  for( 
    vector< vector<sheetNode> >::iterator outIt = spreadsheet.contents.begin();
    outIt != spreadsheet.contents.end();
    outIt++
  )
  {
    for( vector<sheetNode>::iterator inIt = outIt->begin();
      inIt != outIt->end();
      inIt++
    )
    {
      JType type = inIt->getType();
      switch( type ){
        case STRING:
          output += 'S';
          break;
        case NUMBER:
          output += 'N';
          break;
        case BOOL:
          output += 'B';
          break;
        default:
          output += '_';
          break;
      }
      output = output + '\t';
    }
    output = output + '\n';
  }
  return output;
} 

/////////  End of utility functions
/////////////////////////////////////////////////////////////////

// Section ends with cc = " and ifs.peek() != " regardless of the
// length/meaning of the quote.
void skipQuotes( long unsigned &pos, ifstream &ifs, unsigned &error )
{
  char cc;
  cc = ifs.get(); pos++;
  if( cc != '\"' )
  {
    while( ifs.good() && cc != '\"' )
    {
      cc = ifs.get(); pos++;
      if( cc == '\"' && ifs.peek() == '\"' )
      {
        ifs.get(); pos++; cc = ifs.get(); pos++;
      }
    }
    if( ifs.eof() )
    {
      error = 2;
      return;
    }
    if( ifs.fail() )
    {
      error = 1;
      return;
    }
  }
}

void writeCell( sheet &target, size_t row, size_t col, string record )
{
  if( record == "" ) return;
  long double number;
  if( convertData( record, number ) )
  {
    target[row][col].setValue( number );
    return;
  }
  bool boolean;
  if( convertBool( record, boolean ) )
  {
    target[row][col].setValue( boolean );
    return;
  }
  target[row][col].setValue( record );
}

void csvMarkers( vector<long unsigned> &commas, vector<long unsigned> &newls,
    ifstream &ifs, unsigned &error )
{
  long unsigned pos = 0;
  char cc = ifs.get();
  while( ifs.good() )
  {
    if( cc == '\"' ) skipQuotes( pos, ifs, error );
    if( error != 0 ) return;
    if( cc == ',' ) commas.push_back( pos );
    if( cc == '\n' ) newls.push_back( pos );
    cc = ifs.get();
    pos++;
  }
  if( ifs.bad() ) error = 1;
}

void csvDimensions( size_t &rows, size_t &cols,
    const vector<long unsigned> &commas, const vector<long unsigned> &newls )
{
  size_t max = newls.size();
  size_t commaPos = 0;
  size_t newlPos = 0;
  long unsigned nextComma = ( commas.size() > 0 ) ? commas[0] : -1;
  long unsigned nextNewl = ( newls.size() > 0 ) ? newls[0] : -1;
  size_t dimensionPos = 0;
  vector<size_t> dimensions(1,1);
  while( newlPos < max )
  {
    nextNewl = ( newls.size() > newlPos ) ? newls[newlPos] : -1;
    while( nextComma < nextNewl )
    {
      dimensions[dimensionPos]++;
      commaPos++;
      nextComma = ( commas.size() > commaPos ) ? commas[commaPos] : -1;
    }
    dimensions.push_back(1);
    dimensionPos++;
    newlPos++;
  }
  rows = dimensions.size();
  cols = 0;
  for( vector<size_t>::iterator it = dimensions.begin();
      it != dimensions.end(); it++ )
  {
    if( *it > cols ) cols = *it;
  }
}

void csvData( vector<long unsigned> &commas, vector<long unsigned> &newls,
    sheet &result, size_t rows, size_t cols, ifstream &ifs, unsigned &error )
{
  long unsigned pos = 0;
  size_t commaPos = 0;
  size_t newlPos = 0;
  long unsigned nextComma = ( commas.size() > 0 ) ? commas[0] : -1;
  long unsigned nextNewl = ( newls.size() > 0 ) ? newls[0] : -1;
  size_t row = 0;
  size_t col = 0;
  while( ifs.good() )
  {
    col = 0;
    while( nextComma < nextNewl )
    {
      string record;
      while( pos < nextComma )
      {
        char cc = ifs.get(); pos++;
        if( cc == '\"' )
        {
          cc = ifs.get(); pos++;
        }
        record += cc;
      }
      writeCell( result, row, col, record );
      ifs.get(); pos++;
      col++;
      commaPos++;
      nextComma = ( commas.size() > commaPos ) ? commas[commaPos] : -1;
    }
    string record;
    while( pos < nextNewl && !ifs.eof() )
    {
      char cc = ifs.get(); pos++;
      if( cc == '\"' )
      {
        cc = ifs.get(); pos++;
      }
      if( ifs.good() ) record += cc;
    }
    writeCell( result, row, col, record );
    ifs.get(); pos++;
    while( col+1 < cols )
    {
      col++;
      writeCell( result, row, col, "" );
    }
    row++;
    newlPos++;
    nextNewl = ( newls.size() > newlPos ) ? newls[newlPos] : -1;
  }
  if( ifs.bad() ) error = 1;
}

sheet readCSV( string filename, unsigned &error )
{
  error = 0;
  ifstream ifs( filename );
  sheet fail( 0, 0 );
  vector<long unsigned> commas;
  vector<long unsigned> newls;
  cout << "marka" << '\n';
  csvMarkers( commas, newls, ifs, error );
  if( error != 0 ) return fail;
  size_t rows;
  size_t cols;
  cout << "dim" << '\n';
  csvDimensions2( rows, cols, commas, newls );
  sheet result( rows, cols );
  ifs.close();
  ifs.open( filename );
  cout << "dat" << '\n';
  csvData2( commas, newls, result, rows, cols, ifs, error );
  cout << "dun" << '\n';
  if( error != 0 ) return fail;
  return result;
}

//Returns -1 if string is invalid
coord decodeDimension( string dimension )
{
  coord failure;
  failure.x = -1;
  failure.y = -1;
  int width = 0;
  int height = 0;
  size_t lcount = 0;
  while( dimension[lcount] >= 'A' && dimension[lcount] <= 'Z' )
  {
    if( lcount == dimension.size() )
    {
      return failure;
    }
    lcount++;
  }
  string letters = dimension.substr( 0, lcount );
  int value;
  for( size_t count = 0; count < lcount; count++ )
  {
    width *= 26;
    value = letters[count] + 1 - 'A';
    if( value < 1 || value > 26 )
    {
      return failure;
    }
    width += value;
  }
  string numbers = dimension.substr( lcount );
  stringstream converter( numbers );
  if( !( converter >> height ) )
  {
    return failure;
  }
  coord result;
  result.x = width;
  result.y = height;
  return result;
}

int getCommonStrings( vector<string> &commonStrings,
  const string &fileContents )
{
  string startTag("<t>");
  string endTag("</t>");
  size_t currentPos = fileContents.find( startTag );
  size_t nextPos;
  size_t len;
  while( currentPos != string::npos )
  {
    currentPos += 3;
    nextPos = fileContents.find( endTag, currentPos );
    if( nextPos == string::npos )
    {
      return -1;
    }
    len = nextPos - currentPos;
    string newString( fileContents.substr( currentPos, len ) );
    commonStrings.push_back( newString );
    currentPos = fileContents.find( startTag, currentPos );
  }
  return 1;
}

int loadCommonStrings( vector<string> &commonStrings,
  unzFile xlsxFile )
{
  if( unzOpenCurrentFile( xlsxFile ) == UNZ_OK )
  {
    unz_file_info xmlFileInfo; 
    memset( &xmlFileInfo, 0, sizeof( unz_file_info ) );
    if( unzGetCurrentFileInfo( xlsxFile,
      &xmlFileInfo,
      NULL, 0,
      NULL, 0,
      NULL, 0 ) == UNZ_OK )
    {
      string xmlFileName;
      xmlFileName.resize( xmlFileInfo.size_filename );
      unzGetCurrentFileInfo( xlsxFile,
        &xmlFileInfo,
        &xmlFileName[0], xmlFileInfo.size_filename,
        NULL, 0,
        NULL, 0 );
      if( xmlFileName.compare( SHARED_STRINGS_PATH ) == 0 )
      {
        string xmlFileContents;
        uLong xmlFileSize = xmlFileInfo.uncompressed_size;
        unsigned long long maxBufferSize = 1;
        if( sizeof( unsigned long long ) == sizeof( unsigned ) )
        {
          maxBufferSize = -1;
        }
        else
        {
          maxBufferSize <<= sizeof( unsigned ) * 8;
          maxBufferSize--;
        }
        if( xmlFileSize <= maxBufferSize )
        {
          xmlFileContents.resize( xmlFileSize );
          voidp xmlFileBuffer = &xmlFileContents[0];
          if( unzReadCurrentFile( xlsxFile, xmlFileBuffer, xmlFileSize ) )
          {
            if( getCommonStrings( commonStrings, xmlFileContents ) == 1 )
            {
              return 1;
            }
          }
        }
        return -1;
      }
    }
  }
  return 0;
}

int sanitizeXMLStrings( vector<string> &strings )
{
  size_t currentPos;
  size_t endPos;
  size_t len;
  for( vector<string>::iterator it = strings.begin();
    it != strings.end();
    it++ )
  {
    currentPos = it->find_first_of( '&' );
    while( currentPos != string::npos )
    {
      endPos = it->find_first_of( ';', currentPos );
      if( endPos == string::npos )
      {
        return 0;
      }
      len = endPos - currentPos + 1;
      string token = it->substr( currentPos, len );
      if( len == 6 )
      {
        if( token.compare( "&quot;" ) == 0 )
        {
          it->replace( currentPos, 6, """" );
        }
        else if( token.compare( "&apos;" ) == 0 )
        {
          it->replace( currentPos, 6, "'" );
        }
        else
        {
          return 0;
        }
      }
      else if( len == 5 )
      {
        if( token.compare( "&amp;" ) == 0 )
        {
          it->replace( currentPos, 5, "&" );
        }
        else
        {
          return 0;
        }
      }
      else if( len == 4 )
      {
        if( token.compare( "&lt;" ) == 0 )
        {
          it->replace( currentPos, 4, "<" );
        }
        else if( token.compare( "&gt;" ) == 0 )
        {
          it->replace( currentPos, 4, ">" );
        }
        else
        {
          return 0;
        }
      }
      else
      {
        return 0;
      }
      currentPos = it->find_first_of( '&', currentPos );
    }
  }
  return 1;
}

int fileIn( const char * filepath, unzFile zipFile )
{
  if( unzOpenCurrentFile( zipFile ) == UNZ_OK )
  {
    unz_file_info zipFileInfo;
    memset( &zipFileInfo, 0, sizeof( unz_file_info ) );
    if( unzGetCurrentFileInfo( zipFile,
      &zipFileInfo,
      NULL, 0,
      NULL, 0,
      NULL, 0 ) == UNZ_OK )
    {
      string zipFileName;
      zipFileName.resize( zipFileInfo.size_filename );
      unzGetCurrentFileInfo( zipFile,
        &zipFileInfo,
        &zipFileName[0], zipFileInfo.size_filename,
        NULL, 0,
        NULL, 0 );
      unzCloseCurrentFile( zipFile );
      int comparison = 
        zipFileName.substr( 0, strlen( filepath ) ).compare( filepath );
      if( comparison == 0 )
      {
        return 1;
      }
      else
      {
        return 0;
      }
    }
  }
  return -1;
}

int fileIn( const char * filepath, unzFile zipFile, string &title )
{
  if( unzOpenCurrentFile( zipFile ) == UNZ_OK )
  {
    unz_file_info zipFileInfo;
    memset( &zipFileInfo, 0, sizeof( unz_file_info ) );
    if( unzGetCurrentFileInfo( zipFile,
      &zipFileInfo,
      NULL, 0,
      NULL, 0,
      NULL, 0 ) == UNZ_OK )
    {
      string zipFileName;
      zipFileName.resize( zipFileInfo.size_filename );
      unzGetCurrentFileInfo( zipFile,
        &zipFileInfo,
        &zipFileName[0], zipFileInfo.size_filename,
        NULL, 0,
        NULL, 0 );
      unzCloseCurrentFile( zipFile );
      int comparison = 
        zipFileName.substr( 0, strlen( filepath ) ).compare( filepath );
      if( comparison == 0 )
      {
        title = zipFileName;
        title = title.substr( title.find_last_of( '/' ) + 1 );
        title.erase( title.find_last_of( '.' ) );
        return 1;
      }
      else
      {
        return 0;
      }
    }
  }
  return -1;
}

int getSheetContents( vector< vector<sheetNode> > &spreadsheet,
  const vector<string> &commonStrings, const string &fileContents )
{
  size_t currentPosition;
  size_t endPosition;
  currentPosition = fileContents.find( "<d" );
  currentPosition += 16;
  endPosition = fileContents.find_first_of( ':', currentPosition );
  string topleft = fileContents.substr(
    currentPosition, endPosition - currentPosition );
  currentPosition = endPosition + 1;
  endPosition = fileContents.find_first_of( '"', currentPosition );
  string bottomright = fileContents.substr(
    currentPosition, endPosition - currentPosition );
  coord lower = decodeDimension( bottomright );
  coord upper = decodeDimension( topleft );
  coord size;
  size.x = lower.x + 1 - upper.x;
  size.y = lower.y + 1 - upper.y;
  if( (int)lower.x == -1 || (int)upper.x == -1 || (int)lower.y == -1 ||
    (int)upper.y == -1 || (int)size.x == 0 || (int)size.y == 0 )
  {
    return 0;
  }
  sheetNode blankcell;
  vector<sheetNode> blankvector( size.x, blankcell );
  spreadsheet.assign( size.y, blankvector );
  currentPosition = fileContents.find( "<c ", endPosition );
  string newDimension;
  coord newPosition;
  size_t nextType;
  size_t nextCell;
  while( currentPosition != string::npos )
  {
    currentPosition += 6;
    endPosition = fileContents.find_first_of( '"', currentPosition );
    newDimension = fileContents.substr(
      currentPosition, endPosition - currentPosition );
    newPosition = decodeDimension( newDimension );
    newPosition.x -= upper.x;
    newPosition.y -= upper.y;
    nextType = fileContents.find( " t=", endPosition );
    nextCell = fileContents.find_first_of( '/', endPosition );
    sheetNode * newCell;
    if( nextType > nextCell )
    {
      newCell = new sheetNode();
    }
    else
    {
      currentPosition = nextType + 4;
      char cellType = fileContents[currentPosition];
      currentPosition = fileContents.find( "<v", currentPosition );
      currentPosition += 3;
      endPosition = fileContents.find_first_of( '<', currentPosition );
      string value = fileContents.substr(
        currentPosition, endPosition - currentPosition );
      stringstream converter( value );
      long double newValue;
      if( !( converter >> newValue ) )
      {
        newValue = 0;
      }
      switch( cellType )
      {
        case 's':
          newCell = new sheetNode( commonStrings[(int)newValue] );
          break;
        case 'n':
          newCell = new sheetNode( newValue );
          break;
        case 'b':
          if( newValue == 0 )
          {
            newCell = new sheetNode( false );
          }
          else
          {
            newCell = new sheetNode( true );
          }
          break;
        default:
          newCell = new sheetNode();
          break;
      }
    }
    spreadsheet[newPosition.y][newPosition.x] = *newCell;
    currentPosition = fileContents.find( "<c ", endPosition );
  }
  return 1;
}

int readXMLSheet( vector< page > &sheetList, string title,
  const vector<string> &commonStrings, unzFile xlsxFile )
{
  if( unzOpenCurrentFile( xlsxFile ) == UNZ_OK )
  {
    unz_file_info xmlFileInfo;
    memset( &xmlFileInfo, 0, sizeof( unz_file_info ) );
    if( unzGetCurrentFileInfo( xlsxFile,
      &xmlFileInfo,
      NULL, 0,
      NULL, 0,
      NULL, 0 ) == UNZ_OK )
    {
      string xmlFileContents;
      uLong xmlFileSize = xmlFileInfo.uncompressed_size;
      unsigned long long maxBufferSize = 1;
      if( sizeof( unsigned long long ) == sizeof( unsigned ) )
      {
        maxBufferSize = -1;
      }
      else
      {
        maxBufferSize <<= sizeof( unsigned ) * 8;
        maxBufferSize--;
      }
      if( xmlFileSize <= maxBufferSize )
      {
        xmlFileContents.resize( xmlFileSize );
        voidp xmlFileBuffer = &xmlFileContents[0];
        vector< vector<sheetNode> > spreadsheet;
        int fileReadStatus =
          unzReadCurrentFile( xlsxFile, xmlFileBuffer, xmlFileSize );
        if( fileReadStatus > 0 )
        {
          if( getSheetContents( spreadsheet,
            commonStrings, xmlFileContents ) == 1 )
          {
            page newsheet;
            newsheet.name = title;
            newsheet.contents = spreadsheet;
            sheetList.push_back( newsheet );
            return 1;
          }
        }
      }
    }
  }
  return -1;
}

//Necessarily takes an already opened unzFile and leaves it open
int getCommonStrings( vector<string> &commonStrings, unzFile xlsxFile )
{
  int commonStringsFound = 0;
  int fileAccessStatus = unzGoToFirstFile( xlsxFile );
  while( fileAccessStatus == UNZ_OK && commonStringsFound == 0 )
  {
    commonStringsFound = loadCommonStrings( commonStrings, xlsxFile );
    fileAccessStatus = unzGoToNextFile( xlsxFile );
  }
  if( commonStringsFound != 1 )
  {
    return commonStringsFound;
  }
  if( !sanitizeXMLStrings( commonStrings ) )
  {
    return -1;
  }
  return 1;
}

int getXMLSheets( string filename, vector< page > &sheetList,
  vector<string> commonStrings, unzFile xlsxFile )
{
  int fileAccessStatus = unzGoToFirstFile( xlsxFile );
  while( fileAccessStatus == UNZ_OK )
  {
    string title;
    if( fileIn( "xl/worksheets/", xlsxFile, title ) )
    {
      string pageName = filename;
      pageName.resize( pageName.find_last_of('.') );
      pageName = pageName + "_" + title;
      readXMLSheet( sheetList, pageName, commonStrings, xlsxFile );
    }
    fileAccessStatus = unzGoToNextFile( xlsxFile );
  }
  return 1;
}

vector< page > readXLSX( string filename )
{
  vector< page > falseResult;
  unzFile xlsxFile = unzOpen( filename.c_str() );
  if( xlsxFile )
  {
    vector<string> commonStrings;
    int commonStringsFound = getCommonStrings( commonStrings, xlsxFile );
    if( commonStringsFound == 0 )
    {
      return falseResult;
    }
    else if( commonStringsFound == -1 )
    {
      return falseResult;
    }
    vector< page > sheetList;
    int dataFound = getXMLSheets(filename, sheetList, commonStrings, xlsxFile);
    if( dataFound != 1 )
    {
      return falseResult;
    }
    return sheetList;
  }
  return falseResult;
}

vector< page > getFile( string fileName )
{
  unsigned error;
  FileType filetype = getType( fileName );
  if( filetype == CSV )
  {
    vector< page > csvSheet( 1, readCSV( fileName, error ) ); 
    return csvSheet;
  }
  else if( filetype == XLSX )
  {
    return readXLSX( fileName );
  }
  vector< page > failure;
  return failure;
}

<file_sep><?php

/*!
 * This class acts as a wrapper between our application and
 * the XCache var cache, allowing us to store variables in
 * RAM.
 */

class XCache {
    // Define some public variables for whether we should use
    // this class or not.
    public static $enabled = false;

    // Store some stats about the class.
    public static $access_time = 0;
    public static $hits = 0;
    public static $misses = 0;

    /*!
     * Initiate the cache so that we can use it elsewhere in
     * our application, otherwise every other call is bypassed by
     * a flag.
     */

    public static function init() {
        self::$enabled = true;
    }

    /*!
     * Fetch a keyvalue from the variable cache, so that we can
     * mess around with some data!
     */

    public static function fetch($key) {
        if(!self::$enabled) {
            return null;
        }

        $start = microtime(true);
        $data = xcache_get($key);
        $data = ($data) ? unserialize($data) : null;

        if(!is_null($data)) {
            self::$hits++;
        } else {
            self::$misses++;
        }

        $finish = microtime(true);

        self::$access_time += $finish - $start;

        return $data;
    }

    /*!
     * Store some data related to a key for a specified (or unspecified
     * amount of time).
     */

    public static function store($key, $data, $ttl = 0) {
        if(!self::$enabled) {
            return false;
        }

        $data = serialize($data);

        return xcache_set($key, $data, $ttl);
    }

    /*!
     * Remove a key from the local variable cache, which is generally
     * used when we have data we want to update.
     */

    public static function remove($key) {
        if(!self::$enabled) {
            return false;
        }

        return xcache_unset($key);
    }
}

?><file_sep><?php

require_once "../includes/core.php";
require_once "../includes/check_login.php";

header("content-type: application/json; charset=utf8");

$dataset = isset($_GET['dataset']) ? $_GET['dataset'] : null;
$document = array();

foreach($_POST as $name => $value) {
    if($name == "dataset") continue;

    $document[$name] = $value;
}

$result = $rainhawk->insertData($dataset, $document);

if(!$result) {
    echo json_encode(array(
        "Result" => "ERROR",
        "Message" => $rainhawk->error()
    ));
    exit;
}

echo json_encode(array(
    "Result" => "OK",
    "Record" => $result
));
exit;

?>
<file_sep>eco.charts.d3map = function() {

	return {
		title: 'Map',

		render: function(data, latitude, longitude, target) {

			console.log(target);
			var map = new google.maps.Map(target.node(),
			{
				zoom: 2,
				center: new google.maps.LatLng(0, 0),
				mapTypeId: google.maps.MapTypeId.TERRAIN
			});

			// Create overlay
			var overlay = new google.maps.OverlayView();

			// Add the container when the overlay is added
			// to the map
			overlay.onAdd = function()
			{
				var layer = d3.select(this.getPanes().overlayMouseTarget).append("div")
					.attr("class", "map-dot");

				// Draw each marker as a separate SVG element
				overlay.draw = function()
				{
					var projection = this.getProjection(),
						padding = 10;

					var marker = layer.selectAll("svg")
							.data(data)
							.each(transform) //update existing markers
						.enter().append("svg:svg")
							.each(transform)
							.attr("class", "marker");

					// Add a circle
					marker.append("svg:circle")
						.attr("r", 4.5)
						.attr("cx", padding)
						.attr("cy", padding);

					// Add a label
					// marker.data(json.data.results)
					// .on('mouseover', function(d)
					// {
					// 	d3.select(this).append("svg:text")
					// 	.attr("class", "tooltip")
					// 	.attr("x", padding+7)
					// 	.attr("y", padding)
					// 	.attr("dy", ".31em")
					// 	.text(function(d) { return d.name });
					// })
					// .on('mouseout', function(d)
					// {
					// 	d3.selectAll("text").remove();
					// });
	
					function transform(d)
					{
						d = new google.maps.LatLng(+d[latitude], +d[longitude])
						d = projection.fromLatLngToDivPixel(d);
						return d3.select(this)
							.style("left", (d.x - padding) + "px")
							.style("top", (d.y - padding) + "px");
					}

				};
			};

			// Bind overlay to map
			overlay.setMap(map);

			return this;
		}
	}
}
<file_sep><?php

require_once "includes/core.php";
require_once "includes/check_login.php";

$dataset = isset($_GET['dataset']) ? htmlspecialchars($_GET['dataset']) : null;
$datasetInfo = $rainhawk->fetchDataset($dataset);
$fields = $datasetInfo['fields'];
$colWidth = 100 / count($fields);

?>
<!DOCTYPE html>
<html lang="en-GB">
    <head>
        <title>Project Rainhawk - Edit Data</title>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <?php require_once "includes/meta.php"; ?>
        <link rel="stylesheet" href="/js/jtable.2.3.1/themes/metro/blue/jtable.min.css" type="text/css">
        <script src="/js/jtable.2.3.1/jquery.jtable.js"></script>
        <style type="text/css">
            #dataTable {
                margin-left: 15px;
                margin-right: 15px;
                border-radius: 6px;
                overflow: hidden;
            }
        </style>
        <script>
            $(function() {
                $("#dataTable").jtable({
                    title: "Data",
                    paging: true,
                    pageSize: 50,
                    sorting: true,
                    defaultSorting: "name ASC",
                    actions: {
                        listAction: '/proxy/list_data.php?dataset=<?php echo $dataset; ?>',
                        <?php if(in_array($user, $datasetInfo['write_access']) && $datasetInfo['rows'] > 0) { ?>
                            createAction: '/proxy/insert_data.php?dataset=<?php echo $dataset; ?>',
                            updateAction: '/proxy/update_data.php?dataset=<?php echo $dataset; ?>',
                            deleteAction: '/proxy/delete_data.php?dataset=<?php echo $dataset; ?>'
                        <?php } ?>
                    },
                    fields: {
                        <?php foreach($fields as $field) { ?>
                            <?php if($field == "_id") continue; ?>
                            '<?php echo $field; ?>': {
                                title: '<?php echo $field; ?>',
                                width: '<?php echo $colWidth; ?>%'
                            },
                        <?php } ?>
                        _id: {
                            key: true,
                            create: false,
                            edit: false,
                            list: false
                        }
                    }
                }).jtable("load");
            });
        </script>
    </head>

    <body>
        <?php require_once "includes/nav.php"; ?>
        <div class="container">
            <div class="row">
                <div class="col-md-12">
                    <div class="panel panel-default">
                        <div class="panel-body">
                            <h1><?php echo $datasetInfo['name'];?></h1>
                            <p><?php echo $datasetInfo['description'];?></p>
                        </div>
                    </div>
                </div>
            </div>
            <div class="row">
                <?php if($datasetInfo['rows'] > 0) { ?>
                    <div id="dataTable"></div>
                <?php } else { ?>
                    <div class="alert alert-info">
                        <strong>No data!</strong> There's no data here. Why don't you try <a class="alert-link" href="/upload.php?dataset=<?php echo $datasetInfo['name']; ?>">uploading</a> some?
                    </div>
                <?php } ?>
            </div>
        </div>
    </body>
</html><file_sep><?php

include("includes/kernel.php");
include("includes/api/core.php");

/*!
 * Check that the parameters have all been set and sent to the script,
 * including the prefix and the name.
 */

if(empty($data->prefix) || empty($data->name)) {
    echo json_beautify(json_render_error(401, "You didn't pass one or more of the required parameters."));
    exit;
}

/*!
 * Check to see if the dataset exists, and that we have access to it.
 * We need to use the prefix and the name of the dataset to get a
 * reference to it.
 */

// Create a new dataset object.
$dataset = new rainhawk\dataset($data->prefix, $data->name);

// Check that the dataset exists.
if(!$dataset->exists) {
    echo json_beautify(json_render_error(402, "The dataset you specified does not exist."));
    exit;
}

// Check that we can read from the dataset.
if(!$dataset->have_read_access(app::$username)) {
    echo json_beautify(json_render_error(403, "You don't have access to read from this dataset."));
    exit;
}

/*!
 * Define an empty array to store the results of whatever we need
 * to send back.
 */

$json = array(
    "min" => null,
    "max" => null,
    "average" => null,
    "sum" => null,
);

/*!
 * Run some commands to calculate some aggregations for the dataset
 * depending on the query provided.
 */

// Check if we have a field_name or not.
if(empty($data->field)) {
    echo json_beautify(json_render_error(404, "You didn't specify a field name to use in the calculations."));
    exit;
}

// Set some local variables.
$query = array();

// Check if we need to run a pre-query to match certain documents.
if(!empty($data->query)) {
    $query[] = array(
        '$match' => $data->query
    );
}

// Create the query that we need to run for the calculations.
$query[] = array(
    '$group' => array(
        "_id" => null,
        "min" => array(
            '$min' => '$' . $data->field
        ),
        "max" => array(
            '$max' => '$' . $data->field
        ),
        "average" => array(
            '$avg' => '$' . $data->field
        ),
        "sum" => array(
            '$sum' => '$' . $data->field
        )
    )
);

// Run the query.
$result = $dataset->aggregate($query);

// Check if it worked.
if(!$result) {
    echo json_beautify(json_render_error(405, "An unexpected error occured while performing your query - are you sure you formatted it correctly?"));
    exit;
}

// Set the JSON output.
$stats = $result[0];

$json['min'] = $stats['min'];
$json['max'] = $stats['max'];
$json['average'] = $stats['average'];
$json['sum'] = $stats['sum'];

/*!
 * Output our JSON payload for use in whatever needs to be using
 * it.
 */

echo json_beautify(json_render(200, $json));
exit;

?><file_sep><?php

include("includes/kernel.php");
include("includes/api/core.php");

/*!
 * Check if the user provided enough information to create the new
 * dataset, including the name and description.
 */

if(empty($data->prefix) || empty($data->name)) {
    echo json_beautify(json_render_error(401, "You didn't pass one or more of the required parameters."));
    exit;
}

/*!
 * Check to see if the dataset exists, and that we have access to it.
 * We need to use the prefix and the name of the dataset to get a
 * reference to it.
 */

// Create a new dataset object.
$dataset = new rainhawk\dataset($data->prefix, $data->name);

// Check that the dataset exists.
if(!$dataset->exists) {
    echo json_beautify(json_render_error(402, "The dataset you specified does not exist."));
    exit;
}

// Check that we can read from the dataset.
if(!$dataset->have_write_access(app::$username)) {
    echo json_beautify(json_render_error(403, "You don't have access to write to this dataset."));
    exit;
}

/*!
 * Define an empty array to store the results of whatever we need
 * to send back.
 */

$json = array();

/*!
 * Try and add the constraint to the field that was specified, but if none
 * were specified then try and automatically detect which ones to add.
 */

// Check if the field is set.
if(!empty($data->field)) {
    // Check if the field can be constrained.
    if($data->field == "_id") {
        echo json_beautify(json_render_error(405, "You can't apply a constraint to the _id field."));
        exit;
    }

    // Check if the field already has a constraint.
    if(isset($dataset->constraints[$data->field])) {
        echo json_beautify(json_render_error(406, "This field already has a constraint, please remove the current constraint before adding a new one."));
        exit;
    }

    // Check if the datatype has been set.
    if(empty($data->type) || !in_array($data->type, array("string", "integer", "float", "timestamp", "latitude", "longitude"))) {
        echo json_beautify(json_render_error(407, "The data type that you've selected is not supported. We currently support: string, integer, float, timestamp, latitude, longitude."));
        exit;
    }

    // Add the constraint to the field.
    $dataset->constraints[$data->field] = array(
        "type" => $data->type
    );

    // Store the dataset information in the index table.
    \rainhawk\sets::update($dataset);

    // Return the added attribute to the JSON.
    $json['added'] = true;
} else {
    // Get all the records to find all the fields.
    $rows = $dataset->find(array());
    $fields = array();

    // Iterate through the rows.
    foreach($rows as $row) {
        foreach($row as $field => $value) {
            if($field == "_id") continue;

            // Check if the field has already been detected or not.
            if(!isset($fields[$field])) {
                $fields[$field] = \rainhawk\data::detect($value);
            } else {
                $type = \rainhawk\data::detect($value);

                // Check if the field type is in-line.
                if($fields[$field] == $type) {
                    continue;
                } else if($fields[$field] == \rainhawk\data::INTEGER && $type == \rainhawk\data::FLOAT) {
                    $fields[$field] == \rainhawk\data::FLOAT;
                } else if($fields[$field] == \rainhawk\data::FLOAT && $type == \rainhawk\data::INTEGER) {
                    continue;
                } else {
                    $fields[$field] = "unknown";
                }
            }
        }
    }

    // Strip the already-constrained fields.
    if(!empty($dataset->constraints)) {
        foreach($dataset->constraints as $field => $constraint) unset($fields[$field]);
    }

    // Check if there are any constraints to add.
    if(empty($fields)) {
        echo json_beautify(json_render_error(408, "No unconstrained fields were found during automatic detection."));
        exit;
    }

    // Set the JSON array.
    $json['detected'] = array();

    // Add the new constraints.
    foreach($fields as $field => $type) {
        if($type == "unknown") {
            // Set the JSON output.
            $json['detected'][$field] = array(
                "type" => "unknown"
            );
        } else {
            // Set the constraint on the field.
            $dataset->constraints[$field] = array(
                "type" => $type
            );

            // Store the dataset information in the index table.
            \rainhawk\sets::update($dataset);

            // Set the JSON output.
            $json['detected'][$field] = array(
                "type" => $type
            );
        }
    }
}

/*!
 * Output our JSON payload for use in whatever needs to be using
 * it.
 */

echo json_beautify(json_render(200, $json));
exit;

?><file_sep><?php

require_once "includes/core.php";
require_once "includes/check_login.php";

?>
<!DOCTYPE html>
<html lang="en-GB">
    <head>
        <title>Project Rainhawk - Create Dataset</title>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <?php require_once "includes/meta.php"; ?>
        <style type="text/css">
            .row .form-controls {
                margin-top: 40px;
            }
        </style>
        <script>
            rainhawk.apiKey = "<?php echo $mashape_key; ?>";

            $(function() {
                $("form").submit(function(e) {
                    var $submit = $(this).find("button[type=submit]");
                    var dataset = $("#datasetName").val();
                    var description = $("#datasetDescription").val();

                    $submit.attr("disabled", true);

                    rainhawk.datasets.create(dataset, description, function(data) {
                        $(".container:last").prepend($('<div class="alert alert-success fade in"></div>')
                            .append($('<span><strong>Success!</strong> Dataset <a class="alert-link" href="/edit.php?dataset=' + data.name + '">' + data.name + '</a> successfully created.&nbsp;</span>'))
                            .append($('<span>Now try <a class="alert-link" href="/upload.php?dataset=' + data.name + '">uploading</a> some data.</span>'))
                            .append($('<button type="button" class="close pull-right" data-dismiss="alert" aria-hidden="true">&times;</button>'))
                        );
                    }, function(message) {
                        $(".container:last").prepend($('<div class="alert alert-danger fade in"></div>')
                            .append($('<strong>Error!</strong>&nbsp;'))
                            .append(message)
                            .append($('<button type="button" class="close pull-right" data-dismiss="alert" aria-hidden="true">&times;</button>'))
                        );

                        $submit.removeAttr("disabled");
                    });

                    return false;
                });
            });
        </script>
    </head>

    <body>
        <?php require_once "includes/nav.php"; ?>
        <div class="container">
            <div class="row">
                <div class="col-md-12">
                    <div class="panel panel-default">
                        <div class="panel-body">
                            <h1>Create a new dataset...</h1>
                            <p>Get started here by entering a unique name for your dataset along with a description for what this dataset will hold.</p>
                        </div>
                    </div>
                </div>
            </div>
            <div class="row">
                <div class="col-md-12">
                    <div class="panel panel-default">
                        <div class="panel-body">
                            <form role="form">
                                <div class="form-group">
                                    <label for="datasetName">Dataset Name:</label>
                                    <div class="input-group">
                                        <span class="input-group-addon"><?php echo $user; ?>.</span>
                                        <input type="text" class="form-control" id="datasetName" name="datasetName" placeholder="Enter the name of the dataset..." required autofocus>
                                    </div>
                                </div>
                                <div class="form-group">
                                    <label for="datasetDescription">Dataset Description:</label>
                                    <input type="text" class="form-control" id="datasetDescription" name="datasetDescription" placeholder="Enter a description for this dataset..." required>
                                </div>
                                <div class="form-controls">
                                    <button type="submit" class="btn btn-default">Submit</button>
                                    <a href="/datasets.php" type="button" class="btn btn-danger">Back</a>
                                </div>
                            </form>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </body>
</html>
<file_sep>#include <iostream>
#include <string>
#include <vector>
#include <fstream>
#include <sstream>
#include <typeinfo>
#include <functional>
#include "sadUtils.hpp"
#include "sadReader.hpp"

const char TEST_DELIM = '#';
const char* TESTING_PATH = "tests";

using namespace std;

////////////////////////////////////////////////////////////////////////////////
////////  TEXT FUNCTIONS
////////////////////////////////////////////////////////////////////////////////

bool convertBool( std::string &data, bool &v )
{
  std::string input = toUpper( data );
  if( input == "TRUE" )
  {
    v = true;
    return true;
  }
  if( input == "FALSE" )
  {
    v = false;
    return true;
  }
  return false;
}

bool whitespace( char c )
{
  if( c == ' '  || c == '\n' || c == '\r' || c == '\t' ) return true;
  return false;
}

string toUpper( string input )
{
  string output = input;
  for( string::iterator it = output.begin(); it != output.end(); ++it )
  {
    if( (*it >= 'a') && (*it <= 'z') )
    {
      *it += 'A' - 'a';
    }
  }
  return output;
}

string tab( unsigned size, char tabc, unsigned tabw )
{
  string output;
  for( unsigned it = 0; it < size*tabw; it++ ) output += tabc;
  return output;
}

////////////////////////////////////////////////////////////////////////////////
////////  TESTING FUNCTIONS
////////////////////////////////////////////////////////////////////////////////

// All test files should be named after their function (i.e. the test for the
// function "increment" should also be "increment") and must consist of the 
// following format:
// #
// <Test Data A1>#<Test Data A2>#...#<Test Data An>
// <Test Data B1>#<Test Data B2>#...#<Test Data Bn>
// ...
// #
// <Test Result A>
// <Test Result B>
// ...
// #
//
// Where '#' = TEST_DELIM, and must not contain any other instances symbols.
//
// For loading test data, the "error" argument is set to the result of the
// attempt:
// 0: Success
// 1: File I/O error
// 2: Invalid test file
// 3: EOF reached early

template <class V>
struct LoadTest
{
  vector<V> operator() ( string fname, unsigned index, unsigned &error,
      unsigned &pof )
  {
    pof = 0;
    char line[MAX_READ_SIZE];
    vector<V> output;
    string filepath = TESTING_PATH;
    filepath += "/" + fname;
    // Simple file-access stuff
    ifstream ifs ( filepath.c_str() );
    if( !ifs.good() )
    {
      error = 1;
      return output;
    }
    ifs.getline( line, MAX_READ_SIZE ); pof++;
    if( ifs.good() )
    {
      // Verifying that the file matches the format
      if( line[0] != TEST_DELIM )
      {
        error = 2;
        return output;
      }
      // Loops through lines of test data
      ifs.getline( line, MAX_READ_SIZE ); pof++;
      while( line[0] != TEST_DELIM && ifs.good() )
      {
        //Extract the indexed data into a string
        string data (line);
        size_t pos = 0;
        for( unsigned i = 0; i < index; i++ )
        {
          pos = data.find( TEST_DELIM, pos );
          if( pos == string::npos )
          {
            error = 2;
            return output;
          }
          pos++;
        }
        data = data.substr( pos );
        pos = data.find( TEST_DELIM );
        if( pos != string::npos ) data = data.substr( 0, pos );
        // The magic happens...
        V next;
        if( !convertData( data, next ) )
        {
          error = 2;
          return output;
        }
        output.push_back( next );
        ifs.getline( line, MAX_READ_SIZE ); pof++;
      }
      if( !ifs.fail() && line[0] == TEST_DELIM  )
      {
        error = 0;
        return output;
      }
    }
    if( ifs.fail() ) error = 1;
    else error = 3;
    return output;
  }
};

template <class V>
struct LoadResult
{
  vector<V> operator() ( string fname, unsigned &error, unsigned &pof )
  {
    pof = 0;
    char line[MAX_READ_SIZE];
    vector<V> output;
    string filepath = TESTING_PATH;
    filepath += "/" + fname;
    // Simple file-access stuff
    ifstream ifs ( filepath.c_str() );
    if( !ifs.good() )
    {
      error = 1;
      return output;
    }
    ifs.getline( line, MAX_READ_SIZE ); pof++;
    if( ifs.good() )
    {
      // Verifying that the file matches the format
      if( line[0] != TEST_DELIM )
      {
        error = 2;
        return output;
      }
      // Loops through all of the test data, doing nothing
      ifs.getline( line, MAX_READ_SIZE ); pof++;
      while( line[0] != TEST_DELIM && ifs.good() )
      {
        ifs.getline( line, MAX_READ_SIZE ); pof++;
      }
      if( ifs.good() )
      {
        // Loops through all of the test results, adding them to the output 
        ifs.getline( line, MAX_READ_SIZE ); pof++;
        while( line[0] != '#' && ifs.good() )
        {
          V next;
          string data = line;
          if( !convertData( data, next ) )
          {
            error = 2;
            return output;
          }
          output.push_back( next );
          ifs.getline( line, MAX_READ_SIZE ); pof++;
        }
        // Confirm there are no stream errors, with the exception of EOF
        if( !ifs.fail() )
        {
          error = 0;
          return output;
        }
      }
    }
    if( ifs.fail() ) error = 1;
    else error = 3;
    return output;
  }
};

template <class T>
vector<T> getData( LoadResult<T> loadResult, string fname, unsigned index,
    ostream &out, unsigned ind, unsigned &error )
{
  unsigned pof;
  vector<T> data = loadResult( fname, error, pof );
  if( error != 0 )
  {
    switch( error )
    {
      case 1:
        if( pof == 0 )
        {
          out << tab(ind) << "ERROR: I/O error while trying to open test "
            << " file." << '\n';
        }
        else
        {
          out << tab(ind) << "ERROR: I/O error occured on line " << pof
            << " of test file." << '\n';
        }
        break;
      case 2:
        out << tab(ind) << "ERROR: Invalid input on line " << pof
          << " of test file." << '\n';
        break;
      case 3:
        out << tab(ind) << "ERROR: End of file reached early." << '\n';
        break;
      // Should not conceivably happen
      default:
        out << tab(ind) << "ERROR: Unknown error code received while "
          << " reading test file." << '\n';
        break;
    } 
  }
  return data;
}

template <class T>
vector<T> getData( LoadTest<T> loadTest, string fname, unsigned index,
    ostream &out, unsigned ind, unsigned &error )
{
  unsigned pof;
  vector<T> data = loadTest( fname, index, error, pof );
  if( error != 0 )
  {
    switch( error )
    {
      case 1:
        if( pof == 0 )
        {
          out << tab(ind) << "ERROR: I/O error while trying to open test "
            << " file." << '\n';
        }
        else
        {
          out << tab(ind) << "ERROR: I/O error occured on line " << pof
            << " of test file." << '\n';
        }
        break;
      case 2:
        out << tab(ind) << "ERROR: Invalid input on line " << pof
          << " of test file." << '\n';
        break;
      case 3:
        out << tab(ind) << "ERROR: End of file reached early." << '\n';
        break;
      // Should not conceivably happen
      default:
        out << tab(ind) << "ERROR: Unknown error code received while "
          << " reading test file." << '\n';
        break;
    } 
  }
  return data;
}

template <class Ret, class A>
bool runFunctionTests( function<Ret(A)> f, string fname, ostream &out,
    unsigned ind )
{
  // Obtains the test data
  unsigned error;
  LoadResult<Ret> loadResult;
  vector<Ret> resultData = getData( loadResult, fname, 0, out, ind, error );
  if( error != 0 ) return false;
  LoadTest<A> loadTestA;
  vector<A> testA = getData( loadTestA, fname, 0, out, ind, error );
  if( error != 0 ) return false;
  // Checks the volume of test data for validity
  vector<size_t> failures;
  size_t size = resultData.size();
  if( size != testA.size() )
  {
    out << tab(ind) << "ERROR: Different test and result pool sizes." << '\n';
    return false;
  }
  vector<Ret> testResults;
  for( size_t it = 0; it < size; it++ )
  {
    testResults.push_back( f(testA[it]) );
  }
  for( size_t it = 0; it < size; it++ )
  {
    if( resultData[it] != testResults[it] ) failures.push_back( it );
  }
  if( failures.size() == 0 )
  {
    out << tab(ind) << "SUCCESS: All " << size << "  tests passed." << '\n';
    return true;
  }
  out << tab(ind) << "FAILURE: Function failed the following tests:" << '\n';
  ind++;
  for( vector<size_t>::iterator it = failures.begin(); it != failures.end();
      it++ )
  {
    out << tab(ind) << *it << '\n';
  }
  ind--;
  return false;
}

template <class Ret, class A, class B>
bool runFunctionTests( function<Ret(A,B)> f, string fname, ostream &out,
    unsigned ind )
{
  // Obtains the test data
  unsigned error;
  LoadResult<Ret> loadResult;
  vector<Ret> resultData = getData( loadResult, fname, 0, out, ind, error );
  if( error != 0 ) return false;
  LoadTest<A> loadTestA;
  vector<A> testA = getData( loadTestA, fname, 0, out, ind, error );
  if( error != 0 ) return false;
  LoadTest<B> loadTestB;
  vector<B> testB = getData( loadTestB, fname, 1, out, ind, error );
  if( error != 0 ) return false;
  // Checks the volume of test data for validity
  vector<size_t> failures;
  size_t size = resultData.size();
  if( size != testA.size() )
  {
    out << tab(ind) << "ERROR: Different test and result pool sizes." << '\n';
    return false;
  }
  vector<Ret> testResults;
  for( size_t it = 0; it < size; it++ )
  {
    testResults.push_back( f(testA[it],testB[it]) );
  }
  for( size_t it = 0; it < size; it++ )
  {
    if( resultData[it] != testResults[it] ) failures.push_back( it );
  }
  if( failures.size() == 0 )
  {
    out << tab(ind) << "SUCCESS: All " << size << "  tests passed." << '\n';
    return true;
  }
  out << tab(ind) << "FAILURE: Function failed the following tests:" << '\n';
  ind++;
  for( vector<size_t>::iterator it = failures.begin(); it != failures.end();
      it++ )
  {
    out << tab(ind) << *it << '\n';
  }
  ind--;
  return false;
}

template <class Ret, class A, class B, class C>
bool runFunctionTests( function<Ret(A,B,C)> f, string fname, ostream &out,
    unsigned ind )
{
  // Obtains the test data
  unsigned error;
  LoadResult<Ret> loadResult;
  vector<Ret> resultData = getData( loadResult, fname, 0, out, ind, error );
  if( error != 0 ) return false;
  LoadTest<A> loadTestA;
  vector<A> testA = getData( loadTestA, fname, 0, out, ind, error );
  if( error != 0 ) return false;
  LoadTest<B> loadTestB;
  vector<B> testB = getData( loadTestB, fname, 1, out, ind, error );
  if( error != 0 ) return false;
  LoadTest<C> loadTestC;
  vector<C> testC = getData( loadTestC, fname, 2, out, ind, error );
  if( error != 0 ) return false;
  // Checks the volume of test data for validity
  vector<size_t> failures;
  size_t size = resultData.size();
  if( size != testA.size() )
  {
    out << tab(ind) << "ERROR: Different test and result pool sizes." << '\n';
    return false;
  }
  vector<Ret> testResults;
  for( size_t it = 0; it < size; it++ )
  {
    testResults.push_back( f(testA[it],testB[it],testC[it]) );
  }
  for( size_t it = 0; it < size; it++ )
  {
    if( resultData[it] != testResults[it] ) failures.push_back( it );
  }
  if( failures.size() == 0 )
  {
    out << tab(ind) << "SUCCESS: All " << size << "  tests passed." << '\n';
    return true;
  }
  out << tab(ind) << "FAILURE: Function failed the following tests:" << '\n';
  ind++;
  for( vector<size_t>::iterator it = failures.begin(); it != failures.end();
      it++ )
  {
    out << tab(ind) << *it << '\n';
  }
  ind--;
  return false;
}

template <class Ret, class A, class B, class C, class D >
bool runFunctionTests( function<Ret(A,B,C,D)> f, string fname, ostream &out,
    unsigned ind )
{
  // Obtains the test data
  unsigned error;
  LoadResult<Ret> loadResult;
  vector<Ret> resultData = getData( loadResult, fname, 0, out, ind, error );
  if( error != 0 ) return false;
  LoadTest<A> loadTestA;
  vector<A> testA = getData( loadTestA, fname, 0, out, ind, error );
  if( error != 0 ) return false;
  LoadTest<B> loadTestB;
  vector<B> testB = getData( loadTestB, fname, 1, out, ind, error );
  if( error != 0 ) return false;
  LoadTest<C> loadTestC;
  vector<C> testC = getData( loadTestC, fname, 2, out, ind, error );
  if( error != 0 ) return false;
  LoadTest<D> loadTestD;
  vector<D> testD = getData( loadTestD, fname, 3, out, ind, error );
  if( error != 0 ) return false;
  // Checks the volume of test data for validity
  vector<size_t> failures;
  size_t size = resultData.size();
  if( size != testA.size() )
  {
    out << tab(ind) << "ERROR: Different test and result pool sizes." << '\n';
    return false;
  }
  vector<Ret> testResults;
  for( size_t it = 0; it < size; it++ )
  {
    testResults.push_back( f(testA[it],testB[it],testC[it],testD[it]) );
  }
  for( size_t it = 0; it < size; it++ )
  {
    if( resultData[it] != testResults[it] ) failures.push_back( it );
  }
  if( failures.size() == 0 )
  {
    out << tab(ind) << "SUCCESS: All " << size << "  tests passed." << '\n';
    return true;
  }
  out << tab(ind) << "FAILURE: Function failed the following tests:" << '\n';
  ind++;
  for( vector<size_t>::iterator it = failures.begin(); it != failures.end();
      it++ )
  {
    out << tab(ind) << *it << '\n';
  }
  ind--;
  return false;
}

template <class Ret, class A, class B, class C, class D, class E>
bool runFunctionTests( function<Ret(A,B,C,D,E)> f, string fname, ostream &out,
    unsigned ind )
{
  // Obtains the test data
  unsigned error;
  LoadResult<Ret> loadResult;
  vector<Ret> resultData = getData( loadResult, fname, 0, out, ind, error );
  if( error != 0 ) return false;
  LoadTest<A> loadTestA;
  vector<A> testA = getData( loadTestA, fname, 0, out, ind, error );
  if( error != 0 ) return false;
  LoadTest<B> loadTestB;
  vector<B> testB = getData( loadTestB, fname, 1, out, ind, error );
  if( error != 0 ) return false;
  LoadTest<C> loadTestC;
  vector<C> testC = getData( loadTestC, fname, 2, out, ind, error );
  if( error != 0 ) return false;
  LoadTest<D> loadTestD;
  vector<D> testD = getData( loadTestD, fname, 3, out, ind, error );
  if( error != 0 ) return false;
  LoadTest<E> loadTestE;
  vector<E> testE = getData( loadTestE, fname, 4, out, ind, error );
  if( error != 0 ) return false;
  // Checks the volume of test data for validity
  vector<size_t> failures;
  size_t size = resultData.size();
  if( size != testA.size() )
  {
    out << tab(ind) << "ERROR: Different test and result pool sizes." << '\n';
    return false;
  }
  vector<Ret> testResults;
  for( size_t it = 0; it < size; it++ )
  {
    testResults.push_back(
        f(testA[it],testB[it],testC[it],testD[it],testE[it]) );
  }
  for( size_t it = 0; it < size; it++ )
  {
    if( resultData[it] != testResults[it] ) failures.push_back( it );
  }
  if( failures.size() == 0 )
  {
    out << tab(ind) << "SUCCESS: All " << size << "  tests passed." << '\n';
    return true;
  }
  else
  {
    out << tab(ind) << "FAILURE: Function failed the following tests:" << '\n';
    ind++;
    for( vector<size_t>::iterator it = failures.begin(); it != failures.end();
        it++ )
    {
      out << tab(ind) << *it << '\n';
    }
    ind--;
  }
  return false;
}

<file_sep><?php

require "framework/UnitTest.php";
require "../includes/kernel.php";

array_shift($argv);

if(empty($argv)) {
    echo "Usage: php run.php Class\n";
    exit;
}

if($argv[0] == "suite" || $argv[0] == "all") {
    $files = glob("test*.php");
    $files = array_diff($files, array("testSuite.php"));

    foreach($files as $file) {
        $classes = get_declared_classes();
        require_once $file;
        $diff = array_diff(get_declared_classes(), $classes);
        $class = reset($diff);

        $test = new $class;
        $test->exec();
    }
} else {
    foreach($argv as $class) {
        $class = str_replace("\\", "", $class);
        $file = "test" . $class . ".php";
        $class = $class . "Test";

        if(file_exists($file)) {
            require_once $file;

            $test = new $class;
            $test->exec();
        }
    }
}

?><file_sep><?php

include("includes/kernel.php");
include("includes/api/core.php");

/*!
 * Check that the parameters have all been set and sent to the script,
 * including the prefix and the name.
 */

if(empty($data->prefix) || empty($data->name)) {
    echo json_beautify(json_render_error(401, "You didn't pass one or more of the required parameters."));
    exit;
}

/*!
 * Check to see if the dataset exists, and that we have access to it.
 * We need to use the prefix and the name of the dataset to get a
 * reference to it.
 */

// Create a new dataset object.
$dataset = new rainhawk\dataset($data->prefix, $data->name);

// Check that the dataset exists.
if(!$dataset->exists) {
    echo json_beautify(json_render_error(402, "The dataset you specified does not exist."));
    exit;
}

// Check that we can read from the dataset.
if(!$dataset->have_read_access(app::$username)) {
    echo json_beautify(json_render_error(403, "You don't have access to read from this dataset."));
    exit;
}

/*!
 * Define an empty array to store the results of whatever we need
 * to send back.
 */

$json = array(
    "coefficients" => array()
);

/*!
 * Pipe the command to our Python script to calculate the coefficients
 * of the polynomial using NumPy.
 */

$dataset = escapeshellarg($data->dataset);
$degree = $data->degree;

// Check that the two fields are set.
if(!is_array($data->fields) || !(count($data->fields) == 2)) {
    echo json_beautify(json_render_error(404, "You didn't specify the two fields to use!"));
    exit;
}

// Set the fields.
$field_one = escapeshellarg($data->fields[0]);
$field_two = escapeshellarg($data->fields[1]);

// Run the command.
exec("python '../correlation/correlation.py' eco " . $dataset . " " . $field_one . " " . $field_two . " " . $degree, $output);

// Check for empty output.
if(empty($output)) {
    echo json_beautify(json_render_error(405, "Couldn't find a polyfit equation for the given datasets."));
    exit;
}

// Output the coefficients.
foreach($output as $line) {
    $data = json_decode($line, true);

    if(is_array($data)) {
        $json['coefficients'] = $data;
    }
}

/*!
 * Output our JSON payload for use in whatever needs to be using
 * it.
 */

echo json_beautify(json_render(200, $json));
exit;

?><file_sep>#include <string>
#include <vector>
#include "sadReader.hpp"
#include "sadUtils.hpp"
#include "sadTables.hpp"
#include "sadWriter.hpp"

using namespace std;

const unsigned edges[4][6] =
{
  { 0, 1, 2, 3, 4, 5 },
  { 3, 4, 5, 6, 7, 8 },
  { 0, 1, 3, 4, 6, 7 },
  { 1, 2, 4, 5, 7, 8 }
};

const unsigned corners[4][6] =
{
  { 0, 1, 3, 4 },
  { 1, 2, 4, 5 },
  { 3, 4, 6, 7 },
  { 4, 5, 7, 8 }
};

item idProperty( vector<JType> adj )
{
  item r;
  // Header
  r.header = 0;
  if( adj[3] == STRING ) r.header++;
  if( adj[4] == STRING ) r.header += 2;
  if( adj[5] == STRING ) r.header++;
  // Edge
  for( size_t it = 0; it < 4; it++ )
  {
    r.edge[it] = 0;
    if( adj[edges[it][0]] != NULLVALUE ) r.edge[it]++;
    if( adj[edges[it][1]] != NULLVALUE ) r.edge[it]++;
    if( adj[edges[it][2]] != NULLVALUE ) r.edge[it]++;
    if( adj[edges[it][3]] != NULLVALUE ) r.edge[it]++;
    if( adj[edges[it][4]] != NULLVALUE ) r.edge[it]++;
    if( adj[edges[it][5]] != NULLVALUE ) r.edge[it]++;
  }
  // Corner
  for( size_t it = 0; it < 4; it++ )
  {
    r.corner[it] = 0;
    if( adj[corners[it][0]] != NULLVALUE ) r.corner[it]++;
    if( adj[corners[it][1]] != NULLVALUE ) r.corner[it]++;
    if( adj[corners[it][2]] != NULLVALUE ) r.corner[it]++;
    if( adj[corners[it][3]] != NULLVALUE ) r.corner[it]++;
  }
  // Tuple
  r.tuple = 0;
  for( size_t it = 0; it < 9; it++ ) if( adj[it] != NULLVALUE ) r.tuple++;
  // Vertical bridge
  r.vbridge[0] = 0; r.vbridge[1] = 0; r.vbridge[2] = 0;
  if( adj[6] != NULLVALUE )
  {
    r.vbridge[0]++;
    r.vbridge[1]++;
  }
  if( adj[7] != NULLVALUE )
  {
    r.vbridge[0]++;
    r.vbridge[1]++;
    r.vbridge[2]++;
  }
  if( adj[8] != NULLVALUE )
  {
    r.vbridge[1]++;
    r.vbridge[2]++;
  }
  // Horizontal bridge
  r.hbridge = false;
  if( adj[3] != NULLVALUE && adj[5] != NULLVALUE &&
      ( ( adj[0] != NULLVALUE && adj[2] != NULLVALUE ) ||
        ( adj[6] != NULLVALUE && adj[8] != NULLVALUE ) ) )
  {
    r.hbridge = true;
  }
  // End
  return r;
}

vector< vector<item> > propertyGrid( sheet sh )
{
  size_t rows = sh.rows();
  size_t cols = sh.cols();
  vector< vector<item> > properties;
  for( size_t row = 0; row < rows; row++ )
  {
    vector<item> propertyrow;
    for( size_t col = 0; col < cols; col++ )
    {
      vector<JType> adj;
      for( int x = row-1; x <= (int)row+1; x++ )
      {
        for( int y = col-1; y <= (int)col+1; y++ )
        {
          if( x<0 || x>(int)rows-1 || y<0 || y>(int)cols-1 ) adj.push_back( NULLVALUE );
          else adj.push_back( sh[x][y].getType() );
        }
      }
      propertyrow.push_back( idProperty( adj ) );
    }
    properties.push_back( propertyrow );
  }
  return properties;
}

int evalVBridge( const vector< vector<item> > &pgrid, size_t crow, size_t start,
    size_t end )
{
  int midN = 2;
  int edgeN = 1;
  int score = 0;
  score += (int)pgrid[crow][start].vbridge[2] - edgeN;
  score += (int)pgrid[crow][end].vbridge[0] - edgeN;
  for( size_t it = start + 1; it < end; it++ )
  {
    score += (int)pgrid[crow][it].vbridge[1] - midN;
  }
  return score;
}

int evalTopRow( const vector< vector<item> > &pgrid, size_t crow, size_t start,
    size_t end )
{
  int edgeN = 4;
  int cornerN = 3;
  int score = 0;
  score += (int)pgrid[crow][start].corner[3] - cornerN;
  score += (int)pgrid[crow][end].corner[2] - cornerN;
  for( size_t it = start + 1; it < end; it++ )
  {
    score += (int)pgrid[crow][it].edge[1] - edgeN;
  }
  return score;
}

trow evalRow( const vector< vector<item> > &pgrid, size_t crow, size_t start,
    size_t end )
{
  int tupleN = 6;
  int edgeN = 4;
  int cornerN = 3;
  trow thisrow;
  thisrow.mid = 0;
  thisrow.bot = 0;
  thisrow.mid += (int)pgrid[crow][start].edge[3] - edgeN;
  thisrow.mid += (int)pgrid[crow][end].edge[2] - edgeN;
  thisrow.bot += (int)pgrid[crow][start].corner[1] - cornerN;
  thisrow.bot += (int)pgrid[crow][end].corner[0] - cornerN;
  for( size_t it = start + 1; it < end; it++ )
  {
    thisrow.mid += (int)pgrid[crow][it].tuple - tupleN;
    thisrow.bot += (int)pgrid[crow][it].edge[0] - edgeN;
  }
  return thisrow;
}

vector<tableRating> evalTableBody( const vector< vector<item> > &pgrid, 
    vector< vector<bool> > &allowed, size_t x1, size_t x2, size_t y )
{
  size_t crow = y+1;
  bool remaining = true;
  unsigned population = ( ((x2-x1)*(x2-x1)) + (x2-x1) )/2;
  tableRating nothingness;
  nothingness.score = 0;
  nothingness.depth = 0;
  vector<tableRating> scores( population, nothingness );
  while( crow < pgrid.size() && remaining )
  {
    remaining = false;
    size_t selection = 0;
    for( size_t start = x1; start < x2; start++ )
    {
      for( size_t end = start+1; end <= x2; end++ )
      {
        if( allowed[start-x1][end-start-1] )
        {
          trow thisrow = evalRow( pgrid, crow, start, end );
          if( crow == y+1 )
          {
            int bridgeScore = evalVBridge( pgrid, crow, start, end );
            if( bridgeScore > thisrow.mid ) thisrow.mid = bridgeScore;
          }
          if( crow == y+2 )
          {
            int topScore = evalTopRow( pgrid, crow, start, end );
            if( topScore > thisrow.mid ) thisrow.mid = topScore;
          }
          if( thisrow.mid > 0 || thisrow.bot > 0 ) 
          {
            if( thisrow.mid >= thisrow.bot )
            {
              remaining = true;
              scores[selection].score += thisrow.mid;
              scores[selection].depth++;
            }
            else
            {
              allowed[start-x1][end-start-1] = false;
              scores[selection].score += thisrow.bot;
              scores[selection].depth++;
            }
          }
          else allowed[start-x1][end-start-1] = false;
        }
        selection++;
      }
    }
    crow++;
  }
  return scores;
}


// Only call when tbl.x1, tbl.x2, and tbl.y1 have been initialized
bool idTable( const vector< vector<item> > &pgrid, size_t x1, size_t x2,
    size_t y, table &result )
{
  // Filter out headers at the bottom of the sheet
  if( pgrid.size() <= y+1 ) return false;
  // A diagonal grid representing each possible combination of x1 and x2 values
  vector< vector<bool> > allowed;
  for( size_t start = x1; start < x2; start++ )
  {
    vector<bool> endpoints;
    for( size_t end = start+1; end <= x2; end++ )
    {
      endpoints.push_back( true );
    }
    allowed.push_back( endpoints );
  }
  // Loops through each row, determining all the ways it may be valid. Ends
  // either at the end of the grid or when the last row has no more valid forms.
  vector<tableRating> scores = evalTableBody( pgrid, allowed, x1,
      x2, y );
  int max = -1;
  unsigned current = 0;
  for( size_t start = x1; start < x2; start++ )
  {
    for( size_t end = start+1; end <= x2; end++ )
    {
      //  end << "," << scores[current].depth << "] : " << scores[current].score << '\n';
      if( scores[current].score > max )
      {
        result.x1 = start;
        result.x2 = end;
        result.y1 = y;
        result.y2 = y + scores[current].depth;
        result.score = scores[current].score;
        max = scores[current].score;
      }
      current++;
    }
  }
  if( result.y1 != result.y2 ) return true; 
  return false;
}

bool containedIn( table a, table b )
{
  if( a.x1 >= b.x1 && a.x2 <= b.x2 && a.y1 >= b.y1 && a.y2 <= b.y2 ) return true;
  return false;
}

vector<table> scanTables( const vector< vector<item> > &pgrid )
{
  vector<table> result;
  size_t rows = pgrid.size();
  size_t cols = pgrid[0].size();
  for( size_t row = 0; row < rows; row++ )
  {
    table tbl;
    unsigned heads = 0;
    for( size_t col = 0; col < cols; col++ )
    {
      if( pgrid[row][col].header >= 4 )
      {
        // This shouldn't theoretically be possible at all given a valid grid.
        if( heads == 0 )
        {
          tbl.x1 = col;
          tbl.y1 = row;
        }
        heads++;
      }
      else
      {
        if( heads > 0 )
        {
          tbl.x2 = col;
          tbl.y2 = tbl.y1;
          bool contained = false;
          for( vector<table>::iterator it = result.begin();
              it != result.end(); it++ )
          {
            if( containedIn( tbl, *it ) ) contained = true;
          }
          if( !contained ) if( idTable( pgrid, tbl.x1, tbl.x2, tbl.y1, tbl ) )
          {
            result.push_back( tbl );
          }
          heads = 0;
        }
        else if( pgrid[row][col].header == 3 )
        {
          tbl.x1 = col;
          tbl.y1 = row;
          heads++;
        }
        else heads = 0;
      }
    }
  }
  return result;
}

void printProp( vector< vector<item> > pg )
{
  for( size_t x = 0; x < pg.size(); x++ )
  {
    for( size_t y = 0; y < pg[x].size(); y++ )
    {
      cout << "[" << x << "," << y << "]: ";
      cout << "H: " << pg[x][y].header << ", ";
      cout << "E: " << pg[x][y].edge[0] << "," << pg[x][y].edge[1] << "," <<
        pg[x][y].edge[2] << "," << pg[x][y].edge[3] << ", ";
      cout << "C: " << pg[x][y].corner[0] << "," << pg[x][y].corner[1] << "," <<
        pg[x][y].corner[2] << "," << pg[x][y].corner[3] << ", ";
      cout << "T: " << pg[x][y].tuple << ", ";
      cout << "V: " << pg[x][y].vbridge[0] << "," << pg[x][y].vbridge[1] <<
        "," << pg[x][y].vbridge[2];
      cout << '\n';
    }
  }
}

unsigned clashDegree( table a, table b )
{
  if( a.x1 > b.x2 || a.x2 < b.x1 ) return 0;
  else
  {
    if( a.y1 > b.y2 || a.y2 < b.y1 ) return 0;
    else return 1;
  }
}

void removeClashes( vector<table> &input )
{
  size_t current = 0;
  while( current < input.size() )
  {
    size_t next = current + 1;
    while( next < input.size() )
    {
      if( clashDegree( input[current], input[next] ) > 0 )
      {
        if( input[next].score > input[current].score )
        {
          input.erase( input.begin() + current );
          current = 0;
          next = current + 1;
        }
        else input.erase( input.begin() + next );
      }
      else next++;
    }
    current++;
  }
}

vector<table> getSheetTables( sheet sh )
{
  vector< vector<item> > pgrid = propertyGrid( sh );
  vector<table> result = scanTables( pgrid );
  removeClashes( result );
  return result;
}

<file_sep>#ifndef SAD_TABLES
#define SAD_TABLES

struct item
{
  unsigned header;
  unsigned edge[4];
  unsigned corner[4];
  unsigned tuple;
  unsigned vbridge[3];
  bool hbridge;
};

struct trow
{
  long int mid;
  long int bot;
};

struct table
{
  size_t x1;
  size_t x2;
  size_t y1;
  size_t y2;
  long int score;
};

struct tableRating
{
  long int score;
  size_t depth;
};

std::vector<table> getSheetTables( sheet sh );

#endif
<file_sep>#ifndef SAD_UTILS
#define SAD_UTILS

#include <string>
#include <sstream>
#include <fstream>
#include <vector>
#include <functional>

extern const char* TESTING_PATH;
const unsigned MAX_READ_SIZE = 256;

std::string tab( unsigned size, char tabc = ' ', unsigned tabw = 2 );
std::string toUpper( std::string input );
bool whitespace( char c );

// Templated Functions

bool convertBool( std::string &data, bool &v );

// Generic string-to-data function
// Only works with data types that can be converted by a stringstream
template<class V>
bool convertData( std::string &data, V &v )
{
  std::istringstream ss(data);
  ss >> v;
  if( ss.fail() || !ss.eof() ) return false;
  else return true;
}

// Generic testing function
template <class Ret, class... Args>
bool testFunction( std::function<Ret(Args...)> f, std::string fname,
    std::ostream &out, unsigned ind )
{
  int argc = sizeof...(Args);
  out << tab(ind) << "Testing function " << fname << "..." << '\n';
  if( argc > 5 )
  {
    out << tab(ind) << "ERROR: Too many arguments to function." << '\n';
    return false;
  }
  if( argc < 1 )
  {
    out << tab(ind) << "ERROR: Too few arguments to function." << '\n';
    return false;
  }
  return runFunctionTests( f, fname, out, ind );
}

#endif

<file_sep>#include <iostream>
#include <vector>
#include <cstdio>
#include <cstring>
#include "sadReader.hpp"
#include "sadTables.hpp"
#include "sadWriter.hpp"

using namespace std;

int main( int argc, char * argv[] )
{
  if( argc == 1 )
  {
    cout << "Usage: sadparser [filenames]" << '\n';
    return 0;
  }
  else if( argc == 2 )
  {
    if( strcmp( argv[1], "help" ) == 0 )
    {
      cout << "Usage: sadparser [filenames]" << '\n';
      return 0;
    }
  }
  for( int cfile = 1; cfile < argc; cfile++ )
  {
    string filename = argv[cfile];
    if( filename[0] == '-' )
    {
      if( filename.compare("-n") == 0 )
      {
        //Placeholder text
        continue;
      }
      else
      {
        cout << "Flag not recognized: \"" << filename << "\"" << '\n';
        continue;
      }
    }
    cout << "Now reading file " << argv[cfile] << "..." << '\n';
    cout << "Getting contents..." << '\n';
    unsigned error = 0;
    vector<sheet> fileContents = readFile( filename, error );
    if( error == 0 )
    {
      cout << "Processing contents..." << '\n';
      vector< vector<table> > tablesOfSheets;
      for(
        vector<sheet>::iterator it = fileContents.begin();
        it != fileContents.end();
        it++
      )
      {
        vector<table> tableList = getSheetTables( *it );
        tablesOfSheets.push_back( tableList );
      }
      cout << "Printing contents..." << '\n';
      unsigned count = 0;
      for( size_t it = 0; it < fileContents.size(); it++ )
      {
        string title = filename.substr( 0, filename.find_last_of('.') );
        char * newFilename = new char[title.size()+5];
        sprintf( newFilename, "%s-%02d", title.c_str(), count+1 );
        string filetitle( newFilename );
        vector<string> fileList = jsonFile( filetitle, fileContents[it],
            tablesOfSheets[it] );
        for( size_t ot = 0; ot < fileList.size(); ot++ )
        {
          cout << fileList[ot] << " created." << '\n';
        }
        count++;
      }
    }
    else
    {
      switch( error )
      {
        case 1:
          cout << "ERROR: Failure in file access operations." << '\n';
          break;
        case 2:
        case 3:
          cout << "ERROR: Invalid file contents." << '\n';
          break;
        default:
          cout << "ERROR: Unknown error code, something is wrong." << '\n';
          break;
      }
    }
  }
  cout << "All done." << '\n';
  return 0;
}

<file_sep><?php

require_once "includes/core.php";
require_once "includes/check_login.php";

$datasetsInfo = $rainhawk->listDatasets();

?>
<!DOCTYPE html>
<html lang="en-GB">
    <head>
        <title>Project Rainhawk - Datasets</title>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <?php require_once "includes/meta.php"; ?>
        <script>
            $(function() {
                $(".confirm").confirm({
                    text: "Are you sure you wish to delete this dataset? This action is irreversible!",
                    title: "Really?",
                    confirmButton: "Delete"
                });
            });
        </script>
    </head>

    <body>
        <?php require_once "includes/nav.php"; ?>
        <div class="container">
            <?php if(isset($_GET['deletefailed'])) { ?>
                <div class="row">
                    <div class="alert alert-danger fade in">
                        <strong>Error!</strong> Deletion failed
                        <button type="button" class="close" data-dismiss="alert" aria-hidden="true">&times;</button>
                    </div>
                </div>
            <?php } else if(isset($_GET['deleted'])) { ?>
                <div class="row">
                    <div class="alert alert-success fade in">
                        <strong>Gone!</strong> Dataset successfully deleted.
                        <button type="button" class="close" data-dismiss="alert" aria-hidden="true">&times;</button>
                    </div>
                </div>
            <?php } ?>
            <div class="row">
                <table class="table">
                    <thead>
                        <tr>
                            <th>Name</th>
                            <th>Description</th>
                            <th>Records</th>
                            <th>Fields</th>
                            <th>Access</th>
                            <th></th>
                            <th></th>
                            <th></th>
                            <th></th>
                        </tr>
                    </head>
                    <tbody>
                        <?php foreach($datasetsInfo as $dataset) { ?>
                            <tr>
                                <td><a href="/properties.php?dataset=<?php echo $dataset['name']; ?>"><?php echo $dataset['name']; ?></a></td>
                                <td><?php echo $dataset['description']; ?></td>
                                <td><?php echo $dataset['rows']; ?></td>
                                <td><?php echo count($dataset['fields']); ?></td>
                                <td><?php echo in_array($user, $dataset['write_access']) ? "Write" : "Read"; ?></td>
                                <td>
                                    <a href="/edit.php?dataset=<?php echo $dataset['name']; ?>" class="btn btn-info btn-sm">
                                        <i class="fa fa-edit"></i>&nbsp; View
                                    </a>
                                </td>
                                <td>
                                    <a href="/visualise/?dataset=<?php echo $dataset['name']; ?>" class="btn btn-success btn-sm" <?php echo $dataset['rows'] == 0 ? "disabled" : null; ?>>
                                        <i class="fa fa-bar-chart-o"></i>&nbsp; Visualise
                                    </a>
                                </td>
                                <td>
                                    <a href="/upload.php?dataset=<?php echo $dataset['name']; ?>" class="btn btn-primary btn-sm" <?php echo !in_array($user, $dataset['write_access']) ? "disabled" : null; ?>>
                                        <i class="fa fa-cloud-upload"></i>&nbsp; Upload
                                    </a>
                                </td>
                                <td>
                                    <a href="/proxy/delete_dataset.php?dataset=<?php echo $dataset['name']; ?>" id="del<?php echo explode(".", $dataset['name'])[1]; ?>" class="btn btn-danger btn-sm confirm" <?php echo !in_array($user, $dataset['write_access']) ? "disabled" : null; ?>>
                                        <i class="fa fa-ban"></i>&nbsp; Delete
                                    </a>
                                </td>
                            </tr>
                        <?php } ?>
                    </tbody>
                </table>
            </div>
        </div>
    </body>
</html>
<file_sep><?php

require_once "../includes/core.php";
require_once "../includes/check_login.php";

header("content-type: application/json; charset=utf8");

$dataset = isset($_GET['dataset']) ? $_GET['dataset'] : null;
$idvalue = isset($_POST['_id']) ? $_POST['_id'] : null;
$changes = array();

foreach($_POST as $name => $value) {
    if($name == "dataset") continue;
    if($name == "_id") continue;

    $changes[$name] = $value;
}

$query = array(
    "_id" => $idvalue
);

$changes = array(
    '$set' => $changes
);

$result = $rainhawk->updateData($dataset, $query, $changes);

if(!$result) {
    echo json_encode(array(
        "Result" => "ERROR",
        "Message" => $rainhawk->error()
    ));
    exit;
}

echo json_encode(array(
    "Result" => "OK"
));
exit;

?>
<file_sep><?php

require_once "includes/core.php";

if(isset($_POST['apiKey'])) {
    $mashape_key = trim($_POST['apiKey']);
    $rainhawk = new Rainhawk($mashape_key);
    $ping = $rainhawk->ping();
    $user = null;

    if($ping) {
        $user = $ping['mashape_user'];
    }

    if(empty($user)) {
        header("Location: /login.php?fail");
        exit;
    } else {
        $location = isset($_GET['dest']) ? urldecode($_GET['dest']) : "/datasets.php";

        $_SESSION['apiKey'] = $mashape_key;
        $_SESSION['user'] = $user;

        header("Location: " . $location);
        exit;
    }
}

if(isset($_SESSION['apiKey']) && isset($_SESSION['user']) && !isset($_GET['logout'])) {
    header("Location: /datasets.php");
    exit;
}

if(isset($_GET['logout'])) {
    session_unset();
}

?>
<!DOCTYPE html>
<html lang="en-GB">
    <head>
        <title>Project Rainhawk - Login</title>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <?php require_once "includes/meta.php"; ?>
    </head>

    <body>
        <?php require_once "includes/nav.php"; ?>
        <div class="container">
            <?php if(isset($_GET['dest'])) { ?>
                <div class="alert alert-danger">
                    <p>You need to be logged in to access this page!</p>
                </div>
            <?php } ?>
            <?php if(isset($_GET['fail'])) { ?>
                <div class="alert alert-danger">
                    <p>Your API key could not be validated with the service, please try again.</p>
                </div>
            <?php } ?>
            <?php if(isset($_GET['logout'])) { ?>
                <div class="alert alert-success">
                    <p>Successfully logged out.</p>
                </div>
            <?php } ?>
            <div class="row">
                <div class="col-md-12">
                    <div class="panel panel-default">
                        <div class="panel-body">
                            <h1>Login to manage your data!</h1>
                            <p>Please insert your Mashape API key...</p>
                        </div>
                    </div>
                </div>
            </div>
            <div class="row">
                <div class="col-md-12">
                    <div class="panel panel-default">
                        <div class="panel-body">
                            <form action="login.php<?php echo isset($_GET['dest']) ? "?dest=" . $_GET['dest'] : null; ?>" role="form" method="post">
                                <div class="form-group <?php echo isset($_GET['fail']) ? "has-error" : null; ?>">
                                    <label for="apiKey">API Key:</label>
                                    <input type="text" placeholder="Enter your API key here..." name="apiKey" class="form-control" autofocus>
                                </div>
                                <button type="submit" class="btn btn-default">Login</button>
                            </form>
                        </div>
                    </div>
                </div>
            </div>
            <div class="row">
                <div class="col-md-12">
                    <div class="panel panel-default">
                        <div class="panel-body">
                            <h3><i class="fa fa-key"></i>&nbsp; Don't have an API key?</h3>
                            <p>Register an API key for Project Rainhawk at <a href="https://www.mashape.com/sneeza/project-rainhawk">Mashape.com</a>.</p>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </body>
</html>
<file_sep>Guidelines for working on the frontend
---

* All JavaScript and JS libraries should be placed in js/ .
* All images in img/ .
* Example visualizations showcasing the app will be placed in visualizations/example/
* Best to use the minified versions of the libraries.
<file_sep>#include <string>
#include <vector>
#include "sadReader.hpp"
#include "sadTables.hpp"

std::vector<std::string> jsonFile( std::string filename, sheet &sh,
    std::vector<table> tbls );

<file_sep><?php

/**
 * Project Rainhawk
 *
 * Simple PHP wrapper for the Rainhawk API, which provides simple JSON
 * encoded data for a variety of data sources with pre-defined
 * operations.
 *
 * @package Rainhawk
 * @license none
 */

class Rainhawk {
    /**
     * Store some constants for the different request methods.
     *
     * @var string
     */

    const GET = "GET";
    const POST = "POST";
    const PUT = "PUT";
    const DELETE = "DELETE";
    const PUT_FILE = "PUTFILE";

    /**
     * Store the base address for the API.
     *
     * @var string
     */

    private $host = "https://sneeza-eco.p.mashape.com";

    /**
     * Store the Mashape API key to use for authentication to the
     * API. Without this, no requests will succeed.
     *
     * @var string
     */

    private $mashapeKey = null;

    /**
     * Store the last known error number, which is fetched from the
     * API meta response.
     *
     * @var integer
     */

    private $errno = null;

    /**
     * Store the last known error message, which is also fetched from
     * the API data response.
     *
     * @var string
     */

    private $error = null;

    /**
     * When the class is initialised, we have to store the Mashape key
     * in the class for all requests.
     *
     * @param string $mashapeKey  The mashape key to use for all requests.
     * @return eco  Our new class instance.
     */

    public function __construct($mashapeKey) {
        $this->mashapeKey = $mashapeKey;
    }

    /**
     * Send a simple ping request to the API, which will respond with
     * the timestamp of the server's current time.
     *
     * @return array|bool  Returns the data array on success, false on failure.
     */

    public function ping() {
        $url = $this->host . "/ping";
        $data = $this->sendRequest($url, self::GET);
        $json = $this->parseJson($data);

        if(!$json) {
            return false;
        }

        return $json['data'];
    }

    /**
     * Fetch a list of available datasets, in no particular order, returned
     * as an array from the API. We only get back the datasets that we have
     * read or write access to.
     *
     * @return array|bool  Returns the datasets on success, false on failure.
     */

    public function listDatasets() {
        $url = $this->host . "/datasets";
        $data = $this->sendRequest($url, self::GET);
        $json = $this->parseJson($data);

        if(!$json) {
            return false;
        }

        return $json['data']['datasets'];
    }

    /**
     * Get the dataset information, including the total rows and field
     * names. We can also use this to check whether or not the dataset
     * exists.
     *
     * @param string $name  The dataset name.
     * @return array|bool  Returns the data array on success, false on failure.
     */

    public function fetchDataset($name) {
        $url = $this->host . "/datasets/" . $name;
        $data = $this->sendRequest($url, self::GET);
        $json = $this->parseJson($data);

        if(!$json) {
            return false;
        }

        return $json['data'];
    }

    /**
     * Create a new dataset, with a specified name and description. These
     * are both mandatory.
     *
     * @param string $name  The dataset's name.
     * @param string $description  The dataset's description.
     * @return array|bool  Returns the data array on success, false on failure.
     */

    public function createDataset($name, $description) {
        $postData = array(
            "name" => $name,
            "description" => $description
        );

        $url = $this->host . "/datasets";
        $data = $this->sendRequest($url, self::POST, $postData);
        $json = $this->parseJson($data);

        if(!$json) {
            return false;
        }

        return $json['data'];
    }

    /**
     * Delete the specified dataset, provided you have write access to the
     * set. This operation cannot be reversed.
     *
     * @param string $name  The dataset's name.
     * @return bool  Returns the boolean on success, false on failure.
     */

    public function deleteDataset($name) {
        $url = $this->host . "/datasets/" . $name;
        $data = $this->sendRequest($url, self::DELETE);
        $json = $this->parseJson($data);

        if(!$json) {
            return false;
        }

        return $json['data']['deleted'];
    }

    /**
     * Run a select query, finding results from a dataset that match
     * certain conditions. Optionally, leave the query blank to return
     * all rows.
     *
     * @param string $name  The dataset name to query.
     * @param array $query  The query to run, in MongoDB format.
     * @param int $offset  Apply an offset on the query, for pagination.
     * @param int $limit  Apply a row limit on the query.
     * @param array $sort  The sorting order, in array format.
     * @param array $fields  The fields to return from the query.
     * @param array $exclude  The fields to exclude from the results.
     * @return array|bool  Returns the data array on success, false on failure.
     */

    public function selectData($name, $query = null, $offset = 0, $limit = 0, $sort = null, $fields = null, $exclude = null) {
        $query_string = array(
            "query" => json_encode($query),
            "offset" => $offset,
            "limit" => $limit,
            "sort" => json_encode($sort),
            "fields" => json_encode($fields),
            "exclude" => json_encode($exclude)
        );

        $url = $this->host . "/datasets/" . $name . "/data";
        $data = $this->sendRequest($url, self::GET, $query_string);
        $json = $this->parseJson($data);

        if(!$json) {
            return false;
        }

        return $json['data'];
    }

    /**
     * Insert is an alias of insertMulti, and we just manipulate the
     * parameters passed to wrap one row to look like an array of
     * rows.
     *
     * @param string $name  The dataset to insert rows into.
     * @param array $row  The row to insert.
     * @return array|bool  Returns the data array on success, false on failure.
     */

    public function insertData($name, $row) {
        return $this->insertMultiData($name, array($row))[0];
    }

    /**
     * Insert multiple rows into a dataset. The API will return
     * a success parameter as well as the number of rows added
     * which we can use to verify our request.
     *
     * @param string $name  The dataset to insert the rows into.
     * @param array $rows  The rows to insert.
     * @return array|bool  Returns the data array on success, false on failure.
     */

    public function insertMultiData($name, $rows) {
        $postData = array(
            "rows" => json_encode($rows)
        );

        $url = $this->host . "/datasets/" . $name . "/data";
        $data = $this->sendRequest($url, self::POST, $postData);
        $json = $this->parseJson($data);

        if(!$json) {
            return false;
        }

        return $json['data']['rows'];
    }

    /**
     * Update documents using a query, which can be used to match
     * multiple or single documents. The change is essentially just
     * a diff for the record, and the magic keyword \$unset can be
     * sent to remove a field totally.
     *
     * @param string $name  The dataset to update documents in.
     * @param array $query  The query to use to find documents to update.
     * @param array $changes  The changes to make.
     * @return array|bool  Returns the data array on success, false on failure.
     */

    public function updateData($name, $query, $changes) {
        $postData = array(
            "query" => json_encode($query),
            "changes" => json_encode($changes)
        );

        $url = $this->host . "/datasets/" . $name . "/data";
        $data = $this->sendRequest($url, self::PUT, $postData);
        $json = $this->parseJson($data);

        if(!$json) {
            return false;
        }

        return $json['data']['updated'];
    }

    /**
     * Delete documents from the database using a query to match
     * documents in a specified dataset.
     *
     * @param string $name  The dataset that we should delete rows from.
     * @param array $query  The query to match rows to delete.
     * @return array|bool  Returns the data array on success, false on failure.
     */

    public function deleteData($name, $query) {
        $postData = array(
            "query" => json_encode($query)
        );

        $url = $this->host . "/datasets/" . $name . "/data";
        $data = $this->sendRequest($url, self::DELETE, $postData);
        $json = $this->parseJson($data);

        if(!$json) {
            return false;
        }

        return $json['data']['deleted'];
    }

    /**
     * Upload a data file to the specified dataset. We currently support lots
     * of different types of files including .ods, .csv, .txt and more.
     *
     * @param string $name  The dataset to insert the data into.
     * @param string $file  The path to the file to upload.
     * @return array|bool  Returns the data array on success, false on failure.
     */

    public function uploadData($name, $file) {
        $fp = fopen($file, "rb");
        $size = filesize($file);

        $postData = array(
            "fp" => $fp,
            "size" => $size
        );

        $url = $this->host . "/datasets/" . $name . "/upload/" . pathinfo($file, PATHINFO_EXTENSION);
        $data = $this->sendRequest($url, self::PUT_FILE, $postData);
        $json = $this->parseJson($data);

        if(!$json) {
            return false;
        }

        return $json['data'];
    }

    /**
     * List the indexes that have been created on a dataset. We
     * should have at least one on _id by default.
     *
     * @param string $name  The dataset that we should list indexes on.
     * @return array|bool  Returns the indexes on success, false on failure.
     */

    public function listIndexes($name) {
        $url = $this->host . "/datasets/" . $name . "/indexes";
        $data = $this->sendRequest($url, self::GET);
        $json = $this->parseJson($data);

        if(!$json) {
            return false;
        }

        return $json['data']['indexes'];
    }

    /**
     * Remove an index from the specified dataset, which works on a single
     * field and will return a bool.
     *
     * @param string $name  The dataset to remove an index on.
     * @param string $field  The field to remove the index on.
     * @return bool  Returns true or false.
     */

    public function removeIndex($name, $field) {
        $postData = array(
            "field" => $field
        );

        $url = $this->host . "/datasets/" . $name . "/indexes";
        $data = $this->sendRequest($url, self::DELETE, $postData);
        $json = $this->parseJson($data);

        if(!$json) {
            return false;
        }

        return $json['data']['removed'];
    }

    /**
     * Create an index on the specified dataset, which currently
     * only works on a single field.
     *
     * @param string $name  The dataset to create an index on.
     * @param string $field  The field to index. A blank field will cause auto-indexing.
     * @return array|bool  Returns the data array on success, false on failure.
     */

    public function addIndex($name, $field = null) {
        $postData = array(
            "field" => $field
        );

        $url = $this->host . "/datasets/" . $name . "/indexes";
        $data = $this->sendRequest($url, self::POST, $postData);
        $json = $this->parseJson($data);

        if(!$json) {
            return false;
        }

        return !empty($field) ?
            $json['data']['added'] : $json['data']['detected'];
    }

    /**
     * Get the dataset's access list, which requires read access to view. Here
     * we can see which usernames have been given access to read and write from
     * and to the dataset.
     *
     * @param string $name  The dataset name.
     * @return array|bool  Returns the data array on success, false on failure.
     */

    public function listAccess($name) {
        $url = $this->host . "/datasets/" . $name . "/access";
        $data = $this->sendRequest($url, self::GET);
        $json = $this->parseJson($data);

        if(!$json) {
            return false;
        }

        return $json['data'];
    }

    /**
     * Give a user access to the specified dataset, which requires you to
     * send the type and the username in a POST request.
     *
     * @param string $name  The dataset to create an index on.
     * @param string $username  The username to give access to.
     * @param string|array $type  The type of access to give, "read" and "write".
     * @return array|bool  Returns the data array on success, false on failure.
     */

    public function giveAccess($name, $username, $type) {
        $postData = array(
            "username" => $username,
            "type" => is_array($type) ? json_encode($type) : $type
        );

        $url = $this->host . "/datasets/" . $name . "/access";
        $data = $this->sendRequest($url, self::POST, $postData);
        $json = $this->parseJson($data);

        if(!$json) {
            return false;
        }

        return $json['data']['added'];
    }

    /**
     * Remove a user's access from the specified dataset, which requires
     * you to have write access to the set.
     *
     * @param string $name  The dataset to create an index on.
     * @param string $username  The username to remove access from.
     * @param string|null $type  The type of access to give, "read" and "write", or null for both.
     * @return array|bool  Returns the data array on success, false on failure.
     */

    public function removeAccess($name, $username, $type = null) {
        $postData = array(
            "username" => $username,
            "type" => $type
        );

        $url = $this->host . "/datasets/" . $name . "/access";
        $data = $this->sendRequest($url, self::DELETE, $postData);
        $json = $this->parseJson($data);

        if(!$json) {
            return false;
        }

        return $json['data']['removed'];
    }

    /**
     * List the constraints currently applied to a dataset. These constraints
     * are run on the data being returned from any endpoint that shows dataset
     * data.
     *
     * @param string $name  The dataset to list the constraints on.
     * @return array|bool  Returns the constraints on success, false on failure.
     */

    public function listConstraints($name) {
        $url = $this->host . "/datasets/" . $name . "/constraints";
        $data = $this->sendRequest($url, self::GET);
        $json = $this->parseJson($data);

        if(!$json) {
            return false;
        }

        return $json['data']['constraints'];
    }

    /**
     * Add constraints to fields within a dataset. If the field name and type
     * are both left blank then the system will automatically determine the
     * type of each field and apply the constraints itself.
     *
     * @param string $name  The dataset to apply the constraints to.
     * @param string $field  The field to constrain.
     * @param string $type  The type to constrain to.
     * @return mixed  Returns the constraints on success, false on failure.
     */

    public function addConstraint($name, $field = null, $type = null) {
        $postData = array(
            "field" => $field,
            "type" => $type
        );

        $url = $this->host . "/datasets/" . $name . "/constraints";
        $data = $this->sendRequest($url, self::POST, $postData);
        $json = $this->parseJson($data);

        if(!$json) {
            return false;
        }

        return !empty($field) && !empty($type) ?
            $json['data']['added'] : $json['data']['detected'];
    }

    /**
     * Remove a constraint from a field, which requires that the above has
     * already been run on a field (or multiple).
     *
     * @param string $name  The dataset to remove the constraints from.
     * @param string $field  The field to unconstrain.
     * @return bool  Returns true or false.
     */

    public function removeConstraint($name, $field) {
        $postData = array(
            "field" => $field
        );

        $url = $this->host . "/datasets/" . $name . "/constraints";
        $data = $this->sendRequest($url, self::DELETE, $postData);
        $json = $this->parseJson($data);

        if(!$json) {
            return false;
        }

        return $json['data']['removed'];
    }

    /**
     * Calculate the linear regression of two fields of data inside a
     * dataset, to the n-th degree.
     *
     * @param string $name  The dataset that we run calculations on.
     * @param array $fields  The two fields to use.
     * @param int $degree  The degree of the polynomial.
     * @return array|bool  Returns the coefficients on success, false on failure.
     */

    public function calcPolyfit($name, $fields, $degree = 2) {
        $query_string = array(
            "fields" => json_encode($fields),
            "degree" => $degree
        );

        $url = $this->host . "/datasets/" . $name . "/calc/polyfit";
        $data = $this->sendRequest($url, self::GET, $query_string);
        $json = $this->parseJson($data);

        if(!$json) {
            return false;
        }

        return $json['data']['coefficients'];
    }

    /**
     * Calculate some statistics about a set of data, which can be
     * limited to a subset of data in a dataset using a query or the
     * entire table.
     *
     * @param string $name  The dataset that we run calculations on.
     * @param string $field  The field to run the calculations on.
     * @param array $query  The query to run, in MongoDB format.
     * @return array|bool  Returns the stats on success, false on failure.
     */

    public function calcStats($name, $field, $query = array()) {
        $query_string = array(
            "field" => $field,
            "query" => $query
        );

        $url = $this->host . "/datasets/" . $name . "/calc/stats";
        $data = $this->sendRequest($url, self::GET, $query_string);
        $json = $this->parseJson($data);

        if(!$json) {
            return false;
        }

        return $json['data'];
    }

    /**
     * Return the last known error code, which can be populated from
     * the API or a failed cURL request.
     *
     * @return integer  The error code.
     */

    public function errno() {
        return $this->errno;
    }

    /**
     * Return the last known error message, which can be populated
     * from the API or a failed cURL request.
     *
     * @return string  The error message.
     */

    public function error() {
        return $this->error;
    }

    /**
     * Private function to parse JSON into an array, also setting
     * the error code and message in the class if necessary.
     *
     * @param string $data  The JSON string.
     * @return array|bool  The decoded JSON data on success, or false on failure.
     */

    private function parseJson($data) {
        $json = json_decode($data, true);

        if(!is_array($json)) {
            $this->errno = 402;
            $this->error = "Invalid JSON received from API.";

            return false;
        }

        if($json['meta']['code'] !== 200) {
            $this->errno = $json['meta']['code'];
            $this->error = $json['data']['message'];

            return false;
        }

        return $json;
    }

    /**
     * Private function to make a cURL request to the API using two
     * different methods to send the data - POST and GET.
     *
     * @param string $url  The web address to fetch.
     * @param string $method  GET, POST, PUT or DELETE.
     * @param string $params  The query parameters or POST parameters.
     * @param integer $timeout  The timeout to use for the request.
     * @return string  The response data from the request.
     */

    private function sendRequest($url, $method, $params = null, $timeout = 10) {
        $ch = curl_init();

        if($method == self::GET && !empty($params)) {
            $url .= "?" . http_build_query($params);
        }

        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_USERAGENT, "Rainhawk / PHP Wrapper 1.0");
        curl_setopt($ch, CURLOPT_HTTPHEADER, array("X-Mashape-Authorization: " . $this->mashapeKey));
        curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);

        if($method == self::POST) {
            curl_setopt($ch, CURLOPT_POST, true);

            if(!empty($params)) {
                curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($params));
            }
        } else if($method == self::PUT || $method == self::DELETE) {
            curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method);

            if(!empty($params)) {
                curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($params));
            }
        } else if($method == self::PUT_FILE) {
            curl_setopt($ch, CURLOPT_PUT, true);

            curl_setopt($ch, CURLOPT_INFILE, $params['fp']);
            curl_setopt($ch, CURLOPT_INFILESIZE, $params['size']);
        }

        $result = curl_exec($ch);

        if(!$result) {
            $this->errno = curl_errno($ch);
            $this->error = curl_error($ch);
        }

        curl_close($ch);

        return $result;
    }
}

?><file_sep>#include <iostream>
#include <functional>
#include <fstream>
#include <string>
#include <sstream>
#include <vector>
#include <cstring>
#include "unzip.h"
#include "sadUtils.hpp"
#include "sadReader.hpp"

using namespace std;

const char* SHARED_STRINGS_PATH = "xl/sharedStrings.xml";

////////////////////////////////////////////////////////////////////////////////
////////  CELL MEMBER FUNCTIONS
////////////////////////////////////////////////////////////////////////////////

cell::cell()
{
  jType= NULLVALUE;
}

cell::cell( std::string newString )
{
  setValue( newString );
}

cell::cell( long double newNumber )
{
  setValue( newNumber );
}

cell::cell( bool newBool )
{
  setValue( newBool );
}

void cell::setValue( std::string input )
{
  jType = STRING;
  strval = input;
}

void cell::setValue( long double input )
{
  jType = NUMBER;
  numval = input;
}

void cell::setValue( bool input )
{
  jType = BOOL;
  boolval = input;
}

JType cell::getType()
{
  return jType;
}

string cell::getString()
{
  if( jType == STRING ) return strval;
  return "";
}

long double cell::getNumber()
{
  if( jType == NUMBER ) return numval;
  return 0;
}

bool cell::getBool()
{
  if( jType == BOOL ) return boolval;
  return false;
}

////////////////////////////////////////////////////////////////////////////////
////////  XMLNODE MEMBER FUNCTIONS
////////////////////////////////////////////////////////////////////////////////

xmlNode::xmlNode( string input )
{
  name = input;
  attC = 0;
  childC = 0;
  contentC = 0;
}

void xmlNode::addAttribute( string attribute, string value )
{
  attC++;
  attributes.push_back( attribute );
  values.push_back( value );
}

void xmlNode::addChild( xmlNode* child )
{
  childC++;
  children.push_back( child );
}

void xmlNode::addContent( string input )
{
  contentC++;
  content.push_back( input );
}

string xmlNode::getName()
{
  return name;
}

vector<string> xmlNode::getAttribute( size_t index )
{
  vector<string> attribute;
  if( index >= 0 && index < attC )
  {
    attribute.push_back( attributes[index] );
    attribute.push_back( values[index] );
  }
  return attribute;
}

size_t xmlNode::attributeCount()
{
  return attC;
}

xmlNode* xmlNode::getChild( size_t index )
{
  if( index >= 0 && index < childC ) return children[index];
  return 0;
}

size_t xmlNode::childCount()
{
  return childC;
}

std::string xmlNode::getContent( size_t index )
{
  if( index >= 0 && index < contentC ) return content[index];
  return "";
}

size_t xmlNode::contentCount()
{
  return contentC;
}

////////////////////////////////////////////////////////////////////////////////
////////  SHEET MEMBER FUNCTIONS
////////////////////////////////////////////////////////////////////////////////

sheet::sheet( size_t rows, size_t columns, string name ) : rowc(rows),
  colc(columns)
{
  sName = name;
  cell blank;
  vector<cell> column( colc, blank );
  contents.insert( contents.end(), rowc, column );
}

size_t sheet::rows()
{
  return rowc;
}

size_t sheet::cols()
{
  return colc;
}

string sheet::name()
{
  return sName;
}

////////////////////////////////////////////////////////////////////////////////
////////  CSV FILE READING
////////////////////////////////////////////////////////////////////////////////

// Section ends with cc = " and ifs.peek() != " regardless of the
// length/meaning of the quote.
void skipQuotes( long unsigned &pos, istream &ifs, unsigned &error )
{
  char cc;
  cc = ifs.get(); pos++;
  if( cc != '\"' )
  {
    while( ifs.good() && cc != '\"' )
    {
      cc = ifs.get(); pos++;
      if( cc == '\"' && ifs.peek() == '\"' )
      {
        ifs.get(); pos++; cc = ifs.get(); pos++;
      }
    }
    if( ifs.eof() )
    {
      error = 2;
      return;
    }
    if( ifs.fail() )
    {
      error = 1;
      return;
    }
  }
}

void writeCell( sheet &target, size_t row, size_t col, string record )
{
  if( record == "" ) return;
  long double number;
  if( convertData( record, number ) )
  {
    target[row][col].setValue( number );
    return;
  }
  bool boolean;
  if( convertBool( record, boolean ) )
  {
    target[row][col].setValue( boolean );
    return;
  }
  target[row][col].setValue( record );
}

void csvMarkers( vector<long unsigned> &commas, vector<long unsigned> &newls,
    istream &ifs, unsigned &error )
{
  long unsigned pos = 0;
  char cc = ifs.get();
  while( ifs.good() )
  {
    if( cc == '\"' ) skipQuotes( pos, ifs, error );
    if( error != 0 ) return;
    if( cc == ',' ) commas.push_back( pos );
    if( cc == '\n' || cc == '\r' )
    {
      newls.push_back( pos );
      if( cc == '\r' && ifs.peek() == '\n' ) ifs.get();
    }
    cc = ifs.get();
    pos++;
  }
  if( ifs.bad() ) error = 1;
}

void csvDimensions( size_t &rows, size_t &cols,
    const vector<long unsigned> &commas, const vector<long unsigned> &newls )
{
  size_t max = newls.size();
  size_t commaPos = 0;
  size_t newlPos = 0;
  long unsigned nextComma = ( commas.size() > 0 ) ? commas[0] : -1;
  long unsigned nextNewl = ( newls.size() > 0 ) ? newls[0] : -1;
  size_t dimensionPos = 0;
  vector<size_t> dimensions(1,1);
  while( newlPos < max )
  {
    nextNewl = ( newls.size() > newlPos ) ? newls[newlPos] : -1;
    while( nextComma < nextNewl )
    {
      dimensions[dimensionPos]++;
      commaPos++;
      nextComma = ( commas.size() > commaPos ) ? commas[commaPos] : -1;
    }
    dimensions.push_back(1);
    dimensionPos++;
    newlPos++;
  }
  rows = dimensions.size();
  cols = 0;
  for( vector<size_t>::iterator it = dimensions.begin();
      it != dimensions.end(); it++ )
  {
    if( *it > cols ) cols = *it;
  }
}

void csvData( vector<long unsigned> &commas, vector<long unsigned> &newls,
    sheet &result, size_t rows, size_t cols, istream &ifs, unsigned &error )
{
  long unsigned pos = 0;
  size_t commaPos = 0;
  size_t newlPos = 0;
  long unsigned nextComma = ( commas.size() > 0 ) ? commas[0] : -1;
  long unsigned nextNewl = ( newls.size() > 0 ) ? newls[0] : -1;
  size_t row = 0;
  size_t col = 0;
  while( ifs.good() )
  {
    col = 0;
    while( nextComma < nextNewl )
    {
      if( ifs.peek() == '\n' ) ifs.get();
      string record;
      while( pos < nextComma )
      {
        char cc = ifs.get(); pos++;
        if( cc == '\"' )
        {
          cc = ifs.get(); pos++;
        }
        record += cc;
      }
      writeCell( result, row, col, record );
      ifs.get(); pos++;
      col++;
      commaPos++;
      nextComma = ( commas.size() > commaPos ) ? commas[commaPos] : -1;
    }
    string record;
    while( pos < nextNewl && !ifs.eof() )
    {
      char cc = ifs.get(); pos++;
      if( cc == '\"' )
      {
        cc = ifs.get(); pos++;
      }
      if( ifs.good() ) record += cc;
    }
    writeCell( result, row, col, record );
    ifs.get(); pos++;
    while( col+1 < cols )
    {
      col++;
      writeCell( result, row, col, "" );
    }
    row++;
    newlPos++;
    nextNewl = ( newls.size() > newlPos ) ? newls[newlPos] : -1;
  }
  if( ifs.bad() ) error = 1;
}

sheet readCSV( string filename, unsigned &error )
{
  error = 0;
  ifstream ifs( filename );
  sheet fail( 0, 0 );
  vector<long unsigned> commas;
  vector<long unsigned> newls;
  csvMarkers( commas, newls, ifs, error );
  if( error != 0 ) return fail;
  size_t rows;
  size_t cols;
  csvDimensions( rows, cols, commas, newls );
  string name = filename.substr( 0, filename.find_last_of('.') );
  sheet result( rows, cols, name );
  ifs.close();
  ifs.open( filename );
  csvData( commas, newls, result, rows, cols, ifs, error );
  if( error != 0 ) return fail;
  return result;
}

////////////////////////////////////////////////////////////////////////////////
////////  XML FILE READING
////////////////////////////////////////////////////////////////////////////////

// WARNING: Currently there is no support for non-ascii (i.e. full unicode )
// characters, beware!

// Returns true if c is a valid start to an xml name
bool xmlNameStart( char c )
{
  if( (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == ':' || c == '_' )
  {
    return true;
  }
  return false;
}

bool xmlName( char c )
{
  if( xmlNameStart(c) || c == '-' || c == '.' || (c >= '0' && c <= '9') )
  {
    return true;
  }
  return false;
}

// Progresses the stream one xml text character forward, while skipping over
// literal escape strings
// Returns the xml text character
char xmlChar( istream &ifs, unsigned &error )
{
  error = 0;
  char cc;
  try
  {
    cc = ifs.get();
    if( cc == '&' )
    {
      string text;
      cc = ifs.get();
      while( cc != ';' )
      {
        text += cc;
        if( text.size() > 4 )
        {
          error = 2;
          return cc;
        }
        cc = ifs.get();
      }
      if( text == "lt" ) return '<';
      else if( text == "gt" ) return '>';
      else if( text == "amp" ) return '&';
      else if( text == "apos" ) return '\'';
      else if( text == "quot" ) return '\"';
      else
      {
        error = 2;
        return cc;
      }
    }
    return cc;
  }
  catch( istream::failure e )
  {
    if( ifs.fail() ) error = 1;
    else error = 3;
    ifs.clear();
  }
  return cc;
}

// Takes a stream whose next character is the first character of the content
// section of a tag, and sets the value of "text" to the content.
// Note: Takes the stream to the start of the next tag (last character is '<')
void xmlContent( string &text, istream &ifs, unsigned &error )
{
  //cout << "content" << '\n';
  char cc;
  string input;
  while( ifs.peek() != '<' )
  {
    cc = xmlChar( ifs, error );
    if( error != 0 ) return;
    input += cc;
  }
  text = input;
  ifs.get();
}

// Takes a stream whose next character is the first character of an attribute
// name, and sets the value of "name" and "value" to the attribute values.
// Note: Takes stream to the end of any following whitespace.
// Extra note: Exception mask of stream should be set to catch all errors
void xmlAttribute( string &name, string &value, istream &ifs,
    unsigned &error )
{
  //cout << "attribute" << '\n';
  char delim;
  char cc;
  value = "";
  cc = ifs.get();
  while( xmlName(cc) )
  {
    name += cc;
    cc = ifs.get();
  }
  while( whitespace(cc) ) cc = ifs.get();
  if( cc != '=' )
  {
    error = 2;
    return;
  }
  cc = ifs.get();
  while( whitespace(cc) ) cc = ifs.get();
  if( cc != '"' && cc != '\'' )
  {
    error = 2;
    return;
  }
  delim = cc;
  cc = ifs.get();
  while( cc != delim )
  {
    value += cc;
    cc = ifs.get();
  }
  while( whitespace(ifs.peek()) ) cc = ifs.get();
  error = 0;
}

void xmlTagName( string &name, istream &ifs, unsigned &error )
{
  //cout << "tagname" << '\n';
  string input;
  input += ifs.get();
  while( xmlName(ifs.peek()) ) input += ifs.get();
  name = input;
  while( whitespace(ifs.peek()) ) ifs.get();
  if( !ifs.good() )
  {
    if( ifs.eof() ) error = 3;
    else error = 1;
  }
}

// Assumed position of stream at time of calling:
// <tag-name abc...
//           ^
// (i.e. the first ifs.get() would return 'a')
// 
// Guaranteed position at end of call:
// xyz> abc...
//      ^
// (i.e. the next ifs.get() would return 'a')
//
// This is a fairly basic parser that only handles tags, content, and
// attributes. Fortunately, that's all that -should- be required. In the event
// that unaccounted-for input is encountered, the error code will be set
// accordingly.
// Error codes:
// 0: Success
// 1: File I/O error
// 2: Unparsable file
// 3: EOF reached early

void xmlTag( xmlNode *result, istream &ifs, unsigned &error )
{
  //cout << "tag" << '\n';
  error = 0;
  char cc;
  // Read tag internals 
  while( xmlNameStart(ifs.peek()) )
  {
    string name;
    string value;
    xmlAttribute( name, value, ifs, error );
    result->addAttribute( name, value );
    if( error != 0 ) return;
  }
  cc = ifs.get();
  if( cc != '/' && cc != '>' )
  {
    error = 2;
    return;
  }
  if( cc == '/' )
  {
    cc = ifs.get();
    //cout << "empty: " << cc << "->" << (char)ifs.peek() << '\n';
    return;
  }
  string text;
  // Obtain content, including contained tags
  while( true )
  {
    xmlContent( text, ifs, error );
    result->addContent( text );
    if( error != 0 ) return;
    if( ifs.peek() == '/' )
    {
      cc = ifs.get();
      while( cc != '>' ) cc = ifs.get();
      while( whitespace(ifs.peek()) ) ifs.get();
      return;
    }
    xmlTagName( text, ifs, error );
    if( error != 0 ) return;
    xmlNode* child = new xmlNode( text );
    xmlTag( child, ifs, error );
    if( error != 0 ) return;
    result->addChild( child );
  }
}

void xmlMetaTag( xmlNode *result, istream &ifs, unsigned &error )
{
  //cout << "metatag" << '\n';
  try
  {
    error = 0;
    char cc;
    // Read tag internals 
    while( xmlNameStart(ifs.peek()) )
    {
      string name;
      string value;
      xmlAttribute( name, value, ifs, error );
      result->addAttribute( name, value );
      if( error != 0 ) return;
    }
    cc = ifs.get();
    if( cc != '?' || ifs.peek() != '>' )
    {
      error = 2;
      return;
    }
    ifs.get();
    while( whitespace(ifs.peek()) ) ifs.get();
  }
  catch( istream::failure e )
  {
    if( ifs.fail() ) error = 1;
    else error = 3;
    ifs.clear();
  }
}

void xmlDocument( xmlNode *result, string &content, unsigned &error )
{
  //cout << "document" << '\n';
  char cc;
  istringstream ifs( content );
  while( !ifs.eof() && whitespace(ifs.peek())  ) ifs.get();
  while( !ifs.eof() )
  {
    cc = ifs.get();
    if( cc != '<' )
    {
      error = 2;
      return;
    }
    string name;
    xmlNode* child;
    if( ifs.peek() == '?' )
    {
      ifs.get();
      xmlTagName( name, ifs, error );
      if( error != 0 ) return;
      child = new xmlNode( name );
      xmlMetaTag( child, ifs, error ); 
      if( error != 0 ) return;
    }
    else
    {
      xmlTagName( name, ifs, error );
      if( error != 0 ) return;
      child = new xmlNode( name );
      xmlTag( child, ifs, error ); 
      if( error != 0 ) return;
    }
    result->addChild( child );
    while( !ifs.eof() && whitespace(ifs.peek())  ) ifs.get();
  }
  error = 0;
}

void printXMLTree( xmlNode* root, ostream &out, unsigned ind = 0 )
{
  out << tab(ind) << root->getName() << ":" << '\n';
  ind++;
  for( size_t it = 0; it < root->attributeCount(); it++ )
  {
    vector<string> attribute = root->getAttribute(it);
    out << tab(ind) << attribute[0] << "=""" << attribute[1] << """" << '\n';
  }
  string content = root->getContent(0);
  if( content != "" ) out << tab(ind) << content << '\n';
  for( size_t it = 0; it < root->childCount(); it++ )
  {
    xmlNode* child = root->getChild(it);
    printXMLTree( child, out, ind );
    // Due to the way nodes are formatted, there should always be n children and
    // n+1 contents
    content = root->getContent(it+1);
    if( content != "" ) out << tab(ind) << content << '\n';
  }
}

// Returns true if the current file of "zipFile" begins with "title". This is
// used both for identifying files in a directory as well as exact files.
// Sets title to the full file name and path
bool fileMatch( unzFile uzf, string &title, unsigned &error )
{
  if( unzOpenCurrentFile( uzf ) == UNZ_OK )
  {
    unz_file_info uzfInfo;
    memset( &uzfInfo, 0, sizeof( unz_file_info ) );
    if( unzGetCurrentFileInfo( uzf,
      &uzfInfo,
      NULL, 0,
      NULL, 0,
      NULL, 0 ) == UNZ_OK )
    {
      string uzfName;
      uzfName.resize( uzfInfo.size_filename );
      unzGetCurrentFileInfo( uzf,
        &uzfInfo,
        &uzfName[0], uzfInfo.size_filename,
        NULL, 0,
        NULL, 0 );
      unzCloseCurrentFile( uzf );
      string uzfNameCut = uzfName.substr( 0, title.size() );
      if( uzfNameCut == title )
      {
        title = uzfName;
        return true;
      }
      return false;
    }
  }
  error = 1;
  return false;
}

//Returns a list of the filenames of every file in the archive
vector<string> getArchiveFiles( unzFile uzf, unsigned &error )
{
  vector<string> result;
  int fileAccessStatus = unzGoToFirstFile( uzf );
  while( fileAccessStatus == UNZ_OK )
  {
    if( unzOpenCurrentFile( uzf ) == UNZ_OK )
    {
      unz_file_info uzfInfo;
      memset( &uzfInfo, 0, sizeof( unz_file_info ) );
      if( unzGetCurrentFileInfo( uzf,
        &uzfInfo,
        NULL, 0,
        NULL, 0,
        NULL, 0 ) == UNZ_OK )
      {
        string uzfName;
        uzfName.resize( uzfInfo.size_filename );
        unzGetCurrentFileInfo( uzf,
          &uzfInfo,
          &uzfName[0], uzfInfo.size_filename,
          NULL, 0,
          NULL, 0 );
        unzCloseCurrentFile( uzf );
        result.push_back( uzfName );
      }
    }
    else
    {
      error = 1;
      return result;
    }
    fileAccessStatus = unzGoToNextFile( uzf );
  }
  return result;
}

string loadArchiveFile( unzFile uzf, string filename, unsigned &error )
{
  int fileAccessStatus = unzGoToFirstFile( uzf );
  while( fileAccessStatus == UNZ_OK )
  {
    if( fileMatch( uzf, filename, error ) )
    {
      if( unzOpenCurrentFile( uzf ) == UNZ_OK )
      {
        unz_file_info archiveFileInfo; 
        memset( &archiveFileInfo, 0, sizeof( unz_file_info ) );
        if( unzGetCurrentFileInfo( uzf,
          &archiveFileInfo,
          NULL, 0,
          NULL, 0,
          NULL, 0 ) == UNZ_OK )
        {
          string archiveFileContents;
          uLong archiveFileSize = archiveFileInfo.uncompressed_size;
          unsigned long maxBufferSize = -1;
          if( archiveFileSize <= maxBufferSize )
          {
            archiveFileContents.resize( archiveFileSize );
            voidp archiveFileBuffer = &archiveFileContents[0];
            if( unzReadCurrentFile( uzf, archiveFileBuffer, archiveFileSize ) )
            {
              return archiveFileContents;
            }
          }
        }
      }
      error = 1;
      return "";
    }
    if( error != 0 ) return "";
    fileAccessStatus = unzGoToNextFile( uzf );
  }
  error = 2;
  return "";
}

vector<string> loadSharedStrings( xmlNode* ss )
{
  vector<string> result;
  xmlNode* parent = ss->getChild(1);
  size_t childC = parent->childCount();
  for( size_t it = 0; it < childC; it++ )
  {
    xmlNode* sharedString = parent->getChild(it)->getChild(0);
    result.push_back( sharedString->getContent(0) );
  }
  return result;
}

void decodeDimension( string dimension, unsigned &x, unsigned &y )
{
  int width = 0;
  size_t lcount = 0;
  while( dimension[lcount] >= 'A' && dimension[lcount] <= 'Z' ) lcount++;
  string letters = dimension.substr( 0, lcount );
  int value;
  for( size_t count = 0; count < lcount; count++ )
  {
    width *= 26;
    value = letters[count] + 1 - 'A';
    width += value;
  }
  string numbers = dimension.substr( lcount );
  x = width;
  convertData( numbers, y );
}

sheet loadWorksheet( string title, xmlNode* ws, const vector<string> &ss )
{
  xmlNode* top = ws->getChild(1);
  size_t child = 0;
  xmlNode* bottom = top->getChild(0);
  while( bottom->getName() != "dimension" )
  {
    child++;
    bottom = top->getChild(child);
  }
  vector<string> dimensions = bottom->getAttribute(0);
  unsigned cols; unsigned rows;
  decodeDimension( dimensions[1].substr( dimensions[1].find(':') + 1),
      cols, rows );
  sheet result( rows, cols, title );
  while( bottom->getName() != "sheetData" )
  {
    child++;
    bottom = top->getChild(child);
  }
  for( size_t row = 0; row < bottom->childCount(); row++ )
  {
    xmlNode* rowHead = bottom->getChild(row);
    for( size_t col = 0; col < rowHead->childCount(); col++ )
    {
      xmlNode* colHead = rowHead->getChild(col);
      unsigned cellX; unsigned cellY;
      decodeDimension( colHead->getAttribute(0)[1], cellX, cellY );
      cellX--; cellY--;
      string typeString = colHead->getAttribute(2)[1];
      string value = colHead->getChild(0)->getContent(0);
      if( typeString == "s" )
      {
          size_t index;
          convertData( value, index );
          writeCell( result, cellY, cellX, ss[index] );
      }
      else writeCell( result, cellY, cellX, value );
    }
  }
  return result;
}

vector<sheet> readXLSX( string filename, unsigned &error )
{
  vector<sheet> result;
  unzFile uzf = unzOpen( filename.c_str() );
  if( uzf )
  {
    vector<string> commonStrings;
    string sharedStringsContent = loadArchiveFile( uzf, "xl/sharedStrings.xml",
        error );
    if( error != 0 ) return result;
    xmlNode* sharedStringsXML = new xmlNode( "sharedstrings" );
    xmlDocument( sharedStringsXML, sharedStringsContent, error );
    if( error != 0 ) return result;
    vector<string> sharedStringList = loadSharedStrings( sharedStringsXML );
    if( error != 0 ) return result;
    string worksheetPath = "xl/worksheets/";
    vector<string> archiveFiles = getArchiveFiles( uzf, error );
    if( error != 0 ) return result;
    for( vector<string>::iterator it = archiveFiles.begin();
        it != archiveFiles.end(); it++ )
    {
      string filepath = it->substr( 0, it->find_last_of('/')+1 );
      if( filepath == worksheetPath )
      {
        string contents = loadArchiveFile( uzf, *it, error );
        if( error != 0 ) return result;
        xmlNode* worksheetXML = new xmlNode( "worksheet" );
        xmlDocument( worksheetXML, contents, error );
        if( error != 0 ) return result;
        string title = filename.substr( filename.find_last_of('/')+1 );
        title = title.substr( 0, title.find_last_of('.') );
        sheet worksheet = loadWorksheet( title, worksheetXML, sharedStringList );
        result.push_back( worksheet );
      }
    }
    return result;
  }
  error = 1;
  return result;
}

////////////////////////////////////////////////////////////////////////////////
////////  CORE FUNCTIONS
////////////////////////////////////////////////////////////////////////////////

FileType getFileType( string filename )
{
  size_t pos = filename.find_last_of( '.' ); 
  if( pos == string::npos ) return UNDEF;
  string file = toUpper( filename.substr( pos + 1 ) );
  if( file.compare( "CSV" ) == 0 ) return CSV;
  if( file.compare( "ODS" ) == 0 ) return ODS;
  if( file.compare( "XLSX" ) == 0 ) return XLSX;
  return UNDEF;
}

vector<sheet> readFile( string filename, unsigned &error )
{
  FileType filetype = getFileType( filename );
  vector<sheet> result;
  switch( filetype )
  {
    case CSV:
      result.push_back( readCSV( filename, error ) );
      return result;
      break;
    case XLSX:
      return readXLSX( filename, error );
      break;
    case ODS:
    default:
      error = 1;
      return result;
      break;
  }
}

////////////////////////////////////////////////////////////////////////////////
////////  TESTING
////////////////////////////////////////////////////////////////////////////////

// void testUtilities( ostream &out, unsigned &testsRun, unsigned &testsPassed,
//    unsigned ind = 0 )
// {
//   testsRun = 0;
//   testsPassed = 0;
//   //Begin testing
//   out << tab(ind) << "Initiating Utility Function Testing Protocols:" << '\n';
//   ind++;
//   function<int(int)> fAdd = add;
//   testFunction<int,int>( fAdd, "add", out, ind );
//   function<string(string)> fToUpper = toUpper;
//   testsRun++;
//   if( testFunction( fToUpper, "toUpper", out, ind ) ) testsPassed++;
//   ind--;
//   //End of tests
// }
// 
// void testCore( ostream &out, unsigned &testsRun, unsigned &testsPassed,
//    unsigned ind = 0 )
// {
//   testsRun = 0;
//   testsPassed = 0;
//   //Begin testing
//   out << tab(ind) << "Initiating Core Function Testing Protocols:" << '\n';
//   ind++;
//   function<FileType(string)> fGetFileType = getFileType;
//   testsRun++;
//   if( testFunction( fGetFileType, "getFileType", out, ind ) ) testsPassed++;
//   //End of tests
//   ind--;
// }
// 

void printSheet( sheet sh, ostream &out, unsigned ind )
{
  size_t rows = sh.rows();
  size_t cols = sh.cols();
  for( size_t row = 0; row < rows; row++ )
  {
    out << tab(ind);
    for( size_t col = 0; col < cols; col++ )
    {
      JType celltype = sh[row][col].getType();
      switch( celltype )
      {
        case STRING:
          out << "[S:" << sh[row][col].getString() << "]";
          break;
        case BOOL:
          out << "[B:" << sh[row][col].getBool() << "]";
          break;
        case NUMBER:
          out << "[N:" << sh[row][col].getNumber() << "]";
          break;
        default:
          out << "[NULL]";
          break;
      }
    }
    out << '\n';
  }
}

<file_sep><?php

include("includes/kernel.php");
include("includes/api/core.php");

/*!
 * Check if the user provided enough information to create the new
 * dataset, including the name and description.
 */

if(empty($data->prefix) || empty($data->name)) {
    echo json_beautify(json_render_error(401, "You didn't pass one or more of the required parameters."));
    exit;
}

/*!
 * Check to see if the dataset exists, and that we have access to it.
 * We need to use the prefix and the name of the dataset to get a
 * reference to it.
 */

// Create a new dataset object.
$dataset = new rainhawk\dataset($data->prefix, $data->name);

// Check that the dataset exists.
if(!$dataset->exists) {
    echo json_beautify(json_render_error(402, "The dataset you specified does not exist."));
    exit;
}

// Check that we can read from the dataset.
if(!$dataset->have_write_access(app::$username)) {
    echo json_beautify(json_render_error(403, "You don't have access to write to this dataset."));
    exit;
}

/*!
 * Define an empty array to store the results of whatever we need
 * to send back.
 */

$json = array();

/*!
 * Try and remove the index from the dataset provided the field has already
 * been specified.
 */

// Check if the field is set.
if(empty($data->field)) {
    echo json_beautify(json_render_error(404, "You didn't specify the field to remove the index on."));
    exit;
}

// Check if the _id field has been speciifed.
if($data->field == "_id") {
    echo json_beautify(json_render_error(405, "You can't remove the index on the _id field."));
    exit;
}

// Get the current indexes.
$indx = $dataset->fetch_indexes();
$indexes = array();

// Set the indexes properly.
foreach($indx as $index) {
    $indexes[] = array_keys($index['key'])[0];
}

// Check if the index has already been set.
if(!in_array($data->field, $indexes)) {
    echo json_beautify(json_render_error(406, "The field you specified does not have an index."));
    exit;
}

// Remove the index.
if(!$dataset->remove_index($data->field)) {
    echo json_beautify(json_render_error(407, "There was an unknown problem removing the index you specified."));
    exit;
}

// Set the output.
$json['removed'] = true;

/*!
 * Output our JSON payload for use in whatever needs to be using
 * it.
 */

echo json_beautify(json_render(200, $json));
exit;

?><file_sep># -*- mode: ruby -*-
# vi: set ft=ruby :

Vagrant.configure("2") do |config|
    config.vm.box = "debian_squeeze_amd64"
    config.vm.box_url = "http://ergonlogic.com/files/boxes/debian-current.box"

    config.vm.define :rainhawk do |t|
    end

    config.vm.network :forwarded_port, guest: 80, host: 8080
    config.vm.network :forwarded_port, guest: 443, host: 8443
    config.vm.network :forwarded_port, guest: 27017, host: 27017

    config.vm.provider :virtualbox do |v|
        v.customize ["modifyvm", :id, "--memory", "2048"]
    end

    config.vm.provision :puppet do |puppet|
        puppet.manifests_path = ".puppet/manifests"
        puppet.manifest_file = "main.pp"
        puppet.module_path = ".puppet/modules"
        puppet.options = ["--verbose"]
    end
end
<file_sep>'use strict';

eco.charts.d3piechart = function() {

	return {
		title: 'Pie Chart',

		options : {
			width : 1400,
			height : 700,
			margin : {
				top: 30,
				left: 80
			}
		},

		render: function(data, xValue, yValue, target) {
			var options = this.options;

			var width = options.width,
					height = options.height,
					maxRadius = Math.min(width, height)/2;

			var color = d3.scale.category20b();
			
			//holds whether an element is being viewed
			var viewToggle = false;

			var arc = d3.svg.arc()
				.outerRadius(maxRadius)
				.innerRadius(0);

			var pie = d3.layout.pie()
				.sort(null)
				.value(function(d) { return d[yValue]; });

			var svg = target.append("svg")
					.attr("width", width + options.margin.top)
					.attr("height", height + options.margin.left)
					.attr("class", "pie-chart")
				.append("g")
					.attr("transform", "translate(" 
					    + ((width / 2) + options.margin.left) 
					    + "," 
					    + ((height / 2) + options.margin.top) 
					    + ")");

			data.forEach(function(d) {
				d[yValue] = +d[yValue];
			});

			var g = svg.selectAll(".pie-chart-arc")
					.data(pie(data))
				.enter().append("g")
					.attr("class", "pie-chart-arc");

			g.append("path")
				.attr("d", arc)
				.style("fill", function(d) { return color(d.data[xValue]); })
				.attr("opacity", 0.9)
				.on("mouseover", mouseover)
				.on("mouseout", mouseout)
				.on("click", mouseclick);

			g.append("text")
				.attr("transform", function(d) 
				{   
                    d.outerRadius = maxRadius;
                    d.innerRadius = maxRadius/2;
                    return "translate(" + arc.centroid(d) + ")rotate(" + angle(d) + ")";
                })
				.attr("dy", ".35em")
				.style("text-anchor", "middle")
				.text(function(d) { return d.data[xValue]; });
			
			function mouseover(d)
			{
			    d3.selectAll("[class=pie-header-text]").remove();
                
                svg.append("g")
					.append("text")
					.attr("x", 25)
					.attr("y", 50)
					.attr("class", "pie-header-text")
					.attr("fill", "#483D8B")
					.attr("transform", "translate(-" 
					    + (width / 2 + options.margin.left) 
					    + ",-" 
					    + (height / 2 + options.margin.top) 
					    + ")")
                    .text(d["data"][xValue] + ": " + d["data"][yValue]);
                
                d3.select(this)
                    .transition()
                    .duration(100)
                    .attr("opacity", 1);
			}
			
			function mouseout(d)
            {
                if (!viewToggle)
                {
                    d3.selectAll("path")
                        .transition()
                        .duration(200)
                        .attr("opacity", 0.9);
                }
            }
            
            function mouseclick(d)
            {
                //select all but the selected element
                var selectedElement = this;
                d3.selectAll("path")
                    .filter(function(d) 
                    {
                        return (this !== selectedElement);
                    })
                    .transition()
                    .duration(150)
                    .attr("opacity", 0.4);
                    
                viewToggle = !viewToggle;
                if (!viewToggle) mouseout(d);
            }
				
		    // Calculates the arc angle then converts from radians to degrees.
            function angle(d) 
            {
              var a = (d.startAngle + d.endAngle) * 90 / Math.PI - 90;
              return a > 90 ? a - 180 : a;
            }
		}
	}
}
<file_sep>#ifndef FILEREADER
#define FILEREADER

#include <string>
#include <vector>
using namespace std;

enum FileType { CSV, XLSX, UNDEF, ODS };
enum JType { STRING, NUMBER, OBJECT, ARRAY, BOOL, NULLVALUE };

//TODO: Fix value storage; only data of one type (string, number, and
//boolean) needs to be stored for any given instantiation, making it
//inefficient to allocate memory for all of them at once
class sheetNode
{
  public:
    sheetNode();
    sheetNode( string newString );
    sheetNode( long double newNumber );
    sheetNode( bool newBool );
    JType getType();
    string getString();
    long double getNumber();
    bool getBool();
  private:
    JType jType;
    string strval;
    long double numval;
    bool boolval;
};

typedef struct
{
  string name;
  vector< vector<sheetNode> > contents;
} page;
 
vector< page > getFile( string fileName );
string pageString( page spreadsheet );

#endif
<file_sep>CXX = g++
CPPFLAGS = -std=c++0x -g -Wall -Iunzip
ZPATH = unzip
PARSE_SRCS = sadTables.cpp sadReader.cpp sadUtils.cpp sadWriter.cpp sadParser.cpp
PARSE_OBJS = $(PARSE_SRCS:%.cpp=%.o)
ABS_UNZ_OBJS = miniunz.o unzip.o ioapi.o libz.a
UNZ_OBJS = $(ABS_UNZ_OBJS:%=$(ZPATH)/%)

default: sadparser

sadparser: $(PARSE_OBJS) $(UNZ_OBJS)
	$(CXX) $(CPPFLAGS) -o $@ $(PARSE_OBJS) $(UNZ_OBJS)

$(PARSE_OBJS): %.o: %.cpp
	$(CXX) $(CPPFLAGS) -c $<

$(UNZ_OBJS):
	cd unzip && $(MAKE)	

clean: 
	$(RM) sadparser *.o *~
	cd unzip && $(MAKE) clean
	$(RM) examples/*.json
<file_sep><?php

use Rainhawk\Sets;
use Rainhawk\Dataset;

class RainhawkSetsTest extends UnitTest {
    public $class = "Rainhawk\Sets";

    public function __before() {
        app::$username = "test";
        rainhawk::select_database("tests");

        return $this;
    }

    public function testCreate() {
        $dataset = new Dataset(app::$username, "testset");
        $dataset->description = "Test dataset.";
        $dataset->read_access[] = app::$username;
        $dataset->write_access[] = app::$username;

        $this->assertEquals(sets::create($dataset), true);
    }

    public function testUpdate() {
        $dataset = new Dataset(app::$username, "testset");
        $this->assertEquals(sets::update($dataset), true);
    }

    public function testExists() {
        $this->assertEquals(sets::exists(app::$username, "testset"), true);
    }

    public function testFetchMetadata() {
        $metadata = sets::fetch_metadata(app::$username, "testset");
        $description = $metadata['description'];

        $this->assertEquals($description, "Test dataset.");
    }

    public function testSetsForUser() {
        $sets = sets::sets_for_user(app::$username);
        $datasets = array();

        foreach($sets as $set) {
            $datasets[] = $set;
        }

        $this->assertEquals($datasets[0]['description'], "Test dataset.");
    }

    public function testRemove() {
        $dataset = new Dataset(app::$username, "testset");
        $this->assertEquals(sets::remove($dataset), true);
    }
}

?><file_sep>'use strict';

eco.charts.d3linegraph = function() {
    return {
        title: 'Steam Graph',
        options : {
            width : 1500,
            height : 700,
            margin : {
                top: 100,
                left: 50,
                bottom: 50,
                right: 50
            }
        },

        render: function(data, xValue, yValue, target) {
            //store data before wrapping
            var dd = data;
            //wrap the data
            data = [{"values":data}]; 
            var options = this.options;
            
            var width = options.width,
                height = options.height;
                
            var margin = options.margin;

            var yScale = d3.scale.linear()
                .domain([0, d3.max(dd, function(d) { return +d[yValue]; })])
                .range([height-margin.bottom, margin.top]);
            
            var yAxis = d3.svg.axis()
                .scale(yScale)
                .orient("left")
                .ticks(20);
            
            var yAxisR = d3.svg.axis()
                .scale(yScale)
                .orient("right")
                .ticks(20);
                
            var xScale = d3.scale.linear()
                .domain([0,d3.max(dd, function(d) { return +d[xValue]; })]) 
                .range([margin.left,width-margin.right]);

            var xAxis = d3.svg.axis()
                .scale(xScale)
                .orient("bottom");
                
            var stack = d3.layout.stack()
                .offset("zero")
                .values(function(d) { return d.values; });
                
            var colorScale = d3.scale.category20b();
            
            var area = d3.svg.area()
                .x(function(d) { return xScale(d[xValue]); })
                .y0(function(d) { return yScale(d.y0); })
                .y1(function(d) { return yScale(d.y0 + d[yValue]); });
                
            var svg = target.append("svg")
                .attr("width", width)
                .attr("height", height);
            
            //draw horizontal lines
            svg.selectAll("horizontalGrid")
                .data(yScale.ticks(20))
                .enter()
            .append("line")
                .attr(
                {
                    "x1" : margin.left,
                    "x2" : width - margin.right,
                    "y1" : function(d){ return yScale(d);},
                    "y2" : function(d){ return yScale(d);},
                    "shape-rendering" : "crispEdges",
                    "stroke" : "grey",
                    "stroke-width" : "1px"
                });
            
            svg.selectAll("path")
                .data(stack(data))
            .enter().append("path")
                .attr("class", "layer")
                .attr("d", function(d) { return area(d.values); })
                .style("fill", function() { return colorScale(Math.random())});
            
            //left axis  
            svg.append("g")
                .attr("class", "y axis")
                .call(yAxis)
                .attr("transform", "translate("+margin.left+",0)")
            .append("text")
                .attr("transform", "rotate(-90)")
                .attr("y", -40)
                .attr("x", -height/2)
                .attr("dy", ".21em")
                .style("text-anchor", "end")
                .style("font", "12px Helvetica")
                .text(yValue);
                
            //right axis
            svg.append("g")
                .attr("class", "y axis")
                .call(yAxisR)
                .attr("transform", "translate("+(width-margin.right)+",0)")
            
            svg.append("g")
                .attr("class", "x axis")
                .call(xAxis)
                .attr("transform", "translate(0,"+(height-margin.bottom)+")")
            .append("text")
                .attr("transform", "translate("+(width/2)+",35)")
                .style("text-anchor", "end")
                .style("font", "12px Helvetica")
                .text(xValue);
                
            d3.select("path")
                .on("mousemove", mousemove)
                .on("click", mouseclick);
            
            function mousemove()
            {
                var posX = (d3.mouse(this)[0] - margin.left - 1);
                var posY = (height+margin.top - 151) - d3.mouse(this)[1];
                
                var xmouseScale = d3.scale.linear()
                .range([0, d3.max(dd, function(d) { return +d[xValue]; })])
                .domain([0, width-margin.left-margin.right]);
                
                var ymouseScale = d3.scale.linear()
                .range([0, d3.max(dd, function(d) { return +d[yValue]; })])
                .domain([0, height-margin.top-margin.bottom-3]);
                
                d3.selectAll("[class=steam-text]").remove();
                svg.append("g")
                    .append("text")
                    .attr("x", 25)
                    .attr("y", 50)
                    .attr("class", "steam-text")
                    .attr("fill", "#483D8B")
                    .text(xValue + ": " + Math.round(xmouseScale(posX))
                        + ", " 
                        + yValue + ": " + Math.round(ymouseScale(posY)));
            };

            function mouseclick()
            {
                d3.select(this)
                    .style("fill", function() { return colorScale(Math.random())});
            };
            
        }
    }
}
<file_sep>'use strict';

/* controllers */

// to do: Get data from our api, make it all faster

angular.module('eco.controllers', [])
.controller('ecoCtrl', function ($scope, $http, dataService) {

	/*
		TO DO: If one option is null or two or more are the same throw error.

		When Visualize button is pressed, check that all parameters are != null.
	*/

	// selected radio button
	$scope.selectedVizType = {id:1};

	// has the user requested to visualise a certain dataset?
	if (window.location.search == '') {
		$scope.selectedDataset = '';
	} 
	else {
		$scope.selectedDataset = window.location.search.slice(1).split('=')[1];
	}

	// the fields for the selected dataset
	$scope.fields = [];

	// current user's api key
	$scope.apiKey = apiKey;

	// reinitialize $scope.vizTypes here
	$scope.getFields = function() {
		$http({
			method: 'GET',
			url: 'https://sneeza-eco.p.mashape.com/datasets/'+$scope.selectedDataset+'/data',
			headers: {
				'X-Mashape-Authorization' : $scope.apiKey
			}
		}).
		success(function(json) {
			$scope.fields = [];
			if(json.data["rows"] != undefined) {
				$.each(json.data["results"][0], function(key, val) {
					if (key!='_id') {
						$scope.fields.push(key);
					}
				});
			}
			else {
				return null;
			};
		})
	}

	// (don't have this be a function so it only runs once)
	$scope.getDatasetNames = function() {
		// todo: get user's API key from cookie
		$http({
			method: 'GET',
			url: 'https://sneeza-eco.p.mashape.com/datasets',
			headers: {
				'X-Mashape-Authorization' : $scope.apiKey
			}
		}).
		success(function(json) {
			// attach the data to the scope
			$scope.datasets = [];
			$.each(json.data.datasets, function(key,val) {
				// var datasetName = val.name.split('.')[1];
				var datasetName = val.name;
				$scope.datasets.push(datasetName);
			});

			// clean the error messages
			$scope.error = '';
		}).
		error(function (data, status) {
			if (status === 404) {
				$scope.error = 'Datasets not found';
			} else {
				$scope.error = 'Error: ' + status;
			}
		});
	};

	// check to see that no parameter is empty
	$scope.checkParameters = function() {
		// if ($scope.valid == false) {
		// 	alert("Invalid options");
		// }
		$scope.validParams = true;
		for (var field in $scope.vizTypes[$scope.selectedVizType.id].options)
		{
			if ($scope.vizTypes[$scope.selectedVizType.id].options[field] === null) {
				$scope.validParams = false;
				console.log("Invalid parameters!");
				return;
			}
		}
	};

	// send visualization options to the service and vis type.
	// Do this when the 'visualize' is clicked but in the future, 
	// validate the options made accordingly.
	$scope.sendVizOptionsToService = function() {
		console.log('Sending options to service!');
		dataService.selectedVizType = $scope.selectedVizType.id;
		dataService.vizOptions = $scope.vizTypes;
	}

	// $scope.validParams = true;

	// get the datasets immediately
	$scope.getDatasetNames();

	// when a new dataset is selected from the dropdown get its fields
	$scope.$watch('selectedDataset', function() {
		if ($scope.selectedDataset != '')
		{
			$scope.getFields();
			dataService.selectedDataset = $scope.selectedDataset;
			dataService.getSelectedDataset();
			$scope.currentData = dataService.getData($scope.selectedDataset, $scope.apiKey);
		}
		
		// reinitialize parameter options
		$scope.vizTypes = eco.charts();
	});

	$scope.$watch('selectedVizType.id', function() {
		$scope.validParams = true;
	});

	$scope.$watch('vizTypes', function() {
		$scope.checkParameters();
	})

});<file_sep>#ifndef FILE_READER
#define FILE_READER

#include <string>
#include <iostream>
#include <vector>

extern const char* SHARED_STRINGS_PATH;
const long unsigned MAX_BUFFER_SIZE = 65536;

enum FileType : unsigned {
  UNDEF = 0,
  CSV = 1,
  XLSX = 2,
  ODS = 3
};

enum JType : unsigned {
  NULLVALUE = 0,
  STRING = 1,
  NUMBER = 2,
  BOOL = 3,
  OBJECT = 4,
  ARRAY = 5
};

class xmlNode
{
  public:
    xmlNode( std::string input );
    void addAttribute( std::string attribute, std::string value );
    void addChild( xmlNode* child );
    void addContent( std::string input );
    size_t attributeCount();
    size_t childCount();
    size_t contentCount();
    std::vector<std::string> getAttribute( size_t index );
    xmlNode* getChild( size_t index );
    std::string getContent( size_t index );
    std::string getName();
  private:
    std::string name;
    size_t attC;
    std::vector<std::string> attributes;
    std::vector<std::string> values;
    size_t childC;
    std::vector<xmlNode*> children;
    size_t contentC;
    std::vector<std::string> content;
};


class cell
{
  public:
    cell();
    cell( std::string newString );
    cell( long double newNumber );
    cell( bool newBool );
    void setValue( std::string input );
    void setValue( long double input );
    void setValue( bool input );
    JType getType();
    std::string getString();
    long double getNumber();
    bool getBool();
  private:
    JType jType;
    std::string strval;
    long double numval;
    bool boolval;
};

// Access elements with sheet[x][y], where x is the row and y is the col
class sheet
{
  public:
    sheet( size_t rows, size_t columns, std::string name = "sheet" );
    std::vector<cell>& operator [](size_t idx) { return contents[idx]; }
    const std::vector<cell>& operator [](size_t idx) const
    {
      return contents[idx];
    }
    size_t rows();
    size_t cols();
    std::string name();
  private:
    std::string sName;
    size_t rowc;
    size_t colc;
    std::vector< std::vector<cell> > contents;
};

std::vector<sheet> readFile( std::string filename, unsigned &error );
void printSheet( sheet sh, std::ostream &out, unsigned ind = 0 );

#endif
<file_sep><?php

include("includes/kernel.php");
include("includes/api/core.php");

/*!
 * Check that the parameters have all been set and sent to the script,
 * including the prefix and the name.
 */

if(empty($data->prefix) || empty($data->name)) {
    echo json_beautify(json_render_error(401, "You didn't pass one or more of the required parameters."));
    exit;
}

/*!
 * Check to see if the dataset exists, and that we have access to it.
 * We need to use the prefix and the name of the dataset to get a
 * reference to it.
 */

// Create a new dataset object.
$dataset = new rainhawk\dataset($data->prefix, $data->name);

// Check that the dataset exists.
if(!$dataset->exists) {
    echo json_beautify(json_render_error(402, "The dataset you specified does not exist."));
    exit;
}

// Check that we can read from the dataset.
if(!$dataset->have_write_access(app::$username)) {
    echo json_beautify(json_render_error(403, "You don't have access to write to this dataset."));
    exit;
}

/*!
 * Define our output by filling up the JSON array with the variables
 * from the dataset object.
 */

if(!$dataset->remove()) {
    echo json_beautify(json_render_error(404, "There was a problem while trying to remove this dataset."));
    exit;
}

// Set the JSON.
$json = array(
    "deleted" => true
);

/*!
 * Output our JSON payload for use in whatever needs to be using
 * it.
 */

echo json_beautify(json_render(200, $json));
exit;

?><file_sep><?php

require_once "../includes/core.php";
require_once "../includes/check_login.php";

$dataset = isset($_GET['dataset']) ? htmlspecialchars($_GET['dataset']) : null;
$access = $rainhawk->listAccess($dataset);
$readList = $access['read_access'];
$writeList = $access['write_access'];
$accessList = array_unique(array_merge($readList, $writeList));

if(!in_array($user, $writeList)) {
    header("Location: /properties.php?dataset=" . $dataset . "&nowrite");
    exit;
}

$currentUsers = (array)$_POST['currentUser'];
$newUsers = (array)$_POST['newUser'];
$errors = array();

foreach($currentUsers as $username => $access) {
    $result = "";
    $types = array();

    if(isset($access['read'])) $types[] = "read";
    if(isset($access['write'])) $types[] = "write";

    foreach($types as $type) {
        if($type == "read" && !in_array($username, $readList)) {
            $result = $rainhawk->giveAccess($dataset, $username, "read");
        } else if($type == "write" && !in_array($username, $writeList)) {
            $result = $rainhawk->giveAccess($dataset, $username, "write");
        } else {
            $result = true;
        }

        if(!$result) {
            $errors[] = $rainhawk->error();
        }
    }
}

foreach($readList as $username) {
    if(!isset($currentUsers[$username]['read'])) {
        $result = $rainhawk->removeAccess($dataset, $username, "read");

        if(!$result) {
            $errors[] = $rainhawk->error();
        }
    }
}

foreach($writeList as $username) {
    if(!isset($currentUsers[$username]['write'])) {
        $result = $rainhawk->removeAccess($dataset, $username, "write");

        if(!$result) {
            $errors[] = $rainhawk->error();
        }
    }
}

foreach($newUsers as $key => $newUser) {
    $result = "";
    $types = array();

    if(isset($newUser['read'])) $types[] = "read";
    if(isset($newUser['write'])) $types[] = "write";

    if(in_array($newUser["user"], array_keys($currentUsers))) {
        $errors[] = "User " . $newUser['user'] . " already has permissions assigned";
    } else {
        var_dump($dataset);
        var_dump($newUser['user']);
        var_dump($types);

        $result = $rainhawk->giveAccess($dataset, $newUser['user'], $types);
    }

    if(!$result) {
        $errors[] = $rainhawk->error();
    }
}

if(empty($errors)) {
    header("Location: /properties.php?dataset=" . $dataset);
    exit;
}

?><file_sep><?php

include("includes/kernel.php");
include("includes/api/core.php");

/*!
 * Check that the parameters have all been set and sent to the script,
 * including the prefix and the name.
 */

if(empty($data->prefix) || empty($data->name)) {
    echo json_beautify(json_render_error(401, "You didn't pass one or more of the required parameters."));
    exit;
}

/*!
 * Check to see if the dataset exists, and that we have access to it.
 * We need to use the prefix and the name of the dataset to get a
 * reference to it.
 */

// Create a new dataset object.
$dataset = new rainhawk\dataset($data->prefix, $data->name);

// Check that the dataset exists.
if(!$dataset->exists) {
    echo json_beautify(json_render_error(402, "The dataset you specified does not exist."));
    exit;
}

// Check that we can write to the dataset.
if(!$dataset->have_write_access(app::$username)) {
    echo json_beautify(json_render_error(403, "You don't have access to write to this dataset."));
    exit;
}

/*!
 * Define an empty array to store the results of whatever we need
 * to send back.
 */

$json = array(
    "deleted" => 0
);

/*!
 * Run the delete command directly in MongoDB, but be careful that
 * they're not accidentally sending an empty query to delete all
 * documents.
 */

// Set some local variables.
$query = !empty($data->query) ? $data->query : array();

// Change the MongoID if we have one.
foreach($query as $key => $value) {
    if($key == "_id") {
        try {
            $mongoid = new MongoID($value);
        } catch(Exception $e) {
            $mongoid = null;
        }

        $query[$key] = $mongoid;
    }
}

// Run the delete query.
$deleted = $dataset->delete($query);

// Check if the query failed.
if(!is_int($deleted)) {
    echo json_beautify(json_render_error(405, "An unexpected error occured while performing your query - are you sure you formatted all the parameters correctly?"));
    exit;
}

// Set the JSON.
$json['deleted'] = $deleted;

/*!
 * Output our JSON payload for use in whatever needs to be using
 * it.
 */

echo json_beautify(json_render(200, $json));
exit;

?><file_sep><?php

/*!
 * This class is a very, very simple OO style wrapper for the
 * cURL extension for PHP. It also offers a quick-access
 * function to fetch a URL in one-line.
 */

class cURL {
    // Define a global variable that can be overridden.
    public static $timeout = 6;
    public static $user_agent = null;

    /*!
     * Pass all of the functions on with an OO style to the
     * base extension below.
     */

    public static function init() {
        return curl_init();
    }

    public static function setopt($ch, $type, $value) {
        return curl_setopt($ch, $type, $value);
    }

    public static function getinfo($ch, $type) {
        return curl_getinfo($ch, $type);
    }

    public static function exec($ch) {
        return curl_exec($ch);
    }

    public static function errno($ch) {
        return curl_errno($ch);
    }

    public static function error($ch) {
        return curl_error($ch);
    }

    public static function close($ch) {
        return curl_close($ch);
    }

    /*!
     * Quick access function to fetch a URL with an optional
     * timeout, which overrides the class 'timeout' variable.
     * We also set a user agent with the version string provided
     * by the app.
     */

    public static function get($url, $timeout = null, $headers = array()) {
        $ch = self::init();

        self::setopt($ch, CURLOPT_URL, $url);
        self::setopt($ch, CURLOPT_USERAGENT, self::$user_agent);
        self::setopt($ch, CURLOPT_TIMEOUT, ($timeout) ? $timeout : self::$timeout);
        self::setopt($ch, CURLOPT_HTTPHEADER, $headers);
        self::setopt($ch, CURLOPT_FOLLOWLOCATION, true);
        self::setopt($ch, CURLOPT_RETURNTRANSFER, true);
        self::setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
        // self::setopt($ch, CURLOPT_ENCODING, "gzip");

        $result = self::exec($ch);
        self::close($ch);

        return $result;
    }

    /*!
     * Quick access function to fetch a URL with an optional
     * timeout, which overrides the class 'timeout' variable.
     * We also set a user agent with the version string provided
     * by the app.
     */

    public static function post($url, $post_data = array(), $timeout = null, $headers = array()) {
        $ch = self::init();

        self::setopt($ch, CURLOPT_URL, $url);
        self::setopt($ch, CURLOPT_USERAGENT, self::$user_agent);
        self::setopt($ch, CURLOPT_TIMEOUT, ($timeout) ? $timeout : self::$timeout);
        self::setopt($ch, CURLOPT_HTTPHEADER, $headers);
        self::setopt($ch, CURLOPT_FOLLOWLOCATION, true);
        self::setopt($ch, CURLOPT_RETURNTRANSFER, true);
        self::setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
        self::setopt($ch, CURLOPT_POST, true);
        self::setopt($ch, CURLOPT_POSTFIELDS, $post_data);
        // self::setopt($ch, CURLOPT_ENCODING, "gzip");

        $result = self::exec($ch);
        self::close($ch);

        return $result;
    }

    /*!
     * Quick access function to perform a HEAD request on a URL
     * and send us back the last_modified header as a timestamp.
     */

    public static function get_last_modified($url, $timestamp, $timeout = null) {
        $ch = self::init();

        self::setopt($ch, CURLOPT_URL, $url);
        self::setopt($ch, CURLOPT_USERAGENT, self::$user_agent);
        self::setopt($ch, CURLOPT_TIMEOUT, ($timeout) ? $timeout : self::$timeout);
        self::setopt($ch, CURLOPT_FOLLOWLOCATION, true);
        self::setopt($ch, CURLOPT_RETURNTRANSFER, true);
        self::setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
        self::setopt($ch, CURLOPT_TIMECONDITION, CURL_TIMECOND_IFMODSINCE);
        self::setopt($ch, CURLOPT_TIMEVALUE, $timestamp);
        self::setopt($ch, CURLOPT_FILETIME, true);
        // self::setopt($ch, CURLOPT_ENCODING, "gzip");

        $result = self::exec($ch);
        $code = self::getinfo($ch, CURLINFO_HTTP_CODE);
        $last_modified = self::getinfo($ch, CURLINFO_FILETIME);

        if($code == 200) {
            $result = array(
                "last_modified" => $last_modified,
                "result" => $result
            );
        } else {
            $result = false;
        }

        self::close($ch);

        return $result;
    }
}

?><file_sep><?php

include("includes/kernel.php");
include("includes/api/core.php");

/*!
 * Check that the parameters have all been set and sent to the script,
 * including the prefix and the name.
 */

if(empty($data->prefix) || empty($data->name)) {
    echo json_beautify(json_render_error(401, "You didn't pass one or more of the required parameters."));
    exit;
}

/*!
 * Check to see if the dataset exists, and that we have access to it.
 * We need to use the prefix and the name of the dataset to get a
 * reference to it.
 */

// Create a new dataset object.
$dataset = new rainhawk\dataset($data->prefix, $data->name);

// Check that the dataset exists.
if(!$dataset->exists) {
    echo json_beautify(json_render_error(402, "The dataset you specified does not exist."));
    exit;
}

// Check that we can write to the dataset.
if(!$dataset->have_write_access(app::$username)) {
    echo json_beautify(json_render_error(403, "You don't have access to write to this dataset."));
    exit;
}

/*!
 * Define our output by filling up the JSON array with the variables
 * from the dataset object.
 */

$json = array(
    "rows" => array()
);

/*!
 * Add the specified row(s) to the Mongo collection, ignoring any
 * fields and just straight up dumping the values.
 */

$rows = array();

// Check for some actual documents.
if(!empty($data->row)) {
    $rows[] = $data->row;
} else if(!empty($data->rows)) {
    foreach($data->rows as $row) $rows[] = $row;
} else {
    echo json_beautify(json_render_error(404, "There were no rows passed to the endpoint to insert."));
    exit;
}

// Insert the rows of data into the dataset.
$rows = $dataset->insert_multi($rows);

// Check that it was a success.
if(!$rows) {
    echo json_beautify(json_render_error(404, "An unknown error occured while inserting your data into the dataset."));
    exit;
}

// Set the JSON output.
foreach($rows as $row) {
    if(isset($row['_id'])) {
        $_id = (string)$row['_id'];
        unset($row['id']);
        $row = array("_id" => $_id) + $row;
    }

    foreach($row as $field => $value) {
        if(isset($dataset->constraints[$field])) {
            $row[$field] = \rainhawk\data::check($dataset->constraints[$field]['type'], $value);
        }
    }

    $json['rows'][] = $row;
}

/*!
 * Output our JSON payload for use in whatever needs to be using
 * it.
 */

echo json_beautify(json_render(200, $json));
exit;

?><file_sep>#ifndef TABLEPROCESSOR
#define TABLEPROCESSOR

#include "JSONWriter.h"
#include "readFile.h"
#include <vector>

vector< vector<JSONObject> > processData(
  vector< vector<sheetNode> > spreadsheet );

#endif
<file_sep><?php

include("includes/kernel.php");
include("includes/api/core.php");

/*!
 * Check if the user provided enough information to upload data to
 * the dataset, which requires the dataset name and prefix.
 */

if(empty($data->prefix) || empty($data->name)) {
    echo json_beautify(json_render_error(401, "You didn't pass one or more of the required parameters."));
    exit;
}

/*!
 * Check to see if the dataset exists, and that we have access to it.
 * We need to use the prefix and the name of the dataset to get a
 * reference to it.
 */

// Create a new dataset object.
$dataset = new rainhawk\dataset($data->prefix, $data->name);

// Check that the dataset exists.
if(!$dataset->exists) {
    echo json_beautify(json_render_error(402, "The dataset you specified does not exist."));
    exit;
}

// Check that we can read from the dataset.
if(!$dataset->have_write_access(app::$username)) {
    echo json_beautify(json_render_error(403, "You don't have access to write to this dataset."));
    exit;
}

/*!
 * Fetch the input file, 8kB at a time, saving it to a temporary file so that
 * we can run it through the parser.
 */

// Check that the type has been set.
if(empty($data->type)) {
	echo json_beautify(json_render_error(404, "You specified a type that we don't support. Currently we support: csv, xlsx, ods."));
    exit;
}

// Set a temporary location for the file.
$file = "/tmp/upload_" . sprintf("%07d", rand(1, 1000000)) . "." . $data->type;
$fp = fopen($file, "w");

// Check if the user sent a file or not.
if(!$input = fopen("php://input", "r")) {
    echo json_beautify(json_render_error(405, "You didn't send any file contents to process."));
    exit;
}

// Read the data and write it to the file, 8kB at a time.
while($data = fread($input, 1024 * 8)) {
    fwrite($fp, $data);
}

// Close the file pointers.
fclose($fp);
fclose($input);

// Check if the file is empty or not.
if(filesize($file) == 0) {
	unlink($file);

	echo json_beautify(json_render_error(406, "The contents of the file you uploaded is empty."));
    exit;
}

/*!
 * Once we've uploaded the file, send it to the parser for processing.
 */

// Run the parser command.
exec("cd ../parser/ && ./sadparser '" . $file . "' 2>&1", $result);

// Check for any errors.
if(!empty($result)) {
	foreach($result as $line) {
		if(stripos($line, "invalid") !== false) {
			echo json_beautify(json_render_error(407, "There was a problem while processing your data - your data could not be read. Currently we only support: csv, xlsx."));
		    exit;
		} else if(stripos($line, "could not") !== false) {
			echo json_beautify(json_render_error(408, "There was a problem while processing your data - we seem to be having technical difficulties with our parser. Please try again later."));
		    exit;
		} else if(stripos($line, "error") !== false) {
            echo json_beautify(json_render_error(409, "There was a problem while processing your data - make sure that your file contains valid data."));
            exit;
        }
	}
}

// Remove the temporary file.
unlink($file);

// Find the generated JSON files.
$files = scandir("/tmp");
$file = pathinfo($file, PATHINFO_FILENAME);

// Iterate through the /tmp directory to find them.
foreach($files as $tmp_file) {
	if(stripos($tmp_file, $file) !== false && pathinfo($tmp_file, PATHINFO_EXTENSION) == "json") {
		$tmp_file = "/tmp/" . $tmp_file;
		$rows = json_decode(file_get_contents($tmp_file), true);
        $rows = $rows['data'] ?: [];

        // Check if the rows were empty.
        if(empty($rows)) {
            echo json_beautify(json_render_error(409, "One or more of the specified documents contained no data."));
            exit;
        }

		// Insert the rows of data into the dataset.
		$rows = $dataset->insert_multi($rows);

		// Check that it was a success.
		if(!$rows) {
		    echo json_beautify(json_render_error(409, "An unknown error occured while inserting your data into the dataset."));
		    exit;
		}

		// Set the JSON output.
		foreach($rows as $row) {
		    if(isset($row['_id'])) {
		        $_id = (string)$row['_id'];
		        unset($row['id']);
		        $row = array("_id" => $_id) + $row;
		    }

		    foreach($row as $field => $value) {
		        if(isset($dataset->constraints[$field])) {
		            $row[$field] = \rainhawk\data::check($dataset->constraints[$field]['type'], $value);
		        }
		    }

		    $json['rows'][] = $row;
		}

		// Delete JSON.
		unlink($tmp_file);
	}
}

// Check if the number of rows inserted was zero.
if(!isset($json['rows']) || empty($json['rows'])) {
	echo json_beautify(json_render_error(410, "After parsing the uploaded file we couldn't find any data."));
    exit;
}

/*!
 * Output our JSON payload for use in whatever needs to be using
 * it.
 */

echo json_beautify(json_render(200, $json));
exit;

?><file_sep>#include "JSONWriter.h"
#include <iostream>
#include <fstream>
#include <vector>
#include <string>
#include <sstream>
#include <stdexcept>

using namespace std;

/////////////////////////////////////////////////////
// JSONObject Implementation

JSONObject::JSONObject()
{
  fields = 0;
}

int JSONObject::fieldCount()
{
  return fields;
}

string JSONObject::getName( unsigned pos )
{
  if( pos >= 0 && pos < fields )
  {
    return names[pos];
  }
  else
  {
    return NULL;
  }
}

string JSONObject::getValue( unsigned pos )
{
  if( pos >= 0 && pos < fields )
  {
    return values[pos];
  }
  else
  {
    return NULL;
  }
}

void JSONObject::addPair( string name )
{
  names.push_back(name);
  //if( jasdksjdfkvj ) values.push_back("0");
  //else values.push_back("null");
  values.push_back("null");
  fields++;
}

void JSONObject::addPair( string name, long double value )
{
  names.push_back(name);
  ostringstream converter;
  converter << value;
  values.push_back(converter.str());
  fields++;
}

void JSONObject::addPair( string name, const char * value )
{
  string stringValue = value;
  addPair( name, stringValue );
}

void JSONObject::addPair( string name, string &value )
{
  names.push_back(name);
  values.push_back(value);
  fields++;
}

void JSONObject::addPair( string name, bool value )
{
  names.push_back(name);
  if( value )
  {
    values.push_back("true");
  }
  else
  {
    values.push_back("false");
  }
  fields++;
}

// End of JSONObject Implementation
/////////////////////////////////////////////////////

string JSONString( JSONObject object )
{
  string document = "{\n";
  unsigned count = 0;
  unsigned max = object.fieldCount();
  while( count < max )
  {
    document += "\t";
    document += "\"";
    document += object.getName(count);
    document += "\": ";
    string value = object.getValue(count);
    document += value;
    count++;
    if( count < max )
    {
      document += ",";
    }
    document += '\n';
  }
  document += "}";
  return document;
}

//Inputs:
//  string name:                A path and filename for the new file in which
//                              the JSON document will be stored.
//  vector<JSONObject> objects: A vector containing the JSON objects that the
//                              new document contains.
//Output: An integer representing the success of the operation. Values are:
//  0: Successful operation
//  1: File I/O error
int createJDocument( string name, vector<JSONObject> objects )
{
  ofstream ofs( name );
  if( !ofs.good() )
  {
    return 1;
  }
  ofs << "[";
  for( vector<JSONObject>::iterator it = objects.begin();
    it != objects.end();
    it++ )
  {
    if(it != objects.begin()) ofs << ",\n";
    ofs << JSONString( *it );
  }
  ofs << "]";
  ofs.close();
  return 0;
}
<file_sep>eco.charts.d3barchart = function() {

	return {
		title: 'Bar Chart',
		
		options : {
			width : 1400,
			height : 600,
			margin : {
				top: 70,
				right: 20,
				bottom: 140,
				left: 80
			}
		},

		render: function(data, xValue, yValue, target) {

			var options = this.options,
				width = options.width,
				height = options.height;

			var margin = {
				top: options.margin.top,
				bottom: options.margin.bottom,
				left: options.margin.left,
				right: options.margin.right
			};
			
			//holds whether an element is being viewed
			var viewToggle = false;

			var xCount = 0;
			for (k in data) if (data.hasOwnProperty(k)) xCount++;
			console.log(xCount);

			// count the number of rows in the dataset for xValue.
			// xCount = ...;
			var xScale = d3.scale.ordinal()
				.domain(d3.range(xCount))
				.rangeRoundBands([0, width], .1);

			// get the max value of the column for yValue.
			// yMax = ...;
			// CHANGE THIS
			var yScale = d3.scale.linear()
				.domain([0, d3.max(data, function(d) { return +d[yValue]; })])
				.range([height, 0]);

			var colorScale = d3.scale.category20b();
			
			var svg = target
				.append("svg")
					.attr("width", width + margin.left + margin.right)
					.attr("height", height + margin.top + margin.bottom)
					.attr("class", "bar-chart")
				.append("g")
					.attr("transform", "translate(" + margin.left + "," + margin.top + ")");

			// make the number of ticks customizable
			var yAxis = d3.svg.axis()
				.scale(yScale)
				.orient("left")
				.ticks(10);

			var xAxis = d3.svg.axis()
				.scale(xScale)
				.orient("bottom");

			var bar = svg.selectAll("g")
				.data(data, function(d) {
					return d[yValue];
				})
				.enter()
				.append("g");

			bar.append("rect")
				.attr
				({
				    x : function(d,i) 
				        {
					        return xScale(i);
				        },
				    y : function(d) 
				        {
					        return yScale(d[yValue]);
				        },
				    height: function(d) 
				        {
					        return height - yScale(d[yValue]);
				        },
				    width: xScale.rangeBand(),
				    fill: function(d,i) 
				        {
					        return colorScale(i);
				        },
				    opacity: 0.9
			    })
                .on
                ({
                    mouseover : mouseover,
                    mouseout : mouseout,
                    click : mouseclick
                });
				
			// add labels
			bar.append("text")
			    .attr("class", "bar-text")
				.attr("text", "middle")
				.attr("dy", ".35em")
				.attr("height", 10)
				.attr("width", 20)
				.attr('transform', function(d,i) 
				{
				    return d3.transform('translate(' 
				        + (xScale(i) + (xScale.rangeBand()/2))
				        + ',' 
				        + (height + 5)
				        + ') rotate(90)').toString();
				})
				.text(function(d) { console.log(d);return d[xValue]; });

			svg.append("g")
					.attr("class", "y axis")
					.call(yAxis)
				.append("text")
					.attr("transform", "rotate(-90)")
					.attr("y", -40)
		            .attr("x", -height/2)
					.attr("dy", ".21em")
					.style("text-anchor", "end")
					.style("font", "12px Helvetica")
					.text(yValue);

            function mouseover(d)
            {
                var xPos = d3.select(this).attr("x");
                var yPos = d3.select(this).attr("y");
                
                //text showing x and y values
                d3.selectAll("[class=bar-header-text]").remove();
                
                svg.append("g")
					.append("text")
					.attr("x", 25 - margin.left)
					.attr("y", 50 - margin.top)
					.attr("class", "bar-header-text")
					.attr("fill", "#483D8B")
                    .text(d[xValue] + ": " + d[yValue]);
                    
                d3.select(this)
                    .transition()
                    .duration(100)
                    .attr("opacity", 1);
            }
            
            function mouseout(d)
            {
                if (!viewToggle)
                {
                    d3.selectAll("rect")
                        .transition()
                        .duration(200)
                        .attr("opacity", 0.9);
                }
            }
            
            function mouseclick(d)
            {
                //select all but the selected element
                var selectedElement = this;
                d3.selectAll("rect")
                    .filter(function(d) 
                    {
                        return (this !== selectedElement);
                    })
                    .transition()
                    .duration(150)
                    .attr("opacity", 0.4);
                    
                viewToggle = !viewToggle;
                if (!viewToggle) mouseout(d);
            }

			return this;
		}
	}
};
<file_sep>CC=cc
CFLAGS=-O3

UNZ_OBJS = miniunz.o unzip.o ioapi.o libz.a

.c.o:
	$(CC) -c $(CFLAGS) $*.c

all: $(UNZ_OBJS)

%.o: %.c
	$(CC) $(CFLAGS) -c $<

clean:
	$(RM) -f *.o *~ all
<file_sep>Elgar's Coding Orchestra
=====================

Our totally unnamed project is still pretty sparse. We currently have a server set up at spe.sneeza.me, and all members of our group have an account on that server with sudo access. Your default password will be '<PASSWORD>' which you can change by running the passwd command.

Project Page
---------------------

We currently have a public facing website located at:

```
http://project.spe.sneeza.me/
```

Our API
---------------------

You can find detailed documentation about how to use the API on [Mashape](https://www.mashape.com/sneeza/project-rainhawk#!documentation). The API is free to use while it's still in active pre-release development. Mashape have many wrappers for different languages which can be used to make calls to the API using an authorization key, so they'll help you get started.

We are currently in the process of developing more complex and native wrapper classes for different languages, to interface with the API directly and take care of all the error handling for you. Please check back here for more info in the near future.

Developing With Vagrant
---------------------

In order to develop with Vagrant, you need to do the following:

1. Install [Vagrant](http://www.vagrantup.com/) and [VirtualBox](https://www.virtualbox.org/wiki/Downloads).

2. Clone this repository somewhere on your machine.

    ```bash
    git clone https://github.com/zacoppotamus/elgarscodingorchestra.git
    ```

3. Navigate into the cloned directory and start Vagrant. This may take a while to run, so you can move to the next steps while it's downloading all of the necessary files.

    ```bash
    cd elgarscodingorchestra && vagrant up
    ```

4. Add the following records to your `/etc/hosts` or `%systemroot%\system32\drivers\etc\hosts` file:

    ```bash
    127.0.0.1    rainhawk.dev    api.rainhawk.dev
    ```

5. Once step 3 has completed provisioning, navigate your browser to [rainhawk.dev:8080](http://rainhawk.dev:8080) and you'll be able to access the project website.

6. To get access to the dev machine, use the following command from anywhere in the directory. This will create an SSH tunnel into the virtual machine, where `/vagrant` is a syncronised folder to the git directory.

    ```bash
    vagrant ssh
    ```

Unit Tests + Test Cases
---------------------

There are two different types of test cases that can be run - unit tests and procedural tests. To run all of the unit tests for the API, navigate to `/api/tests` and execute:

```bash
$ php run.php suite
Rainhawk

.

OK (1 tests, 0 assertions)
0.00 seconds
Rainhawk\Data

...
.....
.......
...
```

If you wish to run a single unit test, or more than one then execute:

```bash
$ php run.php Rainhawk\Data
Rainhawk\Data

...
.....
.......
...
....
....
...
.....

OK (34 tests, 0 assertions)
0.00 seconds
```

If you wish to test the different wrappers then you can navigate to `/wrappers/{lang}/tests.x`. To run the PHP tests, execute `php tests.php` and to run the Javascript tests open the `tests.html` file in your web browser.
<file_sep><?php

include("includes/kernel.php");
include("includes/api/core.php");

/*!
 * Check if the user provided enough information to create the new
 * dataset, including the name and description.
 */

if(empty($data->name) || empty($data->description)) {
    echo json_beautify(json_render_error(401, "You didn't pass one or more of the required parameters."));
    exit;
}

if(!preg_match("/^[a-zA-Z0-9\-\_]+$/", $data->name)) {
    echo json_beautify(json_render_error(402, "The dataset name you passed contains disallowed characters!"));
    exit;
}

/*!
 * Come up with a new prefix to use for the dataset, by generating
 * a 6 character string and checking that it hasn't already been
 * used. Now we can just use their username.
 */

$data->prefix = app::$username;
$dataset = new rainhawk\dataset($data->prefix, $data->name);

// Check if you already have a dataset with this name.
if($dataset->exists) {
    echo json_beautify(json_render_error(403, "You already have a dataset with this name!"));
    exit;
}

/*!
 * Now that we have our prefix and name, we can create the new
 * dataset and return the details to the user.
 */

$dataset->prefix = $data->prefix;
$dataset->name = $data->name;
$dataset->description = $data->description;

// Give this user read and write access.
$dataset->read_access[] = app::$username;
$dataset->write_access[] = app::$username;

// Perform the creation command.
if(!rainhawk\sets::create($dataset)) {
    echo json_beautify(json_render_error(404, "There was a problem while trying to create your dataset - please try again later."));
    exit;
}

// Get a new reference to the dataset.
$dataset = new rainhawk\dataset($data->prefix, $data->name);

// Create an index on _id to force-create the set.
if(!$dataset->add_index("_id")) {
    echo json_beautify(json_render_error(405, "There was a problem while trying to create your dataset - please try again later."));
    exit;
}

/*!
 * Define our output by filling up the JSON array with the variables
 * from the dataset object.
 */

$json = array(
    "name" => $dataset->prefix . "." . $dataset->name,
    "description" => $dataset->description,
    "rows" => $dataset->rows,
    "fields" => array_keys($dataset->fields),
    "constraints" => $dataset->constraints,
    "read_access" => $dataset->read_access,
    "write_access" => $dataset->write_access
);

/*!
 * Output our JSON payload for use in whatever needs to be using
 * it.
 */

echo json_beautify(json_render(200, $json));
exit;

?><file_sep>#include <vector>
#include <fstream>
#include <sstream>
#include <string>
#include "sadUtils.hpp"
#include "sadWriter.hpp"
#include "sadReader.hpp"
#include "sadTables.hpp"

using namespace std;

string jsonDocument( sheet &sh, table tbl )
{
  vector<string> fieldNames;
  string output;
  unsigned ind = 0;
  output += tab(ind) + "{\n";
  ind++;
  output += tab(ind) + "\"data\" : [\n";
  ind++;
  for( size_t col = tbl.x1; col <= tbl.x2; col++ )
  {
    fieldNames.push_back( sh[tbl.y1][col].getString() );
  }
  for( size_t row = tbl.y1+1; row <= tbl.y2; row++ )
  {
    output += tab(ind) + "{\n";
    ind++;
    for( size_t col = tbl.x1; col <= tbl.x2; col++ )
    {
      output += tab(ind) + "\"";
      output += fieldNames[col-tbl.x1];
      output += "\" : ";
      JType type = sh[row][col].getType();
      ostringstream converter;
      switch( type )
      {
        case STRING:
          output += "\"";
          output += sh[row][col].getString();
          output += "\"";
          break;
        case NUMBER:
          converter << sh[row][col].getNumber();
          output += converter.str();
          break;
        case BOOL:
          if( sh[row][col].getBool() ) output += "true";
          else output += "false";
          break;
        case NULLVALUE:
          output += "null";
          break;
        default:
          break;
      }
      if( col != tbl.x2 ) output += ',';
      output += '\n';
    }
    ind--;
    output += tab(ind) + "}";
    if( row != tbl.y2 ) output += ',';
    output += "\n";
  }
  ind--;
  output += tab(ind) + "]\n";
  ind--;
  output += tab(ind) + "}";
  return output;
}

vector<string> jsonFile( string filename, sheet &sh, vector<table> tbls )
{
  vector<string> outputs;
  for( size_t it = 0; it < tbls.size(); it++ )
  {
    string output = filename + "-" + to_string(it) + ".json";
    ofstream ofs( output.c_str() );
    ofs << jsonDocument( sh, tbls[it] );
    ofs.close();
    outputs.push_back( output );
  }
  return outputs;
}

<file_sep>eco.charts.d3bubblechart = function() {
    return {
        title : 'Bubble Chart',

        options : {
            width : 1500,
            height : 800,
            margin : {
				top: 100,
				right: 20,
				bottom: 30,
				left: 80
			}
        },

        render : function(data, xValue, yValue, maxRadius, target) {
            options = this.options;

            var width = options.width,
                height = options.height;
            
            var color = d3.scale.category20b();
    
            var maxElement = d3.max(d3.values(data),function(i){
                return +i[yValue];
            });
            var minElement = d3.min(d3.values(data),function(i){
                return +i[yValue];
            });
            
            var scale = d3.scale.linear()
                    .range([10, height/15])
                    .domain([minElement, maxElement]);
                    
            var scalingFactor = (height/10)/maxElement;
            
            var svg = target.append("svg")
                .attr("class", "bubble-chart");
            
            var force = d3.layout.force()
                .nodes(data)
                .size([width, height])
                .gravity(0.01)
                .charge(-1000/data.length)
                .start();

            var nodes = svg.selectAll(".bubble-chart-node")
                .data(force.nodes())
                .enter()
                .append("g")
                .attr("class", "bubble-chart-node")
                .on("mouseover", mouseover)
                .on("mouseout", mouseout)
                .call(force.drag);
                
            nodes.append("circle")
                .attr("r", function(data)
                {
                    return scale(data[yValue]);
                })
                .attr("fill", function(d, i) 
                {
                    return color(i);
                });
            
            force.on("tick", function()
            {
                callCollisions();
                
                svg.selectAll("circle")
                  .attr("cx", function(d) { return d.x; })
                  .attr("cy", function(d) { return d.y; });
            });
            
            svg.on("mousemove", function()
            {
                force.resume();
            });
            
            function mouseover(d)
            {
                d3.select(this)
                    .select("circle")
                    .transition()
                    .duration(150)
                    .attr("r", function(data)
                    {
                        return scale(data[yValue]) * 1.2;
                    });
                
                d3.selectAll("[class=bubble-text]").remove();
                
                svg.append("g")
			        .append("text")
			        .attr("x", 25)
			        .attr("y", 50)
			        .attr("class", "bubble-text")
			        .attr("fill", "#483D8B")
                    .text(d[xValue] + ": " + d[yValue]);
                
                callCollisions(d);
            };
            
            function mouseout()
            {
                d3.select("bubble-chart-tooltip").classed("hidden", true);
                
                d3.select(this)
                    .select("circle")
                    .transition()
                    .duration(150)
                    .attr("r", function(data)
                    {
                        return scale(data[yValue]);
                    });
            };
            
            function collide(node,ex) 
            {
                var r = scale(node[yValue]) * ex + 20,
                    nx1 = node["x"] - r,
                    nx2 = node["x"] + r,
                    ny1 = node["y"] - r,
                    ny2 = node["y"] + r;
                return function(quad, x1, y1, x2, y2) 
                {
                    if (quad.point && (quad.point !== node)) 
                    {
                        var x = node["x"] - quad.point.x,
                            y = node["y"] - quad.point.y,
                            l = Math.sqrt(x * x + y * y),
                            r = (scale(node[yValue]) + scale(quad.point[yValue])) * ex;
                        if (l < r) 
                        {
                            l = (l - r) / l * .5;
                            node["x"] -= x *= l;
                            node["y"] -= y *= l;
                            quad.point.x += x;
                            quad.point.y += y;
                        }
                    }
                    return x1 > nx2 || x2 < nx1 || y1 > ny2 || y2 < ny1;
                };
            };
            
            function callCollisions(selected)
            {
                var nn = nodes[0];
                var n = nn.length;
                var i = -1, j = -1;
                var cords = new Array();
                
                function f(d,j)
                {
                    cords[j] = d[j]["__data__"];
                };
                while (++j < n) f(nn,j);
                
                var q = d3.geom.quadtree(cords);
                while (++i < n)
                {
                    if (selected != null)
                    {
                        if (cords[i].index == selected.index)
                            q.visit(collide(cords[i],1.1))
                        else
                            q.visit(collide(cords[i],1));
                    }
                    else
                        q.visit(collide(cords[i],1));
                };
            }
        }
    }
}
<file_sep>#include <vector>
#include <string>
#include <iostream>
#include "readFile.h"
#include "JSONWriter.h"

using namespace std;

typedef struct
{
  unsigned y;
  unsigned x1;
  unsigned x2;
} header;

typedef struct
{
  unsigned x1;
  unsigned y1;
  unsigned x2;
  unsigned y2;
} table;

vector<header> detectRows( vector< vector<sheetNode> > spreadsheet )
{
  unsigned currentPos;
  unsigned stringCount;
  unsigned height = 0;
  vector<header> headers;
  header newHeader;
  for(
    vector< vector<sheetNode> >::iterator outIt = spreadsheet.begin();
    outIt != spreadsheet.end();
    outIt++
  )
  {
    currentPos = 0;
    stringCount = 0;
    for(
      vector<sheetNode>::iterator inIt = outIt->begin();
      inIt != outIt->end();
      inIt++
    )
    {
      if( inIt->getType() == STRING )
      {
        stringCount++;
      }
      else
      {
        if( stringCount > 1 )
        {
          newHeader.y = height;
          newHeader.x1 = currentPos - stringCount;
          newHeader.x2 = currentPos - 1;
          headers.push_back( newHeader );
        }
        stringCount = 0;
      }
      currentPos++;
    }
    if( stringCount > 1 )
    {
      newHeader.x2 = currentPos - 1;
      newHeader.x1 = currentPos - 1 - stringCount ;
      headers.push_back( newHeader );
    }
    height++;
  }
  return headers;
}

bool containedIn( vector<table> tables, header row )
{
  //Assumes that the row is always at the same height or lower on the
  //spreadsheet than the lowest table in tables
  //This is the case in intended use as tables are added to the list in
  //vertical descending order and the row always represents the latest table
  for( vector<table>::iterator it = tables.begin();
    it != tables.end();
    it++ )
  {
    if( ( row.x1 >= it->x1 ) && ( row.x2 <= it->x2 )  )
    {
      if( row.y <= it->y2 )
      {
        return true;
      }
    }
  }
  return false;
}

int area( table square )
{
  int size = ( square.x2 + 1 - square.x1 ) * ( square.y2 + 1 - square.y1 );
  return size;
}

void printBoolGrid( vector< vector<bool> > grid )
{
  for( vector< vector<bool> >::iterator outIt = grid.begin();
    outIt != grid.end();
    outIt++ )
  {
    for( vector<bool>::iterator inIt = outIt->begin();
      inIt != outIt->end();
      inIt++ )
    {
      cout << *inIt << " ";
    }
    cout << '\n';
  }
  cout << '\n';
}

table getTable( vector< vector<sheetNode> > spreadsheet, header row )
{
  table newTable;
  table maxTable;
  maxTable.x1 = 0;
  maxTable.x2 = 0;
  maxTable.y1 = 0;
  maxTable.y2 = 0;
  unsigned cX;
  unsigned cY;
  bool blank;
  vector< vector<bool> > checkedCells;
  vector<bool> newRow( row.x2 - row.x1 + 1, false );
  for( unsigned count = row.x1; count <= row.x2; count++ )
  {
    blank = false;
    cY = row.y;
    newTable.x1 = count;
    newTable.y1 = cY;
    while( !blank )
    {
      cY++;
      if( cY - row.y > checkedCells.size() )
      {
        checkedCells.push_back( newRow );
      }
      if( spreadsheet[cY][count].getType() == NULLVALUE )
      {
        blank = true;
      }
    }
    for( unsigned count2 = cY-1; count2 > row.y; count2-- )
    {
      cX = count;
      if( !checkedCells[count2 - row.y - 1][cX - row.x1] )
      {
        checkedCells[count2 - row.y - 1][cX - row.x1] = true;
        blank = false;
        while( !blank )
        {
          cX++;
          checkedCells[count2 - row.y - 1][cX - row.x1] = true;
          if( spreadsheet[count2][cX].getType() == NULLVALUE )
          {
            blank = true;
          }
        }
        newTable.x2 = cX-1;
        newTable.y2 = count2;
        if( area(newTable) > area(maxTable) )
        {
          maxTable = newTable;
        }
      }
    }
  }
  return maxTable;
}

bool collisionTables( table first, table second )
{
  if( first.x2 < second.x1 || first.x1 > second.x2 )
  {
    return false;
  }
  if( first.y2 < second.y1 || first.y1 > second.y2 )
  {
    return false;
  }
  return true;
}

void collisionScan( vector<table> &tables )
{
  for( vector<table>::iterator outIt = tables.begin();
    outIt != tables.end();
    outIt++ )
  {
    for( vector<table>::iterator inIt = outIt + 1;
      inIt != tables.end();
      inIt++ )
    {
      if( collisionTables( *outIt, *inIt ) )
      {
        if( area( *inIt ) <= area( *outIt ) )
        {
            inIt = tables.erase( inIt );
            inIt--;
        }
        else
        {
          outIt = tables.erase( outIt );
          outIt--;
          break;
        }
      }
    }
  }
}

vector<table> contentScan( vector< vector<sheetNode> > spreadsheet, vector<header> headers )
{
  vector<table> tables;
  table newTable;
  for( vector<header>::iterator it = headers.begin();
    it != headers.end();
    it++ )
  {
    if( !containedIn( tables, *it ) )
    {
      newTable = getTable( spreadsheet, *it );
      if( newTable.y2 - newTable.y1 > 1 )
      {
        tables.push_back( newTable );
      }
    }
  }
  return tables;
}

//Only clears spaces and tabs
string clearWhiteSpace( string target )
{
  size_t start = 0;
  size_t end = target.size() - 1;
  while( target[start] == ' ' || target[start] == '\t' ) start++;
  while( target[end] == ' ' || target[end] == '\t' ) end--;
  if( start != 0 || end != target.size() - 1 )
  {
    if( start >= end || end == string::npos ) return "";
    string result = target;
    cout << '"' << result << "\", " << start << ", " << end << '\n';
    result.resize( end + 1 );
    return result.substr( start );
  }
  return target;
}

vector<JSONObject> encodeTable(
  vector< vector<sheetNode> > spreadsheet, table data )
{
  unsigned cX;
  unsigned cY = data.y1 + 1;
  string nameval;
  string strval;
  long double numval;
  bool boolval;
  JType datatype;
  vector<JSONObject> result;
  while( cY <= data.y2 )
  {
    JSONObject next;
    cX = data.x1;
    while( cX <= data.x2 )
    {
      nameval = spreadsheet[data.y1][cX].getString();
      datatype = spreadsheet[cY][cX].getType();
      switch( datatype )
      {
        case STRING:
          strval = spreadsheet[cY][cX].getString();
          strval = clearWhiteSpace( strval );
          strval = "\"" + strval + "\"";
          next.addPair( nameval, strval );
          break;
        case NUMBER:
          numval = spreadsheet[cY][cX].getNumber();
          next.addPair( nameval, numval );
          break;
        case OBJECT:
        case ARRAY:
          break;
        case BOOL:
          boolval = spreadsheet[cY][cX].getBool();
          next.addPair( nameval, boolval );
          break;
        case NULLVALUE:
          next.addPair( nameval );
          break;
      }
      cX++;
    }
    result.push_back( next );
    cY++;
  }
  return result;
}

vector< vector<JSONObject> > encodeTables(
  vector< vector<sheetNode> > spreadsheet, vector<table> tables )
{
  vector< vector<JSONObject> > result;
  for( vector<table>::iterator it = tables.begin();
    it != tables.end();
    it++ )
  {
    vector<JSONObject> nextObject( encodeTable( spreadsheet, *it ) );
    result.push_back( nextObject );
  }
  return result;
}

vector< vector<JSONObject> > processData( vector< vector<sheetNode> > spreadsheet )
{
  //Padding the spreadsheet to prevent out-of-bounds access errors during
  //table scanning
  sheetNode nullcell;
  vector<sheetNode> nullrow( spreadsheet[0].size()+1, nullcell );
  for( vector< vector<sheetNode> >::iterator it = spreadsheet.begin();
    it != spreadsheet.end();
    it++ )
  {
    it->push_back( nullcell );
  }
  spreadsheet.push_back( nullrow );
  vector<header> initialHeaders( detectRows( spreadsheet ) );
  vector<table> initialTables( contentScan( spreadsheet, initialHeaders ) );
  collisionScan( initialTables );
  vector< vector<JSONObject> > result( encodeTables( spreadsheet, initialTables ) );
  return result;
}
<file_sep>#ifndef FILEWRITER
#define FILEWRITER

#include <vector>
#include <string>

using namespace std;

class JSONObject
{
  public:
    JSONObject();
    void addPair( string name, const char * value );
    void addPair( string name, string &value );
    void addPair( string name, long double value );
    void addPair( string name, bool value );
    void addPair( string name );
    string getName( unsigned pos );
    string getValue( unsigned pos );
    int fieldCount();
  private:
    unsigned fields;
    std::vector<string> names;
    std::vector<string> values;
};

string JSONString( JSONObject object );
int createJDocument( string name, vector<JSONObject> objects );

#endif
<file_sep><?php

/*!
 * This class allows us to connect to a network cache storage
 * to access globally shared objects. We use this for User,
 * Trade and other classes.
 */

class Redis {
    // Define some public variables for connections.
    public static $host = "127.0.0.1";
    public static $port = 6379;
    public static $enabled = false;

    // Store some stats about the class.
    public static $access_time = 0;
    public static $hits = 0;
    public static $misses = 0;
    public static $hit = array();
    public static $missed = array();

    // Define some private variables.
    private static $timeout = 6;
    private static $persistent = false;
    private static $socket = null;

    /*!
     * Connect to the Redis server using the defined host and port,
     * using fsockopen as a data stream. Optionally, accept a toggle
     * for using persistent connections.
     */

    public static function connect($host = null, $port = null, $persistent = false) {
        if($host) self::$host = $host;
        if($port) self::$port = $port;

        $timeout = self::$timeout;
        $flags = STREAM_CLIENT_CONNECT;

        if($persistent) {
            $flags |= STREAM_CLIENT_PERSISTENT;
        }

        self::$enabled = true;
        self::$persistent = $persistent;
        self::$socket = stream_socket_client(self::$host . ":" . self::$port, $errno, $error, $timeout, $flags);
    }

    /*!
     * Close the socket so that we can free up the resource. If we're
     * using persistent connections, then ignore the request.
     */

    public static function close() {
        if(self::$socket && !self::$persistent) {
            fclose(self::$socket);
        }

        return false;
    }

    /*!
     * Define a function for fetching a variable from the Redis store,
     * which stores statistics about the usage of the class.
     */

    public static function fetch($key) {
        if(!self::$socket || !self::$enabled) {
            return null;
        }

        $start = microtime(true);
        $data = self::send_command(array("GET", $key));

        if(!is_null($data)) {
            self::$hits++;
            self::$hit[] = $key;
            $data = unserialize($data);
        } else {
            self::$misses++;
            self::$missed[] = $key;
        }

        $finish = microtime(true);

        self::$access_time += $finish - $start;

        return $data;
    }

    /*!
     * Define a function for fetching multiple variables from the cache
     * in one request, reducing network overhead for simple things.
     */

    public static function fetch_multi() {
        if(!self::$socket || !self::$enabled) {
            return null;
        }

        $args = func_get_args();

        if(count($args) == 1 && is_array($args[0])) {
            $args = array_values($args[0]);
        }

        $start = microtime(true);
        $data = self::send_command(array_merge(array("MGET"), $args));
        $results = array();
        $i = 0;

        foreach($data as $d) {
            if(!is_null($d)) {
                self::$hits++;
                self::$hit[] = $args[$i];
                $d = unserialize($d);
            } else {
                self::$misses++;
                self::$missed[] = $args[$i];
            }

            $results[$args[$i]] = $d;
            $i++;
        }

        $finish = microtime(true);

        self::$access_time += $finish - $start;

        return $results;
    }

    /*!
     * Define a function for fetching a variable from the Redis store,
     * which stores statistics about the usage of the class.
     */

    public static function store($key, $data, $ttl = 0) {
        if(!self::$socket || !self::$enabled) {
            return false;
        }

        $data = serialize($data);
        $args = ($ttl == 0) ? array("SET", $key, $data) : array("SETEX", $key, $ttl, $data);

        return self::send_command($args);
    }

    /*!
     * Delete a key from the Redis cache, which is pretty useful for
     * invalidating cache records.
     */

    public static function remove($key) {
        if(!self::$enabled) {
            return false;
        }

        return self::send_command(array("DEL", $key));
    }

    /*!
     * Run a single FLUSHALL command on the Redis server to clear
     * the entire cache.
     */

    public static function flushall() {
        if(!self::$enabled) {
            return false;
        }

        return self::send_command(array("FLUSHALL"));
    }

    /*!
     * Send the command to the socket specified at the start of
     * the class.
     */

    private static function send_command($args) {
        $command = "*" . count($args) . "\r\n";

        foreach($args as $argument) {
            $command .= "$" . strlen($argument) . "\r\n";
            $command .= $argument . "\r\n";
        }

        fwrite(self::$socket, $command);

        return self::parse_response();
    }

    /*!
     * Parse the response given to us by Redis, depending on the
     * format specified.
     */

    private static function parse_response() {
        $line = fgets(self::$socket);
        list($type, $result) = array($line[0], substr($line, 1, strlen($line) - 3));

        if($type == "-") {
            return null;
        } else if($type == "$") {
            if($result == "-1") {
                return null;
            } else {
                $read = 0;
                $size = intval($result);
                $result = null;

                if($size > 0) {
                    do {
                        $block_size = ($size - $read) > 1024 ? 1024 : ($size - $read);
                        $line = fread(self::$socket, $block_size);
                        $read += strlen($line);
                        $result .= $line;
                    } while ($read < $size);
                }

                fread(self::$socket, 2);
                return $result;
            }
        } else if($type == "*") {
            $count = (int)$result;
            $result = array();

            for($i = 0; $i < $count; $i++) {
                $result[] = self::parse_response();
            }
        }

        return $result;
    }
}<file_sep><?php

// Include the necessary classes.
include("classes/app.class.php");
include("classes/curl.class.php");
include("classes/redis.class.php");
include("classes/xcache.class.php");

include("classes/rainhawk/rainhawk.class.php");
include("classes/rainhawk/dataset.class.php");
include("classes/rainhawk/sets.class.php");
include("classes/rainhawk/data.class.php");

/*!
 * Set the timezone properly.
 */

date_default_timezone_set("UTC");

/*!
 * Make a new instance of App, which is a global singleton that
 * we can use to get vital information about the application's
 * current state and some global variables. In this block, we
 * define all the required variables depending on what machine
 * we're running on.
 */

app::$development = (file_exists("/vagrant") || stripos($_SERVER['HTTP_HOST'], "dev") !== false);
app::$debug = (isset($_GET['d3bug']));
app::$maintenance = false;
app::$version = "γ";
app::$init_time = microtime(true);

app::$stack = array(
    "redis" => array(
        "host" => "127.0.0.1",
        "port" => 6379
    ),
    "mongodb" => array(
        "host" => "127.0.0.1",
        "port" => 27017,
        "database" => "eco"
    )
);

if(!app::$development) {
    app::$root_path = "/home/www/spe.sneeza.me/";
} else {
    app::$debug = true;
    app::$root_path = "/vagrant/www/";
}

if(app::$debug) {
    ini_set("display_errors", 1);
    error_reporting(E_ALL & ~E_NOTICE);
}

/*!
 * Create a new instance of the cache, depending on which deployment
 * we're on. We need one instance of xcache, which is our local
 * cache, and one instance of redis, which is our global cache.
 */

xcache::init();
redis::connect(app::$stack['redis']['host'], app::$stack['redis']['port']);

/*!
 * Set the user agent to be used in the cURL singleton.
 */

curl::$timeout = 6;
curl::$user_agent = "ECO " . app::$version . "; spe.sneeza.me;";

/*!
 * Connect to MongoDB so that we can run queries on different data
 * sets that have been imported.
 */

rainhawk::connect(app::$stack['mongodb']['host'], app::$stack['mongodb']['port']);
rainhawk::select_database(app::$stack['mongodb']['database']);

/*!
 * Check if the Mashape key has been set, and if not then use a
 * default key so we don't break direct integration with our test
 * services.
 */

app::$username = isset($_SERVER['HTTP_X_MASHAPE_USER']) ? trim(strtolower($_SERVER['HTTP_X_MASHAPE_USER'])) : "global";

?><file_sep>'use strict';

// Use shared services here as a way to share data between controllers, or between
// controller/directive

angular.module('eco.services', [])
.factory('dataService', function($http, $q) {
		var dataService = {};
		dataService.currentData = '';
		dataService.selectedDataset = '';
		dataService.selectedVizType;
		dataService.vizOptions = '';

		// get data for the selected dataset
		// TODO check for empty datasets 
		dataService.sayhey = function(name) {
			console.log('Hi, '+name);
		};

		dataService.getData = function(datasetName, apikey) {
			// this happens asynchronously
			var promise = $http({
				method: 'GET',
				url: 'https://sneeza-eco.p.mashape.com/datasets/'+datasetName+'/data',
				headers: {
					'X-Mashape-Authorization' : apikey
				}
			}).
			success(function(json) {
				console.log('Done getting data from ' + datasetName + ' and injecting to controller');
				dataService.currentData = json.data.results;
			});

			return promise;
		}

/*		dataService.getDatasetNames = function(apiKey) {
			// todo: get user's API key from cookie
			var datasets;
			var promise = $http({
				method: 'GET',
				url: 'https://sneeza-eco.p.mashape.com/datasets',
				headers: {
					'X-Mashape-Authorization' : apiKey
				}
			}).
			success(function(json) {
				// attach the data to the scope
				var datasets = [];
				console.log(json);
				$.each(json.data.datasets, function(key,val) {
					var datasetName = val.name.split('.')[1];
					dataService.datasets.push(datasetName);
				});
			});

			console.log(datasets);
			return datasets;
		};*/

		dataService.getSelectedDataset = function() {
			console.log('Selected Dataset: ' + this.selectedDataset);
			return this.selectedDataset;
		};

		dataService.getCurrentData = function() {
			console.log('Current Data: ' + this.currentData);
			return this.currentData;
		};

		dataService.getSelectedVizType = function() {
			console.log('Selected Visualisation Type is: ' + this.selectedVizType);
			return this.selectedVizType;
		};

		

		return dataService;
});<file_sep><?php

require_once "rainhawk.php";
require_once "redis.php";

// Start session handling.
session_start();

// Get the user's information.
$mashape_key = isset($_SESSION['apiKey']) ? $_SESSION['apiKey'] : null;
$user = isset($_SESSION['user']) ? $_SESSION['user'] : null;

// Set up the Rainhawk wrapper.
$rainhawk = new Rainhawk($mashape_key);

// Connect to our Redis cache.
redis::connect("127.0.0.1", 6379);

?><file_sep><?php

include("includes/kernel.php");
include("includes/api/core.php");

/*!
 * Check if the user provided enough information to create the new
 * dataset, including the name and description.
 */

if(empty($data->prefix) || empty($data->name)) {
    echo json_beautify(json_render_error(401, "You didn't pass one or more of the required parameters."));
    exit;
}

/*!
 * Check to see if the dataset exists, and that we have access to it.
 * We need to use the prefix and the name of the dataset to get a
 * reference to it.
 */

// Create a new dataset object.
$dataset = new rainhawk\dataset($data->prefix, $data->name);

// Check that the dataset exists.
if(!$dataset->exists) {
    echo json_beautify(json_render_error(402, "The dataset you specified does not exist."));
    exit;
}

// Check that we can read from the dataset.
if(!$dataset->have_write_access(app::$username)) {
    echo json_beautify(json_render_error(403, "You don't have access to write to this dataset."));
    exit;
}

/*!
 * Define an empty array to store the results of whatever we need
 * to send back.
 */

$json = array();

/*!
 * Try and add the access to the dataset as the user has specified,
 * which will always work even if the access already exists.
 */

// Check if the type is set.
if(empty($data->type)) {
    echo json_beautify(json_render_error(404, "You didn't specify the type of access to give."));
    exit;
}

// Check if the username is set.
if(empty($data->username)) {
    echo json_beautify(json_render_error(404, "You didn't specify the user to give access to."));
    exit;
}

// If the type is JSON, decode it into an array.
$data->type = json_decode($data->type, true) ?: $data->type;

// Iterate through the type or types set and apply them.
if(is_string($data->type)) {
    // Make sure the type is valid.
    if(!in_array($data->type, array("read", "write"))) {
        echo json_beautify(json_render_error(405, "You didn't specify a valid type of access to give."));
        exit;
    }

    // Check if the user already has that access.
    if(in_array($data->username, $dataset->{$data->type . "_access"})) {
        echo json_beautify(json_render_error(406, "The user you specified already has " . $data->type . " access to this dataset."));
        exit;
    }

    // Give the user access.
    $dataset->{$data->type . "_access"}[] = $data->username;
    $dataset->{$data->type . "_access"} = array_unique($dataset->{$data->type . "_access"});
} else {
    // Iterate through the types.
    foreach($data->type as $type) {
        // Make sure the type is valid.
        if(!in_array($type, array("read", "write"))) {
            echo json_beautify(json_render_error(405, "You didn't specify a valid type of access to give."));
            exit;
        }

        // Check if the user already has that access.
        if(in_array($data->username, $dataset->{$type . "_access"})) {
            echo json_beautify(json_render_error(406, "The user you specified already has " . $type . " access to this dataset."));
            exit;
        }

        // Give the user access.
        $dataset->{$type . "_access"}[] = $data->username;
        $dataset->{$type . "_access"} = array_unique($dataset->{$type . "_access"});
    }
}

// Store the dataset information in the index table.
\rainhawk\sets::update($dataset);

// Return the added attribute to the JSON.
$json['added'] = true;

/*!
 * Output our JSON payload for use in whatever needs to be using
 * it.
 */

echo json_beautify(json_render(200, $json));
exit;

?><file_sep>function printDatasets()
{
	$(document).ready(function() {
		$.getJSON("//api.spe.sneeza.me/datasets", function(data) {
			$.each(data.data.datasets, function(key,val) {
				// No description field yet.
				var description = val.description;
				var size = val.rows;
				var tr = $('<tr><td id="dataset"><a href="dataset.html">'+val.name+'</a></td><td>'+description+'</td><td>'+size+'</td></tr>');
				$('table').append(tr);
			})
		})
	});
}

function vizPrintDatasets()
{
	$(document).ready(function() {
		$.getJSON("//api.spe.sneeza.me/datasets", function(data) {
			$.each(data.data.datasets, function(key,val) {
				var rb = $('<div class="row"><div class="btn-group" data-toggle="buttons"><label class="btn btn-default btn-block"><input type="radio" name="options" id="option">'+val.name+'</label></div></div>');
				$('#datasets').append(rb);
			})
		})
		$(document).on('mousedown', '.btn', function() {
			$(this).button();
		})
	});
}

function printDataset()
{
	$(document).ready(function() {
		// Update to GET value
		dataset = window.location.search.slice(1);
        $.getJSON("http://api.spe.sneeza.me/datasets/"+dataset, function(result){
            $.each(result.data.fields[0], function(k, header){
                $('.table').append("<th>" + k + "</th>");
            });
            $.each(result.data.fields, function(i, row){
                var tr;
                $.each(row, function(j, col){
                    tr = tr + "<td>" + col + "</td>";
                });
                $('.table').append("<tr>" + tr + "</tr>");
	        });
        });
    });
}
<file_sep><?php

// Show errors all the time.
error_reporting(E_ALL & ~E_NOTICE);
ini_set("display_errors", 1);

// Set the header content type including charset.
include("includes/classes/route.class.php");
header("content-type: application/json; charset=utf8");
header("access-control-allow-origin: *");
header("access-control-allow-headers: *");

/*!
 * Add all of the possible routes to the class, including
 * API paths and stuff.
 */

// Set the data object.
$data = new stdClass;

// Create a main endpoint.
route::get("/", function() use($data) {
    include("main.php");
});

// Create an endpoint for the ping command.
route::get("/ping", function() use($data) {
    include("ping.php");
});

// Create an endpoint to list all available datasets.
route::get("/datasets", function() use($data) {
    include("datasets.php");
});

// Create an endpoint to create a new dataset.
route::post("/datasets", function() use($data) {
    $data->name = isset($_POST['name']) ? strtolower(trim($_POST['name'])) : null;
    $data->description = isset($_POST['description']) ? trim($_POST['description']) : null;

    include("datasets/create.php");
});

// Create an endpoint to get the info about a dataset.
route::get("/datasets/(\w+|\-+)\.(\w+|\-+)", function($prefix, $name) use($data) {
    $data->prefix = $prefix;
    $data->name = $name;

    include("datasets/info.php");
});

// Create an endpoint to get the info about a dataset.
route::delete("/datasets/(\w+|\-+)\.(\w+|\-+)", function($prefix, $name) use($data) {
    $data->prefix = $prefix;
    $data->name = $name;

    include("datasets/remove.php");
});

// Create an endpoint to perform a query on a dataset.
route::get("/datasets/(\w+|\-+)\.(\w+|\-+)/data", function($prefix, $name) use($data) {
    $data->prefix = $prefix;
    $data->name = $name;

    $data->query = isset($_GET['query']) ? json_decode($_GET['query'], true) : null;
    $data->offset = isset($_GET['offset']) && intval($_GET['offset']) >= 0 ? intval($_GET['offset']) : 0;
    $data->limit = isset($_GET['limit']) && intval($_GET['limit']) >= 1 ? intval($_GET['limit']) : null;
    $data->sort = isset($_GET['sort']) ? json_decode($_GET['sort'], true) : null;
    $data->fields = isset($_GET['fields']) ? json_decode($_GET['fields'], true) : null;
    $data->exclude = isset($_GET['exclude']) ? json_decode($_GET['exclude'], true) : null;

    include("datasets/data.php");
});

// Create an endpoint to insert new data into the dataset.
route::post("/datasets/(\w+|\-+)\.(\w+|\-+)/data", function($prefix, $name) use($data) {
    $data->prefix = $prefix;
    $data->name = $name;

    $data->row = isset($_POST['row']) ? json_decode($_POST['row'], true) : null;
    $data->rows = isset($_POST['rows']) ? json_decode($_POST['rows'], true) : null;

    include("datasets/data/insert.php");
});

// Create an endpoint to update data in the dataset.
route::put("/datasets/(\w+|\-+)\.(\w+|\-+)/data", function($prefix, $name) use($data) {
    parse_str(file_get_contents("php://input"), $_PUT);

    $data->prefix = $prefix;
    $data->name = $name;

    $data->query = isset($_PUT['query']) ? json_decode($_PUT['query'], true) : null;
    $data->changes = isset($_PUT['changes']) ? json_decode($_PUT['changes'], true) : null;

    include("datasets/data/update.php");
});

// Create an endpoint to delete data from the dataset.
route::delete("/datasets/(\w+|\-+)\.(\w+|\-+)/data", function($prefix, $name) use($data) {
    parse_str(file_get_contents("php://input"), $_DELETE);

    $data->prefix = $prefix;
    $data->name = $name;

    $data->query = isset($_DELETE['query']) ? json_decode($_DELETE['query'], true) : null;

    include("datasets/data/delete.php");
});

// Create an endpoint to insert new data into the dataset.
route::put("/datasets/(\w+|\-+)\.(\w+|\-+)/upload/(\w+|\-+)", function($prefix, $name, $type) use($data) {
    $data->prefix = $prefix;
    $data->name = $name;

    $data->type = in_array(strtolower($type), array("csv", "xlsx", "ods")) ? strtolower($type) : null;

    include("datasets/upload.php");
});

// Create an endpoint to list the constraints on a dataset.
route::get("/datasets/(\w+|\-+)\.(\w+|\-+)/constraints", function($prefix, $name) use($data) {
    $data->prefix = $prefix;
    $data->name = $name;

    include("datasets/constraints.php");
});

// Create an endpoint to add a constraint to a dataset.
route::post("/datasets/(\w+|\-+)\.(\w+|\-+)/constraints", function($prefix, $name) use($data) {
    $data->prefix = $prefix;
    $data->name = $name;

    $data->field = isset($_POST['field']) ? trim($_POST['field']) : null;
    $data->type = isset($_POST['type']) ? trim(strtolower($_POST['type'])) : null;

    include("datasets/constraints/add.php");
});

// Create an endpoint to remove a constraint on a dataset.
route::delete("/datasets/(\w+|\-+)\.(\w+|\-+)/constraints", function($prefix, $name) use($data) {
    parse_str(file_get_contents("php://input"), $_DELETE);

    $data->prefix = $prefix;
    $data->name = $name;

    $data->field = isset($_DELETE['field']) ? trim($_DELETE['field']) : null;

    include("datasets/constraints/remove.php");
});

// Create an endpoint to list the indexes on a dataset.
route::get("/datasets/(\w+|\-+)\.(\w+|\-+)/indexes", function($prefix, $name) use($data) {
    $data->prefix = $prefix;
    $data->name = $name;

    include("datasets/indexes.php");
});

// Create an endpoint to add an index to a dataset.
route::post("/datasets/(\w+|\-+)\.(\w+|\-+)/indexes", function($prefix, $name) use($data) {
    $data->prefix = $prefix;
    $data->name = $name;

    $data->field = isset($_POST['field']) ? trim($_POST['field']) : null;

    include("datasets/indexes/add.php");
});

// Create an endpoint to remove an index from a dataset.
route::delete("/datasets/(\w+|\-+)\.(\w+|\-+)/indexes", function($prefix, $name) use($data) {
    parse_str(file_get_contents("php://input"), $_DELETE);

    $data->prefix = $prefix;
    $data->name = $name;

    $data->field = isset($_DELETE['field']) ? trim($_DELETE['field']) : null;

    include("datasets/indexes/remove.php");
});

// Create an endpoint to list the access to a dataset.
route::get("/datasets/(\w+|\-+)\.(\w+|\-+)/access", function($prefix, $name) use($data) {
    $data->prefix = $prefix;
    $data->name = $name;

    include("datasets/access.php");
});

// Create an endpoint to add access to a dataset.
route::post("/datasets/(\w+|\-+)\.(\w+|\-+)/access", function($prefix, $name) use($data) {
    $data->prefix = $prefix;
    $data->name = $name;

    $data->type = isset($_POST['type']) ? $_POST['type'] : null;
    $data->username = isset($_POST['username']) ? trim(strtolower($_POST['username'])) : null;

    include("datasets/access/add.php");
});

// Create an endpoint to remove access to a dataset.
route::delete("/datasets/(\w+|\-+)\.(\w+|\-+)/access", function($prefix, $name) use($data) {
    parse_str(file_get_contents("php://input"), $_DELETE);

    $data->prefix = $prefix;
    $data->name = $name;

    $data->type = isset($_DELETE['type']) && in_array($_DELETE['type'], array("read", "write")) ? trim(strtolower($_DELETE['type'])) : null;
    $data->username = isset($_DELETE['username']) ? trim(strtolower($_DELETE['username'])) : null;

    include("datasets/access/remove.php");
});

// Create an endpoint for the polyfit calculations.
route::get("/datasets/(\w+|\-+)\.(\w+|\-+)/calc/polyfit", function($prefix, $name) use($data) {
    $data->prefix = $prefix;
    $data->name = $name;

    $data->fields = isset($_GET['fields']) ? json_decode($_GET['fields'], true) : null;
    $data->degree = isset($_GET['degree']) && $_GET['degree'] > 0 && $_GET['degree'] <= 20 ? (int)$_GET['degree'] : 2;

    include("datasets/calc/polyfit.php");
});

// Create an endpoint for the stats calculations.
route::get("/datasets/(\w+|\-+)\.(\w+|\-+)/calc/stats", function($prefix, $name) use($data) {
    $data->prefix = $prefix;
    $data->name = $name;

    $data->field = isset($_GET['field']) ? trim($_GET['field']) : null;
    $data->query = isset($_GET['query']) ? json_decode($_GET['query'], true) : null;

    include("datasets/calc/stats.php");
});

/*!
 * Perform the routing request.
 */

route::parse();

include("main.php");
exit;

?>
<file_sep><?php

include("includes/kernel.php");
include("includes/api/core.php");

/*!
 * Check if the user provided enough information to create the new
 * dataset, including the name and description.
 */

if(empty($data->prefix) || empty($data->name)) {
    echo json_beautify(json_render_error(401, "You didn't pass one or more of the required parameters."));
    exit;
}

/*!
 * Check to see if the dataset exists, and that we have access to it.
 * We need to use the prefix and the name of the dataset to get a
 * reference to it.
 */

// Create a new dataset object.
$dataset = new rainhawk\dataset($data->prefix, $data->name);

// Check that the dataset exists.
if(!$dataset->exists) {
    echo json_beautify(json_render_error(402, "The dataset you specified does not exist."));
    exit;
}

// Check that we can read from the dataset.
if(!$dataset->have_write_access(app::$username)) {
    echo json_beautify(json_render_error(403, "You don't have access to write to this dataset."));
    exit;
}

/*!
 * Define an empty array to store the results of whatever we need
 * to send back.
 */

$json = array();

/*!
 * Try and add the access to the dataset as the user has specified,
 * which will always work even if the access already exists.
 */

// Check if the field is set.
if(empty($data->field)) {
    echo json_beautify(json_render_error(404, "You didn't specify the field to remove the constraint on."));
    exit;
}

// Remove the constraint from the field.
unset($dataset->constraints[$data->field]);

// Store the dataset information in the index table.
\rainhawk\sets::update($dataset);

// Set the output.
$json['removed'] = true;

/*!
 * Output our JSON payload for use in whatever needs to be using
 * it.
 */

echo json_beautify(json_render(200, $json));
exit;

?><file_sep><?php

include("includes/kernel.php");
include("includes/api/core.php");

/*!
 * Check if the user provided enough information to create the new
 * dataset, including the name and description.
 */

if(empty($data->prefix) || empty($data->name)) {
    echo json_beautify(json_render_error(401, "You didn't pass one or more of the required parameters."));
    exit;
}

/*!
 * Check to see if the dataset exists, and that we have access to it.
 * We need to use the prefix and the name of the dataset to get a
 * reference to it.
 */

// Create a new dataset object.
$dataset = new rainhawk\dataset($data->prefix, $data->name);

// Check that the dataset exists.
if(!$dataset->exists) {
    echo json_beautify(json_render_error(402, "The dataset you specified does not exist."));
    exit;
}

// Check that we can read from the dataset.
if(!$dataset->have_read_access(app::$username)) {
    echo json_beautify(json_render_error(403, "You don't have access to read from this dataset."));
    exit;
}

/*!
 * Define an empty array to store the results of whatever we need
 * to send back.
 */

$json = array(
    "rows" => 0,
    "offset" => $data->offset,
    "results" => array()
);

/*!
 * Perform our 'find' query based on the information fed to the
 * API. We have to check if some parameters are set/valid here.
 */

$query = array();
$fields = array();

// Check if we have a query or not.
if(!empty($data->query)) {
    $query = $data->query;
}

// Check if any field names were sent.
if(!empty($data->fields)) {
    if(is_array($data->fields)) {
        foreach($data->fields as $field_name) {
            $fields[$field_name] = true;
        }

        if(!isset($fields['_id'])) {
            $fields['_id'] = false;
        }
    } else {
        echo json_beautify(json_render_error(404, "You didn't specify the field names to return correctly, they should be in the form: ['field1', 'field2']."));
        exit;
    }
}

// Check if we need to exclude fields.
if(!empty($data->exclude)) {
    if(is_array($data->exclude)) {
        foreach($data->exclude as $field_name) {
            $fields[$field_name] = false;
        }
    } else {
        echo json_beautify(json_render_error(405, "You didn't specify the field names to exclude correctly, they should be in the form: ['field1', 'field2']."));
        exit;
    }
}

// Change the MongoID if we have one.
foreach($query as $key => $value) {
    if($key == "_id") {
        try {
            $mongoid = new MongoID($value);
        } catch(Exception $e) {
            $mongoid = null;
        }

        $query[$key] = $mongoid;
    }
}

// Run the query.
$query = $dataset->find($query, $fields);

// Check if the query failed.
if(!$query) {
    echo json_beautify(json_render_error(406, "An unexpected error occured while performing your query - are you sure you formatted all the parameters correctly?"));
    exit;
}

// Get the number of rows the query matches.
$json['rows'] = $query->count();

// Sort the query using the provided query.
if(isset($data->sort)) {
    try {
        $query = $query->sort($data->sort);
    } catch(Exception $e) {}
}

// Set the offset if we have one.
if($data->offset > 0) {
    $query = $query->skip($data->offset);
}

// If we have a row limit, apply it.
if(isset($data->limit)) {
    $query = $query->limit($data->limit);
    $json['limit'] = $data->limit;
}

// Iterate through the results and populate the output.
foreach($query as $row) {
    if(isset($row['_id'])) {
        $_id = (string)$row['_id'];
        unset($row['id']);
        $row = array("_id" => $_id) + $row;
    }

    foreach($row as $field => $value) {
        if(isset($dataset->constraints[$field])) {
            $row[$field] = \rainhawk\data::check($dataset->constraints[$field]['type'], $value);
        }
    }

    $json['results'][] = $row;
}

/*!
 * Output our JSON payload for use in whatever needs to be using
 * it.
 */

echo json_beautify(json_render(200, $json));
exit;

?><file_sep><?php

include("includes/kernel.php");
include("includes/api/core.php");

/*!
 * Check that the parameters have all been set and sent to the script,
 * including the prefix and the name.
 */

if(empty($data->prefix) || empty($data->name)) {
    echo json_beautify(json_render_error(401, "You didn't pass one or more of the required parameters."));
    exit;
}

/*!
 * Check to see if the dataset exists, and that we have access to it.
 * We need to use the prefix and the name of the dataset to get a
 * reference to it.
 */

// Create a new dataset object.
$dataset = new rainhawk\dataset($data->prefix, $data->name);

// Check that the dataset exists.
if(!$dataset->exists) {
    echo json_beautify(json_render_error(402, "The dataset you specified does not exist."));
    exit;
}

// Check that we can read from the dataset.
if(!$dataset->have_read_access(app::$username)) {
    echo json_beautify(json_render_error(403, "You don't have access to read from this dataset."));
    exit;
}

/*!
 * Define our output by filling up the JSON array with the variables
 * from the dataset object.
 */

$json = array(
    "name" => $dataset->prefix . "." . $dataset->name,
    "description" => $dataset->description,
    "rows" => $dataset->rows,
    "fields" => array_keys($dataset->fields),
    "constraints" => $dataset->constraints,
    "read_access" => $dataset->read_access,
    "write_access" => $dataset->write_access
);

/*!
 * Output our JSON payload for use in whatever needs to be using
 * it.
 */

echo json_beautify(json_render(200, $json));
exit;

?><file_sep>import json                              # To convert to JSON
import sys                               # To get arguments
import warnings                          # To suppress numpy warnings
import numpy                             # To calculate correlation
from pymongo import MongoClient          # To communicate with MongoDB
warnings.simplefilter('ignore', numpy.RankWarning)

def graphEquation(xPoints, yPoints, degree):
    x = numpy.array(xPoints)
    y = numpy.array(yPoints)
    poly = numpy.polyfit(x, y, degree)
    return poly.tolist()

def query(databaseName, collectionName, xName, yName, degree):
    client     = MongoClient('spe.sneeza.me', 27017)
    db         = client[databaseName]
    collection = db[collectionName]

    xPoints = [p[xName] for p in collection.find()]
    yPoints = [p[yName] for p in collection.find()]

    equation = graphEquation(xPoints, yPoints, degree)
    print json.dumps(equation)
    return json.dumps(equation)

def main():
    if(len(sys.argv) == 6):
        return query(sys.argv[1], sys.argv[2], sys.argv[3], sys.argv[4], sys.argv[5])
    else:
        print "Usage: correlation.py databaseName collectionName xFieldName yFieldName degree"

if __name__ == "__main__":
    main()
<file_sep>New logic for our application
---

This here is going to be the new logic for the frontend part of our project, using AngularJS.

Even though its use is not needed as much in other sections of the website it is almost mandatory in the 'custom visualizations' part of the project and would greatly benefit the modularity of our application if it were also used when editing datasets (if we have time).


Dependencies
----

~~To get dependencies run ```bower install```.~~ Site is deployed automatically, so no point yet.


To-Do
----

* The most important thing: Generating a visualization, maybe in the same, maybe in a different page.
* Write a gruntfile/makefile for this and then the whole frontend.
* Think of how to implement limitations in the data fields.
* Refactor!

PS
----

I know I broke the guidelines (several of them), but I couldn't think of another way to show how the app is going to be structured from now on without putting it in a separate folder! js/,img/,css/ is so 2010 :P.
<file_sep><?php

include("includes/kernel.php");
include("includes/api/core.php");

/*!
 * Check that the parameters have all been set and sent to the script,
 * including the prefix and the name.
 */

if(empty($data->prefix) || empty($data->name)) {
    echo json_beautify(json_render_error(401, "You didn't pass one or more of the required parameters."));
    exit;
}

/*!
 * Check to see if the dataset exists, and that we have access to it.
 * We need to use the prefix and the name of the dataset to get a
 * reference to it.
 */

// Create a new dataset object.
$dataset = new rainhawk\dataset($data->prefix, $data->name);

// Check that the dataset exists.
if(!$dataset->exists) {
    echo json_beautify(json_render_error(402, "The dataset you specified does not exist."));
    exit;
}

// Check that we can read from the dataset.
if(!$dataset->have_read_access(app::$username)) {
    echo json_beautify(json_render_error(403, "You don't have access to read from this dataset."));
    exit;
}

/*!
 * Define an empty array to store the results of whatever we need
 * to send back.
 */

$json = array(
    "indexes" => array()
);

/*!
 * Fetch the indexes from the dataset, and list them back to the
 * user in a friendly format.
 */

// Get a list of indexes.
$indexes = $dataset->fetch_indexes();

// Check if the listing failed.
if(!$indexes) {
    echo json_beautify(json_render_error(404, "There was a problem while fetching the indexes."));
    exit;
}

// Return them into the JSON array.
foreach($indexes as $index) {
    $json['indexes'][] = array_keys($index['key'])[0];
}

/*!
 * Output our JSON payload for use in whatever needs to be using
 * it.
 */

echo json_beautify(json_render(200, $json));
exit;

?><file_sep><?php

require_once "../includes/core.php";
require_once "../includes/check_login.php";

$dataset = isset($_GET['dataset']) ? $_GET['dataset'] : null;
$result = $rainhawk->deleteDataset($dataset);

if($result) {
    header("Location: /datasets.php?deleted");
    exit;
} else {
    header("Location: /datasets.php?deletefailed");
    exit;
}

?>

<file_sep><?php

require_once "includes/core.php";
require_once "includes/check_login.php";

$dataset = isset($_GET['dataset']) ? htmlspecialchars($_GET['dataset']) : null;
$datasetInfo = $rainhawk->fetchDataset($dataset);

?>
<!DOCTYPE html>
<html lang="en-GB">
    <head>
        <title>Project Rainhawk - Upload Data</title>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <?php require_once "includes/meta.php"; ?>
        <style type="text/css">
            .row .form-controls {
                margin-top: 40px;
            }
        </style>
        <script>
            rainhawk.apiKey = "<?php echo $mashape_key; ?>";

            var file;

            $(function() {
                $('form').submit(uploadDataset);
                $('input[type=file]').change(prepareUpload);
            });

            function prepareUpload(event) {
                file = event.target.files[0];
            }

            function verifyDataset(name, success) {
                var url = 'https://sneeza-eco.p.mashape.com/datasets/' + name;

                $.ajax({
                    url: url,
                    type: "GET",
                    success: function(data) {
                        if(data.meta.code === 200) {
                            success();
                        } else {
                            errormsg("Dataset does not exist or you do not have write access. Try creating a dataset using the <a class='alert-link' href='create.php'>create</a> interface.")
                        }
                    },
                    error: function(data) {
                        return false;
                    },
                    beforeSend: function(xhr) {
                        xhr.setRequestHeader("X-Mashape-Authorization", "<?php echo $mashape_key; ?>");
                    }
                });
            }

            function uploadDataset(event) {
                if($('#datasetName').length) {
                    var name = $('#datasetName').val();
                } else {
                    name = '<?php echo $dataset ?>';
                }

                verifyDataset(name, function() {
                    $('#btnSubmit').button('loading');

                    var type = $('#datasetType').val();
                    var url = 'https://sneeza-eco.p.mashape.com/datasets/' + name + "/upload/" + type;

                    $.ajax({
                        url: url,
                        type: 'PUT',
                        processData: false,
                        contentType: false,
                        data: file,
                        datatype: 'json',
                        success: function(data) {
                            if(data.meta.code === 200) {
                                successmsg(name);
                            } else {
                                errormsg(data.data.message);
                            }
                        },
                        error: function(err) {
                            errormsg(JSON.stringify(err));
                        },
                        beforeSend: function(xhr) {
                            xhr.setRequestHeader("X-Mashape-Authorization", "<?php echo $mashape_key; ?>");
                        }
                    });
                });

                return false;
            }

            function errormsg(message) {
                $(".container:last").prepend(
                    "<div class='alert alert-danger fade in'>"+
                        "<strong>Error!</strong> " + message +
                        "<button type='button' class='close' data-dismiss='alert' aria-hidden='true'>&times;</button>"+
                    "</div>");

                $('#btnSubmit').button('reset');
            }

            function successmsg(name) {
                $(".container:last").prepend(
                    "<div class='alert alert-success fade in'>"+
                        "<strong>Done!</strong> Data successfully uploaded to dataset <a class='alert-link' href='/properties.php?dataset="+name+"'>"+name+"</a>. "+
                        "Now try <a class='alert-link' href='/visualise/?dataset="+name+"'>visualising</a> the data."+
                        "<button type='button' class='close' data-dismiss='alert' aria-hidden='true'>&times;</button>"+
                    "</div>");

                $('#btnSubmit').button('reset');
            }
        </script>
    </head>

    <body>
        <?php require_once "includes/nav.php"; ?>
        <div class="container">
            <div class="row">
                <div class="col-md-12">
                    <div class="panel panel-default">
                        <div class="panel-body">
                            <h1>Upload data!</h1>
                            <p>Select a file to upload into the relevant dataset...</p>
                        </div>
                    </div>
                </div>
            </div>
            <div class="row">
                <div class="col-md-12">
                    <div class="panel panel-default">
                        <div class="panel-body">
                            <form role="form">
                                <div class="form-group">
                                    <label for="datasetName">Dataset Name:</label>
                                    <?php if(isset($dataset)) { ?>
                                        <p class="form-control-static"><?php echo $dataset; ?></p>
                                    <?php } else { ?>
                                        <input type="text" class="form-control" id="datasetName" name="datasetName" placeholder='Enter your dataset name here...' required autofocus>
                                    <?php } ?>
                                </div>
                                <div class="form-group">
                                    <label for="datasetFile">Type:</label>
                                    <select class="form-control" id="datasetType">
                                        <option value="csv">csv</option>
                                        <option value="xlsx">xlsx</option>
                                        <option value="ods">ods</option>
                                    </select>
                                </div>
                                <div class="form-group">
                                    <label for="datasetFile">File</label>
                                    <input type="file" id="datasetFile" name="datasetFile">
                                </div>
                                <div class="form-controls">
                                    <button id="btnSubmit" type="submit" data-loading-text="Uploading..." class="btn btn-default">Upload</button>
                                    <a href="/datasets.php" type="button" class="btn btn-danger">Back</a>
                                </div>
                            </form>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </body>
</html><file_sep><?php

require_once "../includes/core.php";
require_once "../includes/check_login.php";

$dataset = isset($_GET['dataset']) ? htmlspecialchars($_GET['dataset']) : null;
$datasetInfo = $rainhawk->fetchDataset($dataset);
$fields = $datasetInfo['fields'];
$constraints = $datasetInfo['constraints'];
$errors = array();

if(isset($_GET['autoapply'])) {
    $result = $rainhawk->addConstraint($dataset);
} else {
    foreach($_POST['constraint'] as $field => $constraint) {
        if($constraints[$field] != $constraint) {
            if(isset($constraints[$field])) {
                $result = $rainhawk->removeConstraint($dataset, $field);

                if(isset($result['message'])) {
                    $errors[] = $result['message'];
                }
            }

            if($constraint != "none") {
                $result = $rainhawk->addConstraint($dataset, $field, $constraint);

                if(isset($result['message'])) {
                    $errors[] = $result['message'];
                }
            }
        }
    }
}

header("Location: /properties.php?dataset=" . $dataset);
exit;

?>
<file_sep><?php

require_once "../includes/core.php";
require_once "../includes/check_login.php";

header("content-type: application/json; charset=utf8");

$dataset = isset($_GET['dataset']) ? $_GET['dataset'] : null;
$query = isset($_GET['query']) ? json_decode($_GET['query']) : null;
$limit = isset($_GET['jtPageSize']) ? (int)$_GET['jtPageSize'] : null;
$offset = isset($_GET['jtStartIndex']) ? (int)$_GET['jtStartIndex'] : 0;
$fields = isset($_GET['fields']) ? json_decode($_GET['fields']) : null;
$exclude = isset($_GET['exclude']) ? json_decode($_GET['exclude']) : null;
$sort = isset($_GET['jtSorting']) ? $_GET['jtSorting'] : null;

if(isset($sort)) {
    list($field, $order) = explode(" ", $sort);
    $sort = array($field => ($order == "ASC" ? 1 : -1));
}

$records = array();
$results = $rainhawk->selectData($dataset, $query, $offset, $limit, $sort, $fields, $exclude);

if(!$results) {
    echo json_encode(array(
        "Result" => "Fail",
        "Message" => $rainhawk->error()
    ));
    exit;
}

foreach($results['results'] as $row) {
    $records[] = $row;
}

echo json_encode(array(
    "Result" => "OK",
    "Records" => $records,
    "TotalRecordCount" => $results["rows"]
));
exit;

?>
<file_sep>'use strict';

angular.module('eco', [
	'eco.controllers',
	'eco.directives',
	'eco.services',
	'ngRoute',
	'ui.bootstrap'
])

.config(function($routeProvider, $locationProvider) {
	// configure routes
	$routeProvider

		// route for the final visualization
		.when('/visualization', {
			templateUrl: 'visualization.html',
			controller: 'ecoCtrl',
		})

		// is '/' when starting server within newlogic/
		.when('/', {
			templateUrl: 'customize.html',
			controller: 'ecoCtrl'
			// resolve: {
			// 	// controller won't be instantiated before all
			// 	// dependencies are resolved
			// 	'dataService':function(dataService) {
			// 		return dataService.getData();
			// 	}
			// }
		});

	$locationProvider.html5Mode(true);
});
<file_sep><?php

// Include the Rainhawk class.
include("rainhawk.class.php");
header("content-type: text/plain; charset=utf8");

// Create a debugging function.
function debug($message) {
    echo "[+] " . $message . "\n";
}

// Create a failure logging function.
function failed($message) {
    global $errors;
    $errors++;

    echo "[!] " . $message . "\n";
}

// Create the new instance.
$rainhawk = new Rainhawk("eSQpirMYxjXUs8xIjjaUo72gutwDJ4CP");
$started = microtime(true);
$username = null;
$name = "phpwrapper";
$errors = 0;

// Create an array of the tests.
$tests = array(
    /**
     * Test #1: Ping the API.
     */

    function() use($rainhawk) {
        global $username;

        debug("Checking that the API is online...");

        $ping = $rainhawk->ping();
        if($ping == false) {
            failed("Could not ping the service - " . $rainhawk->error());
        } else {
            $username = $ping['mashape_user'];

            debug("--> Server time offset: " . (time() - $ping['server_time']));
            debug("--> Found username: " . $username);
        }
    },

    /**
     * Test #2: List the datasets, delete the dataset if it already exists,
     * create a dataset and then get information on the dataset.
     */

    function() use($rainhawk) {
        global $username, $name;

        debug("Fetching a list of the datasets that we have access to...");

        $datasets = $rainhawk->listDatasets();
        if($datasets == false) {
            failed("Could not list the datasets - " . $rainhawk->error());
        } else {
            debug("--> Found a list of datasets.");

            foreach($datasets as $dataset) {
                if($dataset['name'] == $username . "." . $name) {
                    debug("--> Found " . $dataset['name'] . ", cleaning up...");
                    $rainhawk->deleteDataset($dataset['name']);
                }
            }
        }

        debug("Creating the test dataset, '" . $name . "'...");

        $dataset = $rainhawk->createDataset($name, "An example dataset from the PHP class for testing.");
        if($dataset == false) {
            failed("Could not create the dataset - " . $rainhawk->error());
        } else {
            debug("--> " . json_encode($dataset));
        }

        $name = $username . "." . $name;

        debug("Fetching the information for the new dataset...");

        $dataset = $rainhawk->fetchDataset($name);
        if($dataset == false) {
            failed("Could not fetch the dataset - " . $rainhawk->error());
        } else {
            debug("--> " . json_encode($dataset));
        }
    },

    /**
     * Test #3: Add one row of data, add another two rows of data and upload
     * a file to the dataset.
     */

    function() use($rainhawk) {
        global $name;

        debug("Adding one row of data to the dataset...");

        $row = array(
            "name" => "John",
            "age" => 20,
            "weight" => 320.2,
            "role" => "content"
        );

        $inserted = $rainhawk->insertData($name, $row);
        if($inserted == false) {
            failed("Could not insert one row of data - " . $rainhawk->error());
        } else {
            debug("--> " . json_encode($inserted));
        }

        debug("Adding two rows of data in batch to the dataset...");

        $rows = array(
            array(
                "name" => "Jane",
                "age" => 24,
                "weight" => 220.4,
                "role" => "owner"
            ),
            array(
                "name" => "Bob",
                "age" => 36,
                "weight" => 320.6,
                "role" => "manager"
            )
        );

        $inserted = $rainhawk->insertMultiData($name, $rows);
        if($inserted == false) {
            failed("Could not insert two rows of data in batch - " . $rainhawk->error());
        } else {
            debug("--> " . json_encode($inserted));
        }

        debug("Uploading some rows of data to the dataset from a .csv...");

        $file = "../test_data.csv";

        $uploaded = $rainhawk->uploadData($name, $file);
        if($uploaded == false) {
            failed("Could not upload the data into the dataset - " . $rainhawk->error());
        } else {
            debug("--> " . json_encode($uploaded));
        }
    },

    /**
     * Test #4: Detect the constraints that should be applied to the data, and
     * then remove one of the constraints and list the constraints.
     */

    function() use($rainhawk) {
        global $name;

        debug("Automatically detecting constraints and applying them...");

        $constraints = $rainhawk->addConstraint($name);
        if($constraints == false) {
            failed("Could not detect the constraints - " . $rainhawk->error());
        } else {
            debug("--> " . json_encode($constraints));
        }

        debug("Removing the constraint on 'role'...");

        $removed = $rainhawk->removeConstraint($name, "role");
        if($removed == false) {
            failed("Could not remove the constraint on 'role' - " . $rainhawk->error());
        } else {
            debug("--> Removed: " . json_encode($removed));
        }

        debug("Listing all constraints being applied to the dataset...");

        $constraints = $rainhawk->listConstraints($name);
        if($constraints == false) {
            failed("Could not list the constraints - " . $rainhawk->error());
        } else {
            debug("--> " . json_encode($constraints));
        }
    },

    /**
     * Test #5: Add an index to all of the fields on the dataset, remove one
     * of the indexes and then list them.
     */

    function() use($rainhawk) {
        global $name;

        debug("Adding indexes to all of the relevant fields automatically...");

        $indexes = $rainhawk->addIndex($name);
        if($indexes == false) {
            failed("Could not add the indexes - " . $rainhawk->error());
        } else {
            debug("--> " . json_encode($indexes));
        }

        debug("Removing the index on 'name'...");

        $removed = $rainhawk->removeIndex($name, "name");
        if($removed == false) {
            failed("Could not remove the index on 'name' - " . $rainhawk->error());
        } else {
            debug("--> Removed: " . json_encode($removed));
        }

        debug("Listing all indexes currently on the dataset...");

        $indexes = $rainhawk->listIndexes($name);
        if($indexes == false) {
            failed("Could not list the indexes - " . $rainhawk->error());
        } else {
            debug("--> " . json_encode($indexes));
        }
    },

    /**
     * Test #6: Select data from the dataset and then delete all of the data.
     */

    function() use($rainhawk) {
        global $name;

        debug("Selecting some of the data...");

        $query = array(
            "role" => "content"
        );

        $select = $rainhawk->selectData($name, $query);
        if($select == false) {
            failed("Could not run the select query - " . $rainhawk->error());
        } else {
            debug("--> " . json_encode($select));
        }

        debug("Removing all of the data...");

        $deleted = $rainhawk->deleteData($name, array());
        if($deleted == false) {
            failed("Could not delete the data - " . $rainhawk->error());
        } else {
            debug("--> Deleted: " . json_encode($deleted));
        }
    },

    /**
     * Test #7: Delete the dataset and clean up.
     */

    function() use($rainhawk) {
        global $name;

        debug("Removing our test dataset...");

        $deleted = $rainhawk->deleteDataset($name);
        if($deleted == false) {
            failed("Could not delete dataset - " . $rainhawk->error());
        } else {
            debug("--> Deleted: " . json_encode($deleted));
        }
    }
);

// Run the tests.
foreach($tests as $callable) {
    $callable();
}

// Finish up and exit.
debug("Done! All tests completed in " . number_format(microtime(true) - $started, 2) . " second(s).");

// Check for any failures.
if($errors > 0) {
    failed("--> " . $errors . " operation(s) failed.");
}

// Finish execution.
exit;

?><file_sep>/**
 * Project Rainhawk
 *
 * Simple PHP wrapper for the Rainhawk API, which provides simple JSON
 * encoded data for a variety of data sources with pre-defined
 * operations.
 *
 * @package Rainhawk
 * @license none
 */

var rainhawk = {
    /**
     * Set the base URL for the API.
     *
     * @var {string}
     */

    host: "https://sneeza-eco.p.mashape.com",

    /**
     * Store the user's Mashape API key.
     *
     * @var {string}
     */

    apiKey: null,

    /**
     * Create an object that can be used for communicating at an HTTP level
     * with the API.
     */

    http: {
        /**
         * Store the supported methods for communicating with the API, which
         * are only the standard HTTP protocol methods.
         */

        methods: {
            get: "GET",
            post: "POST",
            put: "PUT",
            del: "DELETE"
        },

        /**
         * Method to create a new instance of a HTTP request using the above
         * factories. Each one is tested in turn until a valid object is received
         * at which point we can send the result back.
         *
         * @return {object}
         */

         createRequest: function(method, url) {
            var request = new XMLHttpRequest();

            if("withCredentials" in request) {
                request.open(method, url, true);

                return request;
            } else if(typeof XDomainRequest != "undefined") {
                request = new XDomainRequest();
                request.open(method, url);

                return request;
            }

            return;
         },

         /**
          * Send a HTTP request to the specified endpoint so that we can get
          * some data back and process requests.
          *
          * @param {object} options
          * @return {httprequest}
          */

         send: function(options, success, failure) {
            if(!options.params) options.params = {};
            if(!options.timeout) options.timeout = 10000;

            var request;
            var params;

            if(rainhawk.objectSize(options.params) > 0) {
                params = "";

                for(var key in options.params) {
                    params += key + "=" + encodeURIComponent(options.params[key]) + "&";
                }

                params = params.substring(0, params.length - 1);

                if(options.method == this.methods.get) {
                    options.url = options.url + "?" + params;
                    params = null;
                }
            }

            request = this.createRequest(options.method, options.url);
            request.timeout = options.timeout;
            request.setRequestHeader("X-Mashape-Authorization", rainhawk.apiKey);

            if(params && !options.hasOwnProperty("file")) {
                request.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
            } else if(options.method == this.methods.put && options.hasOwnProperty("file")) {
                params = options.file;
            }

            request.onload = function(e) {
                if(request.status == 200) {
                    var json = JSON.parse(request.responseText);

                    if(json.hasOwnProperty("meta") && json.hasOwnProperty("data") && json.meta.code == 200) {
                        success(json);
                    } else {
                        failure(json.hasOwnProperty("data") && json.data.hasOwnProperty("message") ? json.data.message : "Invalid JSON received from API.");
                    }
                } else {
                    failure(request.statusText ? request.statusText : "An unknown error occured while performing the request.");
                }
            };

            request.onerror = function(e) {
                failure(request.statusText ? request.statusText : "An unknown error occured while performing the request.");
            };

            request.send(params);

            return request;
         }
    },

    /**
     * Send a simple ping request to the API, which will respond with
     * the timestamp of the server's current time.
     *
     * @param {function} success
     * @param {function} error
     */

    ping: function(success, error) {
        var url = rainhawk.host + "/ping";

        return rainhawk.http.send({
            url: url,
            method: rainhawk.http.methods.get
        }, function(json) {
            success(json.data);
        }, error);
    },

    /**
     * Operations specific to datasets go in here.
     */

    datasets: {
        /**
         * List the datasets that the current user can access (either read or
         * write) and some basic information about them.
         *
         * @param {function} success
         * @param {function} error
         */

        list: function(success, error) {
            var url = rainhawk.host + "/datasets";

            return rainhawk.http.send({
                url: url,
                method: rainhawk.http.methods.get
            }, function(json) {
                success(json.data.datasets);
            }, error);
        },

        /**
         * Fetch some information about a specific dataset, returning an array
         * of data that can be used to gather more insight into what the dataset
         * currently looks like.
         *
         * @param {string} name
         * @param {function} success
         * @param {function} error
         */

        info: function(name, success, error) {
            var url = rainhawk.host + "/datasets/" + name;

            return rainhawk.http.send({
                url: url,
                method: rainhawk.http.methods.get
            }, function(json) {
                success(json.data);
            }, error);
        },

        /**
         * Create a new dataset using the POST method on the same endpoint as
         * above, sending only the name of the dataset and not the username as
         * well.
         *
         * @param {string} name
         * @param {string} description
         * @param {function} success
         * @param {function} error
         */

        create: function(name, description, success, error) {
            var url = rainhawk.host + "/datasets";
            var params = {name: name, description: description};

            return rainhawk.http.send({
                url: url,
                method: rainhawk.http.methods.post,
                params: params
            }, function(json) {
                success(json.data);
            }, error);
        },

        /**
         * Remove a dataset using the DELETE method on the dataset's endpoint
         *
         * @param {string} name
         * @param {function} success
         * @param {function} error
         */

        delete: function(name, success, error) {
            var url = rainhawk.host + "/datasets/" + name;

            return rainhawk.http.send({
                url: url,
                method: rainhawk.http.methods.del
            }, function(json) {
                success(json.data.deleted);
            }, error);
        }
    },

    /**
     * Operations specific to data points go here.
     */

    data: {
        /**
         * Fetch some data from the dataset, optionally specifying a query to
         * filter the rows that will be returned.
         *
         * @param {string} name
         * @param {object} options
         * @param {function} success
         * @param {function} error
         */

        select: function(name, options, success, error) {
            if(!options) options = {};

            var url = rainhawk.host + "/datasets/" + name + "/data";
            var params = {
                query: options.hasOwnProperty("query") ? JSON.stringify(options.query) : null,
                offset: options.hasOwnProperty("offset") ? options.offset : 0,
                limit: options.hasOwnProperty("limit") ? options.limit : 0,
                sort: options.hasOwnProperty("sort") ? JSON.stringify(options.sort) : null,
                field: options.hasOwnProperty("field") ? JSON.stringify(options.field) : null,
                exclude: options.hasOwnProperty("exclude") ? JSON.stringify(options.exclude) : null
            };

            return rainhawk.http.send({
                url: url,
                method: rainhawk.http.methods.get,
                params: params
            }, function(json) {
                success(json.data);
            }, error);
        },

        /**
         * Insert a single row of data into the specified dataset, which is
         * just an alias for insertMulti providing an array of one row instead.
         *
         * @param {string} name
         * @param {object} row
         * @param {function} success
         * @param {function} error
         */

        insert: function(name, row, success, error) {
            return this.insertMulti(name, [row], function(rows) {
                success(rows[0]);
            }, error);
        },

        /**
         * Insert multiple rows of data into the specified dataset, which calls
         * the API with an array of objects which are sent in batch.
         *
         * @param {string} name
         * @param {array} rows
         * @param {function} success
         * @param {function} error
         */

        insertMulti: function(name, rows, success, error) {
            var url = rainhawk.host + "/datasets/" + name + "/data";
            var params = {rows: JSON.stringify(rows)};

            return rainhawk.http.send({
                url: url,
                method: rainhawk.http.methods.post,
                params: params
            }, function(json) {
                success(json.data.rows);
            }, error);
        },

        /**
         * Run an update query on the specified dataset, using the query to
         * match rows and then the changes object to specify the changes to
         * make.
         *
         * @param {string} name
         * @param {object} query
         * @param {object} changes
         * @param {function} success
         * @param {function} error
         */

        update: function(name, query, changes, success, error) {
            var url = rainhawk.host + "/datasets/" + name + "/data";
            var params = {query: JSON.stringify(query), changes: JSON.stringify(changes)};

            return rainhawk.http.send({
                url: url,
                method: rainhawk.http.methods.put,
                params: params
            }, function(json) {
                success(json.data.updated);
            }, error);
        },

        /**
         * Delete the specified rows of data from the dataset. This is a
         * destructive operation and cannot be undone, so use it wisely.
         *
         * @param {string} name
         * @param {object} query
         * @param {function} success
         * @param {function} error
         */

        delete: function(name, query, success, error) {
            var url = rainhawk.host + "/datasets/" + name + "/data";
            var params = {query: JSON.stringify(query)};

            return rainhawk.http.send({
                url: url,
                method: rainhawk.http.methods.del,
                params: params
            }, function(json) {
                success(json.data.deleted);
            }, error);
        },

        /**
         * Upload data into the specified dataset, using a File object
         * which can be obtained from a form input field of type file.
         *
         * @param {string} name
         * @param {file} file
         * @param {function} success
         * @param {function} error
         */

        upload: function(name, file, type, success, error) {
            var url = rainhawk.host + "/datasets/" + name + "/upload/" + type;

            return rainhawk.http.send({
                url: url,
                method: rainhawk.http.methods.put,
                file: file
            }, function(json) {
                success(json.data.rows);
            }, error);
        },

        /**
         * Calculations are performed on data so we group the relevant endpoints
         * inside a sub-object.
         */

        calc: {
            /**
             * Calculate the coefficients of a line of best fit through certain
             * data points inside a dataset.
             *
             * @param {string} name
             * @param {array} fields
             * @param {int} degree
             * @param {function} success
             * @param {function} error
             */

            polyfit: function(name, fields, degree, success, error) {
                var url = rainhawk.host + "/datasets/" + name + "/calc/polyfit";
                var params = {fields: JSON.stringify(fields)};

                if(degree) params.degree = degree;

                return rainhawk.http.send({
                    url: url,
                    method: rainhawk.http.methods.get,
                    params: params
                }, function(json) {
                    success(json.data.coefficients);
                }, error);
            },

            /**
             * Calculate the means and other statistical data for certain data
             * within a dataset. This method is mostly for convenience but can
             * also be useful for other things.
             *
             * @param {string} name
             * @param {array} fields
             * @param {object} query
             * @param {function} success
             * @param {function} error
             */

            stats: function(name, field, query, success, error) {
                var url = rainhawk.host + "/datasets/" + name + "/calc/stats";
                var params = {field: field, query: JSON.stringify(query)};

                return rainhawk.http.send({
                    url: url,
                    method: rainhawk.http.methods.get,
                    params: params
                }, function(json) {
                    success(json.data);
                }, error);
            }
        }
    },

    /**
     * Operations specific to manipulate the indexes on a dataset.
     */

    indexes: {
        /**
         * Generate a list of indexes that exist on a dataset which are
         * currently being applied to a dataset.
         *
         * @param {string} name
         * @param {function} success
         * @param {function} error
         */

        list: function(name, success, error) {
            var url = rainhawk.host + "/datasets/" + name + "/indexes";

            return rainhawk.http.send({
                url: url,
                method: rainhawk.http.methods.get
            }, function(json) {
                success(json.data.indexes);
            }, error);
        },

        /**
         * Create an index on the specified fields so that queries on those
         * fields can be much faster and more efficient.
         *
         * @param {string} name
         * @param {string} field
         * @param {function} success
         * @param {function} error
         */

        add: function(name, field, success, error) {
            var url = rainhawk.host + "/datasets/" + name + "/indexes";
            var params = {};

            if(field) params.field = field;

            return rainhawk.http.send({
                url: url,
                method: rainhawk.http.methods.post,
                params: params
            }, function(json) {
                if(field) {
                    success(json.data.added);
                } else {
                    success(json.data.detected);
                }
            }, error);
        },

        /**
         * Delete an index from the specified dataset on the specified fields,
         * which requires that the indexes already exist.
         *
         * @param {string} name
         * @param {array} field
         * @param {function} success
         * @param {function} error
         */

        remove: function(name, field, success, error) {
            var url = rainhawk.host + "/datasets/" + name + "/indexes";
            var params = {
                field: field
            };

            return rainhawk.http.send({
                url: url,
                method: rainhawk.http.methods.del,
                params: params
            }, function(json) {
                success(json.data.removed);
            }, error);
        }
    },

    /**
     * Operations specific to access allowed to/on a dataset.
     */

    access: {
        /**
         * List the access that's currently available to a dataset, giving two
         * arrays - one for the users that have read access and another for those
         * who have write access.
         *
         * @param {string} name
         * @param {function} success
         * @param {function} error
         */

        list: function(name, success, error) {
            var url = rainhawk.host + "/datasets/" + name + "/access";

            return rainhawk.http.send({
                url: url,
                method: rainhawk.http.methods.get
            }, function(json) {
                success(json.data);
            }, error);
        },

        /**
         * Give a user certain access levels to a dataset, provided an array or
         * string of types of access.
         *
         * @param {string} name
         * @param {string} username
         * @param {string|array} type
         * @param {function} success
         * @param {function} error
         */

        give: function(name, username, type, success, error) {
            var url = rainhawk.host + "/datasets/" + name + "/access";
            var params = {
                username: username,
                type: typeof type == "string" ? type : JSON.stringify(type)
            };

            return rainhawk.http.send({
                url: url,
                method: rainhawk.http.methods.post,
                params: params
            }, function(json) {
                success(json.data.added);
            }, error);
        },

        /**
         * Remove access from a user to the specified dataset, which uses a
         * string to represent either "read" or "write".
         *
         * @param {string} name
         * @param {string} username
         * @param {string} type
         * @param {function} success
         * @param {function} error
         */

        remove: function(name, username, type, success, error) {
            var url = rainhawk.host + "/datasets/" + name + "/access";
            var params = {username: username, type: type};

            return rainhawk.http.send({
                url: url,
                method: rainhawk.http.methods.del,
                params: params
            }, function(json) {
                success(json.data.removed);
            }, error);
        }
    },

    /**
     * Operations specific to constraints applied to datasets.
     */

    constraints: {
        /**
         * List the constraints that are currently being applied to a dataset,
         * which are essentially masks on fields. Currently supported: string,
         * array, integer, float, timestamp, latitude, longitude.
         *
         * @param {string} name
         * @param {function} success
         * @param {function} error
         */

        list: function(name, success, error) {
            var url = rainhawk.host + "/datasets/" + name + "/constraints";

            return rainhawk.http.send({
                url: url,
                method: rainhawk.http.methods.get
            }, function(json) {
                success(json.data.constraints);
            }, error);
        },

        /**
         * Add a constraint on a field within a dataset, specifying the type
         * of data that should appear in that field as well as the field name.
         *
         * @param {string} name
         * @param {string} field
         * @param {string} type
         * @param {function} success
         * @param {function} error
         */

        add: function(name, field, type, success, error) {
            var url = rainhawk.host + "/datasets/" + name + "/constraints";
            var params = {};

            if(field) params.field = field;
            if(type) params.type = type;

            return rainhawk.http.send({
                url: url,
                method: rainhawk.http.methods.post,
                params: params
            }, function(json) {
                if(field) {
                    success(json.data.added);
                } else {
                    success(json.data.detected);
                }
            }, error);
        },

        /**
         * Remove the constraint from a field in a dataset, which unmasks the
         * data being returned from queries.
         *
         * @param {string} name
         * @param {string} field
         * @param {function} success
         * @param {function} error
         */

        remove: function(name, field, success, error) {
            var url = rainhawk.host + "/datasets/" + name + "/constraints";
            var params = {field: field};

            return rainhawk.http.send({
                url: url,
                method: rainhawk.http.methods.del,
                params: params
            }, function(json) {
                success(json.data.removed);
            }, error);
        }
    },

    /**
     * Support function to allow the library to count the size of an object,
     * which JS doesn't have native support for.
     *
     * @param {object} obj
     * @return {int}
     */

    objectSize: function(obj) {
        var size = 0, key;

        for(key in obj) {
            if(obj.hasOwnProperty(key)) size++;
        }

        return size;
    }
};
<file_sep><?php

include("includes/kernel.php");
include("includes/api/core.php");

/*!
 * Output our JSON payload for use in whatever needs to be using
 * it.
 */

echo json_beautify(JSON_ERROR_NOMETHOD);
exit;

?><file_sep><?php

/**
 * Rainhawk
 *
 * The main class for our Rainhawk framework, providing an
 * interface from the application to the MongoDB database layer
 * so that we can grab data.
 *
 * @package Rainhawk
 */

class Rainhawk {
    /**
     * Store a connection instance for our database layer.
     *
     * @var MongoClient
     */

    private static $connection;

    /**
     * Store a reference for our MongoDatabase instance.
     *
     * @var MongoDatabase
     */

    private static $database;

    /**
     * Connect to the specified MongoDB server, which defaults to localhost on
     * Mongo's default port of 27017 (coincidentally also Valve's Source server
     * IP range but w/e).
     *
     * @param string $host  The hostname or IP of the Mongo server.
     * @param integer $port  The port that the server is running on.
     * @return bool  Whether the connection was a success or not.
     */

    public static function connect($host = "127.0.0.1", $port = 27017) {
        return (self::$connection = new MongoClient("mongodb://" . $host . ":" . $port . "/"));
    }

    /**
     * Grab a reference to a MongoDatabase object so that we can grab collection
     * objects to pass through to child classes.
     *
     * @param string $database  The name of the database.
     * @return bool  Whether the database was selected or not.
     */

    public static function select_database($database) {
        return (self::$database = self::$connection->selectDB($database));
    }

    /**
     * Grab a reference to a MongoCollection object provided a name.
     *
     * @param string $name  The name of the collection.
     * @return MongoCollection  The object.
     */

    public static function select_collection($name) {
        return self::$database->selectCollection($name);
    }
}

?><file_sep><nav class="navbar navbar-default navbar-fixed-top" role="navigation">
    <div class="container">
        <div class="navbar-header">
            <a href="/" class="navbar-brand">Project<strong>Rainhawk</strong><?php echo $user ? " / " . htmlspecialchars($user, ENT_QUOTES) : null; ?></a>
        </div>
        <ul class="nav navbar-nav navbar-right">
            <?php if($user) { ?>
                <li>
                    <div>
                        <a class="btn btn-default navbar-btn" href="/create.php"><i class="fa fa-file"></i>&nbsp; Create Dataset</a>
                    </div>
                </li>
                <li>
                    <div>
                        <a class="btn btn-default navbar-btn" href="/datasets.php"><i class="fa fa-bars"></i>&nbsp; Browse Datasets</a>
                    </div>
                </li>
                <li>
                    <div>
                        <a class="btn btn-default navbar-btn" href="/login.php?logout"><i class="fa fa-user"></i>&nbsp; Log Out</a>
                    </div>
                </li>
            <?php } else { ?>
                <li>
                    <div>
                        <a class="btn btn-default navbar-btn" href="/login.php"><i class="fa fa-user"></i>&nbsp; Login</a>
                    </div>
                </li>
            <?php } ?>
        </ul>
    </div>
</nav><file_sep><?php

// Define the headers.
header("content-type: text/plain; charset=utf8");
chdir("/var/www");

// Define the commands to be run.
$commands = array(
    "whoami",
    "/usr/bin/git clean -d -f",
    "/usr/bin/git reset --hard",
    "/usr/bin/git pull",
    "/usr/bin/git status",
    "/usr/bin/git submodule sync",
    "/usr/bin/git submodule update",
    "/usr/bin/git submodule status",
    "cd /var/www/parser && /usr/bin/make clean",
    "cd /var/www/parser && /usr/bin/make"
);

// Run them all.
foreach($commands as $command) {
    echo "$ {$command}\n";

    $output = shell_exec("export PATH=/usr/local/bin:/usr/bin:/bin; " . $command . " 2>&1");
    echo "{$output}\n";
}

?>
<file_sep><?php

use Rainhawk\Data;

class RainhawkDataTest extends UnitTest {
    public $class = "Rainhawk\Data";

    public function testCheckString() {
        $this->assertEquals(data::check(data::STRING, "test"), "test");
        $this->assertEquals(data::check(data::STRING, "2"), "2");
        $this->assertEquals(data::check(data::STRING, array()), null);
    }

    public function testCheckInteger() {
        $this->assertEquals(data::check(data::INTEGER, 2), 2);
        $this->assertEquals(data::check(data::INTEGER, 2.2), null);
        $this->assertEquals(data::check(data::INTEGER, "2"), 2);
        $this->assertEquals(data::check(data::INTEGER, "2.5ab"), null);
        $this->assertEquals(data::check(data::INTEGER, array()), null);
    }

    public function testCheckFloat() {
        $this->assertEquals(data::check(data::FLOAT, 2), 2);
        $this->assertEquals(data::check(data::FLOAT, 2.2), 2.2);
        $this->assertEquals(data::check(data::FLOAT, "2"), 2);
        $this->assertEquals(data::check(data::FLOAT, "2.2"), 2.2);
        $this->assertEquals(data::check(data::FLOAT, ".5"), 0.5);
        $this->assertEquals(data::check(data::FLOAT, "5.1.2"), null);
        $this->assertEquals(data::check(data::FLOAT, array()), null);
    }

    public function testCheckTimestamp() {
        $this->assertEquals(data::check(data::TIMESTAMP, 2), null);
        $this->assertEquals(data::check(data::TIMESTAMP, "19 July 2002"), strtotime("19 July 2002"));
        $this->assertEquals(data::check(data::TIMESTAMP, "1335939007"), strtotime("1335939007"));
    }

    public function testCheckLatitude() {
        $this->assertEquals(data::check(data::LATITUDE, 2), null);
        $this->assertEquals(data::check(data::LATITUDE, "123.123"), null);
        $this->assertEquals(data::check(data::LATITUDE, "38.898556"), 38.898556);
        $this->assertEquals(data::check(data::LATITUDE, "0.2123123"), null);
    }

    public function testCheckLongitude() {
        $this->assertEquals(data::check(data::LONGITUDE, 2), null);
        $this->assertEquals(data::check(data::LONGITUDE, "-77.037852"), -77.037852);
        $this->assertEquals(data::check(data::LONGITUDE, "38.898556"), 38.898556);
        $this->assertEquals(data::check(data::LONGITUDE, "0.2123123"), null);
    }

    public function testCheckArray() {
        $this->assertEquals(data::check(data::ARR, 13), null);
        $this->assertEquals(data::check(data::ARR, (object)array(1)), array(1));
        $this->assertEquals(data::check(data::ARR, array(1, 2)), array(1, 2));
    }

    public function testDetect() {
        $this->assertEquals(data::detect("test"), data::STRING);
        $this->assertEquals(data::detect("1"), data::INTEGER);
        $this->assertEquals(data::detect("1.2"), data::FLOAT);
        $this->assertEquals(data::detect("-1.4"), data::FLOAT);
        $this->assertEquals(data::detect(array(1)), data::ARR);
    }
}

?><file_sep><?php

require_once "../includes/core.php";
require_once "../includes/check_login.php";

header("content-type: application/json; charset=utf8");

$dataset = isset($_GET['dataset']) ? $_GET['dataset'] : null;
$idValue = isset($_POST['_id']) ? $_POST['_id'] : null;

$query = array(
    "_id" => $idValue
);

$result = $rainhawk->deleteData($dataset, $query);

if(!$result) {
    echo json_encode(array(
        "Result" => "ERROR",
        "Message" => $rainhawk->error()
    ));
    exit;
}

echo json_encode(array(
    "Result" => "OK"
));
exit;

?>
<file_sep><?php

if(empty($mashape_key) || empty($user)) {
    header("Location: /login.php?dest=" . urlencode($_SERVER['REQUEST_URI']));
    exit;
}

?><file_sep><?php

include("includes/kernel.php");
include("includes/api/core.php");

/*!
 * Check if the user provided enough information to create the new
 * dataset, including the name and description.
 */

if(empty($data->prefix) || empty($data->name)) {
    echo json_beautify(json_render_error(401, "You didn't pass one or more of the required parameters."));
    exit;
}

/*!
 * Check to see if the dataset exists, and that we have access to it.
 * We need to use the prefix and the name of the dataset to get a
 * reference to it.
 */

// Create a new dataset object.
$dataset = new rainhawk\dataset($data->prefix, $data->name);

// Check that the dataset exists.
if(!$dataset->exists) {
    echo json_beautify(json_render_error(402, "The dataset you specified does not exist."));
    exit;
}

// Check that we can read from the dataset.
if(!$dataset->have_write_access(app::$username)) {
    echo json_beautify(json_render_error(403, "You don't have access to write to this dataset."));
    exit;
}

/*!
 * Define an empty array to store the results of whatever we need
 * to send back.
 */

$json = array();

/*!
 * Try and add the indexes to the dataset as the user has specified,
 * which will always work even if the index already exists.
 */

// Check if the fields are set.
if(!empty($data->field)) {
    // Check if the field can be indexed.
    if($data->field == "_id") {
        echo json_beautify(json_render_error(404, "The _id field is always indexed by default."));
        exit;
    }

    // Get the current indexes.
    $indx = $dataset->fetch_indexes();
    $indexes = array();

    // Set the indexes properly.
    foreach($indx as $index) {
        $indexes[] = array_keys($index['key'])[0];
    }

    // Check if the index has already been set.
    if(in_array($data->field, $indexes)) {
        echo json_beautify(json_render_error(405, "The field you specified already has an index."));
        exit;
    }

    // Add the index.
    if(!$dataset->add_index($data->field)) {
        echo json_beautify(json_render_error(406, "There was an unknown problem adding the index you specified."));
        exit;
    }

    // Set the JSON response.
    $json['added'] = true;
} else {
    // Find the fields to index and the indexes.
    $fields = app::find_index_names(array_keys($dataset->fields));
    $indx = $dataset->fetch_indexes();
    $indexes = array();

    // Set the indexes properly.
    foreach($indx as $index) {
        $indexes[] = array_keys($index['key'])[0];
    }

    // Remove the fields that already have indexes.
    foreach($fields as $index => $field) {
        if(in_array($field, $indexes)) unset($fields[$index]);
    }

    // Check if the fields are empty.
    if(empty($fields)) {
        echo json_beautify(json_render_error(404, "We couldn't find any fields to add indexes to."));
        exit;
    }

    // Start buffering the response.
    $json['detected'] = array();

    // Add indexes to each of the fields.
    foreach($fields as $field) {
        if(!$dataset->add_index($field)) {
            echo json_beautify(json_render_error(405, "There was an unknown problem adding one of the indexes."));
            exit;
        }

        // Set the response.
        $json['detected'][] = $field;
    }
}

/*!
 * Output our JSON payload for use in whatever needs to be using
 * it.
 */

echo json_beautify(json_render(200, $json));
exit;

?><file_sep><?php

include("includes/kernel.php");
include("includes/api/core.php");

/*!
 * Check that the parameters have all been set and sent to the script,
 * including the prefix and the name.
 */

if(empty($data->prefix) || empty($data->name)) {
    echo json_beautify(json_render_error(401, "You didn't pass one or more of the required parameters."));
    exit;
}

/*!
 * Check to see if the dataset exists, and that we have access to it.
 * We need to use the prefix and the name of the dataset to get a
 * reference to it.
 */

// Create a new dataset object.
$dataset = new rainhawk\dataset($data->prefix, $data->name);

// Check that the dataset exists.
if(!$dataset->exists) {
    echo json_beautify(json_render_error(402, "The dataset you specified does not exist."));
    exit;
}

// Check that we can read from the dataset.
if(!$dataset->have_read_access(app::$username)) {
    echo json_beautify(json_render_error(403, "You don't have access to read from this dataset."));
    exit;
}

/*!
 * Define an empty array to store the results of whatever we need
 * to send back.
 */

$json = array(
    "read_access" => array(),
    "write_access" => array()
);

/*!
 * Fetch the access arrays from the dataset, and list them back to
 * the user in a friendly format.
 */

// Return the read_access keys into the JSON.
foreach($dataset->read_access as $username) {
    $json['read_access'][] = $username;
}

// Return the write_access keys into the JSON.
foreach($dataset->write_access as $username) {
    $json['write_access'][] = $username;
}

/*!
 * Output our JSON payload for use in whatever needs to be using
 * it.
 */

echo json_beautify(json_render(200, $json));
exit;

?><file_sep><?php

/**
 * @package UnitTest
 */

class UnitTest {
    /**
     * @var string
     */

    protected $class;

    /**
     * @var integer
     */

    protected $passed = 0;

    /**
     * @var integer
     */

    protected $failed = 0;

    /**
     * @var integer
     */
    protected $started;

    /**
     * @param mixed $value
     * @param mixed $expected
     * @return mixed
     */

    public function assertEquals($value, $expected) {
        return $this->outputTestResult($value == $expected);
    }

    /**
     * @param mixed $value
     * @return mixed
     */

    public function assert($value) {
        return $this->outputTestResult($value);
    }

    /**
     * @param boolean $passes
     * @return mixed
     */

    public function outputTestResult($passes) {
        if($passes) {
            $this->passed++;

            echo ".";
        } else {
            $this->failed++;

            echo "F";
        }
    }

    /**
     * @return UnitTest
     */

    public function __before() {
        return $this;
    }

    /**
     * @return void
     */

    public function exec() {
        $this->started = microtime(true);
        $this->__before();

        echo $this->class . "\n\n";

        foreach(get_class_methods($this) as $method) {
            if(stripos($method, "assert") !== false || in_array($method, array("outputTestResult", "exec", "__before"))) {
                continue;
            } else {
                $this->$method();
                echo "\n";
            }
        }

        echo "\n";
        echo ($this->failed == 0 ? "OK" : "FAILED") . " (" . ($this->passed + $this->failed) . " tests, " . $this->failed . " assertions)\n";
        echo number_format(microtime(true) - $this->started, 2) . " seconds\n";

        return;
    }
}

?><file_sep><?php

/*!
 * This class allows us to connect to a network cache storage
 * to access globally shared objects. We use this for User,
 * Trade and other classes.
 */

class MongoCLI {
    // Hold the host and port in variables.
    public static $host = "127.0.0.1";
    public static $port = 27017;

    // Private connection variable for Mongo.
    private static $conn = null;
    private static $database = null;
    private static $datasets = null;

    /*!
     * Create a new instance of the native Mongo driver, which we're
     * essentially wrapping with this class. We don't need to worry about
     * usernames or passwords.
     */

    public static function connect($host = null, $port = null) {
        if($host) self::$host = $host;
        if($port) self::$port = $port;

        self::$conn = new MongoClient("mongodb://" . self::$host . ":" . self::$port . "/");

        if(self::$conn) {
            return true;
        }

        return false;
    }

    /*!
     * Select the database to use within the Mongo instance, so we can
     * separate out our application's logic.
     */

    public static function select_database($database) {
        try {
            self::$database = self::$conn->selectDB($database);
        } catch(Exception $e) {
            return false;
        }

        return true;
    }

    /*!
     * Select the collection to be used for storing/retreiving the data.
     * If the collection doesn't exist, Mongo will create one for us.
     */

    public static function select_collection($collection) {
        try {
            return self::$database->selectCollection($collection);
        } catch(Exception $e) {
            return false;
        }
    }

    /*!
     * Check if a collection exists so that we can prevent people creating
     * two sets with the same name.
     */

    public static function collection_exists($name) {
        $datasets = self::datasets();
        $matches = $datasets->find(array("name" => $name));

        return ($matches->count() > 0);
    }

    /*!
     * Check that a user has access to a specific collection by validating
     * their ownership with the datasets table.
     */

    public static function can_access_collection($name) {
        $datasets = self::datasets();
        $matches = $datasets->find(array("name" => $name, "accessors" => app::$mashape_key));

        return ($matches->count() > 0);
    }

    /*!
     * Get the statistics for a collection given it's label, which is
     * provided by the user.
     */

    public static function get_collection_info($label) {
        //
    }

    /*!
     * Get a list of all of the available collections in the database.
     * This is useful for a variety of reasons.
     */

    public static function get_collections_for_key($mashape_key) {
        $datasets = self::select_collection("system.datasets");

        return $datasets->find(array("owners" => $mashape_key));
    }

    /*!
     * Create a new collection in the database using the specified name
     * and optional parameters.
     */

    public static function create_collection($name, $mashape_key) {
        $internal_name = $mashape_key . "." . $name;
        $collection = self::$database->createCollection($internal_name);

        if($collection) {
            $datasets = self::select_collection("system.datasets");
            $datasets->insert(array(
                "name" => "tes034923",
                "label" => $name,
                "created" => time(),
                "rows" => 0,
                "fields" => array(),
                "have_access" => array($mashape_key)
            ));
        }

        return false;
    }

    /*!
     * Private function for getting a handler to the datasets collection
     * so that we can check which tables exist.
     */

    private static function datasets() {
        if(isset(self::$datasets)) {
            return self::$datasets;
        }

        return self::select_collection("system.datasets");
    }

    /*!
     * A public function to generate a safe name for a dataset, so that users
     * can't access system sets or other user's sets.
     */

    public static function safe_name($name) {
        return "data." . $name;
    }
}
<file_sep><?php

include("includes/kernel.php");
include("includes/api/core.php");

/*!
 * Define our server_time timestamp and set it as the JSON output
 * to be sent back to the client.
 */

$json = array(
    "server_time" => time(),
    "mashape_user" => app::$username
);

/*!
 * Output our JSON payload for use in whatever needs to be using
 * it.
 */

echo json_beautify(json_render(200, $json));
exit;

?><file_sep><?php

include("includes/kernel.php");
include("includes/api/core.php");

/*!
 * Check that the parameters have all been set and sent to the script,
 * including the prefix and the name.
 */

if(empty($data->prefix) || empty($data->name)) {
    echo json_beautify(json_render_error(401, "You didn't pass one or more of the required parameters."));
    exit;
}

/*!
 * Check to see if the dataset exists, and that we have access to it.
 * We need to use the prefix and the name of the dataset to get a
 * reference to it.
 */

// Create a new dataset object.
$dataset = new rainhawk\dataset($data->prefix, $data->name);

// Check that the dataset exists.
if(!$dataset->exists) {
    echo json_beautify(json_render_error(402, "The dataset you specified does not exist."));
    exit;
}

// Check that we can write to the dataset.
if(!$dataset->have_write_access(app::$username)) {
    echo json_beautify(json_render_error(403, "You don't have access to write to this dataset."));
    exit;
}

/*!
 * Define an empty array to store the results of whatever we need
 * to send back.
 */

$json = array(
    "updated" => 0
);

/*!
 * Check that the two required parameters are set, so that we can
 * ensure that no data gets erroneously removed.
 */

// Check the query is set.
if(empty($data->query)) {
    echo json_beautify(json_render_error(404, "You can't use a catch-all query for update statements, dummy."));
    exit;
}

// Check the changes aren't empty.
if(empty($data->changes)) {
    echo json_beautify(json_render_error(405, "You didn't specify any changes to make."));
    exit;
}

// Set some local variables.
$query = $data->query;
$changes = $data->changes;

// Change the MongoID if we have one.
foreach($query as $key => $value) {
    if($key == "_id") {
        try {
            $mongoid = new MongoID($value);
        } catch(Exception $e) {
            $mongoid = null;
        }

        $query[$key] = $mongoid;
    }
}

// Run the update query.
$updated = $dataset->update($query, $changes);

// Check if the query failed.
if(!is_int($updated)) {
    echo json_beautify(json_render_error(406, "An unexpected error occured while performing your query - are you sure you formatted all the parameters correctly?"));
    exit;
}

// Set the output.
$json['updated'] = $updated;

/*!
 * Output our JSON payload for use in whatever needs to be using
 * it.
 */

echo json_beautify(json_render(200, $json));
exit;

?>