Commit 091d89d4 authored by Alejandro De Maria Antolinos's avatar Alejandro De Maria Antolinos

Merge branch 'issue55' into 'master'

Issue55

Closes #55

See merge request !62
parents 08d84431 01a43643
Pipeline #5960 failed with stages
in 11 seconds
......@@ -24,6 +24,7 @@ The grunFile.js has 2 tasks.
To test the changes locally on your computer:
* Prepare the project ( check js files, create the dev.html file from the dev.tpl.html template )
```
cd www
grunt dev
```
* Copy the www folder (not all files are needed but it is easy) to apache folder
......@@ -34,12 +35,13 @@ sudo rm -fr /var/www/html && sudo cp -r www/. /var/www/html/
Replace prefix and suffix with real values. Note that the stating page is dev.html. In production , the starting page will be index.html.
* Once you are satisfied with your work, prepare the project for production
```
cd www
grunt
```
* Submit a merge request on icat repository at https://gitlab.esrf.fr/icat/doi-landing-page
### How to prepare the docker container locally for testing purpose
* Make sure the porject is prepared for production (check js files, concatene them, minify them, handle other js dependancies)
* Make sure the project is prepared for production (check js files, concatene them, minify them, handle other js dependancies)
```
grunt
```
......
RewriteEngine On
RewriteEngine on
RewriteBase /
RewriteCond %{REQUEST_FILENAME} !-f
......
......@@ -7,26 +7,29 @@
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet">
<!-- Using grunt-wiredep, the proper js files from bower components are included below -->
<!-- bower:js -->
<script src="bower_components/jquery/dist/jquery.js"></script>
<script src="bower_components/dustjs-linkedin/dist/dust-full.min.js"></script>
<script src="bower_components/bootstrap/dist/js/bootstrap.js"></script>
<script src="bower_components/dustjs-helpers/dist/dust-helpers.min.js"></script>
<script src="bower_components/lodash/lodash.js"></script>
<script src="bower_components/moment/moment.js"></script>
<!-- endbower -->
<!-- Using grunt-include-source, the proper js files created by us (not minified) are included below -->
<script src="src/author.js"></script>
<script src="src/beamlineurl.js"></script>
<script src="src/citation.js"></script>
<script src="src/constants.js"></script>
<script src="src/doicontroller.js"></script>
<script src="src/doiData.js"></script>
<script src="src/doiview.js"></script>
<script src="src/event.js"></script>
<script src="src/experimentalreportcontroller.js"></script>
<script src="src/jsonextractor.js"></script>
<script src="src/metadataTableHelper.js"></script>
<!-- Using grunt-wiredep, the proper js files from bower components are included below -->
<!-- bower:js -->
<script src="bower_components/jquery/dist/jquery.js"></script>
<script src="bower_components/dustjs-linkedin/dist/dust-full.min.js"></script>
<script src="bower_components/bootstrap/dist/js/bootstrap.js"></script>
<script src="bower_components/dustjs-helpers/dist/dust-helpers.min.js"></script>
<script src="bower_components/lodash/lodash.js"></script>
<script src="bower_components/moment/moment.js"></script>
<!-- endbower -->
<!-- bower:css -->
<link rel="stylesheet" href="bower_components/bootstrap/dist/css/bootstrap.css" />
......
......@@ -7,12 +7,13 @@
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet">
<!-- Using grunt-include-source, the proper js files created by us (not minified) are included below -->
<!-- include: "type": "js", "files": "src/*.js" -->
<!-- Using grunt-wiredep, the proper js files from bower components are included below -->
<!-- bower:js -->
<!-- endbower -->
<!-- Using grunt-include-source, the proper js files created by us (not minified) are included below -->
<!-- include: "type": "js", "files": "src/*.js" -->
<!-- bower:css -->
<!-- endbower -->
......
......@@ -72,4 +72,4 @@
</div>
</footer>
</body>
</html>
\ No newline at end of file
</html>
// Karma configuration
// Generated on Thu Oct 11 2018 23:09:22 GMT+0200 (CEST)
module.exports = function (config) {
config.set({
// base path that will be used to resolve all patterns (eg. files, exclude)
basePath: '',
// frameworks to use
// available frameworks: https://npmjs.org/browse/keyword/karma-adapter
frameworks: ['jasmine', 'browserify'],
// list of files / patterns to load in the browser
files: [
'src/beamlineurl.js',
'src/constants.js',
'src/doicontroller.js',
'src/doiview.js',
'src/experimentalreportcontroller.js',
'src/event.js',
'src/jsonextractor.js',
'tests/jsonExtractor.test.js',
'tests/doiController.test.js',
'bower_components/lodash/lodash.js',
'bower_components/moment/moment.js'
],
// list of files / patterns to exclude
exclude: [
],
// preprocess matching files before serving them to the browser
// available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
preprocessors: {
'tests/jsonExtractor.test.js': ['browserify']
},
// test results reporter to use
// possible values: 'dots', 'progress'
// available reporters: https://npmjs.org/browse/keyword/karma-reporter
reporters: ['progress'],
// web server port
port: 9876,
// enable / disable colors in the output (reporters and logs)
colors: true,
// level of logging
// possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
logLevel: config.LOG_INFO,
// enable / disable watching file and executing tests whenever any file changes
autoWatch: true,
// start these browsers
// available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
browsers: ['Firefox', 'Chrome'],
// Continuous Integration mode
// if true, Karma captures browsers, runs the tests and exits
singleRun: false,
// Concurrency level
// how many browser should be started simultaneous
concurrency: Infinity
})
}
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
......@@ -13,14 +13,22 @@
"grunt-wiredep": "^3.0.1"
},
"devDependencies": {
"browserify": "^16.2.3",
"grunt": "^1.0.1",
"grunt-contrib-cssmin": "^2.2.1",
"grunt-contrib-jshint": "^1.1.0",
"grunt-include-source": "^1.1.0",
"grunt-plato": "^1.4.0"
"grunt-plato": "^1.4.0",
"jasmine-core": "^3.2.1",
"karma": "^3.0.0",
"karma-browserify": "^5.3.0",
"karma-chrome-launcher": "^2.2.0",
"karma-firefox-launcher": "^1.1.0",
"karma-jasmine": "^1.1.2",
"watchify": "^3.11.0"
},
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
"test": "./node_modules/jest/bin/jest.js"
},
"repository": {
"type": "git",
......
var CONSTANTS = {
ES_RESOURCE_TYPE : "experimental session resource type",
DC_RESOURCE_TYPE : "dataset collection resource type"
};
\ No newline at end of file
function DoiData() {
function DoiData(title, doi, dataciteLink, publisher, creator, publication_year) {
this.title = title;
this.doi = doi;
this.dataciteLink = dataciteLink;
this.publisher = publisher;
this.creator = creator;
this.publiclyAccessibleYear = publication_year;
}
/**
* Get the accessibility status
* @param {string} resourceType the resource type for this DOI
* @param {string} sessionDate The timeStamp the experiment was started
* @param {string} publiclyAccessibleYear The year the data will be publicly accesssible.
* @return {string}
......@@ -10,19 +17,24 @@ function DoiData() {
* "Restricted access" : the data is still under embargo. Only accessible to experimental team members
*
* */
DoiData.prototype.getAccessibilityStatus = function( sessionDate, publiclyAccessibleYear ) {
if ( publiclyAccessibleYear && sessionDate ) {
var publiclyAvailableDate = moment( publiclyAccessibleYear, "YYYY" );
var now = moment();
DoiData.prototype.getAccessibilityStatus = function (resourceType, sessionDate, publiclyAccessibleYear) {
if (resourceType) {
if (resourceType === CONSTANTS.ES_RESOURCE_TYPE) {
if (publiclyAccessibleYear && sessionDate) {
var publiclyAvailableDate = moment(publiclyAccessibleYear, "YYYY");
var now = moment();
if ( now > publiclyAvailableDate ) {
if (now > publiclyAvailableDate) {
return "Open access";
} else if (now <= publiclyAvailableDate) {
return "Restricted access";
}
}
} else if (resourceType === CONSTANTS.DC_RESOURCE_TYPE){
return "Open access";
} else if ( now <= publiclyAvailableDate ) {
return "Restricted access";
}
}
console.log( "Failed to determine whether the accessibility of the DOI." );
console.log("Failed to determine whether the accessibility of the DOI.");
return "Accessibility status could not be determined";
};
This diff is collapsed.
......@@ -3,6 +3,7 @@ function DOIView() {
this.mainPanelId = "main";
this.reportPanelId = "expReport";
this.badgePanelId = "badges";
this.metadataDivId = 'metadata';
this.reportExperimentFileNames = null;
this.failedRecievingExpReport = false;
......@@ -58,6 +59,7 @@ DOIView.prototype.renderDOIData = function (doiData) {
$('#' + _this.mainPanelId).html(out);
_this.isDOIAlreadyRendered = true;
_this.renderBadges(doiData);
_this.renderMetadata(doiData);
/**
* If there are experimental reports already retrieved then render them after
* the rendering of the DOI
......@@ -131,22 +133,37 @@ DOIView.prototype.renderErrorForExperimentalReport = function () {
/**
* Render the badge template in the corresponding div
*
* */
* @param {object} data : the doiData object as constructed by the doiController
*/
DOIView.prototype.renderBadges = function (data) {
if (data) {
var _this = this;
dust.render('badges_tpl', {
data: data
data: data,
constants : CONSTANTS
}, function (err, out) {
$('#' + _this.badgePanelId).html(out);
});
}
};
/**
* Render the badge template in the corresponding div
*/
DOIView.prototype.renderMetadata = function (data) {
if (data) {
var _this = this;
dust.render('metadata_tpl', {
data: data,
constants : CONSTANTS
}, function (err, out) {
$('#' + _this.metadataDivId).html(out);
});
}
};
/**
* Set the message which is displayed just above the "Access data button" in experimental data box.
*
......
......@@ -8,17 +8,13 @@ function ExperimentalReportController() {
/**
* Request the experimental report filename list for the given proposal
*
* @param {string}
* proposal The proposal
*
* @param {string} proposal The proposal
*/
ExperimentalReportController.prototype.requestFilenameList = function(proposal) {
var _this = this;
if (proposal) {
// extract the proposal LetterCode (before the hyphen) and the digits (after
// the hyphen)
aroundHyphenRegExp = new RegExp(/^(.*)-(.*)/);
// extract the proposal LetterCode (before the hyphen) and the digits (after the hyphen)
var aroundHyphenRegExp = new RegExp(/^(.*)-(.*)/);
var beforeHyphen = aroundHyphenRegExp.exec(proposal)[1];
var afterHyphen = aroundHyphenRegExp.exec(proposal)[2];
......
function JsonExtractor( json ) {
function JsonExtractor(json) {
}
/**
* Extract investigationId from the data.doi field as received from the json
* response.
*
* @param {string}
* data The data object provided by datacite
* @return {string}
* investigationId The investigationID requested for accessing data in ICAT.
* @param {string} data The data object provided by datacite
* @return {string} investigationId The investigationID requested for accessing data in ICAT.
*/
JsonExtractor.prototype.extractInvestigationId = function( data ) {
var regExp = RegExp( /\/\w*-*[Ee][Ss][Rr][Ff]-[Ee][Ss]-(\d+)$/ );
JsonExtractor.prototype.extractInvestigationId = function (data) {
var regExp = RegExp(/\/\w*-*[Ee][Ss][Rr][Ff]-[Ee][Ss]-(\d+)$/);
return regExp.exec( data.doi )[1];
};
/**
* Extract field value (beamline, proposal number and proposal type) from the
* data json object. Returns null if the field is not found.
*
* @param {array}
* subject The array containing objects of interest template
* @param {string}
* field The text field which is searched
* @param {array} subject The array containing objects of interest template
* @param {string} field The text field which is searched
* @returns {array} the found field in an array. When the field is string composed of coma separated items, each item is returned in the array. When there is no text, an empty array is returned
*/
JsonExtractor.prototype.getFieldFromSubject = function( subject, field ) {
if ( subject ) {
if ( subject[0] ) {
for ( var i = 0; i < subject[0].length; i++ ) {
if ( subject[0][i].subject_scheme === field ) {
return subject[0][i].text;
JsonExtractor.prototype.getFieldFromSubject = function (subject, field) {
if (subject) {
if (subject[0]) {
var regExp = new RegExp("^" + field + "$", 'i');
for (var i = 0; i < subject[0].length; i++) {
if (regExp.test(subject[0][i].subject_scheme)) {
if (subject[0][i].text) {
return _.split(subject[0][i].text, ',');
}
}
}
}
......@@ -39,16 +37,13 @@ JsonExtractor.prototype.getFieldFromSubject = function( subject, field ) {
/**
* Get minting year from the data object.
*
* @param {object}
* data The data object provided by datacite template
* @return {string}
* The minting year. Null if it does not exist.
* @param {object} data The data object provided by datacite template
* @return {string} The minting year. Null if it does not exist.
*/
JsonExtractor.prototype.getMintingYear = function( data ) {
if ( data ) {
if ( data.date_registered && data.date_registered != null ) {
return moment( data.date_registered ).year();
JsonExtractor.prototype.getMintingYear = function (data) {
if (data) {
if (data.date_registered && data.date_registered != null) {
return moment(data.date_registered).year();
}
}
return null;
......@@ -56,16 +51,13 @@ JsonExtractor.prototype.getMintingYear = function( data ) {
/**
* Get session date from the data object.
*
* @param {object}
* data The data object provided by datacite
* @return {string}
* The formatted (YYY-M-D) session date. Null if it does not exist.
* @param {object} data The data object provided by datacite
* @return {string} The formatted (YYY-M-D) session date. Null if it does not exist.
*/
JsonExtractor.prototype.getSessionDate = function( data ) {
if ( data ) {
if ( data.date_collected && data.date_collected != null ) {
return moment( data.date_collected ).format( 'YYYY-M-D' );
JsonExtractor.prototype.getSessionDate = function (data) {
if (data) {
if (data.date_collected && data.date_collected != null) {
return moment(data.date_collected).format('YYYY-M-D');
}
}
return null;
......@@ -74,13 +66,11 @@ JsonExtractor.prototype.getSessionDate = function( data ) {
/**
* Get the resource type general
* @param {object}
* data The data object provided by datacite
* @return {string}
* The resource tyoe general. Null if it does not exist.
* @param {object} data The data object provided by datacite
* @return {string} The resource type general. Null if it does not exist.
* */
JsonExtractor.prototype.getResourceTypeGeneral = function( data ) {
if ( data && data.resource_type_general ) {
JsonExtractor.prototype.getResourceTypeGeneral = function (data) {
if (data && data.resource_type_general) {
return data.resource_type_general;
}
return null;
......@@ -88,14 +78,55 @@ JsonExtractor.prototype.getResourceTypeGeneral = function( data ) {
/**
* Get the resource type
* @param {object}
* data The data object provided by datacite
* @return {string}
* The resource tyoe. Null if it does not exist.
* @param {object} data The data object provided by datacite
* @return {string} Resource type. Null if it does not exist.
* */
JsonExtractor.prototype.getResourceType = function( data ) {
if ( data && data.resource_type ) {
return data.resource_type;
JsonExtractor.prototype.getResourceType = function (data) {
if (data && data.resource_type) {
if (data.resource_type === "Experiment Session") {
return CONSTANTS.ES_RESOURCE_TYPE;
}
if (data.resource_type === "Datacollection") {
return CONSTANTS.DC_RESOURCE_TYPE;
}
}
return null;
};
/**
* Get the abstract from the data
* @param {*} data data object provided by datacite
* @return {string} the abstract . Null if it does not exist.
*/
JsonExtractor.prototype.getAbstract = function (data) {
if (data && data.description) {
var description = data.description;
if (description.type && description.type === 'Abstract') {
return description.text;
}
}
return null;
};
/**
* merge proposal and beamlines together and remove proposal, beamline duplicates if any
* @param {array} proposals the extracted proposals
* @param {array} beamlines the extracted beamlines
* @param {array} beamlineUrls the extracted beamline urls
* @return {array} array of the form [ {proposal: 'proposal1', beamline: 'beamline1', beamlineUrl: 'beamlineUrl' ] , {}, ... ]. Null if input array length are not equal
*/
JsonExtractor.prototype.getMergedProposalAndBeamline = function (proposals, beamlines, beamlineUrls) {
if (proposals && beamlines && beamlineUrls && proposals.length === beamlines.length && proposals.length === beamlineUrls.length) {
var result = [];
for (var index = 0; index < proposals.length; index++) {
result.push({
proposal: proposals[index],
beamline: beamlines[index],
beamlineUrl: beamlineUrls[index]
});
}
return _.uniqBy(result, function (value) { return JSON.stringify(value); });
}
return null;
};
/**
* Helper function which displays the proposals and beamlines under the form of a table. Used in the context of a doi
* created for a dataset collection.
*/
function proposalBeamlineTable(chunk, context, bodies, params) {
var proposalAndBeamlines = params.mergedProposalBeamlines;
chunk.write('<table class="table table-striped">');
chunk.write('<thead>');
chunk.write('<tr>');
chunk.write('<th scope="col">Proposals</th>');
chunk.write('<th scope="col">Beamlines</th>');
chunk.write('</tr>');
chunk.write('</thead>');
chunk.write('<tbody>');
if (proposalAndBeamlines) {
for (var index = 0; index < proposalAndBeamlines.length; index++) {
if (proposalAndBeamlines[index]) {
var proposal = proposalAndBeamlines[index].proposal || 'unknown';
var beamline = proposalAndBeamlines[index].beamline || 'unknown';
var beamlineUrl = proposalAndBeamlines[index].beamlineUrl;
chunk.write('<tr>');
chunk.write('<td>' + proposal + '</td>');
if (beamlineUrl === "noLink") {
chunk.write('<td>' + beamline + '</td>');
} else {
chunk.write('<td> <a href="' + beamlineUrl + '" target="_blank">' + beamline + ' </a> </td>');
}
chunk.write('</tr>');
} else {
chunk.write('<tr>');
chunck.write('<td> unknown </td>');
chunck.write('<td> unknown </td>');
chunk.write('</tr>');
}
}
chunk.write('</tbody>');
chunk.write('</table>');
return chunk.render(bodies.block, context);
}
}
/** Helper function which dislays the abstract for a given doi */
function abstract(chunk, context, bodies, params) {
var abstract = params.abstract;
chunk.write('<div class="myBorder-left">');
chunk.write('<label> Abstract </label>');
chunk.write('<div class="padding-left-15">');
if (abstract) {
chunk.write(abstract);
} else {
chunk.write('There is no abstract for this session.');
}
chunk.write('</div >');
chunk.write('</div >');
return chunk.render(bodies.block, context);
}
dust.helpers.proposalBeamlineTable = proposalBeamlineTable;
dust.helpers.abstract = abstract;
<div class="row margin-left-0 margin-right-0">
{@eq key=data.resourceType value="Experiment Session"}
{@eq key=data.resourceType value=constants.ES_RESOURCE_TYPE}
<div class="col-auto padding-right-2 padding-left-2">
<span class="badge badge-secondary font-1p4rem " > Session </span>
</div>
</div>
{/eq}
{@eq key=data.resourceType value=constants.DC_RESOURCE_TYPE}
<div class="col-auto padding-right-2 padding-left-2">
<span class="badge badge-secondary font-1p4rem " > Data collection </span>
</div>
{/eq}
<div class="col"> </div>
......
......@@ -54,82 +54,8 @@
<hr>
<div class="row">
<div class="col">
<div class="myBorder-left">
<label> Proposal </label>
{?data.proposalNumber}
<div class="padding-left-15">{data.proposalNumber}</div>
{:else}
<div class="padding-left-15"> Not available</div>
{/data.proposalNumber}
</div>
</div>
<div class="col">
<div class="myBorder-left">
<label> Publication year </label>
{?data.mintingYear}
<div class="padding-left-15">{data.mintingYear}</div>
{:else}
<div class="padding-left-15"> Not available</div>
{/data.mintingYear}
</div>
</div>
<div class="col">
<div class="myBorder-left">
<label> Beamline </label>
{?data.beamline}
{@eq key=data.beamlineUrl value="noLink"}
<div class="padding-left-15">{data.beamline}</div>
{:else}
<div class="padding-left-15">
<a href="{data.beamlineUrl}" target="_blank"> {data.beamline} </a>
</div>
{/eq}
{:else}
<div class="padding-left-15">Not available</div>
{/data.beamline}
</div>
</div>
<div class="col">
<div class="myBorder-left">
<label> Session date </label>
{?data.sessionDate}
<div class="padding-left-15">{data.sessionDate}</div>
{:else}
<div class="padding-left-15"> Not available</div>
{/data.sessionDate}
</div>
</div>
<div id="metadata"> </div>
<div class="col-4">
<div class="myBorder-left">
<label>Category </label>
{?data.proposalType}
<div class="padding-left-15">{data.proposalType}</div>
{:else}
<div class="padding-left-15"> Not available</div>
{/data.proposalType}
</div>
</div>
</div>
<hr>
<div class="row">
<div class="col-12">
<div class="myBorder-left">
<label> Abstract </label>
<div class="padding-left-15">
There is no abstract for this session.
</div>
</div>
</div>
</div>
<hr>
<div class="row">
......@@ -157,7 +83,7 @@
<div class="row">
<div class="col-1"> </div>
<div class="col-10">
<form action="https://icat.esrf.fr/#/browse/facility/ESRF/proposal/{data.proposalNumber}/investigation/{data.investigationId}/dataset" target="_blank">
<form action="https://icat.esrf.fr/#/browse/facility/ESRF/proposal/{data.proposalName}/investigation/{data.investigationId}/dataset" target="_blank">
<button type="submit" class="btn btn-primary btn-lg btn-block"> Access data </button>
</form>
</div>
......
{! Presentation of the metadata for the DOI which are from sessions !}
{@eq key=data.resourceType value=constants.ES_RESOURCE_TYPE}
<div class="row">
{?data.mergedProposalBeamlines}
{?data.mergedProposalBeamlines[0]}
<div class="col">
<div class="myBorder-left">
<label> Proposal </label>
{?data.mergedProposalBeamlines[0].proposal}
<div class="padding-left-15">{data.mergedProposalBeamlines[0].proposal}</div>
{:else}
<div class="padding-left-15"> Not available</div>