diff --git a/PULL_REQUEST_TEMPLATE.md b/PULL_REQUEST_TEMPLATE.md deleted file mode 100644 index e793684..0000000 --- a/PULL_REQUEST_TEMPLATE.md +++ /dev/null @@ -1,35 +0,0 @@ - - - -## Metadata - - - Issue # - - - -- [ ] I feel this PR is ready to be merged. -- [ ] This PR is dependent upon [PR #/ nothing] - -Documentation: -- [ ] My code follows the code style of this project. -- [ ] My change requires a change to the documentation. -- [ ] I have updated the documentation accordingly. - -## Description - - - -## Dependencies - - - -## Testing? - - - -- [ ] I tested on a generic Tripal Site -- [ ] I tested on a KnowPulse Clone -- [ ] This PR includes automated testing diff --git a/README.md b/README.md deleted file mode 100644 index b0f7fbe..0000000 --- a/README.md +++ /dev/null @@ -1,40 +0,0 @@ -[![Build Status](https://travis-ci.org/UofS-Pulse-Binfo/rawphenotypes.svg?branch=master)](https://travis-ci.org/UofS-Pulse-Binfo/rawphenotypes) - -This module was designed to aid in collection and further analysis of raw phenotypic data. With this in mind it provides: - -1. An interface for defining projects including which traits are expected to be collected and the users collecting them; -2. An interactive, researcher-friendly excel datasheet upload; -3. Summary charts -4. Data Download functionality with filter criteria and customizable R-friendly headers. - -## Dependencies -1. [Drag & Drop Upload](https://www.drupal.org/project/dragndrop_upload) -2. [Spreadsheet Reader](https://github.com/nuovo/spreadsheet-reader) -3. [Spreadsheet Writer](https://github.com/SystemDevil/PHP_XLSXWriter_plus) -4. [D3.js version 3.5.14](https://github.com/d3/d3/releases/download/v3.5.14/d3.zip) - -__Note: there are some modifications needed to the spreadsheet reader to get it to handle dates the way we expect. Make sure to apply the spreadsheet-reader.patch included with this module to the spreadsheet-reader library.__ - -## Features -- A d3.js heatmap summarizes the raw data available by displaying the number of traits broken down by location (x-axis) and replicate (y-axis; grouped by year). This chart uses a materialized view for improved performance. - -![heatmap](https://raw.githubusercontent.com/wiki/UofS-Pulse-Binfo/rawphenotypes/images/rawphenotypes.screenshot.summary.heatmap.png) -- If a trait is selected on the Heatmap summary, a barchart is dynamically generated beneath to show the range of values collected. - -![barchart](https://raw.githubusercontent.com/wiki/UofS-Pulse-Binfo/rawphenotypes/images/rawphenotypes.screenshot.summary.barchart.png) -- Upload data functionality supporting excel spreadsheets (XLSX). The loader expects the traits specified for a given project but is also flexible enough to allow users to add additional traits (one per column) to the spreadsheet. If additional traits are present, the loader asks the user to describe the trait including the units and any scale used. - -![upload1](https://raw.githubusercontent.com/wiki/UofS-Pulse-Binfo/rawphenotypes/images/rawphenotypes.screenshot.upload1.png) -- During upload the file is validated using a number of tests including general "Is this an excel file?", as well as, "Do all the germplasm in the file already exist in the chado.stock table?". Validation tests are provided via a Drupal hook meaning you can add your own data-specific tests (see github wiki). - -![upload2](https://raw.githubusercontent.com/wiki/UofS-Pulse-Binfo/rawphenotypes/images/rawphenotypes.screenshot.upload2.png) -- A searchable trait collection instruction page defining a standard protocol for collection of the traits. This information is pulled from the trait controlled vocabulary and thus easily updatable. -- Data Download functionality which allows users to select the locations and traits they are interested in. A comma-separated file is produced which can easily be opened in excel for viewing and is R-friendly for analysis. - -![download](https://raw.githubusercontent.com/wiki/UofS-Pulse-Binfo/rawphenotypes/images/rawphenotypes.screenshot.download1.png) - -## Data Storage -Trait information is stored in a custom controlled vocabulary set-up by this module. All the remaining data is stored in a custom relational schema. This allows us to keep the raw data separate from chado and paves the way for an ND Phenotypes module which stored the analyzed/filtered phenotypic data in the chado schema and provides trait pages and summary information on germplasm and project pages. - -## Documentation -In addition to this README there is a wiki available at https://github.com/UofS-Pulse-Binfo/rawphenotypes/wiki which includes an administration tutorial, as well as, other useful information. diff --git a/composer.json b/composer.json deleted file mode 100644 index 750bdaa..0000000 --- a/composer.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "require-dev": { - "statonlab/tripal-test-suite": "^1.1" - } -} diff --git a/composer.lock b/composer.lock deleted file mode 100644 index 6ad409d..0000000 --- a/composer.lock +++ /dev/null @@ -1,1953 +0,0 @@ -{ - "_readme": [ - "This file locks the dependencies of your project to a known state", - "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", - "This file is @generated automatically" - ], - "content-hash": "f47e5443e14a993518b825cf72363118", - "packages": [], - "packages-dev": [ - { - "name": "doctrine/instantiator", - "version": "1.0.5", - "source": { - "type": "git", - "url": "https://github.com/doctrine/instantiator.git", - "reference": "8e884e78f9f0eb1329e445619e04456e64d8051d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/doctrine/instantiator/zipball/8e884e78f9f0eb1329e445619e04456e64d8051d", - "reference": "8e884e78f9f0eb1329e445619e04456e64d8051d", - "shasum": "" - }, - "require": { - "php": ">=5.3,<8.0-DEV" - }, - "require-dev": { - "athletic/athletic": "~0.1.8", - "ext-pdo": "*", - "ext-phar": "*", - "phpunit/phpunit": "~4.0", - "squizlabs/php_codesniffer": "~2.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, - "autoload": { - "psr-4": { - "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Marco Pivetta", - "email": "ocramius@gmail.com", - "homepage": "http://ocramius.github.com/" - } - ], - "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", - "homepage": "https://github.com/doctrine/instantiator", - "keywords": [ - "constructor", - "instantiate" - ], - "time": "2015-06-14T21:17:01+00:00" - }, - { - "name": "fzaninotto/faker", - "version": "v1.7.1", - "source": { - "type": "git", - "url": "https://github.com/fzaninotto/Faker.git", - "reference": "d3ed4cc37051c1ca52d22d76b437d14809fc7e0d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/fzaninotto/Faker/zipball/d3ed4cc37051c1ca52d22d76b437d14809fc7e0d", - "reference": "d3ed4cc37051c1ca52d22d76b437d14809fc7e0d", - "shasum": "" - }, - "require": { - "php": "^5.3.3 || ^7.0" - }, - "require-dev": { - "ext-intl": "*", - "phpunit/phpunit": "^4.0 || ^5.0", - "squizlabs/php_codesniffer": "^1.5" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.8-dev" - } - }, - "autoload": { - "psr-4": { - "Faker\\": "src/Faker/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "François Zaninotto" - } - ], - "description": "Faker is a PHP library that generates fake data for you.", - "keywords": [ - "data", - "faker", - "fixtures" - ], - "time": "2017-08-15T16:48:10+00:00" - }, - { - "name": "guzzlehttp/guzzle", - "version": "6.3.3", - "source": { - "type": "git", - "url": "https://github.com/guzzle/guzzle.git", - "reference": "407b0cb880ace85c9b63c5f9551db498cb2d50ba" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/guzzle/guzzle/zipball/407b0cb880ace85c9b63c5f9551db498cb2d50ba", - "reference": "407b0cb880ace85c9b63c5f9551db498cb2d50ba", - "shasum": "" - }, - "require": { - "guzzlehttp/promises": "^1.0", - "guzzlehttp/psr7": "^1.4", - "php": ">=5.5" - }, - "require-dev": { - "ext-curl": "*", - "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.4 || ^7.0", - "psr/log": "^1.0" - }, - "suggest": { - "psr/log": "Required for using the Log middleware" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "6.3-dev" - } - }, - "autoload": { - "files": [ - "src/functions_include.php" - ], - "psr-4": { - "GuzzleHttp\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Michael Dowling", - "email": "mtdowling@gmail.com", - "homepage": "https://github.com/mtdowling" - } - ], - "description": "Guzzle is a PHP HTTP client library", - "homepage": "http://guzzlephp.org/", - "keywords": [ - "client", - "curl", - "framework", - "http", - "http client", - "rest", - "web service" - ], - "time": "2018-04-22T15:46:56+00:00" - }, - { - "name": "guzzlehttp/promises", - "version": "v1.3.1", - "source": { - "type": "git", - "url": "https://github.com/guzzle/promises.git", - "reference": "a59da6cf61d80060647ff4d3eb2c03a2bc694646" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/guzzle/promises/zipball/a59da6cf61d80060647ff4d3eb2c03a2bc694646", - "reference": "a59da6cf61d80060647ff4d3eb2c03a2bc694646", - "shasum": "" - }, - "require": { - "php": ">=5.5.0" - }, - "require-dev": { - "phpunit/phpunit": "^4.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.4-dev" - } - }, - "autoload": { - "psr-4": { - "GuzzleHttp\\Promise\\": "src/" - }, - "files": [ - "src/functions_include.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Michael Dowling", - "email": "mtdowling@gmail.com", - "homepage": "https://github.com/mtdowling" - } - ], - "description": "Guzzle promises library", - "keywords": [ - "promise" - ], - "time": "2016-12-20T10:07:11+00:00" - }, - { - "name": "guzzlehttp/psr7", - "version": "1.4.2", - "source": { - "type": "git", - "url": "https://github.com/guzzle/psr7.git", - "reference": "f5b8a8512e2b58b0071a7280e39f14f72e05d87c" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/guzzle/psr7/zipball/f5b8a8512e2b58b0071a7280e39f14f72e05d87c", - "reference": "f5b8a8512e2b58b0071a7280e39f14f72e05d87c", - "shasum": "" - }, - "require": { - "php": ">=5.4.0", - "psr/http-message": "~1.0" - }, - "provide": { - "psr/http-message-implementation": "1.0" - }, - "require-dev": { - "phpunit/phpunit": "~4.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.4-dev" - } - }, - "autoload": { - "psr-4": { - "GuzzleHttp\\Psr7\\": "src/" - }, - "files": [ - "src/functions_include.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Michael Dowling", - "email": "mtdowling@gmail.com", - "homepage": "https://github.com/mtdowling" - }, - { - "name": "Tobias Schultze", - "homepage": "https://github.com/Tobion" - } - ], - "description": "PSR-7 message implementation that also provides common utility methods", - "keywords": [ - "http", - "message", - "request", - "response", - "stream", - "uri", - "url" - ], - "time": "2017-03-20T17:10:46+00:00" - }, - { - "name": "myclabs/deep-copy", - "version": "1.7.0", - "source": { - "type": "git", - "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "3b8a3a99ba1f6a3952ac2747d989303cbd6b7a3e" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/3b8a3a99ba1f6a3952ac2747d989303cbd6b7a3e", - "reference": "3b8a3a99ba1f6a3952ac2747d989303cbd6b7a3e", - "shasum": "" - }, - "require": { - "php": "^5.6 || ^7.0" - }, - "require-dev": { - "doctrine/collections": "^1.0", - "doctrine/common": "^2.6", - "phpunit/phpunit": "^4.1" - }, - "type": "library", - "autoload": { - "psr-4": { - "DeepCopy\\": "src/DeepCopy/" - }, - "files": [ - "src/DeepCopy/deep_copy.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "Create deep copies (clones) of your objects", - "keywords": [ - "clone", - "copy", - "duplicate", - "object", - "object graph" - ], - "time": "2017-10-19T19:58:43+00:00" - }, - { - "name": "phpdocumentor/reflection-common", - "version": "1.0.1", - "source": { - "type": "git", - "url": "https://github.com/phpDocumentor/ReflectionCommon.git", - "reference": "21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6", - "reference": "21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6", - "shasum": "" - }, - "require": { - "php": ">=5.5" - }, - "require-dev": { - "phpunit/phpunit": "^4.6" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, - "autoload": { - "psr-4": { - "phpDocumentor\\Reflection\\": [ - "src" - ] - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Jaap van Otterdijk", - "email": "opensource@ijaap.nl" - } - ], - "description": "Common reflection classes used by phpdocumentor to reflect the code structure", - "homepage": "http://www.phpdoc.org", - "keywords": [ - "FQSEN", - "phpDocumentor", - "phpdoc", - "reflection", - "static analysis" - ], - "time": "2017-09-11T18:02:19+00:00" - }, - { - "name": "phpdocumentor/reflection-docblock", - "version": "3.3.2", - "source": { - "type": "git", - "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", - "reference": "bf329f6c1aadea3299f08ee804682b7c45b326a2" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/bf329f6c1aadea3299f08ee804682b7c45b326a2", - "reference": "bf329f6c1aadea3299f08ee804682b7c45b326a2", - "shasum": "" - }, - "require": { - "php": "^5.6 || ^7.0", - "phpdocumentor/reflection-common": "^1.0.0", - "phpdocumentor/type-resolver": "^0.4.0", - "webmozart/assert": "^1.0" - }, - "require-dev": { - "mockery/mockery": "^0.9.4", - "phpunit/phpunit": "^4.4" - }, - "type": "library", - "autoload": { - "psr-4": { - "phpDocumentor\\Reflection\\": [ - "src/" - ] - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Mike van Riel", - "email": "me@mikevanriel.com" - } - ], - "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", - "time": "2017-11-10T14:09:06+00:00" - }, - { - "name": "phpdocumentor/type-resolver", - "version": "0.4.0", - "source": { - "type": "git", - "url": "https://github.com/phpDocumentor/TypeResolver.git", - "reference": "9c977708995954784726e25d0cd1dddf4e65b0f7" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/9c977708995954784726e25d0cd1dddf4e65b0f7", - "reference": "9c977708995954784726e25d0cd1dddf4e65b0f7", - "shasum": "" - }, - "require": { - "php": "^5.5 || ^7.0", - "phpdocumentor/reflection-common": "^1.0" - }, - "require-dev": { - "mockery/mockery": "^0.9.4", - "phpunit/phpunit": "^5.2||^4.8.24" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, - "autoload": { - "psr-4": { - "phpDocumentor\\Reflection\\": [ - "src/" - ] - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Mike van Riel", - "email": "me@mikevanriel.com" - } - ], - "time": "2017-07-14T14:27:02+00:00" - }, - { - "name": "phpspec/prophecy", - "version": "1.7.6", - "source": { - "type": "git", - "url": "https://github.com/phpspec/prophecy.git", - "reference": "33a7e3c4fda54e912ff6338c48823bd5c0f0b712" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpspec/prophecy/zipball/33a7e3c4fda54e912ff6338c48823bd5c0f0b712", - "reference": "33a7e3c4fda54e912ff6338c48823bd5c0f0b712", - "shasum": "" - }, - "require": { - "doctrine/instantiator": "^1.0.2", - "php": "^5.3|^7.0", - "phpdocumentor/reflection-docblock": "^2.0|^3.0.2|^4.0", - "sebastian/comparator": "^1.1|^2.0|^3.0", - "sebastian/recursion-context": "^1.0|^2.0|^3.0" - }, - "require-dev": { - "phpspec/phpspec": "^2.5|^3.2", - "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.5" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.7.x-dev" - } - }, - "autoload": { - "psr-0": { - "Prophecy\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Konstantin Kudryashov", - "email": "ever.zet@gmail.com", - "homepage": "http://everzet.com" - }, - { - "name": "Marcello Duarte", - "email": "marcello.duarte@gmail.com" - } - ], - "description": "Highly opinionated mocking framework for PHP 5.3+", - "homepage": "https://github.com/phpspec/prophecy", - "keywords": [ - "Double", - "Dummy", - "fake", - "mock", - "spy", - "stub" - ], - "time": "2018-04-18T13:57:24+00:00" - }, - { - "name": "phpunit/php-code-coverage", - "version": "4.0.8", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "ef7b2f56815df854e66ceaee8ebe9393ae36a40d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/ef7b2f56815df854e66ceaee8ebe9393ae36a40d", - "reference": "ef7b2f56815df854e66ceaee8ebe9393ae36a40d", - "shasum": "" - }, - "require": { - "ext-dom": "*", - "ext-xmlwriter": "*", - "php": "^5.6 || ^7.0", - "phpunit/php-file-iterator": "^1.3", - "phpunit/php-text-template": "^1.2", - "phpunit/php-token-stream": "^1.4.2 || ^2.0", - "sebastian/code-unit-reverse-lookup": "^1.0", - "sebastian/environment": "^1.3.2 || ^2.0", - "sebastian/version": "^1.0 || ^2.0" - }, - "require-dev": { - "ext-xdebug": "^2.1.4", - "phpunit/phpunit": "^5.7" - }, - "suggest": { - "ext-xdebug": "^2.5.1" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.0.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sb@sebastian-bergmann.de", - "role": "lead" - } - ], - "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", - "homepage": "https://github.com/sebastianbergmann/php-code-coverage", - "keywords": [ - "coverage", - "testing", - "xunit" - ], - "time": "2017-04-02T07:44:40+00:00" - }, - { - "name": "phpunit/php-file-iterator", - "version": "1.4.5", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-file-iterator.git", - "reference": "730b01bc3e867237eaac355e06a36b85dd93a8b4" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/730b01bc3e867237eaac355e06a36b85dd93a8b4", - "reference": "730b01bc3e867237eaac355e06a36b85dd93a8b4", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.4.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sb@sebastian-bergmann.de", - "role": "lead" - } - ], - "description": "FilterIterator implementation that filters files based on a list of suffixes.", - "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", - "keywords": [ - "filesystem", - "iterator" - ], - "time": "2017-11-27T13:52:08+00:00" - }, - { - "name": "phpunit/php-text-template", - "version": "1.2.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-text-template.git", - "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/31f8b717e51d9a2afca6c9f046f5d69fc27c8686", - "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "type": "library", - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Simple template engine.", - "homepage": "https://github.com/sebastianbergmann/php-text-template/", - "keywords": [ - "template" - ], - "time": "2015-06-21T13:50:34+00:00" - }, - { - "name": "phpunit/php-timer", - "version": "1.0.9", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-timer.git", - "reference": "3dcf38ca72b158baf0bc245e9184d3fdffa9c46f" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/3dcf38ca72b158baf0bc245e9184d3fdffa9c46f", - "reference": "3dcf38ca72b158baf0bc245e9184d3fdffa9c46f", - "shasum": "" - }, - "require": { - "php": "^5.3.3 || ^7.0" - }, - "require-dev": { - "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sb@sebastian-bergmann.de", - "role": "lead" - } - ], - "description": "Utility class for timing", - "homepage": "https://github.com/sebastianbergmann/php-timer/", - "keywords": [ - "timer" - ], - "time": "2017-02-26T11:10:40+00:00" - }, - { - "name": "phpunit/php-token-stream", - "version": "1.4.12", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-token-stream.git", - "reference": "1ce90ba27c42e4e44e6d8458241466380b51fa16" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/1ce90ba27c42e4e44e6d8458241466380b51fa16", - "reference": "1ce90ba27c42e4e44e6d8458241466380b51fa16", - "shasum": "" - }, - "require": { - "ext-tokenizer": "*", - "php": ">=5.3.3" - }, - "require-dev": { - "phpunit/phpunit": "~4.2" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.4-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Wrapper around PHP's tokenizer extension.", - "homepage": "https://github.com/sebastianbergmann/php-token-stream/", - "keywords": [ - "tokenizer" - ], - "time": "2017-12-04T08:55:13+00:00" - }, - { - "name": "phpunit/phpunit", - "version": "5.7.27", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "b7803aeca3ccb99ad0a506fa80b64cd6a56bbc0c" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/b7803aeca3ccb99ad0a506fa80b64cd6a56bbc0c", - "reference": "b7803aeca3ccb99ad0a506fa80b64cd6a56bbc0c", - "shasum": "" - }, - "require": { - "ext-dom": "*", - "ext-json": "*", - "ext-libxml": "*", - "ext-mbstring": "*", - "ext-xml": "*", - "myclabs/deep-copy": "~1.3", - "php": "^5.6 || ^7.0", - "phpspec/prophecy": "^1.6.2", - "phpunit/php-code-coverage": "^4.0.4", - "phpunit/php-file-iterator": "~1.4", - "phpunit/php-text-template": "~1.2", - "phpunit/php-timer": "^1.0.6", - "phpunit/phpunit-mock-objects": "^3.2", - "sebastian/comparator": "^1.2.4", - "sebastian/diff": "^1.4.3", - "sebastian/environment": "^1.3.4 || ^2.0", - "sebastian/exporter": "~2.0", - "sebastian/global-state": "^1.1", - "sebastian/object-enumerator": "~2.0", - "sebastian/resource-operations": "~1.0", - "sebastian/version": "^1.0.6|^2.0.1", - "symfony/yaml": "~2.1|~3.0|~4.0" - }, - "conflict": { - "phpdocumentor/reflection-docblock": "3.0.2" - }, - "require-dev": { - "ext-pdo": "*" - }, - "suggest": { - "ext-xdebug": "*", - "phpunit/php-invoker": "~1.1" - }, - "bin": [ - "phpunit" - ], - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "5.7.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "The PHP Unit Testing framework.", - "homepage": "https://phpunit.de/", - "keywords": [ - "phpunit", - "testing", - "xunit" - ], - "time": "2018-02-01T05:50:59+00:00" - }, - { - "name": "phpunit/phpunit-mock-objects", - "version": "3.4.4", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/phpunit-mock-objects.git", - "reference": "a23b761686d50a560cc56233b9ecf49597cc9118" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/a23b761686d50a560cc56233b9ecf49597cc9118", - "reference": "a23b761686d50a560cc56233b9ecf49597cc9118", - "shasum": "" - }, - "require": { - "doctrine/instantiator": "^1.0.2", - "php": "^5.6 || ^7.0", - "phpunit/php-text-template": "^1.2", - "sebastian/exporter": "^1.2 || ^2.0" - }, - "conflict": { - "phpunit/phpunit": "<5.4.0" - }, - "require-dev": { - "phpunit/phpunit": "^5.4" - }, - "suggest": { - "ext-soap": "*" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.2.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sb@sebastian-bergmann.de", - "role": "lead" - } - ], - "description": "Mock Object library for PHPUnit", - "homepage": "https://github.com/sebastianbergmann/phpunit-mock-objects/", - "keywords": [ - "mock", - "xunit" - ], - "time": "2017-06-30T09:13:00+00:00" - }, - { - "name": "psr/http-message", - "version": "1.0.1", - "source": { - "type": "git", - "url": "https://github.com/php-fig/http-message.git", - "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-fig/http-message/zipball/f6561bf28d520154e4b0ec72be95418abe6d9363", - "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363", - "shasum": "" - }, - "require": { - "php": ">=5.3.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, - "autoload": { - "psr-4": { - "Psr\\Http\\Message\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "PHP-FIG", - "homepage": "http://www.php-fig.org/" - } - ], - "description": "Common interface for HTTP messages", - "homepage": "https://github.com/php-fig/http-message", - "keywords": [ - "http", - "http-message", - "psr", - "psr-7", - "request", - "response" - ], - "time": "2016-08-06T14:39:51+00:00" - }, - { - "name": "psr/log", - "version": "1.0.2", - "source": { - "type": "git", - "url": "https://github.com/php-fig/log.git", - "reference": "4ebe3a8bf773a19edfe0a84b6585ba3d401b724d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-fig/log/zipball/4ebe3a8bf773a19edfe0a84b6585ba3d401b724d", - "reference": "4ebe3a8bf773a19edfe0a84b6585ba3d401b724d", - "shasum": "" - }, - "require": { - "php": ">=5.3.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, - "autoload": { - "psr-4": { - "Psr\\Log\\": "Psr/Log/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "PHP-FIG", - "homepage": "http://www.php-fig.org/" - } - ], - "description": "Common interface for logging libraries", - "homepage": "https://github.com/php-fig/log", - "keywords": [ - "log", - "psr", - "psr-3" - ], - "time": "2016-10-10T12:19:37+00:00" - }, - { - "name": "sebastian/code-unit-reverse-lookup", - "version": "1.0.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", - "reference": "4419fcdb5eabb9caa61a27c7a1db532a6b55dd18" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/4419fcdb5eabb9caa61a27c7a1db532a6b55dd18", - "reference": "4419fcdb5eabb9caa61a27c7a1db532a6b55dd18", - "shasum": "" - }, - "require": { - "php": "^5.6 || ^7.0" - }, - "require-dev": { - "phpunit/phpunit": "^5.7 || ^6.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Looks up which function or method a line of code belongs to", - "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", - "time": "2017-03-04T06:30:41+00:00" - }, - { - "name": "sebastian/comparator", - "version": "1.2.4", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "2b7424b55f5047b47ac6e5ccb20b2aea4011d9be" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/2b7424b55f5047b47ac6e5ccb20b2aea4011d9be", - "reference": "2b7424b55f5047b47ac6e5ccb20b2aea4011d9be", - "shasum": "" - }, - "require": { - "php": ">=5.3.3", - "sebastian/diff": "~1.2", - "sebastian/exporter": "~1.2 || ~2.0" - }, - "require-dev": { - "phpunit/phpunit": "~4.4" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.2.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Jeff Welch", - "email": "whatthejeff@gmail.com" - }, - { - "name": "Volker Dusch", - "email": "github@wallbash.com" - }, - { - "name": "Bernhard Schussek", - "email": "bschussek@2bepublished.at" - }, - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Provides the functionality to compare PHP values for equality", - "homepage": "http://www.github.com/sebastianbergmann/comparator", - "keywords": [ - "comparator", - "compare", - "equality" - ], - "time": "2017-01-29T09:50:25+00:00" - }, - { - "name": "sebastian/diff", - "version": "1.4.3", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/diff.git", - "reference": "7f066a26a962dbe58ddea9f72a4e82874a3975a4" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/7f066a26a962dbe58ddea9f72a4e82874a3975a4", - "reference": "7f066a26a962dbe58ddea9f72a4e82874a3975a4", - "shasum": "" - }, - "require": { - "php": "^5.3.3 || ^7.0" - }, - "require-dev": { - "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.4-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Kore Nordmann", - "email": "mail@kore-nordmann.de" - }, - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Diff implementation", - "homepage": "https://github.com/sebastianbergmann/diff", - "keywords": [ - "diff" - ], - "time": "2017-05-22T07:24:03+00:00" - }, - { - "name": "sebastian/environment", - "version": "2.0.0", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/environment.git", - "reference": "5795ffe5dc5b02460c3e34222fee8cbe245d8fac" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/5795ffe5dc5b02460c3e34222fee8cbe245d8fac", - "reference": "5795ffe5dc5b02460c3e34222fee8cbe245d8fac", - "shasum": "" - }, - "require": { - "php": "^5.6 || ^7.0" - }, - "require-dev": { - "phpunit/phpunit": "^5.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Provides functionality to handle HHVM/PHP environments", - "homepage": "http://www.github.com/sebastianbergmann/environment", - "keywords": [ - "Xdebug", - "environment", - "hhvm" - ], - "time": "2016-11-26T07:53:53+00:00" - }, - { - "name": "sebastian/exporter", - "version": "2.0.0", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "ce474bdd1a34744d7ac5d6aad3a46d48d9bac4c4" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/ce474bdd1a34744d7ac5d6aad3a46d48d9bac4c4", - "reference": "ce474bdd1a34744d7ac5d6aad3a46d48d9bac4c4", - "shasum": "" - }, - "require": { - "php": ">=5.3.3", - "sebastian/recursion-context": "~2.0" - }, - "require-dev": { - "ext-mbstring": "*", - "phpunit/phpunit": "~4.4" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Jeff Welch", - "email": "whatthejeff@gmail.com" - }, - { - "name": "Volker Dusch", - "email": "github@wallbash.com" - }, - { - "name": "Bernhard Schussek", - "email": "bschussek@2bepublished.at" - }, - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - }, - { - "name": "Adam Harvey", - "email": "aharvey@php.net" - } - ], - "description": "Provides the functionality to export PHP variables for visualization", - "homepage": "http://www.github.com/sebastianbergmann/exporter", - "keywords": [ - "export", - "exporter" - ], - "time": "2016-11-19T08:54:04+00:00" - }, - { - "name": "sebastian/global-state", - "version": "1.1.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/global-state.git", - "reference": "bc37d50fea7d017d3d340f230811c9f1d7280af4" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/bc37d50fea7d017d3d340f230811c9f1d7280af4", - "reference": "bc37d50fea7d017d3d340f230811c9f1d7280af4", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "require-dev": { - "phpunit/phpunit": "~4.2" - }, - "suggest": { - "ext-uopz": "*" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Snapshotting of global state", - "homepage": "http://www.github.com/sebastianbergmann/global-state", - "keywords": [ - "global state" - ], - "time": "2015-10-12T03:26:01+00:00" - }, - { - "name": "sebastian/object-enumerator", - "version": "2.0.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/object-enumerator.git", - "reference": "1311872ac850040a79c3c058bea3e22d0f09cbb7" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/1311872ac850040a79c3c058bea3e22d0f09cbb7", - "reference": "1311872ac850040a79c3c058bea3e22d0f09cbb7", - "shasum": "" - }, - "require": { - "php": ">=5.6", - "sebastian/recursion-context": "~2.0" - }, - "require-dev": { - "phpunit/phpunit": "~5" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Traverses array structures and object graphs to enumerate all referenced objects", - "homepage": "https://github.com/sebastianbergmann/object-enumerator/", - "time": "2017-02-18T15:18:39+00:00" - }, - { - "name": "sebastian/recursion-context", - "version": "2.0.0", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/recursion-context.git", - "reference": "2c3ba150cbec723aa057506e73a8d33bdb286c9a" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/2c3ba150cbec723aa057506e73a8d33bdb286c9a", - "reference": "2c3ba150cbec723aa057506e73a8d33bdb286c9a", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "require-dev": { - "phpunit/phpunit": "~4.4" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Jeff Welch", - "email": "whatthejeff@gmail.com" - }, - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - }, - { - "name": "Adam Harvey", - "email": "aharvey@php.net" - } - ], - "description": "Provides functionality to recursively process PHP variables", - "homepage": "http://www.github.com/sebastianbergmann/recursion-context", - "time": "2016-11-19T07:33:16+00:00" - }, - { - "name": "sebastian/resource-operations", - "version": "1.0.0", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/resource-operations.git", - "reference": "ce990bb21759f94aeafd30209e8cfcdfa8bc3f52" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/ce990bb21759f94aeafd30209e8cfcdfa8bc3f52", - "reference": "ce990bb21759f94aeafd30209e8cfcdfa8bc3f52", - "shasum": "" - }, - "require": { - "php": ">=5.6.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Provides a list of PHP built-in functions that operate on resources", - "homepage": "https://www.github.com/sebastianbergmann/resource-operations", - "time": "2015-07-28T20:34:47+00:00" - }, - { - "name": "sebastian/version", - "version": "2.0.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/version.git", - "reference": "99732be0ddb3361e16ad77b68ba41efc8e979019" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/99732be0ddb3361e16ad77b68ba41efc8e979019", - "reference": "99732be0ddb3361e16ad77b68ba41efc8e979019", - "shasum": "" - }, - "require": { - "php": ">=5.6" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Library that helps with managing the version number of Git-hosted PHP projects", - "homepage": "https://github.com/sebastianbergmann/version", - "time": "2016-10-03T07:35:21+00:00" - }, - { - "name": "statonlab/tripal-test-suite", - "version": "1.1.2", - "source": { - "type": "git", - "url": "https://github.com/statonlab/TripalTestSuite.git", - "reference": "ca85284fc1da9c9f2863353dcb6eefe6d4b39343" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/statonlab/TripalTestSuite/zipball/ca85284fc1da9c9f2863353dcb6eefe6d4b39343", - "reference": "ca85284fc1da9c9f2863353dcb6eefe6d4b39343", - "shasum": "" - }, - "require": { - "fzaninotto/faker": "^1.7", - "guzzlehttp/guzzle": "^6.3", - "phpunit/phpunit": "^5 || ^6 || ^7.0", - "symfony/console": "^3 || ^4.0" - }, - "bin": [ - "tripaltest" - ], - "type": "library", - "autoload": { - "psr-4": { - "StatonLab\\TripalTestSuite\\": "src/" - }, - "files": [ - "src/Helpers/helpers.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "GPL-3.0" - ], - "authors": [ - { - "name": "Abdullah Almsaeed", - "email": "aalmsaee@utk.edu" - }, - { - "name": "Bradford Condon", - "email": "bcondon@utk.edu" - } - ], - "time": "2018-06-28T17:01:37+00:00" - }, - { - "name": "symfony/console", - "version": "v3.4.12", - "source": { - "type": "git", - "url": "https://github.com/symfony/console.git", - "reference": "1b97071a26d028c9bd4588264e101e14f6e7cd00" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/1b97071a26d028c9bd4588264e101e14f6e7cd00", - "reference": "1b97071a26d028c9bd4588264e101e14f6e7cd00", - "shasum": "" - }, - "require": { - "php": "^5.5.9|>=7.0.8", - "symfony/debug": "~2.8|~3.0|~4.0", - "symfony/polyfill-mbstring": "~1.0" - }, - "conflict": { - "symfony/dependency-injection": "<3.4", - "symfony/process": "<3.3" - }, - "require-dev": { - "psr/log": "~1.0", - "symfony/config": "~3.3|~4.0", - "symfony/dependency-injection": "~3.4|~4.0", - "symfony/event-dispatcher": "~2.8|~3.0|~4.0", - "symfony/lock": "~3.4|~4.0", - "symfony/process": "~3.3|~4.0" - }, - "suggest": { - "psr/log-implementation": "For using the console logger", - "symfony/event-dispatcher": "", - "symfony/lock": "", - "symfony/process": "" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.4-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Component\\Console\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony Console Component", - "homepage": "https://symfony.com", - "time": "2018-05-23T05:02:55+00:00" - }, - { - "name": "symfony/debug", - "version": "v3.4.12", - "source": { - "type": "git", - "url": "https://github.com/symfony/debug.git", - "reference": "47e6788c5b151cf0cfdf3329116bf33800632d75" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/debug/zipball/47e6788c5b151cf0cfdf3329116bf33800632d75", - "reference": "47e6788c5b151cf0cfdf3329116bf33800632d75", - "shasum": "" - }, - "require": { - "php": "^5.5.9|>=7.0.8", - "psr/log": "~1.0" - }, - "conflict": { - "symfony/http-kernel": ">=2.3,<2.3.24|~2.4.0|>=2.5,<2.5.9|>=2.6,<2.6.2" - }, - "require-dev": { - "symfony/http-kernel": "~2.8|~3.0|~4.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.4-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Component\\Debug\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony Debug Component", - "homepage": "https://symfony.com", - "time": "2018-06-25T11:10:40+00:00" - }, - { - "name": "symfony/polyfill-ctype", - "version": "v1.8.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "7cc359f1b7b80fc25ed7796be7d96adc9b354bae" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/7cc359f1b7b80fc25ed7796be7d96adc9b354bae", - "reference": "7cc359f1b7b80fc25ed7796be7d96adc9b354bae", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.8-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Polyfill\\Ctype\\": "" - }, - "files": [ - "bootstrap.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - }, - { - "name": "Gert de Pagter", - "email": "BackEndTea@gmail.com" - } - ], - "description": "Symfony polyfill for ctype functions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "ctype", - "polyfill", - "portable" - ], - "time": "2018-04-30T19:57:29+00:00" - }, - { - "name": "symfony/polyfill-mbstring", - "version": "v1.8.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "3296adf6a6454a050679cde90f95350ad604b171" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/3296adf6a6454a050679cde90f95350ad604b171", - "reference": "3296adf6a6454a050679cde90f95350ad604b171", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "suggest": { - "ext-mbstring": "For best performance" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.8-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Polyfill\\Mbstring\\": "" - }, - "files": [ - "bootstrap.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill for the Mbstring extension", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "mbstring", - "polyfill", - "portable", - "shim" - ], - "time": "2018-04-26T10:06:28+00:00" - }, - { - "name": "symfony/yaml", - "version": "v3.4.12", - "source": { - "type": "git", - "url": "https://github.com/symfony/yaml.git", - "reference": "c5010cc1692ce1fa328b1fb666961eb3d4a85bb0" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/c5010cc1692ce1fa328b1fb666961eb3d4a85bb0", - "reference": "c5010cc1692ce1fa328b1fb666961eb3d4a85bb0", - "shasum": "" - }, - "require": { - "php": "^5.5.9|>=7.0.8", - "symfony/polyfill-ctype": "~1.8" - }, - "conflict": { - "symfony/console": "<3.4" - }, - "require-dev": { - "symfony/console": "~3.4|~4.0" - }, - "suggest": { - "symfony/console": "For validating YAML files using the lint command" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.4-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Component\\Yaml\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony Yaml Component", - "homepage": "https://symfony.com", - "time": "2018-05-03T23:18:14+00:00" - }, - { - "name": "webmozart/assert", - "version": "1.3.0", - "source": { - "type": "git", - "url": "https://github.com/webmozart/assert.git", - "reference": "0df1908962e7a3071564e857d86874dad1ef204a" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/webmozart/assert/zipball/0df1908962e7a3071564e857d86874dad1ef204a", - "reference": "0df1908962e7a3071564e857d86874dad1ef204a", - "shasum": "" - }, - "require": { - "php": "^5.3.3 || ^7.0" - }, - "require-dev": { - "phpunit/phpunit": "^4.6", - "sebastian/version": "^1.0.1" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.3-dev" - } - }, - "autoload": { - "psr-4": { - "Webmozart\\Assert\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Bernhard Schussek", - "email": "bschussek@gmail.com" - } - ], - "description": "Assertions to validate method input/output with nice error messages.", - "keywords": [ - "assert", - "check", - "validate" - ], - "time": "2018-01-29T19:49:41+00:00" - } - ], - "aliases": [], - "minimum-stability": "stable", - "stability-flags": [], - "prefer-stable": false, - "prefer-lowest": false, - "platform": [], - "platform-dev": [] -} diff --git a/config/install/rawphenotypes.settings.yml b/config/install/rawphenotypes.settings.yml new file mode 100644 index 0000000..4d060aa --- /dev/null +++ b/config/install/rawphenotypes.settings.yml @@ -0,0 +1,17 @@ +# @file +# Raw Phenotypes Module variables/configuration. + +# GENERAL CONFIGURATIONS: + +# Colour scheme and window title text. +rawpheno_colour_scheme: +rawpheno_rawdata_title: +rawpheno_download_title: +rawpheno_instructions_title: +rawpheno_upload_title: +rawpheno_backup_title: + +# R transformation parameters. +rawpheno_rtransform_words: +rawpheno_rtransform_characters: +rawpheno_rtransform_replace: \ No newline at end of file diff --git a/css/jquery-ui.min.css b/css/jquery-ui.min.css new file mode 100644 index 0000000..890ea64 --- /dev/null +++ b/css/jquery-ui.min.css @@ -0,0 +1,7 @@ +/*! jQuery UI - v1.12.1 - 2021-08-13 +* http://jqueryui.com +* Includes: core.css, tabs.css, theme.css +* To view and modify this theme, visit http://jqueryui.com/themeroller/?scope=&folderName=base&cornerRadiusShadow=8px&offsetLeftShadow=0px&offsetTopShadow=0px&thicknessShadow=5px&opacityShadow=30&bgImgOpacityShadow=0&bgTextureShadow=flat&bgColorShadow=666666&opacityOverlay=30&bgImgOpacityOverlay=0&bgTextureOverlay=flat&bgColorOverlay=aaaaaa&iconColorError=cc0000&fcError=5f3f3f&borderColorError=f1a899&bgTextureError=flat&bgColorError=fddfdf&iconColorHighlight=777620&fcHighlight=777620&borderColorHighlight=dad55e&bgTextureHighlight=flat&bgColorHighlight=fffa90&iconColorActive=ffffff&fcActive=ffffff&borderColorActive=003eff&bgTextureActive=flat&bgColorActive=007fff&iconColorHover=555555&fcHover=2b2b2b&borderColorHover=cccccc&bgTextureHover=flat&bgColorHover=ededed&iconColorDefault=777777&fcDefault=454545&borderColorDefault=c5c5c5&bgTextureDefault=flat&bgColorDefault=f6f6f6&iconColorContent=444444&fcContent=333333&borderColorContent=dddddd&bgTextureContent=flat&bgColorContent=ffffff&iconColorHeader=444444&fcHeader=333333&borderColorHeader=dddddd&bgTextureHeader=flat&bgColorHeader=e9e9e9&cornerRadius=3px&fwDefault=normal&fsDefault=1em&ffDefault=Arial%2CHelvetica%2Csans-serif +* Copyright jQuery Foundation and other contributors; Licensed MIT */ + +.ui-helper-hidden{display:none}.ui-helper-hidden-accessible{border:0;clip:rect(0 0 0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.ui-helper-reset{margin:0;padding:0;border:0;outline:0;line-height:1.3;text-decoration:none;font-size:100%;list-style:none}.ui-helper-clearfix:before,.ui-helper-clearfix:after{content:"";display:table;border-collapse:collapse}.ui-helper-clearfix:after{clear:both}.ui-helper-zfix{width:100%;height:100%;top:0;left:0;position:absolute;opacity:0;filter:Alpha(Opacity=0)}.ui-front{z-index:100}.ui-state-disabled{cursor:default!important;pointer-events:none}.ui-icon{display:inline-block;vertical-align:middle;margin-top:-.25em;position:relative;text-indent:-99999px;overflow:hidden;background-repeat:no-repeat}.ui-widget-icon-block{left:50%;margin-left:-8px;display:block}.ui-widget-overlay{position:fixed;top:0;left:0;width:100%;height:100%}.ui-tabs{position:relative;padding:.2em}.ui-tabs .ui-tabs-nav{margin:0;padding:.2em .2em 0}.ui-tabs .ui-tabs-nav li{list-style:none;float:left;position:relative;top:0;margin:1px .2em 0 0;border-bottom-width:0;padding:0;white-space:nowrap}.ui-tabs .ui-tabs-nav .ui-tabs-anchor{float:left;padding:.5em 1em;text-decoration:none}.ui-tabs .ui-tabs-nav li.ui-tabs-active{margin-bottom:-1px;padding-bottom:1px}.ui-tabs .ui-tabs-nav li.ui-tabs-active .ui-tabs-anchor,.ui-tabs .ui-tabs-nav li.ui-state-disabled .ui-tabs-anchor,.ui-tabs .ui-tabs-nav li.ui-tabs-loading .ui-tabs-anchor{cursor:text}.ui-tabs-collapsible .ui-tabs-nav li.ui-tabs-active .ui-tabs-anchor{cursor:pointer}.ui-tabs .ui-tabs-panel{display:block;border-width:0;padding:1em 1.4em;background:none}.ui-widget{font-family:Arial,Helvetica,sans-serif;font-size:1em}.ui-widget .ui-widget{font-size:1em}.ui-widget input,.ui-widget select,.ui-widget textarea,.ui-widget button{font-family:Arial,Helvetica,sans-serif;font-size:1em}.ui-widget.ui-widget-content{border:1px solid #c5c5c5}.ui-widget-content{border:1px solid #ddd;background:#fff;color:#333}.ui-widget-content a{color:#333}.ui-widget-header{border:1px solid #ddd;background:#e9e9e9;color:#333;font-weight:bold}.ui-widget-header a{color:#333}.ui-state-default,.ui-widget-content .ui-state-default,.ui-widget-header .ui-state-default,.ui-button,html .ui-button.ui-state-disabled:hover,html .ui-button.ui-state-disabled:active{border:1px solid #c5c5c5;background:#f6f6f6;font-weight:normal;color:#454545}.ui-state-default a,.ui-state-default a:link,.ui-state-default a:visited,a.ui-button,a:link.ui-button,a:visited.ui-button,.ui-button{color:#454545;text-decoration:none}.ui-state-hover,.ui-widget-content .ui-state-hover,.ui-widget-header .ui-state-hover,.ui-state-focus,.ui-widget-content .ui-state-focus,.ui-widget-header .ui-state-focus,.ui-button:hover,.ui-button:focus{border:1px solid #ccc;background:#ededed;font-weight:normal;color:#2b2b2b}.ui-state-hover a,.ui-state-hover a:hover,.ui-state-hover a:link,.ui-state-hover a:visited,.ui-state-focus a,.ui-state-focus a:hover,.ui-state-focus a:link,.ui-state-focus a:visited,a.ui-button:hover,a.ui-button:focus{color:#2b2b2b;text-decoration:none}.ui-visual-focus{box-shadow:0 0 3px 1px rgb(94,158,214)}.ui-state-active,.ui-widget-content .ui-state-active,.ui-widget-header .ui-state-active,a.ui-button:active,.ui-button:active,.ui-button.ui-state-active:hover{border:1px solid #003eff;background:#007fff;font-weight:normal;color:#fff}.ui-icon-background,.ui-state-active .ui-icon-background{border:#003eff;background-color:#fff}.ui-state-active a,.ui-state-active a:link,.ui-state-active a:visited{color:#fff;text-decoration:none}.ui-state-highlight,.ui-widget-content .ui-state-highlight,.ui-widget-header .ui-state-highlight{border:1px solid #dad55e;background:#fffa90;color:#777620}.ui-state-checked{border:1px solid #dad55e;background:#fffa90}.ui-state-highlight a,.ui-widget-content .ui-state-highlight a,.ui-widget-header .ui-state-highlight a{color:#777620}.ui-state-error,.ui-widget-content .ui-state-error,.ui-widget-header .ui-state-error{border:1px solid #f1a899;background:#fddfdf;color:#5f3f3f}.ui-state-error a,.ui-widget-content .ui-state-error a,.ui-widget-header .ui-state-error a{color:#5f3f3f}.ui-state-error-text,.ui-widget-content .ui-state-error-text,.ui-widget-header .ui-state-error-text{color:#5f3f3f}.ui-priority-primary,.ui-widget-content .ui-priority-primary,.ui-widget-header .ui-priority-primary{font-weight:bold}.ui-priority-secondary,.ui-widget-content .ui-priority-secondary,.ui-widget-header .ui-priority-secondary{opacity:.7;filter:Alpha(Opacity=70);font-weight:normal}.ui-state-disabled,.ui-widget-content .ui-state-disabled,.ui-widget-header .ui-state-disabled{opacity:.35;filter:Alpha(Opacity=35);background-image:none}.ui-state-disabled .ui-icon{filter:Alpha(Opacity=35)}.ui-icon{width:16px;height:16px}.ui-icon,.ui-widget-content .ui-icon{background-image:url("images/ui-icons_444444_256x240.png")}.ui-widget-header .ui-icon{background-image:url("images/ui-icons_444444_256x240.png")}.ui-state-hover .ui-icon,.ui-state-focus .ui-icon,.ui-button:hover .ui-icon,.ui-button:focus .ui-icon{background-image:url("images/ui-icons_555555_256x240.png")}.ui-state-active .ui-icon,.ui-button:active .ui-icon{background-image:url("images/ui-icons_ffffff_256x240.png")}.ui-state-highlight .ui-icon,.ui-button .ui-state-highlight.ui-icon{background-image:url("images/ui-icons_777620_256x240.png")}.ui-state-error .ui-icon,.ui-state-error-text .ui-icon{background-image:url("images/ui-icons_cc0000_256x240.png")}.ui-button .ui-icon{background-image:url("images/ui-icons_777777_256x240.png")}.ui-icon-blank{background-position:16px 16px}.ui-icon-caret-1-n{background-position:0 0}.ui-icon-caret-1-ne{background-position:-16px 0}.ui-icon-caret-1-e{background-position:-32px 0}.ui-icon-caret-1-se{background-position:-48px 0}.ui-icon-caret-1-s{background-position:-65px 0}.ui-icon-caret-1-sw{background-position:-80px 0}.ui-icon-caret-1-w{background-position:-96px 0}.ui-icon-caret-1-nw{background-position:-112px 0}.ui-icon-caret-2-n-s{background-position:-128px 0}.ui-icon-caret-2-e-w{background-position:-144px 0}.ui-icon-triangle-1-n{background-position:0 -16px}.ui-icon-triangle-1-ne{background-position:-16px -16px}.ui-icon-triangle-1-e{background-position:-32px -16px}.ui-icon-triangle-1-se{background-position:-48px -16px}.ui-icon-triangle-1-s{background-position:-65px -16px}.ui-icon-triangle-1-sw{background-position:-80px -16px}.ui-icon-triangle-1-w{background-position:-96px -16px}.ui-icon-triangle-1-nw{background-position:-112px -16px}.ui-icon-triangle-2-n-s{background-position:-128px -16px}.ui-icon-triangle-2-e-w{background-position:-144px -16px}.ui-icon-arrow-1-n{background-position:0 -32px}.ui-icon-arrow-1-ne{background-position:-16px -32px}.ui-icon-arrow-1-e{background-position:-32px -32px}.ui-icon-arrow-1-se{background-position:-48px -32px}.ui-icon-arrow-1-s{background-position:-65px -32px}.ui-icon-arrow-1-sw{background-position:-80px -32px}.ui-icon-arrow-1-w{background-position:-96px -32px}.ui-icon-arrow-1-nw{background-position:-112px -32px}.ui-icon-arrow-2-n-s{background-position:-128px -32px}.ui-icon-arrow-2-ne-sw{background-position:-144px -32px}.ui-icon-arrow-2-e-w{background-position:-160px -32px}.ui-icon-arrow-2-se-nw{background-position:-176px -32px}.ui-icon-arrowstop-1-n{background-position:-192px -32px}.ui-icon-arrowstop-1-e{background-position:-208px -32px}.ui-icon-arrowstop-1-s{background-position:-224px -32px}.ui-icon-arrowstop-1-w{background-position:-240px -32px}.ui-icon-arrowthick-1-n{background-position:1px -48px}.ui-icon-arrowthick-1-ne{background-position:-16px -48px}.ui-icon-arrowthick-1-e{background-position:-32px -48px}.ui-icon-arrowthick-1-se{background-position:-48px -48px}.ui-icon-arrowthick-1-s{background-position:-64px -48px}.ui-icon-arrowthick-1-sw{background-position:-80px -48px}.ui-icon-arrowthick-1-w{background-position:-96px -48px}.ui-icon-arrowthick-1-nw{background-position:-112px -48px}.ui-icon-arrowthick-2-n-s{background-position:-128px -48px}.ui-icon-arrowthick-2-ne-sw{background-position:-144px -48px}.ui-icon-arrowthick-2-e-w{background-position:-160px -48px}.ui-icon-arrowthick-2-se-nw{background-position:-176px -48px}.ui-icon-arrowthickstop-1-n{background-position:-192px -48px}.ui-icon-arrowthickstop-1-e{background-position:-208px -48px}.ui-icon-arrowthickstop-1-s{background-position:-224px -48px}.ui-icon-arrowthickstop-1-w{background-position:-240px -48px}.ui-icon-arrowreturnthick-1-w{background-position:0 -64px}.ui-icon-arrowreturnthick-1-n{background-position:-16px -64px}.ui-icon-arrowreturnthick-1-e{background-position:-32px -64px}.ui-icon-arrowreturnthick-1-s{background-position:-48px -64px}.ui-icon-arrowreturn-1-w{background-position:-64px -64px}.ui-icon-arrowreturn-1-n{background-position:-80px -64px}.ui-icon-arrowreturn-1-e{background-position:-96px -64px}.ui-icon-arrowreturn-1-s{background-position:-112px -64px}.ui-icon-arrowrefresh-1-w{background-position:-128px -64px}.ui-icon-arrowrefresh-1-n{background-position:-144px -64px}.ui-icon-arrowrefresh-1-e{background-position:-160px -64px}.ui-icon-arrowrefresh-1-s{background-position:-176px -64px}.ui-icon-arrow-4{background-position:0 -80px}.ui-icon-arrow-4-diag{background-position:-16px -80px}.ui-icon-extlink{background-position:-32px -80px}.ui-icon-newwin{background-position:-48px -80px}.ui-icon-refresh{background-position:-64px -80px}.ui-icon-shuffle{background-position:-80px -80px}.ui-icon-transfer-e-w{background-position:-96px -80px}.ui-icon-transferthick-e-w{background-position:-112px -80px}.ui-icon-folder-collapsed{background-position:0 -96px}.ui-icon-folder-open{background-position:-16px -96px}.ui-icon-document{background-position:-32px -96px}.ui-icon-document-b{background-position:-48px -96px}.ui-icon-note{background-position:-64px -96px}.ui-icon-mail-closed{background-position:-80px -96px}.ui-icon-mail-open{background-position:-96px -96px}.ui-icon-suitcase{background-position:-112px -96px}.ui-icon-comment{background-position:-128px -96px}.ui-icon-person{background-position:-144px -96px}.ui-icon-print{background-position:-160px -96px}.ui-icon-trash{background-position:-176px -96px}.ui-icon-locked{background-position:-192px -96px}.ui-icon-unlocked{background-position:-208px -96px}.ui-icon-bookmark{background-position:-224px -96px}.ui-icon-tag{background-position:-240px -96px}.ui-icon-home{background-position:0 -112px}.ui-icon-flag{background-position:-16px -112px}.ui-icon-calendar{background-position:-32px -112px}.ui-icon-cart{background-position:-48px -112px}.ui-icon-pencil{background-position:-64px -112px}.ui-icon-clock{background-position:-80px -112px}.ui-icon-disk{background-position:-96px -112px}.ui-icon-calculator{background-position:-112px -112px}.ui-icon-zoomin{background-position:-128px -112px}.ui-icon-zoomout{background-position:-144px -112px}.ui-icon-search{background-position:-160px -112px}.ui-icon-wrench{background-position:-176px -112px}.ui-icon-gear{background-position:-192px -112px}.ui-icon-heart{background-position:-208px -112px}.ui-icon-star{background-position:-224px -112px}.ui-icon-link{background-position:-240px -112px}.ui-icon-cancel{background-position:0 -128px}.ui-icon-plus{background-position:-16px -128px}.ui-icon-plusthick{background-position:-32px -128px}.ui-icon-minus{background-position:-48px -128px}.ui-icon-minusthick{background-position:-64px -128px}.ui-icon-close{background-position:-80px -128px}.ui-icon-closethick{background-position:-96px -128px}.ui-icon-key{background-position:-112px -128px}.ui-icon-lightbulb{background-position:-128px -128px}.ui-icon-scissors{background-position:-144px -128px}.ui-icon-clipboard{background-position:-160px -128px}.ui-icon-copy{background-position:-176px -128px}.ui-icon-contact{background-position:-192px -128px}.ui-icon-image{background-position:-208px -128px}.ui-icon-video{background-position:-224px -128px}.ui-icon-script{background-position:-240px -128px}.ui-icon-alert{background-position:0 -144px}.ui-icon-info{background-position:-16px -144px}.ui-icon-notice{background-position:-32px -144px}.ui-icon-help{background-position:-48px -144px}.ui-icon-check{background-position:-64px -144px}.ui-icon-bullet{background-position:-80px -144px}.ui-icon-radio-on{background-position:-96px -144px}.ui-icon-radio-off{background-position:-112px -144px}.ui-icon-pin-w{background-position:-128px -144px}.ui-icon-pin-s{background-position:-144px -144px}.ui-icon-play{background-position:0 -160px}.ui-icon-pause{background-position:-16px -160px}.ui-icon-seek-next{background-position:-32px -160px}.ui-icon-seek-prev{background-position:-48px -160px}.ui-icon-seek-end{background-position:-64px -160px}.ui-icon-seek-start{background-position:-80px -160px}.ui-icon-seek-first{background-position:-80px -160px}.ui-icon-stop{background-position:-96px -160px}.ui-icon-eject{background-position:-112px -160px}.ui-icon-volume-off{background-position:-128px -160px}.ui-icon-volume-on{background-position:-144px -160px}.ui-icon-power{background-position:0 -176px}.ui-icon-signal-diag{background-position:-16px -176px}.ui-icon-signal{background-position:-32px -176px}.ui-icon-battery-0{background-position:-48px -176px}.ui-icon-battery-1{background-position:-64px -176px}.ui-icon-battery-2{background-position:-80px -176px}.ui-icon-battery-3{background-position:-96px -176px}.ui-icon-circle-plus{background-position:0 -192px}.ui-icon-circle-minus{background-position:-16px -192px}.ui-icon-circle-close{background-position:-32px -192px}.ui-icon-circle-triangle-e{background-position:-48px -192px}.ui-icon-circle-triangle-s{background-position:-64px -192px}.ui-icon-circle-triangle-w{background-position:-80px -192px}.ui-icon-circle-triangle-n{background-position:-96px -192px}.ui-icon-circle-arrow-e{background-position:-112px -192px}.ui-icon-circle-arrow-s{background-position:-128px -192px}.ui-icon-circle-arrow-w{background-position:-144px -192px}.ui-icon-circle-arrow-n{background-position:-160px -192px}.ui-icon-circle-zoomin{background-position:-176px -192px}.ui-icon-circle-zoomout{background-position:-192px -192px}.ui-icon-circle-check{background-position:-208px -192px}.ui-icon-circlesmall-plus{background-position:0 -208px}.ui-icon-circlesmall-minus{background-position:-16px -208px}.ui-icon-circlesmall-close{background-position:-32px -208px}.ui-icon-squaresmall-plus{background-position:-48px -208px}.ui-icon-squaresmall-minus{background-position:-64px -208px}.ui-icon-squaresmall-close{background-position:-80px -208px}.ui-icon-grip-dotted-vertical{background-position:0 -224px}.ui-icon-grip-dotted-horizontal{background-position:-16px -224px}.ui-icon-grip-solid-vertical{background-position:-32px -224px}.ui-icon-grip-solid-horizontal{background-position:-48px -224px}.ui-icon-gripsmall-diagonal-se{background-position:-64px -224px}.ui-icon-grip-diagonal-se{background-position:-80px -224px}.ui-corner-all,.ui-corner-top,.ui-corner-left,.ui-corner-tl{border-top-left-radius:3px}.ui-corner-all,.ui-corner-top,.ui-corner-right,.ui-corner-tr{border-top-right-radius:3px}.ui-corner-all,.ui-corner-bottom,.ui-corner-left,.ui-corner-bl{border-bottom-left-radius:3px}.ui-corner-all,.ui-corner-bottom,.ui-corner-right,.ui-corner-br{border-bottom-right-radius:3px}.ui-widget-overlay{background:#aaa;opacity:.3;filter:Alpha(Opacity=30)}.ui-widget-shadow{-webkit-box-shadow:0 0 5px #666;box-shadow:0 0 5px #666} \ No newline at end of file diff --git a/theme/css/rawpheno.admin.style.css b/css/style-admin.css similarity index 92% rename from theme/css/rawpheno.admin.style.css rename to css/style-admin.css index e6e894f..76488f9 100755 --- a/theme/css/rawpheno.admin.style.css +++ b/css/style-admin.css @@ -135,6 +135,10 @@ div.form-item-txt-trait-rfriendly input[type=text] { width: 370px; } +div.form-item-txt-trait-rfriendly { + padding-top: 10px; +} + /* All submit buttons */ input[type=submit] { margin-top: 20px; @@ -143,7 +147,8 @@ input[type=submit] { /* Select a project in main project management control panel */ #admin-sel-project { - width: 100%; + width: 80%; + height: 28px; } /* Add project button */ @@ -175,7 +180,7 @@ a.link-view { /* R version icon */ p.r-ver { - background: url('../img/r.gif') no-repeat; + background: url('../images/r.gif') no-repeat; background-size: 16px 14px; font-size: 0.9em; padding: 0 20px; diff --git a/theme/css/rawpheno.backup.style.css b/css/style-page-backup.css similarity index 75% rename from theme/css/rawpheno.backup.style.css rename to css/style-page-backup.css index 63af578..c12e687 100755 --- a/theme/css/rawpheno.backup.style.css +++ b/css/style-page-backup.css @@ -4,26 +4,161 @@ */ /* Import global css */ -@import 'rawpheno.style.css'; +@import 'style-page.css'; + /* Page icon */ .container-subtitle { - background: url('../img/icon-backup.gif') no-repeat left top; + background: url('../images/icon-backup.gif') no-repeat left top; background-position: 0 5px; } - .container-contents { background-color: #FFFFFF; padding: 0; + position: relative; } +#container-backup, #container-add-file { + position: relative; +} /* Upper left text */ div.subtitle-left { font-size: 2.2em; line-height: 35px; } + +/* Submit form */ +#backup-submit-form { + position: absolute; + right: 7%; +} + +/* File input */ +#field-file { + width: 0.1px; + height: 0.1px; + opacity: 0; + overflow: hidden; + position: absolute; + z-index: -1; +} + +/* File input label */ +.drop-zone label { + color: #666666; + font-size: 1em; + font-weight: 300; + display: block; + line-height: normal; + margin: 0 auto; + position: relative; + text-align: center; + top: 140px; + width: 480px; +} + +.drop-zone label strong { + font-weight: 300; +} + +.drop-zone label span { + font-weight: 300; + display: block; + text-decoration: underline; +} + +/* Drop zone actions */ +.drop-zone.is-dragover { + border-style: dashed; +} + +.drop-zone.has-dragdrop-upload { + background-color: #FCFDFD; +} + +.dragdrop-text { + display: none; +} + +.drop-zone.has-dragdrop-upload .dragdrop-text { + display: block; +} + +.drop-zone #drop-zone-file { + text-align: center; + width: inherit; + top: 20px; + position: absolute; +} + +.drop-zone #drop-zone-remove-file { + margin: 0 5px; +} + +/* Drop Zone */ +.drop-zone { + background: url('../images/icon-excel.gif') no-repeat top center; + background-position: 50% 90px; + background-color: #FFFFFF; + border: 2px double #AAAAAA; + + -moz-box-shadow: 1px 1px 5px 3px #EAEAEA; + -webkit-box-shadow: 1px 1px 5px 3px #EAEAEA; + box-shadow: inset 1px 1px 5px 3px #EAEAEA; + + height: 250px; + margin: 0 0 15px 0; + padding: 0; + position: relative; + width: 99.7%; +} + +/* Drop zone error message */ +.dragdrop-message { + color: red; + font-size: 0.9em; + position: absolute; + font-family: Arial, Helvetica, sans-serif; +} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + /* Add File */ @@ -37,7 +172,7 @@ div.subtitle-left { /* (DnD) File dropzone */ #droppable-bdnd { - background: url('../../theme/img/icon-excel.gif') no-repeat top center; + background: url('../images/icon-excel.gif') no-repeat top center; background-position: 50% 100px; background-color: #FCFDFD; border: 2px double #AAAAAA; diff --git a/theme/css/rawpheno.instructions.style.css b/css/style-page-instructions.css similarity index 78% rename from theme/css/rawpheno.instructions.style.css rename to css/style-page-instructions.css index 9b89dff..1ee13d6 100755 --- a/theme/css/rawpheno.instructions.style.css +++ b/css/style-page-instructions.css @@ -1,377 +1,412 @@ - -/** - * @file - * Style tabs in phenotypes/instructions page. - */ - -/* Import global css */ -@import 'rawpheno.style.css'; - -/* Page icon */ -.container-subtitle { - background: url('../img/icon-instructions.gif') no-repeat left top; - background-position: 0 5px; -} - -.container-contents { - background-color: #FFFFFF; - padding: 0; - text-align: left; -} - -em { - font-weight: bold; -} - -/* Autocomplete search field */ -#edit-txt-search { - background-position: 98% center; - font-family: sans-serif; - font-size: 0.96em; - margin: -15px 0 0 0; - padding: 14px; - width: 355px; -} - -#autocomplete { - background-color: #FFFFFF; - border: 1px solid #CCCCCC; - font-size: 0.8em; - margin-top: -15px; -} - -#autocomplete li { - font-family: sans-serif; - padding: 2px 0 2px 5px; -} - -#btn_submit, #lnk-reset { - font-size: 0.90em; - position: absolute; -} - -#btn_submit { - background-image: url('../img/icon-lens.png'); - background-position: center; - background-repeat: no-repeat; - background-color: #EAEAEA; - height: 40px; - margin: -58px 0 0 395px; - padding-left: 20px; - padding-right: 20px; -} - -#btn_submit:hover { - background-color: #D5D5D5; -} - -#lnk-reset { - font-family: sans-serif; - margin: -42px 0 0 455px; -} - -/* Search Result Container */ -#container-search-result { - padding: 0; -} - -.search-on { - border-bottom: 1px solid #CCCCCC; - cursor: pointer; - padding: 0 0 10px 0; - margin: 0 0 20px 0; - - -moz-box-shadow: 0 5px 7px -9px #333333; - -webkit-box-shadow: 0 5px 7px -9px #333333; - box-shadow: 0 5px 7px -9px #333333; - text-align: left; -} - -#container-search-result table td, #lists table td { - vertical-align: top; -} - -#container-search-result p { - font-size: 1.1em; - margin: 20px 0; -} - -#container-search-result table { - border-bottom: 4px solid #314355; - margin-bottom: 40px; - - -moz-box-shadow: 1px 1px 5px 1px #CCCCCC; - -webkit-box-shadow: 1px 1px 5px 1px #CCCCCC; - box-shadow: 1px 1px 5px 1px #CCCCCC; -} - -/* Tabs */ -#tabs { - background: none; - border-width: 0; - padding: 0; -} - -#tabs .ui-tabs-nav li { - border-radius: 0; - background: #FCFDFD; -} - -#tabs .ui-tabs-nav { - background: transparent; - border-width: 0px 0px 1px 0px; - border-radius: 0px; - padding-left: 0px; - -moz-border-radius: 0px; - -webkit-border-radius: 0px; -} - -#tabs .ui-tabs-panel { - background: #FCFDFD; - border-width: 0px 1px 1px 1px; - border-radius: 0; - font-size: 0.8em; -} - -#tabs .ui-tabs-panel li p { - margin: 0; - padding: 2px; -} - -#tabs .ui-tabs-panel li p { - margin: 0; - padding: 2px; -} - -#tabs .ui-tabs-panel p a { - color: #13B72D; -} - -#tabs td, #lists td { - padding-top: 10px; - padding-bottom: 10px; - vertical-align: top; -} - -#tabs td em { - font-weight: bold; -} - -#tabs tr:nth-child(odd), #lists tr:nth-child(odd) { - background-color: #F7F7F7; -} - -#tabs .data-cells, #lists .data-cells, -#container-search-result table div:first-child { - font-weight: bold; - font-size: 1.1em; - margin: 0 auto; - padding: 2px 7px; - text-align: left; -} - -/* Tab 1 */ -div.ui-tabs-panel { - padding-left: 40px !important; - padding-right: 40px !important; -} - -#fragment-1 ul li { - margin: 2px 0; -} - -#fragment-1 p { - font-size: 1.2em; -} - -#fragment-1 p a { - color: #13b72d; -} - -/* All Tabs */ -.ui-tabs-nav li a { - height: 35px; - line-height: 30px; - font-size: 1em; -} - -a.button:hover { - background: #DEDEDE none repeat scroll 0% 0%; -} - -#essential a { - background-color: green; - color: #FFFFFF; -} - -#photo-appendix a { - background-color: #EAEAEA; -} - -/* photo appendix */ -#photo-container { - background-color: #EAEAEA; - padding: 30px 5px; - margin-top: 20px; - text-align: center; - width: 98%; - margin: 0 auto; -} - -#gallery-container { - display: inline-block; - margin: 0 auto; -} - -.side-nav { - position: absolute; - font-size: 2.5em; - top: 50%; - background-color: rgba(255, 255, 255, 0.2); - padding: 5px; -} - -.nav-go-left { - margin-left: 12px !important; -} - -.nav-go-right { - margin-left: 435px !important; -} - -.side-nav a:link { - color: #CCCCCC; - text-decoration: none; -} - -.side-nav:hover { - color: #000000; - text-decoration: none; - background-color: rgba(255, 255, 255, 0.8); -} - -.gallery-img { - float: left; - width: 470px; -} - -#gallery-container img { - border: 6px solid #FFFFFF; - padding: 0; -} - -.download-template-instructions { - font-style: italic; - font-weight:1.01em; - padding-bottom: 10px; -} - -/* Error message box */ -.messages { - margin: 0 0 15px 0 !important; -} - - -/* Table */ -th { - width: 30%; -} - - -/* Change project panels */ -#container-project-panel { - font-size: 1.7em; - margin: 0 0 20px 0; -} - -#container-project-panel span { - font-size: 0.6em; - float: right; - line-height: 25px; -} - -#container-sel-project { - border-bottom: 1px solid #CCCCCC; - cursor: pointer; - display: none; - margin: 20px 0; - padding: 0 0 15px 0; - - -moz-box-shadow: 0 5px 7px -9px #333333; - -webkit-box-shadow: 0 5px 7px -9px #333333; - box-shadow: 0 5px 7px -9px #333333; -} - -#container-sel-project select { - background: #FCFDFD url('../img/chrome-selectbox-down-arrow.gif') no-repeat; - background-position: 99.3% 50%; - border: 1px solid #AAAAAA; - padding: 7px; - font-size: 0.9em; - - -moz-box-shadow: 1px 1px 4px 3px #EAEAEA; - -webkit-box-shadow: 1px 1px 4px 3px #EAEAEA; - box-shadow: inset 1px 1px 4px 3px #EAEAEA; - - width: 100%; - -webkit-appearance: none; -} - -/* Reference Tab */ -#reference { - background-color: #EAEAEA !important; -} - -/* Resource Links */ -#container-resource-links { - padding: 0 30px; -} - -#container-resource-links a { - font-size: 0.99em; - color: #118324 !important; -} - -#container-resource-links a:hover { - color: #13b72d !important; - font-size: 0.99em; -} - -#container-resource-links div { - float: left; - height: auto; - margin: 15px 20px 0 0; - padding: 0; - width: 27%; -} - -#container-resource-links div:first-child { - background: url('../img/icon-instructions.gif') no-repeat left top; -} - -#container-resource-links div:nth-child(2) { - background: url('../img/icon-backup.gif') no-repeat left top; -} - -#container-resource-links div:nth-child(3) { - background: url('../img/icon-upload.gif') no-repeat left top; -} - -#container-resource-links h3 { - font-size: 1.5em; - margin: 0 0 30px 0; - padding: 0 0 0 55px; -} - -/* Fragment 5 select box: Topic */ -#fragment-6 select { - padding: 5px 15px 5px 10px; -} - -#tabs th { - padding: 10px !important; -} - -#tabs th, #tabs td { - font-size: 1.1em !important; -} +/** + * @file + * Style tabs in phenotypes/instructions page. + */ + +/* Import global css */ +@import 'style-page.css'; + +/* Page icon */ +.container-subtitle { + background: url('../images/icon-instructions.gif') no-repeat left top; + background-position: 0 5px; +} + +.container-contents { + background-color: #FFFFFF; + padding: 0; + text-align: left; +} + +em { + font-weight: bold; +} + +/* Autocomplete search field */ +#edit-txt-search { + background-position: 98% center; + font-family: sans-serif; + font-size: 0.96em; + margin: -15px 0 0 0; + padding: 14px; + width: 355px; +} + +#autocomplete { + background-color: #FFFFFF; + border: 1px solid #CCCCCC; + font-size: 0.8em; + margin-top: -15px; +} + +#autocomplete li { + font-family: sans-serif; + padding: 2px 0 2px 5px; +} + +#btn_submit, #lnk-reset { + font-size: 0.90em; + position: absolute; +} + +#btn_submit { + border-radius: 50px; + border: 1px solid #CCCCCC; + background-image: url('../images/icon-lens.png'); + background-position: center; + background-repeat: no-repeat; + background-color: #EAEAEA; + height: 40px; + margin: -10px 0 0 10px; + padding-left: 20px; + padding-right: 20px; +} + +#btn_submit:hover { + background-color: #D5D5D5; +} + +#lnk-reset { + font-family: sans-serif; + margin: -42px 0 0 455px; +} + +/* Search Result Container */ +#container-search-result { + padding: 0; +} + +.search-on { + border-bottom: 1px solid #CCCCCC; + cursor: pointer; + padding: 0 0 10px 0; + margin: 0 0 20px 0; + + -moz-box-shadow: 0 5px 7px -9px #333333; + -webkit-box-shadow: 0 5px 7px -9px #333333; + box-shadow: 0 5px 7px -9px #333333; + text-align: left; +} + +#container-search-result table td, #lists table td { + vertical-align: top; +} + +#container-search-result p { + font-size: 1.1em; + margin: 20px 0; +} + +#container-search-result table { + border-bottom: 4px solid #314355; + margin-bottom: 40px; + + -moz-box-shadow: 1px 1px 5px 1px #CCCCCC; + -webkit-box-shadow: 1px 1px 5px 1px #CCCCCC; + box-shadow: 1px 1px 5px 1px #CCCCCC; +} + +/* Tabs */ +#tabs { + background: none; + border-width: 0; + padding: 0; +} + +#tabs div { + text-align: left; +} + +#tabs .link-back-to-top { + margin-top: 10px; + display: inline-block; + text-align: right; +} + +#tabs .ui-tabs-nav li { + border-radius: 0; + background: #FCFDFD; +} + +#tabs .ui-tabs-tab, +#tabs .ui-corner-top, +#tabs .ui-state-default, +#tabs .ui-tab, +#tabs .ui-tabs-active, +#tabs .ui-state-active, +#tabs .ui-tabs-anchor { + border-style: solid; + border-color: #EAEAEA; + border-top-width: 1px; + border-left-width: 1px; + border-right-width: 1px; + border-bottom: none; + color: #000000; +} + +#tabs .ui-tabs-nav { + background: transparent; + border-width: 0px 0px 1px 0px; + border-radius: 0px; + padding-left: 0px; + -moz-border-radius: 0px; + -webkit-border-radius: 0px; +} + +#tabs .ui-tabs-panel { + background: #FCFDFD; + border-width: 0px 1px 1px 1px; + border-radius: 0; + font-size: 0.8em; +} + +#tabs .ui-tabs-panel li p { + margin: 0; + padding: 2px; +} + +#tabs .ui-tabs-panel li p { + margin: 0; + padding: 2px; +} + +#tabs .ui-tabs-panel p a { + color: #13B72D; +} + +#tabs td, #lists td { + padding-top: 10px; + padding-bottom: 10px; + vertical-align: top; +} + +#tabs td em { + font-weight: bold; +} + +#tabs tr:nth-child(odd), #lists tr:nth-child(odd) { + background-color: #F7F7F7; +} + +#tabs .data-cells, #lists .data-cells, +#container-search-result table div:first-child { + font-weight: bold; + font-size: 1.1em; + margin: 0 auto; + padding: 2px 7px; + text-align: left; +} + +/* Tab 1 */ +div.ui-tabs-panel { + padding-left: 40px !important; + padding-right: 40px !important; +} + +#fragment-1 h3 { + text-align: left; + font-size: 2em; +} + +#fragment-1 ul li { + margin: 2px 0; + text-align: left; +} + +#fragment-1 p { + font-size: 1.2em; +} + +#fragment-1 p a { + color: #13b72d; +} + +/* All Tabs */ +.ui-tabs-nav li a { + height: 35px; + line-height: 30px; + font-size: 1em; +} + +a.button:hover { + background: #DEDEDE none repeat scroll 0% 0%; +} + +#essential a { + background-color: green; + color: #FFFFFF !important; +} + +#photo-appendix a { + background-color: #EAEAEA; +} + +/* photo appendix */ +#photo-container { + background-color: #EAEAEA; + padding: 30px 5px; + margin-top: 20px; + text-align: center !important; + width: 98%; + margin: 0 auto; +} + +#gallery-container { + display: inline-block; + margin: 0 auto; +} + +.side-nav { + position: absolute; + font-size: 2.5em; + top: 50%; + background-color: rgba(255, 255, 255, 0.2); + padding: 5px; +} + +.nav-go-left { + margin-left: 12px !important; +} + +.nav-go-right { + margin-left: 435px !important; +} + +.side-nav a:link { + color: #CCCCCC; + text-decoration: none; +} + +.side-nav:hover { + color: #000000; + text-decoration: none; + background-color: rgba(255, 255, 255, 0.8); +} + +.gallery-img { + float: left; + width: 470px; +} + +#gallery-container img { + border: 6px solid #FFFFFF; + padding: 0; +} + +.download-template-instructions { + font-style: italic; + font-weight:1.01em; + padding-bottom: 10px; +} + +/* Error message box */ +.messages { + margin: 0 0 15px 0 !important; +} + + +/* Table */ +th { + width: 30%; +} + + +/* Change project panels */ +#container-project-panel { + position: relative; + font-size: 1.7em; + margin: 0 0 20px 0; + text-align: left; +} + +#container-project-panel span { + font-size: 0.6em; + float: right; + line-height: 25px; +} + +#container-sel-project { + border-bottom: 1px solid #CCCCCC; + cursor: pointer; + display: none; + margin: 20px 0; + padding: 0 0 15px 0; + + -moz-box-shadow: 0 5px 7px -9px #333333; + -webkit-box-shadow: 0 5px 7px -9px #333333; + box-shadow: 0 5px 7px -9px #333333; +} + +#rawpheno-ins-sel-project { + background: #FCFDFD url('../images/chrome-selectbox-down-arrow.gif') no-repeat; + background-position: 99.3% 50%; + border: 1px solid #AAAAAA; + padding: 7px; + font-size: 0.9em; + + -moz-box-shadow: 1px 1px 4px 3px #EAEAEA; + -webkit-box-shadow: 1px 1px 4px 3px #EAEAEA; + box-shadow: inset 1px 1px 4px 3px #EAEAEA; + + width: 100%; + -webkit-appearance: none; +} + +/* Reference Tab */ +#reference { + background-color: #EAEAEA !important; +} + +/* Resource Links */ +#container-resource-links { + padding: 0 30px; +} + +#container-resource-links a { + font-size: 0.99em; + color: #118324 !important; +} + +#container-resource-links a:hover { + color: #13b72d !important; + font-size: 0.99em; +} + +#container-resource-links div { + float: left; + height: auto; + margin: 15px 20px 0 0; + padding: 0; + width: 27%; +} + +#container-resource-links div:first-child { + background: url('../images/icon-instructions.gif') no-repeat left top; +} + +#container-resource-links div:nth-child(2) { + background: url('../images/icon-backup.gif') no-repeat left top; +} + +#container-resource-links div:nth-child(3) { + background: url('../images/icon-upload.gif') no-repeat left top; +} + +#container-resource-links h3 { + font-size: 1.5em; + margin: 0 0 30px 0; + padding: 0 0 0 55px; +} + +/* Fragment 5 select box: Topic */ +#fragment-6 select { + padding: 5px 15px 5px 10px; +} + +#tabs th { + padding: 10px !important; +} + +#tabs th, #tabs td { + font-size: 1.1em !important; +} \ No newline at end of file diff --git a/theme/css/rawpheno.style.css b/css/style-page.css similarity index 91% rename from theme/css/rawpheno.style.css rename to css/style-page.css index 58d5165..e737623 100755 --- a/theme/css/rawpheno.style.css +++ b/css/style-page.css @@ -104,7 +104,7 @@ } select:not([multiple]) { - background: #FFFFFF url('../img/chrome-selectbox-down-arrow.gif') no-repeat; + background: #FFFFFF url('../images/chrome-selectbox-down-arrow.gif') no-repeat; background-position: 99.5% 50%; -webkit-appearance:none } @@ -119,3 +119,4 @@ div.container-page .tripal-site-admin-only { div.container-page .tripal-site-admin-only span { background: none !important; } + diff --git a/theme/img/appendix/01-pods-emerged.jpg b/images/appendix/01-pods-emerged.jpg similarity index 100% rename from theme/img/appendix/01-pods-emerged.jpg rename to images/appendix/01-pods-emerged.jpg diff --git a/theme/img/appendix/01-pods-variation.jpg b/images/appendix/01-pods-variation.jpg similarity index 100% rename from theme/img/appendix/01-pods-variation.jpg rename to images/appendix/01-pods-variation.jpg diff --git a/theme/img/appendix/01-tendrils-elongation.jpg b/images/appendix/01-tendrils-elongation.jpg similarity index 100% rename from theme/img/appendix/01-tendrils-elongation.jpg rename to images/appendix/01-tendrils-elongation.jpg diff --git a/theme/img/appendix/01-tendrils-no-elongation.jpg b/images/appendix/01-tendrils-no-elongation.jpg similarity index 100% rename from theme/img/appendix/01-tendrils-no-elongation.jpg rename to images/appendix/01-tendrils-no-elongation.jpg diff --git a/theme/img/appendix/02-pods-emerged.jpg b/images/appendix/02-pods-emerged.jpg similarity index 100% rename from theme/img/appendix/02-pods-emerged.jpg rename to images/appendix/02-pods-emerged.jpg diff --git a/theme/img/appendix/02-pods-variation.jpg b/images/appendix/02-pods-variation.jpg similarity index 100% rename from theme/img/appendix/02-pods-variation.jpg rename to images/appendix/02-pods-variation.jpg diff --git a/theme/img/appendix/02-tendrils-no-elongation.jpg b/images/appendix/02-tendrils-no-elongation.jpg similarity index 100% rename from theme/img/appendix/02-tendrils-no-elongation.jpg rename to images/appendix/02-tendrils-no-elongation.jpg diff --git a/theme/img/chrome-selectbox-down-arrow.gif b/images/chrome-selectbox-down-arrow.gif similarity index 100% rename from theme/img/chrome-selectbox-down-arrow.gif rename to images/chrome-selectbox-down-arrow.gif diff --git a/theme/img/icon-backup.gif b/images/icon-backup.gif similarity index 100% rename from theme/img/icon-backup.gif rename to images/icon-backup.gif diff --git a/theme/img/icon-excel.gif b/images/icon-excel.gif similarity index 100% rename from theme/img/icon-excel.gif rename to images/icon-excel.gif diff --git a/theme/img/icon-instructions.gif b/images/icon-instructions.gif similarity index 100% rename from theme/img/icon-instructions.gif rename to images/icon-instructions.gif diff --git a/theme/img/icon-lens.png b/images/icon-lens.png similarity index 100% rename from theme/img/icon-lens.png rename to images/icon-lens.png diff --git a/theme/img/icon-upload.gif b/images/icon-upload.gif similarity index 100% rename from theme/img/icon-upload.gif rename to images/icon-upload.gif diff --git a/theme/img/r.gif b/images/r.gif similarity index 100% rename from theme/img/r.gif rename to images/r.gif diff --git a/include/rawpheno.admin.form.inc b/include/rawpheno.admin.form.inc deleted file mode 100644 index 1f41881..0000000 --- a/include/rawpheno.admin.form.inc +++ /dev/null @@ -1,2189 +0,0 @@ -

• ' . l('Page Configurations', 'admin/tripal/extension/rawphenotypes/rawpheno_config') - . '
  Apply colour scheme and set page title.


'; - - $links .= '

• ' . l('Define R Transformation Rules', 'admin/tripal/extension/rawphenotypes/rawpheno_rheaders') - . '
  Define R Transfomation Rules to be applied when generating R Friendly alternative of column header.


'; - - $links .= '

• ' . l('Manage Projects', 'admin/tripal/extension/rawphenotypes/all_projects') - . '
  Create project, define column headers and appoint users to project.

'; - - $form['rawphenotypes'] = array( - '#markup' => '

Rawphenotypes Module

' . $links - ); - - return $form; -} - - /** - * Function callback: Construct administrative interface to modify page title and colour scheme. - */ -function rawpheno_admin_page($form, &$form_state) { - $breadcrumbs = array(); - $breadcrumbs[] = l('Home', ''); - $breadcrumbs[] = l('Rawphenotypes', '/admin/tripal/extension/rawphenotypes'); - $breadcrumbs[] = l('Page Configurations', '/admin/tripal/extension/rawphenotypes/rawpheno_config'); - - drupal_set_breadcrumb($breadcrumbs); - - // Set the title of this page. This line will create a page title on upper left hand - // corner of the page preceding the tabs. - drupal_set_title('Page Configurations'); - - // Colour scheme. - $form['fieldset_colour_scheme'] = array( - '#type' => 'fieldset', - '#title' => t('Colour Scheme'), - '#collapsed' => FALSE, - '#collapsible' => TRUE, - ); - - $form['fieldset_colour_scheme']['rawpheno_colour_scheme'] = array( - '#type' => 'textfield', - '#title' => t('Enter colour:'), - '#default_value' => variable_get('rawpheno_colour_scheme'), - '#size' => 40, - '#maxlength' => 20, - '#description' => t('eg. HEX: #304356, blue | get more colours here', - array('@adobe-kuler' => url('https://color.adobe.com/'))), - '#required' => TRUE, - ); - - // Headers and title. - $form['fieldset_page_title'] = array( - '#type' => 'fieldset', - '#title' => t('Page title'), - '#collapsed' => FALSE, - '#collapsible' => TRUE, - ); - - // Rawdata page. - $form['fieldset_page_title']['rawpheno_rawdata_title'] = array( - '#type' => 'textfield', - '#title' => t('Title of Rawdata page:'), - '#default_value' => variable_get('rawpheno_rawdata_title'), - '#size' => 120, - '#maxlength' => 220, - '#required' => TRUE, - ); - - // Download Page. - $form['fieldset_page_title']['rawpheno_download_title'] = array( - '#type' => 'textfield', - '#title' => t('Title of Download page:'), - '#default_value' => variable_get('rawpheno_download_title'), - '#size' => 120, - '#maxlength' => 220, - '#required' => TRUE, - ); - - // Instructions page. - $form['fieldset_page_title']['rawpheno_instructions_title'] = array( - '#type' => 'textfield', - '#title' => t('Title of Instructions page:'), - '#default_value' => variable_get('rawpheno_instructions_title'), - '#size' => 120, - '#maxlength' => 220, - '#required' => TRUE, - ); - - // Upload page. - $form['fieldset_page_title']['rawpheno_upload_title'] = array( - '#type' => 'textfield', - '#title' => t('Title of Upload page:'), - '#default_value' => variable_get('rawpheno_upload_title'), - '#size' => 120, - '#maxlength' => 220, - '#required' => TRUE, - ); - - // Backup page. - $form['fieldset_page_title']['rawpheno_backup_title'] = array( - '#type' => 'textfield', - '#title' => t('Title of Backup page:'), - '#default_value' => variable_get('rawpheno_backup_title'), - '#size' => 120, - '#maxlength' => 220, - '#required' => TRUE, - ); - - $form['req']['#suffix'] = t(' * means field is required'); - - $path = drupal_get_path('module', 'rawpheno') . '/theme/'; - $form['#attached']['css'] = array($path . 'css/rawpheno.admin.style.css'); - - // This will add a save button to this form, thus - // no hook_submit nor hook_validate is required. - return system_settings_form($form); -} - - -// Include function to manage column headers, cv terms and variable names. -module_load_include('inc', 'rawpheno', 'include/rawpheno.function.measurements'); - - -/** - * Function callback: Construct administrative interface to manage R Friendly transformation of column headers. - */ -function rawpheno_admin_rheaders($form, &$form_state) { - $breadcrumbs = array(); - $breadcrumbs[] = l('Home', ''); - $breadcrumbs[] = l('Rawphenotypes', '/admin/tripal/extension/rawphenotypes'); - $breadcrumbs[] = l('Define R Transformation Rules', '/admin/tripal/extension/rawphenotypes/rawpheno_rheaders'); - - drupal_set_breadcrumb($breadcrumbs); - - // Set the title of this page. This line will create a page title on upper left hand - // corner of the page preceding the tabs. - drupal_set_title('Define R Transformation Rules'); - - // Fieldset to contain R-Friendly transformation rules form. - $form['fieldset_rules'] = array( - '#type' => 'fieldset', - '#title' => t('R-Friendly Transformation Rules'), - '#collapsed' => FALSE, - '#collapsible' => FALSE, - ); - - $form['fieldset_rules']['rawpheno_rtransform_words'] = array( - '#type' => 'textarea', - '#title' => t('List of words to remove:'), - '#default_value' => variable_get('rawpheno_rtransform_words'), - '#description' => t('Separate words with commas'), - '#required' => TRUE, - ); - - $form['fieldset_rules']['rawpheno_rtransform_characters'] = array( - '#type' => 'textarea', - '#title' => t('List of special characters to remove:'), - '#default_value' => variable_get('rawpheno_rtransform_characters'), - '#description' => t('Separate characters with commas'), - '#required' => TRUE, - ); - - $form['fieldset_rules']['rawpheno_rtransform_replace'] = array( - '#type' => 'textarea', - '#title' => t('Match and replace (Match Character = Replacement):'), - '#default_value' => variable_get('rawpheno_rtransform_replace'), - '#description' => t('Separate match and replace pairs with commas'), - '#required' => TRUE, - ); - - $form['req']['#suffix'] = t(' * means field is required'); - - $path = drupal_get_path('module', 'rawpheno') . '/theme/'; - $form['#attached']['css'] = array($path . 'css/rawpheno.admin.style.css'); - - - // This will add a save button to this form, thus - // no hook_submit nor hook_validate is required. - return system_settings_form($form); -} - - -/** - * Implements hook_validate(). - * Ensure that the match and replace entry is in match value = replace value format. - */ -function rawpheno_admin_rheaders_validate($form, &$form_state) { - // Read string containing the match and replace rule. Since each pair is separated by comma symbol, - // use it to split and process each entry. In each item, test if both match value and replace value are provided, - // by examining the value before and after the = sign. When a pair is missing either values, send an - // error message to admin that an entry is not in the prescribed format. - $mr = trim($form_state['values']['rawpheno_rtransform_replace']); - - $match_replace = explode(',', $mr); - foreach($match_replace as $r) { - @list($match, $replace) = explode('=', $r); - if (empty(trim($match)) OR empty(trim($replace))) { - form_set_error('rawpheno_rtransform_replace', t('An entry not in the required format (Match value = Replace value) was found.')); - } - } -} - - -/** - * Function callback: Construct form to list all active projects (with headers and users) - * and allow admin to create a new project. - */ -function rawpheno_admin_all_projects($form, &$form_state) { - $breadcrumbs = array(); - $breadcrumbs[] = l('Home', ''); - $breadcrumbs[] = l('Rawphenotypes', '/admin/tripal/extension/rawphenotypes'); - $breadcrumbs[] = l('Manage Projects', '/admin/tripal/extension/rawphenotypes/all_projects'); - - drupal_set_breadcrumb($breadcrumbs); - - // Set the title of this page. This line will create a page title on upper left hand - // corner of the page preceding the tabs. - drupal_set_title('Manage Projects'); - - // CONSTRUCT FORMS REQUIRED BY THIS INTERFACE. - - // FORM - // Construct create project form. - // This form will allow admin to select a project and define column headers, - // as well as, assign active users. - - // Query projects that dont have column headers. - $sql = "SELECT t1.project_id, t1.name - FROM {project} AS t1 LEFT JOIN pheno_project_cvterm AS t2 USING(project_id) - WHERE t2.project_id IS NULL - ORDER BY t1.project_id DESC"; - - $p = chado_query($sql); - - // Add form elements. - $form['fieldset_sel_project'] = array( - '#type' => 'fieldset', - '#title' => t('Create a New Project:'), - '#collapsed' => FALSE, - '#collapsible' => TRUE, - ); - - if ($p->rowCount() > 0) { - // Make sure that the site has set up a project. - $projects = $p->fetchAllKeyed(); - $options = array('0' => '---') + $projects; - - $form['fieldset_sel_project']['sel_project'] = array( - '#type' => 'select', - '#title' => t('Please select a project:'), - '#options' => $options, - '#default_value' => array_keys($options)[0], - '#id' => 'admin-sel-project', - '#description' => t('Please note that column headers Plot, Entry, Rep, Name, Location and Planting Date (date) are added to project by default.'), - ); - - $form['fieldset_sel_project']['add_project'] = array( - '#type' => 'submit', - '#value' => t('Create Project'), - '#id' => 'admin-add-project', - ); - } - else { - $form['fieldset_sel_project']['no_project'] = array( - '#markup' => '
No projects available.
', - ); - } - - // TABLE - // Construct table that lists all active projects in this module. - // The table data consists of project name and summary of column headers and active users. - - // Get trait types array. - $trait_type = rawpheno_function_trait_types(); - - // Array to hold table headers. - $arr_headers = array(); - - // Array to hold table rows. - $arr_rows = array(); - - $empty_table_title = ""; - - // On the bottom of the select field, list (summary of) the active projects, or projects with - // column headers and users assigned to it. - - $sql = "SELECT - t1.name, - COUNT(DISTINCT t2.cvterm_id) AS header_count, - COUNT(DISTINCT t3.cvterm_id) AS essential_count, - COUNT(DISTINCT t4.uid) AS user_count, - t1.project_id - FROM - {project} AS t1 - INNER JOIN pheno_project_cvterm AS t2 USING (project_id) - INNER JOIN pheno_project_cvterm AS t3 USING (project_id) - INNER JOIN pheno_project_user AS t4 USING (project_id) - WHERE - t1.project_id IN (SELECT DISTINCT project_id FROM pheno_project_cvterm) - AND t2.project_id IN (SELECT DISTINCT project_id FROM pheno_project_cvterm) - AND t3.project_id IN (SELECT DISTINCT project_id FROM pheno_project_cvterm) - AND t3.type = 'essential' - AND t4.project_id IN (SELECT DISTINCT project_id FROM pheno_project_cvterm) - GROUP BY t1.project_id - ORDER BY t1.project_id DESC"; - - $args = array(':essential_trait' => $trait_type['type1']); - - $projects = chado_query($sql, $args); - - // Page title. - $form['all_project_info'] = array( - '#type' => 'markup', - '#markup' => '

' . $projects->rowCount() . ' Project(s)

' - ); - - // Table rows. - if ($projects->rowCount() > 0) { - $i = 0; - - foreach($projects as $p) { - $view_link = 'admin/tripal/extension/rawphenotypes/all_projects/' . $p->project_id . '/project/manage'; - $view_cell = l('View', $view_link); - $name_cell = l($p->name, $view_link); - - // Warn user that the project has no essential trait. Project must have at least 1 essential trait - // list of column headers before it can process and store phenotypic data. The same for user. - $warn_header = ($p->essential_count > 0) - ? '' - : '  Project has no essential column header'; - - $warn_user = ($p->user_count > 0) - ? '' - : '  Project has no assigned user'; - - - // + 1 to account for Name column header. - array_push($arr_rows, array(($i+1), $name_cell, ($p->header_count + 1), $p->essential_count . $warn_header, $p->user_count . $warn_user, $view_cell)); - $i++; - } - } - else { - $empty_table_title = t('No project available'); - } - - // Table headers. - array_push($arr_headers, '-', t('Project Name'), t('Column Headers'), t('Essential Headers'), t('Active User'), t('View')); - - $table = theme('table', array('header' => $arr_headers, 'rows' => $arr_rows, 'empty' => $empty_table_title)); - - // Render table. - $form['tbl_projects'] = array( - '#markup' => $table, - ); - - // Attach css. - $path = drupal_get_path('module', 'rawpheno') . '/theme/'; - $form['#attached']['css'] = array($path . 'css/rawpheno.admin.style.css'); - - return $form; -} - - -/** - * Implements hook_validate(). - * Ensure that a project is selected when creating a new project. - */ -function rawpheno_admin_all_projects_validate($form, &$form_state) { - $project_id = $form_state['values']['sel_project']; - if ($project_id <= 0) { - form_set_error('sel_project', t('No project selected. Please select a project and try again')); - } -} - - -/** - * Implements hook_sumbit(). - * Create a new project. - */ -function rawpheno_admin_all_projects_submit($form, &$form_state) { - // When a new project is created, Plant property column headers are - // added to a project by default. The following are the plant property headers: - // Location, Plot Rep and Entry. - - // Plus and Planting Date and make it essential trait. Planting date is important - // as it is used when generating the heatmap. - $project_id = $form_state['values']['sel_project']; - - // Get trait types array. - $trait_type = rawpheno_function_trait_types(); - - // Get Planting Date cvterm id. - $pd = array('name' => 'Planting Date (date)', 'cv_id' => array('name' => 'phenotype_measurement_types')); - - if (function_exists('chado_get_cvterm')) { - $planting_date = chado_get_cvterm($pd); - } - else { - $planting_date = tripal_get_cvterm($pd); - } - - db_insert('pheno_project_cvterm') - ->fields(array( - 'project_id' => $project_id, - 'cvterm_id' => $planting_date->cvterm_id, - 'type' => $trait_type['type1']) - ) - ->execute(); - - // Query all plant property types column headers in chado.cvterm table. - $sql = "SELECT cvterm_id - FROM {cv} AS t1 INNER JOIN {cvterm} AS t2 USING(cv_id) - WHERE t1.name = 'phenotype_plant_property_types' - ORDER BY cvterm_id ASC"; - - $pp = chado_query($sql); - - // Test each plant property if it is present in the project. - foreach($pp as $t) { - db_insert('pheno_project_cvterm') - ->fields(array( - 'project_id' => $project_id, - 'cvterm_id' => $t->cvterm_id, - 'type' => $trait_type['type4']) - ) - ->execute(); - } - - // Make who created the project as the default user. - db_insert('pheno_project_user') - ->fields(array( - 'project_id' => $project_id, - 'uid' => $GLOBALS['user']->uid) - ) - ->execute(); - - if ($GLOBALS['user']->uid != 1) { - // If the user is not the superadmin, add as well. - db_insert('pheno_project_user') - ->fields(array( - 'project_id' => $project_id, - 'uid' => 1) - ) - ->execute(); - } - - - // Redirect user to project asset management page. - $path = url($GLOBALS['base_url'] . '/admin/tripal/extension/rawphenotypes/all_projects/' . $project_id . '/project/manage'); - drupal_goto($path); -} - - -/** - * Function callback: Construct form to manage project assets. - * - * @param $asset_id - * An integer containing the record id. - * @param $asset_type - * A string indicating what asset type the id is, a project, a user or a column header. - * @param $action - * A string containing predefined commands performed to an asset. - */ -function rawpheno_admin_project_management($form, &$form_state, $asset_id = NULL, $asset_type = NULL, $action = NULL) { - // Link to go back. - $goback = ' Go back'; - - // Define valid action/command/operation per asset type. - $arr_valid = array(); - $arr_valid['project']['command'] = array('manage'); - $arr_valid['project']['table'] = 'pheno_project_cvterm'; - $arr_valid['project']['id'] = 'project_id'; - - $arr_valid['header']['command'] = array('edit', 'delete'); - $arr_valid['header']['table'] = 'pheno_project_cvterm'; - $arr_valid['header']['id'] = 'project_cvterm_id'; - - $arr_valid['user']['command'] = array('delete'); - $arr_valid['user']['table'] = 'pheno_project_user'; - $arr_valid['user']['id'] = 'project_user_id'; - - $arr_valid['envdata']['command'] = array('delete'); - $arr_valid['envdata']['table'] = 'pheno_environment_data'; - $arr_valid['envdata']['id'] = 'environment_data_id'; - - // Ensure query strings are valid. The string length check ensure that Posgres - // will not throw Numeric value out of range PDOexception. - if (!isset($asset_id) OR $asset_id <= 0 OR strlen($asset_id) >= 10) { - // When project asset id is invalid. - drupal_set_message(t('Not a valid project asset id number.') . $goback, 'error'); - } - else { - // Id is valid, test if asset type is valid. - if (array_key_exists($asset_type, $arr_valid)) { - // Valid asset type request. Check if the given asset id and asset type - // exists in the database before peforming any command. - $sql = sprintf("SELECT FROM {%s} WHERE %s = :asset_id LIMIT 1", - $arr_valid[$asset_type]['table'], $arr_valid[$asset_type]['id']); - - $args = array(':asset_id' => (int)$asset_id); - $prj_asset = db_query($sql, $args); - - if ($prj_asset->rowCount() == 1) { - // Project asset exists in the database. Procede to command requested. - // Determine what to do with the project asset. - if (in_array($action, $arr_valid[$asset_type]['command'])) { - // Command is valid. Call function that will execute the command. - if ($asset_type == 'project') { - // Call project function. - $form = rawpheno_admin_project($form, $form_state, $asset_id); - } - elseif ($asset_type == 'header') { - /////////// - // Before admin can modify or delete a column header, ensure that a header - // is not a plant property type header, not used by another project and the header has no data associated to it. - // In addition, Loding (scale 1-5) header is not editable. - $trait_type = rawpheno_function_trait_types(); - $plant_property = $trait_type['type4']; - - // Get header properties. - $header_asset = rawpheno_function_header_properties($asset_id, 'full'); - - if ($header_asset['count_data'] > 0 AND $action == 'delete') { - // Header has data. - drupal_set_message(t('Cannot @action this entry. Column header has data associated to it.', - array('@action' => $action)) . $goback, 'error'); - } - else { - // Call header function. - $form = rawpheno_admin_project_headers($form, $form_state, $header_asset, $action); - } - /////////// - } - else if($asset_type == 'user') { - /////////// - // Ensure that user is not deleted when there is data and backup files associated. - $user_project_asset = rawpheno_admin_user_assets($asset_id); - - if ($user_project_asset['project_file_count'] > 0) { - // User is assigned to a project has backup files. - drupal_set_message(t('Cannot @action this user. User has backup files.', - array('@action' => $action)) . $goback, 'error'); - } - elseif ($user_project_asset['project_data_count'] > 0) { - // User is assigned to a project has has data. - drupal_set_message(t('Cannot @action this user. User is assigned to a project that has data.', - array('@action' => $action)) . $goback, 'error'); - } - elseif ($user_project_asset['user_id'] < 2) { - // User is administrator. - drupal_set_message(t('Cannot @action user. The user is administrator of this site.', - array('@action' => $action)) . $goback, 'error'); - } - else { - // Call user function. - $form = rawpheno_admin_project_users($form, $form_state, $user_project_asset, $action); - } - //////////// - } - else if($asset_type == 'envdata') { - //////////// - if ($asset_id > 0 && $action == 'delete') { - // Locate file. Ensure that file to be deleted exists. - $envdata_project_asset = rawpheno_admin_envdata_asset($asset_id); - - if ($envdata_project_asset) { - // Call user function. - $form = rawpheno_admin_project_envdata($form, $form_state, $envdata_project_asset, $action); - } - else { - // User is administrator. - drupal_set_message(t('Cannot @action Environment Data File. File does not exist.', - array('@action' => $action)) . $goback, 'error'); - } - } - //////////// - } - } - else { - // Not a valid command. - drupal_set_message(t('Not a valid request.' . $goback), 'error'); - } - } - else { - // Asset record not found. - drupal_set_message(t('Project asset id number does not exist.' . $goback), 'error'); - } - } - else { - // Asset type does not exist. - drupal_set_message(t('Not a valid project request.' . $goback), 'error'); - } - } - - // Attach css and javascript. - $path = drupal_get_path('module', 'rawpheno') . '/theme/'; - $form['#attached']['css'] = array($path . 'css/rawpheno.admin.style.css'); - $form['#attached']['js'] = array($path . 'js/rawpheno.admin.script.js'); - - return $form; -} - - -/** - * Function callback: Construct interface to manage project asset. - * - * @param $project_id - * An integer containing the project id number. - */ -function rawpheno_admin_project($form, &$form_state, $project_id) { - $breadcrumbs = array(); - $breadcrumbs[] = l('Home', ''); - $breadcrumbs[] = l('Rawphenotypes', '/admin/tripal/extension/rawphenotypes'); - $breadcrumbs[] = l('Manage Projects', '/admin/tripal/extension/rawphenotypes/all_projects'); - - drupal_set_breadcrumb($breadcrumbs); - - // Set the title of this page. This line will create a page title on upper left hand - // corner of the page preceding the tabs. - drupal_set_title('Manage Project Assets'); - - // Construct page title and link back to projects form. - $markup = l('Go Back to Projects Table', 'admin/tripal/extension/rawphenotypes/all_projects'); - - $project = chado_generate_var('project', array('project_id' => $project_id)); - $markup .= '

PROJECT: ' . $project->name . '

'; - - $form['project_info'] = array( - '#type' => 'markup', - '#markup' => $markup, - ); - - // Arguments to all SQL queries below. - $args = array(':project_id' => $project_id); - - // CONSTRUCT FORMS REQUIRED BY THIS INTERFACE. - - // FORM - // Construct add column header form. - // This form will allow admin to add new column header to a project. - - // Get trait types array. - $trait_type = rawpheno_function_trait_types(); - - // Fieldset add column header form. - $form['fieldset_trait'] = array( - '#type' => 'fieldset', - '#title' => t('Add column header'), - '#collapsed' => TRUE, - '#collapsible' => TRUE, - ); - - // Add form elements. - $default_values = array('count_project' => 0, - 'count_data' => 0, - 'txt_id' => $project_id, - 'btn_trait_submit' => 'Add column header'); - - $form = rawpheno_admin_render_form_trait($form, $form_state, $default_values); - - - // FORM - // Construct add/user existing column headers form. - // This form will allow admin to add multiple column headers that are predefined in this module. - - // Fieldset suggest existing column headers. - $form['fieldset_existing_trait'] = array( - '#type' => 'fieldset', - '#title' => t('Add existing column headers'), - '#collapsed' => TRUE, - '#collapsible' => TRUE, - ); - - // Add form elements. - // Query all traits in cvterm that is not listed as among the traits in a project, - // and suggest it to admin in table form with checboxes. - $sql = "SELECT t2.cvterm_id, t2.name - FROM - {cv} AS t1 - INNER JOIN {cvterm} AS t2 - USING(cv_id) - WHERE - t1.name = 'phenotype_measurement_types' - AND t2.cvterm_id NOT IN (SELECT cvterm_id FROM pheno_project_cvterm WHERE project_id = :project_id) - ORDER BY t2.cvterm_id ASC"; - - $headers = chado_query($sql, $args); - - // Table rows. - $arr_tblchkbox_rows = array(); - - if ($headers->rowCount() > 0) { - foreach($headers as $h) { - $arr_tblchkbox_rows[$h->cvterm_id] = - array( - 'name' => $h->name, - // Add select box. - 'traittype' => - array('data' => - array( - '#type' => 'checkbox', - '#title' => 'Yes', - '#options' => array(0, 1), - '#default_value' => 0, - '#name' => 'traittype-' . $h->cvterm_id - ) - ) - ); - } - } - - // Table headers. - $arr_tblchkbox_headers = array('traittype' => t('Is Essential?'), 'name' => t('Name')); - - // Checkboxes and table. - $form['fieldset_existing_trait']['tbl_existing_headers'] = array( - '#type' => 'tableselect', - '#header' => $arr_tblchkbox_headers, - '#options' => $arr_tblchkbox_rows, - '#js_select' => FALSE, - '#prefix' => '

' . - t('The table below lists all column headers available in this module. - Please check the header(s) that you want to add to this project and click Add Selected Traits button.') . '

-
', - '#suffix' => '

' . - t('Check IS ESSENTIAL? to ensure the header must exists in the spreadsheet file') - . '

', - '#empty' => t('No column headers available'), - '#attributes' => array( - 'id' => 'tbl-existing-headers', - 'class' => array( - 'tableheader-processed' - ), - ), - ); - - // Add submit button only there is any options available. - if (count($arr_tblchkbox_rows) > 0) { - $form['fieldset_existing_trait']['add_selected_trait'] = array( - '#type' => 'submit', - '#value' => t('Add selected headers'), - '#validate' => array('rawpheno_admin_validate_add_existing'), - '#submit' => array('rawpheno_admin_submit_add_existing'), - '#limit_validation_errors' => array( - array('tbl_existing_headers'), - array('txt_id') - ), - ); - } - - //FORM - // Construct assign user to a project form. - // This form will allow admin to assign an active user to a project. User will then be restricted - // to upload data to project(s) he is assigned to. - - // Fieldset to suggest user to a project. - $form['fieldset_users'] = array( - '#type' => 'fieldset', - '#title' => t('Add users'), - '#collapsed' => TRUE, - '#collapsible' => TRUE, - ); - - - // Add seach field to filter the list of name. - $form['fieldset_users']['txt_autocomplete_user'] = array( - '#title' => 'Name :', - '#type' => 'textfield', - '#maxlength' => 50, - '#size' => 130, - '#autocomplete_path' => 'admin/tripal/extension/rawphenotypes/username/' . $project_id, - '#description' => t('Type the name or username of the user') - ); - - $form['fieldset_users']['add_selected_user'] = array( - '#type' => 'submit', - '#value' => t('Add user'), - '#validate' => array('rawpheno_admin_validate_add_user'), - '#submit' => array('rawpheno_admin_submit_add_user'), - '#limit_validation_errors' => array( - array('txt_autocomplete_user'), - array('txt_id') - ), - ); - - - // Environement Data File Upload. - $inline_wraper = array( - '#prefix' => '
', - '#suffix' => '
', - ); - - $form['fieldset_envdata'] = array( - '#type' => 'fieldset', - '#title' => t('Upload Environment Data File'), - '#collapsed' => TRUE, - '#collapsible' => TRUE, - ); - - // Add seach field to filter the list of name. - $form['fieldset_envdata']['file'] = array( - '#title' => 'File :', - '#type' => 'file', - ) + $inline_wraper; - - // Location: fetch all location in a project based on uploaded data. - $sql = "SELECT location - FROM {rawpheno_rawdata_mview} INNER JOIN pheno_plant_project USING(plant_id) - WHERE project_id = :project_id - GROUP BY location - ORDER BY location ASC"; - - $locations = chado_query($sql, $args); - $location_options = ($locations->rowCount() > 0) ? $locations->fetchAllKeyed(0,0) : array(); - - $form['fieldset_envdata']['select_location'] = array( - '#title' => 'Location :', - '#type' => 'select', - '#options' => $location_options, - '#empty_option' => t('- Select -'), - - '#ajax' => array( - 'wrapper' => 'envdata-year', - 'callback' => 'rawpheno_ajax_fetch_years', - 'progress' => array('type' => 'throbber', 'message' => ''), - ), - ) + $inline_wraper; - - // Year: fetch all years in a project given a location. - if (isset($form_state['values']['select_location']) && - $form_state['values']['select_location'] != '') { - - $location_value = $form_state['values']['select_location']; - - $sql = "SELECT SUBSTRING(planting_date, 1, 4) AS year - FROM {rawpheno_rawdata_mview} - WHERE location = :location - AND plant_id IN (SELECT plant_id FROM pheno_plant_project WHERE project_id = :project_id) - ORDER BY year DESC"; - - $years = chado_query($sql, $args + array(':location' => $location_value)); - // Assoc. array will remove any duplicates in the query result. - $year_options = ($years->rowCount() > 0) ? $years->fetchAllKeyed(0,0) : array(); - } - else { - $year_options = array(); - } - - $inline_wraper['#prefix'] = '
'; - $form['fieldset_envdata']['select_year'] = array( - '#title' => 'Year :', - '#type' => 'select', - '#options' => $year_options, - '#empty_option' => t('- Select -'), - ) + $inline_wraper; - - // Submit: - $form['fieldset_envdata']['upload_env_file'] = array( - '#type' => 'submit', - '#value' => t('Upload file'), - '#validate' => array('rawpheno_admin_validate_upload_envdata'), - '#submit' => array('rawpheno_admin_submit_upload_envdata'), - '#limit_validation_errors' => array( - array('filefield_file'), - array('select_location'), - array('select_year'), - ), - ); - - // DISPLAY SUMMARY OF DATA REQUIRED BY THIS INTERFACE. - - // List column headers in a project. - // Given a project id, select all column headers belonging to that project. - // Note the loding (scale 1-5) upright-lodged header is the only header using this - // format (all headers use the format: name (trait rep; unit), thus a flag indicating - // if header is such is required to disable edit option and prevent edit functionality - // from processing unfamiliar format. - $sql = "SELECT project_cvterm_id, pheno_project_cvterm.type - FROM {cvterm} RIGHT JOIN pheno_project_cvterm USING(cvterm_id) - WHERE project_id = :project_id ORDER BY type, name ASC"; - - $headers = chado_query($sql, $args); - - // List active users in a project. - $sql = "SELECT project_user_id, uid, name, mail, created, status, login - FROM {users} RIGHT JOIN {pheno_project_user} USING(uid) - WHERE project_id = :project_id - ORDER BY name DESC"; - - $users = db_query($sql, $args); - - // Environment data files. - $sql = "SELECT environment_data_id, location, year, sequence_no, filename, uri, timestamp - FROM {pheno_environment_data} INNER JOIN file_managed USING(fid) - WHERE project_id = :project_id - ORDER BY location, year, sequence_no ASC"; - - $envdata = db_query($sql, $args); - - // Array to hold table headers. - $arr_headers = array(); - - // Array to hold table rows. - $arr_rows = array(); - - // Array to hold table properties. - $arr_tbl_args = array( - 'header' => '', - 'rows' => '', - 'sticky' => FALSE, - 'empty' => '', - 'attributes' => array('id' => ''), - ); - - - // TABLE - // Construct table that lists all column headers specific to a project. - // Options to edit and delete items give admin record management functionality. - // NOTE: Delete option will not physically delete a record in cvterm table. - // When a header is deleted, it is removed from the project only. - - // Table rows. - $has_name = 0; - $count_essential = 0; - - if ($headers->rowCount() > 0) { - $i = 0; - foreach($headers as $h) { - if ($h->type == $trait_type['type1']) { - $count_essential++; - } - - // Get header information. - $header_asset = rawpheno_function_header_properties($h->project_cvterm_id); - - // Edit link. - $edit_cell = '-'; - if ($header_asset['name'] != 'Lodging (Scale: 1-5) upright - lodged') { - $edit_link = 'admin/tripal/extension/rawphenotypes/all_projects/' . $h->project_cvterm_id . '/header/edit'; - $edit_cell = l('Edit', $edit_link); - } - - // Delete link. - $del_link = 'admin/tripal/extension/rawphenotypes/all_projects/' . $h->project_cvterm_id . '/header/delete'; - $del_cell = l('Remove', $del_link, array('attributes' => array('class' => 'link-del'))); - - // No edit and delete when column header is of type plantproperty. - if ($header_asset['type'] == $trait_type['type4'] OR $header_asset['name'] == 'Planting Date (date)') { - // Add Name column header to the row array. - if ($has_name == 0 AND $header_asset['name'] != 'Planting Date (date)') { - array_push($arr_rows, array((count($arr_rows) + 1), '

Name

', 'Name', 'PLANTPROPERTY', '-', '-')); - $has_name += 1; - $i++; - } - - $edit_cell = $del_cell = '-'; - } - - $class = ($header_asset['type'] == $trait_type['type1']) ? 'essential-trait' : 'non-essentialtrait'; - - if ($header_asset['type'] == $trait_type['type4']) { - // Row where trait is plantproperty. - $header_cell = '

' . $header_asset['name'] . '

'; - $definition_cell = $header_asset['name']; - } - else { - // All traits. - $header_cell = '

' . $header_asset['name'] . '

' . rawpheno_admin_mark_empty($header_asset['r_version']) . '

'; - $definition_cell = '

DEFINITION:
' . @rawpheno_admin_mark_empty($header_asset['definition']) . '

' . - '

COLLECTION METHOD:
' . @rawpheno_admin_mark_empty($header_asset['method']) . '

'; - } - - // Register a row. - $arr_rows[] = array( - ($i+1), // Row counter. - $header_cell, // Trait/header name. - $definition_cell, // Trait/header definition. - array( - 'data' => strtoupper($header_asset['type']), - 'class' => array($class) - ), // Add class to cell to highlight text when trait is essential trait. - $edit_cell, // A link to edit a trait. - $del_cell // A link to delete a trait. - ); - - $i++; - } - } - else { - // When no records found, update the empty table property. - $arr_tbl_args['empty'] = t('No column headers in this project'); - } - - // Warn admin that project has no essential trait - if ($count_essential < 1) { - $form['message_no_essential'] = array( - '#markup' => '
Project has no Essential Column Header.
' - ); - } - - // Warn admin that project has no assigned user. - if ($users->rowCount() < 1) { - $form['message_no_essential'] = array( - '#markup' => '
Project has no active users.
' - ); - } - - // Tabs to show either the project column headers table or project active users table. - // Add 1 to account for Name column header. - $form['nav_tabs'] = array( - '#markup' => '' - ); - - - // Table headers. - array_push($arr_headers, '-', t('Column Header (unit)'), t('Definition/Collection Method'), t('Type'), t('Edit'), t('Remove')); - - // Update table properties. - $arr_tbl_args['header'] = $arr_headers; - $arr_tbl_args['rows'] = $arr_rows; - $arr_tbl_args['attributes']['id'] = 'tbl-project-headers'; - - // Render the table element. - $form['tbl_project_headers'] = array( - '#markup' => theme('table', $arr_tbl_args), - '#prefix' => '
', - '#suffix' => '
', - ); - - - // TABLE - // Construct table that lists all active users to a project along with files - // associated to a user in a given project. - // Options to delete items give admin record management functionality. - // NOTE: Delete option will not physically delete a record in users table. - // When a user is deleted, it is removed from the project only. - - $arr_rows = $arr_headers = array(); - - // Query user backup files. - $sql_F = " - SELECT t1.*, t2.filename, t2.filesize, t2.timestamp, t2.uri - FROM pheno_backup_file AS t1 INNER JOIN file_managed AS t2 USING(fid) - WHERE t1.project_user_id = :project_user_id ORDER BY t1.version DESC"; - - - // Table rows - if ($users->rowCount() > 0) { - $i = 0; - foreach($users as $u) { - if (empty($u->name)) { - continue; - } - - // Delete link. - $del_link = 'admin/tripal/extension/rawphenotypes/all_projects/' . $u->project_user_id . '/user/delete'; - $del_cell = l('Remove', $del_link, array('attributes' => array('class' => 'link-del'))); - - // Create a row of user with other relevant user info. - $arr_rows[] = array( - ($i+1), // Row number. - '
' . $u->name . '
', // Name of user. - $u->mail, // Email address. - '' . format_date($u->login) . '', // Date of last login. - $del_cell // Link to remove user from a project. - ); - - // Create user sub-table listing files per user account. - $args_F = array(':project_user_id' => $u->project_user_id); - $result_F = db_query($sql_F, $args_F); - $my_files_count = $result_F->rowCount(); - - - $arr_header_F = array('File', 'Version', 'Archive', 'Notes', 'Validation Result'); - - $arr_rows_F = array(); - if ($my_files_count > 0) { - foreach($result_F as $f) { - // File. - $cell_file = l($f->filename . ' (' . format_size($f->filesize) . ')', file_create_url($f->uri), - array('attributes' => array('target' => '_blank')) - ); - - $cell_file .= t('
Uploaded: @timestamp', array('@timestamp' => format_date($f->timestamp))); - - // Is archive? - $cell_is_archive = ($f->archive == 'y') ? 'Yes' : 'No'; - - // Notes. - $cell_notes = (empty($f->notes)) - ? ' ' - : '
' . $f->notes . '
'; - - // Validation Result. - $vr_tmp = str_replace(array('#item: (passed)', '#item: (failed)'), - array('

(PASSED)', '

* (FAILED)'), - $f->validation_result) . '

'; - $alert = ''; - if (($n = substr_count($vr_tmp, 'FAILED')) > 0) { - $alert = '' . $n . ' Validation errors'; - } - - $cell_validation = $alert . '
'. $vr_tmp .'
'; - - // Create row showing backed up file with file description. - $arr_rows_F[] = array( - $cell_file, // Filename. - '#' . $f->version, // File version. - $cell_is_archive, // Indicate if file is archived or not. - $cell_notes, // Notes, comments to file. - $cell_validation // Validation result performed to the file. - ); - } - } - - $file_table = theme('table', - array('header' => $arr_header_F, - 'rows' => $arr_rows_F, - 'attributes' => array('id' => 'tbl-my-files')) - ); - - $link_show_folder = ($my_files_count > 0) - ? l('[Show]', '#', array('attributes'=> array('id' => 'folder-' . $u->project_user_id, 'class'=>'link-show-folder'))) - : ''; - - // Account status. - $acc_status = ($u->status == 1) ? 'Active' : 'Suspended'; - - $markup = sprintf(' - [ACCOUNT] Status: %s | Created: %s - %d Files uploaded %s -
-
%s
-
', $acc_status, format_date($u->created), $my_files_count, $link_show_folder, $file_table); - - // Create markup (container for table of files) to show user backed up files. - $arr_rows[] = array( - 'data' => array( - array( - 'data' => $markup, - 'colspan' => 5, - 'class' => 'row-user-my-folder', - ) - ) - ); - - $i++; - } - } - else { - // When no records found, update the empty table property. - $arr_tbl_args['empty'] = t('No active user in this project'); - } - - // Table headers. - array_push($arr_headers, '-', t('Name'), t('Email Address'), t('Last Login'), t('Remove')); - - // Update table properties. - $arr_tbl_args['header'] = $arr_headers; - $arr_tbl_args['rows'] = $arr_rows; - $arr_tbl_args['attributes']['id'] = 'tbl-project-users'; - - // Render the table element. - $form['tbl_project_users'] = array( - '#markup' => theme('table', $arr_tbl_args), - '#prefix' => '
', - '#suffix' => '
', - ); - - // - $arr_rows = $arr_headers = array(); - - // Environment Data. - if ($envdata->rowCount() > 0) { - // Table headers. - array_push($arr_headers, '-', t('File'), t('Location'), t('Year'), t('Sequence No.'), t('Delete')); - - // Query environment data files. - $sql = "SELECT environment_data_id, filename, location, year, sequence_no, timestamp, uri, filesize - FROM pheno_environment_data INNER JOIN file_managed USING(fid) - WHERE project_id = :project_id - ORDER BY location, year, sequence_no DESC"; - - $env_datafile = chado_query($sql, $args); - - foreach($env_datafile as $i => $env) { - $cell_file = l($env->filename . ' (' . format_size($env->filesize) . ')', file_create_url($env->uri), - array('attributes' => array('target' => '_blank')) - ); - - $cell_file .= t('
Uploaded: @timestamp', array('@timestamp' => format_date($env->timestamp))); - - $del_link = 'admin/tripal/extension/rawphenotypes/all_projects/' . $env->environment_data_id . '/envdata/delete'; - $cell_del = l('Delete', $del_link, array('attributes' => array('class' => 'link-del'))); - - $arr_rows[] = array($i+1, $cell_file, $env->location, $env->year, '#' . $env->sequence_no, $cell_del); - } - } - else { - // When no records found, update the empty table property. - $arr_tbl_args['empty'] = t('No environment data file in this project.'); - } - - // Update table properties. - $arr_tbl_args['header'] = $arr_headers; - $arr_tbl_args['rows'] = $arr_rows; - $arr_tbl_args['attributes']['id'] = 'tbl-project-envdata'; - - // Render the table element. - $form['tbl_project_envdata'] = array( - '#markup' => theme('table', $arr_tbl_args), - '#prefix' => '
', - '#suffix' => '
', - ); - - return $form; -} - -/** - * AJAX Function callback. - */ -function rawpheno_ajax_fetch_years($form, &$form_state) { - return $form['fieldset_envdata']['select_year']; -} - -function ajax_rawpheno_filter_users($form, &$form_state) { - return $form['fieldset_users']['tbl_users']; -} - -/** - * Function callback: Construct form to edit column header and handle deletion. - * - * @param $header_asset - * An array containing information about a column header. - * @param $action - * A string containing the process to be carried out to a header. - */ -function rawpheno_admin_project_headers($form, $form_state, $header_asset, $action) { - $breadcrumbs = array(); - $breadcrumbs[] = l('Home', ''); - $breadcrumbs[] = l('Rawphenotypes', '/admin/tripal/extension/rawphenotypes'); - $breadcrumbs[] = l('Manage Projects', '/admin/tripal/extension/rawphenotypes/all_projects'); - - drupal_set_breadcrumb($breadcrumbs); - - // Set the title of this page. This line will create a page title on upper left hand - // corner of the page preceding the tabs. - drupal_set_title('Manage Project Assets / Update Header'); - - if ($action == 'edit') { - if ($header_asset['name'] == 'Lodging (Scale: 1-5) upright - lodged' OR $header_asset['name'] == 'Comments') { - // Is lodging header. - $goback = ' Go back'; - drupal_set_message(t('Cannot edit @name.', array('@name' => $header_asset['name'])) . $goback, 'error'); - } - else { - // Other headers. - // CONSTRUCT EDIT FORM. - - // FORM - // Add a link to allow administrator to go back to the list of traits in a project. - $form['back_link'] = array( - '#type' => 'markup', - '#markup' => l('Go back to projects table', 'admin/tripal/extension/rawphenotypes/all_projects') . ' | ' . l(t('Go back to project column headers table') ,'admin/tripal/extension/rawphenotypes/all_projects/' . $header_asset['in_project_id'] . '/project/manage'), - ); - - $form['fieldset_trait'] = array( - '#type' => 'fieldset', - '#title' => t('Eidt Column Header:'), - '#collapsed' => FALSE, - '#collapsible' => TRUE, - ); - - // Trait name field. - // Extract the trait rep value, trait unit and the trait name. - // NOTE: this can only process header following the format: trait name (trait rep; unit) - $trait_rep = $trait_unit = ''; - // Extract the text value inside the parenthesis (unit part). - $t = preg_match("/.*\(([^)]*)\)/", $header_asset['name'], $match); - $u = (isset($match[1])) ? $match[1] : ''; - - // Extract information in a unit only when there is a unit in the first place. - // Get the Trait rep (eg R1, R7, 1st) and measurement unit (eg cm, days) values. - $reps = rawpheno_function_trait_reps(); - - if ($t) { - // Split the information and see if either trait rep or unit is present. - $p = explode(' ', $u); - if (count($p) > 1) { - // Has trait rep and unit. - list($trait_rep, $trait_unit) = $p; - } - else { - if (in_array(trim($u, ';'), $reps)) { - // Just the rep no unit. - $trait_rep = $u; - $trait_unit = ''; - } - else { - // Just the unit no trait rep. - $trait_rep = ''; - $trait_unit = $u; - } - } - } - - // When niether is present, use the default value of the trait rep and unit (null). - // Trait name without the unit part. - $trait_name = preg_replace('/\(.*/', ' ', $header_asset['name']); - - // Add form elements. - // Include the project id when modifying a trait header. - $default_values = array('count_project' => $header_asset['count_project'], - 'count_data' => $header_asset['count_data'], - 'txt_id' => $header_asset['cvterm_id'], - 'prj_id' => $header_asset['in_project_id'], - 'txt_trait_name' => trim($trait_name), - 'sel_trait_rep' => trim($trait_rep), - 'sel_trait_unit' => trim($trait_unit), - 'txt_trait_definition' => $header_asset['definition'], - 'txt_trait_method' => $header_asset['method'], - 'txt_trait_rfriendly' => $header_asset['r_version'], - 'sel_trait_type' => $header_asset['type'], - 'btn_trait_submit' => 'Save'); - - $form = rawpheno_admin_render_form_trait($form, $form_state, $default_values); - - // TABLE - // Construct a table, as a summary, showing information about the header. - - $markup = '

SUMMARY: ' . $header_asset['name'] . '

PROJECT: ' . $header_asset['in_project_name']; - - $header_cell = $header_asset['name'] . '

' . rawpheno_admin_mark_empty($header_asset['r_version']) . '

'; - $definition_cell = '

DEFINITION:
' . @rawpheno_admin_mark_empty($header_asset['definition']) . '

' . - '

COLLECTION METHOD:
' . @rawpheno_admin_mark_empty($header_asset['method']) . '

'; - - - // Array to hold table properties. - $arr_tbl_args = array( - 'header' => array('-', t('Column Header (unit)'), t('Collection Method'), t('Definition'), t('Type')), - 'rows' => array( - array(1, - $header_cell, - $header_asset['method'], - $header_asset['definition'], - strtoupper($header_asset['type']) - ) - ), - 'sticky' => FALSE, - 'attributes' => array('id' => 'tbl_header_summary'), - ); - - // Render the table element. - $form['tbl_project_headers'] = array( - '#markup' => $markup . theme('table', $arr_tbl_args), - ); - - return $form; - } - } - elseif ($action == 'delete') { - $path = url($GLOBALS['base_url'] . '/admin/tripal/extension/rawphenotypes/all_projects/' . $header_asset['in_project_id'] . '/project/manage'); - - // Admin wants to delete/remove the trait from the project. - db_delete('pheno_project_cvterm') - ->condition('cvterm_id', $header_asset['cvterm_id']) - ->condition('project_id', $header_asset['in_project_id']) - ->execute(); - - drupal_goto($path); - } -} - - -/** - * Implements hook_validate(). - * Validate entries in add column headers form. - */ -function rawpheno_admin_project_management_validate($form, &$form_state) { - // Get the submit button that triggered the submit action. Since this is a general hook_validate() - // of the entire form, limit the proces only to submit button in from add column header and save column header. - // When the submit action is determined, perform basic check of ensuring that user is not using cvterm name - // that is in the database already. - $btn_submit = $form_state['triggering_element']['#value']; - - if ($btn_submit == 'Add column header' OR $btn_submit == 'Save') { - - if (strpbrk($form_state['values']['txt_trait_name'], '()')) { - // Test if user added parenthesis in the name. - form_set_error('txt_trait_name', t('Characters "(" and/or ")" found in column header name. Please remove these characters and try again.')); - } - else { - $asset_id = $form_state['values']['txt_id']; - - // Construct the trait name based on given name, rep and unit. - $trait_name = rawpheno_admin_construct_trait_name( - array('name' => $form_state['values']['txt_trait_name'], - 'rep' => $form_state['values']['sel_trait_rep'], - 'unit' => $form_state['values']['sel_trait_unit']) - ); - - if ($btn_submit == 'Add column header') { - // Before adding the cvterm, ensure it is not present in cvterm table. - $fc = array('name' => $trait_name, 'cv_id' => array('name' => 'phenotype_measurement_types')); - - if (function_exists('chado_get_cvterm')) { - $found_cvterm = chado_get_cvterm($fc); - } - else { - $found_cvterm = tripal_get_cvterm($fc); - } - - if (isset($found_cvterm->cvterm_id) AND $found_cvterm->cvterm_id > 0) { - form_set_error('txt_trait_name', t('The column header name exists in the database. Please use a different name.')); - form_set_error('sel_trait_rep'); - form_set_error('sel_trait_unit'); - } - } - else { - // When renaming a field in edit header, ensure that the new or modified name does not exist in cvterm table. - $sql = "SELECT cvterm_id FROM {cv} AS t1 INNER JOIN {cvterm} AS t2 USING(cv_id) - WHERE t1.name = 'phenotype_measurement_types' AND t2.name = :name AND t2.cvterm_id <> :this_cvterm_id"; - - $args = array(':name' => $trait_name, ':this_cvterm_id' => $asset_id); - $found = chado_query($sql, $args); - if ($found->rowCount() > 0) { - form_set_error('txt_trait_name', t('The column header name exists in the database. Please use a different name.')); - form_set_error('sel_trait_rep'); - form_set_error('sel_trait_unit'); - } - } - } - } -} - - -/** - * Implements hook_submit(). - * Add project assets (headers and users). - */ -function rawpheno_admin_project_management_submit($form, &$form_state) { - // Get the submit button that triggered the submit action. Since this is a general hook_submit() - // of the entire form, limit the proces only to submit button in from add column header and save column header. - $btn_submit = $form_state['triggering_element']['#value']; - - if ($btn_submit == 'Add column header' OR $btn_submit == 'Save') { - // Holds the record id number. - $asset_id = $form_state['values']['txt_id']; - - // Get field values. - $unit = $form_state['values']['sel_trait_unit']; - - // Construct the trait name based on given name, rep and unit. - $trait_name = rawpheno_admin_construct_trait_name( - array('name' => $form_state['values']['txt_trait_name'], - 'rep' => $form_state['values']['sel_trait_rep'], - 'unit' => $form_state['values']['sel_trait_unit']) - ); - - // R Friendly version of the Header name. - // When supplied, use it, otherwise transform the name to R friendly. - $rver = empty($form_state['values']['txt_trait_rfriendly']) - ? rawpheno_function_make_r_compatible($trait_name) - : $form_state['values']['txt_trait_rfriendly']; - - $trait_def = $form_state['values']['txt_trait_def']; - $col_method = $form_state['values']['txt_trait_method']; - $trait_type = $form_state['values']['sel_trait_type']; - - // - if (function_exists('chado_get_cv')) { - $cv_type = chado_get_cv(array('name' => 'phenotype_measurement_types')); - $cv_rver = chado_get_cv(array('name' => 'phenotype_r_compatible_version')); - $cv_desc = chado_get_cv(array('name' => 'phenotype_collection_method')); - $cv_unit = chado_get_cv(array('name' => 'phenotype_measurement_units')); - } - else { - $cv_type = tripal_get_cv(array('name' => 'phenotype_measurement_types')); - $cv_rver = tripal_get_cv(array('name' => 'phenotype_r_compatible_version')); - $cv_desc = tripal_get_cv(array('name' => 'phenotype_collection_method')); - $cv_unit = tripal_get_cv(array('name' => 'phenotype_measurement_units')); - } - - // Get the cvterm id of the unit selected. - if (function_exists('chado_get_cvterm')) { - $cvterm_unit = chado_get_cvterm(array('name' => $unit, 'cv_id' => $cv_unit->cv_id)); - } - else { - $cvterm_unit = tripal_get_cvterm(array('name' => $unit, 'cv_id' => $cv_unit->cv_id)); - } - - // ADD. - if ($btn_submit == 'Add column header') { - // Uses project id. - // Insert cvterm. - $cvterm = tripal_insert_cvterm( - array( - 'id' => 'rawpheno_tripal:' . $trait_name, - 'name' => $trait_name, - 'cv_name' => 'phenotype_measurement_types', - 'definition' => $trait_def - ) - ); - - // Add a cvterm prop to store the R friendly version. - chado_insert_record('cvtermprop', - array( - 'cvterm_id' => $cvterm->cvterm_id, - 'type_id' => $cv_rver->cv_id, - 'value' => $rver, - 'rank' => 0 - ) - ); - - // Add a cvter prop to store the collection method. - chado_insert_record('cvtermprop', - array( - 'cvterm_id' => $cvterm->cvterm_id, - 'type_id' => $cv_desc->cv_id, - 'value' => $col_method, - 'rank' => 0 - ) - ); - - // Relate the cvterm to unit. - chado_insert_record('cvterm_relationship', - array( - 'type_id' => $cv_unit->cv_id, - 'object_id' => $cvterm->cvterm_id, - 'subject_id' => $cvterm_unit->cvterm_id, - ) - ); - - // Add entry to project cvterms table. - db_insert('pheno_project_cvterm') - ->fields( - array( - 'project_id' => $asset_id, - 'cvterm_id' => $cvterm->cvterm_id, - 'type' => $trait_type - ) - ) - ->execute(); - - drupal_set_message(t('You have successfully added trait to this project.'), 'status'); - } - // UPDATE. - else { - // Project id the trait is in. - $prj_id = $form_state['values']['prj_id']; - - // Uses cvterm id number. - // Update cvterm record (name and definition). - chado_update_record('cvterm', - array( - 'cvterm_id' => $asset_id, - 'cv_id' => $cv_type->cv_id - ), - array( - 'name' => $trait_name, - 'definition' => $trait_def - ) - ); - - // Update cvtermprop (rfriendly version). - chado_update_record('cvtermprop', - array( - 'cvterm_id' => $asset_id, - 'type_id' => $cv_rver->cv_id - ), - array( - 'value' => $rver - ) - ); - - // Update method of collection. - // When updating, make sure term has a collection method entry in cvtermprop. - // If none, add an entry. - $sql = "SELECT cvterm_id FROM {cvtermprop} - WHERE - cvterm_id = :cvterm_id - AND type_id = :cv_method - LIMIT 1"; - - $args = array(':cvterm_id' => $asset_id, ':cv_method' => $cv_desc->cv_id); - $result = chado_query($sql, $args); - - if ($result->rowCount() == 1) { - chado_update_record('cvtermprop', - array( - 'cvterm_id' => $asset_id, - 'type_id' => $cv_desc->cv_id - ), - array( - 'value' => $col_method - ) - ); - } - else { - chado_insert_record('cvtermprop', - array( - 'cvterm_id' => $asset_id, - 'type_id' => $cv_desc->cv_id, - 'value' => $col_method, - 'rank' => 0 - ) - ); - } - - // Update cvterm relationship (cvterm - unit). - chado_update_record('cvterm_relationship', - array( - 'object_id' => $asset_id - ), - array( - 'subject_id' => $cvterm_unit->cvterm_id - ) - ); - - // Update trait type. - db_update('pheno_project_cvterm') - ->fields(array('type' => $trait_type)) - ->condition('cvterm_id', $asset_id, '=') - ->condition('project_id', $prj_id, '=') - ->execute(); - - drupal_set_message(t('You have successfully updated a column header in this project.'), 'status'); - } - } -} - - -/** - * Function callback: Delete a user from a project. - * Note: The record of user is not physically deleted from the database. - * When deleting a user, it is only removed from the assigned project. - * - * @param $user_project_asset - * An array containing data and file information about a project the user is assigned. - */ -function rawpheno_admin_project_users($form, $form_state, $user_project_asset, $action) { - if ($action == 'delete') { - $path = url($GLOBALS['base_url'] . '/admin/tripal/extension/rawphenotypes/all_projects/' . $user_project_asset['project_id'] . '/project/manage'); - - db_delete('pheno_project_user') - ->condition('project_user_id', $user_project_asset['project_user_id']) - ->execute(); - - // Redirect user. - drupal_goto($path); - } -} - -/** - * Function callback: Delete environment data file from a project. - * - * @param $envdata_project_asset - * An array containing data and file information about an environment data file. - */ -function rawpheno_admin_project_envdata($form, $form_state, $envdata_project_asset, $action) { - if ($action == 'delete') { - db_delete('pheno_environment_data') - ->condition('environment_data_id', $envdata_project_asset['envdata_id']) - ->execute(); - - // To delete a file in Drupal use file_delete function. - // Get file object and load it to file delete funciton. - $file = file_load($envdata_project_asset['fid']); - file_delete($file, TRUE); - - // Assign a new sequence. This will maintain the order of sequence number. - $sql = "SELECT * FROM pheno_environment_data - WHERE project_id = :project_id AND location = :location AND year = :year - ORDER BY sequence_no ASC"; - - $result = chado_query($sql, array( - ':project_id' => $envdata_project_asset['project_id'], - ':location' => $envdata_project_asset['location'], - ':year' => $envdata_project_asset['year'], - )); - - if ($result->rowCount() > 0) { - foreach($result as $seq => $row) { - // Record. - chado_query("UPDATE pheno_environment_data SET sequence_no = :new_seqno WHERE environment_data_id = :envdata_id", - array( - ':new_seqno' => $seq + 1, - ':envdata_id' => $row->environment_data_id, - )); - } - } - - // Redirect user. - $path = url($GLOBALS['base_url'] . '/admin/tripal/extension/rawphenotypes/all_projects/' . $envdata_project_asset['project_id'] . '/project/manage'); - drupal_goto($path); - } -} - -/** - * Function callback: Construct a form to either modify or add a column header. - * - * @param $default - * An array containing default values of each form elements. - */ -function rawpheno_admin_render_form_trait($form, &$form_state, $default) { - // Get trait types array. - $a = rawpheno_function_trait_types(); - // In this trait types array, remove the plant property option. - // Allow only to add essential, optional, contributed or subset column header types. - unset($a['type4']); - - // Type is no user contributed. Do not suggest contributed so user will have not option to add contributed - // trait using admin since contributed traits are traits derived from submitted spreadsheet in stage 01 : Describe New Trait. - if (!isset($default['sel_trait_type']) || $default['sel_trait_type'] != $a['type5']) { - unset($a['type5']); - } - - // Get the values and use it as both key and value. - $t = array_values($a); - $trait_type = array_combine($t, array_map('strtoupper', $t)); - - // Determine if the header has data or is used in another project. - if ($default['count_data'] > 0 OR $default['count_project'] > 0) { - $form['fieldset_trait']['warning'] = array( - '#markup' => '
This column header has data associated to it or is used in another project.
', - ); - - $disabled = TRUE; - } - else { - $disabled = FALSE; - } - - // Project id the trait is in or the trait id number. - $form['fieldset_trait']['txt_id'] = array( - '#type' => 'hidden', - '#value' => $default['txt_id'], - ); - - // Exclusive to modifying trait, include the project id the trait is registered. - if (isset($default['prj_id']) && $default['prj_id'] > 0) { - $form['fieldset_trait']['prj_id'] = array( - '#type' => 'hidden', - '#value' => $default['prj_id'], - ); - } - - // Trait name field. - $form['fieldset_trait']['txt_trait_name'] = array( - '#type' => 'textfield', - '#title' => t('Name:'), - '#description' => t('A Concise human-readable name or label for the column header'), - '#default_value' => isset($default['txt_trait_name']) ? $default['txt_trait_name'] : '', - '#disabled' => $disabled, - '#required' => TRUE, - ); - - // Trait rep field. Default to none. - // Note: Trait Rep to call 1st, 2nd in unit is not final. - // Note: The list might change in the future. - $reps = rawpheno_function_trait_reps(); - $reps_val = array(); - foreach($reps as $r) { - $reps_val[] = $r . ';'; - } - $trait_rep = array('' => 'None') + array_combine($reps_val, $reps); - - $form['fieldset_trait']['sel_trait_rep'] = array( - '#type' => 'select', - '#title' => t('Trait Rep/Stages:'), - '#options' => $trait_rep, - '#default_value' => isset($default['sel_trait_rep']) ? $default['sel_trait_rep'] : reset($trait_rep), - '#disabled' => $disabled, - ); - - // Trait unit field. Default to none. - // Query available units in chado cvterm of type phenotype_measurement_unit. - $sql = " - SELECT t2.name, t2.name || ' : ' || t2.definition - FROM {cv} AS t1 INNER JOIN {cvterm} AS t2 USING (cv_id) - WHERE t1.name = 'phenotype_measurement_units' - ORDER BY t2.name ASC"; - - $unit = chado_query($sql) - ->fetchAllKeyed(); - - // Default to text unit. - $unit_keys = array_keys($unit); - $textunit = array_search('text', $unit_keys); - $textunit_id = $unit_keys[$textunit]; - - $form['fieldset_trait']['sel_trait_unit'] = array( - '#type' => 'select', - '#title' => t('Unit:'), - '#options' => $unit, - '#default_value' => isset($default['sel_trait_unit']) ? $default['sel_trait_unit'] : $textunit_id, - '#disabled' => $disabled, - ); - - // R Friendly field. - $r_version = isset($default['txt_trait_rfriendly']) ? ' (' . $default['txt_trait_rfriendly'] . ')' : ''; - - $form['fieldset_trait']['txt_trait_rfriendly'] = array( - '#type' => 'textfield', - '#title' => t('R Friendly:@r_version', array('@r_version' => $r_version)), - '#description' => t('Leave this field blank to let system generate R Friendly version'), - '#default_value' => '', - '#disabled' => FALSE, - ); - - if (isset($default['txt_trait_rfriendly']) AND !empty($default['txt_trait_rfriendly'])) { - // When editing header, add this hidden field containing the original R version value prior to saving. - // When user decides to provide an alternative r version then save it, otherwise save the value of this - // hidden field. - $form['fieldset_trait']['txt_trait_rfriendly_val'] = array( - '#type' => 'hidden', - '#default_value' => $default['txt_trait_rfriendly'], - ); - } - - // Trait definition field. - $form['fieldset_trait']['txt_trait_def'] = array( - '#type' => 'textarea', - '#title' => t('Definition:'), - '#description' => t('A human-readable text definition'), - '#required' => TRUE, - '#default_value' => isset($default['txt_trait_definition']) ? $default['txt_trait_definition'] : '', - '#disabled' => $disabled, - ); - - // Describe method of collection field. - $form['fieldset_trait']['txt_trait_method'] = array( - '#type' => 'textarea', - '#title' => t('Describe Method:'), - '#description' => t('Describe the method used to collect this data if you used a scale, be specific'), - '#required' => TRUE, - '#default_value' => isset($default['txt_trait_method']) ? $default['txt_trait_method'] : '', - '#disabled' => $disabled, - ); - - // Tell user about contributed trait when detected. Such trait is not included when generating data collection - // spreadsheet file - to incorporate it, please set the trait type to either essential or optional. - if (isset($a['type5']) && isset($default['sel_trait_type']) && $default['sel_trait_type'] == $a['type5']) { - // The header is contributed. Indicate to user as such. - $form['fieldset_trait']['warning_contributed'] = array( - '#markup' => '
This column header is a user contributed trait and is not incorporated in generating Data Collection Spreadsheet file for this Project. To include this trait, set the trait type to Essential or Optional.
', - ); - } - - // Trait is essential field. Default to unchecked. - $form['fieldset_trait']['sel_trait_type'] = array( - '#type' => 'select', - '#title' => t('Type:'), - '#description' => t('Set the type to ESSENTIAL to ensure this header must exists in the spreadsheet file'), - '#options' => $trait_type, - '#default_value' => isset($default['sel_trait_type']) ? $default['sel_trait_type'] : reset($trait_type), - '#disabled' => FALSE, - ); - - // Save trait button. - $form['fieldset_trait']['btn_trait_subtmit'] = array( - '#type' => 'submit', - '#value' => t('@save_or_add', array('@save_or_add' => $default['btn_trait_submit'])), - '#suffix' => ' * means field is required', - ); - - return $form; -} - - -/** - * Function to construct a trait given a name, trait rep and unit. - * - * @param $trait - * An array containing name, trait and unit. - * - * @return - * A string containing containing the column header in name (trait rep; unit) format. - */ -function rawpheno_admin_construct_trait_name($trait) { - $unit = ''; - - if (!empty($trait['unit']) OR !empty($trait['rep'])) { - $u = (empty($trait['rep'])) ? '' : $trait['rep'] . ' '; - if (empty($trait['unit'])) { - $u = trim($u); - } - - $unit = '(' . $u . $trait['unit'] . ')'; - } - - $name = ucfirst($trait['name'] . ' ' . rtrim($unit)); - - return trim($name); -} - - -/** - * Function callback: Save values in add existing column headers form. - */ -function rawpheno_admin_submit_add_existing($form, &$form_state) { - // Uses project id. - $project_id = $form_state['values']['txt_id']; - // All Types. - $types = rawpheno_function_trait_types(); - - // The type of header is set to OPTIONAL trait. - foreach(array_filter($form_state['values']['tbl_existing_headers']) as $m) { - if (isset($form_state['input']['traittype-' . $m]) AND $form_state['input']['traittype-' . $m] == 1) { - $trait_type = $types['type1']; - } - else { - $trait_type = $types['type2']; - } - - db_insert('pheno_project_cvterm') - ->fields( - array( - 'project_id' => $project_id, - 'cvterm_id' => $m, - 'type' => $trait_type) - ) - ->execute(); - } - - drupal_set_message(t('You have successfully added column headers to this project.'), 'status'); -} - - -/** - * Function callback: Validate add existing column headers. - */ -function rawpheno_admin_validate_add_existing($form, &$form_state) { - if (count(array_filter($form_state['values']['tbl_existing_headers'])) <= 0) { - form_set_error('tbl_existing_headers', t('No column headers selected.')); - } -} - - -/** - * Function callback: Save values in add user form. - */ -function rawpheno_admin_submit_add_user($form, $form_state) { - // Uses project id - $project_id = $form_state['values']['txt_id']; - $user = $form_state['values']['txt_autocomplete_user']; - - // Search user - $sql = "SELECT uid FROM {users} - WHERE - name = :name - AND uid NOT IN (SELECT uid FROM {pheno_project_user} WHERE project_id = :project_id AND status = 1) - LIMIT 1"; - - $args = array(':name' => $user, ':project_id' => $project_id); - $u = db_query($sql, $args) - ->fetchField(); - - db_insert('pheno_project_user') - ->fields(array( - 'project_id' => $project_id, - 'uid' => $u)) - ->execute(); - - drupal_set_message(t('You have successfully added users to this project.'), 'status'); -} - - -/** - * Function callback: Validate add user. - */ -function rawpheno_admin_validate_add_user($form, &$form_state) { - $project_id = $form_state['values']['txt_id']; - $user = $form_state['values']['txt_autocomplete_user']; - - if (empty($user)) { - form_set_error('txt_autocomplete_user', t('No user name supplied in the field.')); - } - else { - // Search user - $sql = "SELECT uid FROM {users} WHERE name = :name LIMIT 1"; - $args = array(':name' => $user); - $u = db_query($sql, $args); - - if ($u->rowCount() < 1) { - form_set_error('txt_autocomplete_user', t('User does not exist.')); - } - else { - $user_id = $u->fetchObject(); - - // Test if user was added twice in the same project. - $sql = "SELECT uid FROM pheno_project_user WHERE project_id = :project_id AND uid = :user_id"; - $args = array(':project_id' => $project_id, ':user_id' => $user_id->uid); - $u = db_query($sql, $args); - - if ($u->rowCount() > 0) { - form_set_error('txt_autocomplete_user', t('User was already added and cannot be added again')); - } - } - } -} - -/** - * Function callback: Save environment data file. - */ -function rawpheno_admin_submit_upload_envdata($form, &$form_state) { - $project_id = $form_state['input']['txt_id']; - $location = $form_state['values']['select_location']; - $year = $form_state['values']['select_year']; - - if ($project_id && $location && $year) { - $dir_public = 'public://'; - - // File extension validator. - $file_validate_ext = array( - 'file_validate_extensions' => array('xlsx csv tsv txt docx pptx'), - ); - - // Save env data file. - $env_file = file_save_upload('file', $file_validate_ext, $dir_public, FILE_EXISTS_RENAME); - - if ($env_file) { - // Rename file to include location, year, sequence no. - // Fetch the largest seq no in a project, location and year. - $sql = "SELECT sequence_no FROM pheno_environment_data - WHERE location = :location AND year = :year AND project_id = :project_id - ORDER BY sequence_no DESC LIMIT 1"; - - $result = chado_query($sql, array( - ':project_id' => $project_id, - ':location' => $location, - ':year' => $year, - )); - - $seq_no = ($result->rowCount() > 0) ? (int)$result->fetchField() + 1 : 1; - $u_no = date('ymdis'); - - $env_file_ext = pathinfo($env_file->filename, PATHINFO_EXTENSION); - $new_filename = str_replace(array(' ', '-', ','), '_', $location) . '_' . $year . '_' . $u_no . '_environment_data.' . $env_file_ext; - $new_filename = strtolower($new_filename); - - // Rename file. - file_move($env_file, $dir_public . $new_filename); - - // Update file table, Make file permanent. - $env_file->uri = $dir_public . $new_filename; - $env_file->status = FILE_STATUS_PERMANENT; - $env_file->filename = $new_filename; - - // Update file properties. - file_save($env_file); - // Tell Drupal, rawpheno is using this file. - file_usage_add($env_file, 'rawpheno', 'file', $env_file->fid); - - // Create a row in the table. - db_insert('pheno_environment_data') - ->fields(array( - 'project_id' => $project_id, - 'fid' => $env_file->fid, - 'location' => $location, - 'year' => $year, - 'sequence_no' => $seq_no)) - ->execute(); - - drupal_set_message(t('You have successfully uploaded environment data file to this project.'), 'status'); - } - } -} - -/** - * Function callback: Validate environment data file. - */ -function rawpheno_admin_validate_upload_envdata($form, &$form_state) { - // Location: - if (empty($form_state['values']['select_location'])) { - form_set_error('select_location', t('Location field is empty. Please select an option and try again.')); - } - - // Year: - if (empty($form_state['values']['select_year'])) { - form_set_error('select_year', t('Year field is empty. Please select an option and try again.')); - } - - // Environment Data File: - $field_value = $_FILES['files']['name']['file']; - - if (empty($field_value)) { - form_set_error('file', t('Environment Data File is empty. Please select a file and try again.')); - } - - // File extension is handled by file_validate_extensions above. -} - -/** - * Function get properties of project the user is assigned. - * - * @param $asset_id - * An integer containing the project-user id number. - * - * @return - * An array containing data and file information of a project the user is assigned. - */ -function rawpheno_admin_user_assets($asset_id) { - // Array to hold user assets. - $arr_project_assets = array(); - - $sql = "SELECT project_id, uid, project_user_id - FROM {pheno_project_user} WHERE project_user_id = :project_user_id LIMIT 1"; - - $args = array(':project_user_id' => $asset_id); - $user = db_query($sql, $args) - ->fetchObject(); - - $arr_project_assets['user_id'] = $user->uid; - $arr_project_assets['project_id'] = $user->project_id; - $arr_project_assets['project_user_id'] = $user->project_user_id; - - // Given the project id this user is assigned, test if it has any backup files. - $sql = "SELECT COUNT(file_id) FROM {pheno_backup_file} WHERE project_user_id = :project_user_id"; - $file_count = db_query($sql, $args) - ->fetchField(); - - $arr_project_assets['project_file_count'] = $file_count; - - // Given the project id this user is assigned, test if it has data. - $sql = "SELECT COUNT(plant_id) FROM {pheno_plant_project} - WHERE project_id = (SELECT project_id FROM {pheno_project_user} WHERE project_id = :project_id AND uid = :user_id)"; - - $args = array(':project_id' => $user->project_id, ':user_id' => $user->uid); - $data_count = db_query($sql, $args) - ->fetchField(); - - $arr_project_assets['project_data_count'] = $data_count; - - return $arr_project_assets; -} - -/** - * Function fetch environment data properties. - * - * @param $asset_id - * An integer containing the environment data id number. - * - * @return - * An array containing data and file information of environment data. - */ -function rawpheno_admin_envdata_asset($asset_id) { - $sql = "SELECT environment_data_id, project_id, location, year, fid - FROM pheno_environment_data WHERE environment_data_id = :envdata_id - LIMIT 1"; - - $envdata = chado_query($sql, array(':envdata_id' => $asset_id)); - - if ($envdata) { - $e = $envdata->fetchObject(); - - return array( - 'envdata_id' => $e->environment_data_id, - 'project_id' => $e->project_id, - 'location' => $e->location, - 'year' => $e->year, - 'fid' => $e->fid, - ); - } - else { - return 0; - } -} - - -function rawpheno_admin_mark_empty($val) { - return (empty($val)) ? '-' : $val; -} diff --git a/include/rawpheno.backup.form.inc b/include/rawpheno.backup.form.inc deleted file mode 100644 index 698a1c0..0000000 --- a/include/rawpheno.backup.form.inc +++ /dev/null @@ -1,399 +0,0 @@ -uid); - - // No projects assined to the user. - if (count($all_project) < 1) { - return $form; - } - - // Navigation button. Related page of rawdata/summary page is download page. - $form['page_button'] = array( - '#type' => 'markup', - '#markup' => t('Standard Procedure ❯'), - ); - - - // After a backup file, user is redirected to raw/backup/up. This means that user has backed up a file - // and that in hook_file() function a session is set to contain the validation result of the validation - // performed to the file. This block is to show that validation result to user. - if ($project_id == 'up' AND isset($_SESSION['rawpheno']['backup_file_validation_result'])) { - // Read the session variable. - $status = $_SESSION['rawpheno']['backup_file_validation_result']; - - // Tell user that a file has been successfully backed up. - $form['message_upload_result'] = array( - '#markup' => '
You have successfully uploaded a file into your account. - Below is the result of the validation performed to the file.
', - ); - - // Uset the validation result theme used in upload page to theme the validation result. - if (isset($status['status'])) { - $check_limit_message = '
' . $status['check_limit'] . '
'; - $status = $status['status']; - } - else { - $check_limit_message = ''; - } - - $form['validation_result'] = array( - '#markup' => $check_limit_message . theme('rawpheno_upload_validation_report', array('status' => $status)), - ); - - // Finally, add a link to upload another file. - $form['link_upload_file'] = array( - '#markup' => l(t('Backup Another File'), './phenotypes/raw/backup', - array('attributes'=> array('id' => 'link-success-upload-file'))) - ); - - // A session variable is set in hook_insert_file() that contains the validation result of the - // previous file backup. When user wants to upload another file, reset this session variable. - unset($_SESSION['rawpheno']['backup_file_validation_result']); - } - - - // Archive and delete file commands. - if (isset($file_id_no) AND $file_id_no > 0) { - // Define valid commands. - $arr_valid_commands = array('archive', 'restore', 'delete', 'desc', 'asc'); - - // Test if file exists before modifying the file. - $sql = "SELECT t2.file_id, t2.fid - FROM {pheno_project_user} AS t1 INNER JOIN {pheno_backup_file} AS t2 USING(project_user_id) - WHERE t2.file_id = :file_id AND t1.project_id = :project_id AND t1.uid = :user_id - LIMIT 1"; - - $args = array(':file_id' => $file_id_no, ':project_id' => $project_id, ':user_id' => $GLOBALS['user']->uid); - $f = db_query($sql, $args); - - if ($f->rowCount() == 1 AND in_array($command, $arr_valid_commands)) { - if ($command == 'archive') { - // Archive the file. - db_update('pheno_backup_file') - ->fields(array('archive' => 'y')) - ->condition('file_id', $file_id_no, '=') - ->execute(); - } - elseif ($command == 'restore') { - // Restore the file. - db_update('pheno_backup_file') - ->fields(array('archive' => 'n')) - ->condition('file_id', $file_id_no, '=') - ->execute(); - } - elseif ($command == 'delete') { - // Physically delete file. - // Delete record in pheno_backup_file. - $num_deleted = db_delete('pheno_backup_file') - ->condition('file_id', $file_id_no, '=') - ->condition('archive', 'y', '=') - ->execute(); - - // To delete a file in Drupal use file_delete function. - // Get file object and load it to file delete funciton. - $xlsfile = $f->fetchObject(); - - $file = file_load($xlsfile->fid); - file_delete($file, TRUE); - } - } - else { - // File not found; - drupal_set_message(t('Command cannot be executed to this file.'), 'error'); - } - } - - - // FORM. - // Construct backup file form. - // Array to hold project assigned to user. - $arr_my_project = $all_project; - - if (!isset($project_id) AND $project_id <= 0) { - if (count($arr_my_project) > 0) { - // Default to the only project when there is a single project assigned to user - // otherwise, let user select a project. - if (count($arr_my_project) > 1) { - // When there is more that 1 project, tell user to select a project. - $project_options = array(0 => '---') + $arr_my_project; - } - else { - // Else, default to the only project available. - $project_options = $arr_my_project; - } - - // Yes, user has at least a project in the account. - $form['backup_sel_project'] = array( - '#type' => 'select', - '#title' => t('Please select a project:'), - '#options' => $project_options, - '#default_value' => array_keys($project_options)[0], - '#id' => 'backup_sel_project' - ); - - // 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['bdnd'] = array( - '#type' => 'dragndrop_upload', - '#title' => t('Spreadsheet file:'), - '#file_upload_max_size' => '10M', - '#upload_location' => 'public://', - '#upload_event' => 'manual', - '#upload_validators' => array( - 'file_validate_extensions' => array('xlsx xls'), - ), - '#droppable_area_text' => t('Drag your Microsoft Excel Spreadsheet file here'), - '#progress_indicator' => 'throbber', - '#progress_message' => 'Please wait...', - '#allow_replace' => 1, - '#standard_upload' => 1, - '#id' => 'backup_dropzone', - '#upload_button_text' => t('Upload'), - ); - - $form['backup_txt_description'] = array( - '#type' => 'textarea', - '#title' => t('Describe spreadsheet file:'), - '#description' => t('Use this field to add comments, description or notes to the spreadsheet file that you want to backup'), - '#rows' => 2, - '#resizable' => FALSE, - ); - } - else { - // User has no project in the account. - $form['no_data'] = array( - '#markup' => t('You have no project in your account. Please contact the administrator of this website.'), - '#prefix' => '
', - '#suffix' => '
', - ); - } - } - else { - // User is managing files in a project. - // Upload is disabled. Display a summary, - $go_back_link = l('Go back to projects table', 'phenotypes/raw/backup'); - - if ($project_id > 0 AND isset($arr_my_project[$project_id])) { - $form['summary'] = array( - '#markup' => $go_back_link . t('

Project: @project

', array('@project' => $arr_my_project[$project_id])), - ); - } - } - - - // Array to hold table headers. - $arr_headers = array(); - // Array to hold table rows. - $arr_rows = array(); - // Array to hold archive files. - $arr_row_archive = array(); - // Array to hold table properties. - $arr_tbl_args = array( - 'sticky' => TRUE, - ); - - - // Create a projects table and projects file table (file browser). If a project_id is supplied - // create a table listing all files in that project, otherwise, the default, create a table - // listing all projects the user is assigned to and a summary of the number of backed up file - // a project has. - - // Create a table showing files backed up in a project. - if (isset($project_id) AND $project_id > 0) { - // Test that sort request is valid. If not in the valid sort array default to desc. - $sort = (in_array(trim($command), array('asc', 'desc'))) ? $command : 'desc'; - - // Query files in the project. - $sql = "SELECT - t2.file_id, t2.version, t2.validation_result, t2.notes, - t3.filename, t3.timestamp, t3.filesize, t3.uri, t2.archive - FROM - {pheno_project_user} AS t1 - INNER JOIN {pheno_backup_file} AS t2 using(project_user_id) - INNER JOIN {file_managed} AS t3 using(fid) - WHERE t1.project_id = :project_id AND t1.uid = :user_id - ORDER BY t3.timestamp %s"; - $sql = sprintf($sql, strtoupper($sort)); - - $args = array(':project_id' => $project_id, ':user_id' => $GLOBALS['user']->uid); - $my_files = db_query($sql, $args); - - // Create table elements. - // Rows. - if ($my_files->rowCount() > 0) { - foreach($my_files as $f) { - // File header will contain filename, timestamp and filesize. - $row_file = l($f->filename . '(' . format_size($f->filesize) . ')', - file_create_url($f->uri), - array('attributes' => array('target' => '_blank'))); - - $row_file .= t('
Uploaded: @timestamp', array('@timestamp' => format_date($f->timestamp))); - - // Notes. - $row_notes = (empty($f->notes)) - ? ' ' - : '
' . $f->notes . '
'; - - // Validation result. - $row_validation_entries = str_replace(array('#item: (passed)', '#item: (failed)'), - array('

(PASSED)', '

* (FAILED)'), - $f->validation_result) . '

'; - $alert = ''; - if (($n = substr_count($row_validation_entries, 'FAILED')) > 0) { - $alert = '' . $n . ' Validation errors'; - } - - $row_validation = $alert . '
'. $row_validation_entries .'
'; - - // Determine if file is archived or not. - if ($f->archive == 'n') { - // File is active. - $row_download = l('', file_create_url($f->uri), - array('attributes' => array('target' => '_blank', 'class' => 'link-download', 'title' => 'Download file'))); - - $row_del = l('', './phenotypes/raw/backup/' . $project_id .'/archive/' . $f->file_id, - array('attributes' => array('class' => 'link-archive', 'title' => 'Archive file'))); - - $arr_rows[] = array( - $row_file, // Filename. - '#' . $f->version, // File version. - $row_notes, // Notes, comments to file. - $row_validation, // Validation result performed to file. - $row_download, // Link to download file. - $row_del // Link delete (archive) file. - ); - } - else { - // File is archived. - $row_download = l('', './phenotypes/raw/backup/' . $project_id .'/restore/' . $f->file_id, - array('attributes' => array('class' => 'link-restore', 'title' => 'Restore file'))); - - $row_del = l('', './phenotypes/raw/backup/' . $project_id .'/delete/' . $f->file_id, - array('attributes' => array('class' => 'link-delete', 'title' => 'Delete file'))); - - $arr_row_archive[] = array( - $row_file, // Filename. - '#' . $f->version, // File version. - $row_notes, // Notes, comments to file. - $row_validation, // Validation result performed to file. - $row_download, // Link to download file. - $row_del // Link to delete (physically delete) file. - ); - } - } - } - else { - // No files in a project. - $arr_tbl_args['empty'] = t('No files in this project.'); - } - - // Header. - $s = ($sort == 'desc') ? 'asc' : 'desc'; - // Make the varsion column header an active link to sort records by version number. - $header_sort = l('Version', 'phenotypes/raw/backup/' . $project_id . '/' . $s); - array_push($arr_headers, t('File'), t($header_sort), t('Notes'), t('Validation Result'), '-', '-'); - - // Table id. - $attr_table_id = 'tbl-user-project-file'; - } - else { - // By default, show a table showing summary of projects assigned and the number - // of files or backed up files in a project. - $sql = "SELECT COUNT(t2.file_id) AS filecount, t1.project_id - FROM {pheno_project_user} AS t1 LEFT JOIN {pheno_backup_file} AS t2 USING(project_user_id) - WHERE t1.uid = :uid AND t1.project_id IN (:my_project_id) - GROUP BY project_user_id, t1.project_id - ORDER BY t1.project_id DESC"; - - $args = array(':uid' => $GLOBALS['user']->uid, ':my_project_id' => array_keys($arr_my_project)); - $my_files = db_query($sql, $args); - - // Create table elements. - // Rows. - if ($my_files->rowCount() > 0) { - foreach($my_files as $f) { - $row_view = l('View', './phenotypes/raw/backup/' . $f->project_id); - $row_prj_name = l($arr_my_project[$f->project_id], 'phenotypes/raw/backup/' . $f->project_id); - - $arr_rows[] = array( - $row_prj_name, - $f->filecount, - $row_view - ); - } - } - else { - // No project in the account. - $arr_tbl_args['empty'] = t('No project in your account.'); - } - - // Header. - array_push($arr_headers, t('Project'), t('File'), t('View')); - - // Table id. - $attr_table_id = 'tbl-user-project'; - } - - - // Update table properties. - $arr_tbl_args['header'] = $arr_headers; - $arr_tbl_args['rows'] = $arr_rows; - - // Update table attributes. - $arr_tbl_args['attributes']['id'] = $attr_table_id; - - // Render the table element. - $form['tbl_root_dir'] = array( - '#markup' => theme('table', $arr_tbl_args), - ); - - - // When there are archived files, render it as well. - if (count($arr_row_archive) > 0) { - // Show archive files table. - $archive_link = l(count($arr_row_archive). ' Files', '#', array('attributes' => array('class' => 'link-archive'))); - $form['tbl_archive_file'] = array( - '#type' => 'markup', - '#markup' => '
' . $archive_link . '
', - ); - - $arr_tbl_args['header'] = $arr_headers; - $arr_tbl_args['rows'] = $arr_row_archive; - $arr_tbl_args['attributes']['id'] = 'tbl_project_archive_file'; - - $form['tbl_archive_dir'] = array( - '#markup' => theme('table', $arr_tbl_args), - ); - } - - // Attach JavaScript. - $form['#attached']['js'] = array($path . 'js/rawpheno.backup.script.js'); - - return $form; -} diff --git a/include/rawpheno.download.form.inc b/include/rawpheno.download.form.inc deleted file mode 100644 index 0f85d6e..0000000 --- a/include/rawpheno.download.form.inc +++ /dev/null @@ -1,366 +0,0 @@ - '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('Project'), - '#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 (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 (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), - ), - ), - ); -} diff --git a/include/rawpheno.function.measurements.inc b/include/rawpheno.function.measurements.inc deleted file mode 100644 index c07b1ae..0000000 --- a/include/rawpheno.function.measurements.inc +++ /dev/null @@ -1,1336 +0,0 @@ - 'essential', - 'type2' => 'optional', - 'type3' => 'subset', - 'type4' => 'plantproperty', - 'type5' => 'contributed'); -} - - -/** - * Function that lists trait reps or number of trials or R value/number - * that precedes the unit of a measurement type. - */ -function rawpheno_function_trait_reps() { - return array('1st', '2nd', '3rd', '4th', 'R1', 'R3', 'R5', 'R7'); -} - - -/** - * Function that list default/initial units available to this module. - * - * @param $set - * A string indicating the type of set to return. - * def - unit and definition. - * type - unit and data type. - */ -function rawpheno_function_default_unit($set) { - // Type is required when programmatically generating data collection spreadsheet file - // in instructions page. - return array( - 'date' => ($set == 'def') ? 'Date' : 'date', - 'count' => ($set == 'def') ? 'Count' : 'integer', - 'days' => ($set == 'def') ? 'Days' : 'integer', - 'cm' => ($set == 'def') ? 'Centimeters' : 'integer', - 'scale' => ($set == 'def') ? 'Scale: 1-5' : 'integer', - 'g' => ($set == 'def') ? 'Grams (g)' : 'integer', - 'text' => ($set == 'def') ? 'Alphanumeric' : 'string', - 'y/n/?' => ($set == 'def') ? 'Yes, No or ? - Not sure' : 'string' - ); -} - - -/** - * Function to test if the module has a project set up for user. - * - * @return FALSE when there is no project to work on. - */ -function rawpheno_function_project() { - $sql = "SELECT FROM pheno_project_cvterm"; - $project_count = db_query($sql) - ->rowCount(); - - return ($project_count <= 0) ? 0 : 1; -} - - -/** - * Function to test if the module has a project set up for user and - * that project has dataset associated to it. - * - * @return FALSE when there is no data. - */ -function rawpheno_function_data() { - $sql = "SELECT plant_project_id FROM pheno_plant_project"; - $stock_count = db_query($sql) - ->rowCount(); - - return ($stock_count <= 0) ? 0 : 1; -} - - -/** - * Function to load projects appointed to user. In addition this function will - * filter projects that don't have at least 1 essential trait. - * - * @param $user - * An integer containing the user id of the currently logged user. - * - * @return - * An array containing available projects. - */ -function rawpheno_function_user_project($user_id) { - $trait_type = rawpheno_function_trait_types(); - $my_project = array(); - - $sql = "SELECT - t1.name, - t1.project_id, - COUNT(CASE WHEN t2.type = :essential_trait THEN 1 END) AS essential_count - FROM - {project} AS t1 - INNER JOIN pheno_project_cvterm AS t2 USING (project_id) - LEFT JOIN pheno_project_user AS t3 USING (project_id) - WHERE t3.uid = :user_id - GROUP BY t1.project_id - ORDER BY t1.project_id DESC"; - - $args = array(':essential_trait' => $trait_type['type1'], ':user_id' => $user_id); - $p = chado_query($sql, $args); - - if ($p->rowCount() > 0) { - foreach($p as $a) { - if ($a->essential_count > 0) { - $my_project[$a->project_id] = $a->name; - } - } - } - - return $my_project; -} - - -/** - * Function create JSON summary data required in rawdata page. - * - * @param $project_id - * An integer containing the project id number. - * @param $trait_id - * An integer containing the id number of a trait in a project. - * @param $type - * A string indicating the type of data a given trait holds. It could be integer or text data type. - * @param $data - * An array containing unique values in a given trait. - * - * @return - * A JSON summary data. - * - * NOTE: The following column headers are excluded from the traits that can be visualized. - * Planting Date (date), - * Disease-specific Comments, - * Comments - * # of Seeds Planted (count) - */ -function rawpheno_function_create_json($project_id, $trait_id, $type, $option, $data = NULL) { - // Array to hold the computed Bin. - $arr_bin_json = array(); - // Array to hold the data. - $arr_data_json = array(); - - // Categorize: Location or Year. - $category_val = $option['category']; - // Options: If Location (years) | If Year (location). - $option_val = $option['option']; - - // Array to store each location found in the data set. - $not_data = array(); - - $categorize = ($category_val == 'location') - ? 'AND SUBSTRING(t3.value, 1, 4) = :category_option' - : 'AND t4.value = :category_option'; - - // NUMERIC DATA TYPE. - if ($type == 'int') { - // Get the average of each value and in a stock, in a location. - $sql = sprintf(" - SELECT - t1.stock_id AS stock, - ROUND(AVG(t2.value::numeric)) AS average, - SUBSTRING(t3.value, 1, 4) AS planting_year, - t4.value AS location - FROM - {pheno_plant} AS t1 - INNER JOIN {pheno_measurements} AS t2 USING (plant_id) - INNER JOIN {pheno_measurements} AS t3 USING (plant_id) - INNER JOIN {pheno_plantprop} AS t4 USING (plant_id) - WHERE - t1.plant_id IN (SELECT plant_id FROM {pheno_plant_project} WHERE project_id = :project_id) - AND t2.type_id = :trait_id - AND t3.type_id = (SELECT cvterm_id FROM chado.cvterm cvt LEFT JOIN chado.cv ON cv.cv_id=cvt.cv_id WHERE cvt.name = 'Planting Date (date)' AND cv.name = 'phenotype_measurement_types') - %s - AND t4.type_id = (SELECT cvterm_id FROM chado.cvterm cvt LEFT JOIN chado.cv ON cv.cv_id=cvt.cv_id WHERE cvt.name = 'Location' AND cv.name = 'phenotype_plant_property_types') - GROUP BY t1.stock_id, t2.value, t3.value, t4.value - ORDER BY location, average ASC", $categorize); - - $args = array(':trait_id' => $trait_id, ':project_id' => $project_id, ':category_option' => $option_val); - - // Result set for averages. - $d = db_query($sql, $args); - - // No data return 0. - if ($d->rowCount() < 1) { - return 0; - } - - // Get the min and max values from the averages. - $all_average = $d->fetchCol(1); - $max = max($all_average); - $min = min($all_average); - - // Window size from cookie variable. - $win_width = $_COOKIE['rawphenoRawdataSW']; - - // Total number of bins. - // http://www.statisticshowto.com/choose-bin-sizes-statistics/ - // https://www.qimacros.com/histogram-excel/how-to-determine-histogram-bin-interval/ - $data_points = $d->rowCount(); - - $total_bins = ($win_width <= 850) ? 10 : 20; - - // Size of each bin/ interval. - $interval = round($max/$total_bins); - $stock_bin = 0; - - $i = 0; - $to = 0; - // Round the number down. - // eg. start at 11 round it to 10. - $from = floor($min / 10) * 10; - - while($to < $max) { - $from = ($i == 0) ? $from : ($to + 1); - $to = $from + $interval; - - $arr_bin_json[$i] = $from . '-' . $to; - $i++; - } - - // Assign each row with a bin. - $m = db_query($sql, $args); - - foreach($m as $stock) { - $stock_avg = (int)$stock->average; - - // Find the bin for this row. - foreach($arr_bin_json as $b) { - list($from, $to) = explode('-', $b); - if ($stock_avg >= (int)$from AND $stock_avg <= (int)$to) { - $stock_bin = $b; - break; - } - } - - $title = ($category_val == 'location') ? $stock->location : $stock->planting_year; - $arr_data_json[] = array( - 'title' => $title, - 'bin' => $stock_bin - ); - } - } - // TEXT DATA TYPE - elseif ($type == 'txt' OR $type == 'y/n/?') { - $sql = sprintf(" - SELECT - t1.stock_id AS stock, - t2.value AS v, - SUBSTRING(t3.value, 1, 4) AS planting_year, - t4.value AS location - FROM - {pheno_plant} AS t1 - INNER JOIN {pheno_measurements} AS t2 USING (plant_id) - INNER JOIN {pheno_measurements} AS t3 USING (plant_id) - INNER JOIN {pheno_plantprop} AS t4 USING (plant_id) - WHERE - t1.plant_id IN (SELECT plant_id FROM {pheno_plant_project} WHERE project_id = :project_id) - AND t2.type_id = :trait_id - AND t3.type_id = (SELECT cvterm_id FROM chado.cvterm cvt LEFT JOIN chado.cv ON cv.cv_id=cvt.cv_id WHERE cvt.name = 'Planting Date (date)' AND cv.name = 'phenotype_measurement_types') - %s - AND t4.type_id = (SELECT cvterm_id FROM chado.cvterm cvt LEFT JOIN chado.cv ON cv.cv_id=cvt.cv_id WHERE cvt.name = 'Location' AND cv.name = 'phenotype_plant_property_types') - ORDER BY location ASC", $categorize); - - $args = array(':trait_id' => $trait_id, ':project_id' => $project_id, ':category_option' => $option_val); - - // Result set for data/values. - $d = db_query($sql, $args); - - // No data return 0. - if ($d->rowCount() < 1) { - return 0; - } - - // Text, list, enumeration of values. - $arr_bin_json = ($type == 'txt') ? $data : array('y', 'n', '?'); - - $bin = ''; - - foreach($d as $e) { - // Find the bin for this data. - foreach($arr_bin_json as $b) { - if ($b == $e->v) - $bin = $b; - } - - $title = ($category_val == 'location') ? $e->location : $e->planting_year; - $arr_data_json[] = array( - 'title' => $title, - 'bin' => $bin - ); - } - } - // SCALE DATA TYPE - elseif ($type == 'scale') { - $sql = sprintf(" - SELECT - t1.stock_id AS stock, - t2.value AS v, - SUBSTRING(t3.value, 1, 4) AS planting_year, - t4.value AS location - FROM - {pheno_plant} AS t1 - INNER JOIN {pheno_measurements} AS t2 USING (plant_id) - INNER JOIN {pheno_measurements} AS t3 USING (plant_id) - INNER JOIN {pheno_plantprop} AS t4 USING (plant_id) - WHERE - t1.plant_id IN (SELECT plant_id FROM {pheno_plant_project} WHERE project_id = :project_id) - AND t2.type_id = :trait_id - AND t3.type_id = (SELECT cvterm_id FROM chado.cvterm cvt LEFT JOIN chado.cv ON cv.cv_id=cvt.cv_id WHERE cvt.name = 'Planting Date (date)' AND cv.name = 'phenotype_measurement_types') - %s - AND t4.type_id = (SELECT cvterm_id FROM chado.cvterm cvt LEFT JOIN chado.cv ON cv.cv_id=cvt.cv_id WHERE cvt.name = 'Location' AND cv.name = 'phenotype_plant_property_types') - ORDER BY location ASC", $categorize); - - $args = array(':trait_id' => $trait_id, ':project_id' => $project_id, ':category_option' => $option_val); - - // Result set for data/values. - $d = db_query($sql, $args); - - // No data return 0. - if ($d->rowCount() < 1) { - return 0; - } - - // Scale uses scale codes stored in scale memeber table. - // Get the scale codes and make it the default bins. The scale codes are added in the install of this module. - $s = db_query("SELECT code FROM {pheno_scale_member} ORDER BY code ASC"); - if ($s->rowCount() > 0) { - foreach($s as $b) { - $arr_bin_json[] = $b->code; - } - } - else { - // In case the scales are not available. Do not visualize. - return 0; - } - - if ($d->rowCount() > 0) { - foreach($d as $e) { - - $title = ($category_val == 'location') ? $e->location : $e->planting_year; - $arr_data_json[] = array( - 'title' => $title, - 'bin' => $e->v - ); - } - } - } - - // Return bin and data. - return array('bin' => $arr_bin_json, 'data' => $arr_data_json); -} - - -/** - * Function get cvterm definitions. - * - * @param $cvterm - * A string containing the cvterm name. - * - * @return - * A string containing the cvterm name definition. - */ -function rawpheno_function_get_definition($cvterm) { - // Array to hold definitions. - $definition = array(); - - $method = 'method'; - $define = 'define'; - -$definition['Planting Date (date)'] = array( -$method => -'Record the date the seeds were sown.', -$define => -'The date should be the same for all plots, but could be different if circumstances such as bad weather prevent the seeding of all plots on the same day. If such a situation does occur, highlight rows with a different planting date so it is obvious to the data recorder, since they will have different days after planting values to record for that particular date.' -); - -$definition['Days to Emergence (days)'] = array( -$method => -'Record the number of days after planting for which 10% of seeds have emerged.', -$define => -'Emergence = seedling stem/leaves have become visible.' -); - -$definition['# of Emerged Plants (count)'] = array( -$method => -'Record the number of plants which emerged. -When: Record values once plants begin to flower or have elongated tendrils.', -$define => -'Emergence = seedling stem/leaves have become visible.' -); - -$definition['Days till 10% of Plants have Elongated Tendrils (days)'] = array( -$method => -'Record the number of days after planting for which 10% of plants have an elongated tendril. -Some plants may not produce elongated tendrils but develop a rudimentary tendril only 2-3 mm long. If this applies to more than 90% of plants in the plot, the "Days till 10% have Elongated Tendril" should be left blank.', -$define => -'Elongated tendril = 5 mm and longer.' -); - -$definition['Days till 10% of Plants have One Open Flower (R1; days)'] = array( -$method => -'Record the number of days after planting for which 10% of plants have at least one open flower.', -$define => -'Open flower = flower banner (standard petal) is visible. -R1 = One open flower at any node.' -); - -$definition['# Nodes on Primary Stem at R1 (1st; count)'] = array( -$method => -'Record the number of nodes on the primary stem when the first flower opens. -Record values from 2 plants, taken from the middle of the plot.', -$define => -'Node = positions on stem where leaves and buds/branches grow from. -The first few nodes can loose their leaves and may not be readily visible. First flower may NOT be on the primary stem but we want the # of nodes on the primary stem that day.' -); - -$definition['# Nodes on Primary Stem at R1 (2nd; count)'] = array( -$method => -'Record the number of nodes on the primary stem when the first flower opens. -Record values from 2 plants, taken from the middle of the plot.', -$define => -'Node = positions on stem where leaves and buds/branches grow from. -The first few nodes can loose their leaves and may not be readily visible. First flower may NOT be on the primary stem but we want the # of nodes on the primary stem that day.' -); - -$definition['Days till 10% of Plants have Pods (R3; days)'] = array( -$method => -'Record the number of days after planting for which 10% of plants have pods. Note: Pods can be present but still covered with flower petals, for ease of data collection, only count the plant as having a pod if you can visually see the pod without having to remove flower petals.', -$define => '' -); - -$definition['Days till 10% of Plants have fully Swollen Pods (R5; days)'] = array( -$method => -'Record the number of days after planting for which 10% of plants have pods with fully swollen seeds (that fill more than half of the pod area).', -$define => -'Plant with pods = pods are visible without having to remove flower petals. -Swollen Pod = seeds have swollen to their max size and fill more than half the pod area. -R5 = Seed in any single pod on nodes 10-13 of the basal primary branch are swollen and completely fill the pod cavity. -Genotypic variation in seed size and pod structure will require the use of discretion by the data recorder, since not all genotypes have seeds which fully fill the pod cavity at maturity. -This corresponds to physiological maturity at which point the seeds have swollen to their max size. At this stage, seed coat is formed and there is a colour change in the cotyledons (except QG1!).' -); - -$definition['Days till 10% of Plants have 1/2 Pods Mature (R7; days)'] = array( -$method => -'Record the number of days after planting for which 10% of plants have 1/2 of their pods mature.', -$define => -'Mature pod = dry pod ready to be harvested -Before the pods dry out they lose their green pigmentation, often looking pale, but will still contain moisture, which you can feel when you touch the pod. Pods that are considered mature will have changed colour and be dry to the touch. -R7 = The leaves start yellowing and 50% of the pods have turned yellow. -Pod maturity is not always accompanied by a yellowing of the pod – some pods turn white, some are pigmented and may have patterns, CDC QG2 will remain green' -); - -$definition['R7 Traits: Lowest Pod Height (1st; cm)'] = array( -$method => -'Record the distance (cm) from the soil to the bottom of the lower most pod. -Record values from 2 plants, taken from the middle of the plot. -When: Record values when 10% of plants have 1/2 pods mature (R7). -Record values from 2 plants, taken from the middle of the plot.', -$define => '' -); - -$definition['R7 Traits: Lowest Pod Height (2nd; cm)'] = array( -$method => -'Record the distance (cm) from the soil to the bottom of the lower most pod. -Record values from 2 plants, taken from the middle of the plot. -When: Record values when 10% of plants have 1/2 pods mature (R7). -Record values from 2 plants, taken from the middle of the plot.', -$define => '' -); - -$definition['R7 Traits: Canopy Height (1st; cm)'] = array( -$method => -'Record the distance (cm) from the soil to the highest part of the plant canopy. -Record values from 2 plants, taken from the middle of the plot. -When: Record values when 10% of plants have 1/2 pods mature (R7). -Record values from 2 plants, taken from the middle of the plot. -DO NOT stretch the plant. Leave as is.', -$define => '' -); - -$definition['R7 Traits: Canopy Height (2nd; cm)'] = array( -$method => -'Record the distance (cm) from the soil to the highest part of the plant canopy. -Record values from 2 plants, taken from the middle of the plot. -When: Record values when 10% of plants have 1/2 pods mature (R7). -Record values from 2 plants, taken from the middle of the plot. -DO NOT stretch the plant. Leave as is.', -$define => '' -); - -$definition['Days till Harvest (days)'] = array( -$method => -'Record the number of days from planting to harvest.', -$define => '' -); - -$definition['Diseases Present (y/n/?)'] = array( -$method => -'Record the presence of any disease, and if able, describe or make notes.', -$define => -'Scale: y = disease present, n = no disease present ? = unsure -There is a "Disease-specific Comments" column for making any notes related to disease including but not limited to the observation that many or specific diseases are present.' -); - -$definition['Disease-specific Comments'] = array( -$method => -'Feel free to mention if multiple or specific diseases are present. Note: disease ratings for specific diseases should go in a separate column if you would like to measure them.', -$define => '' -); - -$definition['Lodging (Scale: 1-5) upright - lodged'] = array( -$method => -'Record the degree of plant lodging. -Scale: -1 = vertical/upright -2 = leaning -3 = most plants at 45° angle -4 = all plants 10-45° from ground -5 = most plants flat/prostrate -When: Record value when harvesting the plot.', -$define => -'lodged = plant canopy is no longer vertical to the ground.' -); - -$definition['Subset Traits: # Peduncles (count)'] = array( -$method => -'Leave this column as is, DO NOT make any changes (unless you were unable to obtain 20 peduncles). -This has been preset to 20, because that is how many should be collected.', -$define => -'peduncle = a stalk supporting an inflorescence (group/cluster of flowers).' -); - -$definition['Subset Traits: # Pods (count)'] = array( -$method => -'Record the total number of pods on the 20 peduncles collected for the subset traits.', -$define => '' -); - -$definition['Subset Traits: # Seeds (count)'] = array( -$method => -'Record the total number of seeds from pods counted for the previous trait ("Subset Traits: # Pods").', -$define => '' -); - -$definition['Straw Biomass (g)'] = array( -$method => -'Record the mass (g) of dry, above ground plant material from each plot.', -$define => -'Straw = all above ground biomass excluding the seed.' -); - -$definition['Total Seed Mass (g)'] = array( -$method => -'Record the total mass (g) of all seeds harvested from each plot.', -$define => '' -); - -$definition['Total # of Seeds (count)'] = array( -$method => -'Record the total number of seeds harvested from each plot.', -$define => '' -); - -$definition['100 Seed Mass (g)'] = array( -$method => -'Count 100 seeds and record the mass (g). -Do not calculate this value from "Total Seed Mass" and "Total Number of Seeds".', -$define => '' -); - -$definition['Comments'] = array( -$method => '', -$define => '' -); - -$definition['R7 Traits: Canopy Width (cm)'] = array( -$method => -'Record the max canopy width (cm). -Record values from 2 plants, taken from the middle of the plot. -When: Record values when 10% of plants have 1/2 pods mature (R7). -Record values from 2 plants, taken from the middle of the plot. -Add a column with the header "R7 Traits: Canopy Width (cm)" if you would like to record this trait.', -$define => '' -); - -$definition['R7 Traits: Plant Length (cm)'] = array( -$method => -'Record the distance (cm) from the soil to the end of the longest stem. -Record values from 2 plants, taken from the middle of the plot. -When: Record values when 10% of plants have 1/2 pods mature (R7). -Record values from 2 plants, taken from the middle of the plot. -DO stretch the plant. -Add a column with the header "R7 Traits: Plant Length (cm)" if you would like to record this trait.', -$define => '' -); - - - return (isset($definition[$cvterm])) ? $definition[$cvterm] : null; -} - - -/** - * Function extract the unit from a column header. - * - * @param $header - * A string containing the header. - * - * @return - * A string containing the unit of the header. - */ -function rawpheno_function_header_unit($header) { - preg_match("/.*\(([^)]*)\)/", $header, $match); - $u = (isset($match[1])) ? $match[1] : 'text'; - - $chars = rawpheno_function_trait_reps(); - array_push($chars, ';', ': 1-5'); - - $unit = str_ireplace($chars, '', $u); - - return trim(strtolower($unit)); -} - - -/** - * Function to get all properties of a column header. - * - * @param $asset_id - * An integer containing record id number of a given column header. - * @param $dataset - * An string indicating whether to include data count. - * - * @return - * An array containing all properties (project, name, data, etc.) of a column header. - */ -function rawpheno_function_header_properties($asset_id, $dataset = NULL) { - // Array to hold properties. - $arr_properties = array(); - - // Get project information and header type. - $sql = "SELECT t1.project_id, t1.name, t2.cvterm_id, t2.type - FROM {project} AS t1 INNER JOIN pheno_project_cvterm AS t2 USING(project_id) - WHERE t2.project_cvterm_id = :record_id LIMIT 1"; - - $args = array(':record_id' => $asset_id); - $h = chado_query($sql, $args) - ->fetchObject(); - - $arr_properties['in_project_id'] = $h->project_id; - $arr_properties['in_project_name'] = $h->name; - $arr_properties['cvterm_id'] = $h->cvterm_id; - $arr_properties['type'] = $h->type; - - // cvterm properties. - $sql = "SELECT * FROM {cvterm} WHERE cvterm_id = :cvterm_id"; - $args = array(':cvterm_id' => $arr_properties['cvterm_id']); - - $h = chado_query($sql, $args) - ->fetchObject(); - - $arr_properties['name'] = $h->name; - $arr_properties['definition'] = empty($h->definition) ? '' : $h->definition; - - // cvterm R Version and Collection Method. - $arr_properties['method'] = ''; - - $h = chado_select_record('cvtermprop', array('value', 'type_id'), array('cvterm_id' => $arr_properties['cvterm_id'])); - - if (function_exists('chado_get_cv')) { - $r_version = chado_get_cv(array('name' => 'phenotype_r_compatible_version')); - } - else { - $r_version = tripal_get_cv(array('name' => 'phenotype_r_compatible_version')); - } - - foreach($h as $c) { - $val = isset($c->value) ? $c->value : ''; - - if ($c->type_id == $r_version->cv_id) { - $arr_properties['r_version'] = $val; - } - else { - $arr_properties['method'] = $val; - } - } - - $args = array(':project_id' => $arr_properties['in_project_id'], ':cvterm_id' => $arr_properties['cvterm_id']); - - // Request full dataset including basic stats about the header. - if ($dataset == 'full') { - // Count data associated to column header. - $sql = "SELECT COUNT(type_id) AS data_count FROM {pheno_measurements} - WHERE type_id = :cvterm_id AND plant_id IN (SELECT plant_id FROM {pheno_plant_project} WHERE project_id = :project_id)"; - - $h = db_query($sql, $args) - ->fetchObject(); - - $arr_properties['count_data'] = $h->data_count; - - // Count the projects this same column header is being used. - $sql = "SELECT COUNT(project_id) AS project_count - FROM {pheno_project_cvterm} WHERE project_id <> :project_id AND cvterm_id = :cvterm_id"; - - $h = db_query($sql, $args) - ->fetchObject(); - - $arr_properties['count_project'] = $h->project_count; - } - - return $arr_properties; -} - - -/** - * Function: return the version of the d3 library installed. - * - * @see implementation of hook_libraries_info(). - * implementation of hook_requirements(). - */ -function rawpheno_function_d3_version() { - if (libraries_get_path('d3')) { - $file = libraries_get_path('d3') . '/d3.js'; - $f = fopen($file, 'r'); - - if ($f === FALSE) { - $ver = 0; - } - else { - $c = fread($f, filesize($file)); - - $lines = explode("\n", $c); - $i = 0; - - // Since the version is in the first 20-30 line, break loop past line #. - foreach($lines as $l) { - if ($i > 0 && stristr($l, 'version')) { - $ver = trim(str_replace(array('var version', 'version:', 'version', ':', '"', 'var', '=', ';'), '', strtolower($l))); - break; - } - - if ($i == 30) { - $ver = 0; - break; - } - - $i++; - } - } - - fclose($f); - } - else { - $ver = 0; - } - - return $ver; -} - - -/** - * Function: return the version of the spreadsheet writer library installed. - * - * @see implementation of hook_libraries_info(). - * implementation of hook_requirements(). - */ -function rawpheno_function_sreader_version() { - if (libraries_get_path('d3')) { - $file = libraries_get_path('spreadsheet-reader') . '/CHANGELOG.md'; - - $f = fopen($file, 'r'); - if ($f === FALSE) { - $ver = 0; - } - else { - $c = fread($f, filesize($file)); - - $lines = explode("\n", $c); - $i = 0; - - $ver = str_replace('#', '', $lines[0]); - } - } - else { - $ver = 0; - } - - return $ver; -} - - -/** - * Function: ensure that user applied the patch to spreadsheet reader library - */ -function rawpheno_function_library_patch() { - $lib = array(); - - $lib['xlsx'] = libraries_get_path('spreadsheet-reader') . '/SpreadsheetReader_XLSX.php'; - $lib['xls'] = libraries_get_path('spreadsheet-reader') . '/php-excel-reader/excel_reader2.php'; - - $patch['xlsx'] = '$Format[\'Code\'] = \'Y-m-d\''; - $patch['xls'] = '0xf => "M-d-Y"'; - - $pass = 0; - foreach($lib as $ver => $file) { - $f = fopen($file, 'r'); - $line = fread($f, filesize($file)); - fclose($f); - - if (strpos($line, $patch[$ver])) { - $pass++; - } - } - - return ($pass == 2) ? 1 : 0; -} - - -/** - * Function: ensure that trait name in the spreadsheet file is in trait name (unit) format. - * e.g. Planting Date )date( or Planting Date (date. - * - * @param $trait - * A string containing the trait name parsed in the spreadsheet file. - * - * @return true or false. - */ -function rawpheno_valid_trait_format($trait) { - $is_valid = TRUE; - $trait = trim($trait); - // Test if the trait uses parenthesis. - // ( and ) indicate that unit is supplied with the trait name, - // otherwise, ignore this check (e.g. comments) - if (preg_match('/[(|)]/', $trait) === 1 && $trait != 'Lodging (Scale: 1-5) upright - lodged') { - // With these characters present, test if the format is trait name (unit). - - // Pattern: first string can be any chars but ( and ) followed by 0 or more space then 1 ( then any chars and ) - // plus optional any chars but ( and ) as the end of string. - $is_valid = (preg_match('/\A[^()]+\s*\({1}[^)(]+\)[^)(]*\z/i', $trait) === 1) ? TRUE : FALSE; - } - - return $is_valid; -} - - -/* Function test if a given trait has data for a given project and location. - * - * @param $trait - * An integer containing the cvterm id number of a trait. - * @param $location - * An array of locations. - * @param $project - * An array of project id. - * - * @return - * Boolean true - has data and false - no data. - */ -function rawpheno_download_trait_has_data($trait, $location, $project) { - $in_location = ''; - $args = array(':trait' => $trait, ':project' => $project); - - if (!empty($location)) { - $in_location = ' value IN (:location) AND '; - $args[':location'] = $location; - } - - $sql = sprintf("SELECT type_id - FROM pheno_measurements - WHERE - plant_id IN - (SELECT plant_id - FROM pheno_plantprop INNER JOIN pheno_plant_project USING(plant_id) - WHERE %s project_id IN (:project)) - AND type_id = :trait - LIMIT 1", $in_location); - - $d = chado_query($sql, $args); - - return ($d->rowCount()) ? TRUE : FALSE; -} - - -/** - * Function get all traits given a location and project. - * - * @param $location - * An array containing selected location from the location select box. - * @param $project - * An array containing the selected project form the project select box. - * @return - * An array containing all traits available. - */ -function rawpheno_download_load_traits($location, $project) { - $opt_trait = array(); - - $sql = "SELECT DISTINCT t1.cvterm_id, t1.name - FROM - {cvterm} AS t1 - RIGHT JOIN pheno_project_cvterm AS t2 USING(cvterm_id) - WHERE - t2.project_id IN - - (SELECT project_id - FROM pheno_plantprop INNER JOIN pheno_plant_project USING(plant_id) - WHERE value IN (:location) AND project_id IN (:project)) - - AND t1.name NOT IN ('Rep', 'Entry', 'Location', 'Name', 'Plot', 'Planting Date (date)', '# of Seeds Planted (count)') - ORDER BY t1.cvterm_id ASC"; - - $args = array(':location' => $location, ':project' => $project); - $t = chado_query($sql, $args); - - if ($t->rowCount()) { - foreach($t as $m) { - $has_data = rawpheno_download_trait_has_data($m->cvterm_id, $location, $project); - if ($has_data) { - $opt_trait[$m->cvterm_id] = $m->name; - } - } - } - else { - $opt_trait[0] = 'No traits available'; - } - - - return $opt_trait; -} - - -/** - * Function get the R Compatible version of a trait. - * - * @param: - * $cvterm_id a numeric value containing the cvterm id number of a given trait. - * @return - * A string containing the R compatible version of the trait. - */ -function rawpheno_download_r_compatible($cvterm_id) { - if (function_exists('chado_get_cv')) { - $cv_rver = chado_get_cv(array('name' => 'phenotype_r_compatible_version')); - } - else { - $cv_rver = tripal_get_cv(array('name' => 'phenotype_r_compatible_version')); - } - - // @note cvtermprop cvterm_id + type_id + rank is unique (constraint). - $sql = "SELECT value FROM {cvtermprop} WHERE cvterm_id = :cvterm_id AND type_id = :cv_id AND rank = 0"; - $args = array(':cvterm_id' => $cvterm_id, ':cv_id' => $cv_rver->cv_id); - - $r = chado_query($sql, $args) - ->fetchField(); - - return (isset($r) AND !empty($r)) ? $r : null; -} - - -/** - * Function to check if a given plant property header combination has a record in the database. - * The value returned will help determine if a plant_id should be re-used or a new plant_id id - * should be created/inserted when processing a row in the spreadsheet file. - * - * @param $plot - * A string composed of stock_id, plot, rep, location and year - concatinated as a single string. - * @param $project_id - * An integer containing the current active project id number. - * - * @return integer - * Plant Id number of the record that matched or 0 if no match was found. - */ -function rawpheno_function_plot_exists($plot, $project_id) { - // TODO: move the condition in the where clause of this query. - $sql = " - SELECT - t1.plant_id - FROM - pheno_plant AS t1 - INNER JOIN pheno_plantprop AS t2 USING(plant_id) - INNER JOIN pheno_plantprop AS t3 USING(plant_id) - INNER JOIN pheno_plantprop AS t4 USING(plant_id) - INNER JOIN pheno_measurements AS t5 USING(plant_id) - WHERE - t1.plant_id IN (SELECT plant_id FROM pheno_plant_project WHERE project_id = :project_id) - AND t2.type_id = (SELECT cvterm_id FROM {cvterm} cvt LEFT JOIN {cv} ON cv.cv_id=cvt.cv_id WHERE cvt.name = 'Plot' AND cv.name = 'phenotype_plant_property_types') - AND t3.type_id = (SELECT cvterm_id FROM {cvterm} cvt LEFT JOIN {cv} ON cv.cv_id=cvt.cv_id WHERE cvt.name = 'Rep' AND cv.name = 'phenotype_plant_property_types') - AND t4.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 t5.type_id = (SELECT cvterm_id FROM {cvterm} cvt LEFT JOIN {cv} ON cv.cv_id=cvt.cv_id WHERE cvt.name = 'Planting Date (date)' AND cv.name = 'phenotype_measurement_types') - AND - t1.stock_id || '-' || - t2.value || '-' || - t3.value || '-' || - t4.value || '-' || - SUBSTRING(t5.value, 1, 4) = :plot - LIMIT 1"; - - $args = array(':project_id' => $project_id, ':plot' => $plot); - $m = chado_query($sql, $args); - - return $m->fetchField(0); -} - - -/** - * Function to get stock_id number of a stock name in chado.stock. - * NOTE: AGL applies only to when saving data. Validation (germplasm exists) - * is based on stock w/o this token. - * - * @param $stock_name - * A string, the name of the stock. - * @param $project_name - * A string, the name of the project. - * - * @return - * An integer, the stock id number. - */ -function rawpheno_function_getstockid($stock_name, $project_name) { - // Calling all modules implementing hook_rawpheno_AGILE_stock_name_alter(): - drupal_alter('rawpheno_AGILE_stock_name', $stock_name, $project_name); - - // Limit the search to project organism. - $result = chado_query( - "SELECT stock_id FROM {stock} WHERE name = :name LIMIT 1", - array(':name' => $stock_name) - ); - - return ($result) ? $result->fetchField() : 0; -} - - -/** - * Function: get project name. - * - * @param $project_id - * An integer value, the project id number. - * - * @return - * A string value, the name for the project from chado.project table. - */ -function rawpheno_function_getproject($project_id) { - $result = chado_query("SELECT name FROM {project} WHERE project_id = :project_id LIMIT 1", array( - ':project_id' => $project_id, - )); - - return ($result) ? $result->fetchField() : 0; -} - - -/** - * Function to get environment data files. - * - * @param $project - * An array, the titles or names of projects. - * @param $location - * An array of locations. - * - * @return - * An array of environment data filenames. - */ -function rawpheno_function_getenv($project, $location) { - $sql = "SELECT filename FROM pheno_environment_data INNER JOIN file_managed USING (fid) - WHERE project_id IN (:project) AND location IN(:location) - ORDER BY project_id, location, year, sequence_no ASC"; - - $files = chado_query($sql, array( - ':project' => $project, - ':location' => $location, - )); - - if ($files->rowCount() > 0) { - return $files->fetchAll(); - } - else { - return 0; - } -} - - -/** - * Function to fetch support email address using 4 options below. - * - * 1. Using a hook_alter implementation request a support email by providing a Drupal username. - * 2. Using a hook_alter implementation request a support email by providing a Drupal user id #. - * 3. Using a hook_alter implementation set a support email by providing an email address. - * 4. *Default fetch email address of Drupal user id #1 or Drupal site-wide email address. - * - * @return - * A string, support email address. - */ -function rawpheno_function_get_support_email() { - $support_email = variable_get('kp_rawpheno_support_email'); - - if (!empty($support_email)) { - return $support_email; - } - - // Nothing set. - $support_email = '#'; - - // As provided by hook_alter implementation - following command#value format. - // example: $support_email = 'username#DrupalUsername'; - drupal_alter('rawpheno_support_email', $support_email); - - @list($command, $value) = explode('#', $support_email); - - switch ($command) { - case 'username': - case 'useridno': - // By username or user id no. - $user = ($command == 'username') ? 'name' : 'uid'; - - $sql = sprintf("SELECT mail FROM users WHERE %s = :user LIMIT 1", $user); - $support_email = chado_query($sql, array(':user' => $value)) - ->fetchField(); - - break; - - case 'emailadd': - // Use the email as provided. - $support_email = $value; - - break; - - default: - // Get email from user id #1. - $support_email = chado_query("SELECT mail FROM users WHERE uid = 1") - ->fetchField(); - - if (empty($support_email)) { - // Or site-wide email provided when setting up a Drupal site. - $support_email = variable_get('site-mail'); - } - } - - // Set this variable to support email so this can be accessed - // by the entire rawphenotypes module as well as pages hosting - // this application (eg. video demo page) - - // This will also set an email in case one is determined. - $cur_email = variable_get('kp_rawpheno_support_email'); - if (empty($cur_email) || $cur_email != $support_email) { - // Update only when new support email and no value detected. - variable_set('kp_rawpheno_support_email', $support_email); - } - - - return $support_email; -} diff --git a/include/rawpheno.instructions.form.inc b/include/rawpheno.instructions.form.inc deleted file mode 100755 index ab91e81..0000000 --- a/include/rawpheno.instructions.form.inc +++ /dev/null @@ -1,506 +0,0 @@ - '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' => '
Project 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 a project') + $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 'Project not found.'; - } - } - else { - // Project id is not valid. - print 'Project ID number is invalid.'; - } -} diff --git a/include/rawpheno.rawdata.form.inc b/include/rawpheno.rawdata.form.inc deleted file mode 100755 index 9b84d40..0000000 --- a/include/rawpheno.rawdata.form.inc +++ /dev/null @@ -1,128 +0,0 @@ - '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 project 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/include/rawpheno.tripaldownload.inc deleted file mode 100644 index d55777c..0000000 --- a/include/rawpheno.tripaldownload.inc +++ /dev/null @@ -1,428 +0,0 @@ - 'Raw Phenotypes CSV', - // A human readable description of the format. - 'format' => 'Comma-separated Values', - // An array of functions that the API will use to customize your experience. - 'functions' => array( - // The function that tripal jobs will call to generate the file. - 'generate_file' => 'rawpheno_trpdownload_generate_file', - // OPTIONAL: provide a summary to the user on the download page. - 'summarize' => 'rawpheno_trpdownload_summarize_download', - // OPTIONAL: determine your own filename. - 'get_filename' => 'rawpheno_trpdownload_get_filename', - // OPTIONAL: Change the file suffix (defaults to .txt) - 'get_file_suffix' => 'rawpheno_trpdownload_get_suffix', - // OPTIONAL: determine the human-readable format based on a function. - 'get_format' => 'rawpheno_trpdownload_get_readable_format', - ), - ); - - return $types; -} - -/** - * Generate a file summary. - */ -function rawpheno_trpdownload_summarize_download($vars) { - // Path to module. - $path = drupal_get_path('module', 'rawpheno') . '/theme/'; - // Style nav links. - $style = 'float: left; padding: 20px 0 0 0; height: 50px; font-size: 1em; margin: 0 2.5%; text-align:center; width: 20%; color: #118324 !important'; - // Style span element. - $style2 = 'background-color: #FFFFFF; display: block; margin-top: 24px; padding: 5px 0;'; - - // Div container to hold summary of options selected by user. - $summary = ''; - - // PROJECTS: - $q = trim(implode('', $vars['download_args']['q'])); - $q = base64_decode($q); - list($project, $location, $traits, $r_version, $envdata, $envfile) = explode('&', $q); - - $tmp = trim(str_replace('p=', '', $project)); - $project = explode(',', $tmp); - - $sql = "SELECT name FROM {project} WHERE project_id IN (:project)"; - $args = array(':project' => $project); - $p = chado_query($sql, $args) - ->fetchAllKeyed(0,0); - $summary .= '
  • ' . count($p) . ' PROJECTS:
    ' . implode(', ', $p) . '
  • '; - - // LOCATIONS: - $location = trim(str_replace('l=', '', $location)); - - if ($location == '0') { - // Load all locations available. - $sql = "SELECT DISTINCT value AS 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') - ORDER BY value ASC"; - $location = chado_query($sql) - ->fetchCol(0); - } - else { - $location = explode('+', $location); - } - $summary .= '
  • ' . count($location) . ' LOCATIONS:
    ' . implode(' - ', $location) . '
  • '; - - // TRAITS: - $traits = trim(str_replace('t=', '', $traits)); - - if ($traits == 0) { - // Load all traits available. - $t = rawpheno_download_load_traits($location, $project); - $traits = array_keys($t); - } - else { - $traits = explode(',', $traits); - } - - $sql = "SELECT name FROM {cvterm} WHERE cvterm_id IN (:cvterm_id)"; - $args = array(':cvterm_id' => $traits); - $t = chado_query($sql, $args) - ->fetchAllKeyed(0,0); - $summary .= '
  • ' . count($t) . ' TRAITS:
    ' . implode(', ', $t) . '
  • '; - - // RVERSION: - $r_version = trim(str_replace('r=', '', $r_version)); - - $r_ver = ($r_version == 1) ? 'Yes' : 'No'; - $summary .= '
  • R FRIENDLY:
    ' . $r_ver . '
  • '; - - // INCLUDE ENVIRONMENT DATA. - $envdata = trim(str_replace('e=', '', $envdata)); - $e_data = ($envdata == 1) ? 'Yes' : 'No'; - $summary .= '
  • INCLUDE ENVIRONMENT DATA:
    ' . $e_data . '
  • '; - - $output = '
    ' . $summary . '
    '; - - // Environment data files. - if ($envdata == 1 && $envfile !== 0) { - $envfile = trim(str_replace('file=', '', $envfile)); - - $file_icon = theme_image(array( - 'path' => drupal_get_path('module','trpdownload_api').'/theme/icons/file_generic.128.png', - 'alt' => 'download environment data file', - 'attributes' => array() - )); - - // File type: - $file_type = (strpos($envfile, 'tar')) ? 'TAR Archive File' : 'Microsoft Excel File'; - // File link: - $file_link = l($envfile, - file_create_url('public://') . $envfile, - array('attributes' => array('download' => $envfile, 'target' => '_blank')) - ); - - $output .= '
    -
    -
    ' - - . $file_icon . - - '
    -

    Environment Data File:

    - -
    Format:' . $file_type . '
    -
    -
    -
    -
    '; - - $margin = '200px'; - } - else { - $margin = '30px'; - } - - // Div container to hold navigation links to rawphenotypes page. - $output .= '
    -
    '; - - return $output; -} - - -/** - * Generate a readable and unique filename for the file to be generated. - */ -function rawpheno_trpdownload_get_filename($vars) { - $filename = 'rawpheno_csv' . date('YMd') .'_'. time(); - return $filename; -} - - -/** - * Determine the file suffix for the file to be generated. - */ -function rawpheno_trpdownload_get_suffix($vars) { - return 'csv'; -} - -/** - * Function callback: determine the human-readable file format. - */ -function rawpheno_trpdownload_get_readable_format($vars) { - return 'Comma-separated Values'; -} - -/** - * Function callback: generate csv file. - */ -function rawpheno_trpdownload_generate_file($variables, $job_id = NULL) { - // Get query string and filename. - $code = ''; - foreach($variables as $l => $v) { - if(is_array($v)) { - foreach($v as $j => $m) { - if ($j == 'code') { - $code = $m; - } - } - } - - if ($l == 'filename') { - $filename = $v; - } - } - - $q = base64_decode($code); - list($project, $location, $traits, $r_version,,) = explode('&', $q); - - // Projects: - $tmp = trim(str_replace('p=', '', $project)); - $project = explode(',', $tmp); - - // Locations: - $location = trim(str_replace('l=', '', $location)); - - if ($location == '0') { - // Load all locations available. - $sql = "SELECT DISTINCT value AS 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') - ORDER BY value ASC"; - $location = chado_query($sql) - ->fetchCol(0); - } - else { - $location = explode('+', $location); - } - - // Traits - $traits = trim(str_replace('t=', '', $traits)); - - if ($traits == 0) { - // Load all traits available. - $t = rawpheno_download_load_traits($location, $project); - $traits = array_keys($t); - } - else { - $traits = explode(',', $traits); - } - - // Rversion - $r_version = trim(str_replace('r=', '', $r_version)); - - // Add planting date and # of seeds planted to the list of traits. - // But, before doing this, make sure that these two column headers - // are part or the project in the first place. - $arr_notso_trait = array('Planting Date (date)', '# of Seeds Planted (count)'); - foreach($arr_notso_trait as $nt) { - $sql = "SELECT cvterm_id FROM {cvterm} RIGHT JOIN pheno_project_cvterm USING(cvterm_id) - WHERE name = :cvterm AND project_id IN (:project_id) LIMIT 1"; - - $n = chado_query($sql, array(':cvterm' => $nt, ':project_id' => $project)); - - if ($n->rowCount() == 1) { - $traits[] = $n->fetchField(); - } - } - - // 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)) "; - - // Query values required by sub query. - $arr_q_string = array(':project' => $project, ':location' => $location, ':traits' => $traits); - - // 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. - $sql = "SELECT 'B' || t2.cvterm_id AS id, t2.name - FROM {pheno_plantprop} t1 LEFT JOIN chado.cvterm t2 ON t2.cvterm_id = t1.type_id - WHERE t1.plant_id IN" . $sub_sql . " - - UNION - - SELECT 'C' || t2.cvterm_id AS id, t2.name - FROM {pheno_measurements} t1 LEFT JOIN chado.cvterm t2 ON t2.cvterm_id=t1.type_id - WHERE t2.cvterm_id IN (:traits) AND t1.plant_id IN" . $sub_sql . " - GROUP BY t2.cvterm_id, t2.definition"; - - $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; - - // Get the R Friendly version when user requests for it. - if ($r_version == 1) { - // Get the R compatible version. - $id_no = (int)trim(str_replace(array('B', 'C'), '', $r->id)); - $rfreindly = rawpheno_download_r_compatible($id_no); - - // When no equivalent R version is available, load the definition instead. - $def = ($rfreindly == null) ? $r->name : $rfreindly; - } - - // Column headers array. - $header[ $r->id ] = $def; - } - - // Sort array by key. - ksort($header); - - // 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 - FROM {chado.stock} AS t1 INNER JOIN {pheno_plant} AS t2 USING(stock_id) - WHERE t2.plant_id IN" . $sub_sql - - // Then we add on the required information (ie: plot, entry, rep, location) - . "UNION - - SELECT t1.plant_id AS id, t1.type_id AS tid, t2.name AS def, t1.value AS value, 'B' AS grp - FROM {pheno_plantprop} AS t1 - INNER JOIN chado.cvterm AS t2 ON t1.type_id = t2.cvterm_id - WHERE t1.plant_id IN" . $sub_sql - - // Finally we add in all the traits. - . "UNION - - SELECT t1.plant_id AS id, t1.type_id as tid, t2.name AS def, ARRAY_TO_STRING(ARRAY_AGG(DISTINCT t1.value), '') AS value, 'C' as grp - FROM {pheno_measurements} AS t1 - INNER JOIN chado.cvterm AS t2 ON t1.type_id = t2.cvterm_id - WHERE t2.cvterm_id IN (:traits) AND t1.plant_id IN" . $sub_sql - - // 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"; - - $results = db_query($sql, $arr_q_string); - - if ($results) { - // Directories: - $dir = array( - 'tripal_download' => variable_get('trpdownload_fullpath'), - 'tripal_temp' => variable_get('file_temporary_path'), - 'tripal_public' => drupal_realpath('public://'), - ); - - $filepath = $dir['tripal_download'] . $filename; - - // drush_print("Generating CSV File: " . $filepath); - $FILE = fopen($filepath, 'w') or die ('Uable to create file to write to'); - fputcsv($FILE, $header); - - // Build a multi-dimensional array with all the data in it. This array is keyed - // 1st by plant_id and then by Group||cvterm_id (ie: B4243). - // Note: this first array will not have any missing data cells filled in. - $rows = array(); - foreach($results as $r) { - $rows[ $r->id ][ $r->grp . $r->tid ] = $r->value; - } - - // Total lines; - $total_lines = count($rows); - // INFO: - // drush_print('Total Lines: ' . $total_lines); - - // Now we want to process each row to add entries for missing data. - // We will do this by looping through the header for each row and if there isn't - // already data set for that cell then we will add missing data. - // Note: We will save the data to the file as we go. - $cur_line = 0; - $past_percent = 0; - // drush_print('0% complete...'); - - foreach ($rows as $row) { - // To file... to screen... - $percent = round(($cur_line / $total_lines) * 100); - if ((($percent % 5) === 0) && ($past_percent != $percent)) { - - // drush_print($percent . '% complete...'); - - db_query('UPDATE {tripal_jobs} SET progress=:percent WHERE job_id=:id', - array(':percent' => $percent, ':id' => $job_id)); - } - - // First, add missing data for each entry in the header that is not in the row. - foreach ($header as $id => $title) { - if (!isset($row[$id])) { - $row[$id] = 'NA'; - } - else { - $l = explode('', $row[$id]); - - if (count($l) >= 2) { - $row[$id] = str_replace('',',', $row[$id]); - // INFO: - // Multiple values detected. - // drush_print('Multiple values found: Line #' . ($cur_line + 1) . ' Col: ' . $title); - } - } - } - - // Sort the row based on the keys to make sure they match with the header. - ksort($row); - - // Write this row to the file as CSV. - fputcsv($FILE, $row); - - $cur_line++; - $past_percent = $percent; - } - - // Finally, close the file. - fclose($FILE); - - if ($percent != 100) { - // drush_print('100% complete...'); - } - - // drush_print('Download complete'); - } -} diff --git a/include/rawpheno.upload.excel.inc b/include/rawpheno.upload.excel.inc deleted file mode 100755 index ef4fbd8..0000000 --- a/include/rawpheno.upload.excel.inc +++ /dev/null @@ -1,1271 +0,0 @@ - $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/include/rawpheno.upload.form.inc deleted file mode 100755 index 190784b..0000000 --- a/include/rawpheno.upload.form.inc +++ /dev/null @@ -1,1260 +0,0 @@ - '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 a project') + $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 project 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 = ' -
      -
    • - ' . count($all_headers) . ' Column Headers. -
    • - -
    • - No Additional Column Headers Found. -
    • -
    - '; - - $form['xls_summary_fldset'] = array( - '#type' => 'fieldset', - '#title' => t('Describe new trait'), - ); - - $form['xls_summary_fldset']['information'] = array( - '#type' => 'markup', - '#markup' => $markup, - ); - - $form['notice'] = array( - '#type' => 'markup', - '#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
    ' - ); - } - elseif ($job_status == 'error') { - // Has error. - $form['notice'] = array( - '#markup' => '
    It appears that you are attempting to submit a spreadsheet that has errors.
    ' - ); - } - elseif ($job_status == 'cancelled') { - // Is cancelled. - $form['notice'] = array( - '#markup' => '
    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.
    ' - ); - } - } - else { - // Not authorized. - $form['notice'] = array( - '#markup' => '
    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/include/rawpheno.upload.helpers.inc deleted file mode 100755 index 3bc05be..0000000 --- a/include/rawpheno.upload.helpers.inc +++ /dev/null @@ -1,69 +0,0 @@ - 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; - } -} diff --git a/include/rawpheno.validation.inc b/include/rawpheno.validation.inc deleted file mode 100644 index 7fffba4..0000000 --- a/include/rawpheno.validation.inc +++ /dev/null @@ -1,738 +0,0 @@ - 'Uploaded file is a Microsoft Excel Spreadsheet.', - 'scope' => 'file', - 'message callback' => NULL, - 'validation callback' => 'validator_is_excel_validate_file', - ); - - $validators['tab_exists'] = array( - 'label' => 'The "Measurements" tab exists and has content.', - 'scope' => 'file', - 'message callback' => 'validator_tab_exists_generate_msg', - 'validation callback' => 'validator_tab_exists_validate_file', - ); - - // Ensure that specific traits are in the spreadsheet. - $validators['column_exists'] = array( - 'label' => 'Essential traits are present.', - 'scope' => 'header', - 'message callback' => 'validator_column_exists_generate_msg', - 'validation callback' => 'validator_column_exists_validate_header', - ); - - // Ensure that required information is present for all cells in those columns. - // Get plant prop traits entry, location, plot and rep. - $arr_plantprop_name = rawpheno_function_headers('plantprop'); - // Add name and planting date to the list. - // Planting date must have a value since date in this cell is used in visualizations. - array_push($arr_plantprop_name, 'Name', 'Planting Date'); - - $validators['required_info'] = array( - 'label' => 'Required Information is present and numbers are not negative.', - 'scope' => 'subset', - 'headers' => $arr_plantprop_name, - 'message callback' => 'validator_required_info_generate_msg', - 'validation callback' => 'validator_required_info_validate_cell', - ); - - // Ensure that the type of data in a cell is consistent with the unit of measurement. - $validators['units_match_type'] = array( - 'label' => 'Data was measured using expected units.', - 'scope' => 'subset', - 'message callback' => 'validator_units_match_type_generate_msg', - 'validation callback' => 'validator_units_match_type_validate_cell', - ); - - // Ensure that all germplasm already exists. - $validators['germplasm_present'] = array( - 'label' => 'All Germplasm names are recognized by this resource.', - 'scope' => 'all', - 'headers' => array('Name'), - 'message callback' => 'validator_germplasm_present_generate_msg', - 'validation callback' => 'validator_germplasm_present_validate_cell', - ); - - // Ensure that a project is selected from the project select box - $validators['project_selected'] = array( - 'label' => 'Project is selected.', - 'scope' => 'project', - 'message callback' => NULL, - 'validation callback' => 'validator_is_project_selected', - ); - - return $validators; -} - -/** - * Check that the current file has an extension of xlsx or xls. - * - * @param $file - * A drupal managed_file object describing the uploaded spreadsheet. - * @return - * TRUE if the file is either xls or xlsx; FALSE otherwise. - */ -function validator_is_excel_validate_file($file) { - - // First test extension. - $xls_extension = pathinfo($file->filename, PATHINFO_EXTENSION); - $xls_mime = $file->filemime; - - if (($xls_extension == 'xlsx' OR $xls_extension == 'xls') && - ($xls_mime == 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' - OR $xls_mime == 'application/vnd.ms-excel')) { - - // Then test that the file can be opened. - // This checks against PDFs masquerading as XLS ;-). - try { - $xls_obj = rawpheno_open_file($file); - } - catch (Exception $E) { - return FALSE; - } - - if ($xls_obj) { - return TRUE; - } - } - - return FALSE; -} - -/** - * Check that there is a measurements tab. - * - * @param $file - * A drupal managed_file object describing the uploaded spreadsheet. - * @return - * TRUE if the file has a measurements tab; FALSE otherwise. - */ -function validator_tab_exists_validate_file($file) { - // Required traits - plot, rep, entry, location and name. - $required_trait = rawpheno_function_headers('required'); - - // Check it opens. - $xls_obj = rawpheno_open_file($file); - - $xls_sheets = $xls_obj->Sheets(); - foreach($xls_sheets as $sheet_key => $sheet_value) { - $xls_obj->ChangeSheet($sheet_key); - - // check the measurements tab exists. - if (rawpheno_function_delformat($sheet_value) == 'measurements') { - - // Check there are contents. - $i = 0; - $have_data = 0; - foreach ($xls_obj as $row) { - // For some reason, when a spreadsheet is system generated and submitted - // for validation process, the reader could not locate data in measurements tab. - // This test ensure that a row has at least 5 elements to account for - // Plot, Entry, Name, Location and Rep - then try to validate the spreadsheet - // otherwise, it is assumed that it is a blank spreadsheet downloaded from instructions - // or simply a file with no data. - // NOTE: works however, after file has been resaved. - - if (count($row) >= count($required_trait)) { - // only check the first two lines. - if ($i > 2) break; - - // Check the row isn't empty. - if (strlen(trim(implode('', $row))) >= 5) { - $have_data++; - } - - $i++; - } - } - - if ($have_data > 1) return TRUE; - } - } - - return FALSE; -} - - -/** - * Provide a more useful message to the user when measurements tab is missing. - * - * @return - * A string, human-readable message to the user telling them what might - * caused the tab to fail validation. - */ -function validator_tab_exists_generate_msg() { - return array('Ensure that Measurements tab exists in the file is not renamed.'); -} - -/** - * Provide more useful messages to the user for if validation failed. - * - * @param $error_info - * Whatever information was returned from the validation callback indexed first by - * column name and then by row number. - * @return - * An array of human-readable messages to the user telling them exactly what failed validation. - */ -function validator_column_exists_generate_msg($error_info) { - $messages = array(); - - if (isset($error_info['missing'])) { - $text1 = 'The following Essential Traits must exist: "' . implode('", "',$error_info['missing']). '". Please add empty column in your spreadsheet for each of the missing Essential Trait mentioned. Check the Instructions Page to ensure that Essential Traits required by this project are present and match as listed.'; - $messages[] = $text1; - } - - if (isset($error_info['duplicates'])) { - $messages[] = 'The following columns were duplicated: "' . implode('", "',$error_info['duplicates']). '". Please ensure each column is unique (only occurs once in the spreadsheet).'; - } - - if (isset($error_info['invalidformat'])) { - $messages[] = 'The following columns have units not properly enclosed in parenthesis or is missing: "' . implode('", "',$error_info['invalidformat']). '". Please ensure units are correctly enclosed in parenthesis.'; - } - - return $messages; -} - -/** - * Function to check that expected column headers (essential headers) are present in the spreadsheet. - * - * @param $header - * An array containing the header. - * @param $project_id - * An integer containing the project ID the spreadsheet file is uploaded to. - * - * @return - * TRUE if it passed validation; FALSE otherwise. - */ -function validator_column_exists_validate_header($header, $project_id) { - $result = array(); - - // First check that all the essential traits are there. - $delformat_header = array(); - $nospace_header = array(); - - foreach ($header as $h) { - if ($h['skip'] == 1) continue; - - $delformat_header[] = $h['no units']; - $nospace_header[] = str_replace(' ','', $h['no format']); - } - - $essential_traits = rawpheno_project_essential_traits($project_id); - $missing_traits = array_diff($essential_traits, $delformat_header); - - if (!empty($missing_traits)) { - $result['missing'] = $missing_traits; - } - - // Second check there are no duplicates. - $nospace_header = array_filter($nospace_header); - $counts = array_count_values($nospace_header); - // If there are duplicates then go ahead and find them. - if (sizeof($nospace_header) != sizeof($counts)) { - - $duplicates = array(); - foreach($counts as $val => $c) { - if($c > 1) { - $key = array_search($val, $nospace_header); - $duplicates[] = $header[$key]['no units']; - } - } - $result['duplicates'] = $duplicates; - } - - // Third check - ensure that when ( or ) are present in the name - // then it is assumed that trait has unit thus it mus be check that - // unit is correctly enclosed in parenthesis and or no missing opening - // or closing paren. - $invalid_format = array(); - foreach($header as $f) { - if ($f['skip'] == 1) continue; - - $h = trim(str_replace(array("\n", "\r", " "), ' ', $f['original'])); - - $is_valid = rawpheno_valid_trait_format($h); - if ($is_valid === FALSE) { - // Return the trait name, unit, and all. - $invalid_format[] = $h; - } - } - - if (count($invalid_format)) $result['invalidformat'] = $invalid_format; - - - if (empty($result)) { - return TRUE; - } - else { - return $result; - } -} - -/** - * Provide more useful messages to the user for if validation failed. - * - * @param $error_info - * Whatever information was returned from the validation callback indexed first by - * column name and then by row number. - * @return - * An array of human-readable messages to the user telling them exactly what failed validation. - */ -function validator_required_info_generate_msg($error_info) { - $messages = array(); - - // For each column with missing values. - foreach ($error_info as $column => $rows) { - - // Find the parts and separate the messages. - $empty_parts = array(); - $dup_parts = array(); - foreach ($rows as $row_index => $info) { - if ($info['code'] == 1) { - $empty_parts[] = $row_index; - } - elseif ($info['code'] == 2) { - $dup_parts[ $info['value'] ] = '"' . $info['value'] . '" on lines ' . implode(', ', $info['rows']); - } - } - - // Generate the empty message. - if (!empty($empty_parts)) { - $msg = "You need to supply non-negative values for \"$column\" on row"; - - // pluralize rows if there are more than one with an empty value. - if (sizeof($empty_parts) > 1) { $msg .= 's'; } - - // Add the row numbers to the message. - $msg .= ' ' . implode(', ',$empty_parts) . '.'; - - $messages[] = $msg; - - } - - // Generate the duplicate values message. - if (!empty($dup_parts)) { - // When Plot - message should indicate when/where should it be unique. - $more_info = '.'; - if ($column == 'Plot') { - $more_info = ' in a given Year in the Planting Date (date) and Location.'; - } - - $placeholder = (sizeof($dup_parts) > 1) ? 'values are' : 'value is'; - $msg = "The \"$column\" should be unique$more_info The following $placeholder duplicated: " - . implode('; ', $dup_parts) . '.'; - $messages[] = $msg; - } - - } - - return $messages; -} - -/** - * Ensures that the current cell is not empty. - * - * @param $value - * The value of the current cell to be validated. - * @param $context - * Information providing context to the cell to validate. Includes: - * - 'row index': the index of the tow the cell is in. - * - 'row': an array containing all cells in the current row. - * - 'column index': the index of the column the cell is in. - * - 'header': an array containing the header for the file. Each column in the header - * will be pre-processed with the following self-explanitory keys: 'no format', - * 'original', 'units', 'no units'. - * @param $storage - * An empty variable that is specific to the current validation but persistent across - * rows. This can be helpful when checking that values are unique, etc. - * - * @return - * TRUE if it passed validation; FALSE otherwise. - */ -function validator_required_info_validate_cell($value, $context, &$storage) { - if ($context['header'][ $context['column index'] ]['no format'] == 'name' || - $context['header'][ $context['column index'] ]['no format'] == 'plantingdate(date)') { - - // Planting date and Name (germplasm) remain required - no NAs allowed. - if (empty($value)) { - return array('code' => 1, 'row' => $context['row index']); - } - } - else { - // All else, but plot has extra step. - if (empty($value) || $value < 0) { - return array('code' => 1, 'row' => $context['row index']); - } - - // Is this plot? - // Skip duplicate plot check when value is NA. - if ($context['header'][ $context['column index'] ]['no format'] == 'plot' && $value != 'NA') { - // Get Planting date (date) and location index from the header rows. - $planting_date_i = $context['plot_req']['planting date (date)']; - $location_i = $context['plot_req']['location']; - - // Get the cell value of header idexes. - // Extract the year (YYYY) from planting year (date) - $planting_date = $context['row'][$planting_date_i]; - $planting_year = substr($planting_date, 0, 4); - - // Location - $location = trim($context['row'][$location_i]); - - // Has this value been used before? value being (year+location+plot) - $rows_used_in = array_keys($storage, $planting_year . $location . $value); - - // save to storage. - $storage[ $context['row index'] ] = $planting_year . $location . $value; - - // if it has then save for error. - if ($rows_used_in) { - $rows_used_in[] = $context['row index']; - return array('code' => 2, 'value' => $value, 'rows' => $rows_used_in); - } - } - } - - return TRUE; -} - -/** - * Provide more useful messages to the user for if validation failed. - * - * @param $error_info - * Whatever information was returned from the validation callback indexed first by - * column name and then by row number. - * @return - * An array of human-readable messages to the user telling them exactly what failed validation. - */ -function validator_units_match_type_generate_msg($error_info) { - $messages = array(); - - foreach($error_info as $column_name => $rows) { - - // Expand each row. - $parts = array(); - foreach ($rows as $row_index => $error) { - $parts[] = '"' . $error['value'] .'" (line '.$row_index.')'; - $unit_name = $error['unit']; - } - - // We want our messages consistent but helpful thus we need to provide some - // customization to the message based on the unit. - $unit_msg = ''; - switch($unit_name) { - case 'date': - $unit_name = 'date(s)'; - $unit_msg = 'We expect the date to be YYYY-MM-DD and to have occured in the past.'; - break; - case 'count': - $unit_name = 'count(s)'; - $unit_msg = 'We expect positive whole numbers.'; - break; - case 'days': - $unit_name = 'day(s)'; - $unit_msg = 'We expect positive whole numbers.'; - break; - case 'cm': - $unit_name = 'centimeters'; - break; - case 'g': - $unit_name = 'grams'; - break; - case 'y/n/?': - break; - case 'scale': - $unit_name = 'within the scale'; - $unit_msg . 'It should be a whole number between 1 and 5 where 1 indicates most plants are fully upright and 5 indicates most plants are fully prostrate/flat.'; - break; - case 'text': - } - - // Combine all the parts into a full message. - $messages[] = "The following values in \"$column_name\" are not $unit_name: " . implode(', ', $parts) . '. ' . $unit_msg; - } - - return $messages; -} - -/** - * Ensures that the type of data matches the unit. - * - * @param $value - * The value of the current cell to be validated. - * @param $context - * Information providing context to the cell to validate. Includes: - * - 'row index': the index of the tow the cell is in. - * - 'row': an array containing all cells in the current row. - * - 'column index': the index of the column the cell is in. - * - 'header': an array containing the header for the file. Each column in the header - * will be pre-processed with the following self-explanitory keys: 'no format', - * 'original', 'units', 'no units'. - * @param $storage - * An empty variable that is specific to the current validation but persistent across - * rows. This can be helpful when checking that values are unique, etc. - * - * @return - * TRUE if it passed validation; FALSE otherwise. - */ -function validator_units_match_type_validate_cell($value, $context, &$storage) { - - // Determine the units for this column. - $unit = $context['header'][ $context['column index'] ]['units']; - - // We always want to allow empty cells or NA but not treat zero as empty... - if ((empty($value) AND !(strval($value) === '0')) || - ($value == 'NA' && $context['header'][ $context['column index'] ]['no format'] != 'plantingdate(date)')) { - - return TRUE; - } - - // Then check if it matches based on the unit. - switch($unit) { - case 'date': - // Date must be YYYY-MM-DD format. - if (preg_match('/^([0-9]{4})-([0-9]{2})-([0-9]{2})/', $value, $matches)) { - - // Check that the month/day are in the expected range. - $year = $matches[1]; - $month = $matches[2]; - $day = $matches[3]; - if ($month >= 1 AND $month <= 12 AND $day >= 1 AND $day <= 31 AND $year > 1900) { - - // Then check that it's not in the future. - $today = new DateTime(); - $date = DateTime::createFromFormat('Y-m-d', $value); - if ($date <= $today) { - return TRUE; - } - } - } - else { - // Inspect date for NA and YYYY. - if (preg_match('/^([0-9]{4})/', $value) === 1 && $value <= date('Y')) { - // 4 digit YYYY year. Make sure year is not year in some year in the unknown future. - return TRUE; - } - elseif (in_array(trim(strtolower($value)), array('na', 'n/a', 'n.a.'))) { - // Try NA, N/A, N.A. - return TRUE; - } - } - break; - - case 'count': - case 'days': - // First check that it's a number. - if (is_numeric($value)) { - // Then check it's a positive whole number. - if ((int)$value == $value AND $value > 0) { - return TRUE; - } - } - // Allow 0 for count. - if ($unit == 'count' AND strval($value) === '0') { - return TRUE; - } - break; - - case 'cm': - case 'g': - // Measurements - if it is numeric, then it must be greater than 0. - if (is_numeric($value) && $value > 0) { - return TRUE; - } - // Allow 0 - if (strval($value) === '0') { - return TRUE; - } - break; - - case 'y/n/?': - // Yes or No - if it is char, length is one, then check if y, n, ? (question mark). - if (in_array(strtolower($value), array('y','n','?', 'yes', 'no'))) { - return TRUE; - } - break; - - case 'scale': - // Scales are associated with the trait cvterm and each approved code is stored - // in pheno_scale_member. - // First retrieve the cvterm_id for the trait. - $trait = $context['header'][ $context['column index'] ]['original']; - $trait_id = rawpheno_get_trait_id($trait); - if ($trait_id) { - // Then check that the value is in the scale members - $present = db_query('SELECT true FROM {pheno_scale_member} WHERE scale_id=:trait_id AND code=:value', - array(':trait_id' => $trait_id, ':value' => $value))->fetchField(); - if ($present) return TRUE; - - // If not, make sure there are members. - $has_scale = db_query('SELECT count(*) FROM {pheno_scale_member} WHERE scale_id=:trait_id', - array(':trait_id' => $trait_id))->fetchField(); - if (!$has_scale) return TRUE; - } - // If this is a new trait/column then we don't know how to validate it, so we assume it's correct. - else { - return TRUE; - } - break; - - case 'text': - return TRUE; - break; - - default: - return TRUE; - } - - return array('value' => $value, 'unit' => $unit); -} - -/** - * Provide more useful messages to the user for if validation failed. - * - * @param $error_info - * Whatever information was returned from the validation callback indexed first by - * column name and then by row number. - * @return - * An array of human-readable messages to the user telling them exactly what failed validation. - */ -function validator_germplasm_present_generate_msg($error_info) { - $nonexistant = array(); - $nonunique = array(); - - foreach ($error_info['Name'] as $v) { - if ($v['code'] === 0) { - $nonexistant[] = '"' . $v['name'] . '" (line ' . $v['row number'] . ')'; - } - elseif ($v['code'] === 9) { - $nonunique[] = '"' . $v['name'] . '" (line ' . $v['row number'] . ')'; - } - } - - // Contact support. - $support_email = rawpheno_function_get_support_email(); - $contact_support_message = ($support_email) - ? 'Please contact support.' - : 'Please contact the administrator of this website.'; - - $messages = array(); - if (!empty($nonexistant)) { - $messages[] = 'The following germplasm does not already exist: '. implode(', ',$nonexistant) . '. ' . $contact_support_message; - } - if (!empty($nonunique)) { - $messages[] = 'The following germplasm names are not unique in this resource: '. implode(', ',$nonunique). ' ' . $contact_support_message; - } - - return $messages; -} - -/** - * Ensures that the germplasm in the current cell exists and is unique. - * - * @param $value - * The value of the current cell to be validated. - * @param $context - * Information providing context to the cell to validate. Includes: - * - 'row index': the index of the tow the cell is in. - * - 'row': an array containing all cells in the current row. - * - 'column index': the index of the column the cell is in. - * - 'header': an array containing the header for the file. Each column in the header - * will be pre-processed with the following self-explanitory keys: 'no format', - * 'original', 'units', 'no units'. - * @param $storage - * An empty variable that is specific to the current validation but persistent across - * rows. This can be helpful when checking that values are unique, etc. - * - * @return - * TRUE if it passed validation. If it failed an array providing information about the - * failure is returned. Specifically, the name, row number, an error code, etc. - * - * Error Code: - * 0 = no matching stock. - * 9 = too many matching stocks (not unique). - */ -function validator_germplasm_present_validate_cell($value, $context, &$storage, $project_id) { - - // We don't want this test to catch empty cells. There is a separate test for testing required values are resent. - if (empty($value)) { - return TRUE; - } - - $stocks = chado_query('SELECT stock_id FROM {stock} WHERE name=:name', array(':name' => $value))->fetchAll(); - - if (sizeof($stocks) == 1) { - // Test to see if stock with token exists. - // Function has hook_alter() implementation to handle germplasm name with specific token. - // Hook also contains defition of which project applies and token used. - $project_name = rawpheno_function_getproject($project_id); - $stocks = rawpheno_function_getstockid($value, $project_name); - - return ($stocks <= 0) - ? array('name' => $value, 'row number' => $context['row index'], 'code' => 0) - : TRUE; - } - elseif (empty($stocks)) { - return array('name' => $value, 'row number' => $context['row index'], 'code' => 0); - } - else { - return array('name' => $value, 'row number' => $context['row index'], 'code' => 9, 'stocks' => $stocks); - } - -} - -/** - * Ensure that a project is selected before uploading a file. - * - * @param $project_id - * An integer containing the project id selected. - * - * @return - * TRUE if a project is selected. - * FALSE if no project is selected. - */ -function validator_is_project_selected($project_id) { - if ($project_id <= 0) { - // No project selected. - return FALSE; - } - else { - return TRUE; - } -} diff --git a/js/jquery-tabs.js b/js/jquery-tabs.js new file mode 100644 index 0000000..7b80670 --- /dev/null +++ b/js/jquery-tabs.js @@ -0,0 +1,6 @@ +/*! jQuery UI - v1.12.1 - 2021-08-13 +* http://jqueryui.com +* Includes: widget.js, keycode.js, unique-id.js, widgets/tabs.js +* Copyright jQuery Foundation and other contributors; Licensed MIT */ + +!function(t){"function"==typeof define&&define.amd?define(["jquery"],t):t(jQuery)}(function(l){l.ui=l.ui||{};l.ui.version="1.12.1";var a,i=0,r=Array.prototype.slice;l.cleanData=(a=l.cleanData,function(t){for(var e,i,s=0;null!=(i=t[s]);s++)try{(e=l._data(i,"events"))&&e.remove&&l(i).triggerHandler("remove")}catch(t){}a(t)}),l.widget=function(t,i,e){var s,a,n,o={},r=t.split(".")[0],h=r+"-"+(t=t.split(".")[1]);return e||(e=i,i=l.Widget),l.isArray(e)&&(e=l.extend.apply(null,[{}].concat(e))),l.expr[":"][h.toLowerCase()]=function(t){return!!l.data(t,h)},l[r]=l[r]||{},s=l[r][t],a=l[r][t]=function(t,e){if(!this._createWidget)return new a(t,e);arguments.length&&this._createWidget(t,e)},l.extend(a,s,{version:e.version,_proto:l.extend({},e),_childConstructors:[]}),(n=new i).options=l.widget.extend({},n.options),l.each(e,function(e,s){function a(){return i.prototype[e].apply(this,arguments)}function n(t){return i.prototype[e].apply(this,t)}l.isFunction(s)?o[e]=function(){var t,e=this._super,i=this._superApply;return this._super=a,this._superApply=n,t=s.apply(this,arguments),this._super=e,this._superApply=i,t}:o[e]=s}),a.prototype=l.widget.extend(n,{widgetEventPrefix:s&&n.widgetEventPrefix||t},o,{constructor:a,namespace:r,widgetName:t,widgetFullName:h}),s?(l.each(s._childConstructors,function(t,e){var i=e.prototype;l.widget(i.namespace+"."+i.widgetName,a,e._proto)}),delete s._childConstructors):i._childConstructors.push(a),l.widget.bridge(t,a),a},l.widget.extend=function(t){for(var e,i,s=r.call(arguments,1),a=0,n=s.length;a",options:{classes:{},disabled:!1,create:null},_createWidget:function(t,e){e=l(e||this.defaultElement||this)[0],this.element=l(e),this.uuid=i++,this.eventNamespace="."+this.widgetName+this.uuid,this.bindings=l(),this.hoverable=l(),this.focusable=l(),this.classesElementLookup={},e!==this&&(l.data(e,this.widgetFullName,this),this._on(!0,this.element,{remove:function(t){t.target===e&&this.destroy()}}),this.document=l(e.style?e.ownerDocument:e.document||e),this.window=l(this.document[0].defaultView||this.document[0].parentWindow)),this.options=l.widget.extend({},this.options,this._getCreateOptions(),t),this._create(),this.options.disabled&&this._setOptionDisabled(this.options.disabled),this._trigger("create",null,this._getCreateEventData()),this._init()},_getCreateOptions:function(){return{}},_getCreateEventData:l.noop,_create:l.noop,_init:l.noop,destroy:function(){var i=this;this._destroy(),l.each(this.classesElementLookup,function(t,e){i._removeClass(e,t)}),this.element.off(this.eventNamespace).removeData(this.widgetFullName),this.widget().off(this.eventNamespace).removeAttr("aria-disabled"),this.bindings.off(this.eventNamespace)},_destroy:l.noop,widget:function(){return this.element},option:function(t,e){var i,s,a,n=t;if(0===arguments.length)return l.widget.extend({},this.options);if("string"==typeof t)if(n={},t=(i=t.split(".")).shift(),i.length){for(s=n[t]=l.widget.extend({},this.options[t]),a=0;a?@[\]^`{|}~])/g,function(t){return t.replace(e,"\\$1")}),l.ui.safeActiveElement=function(e){var i;try{i=e.activeElement}catch(t){i=e.body}return i=!(i=i||e.body).nodeName?e.body:i};l.widget("ui.tabs",{version:"1.12.1",delay:300,options:{active:null,classes:{"ui-tabs":"ui-corner-all","ui-tabs-nav":"ui-corner-all","ui-tabs-panel":"ui-corner-bottom","ui-tabs-tab":"ui-corner-top"},collapsible:!1,event:"click",heightStyle:"content",hide:null,show:null,activate:null,beforeActivate:null,beforeLoad:null,load:null},_isLocal:(s=/#.*$/,function(t){var e=t.href.replace(s,""),i=location.href.replace(s,"");try{e=decodeURIComponent(e)}catch(t){}try{i=decodeURIComponent(i)}catch(t){}return 1?@\[\]\^`{|}~]/g,"\\$&"):""},refresh:function(){var t=this.options,e=this.tablist.children(":has(a[href])");t.disabled=l.map(e.filter(".ui-state-disabled"),function(t){return e.index(t)}),this._processTabs(),!1!==t.active&&this.anchors.length?this.active.length&&!l.contains(this.tablist[0],this.active[0])?this.tabs.length===t.disabled.length?(t.active=!1,this.active=l()):this._activate(this._findNextTab(Math.max(0,t.active-1),!1)):t.active=this.tabs.index(this.active):(t.active=!1,this.active=l()),this._refresh()},_refresh:function(){this._setOptionDisabled(this.options.disabled),this._setupEvents(this.options.event),this._setupHeightStyle(this.options.heightStyle),this.tabs.not(this.active).attr({"aria-selected":"false","aria-expanded":"false",tabIndex:-1}),this.panels.not(this._getPanelForTab(this.active)).hide().attr({"aria-hidden":"true"}),this.active.length?(this.active.attr({"aria-selected":"true","aria-expanded":"true",tabIndex:0}),this._addClass(this.active,"ui-tabs-active","ui-state-active"),this._getPanelForTab(this.active).show().attr({"aria-hidden":"false"})):this.tabs.eq(0).attr("tabIndex",0)},_processTabs:function(){var h=this,t=this.tabs,e=this.anchors,i=this.panels;this.tablist=this._getList().attr("role","tablist"),this._addClass(this.tablist,"ui-tabs-nav","ui-helper-reset ui-helper-clearfix ui-widget-header"),this.tablist.on("mousedown"+this.eventNamespace,"> li",function(t){l(this).is(".ui-state-disabled")&&t.preventDefault()}).on("focus"+this.eventNamespace,".ui-tabs-anchor",function(){l(this).closest("li").is(".ui-state-disabled")&&this.blur()}),this.tabs=this.tablist.find("> li:has(a[href])").attr({role:"tab",tabIndex:-1}),this._addClass(this.tabs,"ui-tabs-tab","ui-state-default"),this.anchors=this.tabs.map(function(){return l("a",this)[0]}).attr({role:"presentation",tabIndex:-1}),this._addClass(this.anchors,"ui-tabs-anchor"),this.panels=l(),this.anchors.each(function(t,e){var i,s,a,n=l(e).uniqueId().attr("id"),o=l(e).closest("li"),r=o.attr("aria-controls");h._isLocal(e)?(a=(i=e.hash).substring(1),s=h.element.find(h._sanitizeSelector(i))):(a=o.attr("aria-controls")||l({}).uniqueId()[0].id,(s=h.element.find(i="#"+a)).length||(s=h._createPanel(a)).insertAfter(h.panels[t-1]||h.tablist),s.attr("aria-live","polite")),s.length&&(h.panels=h.panels.add(s)),r&&o.data("ui-tabs-aria-controls",r),o.attr({"aria-controls":a,"aria-labelledby":n}),s.attr("aria-labelledby",n)}),this.panels.attr("role","tabpanel"),this._addClass(this.panels,"ui-tabs-panel","ui-widget-content"),t&&(this._off(t.not(this.tabs)),this._off(e.not(this.anchors)),this._off(i.not(this.panels)))},_getList:function(){return this.tablist||this.element.find("ol, ul").eq(0)},_createPanel:function(t){return l("
    ").attr("id",t).data("ui-tabs-destroy",!0)},_setOptionDisabled:function(t){var e,i;for(l.isArray(t)&&(t.length?t.length===this.anchors.length&&(t=!0):t=!1),i=0;e=this.tabs[i];i++)e=l(e),!0===t||-1!==l.inArray(i,t)?(e.attr("aria-disabled","true"),this._addClass(e,null,"ui-state-disabled")):(e.removeAttr("aria-disabled"),this._removeClass(e,null,"ui-state-disabled"));this.options.disabled=t,this._toggleClass(this.widget(),this.widgetFullName+"-disabled",null,!0===t)},_setupEvents:function(t){var i={};t&&l.each(t.split(" "),function(t,e){i[e]="_eventHandler"}),this._off(this.anchors.add(this.tabs).add(this.panels)),this._on(!0,this.anchors,{click:function(t){t.preventDefault()}}),this._on(this.anchors,i),this._on(this.tabs,{keydown:"_tabKeydown"}),this._on(this.panels,{keydown:"_panelKeydown"}),this._focusable(this.tabs),this._hoverable(this.tabs)},_setupHeightStyle:function(t){var i,e=this.element.parent();"fill"===t?(i=e.height(),i-=this.element.outerHeight()-this.element.height(),this.element.siblings(":visible").each(function(){var t=l(this),e=t.css("position");"absolute"!==e&&"fixed"!==e&&(i-=t.outerHeight(!0))}),this.element.children().not(this.panels).each(function(){i-=l(this).outerHeight(!0)}),this.panels.each(function(){l(this).height(Math.max(0,i-l(this).innerHeight()+l(this).height()))}).css("overflow","auto")):"auto"===t&&(i=0,this.panels.each(function(){i=Math.max(i,l(this).height("").height())}).height(i))},_eventHandler:function(t){var e=this.options,i=this.active,s=l(t.currentTarget).closest("li"),a=s[0]===i[0],n=a&&e.collapsible,o=n?l():this._getPanelForTab(s),r=i.length?this._getPanelForTab(i):l(),i={oldTab:i,oldPanel:r,newTab:n?l():s,newPanel:o};t.preventDefault(),s.hasClass("ui-state-disabled")||s.hasClass("ui-tabs-loading")||this.running||a&&!e.collapsible||!1===this._trigger("beforeActivate",t,i)||(e.active=!n&&this.tabs.index(s),this.active=a?l():s,this.xhr&&this.xhr.abort(),r.length||o.length||l.error("jQuery UI Tabs: Mismatching fragment identifier."),o.length&&this.load(this.tabs.index(s),t),this._toggle(t,i))},_toggle:function(t,e){var i=this,s=e.newPanel,a=e.oldPanel;function n(){i.running=!1,i._trigger("activate",t,e)}function o(){i._addClass(e.newTab.closest("li"),"ui-tabs-active","ui-state-active"),s.length&&i.options.show?i._show(s,i.options.show,n):(s.show(),n())}this.running=!0,a.length&&this.options.hide?this._hide(a,this.options.hide,function(){i._removeClass(e.oldTab.closest("li"),"ui-tabs-active","ui-state-active"),o()}):(this._removeClass(e.oldTab.closest("li"),"ui-tabs-active","ui-state-active"),a.hide(),o()),a.attr("aria-hidden","true"),e.oldTab.attr({"aria-selected":"false","aria-expanded":"false"}),s.length&&a.length?e.oldTab.attr("tabIndex",-1):s.length&&this.tabs.filter(function(){return 0===l(this).attr("tabIndex")}).attr("tabIndex",-1),s.attr("aria-hidden","false"),e.newTab.attr({"aria-selected":"true","aria-expanded":"true",tabIndex:0})},_activate:function(t){var t=this._findActive(t);t[0]!==this.active[0]&&(t=(t=!t.length?this.active:t).find(".ui-tabs-anchor")[0],this._eventHandler({target:t,currentTarget:t,preventDefault:l.noop}))},_findActive:function(t){return!1===t?l():this.tabs.eq(t)},_getIndex:function(t){return t="string"==typeof t?this.anchors.index(this.anchors.filter("[href$='"+l.ui.escapeSelector(t)+"']")):t},_destroy:function(){this.xhr&&this.xhr.abort(),this.tablist.removeAttr("role").off(this.eventNamespace),this.anchors.removeAttr("role tabIndex").removeUniqueId(),this.tabs.add(this.panels).each(function(){l.data(this,"ui-tabs-destroy")?l(this).remove():l(this).removeAttr("role tabIndex aria-live aria-busy aria-selected aria-labelledby aria-hidden aria-expanded")}),this.tabs.each(function(){var t=l(this),e=t.data("ui-tabs-aria-controls");e?t.attr("aria-controls",e).removeData("ui-tabs-aria-controls"):t.removeAttr("aria-controls")}),this.panels.show(),"content"!==this.options.heightStyle&&this.panels.css("height","")},enable:function(i){var t=this.options.disabled;!1!==t&&(t=void 0!==i&&(i=this._getIndex(i),l.isArray(t)?l.map(t,function(t){return t!==i?t:null}):l.map(this.tabs,function(t,e){return e!==i?e:null})),this._setOptionDisabled(t))},disable:function(t){var e=this.options.disabled;if(!0!==e){if(void 0===t)e=!0;else{if(t=this._getIndex(t),-1!==l.inArray(t,e))return;e=l.isArray(e)?l.merge([t],e).sort():[t]}this._setOptionDisabled(e)}},load:function(t,s){t=this._getIndex(t);function a(t,e){"abort"===e&&n.panels.stop(!1,!0),n._removeClass(i,"ui-tabs-loading"),o.removeAttr("aria-busy"),t===n.xhr&&delete n.xhr}var n=this,i=this.tabs.eq(t),t=i.find(".ui-tabs-anchor"),o=this._getPanelForTab(i),r={tab:i,panel:o};this._isLocal(t[0])||(this.xhr=l.ajax(this._ajaxSettings(t,s,r)),this.xhr&&"canceled"!==this.xhr.statusText&&(this._addClass(i,"ui-tabs-loading"),o.attr("aria-busy","true"),this.xhr.done(function(t,e,i){setTimeout(function(){o.html(t),n._trigger("load",s,r),a(i,e)},1)}).fail(function(t,e){setTimeout(function(){a(t,e)},1)})))},_ajaxSettings:function(t,i,s){var a=this;return{url:t.attr("href").replace(/#.*$/,""),beforeSend:function(t,e){return a._trigger("beforeLoad",i,l.extend({jqXHR:t,ajaxSettings:e},s))}}},_getPanelForTab:function(t){t=l(t).attr("aria-controls");return this.element.find(this._sanitizeSelector("#"+t))}}),!1!==l.uiBackCompat&&l.widget("ui.tabs",l.ui.tabs,{_processTabs:function(){this._superApply(arguments),this._addClass(this.tabs,"ui-tab")}});l.ui.tabs}); \ No newline at end of file diff --git a/theme/js/rawpheno.admin.script.js b/js/script-admin.js old mode 100755 new mode 100644 similarity index 94% rename from theme/js/rawpheno.admin.script.js rename to js/script-admin.js index bae18fd..47c218c --- a/theme/js/rawpheno.admin.script.js +++ b/js/script-admin.js @@ -1,99 +1,99 @@ -/** - * @file - * Manage behavior in project management admin control panel - */ -(function($) { - Drupal.behaviors.adminProjectManagement = { - attach: function (context, settings) { - var headers = 'container-prj-hdr'; - var users = 'container-prj-usr'; - var envdata = 'container-prj-env'; - - $('#nav-tabs li').click(function(i) { - - if ($(this).index() == 0) { - // Column headers tab. - $('#' + envdata).hide(); - $('#' + users).hide(); - $('#' + headers).show(); - } - else if ($(this).index() == 1) { - // Active users tab. - $('#' + envdata).hide(); - $('#' + headers).hide(); - $('#' + users).show(); - } - else { - $('#' + users).hide(); - $('#' + headers).hide(); - $('#' + envdata).show(); - } - - $('li.active-tab').removeClass('active-tab'); - $(this).addClass('active-tab'); - }); - - // Confirm if user wants to proceed with the command. - $('.link-del').click(function(event) { - var r = confirm("Are you sure you want to delete?"); - if (!r) return false; - }); - - - // Reveal validation result text. - $('div.container-cell').click(function() { - // Examine the height. When height is 50px, user wants to disclose the entire cell contents - // else, restore it to initial state. - var id = $(this).attr('id'); - var i = id.replace(/vn-file-|vr-file-/, ''); - - var h = $(this).css('height'); - if (h == '65px') { - $('#vr-file-' + i).css('height', '100%'); - - if ($('#vn-file-' + i)) { - $('#vn-file-' + i).css('height', '100%'); - } - } - else { - $('#vr-file-' + i).css('height', '65px'); - - if ($('#vn-file-' + i)) { - $('#vn-file-' + i).css('height', '65px'); - } - } - }); - - - // Add event listener to user my folder. - $('.link-show-folder').click(function(e) { - e.preventDefault(); - - var lnk = $(this); - var attrId = lnk.attr('id'); - var myFolder = '#show-' + attrId; - - if (lnk.text() == '[Show]') { - lnk.text('[Hide]'); - $(myFolder).show(); - - // Reset any view open. - var ls = $('.link-show-folder'); - $.each(ls, function(key, value) { - var c = $(this); - var i = c.attr('id'); - if (i != attrId) { - c.text('[Show]'); - $('#show-' + i).hide(); - } - }); - - } - else { - lnk.text('[Show]'); - $(myFolder).hide(); - } - }); - } - }; -}(jQuery)); +/** + * @file + * Manage behavior in project management admin control panel + */ + (function($) { + Drupal.behaviors.adminProjectManagement = { + attach: function (context, settings) { + var headers = 'container-prj-hdr'; + var users = 'container-prj-usr'; + var envdata = 'container-prj-env'; + + $('#nav-tabs li').click(function(i) { + + if ($(this).index() == 0) { + // Column headers tab. + $('#' + envdata).hide(); + $('#' + users).hide(); + $('#' + headers).show(); + } + else if ($(this).index() == 1) { + // Active users tab. + $('#' + envdata).hide(); + $('#' + headers).hide(); + $('#' + users).show(); + } + else { + $('#' + users).hide(); + $('#' + headers).hide(); + $('#' + envdata).show(); + } + + $('li.active-tab').removeClass('active-tab'); + $(this).addClass('active-tab'); + }); + + // Confirm if user wants to proceed with the command. + $('.link-del').once('.link-del').click(function(event) { + var r = confirm("Are you sure you want to delete?"); + if (!r) return false; + }); + + + // Reveal validation result text. + $('div.container-cell').click(function() { + // Examine the height. When height is 50px, user wants to disclose the entire cell contents + // else, restore it to initial state. + var id = $(this).attr('id'); + var i = id.replace(/vn-file-|vr-file-/, ''); + + var h = $(this).css('height'); + if (h == '65px') { + $('#vr-file-' + i).css('height', '100%'); + + if ($('#vn-file-' + i)) { + $('#vn-file-' + i).css('height', '100%'); + } + } + else { + $('#vr-file-' + i).css('height', '65px'); + + if ($('#vn-file-' + i)) { + $('#vn-file-' + i).css('height', '65px'); + } + } + }); + + + // Add event listener to user my folder. + $('.link-show-folder').click(function(e) { + e.preventDefault(); + + var lnk = $(this); + var attrId = lnk.attr('id'); + var myFolder = '#show-' + attrId; + + if (lnk.text() == '[Show]') { + lnk.text('[Hide]'); + $(myFolder).show(); + + // Reset any view open. + var ls = $('.link-show-folder'); + $.each(ls, function(key, value) { + var c = $(this); + var i = c.attr('id'); + if (i != attrId) { + c.text('[Show]'); + $('#show-' + i).hide(); + } + }); + + } + else { + lnk.text('[Show]'); + $(myFolder).hide(); + } + }); + } + }; +}(jQuery)); diff --git a/js/script-page-backup.js b/js/script-page-backup.js new file mode 100644 index 0000000..d08d394 --- /dev/null +++ b/js/script-page-backup.js @@ -0,0 +1,185 @@ +/** + * @file + * Manage behavior in backup page + * credits to: https://css-tricks.com/drag-and-drop-file-uploading/. + */ + (function($) { + Drupal.behaviors.rawphenoBackupBehaviours = { + attach: function (context, settings) { + var dragdropFile = drupalSettings.rawphenotypes.vars.dragdropfile; + // Reference form element. + var rawphenotypesForm = $('#rawphenotypes-form'); + // Reference to the main drop zone area element. + var dropZone = $('.drop-zone'); + // Variable to hold filename picked up by the drop zone. + var dropFilename = false; + // File uploaded through the drag and drop. + var dropFile = false; + + // Reference container for filename. + var dropFileContainer = $('#drop-zone-file'); + // Reference file field. + var inputFileField = $('#field-file'); + + + // Establish if browser can support drag and drop. + var isAdvancedUpload = function() { + var div = document.createElement('div'); + return (('draggable' in div) || ('ondragstart' in div && 'ondrop' in div)) && 'FormData' in window && 'FileReader' in window; + }(); + + + // The browser supports drag and drop. A conventional way of + // selecting file (choose file link) will be available if it were unsupported. + if (isAdvancedUpload) { + // Add class to indicate that the drop zone can support drag and drop. + // This is white container element when unsupported, matching other field elements. + dropZone.addClass('has-dragdrop-upload'); + + // Listen for events. + dropZone.once() + .on('drag dragstart dragend dragover dragenter dragleave drop', function(e) { + e.preventDefault(); + e.stopPropagation(); + setMessage(''); + }) + .on('dragover dragenter', function() { + // Activate drop zone to indicate that drag and drop + // is active and the element can take the file dragged into it. + dropZone.addClass('is-dragover') + }) + .on('dragleave dragend drop', function() { + // Revert back to the original state. + dropZone.removeClass('is-dragover') + }) + .on('drop', function(e) { + // Set the filename to indicate drop zone has a file. + // Only when the thing drag and drop is a file. + if (e.originalEvent.dataTransfer.items[0].kind === 'file') { + dropFilename = e.originalEvent.dataTransfer.files[0].name; + markupDropzoneFile(dropFilename); + + // File to upload using the drag and drop. + dropFile = e.originalEvent.dataTransfer.files; + rawphenotypesForm.trigger('submit'); + } + }); + } + + var submitForm = 0; + $('#backup-submit-form').once().click(function() { + submitForm = 1; + }); + + // Handle submit. + rawphenotypesForm.once() + .on('submit', function(e) { + // Submit when there is file upload. + if (isAdvancedUpload && dropFile.length > 0) { + if (submitForm == 0) { + e.preventDefault(); + e.stopPropagation(); + + // File data container. + var ajaxData = new FormData(); + + if (dropFile) { + // Append file data container with the file. + ajaxData.append('file', dropFile[0]); + setFileId(); + ajaxData.append('fileId', getFileId()); + } + + $.ajax({ + url: dragdropFile, + type: rawphenotypesForm.attr('method'), + data: ajaxData, + dataType: 'json', + cache: false, + contentType: false, + processData: false, + complete: function() { + // $form.removeClass('is-uploading'); + }, + success: function(data) { + if (data.error) { + setMessage(data.response); + } + }, + error: function() {} + }); + } else { + // ajax for legacy browsers + } + } + }); + + // Whether or not drag and drop is supported, manual upload + // will be available to user. + inputFileField.once() + .on('change', function(e) { + setMessage(''); + + if(this.files && this.files.length == 1) { + dropFilename = e.target.value.split('\\').pop(); + } + + if(dropFilename) { + markupDropzoneFile(dropFilename); + dropFile = [ $('#field-file')[0].files[0] ]; + rawphenotypesForm.trigger('submit'); + } + }); + + // When drop zone has file - a remove link is available. + // Listen to when this link is clicked. + var hasFile = dropFileContainer.has('a'); + if (hasFile) { + dropFileContainer.on('click', function(e) { + e.preventDefault(); + + setMessage(''); + markupDropzoneFile(); + $('#field-file').val(''); + }); + } + + /** + * Create a markup for the file set in the drop zone element. + * Markup will include remove link to remove file from the drop zone. + * @param filename + * Filename picked up by the drop zone element. + */ + function markupDropzoneFile(filename = null) { + // Markup that will show the filename provided in the drop zone or using + // the conventional way to add file plus this markup to remove the file. + var removeFile = ' [remove]'; + + var markup = (filename) ? filename.trim() + removeFile : ''; + dropFileContainer.html(markup); + } + + /** + * Create a file id. + */ + function setFileId() { + var id = Date.now(); + $('#field-file-id').val(id.toString()); + } + + /** + * Get file id. + */ + function getFileId() { + return $('#field-file-id').val(); + } + + /** + * Set status (error) message. + */ + function setMessage(message = '') { + $('.dragdrop-message').html(message); + } + } + }; +}(jQuery)); \ No newline at end of file diff --git a/theme/js/rawpheno.instructions.script.js b/js/script-page-instructions.js old mode 100755 new mode 100644 similarity index 94% rename from theme/js/rawpheno.instructions.script.js rename to js/script-page-instructions.js index 4b9994f..194a55f --- a/theme/js/rawpheno.instructions.script.js +++ b/js/script-page-instructions.js @@ -1,283 +1,293 @@ -/** - * @file - * Manage behaviors in instructions page. - */ -(function($) { - Drupal.behaviors.rawphenoInstructionsTab = { - attach: function (context, settings) { - // Maintain search box width to prevent autocomplete search suggestions - // from being cut off. When width becomes less, remove context button. - // Alternative button is provided in Standard Procedure Tab. - - // To prevent Tabs from stacking each other - use smaller font size. - var parentContainer = $('div.container-page'); - $(window).resize(function() { - var w = parentContainer.width(); - var e = $('.subtitle-right'); - - var eVal = (w <= 750) ? 'none' : ''; - e.css('display', eVal); - - // Manage tabs. - eVal = (w <= 850) ? 0.68 : 1; - e = $('#container-instructions #tabs li a'); - e.css('font-size', eVal + 'em'); - }); - - // IMAGE GALLERY - // Array to hold all image file. - var gallery = new Array(0, 1); - // Array to hold all relevant captions. - var caption = new Array(0, 1); - - // Gallery elements set 1. - gallery[0] = ['01-tendrils-no-elongation', - '02-tendrils-no-elongation', - '01-tendrils-elongation']; - - caption[0] = ['No elongation', - 'No elongation', - 'Elongation']; - - // Gallery elements set 2. - gallery[1] = ['01-pods-emerged', - '02-pods-emerged', - '01-pods-variation', - '02-pods-variation']; - - caption[1] = ['Freshly emerged pods', - 'Freshly emerged pods', - 'Sample of the pod variation', - 'Sample of the pod variation']; - - // Path to appendix folder. - var imgPath = $('#path').val(); - - // Attach behavior to Photo Appendix. - $('#fragment-6 a').click(function(){ - // Option select containing topics. - var topic = $('#fragment-6 select').val(); - // Image container - showing default image. - var curImg = $('#cur-img').val(); - - // Hold the index of the current image shown. - curImg = parseInt(curImg); - - // Determine if user clicks on next or prev link - // in the image gallery. - var showImg = ($(this).attr('class') == 'a-left') ? curImg - 1 : curImg + 1; - - // Replace the image src to show the next or prev image in the topic. - if (gallery[topic][showImg]) { - $('#fragment-6 img').attr('src', imgPath+gallery[topic][showImg]+'.jpg'); - $('#fragment-6 em').contents().replaceWith(caption[topic][showImg]); - $('#cur-img').val(showImg); - } - }); - - // When user will change topic, load default or first item in the - // image and caption array. - $('#fragment-6 select').change(function(){ - var topic = $(this).val(); - $('#fragment-6 img').attr('src', imgPath+gallery[topic][0]+'.jpg'); - $('#fragment-6 em').contents().replaceWith(caption[topic][0]); - $('#cur-img').val(0); - }); - - // SEARCH FUNCTIONALITY - // Reference to search textbox. - var txtField = $('#edit-txt-search'); - // Reference to search buttons. - var btnSearch = $("#btn_submit"); - - // Create a label as default value in the search text field. - // When selected will clear the value. - txtField.focus(function() { - if (this.value == 'Search Trait') { - this.value = ''; - }}).blur(function() { - if (this.value.trim() == '') { - this.value = 'Search Trait'; - removeElements(); - }}); - - // Clear all search when user types in new - // keywords into the search field. - txtField.keypress(function() { - removeElements(); - }); - - // Initialize JQuery Tabs. - $('#tabs').tabs({delay:0}); - - // Array of traits. - var availableTrait = new Array(); - - // Access JSON List of traits created by function callback - // in instructions page. - var pathJSON = $('#traits-json').val(); - var objTraits = $.getJSON(pathJSON, function(result) { - $.each(result, function(i, field) { - availableTrait[i] = field; - }) - }); - - // Search button is clicked. - btnSearch.click(function() { - // Clear or reset previous search. - removeElements(); - - $('#container-search-result').addClass('search-on'); - - if (txtField.val() == null || txtField.val().trim() == '' || txtField.val().length <= 1) { - // Search field is blank. - var title = 'Invalid value in search field.'; - var message = 'Please type in keywords and select the best matching trait from the autocomplete drop-down.'; - errorMessage(title, message); - } - else { - // Array to hold traits with keywords in it. - var traitsWithKey = new Array; - // Get the index of the trait. - var traitIndex = null; - - for(var i = 0; i < availableTrait.length; i++) { - if (availableTrait[i].toLowerCase() == txtField.val().toLowerCase()) { - // Compare user input against the array of traits to see - // if trait is available/present. - traitIndex = i; - break; - } - - if (availableTrait[i].toLowerCase().indexOf(txtField.val().toLowerCase()) > -1) { - // Get traits with keywords in it. - traitsWithKey.push(availableTrait[i]); - } - } - - // Process result of comparison. - if(traitIndex != null) { - // If search is successful - add reset link - // to Clear Search to allow user to reset search. - var resetLink = 'Clear search'; - $('div.subtitle-left').append(resetLink); - - $('#lnk-reset').click(function() { - // Reset search - remove the result and reset button label. - txtField.val('Search Trait'); - // Remove search result. - removeElements(); - txtField.focus(); - }); - - // Determine which category the trait is in. - // Include category in search result. - var traitCategory = ''; - - var countLi = $('#tabs tr').size(); - for(var x = 0; x < countLi; x++) { - var m = $('#tabs tr').eq(x).find('div').text(); - if (m == txtField.val()) { - var traitTrIndex = x; - break; - } - } - - var tabIn = $('tr').eq(x).closest('div').index(); - traitCategory = $('#instructions-tab').find('li').eq(tabIn - 1).text(); - - // Find the table the trait is in. - var traitType = '* This trait is '+traitCategory+''; - - // When table is found. - // Copy the table row with the trait information. - var traitRow = $('#tabs table').find('tr').eq(traitTrIndex).html(); - var tableHeader = $('table').eq(1).find('tr').eq(0).html(); - var newDiv = traitType+''+tableHeader+''+traitRow+'
    '; - $('#container-search-result').append(newDiv); - } - else { - // No traits found, but suggest a trait. - if (traitsWithKey.length > 0) { - // Found traits for suggestion. - var suggestion = ''; - - for(var i = 0; i < traitsWithKey.length; i++) { - suggestion = suggestion + ' • '+traitsWithKey[i]+' '; - } - $('#container-search-result').append('

    Did you mean: ' + suggestion + '

    '); - - // Enable links in did you mean:/suggested traits - $('#list-suggest a').click(function() { - txtField.val($(this).text()); - btnSearch.click(); - }); - } - else { - // No trait found, none to suggest. - var title = 'The trait you entered does not have an exact match.'; - var message = 'Please select the best matching trait from the autocomplete drop-down.'; - errorMessage(title, message); - } - } - } - }); - - // Remove search results and error messages. - function removeElements() { - // Remove any error messages from previous search. - if ($('div.messages').length > 0) { - $('div.messages').remove(); - } - - // Remove any suggested links from previous search. - if ($('#list-suggest').length > 0) { - $('#list-suggest').remove(); - } - - // Remove any search result from previous search. - if ($('#container-search-result table').length > 0) { - $('#container-search-result table, #container-search-result em').hide('slow').remove(); - } - - // Remove any search result from previous search. - if ($('#container-search-result').length > 0) { - $('#container-search-result').removeClass('search-on'); - } - - // Remove clear button from previous search. - if ($('#lnk-reset').length > 0) { - $('#lnk-reset').remove(); - } - } - - // Display error message. - function errorMessage(title, message) { - var error = '
    '+title+' '+message+'
    '; - $('#container-search-result').append(error); - - $('#edit-txt-search').focus(); - } - - // Select project panel. - var speed = 200; - var panel = $('#container-sel-project'); - var linkProject = $('span a'); - - linkProject.click(function(e) { - e.preventDefault(); - - if (panel.is(':hidden')) { - panel.slideDown(speed, function() { - linkProject.text('Close Window'); - }); - } - else { - panel.slideUp(speed, function() { - linkProject.text('Change Project'); - }); - } - }); - } - }; -}(jQuery)); +/** + * @file + * Manage behaviors in instructions page. + */ + (function($) { + Drupal.behaviors.rawphenoInstructionsTab = { + attach: function (context, settings) { + // Make the project select box into a jump menu and add the project id number to the url. + var instructionsRoute = drupalSettings.rawphenotypes.vars.instructions_route; + $('#rawpheno-ins-sel-project') + .once('#rawpheno-ins-sel-project') + .change(function() { + if ($(this).val() > 0) { + window.location.href = instructionsRoute + '/' + $(this).val(); + } + }); + + // Maintain search box width to prevent autocomplete search suggestions + // from being cut off. When width becomes less, remove context button. + // Alternative button is provided in Standard Procedure Tab. + + // To prevent Tabs from stacking each other - use smaller font size. + var parentContainer = $('div.container-page'); + $(window).resize(function() { + var w = parentContainer.width(); + var e = $('.subtitle-right'); + + var eVal = (w <= 750) ? 'none' : ''; + e.css('display', eVal); + + // Manage tabs. + eVal = (w <= 850) ? 0.68 : 1; + e = $('#container-instructions #tabs li a'); + e.css('font-size', eVal + 'em'); + }); + + // IMAGE GALLERY + // Array to hold all image file. + var gallery = new Array(0, 1); + // Array to hold all relevant captions. + var caption = new Array(0, 1); + + // Gallery elements set 1. + gallery[0] = ['01-tendrils-no-elongation', + '02-tendrils-no-elongation', + '01-tendrils-elongation']; + + caption[0] = ['No elongation', + 'No elongation', + 'Elongation']; + + // Gallery elements set 2. + gallery[1] = ['01-pods-emerged', + '02-pods-emerged', + '01-pods-variation', + '02-pods-variation']; + + caption[1] = ['Freshly emerged pods', + 'Freshly emerged pods', + 'Sample of the pod variation', + 'Sample of the pod variation']; + + // Path to appendix folder. + var imgPath = drupalSettings.rawphenotypes.vars.image_gallery_path; + + // Attach behavior to Photo Appendix. + $('#fragment-6 a').click(function(){ + // Option select containing topics. + var topic = $('#fragment-6 select').val(); + // Image container - showing default image. + var curImg = $('#cur-img').val(); + + // Hold the index of the current image shown. + curImg = parseInt(curImg); + + // Determine if user clicks on next or prev link + // in the image gallery. + var showImg = ($(this).attr('class') == 'a-left') ? curImg - 1 : curImg + 1; + + // Replace the image src to show the next or prev image in the topic. + if (gallery[topic][showImg]) { + $('#fragment-6 img').attr('src', imgPath+gallery[topic][showImg]+'.jpg'); + $('#fragment-6 em').contents().replaceWith(caption[topic][showImg]); + $('#cur-img').val(showImg); + } + }); + + // When user will change topic, load default or first item in the + // image and caption array. + $('#fragment-6 select').change(function(){ + var topic = $(this).val(); + $('#fragment-6 img').attr('src', imgPath+gallery[topic][0]+'.jpg'); + $('#fragment-6 em').contents().replaceWith(caption[topic][0]); + $('#cur-img').val(0); + }); + + // SEARCH FUNCTIONALITY + // Reference to search textbox. + var txtField = $('#edit-txt-search'); + // Reference to search buttons. + var btnSearch = $("#btn_submit"); + + // Create a label as default value in the search text field. + // When selected will clear the value. + txtField.focus(function() { + if (this.value == 'Search Trait') { + this.value = ''; + }}).blur(function() { + if (this.value.trim() == '') { + this.value = 'Search Trait'; + removeElements(); + }}); + + // Clear all search when user types in new + // keywords into the search field. + txtField.keypress(function() { + removeElements(); + }); + + // Initialize JQuery Tabs. + $('#tabs').tabs({delay:0}); + + // Array of traits. + var availableTrait = new Array(); + + // Access JSON List of traits created by function callback + // in instructions page. + var pathJSON = $('#traits-json').val(); + var objTraits = $.getJSON(pathJSON, function(result) { + $.each(result, function(i, field) { + availableTrait[i] = field; + }) + }); + + // Search button is clicked. + btnSearch.click(function() { + // Clear or reset previous search. + removeElements(); + + $('#container-search-result').addClass('search-on'); + + if (txtField.val() == null || txtField.val().trim() == '' || txtField.val().length <= 1) { + // Search field is blank. + var title = 'Invalid value in search field.'; + var message = 'Please type in keywords and select the best matching trait from the autocomplete drop-down.'; + errorMessage(title, message); + } + else { + // Array to hold traits with keywords in it. + var traitsWithKey = new Array; + // Get the index of the trait. + var traitIndex = null; + + for(var i = 0; i < availableTrait.length; i++) { + if (availableTrait[i].toLowerCase() == txtField.val().toLowerCase()) { + // Compare user input against the array of traits to see + // if trait is available/present. + traitIndex = i; + break; + } + + if (availableTrait[i].toLowerCase().indexOf(txtField.val().toLowerCase()) > -1) { + // Get traits with keywords in it. + traitsWithKey.push(availableTrait[i]); + } + } + + // Process result of comparison. + if(traitIndex != null) { + // If search is successful - add reset link + // to Clear Search to allow user to reset search. + var resetLink = 'Clear search'; + $('div.subtitle-left').append(resetLink); + + $('#lnk-reset').click(function() { + // Reset search - remove the result and reset button label. + txtField.val('Search Trait'); + // Remove search result. + removeElements(); + txtField.focus(); + }); + + // Determine which category the trait is in. + // Include category in search result. + var traitCategory = ''; + + var countLi = $('#tabs tr').size(); + for(var x = 0; x < countLi; x++) { + var m = $('#tabs tr').eq(x).find('div').text(); + if (m == txtField.val()) { + var traitTrIndex = x; + break; + } + } + + var tabIn = $('tr').eq(x).closest('div').index(); + traitCategory = $('#instructions-tab').find('li').eq(tabIn - 1).text(); + + // Find the table the trait is in. + var traitType = '* This trait is '+traitCategory+''; + + // When table is found. + // Copy the table row with the trait information. + var traitRow = $('#tabs table').find('tr').eq(traitTrIndex).html(); + var tableHeader = $('table').eq(1).find('tr').eq(0).html(); + var newDiv = traitType+''+tableHeader+''+traitRow+'
    '; + $('#container-search-result').append(newDiv); + } + else { + // No traits found, but suggest a trait. + if (traitsWithKey.length > 0) { + // Found traits for suggestion. + var suggestion = ''; + + for(var i = 0; i < traitsWithKey.length; i++) { + suggestion = suggestion + ' • '+traitsWithKey[i]+' '; + } + $('#container-search-result').append('

    Did you mean: ' + suggestion + '

    '); + + // Enable links in did you mean:/suggested traits + $('#list-suggest a').click(function() { + txtField.val($(this).text()); + btnSearch.click(); + }); + } + else { + // No trait found, none to suggest. + var title = 'The trait you entered does not have an exact match.'; + var message = 'Please select the best matching trait from the autocomplete drop-down.'; + errorMessage(title, message); + } + } + } + }); + + // Remove search results and error messages. + function removeElements() { + // Remove any error messages from previous search. + if ($('div.messages').length > 0) { + $('div.messages').remove(); + } + + // Remove any suggested links from previous search. + if ($('#list-suggest').length > 0) { + $('#list-suggest').remove(); + } + + // Remove any search result from previous search. + if ($('#container-search-result table').length > 0) { + $('#container-search-result table, #container-search-result em').hide('slow').remove(); + } + + // Remove any search result from previous search. + if ($('#container-search-result').length > 0) { + $('#container-search-result').removeClass('search-on'); + } + + // Remove clear button from previous search. + if ($('#lnk-reset').length > 0) { + $('#lnk-reset').remove(); + } + } + + // Display error message. + function errorMessage(title, message) { + var error = '
    '+title+' '+message+'
    '; + $('#container-search-result').append(error); + + $('#edit-txt-search').focus(); + } + + // Select project panel. + var speed = 200; + var panel = $('#container-sel-project'); + var linkProject = $('span a'); + + linkProject.once('span a').click(function(e) { + e.preventDefault(); + + if (panel.is(':hidden')) { + panel.slideDown(speed, function() { + linkProject.text('Close Window'); + }); + } + else { + panel.slideUp(speed, function() { + linkProject.text('Change Project'); + }); + } + }); + } + }; +}(jQuery)); \ No newline at end of file diff --git a/phpunit.xml b/phpunit.xml deleted file mode 100644 index 10111ca..0000000 --- a/phpunit.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - tests - - - diff --git a/rawpheno.info b/rawpheno.info deleted file mode 100755 index 740eaad..0000000 --- a/rawpheno.info +++ /dev/null @@ -1,8 +0,0 @@ -name = Raw Phenotypes -description = Provides generic upload, storage, backup and export of raw phenotypic data. -core = 7.x -package = Tripal Extensions -dependencies[] = dragndrop_upload_element -dependencies[] = tripal_cv -dependencies[] = libraries -configure = admin/tripal/extension/rawphenotypes diff --git a/rawpheno.install b/rawpheno.install deleted file mode 100755 index 7fe9d26..0000000 --- a/rawpheno.install +++ /dev/null @@ -1,1345 +0,0 @@ - t('Table for phenotype plant.'), - 'fields' => array( - 'plant_id' => array( - 'description' => t('A unique ID for each row'), - 'type' => 'serial', - 'unsigned' => TRUE, - 'not null' => TRUE, - ), - 'stock_id' => array( - 'description' => t('A unique ID for a stock name.'), - 'type' => 'int', - 'unsigned' => TRUE, - 'not null' => TRUE,), - ), - ); - - // PHENO PLANT PROP - // Type should include entry, plot, rep and location. - $schema['pheno_plantprop'] = array( - 'description' => t('Table for phenotype plantprop.'), - 'fields' => array( - 'plantprop_id' => array( - 'description' => t('Primary key: A unique ID for each plantprop record.'), - 'type' => 'serial', - 'unsigned' => TRUE, - 'not null' => TRUE - ), - 'plant_id' => array( - 'description' => t('Holds plant ID number.'), - 'type' => 'int', - 'unsigned' => TRUE, - 'not null' => TRUE, - ), - 'type_id' => array( - 'description' => t('Term ID number of trait inserted.'), - 'type' => 'int', - 'unsigned' => TRUE, - 'not null' => TRUE - ), - 'value' => array( - 'description' => t('The value of the trait measured.'), - 'type' => 'varchar', - 'length' => 255, - 'not null' => TRUE - ,) - ,), - 'primary key' => array('plantprop_id'), - 'foreign keys' => array( - 'pheno_plant' => array( - 'table' => 'pheno_plant', - 'columns' => array('plant_id' => 'plant_id')),), - ); - - // PHENO SCALE MEMBER - // The scale code is what is written in the spreadsheet, - // whereas the value is what it actually represents. - $schema['pheno_scale_member'] = array( - 'description' => t('Table for pheno scale member.'), - 'fields' => array( - 'member_id' => array( - 'description' => t('Primary key: A unique ID for each scale value.'), - 'type' => 'serial', - 'unsigned' => TRUE, - 'not null' => TRUE, - ), - 'scale_id' => array( - 'description' => t('Scale ID number of a scale value.'), - 'type' => 'int', - 'unsigned' => TRUE, - 'not null' => TRUE, - ), - 'code' => array( - 'description' => t('Corresponding code for each scale ID.'), - 'type' => 'varchar', - 'length' => 255, - ), - 'value' => array( - 'description' => t('Description of a scale value.'), - 'type' => 'text', - 'not null' => TRUE - ,) - ,), - 'primary key' => array('member_id'), - ); - - // PHENO MEASUREMENTS - // Modified is the timestamp that it was last modified. - // This allows us to determine when values were updated. - $schema['pheno_measurements'] = array( - 'description' => t('Table for phenotype measurements'), - 'fields' => array( - 'measurement_id' => array( - 'description' => t('Primary key: A unique ID for each trait measured.'), - 'type' => 'serial', - 'unsigned' => TRUE, - 'not null' => TRUE, - ), - 'plant_id' => array( - 'description' => t('Holds plant ID number.'), - 'type' => 'int', - 'unsigned' => TRUE, - 'not null' => TRUE, - ), - 'type_id' => array( - 'description' => t('Term ID number of trait inserted.'), - 'type' => 'int', - 'unsigned' => TRUE, - 'not null' => TRUE, - ), - 'unit_id' => array( - 'description' => t('Unit ID number of trait inserted.'), - 'type' => 'int', - 'unsigned' => TRUE, - 'not null' => TRUE, - ), - 'value' => array( - 'description' => t('The value of the trait measured.'), - 'type' => 'varchar', - 'length' => 255, - 'not null' => FALSE, - ), - 'cvalue_id' => array( - 'description' => t('Scale member code number when unit is scale.'), - 'type' => 'varchar', - 'length' => 10, - 'not null' => FALSE, - ), - 'modified' => array( - 'description' => t('timestamp of the last modification.'), - 'type' => 'varchar', - 'length' => 30, - 'not null' => TRUE, - ) - ,), - 'primary key' => array('measurement_id'), - 'foreign keys' => array( - 'pheno_plant' => array( - 'table' => 'pheno_plant', - 'columns' => array('plant_id' => 'plant_id')),), - ); - // - - // PROJECT-CVTERM - // Group a set of traits/cvterm to a project. - $schema['pheno_project_cvterm'] = array( - 'description' => t('Table for grouping set of traits to a project.'), - 'fields' => array( - 'project_cvterm_id' => array( - 'description' => t('Primary key: A unique ID number.)'), - 'type' => 'serial', - 'not null' => TRUE, - ), - 'project_id' => array( - 'description' => t('ID number of a project from chado.project table'), - 'type' => 'int', - 'not null' => TRUE, - ), - 'cvterm_id' => array( - 'description' => t('cvterm id number of a trait'), - 'type' => 'int', - 'not null' => TRUE, - ), - // This option will allow for grouping of traits. In AGILE Project, types include: - // Essential Traits, Optional Traits, Subset Trait and Plant Property. - // Use this field to indicate as such. - // A function in rawpheno.function.measurements lists possible types that can be made available - // to admin when adding traits in the admin control panel. - 'type' => array( - 'description' => t('Indicate a column header is essential, not essential (optional), a plant property and other'), - 'type' => 'varchar', - 'length' => 20, - ), - ), - 'primary key' => array('project_cvterm_id'), - ); - - // PROJECT-PLANT/STOCK - $schema['pheno_plant_project'] = array( - 'description' => t('Table for mapping stock to a project'), - 'fields' => array( - 'plant_project_id' => array( - 'description' => t('Primary key: A nunique ID number.'), - 'type' => 'serial', - 'not null' => TRUE, - ), - 'project_id' => array( - 'description' => t('ID number of a project from chado.project table.'), - 'type' => 'int', - 'not null' => TRUE, - ), - 'plant_id' => array( - 'description' => t('ID number of stock from pheno_plant table.'), - 'type' => 'int', - 'not null' => TRUE, - ), - ), - 'primary key' => array('plant_project_id'), - ); - - // PROJECT-USER - $schema['pheno_project_user'] = array( - 'description' => t('This table will hold information about user and project'), - 'fields' => array( - 'project_user_id' => array( - 'description' => t('Primary Key: A unique ID number'), - 'type' => 'serial', - 'not noll' => TRUE, - ), - 'project_id' => array( - 'description' => t('ID number of project from chado.project table'), - 'type' => 'int', - 'not null' => TRUE, - ), - 'uid' => array( - 'description' => t('ID number of logged in user from user table'), - 'type' => 'int', - 'not null' => TRUE, - ), - ), - 'primary key' => array('project_user_id'), - ); - - // PROJECT-BACKUP-FILE - $schema['pheno_backup_file'] = array( - 'description' => t('This table will hold information about files uploaded by user to a project'), - 'fields' => array( - 'file_id' => array( - 'description' => t('Primary Key: A unique ID number'), - 'type' => 'serial', - 'not null' => TRUE, - ), - 'project_user_id' => array( - 'description' => t('The project ID number from table pheno_project_user'), - 'type' => 'int', - 'not null' => TRUE, - ), - 'fid' => array( - 'description' => t('File ID number from file_managed table'), - 'type' => 'int', - 'not null' => TRUE, - ), - 'version' => array( - 'description' => t('The sequence number of the file in relation to a project'), - 'type' => 'int', - ), - 'validation_result' => array( - 'description' => t('Stores the result of file validation performed'), - 'type' => 'text', - ), - 'notes' => array( - 'description' => t('Notes, description and comments made to a file'), - 'type' => 'text', - ), - 'archive' => array( - 'description' => t('Indicates whether a file is temporarily removed from a project.'), - 'type' => 'varchar', - 'length' => 2, - 'default' => 'n', - ), - ), - 'primary key' => array('file_id'), - ); - - return $schema; -} - -// Include function to manage column headers, cv terms and variable names. -module_load_include('inc', 'rawpheno', 'include/rawpheno.function.measurements'); - -/** - * Function to manage terms used by this module. - * - * @return - * An array of string representing scale measurements, variable names, - * controlled vocabulary and measurement units. - */ -function rawpheno_function_terms($type) { - switch($type) { - case 'project': - return 'AGILE: Application of Genomic Innovation in the Lentil Economy'; - break; - - case 'defaults': - // Default colour scheme and heading/title. - return array('colour' => '#304356', - 'rawdata' => 'Phenotypic data available', - 'download' => 'Select locations and traits that you want to download', - 'instructions' => 'Standard Phenotyping Procedure', - 'upload' => 'Drag and Drop phenotypic data collection spreadsheet', - 'backup' => 'My files', - 'r-words' => 'of,to,have,on,at', - 'r-chars' => '(,),/,-,:,;,%', - 'r-replace' => '# = num,/ = div,? = unsure,- = to',); - break; - - case 'scales': - // Scale equivalent for scale units. - return array('Lodging (Scale: 1-5) upright - lodged' => array( - 1 => 'Vertical/upright', - 2 => 'Leaning', - 3 => 'Most plants at 45 degrees angle', - 4 => 'All plants 10-45 degrees from ground', - 5 => 'Most plants flat/prostrate',)); - break; - - case 'variables': - // Configuration variable names. - return array('rawpheno_colour_scheme', - 'rawpheno_rawdata_title', - 'rawpheno_download_title', - 'rawpheno_instructions_title', - 'rawpheno_upload_title', - 'rawpheno_backup_title', - 'rawpheno_rtransform_words', - 'rawpheno_rtransform_characters', - 'rawpheno_rtransform_replace',); - break; - - case 'vocabularies': - // Controlled vocabularies. - return array('cv_prop' => 'phenotype_plant_property_types', - 'cv_unit' => 'phenotype_measurement_units', - 'cv_type' => 'phenotype_measurement_types', - 'cv_rver' => 'phenotype_r_compatible_version', - 'cv_desc' => 'phenotype_collection_method',); - break; - - case 'units': - // Units available in the spreadsheet. - return rawpheno_function_default_unit('def'); - - break; - } -} - - -/** - * Implements hook_install(). - */ -function rawpheno_install() { - // Get variable names and default values. - $vars = rawpheno_function_terms('variables'); - $config = rawpheno_function_terms('defaults'); - - // # 1. INSERT CV. - // Insert all cvs required. - $cv = rawpheno_function_terms('vocabularies'); - - // Array to hold cv_id and cv_name. - // This will be used when creating cvterm relationships. - $arr_cvid_cvname = array(); - - $chado_inscv_exists = function_exists('chado_insert_cv'); - - foreach($cv as $cv_i => $cv_name) { - $def = str_replace('_', ' ', ucfirst($cv_name)); - $tmp = ($chado_inscv_exists) - ? chado_insert_cv($cv_name, $def) - : tripal_insert_cv($cv_name, $def); - - if ($tmp) { - $arr_cvid_cvname[$cv_name] = $tmp->cv_id; - } - else { - tripal_report_error('Raw Phenotypes', TRIPAL_CRITICAL, 'Chado/Tripal failed to insert cv', array(), array('print' => TRUE)); - } - } - - // # 2. INSERT UNITS. - // Insert units and scale member used by scale. - // NOTE: Scale memeber is link to the column header Lodging (scale 1-5) and not to scale unit. - // When the column header Lodging is inserted, only then we can insert the scale members. - - // Array to hold scale members. - $arr_unit = rawpheno_function_terms('units'); - // Array to hold cvterm_id of each inserted unit. - // This will be used when creating a cvterm relationship linking a cvterm to a unit. - $arr_cvtermid_unit = array(); - - $chado_insterm_exists = function_exists('chado_insert_cvterm'); - - foreach($arr_unit as $unit => $def) { - $ins_unit = array( - 'id' => 'rawpheno_tripal:' . $unit, - 'name' => $unit, - 'definition' => $def, - 'cv_name' => $cv['cv_unit'] - ); - - $tmp = ($chado_insterm_exists) - ? chado_insert_cvterm($ins_unit) - : tripal_insert_cvterm($ins_unit); - - if ($tmp) { - $arr_cvtermid_unit[$unit] = $tmp->cvterm_id; - } - else { - tripal_report_error('Raw Phenotypes', TRIPAL_CRITICAL, 'Chado/Tripal failed to insert cvterm (trait unit)', array(), array('print' => TRUE)); - } - } - - // # 3. INSERT COLUMN HEADERS. - // Insert column headers. - // When inserting column headers, generate the R Version, add collection method - // and map it to a AGILE (only when this project is available). - - // Default Project. - // This is the default project, only when such project exists, otherwise, insert the column headers - // and make them available for use in admin control panel. - // @note project.name must be unique so no need to limit 1. - $default_project = rawpheno_function_terms('project'); - $sql = "SELECT project_id FROM {project} WHERE name = :default_project"; - $args = array(':default_project' => $default_project); - $prj_id = chado_query($sql, $args); - - if ($prj_id->rowCount()) { - $project_ID = $prj_id->fetchField(); - - // When AGILE is present in this site, add a user to this project. - db_insert('pheno_project_user') - ->fields(array('project_id' => $project_ID, - 'uid' => $GLOBALS['user']->uid)) - ->execute(); - - // If the user is not the superadmin, add superadmin as well. - if ($GLOBALS['user']->uid != 1) { - db_insert('pheno_project_user') - ->fields(array('project_id' => $project_ID, - 'uid' => 1)) - ->execute(); - } - } - - // Initialize R Transformation rules required in generating R version of header. - // The default rules are the rules required in generating R version of headers default to this module. - variable_set($vars[6], $config['r-words']); - variable_set($vars[7], $config['r-chars']); - variable_set($vars[8], $config['r-replace']); - - // Array to hold header types. - $trait_type = rawpheno_function_trait_types(); - - // Array to hold all column headers, plant property headers and essential headers. - $arr_allheaders = rawpheno_function_headers('expected'); - $arr_subsetheader = rawpheno_function_headers('subset'); - $arr_plantpropheader = rawpheno_function_headers('plantprop'); - $arr_essentialheader = rawpheno_function_headers('essential'); - - foreach($arr_allheaders as $header) { - // Skip when it is Name. - if ($header == 'Name') { - continue; - } - - // Get the definition and collection method. - $cvterm_info = rawpheno_function_get_definition($header); - // When no information is retured, default the definition to the term itself. - $cvterm_definition = (!empty($cvterm_info['define'])) ? $cvterm_info['define'] : $header; - // Set the type of header. - $cvterm_type = (in_array($header, $arr_plantpropheader)) ? $cv['cv_prop'] : $cv['cv_type']; - - $ins_term = array( - 'id' => 'rawpheno_tripal:' . $header, - 'name' => $header, - 'definition' => $cvterm_definition, - 'cv_name' => $cvterm_type - ); - - // Insert the cvterm. - $cvterm = ($chado_insterm_exists) - ? chado_insert_cvterm($ins_term) - : tripal_insert_cvterm($ins_term); - - // Insert the cvterm data collection method. - if (!empty($cvterm_info['method']) && $cvterm) { - // Data collection method. - chado_insert_record('cvtermprop', - array( - 'cvterm_id' => $cvterm->cvterm_id, - 'type_id' => $arr_cvid_cvname[ $cv['cv_desc'] ], - 'value' => $cvterm_info['method'], - 'rank' => 0 - ) - ); - } - else { - tripal_report_error('Raw Phenotypes', TRIPAL_CRITICAL, 'Chado/Tripal failed to insert cvterm (traits)', array(), array('print' => TRUE)); - } - - // Relate cvterm and unit. - // No relationship when header is Plant Property Type. - if (!in_array($header, $arr_plantpropheader) && $cvterm) { - // Extract the unit part from the column header. Usually, the information enclosed in a parenthesis. - $header_unit = rawpheno_function_header_unit($header); - $header_unit = (empty($header_unit)) ? 'text' : $header_unit; - - chado_insert_record('cvterm_relationship', - array( - 'subject_id' => $arr_cvtermid_unit[$header_unit], - 'type_id' => $arr_cvid_cvname[ $cv['cv_unit'] ], - 'object_id' => $cvterm->cvterm_id - ) - ); - } - - // R version. To be implemented to all column headers. - $r_version = rawpheno_function_make_r_compatible($header); - - if ($cvterm) { - chado_insert_record('cvtermprop', - array( - 'cvterm_id' => $cvterm->cvterm_id, - 'type_id' => $arr_cvid_cvname[ $cv['cv_rver'] ], - 'value' => $r_version, - 'rank' => 0 - ) - ); - } - - // Finally, map this particular header to project, again, only when default project is present. - if (isset($project_ID) AND $project_ID > 0) { - // Indicate the type of header. - if (in_array($header, $arr_plantpropheader)) { - // Type is Plant Property. - $type = $trait_type['type4']; - } - elseif (in_array($header, $arr_essentialheader)) { - // Trait is essential (must be in the spreadsheet). - $type = $trait_type['type1']; - } - elseif (in_array($header, $arr_subsetheader)) { - // Subset Trait - $type = $trait_type['type3']; - } - else { - // Trait is optional - $type = $trait_type['type2']; - } - - db_insert('pheno_project_cvterm') - ->fields(array( - 'project_id' => $project_ID, - 'cvterm_id' => $cvterm->cvterm_id, - 'type' => $type - ) - ) - ->execute(); - } - } - - // Lodging (scale 1-5) has been added, so the scale member table can now be generated. - $lodging = rawpheno_function_terms('scales'); - - $cl = array('name' => array_keys($lodging)[0], 'cv_id' => array('name' => $cv['cv_type'])); - - if (function_exists('chado_get_cvterm')) { - $cvterm_lodging = chado_get_cvterm($cl); - } - else { - $cvterm_lodging = tripal_get_cvterm($cl); - } - - // Insert scale values 1 - 5 - foreach (array_values($lodging)[0] as $code => $val) { - db_insert('pheno_scale_member') - ->fields(array( - 'scale_id' => $cvterm_lodging->cvterm_id, - 'code' => $code, - 'value' => $val - )) - ->execute(); - } - - - // # 4. MATERIALIZED VIEW. - // Create materialized view used in phenotypes/rawdata. - // Using Tripal: tripal_add_mview(). - $mv_name = 'rawpheno_rawdata_summary'; - $mv_module = 'rawpheno'; - $mv_comment = 'Materialized view used by rawpheno module to generate summary of traits per location, rep and year.'; - - // Create a summary of data in the following format: - // Plant id, Location, Rep, Planting Date and Total traits count. - // NOTE: trait count includes planting date. - // NOTE: As a materialized view, this get's executed by chado query. - $mv_sql = "SELECT CAST(t1.plant_id AS numeric) AS plant_id, - t2.value AS location, - t3.value AS rep, - t5.value AS planting_year, - COUNT(DISTINCT t4.type_id) AS total_count, - ARRAY_TO_STRING(ARRAY_AGG(DISTINCT t4.type_id), ',') AS all_traits - FROM pheno_plant AS t1 - INNER JOIN [pheno_plantprop] AS t2 USING(plant_id) - INNER JOIN [pheno_plantprop] AS t3 USING(plant_id) - INNER JOIN [pheno_measurements] AS t4 USING(plant_id) - INNER JOIN [pheno_measurements] AS t5 USING(plant_id) - WHERE - t2.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 - t3.type_id = (SELECT cvterm_id FROM {cvterm} cvt LEFT JOIN {cv} ON cv.cv_id=cvt.cv_id WHERE cvt.name = 'Rep' AND cv.name = 'phenotype_plant_property_types') AND - t5.type_id = (SELECT cvterm_id FROM {cvterm} cvt LEFT JOIN {cv} ON cv.cv_id=cvt.cv_id WHERE cvt.name = 'Planting Date (date)' AND cv.name = 'phenotype_measurement_types') - GROUP BY t1.plant_id, t4.plant_id, t2.value, t3.value, t5.value"; - - // Schema array. - $mv_schema = array ( - 'table' => 'rawpheno_rawdata_mview', - 'fields' => array ( - 'plant_id' => array ( - 'type' => 'int'), - 'location' => array ( - 'type' => 'varchar', - 'length' => 255), - 'rep' => array ( - 'type' => 'varchar', - 'length' => 255), - 'planting_date' => array ( - 'type' => 'varchar', - 'length' => 255), - 'total_count' => array ( - 'type' => 'int'), - 'all_traits' => array( - 'type' => 'text'), - ), - 'indexes' => array( - 'plant_id' => array('plant_id'), - 'location' => array('location'), - ) - ); - - // Create materialized view. - if (function_exists('chado_add_mview')) { - chado_add_mview($mv_name, $mv_module, $mv_schema, $mv_sql, $mv_comment, FALSE); - } - else { - tripal_add_mview($mv_name, $mv_module, $mv_schema, $mv_sql, $mv_comment, FALSE); - } - - // #5. DEFAULT SITE SETTINGS. - // Set default colour scheme and page titles. - variable_set($vars[0], $config['colour']); - variable_set($vars[1], $config['rawdata']); - variable_set($vars[2], $config['download']); - variable_set($vars[3], $config['instructions']); - variable_set($vars[4], $config['upload']); - variable_set($vars[5], $config['backup']); - - // Add data collection spreadsheet to public:// directory, - // and link this file for download in instructions page. - $destination = file_default_scheme() . '://'; - if (file_prepare_directory($destination, FILE_MODIFY_PERMISSIONS)) { - // Add this file if directory is writable. - // File is in zip format as it is more friendly for download than xlsx. - $file = 'AGILE-PhenotypeDataCollection-v5.xlsx.zip'; - $source = drupal_get_path('module', 'rawpheno') . '/theme/' . $file; - // Copy file without creating an entry in drupal database. - // If file is present, replace it with the same file. - file_unmanaged_copy($source, $destination, FILE_EXISTS_REPLACE); - } -} - - -/** - * Implements hook_uninstall(). - */ -function rawpheno_uninstall() { - // # 1. UNREGISTER PERSISTENT VARIABLES. - $vars = rawpheno_function_terms('variables'); - foreach($vars as $var) { - if (variable_get($var)) { - variable_del($var); - } - } - - // # 2. DROP MATERIALIZED VIEW TABLE. - // Using tripal: tripal_delete_mview(). - // Get mview id of materilaized view created by hook_install(). - // @note tripal_mviews.mv_table must be unique (constraint). - $sql = "SELECT mview_id FROM {tripal_mviews} WHERE mv_table = :mv_table"; - $mview_id = db_query($sql, array(':mv_table' => 'rawpheno_rawdata_mview')) - ->fetchField(); - - if (isset($mview_id) AND $mview_id > 0) { - // Delete materialized view. - tripal_delete_mview($mview_id); - } - - // # 3. DELETE CV AND CVTERM. - $cv = rawpheno_function_terms('vocabularies'); - - foreach($cv as $cv_i => $cv_name) { - if (function_exists('chado_get_cv')) { - $cv = chado_get_cv(array('name' => $cv_name)); - } - else { - $cv = tripal_get_cv(array('name' => $cv_name)); - } - - $args = array(':cv_id' => $cv->cv_id); - - // Delete relationships. - if ($cv_i == 'cv_unit') { - $del = "DELETE FROM {cvterm_relationship} WHERE type_id = :cv_id"; - chado_query($del, $args); - } - - // Delete R version. - if ($cv_i == 'cv_rver') { - $del = "DELETE FROM {cvtermprop} WHERE type_id = :cv_id"; - chado_query($del, $args); - } - - // Delete cvterms. - if ($cv_i == 'cv_type') { - $del = "DELETE FROM {cvterm} WHERE cv_id = :cv_id"; - chado_query($del, $args); - } - - // Finally, delete the cv - $del = "DELETE FROM {cv} WHERE cv_id = :cv_id"; - chado_query($del, $args); - } -} - - -/** - * Implements hook_update_N(). - * Map cvterm in tripal database to rawpheno_tripal. - * - * N - * 7 - Drupal core compatibility. - * 2 - Module's major release version. - * 01 - Sequence count. - */ -function rawpheno_update_7201() { - // Rawphenotypes database name. - $rawpheno_dbname = 'rawpheno_tripal'; - // db_id - $rawpheno_dbid = ''; - - // In db table, check to see if rawpheno_tripal exists. - // @note db.name must be unique (constraint). - $sql = "SELECT db_id FROM {db} WHERE name = :rawpheno_dbname"; - $args = array(':rawpheno_dbname' => $rawpheno_dbname); - - $db_id = chado_query($sql, $args); - - if ($db_id->rowCount()) { - // Already created. Save the db_id - $rawpheno_dbid = $db_id - ->fetchField(); - } - else { - // Not created. Insert a row and get the id. - $db_id = chado_insert_record('db', array('name' => $rawpheno_dbname)); - $rawpheno_dbid = $db_id['db_id']; - } - - // Get the db id of Tripal db. Use it to futher filter the rows for update - // since previous version use tripal as default db. - // @note db.name must be unique (constraint). - $sql = "SELECT db_id FROM {db} WHERE name = 'tripal'"; - $tripal = chado_query($sql); - - if ($tripal->rowCount()) { - $tripal_dbid = $tripal - ->fetchField(); - - // With the db_id, update all rawphenotypes cvterms to use this db_id in dbxref. - $t = rawpheno_function_terms('vocabularies'); - unset($t['cv_rver'], $t['cv_desc']); - $terms = array_values($t); - - // Update db_id in dbxerf table. Map all rawphenotypes cvterms to rapheno_tripal db. - $sql = " - UPDATE {dbxref} SET db_id = :rawpheno_dbid - WHERE - dbxref_id IN (SELECT t2.dbxref_id FROM {cv} AS t1 INNER JOIN {cvterm} AS t2 USING (cv_id) WHERE t1.name IN (:terms)) - AND db_id = :tripal_dbid"; - - $args = array(':rawpheno_dbid' => $rawpheno_dbid, // Map to rawpheno_tripal instead. - ':terms' => $terms, // Only for terms phenotype_plant_property, phenotype_measurement_units and phenotype_measurement_type - ':tripal_dbid' => $tripal_dbid); // Where previously set to TRIPAL. - - chado_query($sql, $args); - } -} - - -/** - * Implements hook_update_N(). - * Update custom table with tables relating a project to set of cvterms - * and project to stock/name. - * - * N - * 7 - Drupal core compatibility. - * 0 - Module's major release version. - * 02 - Sequence count. - */ -function rawpheno_update_7002() { - // Table definition as per definition in hook_schema() above. - // PROJECT-CVTERM - // Group a set of traits/cvterm to a project. - $schema['pheno_project_cvterm'] = array( - 'description' => t('Table for grouping set of traits to a project.'), - 'fields' => array( - 'project_cvterm_id' => array( - 'description' => t('Primary key: A unique ID number.)'), - 'type' => 'serial', - 'not null' => TRUE, - ), - 'project_id' => array( - 'description' => t('ID number of a project from chado.project table'), - 'type' => 'int', - 'not null' => TRUE, - ), - 'cvterm_id' => array( - 'description' => t('cvterm id number of a trait'), - 'type' => 'int', - 'not null' => TRUE, - ), - // This option will allow for grouping of traits. In for AGILE Project, types include: - // Essential Traits, Optional Traits, Subset Trait and Plant Property. - // Use this field to indicate as such. - // A function in rawpheno.function.measurements lists possible types that can be made available - // to admin when adding traits in the admin control panel. - 'type' => array( - 'description' => t('Indicate a column header is essential, not essential (optional), a plant property and other'), - 'type' => 'varchar', - 'length' => 20, - ), - ), - 'primary key' => array('project_cvterm_id'), - ); - - db_create_table('pheno_project_cvterm', $schema['pheno_project_cvterm']); - - // PROJECT-PLANT/STOCK - $schema['pheno_plant_project'] = array( - 'description' => t('Table for mapping stock to a project'), - 'fields' => array( - 'plant_project_id' => array( - 'description' => t('Primary key: A nunique ID number.'), - 'type' => 'serial', - 'not null' => TRUE, - ), - 'project_id' => array( - 'description' => t('ID number of a project from chado.project table.'), - 'type' => 'int', - 'not null' => TRUE, - ), - 'plant_id' => array( - 'description' => t('ID number of stock from pheno_plant table.'), - 'type' => 'int', - 'not null' => TRUE, - ), - ), - 'primary key' => array('plant_project_id'), - ); - - db_create_table('pheno_plant_project', $schema['pheno_plant_project']); - - // PROJECT-USER - $schema['pheno_project_user'] = array( - 'description' => t('This table will hold information about user and project that'), - 'fields' => array( - 'project_user_id' => array( - 'description' => t('Primary Key: A unique ID number'), - 'type' => 'serial', - 'not noll' => TRUE, - ), - 'project_id' => array( - 'description' => t('ID number of project from chado.project table'), - 'type' => 'int', - 'not null' => TRUE, - ), - 'uid' => array( - 'description' => t('ID number of logged in user from user table'), - 'type' => 'int', - 'not null' => TRUE, - ), - ), - 'primary key' => array('project_user_id'), - ); - - db_create_table('pheno_project_user', $schema['pheno_project_user']); - - // PROJECT-BACKUP-FILE - $schema['pheno_backup_file'] = array( - 'description' => t('This table will hold information about files uploaded by user to a project'), - 'fields' => array( - 'file_id' => array( - 'description' => t('Primary Key: A unique ID number'), - 'type' => 'serial', - 'not null' => TRUE, - ), - 'project_user_id' => array( - 'description' => t('The project ID number from table pheno_project_user'), - 'type' => 'int', - 'not null' => TRUE, - ), - 'fid' => array( - 'description' => t('File ID number from file_managed table'), - 'type' => 'int', - 'not null' => TRUE, - ), - 'version' => array( - 'description' => t('The sequence number of the file in relation to a project'), - 'type' => 'int', - ), - 'validation_result' => array( - 'description' => t('Stores the result of file validation performed'), - 'type' => 'text', - ), - 'notes' => array( - 'description' => t('Notes, description and comments made to a file'), - 'type' => 'text', - ), - 'archive' => array( - 'description' => t('Indicates whether a file is temporarily removed from a project.'), - 'type' => 'varchar', - 'length' => 2, - 'default' => 'n', - ), - ), - 'primary key' => array('file_id'), - ); - - db_create_table('pheno_backup_file', $schema['pheno_backup_file']); - - - // UPDATE First Release to Project Based. - // When updating the first release to project based, first update page configuration - // to add page title to back up page at the same time initialize R transformation rules - // with default rules (rules used to generate R compatible verstion of all AGILE-specific - // traits). Next, update cvterms definition, create a relationship entry relating a unit to cvterm, - // add method of collection method description and transform a R compatible version - // of the cvterm. Finally, tell if AGILE project is present in the host site and if not, create the project - // and reference the project id number. When the module has data, map everything to this - // project and appoint the user running the update and super admin. - - // Update - Add backup page title. - $vars = rawpheno_function_terms('variables'); - $config = rawpheno_function_terms('defaults'); - variable_set($vars[5], $config['backup']); - - // Update - Add default R transformation rules. - variable_set($vars[6], $config['r-words']); - variable_set($vars[7], $config['r-chars']); - variable_set($vars[8], $config['r-replace']); - - // AGILE Project. - $default_project = rawpheno_function_terms('project'); - - // @note project.name must be unique (constraint). - $sql = "SELECT project_id FROM {project} WHERE name = :default_project"; - $args = array(':default_project' => $default_project); - $prj_id = chado_query($sql, $args); - - if ($prj_id->rowCount()) { - $project_ID = $prj_id->fetchField(); - } - else { - // Create AGILE Project when not present in this site. - $tmp = chado_insert_record('project', - array('name' => $default_project) - ); - - $project_ID = $tmp->project_id; - } - - // Trait types array. - $trait_type = rawpheno_function_trait_types(); - // Subset trait types array. - $arr_subsetheader = rawpheno_function_headers('subset'); - // Plant Property trait types array. - $arr_plantpropheader = rawpheno_function_headers('plantprop'); - // Essential trait types array. - $arr_essentialheader = rawpheno_function_headers('essential'); - - // Measurements type and Plant Property type. - $voc = rawpheno_function_terms('vocabularies'); - // CV R version. - $cv_rver = tripal_insert_cv($voc['cv_rver'], str_replace('_', ' ', ucfirst($voc['cv_rver']))); - // CV Method of collection. - $cv_desc = tripal_insert_cv($voc['cv_desc'], str_replace('_', ' ', ucfirst($voc['cv_desc']))); - // CV unit. - if (function_exists('chado_get_cv')) { - $cv_unit = chado_get_cv(array('name' => $voc['cv_unit'])); - } - else { - $cv_unit = tripal_get_cv(array('name' => $voc['cv_unit'])); - } - - // Query traits. - $sql = "SELECT t2.cvterm_id, t2.name, t1.cv_id, t2.name, t1.name AS cv_name - FROM {cv} AS t1 INNER JOIN {cvterm} AS t2 USING(cv_id) - WHERE t1.name IN (:cvs)"; - - $args = array(':cvs' => array($voc['cv_prop'], $voc['cv_type'])); - $result = chado_query($sql, $args); - - // Update each cvterm. - foreach($result as $cvterm) { - // Get definition and method of collection information. - $cvterm_info = rawpheno_function_get_definition($cvterm->name); - // In the first release, term name is also the term definition. - $cvterm_definition = (!empty($cvterm_info['define'])) ? $cvterm_info['define'] : $cvterm->name; - - // DEFINIION: - // Update the term with this information. - chado_query('UPDATE {cvterm} SET definition = :def WHERE cvterm_id = :id', - array(':def' => $cvterm_definition, ':id' => $cvterm->cvterm_id)); - - // METHOD OF COLLECTION: - // Add the method of collection description to cvterm prop. - if (!empty($cvterm_info['method'])) { - chado_insert_record('cvtermprop', - array( - 'cvterm_id' => $cvterm->cvterm_id, - 'type_id' => $cv_desc->cv_id, - 'value' => $cvterm_info['method'], - 'rank' => 0 - ) - ); - } - - // R COMPATIBLE VERSION: - // Transform trait to R compatible version. - $r_version = rawpheno_function_make_r_compatible($cvterm->name); - chado_insert_record('cvtermprop', - array( - 'cvterm_id' => $cvterm->cvterm_id, - 'type_id' => $cv_rver->cv_id, - 'value' => $r_version, - 'rank' => 0 - ) - ); - - // CVTERM AND UNIT RELATIONSHIP: - if ($cvterm->cv_name == $voc['cv_type']) { - // Extract the unit part from the column header. Usually, the information enclosed in a parenthesis. - $header_unit = rawpheno_function_header_unit($cvterm->name); - // eg. Comment - $header_unit = (empty($header_unit)) ? 'text' : $header_unit; - - if (function_exists('chado_get_cvterm')) { - $unit_cvterm_id = chado_get_cvterm(array('name' => $header_unit, 'cv_id' => $cv_unit->cv_id)); - } - else { - $unit_cvterm_id = tripal_get_cvterm(array('name' => $header_unit, 'cv_id' => $cv_unit->cv_id)); - } - - chado_insert_record('cvterm_relationship', - array( - 'subject_id' => $unit_cvterm_id->cvterm_id, - 'type_id' => $cv_unit->cv_id, - 'object_id' => $cvterm->cvterm_id - ) - ); - } - - // PROJECT TO CVTERM: - // Tell the type of the trait. - if (in_array($cvterm->name, $arr_plantpropheader)) { - // Type is Plant Property. - $type = $trait_type['type4']; - } - elseif (in_array($cvterm->name, $arr_essentialheader)) { - // Trait is essential (must be in the spreadsheet). - $type = $trait_type['type1']; - } - elseif (in_array($cvterm->name, $arr_subsetheader)) { - // Subset Trait - $type = $trait_type['type3']; - } - else { - // Trait is optional - $type = $trait_type['type2']; - } - - db_insert('pheno_project_cvterm') - ->fields(array( - 'project_id' => $project_ID, - 'cvterm_id' => $cvterm->cvterm_id, - 'type' => $type - ) - ) - ->execute(); - } - - // WHEN WITH DATA: STOCK TO PROJECT: - // When pheno_measurements has data. - $sql = "SELECT plant_id FROM {pheno_plant} ORDER BY plant_id ASC"; - $plant = db_query($sql); - - if ($plant->rowCount() > 0) { - // STOCK: - foreach($plant as $p) { - db_insert('pheno_plant_project') - ->fields(array('project_id' => $project_ID, - 'plant_id' => $p->plant_id)) - ->execute(); - } - - // USER: - // Default to user updating this module as the user who uploaded the data. - db_insert('pheno_project_user') - ->fields(array('project_id' => $project_ID, - 'uid' => $GLOBALS['user']->uid)) - ->execute(); - - // If the user is not the superadmin, add superadmin as well. - if ($GLOBALS['user']->uid != 1) { - db_insert('pheno_project_user') - ->fields(array('project_id' => $project_ID, - 'uid' => 1)) - ->execute(); - } - } - - // MATERIALIZED VIEW: - // Replace materialized view. - // Using tripal: tripal_delete_mview(). - // Get mview id of materilaized view created by hook_install(). - // @note tripal_mviews.mv_table must be unique (constraint). - $sql = "SELECT mview_id FROM {tripal_mviews} WHERE mv_table = :mv_table"; - $mview_id = db_query($sql, array(':mv_table' => 'rawpheno_rawdata_mview')) - ->fetchField(); - - if (isset($mview_id) AND $mview_id > 0) { - // Delete materialized view. - tripal_delete_mview($mview_id); - } - - // Create materialized view used in phenotypes/rawdata. - // Using Tripal: tripal_add_mview(). - $mv_name = 'rawpheno_rawdata_summary'; - $mv_module = 'rawpheno'; - $mv_comment = 'Materialized view used by rawpheno module to generate summary of traits per location, rep and year.'; - - // Create a summary of data in the following format: - // Plant id, Location, Rep, Planting Date and Total traits count. - // NOTE: trait count includes planting date. - // NOTE: As a materialized view, this get's executed by chado query. - $mv_sql = "SELECT CAST(t1.plant_id AS numeric) AS plant_id, - t2.value AS location, - t3.value AS rep, - t5.value AS planting_year, - COUNT(DISTINCT t4.type_id) AS total_count, - ARRAY_TO_STRING(ARRAY_AGG(DISTINCT t4.type_id), ',') AS all_traits - FROM pheno_plant AS t1 - INNER JOIN [pheno_plantprop] AS t2 USING(plant_id) - INNER JOIN [pheno_plantprop] AS t3 USING(plant_id) - INNER JOIN [pheno_measurements] AS t4 USING(plant_id) - INNER JOIN [pheno_measurements] AS t5 USING(plant_id) - WHERE - t2.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 - t3.type_id = (SELECT cvterm_id FROM {cvterm} cvt LEFT JOIN {cv} ON cv.cv_id=cvt.cv_id WHERE cvt.name = 'Rep' AND cv.name = 'phenotype_plant_property_types') AND - t5.type_id = (SELECT cvterm_id FROM {cvterm} cvt LEFT JOIN {cv} ON cv.cv_id=cvt.cv_id WHERE cvt.name = 'Planting Date (date)' AND cv.name = 'phenotype_measurement_types') - GROUP BY t1.plant_id, t4.plant_id, t2.value, t3.value, t5.value"; - - // Schema array. - $mv_schema = array ( - 'table' => 'rawpheno_rawdata_mview', - 'fields' => array ( - 'plant_id' => array ( - 'type' => 'int'), - 'location' => array ( - 'type' => 'varchar', - 'length' => 255), - 'rep' => array ( - 'type' => 'varchar', - 'length' => 255), - 'planting_date' => array ( - 'type' => 'varchar', - 'length' => 255), - 'total_count' => array ( - 'type' => 'int'), - 'all_traits' => array( - 'type' => 'text'), - ), - 'indexes' => array( - 'plant_id' => array('plant_id'), - 'location' => array('location'), - ) - ); - - // Create materialized view. - tripal_add_mview($mv_name, $mv_module, $mv_schema, $mv_sql, $mv_comment); - - // Populate view. - $mview_id = tripal_get_mview_id('rawpheno_rawdata_mview'); - tripal_populate_mview($mview_id); -} - -/** - * Implements hook_update_N(). - * Add custom table for environment data. - * - * N - * 7 - Drupal core compatibility. - * 0 - Module's major release version. - * 03 - Sequence count. - */ -function rawpheno_update_7003() { - // PHENO-ENVRONMENT-DATA - $schema['pheno_environment_data'] = array( - 'description' => t('This table will hold information about environment data.'), - 'fields' => array( - 'environment_data_id' => array( - 'description' => t('Primary Key: A unique ID number'), - 'type' => 'serial', - 'not null' => TRUE, - ), - 'project_id' => array( - 'description' => t('The project ID number from table chado.project'), - 'type' => 'int', - 'not null' => TRUE, - ), - 'fid' => array( - 'description' => t('File ID number from file_managed table'), - 'type' => 'int', - 'not null' => TRUE, - ), - 'sequence_no' => array( - 'description' => t('The sequence number of the file.'), - 'type' => 'int', - ), - 'location' => array( - 'description' => t('Location information of the file.'), - 'type' => 'text', - ), - 'year' => array( - 'description' => t('Year information of the file.'), - 'type' => 'text', - ), - ), - 'primary key' => array('environment_data_id'), - ); - - db_create_table('pheno_environment_data', $schema['pheno_environment_data']); -} - -/** - * Implements hook_requirements(). - */ -function rawpheno_requirements($phase) { - $requirements = array(); - - // On install of this module, check the following libraries. - if (($phase == 'install' OR $phase == 'update') AND function_exists('libraries_get_path')) { - $t = get_t(); - - $libraries = 'i.e., sites/all/libraries/'; - - $arr_req_libraries = array( - // Libraries. - 'excel_writer' => - array( - 'is_in' => libraries_get_path('PHP_XLSXWriter_plus'), - 'source' => 'https://github.com/SystemDevil/PHP_XLSXWriter_plus/archive/master.zip', - 'path' => $libraries . 'PHP_XLSXWriter_plus' - ), - - 'excel_reader' => - array( - 'is_in' => libraries_get_path('spreadsheet-reader'), - 'source' => 'https://github.com/nuovo/spreadsheet-reader/archive/master.zip', - 'path' => $libraries . 'spreadsheet-reader' - ), - - 'd3' => - array( - 'is_in' => libraries_get_path('d3'), - 'source' => 'https://github.com/d3/d3/releases/download/v3.5.14/d3.zip', - 'path' => $libraries . 'd3' - ) - ); - - foreach($arr_req_libraries as $i => $info) { - if (!$info['is_in']) { - $path = $info['path']; - $source = $info['source']; - - $requirements[$i] = array( - 'severity' => REQUIREMENT_ERROR, - 'description' => - $t('This module uses !library, which is missing. - Please download and extract the entire contents of the archive into Libraries Directory %path directory on your server.', - array('!library' => l(strtoupper(str_replace('_',' ',$i)), - $source, - array('attributes' => array('target' => '_blank'))), '%path' => $path) - ), - ); - } - - // Check version of D3 and esure patch has been implemented. - if ($i == 'd3' && $info['is_in']) { - $ver = rawpheno_function_d3_version(); - $v = explode('.', $ver); - - if (trim($v[0]) != '3' OR (int)$v[1] < 4) { - $requirements['d3_version'] = array( - 'severity' => REQUIREMENT_ERROR, - 'description' => $t('The D3 (ver %ver) library installed is not supported by this module. Please review the module requirements (!rawpheno_module) or download D3 version 3.5.14 (!download).', - array('!rawpheno_module' => l('Rawphenotypes Module', 'https://github.com/UofS-Pulse-Binfo/rawphenotypes'), '%ver' => $ver, '!download' => l('D3 3.5.14', 'https://github.com/d3/d3/releases/download/v3.5.14/d3.zip')) - ), - ); - } - } - elseif ($i == 'excel_reader' && $info['is_in']) { - $is_patched = rawpheno_function_library_patch(); - - if (!$is_patched) { - $requirements['patch'] = array( - 'severity' => REQUIREMENT_ERROR, - 'description' => $t('The Spreadsheet Reader library patch was not implemented. Please review the module requirements (!rawpheno_module).', - array('!rawpheno_module' => l('Rawphenotypes Module', 'https://github.com/UofS-Pulse-Binfo/rawphenotypes')) - ), - ); - } - } - // - } - } - - return $requirements; -} diff --git a/rawpheno.module b/rawpheno.module deleted file mode 100755 index 1262764..0000000 --- a/rawpheno.module +++ /dev/null @@ -1,1179 +0,0 @@ - 'Raw Data', - 'page callback' => 'drupal_get_form', - 'page arguments' => array('rawpheno_rawdata'), - 'access arguments' => array('access rawpheno'), - 'file' => 'include/rawpheno.rawdata.form.inc', - 'type' => MENU_NORMAL_ITEM, - ); - // Menu callback which generates the summary data in JSON, - // required by the heat map in rawdata page. - $items['rawdata'] = array( - 'page callback' => 'rawpheno_rawdata_summary_json', - 'access callback' => true, - 'delivery callback' => 'drupal_json_output', - 'type' => MENU_CALLBACK, - ); - - // Menu callback which returns summary data for a trait (for histogram). - $items['rawdata_trait'] = array( - 'page callback' => 'rawpheno_rawdata_trait_json', - 'access callback' => true, - 'delivery callback' => 'drupal_json_output', - 'type' => MENU_CALLBACK, - ); - - // Menu callback that returns year and location given a trait use to populate select boxes - // to categorize barchart in rawdata page. - $items['rawdata_trait_category'] = array( - 'page callback' => 'rawpheno_rawdata_trait_category_json', - 'access callback' => true, - 'delivery callback' => 'drupal_json_output', - 'type' => MENU_CALLBACK, - ); - - // INSTRUCTIONS PAGE - // A page containing standard phenotyping procedure and - // providing data collection spreadsheet. - $items['phenotypes/raw/instructions'] = array( - 'title' => 'Instructions', - 'page callback' => 'drupal_get_form', - 'page arguments' => array('rawpheno_instructions'), - 'access arguments' => array('access rawpheno'), - 'file' => 'include/rawpheno.instructions.form.inc', - 'type' => MENU_NORMAL_ITEM, - 'weight' => 0, - ); - // Menu callback which generates a project specific data collection spreadsheet. - $items['phenotypes/raw/instructions/%'] = array( - 'title' => 'Instructions', - 'page callback' => 'drupal_get_form', - 'page arguments' => array('rawpheno_instructions', 3), - 'access arguments' => array('access rawpheno'), - 'file' => 'include/rawpheno.instructions.form.inc', - 'type' => MENU_CALLBACK, - ); - // Menu callback which generates a list of headers in JSON, - // used to autocomplete a search field and process search action - // in instructions page. - $items['phenotypes/raw/instructions/autocomplete/%'] = array( - 'page callback' => 'rawpheno_instructions_autocomplete_search', - 'page arguments' => array(4), - 'access arguments' => array('access rawpheno'), - 'type' => MENU_CALLBACK, - ); - - $items['phenotypes/raw/instructions/spreadsheet/%'] = array( - 'page callback' => 'rawpheno_instructions_create_spreadsheet', - 'page arguments' => array(4), - 'access arguments' => array('access rawpheno'), - 'file' => 'include/rawpheno.instructions.form.inc', - 'type' => MENU_CALLBACK, - ); - - // DOWNLOADS PAGE - // A page providing export data options. - // NOTE: Page below uses as different access arguments than - // the rest of the pages. Not everyone can download. - $items['phenotypes/raw/download'] = array( - 'title' => 'Download Data', - 'page callback' => 'drupal_get_form', - 'page arguments' => array('rawpheno_download'), - 'access arguments' => array('download rawpheno'), - 'file' => 'include/rawpheno.download.form.inc', - 'type' => MENU_NORMAL_ITEM, - 'weight' => 6, - ); - - $items['phenotypes/raw/csv'] = array( - 'title' => 'Download Raw Phenotypic Data: CSV/TAR', - 'page callback' => 'trpdownload_download_page', - 'page arguments' => array('rawpheno_csv'), - 'access arguments' => array('download rawpheno'), - 'type' => MENU_CALLBACK, - ); - - // DRAG AND DROP UPLOAD PAGE - // A page for uploading and validating data collection spreadsheet. - $items['phenotypes/raw/upload'] = array( - 'title' => 'Upload Data', - 'page callback' => 'drupal_get_form', - 'page arguments' => array('rawpheno_upload_form_master'), - 'access arguments' => array('access rawpheno'), - 'file' => 'include/rawpheno.upload.form.inc', - 'type' => MENU_NORMAL_ITEM, - 'weight' => 3, - ); - // This callback will generate a job progress and status information - // in JSON format to be used by the progress bar in the last stage of the upload process. - $items['phenotypes/raw/upload/job_summary/%'] = array( - 'page callback' => 'rawpheno_upload_job_progress_json', - 'page arguments' => array(4), - 'access arguments' => array('access content'), - 'type' => MENU_CALLBACK, - ); - - // BACKUP - // A page for uploading spreadsheet. - $items['phenotypes/raw/backup'] = array( - 'title' => 'Backup', - 'page callback' => 'drupal_get_form', - 'page arguments' => array('rawpheno_backup'), - 'access arguments' => array('access rawpheno'), - 'file' => 'include/rawpheno.backup.form.inc', - 'type' => MENU_NORMAL_ITEM, - 'weight' => 7, - ); - // List traits available in a project. - $items['phenotypes/raw/backup/%/%/%'] = array( - 'title' => 'Backup', - 'page callback' => 'drupal_get_form', - 'page arguments' => array('rawpheno_backup', 3, 4, 5), - 'access arguments' => array('access rawpheno'), - 'file' => 'include/rawpheno.backup.form.inc', - 'type' => MENU_CALLBACK, - ); - - // ADMININSTRATIVE PAGE OF THIS MODULE - // A page for changing page colour and page title. - $items['admin/tripal/extension/rawphenotypes'] = array( - 'title' => 'Rawphenotypes', - 'description' => 'Provides an interface for managing projects, users and column headers used in Rawphenotypes', - 'page callback' => 'drupal_get_form', - 'page arguments' => array('rawpheno_admin_main_page'), - 'access arguments' => array('access administration pages'), - 'file' => 'include/rawpheno.admin.form.inc', - 'type' => MENU_NORMAL_ITEM, - ); - - $items['admin/tripal/extension/rawphenotypes/rawpheno_config'] = array( - 'title' => 'Page Configurations', - 'description' => 'Apply a colour scheme and set page title.', - 'page callback' => 'drupal_get_form', - 'page arguments' => array('rawpheno_admin_page'), - 'access arguments' => array('access administration pages'), - 'file' => 'include/rawpheno.admin.form.inc', - 'type' => MENU_LOCAL_TASK, - 'weight' => 1, - ); - - // A page for managing R Transformation rules. - $items['admin/tripal/extension/rawphenotypes/rawpheno_rheaders'] = array( - 'title' => 'Define R Transformation Rules', - 'description' => 'Define R Transfomation Rules to be applied when generating R Friendly alternative of column header.', - 'page callback' => 'drupal_get_form', - 'page arguments' => array('rawpheno_admin_rheaders'), - 'access arguments' => array('access administration pages'), - 'file' => 'include/rawpheno.admin.form.inc', - 'type' => MENU_LOCAL_TASK, - 'weight' => 2, - ); - - // A page for managing project and traits. - // List all projects and count traits available. - $items['admin/tripal/extension/rawphenotypes/all_projects'] = array( - 'title' => 'Manage Projects', - 'description' => 'Create project, define column headers and appoint users to project.', - 'page callback' => 'drupal_get_form', - 'page arguments' => array('rawpheno_admin_all_projects'), - 'access arguments' => array('access administration pages'), - 'file' => 'include/rawpheno.admin.form.inc', - 'type' => MENU_LOCAL_TASK, - 'weight' => 3, - ); - // Edit trait in a project. - $items['admin/tripal/extension/rawphenotypes/all_projects/%/%/%'] = array( - 'page callback' => 'drupal_get_form', - 'page arguments' => array('rawpheno_admin_project_management', 5, 6, 7), - 'access arguments' => array('access administration pages'), - 'file' => 'include/rawpheno.admin.form.inc', - 'type' => MENU_CALLBACK, - ); - - // Menu callback used to search user when appointing the user to a project. - $items['admin/tripal/extension/rawphenotypes/username/%'] = array( - 'page callback' => 'rawpheno_manageproject_autocomplete_search', - 'page arguments' => array(5), - 'access arguments' => array('access administration pages'), - 'type' => MENU_CALLBACK, - ); - - return $items; -} - - -/** - * Implements hook_permission(). - */ -function rawpheno_permission() { - return array( - 'access rawpheno' => array( - 'title' => t('Access Raw Phenotypic pages (Instructions, Upload, Backup and Summary page'), - ), - - 'download rawpheno' => array( - 'title' => t('Download Phenotypic Data (CSV format) in Rawpheno Download Data page'), - ) - ); -} - - -/** - * Implements a hook_preprocess_HOOK(). - * This function prepares variables for Raw Phenotypes Rawdata Page. - */ -function rawpheno_preprocess_rawpheno_rawdata(&$variables, $hook) { - // Colour scheme, project check and data check. - $rawpheno_load = rawpheno_module_defaults(); - list($variables['theme_colour'], $variables['my_projects'], $variables['has_prj'], $variables['has_data']) = $rawpheno_load; - - // Cross-link rawdata page to download page. - $variables['rel_url'] = url('phenotypes/raw/download'); - // Page Title - $variables['page_title'] = variable_get('rawpheno_rawdata_title'); - // D3 version - $ver = rawpheno_function_d3_version(); - $ver = explode('.', $ver); - // Version - $variables['d3ver'] = (isset($ver[0])) ? trim($ver[0]) : 0; - // Release - $variables['d3rel'] = (isset($ver[1])) ? trim($ver[1]) : 0; - - // Does D3 exists - $variables['d3_in'] = libraries_load('d3js'); -} - -/** - * Implements a hook_preprocess_HOOK(). - * This function prepares variables for Raw Phenotypes Instuctions Page. - */ -function rawpheno_preprocess_rawpheno_instructions(&$variables, $hook) { - // Colour scheme, project check and data check. - $rawpheno_load = rawpheno_module_defaults(); - list($variables['theme_colour'], $variables['my_projects'], $variables['has_prj'], $variables['has_data']) = $rawpheno_load; - - // Cross-link instructions page to upload page. - $variables['rel_url'] = url('phenotypes/raw/upload'); - // Page Title - $variables['page_title'] = variable_get('rawpheno_instructions_title'); - - // Path to module used to embed image in appendix tab. - $variables['path'] = drupal_get_path('module', 'rawpheno'); - - // Spreadsheet writer is in? - $is_in = libraries_get_path('PHP_XLSXWriter_plus'); - $variables['writer_in'] = $is_in; -} - -/** - * Implements a hook_preprocess_HOOK(). - * This function prepares variables for Raw Phenotypes Upload Page. - */ -function rawpheno_preprocess_rawpheno_upload_form_master(&$variables, $hook) { - // Colour scheme, project check and data check. - $rawpheno_load = rawpheno_module_defaults(); - list($variables['theme_colour'], $variables['my_projects'], $variables['has_prj'], $variables['has_data']) = $rawpheno_load; - - // Directory permission used in upload and backup. - $pub_dir = 'public://'; - - $perm = file_prepare_directory($pub_dir); - $variables['dir_permission'] = $perm; - - // Does drag and drop module exist? - $is_in = module_exists('dragndrop_upload_element'); - $variables['dnd_in'] = $is_in; - - // Does spreadsheet reader exist? - $is_in = libraries_get_path('spreadsheet-reader'); - $variables['reader_in'] = $is_in; - - // Cross-link upload page to instructions page. - $variables['rel_url'] = url('phenotypes/raw/instructions'); - // Page Title - $variables['page_title'] = variable_get('rawpheno_upload_title'); - // Support Email. - $support_email = rawpheno_function_get_support_email(); - $variables['support_email'] = $support_email; - - // Needs url in help window and links in stage 3. - $variables['page_url'] = - array( - 'rawpheno_rawdata' => url('phenotypes/raw'), - 'rawpheno_download' => url('phenotypes/raw/download'), - 'rawpheno_instructions' => url('phenotypes/raw/instructions'), - 'rawpheno_upload' => url('phenotypes/raw/upload'), - 'rawpheno_backup' => url('phenotypes/raw/backup'), - 'rawpheno_demo' => url('phenotypes/raw/videos'), - ); - - // Stages in upload page. - $variables['upload_stages'] = array('check' => 'Validate Spreadsheet', - 'review' => 'Describe New Trait', - 'save' => 'Save Spreadsheet'); -} - -/** - * Implements a hook_preprocess_HOOK(). - * This function prepares variables for Raw Phenotypes Backup Page. - */ -function rawpheno_preprocess_rawpheno_backup(&$variables, $hook) { - // Colour scheme, project check and data check. - $rawpheno_load = rawpheno_module_defaults(); - list($variables['theme_colour'], $variables['my_projects'], $variables['has_prj'], $variables['has_data']) = $rawpheno_load; - - // Directory permission used in upload and backup. - $pub_dir = 'public://'; - - $perm = file_prepare_directory($pub_dir); - $variables['dir_permission'] = $perm; - - // Does drag and drop module exist? - $is_in = module_exists('dragndrop_upload_element'); - $variables['dnd_in'] = $is_in; - - // Does spreadsheet reader exist? - $is_in = libraries_get_path('spreadsheet-reader'); - $variables['reader_in'] = $is_in; - - // Cross-link backup page to instructions page. - $variables['rel_url'] = url('phenotypes/raw/instructions'); - // Page Title - $variables['page_title'] = variable_get('rawpheno_backup_title'); -} - -/** - * Implements a hook_preprocess_HOOK(). - * This function prepares variables for Raw Phenotypes Download Page. - */ -function rawpheno_preprocess_rawpheno_download(&$variables, $hook) { - // Colour scheme, project check and data check. - $rawpheno_load = rawpheno_module_defaults(); - list($variables['theme_colour'], $variables['my_projects'], $variables['has_prj'], $variables['has_data']) = $rawpheno_load; - - // Cross-link download page to download page. - $variables['rel_url'] = url('phenotypes/raw'); - // Page Title - $variables['page_title'] = variable_get('rawpheno_download_title'); -} - -/** - * Helper function: preforms module page load check and gets module colour scheme - * used in initiating variables in hook_preprocess_HOOK() implementation above. - * - * @return array - * An array containing the default colour scheme, projects appointed to a user, - * check if module has an active project as well as if module has data. - */ -function rawpheno_module_defaults() { - // Colour scheme selected by the user from the admin/configuration. - $colour = variable_get('rawpheno_colour_scheme'); - // This colour will fill background of header and top right button, and border of content area. - $colour = (empty($colour)) ? '#304356' : $colour; - - // Projects assigned to user. - $log_user = $GLOBALS['user']->uid; - $my_projects = rawpheno_function_user_project($log_user); - - // Does the module contain project? - $has_project = rawpheno_function_project(); - - // If it has project, does it have data? - $has_data = rawpheno_function_data(); - - return array( - $colour, // Colour scheme. - $my_projects, // User appointed project. - $has_project, // Module has at least a project. - $has_data, // Module has data. - ); -} - - -/** - * Implements hook_theme(). - * - * Returns HTML for rawpheno pages. - */ -function rawpheno_theme($existing, $type, $theme, $path) { - // Rawdata page. - $items['rawpheno_rawdata'] = array( - 'render element' => 'form', - 'template' => 'rawpheno_pages', - 'path' => $path . '/theme', - ); - - // Instructions page. - $items['rawpheno_instructions'] = array( - 'render element' => 'form', - 'template' => 'rawpheno_pages', - 'path' => $path . '/theme', - ); - - // Download page. - $items['rawpheno_download'] = array( - 'render element' => 'form', - 'template' => 'rawpheno_pages', - 'path' => $path . '/theme', - ); - - // Backup page. - $items['rawpheno_backup'] = array( - 'render element' => 'form', - 'template' => 'rawpheno_pages', - 'path' => $path . '/theme', - ); - - // Upload page. - $items['rawpheno_upload_form_master'] = array( - 'render element' => 'form', - 'template' => 'rawpheno_pages', - 'path' => $path . '/theme', - ); - // Upload Errors. - $items['rawpheno_upload_validation_report'] = array( - 'template' => 'rawpheno_upload_validation_report', - 'path' => $path . '/theme', - ); - - // Notification Block. - $items['rawpheno_notification_block'] = array( - 'template' => 'rawpheno_notification_block', - 'path' => $path . '/theme', - 'variables' => array( - 'alert' => null, - 'username' => null, - 'hostname' => null, - 'file_count' => null, - 'path_module' => null, - 'path_phenotypes_raw' => null, - 'path_phenotypes_video' => null, - ), - ); - - return $items; -} - - -/** - * Implements hook_libraries_info(). - * - * Define external libraries: Spreadsheet Reader and D3JS - */ -function rawpheno_libraries_info() { - // Spreadsheet reader - // File option is empty since library files are included - // individually using include_once(). - // files in: sites/all/libraries/spreadsheet-reader - // - SpreadsheetReader.php - // - SpreadsheetReaderXLSX.php - // - SpreadsheetReaderXLS.php - // - php-excel-reader/excel_reader2.php - - // NOTE: To prevent library form auto formatting data to MM/DD/YYYY, - // suggest a new data format YYYY-mm-dd in the source code in line: - // * 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'); - - // Let Drupal decide where library is. - $lib_reader = libraries_get_path('spreadsheet-reader'); - - $libraries['spreadsheet_reader'] = array( - 'name' => 'NUOVO Spreadsheet', - 'vendor url' => 'https://github.com/nuovo/spreadsheet-reader', - 'version callback' => 'rawpheno_function_reader_version_callback', - 'download url' => 'https://github.com/nuovo/spreadsheet-reader/archive/master.zip', - 'library path' => $lib_reader, - 'path' => $lib_reader . '/', - 'files' => array(), - ); - - // Spreadsheet writer. - // Let Drupal decide where library is. - $lib_writer = libraries_get_path('PHP_XLSXWriter_plus'); - - $libraries['spreadsheet_writer'] = array( - 'name' => 'PHP_XLSXWriter_plus Spreadsheet Writer', - 'vendor url' => 'https://github.com/SystemDevil/PHP_XLSXWriter_plus', - 'version' => 1, - 'download url' => 'https://github.com/SystemDevil/PHP_XLSXWriter_plus/archive/master.zip', - 'library path' => $lib_writer, - 'files' => array('xlsxwriter.class.php'), - ); - - // D3 JavaScript. - // Let Drupal decide where library is - $lib_d3 = libraries_get_path('d3'); - - $libraries['d3js'] = array( - 'name' => 'D3 Data-Driven Documents', - 'vendor url' => 'https://d3js.org/', - 'version callback' => 'rawpheno_function_d3_version_callback', - 'download url' => 'https://github.com/d3/d3/releases/download/v3.5.14/d3.zip', - 'library path' => $lib_d3, - 'files' => array( - 'js' => array('d3.js') - ), - ); - - return $libraries; -} - - -/** - * Function callback: return the version of the spreadsheet reader library installed. - * - * @see implementation of hook_libraries_info(). - */ -function rawpheno_function_reader_version_callback() { - $ver = rawpheno_function_sreader_version(); - - return $ver; -} - -/** - * Function callback: return the version of the d3 library installed. - * - * @see implementation of hook_libraries_info(). - */ -function rawpheno_function_d3_version_callback() { - $ver = rawpheno_function_d3_version(); - - return $ver; -} - - -/** - * Function callback: used by autcomplete search functionality - * used in manage project page - appoint a user to the project. - */ -function rawpheno_manageproject_autocomplete_search($project_id, $key = '') { - // Array to hold all user. - $arr_user = array(); - - $sql = "SELECT name, mail FROM {users} - WHERE - status = 1 - AND LOWER(name) LIKE :name - ORDER BY name DESC LIMIT 10"; - - $args = array(':name' => '%' . strtolower($key) . '%'); - $user = db_query($sql, $args); - - foreach($user as $u) { - $arr_user[$u->name] = $u->name . ' - ' . $u->mail; - } - - drupal_json_output($arr_user); - } - -/** - * Function callback: Generate Tripal Job progress JSON. - * - * @param $job_id - * An integer containing the job id number of the job process to monitor. - * - * @retun - * A JSON and containing progress and status of job object. - */ -function rawpheno_upload_job_progress_json($job_id) { - // This is important as field requires the job id to be numeric value. - $job_id = (int)$job_id; - // Get the Tripal Job Object. - $job = tripal_get_job($job_id); - - // Test if the Tripal Job exists and is that the status is not completed. - if ($job) { - if(trim(strtolower($job->status)) != 'completed') { - // Read the file containing the upload file progress of the Tripal Job. - // Please see rawpheno.upload.excel.inc - line: 69. - $tmp = file_directory_temp(); - $filename = $tmp . '/' . 'job-progress' . $job_id . '.txt'; - $percent = file_get_contents($filename); - - // In the event of file collision or file writing process and file read returns nothing - // show ... (indicate calculating) instead. This will correct itself the next file read. - $percent_completed = empty($percent) ? '...' : $percent; - $message = ($percent == 100 OR trim(strtolower($job->status)) == 'completed') - ? 'Completed' - : $job->status; - } - else { - $percent_completed = 100; - $message = $job->status; - - if ($job->progress != 100) { - // Update Drupal Jobs Table when 100 percent only. This is to ensure that - // the progress wont get non numeric value when file read returns nothing - // usually happens when read and write not in sync. - db_update('tripal_jobs') - ->fields(array('progress' => (int)$progress['percentage'])) - ->condition('job_id', $job_id, '=') - ->execute(); - } - } - - // This is the exact format expected by the progress bar so do not alter it. - $progress = array( - 'percentage' => $percent_completed, - 'message' => $message, - ); - - // Print the array as JSON to the screen. - print drupal_json_output($progress); - - // Do not show the Drupal headers and formatting. - // This is critical as if anything else is printed to the screen you will see - // an AJAX error instead of your progress bar ;-). - exit(); - } -} - - - -/** - * Function callback: Autocomplete search field. - * - * @see hook_menu() - * - * @param $key - * A string containing the search keywords entered in the search field. - * @param $project_id - * An integer containing the project id number. - * - * @return - * A JSON where each trait is both the key and the value. The key is what is - * sent to JavaScript processing the search, while the value is what is suggested - * to user while typing in the seach field. - */ -function rawpheno_instructions_autocomplete_search($project_id, $key = '') { - $trait_type = rawpheno_function_trait_types(); - - $sql = "SELECT name - FROM {cvterm} RIGHT JOIN pheno_project_cvterm USING(cvterm_id) - WHERE project_id = :project_id AND type <> :plantprop"; - - $args = array(':project_id' => $project_id, ':plantprop' => $trait_type['type4']); - $m = chado_query($sql, $args); - - // Array to hold list of traits. - $arr_headers = array(); - - // Determine if keyword is provided. - if (empty($key)) { - foreach($m as $n) { - $arr_headers[] = $n->name; - } - } - else { - foreach($m as $n) { - if (stristr($n->name, $key)) { - $arr_headers[$n->name] = $n->name; - } - } - } - - drupal_json_output($arr_headers); -} - - -/** - * Function callback: Generate data for the heat map. - * - * @see hook_menu() - * - * @return - * A JSON and each member has the following objects: - * location, year, rep, # traits, list of traits with value. - */ -function rawpheno_rawdata_summary_json() { - // Ensure project id is valid. - // Must exists, must not be empty and must not be more that 10 chars/digits. - if (!isset($_GET['project_id']) OR empty($_GET['project_id']) OR strlen($_GET['project_id']) >= 10) { - // No data. - return 0; - } - else { - // Get data for this project. - $project_id = (int)trim(strip_tags($_GET['project_id'])); - - // Get trait id of planting data. - $planting_date = rawpheno_get_trait_id('Planting Date (date)'); - // get trait id of # of seeds planted - $seeds_planted = rawpheno_get_trait_id('# of Seeds Planted (count)'); - - // Minimum rep per year per location. - $min_set = 3; - // Select all location present in the materialized view. - $sql_location = "SELECT DISTINCT location - FROM chado.rawpheno_rawdata_mview INNER JOIN {pheno_plant_project} USING(plant_id) - WHERE project_id = :project - ORDER BY location ASC"; - - $result_location = db_query($sql_location, array(':project' => (int)$project_id)); - $count_location = $result_location->rowCount(); - - if ($count_location > 0) { - // Array to hold data. - $data_json = array(); - // Array to hold planting years and rep. - $arr_range['year'] = array(); - $arr_range['rep'] = array(); - - // Define range of planting years and rep. - for($i = 0; $i < 2; $i++) { - $range = ($i == 0) ? 'year' : 'rep'; - // Query materialized view. - $sql_range = sprintf(" - SELECT DISTINCT %s AS data_range FROM chado.rawpheno_rawdata_mview - WHERE plant_id IN (SELECT plant_id FROM {pheno_plant_project} WHERE project_id = :project_id) - ORDER BY data_range ASC", ($range == 'year') ? 'SUBSTRING(planting_date, 1, 4)' : 'rep'); - - $r = db_query($sql_range, array(':project_id' => $project_id)); - $count_data = $r->rowCount(); - - // Determine if total number of years is less than minimum number. - if ($count_data < $min_set) { - // Year/rep count is less than minimum, fill the gap to make the - // number always to minimum. - - // Initial/starting year/rep. - $start_value = $r->fetchField(); - $arr_range[$range][] = $start_value; - - // Fill the missing years/rep and push to array. - for($c = 1; $c < $min_set; $c++) { - // Next year in the SQL result. - $d = $r->fetchField(); - if (empty($d)) { - // If it is empty, compute the next year. - $start_value += 1; - $arr_range[$range][] = $start_value; - } - else { - // Year is present, store and set the start value to this year. - $arr_range[$range][] = $d; - $start_value = $d; - } - } - } - else { - // There is more than the minimum year/rep returned by the query. - // Store all values to years/rep array. - foreach($r as $range_value) { - $arr_range[$range][] = $range_value->data_range; - } - } - } - - // Construct data set. - // Read each location available and construct data set. - $sql = "SELECT ARRAY_TO_STRING(ARRAY_AGG(DISTINCT all_traits), ',') AS all_traits - FROM chado.rawpheno_rawdata_mview - WHERE - location = :location - AND SUBSTRING(planting_date, 1, 4) = :year - AND rep = :rep - AND plant_id IN - (SELECT plant_id FROM {pheno_plant_project} WHERE project_id = :project_id) - LIMIT 1"; - - while($data = $result_location->fetchAssoc()) { - // In each location, read each planting year. - foreach($arr_range['year'] as $year) { - // In each year, read each rep. - foreach($arr_range['rep'] as $rep) { - // Each location in the materialized view - $location = $data['location']; - - $args = array(':year' => $year, ':rep' => $rep, ':location' => $location, ':project_id' => $project_id); - // Get the entry with the most traits for this particular location, year and rep. - $traits = db_query($sql, $args) - ->fetchObject(); - - if (!empty($traits->all_traits)) { - $trait_list = explode(',', $traits->all_traits); - $trait_list = array_unique($trait_list); - - // NOTE: - // Less 2 to exclude trait Planting Data (date) and # of Seeds Planted (count). - // Before doing that, test if these two traits were part of the traits the count is based on. - $less = 0; - if (in_array($planting_date, $trait_list)) { - $less++; - } - - if (in_array($seeds_planted, $trait_list)) { - $less++; - } - - $trait_count = ((count($trait_list) - $less) < 0) ? 0 : (count($trait_list) - $less); - $trait_list = implode(',', $trait_list); - } - else { - $trait_list = 0; - $trait_count = 0; - } - - // Create a json entry with the following keys: - // location, year, rep and trait. - $arr_json[] = array('location' => $location, - 'year' => $year, - 'rep' => $rep, - 'trait' => $trait_count, - 'type_id' => $trait_list); - } - } - } - - return $arr_json; - } - else { - // No data. - return 0; - } - } -} - - -/** - * Function callback: Generate year and location used in categorizing barchart. - * - * @see hook_menu() - * - * @return - * A JSON containing the location and year for a given trait. - */ -function rawpheno_rawdata_trait_category_json() { - if ((!isset($_GET['project_id']) OR strlen($_GET['project_id']) >= 10) OR - (!isset($_GET['trait_id']) OR strlen($_GET['trait_id']) >= 10)) { - - // No data. - return 0; - } - else { - $project_id = trim(strip_tags($_GET['project_id'])); - $trait_id = trim(strip_tags($_GET['trait_id'])); - - // Location and planting_year (as Year). - $category = array('location', 'planting_date'); - $arr_json = array(); - - foreach($category as $c) { - $col = ($c == 'location') ? 'location' : 'SUBSTRING(planting_date, 1, 4)'; - - $sql = sprintf(" - SELECT DISTINCT %s AS col - FROM {rawpheno_rawdata_mview} WHERE plant_id IN (SELECT plant_id FROM pheno_plant_project WHERE project_id = :project_id) - AND STRPOS(all_traits, :trait_id) > 0 - ORDER BY col ASC", $col); - - $args = array(':project_id' => $project_id, ':trait_id' => $trait_id); - $r = chado_query($sql, $args); - - foreach($r as $d) { - $arr_json[$c][] = $d->col; - } - } - } - - return $arr_json; -} - - -/** - * Function callback: Generate data for the bar chart. - * - * @see hook_menu() - * - * @return - * A JSON containing the bins and data. - * bins is a list of computed bins and each data - * has location and bin objects. - * - * NOTE: The following column headers are excluded from the traits that can be visualized. - * Planting Date (date) - * Disease-specific Comments (text) - * Comments (text) - * # of Seeds Planted (count) - */ -function rawpheno_rawdata_trait_json() { - // When the type of data is txt, we need to ensure that it not sooo long that it will break the chart. - // To ensure that, we use this variable to check if the length is less or more and respond accordingly. - $max_data_length = 10; - - // Types of data. - $txt_type = 'txt'; - $int_type = 'int'; - $scale_type = 'scale'; - $ynunsure_type = 'y/n/?'; - - // Valid categorize options. - $arr_valid_category = array('location', 'year'); - - // Ensure project id and trait id are valid. - // Must exists, must not be empty and must not be more that 10 chars/digits. - // 10 Digits more will throw a PSQL ERROR. - - // Project Id : project id - // Trait Id : trait id - // Option : year or location values - // Category : year or location - if ((!isset($_GET['project_id']) OR empty($_GET['project_id']) OR strlen($_GET['project_id']) >= 10) OR - (!isset($_GET['trait_id']) OR empty($_GET['trait_id']) OR strlen($_GET['trait_id']) >= 10) OR - (!isset($_GET['option']) OR empty($_GET['option']) OR strlen($_GET['option']) >= 20) OR - (!isset($_GET['category']) OR empty($_GET['category']) OR !in_array($_GET['category'], $arr_valid_category))) { - - // No data. - return 0; - } - else { - $project_id = (int)trim(strip_tags($_GET['project_id'])); - $trait_id = (int)trim(strip_tags($_GET['trait_id'])); - - // Get cvterm. - $c = array('cvterm_id' => $trait_id, 'cv_id' => array('name' => 'phenotype_measurement_types')); - - if (function_exists('chado_get_cvterm')) { - $cvterm = chado_get_cvterm($c); - } - else { - $cvterm = tripal_get_cvterm($c); - } - - if (!$cvterm) { - return 0; - } - - // Query unique values in in the column, get data type and char length of each unique entry. - // CASE 1 - predict the data type of the value. - // CASE 2 - determine the lenght of the value. - $sql = "SELECT - value AS data, - CHAR_LENGTH(value) AS data_length - FROM {pheno_measurements} - WHERE type_id = - (SELECT cvterm_id FROM {pheno_project_cvterm} WHERE project_id = :project_id AND cvterm_id = :cvterm_id LIMIT 1) - AND plant_id IN (SELECT plant_id FROM pheno_plant_project WHERE project_id = :project_id) - GROUP BY value ORDER BY value ASC"; - - $args = array(':project_id' => $project_id, ':cvterm_id' => $trait_id); - $d = db_query($sql, $args); - - // Array to hold the data_type. - $arr_data_type = array(); - // Array to hold the length of data. - $arr_data_length = array(); - // Array to hold the data. - $arr_data = array(); - - foreach($d as $m) { - $dtype = (is_numeric($m->data)) ? 'int' : 'txt'; - $arr_data_type[] = $dtype; - - $arr_data_length[] = $m->data_length; - $arr_data[] = $m->data; - } - - // Get the overall data type. - $data_type = array_unique($arr_data_type); - - if (count($data_type) > 1 OR $d->rowCount() <= 0) { - // When there are too many data types or no data at all, do not visualize it. - return 0; - } - else { - $category = trim(strip_tags($_GET['category'])); - $option = trim(strip_tags($_GET['option'])); - - // Pass the values in categorize barchart. - $arr_category = array( - 'category' => $category, - 'option' => $option - ); - - // Else, chart it, but before doing so, determine if text or integer type. - if ($data_type[0] == $int_type) { - // Interger data type. - $type = (rawpheno_function_unit($cvterm->name) == $scale_type) ? $scale_type : $int_type; - return rawpheno_function_create_json($project_id, $trait_id, $type, $arr_category); - } - elseif ($data_type[0] == $txt_type) { - // Text data type, but, if the length of data is sooo long, don't chart it. - if (max($arr_data_length) > $max_data_length) { - // Too long. - return 0; - } - else { - // Yes, chart it! - $type = (rawpheno_function_unit($cvterm->name) == $ynunsure_type) ? $ynunsure_type : $txt_type; - return rawpheno_function_create_json($project_id, $trait_id, $type, $arr_category, $arr_data); - } - } - } - } -} - - -/** - * Implements hook_block_info(). - * Define a block for Rawphenotypes module. - */ -function rawpheno_block_info() { - $block['rawpheno_notification_block'] = array( - 'info' => t('Raw Phenotype Dashboard'), - ); - - return $block; -} - -/** - * Implements hook_block_view(). - * This will define how the block should look like. - */ -function rawpheno_block_view($delta = '') { - $is_front_page = drupal_is_front_page(); - $user_id = $GLOBALS['user']->uid; - - if ($delta == 'rawpheno_notification_block' && $is_front_page && $user_id > 0) { - // Show this block only when it is the front pages. - $sql = "SELECT uid FROM pheno_project_user WHERE uid = :user_id LIMIT 1"; - $args = array(':user_id' => $user_id); - $result = chado_query($sql, $args) - ->rowCount(); - - if ($result) { - // It is the front page, check user id if matches any id in the user_project table - $user = $GLOBALS['user']->name; - - // Number of files. - unset($result); - $sql = "SELECT file_id from pheno_backup_file INNER JOIN pheno_project_user USING(project_user_id) WHERE uid = :user_id"; - $result = chado_query($sql, $args) - ->rowCount(); - - $file_count = ($result > 0) ? $result : 0; - - // Last backed up. - unset($result); - $sql = "SELECT EXTRACT(DAY FROM NOW() - TO_TIMESTAMP(timestamp)) FROM file_managed INNER JOIN pheno_backup_file USING(fid) - WHERE project_user_id IN (SELECT project_user_id FROM pheno_project_user WHERE uid = :user_id) ORDER BY fid DESC LIMIT 1"; - - $recent_file = chado_query($sql, $args) - ->fetchField(0); - - // Colour code the alert message. - if ($recent_file == '') { - $alert = 'red'; - } - elseif ($recent_file >= 0 && $recent_file <= 3) { - $alert = 'green'; - } - elseif ($recent_file > 3 && $recent_file <= 10) { - $alert = 'yellow'; - } - else { - $alert = 'red'; - } - - $path = drupal_get_path('module', 'rawpheno'); - - $url = $_SERVER['SERVER_NAME']; - $u = explode('.', $url); - $hostname = (empty($u[0])) ? $url : ucfirst($u[0]); - - $support_email = rawpheno_function_get_support_email(); - - $content = theme('rawpheno_notification_block', array( - 'alert' => $alert, - 'username' => $user, - 'hostname' => $hostname, - 'file_count' => $file_count, - 'support_email' => $support_email, - 'path_module' => base_path() . $path . '/', - 'path_phenotypes_raw' => $GLOBALS['base_url'] . '/phenotypes/raw/', - 'path_phenotypes_video' => file_create_url('public://') . 'tutorial_files/rawpheno_videos/', - )); - - $block = array( - 'content' => array( - // Create main container for Rawphenotypes block. - '#prefix' => '
    ', - '#suffix' => '
    ', - 'rawpheno_notification_block' => array( - // Wrap the block contents. - '#prefix' => '
    ', - '#suffix' => '
    ', - '#markup' => $content, - '#attached' => array( - 'css' => array( - array( - 'type' => 'file', - 'data' => $path . '/theme/css/rawpheno.notification.block.style.css', - ) - ), - 'js' => array( - array( - 'type' => 'file', - 'data' => $path . '/theme/js/rawpheno.notification.block.script.js', - ) - ) - ) - ) - ) - ); - - return $block; - } - } -} - -/** - * Implements hook_preprocess_block(). - * Replace the id attribute generated by Drupal to custom id. - */ -function rawpheno_preprocess_block(&$vars) { - if ($vars['block']->module == 'rawpheno' - && $vars['block']->delta == 'rawpheno_notification_block') { - - $vars['block_html_id'] = drupal_html_id('rawpheno-notification-block'); - } - - return $vars; -} diff --git a/rawphenotypes.info.yml b/rawphenotypes.info.yml new file mode 100644 index 0000000..2ed2bd8 --- /dev/null +++ b/rawphenotypes.info.yml @@ -0,0 +1,11 @@ +name: Raw Phenotypes +description: Provides generic upload, storage, backup and export of raw phenotypic data. +package: Tripal Extension + +type: module +core: 8.x +configure: rawphenotypes.configure + +dependencies: + - libraries:libraries + diff --git a/rawphenotypes.install b/rawphenotypes.install new file mode 100755 index 0000000..91552b5 --- /dev/null +++ b/rawphenotypes.install @@ -0,0 +1,866 @@ + 'Table for phenotype plant.', + 'fields' => [ + 'plant_id' => [ + 'description' => 'A unique ID for each row', + 'type' => 'serial', + 'unsigned' => TRUE, + 'not null' => TRUE, + ], + 'stock_id' => [ + 'description' => 'A unique ID for a stock name.', + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + ], + ], + ]; + + // PHENO PLANT PROP + // Type should include entry, plot, rep and location. + $schema['pheno_plantprop'] = [ + 'description' => 'Table for phenotype plantprop.', + 'fields' => [ + 'plantprop_id' => [ + 'description' => 'Primary key: A unique ID for each plantprop record.', + 'type' => 'serial', + 'unsigned' => TRUE, + 'not null' => TRUE + ], + 'plant_id' => [ + 'description' => 'Holds plant ID number.', + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + ], + 'type_id' => [ + 'description' => 'Term ID number of trait inserted.', + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE + ], + 'value' => [ + 'description' => 'The value of the trait measured.', + 'type' => 'varchar', + 'length' => 255, + 'not null' => TRUE, + ], + ], + 'primary key' => ['plantprop_id'], + 'foreign keys' => [ + 'pheno_plant' => [ + 'table' => 'pheno_plant', + 'columns' => ['plant_id' => 'plant_id'] + ], + ], + ]; + + // PHENO SCALE MEMBER + // The scale code is what is written in the spreadsheet, whereas the value is what it actually represents. + $schema['pheno_scale_member'] = [ + 'description' => 'Table for pheno scale member.', + 'fields' => [ + 'member_id' => [ + 'description' => 'Primary key: A unique ID for each scale value.', + 'type' => 'serial', + 'unsigned' => TRUE, + 'not null' => TRUE, + ], + 'scale_id' => [ + 'description' => 'Scale ID number of a scale value.', + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + ], + 'code' => [ + 'description' => 'Corresponding code for each scale ID.', + 'type' => 'varchar', + 'length' => 255, + ], + 'value' => [ + 'description' => 'Description of a scale value.', + 'type' => 'text', + 'not null' => TRUE, + ], + ], + 'primary key' => ['member_id'], + ]; + + // PHENO MEASUREMENTS + // Modified is the timestamp that it was last modified. This allows us to determine when values were updated. + $schema['pheno_measurements'] = [ + 'description' => 'Table for phenotype measurements', + 'fields' => [ + 'measurement_id' => [ + 'description' => 'Primary key: A unique ID for each trait measured.', + 'type' => 'serial', + 'unsigned' => TRUE, + 'not null' => TRUE, + ], + 'plant_id' => [ + 'description' => 'Holds plant ID number.', + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + ], + 'type_id' => [ + 'description' => 'Term ID number of trait inserted.', + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + ], + 'unit_id' => [ + 'description' => 'Unit ID number of trait inserted.', + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + ], + 'value' => [ + 'description' => 'The value of the trait measured.', + 'type' => 'varchar', + 'length' => 255, + 'not null' => FALSE, + ], + 'cvalue_id' => [ + 'description' => 'Scale member code number when unit is scale.', + 'type' => 'varchar', + 'length' => 10, + 'not null' => FALSE, + ], + 'modified' => [ + 'description' => 'timestamp of the last modification.', + 'type' => 'varchar', + 'length' => 30, + 'not null' => TRUE, + ], + ], + 'primary key' => ['measurement_id'], + 'foreign keys' => [ + 'pheno_plant' => [ + 'table' => 'pheno_plant', + 'columns' => ['plant_id' => 'plant_id'] + ], + ], + ]; + // + + // PROJECT-CVTERM + // Group a set of traits/cvterm to a project. + $schema['pheno_project_cvterm'] = [ + 'description' => 'Table for grouping set of traits to a project.', + 'fields' => [ + 'project_cvterm_id' => [ + 'description' => 'Primary key: A unique ID number.', + 'type' => 'serial', + 'not null' => TRUE, + ], + 'project_id' => [ + 'description' => 'ID number of a project from chado.project table', + 'type' => 'int', + 'not null' => TRUE, + ], + 'cvterm_id' => [ + 'description' => 'cvterm id number of a trait', + 'type' => 'int', + 'not null' => TRUE, + ], + // This option will allow for grouping of traits. In AGILE Project, types include: + // Essential Traits, Optional Traits, Subset Trait and Plant Property. + // Use this field to indicate as such. + + // A function in rawpheno.function.measurements lists possible types that can be made available + // to admin when adding traits in the admin control panel. + 'type' => [ + 'description' => 'Indicate a column header is essential, not essential (optional), a plant property and other', + 'type' => 'varchar', + 'length' => 20, + ], + ], + 'primary key' => ['project_cvterm_id'], + ]; + + // PROJECT-PLANT/STOCK + $schema['pheno_plant_project'] = [ + 'description' => 'Table for mapping stock to a project', + 'fields' => [ + 'plant_project_id' => [ + 'description' => 'Primary key: A nunique ID number.', + 'type' => 'serial', + 'not null' => TRUE, + ], + 'project_id' => [ + 'description' => 'ID number of a project from chado.project table.', + 'type' => 'int', + 'not null' => TRUE, + ], + 'plant_id' => [ + 'description' => 'ID number of stock from pheno_plant table.', + 'type' => 'int', + 'not null' => TRUE, + ], + ], + 'primary key' => ['plant_project_id'], + ]; + + // PROJECT-USER + $schema['pheno_project_user'] = [ + 'description' => 'This table will hold information about user and project', + 'fields' => [ + 'project_user_id' => [ + 'description' => 'Primary Key: A unique ID number', + 'type' => 'serial', + 'not null' => TRUE, + ], + 'project_id' => [ + 'description' => 'ID number of project from chado.project table', + 'type' => 'int', + 'not null' => TRUE, + ], + 'uid' => [ + 'description' => 'ID number of logged in user from user table', + 'type' => 'int', + 'not null' => TRUE, + ], + ], + 'primary key' => ['project_user_id'], + ]; + + // PROJECT-BACKUP-FILE + $schema['pheno_backup_file'] = [ + 'description' => 'This table will hold information about files uploaded by user to a project', + 'fields' => [ + 'file_id' => [ + 'description' => 'Primary Key: A unique ID number', + 'type' => 'serial', + 'not null' => TRUE, + ], + 'project_user_id' => [ + 'description' => 'The project ID number from table pheno_project_user', + 'type' => 'int', + 'not null' => TRUE, + ], + 'fid' => [ + 'description' => 'File ID number from file_managed table', + 'type' => 'int', + 'not null' => TRUE, + ], + 'version' => [ + 'description' => 'The sequence number of the file in relation to a project', + 'type' => 'int', + ], + 'validation_result' => [ + 'description' => 'Stores the result of file validation performed', + 'type' => 'text', + ], + 'notes' => [ + 'description' => 'Notes, description and comments made to a file', + 'type' => 'text', + ], + 'archive' => [ + 'description' => 'Indicates whether a file is temporarily removed from a project.', + 'type' => 'varchar', + 'length' => 2, + 'default' => 'n', + ], + ], + 'primary key' => ['file_id'], + ]; + + // PHENO-ENVIRONMENT-DATA + $schema['pheno_environment_data'] = [ + 'description' => 'This table will hold information about environment data.', + 'fields' => [ + 'environment_data_id' => [ + 'description' => 'Primary Key: A unique ID number', + 'type' => 'serial', + 'not null' => TRUE, + ], + 'project_id' => [ + 'description' => 'The project ID number from table chado.project', + 'type' => 'int', + 'not null' => TRUE, + ], + 'fid' => [ + 'description' => 'File ID number from file_managed table', + 'type' => 'int', + 'not null' => TRUE, + ], + 'sequence_no' => [ + 'description' => 'The sequence number of the file.', + 'type' => 'int', + ], + 'location' => [ + 'description' => 'Location information of the file.', + 'type' => 'text', + ], + 'year' => [ + 'description' => 'Year information of the file.', + 'type' => 'text', + ], + ], + 'primary key' => ['environment_data_id'], + ]; + + // Create materialized view + // @TODO - remove when Tripal mview api is supported. + $schema['rawpheno_rawdata_mview'] = [ + 'description' => 'Temporary table while Tripal Mview is not available', + 'fields' => [ + 'plant_id' => [ + 'description' => 'Plant id', + 'type' => 'int' + ], + 'location' => [ + 'description' => 'Location', + 'type' => 'varchar', + 'length' => 255 + ], + 'rep' => [ + 'description' => 'Replicate', + 'type' => 'varchar', + 'length' => 255 + ], + 'planting_date' => [ + 'description' => 'Planting date', + 'type' => 'varchar', + 'length' => 255 + ], + 'total_count' => [ + 'description' => 'Total count', + 'type' => 'int' + ], + 'all_traits' => [ + 'description' => 'All traits', + 'type' => 'text' + ], + ], + 'indexes' => [ + 'plant_id' => ['plant_id'], + 'location' => ['location'], + ] + ]; + + return $schema; +} + +/** + * Implements hook_install(). + */ +function rawphenotypes_install() { + // Add services. + $term_service = \Drupal::service('rawphenotypes.term_service'); + $mview_service = \Drupal::service('rawphenotypes.mview_service'); + $logger_service = \Drupal::service('rawphenotypes.logger_service'); + $default_service = \Drupal::service('rawphenotypes.default_service'); + $project_service = \Drupal::service('rawphenotypes.project_service'); + + // Configuration setting values. + $settings = \Drupal::service('config.factory') + ->getEditable('rawphenotypes.settings'); + + // Get variable names and default values. + $vars = $default_service::getDefaultValue('variables'); + $config = $default_service::getDefaultValue('defaults'); + + // # 1. INSERT CV. + // Insert all cvs required. + $cv = $default_service::getDefaultValue('vocabularies'); + + // Array to hold cv_id and cv_name. + // This will be used when creating cvterm relationships + // and add corresponding cvterm. + $arr_cvid_cvname = []; + // cv - cvterm mapping array. + $arr_map_cv_cvterm = []; + // CV to hold terms used to create relationships. + // term - unit, term - method, term - R. + $rawphenotypes_cv = $cv['cv_phenotypes']; + + foreach($cv as $cv_i => $cv_name) { + $cv_exists = $term_service->getTermByName($cv_name, 'cv'); + if ($cv_exists) continue; + + // A constraint in relation cvtermprop requires that + // cvtermprop.type_id is a row in chado.cvterm. + + // First cv (rawphenotypes_terms) will be created that will hold cvterm. + // Each cv will become a cvterm and stored in this initial cv. + // This will be used in creating relationships (type_id) in relations + // cvtermprop and cvterm_relationships. + $cv_def = str_replace('_', ' ', ucfirst($cv_name)); + $tmp = (function_exists('chado_insert_cv')) + ? chado_insert_cv($cv_name, $cv_def) + : tripal_insert_cv($cv_name, $cv_def); + + + if($tmp) { + if ($cv_name == $rawphenotypes_cv) continue; // Don not add as cvterm. + + $cv_cvterm = [ + 'id' => 'rawpheno_tripal:' . $cv_name, + 'name' => $cv_name, + 'definition' => $cv_def, + 'cv_name' => $rawphenotypes_cv + ]; + + $cvcvterm_id = $term_service->addTerm($cv_cvterm); + // Save the cvterm id based on the cv name for later when + // creating relationships. + $arr_map_cv_cvterm[ $cv_name ] = $cvcvterm_id->cvterm_id; + } + else { + $msg = 'Chado/Tripal failed to insert cv.'; + $logger_service::postTripalReportError($msg); + } + } + + // # 2. INSERT UNITS. + // Insert units and scale member used by scale. + // NOTE: Scale memeber is link to the column header Lodging (scale 1-5) and not to scale unit. + // When the column header Lodging is inserted, only then we can insert the scale members. + + // Array to hold scale members. + $arr_unit = $default_service::getDefaultValue('units'); + // Array to hold cvterm_id of each inserted unit. + // This will be used when creating a cvterm relationship linking a cvterm to a unit. + $arr_cvtermid_unit = []; + + foreach($arr_unit as $unit => $def) { + $unit_exists = $term_service->getTermByName($unit); + if ($unit_exists) continue; + + $ins_unit = [ + 'id' => 'rawpheno_tripal:' . $unit, + 'name' => $unit, + 'definition' => $def, + 'cv_name' => $cv['cv_unit'] + ]; + + $tmp = $term_service->addTerm($ins_unit); + + if ($tmp) { + $arr_cvtermid_unit[ $unit ] = $tmp->cvterm_id; + } + else { + $msg = 'Chado/Tripal failed to insert cvterm (trait unit).'; + $logger_service::postTripalReportError($msg); + } + } + + // # 3. INSERT COLUMN HEADERS/TRAITS. + // Insert column headers. + // When inserting column headers, generate the R Version, add collection method + // and map it to a AGILE (only when this project is available). + + // Default Project. + // This is the default project, only when such project exists, otherwise, insert the column headers + // and make them available for use in admin control panel. + // @note project.name must be unique so no need to limit 1. + $default_project = $default_service::getDefaultValue('project'); + $project = $project_service::getProjectByName($default_project); + + if (isset($project)) { + $project_ID = $project->project_id; + + // When AGILE is present in this site, add a user to this project. + $current_user = \Drupal::currentUser()->id(); + $project_service::addUserToProject($current_user, $project_ID); + + // If the user is not the superadmin, add superadmin as well. + if ($current_user != 1) { + $project_service::addUserToProject(1, $project_ID); + } + } + + // Initialize R Transformation rules required in generating R version of header. + // The default rules are the rules required in generating R version of headers default to this module. + // Set default configurations. + $settings + ->set($vars[0], $config['colour']) + ->set($vars[1], $config['rawdata']) + ->set($vars[2], $config['download']) + ->set($vars[3], $config['instructions']) + ->set($vars[4], $config['upload']) + ->set($vars[5], $config['backup']) + ->set($vars[6], $config['r-words']) + ->set($vars[7], $config['r-chars']) + ->set($vars[8], $config['r-replace']) + ->save(); + + // Array to hold header types. + $trait_type = $default_service::getTraitTypes(); + + // Array to hold all column headers, plant property headers and essential headers. + $arr_allheaders = $default_service::getTraits('expected'); + $arr_subsetheader = $default_service::getTraits('subset'); + $arr_plantpropheader = $default_service::getTraits('plantprop'); + $arr_essentialheader = $default_service::getTraits('essential'); + + foreach($arr_allheaders as $header) { + $header_exists = $term_service->getTermByName($header); + + // Skip when it is Name or if header was previously added. + if ($header == 'Name' || $header_exists) { + continue; + } + + // Get the definition and collection method. + $cvterm_info = $default_service::defineTrait($header); + // When no information is retured, default the definition to the term itself. + $cvterm_definition = (!empty($cvterm_info['define'])) ? $cvterm_info['define'] : $header; + // Set the type of header. + $cvterm_type = (in_array($header, $arr_plantpropheader)) ? $cv['cv_prop'] : $cv['cv_type']; + + $ins_term = [ + 'id' => 'rawpheno_tripal:' . $header, + 'name' => $header, + 'definition' => $cvterm_definition, + 'cv_name' => $cvterm_type + ]; + + // Insert the cvterm. + $cvterm = $term_service->addTerm($ins_term); + + // Insert the cvterm data collection method. + if ($cvterm) { + $cvterm_info['method'] = $cvterm_info['method'] ?? $header; + // Data collection method. + $term_service->saveTermProperty([ + 'cvterm_id' => $cvterm->cvterm_id, + // Refer to cv - cvterm mapping array. + 'type_id' => $arr_map_cv_cvterm[ $cv['cv_desc'] ], + 'value' => $cvterm_info['method'], + 'rank' => 0 + ]); + } + else { + $msg = 'Chado/Tripal failed to insert cvterm (trait unit).'; + $logger_service::postTripalReportError($msg); + } + + // Relate cvterm and unit. + // No relationship when header is Plant Property Type. + if (!in_array($header, $arr_plantpropheader) && $cvterm) { + // Extract the unit part from the column header. Usually, the information enclosed in a parenthesis. + $header_unit = $term_service->getTermUnit($header); + $header_unit = (empty($header_unit)) ? 'text' : $header_unit; + + $term_service->saveTermRelationship([ + 'subject_id' => $arr_cvtermid_unit[$header_unit], + // Refer to cv - cvterm mapping array. + 'type_id' => $arr_map_cv_cvterm[ $cv['cv_unit'] ], + 'object_id' => $cvterm->cvterm_id + ]); + } + + // R version. To be implemented to all column headers. + $r_version = $term_service->makeTermRCompatible($header); + + if ($cvterm) { + $term_service->saveTermProperty([ + 'cvterm_id' => $cvterm->cvterm_id, + // Refer to the cv - cvterm mapping array. + 'type_id' => $arr_map_cv_cvterm[ $cv['cv_rver'] ], + 'value' => $r_version, + 'rank' => 0 + ]); + } + + // Finally, map this particular header to project, again, only when default project is present. + if (isset($project_ID) && $project_ID > 0) { + // Indicate the type of header. + if (in_array($header, $arr_plantpropheader)) { + // Type is Plant Property. + $type = $trait_type['type4']; + } + elseif (in_array($header, $arr_essentialheader)) { + // Trait is essential (must be in the spreadsheet). + $type = $trait_type['type1']; + } + elseif (in_array($header, $arr_subsetheader)) { + // Subset Trait + $type = $trait_type['type3']; + } + else { + // Trait is optional + $type = $trait_type['type2']; + } + + $term_service->saveTermToProject($cvterm->cvterm_id, $type, $project_ID); + } + } + + // Lodging (scale 1-5) has been added, so the scale member table can now be generated. + $lodging = $default_service::getDefaultValue('scales'); + + $cl = ['name' => array_keys($lodging)[0], 'cv_id' => ['name' => $cv['cv_type']]]; + $cvterm_lodging = $term_service->getTerm($cl); + + // Insert scale values 1 - 5 + $term_service->setTermScaleValues($cvterm_lodging->cvterm_id, array_values($lodging)[0]); + + // # 4. MATERIALIZED VIEW. + // @TODO: CURRENLTY NOT SUPPORTED IN T4. + // Create materialized view used in phenotypes/rawdata. + $mview_service::createMview(); + + // #5. ADD DEFAULT SAMPLE SPREADSHEET + // Add data collection spreadsheet to public:// directory, + // and link this file for download in instructions page. + $destination = \Drupal::config('system.file') + ->get('default_scheme') . '://'; + + $file_handler = \Drupal::service('file_system'); + $file_dir = $file_handler + ->prepareDirectory($destination, FILE_MODIFY_PERMISSIONS); + + if ($file_dir) { + // Add this file if directory is writable. + // File is in zip format as it is more friendly for download than xlsx. + $file = 'AGILE-PhenotypeDataCollection-v5.xlsx.zip'; + + $source = \Drupal::service('module_handler') + ->getModule('rawphenotypes') + ->getPath(); + + $source .= '/theme/' . $file; + + // Copy file without creating an entry in drupal database. + // If file is present, replace it with the same file. + $file_handler + ->copy($source, $destination, FILE_EXISTS_REPLACE); + } +} + +/** + * Implements hook_uninstall(). + */ +function rawphenotypes_uninstall() { + $term_service = \Drupal::service('rawphenotypes.term_service'); + $mview_service = \Drupal::service('rawphenotypes.mview_service'); + $default_service = \Drupal::service('rawphenotypes.default_service'); + + // # 1. UNREGISTER PERSISTENT VARIABLES. + $settings = \Drupal::service('config.factory') + ->getEditable('rawphenotypes.settings') + ->delete(); + + // # 2. DROP MATERIALIZED VIEW TABLE. + // Using tripal: tripal_delete_mview(). + // Get mview id of materilaized view created by hook_install(). + // @note tripal_mviews.mv_table must be unique (constraint). + $mview_service::dropMview(); + + // # 3. DELETE CV AND CVTERM. + $cv = $default_service::getDefaultValue('vocabularies'); + $db = \Drupal::database(); + + foreach($cv as $cv_i => $cv_name) { + $cv = $term_service->getTerm(['name' => $cv_name], 'cv'); + $args = [':cv_id' => $cv->cv_id]; + + // Delete relationships. + if ($cv_i == 'cv_unit') { + $del = "DELETE FROM chado.cvterm_relationship WHERE type_id = :cv_id"; + $db->query($del, $args); + } + + // Delete R version. + if ($cv_i == 'cv_rver') { + $del = "DELETE FROM chado.cvtermprop WHERE type_id = :cv_id"; + $db->query($del, $args); + } + + // Delete cvterms. + if ($cv_i == 'cv_type') { + $del = "DELETE FROM chado.cvterm WHERE cv_id = :cv_id"; + $db->query($del, $args); + } + + // Finally, delete the cv + $del = "DELETE FROM chado.cv WHERE cv_id = :cv_id"; + $db->query($del, $args); + } +} + +/** + * Implements hook_requirements(). + */ +function rawphenotypes_requirements($phase) { + $requirements = []; + + // On install of this module, check the following libraries. + if (($phase == 'install' || $phase == 'update') && function_exists('libraries_get_path')) { + $libraries = 'i.e., sites/all/libraries/'; + + $arr_req_libraries = [ + // Libraries. + 'excel_writer' => + [ + 'is_in' => libraries_get_path('PHP_XLSXWriter_plus'), + 'source' => 'https://github.com/SystemDevil/PHP_XLSXWriter_plus/archive/master.zip', + 'path' => $libraries . 'PHP_XLSXWriter_plus' + ], + + 'excel_reader' => + [ + 'is_in' => libraries_get_path('spreadsheet-reader'), + 'source' => 'https://github.com/nuovo/spreadsheet-reader/archive/master.zip', + 'path' => $libraries . 'spreadsheet-reader' + ], + + 'd3' => + [ + 'is_in' => libraries_get_path('d3'), + 'source' => 'https://github.com/d3/d3/releases/download/v3.5.14/d3.zip', + 'path' => $libraries . 'd3' + ] + ]; + + foreach($arr_req_libraries as $i => $info) { + if (!$info['is_in']) { + $path = $info['path']; + $source = $info['source']; + + $link = Url::fromUri($source); + $requirements[$i] = [ + 'severity' => REQUIREMENT_ERROR, + 'description' => + t('This module uses @library, which is missing. + Please download and extract the entire contents of the archive into Libraries Directory %path directory on your server.', + ['@library' => \Drupal::l(strtoupper(str_replace('_',' ',$i)), $link), '%path' => $path]), + ]; + } + + // Check version of D3 and esure patch has been implemented. + if ($i == 'd3' && $info['is_in']) { + // @see library function below. + $ver = rawphenotypes_library_D3_get_version(); + $v = explode('.', $ver); + + if (trim($v[0]) != '3' OR (int)$v[1] < 4) { + $link = Url::fromUri('https://github.com/UofS-Pulse-Binfo/rawphenotypes'); + $link_ver = Url::fromUri('https://github.com/d3/d3/releases/download/v3.5.14/d3.zip'); + $requirements['d3_version'] = [ + 'severity' => REQUIREMENT_ERROR, + 'description' => t('The D3 (ver %ver) library installed is not supported by this module. Please review the module requirements (@rawpheno_module) or download D3 version 3.5.14 (@download).', + ['@rawpheno_module' => \Drupal::l('Rawphenotypes Module', $link), '%ver' => $ver, '@download' => \Drupal::l('D3 3.5.14', $link_ver)]), + ]; + } + } + elseif ($i == 'excel_reader' && $info['is_in']) { + // @see library function below. + $is_patched = rawphenotypes_library_reader_patch(); + + if (!$is_patched) { + $link = Url::fromUri('https://github.com/UofS-Pulse-Binfo/rawphenotypes'); + $requirements['patch'] = [ + 'severity' => REQUIREMENT_ERROR, + 'description' => t('The Spreadsheet Reader library patch was not implemented. Please review the module requirements (@rawpheno_module).', + ['@rawpheno_module' => \Drupal::l('Rawphenotypes Module', $link)] + ), + ]; + } + } + } + } + + return $requirements; +} + + +// HELPER FUNCTIONS. + + +/** + * Get the version of D3 library. + * + * @see implementation of hook_libraries_info(). + * implementation of hook_requirements(). + */ +function rawphenotypes_library_D3_get_version() { + if (libraries_get_path('d3')) { + $file = libraries_get_path('d3') . '/d3.js'; + $f = fopen($file, 'r'); + + if ($f === FALSE) { + $ver = 0; + } + else { + $c = fread($f, filesize($file)); + + $lines = explode("\n", $c); + $i = 0; + + // Since the version is in the first 20-30 line, break loop past line #. + foreach($lines as $l) { + if ($i > 0 && stristr($l, 'version')) { + $ver = trim(str_replace(array('var version', 'version:', 'version', ':', '"', 'var', '=', ';'), '', strtolower($l))); + break; + } + + if ($i == 30) { + $ver = 0; + break; + } + + $i++; + } + } + + fclose($f); + } + else { + $ver = 0; + } + + return $ver; +} + +/** + * Ensure that user applied the patch to spreadsheet reader library + */ +function rawphenotypes_library_reader_patch() { + $lib = []; + + $lib['xlsx'] = libraries_get_path('spreadsheet-reader') . '/SpreadsheetReader_XLSX.php'; + $lib['xls'] = libraries_get_path('spreadsheet-reader') . '/php-excel-reader/excel_reader2.php'; + + $patch['xlsx'] = '$Format[\'Code\'] = \'Y-m-d\''; + $patch['xls'] = '0xf => "M-d-Y"'; + + $pass = 0; + foreach($lib as $ver => $file) { + $f = fopen($file, 'r'); + $line = fread($f, filesize($file)); + fclose($f); + + if (strpos($line, $patch[$ver])) { + $pass++; + } + } + + return ($pass == 2) ? 1 : 0; +} \ No newline at end of file diff --git a/rawphenotypes.libraries.yml b/rawphenotypes.libraries.yml new file mode 100644 index 0000000..a689244 --- /dev/null +++ b/rawphenotypes.libraries.yml @@ -0,0 +1,44 @@ +# @file +# Rawphenotypes Module libraries. + +# Style Rawphenotypes configuration pages. +style-admin: + version: v1.x + css: + theme: + css/style-admin.css: {} + +# Script use in administration/configuration pages. +script-admin: + version: v1.x + js: + js/script-admin.js: {} + dependencies: + - core/jquery + - core/jquery.once + +# Library used in instructions page. +page-instructions: + version: v1.x + js: + js/jquery-tabs.js: {} + js/script-page-instructions.js: {} + css: + theme: + css/style-page-instructions.css: {} + css/jquery-ui.min.css: {} + dependencies: + - core/jquery + - core/jquery.once + +# Library used in backup page. +page-backup: + version: v1.x + js: + js/script-page-backup.js: {} + css: + theme: + css/style-page-backup.css: {} + dependencies: + - core/jquery + - core/jquery.once \ No newline at end of file diff --git a/rawphenotypes.links.task.yml b/rawphenotypes.links.task.yml new file mode 100644 index 0000000..a374103 --- /dev/null +++ b/rawphenotypes.links.task.yml @@ -0,0 +1,24 @@ +# Tabs/task items below point to routes defined in +# @see rawphenotypes.routing.yml. +# Order is as they appear in the configuration page (weight). + +# General Configuration Tab. +rawphenotypes.tab1: + title: 'Page Configurations' + route_name: rawphenotypes.configure + base_route: rawphenotypes.configure + weight: 0 + +# Configure R Transformation Rules. +rawphenotypes.tab2: + title: 'Define R Transformation Rules' + route_name: rawphenotypes.rtransform + base_route: rawphenotypes.configure + weight: 1 + +# Manage Phenotyping Experiments. +rawphenotypes.tab3: + title: 'Manage Project' + route_name: rawphenotypes.projects + base_route: rawphenotypes.configure + weight: 2 \ No newline at end of file diff --git a/rawphenotypes.module b/rawphenotypes.module new file mode 100755 index 0000000..0958feb --- /dev/null +++ b/rawphenotypes.module @@ -0,0 +1,155 @@ +get('rawpheno_colour_scheme'); + // Path to module. + $module = \Drupal::service('extension.list.module') + ->getPath('rawphenotypes'); + global $base_url; + $path_module = $base_url. base_path() . $module; + + $variables = [ + // Messages - error, warning and status. + 'theme-rawphenotypes-message' => [ + 'variables' => ['data' => []], + 'template' => 'template-rawphenotypes-message' + ], + // Instructions page. + 'rawphenotypes_page_instructions_template' => [ + 'variables' => ['search_form' => NULL, 'page' => $variables, 'path' => $path_module], + 'template' => 'template-rawphenotypes-page-instructions', + 'render element' => 'form' + ], + // Backup page. + 'rawphenotypes_page_backup_template' => [ + 'variables' => ['upload_form' => NULL, 'page' => $variables, 'path' => $path_module], + 'template' => 'template-rawphenotypes-page-backup', + 'render element' => 'form' + ], + ]; + + return $variables; +} + +/** + * Implements a hook_preprocess_HOOK(). + * This function prepares variables for Raw Phenotypes Instuctions Page. + */ +function rawphenotypes_preprocess_rawphenotypes_page_instructions_template(&$variables, $hook) { + $config = \Drupal::config('rawphenotypes.settings'); + $variables['title'] = $config->get('rawpheno_instructions_title'); + + // Links to other pages. + $variables['to_upload'] = Url::fromRoute('rawphenotypes.page_upload', [], ['absolute' => TRUE]); + $variables['to_backup'] = Url::fromRoute('rawphenotypes.page_backup', [], ['absolute' => TRUE]); + // Parameter project_id is appended in the template. + $variables['to_collect'] = Url::fromRoute('rawphenotypes.callback.generate_spreadsheet', [], ['absolute' => TRUE]); +} + +/** + * Implements a hook_preprocess_HOOK(). + * This function prepares variables for Raw Phenotypes Backup Page. + */ +function rawphenotypes_preprocess_rawphenotypes_page_backup_template(&$variables, $hook) { + $config = \Drupal::config('rawphenotypes.settings'); + $variables['title'] = $config->get('rawpheno_backup_title'); + + // Links to other pages. + $variables['to_instructions'] = Url::fromRoute('rawphenotypes.page_instructions', [], ['absolute' => TRUE]); + + // Currently logged in user. Show the name of the user. + $user_id = \Drupal::currentUser()->id(); + $user = \Drupal::service('rawphenotypes.user_service')::getUser((int)$user_id, ['name']); + $variables['current_user'] = $user['name']; + /* + $variables['to_backup'] = Url::fromRoute('rawphenotypes.page_backup', [], ['absolute' => TRUE]); + // Parameter project_id is appended in the template. + $variables['to_collect'] = Url::fromRoute('rawphenotypes.callback.generate_spreadsheet', [], ['absolute' => TRUE]); + */ +} + + + + +/** + * Implements hook_libraries_info(). + * + * Define external libraries: Spreadsheet Reader and D3JS + */ +function rawphenotypes_libraries_info() { + // Spreadsheet reader + // File option is empty since library files are included + // individually using include_once(). + // files in: sites/all/libraries/spreadsheet-reader + // - SpreadsheetReader.php + // - SpreadsheetReaderXLSX.php + // - SpreadsheetReaderXLS.php + // - php-excel-reader/excel_reader2.php + + // NOTE: To prevent library form auto formatting data to MM/DD/YYYY, + // suggest a new data format YYYY-mm-dd in the source code in line: + // * 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'); + + // Let Drupal decide where library is. + $lib_reader = libraries_get_path('spreadsheet-reader'); + + $libraries['spreadsheet_reader'] = [ + 'name' => 'NUOVO Spreadsheet', + 'vendor url' => 'https://github.com/nuovo/spreadsheet-reader', + 'version callback' => 'rawpheno_function_reader_version_callback', + 'download url' => 'https://github.com/nuovo/spreadsheet-reader/archive/master.zip', + 'library path' => $lib_reader, + 'path' => $lib_reader . '/', + 'files' => [], + ]; + + // Spreadsheet writer. + // Let Drupal decide where library is. + $lib_writer = libraries_get_path('PHP_XLSXWriter_plus'); + + $libraries['spreadsheet_writer'] = [ + 'name' => 'PHP_XLSXWriter_plus Spreadsheet Writer', + 'vendor url' => 'https://github.com/SystemDevil/PHP_XLSXWriter_plus', + 'version' => 1, + 'download url' => 'https://github.com/SystemDevil/PHP_XLSXWriter_plus/archive/master.zip', + 'library path' => $lib_writer, + 'files' => ['xlsxwriter.class.php'], + ]; + + // D3 JavaScript. + // Let Drupal decide where library is + $lib_d3 = libraries_get_path('d3'); + + $libraries['d3js'] = [ + 'name' => 'D3 Data-Driven Documents', + 'vendor url' => 'https://d3js.org/', + 'version callback' => 'rawpheno_function_d3_version_callback', + 'download url' => 'https://github.com/d3/d3/releases/download/v3.5.14/d3.zip', + 'library path' => $lib_d3, + 'files' => [ + 'js' => ['d3.js'] + ], + ]; + + return $libraries; +} \ No newline at end of file diff --git a/rawphenotypes.routing.yml b/rawphenotypes.routing.yml new file mode 100644 index 0000000..7807af9 --- /dev/null +++ b/rawphenotypes.routing.yml @@ -0,0 +1,135 @@ +# Routes for managing Rawphenotypes module (laid out in tabs). +# @see rawphenotypes.links.task.yml + +# Configure Raw Phenotypes module. +rawphenotypes.configure: + path: 'admin/tripal/extension/rawphenotypes' + defaults: + _title: 'Configure Raw Phenotypes Module' + _form: '\Drupal\rawphenotypes\Form\RawphenotypesConfigurationForm' + requirements: + _permission: 'administer tripal' + +rawphenotypes.rtransform: + path: 'admin/tripal/extension/rawphenotypes/rtransform' + defaults: + _title: 'Configure R Tranformation Rules' + _form: '\Drupal\rawphenotypes\Form\RawphenotypesRTransformForm' + requirements: + _permission: 'administer tripal' + +rawphenotypes.projects: + path: 'admin/tripal/extension/rawphenotypes/projects' + defaults: + _title: 'Manage Phenotyping Experiments' + _form: '\Drupal\rawphenotypes\Form\RawphenotypesManageProjectForm' + requirements: + _permission: 'administer tripal' + +# Project assets management and operation. +rawphenotypes.manage_project: + path: 'admin/tripal/extension/rawphenotypes/projects/{asset_id}/{asset_type}/{action}' + defaults: + _title: 'Manage Phenotyping Experiments' + _form: '\Drupal\rawphenotypes\Form\RawphenotypesManageAssetForm' + requirements: + _permission: 'administer tripal' + asset_id: '[0-9]+' + asset_type: 'project|header|user|envdata' + action: 'manage|edit|delete' + + +# Page routes. + +# Instructions page. +rawphenotypes.page_instructions: + path: 'rawphenotypes/instructions/{project_id}' + defaults: + _title: 'Instructions' + _controller: '\Drupal\rawphenotypes\Controller\RawphenotypesPageInstructionsController:loadPage' + project_id: '0' + requirements: + _permission: 'admister tripal' + project_id: '[0-9]+' + +# Backup page. +rawphenotypes.page_backup: + path: 'rawphenotypes/backup' + defaults: + _title: 'Backup' + _controller: '\Drupal\rawphenotypes\Controller\RawphenotypesPageBackupController:loadPage' + requirements: + _permission: 'admister tripal' + +# Download page. +rawphenotypes.page_download: + path: 'rawphenotypes/download' + defaults: + _title: 'Download' + #_controller: '\Drupal\rawphenotypes\Controller\RawphenotypesPageInstructionsController:loadPage' + requirements: + _permission: 'admister tripal' + +# Upload page. +rawphenotypes.page_upload: + path: 'rawphenotypes/upload' + defaults: + _title: 'Upload' + #_controller: '\Drupal\rawphenotypes\Controller\RawphenotypesPageInstructionsController:loadPage' + requirements: + _permission: 'admister tripal' + +# Rawdata page. +rawphenotypes.page_summary: + path: 'rawphenotypes/summary' + defaults: + _title: 'Raw Data Summary' + #_controller: '\Drupal\rawphenotypes\Controller\RawphenotypesPageInstructionsController:loadPage' + requirements: + _permission: 'admister tripal' + + +# Autocomplete routes. + +# Suggest users. +rawphenotypes.autocomplete.user: + path: 'admin/tripal/extension/rawphenotypes/user' + defaults: + #_controller: '\Drupal\rawphenotypes\Controller\RawphenotypesAutocompleteController::autocompleteUser' + _format: json + requirements: + _permission: 'administer tripal' + +rawphenotypes.autocomplete.term: + path: 'admin/tripal/extension/rawphenotypes/term/{project_id}' + defaults: + _controller: '\Drupal\rawphenotypes\Controller\RawphenotypesAutocompleteController::autocompleteTerm' + _format: json + requirements: + _permission: 'administer tripal' + project_id: '[0-9]+' + + + +# Callback routes. + +# Generate project-specific data collection spreadsheet file. +rawphenotypes.callback.generate_spreadsheet: + path: 'rawphenotypes/spreadsheet/{project_id}' + defaults: + _title: 'Data Collection Spreadsheet File' + _controller: '\Drupal\rawphenotypes\Controller\RawphenotypesCreateProjectSpreadsheet::createFile' + project_id: '0' + requirements: + _permission: 'administer tripal' + project_id: '[0-9]+' + +# Handle drag and drop file upload. +rawphenotypes.callback.dragdropfile: + path: 'rawphenotypes/dragdropfile' + defaults: + _controller: '\Drupal\rawphenotypes\Controller\RawphenotypesDragdropFileController::upload' + _format: json + methods: [POST] + requirements: + _permission: 'administer tripal' \ No newline at end of file diff --git a/rawphenotypes.services.yml b/rawphenotypes.services.yml new file mode 100644 index 0000000..86161ea --- /dev/null +++ b/rawphenotypes.services.yml @@ -0,0 +1,18 @@ +# @file +# Rawphenotypes Module Services Definition. + +services: + rawphenotypes.default_service: + class: Drupal\rawphenotypes\Services\RawphenotypesDefaultValueService + rawphenotypes.term_service: + class: Drupal\rawphenotypes\Services\RawphenotypesTermService + rawphenotypes.project_service: + class: Drupal\rawphenotypes\Services\RawphenotypesProjectService + rawphenotypes.logger_service: + class: Drupal\rawphenotypes\Services\RawphenotypesLoggerService + rawphenotypes.mview_service: + class: Drupal\rawphenotypes\Services\RawphenotypesMviewService + rawphenotypes.user_service: + class: Drupal\rawphenotypes\Services\RawphenotypesUserService + rawphenotypes.envdata_service: + class: Drupal\rawphenotypes\Services\RawphenotypesEnvironmentDataService diff --git a/spreadsheet-reader.patch b/spreadsheet-reader.patch deleted file mode 100644 index 157dfac..0000000 --- a/spreadsheet-reader.patch +++ /dev/null @@ -1,28 +0,0 @@ -diff --git a/SpreadsheetReader_XLSX.php b/SpreadsheetReader_XLSX.php -index 9cf8d12..6162cf5 100644 ---- a/SpreadsheetReader_XLSX.php -+++ b/SpreadsheetReader_XLSX.php -@@ -831,6 +831,8 @@ - - if (!$this -> Options['ReturnDateTimeObjects']) - { -+ // We've added this check to change mm-dd-yy format to yyyy-mm-dd. -+ if ($Format['Code'] == 'm-d-y') $Format['Code'] = 'Y-m-d'; - $Value = $Value -> format($Format['Code']); - } - else -diff --git a/php-excel-reader/excel_reader2.php b/php-excel-reader/excel_reader2.php -index 75351b7..88c5ef4 100644 ---- a/php-excel-reader/excel_reader2.php -+++ b/php-excel-reader/excel_reader2.php -@@ -675,7 +675,9 @@ class Spreadsheet_Excel_Reader { - */ - var $dateFormats = array ( - 0xe => "m/d/Y", -- 0xf => "M-d-Y", -+ // We are replacing the M-d-Y format with our Y-m-d format. -+ // 0xf => "M-d-Y", -+ 0xf => "Y-m-d", - 0x10 => "d-M", - 0x11 => "M-Y", - 0x12 => "h:i a", diff --git a/src/Controller/RawphenotypesAutocompleteController.php b/src/Controller/RawphenotypesAutocompleteController.php new file mode 100644 index 0000000..e9e96e1 --- /dev/null +++ b/src/Controller/RawphenotypesAutocompleteController.php @@ -0,0 +1,71 @@ + :plantprop + "; + $args = [':project_id' => $project_id, ':plantprop' => $trait_type['type4']]; + + $result = \Drupal::database() + ->query($sql, $args); + + // Array to hold list of traits. + $arr_headers = []; + + // Determine if keyword is provided. + if (empty($request)) { + foreach($result as $n) { + $arr_headers[] = $n->name; + } + } + else { + foreach($result as $n) { + if (str_contains(strtolower($n->name), strtolower($request->query->get('q')))) { + $arr_headers[ $n->name ] = $n->name; + } + } + } + + return new JsonResponse($arr_headers); + } +} \ No newline at end of file diff --git a/src/Controller/RawphenotypesCreateProjectSpreadsheet.php b/src/Controller/RawphenotypesCreateProjectSpreadsheet.php new file mode 100644 index 0000000..75234cf --- /dev/null +++ b/src/Controller/RawphenotypesCreateProjectSpreadsheet.php @@ -0,0 +1,318 @@ +project_service = \Drupal::service('rawphenotypes.project_service'); + $this->term_service = \Drupal::service('rawphenotypes.term_service'); + $this->default_service = \Drupal::service('rawphenotypes.default_service'); + $this->user_service = \Drupal::service('rawphenotypes.user_service'); + } + + /** + * Construct spreadsheet. + */ + public function createFile($project_id = NULL) { + // 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 = $this->default_service::getTraitTypes(); + $cvterm = $this->project_service::getProjectTerms($project_id); + + // Only when project has headers. + if (count($cvterm) > 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 = $this->term_service->getUnitDataType('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) { + // Exclude the plant property from the set of headers. They are pre-inserted to the array + // of column headers passed to the spreadsheet writer. + if ($trait->type == $trait_type['type4']) continue; + + // Get the unit. + $u = $this->term_service->getTermUnit($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 = $this->term_service->getTermProperties($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, [$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) ? [$star . $m . ':', $chunk] : ['', $chunk]; + array_push($instructions_data, $ins_text); + } + } + } + + // Add extra new line. + array_push($instructions_data, ['' , '']); + } + + // 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'), + ) + ) + ); + + // Append user name into the filename. + // Current user logged in. + $user_id = \Drupal::currentUser()->id(); + $user = $this->user_service::getUser((int)$user_id, ['name']); + + $filename = 'datacollection_' . $project_id . '_' . str_replace(' ', '_', $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'; + } + + return [ + '#markup' => 'Download Data Collection Spreadsheet File' + ]; + } + } + } +} \ No newline at end of file diff --git a/src/Controller/RawphenotypesDragdropFileController.php b/src/Controller/RawphenotypesDragdropFileController.php new file mode 100644 index 0000000..cdfcb97 --- /dev/null +++ b/src/Controller/RawphenotypesDragdropFileController.php @@ -0,0 +1,54 @@ +files->get('file'); + $fileid = $request->get('fileId'); + + $name = $file->getClientOriginalName(); + $extn = $file->getClientOriginalExtension(); + + if ($extn == 'xlsx') { + $destination = \Drupal::service('file_system') + ->getTempDirectory(); + $file->move($destination, $fileid . '.' . $extn); + + $response = 'File is excel file'; + $error = FALSE; + } + else { + $response = 'File is not an excel file'; + $error = TRUE; + } + + return new JsonResponse(['response' => $response, 'error' => $error]); + } +} \ No newline at end of file diff --git a/src/Controller/RawphenotypesPageBackupController.php b/src/Controller/RawphenotypesPageBackupController.php new file mode 100644 index 0000000..5b89891 --- /dev/null +++ b/src/Controller/RawphenotypesPageBackupController.php @@ -0,0 +1,42 @@ +getForm('Drupal\rawphenotypes\Form\RawphenotypesBackupUploadForm', $project_id); + + // Drag and drop callback. + $to_Drupalsettings['rawphenotypes']['vars']['dragdropfile'] = Url::fromRoute('rawphenotypes.callback.dragdropfile', [], ['absolute' => TRUE]) + ->toString(); + + return [ + '#theme' => 'rawphenotypes_page_backup_template', + '#upload_form' => $form, + '#attached' => [ + 'library' => 'rawphenotypes/page-backup', + 'drupalSettings' => $to_Drupalsettings, + ], + ]; + } +} \ No newline at end of file diff --git a/src/Controller/RawphenotypesPageInstructionsController.php b/src/Controller/RawphenotypesPageInstructionsController.php new file mode 100644 index 0000000..291e9c5 --- /dev/null +++ b/src/Controller/RawphenotypesPageInstructionsController.php @@ -0,0 +1,50 @@ +getForm('Drupal\rawphenotypes\Form\RawphenotypesInstructionsSearchForm', $project_id); + + // Pass route to instructions page with the selected project in the + // select project (change project) as route parameter. + $instructions_route = Url::fromRoute('rawphenotypes.page_instructions', [], ['absolute' => TRUE]) + ->toString(); + $to_Drupalsettings['rawphenotypes']['vars']['instructions_route'] = $instructions_route; + + // Image gallery path to photo appendix. + $gallery_path = \Drupal::service('extension.list.module') + ->getPath('rawphenotypes'); + global $base_url; + $to_Drupalsettings['rawphenotypes']['vars']['image_gallery_path'] = $base_url. base_path() . $gallery_path . '/images/appendix/'; + + return [ + '#theme' => 'rawphenotypes_page_instructions_template', + '#search_form' => $form, + '#attached' => [ + 'library' => 'rawphenotypes/page-instructions', + 'drupalSettings' => $to_Drupalsettings, + ], + ]; + } +} \ No newline at end of file diff --git a/src/Form/RawphenotypesBackupUploadForm.php b/src/Form/RawphenotypesBackupUploadForm.php new file mode 100644 index 0000000..cecb214 --- /dev/null +++ b/src/Form/RawphenotypesBackupUploadForm.php @@ -0,0 +1,168 @@ +term_service = \Drupal::service('rawphenotypes.term_service'); + $this->default_service = \Drupal::service('rawphenotypes.default_service'); + $this->project_service = \Drupal::service('rawphenotypes.project_service'); + + $this->user_service = \Drupal::service('rawphenotypes.user_service'); + $this->current_user = \Drupal::currentUser() + ->id(); + } + + /** + * {@inheritdoc} + */ + public function getFormId() { + return 'rawphenotypes-form'; + } + + /** + * {@inheritdoc} + * Build form. + */ + public function buildForm(array $form, FormStateInterface $form_state, $project_id = NULL) { + // 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 = $this->user_service::getUserProjects($this->current_user); + + // No projects assined to the user. + if (count($all_project) < 1) { + return $form; + } + + // FORM. + // Construct backup file form. + // Array to hold project assigned to user. + $arr_my_project = $all_project; + + if (!isset($project_id) AND $project_id <= 0) { + if (count($arr_my_project) > 0) { + // Default to the only project when there is a single project assigned to user + // otherwise, let user select a project. + if (count($arr_my_project) > 1) { + // When there is more that 1 project, tell user to select a project. + $project_options = [0 => '---'] + $arr_my_project; + } + else { + // Else, default to the only project available. + $project_options = $arr_my_project; + } + + + // Yes, user has at least a project in the account. + $form['backup_sel_project'] = [ + '#type' => 'select', + '#title' => $this->t('Please select a project:'), + '#options' => $project_options, + '#default_value' => array_keys($project_options)[0], + '#id' => 'backup_sel_project' + ]; + + $form['backup_txt_description'] = [ + '#type' => 'textarea', + '#title' => $this->t('Describe spreadsheet file:'), + '#description' => t('Use this field to add comments, description or notes to the spreadsheet file that you want to backup'), + '#rows' => 2, + '#resizable' => FALSE, + ]; + + $form['field_file'] = [ + '#type' => 'file', + '#title' => $this->t('Drag your Microsoft Excel Spreadsheet file here or + Choose file'), + '#prefix' => '
     
    ', + '#suffix' => '
    ', + '#id' => 'field-file', + ]; + + // This will be set to the filename generated by the drop zone. + // Use this to reference back to locate the file when + // form is submitted. + $form['field_filename'] = [ + '#type' => 'hidden', + '#value' => '', + '#attributes' => ['id' => 'field-file-id'] + ]; + + $form['submit'] = [ + '#type' => 'submit', + '#value' => 'Backup', + '#id' => 'backup-submit-form' + ]; + } + else { + // User has no project in the account. + $form['message_invalid_project'] = [ + '#type' => 'inline_template', + '#theme' => 'theme-rawphenotypes-message', + '#data' => [ + 'message' => $this->t('You have no project in your account. Please contact the administrator of this website.'), + 'type' => 'warning' + ] + ]; + } + } + else { + // User is managing files in a project. + // Upload is disabled. Display a summary, + } + + return $form; + } + + /** + * {@inheritdoc} + * Save configuration. + */ + public function validateForm(array &$form, FormStateInterface $form_state) { + } + + /** + * {@inheritdoc} + * Save configuration. + */ + public function submitForm(array &$form, FormStateInterface $form_state) { + $get_field = $this->getRequest()->files->get('files', []); + $file_field = $get_field['file_field']; + var_dump($get_field); + die(); + } +} \ No newline at end of file diff --git a/src/Form/RawphenotypesConfigurationForm.php b/src/Form/RawphenotypesConfigurationForm.php new file mode 100644 index 0000000..39af21c --- /dev/null +++ b/src/Form/RawphenotypesConfigurationForm.php @@ -0,0 +1,149 @@ +config(static::SETTINGS); + + // Colour scheme. + $form['fieldset_colour_scheme'] = [ + '#type' => 'details', + '#title' => $this->t('Colour Scheme'), + '#open' => TRUE + ]; + + $form['fieldset_colour_scheme']['rawpheno_colour_scheme'] = [ + '#type' => 'textfield', + '#title' => $this->t('Enter colour:'), + '#default_value' => $config->get('rawpheno_colour_scheme'), + '#size' => 40, + '#maxlength' => 20, + '#required' => TRUE, + '#description' => $this->t('eg. HEX: #304356, blue'), + ]; + + // Headers and title. + $form['fieldset_page_title'] = [ + '#type' => 'details', + '#title' => $this->t('Page title'), + '#open' => TRUE, + ]; + // Rawdata page. + $form['fieldset_page_title']['rawpheno_rawdata_title'] = [ + '#type' => 'textfield', + '#title' => $this->t('Title of Rawdata page:'), + '#default_value' => $config->get('rawpheno_rawdata_title'), + '#size' => 120, + '#maxlength' => 220, + '#required' => TRUE, + ]; + + // Download Page. + $form['fieldset_page_title']['rawpheno_download_title'] = [ + '#type' => 'textfield', + '#title' => $this->t('Title of Download page:'), + '#default_value' => $config->get('rawpheno_download_title'), + '#size' => 120, + '#maxlength' => 220, + '#required' => TRUE, + ]; + + // Instructions page. + $form['fieldset_page_title']['rawpheno_instructions_title'] = [ + '#type' => 'textfield', + '#title' => $this->t('Title of Instructions page:'), + '#default_value' => $config->get('rawpheno_instructions_title'), + '#size' => 120, + '#maxlength' => 220, + '#required' => TRUE, + ]; + + // Upload page. + $form['fieldset_page_title']['rawpheno_upload_title'] = [ + '#type' => 'textfield', + '#title' => $this->t('Title of Upload page:'), + '#default_value' => $config->get('rawpheno_upload_title'), + '#size' => 120, + '#maxlength' => 220, + '#required' => TRUE, + ]; + + // Backup page. + $form['fieldset_page_title']['rawpheno_backup_title'] = [ + '#type' => 'textfield', + '#title' => $this->t('Title of Backup page:'), + '#default_value' => $config->get('rawpheno_backup_title'), + '#size' => 120, + '#maxlength' => 220, + '#required' => TRUE, + ]; + + $form['req'] = [ + '#type' => 'inline_template', + '#template' => ' * means field is required' + ]; + + return parent::buildForm($form, $form_state); + } + + /** + * {@inheritdoc} + * Save configuration. + */ + public function submitForm(array &$form, FormStateInterface $form_state) { + $this->configFactory->getEditable(static::SETTINGS) + ->set('rawpheno_colour_scheme', $form_state->getValue('rawpheno_colour_scheme')) + ->set('rawpheno_rawdata_title', $form_state->getValue('rawpheno_rawdata_title')) + ->set('rawpheno_download_title', $form_state->getValue('rawpheno_download_title')) + ->set('rawpheno_instructions_title', $form_state->getValue('rawpheno_instructions_title')) + ->set('rawpheno_upload_title', $form_state->getValue('rawpheno_upload_title')) + ->set('rawpheno_backup_title', $form_state->getValue('rawpheno_backup_title')) + ->set('rawpheno_rtransform_words', $form_state->getValue('rawpheno_rtransform_words')) + ->set('rawpheno_rtransform_characters', $form_state->getValue('rawpheno_rtransform_characters')) + ->set('rawpheno_rtransform_replace', $form_state->getValue('rawpheno_rtransform_replace')) + ->save(); + + return parent::submitForm($form, $form_state); + } +} \ No newline at end of file diff --git a/src/Form/RawphenotypesInstructionsSearchForm.php b/src/Form/RawphenotypesInstructionsSearchForm.php new file mode 100644 index 0000000..fdf2091 --- /dev/null +++ b/src/Form/RawphenotypesInstructionsSearchForm.php @@ -0,0 +1,177 @@ +term_service = \Drupal::service('rawphenotypes.term_service'); + $this->default_service = \Drupal::service('rawphenotypes.default_service'); + $this->project_service = \Drupal::service('rawphenotypes.project_service'); + + $this->user_service = \Drupal::service('rawphenotypes.user_service'); + $this->current_user = \Drupal::currentUser() + ->id(); + } + + /** + * {@inheritdoc} + */ + public function getFormId() { + return 'rawphenotypes_search_trait_form'; + } + + /** + * {@inheritdoc} + * Build form. + */ + public function buildForm(array $form, FormStateInterface $form_state, $project_id = NULL) { + // 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 = $this->user_service::getUserProjects($this->current_user); + + // 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'] = [ + '#type' => 'inline_template', + '#theme' => 'theme-rawphenotypes-message', + '#data' => [ + 'message' => $this->t('Project does not exist.'), + 'type' => 'warning' + ] + ]; + } + else { + // Project is valid. Null this. + $form['message_invalid_project'] = []; + } + } + 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]; + } + + // Make project id available to template. + $form['project_id'] = $project_id; + + $form['txt_search'] = [ + '#title' => '', + '#type' => 'textfield', + '#maxlength' => 65, + '#size' => 65, + '#default_value' => $this->t('Search Trait'), + '#autocomplete_route_name' => 'rawphenotypes.autocomplete.term', + '#autocomplete_route_parameters' => ['project_id' => $project_id], + '#theme_wrappers' => [], + ]; + + // Given a project id, construct the table required for each trait type set. + // All trait types, need to remove type plantproperty. + $trait_set = $this->default_service::getTraitTypes(); + $project_terms = $this->project_service::getProjectTerms($project_id); + + // Array to hold trait row. + $arr_cvterm = []; + + foreach($project_terms as $term) { + if ($term->type != $trait_set['type4']) { + // Exclude plantproperty types from official list + // of traits of the project. + $cvterm = $this->term_service->getTermProperties($term->project_cvterm_id); + $arr_cvterm[ $cvterm['type'] ][] = [ + Markup::create('
    ' . $cvterm['name'] . '
    '), + $cvterm['method'], + $cvterm['definition'] + ]; + } + } + + $arr_tbl_args['empty'] = $this->t('0 Column Header'); + $arr_tbl_args['header'] = [$this->t('Column Header/Trait'), $this->t('Collection Method'), $this->t('Definition')]; + + unset($trait_set['type4']); + + foreach($trait_set as $type) { + $arr_tbl_args['rows'] = isset($arr_cvterm[ $type ]) ? $arr_cvterm[ $type ] : []; + + if (count($arr_tbl_args['rows']) > 0) { + $form['tbl_project_headers_' . $type] = [ + '#type' => 'table', + '#title' => $this->t($type), + '#header' => $arr_tbl_args['header'], + '#rows' => $arr_tbl_args['rows'] + ]; + } + else { + $form['tbl_project_headers_' . $type] = FALSE; + } + } + + $form['active_project'] = [ + '#type' => 'inline_template', + '#template' => $all_project[ $project_id ], + + '#empty' => $arr_tbl_args['empty'] + ]; + + $form['sel_project'] = array( + '#type' => 'select', + '#options' => array(0 => 'Please select a project') + $all_project, + '#id' => 'rawpheno-ins-sel-project' + ); + + return $form; + } + + /** + * {@inheritdoc} + * Save configuration. + */ + public function validateForm(array &$form, FormStateInterface $form_state) { + } + + /** + * {@inheritdoc} + * Save configuration. + */ + public function submitForm(array &$form, FormStateInterface $form_state) { + } +} \ No newline at end of file diff --git a/src/Form/RawphenotypesManageAssetForm.php b/src/Form/RawphenotypesManageAssetForm.php new file mode 100644 index 0000000..619c69f --- /dev/null +++ b/src/Form/RawphenotypesManageAssetForm.php @@ -0,0 +1,1540 @@ +term_service = \Drupal::service('rawphenotypes.term_service'); + $this->user_service = \Drupal::service('rawphenotypes.user_service'); + $this->project_service = \Drupal::service('rawphenotypes.project_service'); + $this->envdata_service = \Drupal::service('rawphenotypes.envdata_service'); + + // Initialize default values for unit, trait_types, replicates and vocabularies. + $default_service = \Drupal::service('rawphenotypes.default_service'); + $this->vocabularies = $default_service::getDefaultValue('vocabularies'); + $this->trait_types = $default_service::getTraitTypes(); + $this->trait_reps = $default_service::getTraitReps(); + + // Save terms service object. + $this->trait_units = $this->term_service::getTermsByType('phenotype_measurement_units'); + } + + /** + * {@inheritdoc} + */ + public function getFormId() { + return 'rawphenotypes_assets'; + } + + /** + * {@inheritdoc} + * Build form. + */ + public function buildForm(array $form, FormStateInterface $form_state, $asset_id = NULL, $asset_type = NULL, $action = NULL) { + // Define valid action/command/operation per asset type. + $arr_valid = []; + $arr_valid['project']['command'] = ['manage']; + $arr_valid['project']['table'] = 'pheno_project_cvterm'; + $arr_valid['project']['id'] = 'project_id'; + + $arr_valid['header']['command'] = ['edit', 'delete']; + $arr_valid['header']['table'] = 'pheno_project_cvterm'; + $arr_valid['header']['id'] = 'project_cvterm_id'; + + $arr_valid['user']['command'] = ['delete']; + $arr_valid['user']['table'] = 'pheno_project_user'; + $arr_valid['user']['id'] = 'project_user_id'; + + $arr_valid['envdata']['command'] = ['delete']; + $arr_valid['envdata']['table'] = 'pheno_environment_data'; + $arr_valid['envdata']['id'] = 'environment_data_id'; + + // Ensure query strings are valid. The string length check ensure that Posgres + // will not throw Numeric value out of range PDOexception. + if (!isset($asset_id) || $asset_id <= 0 || strlen($asset_id) >= 10) { + // When project asset id is invalid. + $msg = $this->t('Not a valid project asset id number.'); + \Drupal::messenger()->addMessage($msg, 'error'); + } + else { + // Id is valid, test if asset type is valid. + if (array_key_exists($asset_type, $arr_valid)) { + // Valid asset type request. Check if the given asset id and asset type + // exists in the database before peforming any command. + $sql = sprintf("SELECT FROM {%s} WHERE %s = :asset_id LIMIT 1", + $arr_valid[ $asset_type ]['table'], $arr_valid[ $asset_type ]['id']); + + $args = [':asset_id' => (int)$asset_id]; + $prj_asset = \Drupal::database() + ->query($sql, $args); + + $prj_asset->allowRowCount = TRUE; + $this->asset_id = $asset_id; + + if ($prj_asset->rowCount() == 1) { + // Project asset exists in the database. Procede to command requested. + // Determine what to do with the project asset. + if (in_array($action, $arr_valid[ $asset_type ]['command'])) { + // Command is valid. Call function that will execute the command. + if ($asset_type == 'project') { + // Call project function. + $form = $this->projectAssetForm(); + } + elseif ($asset_type == 'header') { + // Before admin can modify or delete a column header, ensure that a header + // is not a plant property type header, not used by another project and the header has no data associated to it. + // In addition, Loding (scale 1-5) header is not editable. + $plant_property = $this->trait_types['type4']; + // Get header properties. + $header_asset = $this->term_service->getTermProperties($asset_id, 'full'); + + if ($header_asset['count_data'] > 0 && $action == 'delete') { + // Header has data. + $msg = $this->t('Cannot @action this entry. Column header has data associated to it.', ['@action' => $action]); + \Drupal::messenger()->addMessage($msg, 'error'); + } + else { + // Call header function. + $form = $this->manageHeaders($header_asset, $action); + } + } + else if($asset_type == 'user') { + // Ensure that user is not deleted when there is data and backup files associated. + $user_project_asset = $this->user_service::getUserAssets($asset_id); + + if ($user_project_asset['project_file_count'] > 0) { + // User is assigned to a project has backup files. + $msg = $this->t('Cannot @action this user. User has backed up files.', ['@action' => $action]); + \Drupal::messenger()->addMessage($msg, 'error'); + } + elseif ($user_project_asset['project_data_count'] > 0) { + // User is assigned to a project has has data. + $msg = $this->t('Cannot @action this user. User is assigned to a project that has data.', ['@action' => $action]); + \Drupal::messenger()->addMessage($msg, 'error'); + } + elseif ($user_project_asset['user_id'] < 2) { + // User is administrator. + $msg = $this->t('Cannot @action this user. User is the administrator of thi site.', ['@action' => $action]); + \Drupal::messenger()->addMessage($msg, 'error'); + } + else { + // Call user function. + $form = $this->manageUsers($user_project_asset, $action); + } + } + else if($asset_type == 'envdata') { + if ($asset_id > 0 && $action == 'delete') { + // Locate file. Ensure that file to be deleted exists. + $envdata_project_asset = $this->project_service::getProjectEnvDataAssets($asset_id); + + if ($envdata_project_asset) { + // Call user function. + $form = $this->manageEnvData($envdata_project_asset, $action); + } + else { + // User is administrator. + $msg = $this->t('Cannot @action Environment Data File. File does not exist.', ['@action' => $action]); + \Drupal::messenger()->addMessage($msg, 'error'); + } + } + } + } + else { + // Not a valid command. + $msg = $this->t('Not a valid request.'); + \Drupal::messenger()->addMessage($msg, 'error'); + } + } + else { + // Asset record not found. + $msg = $this->t('Project asset id number does not exist.'); + \Drupal::messenger()->addMessage($msg, 'error'); + } + } + else { + // Asset type does not exist. + $msg = $this->t('Not a valid project request.'); + \Drupal::messenger()->addMessage($msg, 'error'); + } + } + + // Attach library. + $form['#attached']['library'][] = 'rawphenotypes/style-admin'; + $form['#attached']['library'][] = 'rawphenotypes/script-admin'; + + return $form; + } + + + // MAIN FORM - Project asset summmary and forms (header, user, env data): + + + /** + * Main project asset page. Show all assets in tabs namely + * column headers, users and environment data. + * + * This page will also give user to add new items to individual + * project asset types. + * + * Property - project_id is set in the switch and will be used + * in this method. + */ + public function projectAssetForm() { + $form = []; + + // Get project. Asset id is the project id in this context. + $project_id = $this->asset_id; + $project = $this->project_service::getProject($project_id); + + // Trait Types. + $trait_types = $this->trait_types; + + $form['project_name'] = [ + '#type' => 'inline_template', + '#template' => '

    PROJECT: ' . $project->name . '

    ' + ]; + + // Construct page. Layout will be forms (add headers, user and env. data) followed + // by summary (in tables) of all assets organized into tabs. + + // FORMS: + // Layout forms. + + // Add new column headers. + // Fieldset add column header form. + $form['fieldset_trait'] = [ + '#type' => 'details', + '#title' => $this->t('Add column header'), + '#open' => FALSE, + ]; + + $default = [ + 'count_project' => 0, + 'count_data' => 0, + 'txt_id' => $project_id, + 'btn_trait_submit' => 'Add column header' + ]; + + // LOAD FORM. + $new_header_elements = $this->newHeaderForm($default); + $form['fieldset_trait'][] = $new_header_elements; + + // Add/Reuse headers. + // Fieldset suggest existing column headers. + $form['fieldset_existing_trait'] = [ + '#type' => 'details', + '#title' => $this->t('Add existing column headers'), + '#open' => FALSE, + ]; + + $default = [ + 'txt_id' => $project_id, + ]; + + // LOAD FORM. + $existing_header_elements = $this->existingHeaderForm($default); + $form['fieldset_existing_trait'][] = $existing_header_elements; + + // Add users (data collector accounts) to the project. + // Fieldset to suggest user to a project. + $form['fieldset_users'] = [ + '#type' => 'details', + '#title' => $this->t('Add users'), + '#open' => FALSE, + ]; + + // LOAD FORM. + $user_elements = $this->userForm(); + $form['fieldset_users'][] = $user_elements; + + // Manage environment data. + // Construct form to manage Environment data. + $form['fieldset_envdata'] = [ + '#type' => 'details', + '#title' => $this->t('Upload Environment Data File'), + '#open' => FALSE, + ]; + + $env_elements = $this->environmentDataForm(); + $form['fieldset_envdata'][] = $env_elements; + + + // TABLES: + // Layout summary tables. + + // List column headers in a project. + // Given a project id, select all column headers belonging to that project. + // Note the loding (scale 1-5) upright-lodged header is the only header using this + // format (all headers use the format: name (trait rep; unit), thus a flag indicating + // if header is such is required to disable edit option and prevent edit functionality + // from processing unfamiliar format. + $headers = $this->project_service::getProjectTerms($project_id); + $users = $this->project_service::getProjectActiveUsers($project_id); + $envdata = $this->project_service::getProjectEnvDataFiles($project_id); + + // Array to hold table headers. + $arr_headers = []; + + // Array to hold table rows. + $arr_rows = []; + + // Warn admin that project has no essential trait + foreach($headers as $h) { + if ($h->type == $trait_types['type1']) $count_essential++; + } + + if ($count_essential < 1) { + $form['message_no_essential'] = [ + '#type' => 'inline_template', + '#theme' => 'theme-rawphenotypes-message', + '#data' => [ + 'message' => $this->t('Project has no Essential Column Header.'), + 'type' => 'warning' + ] + ]; + } + + // Warn admin that project has no assigned user. + if (count($users) < 1) { + $form['message_no_user'] = [ + '#type' => 'inline_template', + '#theme' => 'theme-rawphenotypes-message', + '#data' => [ + 'message' => $this->t('Project has no active users.'), + 'type' => 'warning' + ] + ]; + } + + // Tabs to show either the project column headers table or project active users table. + // Add 1 to account for Name column header. + $form['nav_tabs'] = [ + '#type' => 'inline_template', + '#template' => ' + + ' + ]; + + + // Table column headers. + // Construct table that lists all column headers specific to a project. + // Options to edit and delete items give admin record management functionality. + // NOTE: Delete option will not physically delete a record in cvterm table. + // When a header is deleted, it is removed from the project only. + + $has_name = 0; + if (count($headers) > 0) { + $i = 0; + + foreach($headers as $h) { + // Get header information. + $header_asset = $this->term_service->getTermProperties($h->project_cvterm_id); + + $edit_cell = '-'; + if ($header_asset['name'] != 'Lodging (Scale: 1-5) upright - lodged') { + $link = Url::fromRoute('rawphenotypes.manage_project', ['asset_id' => $h->project_cvterm_id, 'asset_type' => 'header', 'action' => 'edit']); + $edit_cell = \Drupal::l($this->t('Edit'), $link); + } + + // Delete link. + $link = Url::fromRoute('rawphenotypes.manage_project', ['asset_id' => $h->project_cvterm_id, 'asset_type' => 'header', 'action' => 'delete'], ['attributes' => ['class' => ['link-del']]]); + $del_cell = \Drupal::l($this->t('Remove'), $link); + + // No edit and delete when column header is of type plantproperty. + if ($header_asset['type'] == $trait_types['type4'] || $header_asset['name'] == 'Planting Date (date)') { + // Add Name column header to the row array. + if ($has_name == 0 AND $header_asset['name'] != 'Planting Date (date)') { + $markup_name = Markup::create('

    Name

    '); + array_push($arr_rows, array((count($arr_rows) + 1), $markup_name, 'Name', 'PLANTPROPERTY', '-', '-')); + $has_name += 1; + $i++; + } + + $edit_cell = $del_cell = '-'; + } + + $class = ($header_asset['type'] == $trait_types['type1']) ? 'essential-trait' : 'non-essentialtrait'; + + if ($header_asset['type'] == $trait_types['type4']) { + // Row where trait is plantproperty. + $header_cell = '

    ' . $header_asset['name'] . '

    '; + $definition_cell = $header_asset['name']; + } + else { + // All traits. + $header_cell = '

    ' . $header_asset['name'] . '

    ' . self::markEmpty($header_asset['r_version']) . '

    '; + $definition_cell = '

    DEFINITION:
    ' . @self::markEmpty($header_asset['definition']) . '

    ' . + '

    COLLECTION METHOD:
    ' . @self::markEmpty($header_asset['method']) . '

    '; + + $header_cell = $header_cell; + $definition_cell = $definition_cell; + } + + // Register a row. + $arr_rows[] = [ + ($i+1), // Row counter. + Markup::create($header_cell), // Trait/header name. + Markup::create($definition_cell), // Trait/header definition. + [ + 'data' => strtoupper($header_asset['type']), + 'class' => [$class] + ], // Add class to cell to highlight text when trait is essential trait. + Markup::create($edit_cell), // A link to edit a trait. + Markup::create($del_cell) // A link to delete a trait. + ]; + + $i++; + } + } + + array_push($arr_headers, '-', $this->t('Column Header (unit)'), $this->t('Definition/Collection Method'), $this->t('Type'), $this->t('Edit'), $this->t('Remove')); + $form['tbl_project_headers'] = [ + '#type' => 'table', + '#title' => $this->t('Projects'), + '#header' => $arr_headers, + '#rows' => $arr_rows, + '#empty' => $this->t('No column headers in this project'), + '#prefix' => '
    ', + '#suffix' => '
    ', + '#attributes' => ['id' => 'tbl-project-headers'] + ]; + + // Table active users. + // Construct table that lists all active users to a project along with files + // associated to a user in a given project. + // Options to delete items give admin record management functionality. + // NOTE: Delete option will not physically delete a record in users table. + // When a user is deleted, it is removed from the project only. + + $arr_rows = $arr_headers = []; + if (count($users) > 0) { + $i = 0; + + foreach($users as $p_uid => $u) { + if (empty($u->name)) continue; + + // Delete link. + $link = Url::fromRoute('rawphenotypes.manage_project', ['asset_id' => $p_uid, 'asset_type' => 'user', 'action' => 'delete'], ['attributes' => ['class' => ['link-del']]]); + $del_cell = \Drupal::l($this->t('Remove'), $link); + + // Create a row of user with other relevant user info. + $cell_user_name = Markup::create('
    ' . $u->name . '
    '); + $cell_last_login = Markup::create('' . format_date($u->login) . ''); + + $arr_rows[] = [ + ($i+1), // Row number. + $cell_user_name, // Name of user. + $u->mail, // Email address. + $cell_last_login, // Date of last login. + $del_cell // Link to remove user from a project. + ]; + + $result_F = $this->user_service::getUserFiles($p_uid); + $my_files_count = count($result_F); + + $arr_header_F = ['File', 'Version', 'Archive', 'Notes', 'Validation Result']; + $arr_rows_F = []; + + if ($my_files_count > 0) { + foreach($result_F as $f) { + // File. + $link = Url::fromUri($f->uri, ['attributes' => ['target' => '_blank']]); + $cell_file = \Drupal::l($f->filename . ' (' . format_size($f->filesize) . ')', $link); + $cell_file .= Markup::create('
    Uploaded: ' . \Drupal::service('date.formatter')->format($f->timestamp) . ''); + + // Is archive? + $cell_is_archive = ($f->archive == 'y') ? 'Yes' : 'No'; + + // Notes. + $cell_notes = (empty($f->notes)) + ? ' ' + : Markup::create('
    ' . $f->notes . '
    '); + + $vr_tmp = str_replace(['#item: (passed)', '#item: (failed)'], + ['

    (PASSED)', '

    * (FAILED)'], + $f->validation_result) . '

    '; + + $vr_tmp = Markup::create($vr_tmp); + + $alert = ''; + if (($n = substr_count($vr_tmp, 'FAILED')) > 0) { + $alert = Markup::create('' . $n . ' Validation errors'); + } + + $cell_validation = $alert . '
    '. $vr_tmp .'
    '; + $cell_validation = Markup::create($cell_validation); + + // Create row showing backed up file with file description. + $arr_rows_F[] = [ + $cell_file, // Filename. + '#' . $f->version, // File version. + $cell_is_archive, // Indicate if file is archived or not. + $cell_notes, // Notes, comments to file. + $cell_validation // Validation result performed to the file. + ]; + } + } + + $file_table = [ + '#type' => 'table', + '#title' => 'Files', + '#header' => $arr_header_F, + '#rows' => $arr_rows_F, + '#empty' => $this->t('0 Files'), + '#attributes' => ['id' => 'tbl-my-files'] + ]; + + $link_show_folder = ($my_files_count > 0) + ? Markup::create('[Show]') + : ''; + + // Account status. + $acc_status = ($u->status == 1) ? 'Active' : 'Suspended'; + + $markup = sprintf(' + [ACCOUNT] Status: %s | Created: %s - %d Files uploaded %s +
    +
    %s
    +
    ', $acc_status, \Drupal::service('date.formatter')->format($u->created), $my_files_count, $link_show_folder, $file_table); + + // Create markup (container for table of files) to show user backed up files. + $arr_rows[] = [ + 'data' => [ + [ + 'data' => Markup::create($markup), + 'colspan' => 5, + 'class' => 'row-user-my-folder', + ] + ] + ]; + + $i++; + } + } + + array_push($arr_headers, '-', $this->t('Name'), $this->t('Email Address'), $this->t('Last Login'), $this->t('Remove')); + $form['tbl_project_users'] = [ + '#type' => 'table', + '#title' => $this->t('User'), + '#header' => $arr_headers, + '#rows' => $arr_rows, + '#empty' => $this->t('No users in this project'), + '#prefix' => '
    ', + '#suffix' => '
    ', + '#attributes' => ['id' => 'tbl-project-users'] + ]; + + // Table environment data. + $arr_rows = $arr_headers = []; + + if (count($envdata) > 0) { + array_push($arr_headers, '-', $this->t('File'), $this->t('Location'), $this->t('Year'), $this->t('Sequence No.'), $this->t('Delete')); + + foreach($envdata as $i => $env) { + $link = Url::fromUri($env->uri, ['attributes' => ['target' => '_blank']]); + $cell_file = \Drupal::l($env->filename . ' (' . $env->filesize . ')', $link); + $cell_file .= '
    Uploaded: ' . $env->created . ''; + $cell_file = Markup::create($cell_file); + + $link = Url::fromRoute('rawphenotypes.manage_project', ['asset_id' => $env->environment_data_id, 'asset_type' => 'envdata', 'action' => 'delete'], ['attributes' => ['class' => ['link-del']]]); + $cell_del = \Drupal::l($this->t('Delete'), $link); + + $arr_rows[] = [$i+1, $cell_file, $env->location, $env->year, '#' . $env->sequence_no, $cell_del]; + } + } + + $empty_table_title = $this->t('No environment data in this project.'); + $form['tbl_project_envdata'] = [ + '#type' => 'table', + '#title' => $this->t('Environment Data'), + '#header' => $arr_headers, + '#rows' => $arr_rows, + '#empty' => $empty_table_title, + '#prefix' => '
    ', + '#suffix' => '
    ', + '#attributes' => ['id' => 'tbl-project-envdata'] + ]; + + return $form; + } + + + // ASSET MANAGEMENT FORMS - new headers form, re-use header form, add user and evironment data: + + /** + * Add or update new header form. + */ + public function newHeaderForm($default) { + $a = $this->trait_types; + + // In this trait types array, remove the plant property option. + // Allow only to add essential, optional, contributed or subset column header types. + unset($a['type4']); + + // Type is no user contributed. Do not suggest contributed so user will have not option to add contributed + // trait using admin since contributed traits are traits derived from submitted spreadsheet in stage 01 : Describe New Trait. + if (!isset($default['sel_trait_type']) || $default['sel_trait_type'] != $a['type5']) { + unset($a['type5']); + } + + // Get the values and use it as both key and value. + $t = array_values($a); + $option_trait_type = array_combine($t, array_map('strtoupper', $t)); + + // Determine if the header has data or is used in another project. + if ($default['count_data'] > 0 || $default['count_project'] > 0) { + $form['fieldset_trait']['warning'] = [ + '#type' => 'inline_template', + '#theme' => 'theme-rawphenotypes-message', + '#data' => [ + 'message' => $this->t('This column header has data associated to it or is used in another project.'), + 'type' => 'warning' + ] + ]; + + $disabled = TRUE; + } + else { + $disabled = FALSE; + } + + // Project id the trait is in or the trait id number. + $form['txt_id'] = [ + '#type' => 'hidden', + '#value' => $default['txt_id'], + ]; + + // Exclusive to modifying trait, include the project id the trait is registered. + if (isset($default['prj_id']) && $default['prj_id'] > 0) { + $form['fieldset_trait']['prj_id'] = [ + '#type' => 'hidden', + '#value' => $default['prj_id'], + ]; + } + + // Trait name field. + $form['fieldset_trait']['txt_trait_name'] = [ + '#type' => 'textfield', + '#title' => $this->t('Name:'), + '#description' => $this->t('A Concise human-readable name or label for the column header'), + '#default_value' => isset($default['txt_trait_name']) ? $default['txt_trait_name'] : '', + '#disabled' => $disabled, + '#required' => TRUE, + ]; + + // Trait rep field. Default to none. + // Note: Trait Rep to call 1st, 2nd in unit is not final. + // Note: The list might change in the future. + $reps = $this->trait_reps; + $reps_val = []; + foreach($reps as $r) { + $reps_val[] = $r . ';'; + } + $trait_rep = ['' => 'None'] + array_combine($reps_val, $reps); + + $form['fieldset_trait']['sel_trait_rep'] = [ + '#type' => 'select', + '#title' => $this->t('Trait Rep/Stages:'), + '#options' => $trait_rep, + '#default_value' => isset($default['sel_trait_rep']) ? $default['sel_trait_rep'] : reset($trait_rep), + '#disabled' => $disabled, + ]; + + // Trait unit field. Default to none. + // Query available units in chado cvterm of type phenotype_measurement_unit. + $u = $this->trait_units; + $unit = []; + foreach($u as $i_unit => $value_unit) { + $unit[ $i_unit ] = $value_unit; + } + + // Default to text unit. + $unit_keys = array_keys($unit); + $textunit = array_search('text', $unit_keys); + $textunit_id = $unit_keys[ $textunit ]; + + $form['fieldset_trait']['sel_trait_unit'] = [ + '#type' => 'select', + '#title' => $this->t('Unit:'), + '#options' => $unit, + '#default_value' => isset($default['sel_trait_unit']) ? $default['sel_trait_unit'] : $textunit_id, + '#disabled' => $disabled, + ]; + + // R Friendly field. + $r_version = isset($default['txt_trait_rfriendly']) ? ' (' . $default['txt_trait_rfriendly'] . ')' : ''; + + $form['fieldset_trait']['txt_trait_rfriendly'] = [ + '#type' => 'textfield', + '#title' => $this->t('R Friendly:@r_version', ['@r_version' => $r_version]), + '#description' => $this->t('Leave this field blank to let system generate R Friendly version'), + '#default_value' => '', + '#disabled' => FALSE, + ]; + + if (isset($default['txt_trait_rfriendly']) && !empty($default['txt_trait_rfriendly'])) { + // When editing header, add this hidden field containing the original R version value prior to saving. + // When user decides to provide an alternative r version then save it, otherwise save the value of this + // hidden field. + $form['fieldset_trait']['txt_trait_rfriendly_val'] = [ + '#type' => 'hidden', + '#default_value' => $default['txt_trait_rfriendly'], + ]; + } + + // Trait definition field. + $form['fieldset_trait']['txt_trait_def'] = [ + '#type' => 'textarea', + '#title' => $this->t('Definition:'), + '#description' => $this->t('A human-readable text definition'), + '#required' => TRUE, + '#default_value' => isset($default['txt_trait_definition']) ? $default['txt_trait_definition'] : '', + '#disabled' => $disabled, + ]; + + // Describe method of collection field. + $form['fieldset_trait']['txt_trait_method'] = [ + '#type' => 'textarea', + '#title' => $this->t('Describe Method:'), + '#description' => $this->t('Describe the method used to collect this data if you used a scale, be specific'), + '#required' => TRUE, + '#default_value' => isset($default['txt_trait_method']) ? $default['txt_trait_method'] : '', + '#disabled' => $disabled, + ]; + + // Tell user about contributed trait when detected. Such trait is not included when generating data collection + // spreadsheet file - to incorporate it, please set the trait type to either essential or optional. + if (isset($a['type5']) && isset($default['sel_trait_type']) && $default['sel_trait_type'] == $a['type5']) { + // The header is contributed. Indicate to user as such. + $form['fieldset_trait']['warning_contributed'] = [ + '#type' => 'inline_template', + '#theme' => 'theme-rawphenotypes-message', + '#data' => [ + '#message' => $this->t('This column header is a user contributed trait and is not incorporated in + generating Data Collection Spreadsheet file for this Project. To include this trait, + set the trait type to Essential or Optional.'), + '#type' => 'warning' + ] + ]; + } + + // Trait is essential field. Default to unchecked. + $form['sel_trait_type'] = [ + '#type' => 'select', + '#title' => $this->t('Type:'), + '#description' => $this->t('Set the type to ESSENTIAL to ensure this header must exists in the spreadsheet file'), + '#options' => $option_trait_type, + '#default_value' => isset($default['sel_trait_type']) ? $default['sel_trait_type'] : reset($option_trait_type), + '#disabled' => FALSE, + ]; + + // Save trait button. + // #name key property is used to refer to later as a triggering element. + $btn_name = str_replace(' ', '-', strtolower($default['btn_trait_submit'])); + $form['btn_trait_subtmit'] = [ + '#type' => 'submit', + '#name' => $btn_name, + '#value' => $this->t('@save_or_add', ['@save_or_add' => $default['btn_trait_submit']]), + '#suffix' => ' * means field is required', + ]; + + return $form; + } + + /** + * Re-use existing header form. + */ + public function existingHeaderForm($default) { + // Table rows. + $arr_tblchkbox_rows = []; + // Get overall headers in the module and load only the headers + // not specific to the project. + $headers = $this->term_service->getTermsNotInProject($default['txt_id']); + + if (count($headers) > 0) { + foreach($headers as $h) { + $arr_tblchkbox_rows[$h->cvterm_id] = + [ + 'name' => $h->name, + // Add select box. + 'traittype' => + ['data' => + [ + '#type' => 'checkbox', + '#title' => 'Yes', + '#options' => [0, 1], + '#default_value' => 0, + '#name' => 'traittype-' . $h->cvterm_id + ] + ] + ]; + } + } + + // Table headers. + $arr_tblchkbox_headers = ['traittype' => $this->t('Is Essential?'), 'name' => $this->t('Name')]; + + // Checkboxes and table. + $form['tbl_existing_headers'] = [ + '#type' => 'tableselect', + '#header' => $arr_tblchkbox_headers, + '#options' => $arr_tblchkbox_rows, + '#js_select' => FALSE, + '#prefix' => '

    ' . + $this->t('The table below lists all column headers available in this module. + Please check the header(s) that you want to add to this project and click Add Selected Headers button.') . '

    +
    ', + '#suffix' => '

    ' . + $this->t('Check IS ESSENTIAL? to ensure the header must exists in the spreadsheet file') + . '

    ', + '#empty' => t('No column headers available'), + '#attributes' => [ + 'id' => 'tbl-existing-headers', + 'class' => [ + 'tableheader-processed' + ], + ], + '#theme_wrappers' => [] + ]; + + // Add submit button only there is any options available. + // #name key property is used to refer to later as a triggering element. + if (count($arr_tblchkbox_rows) > 0) { + $form['add_selected_trait'] = [ + '#type' => 'submit', + '#value' => $this->t('Add selected headers'), + '#name' => 'add-selected-headers', + '#submit' => ['::submitSelectedHeaders'], + '#validate' => ['::validateSelectedHeaders'], + '#limit_validation_errors' => [ + ['tbl_existing_headers'], + ['txt_id'] + ], + ]; + } + + return $form; + } + + /** + * Add data collector user to project. + */ + public function userForm() { + // Autocomplet search field. + // Add seach field to filter the list of name. + $form['txt_autocomplete_user'] = [ + '#title' => $this->t('Name :'), + '#type' => 'textfield', + '#maxlength' => 50, + '#size' => 130, + '#autocomplete_route_name' => 'rawphenotypes.autocomplete.user', + '#description' => $this->t('Type the name or username of the user'), + ]; + + $form['add_selected_user'] = [ + '#type' => 'submit', + '#value' => $this->t('Add user'), + '#submit' => ['::submitUser'], + '#validate' => ['::validateUser'], + '#limit_validation_errors' => [ + ['txt_autocomplete_user'], + ['txt_id'] + ], + ]; + + return $form; + } + + /** + * Add environment data asset form. + */ + public function environmentDataForm() { + // Environement Data File Upload. + $inline_wraper = [ + '#prefix' => '
    ', + '#suffix' => '
    ', + ]; + + // Add seach field to filter the list of name. + $form['file_env_file'] = [ + '#title' => $this->t('File :'), + '#type' => 'file', + ]; + + //$location_options = $project_service::getProjectLocations($project_id); + + $form['select_location'] = [ + '#title' => $this->t('Location :'), + '#type' => 'select', + '#options' => ['canada' => 'Canada'], + '#empty_option' => $this->t('- Select -'), + ]; + + $form['select_year'] = [ + '#title' => $this->t('Year :'), + '#type' => 'select', + '#options' => ['2001' => '2001'], + '#empty_option' => $this->t('- Select -'), + ]; + + $form['upload_env_file'] = [ + '#type' => 'submit', + '#submit' => ['::submitEnvData'], + '#validate' => ['::validateEnvData'], + '#limit_validation_errors' => [ + ['file_env_file'], + ['select_location'], + ['select_year'], + ['txt_id'] + ], + '#value' => $this->t('Upload file') + ]; + + return $form; + } + + + // FORM SUBMIT AND VALIDATE - submit and validate each form. + + + /** + * {@inheritdoc} + * Validate headers - this method is only for Add/Edit column headers only. + * Other form in this page uses a custom validate routine. + */ + public function validateForm(array &$form, FormStateInterface $form_state) { + // Get the submit button that triggered the submit action. Since this is a general hook_validate() + // of the entire form, limit the proces only to submit button in from add column header and save column header. + // When the submit action is determined, perform basic check of ensuring that user is not using cvterm name + // that is in the database already. + $btn_submit = $form_state->getTriggeringElement(); + $action = $btn_submit['#name']; + + // Submit buttons has property #name which will be used to determine which among the submit buttons + // was click and load corresponding action. + if ($action == 'add-column-header' || $action == 'save') { + // ADD AND SAVE NEW COLUMN HEADER. + $fld_name_trait_name = 'txt_trait_name'; + $fld_value_trait_name = trim($form_state->getValue($fld_name_trait_name)); + + if (strpbrk($fld_value_trait_name, '()')) { + // Test if user added parenthesis in the name. + $form_state->setErrorByName($fld_name_trait_name, + $this->t('Characters "(" and/or ")" found in column header name. Please remove these characters and try again.')); + } + else { + $asset_id = $form_state->getValue('txt_id'); + + $trait_name = $this->term_service->constructTerm( + [ + 'name' => $fld_value_trait_name, + 'rep' => trim($form_state->getValue('sel_trait_rep')), + 'unit' => trim($form_state->getValue('sel_trait_unit')) + ] + ); + + if ($action == 'add-column-header') { + // Add: + // Before adding the cvterm, ensure it is not present in cvterm table. + $fc = ['name' => $trait_name, 'cv_id' => ['name' => 'phenotype_measurement_types']]; + $found_cvterm = $this->term_service->getTerm($fc); + + if (isset($found_cvterm->cvterm_id) AND $found_cvterm->cvterm_id > 0) { + $form_state->setErrorByName($fld_name_trait_name, + $this->t('The column header name exists in the database. Please use a different name.')); + + $form_state->setErrorByName('sel_trait_rep'); + $form_state->setErrorByName('sel_trait_unit'); + } + } + else { + // Edit: + // When renaming a field in edit header, ensure that the new or modified name does not exist in cvterm table. + $sql = " + SELECT cvterm_id FROM chado.cv AS t1 INNER JOIN chado.cvterm AS t2 USING(cv_id) + WHERE t1.name = 'phenotype_measurement_types' AND t2.name = :name AND t2.cvterm_id <> :this_cvterm_id + "; + $args = [':name' => $trait_name, ':this_cvterm_id' => $asset_id]; + $found = \Drupal::database() + ->query($sql, $args); + + $found->allowRowCount = TRUE; + + if ($found->rowCount() > 0) { + $form_state->setErrorByName($fld_name_trait_name, + $this->t('The column header name exists in the database. Please use a different name.')); + + $form_state->setErrorByName('sel_trait_rep'); + $form_state->setErrorByName('sel_trait_unit'); + } + } + } + } + } + + /** + * {@inheritdoc} + * Submit headers - this method is only for Add/Edit column headers only. + * Other form in this page uses a custom submit routine. + */ + public function submitForm(array &$form, FormStateInterface $form_state) { + $vocabularies = $this->vocabularies; + + // Get the submit button that triggered the submit action. Since this is a general hook_submit() + // of the entire form, limit the proces only to submit button in from add column header and save column header. + $btn_submit = $form_state->getTriggeringElement(); + $action = $btn_submit['#name']; + + if ($action == 'add-column-header' || $action == 'save') { + // Holds the record id number. + $asset_id = $form_state->getValue('txt_id'); + + // Get field values. + $unit = $form_state->getValue('sel_trait_unit'); + + // Construct the trait name based on given name, rep and unit. + $trait_name = $this->term_service->constructTerm( + [ + 'name' => trim($form_state->getValue('txt_trait_name')), + 'rep' => trim($form_state->getValue('sel_trait_rep')), + 'unit' => trim($form_state->getValue('sel_trait_unit')) + ] + ); + + // R Friendly version of the Header name. + // When supplied, use it, otherwise transform the name to R friendly. + $rver = empty($form_state->getValue('txt_trait_rfriendly')) + ? $this->term_service->makeTermRCompatible($trait_name) + : $form_state->getValue('txt_trait_rfriendly'); + + $trait_def = trim($form_state->getValue('txt_trait_def')); + $col_method = trim($form_state->getValue('txt_trait_method')); + $trait_type = trim($form_state->getValue('sel_trait_type')); + + // These vocabulary terms are for relationship and property. + $cv_rver = $this->term_service->getTerm([ + 'name' => $vocabularies['cv_rver'], + 'cv_id' => ['name' => $vocabularies['cv_phenotypes']] + ]); + + $cv_desc = $this->term_service->getTerm([ + 'name' => $vocabularies['cv_desc'], + 'cv_id' => ['name' => $vocabularies['cv_phenotypes']] + ]); + + $cv_unit_rel = $this->term_service->getTerm([ + 'name' => $vocabularies['cv_unit'], + 'cv_id' => ['name' => $vocabularies['cv_phenotypes']] + ]); + + // This vocabulary term is for type. + $cv_unit = $this->term_service->getTerm(['name' => $vocabularies['cv_unit']], 'cv'); + + // Get the cvterm id of the unit selected. + $cvterm_unit = $this->term_service->getTerm(['name' => $unit, 'cv_id' => $cv_unit->cv_id]); + + if ($action == 'add-column-header') { + // Add: + // Uses project id. + // Insert cvterm. + $cvterm = $this->term_service->addTerm( + [ + 'id' => 'rawpheno_tripal:' . $trait_name, + 'name' => $trait_name, + 'cv_name' => 'phenotype_measurement_types', + 'definition' => $trait_def + ] + ); + + // Add a cvterm prop to store the R friendly version. + $this->term_service->saveTermProperty( + [ + 'cvterm_id' => $cvterm->cvterm_id, + 'type_id' => $cv_rver->cvterm_id, + 'value' => $rver, + 'rank' => 0 + ] + ); + + // Add a cvter prop to store the collection method. + $this->term_service->saveTermProperty( + [ + 'cvterm_id' => $cvterm->cvterm_id, + 'type_id' => $cv_desc->cvterm_id, + 'value' => $col_method, + 'rank' => 0 + ] + ); + + // Relate the cvterm to unit. + $this->term_service->saveTermRelationship( + [ + 'type_id' => $cv_unit_rel->cvterm_id, + 'object_id' => $cvterm->cvterm_id, + 'subject_id' => $cvterm_unit->cvterm_id, + ] + ); + + // Add entry to project cvterm table - add trait to project. + $this->term_service->saveTermToProject($cvterm->cvterm_id, $trait_type, $asset_id); + } + else { + // Update: + $this->term_service->updateTerm($asset_id, $trait_name, $trait_def); + + // Update rfriendly version in cvtermprop. + $type_rversion = $this->term_service->getTerm( + ['name' => 'phenotype_r_compatible_version'], + ['cv_id' => ['name' => 'rawphenotypes_terms']] + ); + + $this->term_service->updateTermProperty($asset_id, $type_rversion->cvterm_id, $rver); + + // Update method of collection. + // When updating, make sure term has a collection method entry in cvtermprop. + // If none, add an entry. + $has_method = $this->term_service->getTermProperty($asset_id); + + $type_method = $this->term_service->getTerm( + ['name' => 'phenotype_collection_method'], + ['cv_id' => ['name' => 'rawphenotypes_terms']] + ); + + if (empty($has_method)) { + // None property for method found, create one. + $this->term_service->saveTermProperty( + [ + 'cvterm_id' => $asset_id, + 'type_id' => $type_method->cvterm_id, + 'value' => $col_method, + 'rank' => 0 + ] + ); + } + else { + // Has method property, update record. + $this->term_service->updateTermProperty($asset_id, $type_method->cvterm_id, $col_method); + } + + // Update relationship term - unit. + $type_unit = $this->term_service->getTermByName('phenotype_measurement_units', 'cv'); + + // Get the unit id. + $unit_cvterm = $this->term_service->getTerm( + [ + 'name' => $unit, + 'cv_id' => $type_unit->cv_id + ] + ); + + $this->term_service->updateTermRelationship($asset_id, $unit_cvterm->cvterm_id); + + // Update trait type. + // Project id the trait is in. + $project_id = $form_state->getValue('prj_id'); + $this->term_service->updateTermProperty($asset_id, 0, $trait_type, $project_id, FALSE); + + // Inform user of the updated header. + \Drupal::messenger()->addStatus($this->t('You have successfully updated a column header in this project.')); + } + } + } + + /** + * Custom validate - validate selected column headers in Form fieldset #2 - Add existing + * column headers. + */ + public function validateSelectedHeaders(array &$form, FormStateInterface $form_state) { + // ADD/REUSE EXISTING COLUMN HEADER. + $btn_submit = $form_state->getTriggeringElement(); + $action = $btn_submit['#name']; + + if ($action == 'add-selected-headers') { + $selected_headers = $form_state->getValue('tbl_existing_headers'); + + if (count(array_filter($selected_headers)) <= 0) { + $form_state->setErrorByName('tbl_existing_headers', $this->t('No column headers selected.')); + } + } + } + + /** + * Custom submit - submit selected column headers form in Form fieldset #2 - Add existing + * column headers. + */ + public function submitSelectedHeaders(array &$form, FormStateInterface $form_state) { + // ADD/REUSE EXISTING COLUMN HEADER. + // Uses project id. + $project_id = $form_state->getValue('txt_id'); + // All Types. + $types = $this->trait_types; + + // All the headers option - this will be filtered to just + // the items that were selected. + $selected_headers = $form_state->getValue('tbl_existing_headers'); + // Is essential option - this will contain all the option to + // set header as essential or not. + $selected_headers_type = $form_state->getUserInput(); + + // Only headers that were selected then inspect the is essential + // field to determine the type. + foreach(array_filter($selected_headers) as $m) { + $key = 'traittype-' . $m; + $trait_type = ($selected_headers_type[ $key ]) ? $types['type1'] : $types['type2']; + + $cvterm_id = (int)$m; + $this->term_service->saveTermToProject($cvterm_id, $trait_type, $project_id); + } + + // Inform user of the newly added headers. + \Drupal::messenger()->addStatus($this->t('You have successfully added a column header to this project.')); + } + + /** + * Custom validate - validate add user to project. + */ + public function validateUser(array &$form, FormStateInterface $form_state) { + $project_id = $form_state->getValue('txt_id'); + $user = $form_state->getValue('txt_autocomplete_user'); + + if (empty($user)) { + $form_state->setErrorByName('txt_autocomplete_user', $this->t('No user name supplied in the field.')); + } + else { + // Search user + $user_id = $this->user_service::getUserIdByUsername($user); + + if (empty($user_id)) { + $form_state->setErrorByName('txt_autocomplete_user', $this->t('User does not exist.')); + } + else { + // Test if user was added twice in the same project. + $project_users = $this->project_service::getProjectActiveUsers($project_id); + + foreach($project_users as $u) { + if ($u->uid == $user_id) { + $form_state->setErrorByName('txt_autocomplete_user', + $this->t('User is already active in this project and cannot be added again.')); + + break; + } + } + } + } + } + + /** + * Custom submit - submit add user form. + */ + public function submitUser(array &$form, FormStateInterface $form_state) { + // Uses project id + $project_id = $form_state->getValue('txt_id'); + $user = $form_state->getValue('txt_autocomplete_user'); + $user_id = $this->user_service::getUserIdByUsername($user); + + $this->project_service::addUserToProject($user_id, $project_id); + \Drupal::messenger()->addStatus($this->t('You have successfully added a user to this project.')); + } + + /** + * Custom validate - add environment data file. + */ + public function validateEnvData(array &$form, FormStateInterface $form_state) { + // Environment Data File: + $field_value = $this->getRequest()->files->get('files', []); + + // File field: + if ($field_value['file_env_file'] == NULL) { + $form_state->setErrorByName('file_env_file', $this->t('Environment Data File is empty. Please select a file and try again.')); + } + + // Location: + if (empty($form_state->getValue('select_location'))) { + $form_state->setErrorByName('select_location', $this->t('Location field is empty. Please select an option and try again.')); + } + + // Year: + if (empty($form_state->getValue('select_year'))) { + $form_state->setErrorByName('select_year', $this->t('Year field is empty. Please select an option and try again.')); + } + } + + /** + * Custom submit - add environment data file. + */ + public function submitEnvData(array &$form, FormStateInterface $form_state) { + $project_id = $form_state->getValue('txt_id'); + $location = $form_state->getValue('select_location'); + $year = $form_state->getValue('select_year'); + + if ($project_id && $location && $year) { + $get_field = $this->getRequest()->files->get('files', []); + $file_field = $get_field['file_env_file']; + + $destination = \Drupal::config('system.file') + ->get('default_scheme') . '://rawphenotypes_env_data'; + + \Drupal::service('file_system') + ->prepareDirectory($destination, FILE_MODIFY_PERMISSIONS); + + $env_filename = $file_field->getClientOriginalName(); + $env_file_ext = $file_field->getClientOriginalExtension(); + + $seq_no = $this->envdata_service::getSequenceNumber($project_id, $location, $year); + $u_no = date('ymdis'); + + $new_filename = str_replace(array(' ', '-', ','), '_', $location) . '_' . $year . '_' . $u_no . '_environment_data.' . $env_file_ext; + $new_filename = strtolower($new_filename); + + $file_field->move($destination, $new_filename); + + // Create a Drupal 8 file entity. + $file_create = File::create([ + 'filename' => $new_filename, + 'uri' => $destination . '/' . $new_filename, + 'status' => 1 + ]); + + $file_create->save(); + + $fid = $file_create->id(); + + $this->envdata_service::saveEnvData( + [ + 'project_id' => $project_id, + 'fid' => $fid, + 'location' => $location, + 'year' => $year, + 'sequence_no' => $seq_no + ] + ); + + \Drupal::messenger()->addStatus($this->t('You have successfully uploaded environment data file to this project.')); + } + } + + + // HANDLE REQUESTED ACTION - delete, remove and view. + + + /** + * Handle action requested to headers asset. + * + * Property - trait id is set in the switch and will be used + * in this method. + */ + public function manageHeaders($header_asset, $action) { + $form['project_name'] = [ + '#type' => 'inline_template', + '#template' => '

    Manage Project Assets / Update Header

    ' + ]; + + if ($action == 'edit') { + if ($header_asset['name'] == 'Lodging (Scale: 1-5) upright - lodged' || $header_asset['name'] == 'Comments') { + // Is lodging header. + $goback = Markup::create(' Go back'); + drupal_set_message($this->t('Cannot edit @name.', array('@name' => $header_asset['name'])) . $goback, 'error'); + } + else { + // Other headers. + // CONSTRUCT EDIT FORM. + + // FORM + // Add a link to allow administrator to go back to the list of traits in a project. + $link = Url::fromRoute('rawphenotypes.projects', []); + $link_project = \Drupal::l($this->t('Go back to projects tables'), $link); + + $link = Url::fromRoute('rawphenotypes.manage_project', ['asset_id' => $header_asset['in_project_id'], 'asset_type' => 'project', 'action' => 'manage']); + $link_manage_asset = \Drupal::l($this->t('Go back to project column headers table'), $link); + + $form['back_link'] = [ + '#type' => 'inline_template', + '#template' => Markup::create($link_project . ' | ' . $link_manage_asset), + ]; + + $form['fieldset_trait'] = [ + '#type' => 'details', + '#title' => $this->t('Eidt Column Header:'), + '#open' => TRUE, + ]; + + // Trait name field. + // Extract the trait rep value, trait unit and the trait name. + // NOTE: this can only process header following the format: trait name (trait rep; unit) + $trait_rep = $trait_unit = ''; + // Extract the text value inside the parenthesis (unit part). + $t = preg_match("/.*\(([^)]*)\)/", $header_asset['name'], $match); + $u = (isset($match[1])) ? $match[1] : ''; + + // Extract information in a unit only when there is a unit in the first place. + // Get the Trait rep (eg R1, R7, 1st) and measurement unit (eg cm, days) values. + $reps = $this->trait_reps; + + if ($t) { + // Split the information and see if either trait rep or unit is present. + $p = explode(' ', $u); + if (count($p) > 1) { + // Has trait rep and unit. + list($trait_rep, $trait_unit) = $p; + } + else { + if (in_array(trim($u, ';'), $reps)) { + // Just the rep no unit. + $trait_rep = $u; + $trait_unit = ''; + } + else { + // Just the unit no trait rep. + $trait_rep = ''; + $trait_unit = $u; + } + } + } + + // When niether is present, use the default value of the trait rep and unit (null). + // Trait name without the unit part. + $trait_name = preg_replace('/\(.*/', ' ', $header_asset['name']); + + // Add form elements. + // Include the project id when modifying a trait header. + $default_values = [ + 'count_project' => $header_asset['count_project'], + 'count_data' => $header_asset['count_data'], + 'txt_id' => $header_asset['cvterm_id'], + 'prj_id' => $header_asset['in_project_id'], + 'txt_trait_name' => trim($trait_name), + 'sel_trait_rep' => trim($trait_rep), + 'sel_trait_unit' => trim($trait_unit), + 'txt_trait_definition' => $header_asset['definition'], + 'txt_trait_method' => $header_asset['method'], + 'txt_trait_rfriendly' => $header_asset['r_version'], + 'sel_trait_type' => $header_asset['type'], + 'btn_trait_submit' => 'Save' + ]; + + $form['fieldset_trait'][] = $this->newHeaderForm($default_values); + + // TABLE + // Construct a table, as a summary, showing information about the header. + $markup = '

    SUMMARY: ' . $header_asset['name'] . ' : PROJECT: ' . $header_asset['in_project_name'] . '

    '; + $form['header'] = [ + '#type' => 'inline_template', + '#template' => $markup + ]; + + $header_cell = Markup::create($header_asset['name'] . '

    ' . self::markEmpty($header_asset['r_version']) . '

    '); + $arr_headers = ['-', $this->t('Column Header (unit)'), $this->t('Collection Method'), $this->t('Definition'), $this->t('Type')]; + $arr_rows[] = [ + 1, + Markup::create($header_cell), + $header_asset['method'], + $header_asset['definition'], + strtoupper($header_asset['type']) + ]; + + $form['tbl_project_headers'] = [ + '#type' => 'table', + '#title' => $this->t('Header summary'), + '#header' => $arr_headers, + '#rows' => $arr_rows, + '#empty' => $this->t('Header summary not found'), + '#attributes' => ['id' => 'tbl_header_summary'] + ]; + } + } + elseif ($action == 'delete') { + $this->term_service->removeTermFromProject($header_asset['cvterm_id'], $header_asset['in_project_id']); + + $link = Url::fromRoute('rawphenotypes.manage_project', + ['asset_id' => $header_asset['in_project_id'], 'asset_type' => 'project', 'action' => 'manage']); + + $redirect = new RedirectResponse($link->toString()); + $redirect->send(); + + return null; + } + + return $form; + } + + /** + * Handle action requested pertaining to project user asset. + */ + public function manageUsers($user_asset, $action) { + if ($action == 'delete') { + $this->user_service::removeUserFromProject($user_asset['project_user_id']); + + $link = Url::fromRoute('rawphenotypes.manage_project', + ['asset_id' => $user_asset['project_id'], 'asset_type' => 'project', 'action' => 'manage']); + + $redirect = new RedirectResponse($link->toString()); + $redirect->send(); + + return null; + } + } + + /** + * Handle action requested pertaining to environment data asset. + */ + public function manageEnvData($envdata_project_asset, $action) { + if ($action == 'delete') { + $this->envdata_service::deleteEnvData($envdata_project_asset); + + $link = Url::fromRoute('rawphenotypes.manage_project', + ['asset_id' => $envdata_project_asset['project_id'], 'asset_type' => 'project', 'action' => 'manage']); + + $redirect = new RedirectResponse($link->toString()); + $redirect->send(); + + return null; + } + } + + + // HELPER FUNCTION. + + + /** + * Function set empty value. + */ + public static function markEmpty($val) { + return $val ?? '-'; + } +} \ No newline at end of file diff --git a/src/Form/RawphenotypesManageProjectForm.php b/src/Form/RawphenotypesManageProjectForm.php new file mode 100644 index 0000000..5063218 --- /dev/null +++ b/src/Form/RawphenotypesManageProjectForm.php @@ -0,0 +1,188 @@ +term_service = \Drupal::service('rawphenotypes.term_service'); + $this->project_service = \Drupal::service('rawphenotypes.project_service'); + $this->default_service = \Drupal::service('rawphenotypes.default_service'); + } + + /** + * {@inheritdoc} + */ + public function getFormId() { + return 'rawphenotypes_projects'; + } + + /** + * {@inheritdoc} + * Build form. + */ + public function buildForm(array $form, FormStateInterface $form_state) { + // Attach library. + $form['#attached']['library'][] = 'rawphenotypes/style-admin'; + $new_projects = $this->project_service::getNewProjects(); + + $form['fieldset_sel_project'] = [ + '#type' => 'details', + '#title' => $this->t('Create a New Project:'), + '#open' => TRUE, + ]; + + if ($new_projects) { + $options = array('0' => '---') + $new_projects; + + $form['fieldset_sel_project']['sel_project'] = [ + '#type' => 'select', + '#title' => $this->t('Please select a project:'), + '#options' => $options, + '#default_value' => array_keys($options)[0], + '#id' => 'admin-sel-project', + '#description' => $this->t('Please note that column headers Plot, Entry, Rep, Name, Location and Planting Date (date) are added to project by default.'), + '#theme_wrappers' => [] + ]; + + $form['fieldset_sel_project']['add_project'] = [ + '#type' => 'submit', + '#value' => $this->t('Create Project'), + '#id' => 'admin-add-project', + ]; + } + else { + $form['fieldset_sel_project']['no_project'] = [ + '#type' => 'inline_template', + '#theme' => 'theme-rawphenotypes-message', + '#data' => [ + 'message' => $this->t('No projects available.'), + 'type' => 'warning' + ] + ]; + } + + // TABLE + // Construct table that lists all active projects in this module. + // The table data consists of project name and summary of column headers and active users. + + // Get trait types array. + $trait_type = $this->default_service::getTraitTypes(); + + // Array to hold table headers. + $arr_headers = []; + + // Array to hold table rows. + $arr_rows = []; + + // Get all active projects - projects added to this module. + $projects = $this->project_service::getActiveProjects(); + + // Page title. + $form['all_project_info'] = [ + '#type' => 'inline_template', + '#template' => '

    ' . count($projects) . ' Project(s)

    ' + ]; + + if (count($projects) > 0) { + $i = 0; + + foreach($projects as $p) { + $link = Url::fromRoute('rawphenotypes.manage_project', ['asset_id' => $p->project_id, 'asset_type' => 'project', 'action' => 'manage']); + $view_cell = \Drupal::l($this->t('View'), $link); + $name_cell = \Drupal::l($this->t($p->name), $link); + + // Warn user that the project has no essential trait. Project must have at least 1 essential trait + // list of column headers before it can process and store phenotypic data. The same for user. + $warn_header = ($p->essential_count > 0) + ? '' + : 'Project has no essential column header'; + + $warn_user = ($p->user_count > 0) + ? '' + : 'Project has no assigned user'; + + + // + 1 to account for Name column header. + array_push($arr_rows, [($i+1), $name_cell, ($p->header_count + 1), $p->essential_count . $warn_header, $p->user_count . $warn_user, $view_cell]); + $i++; + } + } + + array_push($arr_headers, '-', $this->t('Project Name'), $this->t('Column Headers'), $this->t('Essential Headers'), $this->t('Active User'), $this->t('View')); + + $form['tbl_projects'] = [ + '#type' => 'table', + '#title' => $this->t('Projects'), + '#header' => $arr_headers, + '#rows' => $arr_rows, + '#empty' => $this->t('No project available') + ]; + + return $form; + } + + /** + * {@inheritdoc} + * Save configuration. + */ + public function validateForm(array &$form, FormStateInterface $form_state) { + $project_id = $form_state->getValue('sel_project'); + + if ($project_id <= 0) { + $form_state->setErrorByName('sel_project', $this->t('No project selected. Please select a project and try again')); + } + } + + /** + * {@inheritdoc} + * Save configuration. + */ + public function submitForm(array &$form, FormStateInterface $form_state) { + // When a new project is created, Plant property column headers are + // added to a project by default. The following are the plant property headers: + // Location, Plot Rep and Entry. + + // Plus and Planting Date and make it essential trait. Planting date is important + // as it is used when generating the heatmap. + $project_id = $form_state->getValue('sel_project'); + + // Get trait types array. + $trait_type = $this->default_service::getTraitTypes(); + + // Get Planting Date cvterm id. + $pd = ['name' => 'Planting Date (date)', 'cv_id' => ['name' => 'phenotype_measurement_types']]; + $planting_date = $this->term_service->getTerm($pd); + $this->term_service->saveTermToProject($planting_date->cvterm_id, $trait_type['type1'], $project_id); + + // Query all plant property types column headers in chado.cvterm table. + // Insert to project. + $plantprop = $this->term_service->getPlantPropertyTerm(); + foreach($plantprop as $prop) { + $this->term_service->saveTermToProject($prop->cvterm_id, $trait_type['type4'], $project_id); + } + + $this->project_service::addUserToProject(\Drupal::currentUser()->id(), $project_id); + // If the user is not the superadmin, add superadmin as well. + if (\Drupal::currentUser()->id() != 1) { + $this->project_service::addUserToProject(1, $project_id); + } + } +} \ No newline at end of file diff --git a/src/Form/RawphenotypesRTransformForm.php b/src/Form/RawphenotypesRTransformForm.php new file mode 100644 index 0000000..20b36e2 --- /dev/null +++ b/src/Form/RawphenotypesRTransformForm.php @@ -0,0 +1,100 @@ +config(static::SETTINGS); + + // Fieldset to contain R-Friendly transformation rules form. + $form['fieldset_rules'] = [ + '#type' => 'details', + '#title' => $this->t('R-Friendly Transformation Rules'), + '#open' => TRUE, + ]; + + $form['fieldset_rules']['rawpheno_rtransform_words'] = [ + '#type' => 'textarea', + '#title' => $this->t('List of words to remove:'), + '#default_value' => $config->get('rawpheno_rtransform_words'), + '#description' => $this->t('Separate words with commas'), + '#required' => TRUE, + ]; + + $form['fieldset_rules']['rawpheno_rtransform_characters'] = [ + '#type' => 'textarea', + '#title' => $this->t('List of special characters to remove:'), + '#default_value' => $config->get('rawpheno_rtransform_characters'), + '#description' => $this->t('Separate characters with commas'), + '#required' => TRUE, + ]; + + $form['fieldset_rules']['rawpheno_rtransform_replace'] = [ + '#type' => 'textarea', + '#title' => $this->t('Match and replace (Match Character = Replacement):'), + '#default_value' => $config->get('rawpheno_rtransform_replace'), + '#description' => $this->t('Separate match and replace pairs with commas'), + '#required' => TRUE, + ]; + + $form['req'] = [ + '#type' => 'inline_template', + '#template' => ' * means field is required' + ]; + + return parent::buildForm($form, $form_state); + } + + /** + * {@inheritdoc} + * Save configuration. + */ + public function submitForm(array &$form, FormStateInterface $form_state) { + $this->configFactory->getEditable(static::SETTINGS) + ->set('rawpheno_rtransform_words', $form_state->getValue('rawpheno_rtransform_words')) + ->set('rawpheno_rtransform_characters', $form_state->getValue('rawpheno_rtransform_characters')) + ->set('rawpheno_rtransform_replace', $form_state->getValue('rawpheno_rtransform_replace')) + ->save(); + + return parent::submitForm($form, $form_state); + } +} \ No newline at end of file diff --git a/src/Services/RawphenotypesDefaultValueService.php b/src/Services/RawphenotypesDefaultValueService.php new file mode 100644 index 0000000..badc58a --- /dev/null +++ b/src/Services/RawphenotypesDefaultValueService.php @@ -0,0 +1,530 @@ + '#304356', + 'rawdata' => 'Phenotypic data available', + 'download' => 'Select locations and traits that you want to download', + 'instructions' => 'Standard Phenotyping Procedure', + 'upload' => 'Drag and Drop phenotypic data collection spreadsheet', + 'backup' => 'My files', + 'r-words' => 'of,to,have,on,at', + 'r-chars' => '(,),/,-,:,;,%', + 'r-replace' => '# = num,/ = div,? = unsure,- = to', + ]; + break; + + case 'scales': + // Scale equivalent for scale units. + $default = [ + 'Lodging (Scale: 1-5) upright - lodged' => [ + 1 => 'Vertical/upright', + 2 => 'Leaning', + 3 => 'Most plants at 45 degrees angle', + 4 => 'All plants 10-45 degrees from ground', + 5 => 'Most plants flat/prostrate', + ] + ]; + break; + + case 'variables': + // Configuration variable names. + $default = [ + 'rawpheno_colour_scheme', + 'rawpheno_rawdata_title', + 'rawpheno_download_title', + 'rawpheno_instructions_title', + 'rawpheno_upload_title', + 'rawpheno_backup_title', + 'rawpheno_rtransform_words', + 'rawpheno_rtransform_characters', + 'rawpheno_rtransform_replace', + ]; + break; + + case 'vocabularies': + // Controlled vocabularies. + $default = [ + // Use this cv to create relationships + // ie. term-unit, unit-method. + 'cv_phenotypes' => 'rawphenotypes_terms', + 'cv_prop' => 'phenotype_plant_property_types', + 'cv_unit' => 'phenotype_measurement_units', + 'cv_type' => 'phenotype_measurement_types', + 'cv_rver' => 'phenotype_r_compatible_version', + 'cv_desc' => 'phenotype_collection_method' + ]; + break; + + case 'units': + // Units available in the spreadsheet. + $default = [ + 'date' => 'Date', + 'count' => 'Count', + 'days' => 'Days', + 'cm' => 'Centimeters', + 'scale' => 'Scale: 1-5', + 'g' => 'Grams (g)', + 'text' => 'Alphanumeric', + 'y/n/?' => 'Yes, No or ? - Not sure', + ]; + break; + } + + return $default; + } + + /** + * Default column headers used by default project. The whole set is defined + * in the array below and a subset can be requested. + * Subset keys: + * - phenotyping: request standard phenotyping headers. + * - required: request headers that must contain a value. + * - expected: request headers expected to be measured. + * - plantprop: request plant property headers (name, entry). + * - milti-trial: request headers that require multiple (1st and 2nd) trials. + * - essential: request essential headers (must exists). + * - plot: request plot specific headers (plot, location) + * - subset: request subset headers. + * + * @param $type + * A string containing a description of column header set required. Listed above. + * + * @return + * An array of column headers based on the type of set requested. + */ + public static function getTraits($type) { + // List of traits/measurements from AGILE-PhenotypeDataCollection-v5.xlsx. + // in AGILE project. + // TRAIT/MEASUREMENT ---------------------------------------- INDEX + // Order is the same order as in the spreadsheet file. + // Note: Subset traits: 24, 25 and 26 are Hidden Column. + // Traits with Second trial eg. 1st; cm, 2nd; cm + $arr_headers = [ + 'Plot', //0 + 'Entry', //1 + 'Name', //2 + 'Rep', //3 + 'Location', //4 + 'Planting Date (date)', //5 + '# of Seeds Planted (count)', //6 + 'Days to Emergence (days)', //7 + '# of Emerged Plants (count)', //8 + 'Days till 10% of Plants have Elongated Tendrils (days)', //9 + 'Days till 10% of Plants have One Open Flower (R1; days)', //10 + '# Nodes on Primary Stem at R1 (1st; count)', //11 + '# Nodes on Primary Stem at R1 (2nd; count)', //12 + 'Days till 10% of Plants have Pods (R3; days)', //13 + 'Days till 10% of Plants have fully Swollen Pods (R5; days)', //14 + 'Days till 10% of Plants have 1/2 Pods Mature (R7; days)', //15 + 'R7 Traits: Lowest Pod Height (1st; cm)', //16 + 'R7 Traits: Lowest Pod Height (2nd; cm)', //17 + 'R7 Traits: Canopy Height (1st; cm)', //18 + 'R7 Traits: Canopy Height (2nd; cm)', //19 + 'Days till Harvest (days)', //20 + 'Diseases Present (y/n/?)', //21 + 'Disease-specific Comments', //22 + 'Lodging (Scale: 1-5) upright - lodged', //23 + 'Subset Traits: # Peduncles (count)', //24 + 'Subset Traits: # Pods (count)', //25 + 'Subset Traits: # Seeds (count)', //26 + 'Straw Biomass (g)', //27 + 'Total Seed Mass (g)', //28 + 'Total # of Seeds (count)', //29 + '100 Seed Mass (g)', //30 + 'Comments' //31 + ]; + + // GET SUBSET. + // Determine the type of request. + switch($type) { + case 'phenotyping': + // List of column headers used in standard phenotyping instructions page. + // Used in: Instructions page. + $type_id = [5, 7, 8, 9, 10, 11, 14, 15, 16, 18, 20, 21, 23, 24, 25, 26, 27, 28, 29, 30]; + break; + + case 'required': + // List of required column headers - must have a value. + // Used in: Upload Data - validate spreadsheet. + $type_id = [0, 1, 2, 3, 4]; + break; + + case 'expected': + // List of column headers ids expected to be present in spreadsheet. + // Used in: Upload Data - validate spreadsheet. + // .install file of this module. + $type_id = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, + 22, 23, 24, 25, 26, 27, 28, 29, 30, 31]; + break; + + case 'plantprop': + // List of required column headers excluding name - Plant Prop Traits. + // Used in: Upload Data - Save spreadsheet. + $type_id = [0, 1, 3, 4]; + break; + + case 'multi-trial': + // List of column headers with first and secondary try. + // Used in: .install file and Upload Data - save spreadsheet. + $type_id = [11, 12, 16, 17, 18, 19]; + break; + + case 'essential': + // List of essential column headers. + // Used in: .install file. + $type_id = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 15, 20, 21]; + break; + + case 'plot': + // Plot column headers + // Used in: upload file to determine if to re-use plant_id or insert a new row. + // plot, rep, year (planting date), location. + $type_id = [0, 3, 4, 5]; + break; + + case 'subset': + $type_id = [24, 25, 26]; + break; + } + + $arr_requested_trait = []; + // Create the array of traits based on requested trait ids. + foreach($type_id as $id) { + // Push the trait into the array. + if ($type == 'phenotyping') { + // Store the actual index number associated to a trait. + // This is important since order is relevant in the instructions page. + $arr_requested_trait[$id] = $arr_headers[$id]; + } + else { + // Order is irrelevant, just need the array of traits. + array_push($arr_requested_trait, $arr_headers[$id]); + } + } + + return $arr_requested_trait; + } + + /** + * Function that lists types of column headers. This list will be available + * as an option when adding traits. Plant Property is used to indicate + * column headers in the pheno_plantprop. + */ + public static function getTraitTypes() { + return [ + 'type1' => 'essential', + 'type2' => 'optional', + 'type3' => 'subset', + 'type4' => 'plantproperty', + 'type5' => 'contributed' + ]; + } + + /** + * Function that lists trait reps or number of trials or R value/number + * that precedes the unit of a measurement type. + */ + public static function getTraitReps() { + return [ + '1st', + '2nd', + '3rd', + '4th', + 'R1', + 'R3', + 'R5', + 'R7' + ]; + } + + /** + * Default terms/trait with definition and method information. + * + * @param $cvterm + * A string containing the cvterm name. + * + * @return + * A string containing the cvterm name definition. + */ + public static function defineTrait($cvterm) { + // Array to hold definitions. + $definition = []; + + $method = 'method'; + $define = 'define'; + +$definition['Planting Date (date)'] = [ +$method => +'Record the date the seeds were sown.', +$define => +'The date should be the same for all plots, but could be different if circumstances such as bad weather prevent the seeding of all plots on the same day. If such a situation does occur, highlight rows with a different planting date so it is obvious to the data recorder, since they will have different days after planting values to record for that particular date.' +]; + +$definition['Days to Emergence (days)'] = [ +$method => +'Record the number of days after planting for which 10% of seeds have emerged.', +$define => +'Emergence = seedling stem/leaves have become visible.' +]; + +$definition['# of Emerged Plants (count)'] = [ +$method => +'Record the number of plants which emerged. +When: Record values once plants begin to flower or have elongated tendrils.', +$define => +'Emergence = seedling stem/leaves have become visible.' +]; + +$definition['Days till 10% of Plants have Elongated Tendrils (days)'] = [ +$method => +'Record the number of days after planting for which 10% of plants have an elongated tendril. +Some plants may not produce elongated tendrils but develop a rudimentary tendril only 2-3 mm long. If this applies to more than 90% of plants in the plot, the "Days till 10% have Elongated Tendril" should be left blank.', +$define => +'Elongated tendril = 5 mm and longer.' +]; + +$definition['Days till 10% of Plants have One Open Flower (R1; days)'] = [ +$method => +'Record the number of days after planting for which 10% of plants have at least one open flower.', +$define => +'Open flower = flower banner (standard petal) is visible. +R1 = One open flower at any node.' +]; + +$definition['# Nodes on Primary Stem at R1 (1st; count)'] = [ +$method => +'Record the number of nodes on the primary stem when the first flower opens. +Record values from 2 plants, taken from the middle of the plot.', +$define => +'Node = positions on stem where leaves and buds/branches grow from. +The first few nodes can loose their leaves and may not be readily visible. First flower may NOT be on the primary stem but we want the # of nodes on the primary stem that day.' +]; + +$definition['# Nodes on Primary Stem at R1 (2nd; count)'] = [ +$method => +'Record the number of nodes on the primary stem when the first flower opens. +Record values from 2 plants, taken from the middle of the plot.', +$define => +'Node = positions on stem where leaves and buds/branches grow from. +The first few nodes can loose their leaves and may not be readily visible. First flower may NOT be on the primary stem but we want the # of nodes on the primary stem that day.' +]; + +$definition['Days till 10% of Plants have Pods (R3; days)'] = [ +$method => +'Record the number of days after planting for which 10% of plants have pods. Note: Pods can be present but still covered with flower petals, for ease of data collection, only count the plant as having a pod if you can visually see the pod without having to remove flower petals.', +$define => '' +]; + +$definition['Days till 10% of Plants have fully Swollen Pods (R5; days)'] = [ +$method => +'Record the number of days after planting for which 10% of plants have pods with fully swollen seeds (that fill more than half of the pod area).', +$define => +'Plant with pods = pods are visible without having to remove flower petals. +Swollen Pod = seeds have swollen to their max size and fill more than half the pod area. +R5 = Seed in any single pod on nodes 10-13 of the basal primary branch are swollen and completely fill the pod cavity. +Genotypic variation in seed size and pod structure will require the use of discretion by the data recorder, since not all genotypes have seeds which fully fill the pod cavity at maturity. +This corresponds to physiological maturity at which point the seeds have swollen to their max size. At this stage, seed coat is formed and there is a colour change in the cotyledons (except QG1!).' +]; + +$definition['Days till 10% of Plants have 1/2 Pods Mature (R7; days)'] = [ +$method => +'Record the number of days after planting for which 10% of plants have 1/2 of their pods mature.', +$define => +'Mature pod = dry pod ready to be harvested +Before the pods dry out they lose their green pigmentation, often looking pale, but will still contain moisture, which you can feel when you touch the pod. Pods that are considered mature will have changed colour and be dry to the touch. +R7 = The leaves start yellowing and 50% of the pods have turned yellow. +Pod maturity is not always accompanied by a yellowing of the pod – some pods turn white, some are pigmented and may have patterns, CDC QG2 will remain green' +]; + +$definition['R7 Traits: Lowest Pod Height (1st; cm)'] = [ +$method => +'Record the distance (cm) from the soil to the bottom of the lower most pod. +Record values from 2 plants, taken from the middle of the plot. +When: Record values when 10% of plants have 1/2 pods mature (R7). +Record values from 2 plants, taken from the middle of the plot.', +$define => '' +]; + +$definition['R7 Traits: Lowest Pod Height (2nd; cm)'] = [ +$method => +'Record the distance (cm) from the soil to the bottom of the lower most pod. +Record values from 2 plants, taken from the middle of the plot. +When: Record values when 10% of plants have 1/2 pods mature (R7). +Record values from 2 plants, taken from the middle of the plot.', +$define => '' +]; + +$definition['R7 Traits: Canopy Height (1st; cm)'] = [ +$method => +'Record the distance (cm) from the soil to the highest part of the plant canopy. +Record values from 2 plants, taken from the middle of the plot. +When: Record values when 10% of plants have 1/2 pods mature (R7). +Record values from 2 plants, taken from the middle of the plot. +DO NOT stretch the plant. Leave as is.', +$define => '' +]; + +$definition['R7 Traits: Canopy Height (2nd; cm)'] = [ +$method => +'Record the distance (cm) from the soil to the highest part of the plant canopy. +Record values from 2 plants, taken from the middle of the plot. +When: Record values when 10% of plants have 1/2 pods mature (R7). +Record values from 2 plants, taken from the middle of the plot. +DO NOT stretch the plant. Leave as is.', +$define => '' +]; + +$definition['Days till Harvest (days)'] = [ +$method => +'Record the number of days from planting to harvest.', +$define => '' +]; + +$definition['Diseases Present (y/n/?)'] = [ +$method => +'Record the presence of any disease, and if able, describe or make notes.', +$define => +'Scale: y = disease present, n = no disease present ? = unsure +There is a "Disease-specific Comments" column for making any notes related to disease including but not limited to the observation that many or specific diseases are present.' +]; + +$definition['Disease-specific Comments'] = [ +$method => +'Feel free to mention if multiple or specific diseases are present. Note: disease ratings for specific diseases should go in a separate column if you would like to measure them.', +$define => '' +]; + +$definition['Lodging (Scale: 1-5) upright - lodged'] = [ +$method => +'Record the degree of plant lodging. +Scale: +1 = vertical/upright +2 = leaning +3 = most plants at 45° angle +4 = all plants 10-45° from ground +5 = most plants flat/prostrate +When: Record value when harvesting the plot.', +$define => +'lodged = plant canopy is no longer vertical to the ground.' +]; + +$definition['Subset Traits: # Peduncles (count)'] = [ +$method => +'Leave this column as is, DO NOT make any changes (unless you were unable to obtain 20 peduncles). +This has been preset to 20, because that is how many should be collected.', +$define => +'peduncle = a stalk supporting an inflorescence (group/cluster of flowers).' +]; + +$definition['Subset Traits: # Pods (count)'] = [ +$method => +'Record the total number of pods on the 20 peduncles collected for the subset traits.', +$define => '' +]; + +$definition['Subset Traits: # Seeds (count)'] = [ +$method => +'Record the total number of seeds from pods counted for the previous trait ("Subset Traits: # Pods").', +$define => '' +]; + +$definition['Straw Biomass (g)'] = [ +$method => +'Record the mass (g) of dry, above ground plant material from each plot.', +$define => +'Straw = all above ground biomass excluding the seed.' +]; + +$definition['Total Seed Mass (g)'] = [ +$method => +'Record the total mass (g) of all seeds harvested from each plot.', +$define => '' +]; + +$definition['Total # of Seeds (count)'] = [ +$method => +'Record the total number of seeds harvested from each plot.', +$define => '' +]; + +$definition['100 Seed Mass (g)'] = [ +$method => +'Count 100 seeds and record the mass (g). +Do not calculate this value from "Total Seed Mass" and "Total Number of Seeds".', +$define => '' +]; + +$definition['Comments'] = [ +$method => 'Feel free to mention any remarks or observations.', +$define => 'Comments' +]; + +$definition['R7 Traits: Canopy Width (cm)'] = [ +$method => +'Record the max canopy width (cm). +Record values from 2 plants, taken from the middle of the plot. +When: Record values when 10% of plants have 1/2 pods mature (R7). +Record values from 2 plants, taken from the middle of the plot. +Add a column with the header "R7 Traits: Canopy Width (cm)" if you would like to record this trait.', +$define => '' +]; + +$definition['R7 Traits: Plant Length (cm)'] = [ +$method => +'Record the distance (cm) from the soil to the end of the longest stem. +Record values from 2 plants, taken from the middle of the plot. +When: Record values when 10% of plants have 1/2 pods mature (R7). +Record values from 2 plants, taken from the middle of the plot. +DO stretch the plant. +Add a column with the header "R7 Traits: Plant Length (cm)" if you would like to record this trait.', +$define => '' +]; + + return (isset($definition[$cvterm])) ? $definition[$cvterm] : null; + } +} \ No newline at end of file diff --git a/src/Services/RawphenotypesEnvironmentDataService.php b/src/Services/RawphenotypesEnvironmentDataService.php new file mode 100644 index 0000000..d6e4668 --- /dev/null +++ b/src/Services/RawphenotypesEnvironmentDataService.php @@ -0,0 +1,126 @@ +insert($table) + ->fields([ + 'project_id' => $details['project_id'], + 'fid' => $details['fid'], + 'location' => $details['location'], + 'year' => $details['year'], + 'sequence_no' => $details['sequence_no'] + ]) + ->execute(); + } + + /** + * Calculate the next sequence order of a file given a + * project-location-year combination. + * + * @param $project_id + * Integer, project id number. + * @param $location + * String, location information the file is specific to. + * @param $year + * Year value, in relation to location and project, the year data was collected. + * + * @return integer + * Next sequence number or 1 to indicate the first sequence when none was uploaded. + */ + public static function getSequenceNumber($project_id, $location, $year) { + // Rename file to include location, year, sequence no. + // Fetch the largest seq no in a project, location and year. + $sql = " + SELECT sequence_no FROM pheno_environment_data + WHERE location = :location AND year = :year AND project_id = :project_id + ORDER BY sequence_no DESC LIMIT 1 + "; + $args = [ + ':project_id' => $project_id, + ':location' => $location, + ':year' => $year, + ]; + + $result = \Drupal::database() + ->query($sql, $args); + + $result->allowRowCount = TRUE; + + return ($result->rowCount()) ? $result->fetchField() + 1 : 1; + } + + /** + * Remove environment data file entry from pheno_environment_data table. + * Physical file is still available in the Drupal public:// file system. + * + * @param $envdata + * Array, containing the record id, location, year and project_id. + */ + public static function deleteEnvData($envdata) { + $table = 'pheno_environment_data'; + + \Drupal::database() + ->delete($table) + ->condition('environment_data_id', $envdata['envdata_id']) + ->execute(); + + $file_entity = File::load($envdata['fid']); + file_delete($file_entity->id()); + + $sql = " + SELECT environment_data_id, sequence_no + FROM pheno_environment_data + WHERE location = :location AND year = :year AND project_id = :project_id + ORDER BY sequence_no ASC + "; + $args = [ + ':project_id' => $envdata['project_id'], + ':location' => $envdata['location'], + ':year' => $envdata['year'], + ]; + + $result = \Drupal::database() + ->query($sql, $args); + + $result->allowRowCount = TRUE; + + if ($result->rowCount()) { + $seq = 1; + foreach($result as $id => $seq_no) { + \Drupal::database() + ->update($table) + ->fields([ + 'sequence_no' => $seq + 1 + ]) + ->condition('environment_data_id', $id, '=') + ->execute(); + + $sql++; + } + } + } +} \ No newline at end of file diff --git a/src/Services/RawphenotypesLoggerService.php b/src/Services/RawphenotypesLoggerService.php new file mode 100644 index 0000000..66aa0a3 --- /dev/null +++ b/src/Services/RawphenotypesLoggerService.php @@ -0,0 +1,22 @@ + TRUE]); + } +} \ No newline at end of file diff --git a/src/Services/RawphenotypesMviewService.php b/src/Services/RawphenotypesMviewService.php new file mode 100644 index 0000000..53d0520 --- /dev/null +++ b/src/Services/RawphenotypesMviewService.php @@ -0,0 +1,130 @@ + 'rawpheno_rawdata_mview', + 'fields' => [ + 'plant_id' => ['type' => 'int'], + 'location' => [ + 'type' => 'varchar', + 'length' => 255 + ], + 'rep' => [ + 'type' => 'varchar', + 'length' => 255 + ], + 'planting_date' => [ + 'type' => 'varchar', + 'length' => 255 + ], + 'total_count' => [ + 'type' => 'int' + ], + 'all_traits' => [ + 'type' => 'text' + ], + ], + 'indexes' => [ + 'plant_id' => ['plant_id'], + 'location' => ['location'], + ] + ]; + + /** + * Create materialized view required by this modules. + * + * @TODO: Implement upgraded service relating to materialized views. + */ + public static function createMview() { + $mv_name = self::$mv_name; + $mv_module = self::$mv_module; + $mv_schema = self::$mv_schema; + $mv_sql = self::$mv_sql; + $mv_comment = self::$mv_comment; + + // Create materialized view. + if (function_exists('chado_add_mview')) { + chado_add_mview($mv_name, $mv_module, $mv_schema, $mv_sql, $mv_comment, FALSE); + } + else { + // tripal_add_mview($mv_name, $mv_module, $mv_schema, $mv_sql, $mv_comment, FALSE); + // CREATE MATERIALIZED VIEWS. + + // CREATE MVIEW with sample data. + + } + } + + /** + * Drop materialized view. + * + * @see uninstall hook. + * @TODO: Implement upgraded service relating to materialized views. + */ + public static function dropMview() { + $schema = \Drupal::service('database')->schema(); + if ($schema->tableExists('tripal_mview')) { + // Tripal maview table exists, check if it has a row + // matching the materialized view this module is using. + $sql = "SELECT mview_id FROM {tripal_mviews} WHERE mv_table = :mv_table"; + $args = [':mv_table' => self::$mv_table]; + + $mview = \Drupal::database() + ->query($sql, $args); + + if (isset($mview_id) AND $mview_id > 0) { + // Delete materialized view. + // DELETE MVIEW HERE + } + } + } +} \ No newline at end of file diff --git a/src/Services/RawphenotypesProjectService.php b/src/Services/RawphenotypesProjectService.php new file mode 100644 index 0000000..38592a1 --- /dev/null +++ b/src/Services/RawphenotypesProjectService.php @@ -0,0 +1,370 @@ + $project_name]; + + $project = \Drupal::database() + ->query($sql, $args); + + return $project->fetchObject() ?? null; + } + + /** + * Get project project records project_id, name and description by + * using project id number as search key. + * + * @param $project_id + * Integer, project id number. + * + * @return object + * Project row object. + */ + public static function getProject($project_id) { + // project_id, name and description. + $project_id = (int) trim($project_id); + + $sql = "SELECT * FROM chado.project WHERE project_id = :project_id LIMIT 1"; + $args = [':project_id' => $project_id]; + + $project = \Drupal::database() + ->query($sql, $args); + + return $project->fetchObject() ?? null; + } + + /** + * Delegate a user (registered Drupal user) to a project. + * This will give user the priviledge to among others upload data. + * + * In custom table pheno_project_user + * - uid: Drupal user id. + * - project_id: project the user is assigned to. + * + * @param $user_id + * Integer, Drupal user user id. + * @param $project_id + * Integer, project id number. + */ + public static function addUserToProject($user_id, $project_id) { + $sql = " + SELECT project_user_id FROM pheno_project_user + WHERE uid = :user AND project_id = :project + "; + $args = [':user' => $user_id, ':project' => $project_id]; + + $query = \Drupal::database() + ->query($sql, $args); + + $query->allowRowCount = TRUE; + + if ($query->rowCount() <= 0) { + // Check to ensure that user is assigned to a project once. + // User is not in this project yet. + \Drupal::service('database') + ->insert('pheno_project_user') + ->fields([ + 'uid' => $user_id, + 'project_id' => $project_id + ]) + ->execute(); + } + } + + /** + * Get a list of project from chado.project table. + * The list will provide administrators a selection for + * phenotyping experiment using this module. + * + * @return array + * Project rows from chado.project table. + */ + public static function getNewProjects() { + $sql = " + SELECT t1.project_id, t1.name + FROM chado.project AS t1 LEFT JOIN pheno_project_cvterm AS t2 USING(project_id) + WHERE t2.project_id IS NULL ORDER BY t1.project_id DESC + "; + + $project = \Drupal::database() + ->query($sql); + + $project->allowRowCount = TRUE; + + return ($project->rowCount()) ? $project->fetchAllKeyed() : null; + } + + /** + * Get all active project and provide a basic summary of project assets. + * - Header count: number of column header a project is measuring. + * - Essential header count: number of essential header (important header/ must exists). + * - User count: number of user assigned to a project. + * + * @return array + */ + public static function getActiveProjects() { + $sql = " + SELECT + t1.name, + COUNT(DISTINCT t2.cvterm_id) AS header_count, + COUNT(DISTINCT t3.cvterm_id) AS essential_count, + COUNT(DISTINCT t4.uid) AS user_count, + t1.project_id + FROM + chado.project AS t1 + INNER JOIN pheno_project_cvterm AS t2 USING (project_id) + INNER JOIN pheno_project_cvterm AS t3 USING (project_id) + INNER JOIN pheno_project_user AS t4 USING (project_id) + WHERE + t1.project_id IN (SELECT DISTINCT project_id FROM pheno_project_cvterm) + AND t2.project_id IN (SELECT DISTINCT project_id FROM pheno_project_cvterm) + AND t3.project_id IN (SELECT DISTINCT project_id FROM pheno_project_cvterm) + AND t3.type = 'essential' + AND t4.project_id IN (SELECT DISTINCT project_id FROM pheno_project_cvterm) + GROUP BY t1.project_id + ORDER BY t1.project_id DESC + "; + + $project = \Drupal::database() + ->query($sql); + + $project->allowRowCount = TRUE; + + return ($project->rowCount()) ? $project->fetchAll() : []; + } + + /** + * Get all locations in a project. + * + * @param $project_id + * Integer, project id number. + * + * @return array + * All locations identified in a project. + */ + public static function getProjectLocations($project_id) { + $sql = " + SELECT location + FROM chado.rawpheno_rawdata_mview INNER JOIN pheno_plant_project USING(plant_id) + WHERE project_id = :project_id + GROUP BY location + ORDER BY location ASC + "; + $args = [':project_id' => $project_id]; + + $query = \Drupal::database() + ->query($sql, $args); + + $query->allowRowCount = TRUE; + + return ($query->rowCount() > 0) ? $query->fetchAllKeyed(0, 0) : []; + } + + /** + * Get all planting year project has carried an experiment and filtered + * by a corresponding location. + * + * @param $project_id + * Integer, project id number. + * @param $location + * String, a filter to return only years in a project + * and location combination. + * + * @return $array + * All years identified in a project. + */ + public static function getProjectYears($project_id, $location = null) { + $sql = " + SELECT SUBSTRING(planting_date, 1, 4) AS year + FROM chado.rawpheno_rawdata_mview + WHERE location = :location + AND plant_id IN (SELECT plant_id FROM pheno_plant_project WHERE project_id = :project_id) + ORDER BY year DESC + "; + $args = [':project_id' => $project_id]; + + $query = \Drupal::database() + ->query($sql, $args); + + $query->allowRowCount = TRUE; + + return ($query->rowCount() > 0) ? $query->fetchAllKeyed(0, 0) : []; + } + + /** + * Get all column header asset of a project. + * + * @param $project_id + * Integer, project id number. + * + * @return array + * An array of all column headers (cvterms and type) in a project. + */ + public static function getProjectTerms($project_id) { + $sql = " + SELECT project_cvterm_id, pheno_project_cvterm.type, name, cvterm_id + FROM chado.cvterm RIGHT JOIN pheno_project_cvterm USING(cvterm_id) + WHERE project_id = :project_id ORDER BY type, name ASC + "; + $args = [':project_id' => $project_id]; + + $query = \Drupal::database() + ->query($sql, $args); + + $query->allowRowCount = TRUE; + + return ($query->rowCount() > 0) ? $query->fetchAll() : []; + } + + /** + * Get active user of a project. + * + * @param $project_id + * Integer, project id number. + * + * @return array + * Drupal user object - name, uid, mail, created, status and last login information. + */ + public static function getProjectActiveUsers($project_id) { + $sql = " + SELECT project_user_id, uid FROM pheno_project_user + WHERE project_id = :project_id + ORDER BY uid DESC + "; + $args = [':project_id' => $project_id]; + + $query = \Drupal::database() + ->query($sql, $args); + + $query->allowRowCount = TRUE; + + $users = []; + $cols = ['uid', 'name', 'mail', 'created', 'status', 'login']; + if ($query->rowCount() > 0) { + $user_service = \Drupal::service('rawphenotypes.user_service'); + + foreach($query as $user) { + $user_profile = $user_service::getUser((int)$user->uid, $cols); + $users[ $user->project_user_id ] = (object) $user_profile; + } + } + + return $users; + } + + /** + * Get project environment data files. + * + * @param $project_id + * Integer, project id number. + * + * @return array + * Environment data and file information (filename, uri and file timestamp). + */ + public static function getProjectEnvDataFiles($project_id) { + $files = []; + + $sql = " + SELECT environment_data_id, location, year, sequence_no, fid + FROM pheno_environment_data + WHERE project_id = :project_id + ORDER BY location, year, sequence_no DESC + "; + $args = [':project_id' => $project_id]; + + $query = \Drupal::database() + ->query($sql, $args); + + $query->allowRowCount = TRUE; + + if ($query->rowCount() > 0) { + foreach($query as $i => $env) { + $f = new \stdClass(); + $f->environment_data_id = $env->environment_data_id; + $f->location = $env->location; + $f->year = $env->year; + $f->sequence_no = $env->sequence_no; + + $file_entity = File::load($env->fid); + $f->filename = $file_entity->getFilename(); + $f->uri = file_create_url($file_entity->getFileUri()); + $timestamp = \Drupal::service('date.formatter') + ->format($file_entity->getCreatedTime()); + $f->created = $timestamp; + $f->filesize = format_size($file_entity->getSize()) + ->render(); + + $files[ $i ] = $f; + } + } + + return $files; + } + + /** + * Get project environment data assets - year, location and file. + * + * @param $asset_id + * Integer, environemnt data asset id. + * + * @return array + * Evironment data assets location, year and file. + */ + public static function getProjectEnvDataAssets($asset_id) { + $sql = " + SELECT environment_data_id, project_id, location, year, fid + FROM pheno_environment_data + WHERE environment_data_id = :envdata_id + LIMIT 1 + "; + $args = [':envdata_id' => $asset_id]; + $envdata = \Drupal::database() + ->query($sql, $args); + + if ($envdata) { + $e = $envdata->fetchObject(); + + return array( + 'envdata_id' => $e->environment_data_id, + 'project_id' => $e->project_id, + 'location' => $e->location, + 'year' => $e->year, + 'fid' => $e->fid, + ); + } + else { + return 0; + } + } +} \ No newline at end of file diff --git a/src/Services/RawphenotypesTermService.php b/src/Services/RawphenotypesTermService.php new file mode 100644 index 0000000..45d6902 --- /dev/null +++ b/src/Services/RawphenotypesTermService.php @@ -0,0 +1,688 @@ +vocabularies = $default_service::getDefaultValue('vocabularies'); + + $settings = \Drupal::service('config.factory') + ->getEditable('rawphenotypes.settings'); + + $this->rtransform_rules = [ + 'word_rules' => $settings->get('rawpheno_rtransform_words'), + 'char_rules' => $settings->get('rawpheno_rtransform_characters'), + 'replace_rules' => $settings->get('rawpheno_rtransform_replace'), + ]; + + $this->replicates = $default_service::getTraitReps(); + } + + /** + * Create or add controlled vocabulary terms. + * + * @param $details + * Associative array where key corresponds to fields in cv or cvterm. + * Fields/Key: + * - name + * - definition + * - cv_name/cv_id + * + * @return object + * Inserted term row object. + * + * @TODO: Update to comply with Tripal 4 terms and vocab services only + * when term service used for non-content type become available. + */ + public function addTerm($details) { + $term = (function_exists('chado_insert_cvterm')) + ? chado_insert_cvterm($details) + : chado_insert_cvterm($details); + + return $term ?? null; + } + + /** + * Update values name and definition values of a term. + * + * @param $term_id + * Integer, term id/cvterm id number to be updated. + * @param $new_name + * New term name to replace existing term name. + * @param $new_definition + * New term definition to replace existing term definition. + * + * @TODO: Update to comply with Tripal 4 terms and vocab services only + * when term service used for non-content type become available. + */ + public function updateTerm($term_id, $new_name, $new_definition): void { + $table = 'cvterm'; + $match = ['cvterm_id' => $term_id]; + + chado_update_record($table, + $match, + [ + 'name' => $new_name, + 'definition' => $new_definition + ] + ); + } + + /** + * Get controlled vocabulary term or cv terms using name field. + * + * @param $term_name + * String, term or vocabulary term name value. + * @param $source + * String, source table to search the term name. + * Default to chado.cvterm. + * + * @return object + * Term row matching the name (cv.name or cvterm.name) as an object. + */ + public function getTermByName($term_name, $source = 'cvterm') { + if ($source == 'cv') { + // Term vocabulary source - find in chado.cv. + $table = 'chado.cv'; + $primary_key = 'cv_id'; + } + else { + // Term, cvterm source - find in chado.cvterm. + $table = 'chado.cvterm'; + $primary_key = 'cvterm_id'; + } + + $term_name = trim($term_name); + + // This is a case-sensitive search so terms Null and null are considered + // two difference terms. + $sql = 'SELECT %s, name, definition FROM %s WHERE name = :term_name'; + $sql = sprintf($sql, $primary_key, $table); + $args = [':term_name' => $term_name]; + + $query = \Drupal::database() + ->query($sql, $args); + + return $query->fetchObject() ?? null; + } + + /** + * Get term. + * + * @param $details + * Associative array, where key corresponds to field in chado.cvterm or chado.cv. + * @param $source + * Source table cv or cvterm. Default to cvterm. + * + * @return object + * CV term row object. + */ + public function getTerm($details, $source = 'cvterm') { + if ($source == 'cv') { + return (function_exists('chado_get_cv')) + ? chado_get_cv($details) + : tripal_get_cv($details); + } + else { + return (function_exists('chado_get_cvterm')) + ? chado_get_cvterm($details) + : tripal_get_cvterm($details); + } + } + + /** + * Base on R transformation rules, convert a term into + * R-compatible version. This version can be reference using + * the relationship between a term and r-version in cvtermprop table. + * + * Transformation rules can be set in the control panel of this module. + * + * @param $column_header + * A string containing the column header to transform. + * + * @return + * A string containing the R compatible column header. + */ + public function makeTermRCompatible($column_header) { + // Get R transformation rules set in the admin control panel. + $word_rules = $this->rtransform_rules['word_rules']; + $char_rules = $this->rtransform_rules['char_rules']; + $replace_rules = $this->rtransform_rules['replace_rules']; + + $arr_match = []; + $arr_replace = []; + $r = explode(',', $replace_rules); + + // Convert the rule to key and value pair. The key is the matching character/word and + // the values is the replacement value when key is found in a string. + foreach($r as $g) { + list($match, $replace) = explode('=', $g); + $arr_match[] = trim($match); + $arr_replace[] = trim($replace); + } + + // Convert special characters transformation rules in string to array. + $char_rules = explode(',', $char_rules); + + // Convert words transformation rules in string to array. + $word_rules = explode(',', $word_rules); + + // Remove leading and trailing spaces from the selected trait. + // Convert string to lowercase. + $selected_trait = trim(strtolower($column_header)); + // 1. Break the column header in string to individual words, + // and remove all words that matches an entry in the words transfomation rules. + $w = explode(' ', $selected_trait); + foreach($w as $c) { + $c = trim($c); + + // Skip the words in the traits that are present in the + // words transformation rules. + if (!in_array($c, $word_rules)) { + // Do match and replace, as well as, removal of special characters + // only when the current word is not in the words transformation rules. + // 2. Match and replace based on match and replace rule. + $c = str_replace($arr_match, $arr_replace, $c); + // 3. Remove all special characters listed in remove chars rule. + $c = str_replace($char_rules, '', $c); + + // All transformation rules applied, make sure that + // the result is not a blank space. + if (!empty($c)) { + $rfriendly[] = trim($c); + } + } + } + + // Final transformation is replacing all spaces to dots/period (.) + return ucfirst(implode('.', $rfriendly)); + } + + /** + * Parse a line of text which is a term/column header to find and + * extract the unit part present in the string. + * + * @param $header + * A string containing the header. Usually following + * the Trait name (unit) format. + * + * @return + * A string containing the unit of the header. + */ + public function getTermUnit($header) { + // Find the unit part in the string (unit). + // Not found return text. + preg_match("/.*\(([^)]*)\)/", $header, $match); + $u = (isset($match[1])) ? $match[1] : 'text'; + + // Unit can contain replicates, fetch all replicates + // used and remove it from the unit. + $chars = $this->replicates; + array_push($chars, ';', ': 1-5'); + $unit = str_ireplace($chars, '', $u); + + return trim(strtolower($unit)); + } + + /** + * Set data collection method of a trait. + * Example: trait method and trait R version. + * + * Key and value expected. + * - cvterm_id: term id number. + * - type_id: describes the property created, it is the cvterm_id value + * corresponding to phenotype_collection_method and phenotype_r_compatible_version. + * - value: method description or the r transformed value. + * - rank: default and set to 0. + * + * @param $details + * Associative array, where key corresponds to field in chado.cvtermprop table. + */ + public function saveTermProperty($details): void { + if ($details) { + chado_insert_record($this->table_property, $details); + } + } + + /** + * Update a term property whether in chado specific database + * and module specific table. + * + * @param $term_id + * Integer, term id/cvterm id number to be updated. + * @param $type_id + * Integer, type id number corresponding to which property type to be updated. + * @param $value + * New value to replace. + * @param $project + * Integer, project id number used for property that requires project specific constraints. + * Default to null. + * @param $chadoprop + * Boolean, True if property is from a chado table and False if from custom table. + * Default to TRUE - updates to chado table. + */ + public function updateTermProperty($term_id, $type_id, $value, $project = null, $chadoprop = TRUE) { + if ($chadoprop) { + // Update term method or r-version value from a + // chado table. + $table = $this->table_property; + $match = ['cvterm_id' => $term_id, 'type_id' => $type_id]; + + chado_update_record($table, + $match, + ['value' => $value] + ); + } + else { + // Update term type whether essential, optional or custom type. + // Source table is custom table. + $table = 'pheno_project_cvterm'; + \Drupal::database() + ->update($table) + ->fields(['type' => $value]) + ->condition('cvterm_id', $term_id, '=') + ->condition('project_id', $project, '=') + ->execute(); + } + } + + /** + * Get term R version or Method property. + * + * @param $term_id + * Integer, term id/cvterm id number to be updated. + * @param $property + * String, method or rversion property of a term. + * [method or rversion], default to method property. + * + * @return string + * Method or Rversion information of a term. + */ + public function getTermProperty($term_id, $property = 'method') { + $properties = []; + $properties['method'] = $this->vocabularies['cv_desc']; + $properties['rversion'] = $this->vocabularies['cv_rver']; + + $property = $properties[ $property ]; + // Get the cvterm equivalent of a cv used in tagging a property (type_id). + $type = self::getTerm(['name' => $property], ['cv_id' => ['name' => $this->vocabularies['cv_phenotypes']]]); + + $sql = "SELECT value FROM chado.cvtermprop WHERE cvterm_id = :term_id AND type_id = :cv_id"; + $args = [':term_id' => $term_id, ':cv_id' => $type->cvterm_id]; + + $property_value = \Drupal::database() + ->query($sql, $args) + ->fetchField(); + + return $property_value ?? null; + } + + /** + * Create trait relationship ie. trait-unit relationship. + * + * Key and value expected. + * - subject_id: the term id - the unit cvterm id. + * - type_id: cvterm id for term phenotype_measurement_units. + * - object_id: term id - the trait/header cvterm id. + * + * @param $details + * Associative array, where key corresponds to field in chado.cvterm_relationship table. + */ + public function saveTermRelationship($details): void { + if ($details) { + chado_insert_record($this->table_relationship, $details); + } + } + + /** + * Update term relationship. + * + * @param $term_id + * Integer, term id/cvterm id number to be updated. + * @param $value + * New value to replace the existing relationship. + * Value will be the subject in the relationship. + */ + public function updateTermRelationship($term_id, $value): void { + if ($value) { + $match = ['object_id' => $term_id]; + chado_update_record($this->table_relationship, + $match, + [ + 'subject_id' => $value + ] + ); + } + } + + /** + * Assign term to a project. + * + * @param $term_id + * Integer, term/trait id. + * @param $type + * String, type (essential, optional, subset or plant property). + * @param $project_id + * Integer, project id to where a term is to be assigned. + */ + public function saveTermToProject($term_id, $type, $project_id): void { + $table = 'pheno_project_cvterm'; + $type = trim($type); + $project_id = trim($project_id); + + \Drupal::database() + ->insert($table) + ->fields([ + 'cvterm_id' => $term_id, + 'type' => $type, + 'project_id' => $project_id, + ]) + ->execute(); + } + + /** + * For types using scale as unit, create each scale item. + * + * @param $term_id + * Term id measuring scale values. + * @param $scales + * Scale items or range of values. + */ + public function setTermScaleValues($term_id, $scales) { + $table = 'pheno_scale_member'; + + $query = \Drupal::database() + ->insert($table) + ->fields(['scale_id', 'code', 'value']); + + foreach($scales as $code => $value) { + $query->fields([ + 'scale_id' => $term_id, + 'code' => $code, + 'value' => $value + ]); + } + + $query->execute(); + } + + /** + * Get all plant property type column headers. + */ + public function getPlantPropertyTerm() { + $sql = " + SELECT cvterm_id + FROM chado.cv AS t1 INNER JOIN chado.cvterm AS t2 USING(cv_id) + WHERE t1.name = :plantprop_type + ORDER BY cvterm_id ASC + "; + $args = [':plantprop_type' => $this->vocabularies['cv_prop']]; + + $query = \Drupal::database() + ->query($sql, $args); + + $query->allowRowCount = TRUE; + + return ($query->rowCount()) ? $query : []; + } + + /** + * Get all terms by type. + * + * @param $type + * Type of term that corresponds to the cv_id of the cvterm row. + * One of the following: + * phenotype_plant_property_types + * phenotype_measurement_units + * phenotype_measurement_types + * phenotype_r_compatible_version + * phenotype_collection_method + * + * @param return + * Array, rows matching the type (cv_id) in cvterm table. + */ + public function getTermsByType($type) { + $sql = " + SELECT t2.name, t2.definition + FROM chado.cv AS t1 INNER JOIN chado.cvterm AS t2 USING (cv_id) + WHERE t1.name = :type + ORDER BY t2.name ASC + "; + $args = [':type' => $type]; + + $query = \Drupal::database() + ->query($sql, $args); + + $query->allowRowCount = TRUE; + + return ($query->rowCount()) ? $query->fetchAllKeyed() : []; + } + + /** + * Get terms that are not listed as among the traits in a project. + * + * @param $project_id + * Integer, Project id number terms should not be registered to. + * + * @return array + * Term rows in chado.cvterm. + */ + public function getTermsNotInProject($project_id) { + $sql = " + SELECT t2.cvterm_id, t2.name + FROM chado.cv AS t1 INNER JOIN chado.cvterm AS t2 USING(cv_id) + WHERE t1.name = 'phenotype_measurement_types' + AND t2.cvterm_id NOT IN (SELECT cvterm_id FROM pheno_project_cvterm WHERE project_id = :project_id) + ORDER BY t2.cvterm_id ASC + "; + $args = [':project_id' => $project_id]; + + $query = \Drupal::database() + ->query($sql, $args); + + $query->allowRowCount = TRUE; + + return ($query->rowCount()) ? $query : []; + } + + /** + * Get term properties. + * + * @param $term_id + * Integer, term or cvterm id number. + * @param $dataset + * An string indicating whether to include data count. + * + * @return + * An array containing all properties (project, name, data, etc.) of a column header. + */ + public function getTermProperties($term_id, $dataset = null) { + // Array to hold properties. + $arr_properties = []; + + // Get project information and header type. + $sql = " + SELECT t1.project_id, t1.name, t2.cvterm_id, t2.type + FROM chado.project AS t1 INNER JOIN pheno_project_cvterm AS t2 USING(project_id) + WHERE t2.project_cvterm_id = :record_id LIMIT 1 + "; + $args = [':record_id' => $term_id]; + + $h = \Drupal::database() + ->query($sql, $args) + ->fetchObject(); + + $arr_properties['in_project_id'] = $h->project_id; + $arr_properties['in_project_name'] = $h->name; + $arr_properties['cvterm_id'] = $h->cvterm_id; + $arr_properties['type'] = $h->type; + + $h = self::getTerm(['cvterm_id' => $arr_properties['cvterm_id']]); + $arr_properties['name'] = $h->name; + $arr_properties['definition'] = empty($h->definition) ? '' : $h->definition; + + // cvterm R Version and Collection Method. + $arr_properties['method'] = ''; + + $sql = " + SELECT value, type_id FROM chado.cvtermprop + WHERE cvterm_id = :record_id + "; + + $args = [':record_id' => $arr_properties['cvterm_id']]; + $h = \Drupal::database() + ->query($sql, $args); + + $rversion_prop = self::getTermByName($this->vocabularies['cv_rver']); + $method_prop = self::getTermByName($this->vocabularies['cv_desc']); + + // From the query which will return both properties (method and r version), + // Identify which is method from r version and tag accordingly using + // corresponding property id numbers. + foreach($h as $c) { + $val = isset($c->value) ? $c->value : ''; + + if ($c->type_id == $rversion_prop->cvterm_id) { + $arr_properties['r_version'] = $val; + } + elseif ($c->type_id == $method_prop->cvterm_id) { + $arr_properties['method'] = $val; + } + } + + // Request full dataset including basic stats about the header. + if ($dataset == 'full') { + // Count data associated to column header. + $sql = " + SELECT COUNT(type_id) AS data_count FROM pheno_measurements + WHERE type_id = :cvterm_id + AND plant_id IN (SELECT plant_id FROM pheno_plant_project WHERE project_id = :project_id) + "; + $args = [':project_id' => $arr_properties['in_project_id'], ':cvterm_id' => $arr_properties['cvterm_id']]; + + $h = \Drupal::database() + ->query($sql, $args) + ->fetchObject(); + + $arr_properties['count_data'] = $h->data_count; + + // Count the projects this same column header is being used. + $sql = " + SELECT COUNT(project_id) AS project_count + FROM pheno_project_cvterm + WHERE project_id <> :project_id AND cvterm_id = :cvterm_id + "; + + $h = \Drupal::database() + ->query($sql, $args) + ->fetchObject(); + + $arr_properties['count_project'] = $h->project_count; + } + + return $arr_properties; + } + + /** + * Construct a trait given a name, trait rep and unit. + * + * @param $trait + * An array containing name, trait and unit. + * + * @return + * A string containing containing the column header in name (trait rep; unit) format. + */ + public function constructTerm($trait) { + $unit = ''; + + if (!empty($trait['unit']) OR !empty($trait['rep'])) { + $u = (empty($trait['rep'])) ? '' : $trait['rep'] . ' '; + if (empty($trait['unit'])) { + $u = trim($u); + } + + $unit = '(' . $u . $trait['unit'] . ')'; + } + + $name = ucfirst($trait['name'] . ' ' . rtrim($unit)); + + return trim($name); + } + + /** + * Removes a term from project. + * Deassociate the term from a project and not physical deletion of the + * trait record. + * + * @param $term_id + * Integer, term id number that corresponds to cvterm_id (trait/header id). + * @param $project_id + * Integer, project id number the term is part of. + */ + public function removeTermFromProject($term_id, $project_id): void { + $table = 'pheno_project_cvterm'; + \Drupal::database() + ->delete($table) + ->condition('cvterm_id', $term_id) + ->condition('project_id', $project_id) + ->execute(); + } + + /** + * Function that list default/initial units available to this module. + * + * @param $set + * A string indicating the type of set to return. + * def - unit and definition. + * type - unit and data type. + */ + public function getUnitDataType($set) { + // Type is required when programmatically generating data collection spreadsheet file + // in instructions page. + return [ + 'date' => ($set == 'def') ? 'Date' : 'date', + 'count' => ($set == 'def') ? 'Count' : 'integer', + 'days' => ($set == 'def') ? 'Days' : 'integer', + 'cm' => ($set == 'def') ? 'Centimeters' : 'integer', + 'scale' => ($set == 'def') ? 'Scale: 1-5' : 'integer', + 'g' => ($set == 'def') ? 'Grams (g)' : 'integer', + 'text' => ($set == 'def') ? 'Alphanumeric' : 'string', + 'y/n/?' => ($set == 'def') ? 'Yes, No or ? - Not sure' : 'string' + ]; + } +} \ No newline at end of file diff --git a/src/Services/RawphenotypesUserService.php b/src/Services/RawphenotypesUserService.php new file mode 100644 index 0000000..6368ed2 --- /dev/null +++ b/src/Services/RawphenotypesUserService.php @@ -0,0 +1,237 @@ + $project_user_id]; + + $user = \Drupal::database() + ->query($sql, $args) + ->fetchObject(); + + $arr_project_assets['user_id'] = $user->uid; + $arr_project_assets['project_id'] = $user->project_id; + $arr_project_assets['project_user_id'] = $user->project_user_id; + + // Given the project id this user is assigned, test if it has any backup files. + $sql = " + SELECT COUNT(file_id) + FROM pheno_backup_file + WHERE project_user_id = :project_user_id + "; + + $file_count = \Drupal::database() + ->query($sql, $args) + ->fetchField(); + + $arr_project_assets['project_file_count'] = $file_count; + + // Given the project id this user is assigned, test if it has data. + $sql = " + SELECT COUNT(plant_id) FROM pheno_plant_project + WHERE project_id = (SELECT project_id FROM pheno_project_user WHERE project_id = :project_id AND uid = :user_id)"; + + $args = [':project_id' => $user->project_id, ':user_id' => $user->uid]; + $data_count = \Drupal::database() + ->query($sql, $args) + ->fetchField(); + + $arr_project_assets['project_data_count'] = $data_count; + + return $arr_project_assets; + } + + /** + * Get user by username. + * + * @param $username + * String, name/username of user account. + * + * @return integer + * Id/uid number of user account. + */ + public static function getUserIdByUsername($username) { + $username = trim($username); + $user_id = null; + + $user = \Drupal::entityTypeManager() + ->getStorage('user') + ->loadByProperties(['name' => $username]); + + if ($user) { + $user = reset($user); + $user_id = $user->get('uid')->value; + } + + return $user_id; + } + + /** + * Get list of users. + * + * @param $key + * String, keywords that make up the name of the user. + * @param $limit + * Integer, limit result set returned. Default to 10 rows. + * + * @return array + * Associative array where the key is the user name and value + * is string concatenation of name and email address. + */ + public static function getUserByKeyword($key, $limit = 10) { + $ids = \Drupal::entityQuery('user') + ->condition('status', 1) + ->condition('name', '%' . $key . '%', 'LIKE') + ->range(0, $limit) + ->execute(); + + $users = User::loadMultiple($ids); + $arr_users = []; + foreach($users as $user) { + $name = $user->get('name')->value; + $uid = $user->get('uid')->value; + $mail = $user->get('mail')->value; + + $arr_users[] = [ + 'id' => $uid, + 'name' => $name, + 'mail' => $mail + ]; + } + + return $arr_users; + } + + /** + * Get user. + * + * @param $user_id + * Integer, user id number + * @param $options + * User columns required ie. name, email, status, created etc. + */ + public static function getUser($user_id, $options) { + $user_profile = []; + + if (is_int($user_id) && is_array($options)) { + $user = User::load($user_id); + + if ($user) { + foreach($options as $col) { + if (isset($user->get($col)->value)) { + $user_profile[ $col ] = $user->get($col)->value; + } + } + } + } + + return $user_profile; + } + + /** + * Get user files (backed up files). + * + * @param $project_user_id + * Integer, project user id number that maps a user to a project in pheno_project_user table. + * THIS IS NOT THE USER ID. + */ + public static function getUserFiles($project_user_id) { + $sql = " + SELECT t1.*, t2.filename, t2.filesize, t2.created, t2.uri + FROM pheno_backup_file AS t1 INNER JOIN file_managed AS t2 USING(fid) + WHERE t1.project_user_id = :project_user_id ORDER BY t1.version DESC + "; + $args = [':project_user_id' => $project_user_id]; + + $query = \Drupal::database() + ->query($sql, $args); + + $query->allowRowCount = TRUE; + + return ($query->rowCount()) ? $query->fetchAll() : []; + } + + /** + * Dissociate a user from a project. + * NOTE: This will not delete user from your Drupal system. + */ + public static function removeUserFromProject($project_user_id) { + $table = 'pheno_project_user'; + + \Drupal::database() + ->delete('pheno_project_user') + ->condition('project_user_id', $project_user_id) + ->execute(); + } + + /** + * Get all the projects a registered user is assigned to. + * All projects retured are those that have at least + * 1 essential trait. + * + * @param $user_id + * Drupal user id number of the currently logged user. + * + * @return array + * List projects. + */ + public static function getUserProjects($user_id) { + $trait_type = \Drupal::service('rawphenotypes.default_service')::getTraitTypes(); + $user_project = []; + + $sql = " + SELECT + t1.name, t1.project_id, + COUNT(CASE WHEN t2.type = :essential_trait THEN 1 END) AS essential_count + FROM chado.project AS t1 + INNER JOIN pheno_project_cvterm AS t2 USING (project_id) + LEFT JOIN pheno_project_user AS t3 USING (project_id) + WHERE t3.uid = :user_id + GROUP BY t1.project_id ORDER BY t1.project_id DESC + "; + $args = [':essential_trait' => $trait_type['type1'], ':user_id' => $user_id]; + + $query = \Drupal::database() + ->query($sql, $args); + + $query->allowRowCount = TRUE; + + if ($query->rowCount() > 0) { + foreach($query as $project) { + if ($project->essential_count > 0) { + $user_project[ $project->project_id ] = $project->name; + } + } + } + + return $user_project; + } +} \ No newline at end of file diff --git a/templates/template-rawphenotypes-message.html.twig b/templates/template-rawphenotypes-message.html.twig new file mode 100644 index 0000000..7815227 --- /dev/null +++ b/templates/template-rawphenotypes-message.html.twig @@ -0,0 +1,14 @@ + +{# +/** + * @file + * Theme messages. + */ +#} + + \ No newline at end of file diff --git a/templates/template-rawphenotypes-page-backup.html.twig b/templates/template-rawphenotypes-page-backup.html.twig new file mode 100644 index 0000000..ff3f545 --- /dev/null +++ b/templates/template-rawphenotypes-page-backup.html.twig @@ -0,0 +1,33 @@ +{# +/** + * @file + * Theme backup page. + */ +#} +{{ form.form_build_id }} +{{ form.form_token }} +{{ form.form_id }} + +
    + {{ title }} +
    + +
    +
    +
    +
    {{ current_user }}
    + +
    + +
    +
    + {{ upload_form }} +
    +
    +

    Manage My Files:

    +
    +
    +
    +
    \ No newline at end of file diff --git a/templates/template-rawphenotypes-page-instructions.html.twig b/templates/template-rawphenotypes-page-instructions.html.twig new file mode 100644 index 0000000..0f4d07d --- /dev/null +++ b/templates/template-rawphenotypes-page-instructions.html.twig @@ -0,0 +1,178 @@ + +{# +/** + * @file + * Theme instructions page. + */ +#} +{{ form.form_build_id }} +{{ form.form_token }} +{{ form.form_id }} + +{{ search_form.message_invalid_project }} + +
    + {{ title }} +
    + +
    +
    +
    +
    + {{ search_form.txt_search }} + +
    + +
    + +
     
    + +
    + {{ search_form.active_project }} + Change Project +
    + +
    + {{ search_form.sel_project }} +
    + +
    +
    +
    +
    + + +
    +

    Spreadsheet Tips

    +
      +
    • +

      Comments with instructions, tips and scales are included with the header for each trait.
      + Simply click the speech bubble icon beside the header.

      +
    • +
    • Essential Traits have green headers; all other traits are optional.

    • +
    • You should hide optional traits you are not taking data for by long pressing a column header then selecting “hide”.

    • +
    • We’ve included an easy calculator to determine “Days from Planting” in a separate tab.

    • +
    • +

      Special Interest Traits:
      Add a column on the spreadsheet for any other trait you are interested in collecting data for.
      + When uploading you will be asked to provide a description including units or scale used to take the measurement.

      +
    • +
    + + +
     
    +
    + +
    +

    These traits are essential to this project and data should be collected for all genotypes sent to you.

    + {{ search_form.tbl_project_headers_essential }} +

    Further reference for Reproductive stages:

    + Erskine et al. (1990) Stages of Development in Lentil. Experimental Agriculture. 26(3): 297-302. +
      +
    • R1 - First Bloom

      One open flower at any node

    • +
    • R3 - Early Pod

      Pod on nodes 10-13 of the basal primary branch visible

    • +
    • R5 - Full Seed

      Seeds in any single pod on nodes 10-13 of the basal primary branch are swollen and completely fill the pod cavity

    • +
    • R7 - Physiological Maturity

      The leaves start yellowing and 50% of the pods have turned yellow

    • +
    + Go to top page +
    + + {% if search_form.tbl_project_headers_optional %} +
    +

    + {% set projectname = search_form.active_project %} + {% if projectname == "AGILE: Application of Genetic Innovation in the Lentil Economy" %} + These traits are optional. Please contact the Project Manager if you are interested in these traits. + {% else %} + These traits are optional. We will be taking them in our location and have thus provided + our procedure in case you interested in taking these data in your location as well. + Feel free to record ANY data you are interested in (including traits not listed below + –just add a column to the accompanying data spreadsheet for traits not listed below). + {% endif %} +

    + {{ search_form.tbl_project_headers_optional }} + Go to top page +
    + {% endif %} + + {% if search_form.tbl_project_headers_subset %} +
    +

    + The following traits require a fair amount of work and, as such, are completely optional. + We will collect them in SK, if you are interested in these traits, please contact us to + make sure we are collecting the same thing. Note: These columns are hidden by default. + If you would like to record this data, select columns “X” and "AB" and either right-click + (computer) or long-press (tablet) the column header then select “Unhide”. For the following + “Subset Traits”, select 2 plants from the middle of each plot and randomly collect 10 + peduncles from each plant, ranging from the top to bottom, for a total of 20 peduncles. + If 20 peduncles cannot be obtained, sample from a 3rd plant. +

    + {{ search_form.tbl_project_headers_subset }} + Go to top page +
    + {% endif %} + + {% if search_form.tbl_project_headers_contributed %} +
    +

    + These traits are additional traits contributed by Phenotypic Data Collectors. + Trait definition and method of collection were provided by the data collector. +

    + {{ search_form.tbl_project_headers_contributed }} + Go to top page +
    + {% endif %} + +
    +

    Topic:

    +
    + +
    +
    +
    +
    +
    +
    +
    \ No newline at end of file diff --git a/tests/DataFactory.php b/tests/DataFactory.php deleted file mode 100644 index 6db7689..0000000 --- a/tests/DataFactory.php +++ /dev/null @@ -1,73 +0,0 @@ - $faker->name, - 'definition' => $faker->text, - ]; -}); - -/** @see StatonLab\TripalTestSuite\Database\Factory::define() */ -Factory::define('chado.db', function (Faker\Generator $faker) { - return [ - 'name' => $faker->name, - 'description' => $faker->text, - 'urlprefix' => $faker->url, - 'url' => $faker->url, - ]; -}); - -/** @see StatonLab\TripalTestSuite\Database\Factory::define() */ -Factory::define('chado.dbxref', function (Faker\Generator $faker) { - return [ - 'db_id' => factory('chado.db')->create()->db_id, - 'accession' => $faker->numberBetween(), - 'version' => $faker->numberBetween(), - 'description' => $faker->text, - ]; -}); - -/** @see StatonLab\TripalTestSuite\Database\Factory::define() */ -Factory::define('chado.cvterm', function (Faker\Generator $faker) { - return [ - 'cv_id' => factory('chado.cv')->create()->cv_id, - 'dbxref_id' => factory('chado.dbxref')->create()->dbxref_id, - 'name' => $faker->name, - 'definition' => $faker->text, - 'is_obsolete' => 0, - 'is_relationshiptype' => 0, - ]; -}); - -/** @see StatonLab\TripalTestSuite\Database\Factory::define() */ -Factory::define('chado.organism', function (Faker\Generator $faker) { - return [ - 'abbreviation' => $faker->name, - 'genus' => $faker->name, - 'species' => $faker->name, - 'common_name' => $faker->name, - 'type_id' => factory('chado.cvterm')->create()->cvterm_id, - ]; -}); - -/** @see StatonLab\TripalTestSuite\Database\Factory::define() */ -Factory::define('chado.feature', function (Faker\Generator $faker) { - return [ - 'name' => $faker->name, - 'uniquename' => $faker->unique()->name, - 'organism_id' => factory('chado.organism')->create()->organism_id, - 'type_id' => factory('chado.cvterm')->create()->cvterm_id, - ]; -}); diff --git a/tests/DatabaseSeeders/UsersTableSeeder.php b/tests/DatabaseSeeders/UsersTableSeeder.php deleted file mode 100644 index 2d28f29..0000000 --- a/tests/DatabaseSeeders/UsersTableSeeder.php +++ /dev/null @@ -1,28 +0,0 @@ - 'test user', - 'pass' => 'secret', - 'mail' => 'test@example.com', - 'status' => 1, - 'init' => 'Email', - 'roles' => [ - DRUPAL_AUTHENTICATED_RID => 'authenticated user', - ], - ]; - - // The first parameter is sent blank so a new user is created. - user_save(new \stdClass(), $new_user); - } -} diff --git a/tests/InstallTest.php b/tests/InstallTest.php deleted file mode 100644 index e068554..0000000 --- a/tests/InstallTest.php +++ /dev/null @@ -1,42 +0,0 @@ -assertTrue($exists, "Checking that $table table exists."); - } - - } -} diff --git a/tests/bootstrap.php b/tests/bootstrap.php deleted file mode 100644 index 6bf56ce..0000000 --- a/tests/bootstrap.php +++ /dev/null @@ -1,4 +0,0 @@ - span { - color: #666666; - font-size: 1.6em; - display: block; - line-height: normal; - margin: 0 auto; - position: relative; - text-align: center; - top: 200px; - width: 480px; -} - -.droppable-message span.text-drag { - width: 480px !important; -} - - -/* Choose file link */ -div.droppable-standard-upload { - color: #666666; - font-family: inherit; - font-size: 1.3em; - line-height: normal; - margin: 7px auto 0 auto; - min-height: 20px; - padding: 0; - position: relative; - top: 200px; - width: 165px; -} - -#droppable-dnd a, -#droppable-dnd a:link { - background: none; - border: none; - color: #118324; - font-family: inherit; - font-weight: 300; - left: 0; - line-height: 0; - margin: 0; - padding: 0; - text-decoration: underline; -} - - -/* Preview file */ -.droppable-preview { - position: absolute; - display: none; - height: 0; - overflow: hidden; - width: 0; -} - - -/* AJAX wait animation */ -div.droppable-controls { - left: 0; - position: absolute; - text-align: center; - width: 100%; - top: 80%; -} - -.droppable-controls div { - display: inline; - margin: 0 auto; - padding: 0 12px 0 0 !important; -} - - -/* Remove button */ -#edit-dnd-remove-button, .file { - display: none; - height: 0; - width: 0; -} - -/* Float text - select a project first */ -#float-text { - background-color: #FFFFFF; - border-radius: 5px; - font-size: 1.2em; - height: 110px; - position: absolute; - text-align: left; - padding: 35px 25px 20px 25px; - margin: 0 auto; - top: 355px; - width: 270px; - - -webkit-filter: drop-shadow(0 0 5px rgba(49, 67, 85, 0.9)); -} - -#float-text a, #float-text a:link, #float-text a:visited { - border: 1px solid #EAEAEA; - border-radius: 5px; - color: #FFFFFF; - display: block; - font-family: "Lucida Grande", "Lucida Sans Unicode", Verdana, sans-serif; - font-size: 0.8em; - font-weight: 300; - margin: 20px 0 20px 20px; - float: right; - padding: 7px 10px 7px 10px; - text-decoration: none; - text-align: center; - width: 80px; - z-index: 1000; -} - -#float-text a:hover { - border: 1px solid #666666; - color: #FFFFFF; - - -moz-box-shadow: 1px 1px 5px 1px #CCCCCC; - -webkit-box-shadow: 1px 1px 5px 1px #CCCCCC; - box-shadow: 1px 1px 5px 1px #CCCCCC; -} - -#float-text:before { - display: block; - border: 15px solid; - border-color: transparent transparent #FFFFFF transparent; - content: ' '; - height: 0; - position: absolute; - left: 25px; - width: 0; - top: -29px; - - -webkit-filter: drop-shadow(0 -4px 2px rgba(49, 67, 85, 0.1)); -} - -#edit-dnd-upload .throbber { - background: url('../../theme/img/wait-validating.gif') no-repeat bottom center !important; - padding: 90px 90px 10px 90px !important; -} - -#edit-dnd-upload .message { - display: block; - padding: 20px 0 0 0 !important; -} diff --git a/theme/css/rawpheno.upload.stage02.css b/theme/css/rawpheno.upload.stage02.css deleted file mode 100755 index ba0e084..0000000 --- a/theme/css/rawpheno.upload.stage02.css +++ /dev/null @@ -1,83 +0,0 @@ -/** - * @file - * Style phenotypes/upload stage 02 - describe additional trait - */ - -/* Import global css */ -@import 'rawpheno.upload.style.css'; - -/* Checkbox and Trait link */ -input[type=checkbox] { - margin-right: 10px; - /* All browsers except webkit*/ - transform: scale(1.2); - /* Webkit browsers*/ - -webkit-transform: scale(1.2); -} - -label.option { - color: #118324; - cursor: pointer; - font-weight: bold; - margin: 3px 1px; - padding: 4px 0px; - font-size: 2.5em; -} - -label.option:hover { - text-decoration: underline; -} - - -/* Form fields elements */ -input[type=text] { - width: 465px; -} - -.form-type-checkbox { - border: 1px solid #CCCCCC; - border-bottom: none; - background-color: #F7F7F7; - margin: 0 !important; - padding: 15px; -} - -div.container-trait { - border: 1px solid #CCCCCC; - border-top: none; -} - -div.container-trait div { - border-bottom: none; - background-color: #FFFFFF; -} - -div.container-trait div.form-item { - border: none; - margin-top: 0 !important; - padding: 10px 30px 10px 35px; -} - - -/* Progress bar when check box is clicked */ -.ajax-progress { - float: right; - margin-right: 20px; -} - - -/* Select field */ -select:disabled { - color: #000000; -} - -select.sel-header { - width: 465px; -} - -/* Line when similar header is found in describe header */ -div.container-option { - border-bottom: 1px solid #CCCCCC !important; - padding-bottom: 10px !important; - margin-bottom: 10px !important; -} diff --git a/theme/css/rawpheno.upload.stage03.css b/theme/css/rawpheno.upload.stage03.css deleted file mode 100755 index 01d1b20..0000000 --- a/theme/css/rawpheno.upload.stage03.css +++ /dev/null @@ -1,86 +0,0 @@ -/** - * @file - * Style phenotypes/upload stage 03 - save spreadsheet - */ - -/* Import global css */ -@import 'rawpheno.upload.style.css'; - -/* Remove tripal job message - for instructional video */ -/* -#messages { - display: none !important; -} -*/ - -/* Main container of the progress bar */ -.container-status { - background-color: #F7F7F7; - border: 1px solid #CCCCCC; - margin: 30px 0 0 0; - padding: 25px; -} - - -/* Links to the rest of the pages */ -.container-buttons { - padding: 30px 0 0 0; - margin: 30px 0 0 0; - text-align: center; - width: 100%; -} - -div.buttons-wrapper { - display: inline-block; - margin: 0 auto; - width: 90%; -} - -a.nav-buttons { - float: left; - padding: 20px 0 0 0; - height: 50px; - font-size: 1em; - margin: 0 2.5%; - text-align:center; - width: 20%; -} - -a.nav-buttons:nth-child(1) { - background: url('../../theme/img/icon-upload.gif') no-repeat center top; -} - -a.nav-buttons:nth-child(2) { - background: url('../../theme/img/icon-download.gif') no-repeat center top; -} - -a.nav-buttons:nth-child(3) { - background: url('../../theme/img/icon-rawdata.gif') no-repeat center top ; -} - -a.nav-buttons:nth-child(4) { - background: url('../../theme/img/icon-instructions.gif') no-repeat center top; -} - -a.nav-buttons span { - background-color: #FFFFFF; - display: block; - margin-top: 24px; - padding: 5px 0; -} - - -/* Status message */ -div.messages { - margin: 0 0 15px 0 !important; -} - -.fieldset-wrapper { - margin-top: 20px !important; -} - - -/* Select field */ -select:disabled { - color: #000000; -} diff --git a/theme/css/rawpheno.upload.style.css b/theme/css/rawpheno.upload.style.css deleted file mode 100755 index 2a4683c..0000000 --- a/theme/css/rawpheno.upload.style.css +++ /dev/null @@ -1,294 +0,0 @@ -/** - * @file - * Style phenotypes/upload global css - */ - -/* Import global css */ -@import 'rawpheno.style.css'; - -/* Page icon */ -.container-subtitle { - background: url('../img/icon-upload.gif') no-repeat left top; - background-position: 0 5px; -} - -.container-contents { - background-color: #FFFFFF; - padding: 0; - text-align: left; -} - - -/** - * Next step button: this button is not available - * on initial page and when error is detected. - */ -input#edit-next-step { - float: right; - margin: 15px 0 0 0; -} - -input#edit-next-step.form-button-disabled { - display: none; -} - - -/* Text stage indicator */ -div.subtitle-left { - font-size: 1.6em; - line-height: 35px; -} - - -/* Graphical stage indicator */ -#container-progress { - margin: 0 0 20px 0; -} - -div.progress-stage { - background-color: #000000; - border: none; - color: #FFFFFF; - cursor: pointer; - display: inline-block; - float: left; - font-size: 1em; - font-weight: 100; - height: 50px; - line-height: 50px; - margin: 5px 0 0 0; - width: 33.3333333%; -} - -div.progress-stage span { - background: url('../img/icon-chevron.gif') no-repeat; - border: none; - display: inline-block; - height: 50px; - margin: 0; - padding: 0 10px 0 0; -} - -div.progress-stage span:first-child { - background-position: 0 50%; - width: 10px; - margin: 0 1.8% 0 0; -} - -div.progress-stage span:last-child { - background-position: -18px 50%; - float: right; - width: 10px; -} - -div.progress-stage-todo { - opacity: 0.2; -} - -div.progress-stage-todo:hover { - opacity: 0.4; -} - -div.clear-both { - clear: both; -} - - -/* Help information */ -#container-progress #link-help { - position: absolute; - margin: -20px 0 0 0; -} - -#link-help:link, #link-help:active, #link-help:visited { - color: #118324; - text-decoration: none; -} - -#link-help:hover { - text-decoration: underline; -} - -#container-help-information { - border-bottom: 1px solid #CCCCCC; - cursor: pointer; - display: none; - padding: 10px 0; - margin: 0 0 10px 0; - - -moz-box-shadow: 0 5px 7px -9px #333333; - -webkit-box-shadow: 0 5px 7px -9px #333333; - box-shadow: 0 5px 7px -9px #333333; -} - -#container-help-information:hover { - border-bottom: 1px solid #AAAAAA; -} - -#container-help-information h2 { - font-size: 1.3em !important; - margin: 0; - padding: 0; -} - -#container-help-information ul { - list-style-type: none; - padding: 0; - margin: 10px 0 0 0; -} - -#container-help-information li { - display: table-cell; - margin: 0; - padding: 0 0 0 40px; - width: 33.3333333%; -} - - -/* Add stage number image as background */ -#container-help-information ul li:nth-child(1) { - background: url('../img/01_stage.gif') no-repeat left top; -} - -#container-help-information ul li:nth-child(2) { - background: url('../img/02_stage.gif') no-repeat left top; - border-left: 10px solid #FFFFFF; - border-right: 10px solid #FFFFFF; -} - -#container-help-information ul li:nth-child(3) { - background: url('../img/03_stage.gif') no-repeat left top; -} - - -/* Error message box */ -.messages { - margin: 0 0 15px 0 !important; -} - -div.status { - margin-top: 25px !important; -} - -/* Validation result container */ -.rawpheno-validate-progress { - border: none !important; - padding: 0 !important; - margin: 0 0 40px 0 !important; -} - - -/* Validation result */ -div.rawpheno-validate-progress { - cursor: pointer; -} - -fieldset { - margin: 0 !important; - padding: 0 !important; - - -moz-box-shadow: 1px 1px 7px 2px #EAEAEA; - -webkit-box-shadow: 1px 1px 7px 2px #EAEAEA; - box-shadow: 1px 1px 7px 2px #EAEAEA; -} - -.fieldset-wrapper { - padding: 30px 20px 20px 20px !important; -} - -legend { - height: 40px !important; - line-height: 35px !important; -} - -.error-main { - list-style-type: none; - margin: 0 !important; - padding: 0 !important; -} - -ul.error-main > li { - padding: 0 0 10px 15px; -} - -ul.error-main > li em { - margin-left: 18px; -} - -ul.error-main { - padding: 15px; - margin: 0 15px 0 0; -} - -.error-detail { - background-color: #F7F7F7; - margin: 0 20px 0 0 !important; - padding: 10px !important; -} - -li.error-item { - margin-left: 20px; -} - -li em { - font-weight: bold; -} - -.info { - background: url('../img/icon-info.jpg') no-repeat; - background-position: 5px 0; -} - - -/* Validation Result - Success and Failed rule */ -.success { - background: url('../img/icon-success.jpg') no-repeat; - background-position: 5px 0; -} - -.failed { - background: url('../img/icon-failed.jpg') no-repeat; - background-position: 5px 0; -} - -.todo { - background: url('../img/icon-todo.jpg') no-repeat; - background-position: 5px 0; - opacity: 0.2; -} - -/* Next stage indicator preceding next step button */ -span.text-next-step { - font-size: 0.9em; - font-weight: bold; - font-family: "Lucida Grande", "Lucida Sans Unicode", Verdana, sans-serif; - float: right; - padding: 20px 20px 0 0; -} - - -/* Style select a project select box */ -#rawpheno-select-project-field { - background: #FCFDFD url('../img/chrome-selectbox-down-arrow.gif') no-repeat; - background-position: 99.3% 50%; - border: 1px solid #AAAAAA; - padding: 10px; - font-size: 1.0em; - - -moz-box-shadow: 1px 1px 4px 3px #EAEAEA; - -webkit-box-shadow: 1px 1px 4px 3px #EAEAEA; - box-shadow: inset 1px 1px 4px 3px #EAEAEA; - - width: 100%; - -webkit-appearance: none; -} - -#rawpheno-select-project-field option { - background-color: #FFFFFF; -} - -div.form-item-sel-project { - border: none; - padding: 0; - margin-bottom: 20px !important; -} diff --git a/theme/img/01_stage.gif b/theme/img/01_stage.gif deleted file mode 100644 index 23f5f61..0000000 Binary files a/theme/img/01_stage.gif and /dev/null differ diff --git a/theme/img/02_stage.gif b/theme/img/02_stage.gif deleted file mode 100644 index 7a3acd9..0000000 Binary files a/theme/img/02_stage.gif and /dev/null differ diff --git a/theme/img/03_stage.gif b/theme/img/03_stage.gif deleted file mode 100644 index c598633..0000000 Binary files a/theme/img/03_stage.gif and /dev/null differ diff --git a/theme/img/cloud-image.png b/theme/img/cloud-image.png deleted file mode 100755 index ff4c45a..0000000 Binary files a/theme/img/cloud-image.png and /dev/null differ diff --git a/theme/img/env.gif b/theme/img/env.gif deleted file mode 100755 index a6f9b25..0000000 Binary files a/theme/img/env.gif and /dev/null differ diff --git a/theme/img/icon-chevron.gif b/theme/img/icon-chevron.gif deleted file mode 100644 index add23be..0000000 Binary files a/theme/img/icon-chevron.gif and /dev/null differ diff --git a/theme/img/icon-delete-file.png b/theme/img/icon-delete-file.png deleted file mode 100644 index f793116..0000000 Binary files a/theme/img/icon-delete-file.png and /dev/null differ diff --git a/theme/img/icon-download-file.png b/theme/img/icon-download-file.png deleted file mode 100644 index cc245f8..0000000 Binary files a/theme/img/icon-download-file.png and /dev/null differ diff --git a/theme/img/icon-download.gif b/theme/img/icon-download.gif deleted file mode 100644 index 56820fd..0000000 Binary files a/theme/img/icon-download.gif and /dev/null differ diff --git a/theme/img/icon-excel-file.gif b/theme/img/icon-excel-file.gif deleted file mode 100755 index 8bd5a5d..0000000 Binary files a/theme/img/icon-excel-file.gif and /dev/null differ diff --git a/theme/img/icon-failed.jpg b/theme/img/icon-failed.jpg deleted file mode 100644 index c4ccb07..0000000 Binary files a/theme/img/icon-failed.jpg and /dev/null differ diff --git a/theme/img/icon-folder.gif b/theme/img/icon-folder.gif deleted file mode 100755 index f8eee3f..0000000 Binary files a/theme/img/icon-folder.gif and /dev/null differ diff --git a/theme/img/icon-info.jpg b/theme/img/icon-info.jpg deleted file mode 100644 index 855e008..0000000 Binary files a/theme/img/icon-info.jpg and /dev/null differ diff --git a/theme/img/icon-leaf.gif b/theme/img/icon-leaf.gif deleted file mode 100644 index 5c9a8e0..0000000 Binary files a/theme/img/icon-leaf.gif and /dev/null differ diff --git a/theme/img/icon-notification-backup.gif b/theme/img/icon-notification-backup.gif deleted file mode 100755 index 3542d1f..0000000 Binary files a/theme/img/icon-notification-backup.gif and /dev/null differ diff --git a/theme/img/icon-notification-green.gif b/theme/img/icon-notification-green.gif deleted file mode 100755 index 283bcb9..0000000 Binary files a/theme/img/icon-notification-green.gif and /dev/null differ diff --git a/theme/img/icon-notification-help.gif b/theme/img/icon-notification-help.gif deleted file mode 100755 index 196d036..0000000 Binary files a/theme/img/icon-notification-help.gif and /dev/null differ diff --git a/theme/img/icon-notification-red.gif b/theme/img/icon-notification-red.gif deleted file mode 100755 index 3b3fe5d..0000000 Binary files a/theme/img/icon-notification-red.gif and /dev/null differ diff --git a/theme/img/icon-notification-upload.gif b/theme/img/icon-notification-upload.gif deleted file mode 100755 index 1cb6576..0000000 Binary files a/theme/img/icon-notification-upload.gif and /dev/null differ diff --git a/theme/img/icon-notification-yellow.gif b/theme/img/icon-notification-yellow.gif deleted file mode 100755 index cf0051e..0000000 Binary files a/theme/img/icon-notification-yellow.gif and /dev/null differ diff --git a/theme/img/icon-rawdata.gif b/theme/img/icon-rawdata.gif deleted file mode 100644 index 03f6541..0000000 Binary files a/theme/img/icon-rawdata.gif and /dev/null differ diff --git a/theme/img/icon-restore-file.png b/theme/img/icon-restore-file.png deleted file mode 100644 index 0211a0e..0000000 Binary files a/theme/img/icon-restore-file.png and /dev/null differ diff --git a/theme/img/icon-success.jpg b/theme/img/icon-success.jpg deleted file mode 100644 index 18c7b6f..0000000 Binary files a/theme/img/icon-success.jpg and /dev/null differ diff --git a/theme/img/icon-todo.jpg b/theme/img/icon-todo.jpg deleted file mode 100644 index ba7fb74..0000000 Binary files a/theme/img/icon-todo.jpg and /dev/null differ diff --git a/theme/img/icon-trash.png b/theme/img/icon-trash.png deleted file mode 100644 index ad56408..0000000 Binary files a/theme/img/icon-trash.png and /dev/null differ diff --git a/theme/img/line-arrow-up.gif b/theme/img/line-arrow-up.gif deleted file mode 100755 index 979b91d..0000000 Binary files a/theme/img/line-arrow-up.gif and /dev/null differ diff --git a/theme/img/menu-collapsed.png b/theme/img/menu-collapsed.png deleted file mode 100644 index 91f3fd4..0000000 Binary files a/theme/img/menu-collapsed.png and /dev/null differ diff --git a/theme/img/message-16-warning.png b/theme/img/message-16-warning.png deleted file mode 100644 index ffc4317..0000000 Binary files a/theme/img/message-16-warning.png and /dev/null differ diff --git a/theme/img/throbber-active.gif b/theme/img/throbber-active.gif deleted file mode 100644 index f88bfde..0000000 Binary files a/theme/img/throbber-active.gif and /dev/null differ diff --git a/theme/img/wait-validating.gif b/theme/img/wait-validating.gif deleted file mode 100644 index 2fc8027..0000000 Binary files a/theme/img/wait-validating.gif and /dev/null differ diff --git a/theme/js/rawpheno.backup.script.js b/theme/js/rawpheno.backup.script.js deleted file mode 100755 index 465e6cb..0000000 --- a/theme/js/rawpheno.backup.script.js +++ /dev/null @@ -1,125 +0,0 @@ -/** - * @file - * Manage behavior in backup page - */ -(function($) { - Drupal.behaviors.rawphenoBackupBehaviours = { - attach: function (context, settings) { - $('.droppable-browse-button').text('choose your file'); - - // Drop area element. - var dropZone = document.getElementById('droppable-bdnd'); - // Initial/default meassage of the drop area. - var dropMessage = $('.droppable-message'); - - // Add corresponding message on mouse event. - if (dropZone) { - // User drags file into the drop area. - dropZone.addEventListener('dragover', function() { - dropMessage.css('border','3px dashed #AAAAAA'); - // Create a new instruction to user. - dropMessage.children().hide(); - $('.droppable-message span').eq(0).show().text('Drop to backup your spreadsheet'); - }); - - // User cancels file drop. - dropZone.addEventListener('dragleave', function() { - dropMessage.css('border','none'); - // Create a new instruction to user. - $('.droppable-message span').eq(0).text('Drag your Microsoft Excel Spreadsheet file here'); - dropMessage.children().show(); - }); - - // User drops file. - dropZone.addEventListener('drop', function() { - dropMessage.css('border','none'); - // Create a new instruction to user. - $('.droppable-message span').eq(0).text('Drag your Microsoft Excel Spreadsheet file here'); - dropMessage.children().show(); - // AJAX dies or is frozen after an error. - if ($('div.messages').length) { - alert(); - - // Remove validation result and drupal error message. - $('div.messages').remove(); - } - }); - } - - // Remove validation result and error messages as soon - // as DND receives a file. This is for both drag and drop and - // using the choose a file link (file browser). - $(document).ajaxStart(function() { - // AJAX start. - if ($('div.messages').length) { - $('div.messages').remove(); - } - }); - - // Reveal validation result text. - $('div.container-cell').click(function() { - // Examine the height. When height is 50px, user wants to disclose the entire cell contents - // else, restore it to initial state. - var id = $(this).attr('id'); - var i = id.replace(/vn-file-|vr-file-/, ''); - - var h = $(this).css('height'); - if (h == '65px') { - $('#vr-file-' + i).css('height', '100%'); - - if ($('#vn-file-' + i)) { - $('#vn-file-' + i).css('height', '100%'); - } - } - else { - $('#vr-file-' + i).css('height', '65px'); - - if ($('#vn-file-' + i)) { - $('#vn-file-' + i).css('height', '65px'); - } - } - }); - - - // Confirm action. - $('table a.link-archive, table a.link-delete').click(function(i) { - var title = i.target.title; - - var r = confirm('Are you sure want to ' + title + '?'); - if (!r) return false; - }); - - // Show/hide archive table. - $('#container-archive-files a.link-archive').click(function(event) { - event.preventDefault(); - var archiveTable = $('#tbl_project_archive_file'); - if (archiveTable.is(':hidden')) { - archiveTable.show(); - } - else { - archiveTable.hide(); - } - }) - - // Reload the page when file has no error. - $(document).ajaxComplete(function() { - //ajax end - if ($('.rawpheno-validate-progress').length <= 0 && $('.messages').length <= 0) { - var link = '../raw/backup/up'; - location.assign(link); - } - }); - - - // For security, suppress any alert message showing snippet of code or - // module settings to the user. - alert = function(e) { - // Create an error message. - $('div.droppable-preview-file').hide(); - $('
    The specified file is not a valid Microsoft Excel File and could not be uploaded.
    ').insertAfter('select'); - - return false; - }; - } - }; -}(jQuery)); diff --git a/theme/js/rawpheno.download.script.js b/theme/js/rawpheno.download.script.js deleted file mode 100755 index af2048d..0000000 --- a/theme/js/rawpheno.download.script.js +++ /dev/null @@ -1,152 +0,0 @@ -/** - * @file - * Manage behavior in download form - */ -(function($) { - Drupal.behaviors.rawphenoSelTrait = { - attach: function (context, settings) { - // When project is selected. - // Reset the form - uncheck and unselect. - - // Reference project select box. - var selPrj = $('#download-sel-project'); - // Reference locations select box, checkbox, and label. - var locations = {'selectbox': $('#download-sel-location'), - 'checkbox' : $('#chk-select-all-locations'), - 'label' : $('label[for="chk-select-all-locations"]')}; - - // Reference traits. - var traits = {'selectbox': $('#download-sel-trait'), - 'checkbox' : $('#chk-select-all-traits'), - 'label' : $('label[for="chk-select-all-traits"]')}; - - // Add event listener to select a project field. - selPrj.focus(function() { - // Reset locations and related fields. - resetSelect(locations.selectbox, ''); - checkUncheck(locations.checkbox, ''); - - // Reset traits and related fields. - resetSelect(traits.selectbox, ''); - checkUncheck(traits.checkbox, ''); - }); - - // Add event listener to locations checkboxes/labels. - locations.label.add(locations.checkbox).click(function(e) { - // Check the state of the checkbox and reset or select the select box. - var s = whatState(locations.checkbox); - - // Support for Safari and IE - // Source: http://stackoverflow.com/questions/5899783/detect-safari-chrome-ie-firefox-opera-with-user-agent - // http://stackoverflow.com/questions/19999388/check-if-user-is-using-ie-with-jquery/21712356#21712356 - var ua = window.navigator.userAgent; - var old_ie = ua.indexOf('MSIE '); - var new_ie = ua.indexOf('Trident/'); - - if ((navigator.userAgent.indexOf('Safari') != -1 && navigator.userAgent.indexOf('Chrome') == -1) || old_ie > -1 || new_ie > -1) { - s = !s; - } - - resetSelect(locations.selectbox, s); - // Reset the traits select box and uncheck the traits checkbox when this checkbox is clicked. - resetSelect(traits.selectbox, ''); - checkUncheck(traits.checkbox); - - // Set the focus to locations select box and first option in the list. - if (s == 'selected') { - locations.selectbox.scrollTop(0).focus(); - } - }); - - // Add event listener to traits checkboxes/labels - traits.label.add(traits.checkbox).click(function(e) { - // Check the state of the checkbox and reset or select the select box. - var s = whatState(traits.checkbox); - - resetSelect(traits.selectbox, s); - traits.selectbox.scrollTop(0).focus(); - }) - - // Add event listener to select boxes. - // Locations. - locations.selectbox.change(function(e) { - // Reset checkboxes. - checkUncheck(locations.checkbox); - checkUncheck(traits.checkbox); - }); - - // Traits. - traits.selectbox.change(function(e) { - // Reset checkboxes. - checkUncheck(traits.checkbox); - }); - - // Environment data file option. - // Variable settings passed on by hook_form() indicating - // if filter combination resulted an enivronment data file. - var env = Drupal.settings.rawpheno.envdata_option; - // Disabled by default - nothing selected. - // #disabled options in form api reloads the form - // instead of redirecting to Tripal Download. - $('#chk-envdata').attr('disabled', 'disabled'); - - // Disable fields on AJAX (selectbox, checkbox, buttons and all). - $(document).ajaxStart(function() { - //ajax start - $(':input').attr('disabled', 'disabled'); - }).ajaxComplete(function() { - //ajax end - resetSelect(traits.selectbox, '') - $(':input').removeAttr('disabled'); - - // Make environment data available when filter combination - // yields environment data. - if (!env) { - $('#chk-envdata').attr({ - 'checked' : false, - 'disabled': 'disabled' - }); - } - }); - - - // Reference form submit button. - var btnSubmit = $('#edit-download-submit-download'); - - // Submit button event with timer. - // Server side scrip in sync with this timer. - btnSubmit.click(function(e) { - $(this).hide(); - $('#div-button').once(function() { - $(this).append('Please wait...'); - }); - }); - - - - // Function return the state of the checkbox. - function whatState(fld) { - return (fld.is(':checked')) ? 'selected' : ''; - } - - // Function reset and select select box. - function resetSelect(fld, v) { - $(fld).find('option').each(function() { - var s = (v == '') ? '' : v; - $(this).attr('selected', s); - }); - } - - // Function uncheck and check checkbox. - function checkUncheck(fld) { - // Uncheck if it is checked. - if (fld.is(':checked')) { - fld.attr('checked', false); - } - } - - // Suppress any AJAX error showing snippet of code. - alert = function(){ }; - } - }; -}(jQuery)); diff --git a/theme/js/rawpheno.notification.block.script.js b/theme/js/rawpheno.notification.block.script.js deleted file mode 100755 index c46a89b..0000000 --- a/theme/js/rawpheno.notification.block.script.js +++ /dev/null @@ -1,62 +0,0 @@ -/** - * @file - * Manage behavior in notification block. - */ -(function($) { - Drupal.behaviors.notificationBlock = { - attach: function (context, settings) { - // Reference elements. - var helpLink = $('#rawpheno-notification-need-help a'); - var helpTopic = $('#rawpheno-notification-help-topics'); - var myDashboard = $('#rawpheno-notification-dashboard'); - var whyBackupLink = $('#rawpheno-notification-why-backup-link'); - var whyBackupInfo = $('#rawpheno-notification-info-why-backup'); - var okBackup = $('#rawpheno-notification-ok-button'); - - // Need Help? - helpLink.click(function(e) { - e.preventDefault(); - - var btn = $(this); - if (btn.text() == 'Close Help') { - btn.text('Need Help?'); - - helpTopic.slideUp().hide() - myDashboard.slideDown(); - } - else { - btn.text('Close Help'); - - myDashboard.slideUp().hide(); - helpTopic.slideDown() - } - }); - - // Why Backup? - whyBackupLink.click(function(e) { - e.preventDefault(); - - whyBackupInfo.show().slideDown(); - }); - - // Why Backup acknowledged. - okBackup.click(function(e) { - e.preventDefault(); - - whyBackupInfo.slideUp().hide(); - }); - - // Convert help topic select box to a jump menu. - if ($('#rawpheno-notification-helptopic-select').length) { - $('#rawpheno-notification-helptopic-select').change(function() { - var url = $(this).attr('value'); - - if (url) { - window.open(url, '_blank'); - } - }); - } - - } - }; -}(jQuery)); diff --git a/theme/js/rawpheno.rawdata.script.js b/theme/js/rawpheno.rawdata.script.js deleted file mode 100755 index defcb7f..0000000 --- a/theme/js/rawpheno.rawdata.script.js +++ /dev/null @@ -1,1244 +0,0 @@ -/** - * @file - * Create graphical representation of phenotypic data. - */ -(function($) { - Drupal.behaviors.rawphenoRawDataCreateHeatMap = { - attach: function (context, settings) { - //// - // Variable to hold the default option when a category is selected. - var categoryOptionLocation, categoryOptionYear; - - // Add event listener to window resize. - // Reposition elements with the new window width. - d3.select(window).on('resize', render); - - // Add event listener to select fields. - $('#container-form-select select').change(function(i) { - // Clear warning message. - if ($('.warn-message')) { - $('.warn-message').remove(); - } - - // Remove previous barchart categorize options and start with a new set of selectboxes. - if($('#chart-control-container')) { - // Remove container and everything inside it (children). - $('#chart-control-container').remove(); - } - - // Determine which select box was changed. - var selectID = i.target.id; - - // UPDATE HEATMAP OR DISPLAY BARCHART. - // When user select a project. - if (selectID == 'rawdata-sel-project') { - // Start loading animation when user changes project. - loading('heatmap'); - - // Reset the select trait default to the word - // Select a trait to highlight in the chart. - $('select[name=rawdata-sel-trait] option:first-child').attr('selected', 'selected'); - - // Clear the chart before visualizing a dataset. - $('g').remove(); - - // Start the heat map chart. - // Read JSON data for heat map. - var project_id = i.target.value; - heatmapFile = file + 'rawdata/?project_id=' + project_id; - d3.json(heatmapFile, function(error, data) { - if (error) { - // Error reading JSON. - throw error; - } - else if(data == 0) { - // No data. - noData('.data-chart'); - } - else { - // Initialize heat map chart by adding all elements (rect, g, text, etc.) into the DOM, - // and then call render function and position these elements in the right place. - initializeHeatmapChart(data); - } - }); - } - else { - var project_id = $('#rawdata-sel-project').val(); - var traitId = i.target.value; - var traitSelectedName = $('#' + i.target.id + ' option:selected').text(); - - // Mark rep where trait was measured. - markRep(project_id, traitId, traitSelectedName); - // Start loading animation when user changes project. - loading('barchart'); - - // Extract the available location and year for this particular trait. - // The values will be used to populate select boxes. - var categoryFile = file + 'rawdata_trait_category' + '?project_id=' + project_id + '&trait_id=' + traitId; - - d3.json(categoryFile, function(error, categoryData) { - if (error) { - // Error reading JSON. - throw error; - } - else if(categoryData == 0) { - // No data. - noData('.bar-chart'); - } - else { - // Default the barchart to the first entry/option in the select box. - var defaultYear = categoryData.planting_date[0]; - - // Render bar chart. - // Default to location and a year - first in the list. - var barchartFile = file + 'rawdata_trait' + '?project_id=' + project_id + '&trait_id=' + traitId + '&category=location' + '&option=' + defaultYear; - - d3.json(barchartFile, function(error, barchartData) { - if (error) { - // Error reading JSON. - throw error; - } - else if(barchartData == 0) { - // No data. - noData('.bar-chart'); - } - else { - // Initialize barchart by adding all elements (rect, g, text, etc.) into the DOM, - // and then call render function and position these elements in the right place. - initializeBarChart(barchartData, categoryData, project_id, traitId); - } - }); - } - }); - } - }); - - // Leaf path/symbol used to mark a rep. - var leaf = 'm2.46906,17.20495c-0.42208,-1.86678 -3.37632,-8.39646 3.06637,-10.19122c6.44269,-1.79476 9.1144,-2.35212 11.17243,-3.20865c2.05803,-0.85653 5.22901,-3.1601 5.15685,-3.77696c-0.07214,-0.61685 1.40663,9.13151 -0.30335,14.13307c-1.70998,5.00156 -5.52874,7.53668 -10.36077,7.28237c-4.83199,-0.25432 -5.67973,-1.27159 -6.61222,-1.10203c-0.9325,0.16953 -1.56016,2.17883 -2.03454,3.39088c-0.47435,1.21206 -0.59338,1.35635 -0.50862,1.35635c0.08476,0 -1.78022,-1.01725 -2.03454,-1.35635c-0.25431,-0.33909 3.98429,-6.86654 8.98585,-8.64676c5.00161,-1.78023 7.19302,-4.28732 7.88385,-5.51021c0.69078,-1.22288 1.52588,-3.13656 1.52588,-3.17895c0,0.04239 -1.15433,3.08336 -3.6452,4.36577c-2.49086,1.2824 -7.79905,2.96701 -9.91833,4.66247c-2.11929,1.69544 -1.95157,3.647 -2.37363,1.78021l-0.00001,0z'; - - // Tooltip/information box. - var infoBox = d3.select('body') - .append('div') - .attr('class', 'tool-tip') - .style('opacity', 0); - - // Div container of svg canvas. - // The height and width of the chart canvas is based on the height and width of this - // HTML element (DIV). - var divChartContainer = d3.select('#container-rawdata'); - - // Heatmap colour code. - var color = ['#FFFFFF', '#E2EFDA', '#C6E0BA', '#A9D08E', '#70AD47', '#548235', '#375623']; - - // List of traits measured in a rep. - var traitsList = []; - - var dataSet = {}, chartDimension = {}, rectDimension = {}; - - // Canvas width and height; - var width, height, margin = {}; - - // Chart margins. - // NOTE: THIS MARGIN IS FOR BOTH HEATMAP AND HISTOGRAM! - margin.top = 40; - margin.left = 90; - margin.bottom = 80; - margin.right = 30; - - // Use this variable to increase or decrease - // the current margin bottom value and not affect - // the margin for the histogram. - // @see note above. - - // Adjusted margin bottom to accommodate - // text wrapping of long location value. - marginAdjust = 30; - - // x axis scale. - var x0, xAxis; - - // Height of the chart defined in the css rule for #container-rawdata. - height = parseInt(divChartContainer.style('height'), 10); - chartDimension.height = height - margin.top - (margin.bottom + marginAdjust); - - // Main svg canvas of the heat map. - var svg; - - // Barchart variables - // Main the svg canvas of the bar chart. - var bsvg; - var bx0, bxAxis, bins, dataByBins, dataByTitles, traitSelectedId, traitSelectedName; - // 10 px either side of a bin set (10X2) - var gutter = 20; - var barchartColor = d3.scale.category20b().domain([0, 20]); - var categorize; - - // Option to clear the chart. - $('#container-marker-information, #container-marker-information a').click(function(event) { - event.preventDefault(); - - // Close window. - infoWindow('off'); - // Remove all markers. - delMarker(''); - // Remove barchart. - $('#container-barchart').remove(); - }); - - // URL to knowpulse. - var file = $('#rawdata-json').val(); - - // Start loading animation upon initial load of the page. - loading('heatmap'); - - // Start the heat map chart default to the first project in the select a - // project select box. - // Read JSON data for heat map. - var project_id = $('#rawdata-sel-project').val(); - heatmapFile = file + 'rawdata/?project_id=' + project_id; - d3.json(heatmapFile, function(error, data) { - if (error) { - // Error reading JSON. - throw error; - } - else if(data == 0) { - // No data. - updateWidth(); - noData('.data-chart'); - } - else { - // Initialize heat map chart by adding all elements (rect, g, text, etc.) into the DOM, - // and then call render function and position these element in the right place. - initializeHeatmapChart(data); - } - }); - - - // Function to add all heat map chart elements into the DOM. - // The height attribute or property of the elements remains constant whereas the - // width is to be determined by the render function. - function initializeHeatmapChart(data) { - // Remove loading animation. - $('.win-loading').remove(); - - // Main svg canvas. - svg = d3.select('.data-chart'); - svg.attr('height', height); - - // CHART DATA - // Group data with location as primary key. - // year as secondary key and rep as tertiary. - // data /location/year/rep - values. - var n = 0; - traitsList = []; - - dataByLocation = d3.nest() - .key(function(d) { return d.location; }) - .key(function(d) { return d.year; }) - .sortKeys(d3.descending) - .key(function(d) { return d.rep; }) - .sortKeys(d3.sort) - .rollup(function(v) { - // Splice the data array values specific to a particular location, year and rep. - var objEntry = v[0]; - - n++; - // This array associates an id (a rect) to list of traits the total count is based. - traitsList[n] = objEntry.type_id; - - return { - 'count': objEntry.trait, - 'id': 'obj-' + n.toString() - } - }) - .entries(data); - - // All year. - var dataByYear = d3.nest() - .key(function(d) { return d.year }) - .sortKeys(d3.descending) - .entries(data); - - // All reps. - var dataByRep = d3.nest() - .key(function(d) { return d.rep; }) - .entries(data); - - // Count number of location, year and rep. - dataSet.countLocation = dataByLocation.length; - dataSet.countYear = dataByYear.length; - dataSet.countRep = dataByRep.length; - - // CHART ELEMENTS - // Titles and legend - heatmapChartInfo(); - - // Scales - // X axis (locations) - x0 = d3.scale.ordinal(); - xAxis = d3.svg.axis().orient('bottom'); - - // Y axis (year) - var y0 = d3.scale.ordinal() - .rangeRoundBands([0, chartDimension.height]); - - var yAxis = d3.svg.axis() - .scale(y0) - .orient('left'); - - // Main chart wrapper. Add g element on position - // x = margin.left and y = margin.top - var chartWrapper = svg.append('g') - .attr('id', 'g-chart-wrapper') - .attr('transform', 'translate(' + margin.left + ',' + margin.top + ')'); - - // Each location, add a g container to hold a set of reps and year. - var locationWrapper = chartWrapper - .selectAll('g') - .data(dataByLocation).enter() - .append('g') - .attr('class', 'g-each-location') - .attr('id', function(d, i) { return 'location-' + i; }); - - // Each year, add a g container to hold a set of reps in a year. - rectDimension.height = Math.round(chartDimension.height/dataSet.countYear); - var yearWrapper = locationWrapper.selectAll('g') - .data(function(d) { return d.values; }).enter() - .append('g') - .attr('class', 'g-each-year') - .attr('transform', function(d, i) { - return 'translate(0, '+ (i * rectDimension.height) +')'; - }); - - // In g year container, add the reps (rect). - yearWrapper.selectAll('rect') - .data(function(d) { return d.values; }).enter() - .append('g') - .attr('id', function(d) { return d.values.id; }) - .attr('class', 'g-each-rep') - - .on('mousemove', function(d) { - d3.select(this).style('opacity', 0.5); - infoBox.transition().style('opacity', 1); - infoBox - .html('Rep ' + d.key + ': '+ d.values.count + ' Traits') - .style('left', (d3.event.pageX + 10) + 'px') - .style('top', (d3.event.pageY) + 'px'); - }) - .on('mouseout', function(d) { - d3.select(this).style('opacity', 1); - infoBox.transition().style('opacity', 0); - }) - - .append('rect') - .attr('class', 'rect-each-rep') - .attr('fill', function(d) { - var c = setColour(parseInt(d.values.count)); - return color[c]; - }) - .attr('height', rectDimension.height) - .attr('y', 0); - - // With all reps in place, wrap in a rect with thick border to visually - // group a location with its reps. - locationWrapper.append('rect') - .attr('class', 'rect-wrap-each-location') - .attr('height', rectDimension.height * dataSet.countYear); - - // Finally, add scales - // x axis (location). - x0.domain(dataByLocation.map(function(d) { return d.key; })); - chartWrapper.append('g') - .attr('id', 'g-x-axis') - .attr('class', 'axis') - .attr('transform', 'translate(0,' + rectDimension.height * dataSet.countYear +')'); - - // y axis (year). - y0.domain(dataByYear.map(function(d) { return d.key; })); - chartWrapper.append('g') - .attr('class', 'axis') - .attr('transform', 'translate(1,0)') - .call(yAxis); - - - // Render heat map chart elements into the right place. - render(); - } - // End initialize heat map chart. - - // Function to add all bar map chart elements into the DOM. - // The height attribute or property of the elements remains constant whereas the - // width is to be determined by the render function. - function initializeBarChart(data, dataCategory, pId, tId) { - // Remove loading animation. - $('.win-loading').remove(); - - // Add filter/categorize controls only when none is available in the DOM. - if ($('#chart-control-container').length <= 0) { - // Add the container. - $('#container-barchart') - .prepend('
    Categorize by:
    '); - - // Add select boxes. - // Select a category. - var selCategory = $('').appendTo('#chart-control-container'); - // On each new trait, default the select set to location and the first option - // in the select option select box. - barchartFillSelect('location', dataCategory); - - // Once the element is in the DOM, attach an event listener to them. - var catSel = $('#container-barchart select'); - catSel.change(function() { - // Read the values in the select box and process accordingly. - var selValues = []; - catSel.each(function(i) { - selValues[i] = $(this).val(); - }); - - // Store the value of select category. - // Either by location or year. - categorize = selValues[0]; - - var selId = $(this).attr('id'); - // Category value, Option value, the select box changed and the category data. - barchartCategorize(selValues[0], selValues[1], selId, dataCategory, pId, tId); - }); - } - - // Add all barchart elements. - bsvg = d3.select('.bar-chart') - .attr('height', height - 30); - - var wrapper = bsvg.append('g') - .attr('id', 'g-main-barchart-container') - .attr('transform', 'translate(' + (margin.left + gutter) + ',' + margin.top + ')'); - - // CHART DATA. - // Titles / data. - dataByTitles = d3.nest() - .key(function(d) { return d.title; }) - .sortKeys(d3.ascending) - .entries(data.data); - - // Bins. - bins = data.bin; - - // Count the number of rows per title in the same bin. - // Bin / title / count stocks in the same bin. - dataByBins = d3.nest() - .key(function(d) { return d.bin; }) - .key(function(d) { return d.title; }) - .rollup(function(v) { - return v.length; - }) - .entries(data.data); - - var allCount = dataByBins.map(function(d) { - var c = d.values.map(function(d) { return d.values; }); - return c; - }); - allCount = d3.merge(allCount); - - // CHART ELEMENTS. - // Titles and legend - barChartInfo(categorize); - - // X axis scale. - bx0 = d3.scale.ordinal().domain(bins); - bxAxis = d3.svg.axis() - .orient('bottom') - .scale(bx0); - - // Y axis scale. - // From the stock count, get the maximum. - var maxCount = d3.max(allCount); - - var by0 = d3.scale.linear() - .domain([0, maxCount]) - .range([chartDimension.height + marginAdjust, 0]) - .nice(); - - - // Compute the number of tick marks. - var noTickMark; - if (maxCount >= 1 && maxCount <= 10) { - // Use the max number when maxCount is in range 1 to 10 - noTickMark = maxCount; - } - else { - // Let d3 use scale to generate all tick values. - noTickMark = null; - } - - var byAxis = d3.svg.axis().orient('left') - .scale(by0) - .ticks(noTickMark) - .tickFormat(d3.format('d')); - // - - // Add grid. - wrapper.selectAll('line.y') - .data(by0.ticks()) - .enter() - .append('line') - .attr('class', 'grid-lines') - .attr('class', 'y') - .attr('x1', -20) - .attr('y1', by0) - .attr('y2', by0) - .style('stroke', '#333333') - .style('opacity', 0.1) - .attr('shape-rendering', 'crispEdges') - .attr('stroke-width', '1px'); - - // Bar chart. - // Add g container for each bin. Apply gutter/space to adjacent bins. - // This will group elements into bins. - wrapper.selectAll('g') - .data(bins) - .enter() - .append('g') - .attr('class', 'g-each-bin') - .attr('id', function(d, i) { - return 'bin-' + i; - }); - - // In each bin container, add same number of bars representing each title - // present in the data. When a title is not present, add a space to maintain - // equal order of title per bin. - var title = dataByTitles.map(function(d) { return d.key; }); - - bins.forEach(function(bin, bin_i) { - title.forEach(function(title, title_i) { - - var stockCount = +getStockCount(bin, title); - var h = by0(stockCount); - - d3.select('#bin-' + bin_i) - .append('g') - - .on('mousemove', function(d) { - d3.select(this).style('opacity', 0.5); - infoBox - .transition() - .style('opacity', 1); - infoBox - .html(title +' ('+stockCount+' stocks)') - .style('left', (d3.event.pageX + 10) + 'px') - .style('top', (d3.event.pageY) + 'px'); - }) - .on('mouseout', function(d) { - d3.select(this).style('opacity', 1); - infoBox.transition().style('opacity', 0); - }) - - .append('rect') - .attr('class', 'rect-each-bar') - .attr('fill', barchartColor(title_i)) - .attr('y', h) - .attr('height', (chartDimension.height - h) + marginAdjust); - }); - }); - - // X axis. - bsvg.append('g') - .attr('id', 'barchart-x-axis') - .attr('class', 'barchart-axis') - .attr('transform', 'translate(' + (margin.left + 7) + ',' + (height - margin.bottom) + ')') - .call(bxAxis); - - // Y axis. - bsvg.append('g') - .attr('class', 'barchart-axis') - .attr('transform', 'translate(' + (margin.left + 7) + ', ' + margin.top +')') - .call(byAxis); - - // Add chart title - d3.select('#container-barchart') - .append('h2') - .text('Figure: Distribution of ' + traitSelectedName + ' across Germplasm Phenotyped'); - - d3.select('#container-barchart') - .append('div') - .attr('class', 'messages warning warn-message') - .html('The following chart uses raw data and, as such, should never be used in publication and presentation. It is meant to give you a quick visual and to identify problems such as outliers to aid you in your analysis.'); - - // Position all bar chart elements into the right place. - renderBarChart(); - } - // End initialize bar chart. - - - - // Render function to position chart elements in the svg canvas. - // Function update stage with the current window width. - function updateWidth() { - // This is the width of the svg container. - var containerWidth = divChartContainer.style('width'); - // Update the width to be used in redering the chart elements. - width = parseInt(containerWidth, 10); - } - - // Function to render/position chart elements. - function render() { - // Get the width of the window before rendering chart elements. - updateWidth(); - - // Store the window width/size - use the value in computing - // the required bin size base on the given screen width available - // for visualization. - storeScreenWidth(width); - - // Render heatmap. - renderHeatmap(); - - // Render barchart. - // If barchart is present - if ($('#container-barchart').length > 0) { - renderBarChart(); - } - - // Remove all markers. - delMarker(''); - // Close all windows. - infoWindow('off'); - // Remove the bar chart. - d3.selectAll('#container-barchart').remove(); - } - - // Render heat map chart elements. - function renderHeatmap() { - // Adjust the width of svg canvas. - svg.attr('width', width); - - // The width of the main chart. - chartDimension.width = width - margin.left - margin.right; - - // Compute width of chart elements. - var gLocationWrapperWidth = Math.round(chartDimension.width/dataSet.countLocation); - rectDimension.width = Math.round(gLocationWrapperWidth/dataSet.countRep); - - // Render g elements for each location. - // Render rect elements for each rep and wrapper for each location. - d3.selectAll('.g-each-location') - .attr('transform', function(d, i) { - return 'translate('+ (i * gLocationWrapperWidth) +', 0)'; - }); - - d3.selectAll('.rect-wrap-each-location') - .attr('width', gLocationWrapperWidth); - - var x = 0; - d3.selectAll('.rect-each-rep') - .style('opacity', 0) - .transition() - .duration(function() { return randomNumber(); }) - .ease('back') - .style('opacity', 1) - .attr('width', rectDimension.width) - .attr('x', function(d, i) { - x = (i%dataSet.countRep == 0) ? 0 : x + 1; - return x * rectDimension.width; - }); - - // Render chart title (keep center) and x axis. - d3.select('#standard-chart-title') - .attr('transform', function() { - return 'translate('+ Math.round(width/2) +', 15)'; - }); - - // Render locations title. - d3.select('#location-title') - .attr('transform', function() { - return 'translate('+ Math.round(width/2) + ', ' + (height - margin.top) +')'; - }); - - // Render scales - x0.rangeRoundBands([0, chartDimension.width]); - xAxis.scale(x0); - d3.select('#g-x-axis') - .call(xAxis) - .selectAll('text') - .call(wrapWords); - } - - // Render bar chart elements. - function renderBarChart() { - // Less 125px to accommodate legend. - var legendArea = 125; - - // Adjust the width of svg canvas. - bsvg.attr('width', width); - - var gBinWidth = Math.round((chartDimension.width - legendArea) / bins.length) - gutter; - - // render bins - d3.selectAll('.g-each-bin') - .attr('transform', function(d, i) { - return 'translate('+ ((gBinWidth + gutter) * i) +', 0)'; - }); - - var barWidth = Math.round(gBinWidth / dataByTitles.length); - - var x = 0; - d3.selectAll('.rect-each-bar') - .style('opacity', 0) - .transition() - .duration(function() { return randomNumber(); }) - .ease('back') - .style('opacity', 1) - .attr('width', (barWidth - 2)) - .attr('x', function(d, i) { - x = (i%dataByTitles.length == 0) ? 0 : x + 1; - return x * (barWidth + 1); - }); - - // Render scales - bx0.rangeRoundBands([0, chartDimension.width - legendArea]); - bxAxis.scale(bx0); - d3.select('#barchart-x-axis').call(bxAxis); - - // Render Legend - d3.select('#barchart-legend-rect') - .attr('transform', 'translate('+ (width - margin.left - margin.right - 25) +', '+ margin.top +')'); - - d3.select('#barchart-legend-text') - .attr('transform', 'translate('+ (width - margin.left - margin.right - 25) +', '+ (margin.top + 10) +')'); - - // Render x caption - d3.select('#x-caption') - .attr('transform', function() { - return 'translate(' + Math.round(width/2) + ', '+ (height - margin.bottom + 40) +')'; - }) - - // Render grids. - d3.selectAll('line.y') - .attr('x2', chartDimension.width - margin.right - margin.left - gutter); - } - - - - // Label, caption, legend - // Heat map - function heatmapChartInfo() { - var chartTitle = 'Number of Trials per Location'; - var chartXTitle = 'Locations'; - var chartYTitle = 'Growing Season (year)'; - var chartLegendTitle = 'Number of traits'; - - // Title - var captions = svg.append('g').attr('id', 'g-captions'); - captions.append('text') - .attr('id', 'standard-chart-title') - .attr('class', 'chart-title') - .text(chartTitle); - - // Growing season - y axis - captions.append('text') - .attr('class', 'chart-axes') - .attr('transform', function() { - return 'translate(35, '+ Math.round(height/2) +') rotate(-90)'; - }) - .text(chartYTitle); - - // Location - x axis. - captions.append('text') - .attr('id', 'location-title') - .attr('class', 'chart-axes') - .text(chartXTitle); - - // Legend. - var legend = svg.append('g') - .attr('id', 'g-legend') - .attr('transform', 'translate(' + margin.left + ', '+ ((height - margin.top) + 20) +')'); - - legend.append('text') - .attr('class', 'legend-text') - .text(chartLegendTitle) - .attr('y', -5); - - var j = 0; - for(var m = 0; m < 7; m++) { - legend.append('rect') - .attr('transform', 'translate('+ (31 * j) +', 0)') - .attr('width', 30) - .attr('height', 8) - .attr('fill', color[m]); - - legend.append('text') - .attr('class', 'legend-text') - .attr('x', (30 * j) + 20) - .attr('y', 17) - .text(function() { - return (m * 5 == 30) ? '30+' : (m * 5); - }); - - j++; - } - } - - // Bar chart. - function barChartInfo(categorize) { - // Add legend. - var yCaption = 'Number of Experimental Units'; - var bcLegend; - - if (categorize == 'location' || typeof categorize === 'undefined') { - bcLegend = dataByTitles; - } - else { - bcLegend = []; - d3.map(dataByTitles, function(d) { - bcLegend.push({'key' : d.key}); - }); - } - - var legend = bsvg.append('g') - .attr('id', 'barchart-legend-rect'); - - var legendText = bsvg.append('g') - .attr('id', 'barchart-legend-text'); - - legend.selectAll('rect') - .data(bcLegend) - .enter() - .append('rect') - .attr('id', function(d, i) { - return 'legend-rect-' + i; - }) - .on('mousemove', highlight) - .on('mouseout', nohighlight) - .attr('fill', function(d, i) { return barchartColor(i); }) - .attr('height', 14) - .attr('width', 15) - .attr('x', 0) - .attr('y', function(d, i) { return 17 * i; }) - - legendText.selectAll('text') - .data(bcLegend) - .enter() - .append('text') - .attr('id', function(d, i) { - return 'legend-text-' + i; - }) - .on('mousemove', highlight) - .on('mouseout', nohighlight) - .text(function(d, i) { - // Trim location when it is too long. - var loc = d.key; - return (loc.length >= 28) ? loc.substr(0, 25) + '...' : loc; - }) - .attr('x', 19) - .attr('class', 'legend-text') - .attr('y', function(d, i) { return 17 * i; }); - - bsvg.append('g').append('text') - .attr('class', 'chart-axes') - .attr('id', 'y-caption') - .attr('transform', function() { - return 'translate(45, '+ Math.round(height/2) +') rotate(-90)'; - }) - .text(yCaption); - - // Location - x axis. - var baseUnit = extractBaseUnit(traitSelectedName); - - bsvg.append('g').append('text') - .attr('id', 'x-caption') - .attr('class', 'chart-axes') - .text('Average Observed Measurements per Experiment Unit ' + baseUnit); - } - - - - // Helper functions: - // Get the unit part given a trait (unit) string. - // Function highlight rect corresponding to an item in the legend. - function nohighlight() { - d3.selectAll('.rect-each-bar') - .transition() - .duration(function() { return randomNumber(); }) - .ease('back') - .style('opacity', 1); - } - - function highlight() { - var id = d3.select(this).attr('id'); - var rect_id = '#' + id.replace(/text/i, 'rect'); - var fill = d3.select(rect_id).attr('fill'); - - // Lower the opacity of all rect except rect with fill above. - d3.selectAll('rect.rect-each-bar') - .transition() - .duration(function() { return randomNumber(); }) - .ease('back') - .style('opacity', function() { - var m = d3.select(this).attr('fill'); - return (m == fill) ? 1 : 0.1; - }); - } - - function extractBaseUnit(traitName) { - var regExp = /\(.*\)/; - var matches = regExp.exec(traitName); - // The unit might contain other text information. - // In AGILE project - R1, R3, R5, R7, 1st, 2nd - var u = matches[0]; - var baseUnit; - baseUnit = u.replace(/(R1|R3|R5|R7|1st|2nd);\s/i, ''); - - return baseUnit; - } - - // Mark/hightlight reps and add informaton about the marker. - function markRep(project, trait_id, trait_name) { - d3.select('#text-not-found').remove(); - - // Selected trait name. - traitSelectedName = trait_name; - - // Get selected trait. Trait select box returns cvterm id of a trait. - traitSelectedId = trait_id; - traitSelectedId.toString(); - - if (traitSelectedId == '0') { - // First option in the select box. - // Remove all markers - delMarker(''); - // Add Information window. - infoWindow('off'); - } - else { - // Find trait selected in all rep and mark when present. - var countRep = 0; - for (var i = 1; i < traitsList.length; i++) { - var id = '#obj-' + i.toString(); - - if (traitsList[i].toString().indexOf(traitSelectedId) !== -1) { - countRep++; - // Trait present - // Add marker if none is present. - addMarker(id); - } - else { - // Not here. - // Remove maker if marker is present. - delMarker(id); - } - } - } - - // Add information window when marker is present. - if (countRep > 0) { - // Make the trait selected title of the info window. - $('#title-pheno').text(function() { - infoWindow(); - // Tell user how many reps a trait was measured. - $('#text-rep').text(countRep + ' Rep'); - - return traitSelectedName; - }); - } - - // Test if barchart is part of the DOM already. - var containerBarchart = ($('#container-barchart').length > 0) ? 1 : 0; - if (containerBarchart > 0 && traitSelectedId === '0') { - // Remove barchart when it is present and 'select a trait' option is selected. - d3.select('#container-barchart') - .transition() - .style('opacity', 0) - .remove(); - } - else if (traitSelectedId !== '0') { - // If trait selected is not equals to 'select a trait'. - if (containerBarchart < 1) { - // No barchart present in the DOM, Add chart elements. - d3.select('.container-contents') - .append('div') - .attr('id', 'container-barchart') - .transition() - .style('opacity',1); - - d3.select('#container-barchart') - .append('svg') - .attr('class', 'bar-chart'); - } - else { - // Remove previous barchart g container elements. - // Leave the svg canvas and append a new set of chart elements below. - d3.selectAll('.bar-chart g, #container-barchart h2').remove(); - } - } - } - - // Show or hide the marker information window. - function infoWindow(state) { - var window = $('#container-marker-information'); - - if (state == 'off') { - window.slideUp(300); - - // Reset selectbox - $('select[name=rawdata-sel-trait]').val(''); - } - else { - window.slideDown(300); - } - } - - // Remove marker. - function delMarker(id) { - var m; - if (id == '') { - id = '.marker-rep'; - m = d3.selectAll(id); - } - else { - id = id + ' path'; - m = d3.select(id); - } - - if (m.size() > 0) { - m - .transition() - .duration(function() { return randomNumber(); }) - .style('opacity', 0) - .remove(); - } - } - - // Mark rep when trait selected is measured in this rep. - function addMarker(id) { - if (d3.select(id + ' path').size() <= 0) { - var rect = d3.select(id + ' rect'); - - if (rect.size() > 0) { - var x = parseInt(rect.attr('x')); - var y = parseInt(rect.attr('y')); - - d3.select(id) - .append('path') - .attr('class', 'marker-rep') - .attr('d', leaf) - .attr('transform', function() { - // MARKER DIMENSION: 20X20 pixels - // Location of rect plus half the bar width less the 1/2 width of the marker - x = x + Math.round(rectDimension.width/2) - 10; - y = y + Math.round(rectDimension.height/2) - 15; - - return 'translate(' + x + ',' + y + ')'; - }) - .style('opacity', 0) - .transition() - .duration(function() { return randomNumber(); }) - .style('opacity', 1) - .attr('filter', 'url(#dropshadow)'); - } - } - } - - // Map trait count to colour code. - // Return number as an index to a color element - // in color array. - function setColour(num) { - if (num == 0) - return 0; - else if (num >= 1 && num <= 5) - return 1; - else if (num >= 6 && num <= 10) - return 2; - else if (num >= 11 && num <= 15) - return 3; - else if (num >= 16 && num <= 20) - return 4; - else if (num >= 21 && num <= 25) - return 5; - else if (num >= 26) - return 6; - } - - // Generate random numbers. - function randomNumber() { - var min = 1; - var max = 10; - - return (Math.random() * (max - min) + min ) * 100; - } - - // Get the stock count of a location in a bin. - function getStockCount(bin, loc) { - var d = dataByBins; - for(var i = 0; i < d.length; i++) { - if (d[i].key == bin) { - for(var j = 0; j < d[i].values.length; j++) { - var m = d3.values(d[i].values[j]); - if (m[0] == loc) { - return m[1]; - } - } - - // Just search in the requested bin. - return 0; - } - } - } - - // Text and icon shown when there is no data. - function noData(canvas) { - // Remove loading animation. - $('.win-loading').remove(); - - var s = (canvas == '.data-chart') ? '.data-chart' : 'svg:last-child'; - - d3.select(s) - .attr('width', width) - .attr('height', 150); - - var message = d3.select(canvas) - .append('g') - .attr('transform', 'translate('+ (Math.floor(width/2) - 40) +', 50)'); - - message - .append('text') - .attr('class', 'chart-axes') - .attr('y', 20) - .attr('x', 50) - .text('Could not visualize data.'); - } - - // Debugging function. Echo the contents of d. - function echo(d) { - alert(JSON.stringify(d)); - //console.log(JSON.stringify(d)); - //console.log(d); - } - //// - - // Save the screen/window width to a cookie and read this - // value to let php decide bin size information. - function storeScreenWidth(winWidth) { - document.cookie = 'rawphenoRawdataSW=' + winWidth + ';path=/;domain=.knowpulse.usask.ca'; - } - - // Populate select box based on categorized value. - // Location + Year by default. - function barchartFillSelect(category, dataCategory) { - // Reference select option. - var selOption = $('#sel-barchart-option'); - - if (category == 'location') { - // Fill the select box with years. - $(dataCategory.planting_date).each(function(i, v) { - selOption.append($('
    - Window title - //
    - Main div - //
    - div sub title - //
    left
    - Left column div - page subtitle, form fields, state indicator - //
    right
    - Right column div - navigation button to related page - //
    - // - //
    content
    - Content div - // - - - // Only the upload page has this page id. Other pages, for instance, - // Rawdata page has page id rawpheno_rawdata and Download page has page id - // rawpheno_download and so on. For consistency, when page id is rawpheno_upload_form_master - // which is the upload data page, replace it with rawpheno_upload. - $page_id = ($form['#form_id'] == 'rawpheno_upload_form_master') - ? 'rawpheno_upload' : $form['#form_id']; - - // Content of the secondary page title. - $subtitle = ''; - if ($page_id == 'rawpheno_instructions') { - // Search box in instructions page. - $subtitle .= drupal_render($form['txt_search']); - $subtitle .= drupal_render($form['btn_search']); - } - elseif ($page_id == 'rawpheno_upload') { - // Stage indicator in upload page. - $current_stage = $form['current_stage']['#value']; - $stages = array_keys($upload_stages); - $stage_number = array_search($current_stage, $stages) + 1; - $subtitle .= 'Stage ' . $stage_number . ' of 3 - ' . $upload_stages[$current_stage]; - } - elseif ($page_id == 'rawpheno_backup') { - // Currently logged in user. Show the name of the user. - $subtitle .= $GLOBALS['user']->name; - } - else { - // Default to space to prevent the container from collapsing. - $subtitle .= ' '; - } - - - // Markup the button that links to related page. - // - Upload page and Instructions page. - // - Instructions page and Upload data page. - // - Rawdata page and Download data page. - // - Download page and Rawdata page. - // - Backup page and Instructions page. - $form['page_button']['#prefix'] = ''; - $form['page_button']['#suffix'] = ''; -?> - - -
    "> - -
    - - -
    "> -
    - -
    - Browser not supported. Please update your browser. -
    - -
    - This module requires Drag and Drop Upload module to be installed and enabled. Please contact the administrator of this website. -
    - ' . $link_to_git_rawphenotypes, TRIPAL_INFO, array('return_html' => TRUE)); - unset($form); - } - elseif (!$has_prj) { - // No project available. - ?> -
    - There is no project available in this module. Please contact the administrator of this website. -
    - ' . $link_to_manage_project, TRIPAL_INFO, array('return_html' => TRUE)); - unset($form); - } - elseif (!$has_data AND in_array($page_id, array('rawpheno_rawdata', 'rawpheno_download'))) { - // Has project but project has no data associated to it. - // Excempt pages upload, backup and instructions. - ?> -
    - There is no project with data available in this module. -
    - ' . $link_to_manage_project, TRIPAL_INFO, array('return_html' => TRUE)); - unset($form); - } - elseif (count($my_projects) < 1) { - // User is not appointed to project. - ?> -
    - You have no projects assigned to your account. Please contact the administrator of this website. -
    - ' . $link_to_manage_project, TRIPAL_INFO, array('return_html' => TRUE)); - unset($form); - } - elseif (isset($dir_permission) AND $dir_permission === FALSE AND in_array($page_id, array('rawpheno_upload', 'rawpheno_backup'))) { - // Upload destination directory is not writable. - ?> -
    - The file destination directory is not writable. Please contact the administrator of this website. -
    - -
    - The module requires Spreadsheet Reader library, which is missing. Please contact the administrator of this website. -
    - ' . $link_to_git_rawphenotypes, TRIPAL_INFO, array('return_html' => TRUE)); - unset($form); - } - elseif ($page_id == 'rawpheno_rawdata' AND ($d3ver != '3' OR (int)$d3rel < 4 OR !$d3_in)) { - // Ensure that d3 is version 3.x.x - ?> -
    - Failed to initiliaze visualization library. Please contact the administrator of this website. -
    - ' . $link_to_manage_project, TRIPAL_INFO, array('return_html' => TRUE)); - unset($form); - } - elseif (isset($writer_in) AND empty($writer_in) AND $page_id == 'rawpheno_instructions') { - ?> -
    - The module requires Spreadsheet Writer library, which is missing. Please contact the administrator of this website. -
    - ' . $link_to_git_rawphenotypes, TRIPAL_INFO, array('return_html' => TRUE)); - unset($form); - } - else { - // Project is available. - ?> -
    -
    -
    -
    - -
    - -
    -

     

    -  : are measured in the   designated with a leaf symbol ( ) Clear chart -
    - -
    -
    - -
    - -
    -
    - -
    - -
    - -
    -
    - Need help? - -
    -
      -
    • -

      Stage 1 - Validate Spreadsheet

      - This form will guide you through uploading your raw phenotypic data. Your data should be in a - Microsoft Excel Workbook (XLSX) following the format described on the - Instructions Page. -
    • - -
    • -

      Stage 2 - Describe New Trait

      - In the second step we ask that you describe any additional phenotypes. -
    • - -
    • -

      Stage 3 - Save Spreadsheet

      - Finally, the spreadsheet is saved to , at which point the - phenotypic data is available through - summaries and - downloads. -
    • -
    - -
    -
    -

    • Still Need Help?

    - Watch Raw Phenotypes Video Demonstrations - - - or Send Us an Email: KnowPulse Support - -
    -
    - -
    - -
    -
    -
    - - -
    - Spreadsheet submitted -
    - -
    -
    -
    - - - - - - - -
    - - -
    - - -
    - -
    - -
    -

    Manage My Files:

    -
    - - -
    - - - -
    - -
    - -
    -
    -
    - -
    - - -
    -

    Spreadsheet Tips

    -
      -
    • Comments with instructions, tips and scales are included with the header for each trait. Simply click the speech bubble icon beside the header.

    • -
    • Essential Traits have green headers; all other traits are optional.

    • -
    • You should hide optional traits you are not taking data for by long pressing a column header then selecting “hide”.

    • -
    • We’ve included an easy calculator to determine “Days from Planting” in a separate tab.

    • -
    • Special Interest Traits:
      Add a column on the spreadsheet for any other trait you are interested in collecting data for. When uploading you will be asked to provide a description including units or scale used to take the measurement.

    • -
    - - - -
     
    -
    - - 'These traits are essential to this project and data should be collected for all genotypes sent to you.', - 3 => 'These traits are optional. We will be taking them in our location and have thus provided our procedure in case you interested in taking these data in your location as well. Feel free to record ANY data you are interested in (including traits not listed below –just add a column to the accompanying data spreadsheet for traits not listed below).', - 4 => 'The following traits require a fair amount of work and, as such, are completely optional. We will collect them in SK, if you are interested in these traits, please contact us to make sure we are collecting the same thing. Note: These columns are hidden by default. If you would like to record this data, select columns “X” and "AB" and either right-click (computer) or long-press (tablet) the column header then select “Unhide”. For the following “Subset Traits”, select 2 plants from the middle of each plot and randomly collect 10 peduncles from each plant, ranging from the top to bottom, for a total of 20 peduncles. If 20 peduncles cannot be obtained, sample from a 3rd plant.', - 5 => 'These traits are additional traits contributed by Phenotypic Data Collectors. Trait definition and method of collection were provided by the data collector.', - 6 => 'These traits are optional. Please contact the Project Manager if you are interested in these traits.' - ); - - $i = 2; - - // Plant property traits not included. - unset($trait_type['type4']); - - foreach($trait_type as $type) { - if ($form['tbl_project_headers_' . $type]['#markup'] == 'no-trait') { - unset($form['tbl_project_headers_' . $type]); - } - else { - // Notes: - if ($type == $trait_type['type3'] && trim($form['project_panel']['#markup']) != 'AGILE: Application of Genomic Innovation in the Lentil Economy') { - $notes = $arr_type_note[6]; - } - else { - $notes = $arr_type_note[$i]; - } - - print '
    '; - print '

    ' . $notes . '

    '; - print drupal_render($form['tbl_project_headers_' . $type]); - - - if ($i == 2) { - print ' -

    Further reference for Reproductive stages:

    - Erskine et al. (1990) Stages of Development in Lentil. Experimental Agriculture. 26(3): 297-302. -
      -
    • R1 - First Bloom

      One open flower at any node

    • -
    • R3 - Early Pod

      Pod on nodes 10-13 of the basal primary branch visible

    • -
    • R5 - Full Seed

      Seeds in any single pod on nodes 10-13 of the basal primary branch are swollen and completely fill the pod cavity

    • -
    • R7 - Physiological Maturity

      The leaves start yellowing and 50% of the pods have turned yellow

    • -
    '; - } - - - print '
    '; - } - - $i++; - } - ?> - -
    -

    Topic:

    - -
    - -
    -
    -
    -
    -
    - - -
    - - -
    -
    diff --git a/theme/rawpheno_upload_validation_report.tpl.php b/theme/rawpheno_upload_validation_report.tpl.php deleted file mode 100644 index 9f57366..0000000 --- a/theme/rawpheno_upload_validation_report.tpl.php +++ /dev/null @@ -1,54 +0,0 @@ - - -
    - - - - Hide - Validation Result - - - - - -
    - -
    -