Browse Source

Added new cvterm field. Still working on it, but also fixed a bug with loading data into fields (switched to chado_generate_var earlier which created a bug)

Stephen Ficklin 9 years ago
parent
commit
8ea16120a7

+ 354 - 0
tripal_chado/includes/fields/cvterm.inc

@@ -0,0 +1,354 @@
+<?php
+
+/**
+ *
+ * @param unknown $entity_type
+ * @param unknown $entity
+ * @param unknown $field
+ * @param unknown $instance
+ * @param unknown $langcode
+ * @param unknown $items
+ * @param unknown $display
+ */
+function tripal_chado_cvterm_formatter(&$element, $entity_type, $entity, $field,
+    $instance, $langcode, $items, $display) {
+
+/*   $chado_table = $field['settings']['chado_table'];
+  foreach ($items as $delta => $item) {
+    $accession = '';
+    if ($item[$chado_table . '__cvterm_id']) {
+      $cvterm = chado_generate_var('cvterm', array('cvterm_id' => $item[$chado_table . '__cvterm_id']));
+      $dbxref = $cvterm->dbxref_id;
+      $accession = $dbxref->db_id->name . ':' . $dbxref->accession;
+      if ($dbxref->db_id->urlprefix) {
+        $accession = l($accession, $dbxref->db_id->urlprefix . '/' . $dbxref->accession, array('attributes' => array('target' => '_blank')));
+      }
+    }
+    $element[$delta] = array(
+      '#type' => 'markup',
+      '#markup' => $accession,
+    );
+  } */
+}
+/**
+ *
+ * @param unknown $field_name
+ * @param unknown $widget
+ * @param unknown $form
+ * @param unknown $form_state
+ * @param unknown $field
+ * @param unknown $instance
+ * @param unknown $langcode
+ * @param unknown $items
+ * @param unknown $delta
+ * @param unknown $element
+ */
+function tripal_chado_cvterm_widget(&$widget, $form, $form_state, $field,
+    $instance, $langcode, $items, $delta, $element) {
+
+  $field_name = $field['field_name'];
+
+  // Get the FK column that links to the base table.
+  $chado_table = $field['settings']['chado_table'];
+  $base_table = $field['settings']['base_table'];
+  $schema = chado_get_schema($chado_table);
+  $pkey = $schema['primary key'][0];
+  $fkey = array_values($schema['foreign keys'][$base_table]['columns'])[0];
+
+  // Get the field defaults.
+  $record_id = '';
+  $fkey_value = $element['#entity']->chado_record_id;
+  $dbxref_id = '';
+  $db_id = '';
+  $accession = '';
+  $version = '';
+  $description = '';
+
+  // If the field already has a value then it will come through the $items
+  // array.  This happens when editing an existing record.
+  if (array_key_exists($delta, $items)) {
+    $record_id = $items[$delta]['value'];
+    $fkey_value = $items[$delta][$field_name . '__' . $fkey];
+    $dbxref_id = $items[$delta][$field_name . '__dbxref_id'];
+    $db_id = $items[$delta][$field_name . '--dbxref__db_id'];
+    $accession = $items[$delta][$field_name . '--dbxref__accession'];
+    $version = $items[$delta][$field_name . '--dbxref__version'];
+    $description = $items[$delta][$field_name . '--dbxref__description'];
+  }
+
+  // Check $form_state['values'] to see if an AJAX call set the values.
+  if (array_key_exists('values', $form_state)) {
+    $record_id = tripal_chado_get_field_form_values($field_name, $form_state, $delta, $field_name);
+    $fkey_value = tripal_chado_get_field_form_values($field_name, $form_state, $delta, $field_name . '__' . $fkey);
+    $dbxref_id = tripal_chado_get_field_form_values($field_name, $form_state, $delta, $field_name . '__dbxref_id');
+    $db_id = tripal_chado_get_field_form_values($field_name, $form_state, $delta, $field_name . '--dbxref__db_id');
+    $accession = tripal_chado_get_field_form_values($field_name, $form_state, $delta, $field_name . '--dbxref__accession');
+    $version = tripal_chado_get_field_form_values($field_name, $form_state, $delta, $field_name . '--dbxref__version');
+    $description = tripal_chado_get_field_form_values($field_name, $form_state, $delta, $field_name . '--dbxref__description');
+  }
+
+  // If we are here because our parent was triggered in a form submit
+  // then that means an ajax call was made and we don't want the fieldset to
+  // be closed when it returns from the ajax call.
+  $collapsed = TRUE;
+  if (array_key_exists('triggering_element', $form_state) and
+      $form_state['triggering_element']['#parents'][0] == $field_name) {
+    $collapsed = FALSE;
+  }
+  if ($dbxref_id) {
+    $collapsed = FALSE;
+  }
+
+  $schema = chado_get_schema('dbxref');
+  $options = tripal_get_db_select_options();
+
+  $widget['#table_name'] = $chado_table;
+  $widget['#fkey_field'] = $fkey;
+  $widget['#element_validate'] = array('tripal_chado_cvterm_widget_validate');
+  $widget['#theme'] = 'tripal_chado_cvterm_widget';
+  $widget['#prefix'] =  "<span id='$field_name-dbxref--db-id-$delta'>";
+  $widget['#suffix'] =  "</span>";
+
+  // A temporary element used for theming the fieldset.
+  $widget['#theme_settings'] = array(
+    '#title' => $element['#title'],
+    '#description' =>  $element['#description'],
+    '#weight' => isset($element['#weight']) ? $element['#weight'] : 0,
+    '#theme' => 'tripal_chado_cvterm_widget',
+    '#collapsible' => TRUE,
+    '#collapsed' => $collapsed,
+  );
+
+  $widget['value'] = array(
+    '#type' => 'value',
+    '#default_value' => $record_id,
+  );
+  $widget[$field_name . '__dbxref_id'] = array(
+    '#type' => 'value',
+    '#default_value' => $dbxref_id,
+  );
+  $widget[$field_name . '__' . $fkey] = array(
+    '#type' => 'value',
+    '#default_value' => $fkey_value,
+  );
+  $widget[$field_name . '--dbxref__dbxref_id'] = array(
+    '#type' => 'value',
+    '#default_value' => $dbxref_id,
+  );
+  $widget[$field_name . '--dbxref__db_id'] = array(
+    '#type' => 'select',
+    '#title' => t('Database'),
+    '#options' => $options,
+    '#required' => $element['#required'],
+    '#default_value' => $db_id,
+    '#ajax' => array(
+      'callback' => "tripal_chado_cvterm_widget_form_ajax_callback",
+      'wrapper' => "$field_name-dbxref--db-id-$delta",
+      'effect' => 'fade',
+      'method' => 'replace'
+    ),
+  );
+  $widget[$field_name . '--dbxref__accession'] = array(
+    '#type' => 'textfield',
+    '#title' => t('Accession'),
+    '#default_value' => $accession,
+    '#required' => $element['#required'],
+    '#maxlength' => array_key_exists('length', $schema['fields']['accession']) ? $schema['fields']['accession']['length'] : 255,
+    '#size' => 15,
+    '#autocomplete_path' => 'admin/tripal/storage/chado/auto_name/dbxref/' . $db_id,
+    '#ajax' => array(
+      'callback' => "tripal_chado_cvterm_widget_form_ajax_callback",
+      'wrapper' => "$field_name-dbxref--db-id-$delta",
+      'effect' => 'fade',
+      'method' => 'replace'
+    )
+  );
+  $widget[$field_name . '--dbxref__version'] = array(
+    '#type' => 'textfield',
+    '#title' => t('Version'),
+    '#default_value' => $version,
+    '#maxlength' => array_key_exists('length', $schema['fields']['version']) ? $schema['fields']['version']['length'] : 255,
+    '#size' => 5,
+  );
+  $widget[$field_name . '--dbxref__description'] = array(
+    '#type' => 'textfield',
+    '#title' => t('Description'),
+    '#default_value' => $description,
+    '#size' => 20,
+  );
+  $widget['links'] = array(
+    '#type' => 'item',
+    '#markup' => l('Add a new database', 'admin/tripal/chado/tripal_db/add', array('attributes' => array('target' => '_blank')))
+  );
+}
+/**
+ * An Ajax callback for the dbxref widget.
+ */
+function tripal_chado_cvterm_widget_form_ajax_callback($form, $form_state) {
+
+  $field_name = $form_state['triggering_element']['#parents'][0];
+  $delta = $form_state['triggering_element']['#parents'][2];
+  $db_id = tripal_chado_get_field_form_values($field_name, $form_state, $delta, $field_name . '--dbxref__db_id');
+  $accession = tripal_chado_get_field_form_values($field_name, $form_state, $delta, $field_name . '--dbxref__accession');
+  if ($db_id and $accession) {
+    $values = array(
+      'db_id' => $db_id,
+      'accession' => $accession,
+    );
+    $options = array('is_duplicate' => TRUE);
+    $has_duplicate = chado_select_record('dbxref', array('*'), $values, $options);
+    if (!$has_duplicate) {
+      drupal_set_message('The selected cross reference is new and will be added for future auto completions.');
+    }
+  }
+
+  return $form[$field_name]['und'][$delta];
+}
+
+/**
+ * Callback function for validating the tripal_chado_organism_select_widget.
+ */
+function tripal_chado_cvterm_widget_validate($element, &$form_state) {
+
+  $field_name = $element['#field_name'];
+  $delta = $element['#delta'];
+  $table_name = $element['#table_name'];
+  $fkey = $element['#fkey_field'];
+
+  // If the form ID is field_ui_field_edit_form, then the user is editing the
+  // field's values in the manage fields form of Drupal.  We don't want
+  // to validate it as if it were being used in a data entry form.
+  if ($form_state['build_info']['form_id'] =='field_ui_field_edit_form') {
+    return;
+  }
+
+  // Get the field values.
+  $dbxref_id = tripal_chado_get_field_form_values($field_name, $form_state, $delta, $field_name . '__dbxref_id');
+  $db_id = tripal_chado_get_field_form_values($field_name, $form_state, $delta, $field_name . '--dbxref__db_id');
+  $accession = tripal_chado_get_field_form_values($field_name, $form_state, $delta, $field_name . '--dbxref__accession');
+  $version = tripal_chado_get_field_form_values($field_name, $form_state, $delta, $field_name . '--dbxref__version');
+  $description = tripal_chado_get_field_form_values($field_name, $form_state, $delta, $field_name . '--dbxref__description');
+
+  // Make sure that if a database ID is provided that an accession is also
+  // provided.  Here we use the form_set_error function rather than the
+  // form_error function because the form_error will add a red_highlight
+  // around all of the fields in the fieldset which is confusing as it's not
+  // clear to the user what field is required and which isn't. Therefore,
+  // we borrow the code from the 'form_error' function and append the field
+  // so that the proper field is highlighted on error.
+  if (!$db_id and $accession) {
+    form_set_error(implode('][', $element ['#parents']) . '][' . $table_name . '--dbxref__db_id', t("A database and the accession must both be provided for the primary cross reference."));
+  }
+  if ($db_id and !$accession) {
+    form_set_error(implode('][', $element ['#parents']) . '][' . $table_name . '--dbxref__accession', t("A database and the accession must both be provided for the primary cross reference."));
+  }
+  if (!$db_id and !$accession and ($version or $description)) {
+    form_set_error(implode('][', $element ['#parents']) . '][' . $table_name . '--dbxref__db_id', t("A database and the accession must both be provided for the primary cross reference."));
+  }
+
+  // If user did not select a database, we want to remove dbxref_id from the
+  // field.
+  if (!$db_id) {
+    tripal_chado_set_field_form_values($field_name, $form_state, '__NULL__');
+  }
+  // If the dbxref_id does not match the db_id + accession then the user
+  // has selected a new dbxref record and we need to update the hidden
+  // value accordingly.
+  if ($db_id and $accession) {
+    $dbxref = chado_generate_var('dbxref', array('db_id' => $db_id, 'accession' => $accession));
+    if ($dbxref and $dbxref->dbxref_id != $dbxref_id) {
+      tripal_chado_set_field_form_values($field_name, $form_state, $dbxref->dbxref_id, $delta, $table_name . '__dbxref_id');
+      tripal_chado_set_field_form_values($field_name, $form_state, $dbxref->dbxref_id, $delta, $table_name . '--dbxref__dbxref_id');
+    }
+  }
+}
+/**
+ * Theme function for the dbxref_id_widget.
+ *
+ * @param $variables
+ */
+function theme_tripal_chado_cvterm_widget($variables) {
+  $element = $variables['element'];
+
+  // These two fields were added to the widget to help identify the fields
+  // for layout.
+  $table_name = $element['#table_name'];
+  $fkey = $element['#fkey_field'];
+
+  $layout = "
+    <div class=\"secondary-dbxref-widget\">
+      <div class=\"secondary-dbxref-widget-item\">" .
+        drupal_render($element[$table_name . '--dbxref__db_id']) . "
+      </div>
+      <div class=\"secondary-dbxref-widget-item\">" .
+        drupal_render($element[$table_name . '--dbxref__accession']) . "
+      </div>
+      <div class=\"secondary-dbxref-widget-item\">" .
+        drupal_render($element[$table_name . '--dbxref__version']) . "
+      </div>
+      <div class=\"secondary-dbxref-widget-item\">" .
+        drupal_render($element[$table_name . '--dbxref__description']) . "
+      </div>
+      <div class=\"secondary-dbxref-widget-links\">" . drupal_render($element['links']) . "</div>
+    </div>
+  ";
+
+  return $layout;
+}
+
+/**
+ * Loads the field values with appropriate data.
+ *
+ * This function is called by the tripal_chado_field_storage_load() for
+ * each property managed by the field_chado_storage storage type.  This is
+ * an optional hook function that is only needed if the field has
+ * multiple form elements.
+ *
+ * @param $field
+ * @param $entity
+ * @param $base_table
+ * @param $record
+ */
+function tripal_chado_cvterm_field_load($field, $entity, $base_table, $record) {
+
+  $field_name = $field['field_name'];
+  $field_type = $field['type'];
+  $chado_table = $field['settings']['chado_table'];
+  $schema = chado_get_schema($chado_table);
+  $pkey = $schema['primary key'][0];
+  $fkey = array_values($schema['foreign keys'][$base_table]['columns'])[0];
+
+  // Set some defaults for the empty record.
+  $entity->{$field_name}['und'][0] = array(
+    'value' => '',
+    $chado_table . '__' . $fkey => '',
+    $chado_table . '__' . 'cvterm_id' => '',
+    $chado_table . '__' . 'pub_id' => '',
+    $chado_table . '__' . 'is_not' => '',
+    $chado_table . '__' . 'rank' => '',
+    $chado_table . '--' . 'cvterm__name' => '',
+    $chado_table . '--' . 'cvterm__cvterm_id' => '',
+  );
+
+  $linker_table = $base_table . '_cvterm';
+  dpm($linker_table);
+  /* $options = array('return_array' => 1);
+  $record = chado_expand_var($record, 'table', $linker_table, $options);
+  $i = 0;
+  foreach ($record->$linker_table as $index => $linker) {
+    $cvterm = $linker->cvterm_id;
+    $entity->{$field_name}['und'][$i] = array(
+      'value' => $linker->$pkey,
+      $chado_table . '__' . $fkey => $linker->$fkey->$fkey,
+      $chado_table . '__' . 'cvterm_id' => $linker->cvterm_id->cvterm_id,
+      $chado_table . '__' . 'pub_id' => property_exists($linker, 'pub_id') ? $linker->pub_id : '',
+      $chado_table . '__' . 'is_not' => property_exists($linker, 'is_not') ? $linker->is_not : '',
+      $chado_table . '__' . 'rank' => property_exists($linker, 'rank') ? $linker->rank : '',
+      $chado_table . '__' . 'cvterm_type_id' => property_exists($linker, 'cvterm_type_id') ? $linker->cvterm_type_id : '',
+      $chado_table . '--' . 'cvterm__name' => $cvterm->name,
+      $chado_table . '--' . 'cvterm__cvterm_id' => $cvterm->cvterm_id,
+    );
+    $i++;
+  } */
+}
+

+ 9 - 1
tripal_chado/includes/tripal_chado.field_storage.inc

@@ -311,7 +311,15 @@ function tripal_chado_field_storage_load($entity_type, $entities, $age,
         // Set an empty value by default, and if there is a record, then update.
         $entity->{$field_name}['und'][0]['value'] = '';
         if ($record and property_exists($record, $field_column)) {
-          $entity->{$field_name}['und'][0]['value'] = $record->$field_column;
+          // If the field column is an object then it's a FK to another table.
+          // and because $record object is created by the chado_generate_var()
+          // function we must go one more level deeper to get the value
+          if (is_object($record->$field_column)) {
+            $entity->{$field_name}['und'][0]['value'] = $record->$field_column->$field_column;
+          }
+          else {
+            $entity->{$field_name}['und'][0]['value'] = $record->$field_column;
+          }
         }
         // Allow the creating module to alter the value if desired.  The
         // module should do this if the field has any other form elements

+ 94 - 1
tripal_chado/tripal_chado.module

@@ -510,6 +510,23 @@ function tripal_chado_field_info() {
         'active' => TRUE
       ),
     ),
+
+    // The field provides form elements for adding multiple cvterms to an
+    // entity that in turn get stored in a [base]_cvterm table of Chado
+    // (e.g. feature_cvterm).
+    'cvterm' => array(
+      'label' => t('Annotations'),
+      'description' => t('This record can be annotated with terms
+          from other vocabularies.'),
+      'default_widget' => 'tripal_chado_cvterm_widget',
+      'default_formatter' => 'tripal_chado_cvterm_formatter',
+      'settings' => array(),
+      'storage' => array(
+        'type' => 'field_chado_storage',
+        'module' => 'tripal_chado',
+        'active' => TRUE
+      ),
+    ),
   );
   return $fields;
 }
@@ -539,6 +556,12 @@ function tripal_chado_field_widget_info() {
         prominent reference.  At a minimum, the database and accession
         must be provided.'),
     ),
+    'tripal_chado_cvterm_widget' => array(
+      'label' => t('Annotations'),
+      'field types' => array('cvterm'),
+      'description' => t('This record can be annotated with terms
+          from other vocabularies.'),
+    ),
     'tripal_chado_md5checksum_checkbox_widget' => array(
       'label' => t('MD5 Checksum Checkbox'),
       'field types' => array('md5checksum'),
@@ -578,6 +601,10 @@ function tripal_chado_field_formatter_info() {
       'label' => t('Cross references'),
       'field types' => array('dbxref')
     ),
+    'tripal_chado_cvterm_formatter' => array(
+      'label' => t('Annotations'),
+      'field types' => array('cvterm')
+    ),
     'tripal_chado_md5checksum_formatter' => array(
       'label' => t('MD5 checksum'),
       'field types' => array('md5checksum')
@@ -687,6 +714,11 @@ function tripal_chado_field_formatter_view($entity_type, $entity, $field,
           tripal_chado_dbxref_formatter($element, $entity_type, $entity, $field,
               $instance, $langcode, $items, $display);
           break;
+        case 'tripal_chado_cvterm_formatter':
+          module_load_include('inc', 'tripal_chado', 'includes/fields/cvterm');
+          tripal_chado_cvterm_formatter($element, $entity_type, $entity, $field,
+              $instance, $langcode, $items, $display);
+          break;
         case 'tripal_chado_md5checksum_formatter':
           module_load_include('inc', 'tripal_chado', 'includes/fields/md5checksum');
           tripal_chado_md5checksum_checkbox_formatter($element, $entity_type, $entity, $field,
@@ -741,6 +773,11 @@ function tripal_chado_field_widget_form(&$form, &$form_state, $field,
       module_load_include('inc', 'tripal_chado', 'includes/fields/dbxref');
       tripal_chado_dbxref_widget($widget, $form, $form_state, $field, $instance, $langcode, $items, $delta, $element);
       break;
+    case 'tripal_chado_cvterm_widget':
+      form_load_include($form_state, 'inc', 'tripal_chado', 'includes/fields/cvterm');
+      module_load_include('inc', 'tripal_chado', 'includes/fields/cvterm');
+      tripal_chado_cvterm_widget($widget, $form, $form_state, $field, $instance, $langcode, $items, $delta, $element);
+      break;
     case 'tripal_chado_md5checksum_checkbox_widget':
       form_load_include($form_state, 'inc', 'tripal_chado', 'includes/fields/md5checksum');
       module_load_include('inc', 'tripal_chado', 'includes/fields/md5checksum');
@@ -1025,6 +1062,17 @@ function tripal_chado_add_bundle_fields($entity_type, $bundle, $term) {
   if (chado_table_exists($dbxref_table)) {
     tripal_chado_add_bundle_dbxref_field($entity_type, $bundle_name, $dbxref_table, $bundle_data['data_table']);
   }
+
+  ////
+  //
+  // Cvterm table fields.
+  //
+  // Check to see if there are any cvterm tables with FKs to this
+  // base table. If so, add the fields for that type of table.
+  $cvterm_table = $bundle_data['data_table'] . '_cvterm';
+  if (chado_table_exists($cvterm_table)) {
+    tripal_chado_add_bundle_cvterm_field($entity_type, $bundle_name, $cvterm_table, $bundle_data['data_table']);
+  }
 }
 
 /**
@@ -1044,7 +1092,7 @@ function tripal_chado_add_bundle_dbxref_field($entity_type_name, $bundle_name, $
   // Initialize the field array.
   $field_info = array(
     'field_type' => 'dbxref',
-    'widget_type' => 'tripal_fields_kvproperty_adder_widget',
+    'widget_type' => 'tripal_fields_dbxfref_widget',
     'widget_settings' => array('display_label' => 1),
     'description' => '',
     'label' => 'Cross References',
@@ -1077,6 +1125,51 @@ function tripal_chado_add_bundle_dbxref_field($entity_type_name, $bundle_name, $
   }
   tripal_add_bundle_field($field_name, $field_info, $entity_type_name, $bundle_name);
 }
+
+/**
+ * Adds the fields for managing xrefs that are stored in a [base]_dbxref table.
+ *
+ * @param $entity_type
+ * @param $bundle_name
+ * @param $base_table
+ * @param $dbxref_table
+ */
+function tripal_chado_add_bundle_cvterm_field($entity_type_name, $bundle_name, $cvterm_table, $base_table) {
+  // We already have a dbxref_id field.
+  $field_name = $cvterm_table;
+  $schema = chado_get_schema($cvterm_table);
+  $pkey = $schema['primary key'][0];
+
+  // Initialize the field array.
+  $field_info = array(
+    'field_type' => 'cvterm',
+    'widget_type' => 'tripal_fields_cvterm_widget',
+    'widget_settings' => array('display_label' => 1),
+    'description' => '',
+    'label' => 'Annotations',
+    'is_required' => 0,
+    'cardinality' => FIELD_CARDINALITY_UNLIMITED,
+    'field_settings' => array(
+      // The Chado table that this field maps to.
+      'chado_table' => $cvterm_table,
+      // The column in the chado table that this field maps to.
+      'chado_column' => $pkey,
+      // The base table that this field is connected to.
+      'base_table' => $base_table,
+      'semantic_web' => array(
+        // The type is the term from a vocabulary that desribes this field..
+        'type' => '',
+        // The namepsace for the vocabulary (e.g. 'foaf').
+        'ns' => '',
+        // The URL for the namespace.  It must be that the type can be
+        // appended to the URL.
+        'nsurl' => '',
+      ),
+    ),
+  );
+
+  tripal_add_bundle_field($field_name, $field_info, $entity_type_name, $bundle_name);
+}
 /**
  * Adds the fields for managing properties that are stored in a prop table.
  *