diff --git a/includes/TripalFields/ncit__raw_data/ncit__raw_data.inc b/includes/TripalFields/ncit__raw_data/ncit__raw_data.inc
new file mode 100644
index 0000000..aa1df10
--- /dev/null
+++ b/includes/TripalFields/ncit__raw_data/ncit__raw_data.inc
@@ -0,0 +1,255 @@
+ 'tripal_no_storage',
+ // It is expected that all fields set a 'value' in the load() function.
+ // In many cases, the value may be an associative array of key/value pairs.
+ // In order for Tripal to provide context for all data, the keys should
+ // be a controlled vocabulary term (e.g. rdfs:type). Keys in the load()
+ // function that are supported by the query() function should be
+ // listed here.
+ 'browseable_keys' => array(),
+ );
+
+ // Provide a list of instance specific settings. These can be access within
+ // the instanceSettingsForm. When the instanceSettingsForm is submitted
+ // then Drupal with automatically change these settings for the instance.
+ // It is recommended to put settings at the instance level whenever possible.
+ // If you override this variable in a child class be sure to replicate the
+ // term_name, term_vocab, term_accession and term_fixed keys as these are
+ // required for all TripalFields.
+ public static $default_instance_settings = array(
+ // The short name for the vocabulary (e.g. schema, SO, GO, PATO, etc.).
+ 'term_vocabulary' => 'NCIT',
+ // The name of the term.
+ 'term_name' => 'Raw Data',
+ // The unique ID (i.e. accession) of the term.
+ 'term_accession' => 'C142663',
+ // Set to TRUE if the site admin is not allowed to change the term
+ // type, otherwise the admin can change the term mapped to a field.
+ 'term_fixed' => FALSE,
+ // Indicates if this field should be automatically attached to display
+ // or web services or if this field should be loaded separately. This
+ // is convenient for speed. Fields that are slow should for loading
+ // should have auto_attach set to FALSE so tha their values can be
+ // attached asynchronously.
+ 'auto_attach' => FALSE,
+ // The table where the options for this specific field are stored.
+ // This can be one of trpfancy_browse_options or trpfancy_browse_options_per_entity
+ // based on admin configuration. Default: trpfancy_browse_options.
+ 'option_storage' => '',
+ // A list of browser types this field intends to provide.
+ 'browser_types' => '',
+ );
+
+ // A boolean specifying that users should not be allowed to create
+ // fields and instances of this field type through the UI. Such
+ // fields can only be created programmatically with field_create_field()
+ // and field_create_instance().
+ public static $no_ui = FALSE;
+ // A boolean specifying that the field will not contain any data. This
+ // should exclude the field from web services or downloads. An example
+ // could be a quick browse field that appears on the page that redirects
+ // the user but otherwise provides no data.
+ public static $no_data = TRUE;
+
+ /**
+ * Loads the field values from the underlying data store.
+ *
+ * @param $entity
+ *
+ * @return
+ * An array of the following format:
+ * $entity->{$field_name}['und'][0]['value'] = $value;
+ * where:
+ * - $entity is the entity object to which this field is attached.
+ * - $field_name is the name of this field
+ * - 'und' is the language code (in this case 'und' == undefined)
+ * - 0 is the cardinality. Increment by 1 when more than one item is
+ * available.
+ * - 'value' is the key indicating the value of this field. It should
+ * always be set. The value of the 'value' key will be the contents
+ * used for web services and for downloadable content. The value
+ * should be of the follow format types: 1) A single value (text,
+ * numeric, etc.) 2) An array of key value pair. 3) If multiple entries
+ * then cardinality should incremented and format types 1 and 2 should
+ * be used for each item.
+ * The array may contain as many other keys at the same level as 'value'
+ * but those keys are for internal field use and are not considered the
+ * value of the field.
+ */
+ public function load($entity) {
+ global $user;
+
+ // User permissions.
+ $rawpheno_permission = array('access rawpheno', 'download rawpheno');
+ $count_permission = 0;
+ foreach($rawpheno_permission as $permission) {
+ if (user_access($permission, $user)) {
+ $count_permission++;
+ }
+ }
+
+ // If user has no permission to begin with, skip all and report
+ // raw phenotypes not available.
+ $field_name = $this->instance['field_name'];
+ $entity->{$field_name}['und'][0]['value'] = array();
+
+ if (user_is_logged_in() && $count_permission == 2) {
+ // # TRAITS/EXPERIMENT/LOCATION:
+ $traits = array();
+ // This query is identical to the rawphenotypes download page.
+ // Get all experiments (by plant id) where germplasm was used.
+ $all_experiment_locations = chado_query("
+ SELECT p2.project_id, p2.name, value AS location
+ FROM pheno_plantprop
+ INNER JOIN pheno_plant_project AS p1 USING (plant_id)
+ INNER JOIN {project} AS p2 ON p1.project_id = p2.project_id
+ WHERE
+ type_id = (SELECT cvterm_id FROM {cvterm} cvt LEFT JOIN {cv} cv ON cv.cv_id = cvt.cv_id
+ WHERE cvt.name = 'Location' AND cv.name = 'phenotype_plant_property_types') AND
+ plant_id IN (SELECT plant_id FROM pheno_plant WHERE stock_id = :germplasm GROUP BY plant_id)
+ GROUP BY p2.project_id, p2.name, value
+ ", array(':germplasm' => $entity->chado_record->stock_id));
+ $experiment_locations = $all_experiment_locations->fetchAll();
+
+ // Only when germplasm returned rawphenotypic data.
+ if (count($experiment_locations) > 0) {
+ // User appointed experiments.
+ // See includes/rawpheno.function.measurements.inc file for function definition
+ // Given the user id this function returns an array of chado projects keyed by project_id
+ // that the user has permission to see.
+ $user_experiment = rawpheno_function_user_project($user->uid);
+ $user_experiment = array_keys($user_experiment);
+
+ // All traits in experiment and location.
+ $sql_cvterm = "
+ SELECT c_j.cvterm_json->>'id', c_j.cvterm_json->>'name' FROM (
+ SELECT JSON_BUILD_OBJECT('id', cvterm_id, 'name', name) AS cvterm_json FROM {cvterm} WHERE cvterm_id = ANY ((
+ SELECT STRING_TO_ARRAY(list_id.all_traits, ',') FROM (
+ SELECT string_agg(DISTINCT all_traits, ',') AS all_traits
+ FROM {rawpheno_rawdata_mview}
+ WHERE
+ location IN(:location)
+ AND plant_id IN (SELECT plant_id FROM pheno_plant_project WHERE project_id = :project_id)
+ ) AS list_id
+ )::int[])
+ ) AS c_j
+ WHERE c_j.cvterm_json->>'name' NOT IN ('Rep', 'Entry', 'Location', 'Name', 'Plot', 'Planting Date (date)', '# of Seeds Planted (count)')
+ ORDER BY c_j.cvterm_json->>'name' ASC
+ ";
+
+ $trait_experiment_location = array();
+ $cache_exp = array();
+ $cache_loc = array();
+
+ foreach($experiment_locations as $item) {
+ $cache_exp[] = $item->project_id;
+ $cache_loc[] = $item->location;
+
+ $trait_set = chado_query($sql_cvterm, array(':location' => $item->location, ':project_id' => $item->project_id))
+ ->fetchAllKeyed(0, 1);
+
+ if ($trait_set) {
+ foreach($trait_set as $trait_id => $trait_name) {
+ $allow = (in_array($item->project_id, $user_experiment)) ? 1 : 0;
+
+ // Save basic information about the trait (name + id key) and all experiment + location it was measured.
+ $entity->{$field_name}['und'][0]['value']['hydra:member'][ $trait_name . '_' . $trait_id ][] = array(
+ 'phenotype_customfield_terms:id' => $item->project_id, // Project id number.
+ 'phenotype_customfield_terms:name' => $item->name, // Project name.
+ 'phenotype_customfield_terms:location' => $item->location, // Location in a project trait was measured.
+ 'phenotype_customfield_terms:user_experiment' => $allow // Does user have permission?
+ );
+ }
+ }
+ }
+
+ // Save a complete summary count as a quick raw phenotypic data summary related to the germplasm.
+ $entity->{$field_name}['und'][0]['value']['phenotype_customfield_terms:summary'] = array(
+ 'phenotype_customfield_terms:experiment' => count(array_unique($cache_exp)), // Summary count of experiments.
+ 'phenotype_customfield_terms:location' => count(array_unique($cache_loc)), // Summary count of locations.
+ 'phenotype_customfield_terms:trait' => count($entity->{$field_name}['und'][0]['value']['hydra:member']), // Summary count of traits.
+ );
+ }
+ }
+ }
+
+ /**
+ * Provides a form for the 'Field Settings' of an instance of this field.
+ *
+ * This function corresponds to the hook_field_instance_settings_form()
+ * function of the Drupal Field API.
+ *
+ * Validation of the instance settings form is not supported by Drupal, but
+ * the TripalField class does provide a mechanism for supporting validation.
+ * To allow for validation of your setting form you must call the parent
+ * in your child class:
+ *
+ * @code
+ * $element = parent::instanceSettingsForm();
+ * @endcode
+ *
+ * Please note, the form generated with this function does not easily
+ * support AJAX calls in the same way that other Drupal forms do. If you
+ * need to use AJAX you must manually alter the $form in your ajax call.
+ * The typical way to handle updating the form via an AJAX call is to make
+ * the changes in the form function itself but that doesn't work here.
+ */
+ public function instanceSettingsForm() {
+
+ // Retrieve the current settings.
+ // If this field was just created these will contain the default values.
+ $settings = $this->instance['settings'];
+
+ // Allow the parent Tripal Field to set up the form element for us.
+ $element = parent::instanceSettingsForm();
+
+ return $element;
+ }
+
+ /**
+ * @see ChadoField::elementInfo()
+ *
+ */
+ public function elementInfo() {
+ $field_term = $this->getFieldTermID();
+ return array(
+ $field_term => array(
+ 'operations' => array('eq', 'ne', 'contains', 'starts'),
+ 'sortable' => TRUE,
+ 'searchable' => TRUE,
+ ),
+ );
+ }
+}
\ No newline at end of file
diff --git a/includes/TripalFields/ncit__raw_data/ncit__raw_data_formatter.inc b/includes/TripalFields/ncit__raw_data/ncit__raw_data_formatter.inc
new file mode 100644
index 0000000..a7c71bf
--- /dev/null
+++ b/includes/TripalFields/ncit__raw_data/ncit__raw_data_formatter.inc
@@ -0,0 +1,164 @@
+chado_record->stock_id;
+ // Name (stock name) of the current germplasm.
+ $germplasm_name = $entity->chado_record->name;
+
+ // Reference directory path.
+ $base_path = $GLOBALS['base_url'] . '/';
+ $module_path = drupal_get_path('module', 'rawpheno') . '/';
+ $theme_path = $base_path . $module_path . '/includes/TripalFields/ncit__raw_data/theme/';
+
+ // Append image as bullet points, header icon and export button or link.
+ $img = '';
+
+
+ // CONSTRUCT SUMMARY TABLE:
+ // Each row will contain the trait name (trait header), location and experiment combination (LOCATION/Experiment header)
+ // as a select box and download link (download icon header).
+
+ // # TABLE HEADER:
+ $table_header = array(
+ sprintf($img, '', $theme_path . 'icon-download-all.jpg', 'Download all for this trait'),
+ 'Trait',
+ 'Filter by:',
+ sprintf($img, '', $theme_path . 'icon-download.jpg', 'Download')
+ );
+
+ // 2 select fields are required:
+ // A. Select field to filter by Location + Experiment.
+ // B. Select field to filter by Experiment (All location included).
+ // Depending on which Filter by option user selects, load corresponding select.
+
+ // # TABLE ROWS:
+ $table_row = array();
+ foreach($germplasm_raw_phenotypes as $trait => $exp_loc) {
+ $tmp = explode('_', $trait);
+ $trait = array('id' => $tmp[1], 'name' => $tmp[0]);
+
+ $table_row[] = array(
+ sprintf($img, $trait['id'] . '-all', $theme_path . 'icon-export.png', 'Download all for this trait'),
+ ucfirst($trait['name']),
+ $this->create_select($trait['id']),
+ sprintf($img, $trait['id'], $theme_path . 'icon-export.png', 'Download')
+ );
+ }
+
+ // # THEME TABLE:
+ $summary_table = theme('table', array(
+ 'header' => $table_header,
+ 'rows' => $table_row,
+ 'sticky' => FALSE,
+ 'attributes' => array('id' => 'rawphenotypes-germplasm-field-table'))
+ );
+
+
+ // Make field elements generated by formatter avaiable to the template as template vars.
+ // @see template file for this field in rawphenotypes/theme directory.
+ $markup = theme('rawpheno_germplasm_field', array(
+ 'element_id' => 'rawphenotypes-germplasm-raw-phenotypes-field',
+ 'summary_table' => array(
+ 'table' => $summary_table,
+ 'headers' => array(
+ 'germplasm' => $germplasm_name,
+ 'experiments' => $summary_values['phenotype_customfield_terms:experiment'],
+ 'locations' => $summary_values['phenotype_customfield_terms:location'],
+ 'traits' => $summary_values['phenotype_customfield_terms:trait'],
+ )
+ )
+ ));
+
+ // Rawphenotypes download link:
+ drupal_add_js(array('rawpheno' => array('exportLink' => $base_path . '/phenotypes/raw/download')), array('type' => 'setting'));
+ // Autocomplete UI.
+ drupal_add_library('system', 'ui.autocomplete');
+ // All datapoints available to JS to populate select field.
+ drupal_add_js(array('rawpheno' => array('germRawdata' => $germplasm_raw_phenotypes, 'germ' => $germplasm_id)), array('type' => 'setting'));
+
+
+ // Construct field render array.
+ $element[0] = array(
+ '#type' => 'markup',
+ '#markup' => $markup,
+ '#attached' => array(
+ 'css' => array($module_path . 'theme/css/rawpheno.germplasmfield.style.css'),
+ 'js' => array($module_path . 'theme/js/rawpheno.germplasmfield.script.js')
+ )
+ );
+ }
+
+ return $element;
+ }
+
+ /**
+ * Create select field.
+ *
+ * @param $trait_id
+ * Trait id number (cvterm id number) used as value for id attribute.
+ */
+ public function create_select($trait_id) {
+ $attributes = array(
+ 'id' => 'rawphenotypes-germplasm-field-filterby-' . $trait_id,
+ 'class' => array('rawphenotypes-germplasm-field-filterby')
+ );
+
+ $options = array(
+ 0 => '---'
+ );
+
+ return theme('select', array('element' => array('#attributes' => $attributes, '#options' => $options)));
+ }
+}
\ No newline at end of file
diff --git a/includes/TripalFields/ncit__raw_data/ncit__raw_data_widget.inc b/includes/TripalFields/ncit__raw_data/ncit__raw_data_widget.inc
new file mode 100644
index 0000000..923fd48
--- /dev/null
+++ b/includes/TripalFields/ncit__raw_data/ncit__raw_data_widget.inc
@@ -0,0 +1,44 @@
+data_table) AND ($bundle->data_table == 'stock')) {
+ // Insert auxiliary terms used by Raw Data field.
+ // Create cv to hold terms.
+ $cv = 'phenotype_customfield_terms';
+ chado_insert_cv($cv, 'vocabulary term to hold terms used by Raw Data field');
+
+ // Insert terms to cv above.
+ $terms = array('experiment', 'id', 'location', 'name', 'summary', 'trait', 'user_experiment');
+ foreach($terms as $term) {
+ tripal_insert_cvterm(array(
+ 'id' => 'rawpheno_tripal:' . $term,
+ 'name' => $term,
+ 'definition' => $term,
+ 'cv_name' => $cv
+ ));
+ }
+
+ // Raw Data term.
+ tripal_insert_cvterm(array(
+ 'id' => 'NCIT:C142663',
+ 'name' => 'Raw Data',
+ 'cv_name' => 'NCIT',
+ 'definition' => 'The original information, collected from the primary source. Used in Germplasm Raw Phenotypes Field.',
+ ));
+
+ $field_name = 'ncit__raw_data';
+ $field_type = 'ncit__raw_data';
+ $fields[$field_name] = array(
+ 'field_name' => $field_name,
+ 'type' => $field_type,
+ 'cardinality' => 1,
+ 'locked' => FALSE,
+ 'storage' => array(
+ 'type' => 'field_chado_storage',
+ ),
+ );
+ }
+
+ return $fields;
+}
+
+/**
+ * Implements hook_bundle_instances_info().
+ *
+ * This hook tells Drupal/Tripal to create a field instance of a given field type on a
+ * specific Tripal Content type (otherwise known as the bundle). Make sure to implement
+ * hook_bundle_create_fields() to create your field type before trying to create an
+ * instance of that field.
+ *
+ * @param $entity_type
+ * This should be 'TripalEntity' for all Tripal Content.
+ * @param $bundle
+ * This object describes the Type of Tripal Entity (e.g. Organism or Gene) the field
+ * instances are being created for. Thus this hook is called once per Tripal Content Type on your
+ * site. The name of the bundle is the machine name of the type (e.g. bio_data_1) and
+ * the label of the bundle (e.g. Organism) is what you see in the interface. Since the
+ * label can be changed by site admin, we suggest checking the data_table to determine
+ * if this is the entity you want to add field instances to.
+ * @return
+ * An array of field instance definitions. This is where you can define the defaults
+ * for any settings you use in your field. Each entry in this array will be used to
+ * create an instance of an already existing field.
+ */
+function rawpheno_bundle_instances_info($entity_type, $bundle) {
+ $instances = array();
+
+ // IN GERMPLASM PAGE ONLY:
+ if (isset($bundle->data_table) AND ($bundle->data_table == 'stock')) {
+ // Number of Values Recorded.
+ $field_name = 'ncit__raw_data';
+ $field_type = 'ncit__raw_data';
+ $instances[$field_name] = array(
+ 'field_name' => $field_name,
+ 'entity_type' => $entity_type,
+ 'bundle' => $bundle->name,
+ 'label' => 'Germplasm Raw Phenotypes',
+ 'description' => 'Field to add interface to raw phenotypes available to a germplasm.',
+ 'required' => FALSE,
+ 'settings' => array(
+ 'term_vocabulary' => 'NCIT',
+ 'term_name' => 'Raw Data',
+ 'term_accession' => 'C142663',
+ 'auto_attach' => FALSE,
+ 'chado_table' => $bundle->data_table,
+ 'chado_column' => $bundle->data_table . '_id',
+ 'base_table' => $bundle->data_table,
+ ),
+ 'widget' => array(
+ 'type' => 'ncit__raw_data_widget',
+ 'settings' => array(),
+ ),
+ 'display' => array(
+ 'default' => array(
+ 'label' => 'hidden',
+ 'type' => 'ncit__raw_data_formatter',
+ 'settings' => array(),
+ ),
+ ),
+ );
+ }
+
+ return $instances;
+}
\ No newline at end of file
diff --git a/include/rawpheno.admin.form.inc b/includes/rawpheno.admin.form.inc
similarity index 99%
rename from include/rawpheno.admin.form.inc
rename to includes/rawpheno.admin.form.inc
index ce5dbe3..206c3f3 100644
--- a/include/rawpheno.admin.form.inc
+++ b/includes/rawpheno.admin.form.inc
@@ -129,7 +129,7 @@ function rawpheno_admin_page($form, &$form_state) {
// Include function to manage column headers, cv terms and variable names.
-module_load_include('inc', 'rawpheno', 'include/rawpheno.function.measurements');
+module_load_include('inc', 'rawpheno', 'includes/rawpheno.function.measurements');
/**
diff --git a/include/rawpheno.backup.form.inc b/includes/rawpheno.backup.form.inc
similarity index 100%
rename from include/rawpheno.backup.form.inc
rename to includes/rawpheno.backup.form.inc
diff --git a/include/rawpheno.download.form.inc b/includes/rawpheno.download.form.inc
similarity index 87%
rename from include/rawpheno.download.form.inc
rename to includes/rawpheno.download.form.inc
index c48bce2..8ef6748 100644
--- a/include/rawpheno.download.form.inc
+++ b/includes/rawpheno.download.form.inc
@@ -1,366 +1,414 @@
- 'markup',
- '#markup' => t('View Summary ❯'),
- );
-
- // PROJECT SELECT BOX.
- if (isset($form_state['values']['sel_project'])) {
- // Project selected.
- $project_selected = $form_state['values']['sel_project'];
- }
-
- // Sort the project names according to Planting Date.
- // Put project with recently uploaded data/ based on planting year
- // first in the list.
- $sql = "SELECT project_id, name
- FROM {project} AS t1
- RIGHT JOIN pheno_plant_project AS t2 USING (project_id)
- LEFT JOIN pheno_measurements AS t3 USING (plant_id)
- WHERE t3.type_id = (SELECT cvterm_id FROM {cvterm} WHERE name = 'Planting Date (date)' LIMIT 1)
- GROUP BY project_id, name, t3.value
- ORDER BY t3.value DESC";
-
- $opt_project = chado_query($sql)
- ->fetchAllKeyed();
-
- // Project options:
- if (count($opt_project) <= 0) {
- // Module has no projects w/ data yet.
- return $form;
- }
- else {
- // Remove any duplicates from the sorted list.
- $opt_project = array_unique($opt_project, SORT_REGULAR);
- }
-
- // AJAX wrapper.
- // Main wrapper
- $form['ajax_container'] = array(
- '#type' => 'markup',
- '#prefix' => '
',
- '#suffix' => '
',
- );
-
- // This a hidden field containing all project id.
- // This field will allow callback functions to get all project ids which is
- // the equivalent of the option select all project from the project select box.
- $form['ajax_container']['txt_project'] = array(
- '#type' => 'hidden',
- '#value' => implode(',', array_keys($opt_project)),
- );
-
- $form['ajax_container']['sel_project'] = array(
- '#type' => 'select',
- '#title' => t('Experiment'),
- '#options' => $opt_project,
- '#multiple' => FALSE,
- '#id' => 'download-sel-project',
- '#ajax' => array(
- 'event' => 'change',
- 'callback' => 'rawpheno_download_get_locations_traits',
- 'wrapper' => 'download-ajax-wrapper',
- 'progress' => array('type' => '', 'message' => '')
- ),
- );
-
- // This will reset the project select box on load and page refresh.
- drupal_add_js('jQuery(document).ready(function() {
- jQuery("#download-sel-project").val(0);
- })', 'inline');
-
- // Define the project ids required by the next field.
- if (isset($project_selected)) {
- // When a project is selected. Default to the project selected.
- $project_id = $project_selected;
- }
- else {
- // No project select. This is the default to the first project.
- $p = array_keys($opt_project);
- $project_id = reset($p);
- }
-
- // All Locations for the default project above. Default project is the
- // first project in the list.
- $sql = "SELECT DISTINCT value, value AS prj_location
- FROM pheno_plantprop
- WHERE
- type_id = (SELECT cvterm_id FROM {cvterm} cvt LEFT JOIN {cv} cv ON cv.cv_id = cvt.cv_id
- WHERE cvt.name = 'Location' AND cv.name = 'phenotype_plant_property_types') AND
- plant_id IN (SELECT plant_id FROM pheno_plant_project WHERE project_id IN (:project_id))
- ORDER BY value ASC";
-
- $opt_location = chado_query($sql, array(':project_id' => $project_id))
- ->fetchAllKeyed();
-
- $form['ajax_container']['sel_location'] = array(
- '#type' => 'select',
- '#title' => t('Location'),
- '#options' => $opt_location,
- '#multiple' => TRUE,
- '#size' => 7,
- '#id' => 'download-sel-location',
- '#ajax' => array(
- 'event' => 'change',
- 'callback' => 'rawpheno_download_get_traits',
- 'wrapper' => 'download-ajax-wrapper-traits',
- 'progress' => array('type' => '', 'message' => '')
- ),
- );
-
- $form['ajax_container']['chk_select_all_locations'] = array(
- '#title' => t('Select all Locations'),
- '#type' => 'checkbox',
- '#default_value' => 0,
- '#ajax' => array(
- 'event' => 'change',
- 'callback' => 'rawpheno_download_get_locations_traits',
- 'wrapper' => 'download-ajax-wrapper',
- 'progress' => array('type' => '', 'message' => '')
- ),
- '#id' => 'chk-select-all-locations',
- );
-
- $location_id = $opt_location;
-
- // Manage environment data file option.
- // Allow option when a project is selected and project and location combination
- // returns an environment data file.
- $add_option = FALSE;
-
- if (isset($project_selected) && $project_selected > 0) {
- if (isset($form_state['values']['sel_location'])
- && count($form_state['values']['sel_location']) > 0) {
-
- $location = $form_state['values']['sel_location'];
-
- $envfile = rawpheno_function_getenv($project_selected, $location);
- if ($envfile) {
- $add_option = TRUE;
- }
- }
- }
-
- drupal_add_js(array('rawpheno' => array('envdata_option' => $add_option)), array('type' => 'setting'));
-
-
- // TRAITS.
- // Select traits wrapper.
- $form['ajax_container']['ajax_container_traits'] = array(
- '#type' => 'markup',
- '#prefix' => '
',
- '#suffix' => '
',
- );
-
- // Get traits given a location and project.
- if (isset($project_selected) && isset($location)) {
- $project_id = $project_selected;
- $location_id = $location;
- }
-
- // The summarized list of cvterm_ids from MVIEW returned by inner most query will be passed to function that converts
- // comma separated values into individual values (cvterm_id numbers) and the result is the parameter of ANY clause
- // that will filter cvterms to only those in the list. Final rows are in JSON object and sorted alphabetically by name
- // that will be passed on to the select field of rawdata form.
- $sql_cvterm = "
- SELECT c_j.cvterm_json->>'id', c_j.cvterm_json->>'name' FROM (
- SELECT JSON_BUILD_OBJECT('id', cvterm_id, 'name', name) AS cvterm_json FROM {cvterm} WHERE cvterm_id = ANY ((
- SELECT STRING_TO_ARRAY(list_id.all_traits, ',') FROM (
- SELECT string_agg(DISTINCT all_traits, ',') AS all_traits
- FROM {rawpheno_rawdata_mview}
- WHERE
- location IN(:location)
- AND plant_id IN (SELECT plant_id FROM pheno_plant_project WHERE project_id = :project_id)
- ) AS list_id
- )::int[])
- ) AS c_j
- WHERE c_j.cvterm_json->>'name' NOT IN ('Rep', 'Entry', 'Location', 'Name', 'Plot', 'Planting Date (date)', '# of Seeds Planted (count)')
- ORDER BY c_j.cvterm_json->>'name' ASC
- ";
-
- $trait_set = chado_query($sql_cvterm, array(':location' => $location_id, ':project_id' => $project_id))
- ->fetchAllKeyed();
-
- $opt_trait = array_unique($trait_set);
-
- $form['ajax_container']['ajax_container_traits']['sel_trait'] = array(
- '#type' => 'select',
- '#title' => t('@trait_count Traits available', array('@trait_count' => count($opt_trait))),
- '#options' => $opt_trait,
- '#multiple' => TRUE,
- '#size' => 15,
- '#id' => 'download-sel-trait',
- );
-
- $form['ajax_container']['chk_select_all_traits'] = array(
- '#title' => t('Select all Traits'),
- '#type' => 'checkbox',
- '#default_value' => 0,
- '#id' => 'chk-select-all-traits',
- );
-
- $form['div_buttons'] = array(
- '#prefix' => '
',
- '#suffix' => '
',
- );
-
- $form['div_buttons']['chk_envdata'] = array(
- '#title' => t(' (Include Environment Data)', array('@img' => '../../' . $path . 'img/env.gif')),
- '#type' => 'checkbox',
- '#default_value' => 0,
- '#id' => 'chk-envdata',
- );
-
- $form['div_buttons']['chk_rfriendly'] = array(
- '#title' => t(' (Make R Friendly)', array('@img' => '../../' . $path . 'img/r.gif')),
- '#type' => 'checkbox',
- '#default_value' => 0,
- );
-
-
- $form['div_buttons']['download_submit_download'] = array(
- '#type' => 'submit',
- '#value' => 'Download',
- );
-
- $form['#attached']['js'] = array($path . 'js/rawpheno.download.script.js');
-
-
- return $form;
-}
-
-
-/**
- * Function callback: AJAX update location and traits select boxes when project is selected.
- */
-function rawpheno_download_get_locations_traits($form, $form_state) {
- return $form['ajax_container'];
-}
-
-
-/**
- * Function callback: AJAX update trait select box.
- */
-function rawpheno_download_get_traits($form, $form_state) {
- /*
- $location = $form_state['values']['sel_location'];
- $project = $form_state['values']['sel_project'];
-
- // Determine if the selected project is all project.
- if ($project == 0) {
- // Yes, then read the value of the hidden field containing project ids.
- $t = $form_state['values']['txt_project'];
- $project = explode(',', $t);
- }
-
- // Get all traits given a location and project.
- $opt_trait = rawpheno_download_load_traits($location, $project);
-
- // Update the #options value of select a trait select box.
- $form['ajax_container']['ajax_container_traits']['sel_trait']['#options'] = $opt_trait;
- // Update the title.
- $form['ajax_container']['ajax_container_traits']['sel_trait']['#title'] = t('@count_trait Traits available', array('@count_trait' => count($opt_trait)));
-*/
-
- return $form['ajax_container']['ajax_container_traits'];
-}
-
-
-/**
- * Implements hook_form_submit().
- *
- * Generate a comma separated values (csv) file based on the location and trait set selected.
- */
-function rawpheno_download_submit($form, &$form_state) {
- // Project select field.
- // Project by default is 0 - all projects then we want all project id field.
- // This is field is never an array.
- $prj = $form_state['values']['sel_project'];
- $all_prj = $form_state['values']['txt_project'];
- $prj = ($prj == 0) ? $all_prj : $prj;
-
- // Location select field.
- // Location select field is an empty array - all locations.
- // Otherwise, it will be an associative array where location is both key and value.
- // Convert this to comma separated string when there's anything else set to 0 - for all locations.
- $loc = $form_state['values']['sel_location'];
- // Location 1 + (and) Location 2 + .....
- $loc = (count($loc) > 0) ? implode('+', $loc) : 0;
-
- // Trait select field.
- // Trait select field is an empty array - all traits.
- // Otherwise, it will be an associative array where trait is both key and value.
- // Convert this to comma separated string when there's anything else set to 0 - for all traits.
- $trt = $form_state['values']['sel_trait'];
- $trt = (count($trt) > 0) ? implode(',', $trt) : 0;
-
- // Lastly, if user wants Environment Data and R version.
- $env = $form_state['values']['chk_envdata'];
- $rvr = $form_state['values']['chk_rfriendly'];
-
- // Construct environment data files archive.
- $env_filename = 0;
-
- if (isset($env) && $env == 1) {
- // Ensure that project and location combination return an environment data file.
- $project = explode(',', $prj);
- $location = explode('+', $loc);
-
- $files = rawpheno_function_getenv($project, $location);
-
- if (count($files) > 0) {
- // Env file available.
- $envs = array();
-
- foreach($files as $file) {
- $envs[] = $file->filename;
- }
-
- if (count($envs) == 1) {
- // Single env file found. Fetch the file (xlsx usually) and submit to tripal download.
- $env_filename = reset($envs);
- }
- else {
- // Multiple env files found. Fetch all files, tar (archive) and submit to tripal download.
- $public = drupal_realpath('public://');
- $tar_filename = 'environment_data_' . date('ymdis') . '.tar';
- $tar_file = $public . '/' . $tar_filename;
-
- $tar_cmd = 'tar -cf ' . escapeshellarg($tar_file) . ' -C ' . escapeshellarg($public) . ' ';
- $tar_cmd .= implode(' ', $envs) . ' 2>&1';
-
- // Package everything...
- shell_exec($tar_cmd);
- $env_filename = $tar_filename;
- }
- }
- }
-
- // Contain all query parameters/string into one string.
- // Decode first when reading this string using base64_decode() function.
- $url = 'p=' . $prj . '&l=' . $loc . '&t=' . $trt . '&r=' . $rvr . '&e=' . $env . '&file=' . $env_filename;
-
- // Format url for redirect.
- $form_state['redirect'] = array(
- '/phenotypes/raw/csv',
- array(
- 'query' => array(
- 'code' => base64_encode($url),
- ),
- ),
- );
-}
+uid);
+ $user_experiment = array_keys($user_experiment);
+ $allowed_experiment = array();
+
+ $experiments = explode('+', $param_experiment);
+ foreach($experiments as $exp) {
+ if (in_array($exp, $user_experiment)) {
+ $allowed_experiment[] = $exp;
+ }
+ }
+
+ if (count($allowed_experiment) > 0) {
+ $param_location = $query_vars['l'];
+ // Expected single value only for trait and germplasm.
+ $param_trait = (int) $query_vars['t'];
+ $param_stock = (int) $query_vars['g'];
+
+ if ($param_experiment > 0 && $param_location && $param_trait > 0) {
+ // Create query string.
+ $query_string = 'p=' . implode('+', $allowed_experiment) . '&l=' . $param_location . '&t=' . $param_trait . '&r=0&e=0&file=0&g=' . $param_stock;
+ drupal_goto('/phenotypes/raw/csv', array('query' => array('code' => base64_encode($query_string))));
+ }
+ }
+ }
+ }
+
+ // Attach CSS and JavaScript
+ $path = drupal_get_path('module', 'rawpheno') . '/theme/';
+ $form['#attached']['css'] = array($path . 'css/rawpheno.download.style.css');
+
+ // Navigation button. Related page of download page is rawdata/summary page.
+ $form['page_button'] = array(
+ '#type' => 'markup',
+ '#markup' => t('View Summary ❯'),
+ );
+
+ // PROJECT SELECT BOX.
+ if (isset($form_state['values']['sel_project'])) {
+ // Project selected.
+ $project_selected = $form_state['values']['sel_project'];
+ }
+
+ // Sort the project names according to Planting Date.
+ // Put project with recently uploaded data/ based on planting year
+ // first in the list.
+ $sql = "SELECT project_id, name
+ FROM {project} AS t1
+ RIGHT JOIN pheno_plant_project AS t2 USING (project_id)
+ LEFT JOIN pheno_measurements AS t3 USING (plant_id)
+ WHERE t3.type_id = (SELECT cvterm_id FROM {cvterm} WHERE name = 'Planting Date (date)' LIMIT 1)
+ GROUP BY project_id, name, t3.value
+ ORDER BY t3.value DESC";
+
+ $opt_project = chado_query($sql)
+ ->fetchAllKeyed();
+
+ // Project options:
+ if (count($opt_project) <= 0) {
+ // Module has no projects w/ data yet.
+ return $form;
+ }
+ else {
+ // Remove any duplicates from the sorted list.
+ $opt_project = array_unique($opt_project, SORT_REGULAR);
+ }
+
+ // AJAX wrapper.
+ // Main wrapper
+ $form['ajax_container'] = array(
+ '#type' => 'markup',
+ '#prefix' => '
',
+ '#suffix' => '
',
+ );
+
+ // This a hidden field containing all project id.
+ // This field will allow callback functions to get all project ids which is
+ // the equivalent of the option select all project from the project select box.
+ $form['ajax_container']['txt_project'] = array(
+ '#type' => 'hidden',
+ '#value' => implode(',', array_keys($opt_project)),
+ );
+
+ $form['ajax_container']['sel_project'] = array(
+ '#type' => 'select',
+ '#title' => t('Experiment'),
+ '#options' => $opt_project,
+ '#multiple' => FALSE,
+ '#id' => 'download-sel-project',
+ '#ajax' => array(
+ 'event' => 'change',
+ 'callback' => 'rawpheno_download_get_locations_traits',
+ 'wrapper' => 'download-ajax-wrapper',
+ 'progress' => array('type' => '', 'message' => '')
+ ),
+ );
+
+ // This will reset the project select box on load and page refresh.
+ drupal_add_js('jQuery(document).ready(function() {
+ jQuery("#download-sel-project").val(0);
+ })', 'inline');
+
+ // Define the project ids required by the next field.
+ if (isset($project_selected)) {
+ // When a project is selected. Default to the project selected.
+ $project_id = $project_selected;
+ }
+ else {
+ // No project select. This is the default to the first project.
+ $p = array_keys($opt_project);
+ $project_id = reset($p);
+ }
+
+ // All Locations for the default project above. Default project is the
+ // first project in the list.
+ $sql = "SELECT DISTINCT value, value AS prj_location
+ FROM pheno_plantprop
+ WHERE
+ type_id = (SELECT cvterm_id FROM {cvterm} cvt LEFT JOIN {cv} cv ON cv.cv_id = cvt.cv_id
+ WHERE cvt.name = 'Location' AND cv.name = 'phenotype_plant_property_types') AND
+ plant_id IN (SELECT plant_id FROM pheno_plant_project WHERE project_id IN (:project_id))
+ ORDER BY value ASC";
+
+ $opt_location = chado_query($sql, array(':project_id' => $project_id))
+ ->fetchAllKeyed();
+
+ $form['ajax_container']['sel_location'] = array(
+ '#type' => 'select',
+ '#title' => t('Location'),
+ '#options' => $opt_location,
+ '#multiple' => TRUE,
+ '#size' => 7,
+ '#id' => 'download-sel-location',
+ '#ajax' => array(
+ 'event' => 'change',
+ 'callback' => 'rawpheno_download_get_traits',
+ 'wrapper' => 'download-ajax-wrapper-traits',
+ 'progress' => array('type' => '', 'message' => '')
+ ),
+ );
+
+ $form['ajax_container']['chk_select_all_locations'] = array(
+ '#title' => t('Select all Locations'),
+ '#type' => 'checkbox',
+ '#default_value' => 0,
+ '#ajax' => array(
+ 'event' => 'change',
+ 'callback' => 'rawpheno_download_get_locations_traits',
+ 'wrapper' => 'download-ajax-wrapper',
+ 'progress' => array('type' => '', 'message' => '')
+ ),
+ '#id' => 'chk-select-all-locations',
+ );
+
+ $location_id = $opt_location;
+
+ // Manage environment data file option.
+ // Allow option when a project is selected and project and location combination
+ // returns an environment data file.
+ $add_option = FALSE;
+
+ if (isset($project_selected) && $project_selected > 0) {
+ if (isset($form_state['values']['sel_location'])
+ && count($form_state['values']['sel_location']) > 0) {
+
+ $location = $form_state['values']['sel_location'];
+
+ $envfile = rawpheno_function_getenv($project_selected, $location);
+ if ($envfile) {
+ $add_option = TRUE;
+ }
+ }
+ }
+
+ drupal_add_js(array('rawpheno' => array('envdata_option' => $add_option)), array('type' => 'setting'));
+
+
+ // TRAITS.
+ // Select traits wrapper.
+ $form['ajax_container']['ajax_container_traits'] = array(
+ '#type' => 'markup',
+ '#prefix' => '
',
+ '#suffix' => '
',
+ );
+
+ // Get traits given a location and project.
+ if (isset($project_selected) && isset($location)) {
+ $project_id = $project_selected;
+ $location_id = $location;
+ }
+
+ // The summarized list of cvterm_ids from MVIEW returned by inner most query will be passed to function that converts
+ // comma separated values into individual values (cvterm_id numbers) and the result is the parameter of ANY clause
+ // that will filter cvterms to only those in the list. Final rows are in JSON object and sorted alphabetically by name
+ // that will be passed on to the select field of rawdata form.
+ $sql_cvterm = "
+ SELECT c_j.cvterm_json->>'id', c_j.cvterm_json->>'name' FROM (
+ SELECT JSON_BUILD_OBJECT('id', cvterm_id, 'name', name) AS cvterm_json FROM {cvterm} WHERE cvterm_id = ANY ((
+ SELECT STRING_TO_ARRAY(list_id.all_traits, ',') FROM (
+ SELECT string_agg(DISTINCT all_traits, ',') AS all_traits
+ FROM {rawpheno_rawdata_mview}
+ WHERE
+ location IN(:location)
+ AND plant_id IN (SELECT plant_id FROM pheno_plant_project WHERE project_id = :project_id)
+ ) AS list_id
+ )::int[])
+ ) AS c_j
+ WHERE c_j.cvterm_json->>'name' NOT IN ('Rep', 'Entry', 'Location', 'Name', 'Plot', 'Planting Date (date)', '# of Seeds Planted (count)')
+ ORDER BY c_j.cvterm_json->>'name' ASC
+ ";
+
+ $trait_set = chado_query($sql_cvterm, array(':location' => $location_id, ':project_id' => $project_id))
+ ->fetchAllKeyed();
+
+ $opt_trait = array_unique($trait_set);
+
+ $form['ajax_container']['ajax_container_traits']['sel_trait'] = array(
+ '#type' => 'select',
+ '#title' => t('@trait_count Traits available', array('@trait_count' => count($opt_trait))),
+ '#options' => $opt_trait,
+ '#multiple' => TRUE,
+ '#size' => 15,
+ '#id' => 'download-sel-trait',
+ );
+
+ $form['ajax_container']['chk_select_all_traits'] = array(
+ '#title' => t('Select all Traits'),
+ '#type' => 'checkbox',
+ '#default_value' => 0,
+ '#id' => 'chk-select-all-traits',
+ );
+
+ $form['div_buttons'] = array(
+ '#prefix' => '
',
+ '#suffix' => '
',
+ );
+
+ $form['div_buttons']['chk_envdata'] = array(
+ '#title' => t(' (Include Environment Data)', array('@img' => '../../' . $path . 'img/env.gif')),
+ '#type' => 'checkbox',
+ '#default_value' => 0,
+ '#id' => 'chk-envdata',
+ );
+
+ $form['div_buttons']['chk_rfriendly'] = array(
+ '#title' => t(' (Make R Friendly)', array('@img' => '../../' . $path . 'img/r.gif')),
+ '#type' => 'checkbox',
+ '#default_value' => 0,
+ );
+
+
+ $form['div_buttons']['download_submit_download'] = array(
+ '#type' => 'submit',
+ '#value' => 'Download',
+ );
+
+ $form['#attached']['js'] = array($path . 'js/rawpheno.download.script.js');
+
+ return $form;
+}
+
+
+/**
+ * Function callback: AJAX update location and traits select boxes when project is selected.
+ */
+function rawpheno_download_get_locations_traits($form, $form_state) {
+ return $form['ajax_container'];
+}
+
+
+/**
+ * Function callback: AJAX update trait select box.
+ */
+function rawpheno_download_get_traits($form, $form_state) {
+ /*
+ $location = $form_state['values']['sel_location'];
+ $project = $form_state['values']['sel_project'];
+
+ // Determine if the selected project is all project.
+ if ($project == 0) {
+ // Yes, then read the value of the hidden field containing project ids.
+ $t = $form_state['values']['txt_project'];
+ $project = explode(',', $t);
+ }
+
+ // Get all traits given a location and project.
+ $opt_trait = rawpheno_download_load_traits($location, $project);
+
+ // Update the #options value of select a trait select box.
+ $form['ajax_container']['ajax_container_traits']['sel_trait']['#options'] = $opt_trait;
+ // Update the title.
+ $form['ajax_container']['ajax_container_traits']['sel_trait']['#title'] = t('@count_trait Traits available', array('@count_trait' => count($opt_trait)));
+*/
+
+ return $form['ajax_container']['ajax_container_traits'];
+}
+
+
+/**
+ * Implements hook_form_submit().
+ *
+ * Generate a comma separated values (csv) file based on the location and trait set selected.
+ */
+function rawpheno_download_submit($form, &$form_state) {
+ // Project select field.
+ // Project by default is 0 - all projects then we want all project id field.
+ // This is field is never an array.
+ $prj = $form_state['values']['sel_project'];
+ $all_prj = $form_state['values']['txt_project'];
+ $prj = ($prj == 0) ? $all_prj : $prj;
+
+ // Location select field.
+ // Location select field is an empty array - all locations.
+ // Otherwise, it will be an associative array where location is both key and value.
+ // Convert this to comma separated string when there's anything else set to 0 - for all locations.
+ $loc = $form_state['values']['sel_location'];
+ // Location 1 + (and) Location 2 + .....
+ $loc = (count($loc) > 0) ? implode('+', $loc) : 0;
+
+ // Trait select field.
+ // Trait select field is an empty array - all traits.
+ // Otherwise, it will be an associative array where trait is both key and value.
+ // Convert this to comma separated string when there's anything else set to 0 - for all traits.
+ $trt = $form_state['values']['sel_trait'];
+ $trt = (count($trt) > 0) ? implode(',', $trt) : 0;
+
+ // Lastly, if user wants Environment Data and R version.
+ $env = $form_state['values']['chk_envdata'];
+ $rvr = $form_state['values']['chk_rfriendly'];
+
+ // Construct environment data files archive.
+ $env_filename = 0;
+
+ if (isset($env) && $env == 1) {
+ // Ensure that project and location combination return an environment data file.
+ $project = explode(',', $prj);
+ $location = explode('+', $loc);
+
+ $files = rawpheno_function_getenv($project, $location);
+
+ if (count($files) > 0) {
+ // Env file available.
+ $envs = array();
+
+ foreach($files as $file) {
+ $envs[] = $file->filename;
+ }
+
+ if (count($envs) == 1) {
+ // Single env file found. Fetch the file (xlsx usually) and submit to tripal download.
+ $env_filename = reset($envs);
+ }
+ else {
+ // Multiple env files found. Fetch all files, tar (archive) and submit to tripal download.
+ $public = drupal_realpath('public://');
+ $tar_filename = 'environment_data_' . date('ymdis') . '.tar';
+ $tar_file = $public . '/' . $tar_filename;
+
+ $tar_cmd = 'tar -cf ' . escapeshellarg($tar_file) . ' -C ' . escapeshellarg($public) . ' ';
+ $tar_cmd .= implode(' ', $envs) . ' 2>&1';
+
+ // Package everything...
+ shell_exec($tar_cmd);
+ $env_filename = $tar_filename;
+ }
+ }
+ }
+
+ // Contain all query parameters/string into one string.
+ // Decode first when reading this string using base64_decode() function.
+ $url = 'p=' . $prj . '&l=' . $loc . '&t=' . $trt . '&r=' . $rvr . '&e=' . $env . '&file=' . $env_filename . '&g=0';
+
+ // Format url for redirect.
+ $form_state['redirect'] = array(
+ '/phenotypes/raw/csv',
+ array(
+ 'query' => array(
+ 'code' => base64_encode($url),
+ ),
+ ),
+ );
+}
diff --git a/include/rawpheno.function.measurements.inc b/includes/rawpheno.function.measurements.inc
similarity index 100%
rename from include/rawpheno.function.measurements.inc
rename to includes/rawpheno.function.measurements.inc
diff --git a/include/rawpheno.instructions.form.inc b/includes/rawpheno.instructions.form.inc
old mode 100755
new mode 100644
similarity index 97%
rename from include/rawpheno.instructions.form.inc
rename to includes/rawpheno.instructions.form.inc
index 513483c..a095565
--- a/include/rawpheno.instructions.form.inc
+++ b/includes/rawpheno.instructions.form.inc
@@ -1,506 +1,506 @@
- 'markup',
- '#markup' => t('Upload Data ❯'),
- );
-
- // Attach CSS.
- $path = drupal_get_path('module', 'rawpheno') . '/theme/';
- $form['#attached']['css'] = array($path . 'css/rawpheno.instructions.style.css');
-
- // Create a select box containing projects available - these are projects
- // that have associated column header set and must have at least 1 essential column header.
- // The projects are filtered to show only projects assigned to user.
- $all_project = rawpheno_function_user_project($GLOBALS['user']->uid);
-
- // No projects assined to the user.
- if (count($all_project) < 1) {
- return $form;
- }
-
- // Ensure that project is valid.
- if ($project_id) {
- if (!in_array($project_id, array_keys($all_project))) {
- // Project does not exist.
- $form['message_invalid_project'] = array(
- '#markup' => '
Experiment does not exist.
'
- );
- }
- else {
- // Project is valid. Null this.
- $form['message_invalid_project'] = array();
- }
- }
- else {
- // When no project is supplied, then default this page to the most recent project
- // available from the projects defined by admin.
- $project_id = array_keys($all_project)[0];
- }
-
- // Project Name.
- $project_name = $all_project[$project_id];
-
- // Given a project id, construct the table required for each trait type set.
- // All trait types, need to remove type plantproperty.
- $trait_set = rawpheno_function_trait_types();
-
- $sql = "SELECT project_cvterm_id AS id FROM {pheno_project_cvterm}
- WHERE project_id = :project_id AND type <> :plantprop
- ORDER BY type, cvterm_id ASC";
-
- $args = array(':project_id' => $project_id, ':plantprop' => $trait_set['type4']);
- $cvterm_id = db_query($sql, $args);
-
- // Array to hold trait row.
- $arr_cvterm = array();
-
- foreach($cvterm_id as $id) {
- $cvterm = rawpheno_function_header_properties($id->id);
- // Create an array that will translate to a row in table.
- $arr_cvterm[ $cvterm['type'] ][] = array(
- '
' . $cvterm['name'] . '
',
- $cvterm['method'],
- $cvterm['definition']
- );
- }
-
- // Construct table.
- $arr_tbl_args['empty'] = '0 Column Header';
- $arr_tbl_args['header'] = array(t('Column Header/Trait'), t('Collection Method'), t('Definition'));
-
- unset($trait_set['type4']);
-
- foreach($trait_set as $type) {
- $arr_tbl_args['rows'] = isset($arr_cvterm[$type]) ? $arr_cvterm[$type] : array();
-
- $form['tbl_project_headers_' . $type] = array(
- '#markup' => (count($arr_tbl_args['rows']) <= 0) ? 'no-trait' : theme('table', $arr_tbl_args),
- );
- }
-
- // Project and project select box.
- $form['project_panel'] = array(
- '#markup' => $project_name
- );
-
- $form['sel_project'] = array(
- '#type' => 'select',
- '#options' => array(0 => 'Please select an experiment') + $all_project,
- '#id' => 'rawpheno-ins-sel-project'
- );
-
- $ins_path = base_path() . 'phenotypes/raw/instructions/';
- // Make the project select box into a jump menu and add the project id number to the url.
- drupal_add_js('jQuery(document).ready(function() {
- jQuery("#rawpheno-ins-sel-project").change(function(){
- if (jQuery(this).val() > 0) {
- window.location.href = "'. $ins_path .'" + jQuery(this).val();
- }
- });
- })', 'inline');
-
-
- // NOTE: When project is AGILE, download data collection spreadsheet link points to
- // pre-made AGILE spreadsheet, otherwise, instructions page will generate the spreadsheet file. To ensure that
- // the column headers matched for AGILE project, a check (when no new column header added tru admin then download
- // pre-made, else generate) is required before downloading the file.
-
- // Provide user with link to download project specific data collection spreadsheet file.
- // When the project is the default project (AGILE) then supply a ready made spreadsheet.
-
- $file_path = ($tmp_project == null) ? 'instructions/spreadsheet/' : 'spreadsheet/';
- $link_to_xls = $file_path . $project_id;
-
- if ($project_name == 'AGILE: Application of Genomic Innovation in the Lentil Economy') {
- // Compare the AGILE-specific column header to check if they match
- $AGILE_column_headers = rawpheno_function_headers('expected');
- $AGILE_essential_headers = rawpheno_function_headers('essential');
- $AGILE_required_headers = rawpheno_function_headers('required');
- $AGILE_essential_headers = array_diff($AGILE_essential_headers, $AGILE_required_headers);
-
- // Query the AGILE project column headers.
- $sql = "SELECT name, type FROM {cvterm} RIGHT JOIN pheno_project_cvterm USING(cvterm_id) WHERE project_id = :project_id";
- $args = array(':project_id' => $project_id);
- $h = chado_query($sql, $args)
- ->fetchAllKeyed();
-
- // Manually add Name, since Name is not added to project.
- $h['Name'] = 'plantproperty';
-
- $header_found_count = 0;
- $essential_found_count = 0;
- $essential_count = 0;
- $header_count = 0;
-
- foreach($h as $header => $type) {
- // Ignore trait contributed.
- if ($type == $trait_set['type5']) {
- continue;
- }
-
- $header_count++;
-
- // Use ready made AGILE data collection spreadsheet.
- // Total number of headers, excluding contributed matches AGILE traits.
- if (in_array($header, $AGILE_column_headers)) {
- $header_found_count++;
- }
-
- // Same essential traits as in AGILE traits.
- if ($type == $trait_set['type1'] AND in_array($header, $AGILE_essential_headers)) {
- $essential_found_count++;
- }
-
- // Same number of essential traits as in AGILE traits.
- if ($type == $trait_set['type1']) {
- $essential_count++;
- }
-
- // Finally, name must be AGILE: Application of Genomic Innovation in the Lentil Economy as condition for this block.
- }
-
- // Check if AGILE trait set was not altered.
- if ($header_count == count($AGILE_column_headers)
- && $header_found_count == count($AGILE_column_headers)
- && $essential_found_count == count($AGILE_essential_headers)
- && $essential_count == count($AGILE_essential_headers)) {
- // AGILE Project match the original trait set. Download ready-made data collection spreadsheet file.
- $link_to_xls = file_create_url('public://AGILE-PhenotypeDataCollection-v5.xlsx.zip');
- }
- }
-
- $form['download_data_collection'] = array(
- '#markup' => t('Download Data Collection Spreadsheet', array('@link' => $link_to_xls))
- );
-
- // Search field with autocomplete feature.
- $form['txt_search'] = array(
- '#title' => '',
- '#type' => 'textfield',
- '#maxlength' => 65,
- '#size' => 65,
- '#default_value' => t('Search Trait'),
- '#autocomplete_path' => 'phenotypes/raw/instructions/autocomplete/' . $project_id,
- );
-
- $form['btn_search'] = array(
- '#type' => 'markup',
- '#markup' => '',
- );
-
- // Hidden field containing url to json.
- $form['json_url'] = array(
- '#type' => 'hidden',
- '#value' => $GLOBALS['base_url'] . '/phenotypes/raw/instructions/autocomplete/' . $project_id,
- '#attributes' => array('id' => array('traits-json'))
- );
-
- // Attach JQuery UI library and JavaScript.
- $form['#attached']['library'][] = array('system', 'ui.tabs');
- $form['#attached']['js'] = array($path . 'js/rawpheno.instructions.script.js');
-
- return $form;
-}
-
-
-/**
- * Function create a spreadsheet file.
- *
- */
-function rawpheno_instructions_create_spreadsheet($project_id) {
- // Query column headers specific to a project, given a project id.
- if (isset($project_id) AND $project_id > 0) {
- // Array to hold all trait types.
- $trait_type = rawpheno_function_trait_types();
-
- // Exclude the plant property from the set of headers. They are pre-inserted to the array
- // of column headers passed to the spreadsheet writer.
- $sql = "SELECT project_cvterm_id, name, type
- FROM {cvterm} RIGHT JOIN pheno_project_cvterm USING(cvterm_id)
- WHERE project_id = :project_id AND type NOT IN ( :exclude_property )
- ORDER BY type ASC";
-
- $args = array(':project_id' => $project_id, ':exclude_property' => array($trait_type['type4'], $trait_type['type5']));
- $cvterm = chado_query($sql, $args);
-
- // Only when project has headers.
- if ($cvterm->rowCount() > 0) {
- // Array to hold the column headers passed to the excel writer.
- $col_headers = array();
- // Array to hold standard procedure, which basically is
- // the traits definition and collection method.
- $instructions_data = array();
-
- // Get the data type per unit. This type will be the cell type in the spreadsheet.
- $data_type = rawpheno_function_default_unit('type');
-
- // Prepend the array with plant property column headers.
- $col_headers = array(
- 'Plot' => 'integer',
- 'Entry' => 'integer',
- 'Name' => 'string',
- 'Rep' => 'integer',
- 'Location' => 'string'
- );
-
- // Start at F column taking into account plant properties.
- // A for Plot, B for Entry and so on (A-E is 5 cols).
- $l = 'F';
- $cell_i = array();
-
- // Assign the data type for each header based on the unit it contains.
- $h = array('name' => 'Trait', 'definition' => 'Definition', 'method' => 'Collection Method');
-
- foreach($cvterm as $trait) {
- // Get the unit.
- $u = rawpheno_function_header_unit($trait->name);
- $unit = isset($data_type[$u]) ? $data_type[$u] : 'string';
-
- $col_headers[$trait->name] = $unit;
-
- // Highlight the cells when it is essential trait.
- if ($trait->type == $trait_type['type1']) {
- array_push($cell_i, $l . '1');
- // Increment F column.
- $l++;
- }
-
- // Get header method and definition information.
- $t = rawpheno_function_header_properties($trait->project_cvterm_id);
-
- foreach($h as $m_i => $m) {
- $star = ($m_i == 'name') ? '*' : '';
-
- if (strlen($t[$m_i]) < 80) {
- // Short text, save it.
- array_push($instructions_data, array($star . $m . ':', $t[$m_i]));
- }
- else {
- // Hard-wrap long lines into shorter line and put each
- // line into a cell/row.
- $wrapped_string = wordwrap($t[$m_i], 100, "\n");
- $chunks = explode("\n", $wrapped_string);
-
- foreach($chunks as $i => $chunk) {
- $ins_text = ($i == 0) ? array($star . $m . ':', $chunk) : array('', $chunk);
- array_push($instructions_data, $ins_text);
- }
- }
- }
-
- // Add extra new line.
- array_push($instructions_data, array('' , ''));
- }
-
- // Load spreadsheet writer library.
- $xlsx_writer = libraries_load('spreadsheet_writer');
- include_once $xlsx_writer['library path'] . '/'. $xlsx_writer['files'][0];
-
- $writer = new XLSXWriter();
- // Measurement tab.
- @$writer->writeSheet(array(), 'Measurements', $col_headers,
- array(
- // The entire header row apply these styles.
- array(
- 'font' =>
- array(
- 'name' => 'Arial',
- 'size' => '11',
- 'color' => '000000',
- 'bold' => false,
- 'italic' => false,
- 'underline' => false
- ),
- 'wrapText' => true,
- 'verticalAlign' => 'top',
- 'horizontalAlign' => 'center',
- 'fill' => array('color' => 'F7F7F7'),
- 'border' => array('style' => 'thin', 'color' => 'A0A0A0'),
- 'rows' => array('0')
- ),
- // Once the styles above have been applied, style the plant property headers.
- array(
- 'font' =>
- array(
- 'name' => 'Arial',
- 'size' => '11',
- 'color' => '000000',
- 'bold' => true,
- 'italic' => false,
- 'underline' => false
- ),
- 'verticalAlign' => 'bottom',
- 'horizontalAlign' => 'center',
- 'fill' => array('color' => 'EAEAEA'),
- 'border' => array('style' => 'thin', 'color' => 'A0A0A0'),
- 'cells' => array('A1', 'B1', 'C1', 'D1', 'E1')
- ),
- // Make sure to style the essential trait/header.
- array(
- 'font' =>
- array(
- 'name' => 'Arial',
- 'size' => '11',
- 'color' => '008000',
- 'bold' => true,
- 'italic' => false,
- 'underline' => false
- ),
- 'wrapText' => true,
- 'verticalAlign' => 'top',
- 'horizontalAlign' => 'center',
- 'fill' => array('color' => 'F5FFDF'),
- 'border' => array('style' => 'thin', 'color' => 'A0A0A0'),
- 'cells' => $cell_i
- )
- )
- );
-
- // Standard procedure tab.
- // Load trait definition and data collection method to this sheet.
- $instructions_header = array();
- @$writer->writeSheet($instructions_data, 'Instructions', $instructions_header,
- array(
- array(
- 'font' =>
- array(
- 'name' => 'Arial',
- 'size' => '11',
- 'color' => '000000',
- 'bold' => true,
- 'italic' => false,
- 'underline' => false
- ),
- 'wrapText' => true,
- 'columns' => '0',
- ),
- array(
- 'font' =>
- array(
- 'size' => '12',
- ),
- 'wrapText' => false,
- 'columns' => '1',
- ),
- )
- );
-
- // Calculator tab.
- $calc_header = array('CALCULATE DAYS TO' => 'string');
- $calc_data =
- array(
- array('Planting Date', '2015-10-06'),
- array('Current Date', date('Y-m-d')),
- array('Current "Days till"', '=B3 - B2'),
- array('',''),
- array('Instructions', ''),
- array('', ''),
- array('Fill out the planting date indicated in the measurements tab, as well as, the current date.', ''),
- array('The "Days till" date will then be calculated for you.', '')
- );
-
- @$writer->writeSheet($calc_data, 'Calculate Days to', $calc_header,
- array(
- array(
- 'font' =>
- array(
- 'name' => 'Arial',
- 'size' => '20',
- 'color' => '000000',
- 'bold' => true,
- 'italic' => false,
- 'underline' => false
- ),
- 'wrapText' => false,
- 'rows' => array('0'),
- ),
- array(
- 'font' =>
- array(
- 'name' => 'Arial',
- 'size' => '11',
- 'color' => 'FFFFFF',
- 'bold' => true,
- 'italic' => false,
- 'underline' => false
- ),
- 'wrapText' => true,
- 'fill' => array('color' => '305673'),
- 'border' => array('style' => 'thin', 'color' => 'A0A0A0'),
- 'rows' => array('1'),
- ),
- array(
- 'font' =>
- array(
- 'name' => 'Arial',
- 'size' => '11',
- 'color' => '000000',
- 'bold' => true,
- 'italic' => false,
- 'underline' => false
- ),
- 'wrapText' => true,
- 'fill' => array('color' => 'F7F7F7'),
- 'border' => array('style' => 'thin', 'color' => 'A0A0A0'),
- 'rows' => array('2'),
- ),
- array(
- 'font' =>
- array(
- 'name' => 'Arial',
- 'size' => '11',
- 'color' => '000000',
- 'bold' => true,
- 'italic' => false,
- 'underline' => false
- ),
- 'wrapText' => true,
- 'fill' => array('color' => '79a183'),
- 'border' => array('style' => 'thin', 'color' => 'A0A0A0'),
- 'rows' => array('3'),
- )
- )
- );
-
- // Data collection spreadsheet name contains the following:
- // Project id, name of the user, date and time.
- $filename = 'datacollection_' . $project_id . '_' . str_replace(' ', '_', $GLOBALS['user']->name) .'_'. date('YMd') .'_'. time() . '.xlsx';
- $file = file_save_data($writer->writeToString(), 'public://' . $filename);
-
- // Launch save file window and ask user to save file.
- $http_headers = array(
- 'Content-Type' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
- 'Content-Disposition' => 'attachment; filename="' . $filename . '"',
- );
-
- if (strpos($_SERVER['HTTP_USER_AGENT'], 'MSIE')) {
- $http_headers['Cache-Control'] = 'must-revalidate, post-check=0, pre-check=0';
- $http_headers['Pragma'] = 'public';
- }
- else {
- $http_headers['Pragma'] = 'no-cache';
- }
-
- file_transfer($file->uri, $http_headers);
-
- // Just in case the auto download fails, provide a link to manually download the file.
- print 'Download Data Collection Spreadsheet';
- }
- else {
- // Here project not found.
- print 'Experiment not found.';
- }
- }
- else {
- // Project id is not valid.
- print 'Experiment ID number is invalid.';
- }
-}
+ 'markup',
+ '#markup' => t('Upload Data ❯'),
+ );
+
+ // Attach CSS.
+ $path = drupal_get_path('module', 'rawpheno') . '/theme/';
+ $form['#attached']['css'] = array($path . 'css/rawpheno.instructions.style.css');
+
+ // Create a select box containing projects available - these are projects
+ // that have associated column header set and must have at least 1 essential column header.
+ // The projects are filtered to show only projects assigned to user.
+ $all_project = rawpheno_function_user_project($GLOBALS['user']->uid);
+
+ // No projects assined to the user.
+ if (count($all_project) < 1) {
+ return $form;
+ }
+
+ // Ensure that project is valid.
+ if ($project_id) {
+ if (!in_array($project_id, array_keys($all_project))) {
+ // Project does not exist.
+ $form['message_invalid_project'] = array(
+ '#markup' => '
Experiment does not exist.
'
+ );
+ }
+ else {
+ // Project is valid. Null this.
+ $form['message_invalid_project'] = array();
+ }
+ }
+ else {
+ // When no project is supplied, then default this page to the most recent project
+ // available from the projects defined by admin.
+ $project_id = array_keys($all_project)[0];
+ }
+
+ // Project Name.
+ $project_name = $all_project[$project_id];
+
+ // Given a project id, construct the table required for each trait type set.
+ // All trait types, need to remove type plantproperty.
+ $trait_set = rawpheno_function_trait_types();
+
+ $sql = "SELECT project_cvterm_id AS id FROM {pheno_project_cvterm}
+ WHERE project_id = :project_id AND type <> :plantprop
+ ORDER BY type, cvterm_id ASC";
+
+ $args = array(':project_id' => $project_id, ':plantprop' => $trait_set['type4']);
+ $cvterm_id = db_query($sql, $args);
+
+ // Array to hold trait row.
+ $arr_cvterm = array();
+
+ foreach($cvterm_id as $id) {
+ $cvterm = rawpheno_function_header_properties($id->id);
+ // Create an array that will translate to a row in table.
+ $arr_cvterm[ $cvterm['type'] ][] = array(
+ '
' . $cvterm['name'] . '
',
+ $cvterm['method'],
+ $cvterm['definition']
+ );
+ }
+
+ // Construct table.
+ $arr_tbl_args['empty'] = '0 Column Header';
+ $arr_tbl_args['header'] = array(t('Column Header/Trait'), t('Collection Method'), t('Definition'));
+
+ unset($trait_set['type4']);
+
+ foreach($trait_set as $type) {
+ $arr_tbl_args['rows'] = isset($arr_cvterm[$type]) ? $arr_cvterm[$type] : array();
+
+ $form['tbl_project_headers_' . $type] = array(
+ '#markup' => (count($arr_tbl_args['rows']) <= 0) ? 'no-trait' : theme('table', $arr_tbl_args),
+ );
+ }
+
+ // Project and project select box.
+ $form['project_panel'] = array(
+ '#markup' => $project_name
+ );
+
+ $form['sel_project'] = array(
+ '#type' => 'select',
+ '#options' => array(0 => 'Please select an experiment') + $all_project,
+ '#id' => 'rawpheno-ins-sel-project'
+ );
+
+ $ins_path = base_path() . 'phenotypes/raw/instructions/';
+ // Make the project select box into a jump menu and add the project id number to the url.
+ drupal_add_js('jQuery(document).ready(function() {
+ jQuery("#rawpheno-ins-sel-project").change(function(){
+ if (jQuery(this).val() > 0) {
+ window.location.href = "'. $ins_path .'" + jQuery(this).val();
+ }
+ });
+ })', 'inline');
+
+
+ // NOTE: When project is AGILE, download data collection spreadsheet link points to
+ // pre-made AGILE spreadsheet, otherwise, instructions page will generate the spreadsheet file. To ensure that
+ // the column headers matched for AGILE project, a check (when no new column header added tru admin then download
+ // pre-made, else generate) is required before downloading the file.
+
+ // Provide user with link to download project specific data collection spreadsheet file.
+ // When the project is the default project (AGILE) then supply a ready made spreadsheet.
+
+ $file_path = ($tmp_project == null) ? 'instructions/spreadsheet/' : 'spreadsheet/';
+ $link_to_xls = $file_path . $project_id;
+
+ if ($project_name == 'AGILE: Application of Genomic Innovation in the Lentil Economy') {
+ // Compare the AGILE-specific column header to check if they match
+ $AGILE_column_headers = rawpheno_function_headers('expected');
+ $AGILE_essential_headers = rawpheno_function_headers('essential');
+ $AGILE_required_headers = rawpheno_function_headers('required');
+ $AGILE_essential_headers = array_diff($AGILE_essential_headers, $AGILE_required_headers);
+
+ // Query the AGILE project column headers.
+ $sql = "SELECT name, type FROM {cvterm} RIGHT JOIN pheno_project_cvterm USING(cvterm_id) WHERE project_id = :project_id";
+ $args = array(':project_id' => $project_id);
+ $h = chado_query($sql, $args)
+ ->fetchAllKeyed();
+
+ // Manually add Name, since Name is not added to project.
+ $h['Name'] = 'plantproperty';
+
+ $header_found_count = 0;
+ $essential_found_count = 0;
+ $essential_count = 0;
+ $header_count = 0;
+
+ foreach($h as $header => $type) {
+ // Ignore trait contributed.
+ if ($type == $trait_set['type5']) {
+ continue;
+ }
+
+ $header_count++;
+
+ // Use ready made AGILE data collection spreadsheet.
+ // Total number of headers, excluding contributed matches AGILE traits.
+ if (in_array($header, $AGILE_column_headers)) {
+ $header_found_count++;
+ }
+
+ // Same essential traits as in AGILE traits.
+ if ($type == $trait_set['type1'] AND in_array($header, $AGILE_essential_headers)) {
+ $essential_found_count++;
+ }
+
+ // Same number of essential traits as in AGILE traits.
+ if ($type == $trait_set['type1']) {
+ $essential_count++;
+ }
+
+ // Finally, name must be AGILE: Application of Genomic Innovation in the Lentil Economy as condition for this block.
+ }
+
+ // Check if AGILE trait set was not altered.
+ if ($header_count == count($AGILE_column_headers)
+ && $header_found_count == count($AGILE_column_headers)
+ && $essential_found_count == count($AGILE_essential_headers)
+ && $essential_count == count($AGILE_essential_headers)) {
+ // AGILE Project match the original trait set. Download ready-made data collection spreadsheet file.
+ $link_to_xls = file_create_url('public://AGILE-PhenotypeDataCollection-v5.xlsx.zip');
+ }
+ }
+
+ $form['download_data_collection'] = array(
+ '#markup' => t('Download Data Collection Spreadsheet', array('@link' => $link_to_xls))
+ );
+
+ // Search field with autocomplete feature.
+ $form['txt_search'] = array(
+ '#title' => '',
+ '#type' => 'textfield',
+ '#maxlength' => 65,
+ '#size' => 65,
+ '#default_value' => t('Search Trait'),
+ '#autocomplete_path' => 'phenotypes/raw/instructions/autocomplete/' . $project_id,
+ );
+
+ $form['btn_search'] = array(
+ '#type' => 'markup',
+ '#markup' => '',
+ );
+
+ // Hidden field containing url to json.
+ $form['json_url'] = array(
+ '#type' => 'hidden',
+ '#value' => $GLOBALS['base_url'] . '/phenotypes/raw/instructions/autocomplete/' . $project_id,
+ '#attributes' => array('id' => array('traits-json'))
+ );
+
+ // Attach JQuery UI library and JavaScript.
+ $form['#attached']['library'][] = array('system', 'ui.tabs');
+ $form['#attached']['js'] = array($path . 'js/rawpheno.instructions.script.js');
+
+ return $form;
+}
+
+
+/**
+ * Function create a spreadsheet file.
+ *
+ */
+function rawpheno_instructions_create_spreadsheet($project_id) {
+ // Query column headers specific to a project, given a project id.
+ if (isset($project_id) AND $project_id > 0) {
+ // Array to hold all trait types.
+ $trait_type = rawpheno_function_trait_types();
+
+ // Exclude the plant property from the set of headers. They are pre-inserted to the array
+ // of column headers passed to the spreadsheet writer.
+ $sql = "SELECT project_cvterm_id, name, type
+ FROM {cvterm} RIGHT JOIN pheno_project_cvterm USING(cvterm_id)
+ WHERE project_id = :project_id AND type NOT IN ( :exclude_property )
+ ORDER BY type ASC";
+
+ $args = array(':project_id' => $project_id, ':exclude_property' => array($trait_type['type4'], $trait_type['type5']));
+ $cvterm = chado_query($sql, $args);
+
+ // Only when project has headers.
+ if ($cvterm->rowCount() > 0) {
+ // Array to hold the column headers passed to the excel writer.
+ $col_headers = array();
+ // Array to hold standard procedure, which basically is
+ // the traits definition and collection method.
+ $instructions_data = array();
+
+ // Get the data type per unit. This type will be the cell type in the spreadsheet.
+ $data_type = rawpheno_function_default_unit('type');
+
+ // Prepend the array with plant property column headers.
+ $col_headers = array(
+ 'Plot' => 'integer',
+ 'Entry' => 'integer',
+ 'Name' => 'string',
+ 'Rep' => 'integer',
+ 'Location' => 'string'
+ );
+
+ // Start at F column taking into account plant properties.
+ // A for Plot, B for Entry and so on (A-E is 5 cols).
+ $l = 'F';
+ $cell_i = array();
+
+ // Assign the data type for each header based on the unit it contains.
+ $h = array('name' => 'Trait', 'definition' => 'Definition', 'method' => 'Collection Method');
+
+ foreach($cvterm as $trait) {
+ // Get the unit.
+ $u = rawpheno_function_header_unit($trait->name);
+ $unit = isset($data_type[$u]) ? $data_type[$u] : 'string';
+
+ $col_headers[$trait->name] = $unit;
+
+ // Highlight the cells when it is essential trait.
+ if ($trait->type == $trait_type['type1']) {
+ array_push($cell_i, $l . '1');
+ // Increment F column.
+ $l++;
+ }
+
+ // Get header method and definition information.
+ $t = rawpheno_function_header_properties($trait->project_cvterm_id);
+
+ foreach($h as $m_i => $m) {
+ $star = ($m_i == 'name') ? '*' : '';
+
+ if (strlen($t[$m_i]) < 80) {
+ // Short text, save it.
+ array_push($instructions_data, array($star . $m . ':', $t[$m_i]));
+ }
+ else {
+ // Hard-wrap long lines into shorter line and put each
+ // line into a cell/row.
+ $wrapped_string = wordwrap($t[$m_i], 100, "\n");
+ $chunks = explode("\n", $wrapped_string);
+
+ foreach($chunks as $i => $chunk) {
+ $ins_text = ($i == 0) ? array($star . $m . ':', $chunk) : array('', $chunk);
+ array_push($instructions_data, $ins_text);
+ }
+ }
+ }
+
+ // Add extra new line.
+ array_push($instructions_data, array('' , ''));
+ }
+
+ // Load spreadsheet writer library.
+ $xlsx_writer = libraries_load('spreadsheet_writer');
+ include_once $xlsx_writer['library path'] . '/'. $xlsx_writer['files'][0];
+
+ $writer = new XLSXWriter();
+ // Measurement tab.
+ @$writer->writeSheet(array(), 'Measurements', $col_headers,
+ array(
+ // The entire header row apply these styles.
+ array(
+ 'font' =>
+ array(
+ 'name' => 'Arial',
+ 'size' => '11',
+ 'color' => '000000',
+ 'bold' => false,
+ 'italic' => false,
+ 'underline' => false
+ ),
+ 'wrapText' => true,
+ 'verticalAlign' => 'top',
+ 'horizontalAlign' => 'center',
+ 'fill' => array('color' => 'F7F7F7'),
+ 'border' => array('style' => 'thin', 'color' => 'A0A0A0'),
+ 'rows' => array('0')
+ ),
+ // Once the styles above have been applied, style the plant property headers.
+ array(
+ 'font' =>
+ array(
+ 'name' => 'Arial',
+ 'size' => '11',
+ 'color' => '000000',
+ 'bold' => true,
+ 'italic' => false,
+ 'underline' => false
+ ),
+ 'verticalAlign' => 'bottom',
+ 'horizontalAlign' => 'center',
+ 'fill' => array('color' => 'EAEAEA'),
+ 'border' => array('style' => 'thin', 'color' => 'A0A0A0'),
+ 'cells' => array('A1', 'B1', 'C1', 'D1', 'E1')
+ ),
+ // Make sure to style the essential trait/header.
+ array(
+ 'font' =>
+ array(
+ 'name' => 'Arial',
+ 'size' => '11',
+ 'color' => '008000',
+ 'bold' => true,
+ 'italic' => false,
+ 'underline' => false
+ ),
+ 'wrapText' => true,
+ 'verticalAlign' => 'top',
+ 'horizontalAlign' => 'center',
+ 'fill' => array('color' => 'F5FFDF'),
+ 'border' => array('style' => 'thin', 'color' => 'A0A0A0'),
+ 'cells' => $cell_i
+ )
+ )
+ );
+
+ // Standard procedure tab.
+ // Load trait definition and data collection method to this sheet.
+ $instructions_header = array();
+ @$writer->writeSheet($instructions_data, 'Instructions', $instructions_header,
+ array(
+ array(
+ 'font' =>
+ array(
+ 'name' => 'Arial',
+ 'size' => '11',
+ 'color' => '000000',
+ 'bold' => true,
+ 'italic' => false,
+ 'underline' => false
+ ),
+ 'wrapText' => true,
+ 'columns' => '0',
+ ),
+ array(
+ 'font' =>
+ array(
+ 'size' => '12',
+ ),
+ 'wrapText' => false,
+ 'columns' => '1',
+ ),
+ )
+ );
+
+ // Calculator tab.
+ $calc_header = array('CALCULATE DAYS TO' => 'string');
+ $calc_data =
+ array(
+ array('Planting Date', '2015-10-06'),
+ array('Current Date', date('Y-m-d')),
+ array('Current "Days till"', '=B3 - B2'),
+ array('',''),
+ array('Instructions', ''),
+ array('', ''),
+ array('Fill out the planting date indicated in the measurements tab, as well as, the current date.', ''),
+ array('The "Days till" date will then be calculated for you.', '')
+ );
+
+ @$writer->writeSheet($calc_data, 'Calculate Days to', $calc_header,
+ array(
+ array(
+ 'font' =>
+ array(
+ 'name' => 'Arial',
+ 'size' => '20',
+ 'color' => '000000',
+ 'bold' => true,
+ 'italic' => false,
+ 'underline' => false
+ ),
+ 'wrapText' => false,
+ 'rows' => array('0'),
+ ),
+ array(
+ 'font' =>
+ array(
+ 'name' => 'Arial',
+ 'size' => '11',
+ 'color' => 'FFFFFF',
+ 'bold' => true,
+ 'italic' => false,
+ 'underline' => false
+ ),
+ 'wrapText' => true,
+ 'fill' => array('color' => '305673'),
+ 'border' => array('style' => 'thin', 'color' => 'A0A0A0'),
+ 'rows' => array('1'),
+ ),
+ array(
+ 'font' =>
+ array(
+ 'name' => 'Arial',
+ 'size' => '11',
+ 'color' => '000000',
+ 'bold' => true,
+ 'italic' => false,
+ 'underline' => false
+ ),
+ 'wrapText' => true,
+ 'fill' => array('color' => 'F7F7F7'),
+ 'border' => array('style' => 'thin', 'color' => 'A0A0A0'),
+ 'rows' => array('2'),
+ ),
+ array(
+ 'font' =>
+ array(
+ 'name' => 'Arial',
+ 'size' => '11',
+ 'color' => '000000',
+ 'bold' => true,
+ 'italic' => false,
+ 'underline' => false
+ ),
+ 'wrapText' => true,
+ 'fill' => array('color' => '79a183'),
+ 'border' => array('style' => 'thin', 'color' => 'A0A0A0'),
+ 'rows' => array('3'),
+ )
+ )
+ );
+
+ // Data collection spreadsheet name contains the following:
+ // Project id, name of the user, date and time.
+ $filename = 'datacollection_' . $project_id . '_' . str_replace(' ', '_', $GLOBALS['user']->name) .'_'. date('YMd') .'_'. time() . '.xlsx';
+ $file = file_save_data($writer->writeToString(), 'public://' . $filename);
+
+ // Launch save file window and ask user to save file.
+ $http_headers = array(
+ 'Content-Type' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
+ 'Content-Disposition' => 'attachment; filename="' . $filename . '"',
+ );
+
+ if (strpos($_SERVER['HTTP_USER_AGENT'], 'MSIE')) {
+ $http_headers['Cache-Control'] = 'must-revalidate, post-check=0, pre-check=0';
+ $http_headers['Pragma'] = 'public';
+ }
+ else {
+ $http_headers['Pragma'] = 'no-cache';
+ }
+
+ file_transfer($file->uri, $http_headers);
+
+ // Just in case the auto download fails, provide a link to manually download the file.
+ print 'Download Data Collection Spreadsheet';
+ }
+ else {
+ // Here project not found.
+ print 'Experiment not found.';
+ }
+ }
+ else {
+ // Project id is not valid.
+ print 'Experiment ID number is invalid.';
+ }
+}
diff --git a/include/rawpheno.rawdata.form.inc b/includes/rawpheno.rawdata.form.inc
old mode 100755
new mode 100644
similarity index 97%
rename from include/rawpheno.rawdata.form.inc
rename to includes/rawpheno.rawdata.form.inc
index 8d92588..e4e0c56
--- a/include/rawpheno.rawdata.form.inc
+++ b/includes/rawpheno.rawdata.form.inc
@@ -1,128 +1,128 @@
- 'markup',
- '#markup' => t('Download Data ❯'),
- );
-
- // Query project that has data saved to it.
- $sql = "SELECT DISTINCT t1.project_id, t1.name
- FROM {project} AS t1 RIGHT JOIN pheno_plant_project AS t2 USING(project_id)
- WHERE plant_id IS NOT NULL
- ORDER BY t1.project_id ASC";
-
- $project = chado_query($sql)
- ->fetchAllKeyed();
-
- $form['rawdata_txt_project'] = array(
- '#type' => 'hidden',
- '#value' => implode(',', array_keys($project))
- );
-
- // Select project select box.
- $form['rawdata_sel_project'] = array(
- '#type' => 'select',
- '#title' => t('Select experiment and trait:'),
- '#options' => $project,
- '#id' => 'rawdata-sel-project',
- );
-
- // The summarized list of cvterm_ids from MVIEW returned by inner most query will be passed to function that converts
- // comma separated values into individual values (cvterm_id numbers) and the result is the parameter of ANY clause
- // that will filter cvterms to only those in the list. Final rows are in JSON object and sorted alphabetically by name
- // that will be passed on to the select field of rawdata form.
- $sql_cvterm = "
- SELECT c_j.cvterm_json->>'id', c_j.cvterm_json->>'name' FROM (
- SELECT JSON_BUILD_OBJECT('id', cvterm_id, 'name', name) AS cvterm_json FROM {cvterm} WHERE cvterm_id = ANY ((
- SELECT STRING_TO_ARRAY(list_id.all_traits, ',') FROM (
- SELECT string_agg(DISTINCT all_traits, ',') AS all_traits
- FROM {rawpheno_rawdata_mview}
- WHERE plant_id IN (SELECT plant_id FROM pheno_plant_project WHERE project_id = :project_id)
- ) AS list_id
- )::int[])
- ) AS c_j
- WHERE c_j.cvterm_json->>'name' NOT IN ('Rep', 'Entry', 'Location', 'Name', 'Plot', 'Planting Date (date)', '# of Seeds Planted (count)')
- ORDER BY c_j.cvterm_json->>'name' ASC
- ";
-
- // Add first option as instruction to this field.
- $default_option = array(0 => 'Select a trait to hightlight in the chart');
-
- // Create the select field and populate it with traits specific to a project.
- foreach(array_keys($project) as $p_id) {
- // Trait id numbers.
- $trait_ids = chado_query($sql_cvterm, array(':project_id' => $p_id))
- ->fetchAllKeyed();
-
- $traits_array = array_unique($trait_ids);
- $traits = $default_option + $traits_array;
-
- $form['sel_' . $p_id] = array(
- '#type' => 'select',
- '#title' => ' ',
- '#options' => $traits,
- '#default_value' => reset($traits),
- '#attributes' => array(
- 'name' => 'rawdata-sel-trait'
- ),
- '#states' => array(
- 'visible' => array(':input[name="rawdata_sel_project"]' => array('value' => $p_id)),
- ),
- );
- }
-
- // SVG elements.
- // SVG canvas.
- $form['page_content'] = array(
- '#type' => 'markup',
- '#markup' => '',
- );
-
- // Hidden field containing url to JSON (summary data).
- $form['json_url'] = array(
- '#type' => 'hidden',
- // Update this line if this module is in a different directory structure.
- '#value' => url('/'),
- '#attributes' => array('id' => array('rawdata-json'))
- );
-
- // Attach D3 JS library.
- $d3_lib = libraries_load('d3js');
-
- if (isset($d3_lib) && !empty($d3_lib['loaded'])) {
- $form['d3lib']['#attached']['libraries_load'][] = array('d3js');
- }
-
- // Attach CSS and JavaScript.
- $path = drupal_get_path('module', 'rawpheno') . '/theme/';
- $form['#attached']['css'] = array($path . 'css/rawpheno.rawdata.style.css');
- $form['#attached']['js'] = array($path . 'js/rawpheno.rawdata.script.js');
-
- return $form;
-}
+ 'markup',
+ '#markup' => t('Download Data ❯'),
+ );
+
+ // Query project that has data saved to it.
+ $sql = "SELECT DISTINCT t1.project_id, t1.name
+ FROM {project} AS t1 RIGHT JOIN pheno_plant_project AS t2 USING(project_id)
+ WHERE plant_id IS NOT NULL
+ ORDER BY t1.project_id ASC";
+
+ $project = chado_query($sql)
+ ->fetchAllKeyed();
+
+ $form['rawdata_txt_project'] = array(
+ '#type' => 'hidden',
+ '#value' => implode(',', array_keys($project))
+ );
+
+ // Select project select box.
+ $form['rawdata_sel_project'] = array(
+ '#type' => 'select',
+ '#title' => t('Select experiment and trait:'),
+ '#options' => $project,
+ '#id' => 'rawdata-sel-project',
+ );
+
+ // The summarized list of cvterm_ids from MVIEW returned by inner most query will be passed to function that converts
+ // comma separated values into individual values (cvterm_id numbers) and the result is the parameter of ANY clause
+ // that will filter cvterms to only those in the list. Final rows are in JSON object and sorted alphabetically by name
+ // that will be passed on to the select field of rawdata form.
+ $sql_cvterm = "
+ SELECT c_j.cvterm_json->>'id', c_j.cvterm_json->>'name' FROM (
+ SELECT JSON_BUILD_OBJECT('id', cvterm_id, 'name', name) AS cvterm_json FROM {cvterm} WHERE cvterm_id = ANY ((
+ SELECT STRING_TO_ARRAY(list_id.all_traits, ',') FROM (
+ SELECT string_agg(DISTINCT all_traits, ',') AS all_traits
+ FROM {rawpheno_rawdata_mview}
+ WHERE plant_id IN (SELECT plant_id FROM pheno_plant_project WHERE project_id = :project_id)
+ ) AS list_id
+ )::int[])
+ ) AS c_j
+ WHERE c_j.cvterm_json->>'name' NOT IN ('Rep', 'Entry', 'Location', 'Name', 'Plot', 'Planting Date (date)', '# of Seeds Planted (count)')
+ ORDER BY c_j.cvterm_json->>'name' ASC
+ ";
+
+ // Add first option as instruction to this field.
+ $default_option = array(0 => 'Select a trait to hightlight in the chart');
+
+ // Create the select field and populate it with traits specific to a project.
+ foreach(array_keys($project) as $p_id) {
+ // Trait id numbers.
+ $trait_ids = chado_query($sql_cvterm, array(':project_id' => $p_id))
+ ->fetchAllKeyed();
+
+ $traits_array = array_unique($trait_ids);
+ $traits = $default_option + $traits_array;
+
+ $form['sel_' . $p_id] = array(
+ '#type' => 'select',
+ '#title' => ' ',
+ '#options' => $traits,
+ '#default_value' => reset($traits),
+ '#attributes' => array(
+ 'name' => 'rawdata-sel-trait'
+ ),
+ '#states' => array(
+ 'visible' => array(':input[name="rawdata_sel_project"]' => array('value' => $p_id)),
+ ),
+ );
+ }
+
+ // SVG elements.
+ // SVG canvas.
+ $form['page_content'] = array(
+ '#type' => 'markup',
+ '#markup' => '',
+ );
+
+ // Hidden field containing url to JSON (summary data).
+ $form['json_url'] = array(
+ '#type' => 'hidden',
+ // Update this line if this module is in a different directory structure.
+ '#value' => url('/'),
+ '#attributes' => array('id' => array('rawdata-json'))
+ );
+
+ // Attach D3 JS library.
+ $d3_lib = libraries_load('d3js');
+
+ if (isset($d3_lib) && !empty($d3_lib['loaded'])) {
+ $form['d3lib']['#attached']['libraries_load'][] = array('d3js');
+ }
+
+ // Attach CSS and JavaScript.
+ $path = drupal_get_path('module', 'rawpheno') . '/theme/';
+ $form['#attached']['css'] = array($path . 'css/rawpheno.rawdata.style.css');
+ $form['#attached']['js'] = array($path . 'js/rawpheno.rawdata.script.js');
+
+ return $form;
+}
diff --git a/include/rawpheno.tripaldownload.inc b/includes/rawpheno.tripaldownload.inc
similarity index 85%
rename from include/rawpheno.tripaldownload.inc
rename to includes/rawpheno.tripaldownload.inc
index d55777c..aa6fa1c 100644
--- a/include/rawpheno.tripaldownload.inc
+++ b/includes/rawpheno.tripaldownload.inc
@@ -30,7 +30,7 @@ function rawpheno_register_trpdownload_type() {
'get_format' => 'rawpheno_trpdownload_get_readable_format',
),
);
-
+
return $types;
}
@@ -54,7 +54,7 @@ function rawpheno_trpdownload_summarize_download($vars) {
list($project, $location, $traits, $r_version, $envdata, $envfile) = explode('&', $q);
$tmp = trim(str_replace('p=', '', $project));
- $project = explode(',', $tmp);
+ $project = explode('+', $tmp);
$sql = "SELECT name FROM {project} WHERE project_id IN (:project)";
$args = array(':project' => $project);
@@ -191,7 +191,7 @@ function rawpheno_trpdownload_get_readable_format($vars) {
/**
* Function callback: generate csv file.
*/
-function rawpheno_trpdownload_generate_file($variables, $job_id = NULL) {
+function rawpheno_trpdownload_generate_file($variables, $job_id = NULL) {
// Get query string and filename.
$code = '';
foreach($variables as $l => $v) {
@@ -209,12 +209,11 @@ function rawpheno_trpdownload_generate_file($variables, $job_id = NULL) {
}
$q = base64_decode($code);
- list($project, $location, $traits, $r_version,,) = explode('&', $q);
-
+ list($project, $location, $traits, $r_version,,,$germplasm) = explode('&', $q);
// Projects:
$tmp = trim(str_replace('p=', '', $project));
- $project = explode(',', $tmp);
-
+ $project = explode('+', $tmp);
+
// Locations:
$location = trim(str_replace('l=', '', $location));
@@ -241,6 +240,9 @@ function rawpheno_trpdownload_generate_file($variables, $job_id = NULL) {
}
else {
$traits = explode(',', $traits);
+ // Backup trait selection to account for trait w/o plant id
+ // but is a trait in experiment trait list.
+ $rd_field_trait = $traits;
}
// Rversion
@@ -261,15 +263,40 @@ function rawpheno_trpdownload_generate_file($variables, $job_id = NULL) {
}
}
+ // Germplasm/Stock id - request from germplasm field.
+ if ($germplasm) {
+ $stock_id = trim(str_replace('g=', '', $germplasm));
+ $limit_stock = '';
+ }
+
// Sub-query to select plant_id given a location and project.
// NOTE: leading and trailing spaces are required.
$sub_sql = " (SELECT plant_id
FROM {pheno_plantprop} INNER JOIN {pheno_plant_project} USING(plant_id)
- WHERE value IN (:location) AND project_id IN (:project)) ";
+ WHERE value IN (:location) AND project_id IN (:project) %s) ";
// Query values required by sub query.
$arr_q_string = array(':project' => $project, ':location' => $location, ':traits' => $traits);
+ // Array to hold column headers.
+ // Add Name/Stock name column headers array.
+ $header = array('A0' => 'Name');
+
+ // Filter by germplasm.
+ if ($stock_id != '' && (int) $stock_id > 0) {
+ // Additional filter to limit datapoints to only those that match stock/germplasm.
+ $limit_stock = "AND plant_id IN (SELECT plant_id FROM pheno_plant WHERE stock_id = :stock_id)";
+ $arr_q_string[':stock_id'] = $stock_id;
+
+ // Experiment name is added to the result as export from multiple
+ // experiments is supported by the field.
+
+ // Query below is also updated to correspond to this header.
+ $header['E0'] = 'Experiment';
+ }
+
+ $sub_sql = sprintf($sub_sql, $limit_stock);
+
// First we need to get the header. This will allow us to ensure that the data
// downloaded all matches up with the trait it is associated with. Furthermore,
// it will allow us to handle missing data.
@@ -286,10 +313,6 @@ function rawpheno_trpdownload_generate_file($variables, $job_id = NULL) {
$result = db_query($sql, $arr_q_string);
- // Array to hold column headers.
- //Add Name/Stock name column headers array.
- $header = array('A0' => 'Name');
-
foreach ($result as $r) {
$def = $r->name;
@@ -302,22 +325,51 @@ function rawpheno_trpdownload_generate_file($variables, $job_id = NULL) {
// When no equivalent R version is available, load the definition instead.
$def = ($rfreindly == null) ? $r->name : $rfreindly;
}
-
+
// Column headers array.
$header[ $r->id ] = $def;
}
+ // When request is from field, it has additional filter to phenotypes
+ // by comparing stock id + plant id. In some case when a trait has no values,
+ // the final result will exclude this trait inspite this trait being part
+ // of trait set of the experiment, since it has no plant_id (stock_id) to compare.
+
+ // Include the trait and the subsequent code will assign the value NA. This to show
+ // that trait is part of the experiment.
+ // NOTE: rversion is always off when request comes from field.
+ foreach($rd_field_trait as $exp_trait) {
+ if (!in_array($exp_trait, array_keys($header))) {
+ $t = chado_get_cvterm(array('cvterm_id' => $exp_trait));
+ $header[ 'C' . $t->cvterm_id ] = $t->name;
+ }
+ }
+
// Sort array by key.
ksort($header);
+ // Filter by germplasm.
+ $sql_experiment = '';
+ if ($stock_id != '' && (int) $stock_id > 0) {
+ // Include a query to mark datapoints at to which experiment to correspond
+ // to experiment header.
+ $sql_experiment = "
+ SELECT t1.plant_id AS id, 0 AS tid, 'e' AS def, t2.name AS value, 'E' AS grp
+ FROM {pheno_plant_project} AS t1
+ INNER JOIN {chado.project} AS t2 ON t1.project_id = t2.project_id
+ WHERE plant_id IN" . $sub_sql . "UNION ";
+ }
+
// Query to join data from different tables.
// Result: plant_id, trait_id, definition, data, and a grouping string
// The result is sorted by plant_id and the grouping string ensuring that the first
// row is Name - containing the stock name.
// The result will be sorted into standard order: plot,entry,name,rep,location,traits.....
- // Thus first we select the name. Note that the tid is 0 because this doesn't have a cvterm (ie: not a trait).
- $sql = "SELECT t2.plant_id AS id, '0' AS tid, 'Name' AS def, t1.name AS value, 'A' AS grp
+ // Thus first we select experiment and name. Note that the tid is 0 because this doesn't have a cvterm (ie: not a trait).
+ $sql = "%s
+
+ SELECT t2.plant_id AS id, '0' AS tid, 'Name' AS def, t1.name AS value, 'A' AS grp
FROM {chado.stock} AS t1 INNER JOIN {pheno_plant} AS t2 USING(stock_id)
WHERE t2.plant_id IN" . $sub_sql
@@ -340,7 +392,8 @@ function rawpheno_trpdownload_generate_file($variables, $job_id = NULL) {
// Lastly we order the results by plant_id and grouping string, and tid.
. "GROUP BY t1.plant_id, t1.type_id, t2.name
ORDER BY id, grp, tid ASC";
-
+
+ $sql = sprintf($sql, $sql_experiment);
$results = db_query($sql, $arr_q_string);
if ($results) {
diff --git a/include/rawpheno.upload.excel.inc b/includes/rawpheno.upload.excel.inc
old mode 100755
new mode 100644
similarity index 97%
rename from include/rawpheno.upload.excel.inc
rename to includes/rawpheno.upload.excel.inc
index ef4fbd8..9c6fdd9
--- a/include/rawpheno.upload.excel.inc
+++ b/includes/rawpheno.upload.excel.inc
@@ -1,1271 +1,1271 @@
- $fid),
- array('print' => TRUE)
- );
- tripal_report_error('rawpheno', TRIPAL_CRITICAL, '[CODE 100] Failed to load phenoypic data (job !id)', array('!id' => $job_id), array('print' => TRUE));
- exit(1);
- }
-
- $xls_file = drupal_realpath($file->uri);
-
- // INFO:
- // Keep a record of which file we are loading in the logs.
- // print "\nXLSX File: " . $xls_file . "\n";
-
- // Add the libraries needed to parse excel files.
- rawpheno_add_parsing_libraries();
-
- // Open the file for reading
- $xls_obj = rawpheno_open_file($file);
- if (!$xls_obj) {
- tripal_report_error(
- 'rawpheno',
- TRIPAL_CRITICAL,
- 'Uploading Phenoypic Data: Unable to open file. File=@file',
- array('@file' => print_r($file, TRUE)),
- array('print' => TRUE)
- );
- tripal_report_error('rawpheno', TRIPAL_CRITICAL, '[CODE 101] Failed to load phenoypic data (job !id)', array('!id' => $job_id), array('print' => TRUE));
- exit(2);
- }
-
- // Change to the correct spreadsheet.
- rawpheno_change_sheet($xls_obj, 'measurements');
-
- // Compute the total number of rows parsed.
- $row_count = rawpheno_count_rows($xls_obj);
-
- // File to write progress status.
- $tmp = file_directory_temp();
- $filename = $tmp . '/' . 'job-progress' . $job_id . '.txt';
-
- // Variations of Not Applicable.
- $not_applicable = array('na', 'n/a', 'n.a.');
-
- // Start Transaction.
- $TRANSACTION = db_transaction();
- try {
-
- // Read each row.
- // INFO:
- // print "\nNow parsing each row and saving it to the database...\nNumber of rows saved: \n";
- $i = 0;
-
- // Skip columns.
- $skip = array();
- // Project name.
- $project_name = rawpheno_function_getproject($project_id);
- // Calling all modules implementing hook_rawpheno_ignorecols_valsave_alter():
- drupal_alter('rawpheno_ignorecols_valsave', $skip, $project_name);
-
- // Each row in the spreadsheet.
- foreach ($xls_obj as $row) {
- // Create progress update by computing the number of rows saved in percent.
- // Write to file and progress bar API reads the content and pass it JSON generator
- // in file rawpheno.module function: rawpheno_upload_job_progress_json().
- // Echo to terminal.
-
- $percent = round(($i / $row_count) * 100);
- if ($percent % 10 == 0) {
- print $percent . '% complete...' . "\n";
- }
-
- // To file.
- file_unmanaged_save_data($percent, $filename, FILE_EXISTS_REPLACE);
-
- // HEADER!
- // This is the header.
- if ($i == 0) {
- $header = $row;
-
- // Find the index number of name header in the spreadsheet.
- $name_index = array_search('name', array_map('rawpheno_function_delformat', $header));
- if ($name_index === FALSE) {
- tripal_report_error(
- 'rawpheno',
- TRIPAL_CRITICAL,
- 'Uploading Phenoypic Data: Unable to determine the name column.',
- array(),
- array('print' => TRUE)
- );
- $TRANSACTION->rollback();
- tripal_report_error('rawpheno', TRIPAL_CRITICAL, '[CODE 102] Failed to load phenoypic data (job !id)', array('!id' => $job_id), array('print' => TRUE));
- exit(31);
- }
-
- $i++;
- continue;
- }
-
- // NEXT ROW IF EMPTY!
- // Don't continue processing if this row is empty.
- if (strlen(trim(implode('', $row))) <= 5) {
- break;
- }
-
- // VALID EACH ROW!
- // Process the name column first since we need a plant_id before we can insert any more data.
- // Name column header goes into pheno_plant.
- // Check if stock name exists.
- unset($stock_id);
-
- // Prior to saving, Remove non-breaking whitespace by converting it to a blank space instead of removing it,
- // in case user intends a space between words/values.
- // trim() implementation below should drop unecessary leading and trailing spaces.
- if (preg_match('/\xc2\xa0/', $row[$name_index])) {
- $row[$name_index] = preg_replace('/\xc2\xa0/', ' ', $row[$name_index]);
- }
-
- $stock_id = rawpheno_function_getstockid(trim($row[$name_index]), $project_name);
- // print $stock_id . ' -- ' . $row[$name_index] . "\n";
-
- // Determine if name has a stock id number.
- if (isset($stock_id) && $stock_id > 0) {
- $p_id = 0;
-
- // Test if stock was measured in the active project, if not, insert as a new record.
- // Otherwise, do more check (plot) to see if plant_id should be re-used.
- $sql = "
- SELECT plant_id
- FROM pheno_plant AS t1 INNER JOIN pheno_plant_project AS t2 USING(plant_id)
- WHERE t1.stock_id = :stock_id AND t2.project_id = :project_id LIMIT 1";
-
- $args = array(':stock_id' => $stock_id, ':project_id' => $project_id);
- $p = chado_query($sql, $args);
-
- if ($p->rowCount()) {
- // Found a stock record in the project. Do more test.
- // Array to hold plot headers.
- // Plot, Rep, Location, Planting Date (date)
- $arr_plot_cols = rawpheno_function_headers('plot');
-
- // Construct query string.
- // String : stock_id - plot - rep - location - year
- // eg. 147-5-2-Saskatoon-2015
- $plot = $stock_id;
-
- // Given a row, construct the search string (format) above and use it to search if
- // the such combination matched any record in the database.
- foreach($arr_plot_cols as $plot_col) {
- $plot_col = rawpheno_function_delformat($plot_col);
-
- // Cell value of plot property header.
- $col_index = array_search(strtolower($plot_col), array_map('rawpheno_function_delformat', $header));
- $cell_val = trim($row[$col_index]);
-
- // If planting date - extract the year value.
- // Support NA and YYYY in planting date. Use this value when
- // value cannot be split by -.
- if ($plot_col == 'plantingdate(date)') {
- $y = explode('-', $cell_val);
-
- if (is_array($y)) {
- // Extract the year only from planting date.
- $cell_val = $y[0];
- }
-
- // Else use the NA or YYYY.
- }
-
- $plot .= '-' . $cell_val;
- }
-
- // Search the query string.
- $p_id = rawpheno_function_plot_exists($plot, $project_id);
- }
-
-
- if ($p_id) {
- // Plot found - re-use the plant_id.
- $pheno_plantid = $p_id;
- // INFO:
- // print 'FOUND PLOT: ' . $plot . ' [re-using plot id #' . $pheno_plantid . '] ~ ';
- }
- else {
- // Plot not found - insert as new row.
- $pheno_plantid = db_insert('pheno_plant')
- ->fields(array('stock_id' => $stock_id))
- ->execute();
-
- // Map this record/stock to a project.
- db_insert('pheno_plant_project')
- ->fields(array('project_id' => $project_id,
- 'plant_id' => $pheno_plantid))
- ->execute();
-
- // INFO:
- // print 'NEW STOCK: #' . $stock_id . ' [adding plot id #' . $pheno_plantid . '] ~ ';
- }
- }
- else {
- // Warn the admin that germplasm is not available...
- // We want to stop loading if this is the case.
- tripal_report_error(
- 'rawpheno',
- TRIPAL_CRITICAL,
- 'Uploading Phenoypic Data: Germplasm doesn\'t exist (name=!name; row=!row)',
- array('!name' => $row[$name_index], '!row' => $i),
- array('print' => TRUE)
- );
- $TRANSACTION->rollback();
- tripal_report_error('rawpheno', TRIPAL_CRITICAL, '[CODE 103] Failed to load phenoypic data (job !id)', array('!id' => $job_id), array('print' => TRUE));
- exit(32);
- }
-
- // Read each row and each cell.
- // Each row will be an array where name is always the first element.
- foreach($row as $cell_index => $cell_entry) {
- // Skip this cell when col from durpal_alter hook matches the col.
- $h = rawpheno_function_delformat($header[$cell_index]);
-
- if (count($skip) > 0 && in_array($h, $skip)) {
- // print 'skipping value : ' . $h . '=' . $cell_entry . "\n";
- continue;
- }
-
- // For consistency, convert all variations of not applicable to NA.
- if (is_string($cell_entry) && in_array(strtolower($cell_entry), $not_applicable)) {
- $cell_entry = 'NA';
- }
-
- // We don't want to insert empty data.
- // That said, while PHP thinks 0 is empty, we do not.
- if (!empty($cell_entry) OR (strval($cell_entry) === '0')) {
-
- // Get the column header of a cell.
- $cell_colheader = trim(str_replace(array("\n", "\r", " "), ' ', $header[$cell_index]));
- // Remove additional spaces from column headers.
- $cell_colheader = preg_replace('/\s+/', ' ', $cell_colheader);
-
-
- // Determine if user wants to save this trait.
- if (count($arr_newheaders) > 0 AND array_key_exists($cell_colheader, $arr_newheaders)) {
- if ($arr_newheaders[$cell_colheader]['flag'] == 0) {
- // Skip this cell if it is a new column header and user does not want to save
- // this new trait;
- continue;
- }
- elseif ($arr_newheaders[$cell_colheader]['flag'] == 1) {
- // Get the cvterm name for this new header.
- $alt_name = $arr_newheaders[$cell_colheader]['alt_header'];
- $n = array('cvterm_id' => $alt_name, 'cv_id' => array('name' => 'phenotype_measurement_types'));
-
- if (function_exists('chado_get_cvterm')) {
- $name = chado_get_cvterm($n);
- }
- else {
- $name = tripal_get_cvterm($n);
- }
-
- $cell_colheader = $name->name;
- }
- }
-
- // Prior to saving, Remove non-breaking whitespace by converting it to a blank space instead of removing it,
- // in case user intends a space between words/values.
- // trim() implementation below should drop unecessary leading and trailing spaces.
- if (preg_match('/\xc2\xa0/', $cell_entry)) {
- $cell_entry = preg_replace('/\xc2\xa0/', ' ', $cell_entry);
- }
-
- // We always want to strip flanking white space.
- // FYI: This is done when the data is validated as well.
- $cell_entry = trim($cell_entry);
- $cell_colheader = trim($cell_colheader);
-
- // Determine which table to insert a column header.
- // If this is the name column then doing nothing since we've already delt with it above.
- if ($cell_index == $name_index) { continue; }
-
- // PLOT, ENTRY, REP and LOCATION
- // Cells containing column headers that are required.
- // Traits: plot, entry, rep, location into pheno_plantprop.
- elseif (in_array($cell_colheader, $plantprop_headers) && !empty($cell_colheader)) {
- $t = array('name' => $cell_colheader, 'cv_id' => array('name' => 'phenotype_plant_property_types'));
-
- if (function_exists('chado_get_cvterm')) {
- $type = chado_get_cvterm($t);
- }
- else {
- $type = tripal_get_cvterm($t);
- }
-
- $type_id = $type->cvterm_id;
-
- // Ensure that cvterm_id is present before inserting to table
- if(isset($type_id)) {
- $tmp = db_insert('pheno_plantprop')
- ->fields(array('plant_id' => $pheno_plantid,
- 'type_id' => $type_id,
- 'value' => $cell_entry))
- ->execute();
-
- if (!$tmp) {
- tripal_report_error(
- 'rawpheno',
- TRIPAL_ERROR,
- 'Uploading Phenoypic Data: Unable to insert plant property. Values=@values',
- array('@values' => print_r(array('plant_id' => $pheno_plantid, 'type_id' => $type_id, 'value' => $cell_entry),TRUE)),
- array('print' => TRUE)
- );
- $TRANSACTION->rollback();
- tripal_report_error('rawpheno', TRIPAL_CRITICAL, '[CODE 104] Failed to load phenoypic data (job !id)', array('!id' => $job_id), array('print' => TRUE));
- exit(33);
- }
- }
-
- else {
- tripal_report_error(
- 'rawpheno',
- TRIPAL_ERROR,
- 'Uploading Phenoypic Data: Plant Property type !type does\'t exist.',
- array('!type' => $cell_colheader),
- array('print' => TRUE)
- );
- $TRANSACTION->rollback();
- tripal_report_error('rawpheno', TRIPAL_CRITICAL, '[CODE 105] Failed to load phenoypic data (job !id)', array('!id' => $job_id), array('print' => TRUE));
- exit(34);
- }
-
- }
- // THE REST OF THE COLUMN HEADERS
- // Everything else into pheno_measurements.
- elseif ((!empty($cell_colheader) && $cell_entry != 'NA')
- || ($cell_colheader == 'Planting Date (date)' && $cell_entry == 'NA')) {
-
- // Allow NA only when header is planting date.
-
- $c_h = rawpheno_function_delformat($cell_colheader);
- if (in_array($c_h, $skip)) {
- // print 'skipping header : ' . $cell_colheader . "\n";
- continue;
- }
-
- // Get the cvterm_id for the trait measurement.
- $type_id = rawpheno_get_trait_id($cell_colheader);
-
- if (!$type_id) {
- tripal_report_error(
- 'rawpheno',
- TRIPAL_ERROR,
- 'Uploading Phenoypic Data: Missing Plant Measurement Type (Header=!colheader).',
- array('!colheader' => $cell_colheader),
- array('print' => TRUE)
- );
- $TRANSACTION->rollback();
- tripal_report_error('rawpheno', TRIPAL_CRITICAL, '[CODE 106] Failed to load phenoypic data (job !id)', array('!id' => $job_id), array('print' => TRUE));
- exit(37);
- }
-
- // Retrieve the unit for this trait.
- $cv_unit = rawpheno_get_trait_unit($cell_colheader, $type_id);
-
- if ($cv_unit) {
- $unit_id = $cv_unit['id'];
- $unit = $cv_unit['name'];
- }
- else {
- tripal_report_error(
- 'rawpheno',
- TRIPAL_ERROR,
- 'Uploading Phenoypic Data: Unable to find unit for Plant Measurement Type (Term=!name; Type ID=!id).',
- array('!name' => $cell_colheader, '!id' => $type_id),
- array('print' => TRUE)
- );
- $TRANSACTION->rollback();
- tripal_report_error('rawpheno', TRIPAL_CRITICAL, '[CODE 107] Failed to load phenoypic data (job !id)', array('!id' => $job_id), array('print' => TRUE));
- exit(37);
- }
-
- // Determine if cell requires scale member code.
- // When unit is scale, find code equivalent in pheno_scale_member table.
- if ($unit == 'scale') {
- // Get pheno scale member code
- $cvalue_id = db_query("SELECT member_id FROM {pheno_scale_member}
- WHERE code = :code LIMIT 1",
- array(':code' => trim($cell_entry)))
- ->fetchField();
- // We want to report an error if we can't find the scale memeber
- // but only if there are any in the first place!
- $num_members = db_query('SELECT count(*) FROM {pheno_scale_member} WHERE scale_id=:unit_id',
- array(':unit_id' => $unit_id))->fetchField();
- if (!$cvalue_id AND !empty($num_members)) {
- tripal_report_error(
- 'rawpheno',
- TRIPAL_WARNING,
- 'Uploading Phenoypic Data: Unable to find scale id for Plant Measurement Type (Trait=!trait; Term=!name; Type ID=!id; Scale Value=!scale).',
- array('!trait' => $cell_colheader, '!name' => $unit, '!id' => $unit_id, '!scale' => $cell_entry),
- array('print' => TRUE)
- );
- }
-
- // Use default value in the cell if query to find scale member code
- // has no equivalent value.
- $cvalue_id = (isset($cvalue_id) && $cvalue_id > 0) ? $cvalue_id : $cell_entry;
- }
- else {
- // No scale member value for the rest of traits.
- $cvalue_id = '';
- }
-
- // Insert trait only when type_id and unit_id are not null.
- if (isset($type_id) && isset($unit_id)) {
-
- $temp = db_insert('pheno_measurements')
- ->fields(array('plant_id' => $pheno_plantid,
- 'type_id' => $type_id,
- 'unit_id' => $unit_id,
- 'cvalue_id' => $cvalue_id,
- 'value' => $cell_entry,
- 'modified' => date("D M d, Y h:i:s a", time())))
- ->execute();
-
- if (!$temp) {
- tripal_report_error(
- 'rawpheno',
- TRIPAL_ERROR,
- 'Uploading Phenoypic Data: Unable to insert measurement. Values=@values.',
- array('@values' => print_r(array('plant_id' => $pheno_plantid,
- 'type_id' => $type_id,
- 'unit_id' => $unit_id,
- 'cvalue_id' => $cvalue_id,
- 'value' => $cell_entry,
- 'modified' => date("D M d, Y h:i:s a", time())),TRUE)),
- array('print' => TRUE)
- );
- $TRANSACTION->rollback();
- tripal_report_error('rawpheno', TRIPAL_CRITICAL, '[CODE 108] Failed to load phenoypic data (job !id)', array('!id' => $job_id), array('print' => TRUE));
- exit(38);
- }
- }
- }
- }
- }
-
- $i++;
- }
- }
- catch (Exception $e) {
- $TRANSACTION->rollback();
- watchdog_exception('rawpheno', $e);
- tripal_report_error('rawpheno', TRIPAL_CRITICAL, '[CODE 109] Failed to load phenoypic data (job !id)', array('!id' => $job_id), array('print' => TRUE));
- exit(4);
- }
-
- unset($TRANSACTION); //Commit
- print "Upload complete.\n";
-
- print "\nUpdating the materialized view summarizing phenotypic data.\n";
- $mview_id = tripal_get_mview_id('rawpheno_rawdata_summary');
- if ($mview_id) tripal_populate_mview($mview_id);
-}
-
-
-/**
- * Validates an excel file using any validators registered with rawpheno.
- *
- * @param $file
- * A drupal managed_file object describing the uploaded spreadsheet.
- * @param $project_id
- * An integer containing project id selected in the project select box.
- * This will map the data submitted to a project.
- * @param $source
- * A string containing the source of the file upload - Upload Data or Backup File.
- *
- * @return
- * An array containing the validation result from each validator.
- */
-function rawpheno_validate_excel_file($file, $project_id, $source) {
- $status = array();
-
- // Process the validators to make them easier to use.
- // Specifically, sort them by their scope.
- $validators = array();
- $all_validators = module_invoke_all('rawpheno_validators');
- foreach($all_validators as $k => $v) {
- $validators[ $v['scope'] ][ $k ] = $v;
- }
-
- // Todo list.
- $all_scope_validators = array('project', 'file', 'all', 'header', 'subset');
-
- // Add the libraries needed to parse excel files.
- rawpheno_add_parsing_libraries();
-
- // Before performing any validation to the excel file. Ensure first that a project is selected.
- foreach ($validators['project'] as $prj_validator_name => $prj_validator) {
- if (isset($prj_validator['validation callback']) AND function_exists($prj_validator['validation callback'])) {
- $status[ $prj_validator_name ] = call_user_func($prj_validator['validation callback'], $project_id);
-
- // If returned false then halt validation.
- if ($status[ $prj_validator_name ] === FALSE) {
- // Fail the project and set the rest to TODO.
- $status[ $prj_validator_name ] = FALSE;
-
- // Todo the rest of validators.
- // Since this is project scope and it got falsed - remove the project.
- unset($all_scope_validators[0]);
- foreach($all_scope_validators as $v) {
- foreach($validators[ $v ] as $v_name => $validator) {
- $status[ $v_name ] = 'todo';
- }
- }
-
- return $status;
- }
- }
- }
-
- // First validate the whole file. If any of these fail then halt validation.
- foreach ($validators['file'] as $validator_name => $validator) {
- if (isset($validator['validation callback']) AND function_exists($validator['validation callback'])) {
- $status[ $validator_name ] = call_user_func($validator['validation callback'], $file);
-
- // If returned false then halt validation.
- if ($status[ $validator_name ] === FALSE) {
- // Fail the file and set the rest to TODO but set the project to passed
- // first since it is assumed that project validator returned a passed value.
- $status[ 'project_selected' ] = TRUE;
- $status[ $validator_name ] = FALSE;
-
- // Todo the rest of validators.
- // Since project is completed. skip this scope.
- unset($all_scope_validators[0]);
- foreach($all_scope_validators as $v) {
- foreach($validators[ $v ] as $v_name => $validator) {
- if ($status[ $v_name ] === TRUE) {
- $status[ $v_name ] = TRUE;
- }
- elseif ($status[ $v_name ] === FALSE) {
- $status[ $v_name ] = FALSE;
- }
- else {
- $status[ $v_name ] = 'todo';
- }
- }
- }
-
- return $status;
- }
- }
- }
-
- // Open the file for reading
- $xls_obj = rawpheno_open_file($file);
-
- // Change to the correct spreadsheet.
- rawpheno_change_sheet($xls_obj, 'measurements');
-
- // This increment variable $i is required since xls and xlsx
- // parsers assign array index differently.
- // XLS starts at 1, while XLSX at 0;
- $i = 0;
-
- // Variations of Not Applicable.
- $not_applicable = array('na', 'n/a', 'n.a.');
-
- // Skip columns.
- $skip = array();
- // Project name.
- $project_name = rawpheno_function_getproject($project_id);
- // Calling all modules implementing hook_rawpheno_ignorecols_valsave_alter():
- drupal_alter('rawpheno_ignorecols_valsave', $skip, $project_name);
-
- // Iterate though each row.
- $num_errored_rows = 0;
- $storage = array();
- foreach($xls_obj as $row) {
- $i++;
-
- // Convert row into a string and check the length.
- // This will exclude empty rows.
- if (strlen(trim(implode('', $row))) >= 5) {
-
- // VALIDATE THE HEADER.
- if ($i == 1) {
- // Save the header for later.
- $header = array();
- $new_header = array();
- // Checking plot value requires cell value in Planting Date (date) and Location.
- // Store index numbers of these two traits.
- $plot_req = array();
-
- $o = 0;
- foreach ($row as $r) {
- $without_format = rawpheno_function_delformat($r);
-
- // To maintain index of both cells and header, tag either to skip or process
- // based on headers in drupal_alter hook.
- $s = (in_array($without_format, $skip)) ? 1 : 0;
-
- // Remove new lines.
- $rem_newline = str_replace(array("\n", "\r"), ' ', $r);
- // Remove extra spaces.
- $rem_spaces = preg_replace('/\s+/', ' ', $rem_newline);
- // Remove leading and trailing spaces.
- $r = trim($rem_spaces);
- $no_units = rawpheno_get_trait_name($r);
-
- $header[] = array(
- 'no format' => $without_format,
- 'original' => $r,
- 'units' => rawpheno_function_unit($without_format),
- 'no units' => $no_units,
- 'skip' => $s,
- );
-
- // Store index number of Plot trait requirements.
- if (!isset($plot_req['planting date (date)']) && $without_format == 'plantingdate(date)') {
- $plot_req['planting date (date)'] = $o;
- }
- elseif (!isset($plot_req['location']) && $without_format == 'location') {
- $plot_req['location'] = $o;
- }
-
- $o++;
- }
-
- // Foreach validator with a scope of header, execute the validation callback & save the results.
- foreach($validators['header'] as $validator_name => $validator) {
- if (isset($validator['validation callback']) AND function_exists($validator['validation callback'])) {
- $result = call_user_func($validator['validation callback'], $header, $project_id);
-
- // The status needs to keep track of which rows failed for a given header.
- if ($result === FALSE) {
- $status[ $validator_name ] = $i;
- }
- elseif (is_array($result)) {
- $status[ $validator_name ] = $result;
- }
- }
- }
- }
- // VALIDATE THE ROW.
- else {
-
- $row_has_error = FALSE;
- foreach ($row as $column_index => $cell) {
- if ($header[$column_index]['skip'] == 1) continue;
-
- $column_name = $header[$column_index]['no units'];
- if (empty($column_name)) continue;
-
- // Prior to validating, Remove non-breaking whitespace by converting it to a blank space instead of removing it,
- // in case user intends a space between words/values.
- // trim() implementation below should drop unecessary leading and trailing spaces.
- if (preg_match('/\xc2\xa0/', $cell)) {
- $cell = preg_replace('/\xc2\xa0/', ' ', $cell);
- }
-
- // We always want to strip flanking white space.
- // FYI: This is done when the data is loaded as well.
- $cell = trim($cell);
-
- // For consistency, convert all variations of not applicable to NA.
- if (is_string($cell) && in_array(strtolower($cell), $not_applicable)) {
- $cell = 'NA';
- }
-
- // Foreach validator:
- foreach (array('all','subset') as $scope) {
- foreach($validators[$scope] as $validator_name => $validator) {
-
- // Only validate if there is a validation callback.
- if (isset($validator['validation callback']) AND function_exists($validator['validation callback'])) {
-
- // Only validate if the current validator applies to the current column.
- // Specifically, if there are no defined headers it's applicable to
- // OR if the current header is in the list of applicable headers.
- if (!isset($validator['headers']) OR in_array($column_name, $validator['headers'])) {
-
- // Execute the validation callback & save the results.
- $tmp_storage = (isset($storage[$validator_name])) ? $storage[$validator_name] : array();
- $context = array(
- 'row index' => $i,
- 'column index' => $column_index,
- 'row' => $row,
- 'header' => $header
- );
-
- // If column header is Plot, attach Plot validation requirement to
- // $context array. The indexes will be used to fetch the cell value in context row.
- if ($column_name == 'Plot') {
- $context['plot_req'] = $plot_req;
- }
-
- $result = $validator['validation callback']($cell, $context, $tmp_storage, $project_id);
-
- // Note: we use tmp storage b/c passing $storage[$validator_name] directly
- // doesn't seem to work.
- $storage[$validator_name] = $tmp_storage;
-
- // The status needs to keep track of which rows failed for a given header.
- if (is_array($result)) {
- $status[ $validator_name ][ $column_name ][$i] = $result;
- $row_has_error = TRUE;
- }
- elseif ($result !== TRUE) {
- $status[ $validator_name ][ $column_name ][$i] = $i;
- $row_has_error = TRUE;
- }
- }
- }
- }
- }
- }
-
- if ($row_has_error) $num_errored_rows++;
- }
-
- // Only check until you have 10 rows with errors.
- if ($num_errored_rows >= 10) {
- // We only want to present the warning if this is not the end of the file ;-)
- $has_next = $xls_obj->next();
- if ($has_next AND strlen(trim(implode('', $has_next))) >= 1) {
- $check_limit_message = "We have only checked the first $i lines of your file. Please fix the errors reported below and then upload the fixed file.";
-
- if ($source == 'upload') {
- drupal_set_message($check_limit_message, 'error');
- return $status;
- }
- elseif ($source == 'backup') {
- return array('status' => $status, 'check_limit' => $check_limit_message);
- }
- }
- }
- }
- }
-
- // Make sure all validators are represented in status.
- // If they are not already then a failure wasn't recorded -thus they passed :-).
- foreach($all_validators as $validator_name => $validator) {
- if (!isset($status[$validator_name])) {
- $status[$validator_name] = TRUE;
- }
- }
-
- return $status;
-}
-
-/**
- * Open the Excel file using the spreadsheet reader.
- *
- * @param $file
- * A Drupal managed file object.
- * @return
- * An object representing the Excel file.
- */
-function rawpheno_open_file($file) {
- // Grab the path and extension from the file.
- $xls_file = drupal_realpath($file->uri);
- $xls_extension = pathinfo($file->filename, PATHINFO_EXTENSION);
-
- // Validate that the spreadsheet is either xlsx or xls and open the spreadsheet using
- // the correct class.
- // XLSX:
- if ($xls_extension == 'xlsx') {
- $xls_obj = new SpreadsheetReader_XLSX($xls_file);
- }
- // XLS:
- elseif ($xls_extension == 'xls') {
- // PLS INCLUDE THIS FILE ONLY FOR XLS TYPE.
- $xls_lib = libraries_load('spreadsheet_reader');
- $lib_path = $xls_lib['path'];
-
- include_once $lib_path . 'SpreadsheetReader_XLS.php';
- $xls_obj = new SpreadsheetReader_XLS($xls_file);
- }
-
- return $xls_obj;
-}
-
-/**
- * Changes the worksheet in the Excel Object.
- *
- * @param $xls_obj
- * The object describing this Excel workbook.
- * @param $tab_name
- * The name of the tab you would like to switch to.
- * @return
- * TRUE if it found the tab and FALSE otherwise.
- */
-function rawpheno_change_sheet(&$xls_obj, $tab_name) {
- // Get all the sheets in the workbook.
- $xls_sheets = $xls_obj->Sheets();
-
- // Locate the measurements sheet.
- foreach($xls_sheets as $sheet_key => $sheet_value) {
- $xls_obj->ChangeSheet($sheet_key);
-
- // Only process the measurements worksheet.
- if (rawpheno_function_delformat($sheet_value) == 'measurements') {
- return TRUE;
- }
- }
-
- return FALSE;
-}
-
-
-/**
- * Adds the necessary files for EXCEL parsing.
- */
-function rawpheno_add_parsing_libraries($file_type = 'XLSX') {
- // Function call libraries_load() base on the implementation
- // of hook_libraries_info() in rawpheno.module.
- $xls_lib = libraries_load('spreadsheet_reader');
- // Library path information returned will be used
- // to include individual library files required.
- $lib_path = $xls_lib['path'];
-
- // Include parser library. PLS DO NOT ALTER ORDER!!!
- // To stop parser from auto formatting date to MM/DD/YY,
- // suggest a new date format YYYY-mm-dd in:
- // line 678 in excel_reader2.php
- // 0xe => "m/d/Y", to 0xe => "Y-m-d",
- // line 834 in SpreadsheetReader_XLSX.php
- // $Value = $Value -> format($Format['Code']); to $Value = $Value -> format('Y-m-d');
- //
- include_once $lib_path . 'php-excel-reader/excel_reader2.php';
- include_once $lib_path . 'SpreadsheetReader_XLSX.php';
- include_once $lib_path . 'SpreadsheetReader.php';
-
- if ($file_type == 'XLS') {
- // PLS INCLUDE THIS FILE ONLY FOR XLS TYPE.
- include_once $lib_path . 'SpreadsheetReader_XLS.php';
- }
-}
-
-
-/**
- * Function to remove all formatting from a cell value.
- *
- * @param $xls_cell_value
- * Contains a value of a cell.
- * @return
- * Contains a cell value with all formatting removed.
- */
-function rawpheno_function_delformat($xls_cell_value) {
- // Remove any extra spaces, new lines, leading and trainling spaces
- // and covert the final result to lowercase.
- return trim(strtolower(preg_replace('!\s+!', '', $xls_cell_value)));
-}
-
-
-/**
- * Function to extract the unit from the column header.
- *
- * @param $xls_header_cell
- * A string containing a column header.
- * @return
- * A string containing the unit found from the column header.
- */
-function rawpheno_function_unit($xls_header_cell) {
- // Remove all formatting.
- $temp_value = rawpheno_function_delformat($xls_header_cell);
-
- // If this is a scale then return that.
- if (preg_match('/\(scale/',$temp_value)) {
- return 'scale';
- }
-
- // Remove the following characters.
- $cell_value = str_replace(array(';', '1st', '2nd', 'r1', 'r3', 'r5', 'r7', ': 1-5'), '', $temp_value);
-
- // Extract text information inside the parenthesis.
- preg_match("/.*\(([^)]*)\)/", $cell_value, $match);
-
- // Return unit found, or default to text if no unit.
- return (isset($match[1])) ? trim($match[1]) : 'text';
-}
-
-
-/**
- * Function to determine additional column headers in the spreadsheet. Additional column headers are
- * headers that are no part of the predefined headers set of the project.
- *
- * @param $file
- * The full path to the excel file containing data.
- * @param $project_id
- * Project id number the spreadsheet is specific to.
- * @return
- * An array containing all additional column headers detected.
- */
-function rawpheno_indicate_new_headers($file, $project_id) {
- // Retrieve the header for the indicated file.
- rawpheno_add_parsing_libraries();
- $xls_obj = rawpheno_open_file($file);
- rawpheno_change_sheet($xls_obj, 'measurements');
-
- // Note: we use the foreach here
- // because the library documentation doesn't have a single fetch function.
- foreach ($xls_obj as $xls_headers) { break; }
-
- // Array to hold epected column headers specific to a given project.
- $expected_headers = rawpheno_project_traits($project_id);
-
- // Remove any formatting in each column headers.
- $expected_headers = array_map('rawpheno_function_delformat', $expected_headers);
-
- // Array to hold new column headers.
- $new_headers = array();
-
- // Assuming the file actually has a non-empty header row...
- if (count($xls_headers) > 0) {
- // Read each column header and compare against expected column headers.
- foreach($xls_headers as $value) {
- $temp_value = rawpheno_function_delformat($value);
-
- // Determine if column header exists in the expected column headers.
- if (!in_array($temp_value, $expected_headers) && !empty($value)) {
- // Not in expected column headers, save it as new header.
- $value = preg_replace('/\s+/', ' ', $value);
- $new_headers[] = $value;
- }
- }
- }
-
- return $new_headers;
-}
-
-
-/**
- * Get all column headers.
- *
- * @param $file
- * The full path to the excel file containing data.
- * @return
- * An array of headers.
- */
-function rawpheno_all_headers($file) {
- // Retrieve the header for the indicated file.
- rawpheno_add_parsing_libraries();
- $xls_obj = rawpheno_open_file($file);
- rawpheno_change_sheet($xls_obj, 'measurements');
- // Note: we use the foreach here
- // because the library documentation doesn't have a single fetch function.
-
- $arr_headers = array();
- foreach ($xls_obj as $xls_headers) {
- foreach($xls_headers as $h) {
- if (strlen($h) > 2) {
- $arr_headers[] = trim($h);
- }
- }
- break;
- }
-
- return $arr_headers;
-}
-
-
-/**
- * Count all rows in a spreadsheet.
- *
- * @param $file
- * The full path to the excel file containing data.
- * @return
- * An integer value of the total rows.
- */
-function rawpheno_count_rows($xls_obj) {
- // Row of 5 chars or more long is a row.
- $count_rows = 0;
- foreach ($xls_obj as $row) {
- if (strlen(implode('', $row)) > 5) {
- $count_rows++;
- }
- }
-
- // Less header row.
- return $count_rows - 1;
-}
-
-
-/**
- * Retrieve the cvterm_id for a given header.
- *
- * @param $header
- * The unchanged/original header text for the trait.
- * @return
- * The cvterm_id for the trait.
- */
-function rawpheno_get_trait_id($header) {
- // New lines.
- $header = str_replace(array("\n", "\r"), ' ', $header);
- // Extra spaces.
- $header = preg_replace('!\s+!', ' ', $header);
-
- // Query trait. Module stores unit in lowercase but user can use any case
- // in the spreadsheet. eg Planting Date (date) and Planting Date (Date).
- // @note cvterm.name + cv.name + not obsolete combination is unique (constraints).
- $sql = "SELECT t2.cvterm_id
- FROM {cv} AS t1 INNER JOIN {cvterm} AS t2 USING(cv_id)
- WHERE lower(t2.name) = :cvterm_name AND t1.name = :cv_name AND is_obsolete = 0";
-
- $args = array(':cvterm_name' => trim(strtolower($header)), ':cv_name' => 'phenotype_measurement_types');
- $type = chado_query($sql, $args)
- ->fetchObject();
-
- if ($type->cvterm_id) {
- return $type->cvterm_id;
- }
-
- return FALSE;
-}
-
-
-/**
- * Retrieve the unit for the trait.
- *
- * @param $trait_name
- * The name of the trait as found in the column header.
- * @param $trait_id
- * The cvterm_id of the trait if you have it (OPTIONAL).
- * @return
- * Returns an array with the cvterm_id and name of the unit.
- */
-function rawpheno_get_trait_unit($trait_name, $trait_id = NULL) {
- // Get the trait id if that is not provided to us.
- if ($trait_id == NULL) {
- $trait_id = rawpheno_get_trait_id($trait_name);
- }
-
- // First we try to get the unit through relationships since that avoids making assumptions.
- // in chado.cvterm_relationship.
- // @todo make this more specific (restrict relationship by type?)
- // @todo rather then limit, check if there are 1+ and warn the admin.
- $sql = "SELECT cvterm_id, name FROM {cvterm} WHERE cvterm_id =
- (SELECT subject_id FROM {cvterm_relationship} WHERE object_id = :trait LIMIT 1)";
-
- $args = array(':trait' => $trait_id);
- $unit = chado_query($sql, $args);
-
- if ($unit->rowCount() > 0) {
- $r = $unit->fetchObject();
- return array('id' => $r->cvterm_id, 'name' => $r->name);
- }
-
- // If that doesn't work then we try to extract it from the name.
- // Note: if the following function is unable to extract the unit then it will default to text.
- $unit_name = rawpheno_function_unit($trait_name);
-
- // Column header does not contain unit, use text as default
- if (function_exists('chado_get_cvterm')) {
- $cvterm = chado_get_cvterm(array('name' => $unit_name, 'cv_id' => array('name' => 'phenotype_measurement_units')));
- }
- else {
- $cvterm = tripal_get_cvterm(array('name' => $unit_name, 'cv_id' => array('name' => 'phenotype_measurement_units')));
- }
-
- if ($cvterm) {
- return array('id' => $cvterm->cvterm_id, 'name' => $cvterm->name);
- }
-
- return FALSE;
-}
-
-
-/**
- * Remove the unit part from a trait.
- *
- * @param $trait_name
- * A string containing the trait name as formatted in cvterm name.
- * @return
- * A string containing the trait name without the unit.
- */
-function rawpheno_get_trait_name($trait_name) {
- $t = explode('(', $trait_name);
-
- // Given a trait as defined in cvterm name in the following format:
- // Trait name (Trait Rep; Unit), extract the trait name only and return
- // the extracted name.
- return (count($t) > 1) ? trim(preg_replace('/\(.*/', ' ', $trait_name)) : $trait_name;
-}
-
-
-/**
- * Get all the essential traits in a project.
- *
- * @param $project_id
- * An integer containing the project ID number.
- * @return
- * An array containing all essential traits in a project.
- */
-function rawpheno_project_essential_traits($project_id) {
- if (isset($project_id) AND $project_id > 0) {
- // Get array of trait types
- $trait_type = rawpheno_function_trait_types();
-
- // Array to hold trait names.
- $arr_essential_traits = array();
-
- // Query essential traits in a project.
- $sql = "SELECT TRIM(t1.name) AS cvterm
- FROM {cvterm} AS t1 RIGHT JOIN pheno_project_cvterm AS t2 USING (cvterm_id)
- WHERE
- t2.project_id = :project_id
- AND t2.type IN (:essential)
- ORDER BY t1.name ASC";
-
- $args = array(':project_id' => $project_id, ':essential' => array($trait_type['type1'], $trait_type['type4']));
- $trait = chado_query($sql, $args);
-
- foreach($trait as $t) {
- $m = rawpheno_get_trait_name($t->cvterm);
- $arr_essential_traits[] = $m;
- }
-
- // Add Name column header to the traits returned.
- $arr_essential_traits[] = 'Name';
-
- return $arr_essential_traits;
- }
-}
-
-
-/**
- * Get all the plant property traits in a project selected.
- *
- * @param $project_id
- * An integer containing the project ID number.
- * @return
- * An array containing all essential traits in a project.
- */
-function rawpheno_project_plantproperty_traits($project_id) {
- if (isset($project_id) AND $project_id > 0) {
- // Get array of trait types
- $trait_type = rawpheno_function_trait_types();
-
- // Array to hold trait names.
- $arr_plantproperty_traits = array();
-
- // Query plant property traits in a project.
- $sql = "SELECT TRIM(t1.name) AS cvterm
- FROM {cvterm} AS t1 RIGHT JOIN pheno_project_cvterm AS t2 USING (cvterm_id)
- WHERE t2.project_id = :project_id AND t2.type = :plantproperty
- ORDER BY t1.name ASC";
-
- // traits of type plantproperty.
- $args = array(':project_id' => $project_id, ':plantproperty' => $trait_type['type4']);
- $trait = chado_query($sql, $args);
-
- foreach($trait as $t) {
- // Remove the trait rep and unit from the trait.
- $m = rawpheno_get_trait_name($t->cvterm);
- $arr_plantproperty_traits[] = $m;
- }
-
- return $arr_plantproperty_traits;
- }
-}
-
-
-/**
- * Get all traits available in a project whether essential or not.
- *
- * @param $project_id
- * An integer containing the project ID number.
- * @return
- * An array containing all traits available in a project.
- */
-function rawpheno_project_traits($project_id) {
- if (isset($project_id) AND $project_id > 0) {
- $arr_trait = array();
-
- // Query column headers in a project.
- $sql = "SELECT TRIM(t1.name) AS cvterm
- FROM {cvterm} AS t1 RIGHT JOIN pheno_project_cvterm AS t2 USING (cvterm_id)
- WHERE t2.project_id = :project_id
- ORDER BY t1.name ASC";
-
- $args = array(':project_id' => $project_id);
- $trait = chado_query($sql, $args);
-
- foreach($trait as $t) {
- $arr_trait[] = $t->cvterm;
- }
-
- // Add Name column header to the traits returned.
- $arr_trait[] = 'Name';
-
- return $arr_trait;
- }
-}
-
-
-/**
- * Function to fetch information about a unit (Describe method) when available.
- *
- * @param $cvterm_id
- * An integer containing the cvterm id of a trait.
- */
-function rawpheno_function_cvterm_properties($cvterm_id) {
- // Narrow the search to cvterm of type measurement units.
- if (function_exists('chado_get_cv')) {
- $cv_unit = chado_get_cv(array('name' => 'phenotype_measurement_units'));
- }
- else {
- $cv_unit = tripal_get_cv(array('name' => 'phenotype_measurement_units'));
- }
-
- // In form state 2, describe header, user has the opportunity to describe the unit (Describe method field).
- // This information is stored in cvterm relationship together with the cvterm id of the unit as the subject_id
- // and cvterm_id of the header as the object_id. Given a header cvterm id, get the subject id and use it to
- // get the information required from cvtermprop table.
- // @todo check if there is more then one unit for a given trait and if so, warn the admin.
- $sql = "SELECT subject_id FROM {cvterm_relationship} WHERE object_id = :cvterm_id AND type_id = :cv_unit LIMIT 1";
- $args = array(':cvterm_id' => $cvterm_id, ':cv_unit' => $cv_unit->cv_id);
-
- $d = chado_query($sql, $args)
- ->fetchField();
-
- // @note cvterm_id + type_id + rank is unique (constraint).
- $sql = "SELECT value FROM {cvtermprop} WHERE type_id = :cv_unit AND cvterm_id = :cvterm_id AND rank = 0";
- $args = array(':cv_unit' => $cv_unit->cv_id, ':cvterm_id' => $d);
-
- $d = chado_query($sql, $args);
-
- if ($d->rowCount() == 1) {
- return $d->fetchField();
- }
- else {
- return 'Describe the method used not available';
- }
-}
+ $fid),
+ array('print' => TRUE)
+ );
+ tripal_report_error('rawpheno', TRIPAL_CRITICAL, '[CODE 100] Failed to load phenoypic data (job !id)', array('!id' => $job_id), array('print' => TRUE));
+ exit(1);
+ }
+
+ $xls_file = drupal_realpath($file->uri);
+
+ // INFO:
+ // Keep a record of which file we are loading in the logs.
+ // print "\nXLSX File: " . $xls_file . "\n";
+
+ // Add the libraries needed to parse excel files.
+ rawpheno_add_parsing_libraries();
+
+ // Open the file for reading
+ $xls_obj = rawpheno_open_file($file);
+ if (!$xls_obj) {
+ tripal_report_error(
+ 'rawpheno',
+ TRIPAL_CRITICAL,
+ 'Uploading Phenoypic Data: Unable to open file. File=@file',
+ array('@file' => print_r($file, TRUE)),
+ array('print' => TRUE)
+ );
+ tripal_report_error('rawpheno', TRIPAL_CRITICAL, '[CODE 101] Failed to load phenoypic data (job !id)', array('!id' => $job_id), array('print' => TRUE));
+ exit(2);
+ }
+
+ // Change to the correct spreadsheet.
+ rawpheno_change_sheet($xls_obj, 'measurements');
+
+ // Compute the total number of rows parsed.
+ $row_count = rawpheno_count_rows($xls_obj);
+
+ // File to write progress status.
+ $tmp = file_directory_temp();
+ $filename = $tmp . '/' . 'job-progress' . $job_id . '.txt';
+
+ // Variations of Not Applicable.
+ $not_applicable = array('na', 'n/a', 'n.a.');
+
+ // Start Transaction.
+ $TRANSACTION = db_transaction();
+ try {
+
+ // Read each row.
+ // INFO:
+ // print "\nNow parsing each row and saving it to the database...\nNumber of rows saved: \n";
+ $i = 0;
+
+ // Skip columns.
+ $skip = array();
+ // Project name.
+ $project_name = rawpheno_function_getproject($project_id);
+ // Calling all modules implementing hook_rawpheno_ignorecols_valsave_alter():
+ drupal_alter('rawpheno_ignorecols_valsave', $skip, $project_name);
+
+ // Each row in the spreadsheet.
+ foreach ($xls_obj as $row) {
+ // Create progress update by computing the number of rows saved in percent.
+ // Write to file and progress bar API reads the content and pass it JSON generator
+ // in file rawpheno.module function: rawpheno_upload_job_progress_json().
+ // Echo to terminal.
+
+ $percent = round(($i / $row_count) * 100);
+ if ($percent % 10 == 0) {
+ print $percent . '% complete...' . "\n";
+ }
+
+ // To file.
+ file_unmanaged_save_data($percent, $filename, FILE_EXISTS_REPLACE);
+
+ // HEADER!
+ // This is the header.
+ if ($i == 0) {
+ $header = $row;
+
+ // Find the index number of name header in the spreadsheet.
+ $name_index = array_search('name', array_map('rawpheno_function_delformat', $header));
+ if ($name_index === FALSE) {
+ tripal_report_error(
+ 'rawpheno',
+ TRIPAL_CRITICAL,
+ 'Uploading Phenoypic Data: Unable to determine the name column.',
+ array(),
+ array('print' => TRUE)
+ );
+ $TRANSACTION->rollback();
+ tripal_report_error('rawpheno', TRIPAL_CRITICAL, '[CODE 102] Failed to load phenoypic data (job !id)', array('!id' => $job_id), array('print' => TRUE));
+ exit(31);
+ }
+
+ $i++;
+ continue;
+ }
+
+ // NEXT ROW IF EMPTY!
+ // Don't continue processing if this row is empty.
+ if (strlen(trim(implode('', $row))) <= 5) {
+ break;
+ }
+
+ // VALID EACH ROW!
+ // Process the name column first since we need a plant_id before we can insert any more data.
+ // Name column header goes into pheno_plant.
+ // Check if stock name exists.
+ unset($stock_id);
+
+ // Prior to saving, Remove non-breaking whitespace by converting it to a blank space instead of removing it,
+ // in case user intends a space between words/values.
+ // trim() implementation below should drop unecessary leading and trailing spaces.
+ if (preg_match('/\xc2\xa0/', $row[$name_index])) {
+ $row[$name_index] = preg_replace('/\xc2\xa0/', ' ', $row[$name_index]);
+ }
+
+ $stock_id = rawpheno_function_getstockid(trim($row[$name_index]), $project_name);
+ // print $stock_id . ' -- ' . $row[$name_index] . "\n";
+
+ // Determine if name has a stock id number.
+ if (isset($stock_id) && $stock_id > 0) {
+ $p_id = 0;
+
+ // Test if stock was measured in the active project, if not, insert as a new record.
+ // Otherwise, do more check (plot) to see if plant_id should be re-used.
+ $sql = "
+ SELECT plant_id
+ FROM pheno_plant AS t1 INNER JOIN pheno_plant_project AS t2 USING(plant_id)
+ WHERE t1.stock_id = :stock_id AND t2.project_id = :project_id LIMIT 1";
+
+ $args = array(':stock_id' => $stock_id, ':project_id' => $project_id);
+ $p = chado_query($sql, $args);
+
+ if ($p->rowCount()) {
+ // Found a stock record in the project. Do more test.
+ // Array to hold plot headers.
+ // Plot, Rep, Location, Planting Date (date)
+ $arr_plot_cols = rawpheno_function_headers('plot');
+
+ // Construct query string.
+ // String : stock_id - plot - rep - location - year
+ // eg. 147-5-2-Saskatoon-2015
+ $plot = $stock_id;
+
+ // Given a row, construct the search string (format) above and use it to search if
+ // the such combination matched any record in the database.
+ foreach($arr_plot_cols as $plot_col) {
+ $plot_col = rawpheno_function_delformat($plot_col);
+
+ // Cell value of plot property header.
+ $col_index = array_search(strtolower($plot_col), array_map('rawpheno_function_delformat', $header));
+ $cell_val = trim($row[$col_index]);
+
+ // If planting date - extract the year value.
+ // Support NA and YYYY in planting date. Use this value when
+ // value cannot be split by -.
+ if ($plot_col == 'plantingdate(date)') {
+ $y = explode('-', $cell_val);
+
+ if (is_array($y)) {
+ // Extract the year only from planting date.
+ $cell_val = $y[0];
+ }
+
+ // Else use the NA or YYYY.
+ }
+
+ $plot .= '-' . $cell_val;
+ }
+
+ // Search the query string.
+ $p_id = rawpheno_function_plot_exists($plot, $project_id);
+ }
+
+
+ if ($p_id) {
+ // Plot found - re-use the plant_id.
+ $pheno_plantid = $p_id;
+ // INFO:
+ // print 'FOUND PLOT: ' . $plot . ' [re-using plot id #' . $pheno_plantid . '] ~ ';
+ }
+ else {
+ // Plot not found - insert as new row.
+ $pheno_plantid = db_insert('pheno_plant')
+ ->fields(array('stock_id' => $stock_id))
+ ->execute();
+
+ // Map this record/stock to a project.
+ db_insert('pheno_plant_project')
+ ->fields(array('project_id' => $project_id,
+ 'plant_id' => $pheno_plantid))
+ ->execute();
+
+ // INFO:
+ // print 'NEW STOCK: #' . $stock_id . ' [adding plot id #' . $pheno_plantid . '] ~ ';
+ }
+ }
+ else {
+ // Warn the admin that germplasm is not available...
+ // We want to stop loading if this is the case.
+ tripal_report_error(
+ 'rawpheno',
+ TRIPAL_CRITICAL,
+ 'Uploading Phenoypic Data: Germplasm doesn\'t exist (name=!name; row=!row)',
+ array('!name' => $row[$name_index], '!row' => $i),
+ array('print' => TRUE)
+ );
+ $TRANSACTION->rollback();
+ tripal_report_error('rawpheno', TRIPAL_CRITICAL, '[CODE 103] Failed to load phenoypic data (job !id)', array('!id' => $job_id), array('print' => TRUE));
+ exit(32);
+ }
+
+ // Read each row and each cell.
+ // Each row will be an array where name is always the first element.
+ foreach($row as $cell_index => $cell_entry) {
+ // Skip this cell when col from durpal_alter hook matches the col.
+ $h = rawpheno_function_delformat($header[$cell_index]);
+
+ if (count($skip) > 0 && in_array($h, $skip)) {
+ // print 'skipping value : ' . $h . '=' . $cell_entry . "\n";
+ continue;
+ }
+
+ // For consistency, convert all variations of not applicable to NA.
+ if (is_string($cell_entry) && in_array(strtolower($cell_entry), $not_applicable)) {
+ $cell_entry = 'NA';
+ }
+
+ // We don't want to insert empty data.
+ // That said, while PHP thinks 0 is empty, we do not.
+ if (!empty($cell_entry) OR (strval($cell_entry) === '0')) {
+
+ // Get the column header of a cell.
+ $cell_colheader = trim(str_replace(array("\n", "\r", " "), ' ', $header[$cell_index]));
+ // Remove additional spaces from column headers.
+ $cell_colheader = preg_replace('/\s+/', ' ', $cell_colheader);
+
+
+ // Determine if user wants to save this trait.
+ if (count($arr_newheaders) > 0 AND array_key_exists($cell_colheader, $arr_newheaders)) {
+ if ($arr_newheaders[$cell_colheader]['flag'] == 0) {
+ // Skip this cell if it is a new column header and user does not want to save
+ // this new trait;
+ continue;
+ }
+ elseif ($arr_newheaders[$cell_colheader]['flag'] == 1) {
+ // Get the cvterm name for this new header.
+ $alt_name = $arr_newheaders[$cell_colheader]['alt_header'];
+ $n = array('cvterm_id' => $alt_name, 'cv_id' => array('name' => 'phenotype_measurement_types'));
+
+ if (function_exists('chado_get_cvterm')) {
+ $name = chado_get_cvterm($n);
+ }
+ else {
+ $name = tripal_get_cvterm($n);
+ }
+
+ $cell_colheader = $name->name;
+ }
+ }
+
+ // Prior to saving, Remove non-breaking whitespace by converting it to a blank space instead of removing it,
+ // in case user intends a space between words/values.
+ // trim() implementation below should drop unecessary leading and trailing spaces.
+ if (preg_match('/\xc2\xa0/', $cell_entry)) {
+ $cell_entry = preg_replace('/\xc2\xa0/', ' ', $cell_entry);
+ }
+
+ // We always want to strip flanking white space.
+ // FYI: This is done when the data is validated as well.
+ $cell_entry = trim($cell_entry);
+ $cell_colheader = trim($cell_colheader);
+
+ // Determine which table to insert a column header.
+ // If this is the name column then doing nothing since we've already delt with it above.
+ if ($cell_index == $name_index) { continue; }
+
+ // PLOT, ENTRY, REP and LOCATION
+ // Cells containing column headers that are required.
+ // Traits: plot, entry, rep, location into pheno_plantprop.
+ elseif (in_array($cell_colheader, $plantprop_headers) && !empty($cell_colheader)) {
+ $t = array('name' => $cell_colheader, 'cv_id' => array('name' => 'phenotype_plant_property_types'));
+
+ if (function_exists('chado_get_cvterm')) {
+ $type = chado_get_cvterm($t);
+ }
+ else {
+ $type = tripal_get_cvterm($t);
+ }
+
+ $type_id = $type->cvterm_id;
+
+ // Ensure that cvterm_id is present before inserting to table
+ if(isset($type_id)) {
+ $tmp = db_insert('pheno_plantprop')
+ ->fields(array('plant_id' => $pheno_plantid,
+ 'type_id' => $type_id,
+ 'value' => $cell_entry))
+ ->execute();
+
+ if (!$tmp) {
+ tripal_report_error(
+ 'rawpheno',
+ TRIPAL_ERROR,
+ 'Uploading Phenoypic Data: Unable to insert plant property. Values=@values',
+ array('@values' => print_r(array('plant_id' => $pheno_plantid, 'type_id' => $type_id, 'value' => $cell_entry),TRUE)),
+ array('print' => TRUE)
+ );
+ $TRANSACTION->rollback();
+ tripal_report_error('rawpheno', TRIPAL_CRITICAL, '[CODE 104] Failed to load phenoypic data (job !id)', array('!id' => $job_id), array('print' => TRUE));
+ exit(33);
+ }
+ }
+
+ else {
+ tripal_report_error(
+ 'rawpheno',
+ TRIPAL_ERROR,
+ 'Uploading Phenoypic Data: Plant Property type !type does\'t exist.',
+ array('!type' => $cell_colheader),
+ array('print' => TRUE)
+ );
+ $TRANSACTION->rollback();
+ tripal_report_error('rawpheno', TRIPAL_CRITICAL, '[CODE 105] Failed to load phenoypic data (job !id)', array('!id' => $job_id), array('print' => TRUE));
+ exit(34);
+ }
+
+ }
+ // THE REST OF THE COLUMN HEADERS
+ // Everything else into pheno_measurements.
+ elseif ((!empty($cell_colheader) && $cell_entry != 'NA')
+ || ($cell_colheader == 'Planting Date (date)' && $cell_entry == 'NA')) {
+
+ // Allow NA only when header is planting date.
+
+ $c_h = rawpheno_function_delformat($cell_colheader);
+ if (in_array($c_h, $skip)) {
+ // print 'skipping header : ' . $cell_colheader . "\n";
+ continue;
+ }
+
+ // Get the cvterm_id for the trait measurement.
+ $type_id = rawpheno_get_trait_id($cell_colheader);
+
+ if (!$type_id) {
+ tripal_report_error(
+ 'rawpheno',
+ TRIPAL_ERROR,
+ 'Uploading Phenoypic Data: Missing Plant Measurement Type (Header=!colheader).',
+ array('!colheader' => $cell_colheader),
+ array('print' => TRUE)
+ );
+ $TRANSACTION->rollback();
+ tripal_report_error('rawpheno', TRIPAL_CRITICAL, '[CODE 106] Failed to load phenoypic data (job !id)', array('!id' => $job_id), array('print' => TRUE));
+ exit(37);
+ }
+
+ // Retrieve the unit for this trait.
+ $cv_unit = rawpheno_get_trait_unit($cell_colheader, $type_id);
+
+ if ($cv_unit) {
+ $unit_id = $cv_unit['id'];
+ $unit = $cv_unit['name'];
+ }
+ else {
+ tripal_report_error(
+ 'rawpheno',
+ TRIPAL_ERROR,
+ 'Uploading Phenoypic Data: Unable to find unit for Plant Measurement Type (Term=!name; Type ID=!id).',
+ array('!name' => $cell_colheader, '!id' => $type_id),
+ array('print' => TRUE)
+ );
+ $TRANSACTION->rollback();
+ tripal_report_error('rawpheno', TRIPAL_CRITICAL, '[CODE 107] Failed to load phenoypic data (job !id)', array('!id' => $job_id), array('print' => TRUE));
+ exit(37);
+ }
+
+ // Determine if cell requires scale member code.
+ // When unit is scale, find code equivalent in pheno_scale_member table.
+ if ($unit == 'scale') {
+ // Get pheno scale member code
+ $cvalue_id = db_query("SELECT member_id FROM {pheno_scale_member}
+ WHERE code = :code LIMIT 1",
+ array(':code' => trim($cell_entry)))
+ ->fetchField();
+ // We want to report an error if we can't find the scale memeber
+ // but only if there are any in the first place!
+ $num_members = db_query('SELECT count(*) FROM {pheno_scale_member} WHERE scale_id=:unit_id',
+ array(':unit_id' => $unit_id))->fetchField();
+ if (!$cvalue_id AND !empty($num_members)) {
+ tripal_report_error(
+ 'rawpheno',
+ TRIPAL_WARNING,
+ 'Uploading Phenoypic Data: Unable to find scale id for Plant Measurement Type (Trait=!trait; Term=!name; Type ID=!id; Scale Value=!scale).',
+ array('!trait' => $cell_colheader, '!name' => $unit, '!id' => $unit_id, '!scale' => $cell_entry),
+ array('print' => TRUE)
+ );
+ }
+
+ // Use default value in the cell if query to find scale member code
+ // has no equivalent value.
+ $cvalue_id = (isset($cvalue_id) && $cvalue_id > 0) ? $cvalue_id : $cell_entry;
+ }
+ else {
+ // No scale member value for the rest of traits.
+ $cvalue_id = '';
+ }
+
+ // Insert trait only when type_id and unit_id are not null.
+ if (isset($type_id) && isset($unit_id)) {
+
+ $temp = db_insert('pheno_measurements')
+ ->fields(array('plant_id' => $pheno_plantid,
+ 'type_id' => $type_id,
+ 'unit_id' => $unit_id,
+ 'cvalue_id' => $cvalue_id,
+ 'value' => $cell_entry,
+ 'modified' => date("D M d, Y h:i:s a", time())))
+ ->execute();
+
+ if (!$temp) {
+ tripal_report_error(
+ 'rawpheno',
+ TRIPAL_ERROR,
+ 'Uploading Phenoypic Data: Unable to insert measurement. Values=@values.',
+ array('@values' => print_r(array('plant_id' => $pheno_plantid,
+ 'type_id' => $type_id,
+ 'unit_id' => $unit_id,
+ 'cvalue_id' => $cvalue_id,
+ 'value' => $cell_entry,
+ 'modified' => date("D M d, Y h:i:s a", time())),TRUE)),
+ array('print' => TRUE)
+ );
+ $TRANSACTION->rollback();
+ tripal_report_error('rawpheno', TRIPAL_CRITICAL, '[CODE 108] Failed to load phenoypic data (job !id)', array('!id' => $job_id), array('print' => TRUE));
+ exit(38);
+ }
+ }
+ }
+ }
+ }
+
+ $i++;
+ }
+ }
+ catch (Exception $e) {
+ $TRANSACTION->rollback();
+ watchdog_exception('rawpheno', $e);
+ tripal_report_error('rawpheno', TRIPAL_CRITICAL, '[CODE 109] Failed to load phenoypic data (job !id)', array('!id' => $job_id), array('print' => TRUE));
+ exit(4);
+ }
+
+ unset($TRANSACTION); //Commit
+ print "Upload complete.\n";
+
+ print "\nUpdating the materialized view summarizing phenotypic data.\n";
+ $mview_id = tripal_get_mview_id('rawpheno_rawdata_summary');
+ if ($mview_id) tripal_populate_mview($mview_id);
+}
+
+
+/**
+ * Validates an excel file using any validators registered with rawpheno.
+ *
+ * @param $file
+ * A drupal managed_file object describing the uploaded spreadsheet.
+ * @param $project_id
+ * An integer containing project id selected in the project select box.
+ * This will map the data submitted to a project.
+ * @param $source
+ * A string containing the source of the file upload - Upload Data or Backup File.
+ *
+ * @return
+ * An array containing the validation result from each validator.
+ */
+function rawpheno_validate_excel_file($file, $project_id, $source) {
+ $status = array();
+
+ // Process the validators to make them easier to use.
+ // Specifically, sort them by their scope.
+ $validators = array();
+ $all_validators = module_invoke_all('rawpheno_validators');
+ foreach($all_validators as $k => $v) {
+ $validators[ $v['scope'] ][ $k ] = $v;
+ }
+
+ // Todo list.
+ $all_scope_validators = array('project', 'file', 'all', 'header', 'subset');
+
+ // Add the libraries needed to parse excel files.
+ rawpheno_add_parsing_libraries();
+
+ // Before performing any validation to the excel file. Ensure first that a project is selected.
+ foreach ($validators['project'] as $prj_validator_name => $prj_validator) {
+ if (isset($prj_validator['validation callback']) AND function_exists($prj_validator['validation callback'])) {
+ $status[ $prj_validator_name ] = call_user_func($prj_validator['validation callback'], $project_id);
+
+ // If returned false then halt validation.
+ if ($status[ $prj_validator_name ] === FALSE) {
+ // Fail the project and set the rest to TODO.
+ $status[ $prj_validator_name ] = FALSE;
+
+ // Todo the rest of validators.
+ // Since this is project scope and it got falsed - remove the project.
+ unset($all_scope_validators[0]);
+ foreach($all_scope_validators as $v) {
+ foreach($validators[ $v ] as $v_name => $validator) {
+ $status[ $v_name ] = 'todo';
+ }
+ }
+
+ return $status;
+ }
+ }
+ }
+
+ // First validate the whole file. If any of these fail then halt validation.
+ foreach ($validators['file'] as $validator_name => $validator) {
+ if (isset($validator['validation callback']) AND function_exists($validator['validation callback'])) {
+ $status[ $validator_name ] = call_user_func($validator['validation callback'], $file);
+
+ // If returned false then halt validation.
+ if ($status[ $validator_name ] === FALSE) {
+ // Fail the file and set the rest to TODO but set the project to passed
+ // first since it is assumed that project validator returned a passed value.
+ $status[ 'project_selected' ] = TRUE;
+ $status[ $validator_name ] = FALSE;
+
+ // Todo the rest of validators.
+ // Since project is completed. skip this scope.
+ unset($all_scope_validators[0]);
+ foreach($all_scope_validators as $v) {
+ foreach($validators[ $v ] as $v_name => $validator) {
+ if ($status[ $v_name ] === TRUE) {
+ $status[ $v_name ] = TRUE;
+ }
+ elseif ($status[ $v_name ] === FALSE) {
+ $status[ $v_name ] = FALSE;
+ }
+ else {
+ $status[ $v_name ] = 'todo';
+ }
+ }
+ }
+
+ return $status;
+ }
+ }
+ }
+
+ // Open the file for reading
+ $xls_obj = rawpheno_open_file($file);
+
+ // Change to the correct spreadsheet.
+ rawpheno_change_sheet($xls_obj, 'measurements');
+
+ // This increment variable $i is required since xls and xlsx
+ // parsers assign array index differently.
+ // XLS starts at 1, while XLSX at 0;
+ $i = 0;
+
+ // Variations of Not Applicable.
+ $not_applicable = array('na', 'n/a', 'n.a.');
+
+ // Skip columns.
+ $skip = array();
+ // Project name.
+ $project_name = rawpheno_function_getproject($project_id);
+ // Calling all modules implementing hook_rawpheno_ignorecols_valsave_alter():
+ drupal_alter('rawpheno_ignorecols_valsave', $skip, $project_name);
+
+ // Iterate though each row.
+ $num_errored_rows = 0;
+ $storage = array();
+ foreach($xls_obj as $row) {
+ $i++;
+
+ // Convert row into a string and check the length.
+ // This will exclude empty rows.
+ if (strlen(trim(implode('', $row))) >= 5) {
+
+ // VALIDATE THE HEADER.
+ if ($i == 1) {
+ // Save the header for later.
+ $header = array();
+ $new_header = array();
+ // Checking plot value requires cell value in Planting Date (date) and Location.
+ // Store index numbers of these two traits.
+ $plot_req = array();
+
+ $o = 0;
+ foreach ($row as $r) {
+ $without_format = rawpheno_function_delformat($r);
+
+ // To maintain index of both cells and header, tag either to skip or process
+ // based on headers in drupal_alter hook.
+ $s = (in_array($without_format, $skip)) ? 1 : 0;
+
+ // Remove new lines.
+ $rem_newline = str_replace(array("\n", "\r"), ' ', $r);
+ // Remove extra spaces.
+ $rem_spaces = preg_replace('/\s+/', ' ', $rem_newline);
+ // Remove leading and trailing spaces.
+ $r = trim($rem_spaces);
+ $no_units = rawpheno_get_trait_name($r);
+
+ $header[] = array(
+ 'no format' => $without_format,
+ 'original' => $r,
+ 'units' => rawpheno_function_unit($without_format),
+ 'no units' => $no_units,
+ 'skip' => $s,
+ );
+
+ // Store index number of Plot trait requirements.
+ if (!isset($plot_req['planting date (date)']) && $without_format == 'plantingdate(date)') {
+ $plot_req['planting date (date)'] = $o;
+ }
+ elseif (!isset($plot_req['location']) && $without_format == 'location') {
+ $plot_req['location'] = $o;
+ }
+
+ $o++;
+ }
+
+ // Foreach validator with a scope of header, execute the validation callback & save the results.
+ foreach($validators['header'] as $validator_name => $validator) {
+ if (isset($validator['validation callback']) AND function_exists($validator['validation callback'])) {
+ $result = call_user_func($validator['validation callback'], $header, $project_id);
+
+ // The status needs to keep track of which rows failed for a given header.
+ if ($result === FALSE) {
+ $status[ $validator_name ] = $i;
+ }
+ elseif (is_array($result)) {
+ $status[ $validator_name ] = $result;
+ }
+ }
+ }
+ }
+ // VALIDATE THE ROW.
+ else {
+
+ $row_has_error = FALSE;
+ foreach ($row as $column_index => $cell) {
+ if ($header[$column_index]['skip'] == 1) continue;
+
+ $column_name = $header[$column_index]['no units'];
+ if (empty($column_name)) continue;
+
+ // Prior to validating, Remove non-breaking whitespace by converting it to a blank space instead of removing it,
+ // in case user intends a space between words/values.
+ // trim() implementation below should drop unecessary leading and trailing spaces.
+ if (preg_match('/\xc2\xa0/', $cell)) {
+ $cell = preg_replace('/\xc2\xa0/', ' ', $cell);
+ }
+
+ // We always want to strip flanking white space.
+ // FYI: This is done when the data is loaded as well.
+ $cell = trim($cell);
+
+ // For consistency, convert all variations of not applicable to NA.
+ if (is_string($cell) && in_array(strtolower($cell), $not_applicable)) {
+ $cell = 'NA';
+ }
+
+ // Foreach validator:
+ foreach (array('all','subset') as $scope) {
+ foreach($validators[$scope] as $validator_name => $validator) {
+
+ // Only validate if there is a validation callback.
+ if (isset($validator['validation callback']) AND function_exists($validator['validation callback'])) {
+
+ // Only validate if the current validator applies to the current column.
+ // Specifically, if there are no defined headers it's applicable to
+ // OR if the current header is in the list of applicable headers.
+ if (!isset($validator['headers']) OR in_array($column_name, $validator['headers'])) {
+
+ // Execute the validation callback & save the results.
+ $tmp_storage = (isset($storage[$validator_name])) ? $storage[$validator_name] : array();
+ $context = array(
+ 'row index' => $i,
+ 'column index' => $column_index,
+ 'row' => $row,
+ 'header' => $header
+ );
+
+ // If column header is Plot, attach Plot validation requirement to
+ // $context array. The indexes will be used to fetch the cell value in context row.
+ if ($column_name == 'Plot') {
+ $context['plot_req'] = $plot_req;
+ }
+
+ $result = $validator['validation callback']($cell, $context, $tmp_storage, $project_id);
+
+ // Note: we use tmp storage b/c passing $storage[$validator_name] directly
+ // doesn't seem to work.
+ $storage[$validator_name] = $tmp_storage;
+
+ // The status needs to keep track of which rows failed for a given header.
+ if (is_array($result)) {
+ $status[ $validator_name ][ $column_name ][$i] = $result;
+ $row_has_error = TRUE;
+ }
+ elseif ($result !== TRUE) {
+ $status[ $validator_name ][ $column_name ][$i] = $i;
+ $row_has_error = TRUE;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if ($row_has_error) $num_errored_rows++;
+ }
+
+ // Only check until you have 10 rows with errors.
+ if ($num_errored_rows >= 10) {
+ // We only want to present the warning if this is not the end of the file ;-)
+ $has_next = $xls_obj->next();
+ if ($has_next AND strlen(trim(implode('', $has_next))) >= 1) {
+ $check_limit_message = "We have only checked the first $i lines of your file. Please fix the errors reported below and then upload the fixed file.";
+
+ if ($source == 'upload') {
+ drupal_set_message($check_limit_message, 'error');
+ return $status;
+ }
+ elseif ($source == 'backup') {
+ return array('status' => $status, 'check_limit' => $check_limit_message);
+ }
+ }
+ }
+ }
+ }
+
+ // Make sure all validators are represented in status.
+ // If they are not already then a failure wasn't recorded -thus they passed :-).
+ foreach($all_validators as $validator_name => $validator) {
+ if (!isset($status[$validator_name])) {
+ $status[$validator_name] = TRUE;
+ }
+ }
+
+ return $status;
+}
+
+/**
+ * Open the Excel file using the spreadsheet reader.
+ *
+ * @param $file
+ * A Drupal managed file object.
+ * @return
+ * An object representing the Excel file.
+ */
+function rawpheno_open_file($file) {
+ // Grab the path and extension from the file.
+ $xls_file = drupal_realpath($file->uri);
+ $xls_extension = pathinfo($file->filename, PATHINFO_EXTENSION);
+
+ // Validate that the spreadsheet is either xlsx or xls and open the spreadsheet using
+ // the correct class.
+ // XLSX:
+ if ($xls_extension == 'xlsx') {
+ $xls_obj = new SpreadsheetReader_XLSX($xls_file);
+ }
+ // XLS:
+ elseif ($xls_extension == 'xls') {
+ // PLS INCLUDE THIS FILE ONLY FOR XLS TYPE.
+ $xls_lib = libraries_load('spreadsheet_reader');
+ $lib_path = $xls_lib['path'];
+
+ include_once $lib_path . 'SpreadsheetReader_XLS.php';
+ $xls_obj = new SpreadsheetReader_XLS($xls_file);
+ }
+
+ return $xls_obj;
+}
+
+/**
+ * Changes the worksheet in the Excel Object.
+ *
+ * @param $xls_obj
+ * The object describing this Excel workbook.
+ * @param $tab_name
+ * The name of the tab you would like to switch to.
+ * @return
+ * TRUE if it found the tab and FALSE otherwise.
+ */
+function rawpheno_change_sheet(&$xls_obj, $tab_name) {
+ // Get all the sheets in the workbook.
+ $xls_sheets = $xls_obj->Sheets();
+
+ // Locate the measurements sheet.
+ foreach($xls_sheets as $sheet_key => $sheet_value) {
+ $xls_obj->ChangeSheet($sheet_key);
+
+ // Only process the measurements worksheet.
+ if (rawpheno_function_delformat($sheet_value) == 'measurements') {
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+
+/**
+ * Adds the necessary files for EXCEL parsing.
+ */
+function rawpheno_add_parsing_libraries($file_type = 'XLSX') {
+ // Function call libraries_load() base on the implementation
+ // of hook_libraries_info() in rawpheno.module.
+ $xls_lib = libraries_load('spreadsheet_reader');
+ // Library path information returned will be used
+ // to include individual library files required.
+ $lib_path = $xls_lib['path'];
+
+ // Include parser library. PLS DO NOT ALTER ORDER!!!
+ // To stop parser from auto formatting date to MM/DD/YY,
+ // suggest a new date format YYYY-mm-dd in:
+ // line 678 in excel_reader2.php
+ // 0xe => "m/d/Y", to 0xe => "Y-m-d",
+ // line 834 in SpreadsheetReader_XLSX.php
+ // $Value = $Value -> format($Format['Code']); to $Value = $Value -> format('Y-m-d');
+ //
+ include_once $lib_path . 'php-excel-reader/excel_reader2.php';
+ include_once $lib_path . 'SpreadsheetReader_XLSX.php';
+ include_once $lib_path . 'SpreadsheetReader.php';
+
+ if ($file_type == 'XLS') {
+ // PLS INCLUDE THIS FILE ONLY FOR XLS TYPE.
+ include_once $lib_path . 'SpreadsheetReader_XLS.php';
+ }
+}
+
+
+/**
+ * Function to remove all formatting from a cell value.
+ *
+ * @param $xls_cell_value
+ * Contains a value of a cell.
+ * @return
+ * Contains a cell value with all formatting removed.
+ */
+function rawpheno_function_delformat($xls_cell_value) {
+ // Remove any extra spaces, new lines, leading and trainling spaces
+ // and covert the final result to lowercase.
+ return trim(strtolower(preg_replace('!\s+!', '', $xls_cell_value)));
+}
+
+
+/**
+ * Function to extract the unit from the column header.
+ *
+ * @param $xls_header_cell
+ * A string containing a column header.
+ * @return
+ * A string containing the unit found from the column header.
+ */
+function rawpheno_function_unit($xls_header_cell) {
+ // Remove all formatting.
+ $temp_value = rawpheno_function_delformat($xls_header_cell);
+
+ // If this is a scale then return that.
+ if (preg_match('/\(scale/',$temp_value)) {
+ return 'scale';
+ }
+
+ // Remove the following characters.
+ $cell_value = str_replace(array(';', '1st', '2nd', 'r1', 'r3', 'r5', 'r7', ': 1-5'), '', $temp_value);
+
+ // Extract text information inside the parenthesis.
+ preg_match("/.*\(([^)]*)\)/", $cell_value, $match);
+
+ // Return unit found, or default to text if no unit.
+ return (isset($match[1])) ? trim($match[1]) : 'text';
+}
+
+
+/**
+ * Function to determine additional column headers in the spreadsheet. Additional column headers are
+ * headers that are no part of the predefined headers set of the project.
+ *
+ * @param $file
+ * The full path to the excel file containing data.
+ * @param $project_id
+ * Project id number the spreadsheet is specific to.
+ * @return
+ * An array containing all additional column headers detected.
+ */
+function rawpheno_indicate_new_headers($file, $project_id) {
+ // Retrieve the header for the indicated file.
+ rawpheno_add_parsing_libraries();
+ $xls_obj = rawpheno_open_file($file);
+ rawpheno_change_sheet($xls_obj, 'measurements');
+
+ // Note: we use the foreach here
+ // because the library documentation doesn't have a single fetch function.
+ foreach ($xls_obj as $xls_headers) { break; }
+
+ // Array to hold epected column headers specific to a given project.
+ $expected_headers = rawpheno_project_traits($project_id);
+
+ // Remove any formatting in each column headers.
+ $expected_headers = array_map('rawpheno_function_delformat', $expected_headers);
+
+ // Array to hold new column headers.
+ $new_headers = array();
+
+ // Assuming the file actually has a non-empty header row...
+ if (count($xls_headers) > 0) {
+ // Read each column header and compare against expected column headers.
+ foreach($xls_headers as $value) {
+ $temp_value = rawpheno_function_delformat($value);
+
+ // Determine if column header exists in the expected column headers.
+ if (!in_array($temp_value, $expected_headers) && !empty($value)) {
+ // Not in expected column headers, save it as new header.
+ $value = preg_replace('/\s+/', ' ', $value);
+ $new_headers[] = $value;
+ }
+ }
+ }
+
+ return $new_headers;
+}
+
+
+/**
+ * Get all column headers.
+ *
+ * @param $file
+ * The full path to the excel file containing data.
+ * @return
+ * An array of headers.
+ */
+function rawpheno_all_headers($file) {
+ // Retrieve the header for the indicated file.
+ rawpheno_add_parsing_libraries();
+ $xls_obj = rawpheno_open_file($file);
+ rawpheno_change_sheet($xls_obj, 'measurements');
+ // Note: we use the foreach here
+ // because the library documentation doesn't have a single fetch function.
+
+ $arr_headers = array();
+ foreach ($xls_obj as $xls_headers) {
+ foreach($xls_headers as $h) {
+ if (strlen($h) > 2) {
+ $arr_headers[] = trim($h);
+ }
+ }
+ break;
+ }
+
+ return $arr_headers;
+}
+
+
+/**
+ * Count all rows in a spreadsheet.
+ *
+ * @param $file
+ * The full path to the excel file containing data.
+ * @return
+ * An integer value of the total rows.
+ */
+function rawpheno_count_rows($xls_obj) {
+ // Row of 5 chars or more long is a row.
+ $count_rows = 0;
+ foreach ($xls_obj as $row) {
+ if (strlen(implode('', $row)) > 5) {
+ $count_rows++;
+ }
+ }
+
+ // Less header row.
+ return $count_rows - 1;
+}
+
+
+/**
+ * Retrieve the cvterm_id for a given header.
+ *
+ * @param $header
+ * The unchanged/original header text for the trait.
+ * @return
+ * The cvterm_id for the trait.
+ */
+function rawpheno_get_trait_id($header) {
+ // New lines.
+ $header = str_replace(array("\n", "\r"), ' ', $header);
+ // Extra spaces.
+ $header = preg_replace('!\s+!', ' ', $header);
+
+ // Query trait. Module stores unit in lowercase but user can use any case
+ // in the spreadsheet. eg Planting Date (date) and Planting Date (Date).
+ // @note cvterm.name + cv.name + not obsolete combination is unique (constraints).
+ $sql = "SELECT t2.cvterm_id
+ FROM {cv} AS t1 INNER JOIN {cvterm} AS t2 USING(cv_id)
+ WHERE lower(t2.name) = :cvterm_name AND t1.name = :cv_name AND is_obsolete = 0";
+
+ $args = array(':cvterm_name' => trim(strtolower($header)), ':cv_name' => 'phenotype_measurement_types');
+ $type = chado_query($sql, $args)
+ ->fetchObject();
+
+ if ($type->cvterm_id) {
+ return $type->cvterm_id;
+ }
+
+ return FALSE;
+}
+
+
+/**
+ * Retrieve the unit for the trait.
+ *
+ * @param $trait_name
+ * The name of the trait as found in the column header.
+ * @param $trait_id
+ * The cvterm_id of the trait if you have it (OPTIONAL).
+ * @return
+ * Returns an array with the cvterm_id and name of the unit.
+ */
+function rawpheno_get_trait_unit($trait_name, $trait_id = NULL) {
+ // Get the trait id if that is not provided to us.
+ if ($trait_id == NULL) {
+ $trait_id = rawpheno_get_trait_id($trait_name);
+ }
+
+ // First we try to get the unit through relationships since that avoids making assumptions.
+ // in chado.cvterm_relationship.
+ // @todo make this more specific (restrict relationship by type?)
+ // @todo rather then limit, check if there are 1+ and warn the admin.
+ $sql = "SELECT cvterm_id, name FROM {cvterm} WHERE cvterm_id =
+ (SELECT subject_id FROM {cvterm_relationship} WHERE object_id = :trait LIMIT 1)";
+
+ $args = array(':trait' => $trait_id);
+ $unit = chado_query($sql, $args);
+
+ if ($unit->rowCount() > 0) {
+ $r = $unit->fetchObject();
+ return array('id' => $r->cvterm_id, 'name' => $r->name);
+ }
+
+ // If that doesn't work then we try to extract it from the name.
+ // Note: if the following function is unable to extract the unit then it will default to text.
+ $unit_name = rawpheno_function_unit($trait_name);
+
+ // Column header does not contain unit, use text as default
+ if (function_exists('chado_get_cvterm')) {
+ $cvterm = chado_get_cvterm(array('name' => $unit_name, 'cv_id' => array('name' => 'phenotype_measurement_units')));
+ }
+ else {
+ $cvterm = tripal_get_cvterm(array('name' => $unit_name, 'cv_id' => array('name' => 'phenotype_measurement_units')));
+ }
+
+ if ($cvterm) {
+ return array('id' => $cvterm->cvterm_id, 'name' => $cvterm->name);
+ }
+
+ return FALSE;
+}
+
+
+/**
+ * Remove the unit part from a trait.
+ *
+ * @param $trait_name
+ * A string containing the trait name as formatted in cvterm name.
+ * @return
+ * A string containing the trait name without the unit.
+ */
+function rawpheno_get_trait_name($trait_name) {
+ $t = explode('(', $trait_name);
+
+ // Given a trait as defined in cvterm name in the following format:
+ // Trait name (Trait Rep; Unit), extract the trait name only and return
+ // the extracted name.
+ return (count($t) > 1) ? trim(preg_replace('/\(.*/', ' ', $trait_name)) : $trait_name;
+}
+
+
+/**
+ * Get all the essential traits in a project.
+ *
+ * @param $project_id
+ * An integer containing the project ID number.
+ * @return
+ * An array containing all essential traits in a project.
+ */
+function rawpheno_project_essential_traits($project_id) {
+ if (isset($project_id) AND $project_id > 0) {
+ // Get array of trait types
+ $trait_type = rawpheno_function_trait_types();
+
+ // Array to hold trait names.
+ $arr_essential_traits = array();
+
+ // Query essential traits in a project.
+ $sql = "SELECT TRIM(t1.name) AS cvterm
+ FROM {cvterm} AS t1 RIGHT JOIN pheno_project_cvterm AS t2 USING (cvterm_id)
+ WHERE
+ t2.project_id = :project_id
+ AND t2.type IN (:essential)
+ ORDER BY t1.name ASC";
+
+ $args = array(':project_id' => $project_id, ':essential' => array($trait_type['type1'], $trait_type['type4']));
+ $trait = chado_query($sql, $args);
+
+ foreach($trait as $t) {
+ $m = rawpheno_get_trait_name($t->cvterm);
+ $arr_essential_traits[] = $m;
+ }
+
+ // Add Name column header to the traits returned.
+ $arr_essential_traits[] = 'Name';
+
+ return $arr_essential_traits;
+ }
+}
+
+
+/**
+ * Get all the plant property traits in a project selected.
+ *
+ * @param $project_id
+ * An integer containing the project ID number.
+ * @return
+ * An array containing all essential traits in a project.
+ */
+function rawpheno_project_plantproperty_traits($project_id) {
+ if (isset($project_id) AND $project_id > 0) {
+ // Get array of trait types
+ $trait_type = rawpheno_function_trait_types();
+
+ // Array to hold trait names.
+ $arr_plantproperty_traits = array();
+
+ // Query plant property traits in a project.
+ $sql = "SELECT TRIM(t1.name) AS cvterm
+ FROM {cvterm} AS t1 RIGHT JOIN pheno_project_cvterm AS t2 USING (cvterm_id)
+ WHERE t2.project_id = :project_id AND t2.type = :plantproperty
+ ORDER BY t1.name ASC";
+
+ // traits of type plantproperty.
+ $args = array(':project_id' => $project_id, ':plantproperty' => $trait_type['type4']);
+ $trait = chado_query($sql, $args);
+
+ foreach($trait as $t) {
+ // Remove the trait rep and unit from the trait.
+ $m = rawpheno_get_trait_name($t->cvterm);
+ $arr_plantproperty_traits[] = $m;
+ }
+
+ return $arr_plantproperty_traits;
+ }
+}
+
+
+/**
+ * Get all traits available in a project whether essential or not.
+ *
+ * @param $project_id
+ * An integer containing the project ID number.
+ * @return
+ * An array containing all traits available in a project.
+ */
+function rawpheno_project_traits($project_id) {
+ if (isset($project_id) AND $project_id > 0) {
+ $arr_trait = array();
+
+ // Query column headers in a project.
+ $sql = "SELECT TRIM(t1.name) AS cvterm
+ FROM {cvterm} AS t1 RIGHT JOIN pheno_project_cvterm AS t2 USING (cvterm_id)
+ WHERE t2.project_id = :project_id
+ ORDER BY t1.name ASC";
+
+ $args = array(':project_id' => $project_id);
+ $trait = chado_query($sql, $args);
+
+ foreach($trait as $t) {
+ $arr_trait[] = $t->cvterm;
+ }
+
+ // Add Name column header to the traits returned.
+ $arr_trait[] = 'Name';
+
+ return $arr_trait;
+ }
+}
+
+
+/**
+ * Function to fetch information about a unit (Describe method) when available.
+ *
+ * @param $cvterm_id
+ * An integer containing the cvterm id of a trait.
+ */
+function rawpheno_function_cvterm_properties($cvterm_id) {
+ // Narrow the search to cvterm of type measurement units.
+ if (function_exists('chado_get_cv')) {
+ $cv_unit = chado_get_cv(array('name' => 'phenotype_measurement_units'));
+ }
+ else {
+ $cv_unit = tripal_get_cv(array('name' => 'phenotype_measurement_units'));
+ }
+
+ // In form state 2, describe header, user has the opportunity to describe the unit (Describe method field).
+ // This information is stored in cvterm relationship together with the cvterm id of the unit as the subject_id
+ // and cvterm_id of the header as the object_id. Given a header cvterm id, get the subject id and use it to
+ // get the information required from cvtermprop table.
+ // @todo check if there is more then one unit for a given trait and if so, warn the admin.
+ $sql = "SELECT subject_id FROM {cvterm_relationship} WHERE object_id = :cvterm_id AND type_id = :cv_unit LIMIT 1";
+ $args = array(':cvterm_id' => $cvterm_id, ':cv_unit' => $cv_unit->cv_id);
+
+ $d = chado_query($sql, $args)
+ ->fetchField();
+
+ // @note cvterm_id + type_id + rank is unique (constraint).
+ $sql = "SELECT value FROM {cvtermprop} WHERE type_id = :cv_unit AND cvterm_id = :cvterm_id AND rank = 0";
+ $args = array(':cv_unit' => $cv_unit->cv_id, ':cvterm_id' => $d);
+
+ $d = chado_query($sql, $args);
+
+ if ($d->rowCount() == 1) {
+ return $d->fetchField();
+ }
+ else {
+ return 'Describe the method used not available';
+ }
+}
diff --git a/include/rawpheno.upload.form.inc b/includes/rawpheno.upload.form.inc
old mode 100755
new mode 100644
similarity index 97%
rename from include/rawpheno.upload.form.inc
rename to includes/rawpheno.upload.form.inc
index d03a742..b33378b
--- a/include/rawpheno.upload.form.inc
+++ b/includes/rawpheno.upload.form.inc
@@ -1,1260 +1,1260 @@
- 'markup',
- '#markup' => t('Standard Procedure ❯'),
- );
-
- // If the stage is not set then default to the first stage (i.e. 'check' )
- if (!isset($form_state['stage'])) {
- $form_state['stage'] = 'check';
- }
-
- // If a job_id was provided in the URL then the user wants information
- // on a prvious bulk loading job. Thus we should show them the last step.
- if (isset($form_state['build_info']['args'][0])) {
- $form_state['stage'] = 'save';
- }
-
- // Add the stage tracker/header.
- $form = rawpheno_get_header($form, $form_state);
- // Holds the current stage.
- $form_stage = $form_state['stage'];
-
- // Stage indicator for theme function.
- $form['current_stage'] = array(
- '#type' => 'value',
- '#value' => $form_stage,
- );
-
- // Add the next button for all but the last step.
- if ($form_stage != 'save') {
- $form['next_step'] = array(
- '#type' => 'submit',
- '#value' => 'Next Step',
- '#weight' => 100
- );
- }
-
- // Create a select box containing projects available - these are projects
- // that have associated column header set and must have at least 1 essential column header.
- // The projects are filtered to show only projects assigned to user.
- if ($form_stage != 'save') {
- $my_project = rawpheno_function_user_project($GLOBALS['user']->uid);
-
- if (count($my_project) > 0) {
- // When there is more than 1 project assigned to user, tell user to select a project
- // otherwise default to the only project available.
- if (count($my_project) > 1) {
- $my_project = array(0 => 'Please select an experiment') + $my_project;
- }
-
- // Default Project in project selector field.
- if (isset($_POST['sel_project']) AND $_POST['sel_project'] > 0) {
- // HTTP method post has the project id.
- $default_value = $_POST['sel_project'];
- }
- elseif (isset($form_state['values']['sel_project'])) {
- // Form state has the project id.
- $default_value = $form_state['values']['sel_project'];
- }
- else {
- // Neither has the project id number default to the first project in the project list.
- $default_value = 0;
- }
-
- // Determine if the project select box should be enabled.
- // Disabled in all stages but stage 01.
- $disabled = ($form_stage == 'check') ? FALSE : TRUE;
-
- $form['sel_project'] = array(
- '#type' => 'select',
- '#options' => $my_project,
- '#default_value' => $default_value,
- '#disabled' => $disabled,
- '#id' => 'rawpheno-select-project-field',
- );
-
- // This block is to ensure select project select box is always default to
- // "please select a project" or to the first project option in the beginning of the upload process.
- // It will also default to this option when user refreshes the page.
- if ($form_stage == 'check') {
- drupal_add_js('jQuery(document).ready(function() {
- jQuery("#rawpheno-select-project-field").val(0);
- })', 'inline');
- }
- }
- else {
- // No project is assigned to user.
- $form['no_project'] = array(
- '#markup' => '
' . t('No experiment is assigned to this account. Please contact the administrator of this website.') . '
',
- );
- }
- }
-
- // Note:
- // When there is no project defined in the module, the error message is handled by the theme.
-
- // Get the directory path of rawpheno module.
- $path = drupal_get_path('module', 'rawpheno') . '/theme/';
-
- // Load corresponding function callback together with JavaScript and CSS.
- switch($form_stage) {
- case 'check':
- // Stage 01 - upload and check spreadsheet.
- $form['#attached']['css'] = array($path . 'css/rawpheno.upload.stage01.css');
- $form['#attached']['js'] = array($path . 'js/rawpheno.upload.stage01.js',
- $path . 'js/rawpheno.upload.script.js');
-
- $form = rawpheno_upload_form_stage_check($form, $form_state);
- break;
-
- case 'review':
- // Stage 02 - describe form (only when there is additional trait).
- $form['#attached']['css'] = array($path . 'css/rawpheno.upload.stage02.css');
- $form['#attached']['js'] = array($path . 'js/rawpheno.upload.script.js');
-
- $form = rawpheno_upload_form_stage_review($form, $form_state);
- break;
-
- case 'save':
- // Stage 03 - save to databse and success page.
- $form['#attached']['css'] = array($path . 'css/rawpheno.upload.stage03.css');
- $form['#attached']['js'] = array($path . 'js/rawpheno.upload.stage03.js',
- $path . 'js/rawpheno.upload.script.js');
-
- $form = rawpheno_upload_form_stage_save($form, $form_state);
- break;
- }
-
- return $form;
-}
-
-
-/**
- * Function callback: Construct form for Stage 01.
- *
- * Stage 01 form allows user to upload data collection spreadsheet and perform basic compliance test.
- */
-function rawpheno_upload_form_stage_check($form, &$form_state) {
- // Create an instance of DragNDrop Upload.
- // SETTINGS:
- // #file_upload_max_size: max file size allowed
- // #upload_location: destination of file
- // #upload_event: manual - show an upload button or auto - uploads after drag drop
- // #upload_validators: allowed file extensions
- // #upload_button_text: label of upload button
- // #droppable_area_text: text in drop area
- // #progress_indicator: none, throbber or bar
- // #progress_message: message to display while processing
- // #allow_replace: allow user to replace file by drag and drop another file
- // #standard_upload: show browse button or not
- // #upload_button_text: submit button text (not required when auto submit is auto)
-
- $form['dnd'] = array(
- '#type' => 'dragndrop_upload',
- '#file_upload_max_size' => '10M',
- '#upload_location' => 'public://',
- '#upload_event' => 'auto',
- // NOTE: Accept the listed file extension and let the spreadsheet reader tell if file is valid to generate an an error message.
- // No silent treatment.
- '#upload_validators' => array(
- 'file_validate_extensions' => array('xlsx xls jpg jpeg gif png txt doc pdf ppt pps odt ods odp csv'),
- ),
- '#droppable_area_text' => t('Drag your Microsoft Excel Spreadsheet file here'),
- '#progress_indicator' => 'throbber',
- '#progress_message' => 'Validating your spreadsheet file. Please wait...',
- '#allow_replace' => 1,
- '#standard_upload' => 1,
- '#upload_button_text' => '',
-
- // We are adding our own element process function so that we can make a successfully
- // uploaded/validated file permanent during the AJAX process rather than waiting for
- // them to click the "Next" button.
- '#process' => array(
- 'file_managed_file_process',
- 'dragndrop_upload_element_element_process',
- 'rawpheno_phenotype_upload_file_element_process',
- ),
- );
-
- return $form;
-}
-
-
-/**
- * Function callback: Construct form for Stage 02.
- *
- * Stage 02 form allows user to describe and save a additional trait/s found in the spreadsheet submitted in Stage 01.
- *
- * Assuming the file uploaded properly, we have access to an excel file and need to
- * ensure that all traits have been described. If there are any that haven't then
- * we need to ask the the user to define them now.
- *
- * NOTE: User has the option to skip this stage by not checking any of the new traits found and clicking next step.
- */
-function rawpheno_upload_form_stage_review(&$form, &$form_state) {
- // Array to hold new headers.
- $new_header = array();
-
- // The project id number the spreadsheet and column headers are specific to.
- $project_id = $form_state['values']['sel_project'];
- $project_name = rawpheno_function_getproject($project_id);
-
- // FIND NEW HEADERS.
- // First step, determine which headers/traits need to be described.
- if (isset($form_state['multistep_values']['fid'])) {
- // Get Drupal file object.
- $file = file_load($form_state['multistep_values']['fid']);
-
- // Ensure that the file exits and project id is selected.
- // The form will unset the project id upon page refresh, this will catch the condition
- // when no project id is selected, user will be stopped and is requested to retry the process.
- if ($file AND !empty($project_id)) {
- $new_header = rawpheno_indicate_new_headers($file, $project_id);
-
- // Calling all modules implementing hook_rawpheno_AGILE_stock_name_alter():
- drupal_alter('rawpheno_ignorecols_newcolumn', $new_header, $project_name);
-
- $form_state['multistep_values']['new_headers'] = $new_header;
- }
- else {
- drupal_set_message(t('Unable to access your file. Please try uploading again.'), 'error');
- }
- }
- else {
- drupal_set_message(t('We have no record of your uploaded file. Please try uploading it again.'), 'error');
- }
-
- // If we were unable to access the file or project is not selected then don't let them proceed.
- if (!isset($file) OR empty($file) OR empty($project_id)) {
- $form['notice'] = array(
- '#type' => 'markup',
- '#markup' => '
'
- . t('Unable to access uploaded file. Please attempt to upload your file again on the previous page. If the problem persists then contact the administrator.',
- array('@upload-page' => url('phenotypes/raw/upload')))
- . '
',
- );
-
- // No submit button as well.
- unset($form['next_step']);
-
- return $form;
- }
-
-
- // NO NEW HEADER.
- // If there are no new headers then they don't have to do anything. The module will display a summary
- // showing the number of parsed column headers in the spreadsheet.
- if (empty($new_header)) {
- $all_headers = rawpheno_all_headers($file);
-
- $markup = '
-
No new traits were detected in the spreadsheet. Please click "Next Step".
',
- );
-
- return $form;
- }
-
-
- // NEW HEADERS FOUND.
- if (function_exists('chado_get_cv')) {
- $cv = chado_get_cv(array('name' => 'phenotype_measurement_types'));
- }
- else {
- $cv = tripal_get_cv(array('name' => 'phenotype_measurement_types'));
- }
-
- $cv_id = $cv->cv_id;
-
- // Where clause that form part of the SQL below.
- $where = array(
- 'yes' => "TRIM(LOWER(name)) = :cvterm LIMIT 1",
- 'no' => "TRIM(LOWER(SPLIT_PART(name, '(', 1))) LIKE :cvterm"
- );
-
- $sel = "SELECT * FROM {cvterm} WHERE
- cvterm_id NOT IN (SELECT cvterm_id FROM pheno_project_cvterm WHERE project_id = :project_id)
- AND cv_id = :cv_id AND %s";
-
- // Otherwise, we need a form!
- // Main fieldset container for form elements.
- $form['xls_review_fldset'] = array(
- '#type' => 'fieldset',
- '#title' => t('Check the traits that you want to describe and save'),
- );
-
- $headers_no_format = array_map('rawpheno_function_delformat', $new_header);
-
- // Array to hold all checked headers.
- $arr_checked_header = array();
-
- foreach($new_header as $i => $k) {
- if (isset($k) AND !empty($k)) {
- // To prevent spills of information to other form set, reset this variable that holds
- // query result object.
- if (isset($cvterm_info)) {
- unset($cvterm_info);
- }
-
- // CHECKBOX to let user select a trait to describe and save. If left unchecked, system will not save it.
- $form['xls_review_fldset']['chk_' . $i] = array(
- '#type' => 'checkbox',
- '#title' => t(ucwords($k)),
- '#ajax' => array(
- 'callback' => 'ajax_rawpheno_upload_form_step2_expand_trait_callback',
- 'wrapper' => 'trait-description-' . $i,
- 'effect' => 'fade',
- 'trait_index' => $i,
- ),
- );
-
- // Container div that holds form elements.
- $form['xls_review_fldset']['fldset_' . $i] = array(
- '#type' => 'markup',
- '#prefix' => '
',
- '#suffix' => '
',
- );
-
- // By default, show the describe form.
- $show_form = 'yes';
-
- // If the checkbox is checked then show the fields user want to described.
- if (isset($form_state['values']['chk_' . $i]) AND ($form_state['values']['chk_' . $i] == TRUE)) {
- // TERM NAME/TRAIT/HEADER
- $form['xls_review_fldset']['fldset_' . $i]['txt_header_' . $i] = array(
- '#type' => 'hidden',
- '#value' => $k,
- );
-
- // Clean up the current header.
- $name = trim(strtolower(preg_replace('!\s+!', ' ', $k)));
- $arr_checked_header[] = $name;
-
- // Test if the header has a unit component and set the variable accordingly.
- $has_unit = (strpbrk($name, '()')) ? 'yes' : 'no';
-
- // Format the name to be used in the following SQL. When the name has a unit component,
- // we just feed the name to the SQL using equal operator, otherwise, we use like operator
- // to find all similar headers and suggest it.
- $cvterm = ($has_unit == 'yes') ? $name : '%' . $name . '%';
-
- // Construct the query statement.
- $sql = sprintf($sel, $where[$has_unit]);
- $args = array(':project_id' => $project_id, ':cv_id' => $cv_id, ':cvterm' => $cvterm);
- $h = chado_query($sql, $args);
-
- if ($h->rowCount() > 0) {
- if ($has_unit == 'yes') {
- // Load information about the header.
- $cvterm_info = $h->fetchObject();
-
- // Tell user that the column header exists already.
- $form['xls_review_fldset']['fldset_' . $i]['notice_' . $i] = array(
- '#markup' => '
The system has detected this column header in the database.
- All form fields are disabled to prevent alteration to the original version.
- To save this header and data associated to it, please keep the checkbox checked.
',
- );
-
- $show_form = 'yes';
- }
- else {
- // Suggest similar header.
- // Ensure that the list of headers to be suggested is not in the list of headers detected,
- // that way we can avoid duplicate headers.
- $header_options = array();
- foreach($h as $m) {
- $this_header = trim(strtolower($m->name));
-
- if (!in_array($this_header, $headers_no_format)) {
- $header_options[$m->cvterm_id] = $m->name;
- }
- }
-
- if (count($header_options) > 0) {
- $form['xls_review_fldset']['fldset_' . $i]['sel_header_' . $i] = array(
- '#type' => 'select',
- '#title' => t('Did you mean?'),
- '#options' => array('-1' => '---', 0 => 'None of these apply') + $header_options,
- '#ajax' => array(
- 'callback' => 'ajax_rawpheno_upload_form_step2_load_header_info',
- 'wrapper' => 'trait-description-' . $i,
- 'effect' => 'fade',
- 'trait_index' => $i,
- ),
- '#element_validate' => array('rawpheno_newheader_didyoumean_validate'),
- '#attributes' => array('class' => array('sel-header')),
- '#description' => t('The system has detected a similar header in the database.
- It is recommended that you select the header from the select box that best describes your data.
- If the header is not listed, please select None of these apply option and use the form below to describe this column header.'),
- );
-
- $show_form = 'no';
- }
- else {
- $show_form = 'yes';
- }
-
- if (isset($form_state['values']['sel_header_' . $i])) {
- if ($form_state['values']['sel_header_' . $i] > 0) {
- $cvterm_id = $form_state['values']['sel_header_' . $i];
-
- if (function_exists('chado_get_cvterm')) {
- $cvterm_info = chado_get_cvterm(array('cvterm_id' => $cvterm_id));
- }
- else {
- $cvterm_info = tripal_get_cvterm(array('cvterm_id' => $cvterm_id));
- }
-
- $show_form = 'yes';
- }
- elseif ($form_state['values']['sel_header_' . $i] == 0) {
- $show_form = 'yes';
- }
- }
- }
- }
-
-
- if ($show_form == 'yes') {
- // TERM DEFINITION
- $form['xls_review_fldset']['fldset_' . $i]['txt_def_' . $i] = array(
- '#type' => 'textarea',
- '#title' => t('Definition'),
- '#required' => TRUE,
- '#description' => t('A human-readable text definition'),
- );
-
- if (isset($cvterm_info) && isset($cvterm_info->definition)) {
- $form['xls_review_fldset']['fldset_' . $i]['txt_def_' . $i]['#value'] = $cvterm_info->definition;
- $form['xls_review_fldset']['fldset_' . $i]['txt_def_' . $i]['#disabled'] = TRUE;
- }
-
-
- // UNIT
- $form['xls_review_fldset']['fldset_' . $i]['txt_unit_' . $i] = array(
- '#type' => 'textfield',
- '#title' => t('Unit'),
- '#required' => TRUE,
- '#maxlength' => 100,
- '#element_validate' => array('rawpheno_newheader_unit_validate'),
- '#description' => t('Unit of measurement used'),
- );
-
- if (isset($cvterm_info)) {
- $unit_val = strpbrk($cvterm_info->name, '()');
- // Remove any parenthesis making its way to the final value.
- $unit_val = str_replace(array('(', ')'), '', $unit_val);
-
- $form['xls_review_fldset']['fldset_' . $i]['txt_unit_' . $i]['#value'] = trim($unit_val);
- $form['xls_review_fldset']['fldset_' . $i]['txt_unit_' . $i]['#disabled'] = TRUE;
- }
- else {
- if ($has_unit == 'yes') {
- $u = strpbrk($name, '()');
- // Remove any parenthesis making its way to the final value.
- $u = str_replace(array('(', ')'), '', $u);
-
- $form['xls_review_fldset']['fldset_' . $i]['txt_unit_' . $i]['#value'] = trim($u);
- $form['xls_review_fldset']['fldset_' . $i]['txt_unit_' . $i]['#disabled'] = FALSE;
- }
- }
-
-
- // DESCRIPTION - describe the trait.
- $form['xls_review_fldset']['fldset_' . $i]['txtarea_describe_' . $i] = array(
- '#type' => 'textarea',
- '#title' => t('Describe the method used'),
- '#required' => TRUE,
- '#description' => t('Describe the method used to collect this data if you used a scale, be specific'),
- );
-
- if (isset($cvterm_info)) {
- $cvterm_describe_unit = rawpheno_function_cvterm_properties($cvterm_info->cvterm_id);
-
- $form['xls_review_fldset']['fldset_' . $i]['txtarea_describe_' . $i]['#value'] = $cvterm_describe_unit;
- $form['xls_review_fldset']['fldset_' . $i]['txtarea_describe_' . $i]['#disabled'] = TRUE;
- }
-
-
- // Note fields are required
- $form['xls_review_fldset']['fldset_' . $i]['required_' . $i] = array(
- '#markup' => '
* means field is required
'
- );
- }
- }
- }
- }
-
- // Hidden field containing all the checked new headers
- if (isset($arr_checked_header) AND count($arr_checked_header) > 0) {
- $form['all_header_checked'] = array(
- '#type' => 'hidden',
- '#value' => implode(',', $arr_checked_header),
- );
- }
-
- // Indicator to user of how many of the new traits found has been described.
- $form['traits_checked'] = array(
- '#type' => 'markup',
- '#markup' => '
You have described 0 trait. Please click "Next Step".
'
- );
-
- return $form;
-}
-
-
-/**
- * Function validate the unit field when new header is detected in the spreadsheet.
- * Validation includes ensuring that user does not use parenthesis ( and ) in the unit.
- */
-function rawpheno_newheader_unit_validate($element, &$form_state) {
- $unit_value = trim($element['#value']);
- $project_id = $form_state['values']['sel_project'];
-
- // strpbrk() Returns a string starting from the character found, or FALSE if it is not found.
- if (strpbrk($unit_value, '()')) {
- form_set_error($element['#name'], 'The value in the unit field contains characters "(" and/or ")". Please remove these characters and try again.');
- }
- else {
- // Test the name plus the unit combination if it is in the project.
- $header_field = str_replace('unit', 'header', $element['#name']);
- $header_value = $form_state['values'][$header_field];
-
- if (!strpbrk($header_value, '()')) {
- $name_value = trim(strtolower($header_value . ' (' . strtolower($unit_value) . ')'));
-
- $sql = "SELECT cvterm_id
- FROM {cvterm} INNER JOIN pheno_project_cvterm USING(cvterm_id)
- WHERE project_id = :project_id AND TRIM(LOWER(name)) = :cvterm LIMIT 1";
-
- $args = array(':project_id' => $project_id, ':cvterm' => $name_value);
- $h = chado_query($sql, $args);
-
- if ($h->rowCount() == 1) {
- form_set_error($element['#name'], 'Cannot save column header and unit. ' . ucfirst($name_value) . ' exists in this project.');
- }
-
- // Test if user is about to save same headers.
- if (isset($form_state['values']['all_header_checked'])) {
- $h = $form_state['values']['all_header_checked'];
- $header_validation = explode(',', $h);
-
- if (in_array($name_value, $header_validation)) {
- form_set_error($element['#name'], 'Cannot save multiple entries of the same column header and unit combination.');
- }
- }
- }
- }
-}
-
-
-/**
- * Function callback: validate Did you mean? select box
- */
-function rawpheno_newheader_didyoumean_validate($element, &$form_state) {
- if ($element['#value'] < 0) {
- form_set_error($element['#name'], 'Please select an option and try again.');
- }
-}
-
-
-/**
- * Function load column header information.
- */
-function ajax_rawpheno_upload_form_step2_load_header_info($form, $form_state) {
- $i = $form_state['triggering_element']['#ajax']['trait_index'];
-
- return $form['xls_review_fldset']['fldset_' . $i];
-}
-
-
-/*
- * Selects the piece of the form we want to use as replacement text and returns it as a form (renderable array).
- *
- * @return renderable array (the trait description elements)
- */
-function ajax_rawpheno_upload_form_step2_expand_trait_callback($form, $form_state) {
- // Unique id of each form set.
- $i = $form_state['triggering_element']['#ajax']['trait_index'];
-
- return $form['xls_review_fldset']['fldset_' . $i];
-}
-
-
-/**
- * Function callback: Construct form for Stage 03.
- *
- * Stage 03 form is the final stage that displays a status message
- * and a navigation button to direct user after a successful file upload.
- */
-function rawpheno_upload_form_stage_save($form, &$form_state) {
- $job_id = NULL;
- global $user;
-
- if (isset($form_state['build_info']['args'][0])) {
- $job_id = $form_state['build_info']['args'][0];
-
- // We only want to run jobs that has phenotypic data in it. Otherwise we tell user
- // job is not valid (in case user will hack the url containing the job id).
- // Retrieve the tripal job and determine the percent complete.
- $job = tripal_get_job($job_id);
-
- // If job is valid.
- if ($job) {
- if ($job->uid == $user->uid) {
- // Job id belongs to the user. Authorized.
- $job_status = trim(strtolower($job->status));
-
- // Check if it is a valid job and not a BLAST or other job type.
- if ($job->callback == 'rawpheno_load_spreadsheet') {
- if ($job_status == 'completed') {
- // Is completed some time ago.
- $form['notice'] = array(
- '#markup' => '
It appears that you are attempting to submit a spreadsheet that has been processed already
It appears that you are attempting to submit a spreadsheet that has been cancelled.
'
- );
- }
- else {
- // Not processed yet - show the progress bar.
- // A valid job - work on it.
- $form['notice'] = array(
- '#type' => 'markup',
- '#markup' =>
- '
Your spreadsheet has been successfully submitted and will not be interupted if you choose to leave this page.
'
- . '
The progress bar below indicates our progress updating ' . strtoupper($_SERVER['SERVER_NAME']) . '. Your data will not be available until the progress bar below completes.
'
- );
-
- // Add Progress JS Library.
- drupal_add_js('misc/progress.js');
-
- // This is the link passed to the JavaScript Progress.js as the parameter to a function
- // that monitors a link. The link is a function callback that generates a JSON object
- // containing the number of rows save in percent. See file: rawpheno.module.
- $form['tripal_job_id'] = array(
- '#type' => 'hidden',
- '#value' => $GLOBALS['base_url'] . '/phenotypes/raw/upload/job_summary/' . $job->job_id,
- '#attributes' => array('id' => 'tripal-job-id'),
- );
-
- // We make a DIV which the progress bar can occupy. You can see this in use
- // in ajax_example_progressbar_callback().
- $form['status'] = array(
- '#type' => 'markup',
- '#markup' => ''
- );
- }
- }
- else {
- // Job not supported by this module.
- $form['notice'] = array(
- '#markup' => '
It appears that you are attempting to request a process that is not supported by this module.
It appears that you are attempting to submit a spreadsheet that is not in your account.
'
- );
- }
- }
- else {
- // Job is not valid or does not exists.
- $form['notice'] = array(
- '#markup' => '
The job request to save spreadsheet file does not exist.
'
- );
- }
- }
-
- return $form;
-}
-
-
-/**
- * Implements hook_file_insert().
- * Save file information when file is saved (backup).
- *
- * @param $file
- * Drupal file opbject.
- */
-function rawpheno_file_insert($file) {
- // Process file only when there is a request to save a file and that request is coming from backup page.
- if (isset($file->source)) {
- if ($file->source == 'bdnd') {
- // User id of the currently logged in user.
- $user_id = $GLOBALS['user']->uid;
- // The project id field.
- $project_id = $_POST['backup_sel_project'];
- // The notes field.
- $notes = trim(strip_tags($_POST['backup_txt_description']));
-
- // Query the record id of the project to user record.
- // The result id will be used to map a backup file to user and to project.
- $sql = "SELECT project_user_id FROM pheno_project_user WHERE project_id = :project_id AND uid = :user_id LIMIT 1";
- $args = array(':project_id' => $project_id, ':user_id' => $user_id);
- $prj_usr_id = db_query($sql, $args)
- ->fetchField();
-
- // Get the validation result performed to the spreadsheet file and store the result along with the file information.
- // The same validation process performed in upload data page is carried out to backup file. However, the result
- // is stored as plain text and passed and failed icons are replaced by words passed and failed, respectively.
- $status = rawpheno_validate_excel_file($file, $project_id, 'backup');
-
- $s = (isset($status['status'])) ? $status['status'] : $status;
-
- // Express the validation result array into human readable non-html content format.
- $validation_result = '';
- // Call the same validator function used in upload data.
- $validators = module_invoke_all('rawpheno_validators');
-
- // For each status result, convert it to text based and add a unique text indicator to be used
- // as key to explode the entire text and create a list using the tag.
- foreach($s as $key => $result) {
- $flag = ($result === TRUE) ? 'passed' : 'failed';
-
- // The item keyword will be used to create a list of validation entries when displaying validaiton result to user.
- $validation_result .= '#item: (' . $flag . ') ' . $validators[$key]['label'] . "\n";
- if ($result !== TRUE) {
- $message = call_user_func($validators[$key]['message callback'], $result);
- if (!empty($message)) {
- $validation_result .= implode("\n", $message);
- }
- }
- }
-
- // Compute the version number of this file.
- $sql = "SELECT MAX(t2.version) + 1 AS version
- FROM {pheno_project_user} AS t1 RIGHT JOIN {pheno_backup_file} AS t2 USING(project_user_id)
- WHERE t1.project_id = :project_id AND t1.uid = :user_id LIMIT 1";
-
- $args = array(':project_id' => $project_id, ':user_id' => $user_id);
- $version = db_query($sql, $args)
- ->fetchField();
-
- // On initial upload the file version is null, in this case
- // version is set to 1. Version is incremented by 1 (+1) in
- // subsequent uploads.
- $version = ($version === null) ? 1 : $version;
-
- // Insert a record of this file.
- // File version, which is a sequential order (integer) is handled by the rdbms as it is set to serial type.
- if (isset($status['check_limit'])) {
- $validation_result .= "#item: (failed) NOTICE : " . $status['check_limit'] . "\n";
- }
-
- db_insert('pheno_backup_file')
- ->fields(array('fid' => $file->fid,
- 'notes' => $notes,
- 'version' => $version,
- 'project_user_id' => $prj_usr_id,
- 'validation_result' => $validation_result))
- ->execute();
-
- // Finally, make the file permanent.
- rawpheno_upload_make_file_permanent($file->fid);
-
- // Make the validation result available to frontend.
- $_SESSION['rawpheno']['backup_file_validation_result'] = $status;
- }
- }
-}
-
-
-/**
- * Upload validators callback:
- * Basic compliance test to spreadsheet submitted.
- *
- * @param $file
- * Drupal file opbject.
- */
-function rawpheno_file_validate($file) {
- // 10 mins (60 * 10).
- $max_time = 600;
-
- if ($file->source == 'dnd') {
- // Set processing time to 10 mins.
- ini_set('max_execution_time', $max_time);
-
- // Upload data page.
-
- // Project id number the spreadsheet and column headers are specific to.
- $project_id = (int)$_POST['sel_project'];
-
- // Validate the file.
- // The following function will return an array specifying which of the validation
- // steps passed and providing infomration for those that failed.
- $status = rawpheno_validate_excel_file($file, $project_id, 'upload');
-
- // We want to show the user which steps passed/failed even if all of them passed,
- // so lets do that now. We use drupal_set_message() because returning from this function
- // creates an error message and halts file upload, whereas, using drupal_set_message()
- // allows us to print to the screen regardless of failure/success.
- drupal_set_message(theme('rawpheno_upload_validation_report', array('status' => $status)), 'rawpheno-validate-progress');
-
- // Now we want to determine if validation passed or failed as a whole.
- // To do that we have to look at each step and only if all steps passed
- // did the file pass validation and can be uploaded.
- $all_passed = TRUE;
- foreach ($status as $test_result) {
- if ($test_result !== TRUE) {
- $all_passed = FALSE;
- break; break;
- }
- }
-
- // hook_file_validate() expects an array of error messages if validation failed and
- // and empty array if there are no errors. We don't want this system to print the errors
- // for us since we are using our more friendly theme (see drupal_set_message() above).
- // The work-around is to pass FALSE if validation failed.
- if ($all_passed) {
- drupal_set_message('Your file uploaded successfully. Please click "Next" to continue.');
- return array();
- }
- else {
- return FALSE;
- }
- }
- elseif ($file->source == 'bdnd') {
- // Set processing time to 10 mins.
- ini_set('max_execution_time', $max_time);
-
- // Backup file page.
-
- // Array to hold the validation result.
- $status = array();
-
- // Project id number the spreadsheet and column headers are specific to.
- $project_id = (int)$_POST['backup_sel_project'];
-
- // Perform basic compliance test:
- // - A project is selected.
- // - File is Microsoft Excel Spreadhseet file.
- // - Measurement tab exists.
- // - Essential column headers defined in the project are present.
- $flag_index = array('project_selected', 'is_excel', 'tab_exists', 'column_exists');
-
- // Validate file.
- $flags = rawpheno_validate_excel_file($file, $project_id, 'backup');
-
- $s = (isset($flags['status'])) ? $flags['status'] : $flags;
-
- // If DND backup, missing measurements, skip all validator but
- // save/backup the file anyway.
- if ($s['tab_exists'] === FALSE) {
- return array();
- }
-
- // Read only the status from test listed above.
- foreach($s as $i => $v) {
- if (in_array($i, $flag_index) AND ($v === FALSE || $v === 'todo')) {
- $status[$i] = $v;
- }
- }
-
- // When any of the mentioned test failed, show them to user.
- if (count($status) > 0) {
- if (isset($flags['check_limit'])) {
- drupal_set_message($flags['check_limit'], 'error');
- }
-
- drupal_set_message(theme('rawpheno_upload_validation_report', array('status' => $status)), 'rawpheno-validate-progress');
- return FALSE;
- }
-
- // Else, proceed to hook_file_insert().
- }
- else {
- // File source is one that is not of interest to us.
- // Do not return anything or it will trigger validation errors for other modules.
- }
-}
-
-
-/**
- * Make the phenotype excel file permanent on successful upload.
- *
- * This is an additional process handler used by the form API to generate the form array
- * for a given element. Usually it is used to make a custom form element or enhance a
- * standard form element.
- *
- * We are using it to capture the just uploaded file within the AJAX call by checking
- * when the element is rendered if it has a fid (ie: has been saved).
- */
-function rawpheno_phenotype_upload_file_element_process($element, &$form_state, $form) {
- if (isset($element['#value']['fid']) AND !empty($element['#value']['fid'])) {
- $file_id = $element['#value']['fid'];
- rawpheno_upload_make_file_permanent($file_id);
- }
-
- return $element;
-}
-
-
-/**
- * Make the file uploaded permanent and make a record indicating that file is used by the module.
- *
- * @param $file_id
- * File id in Drupal file object.
- */
-function rawpheno_upload_make_file_permanent($file_id) {
- // Get the file object.
- $file = file_load($file_id);
-
- if ($file) {
- // Make the file permanent.
- $file->status = FILE_STATUS_PERMANENT;
- file_save($file);
-
- // Also, point out that we are using it ;-)
- // Note, the file_usage_add() function expects a numerical unique id which we don't have.
- // We have gotten around this by using the uid concatenated with the timestamp using
- // the assumption that a single user cannot upload more than one phenotype file within a second.
- file_usage_add($file, 'rawpheno', 'rawphenotypes-file', $file->uid . $file->timestamp);
- }
-}
-
-
-/**
- * Implements hook_validate().
- */
-function rawpheno_upload_form_master_validate($form, &$form_state) { }
-
-
-/**
- * Implements hook_submit().
- *
- * Master submit to handle form submit.
- */
-function rawpheno_upload_form_master_submit(&$form, &$form_state) {
- // Which button triggers a submit action.
- $btn_submit = $form_state['triggering_element']['#value'];
-
- // Save any additional traits and then submit a job to save the spreadsheet.
- if ($form_state['stage'] == 'review') {
- $job_id = rawpheno_submit_review($form, $form_state);
-
- // Then we need to add the job_id to the path so the system can keep track of it.
- if ($job_id) {
- drupal_goto(current_path() . '/' . $job_id);
- }
- }
-
- // If we just uploaded the file then we want to save the fid for easy access.
- if (isset($form_state['values']['dnd'])) {
- $form_state['multistep_values']['fid'] = $form_state['values']['dnd'];
- }
-
- // If the next step button was pressed then iterate to the next step.
- if ($btn_submit == 'Next Step') {
- // Definitely save the form id.
- if(isset($form_state['multistep_values']['form_build_id'])) {
- $form_state['values']['form_build_id'] = $form_state['multistep_values']['form_build_id'];
- }
-
- // Save the values from the current step.
- $form_state['multistep_values'][$form_state['stage']] = $form_state['values'];
-
- // Iterate to the next step.
- $form_state['new_stage'] = rawpheno_next_page($form, $form_state);
-
- // Ensure the form state is saved and the form is rebuilt.
- $form_state['multistep_values']['form_build_id'] = $form_state['values']['form_build_id'];
- $form_state['stage'] = $form_state['new_stage'];
- $form_state['rebuild'] = TRUE;
- }
-}
-
-
-/**
- * Save spreadsheet to database.
- */
-function rawpheno_submit_review($form, &$form_state) {
- // Project id number the spreadsheet and column headers are specific to.
- $project_id = $form_state['values']['sel_project'];
-
- // Save spreadsheet data in the following order.
- // 1. New column headers.
- // 2. The entire spreadsheet.
-
- // cvterm id of controlled vocabulary.
- if (function_exists('chado_get_cv')) {
- $cvid = chado_get_cv(array('name' => 'phenotype_measurement_units'));
- }
- else {
- $cvid = tripal_get_cv(array('name' => 'phenotype_measurement_units'));
- }
-
- $cv_measurements_unit = $cvid->cv_id;
-
- // 1. Save new headers.
- // Read variable that holds new column headers.
- $new_header = $form_state['multistep_values']['new_headers'];
-
- // Create an array of new hearders with flag/status if user wants to save it.
- // This array will be passed to rawpheno_load_spreadsheet.
- $arr_newheaders = array();
-
- // Determine if there is new header.
- if (count($new_header) > 0) {
- $trait_type = rawpheno_function_trait_types();
-
- // Read each column header.
- foreach($new_header as $i => $header) {
- // For each new header store information provided in the interface.
- // Indicates if user has check this header for saving.
- $header = trim(str_replace(array("\n", "\r", " "), ' ', $header));
- $header = preg_replace('/\s+/', ' ', $header);
-
- $arr_newheaders[$header]['flag'] = ($form_state['values']['chk_' . $i] == 1) ? 1 : 0;
-
- // Determine if the form in review traits has been filled out and checkbox
- // has been checked by user. If it has been checked then save the trait.
- if ($form_state['values']['chk_' . $i] === 1 && !empty($form_state['values']['txt_header_' . $i])) {
- // Before save, we need to tell if the header is present in the database and
- // user just wants to reuse them. Otherwise, add a new header.
- // Reuse header - set to OPTIONAL.
- if ((isset($form_state['values']['sel_header_' . $i]) AND $form_state['values']['sel_header_' . $i] > 0) OR
- (isset($form_state['values']['txt_header_cvterm_id_' . $i]))) {
-
- // User selected from a list of similar headers.
- $cvterm_id = (isset($form_state['values']['sel_header_' . $i]))
- ? $form_state['values']['sel_header_' . $i]
- : $form_state['values']['txt_header_cvterm_id_' . $i];
-
- // Map this header to the project.
- $sql = "SELECT cvterm_id FROM {pheno_project_cvterm} WHERE project_id = :project_id AND cvterm_id = :cvterm_id LIMIT 1";
- $args = array(':project_id' => $project_id, ':cvterm_id' => $cvterm_id);
-
- $h = db_query($sql, $args);
- if ($h->rowCount() <= 0) {
- // Add to project only when it is not in the project.
- // Set the trait type to contributed.
- db_insert('pheno_project_cvterm')
- ->fields(array(
- 'project_id' => $project_id,
- 'cvterm_id' => $cvterm_id,
- 'type' => $trait_type['type2'])
- )
- ->execute();
- }
-
- // When saving this data for this header, use the cvterm_id.
- $arr_newheaders[$header]['alt_header'] = $cvterm_id;
- continue;
- }
-
- // Check if the trait exists in the database, then it is likely
- // that the user is reusing the trait - threfore it is not contributed and just map
- // the cvterm id to a project.
-
- // Add the as contributed - set to CONTRIBUTED.
- // Construct the column header name.
- $name = trim($form_state['values']['txt_header_' . $i]);
- $name = preg_replace('/\s+/', ' ', $name);
-
- $unit = trim($form_state['values']['txt_unit_' . $i]);
- $method = trim($form_state['values']['txtarea_describe_' . $i]);
- $def = trim($form_state['values']['txt_def_' . $i]);
-
- // Format the header.
- if (strpbrk($name, '()')) {
- // Header has a unit part.
- $name = trim(str_replace(array("\n", "\r", " "), ' ', $name));
- }
- else {
- // Construct header plus the unit.
- $name = $name . ' (' . strtolower($unit) . ')';
- }
-
- // Trait properties - use when inserting the cterm and reference to other property.
- $m_cvterm = array(
- 'id' => 'rawpheno_tripal:' . $name,
- 'name' => $name,
- 'definition' => $def,
- 'cv_name' => 'phenotype_measurement_types'
- );
-
- // Search the name in cvterm and decide if trait should be considered optional or contributed.
- $sql = "SELECT t2.cvterm_id
- FROM {cv} AS t1 INNER JOIN {cvterm} AS t2 USING (cv_id)
- WHERE
- trim(lower(t2.name)) = trim(lower(:cvterm_name))
- AND t1.name = :cv_name LIMIT 1";
-
- $args = array(':cvterm_name' => $m_cvterm['name'], ':cv_name' => $m_cvterm['cv_name']);
- $result = chado_query($sql, $args)
- ->fetchObject();
-
- if ($result) {
- // Found use the id.
- $m_cvterm_id = $result->cvterm_id;
- // Trait is optional.
- $type = $trait_type['type2'];
- }
- else {
- // Not found, insert and get the inserted id.
- $m = tripal_insert_cvterm($m_cvterm);
- $m_cvterm_id = $m->cvterm_id;
- // Trait is contributed.
- $type = $trait_type['type5'];
- }
-
- // When saving this data for this header, use the cvterm_id.
- $arr_newheaders[$header]['alt_header'] = $m_cvterm_id;
- db_insert('pheno_project_cvterm')
- ->fields(array('project_id' => $project_id,
- 'cvterm_id' => $m_cvterm_id,
- 'type' => $type))
- ->execute();
-
- // Create a R Friendly version.
- $r_version = rawpheno_function_make_r_compatible($m_cvterm['name']);
-
- if (function_exists('chado_get_cv')) {
- $cv_rfriendly = chado_get_cv(array('name' => 'phenotype_r_compatible_version'));
- }
- else {
- $cv_rfriendly = tripal_get_cv(array('name' => 'phenotype_r_compatible_version'));
- }
-
- $values = array(
- 'cvterm_id' => $m_cvterm_id,
- 'type_id' => $cv_rfriendly->cv_id,
- 'value' => $r_version,
- 'rank' => 0
- );
-
- chado_insert_record('cvtermprop', $values);
-
- // Then save the Unit.
- $u_cvterm = array(
- 'id' => 'rawpheno_tripal:' . strtolower($unit),
- 'name' => strtolower($unit),
- 'definition' => $unit,
- 'cv_name' => 'phenotype_measurement_units'
- );
-
- $u_cvterm_id = chado_select_record('cvterm',array('cvterm_id'),
- array('name' => $u_cvterm['name'],
- 'cv_id' => array('name' => $u_cvterm['cv_name'])));
- if (!$u_cvterm_id) {
- $u_cvterm_id = tripal_insert_cvterm($u_cvterm);
- }
-
- // Grab just the id.
- if (is_array($u_cvterm_id)) {
- $u_cvterm_id = $u_cvterm_id[0]->cvterm_id;
- }
- elseif (is_object($u_cvterm_id)) {
- $u_cvterm_id = $u_cvterm_id->cvterm_id;
- }
-
- // Don't forget the method description.
- $prop = array(
- 'cvterm_id' => $m_cvterm_id,
- 'type_id' => $cv_measurements_unit,
- 'value' => $method,
- 'rank' => 0,
- );
-
- $prop_id = chado_select_record('cvtermprop', array('cvtermprop_id'), $prop);
- if (!$prop_id) {
- $prop = chado_insert_record('cvtermprop', $prop);
- }
-
- // Finally relate the measurement and unit.
- $rel = array(
- 'subject_id' => $u_cvterm_id,
- 'type_id' => $cv_measurements_unit,
- 'object_id' => $m_cvterm_id,
- );
- $rel_id = chado_select_record('cvterm_relationship', array('cvterm_relationship_id'), $rel);
- if (!$rel_id) {
- chado_insert_record('cvterm_relationship', $rel);
- }
- }
- }
- }
-
- // 2. The entire spreadsheet.
- // Get the variable that holds the path to the spreadsheet file in the server.
- $file = file_load($form_state['multistep_values']['fid']);
- $xls_file = drupal_realpath($file->uri);
-
- // Array of required traits excluding Name.
- $plantprop_headers = rawpheno_project_plantproperty_traits($project_id);
-
- // Drupal user object.
- global $user;
-
- if (isset($xls_file) && !empty($xls_file)) {
- $job_id = tripal_add_job(
- "Upload Phenoypic data: " . $xls_file,
- 'rawpheno',
- 'rawpheno_load_spreadsheet',
- array(
- $project_id,
- serialize($arr_newheaders),
- $form_state['multistep_values']['fid'],
- serialize($plantprop_headers)
- ),
- $user->uid
- );
-
- return $job_id;
- }
-}
+ 'markup',
+ '#markup' => t('Standard Procedure ❯'),
+ );
+
+ // If the stage is not set then default to the first stage (i.e. 'check' )
+ if (!isset($form_state['stage'])) {
+ $form_state['stage'] = 'check';
+ }
+
+ // If a job_id was provided in the URL then the user wants information
+ // on a prvious bulk loading job. Thus we should show them the last step.
+ if (isset($form_state['build_info']['args'][0])) {
+ $form_state['stage'] = 'save';
+ }
+
+ // Add the stage tracker/header.
+ $form = rawpheno_get_header($form, $form_state);
+ // Holds the current stage.
+ $form_stage = $form_state['stage'];
+
+ // Stage indicator for theme function.
+ $form['current_stage'] = array(
+ '#type' => 'value',
+ '#value' => $form_stage,
+ );
+
+ // Add the next button for all but the last step.
+ if ($form_stage != 'save') {
+ $form['next_step'] = array(
+ '#type' => 'submit',
+ '#value' => 'Next Step',
+ '#weight' => 100
+ );
+ }
+
+ // Create a select box containing projects available - these are projects
+ // that have associated column header set and must have at least 1 essential column header.
+ // The projects are filtered to show only projects assigned to user.
+ if ($form_stage != 'save') {
+ $my_project = rawpheno_function_user_project($GLOBALS['user']->uid);
+
+ if (count($my_project) > 0) {
+ // When there is more than 1 project assigned to user, tell user to select a project
+ // otherwise default to the only project available.
+ if (count($my_project) > 1) {
+ $my_project = array(0 => 'Please select an experiment') + $my_project;
+ }
+
+ // Default Project in project selector field.
+ if (isset($_POST['sel_project']) AND $_POST['sel_project'] > 0) {
+ // HTTP method post has the project id.
+ $default_value = $_POST['sel_project'];
+ }
+ elseif (isset($form_state['values']['sel_project'])) {
+ // Form state has the project id.
+ $default_value = $form_state['values']['sel_project'];
+ }
+ else {
+ // Neither has the project id number default to the first project in the project list.
+ $default_value = 0;
+ }
+
+ // Determine if the project select box should be enabled.
+ // Disabled in all stages but stage 01.
+ $disabled = ($form_stage == 'check') ? FALSE : TRUE;
+
+ $form['sel_project'] = array(
+ '#type' => 'select',
+ '#options' => $my_project,
+ '#default_value' => $default_value,
+ '#disabled' => $disabled,
+ '#id' => 'rawpheno-select-project-field',
+ );
+
+ // This block is to ensure select project select box is always default to
+ // "please select a project" or to the first project option in the beginning of the upload process.
+ // It will also default to this option when user refreshes the page.
+ if ($form_stage == 'check') {
+ drupal_add_js('jQuery(document).ready(function() {
+ jQuery("#rawpheno-select-project-field").val(0);
+ })', 'inline');
+ }
+ }
+ else {
+ // No project is assigned to user.
+ $form['no_project'] = array(
+ '#markup' => '
' . t('No experiment is assigned to this account. Please contact the administrator of this website.') . '
',
+ );
+ }
+ }
+
+ // Note:
+ // When there is no project defined in the module, the error message is handled by the theme.
+
+ // Get the directory path of rawpheno module.
+ $path = drupal_get_path('module', 'rawpheno') . '/theme/';
+
+ // Load corresponding function callback together with JavaScript and CSS.
+ switch($form_stage) {
+ case 'check':
+ // Stage 01 - upload and check spreadsheet.
+ $form['#attached']['css'] = array($path . 'css/rawpheno.upload.stage01.css');
+ $form['#attached']['js'] = array($path . 'js/rawpheno.upload.stage01.js',
+ $path . 'js/rawpheno.upload.script.js');
+
+ $form = rawpheno_upload_form_stage_check($form, $form_state);
+ break;
+
+ case 'review':
+ // Stage 02 - describe form (only when there is additional trait).
+ $form['#attached']['css'] = array($path . 'css/rawpheno.upload.stage02.css');
+ $form['#attached']['js'] = array($path . 'js/rawpheno.upload.script.js');
+
+ $form = rawpheno_upload_form_stage_review($form, $form_state);
+ break;
+
+ case 'save':
+ // Stage 03 - save to databse and success page.
+ $form['#attached']['css'] = array($path . 'css/rawpheno.upload.stage03.css');
+ $form['#attached']['js'] = array($path . 'js/rawpheno.upload.stage03.js',
+ $path . 'js/rawpheno.upload.script.js');
+
+ $form = rawpheno_upload_form_stage_save($form, $form_state);
+ break;
+ }
+
+ return $form;
+}
+
+
+/**
+ * Function callback: Construct form for Stage 01.
+ *
+ * Stage 01 form allows user to upload data collection spreadsheet and perform basic compliance test.
+ */
+function rawpheno_upload_form_stage_check($form, &$form_state) {
+ // Create an instance of DragNDrop Upload.
+ // SETTINGS:
+ // #file_upload_max_size: max file size allowed
+ // #upload_location: destination of file
+ // #upload_event: manual - show an upload button or auto - uploads after drag drop
+ // #upload_validators: allowed file extensions
+ // #upload_button_text: label of upload button
+ // #droppable_area_text: text in drop area
+ // #progress_indicator: none, throbber or bar
+ // #progress_message: message to display while processing
+ // #allow_replace: allow user to replace file by drag and drop another file
+ // #standard_upload: show browse button or not
+ // #upload_button_text: submit button text (not required when auto submit is auto)
+
+ $form['dnd'] = array(
+ '#type' => 'dragndrop_upload',
+ '#file_upload_max_size' => '10M',
+ '#upload_location' => 'public://',
+ '#upload_event' => 'auto',
+ // NOTE: Accept the listed file extension and let the spreadsheet reader tell if file is valid to generate an an error message.
+ // No silent treatment.
+ '#upload_validators' => array(
+ 'file_validate_extensions' => array('xlsx xls jpg jpeg gif png txt doc pdf ppt pps odt ods odp csv'),
+ ),
+ '#droppable_area_text' => t('Drag your Microsoft Excel Spreadsheet file here'),
+ '#progress_indicator' => 'throbber',
+ '#progress_message' => 'Validating your spreadsheet file. Please wait...',
+ '#allow_replace' => 1,
+ '#standard_upload' => 1,
+ '#upload_button_text' => '',
+
+ // We are adding our own element process function so that we can make a successfully
+ // uploaded/validated file permanent during the AJAX process rather than waiting for
+ // them to click the "Next" button.
+ '#process' => array(
+ 'file_managed_file_process',
+ 'dragndrop_upload_element_element_process',
+ 'rawpheno_phenotype_upload_file_element_process',
+ ),
+ );
+
+ return $form;
+}
+
+
+/**
+ * Function callback: Construct form for Stage 02.
+ *
+ * Stage 02 form allows user to describe and save a additional trait/s found in the spreadsheet submitted in Stage 01.
+ *
+ * Assuming the file uploaded properly, we have access to an excel file and need to
+ * ensure that all traits have been described. If there are any that haven't then
+ * we need to ask the the user to define them now.
+ *
+ * NOTE: User has the option to skip this stage by not checking any of the new traits found and clicking next step.
+ */
+function rawpheno_upload_form_stage_review(&$form, &$form_state) {
+ // Array to hold new headers.
+ $new_header = array();
+
+ // The project id number the spreadsheet and column headers are specific to.
+ $project_id = $form_state['values']['sel_project'];
+ $project_name = rawpheno_function_getproject($project_id);
+
+ // FIND NEW HEADERS.
+ // First step, determine which headers/traits need to be described.
+ if (isset($form_state['multistep_values']['fid'])) {
+ // Get Drupal file object.
+ $file = file_load($form_state['multistep_values']['fid']);
+
+ // Ensure that the file exits and project id is selected.
+ // The form will unset the project id upon page refresh, this will catch the condition
+ // when no project id is selected, user will be stopped and is requested to retry the process.
+ if ($file AND !empty($project_id)) {
+ $new_header = rawpheno_indicate_new_headers($file, $project_id);
+
+ // Calling all modules implementing hook_rawpheno_AGILE_stock_name_alter():
+ drupal_alter('rawpheno_ignorecols_newcolumn', $new_header, $project_name);
+
+ $form_state['multistep_values']['new_headers'] = $new_header;
+ }
+ else {
+ drupal_set_message(t('Unable to access your file. Please try uploading again.'), 'error');
+ }
+ }
+ else {
+ drupal_set_message(t('We have no record of your uploaded file. Please try uploading it again.'), 'error');
+ }
+
+ // If we were unable to access the file or project is not selected then don't let them proceed.
+ if (!isset($file) OR empty($file) OR empty($project_id)) {
+ $form['notice'] = array(
+ '#type' => 'markup',
+ '#markup' => '
'
+ . t('Unable to access uploaded file. Please attempt to upload your file again on the previous page. If the problem persists then contact the administrator.',
+ array('@upload-page' => url('phenotypes/raw/upload')))
+ . '
',
+ );
+
+ // No submit button as well.
+ unset($form['next_step']);
+
+ return $form;
+ }
+
+
+ // NO NEW HEADER.
+ // If there are no new headers then they don't have to do anything. The module will display a summary
+ // showing the number of parsed column headers in the spreadsheet.
+ if (empty($new_header)) {
+ $all_headers = rawpheno_all_headers($file);
+
+ $markup = '
+
No new traits were detected in the spreadsheet. Please click "Next Step".
',
+ );
+
+ return $form;
+ }
+
+
+ // NEW HEADERS FOUND.
+ if (function_exists('chado_get_cv')) {
+ $cv = chado_get_cv(array('name' => 'phenotype_measurement_types'));
+ }
+ else {
+ $cv = tripal_get_cv(array('name' => 'phenotype_measurement_types'));
+ }
+
+ $cv_id = $cv->cv_id;
+
+ // Where clause that form part of the SQL below.
+ $where = array(
+ 'yes' => "TRIM(LOWER(name)) = :cvterm LIMIT 1",
+ 'no' => "TRIM(LOWER(SPLIT_PART(name, '(', 1))) LIKE :cvterm"
+ );
+
+ $sel = "SELECT * FROM {cvterm} WHERE
+ cvterm_id NOT IN (SELECT cvterm_id FROM pheno_project_cvterm WHERE project_id = :project_id)
+ AND cv_id = :cv_id AND %s";
+
+ // Otherwise, we need a form!
+ // Main fieldset container for form elements.
+ $form['xls_review_fldset'] = array(
+ '#type' => 'fieldset',
+ '#title' => t('Check the traits that you want to describe and save'),
+ );
+
+ $headers_no_format = array_map('rawpheno_function_delformat', $new_header);
+
+ // Array to hold all checked headers.
+ $arr_checked_header = array();
+
+ foreach($new_header as $i => $k) {
+ if (isset($k) AND !empty($k)) {
+ // To prevent spills of information to other form set, reset this variable that holds
+ // query result object.
+ if (isset($cvterm_info)) {
+ unset($cvterm_info);
+ }
+
+ // CHECKBOX to let user select a trait to describe and save. If left unchecked, system will not save it.
+ $form['xls_review_fldset']['chk_' . $i] = array(
+ '#type' => 'checkbox',
+ '#title' => t(ucwords($k)),
+ '#ajax' => array(
+ 'callback' => 'ajax_rawpheno_upload_form_step2_expand_trait_callback',
+ 'wrapper' => 'trait-description-' . $i,
+ 'effect' => 'fade',
+ 'trait_index' => $i,
+ ),
+ );
+
+ // Container div that holds form elements.
+ $form['xls_review_fldset']['fldset_' . $i] = array(
+ '#type' => 'markup',
+ '#prefix' => '
',
+ '#suffix' => '
',
+ );
+
+ // By default, show the describe form.
+ $show_form = 'yes';
+
+ // If the checkbox is checked then show the fields user want to described.
+ if (isset($form_state['values']['chk_' . $i]) AND ($form_state['values']['chk_' . $i] == TRUE)) {
+ // TERM NAME/TRAIT/HEADER
+ $form['xls_review_fldset']['fldset_' . $i]['txt_header_' . $i] = array(
+ '#type' => 'hidden',
+ '#value' => $k,
+ );
+
+ // Clean up the current header.
+ $name = trim(strtolower(preg_replace('!\s+!', ' ', $k)));
+ $arr_checked_header[] = $name;
+
+ // Test if the header has a unit component and set the variable accordingly.
+ $has_unit = (strpbrk($name, '()')) ? 'yes' : 'no';
+
+ // Format the name to be used in the following SQL. When the name has a unit component,
+ // we just feed the name to the SQL using equal operator, otherwise, we use like operator
+ // to find all similar headers and suggest it.
+ $cvterm = ($has_unit == 'yes') ? $name : '%' . $name . '%';
+
+ // Construct the query statement.
+ $sql = sprintf($sel, $where[$has_unit]);
+ $args = array(':project_id' => $project_id, ':cv_id' => $cv_id, ':cvterm' => $cvterm);
+ $h = chado_query($sql, $args);
+
+ if ($h->rowCount() > 0) {
+ if ($has_unit == 'yes') {
+ // Load information about the header.
+ $cvterm_info = $h->fetchObject();
+
+ // Tell user that the column header exists already.
+ $form['xls_review_fldset']['fldset_' . $i]['notice_' . $i] = array(
+ '#markup' => '
The system has detected this column header in the database.
+ All form fields are disabled to prevent alteration to the original version.
+ To save this header and data associated to it, please keep the checkbox checked.
',
+ );
+
+ $show_form = 'yes';
+ }
+ else {
+ // Suggest similar header.
+ // Ensure that the list of headers to be suggested is not in the list of headers detected,
+ // that way we can avoid duplicate headers.
+ $header_options = array();
+ foreach($h as $m) {
+ $this_header = trim(strtolower($m->name));
+
+ if (!in_array($this_header, $headers_no_format)) {
+ $header_options[$m->cvterm_id] = $m->name;
+ }
+ }
+
+ if (count($header_options) > 0) {
+ $form['xls_review_fldset']['fldset_' . $i]['sel_header_' . $i] = array(
+ '#type' => 'select',
+ '#title' => t('Did you mean?'),
+ '#options' => array('-1' => '---', 0 => 'None of these apply') + $header_options,
+ '#ajax' => array(
+ 'callback' => 'ajax_rawpheno_upload_form_step2_load_header_info',
+ 'wrapper' => 'trait-description-' . $i,
+ 'effect' => 'fade',
+ 'trait_index' => $i,
+ ),
+ '#element_validate' => array('rawpheno_newheader_didyoumean_validate'),
+ '#attributes' => array('class' => array('sel-header')),
+ '#description' => t('The system has detected a similar header in the database.
+ It is recommended that you select the header from the select box that best describes your data.
+ If the header is not listed, please select None of these apply option and use the form below to describe this column header.'),
+ );
+
+ $show_form = 'no';
+ }
+ else {
+ $show_form = 'yes';
+ }
+
+ if (isset($form_state['values']['sel_header_' . $i])) {
+ if ($form_state['values']['sel_header_' . $i] > 0) {
+ $cvterm_id = $form_state['values']['sel_header_' . $i];
+
+ if (function_exists('chado_get_cvterm')) {
+ $cvterm_info = chado_get_cvterm(array('cvterm_id' => $cvterm_id));
+ }
+ else {
+ $cvterm_info = tripal_get_cvterm(array('cvterm_id' => $cvterm_id));
+ }
+
+ $show_form = 'yes';
+ }
+ elseif ($form_state['values']['sel_header_' . $i] == 0) {
+ $show_form = 'yes';
+ }
+ }
+ }
+ }
+
+
+ if ($show_form == 'yes') {
+ // TERM DEFINITION
+ $form['xls_review_fldset']['fldset_' . $i]['txt_def_' . $i] = array(
+ '#type' => 'textarea',
+ '#title' => t('Definition'),
+ '#required' => TRUE,
+ '#description' => t('A human-readable text definition'),
+ );
+
+ if (isset($cvterm_info) && isset($cvterm_info->definition)) {
+ $form['xls_review_fldset']['fldset_' . $i]['txt_def_' . $i]['#value'] = $cvterm_info->definition;
+ $form['xls_review_fldset']['fldset_' . $i]['txt_def_' . $i]['#disabled'] = TRUE;
+ }
+
+
+ // UNIT
+ $form['xls_review_fldset']['fldset_' . $i]['txt_unit_' . $i] = array(
+ '#type' => 'textfield',
+ '#title' => t('Unit'),
+ '#required' => TRUE,
+ '#maxlength' => 100,
+ '#element_validate' => array('rawpheno_newheader_unit_validate'),
+ '#description' => t('Unit of measurement used'),
+ );
+
+ if (isset($cvterm_info)) {
+ $unit_val = strpbrk($cvterm_info->name, '()');
+ // Remove any parenthesis making its way to the final value.
+ $unit_val = str_replace(array('(', ')'), '', $unit_val);
+
+ $form['xls_review_fldset']['fldset_' . $i]['txt_unit_' . $i]['#value'] = trim($unit_val);
+ $form['xls_review_fldset']['fldset_' . $i]['txt_unit_' . $i]['#disabled'] = TRUE;
+ }
+ else {
+ if ($has_unit == 'yes') {
+ $u = strpbrk($name, '()');
+ // Remove any parenthesis making its way to the final value.
+ $u = str_replace(array('(', ')'), '', $u);
+
+ $form['xls_review_fldset']['fldset_' . $i]['txt_unit_' . $i]['#value'] = trim($u);
+ $form['xls_review_fldset']['fldset_' . $i]['txt_unit_' . $i]['#disabled'] = FALSE;
+ }
+ }
+
+
+ // DESCRIPTION - describe the trait.
+ $form['xls_review_fldset']['fldset_' . $i]['txtarea_describe_' . $i] = array(
+ '#type' => 'textarea',
+ '#title' => t('Describe the method used'),
+ '#required' => TRUE,
+ '#description' => t('Describe the method used to collect this data if you used a scale, be specific'),
+ );
+
+ if (isset($cvterm_info)) {
+ $cvterm_describe_unit = rawpheno_function_cvterm_properties($cvterm_info->cvterm_id);
+
+ $form['xls_review_fldset']['fldset_' . $i]['txtarea_describe_' . $i]['#value'] = $cvterm_describe_unit;
+ $form['xls_review_fldset']['fldset_' . $i]['txtarea_describe_' . $i]['#disabled'] = TRUE;
+ }
+
+
+ // Note fields are required
+ $form['xls_review_fldset']['fldset_' . $i]['required_' . $i] = array(
+ '#markup' => '
* means field is required
'
+ );
+ }
+ }
+ }
+ }
+
+ // Hidden field containing all the checked new headers
+ if (isset($arr_checked_header) AND count($arr_checked_header) > 0) {
+ $form['all_header_checked'] = array(
+ '#type' => 'hidden',
+ '#value' => implode(',', $arr_checked_header),
+ );
+ }
+
+ // Indicator to user of how many of the new traits found has been described.
+ $form['traits_checked'] = array(
+ '#type' => 'markup',
+ '#markup' => '
You have described 0 trait. Please click "Next Step".
'
+ );
+
+ return $form;
+}
+
+
+/**
+ * Function validate the unit field when new header is detected in the spreadsheet.
+ * Validation includes ensuring that user does not use parenthesis ( and ) in the unit.
+ */
+function rawpheno_newheader_unit_validate($element, &$form_state) {
+ $unit_value = trim($element['#value']);
+ $project_id = $form_state['values']['sel_project'];
+
+ // strpbrk() Returns a string starting from the character found, or FALSE if it is not found.
+ if (strpbrk($unit_value, '()')) {
+ form_set_error($element['#name'], 'The value in the unit field contains characters "(" and/or ")". Please remove these characters and try again.');
+ }
+ else {
+ // Test the name plus the unit combination if it is in the project.
+ $header_field = str_replace('unit', 'header', $element['#name']);
+ $header_value = $form_state['values'][$header_field];
+
+ if (!strpbrk($header_value, '()')) {
+ $name_value = trim(strtolower($header_value . ' (' . strtolower($unit_value) . ')'));
+
+ $sql = "SELECT cvterm_id
+ FROM {cvterm} INNER JOIN pheno_project_cvterm USING(cvterm_id)
+ WHERE project_id = :project_id AND TRIM(LOWER(name)) = :cvterm LIMIT 1";
+
+ $args = array(':project_id' => $project_id, ':cvterm' => $name_value);
+ $h = chado_query($sql, $args);
+
+ if ($h->rowCount() == 1) {
+ form_set_error($element['#name'], 'Cannot save column header and unit. ' . ucfirst($name_value) . ' exists in this project.');
+ }
+
+ // Test if user is about to save same headers.
+ if (isset($form_state['values']['all_header_checked'])) {
+ $h = $form_state['values']['all_header_checked'];
+ $header_validation = explode(',', $h);
+
+ if (in_array($name_value, $header_validation)) {
+ form_set_error($element['#name'], 'Cannot save multiple entries of the same column header and unit combination.');
+ }
+ }
+ }
+ }
+}
+
+
+/**
+ * Function callback: validate Did you mean? select box
+ */
+function rawpheno_newheader_didyoumean_validate($element, &$form_state) {
+ if ($element['#value'] < 0) {
+ form_set_error($element['#name'], 'Please select an option and try again.');
+ }
+}
+
+
+/**
+ * Function load column header information.
+ */
+function ajax_rawpheno_upload_form_step2_load_header_info($form, $form_state) {
+ $i = $form_state['triggering_element']['#ajax']['trait_index'];
+
+ return $form['xls_review_fldset']['fldset_' . $i];
+}
+
+
+/*
+ * Selects the piece of the form we want to use as replacement text and returns it as a form (renderable array).
+ *
+ * @return renderable array (the trait description elements)
+ */
+function ajax_rawpheno_upload_form_step2_expand_trait_callback($form, $form_state) {
+ // Unique id of each form set.
+ $i = $form_state['triggering_element']['#ajax']['trait_index'];
+
+ return $form['xls_review_fldset']['fldset_' . $i];
+}
+
+
+/**
+ * Function callback: Construct form for Stage 03.
+ *
+ * Stage 03 form is the final stage that displays a status message
+ * and a navigation button to direct user after a successful file upload.
+ */
+function rawpheno_upload_form_stage_save($form, &$form_state) {
+ $job_id = NULL;
+ global $user;
+
+ if (isset($form_state['build_info']['args'][0])) {
+ $job_id = $form_state['build_info']['args'][0];
+
+ // We only want to run jobs that has phenotypic data in it. Otherwise we tell user
+ // job is not valid (in case user will hack the url containing the job id).
+ // Retrieve the tripal job and determine the percent complete.
+ $job = tripal_get_job($job_id);
+
+ // If job is valid.
+ if ($job) {
+ if ($job->uid == $user->uid) {
+ // Job id belongs to the user. Authorized.
+ $job_status = trim(strtolower($job->status));
+
+ // Check if it is a valid job and not a BLAST or other job type.
+ if ($job->callback == 'rawpheno_load_spreadsheet') {
+ if ($job_status == 'completed') {
+ // Is completed some time ago.
+ $form['notice'] = array(
+ '#markup' => '
It appears that you are attempting to submit a spreadsheet that has been processed already
It appears that you are attempting to submit a spreadsheet that has been cancelled.
'
+ );
+ }
+ else {
+ // Not processed yet - show the progress bar.
+ // A valid job - work on it.
+ $form['notice'] = array(
+ '#type' => 'markup',
+ '#markup' =>
+ '
Your spreadsheet has been successfully submitted and will not be interupted if you choose to leave this page.
'
+ . '
The progress bar below indicates our progress updating ' . strtoupper($_SERVER['SERVER_NAME']) . '. Your data will not be available until the progress bar below completes.
'
+ );
+
+ // Add Progress JS Library.
+ drupal_add_js('misc/progress.js');
+
+ // This is the link passed to the JavaScript Progress.js as the parameter to a function
+ // that monitors a link. The link is a function callback that generates a JSON object
+ // containing the number of rows save in percent. See file: rawpheno.module.
+ $form['tripal_job_id'] = array(
+ '#type' => 'hidden',
+ '#value' => $GLOBALS['base_url'] . '/phenotypes/raw/upload/job_summary/' . $job->job_id,
+ '#attributes' => array('id' => 'tripal-job-id'),
+ );
+
+ // We make a DIV which the progress bar can occupy. You can see this in use
+ // in ajax_example_progressbar_callback().
+ $form['status'] = array(
+ '#type' => 'markup',
+ '#markup' => ''
+ );
+ }
+ }
+ else {
+ // Job not supported by this module.
+ $form['notice'] = array(
+ '#markup' => '
It appears that you are attempting to request a process that is not supported by this module.
It appears that you are attempting to submit a spreadsheet that is not in your account.
'
+ );
+ }
+ }
+ else {
+ // Job is not valid or does not exists.
+ $form['notice'] = array(
+ '#markup' => '
The job request to save spreadsheet file does not exist.
'
+ );
+ }
+ }
+
+ return $form;
+}
+
+
+/**
+ * Implements hook_file_insert().
+ * Save file information when file is saved (backup).
+ *
+ * @param $file
+ * Drupal file opbject.
+ */
+function rawpheno_file_insert($file) {
+ // Process file only when there is a request to save a file and that request is coming from backup page.
+ if (isset($file->source)) {
+ if ($file->source == 'bdnd') {
+ // User id of the currently logged in user.
+ $user_id = $GLOBALS['user']->uid;
+ // The project id field.
+ $project_id = $_POST['backup_sel_project'];
+ // The notes field.
+ $notes = trim(strip_tags($_POST['backup_txt_description']));
+
+ // Query the record id of the project to user record.
+ // The result id will be used to map a backup file to user and to project.
+ $sql = "SELECT project_user_id FROM pheno_project_user WHERE project_id = :project_id AND uid = :user_id LIMIT 1";
+ $args = array(':project_id' => $project_id, ':user_id' => $user_id);
+ $prj_usr_id = db_query($sql, $args)
+ ->fetchField();
+
+ // Get the validation result performed to the spreadsheet file and store the result along with the file information.
+ // The same validation process performed in upload data page is carried out to backup file. However, the result
+ // is stored as plain text and passed and failed icons are replaced by words passed and failed, respectively.
+ $status = rawpheno_validate_excel_file($file, $project_id, 'backup');
+
+ $s = (isset($status['status'])) ? $status['status'] : $status;
+
+ // Express the validation result array into human readable non-html content format.
+ $validation_result = '';
+ // Call the same validator function used in upload data.
+ $validators = module_invoke_all('rawpheno_validators');
+
+ // For each status result, convert it to text based and add a unique text indicator to be used
+ // as key to explode the entire text and create a list using the tag.
+ foreach($s as $key => $result) {
+ $flag = ($result === TRUE) ? 'passed' : 'failed';
+
+ // The item keyword will be used to create a list of validation entries when displaying validaiton result to user.
+ $validation_result .= '#item: (' . $flag . ') ' . $validators[$key]['label'] . "\n";
+ if ($result !== TRUE) {
+ $message = call_user_func($validators[$key]['message callback'], $result);
+ if (!empty($message)) {
+ $validation_result .= implode("\n", $message);
+ }
+ }
+ }
+
+ // Compute the version number of this file.
+ $sql = "SELECT MAX(t2.version) + 1 AS version
+ FROM {pheno_project_user} AS t1 RIGHT JOIN {pheno_backup_file} AS t2 USING(project_user_id)
+ WHERE t1.project_id = :project_id AND t1.uid = :user_id LIMIT 1";
+
+ $args = array(':project_id' => $project_id, ':user_id' => $user_id);
+ $version = db_query($sql, $args)
+ ->fetchField();
+
+ // On initial upload the file version is null, in this case
+ // version is set to 1. Version is incremented by 1 (+1) in
+ // subsequent uploads.
+ $version = ($version === null) ? 1 : $version;
+
+ // Insert a record of this file.
+ // File version, which is a sequential order (integer) is handled by the rdbms as it is set to serial type.
+ if (isset($status['check_limit'])) {
+ $validation_result .= "#item: (failed) NOTICE : " . $status['check_limit'] . "\n";
+ }
+
+ db_insert('pheno_backup_file')
+ ->fields(array('fid' => $file->fid,
+ 'notes' => $notes,
+ 'version' => $version,
+ 'project_user_id' => $prj_usr_id,
+ 'validation_result' => $validation_result))
+ ->execute();
+
+ // Finally, make the file permanent.
+ rawpheno_upload_make_file_permanent($file->fid);
+
+ // Make the validation result available to frontend.
+ $_SESSION['rawpheno']['backup_file_validation_result'] = $status;
+ }
+ }
+}
+
+
+/**
+ * Upload validators callback:
+ * Basic compliance test to spreadsheet submitted.
+ *
+ * @param $file
+ * Drupal file opbject.
+ */
+function rawpheno_file_validate($file) {
+ // 10 mins (60 * 10).
+ $max_time = 600;
+
+ if ($file->source == 'dnd') {
+ // Set processing time to 10 mins.
+ ini_set('max_execution_time', $max_time);
+
+ // Upload data page.
+
+ // Project id number the spreadsheet and column headers are specific to.
+ $project_id = (int)$_POST['sel_project'];
+
+ // Validate the file.
+ // The following function will return an array specifying which of the validation
+ // steps passed and providing infomration for those that failed.
+ $status = rawpheno_validate_excel_file($file, $project_id, 'upload');
+
+ // We want to show the user which steps passed/failed even if all of them passed,
+ // so lets do that now. We use drupal_set_message() because returning from this function
+ // creates an error message and halts file upload, whereas, using drupal_set_message()
+ // allows us to print to the screen regardless of failure/success.
+ drupal_set_message(theme('rawpheno_upload_validation_report', array('status' => $status)), 'rawpheno-validate-progress');
+
+ // Now we want to determine if validation passed or failed as a whole.
+ // To do that we have to look at each step and only if all steps passed
+ // did the file pass validation and can be uploaded.
+ $all_passed = TRUE;
+ foreach ($status as $test_result) {
+ if ($test_result !== TRUE) {
+ $all_passed = FALSE;
+ break; break;
+ }
+ }
+
+ // hook_file_validate() expects an array of error messages if validation failed and
+ // and empty array if there are no errors. We don't want this system to print the errors
+ // for us since we are using our more friendly theme (see drupal_set_message() above).
+ // The work-around is to pass FALSE if validation failed.
+ if ($all_passed) {
+ drupal_set_message('Your file uploaded successfully. Please click "Next" to continue.');
+ return array();
+ }
+ else {
+ return FALSE;
+ }
+ }
+ elseif ($file->source == 'bdnd') {
+ // Set processing time to 10 mins.
+ ini_set('max_execution_time', $max_time);
+
+ // Backup file page.
+
+ // Array to hold the validation result.
+ $status = array();
+
+ // Project id number the spreadsheet and column headers are specific to.
+ $project_id = (int)$_POST['backup_sel_project'];
+
+ // Perform basic compliance test:
+ // - A project is selected.
+ // - File is Microsoft Excel Spreadhseet file.
+ // - Measurement tab exists.
+ // - Essential column headers defined in the project are present.
+ $flag_index = array('project_selected', 'is_excel', 'tab_exists', 'column_exists');
+
+ // Validate file.
+ $flags = rawpheno_validate_excel_file($file, $project_id, 'backup');
+
+ $s = (isset($flags['status'])) ? $flags['status'] : $flags;
+
+ // If DND backup, missing measurements, skip all validator but
+ // save/backup the file anyway.
+ if ($s['tab_exists'] === FALSE) {
+ return array();
+ }
+
+ // Read only the status from test listed above.
+ foreach($s as $i => $v) {
+ if (in_array($i, $flag_index) AND ($v === FALSE || $v === 'todo')) {
+ $status[$i] = $v;
+ }
+ }
+
+ // When any of the mentioned test failed, show them to user.
+ if (count($status) > 0) {
+ if (isset($flags['check_limit'])) {
+ drupal_set_message($flags['check_limit'], 'error');
+ }
+
+ drupal_set_message(theme('rawpheno_upload_validation_report', array('status' => $status)), 'rawpheno-validate-progress');
+ return FALSE;
+ }
+
+ // Else, proceed to hook_file_insert().
+ }
+ else {
+ // File source is one that is not of interest to us.
+ // Do not return anything or it will trigger validation errors for other modules.
+ }
+}
+
+
+/**
+ * Make the phenotype excel file permanent on successful upload.
+ *
+ * This is an additional process handler used by the form API to generate the form array
+ * for a given element. Usually it is used to make a custom form element or enhance a
+ * standard form element.
+ *
+ * We are using it to capture the just uploaded file within the AJAX call by checking
+ * when the element is rendered if it has a fid (ie: has been saved).
+ */
+function rawpheno_phenotype_upload_file_element_process($element, &$form_state, $form) {
+ if (isset($element['#value']['fid']) AND !empty($element['#value']['fid'])) {
+ $file_id = $element['#value']['fid'];
+ rawpheno_upload_make_file_permanent($file_id);
+ }
+
+ return $element;
+}
+
+
+/**
+ * Make the file uploaded permanent and make a record indicating that file is used by the module.
+ *
+ * @param $file_id
+ * File id in Drupal file object.
+ */
+function rawpheno_upload_make_file_permanent($file_id) {
+ // Get the file object.
+ $file = file_load($file_id);
+
+ if ($file) {
+ // Make the file permanent.
+ $file->status = FILE_STATUS_PERMANENT;
+ file_save($file);
+
+ // Also, point out that we are using it ;-)
+ // Note, the file_usage_add() function expects a numerical unique id which we don't have.
+ // We have gotten around this by using the uid concatenated with the timestamp using
+ // the assumption that a single user cannot upload more than one phenotype file within a second.
+ file_usage_add($file, 'rawpheno', 'rawphenotypes-file', $file->uid . $file->timestamp);
+ }
+}
+
+
+/**
+ * Implements hook_validate().
+ */
+function rawpheno_upload_form_master_validate($form, &$form_state) { }
+
+
+/**
+ * Implements hook_submit().
+ *
+ * Master submit to handle form submit.
+ */
+function rawpheno_upload_form_master_submit(&$form, &$form_state) {
+ // Which button triggers a submit action.
+ $btn_submit = $form_state['triggering_element']['#value'];
+
+ // Save any additional traits and then submit a job to save the spreadsheet.
+ if ($form_state['stage'] == 'review') {
+ $job_id = rawpheno_submit_review($form, $form_state);
+
+ // Then we need to add the job_id to the path so the system can keep track of it.
+ if ($job_id) {
+ drupal_goto(current_path() . '/' . $job_id);
+ }
+ }
+
+ // If we just uploaded the file then we want to save the fid for easy access.
+ if (isset($form_state['values']['dnd'])) {
+ $form_state['multistep_values']['fid'] = $form_state['values']['dnd'];
+ }
+
+ // If the next step button was pressed then iterate to the next step.
+ if ($btn_submit == 'Next Step') {
+ // Definitely save the form id.
+ if(isset($form_state['multistep_values']['form_build_id'])) {
+ $form_state['values']['form_build_id'] = $form_state['multistep_values']['form_build_id'];
+ }
+
+ // Save the values from the current step.
+ $form_state['multistep_values'][$form_state['stage']] = $form_state['values'];
+
+ // Iterate to the next step.
+ $form_state['new_stage'] = rawpheno_next_page($form, $form_state);
+
+ // Ensure the form state is saved and the form is rebuilt.
+ $form_state['multistep_values']['form_build_id'] = $form_state['values']['form_build_id'];
+ $form_state['stage'] = $form_state['new_stage'];
+ $form_state['rebuild'] = TRUE;
+ }
+}
+
+
+/**
+ * Save spreadsheet to database.
+ */
+function rawpheno_submit_review($form, &$form_state) {
+ // Project id number the spreadsheet and column headers are specific to.
+ $project_id = $form_state['values']['sel_project'];
+
+ // Save spreadsheet data in the following order.
+ // 1. New column headers.
+ // 2. The entire spreadsheet.
+
+ // cvterm id of controlled vocabulary.
+ if (function_exists('chado_get_cv')) {
+ $cvid = chado_get_cv(array('name' => 'phenotype_measurement_units'));
+ }
+ else {
+ $cvid = tripal_get_cv(array('name' => 'phenotype_measurement_units'));
+ }
+
+ $cv_measurements_unit = $cvid->cv_id;
+
+ // 1. Save new headers.
+ // Read variable that holds new column headers.
+ $new_header = $form_state['multistep_values']['new_headers'];
+
+ // Create an array of new hearders with flag/status if user wants to save it.
+ // This array will be passed to rawpheno_load_spreadsheet.
+ $arr_newheaders = array();
+
+ // Determine if there is new header.
+ if (count($new_header) > 0) {
+ $trait_type = rawpheno_function_trait_types();
+
+ // Read each column header.
+ foreach($new_header as $i => $header) {
+ // For each new header store information provided in the interface.
+ // Indicates if user has check this header for saving.
+ $header = trim(str_replace(array("\n", "\r", " "), ' ', $header));
+ $header = preg_replace('/\s+/', ' ', $header);
+
+ $arr_newheaders[$header]['flag'] = ($form_state['values']['chk_' . $i] == 1) ? 1 : 0;
+
+ // Determine if the form in review traits has been filled out and checkbox
+ // has been checked by user. If it has been checked then save the trait.
+ if ($form_state['values']['chk_' . $i] === 1 && !empty($form_state['values']['txt_header_' . $i])) {
+ // Before save, we need to tell if the header is present in the database and
+ // user just wants to reuse them. Otherwise, add a new header.
+ // Reuse header - set to OPTIONAL.
+ if ((isset($form_state['values']['sel_header_' . $i]) AND $form_state['values']['sel_header_' . $i] > 0) OR
+ (isset($form_state['values']['txt_header_cvterm_id_' . $i]))) {
+
+ // User selected from a list of similar headers.
+ $cvterm_id = (isset($form_state['values']['sel_header_' . $i]))
+ ? $form_state['values']['sel_header_' . $i]
+ : $form_state['values']['txt_header_cvterm_id_' . $i];
+
+ // Map this header to the project.
+ $sql = "SELECT cvterm_id FROM {pheno_project_cvterm} WHERE project_id = :project_id AND cvterm_id = :cvterm_id LIMIT 1";
+ $args = array(':project_id' => $project_id, ':cvterm_id' => $cvterm_id);
+
+ $h = db_query($sql, $args);
+ if ($h->rowCount() <= 0) {
+ // Add to project only when it is not in the project.
+ // Set the trait type to contributed.
+ db_insert('pheno_project_cvterm')
+ ->fields(array(
+ 'project_id' => $project_id,
+ 'cvterm_id' => $cvterm_id,
+ 'type' => $trait_type['type2'])
+ )
+ ->execute();
+ }
+
+ // When saving this data for this header, use the cvterm_id.
+ $arr_newheaders[$header]['alt_header'] = $cvterm_id;
+ continue;
+ }
+
+ // Check if the trait exists in the database, then it is likely
+ // that the user is reusing the trait - threfore it is not contributed and just map
+ // the cvterm id to a project.
+
+ // Add the as contributed - set to CONTRIBUTED.
+ // Construct the column header name.
+ $name = trim($form_state['values']['txt_header_' . $i]);
+ $name = preg_replace('/\s+/', ' ', $name);
+
+ $unit = trim($form_state['values']['txt_unit_' . $i]);
+ $method = trim($form_state['values']['txtarea_describe_' . $i]);
+ $def = trim($form_state['values']['txt_def_' . $i]);
+
+ // Format the header.
+ if (strpbrk($name, '()')) {
+ // Header has a unit part.
+ $name = trim(str_replace(array("\n", "\r", " "), ' ', $name));
+ }
+ else {
+ // Construct header plus the unit.
+ $name = $name . ' (' . strtolower($unit) . ')';
+ }
+
+ // Trait properties - use when inserting the cterm and reference to other property.
+ $m_cvterm = array(
+ 'id' => 'rawpheno_tripal:' . $name,
+ 'name' => $name,
+ 'definition' => $def,
+ 'cv_name' => 'phenotype_measurement_types'
+ );
+
+ // Search the name in cvterm and decide if trait should be considered optional or contributed.
+ $sql = "SELECT t2.cvterm_id
+ FROM {cv} AS t1 INNER JOIN {cvterm} AS t2 USING (cv_id)
+ WHERE
+ trim(lower(t2.name)) = trim(lower(:cvterm_name))
+ AND t1.name = :cv_name LIMIT 1";
+
+ $args = array(':cvterm_name' => $m_cvterm['name'], ':cv_name' => $m_cvterm['cv_name']);
+ $result = chado_query($sql, $args)
+ ->fetchObject();
+
+ if ($result) {
+ // Found use the id.
+ $m_cvterm_id = $result->cvterm_id;
+ // Trait is optional.
+ $type = $trait_type['type2'];
+ }
+ else {
+ // Not found, insert and get the inserted id.
+ $m = tripal_insert_cvterm($m_cvterm);
+ $m_cvterm_id = $m->cvterm_id;
+ // Trait is contributed.
+ $type = $trait_type['type5'];
+ }
+
+ // When saving this data for this header, use the cvterm_id.
+ $arr_newheaders[$header]['alt_header'] = $m_cvterm_id;
+ db_insert('pheno_project_cvterm')
+ ->fields(array('project_id' => $project_id,
+ 'cvterm_id' => $m_cvterm_id,
+ 'type' => $type))
+ ->execute();
+
+ // Create a R Friendly version.
+ $r_version = rawpheno_function_make_r_compatible($m_cvterm['name']);
+
+ if (function_exists('chado_get_cv')) {
+ $cv_rfriendly = chado_get_cv(array('name' => 'phenotype_r_compatible_version'));
+ }
+ else {
+ $cv_rfriendly = tripal_get_cv(array('name' => 'phenotype_r_compatible_version'));
+ }
+
+ $values = array(
+ 'cvterm_id' => $m_cvterm_id,
+ 'type_id' => $cv_rfriendly->cv_id,
+ 'value' => $r_version,
+ 'rank' => 0
+ );
+
+ chado_insert_record('cvtermprop', $values);
+
+ // Then save the Unit.
+ $u_cvterm = array(
+ 'id' => 'rawpheno_tripal:' . strtolower($unit),
+ 'name' => strtolower($unit),
+ 'definition' => $unit,
+ 'cv_name' => 'phenotype_measurement_units'
+ );
+
+ $u_cvterm_id = chado_select_record('cvterm',array('cvterm_id'),
+ array('name' => $u_cvterm['name'],
+ 'cv_id' => array('name' => $u_cvterm['cv_name'])));
+ if (!$u_cvterm_id) {
+ $u_cvterm_id = tripal_insert_cvterm($u_cvterm);
+ }
+
+ // Grab just the id.
+ if (is_array($u_cvterm_id)) {
+ $u_cvterm_id = $u_cvterm_id[0]->cvterm_id;
+ }
+ elseif (is_object($u_cvterm_id)) {
+ $u_cvterm_id = $u_cvterm_id->cvterm_id;
+ }
+
+ // Don't forget the method description.
+ $prop = array(
+ 'cvterm_id' => $m_cvterm_id,
+ 'type_id' => $cv_measurements_unit,
+ 'value' => $method,
+ 'rank' => 0,
+ );
+
+ $prop_id = chado_select_record('cvtermprop', array('cvtermprop_id'), $prop);
+ if (!$prop_id) {
+ $prop = chado_insert_record('cvtermprop', $prop);
+ }
+
+ // Finally relate the measurement and unit.
+ $rel = array(
+ 'subject_id' => $u_cvterm_id,
+ 'type_id' => $cv_measurements_unit,
+ 'object_id' => $m_cvterm_id,
+ );
+ $rel_id = chado_select_record('cvterm_relationship', array('cvterm_relationship_id'), $rel);
+ if (!$rel_id) {
+ chado_insert_record('cvterm_relationship', $rel);
+ }
+ }
+ }
+ }
+
+ // 2. The entire spreadsheet.
+ // Get the variable that holds the path to the spreadsheet file in the server.
+ $file = file_load($form_state['multistep_values']['fid']);
+ $xls_file = drupal_realpath($file->uri);
+
+ // Array of required traits excluding Name.
+ $plantprop_headers = rawpheno_project_plantproperty_traits($project_id);
+
+ // Drupal user object.
+ global $user;
+
+ if (isset($xls_file) && !empty($xls_file)) {
+ $job_id = tripal_add_job(
+ "Upload Phenoypic data: " . $xls_file,
+ 'rawpheno',
+ 'rawpheno_load_spreadsheet',
+ array(
+ $project_id,
+ serialize($arr_newheaders),
+ $form_state['multistep_values']['fid'],
+ serialize($plantprop_headers)
+ ),
+ $user->uid
+ );
+
+ return $job_id;
+ }
+}
diff --git a/include/rawpheno.upload.helpers.inc b/includes/rawpheno.upload.helpers.inc
old mode 100755
new mode 100644
similarity index 96%
rename from include/rawpheno.upload.helpers.inc
rename to includes/rawpheno.upload.helpers.inc
index 3bc05be..e86f45e
--- a/include/rawpheno.upload.helpers.inc
+++ b/includes/rawpheno.upload.helpers.inc
@@ -1,69 +1,69 @@
- 1, 'review' => 2, 'save' => 3);
- $current_step = (isset($form_stages[$form_state['stage']])) ? $form_stages[$form_state['stage']] : 1;
-
- // Array of stage indicators.
- $stages = array(1 => '1. Validate Spreadsheet',
- 2 => '2. Describe New Trait',
- 3 => '3. Save Spreadsheet');
-
- $markup = '';
- foreach($stages as $k => $v) {
- $class = ($k <= $current_step) ? '' : ' progress-stage-todo';
- $markup .= '
- ' . $v . '
-
';
- }
-
- // Add header to each stage with corresponding
- // stage information defined above.
- $form['header_upload'] = array(
- '#type' => 'markup',
- '#markup' => $markup,
- );
-
- return $form;
-}
-
-
-/**
- * Function to calculate the next stage.
- *
- * @param $form
- * @param $form_state
- *
- * @return
- * A string containing the stage name.
- */
-function rawpheno_next_page($form, &$form_state) {
- // Get the address/name of the next page based on the current stage.
- switch($form_state['stage']) {
- case 'check':
- // In stage check, next is stage 03 or stage 02.
- $btn_submit = $form_state['triggering_element']['#value'];
- return ($btn_submit == 'Save spreadheet') ? 'save' : 'review';
- break;
-
- case 'review':
- // In stage review, next is stage 03.
- return 'save';
- break;
- }
-}
+ 1, 'review' => 2, 'save' => 3);
+ $current_step = (isset($form_stages[$form_state['stage']])) ? $form_stages[$form_state['stage']] : 1;
+
+ // Array of stage indicators.
+ $stages = array(1 => '1. Validate Spreadsheet',
+ 2 => '2. Describe New Trait',
+ 3 => '3. Save Spreadsheet');
+
+ $markup = '';
+ foreach($stages as $k => $v) {
+ $class = ($k <= $current_step) ? '' : ' progress-stage-todo';
+ $markup .= '