diff --git a/.jshintrc b/.jshintrc index 047e923..10cdfcf 100644 --- a/.jshintrc +++ b/.jshintrc @@ -18,5 +18,7 @@ "trailing": true, "smarttabs": true, "white": true, - "predef": ["angular"] + "predef": [ + "angular" + ] } diff --git a/.yo-rc.json b/.yo-rc.json index d3500dc..98bdaf4 100644 --- a/.yo-rc.json +++ b/.yo-rc.json @@ -1,25 +1,25 @@ { - "generator-jhipster": { - "baseName": "malariaplantdb", - "packageName": "nc.ird.malariaplantdb", - "packageFolder": "nc/ird/malariaplantdb", - "authenticationType": "xauth", - "hibernateCache": "ehcache", - "clusteredHttpSession": "no", - "websocket": "no", - "databaseType": "sql", - "devDatabaseType": "h2Memory", - "prodDatabaseType": "postgresql", - "searchEngine": "elasticsearch", - "useSass": true, - "buildTool": "maven", - "frontendBuilder": "grunt", - "javaVersion": "8", - "enableTranslation": false, - "rememberMeKey": "cb0f037ff3c3646f9796359f3916f7e654f33744", - "enableSocialSignIn": false, - "testFrameworks": [ - "gatling" - ] - } -} \ No newline at end of file + "generator-jhipster": { + "baseName": "malariaplantdb", + "packageName": "nc.ird.malariaplantdb", + "packageFolder": "nc/ird/malariaplantdb", + "authenticationType": "xauth", + "hibernateCache": "ehcache", + "clusteredHttpSession": "no", + "websocket": "no", + "databaseType": "sql", + "devDatabaseType": "h2Memory", + "prodDatabaseType": "postgresql", + "searchEngine": "elasticsearch", + "useSass": true, + "buildTool": "maven", + "frontendBuilder": "grunt", + "javaVersion": "8", + "enableTranslation": false, + "rememberMeKey": "cb0f037ff3c3646f9796359f3916f7e654f33744", + "enableSocialSignIn": false, + "testFrameworks": [ + "gatling" + ] + } +} diff --git a/Gruntfile.js b/Gruntfile.js index bbc44ba..dfbff1a 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -4,10 +4,10 @@ var fs = require('fs'); var parseString = require('xml2js').parseString; // Returns the second occurence of the version number -var parseVersionFromPomXml = function() { +var parseVersionFromPomXml = function () { var version; var pomXml = fs.readFileSync('pom.xml', "utf8"); - parseString(pomXml, function (err, result){ + parseString(pomXml, function (err, result) { version = result.project.version[0]; }); return version; @@ -16,8 +16,8 @@ var parseVersionFromPomXml = function() { // usemin custom step var useminAutoprefixer = { name: 'autoprefixer', - createConfig: function(context, block) { - if(block.src.length === 0) { + createConfig: function (context, block) { + if (block.src.length === 0) { return {}; } else { return require('grunt-usemin/lib/config/cssmin').createConfig(context, block); // Reuse cssmins createConfig @@ -101,7 +101,7 @@ module.exports = function (grunt) { browserSync: { dev: { bsFiles: { - src : [ + src: [ 'src/main/webapp/**/*.html', 'src/main/webapp/scripts/**/*.html', 'src/main/webapp/**/*.json', @@ -185,9 +185,9 @@ module.exports = function (grunt) { js: ['concat', 'uglifyjs'], css: ['cssmin', useminAutoprefixer] // Let cssmin concat files so it corrects relative paths to fonts and images }, - post: {} - } + post: {} } + } } }, usemin: { @@ -227,7 +227,7 @@ module.exports = function (grunt) { cssmin: { // src and dest is configured in a subtask called "generated" by usemin }, - ngtemplates: { + ngtemplates: { dist: { cwd: 'src/main/webapp', src: ['scripts/app/**/*.html', 'scripts/components/**/*.html'], @@ -271,7 +271,7 @@ module.exports = function (grunt) { cwd: 'src/main/webapp', dest: '<%= yeoman.dist %>/assets/fonts', src: [ - 'bower_components/bootstrap/fonts/*.*' + 'bower_components/bootstrap/fonts/*.*' ] }] }, @@ -297,11 +297,11 @@ module.exports = function (grunt) { }] }, generateOpenshiftDirectory: { - expand: true, - dest: 'deploy/openshift', - src: [ - 'pom.xml', - 'src/main/**' + expand: true, + dest: 'deploy/openshift', + src: [ + 'pom.xml', + 'src/main/**' ] } }, diff --git a/package.json b/package.json index ad45a4b..998f090 100644 --- a/package.json +++ b/package.json @@ -29,7 +29,7 @@ "grunt-svgmin": "2.0.1", "grunt-text-replace": "0.4.0", "grunt-usemin": "3.0.0", - "grunt-angular-templates":"0.5.7", + "grunt-angular-templates": "0.5.7", "load-grunt-tasks": "3.1.0", "grunt-karma": "0.11.0", "time-grunt": "1.1.0", diff --git a/pom.xml b/pom.xml index 4ecef9b..03468fb 100755 --- a/pom.xml +++ b/pom.xml @@ -1,5 +1,6 @@ - + 4.0.0 @@ -68,6 +69,10 @@ 0.6 1.0.1-SNAPSHOT 1.0.1-SNAPSHOT + 1.16.18 + 1.9.3 + 1.0.6 + 0.9.9 false 4.0.2.RELEASE 2.0.3 @@ -75,13 +80,18 @@ ${project.build.directory}/test-results 2.6 0.7.4.201502262128 - src/main/webapp/assets/**/*.*, src/main/webapp/bower_components/**/*.*, src/main/webapp/dist/**/*.* + src/main/webapp/assets/**/*.*, src/main/webapp/bower_components/**/*.*, + src/main/webapp/dist/**/*.* + jacoco - ${project.testresult.directory}/coverage/jacoco/jacoco-it.exec + ${project.testresult.directory}/coverage/jacoco/jacoco-it.exec + ${project.testresult.directory}/coverage/jacoco/jacoco.exec - ${project.testresult.directory}/karma - ${project.testresult.directory}/coverage/report-lcov/lcov.info + ${project.testresult.directory}/karma + + ${project.testresult.directory}/coverage/report-lcov/lcov.info + ${project.basedir}/src/main/ ${project.testresult.directory}/surefire-reports @@ -403,7 +413,35 @@ locales ${citation-locales.version} - + + org.projectlombok + lombok + ${lombok.version} + provided + + + commons-beanutils + commons-beanutils + ${commons-beanutils.version} + + + + net.sf.jxls + jxls-core + ${jxls.version} + + + net.sf.jxls + jxls-reader + ${jxls.version} + + + + org.reflections + reflections + ${reflections.version} + + spring-boot:run @@ -482,11 +520,14 @@ - You are running an older version of Maven. JHipster requires at least Maven 3.0 + You are running an older version of Maven. JHipster requires at least Maven 3.0 + [3.0.0,) - You are running an older version of Java. JHipster requires at least JDK ${java.version} + You are running an older version of Java. JHipster requires at least JDK + ${java.version} + [${java.version}.0,) @@ -583,7 +624,9 @@ ${liquibase.version} src/main/resources/config/liquibase/master.xml - src/main/resources/config/liquibase/changelog/${maven.build.timestamp}_changelog.xml + + src/main/resources/config/liquibase/changelog/${maven.build.timestamp}_changelog.xml + diff --git a/src/main/java/nc/ird/malariaplantdb/Application.java b/src/main/java/nc/ird/malariaplantdb/Application.java index 42c7c0d..6149d7e 100644 --- a/src/main/java/nc/ird/malariaplantdb/Application.java +++ b/src/main/java/nc/ird/malariaplantdb/Application.java @@ -2,7 +2,6 @@ import nc.ird.malariaplantdb.config.Constants; import nc.ird.malariaplantdb.config.JHipsterProperties; - import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.boot.SpringApplication; diff --git a/src/main/java/nc/ird/malariaplantdb/ApplicationWebXml.java b/src/main/java/nc/ird/malariaplantdb/ApplicationWebXml.java index c86e102..cbd2a6f 100644 --- a/src/main/java/nc/ird/malariaplantdb/ApplicationWebXml.java +++ b/src/main/java/nc/ird/malariaplantdb/ApplicationWebXml.java @@ -1,7 +1,6 @@ package nc.ird.malariaplantdb; import nc.ird.malariaplantdb.config.Constants; - import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.boot.builder.SpringApplicationBuilder; diff --git a/src/main/java/nc/ird/malariaplantdb/aop/logging/LoggingAspect.java b/src/main/java/nc/ird/malariaplantdb/aop/logging/LoggingAspect.java index 784465d..091f8b2 100644 --- a/src/main/java/nc/ird/malariaplantdb/aop/logging/LoggingAspect.java +++ b/src/main/java/nc/ird/malariaplantdb/aop/logging/LoggingAspect.java @@ -1,7 +1,6 @@ package nc.ird.malariaplantdb.aop.logging; import nc.ird.malariaplantdb.config.Constants; - import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.AfterThrowing; diff --git a/src/main/java/nc/ird/malariaplantdb/async/ExceptionHandlingAsyncTaskExecutor.java b/src/main/java/nc/ird/malariaplantdb/async/ExceptionHandlingAsyncTaskExecutor.java index 4a5a6ff..8520c20 100644 --- a/src/main/java/nc/ird/malariaplantdb/async/ExceptionHandlingAsyncTaskExecutor.java +++ b/src/main/java/nc/ird/malariaplantdb/async/ExceptionHandlingAsyncTaskExecutor.java @@ -1,14 +1,14 @@ package nc.ird.malariaplantdb.async; -import java.util.concurrent.Callable; -import java.util.concurrent.Future; - import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.DisposableBean; import org.springframework.beans.factory.InitializingBean; import org.springframework.core.task.AsyncTaskExecutor; +import java.util.concurrent.Callable; +import java.util.concurrent.Future; + public class ExceptionHandlingAsyncTaskExecutor implements AsyncTaskExecutor, InitializingBean, DisposableBean { diff --git a/src/main/java/nc/ird/malariaplantdb/config/AsyncConfiguration.java b/src/main/java/nc/ird/malariaplantdb/config/AsyncConfiguration.java index 673e8df..157f5b5 100644 --- a/src/main/java/nc/ird/malariaplantdb/config/AsyncConfiguration.java +++ b/src/main/java/nc/ird/malariaplantdb/config/AsyncConfiguration.java @@ -1,19 +1,19 @@ package nc.ird.malariaplantdb.config; import nc.ird.malariaplantdb.async.ExceptionHandlingAsyncTaskExecutor; - import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler; import org.springframework.aop.interceptor.SimpleAsyncUncaughtExceptionHandler; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.scheduling.annotation.*; +import org.springframework.scheduling.annotation.AsyncConfigurer; +import org.springframework.scheduling.annotation.EnableAsync; +import org.springframework.scheduling.annotation.EnableScheduling; import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; -import java.util.concurrent.Executor; - import javax.inject.Inject; +import java.util.concurrent.Executor; @Configuration @EnableAsync diff --git a/src/main/java/nc/ird/malariaplantdb/config/CacheConfiguration.java b/src/main/java/nc/ird/malariaplantdb/config/CacheConfiguration.java index 137f811..87872bc 100644 --- a/src/main/java/nc/ird/malariaplantdb/config/CacheConfiguration.java +++ b/src/main/java/nc/ird/malariaplantdb/config/CacheConfiguration.java @@ -7,8 +7,10 @@ import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.cache.CacheManager; import org.springframework.cache.annotation.EnableCaching; -import org.springframework.context.annotation.*; import org.springframework.cache.ehcache.EhCacheCacheManager; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Profile; import org.springframework.util.Assert; import javax.annotation.PreDestroy; diff --git a/src/main/java/nc/ird/malariaplantdb/config/CloudDatabaseConfiguration.java b/src/main/java/nc/ird/malariaplantdb/config/CloudDatabaseConfiguration.java index 421b174..df739ae 100644 --- a/src/main/java/nc/ird/malariaplantdb/config/CloudDatabaseConfiguration.java +++ b/src/main/java/nc/ird/malariaplantdb/config/CloudDatabaseConfiguration.java @@ -3,7 +3,9 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.cloud.config.java.AbstractCloudConfig; -import org.springframework.context.annotation.*; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Profile; import javax.sql.DataSource; diff --git a/src/main/java/nc/ird/malariaplantdb/config/DatabaseConfiguration.java b/src/main/java/nc/ird/malariaplantdb/config/DatabaseConfiguration.java index ce168d9..8ddd9cc 100644 --- a/src/main/java/nc/ird/malariaplantdb/config/DatabaseConfiguration.java +++ b/src/main/java/nc/ird/malariaplantdb/config/DatabaseConfiguration.java @@ -1,12 +1,11 @@ package nc.ird.malariaplantdb.config; -import nc.ird.malariaplantdb.config.liquibase.AsyncSpringLiquibase; - import com.codahale.metrics.MetricRegistry; import com.fasterxml.jackson.datatype.hibernate4.Hibernate4Module; import com.zaxxer.hikari.HikariConfig; import com.zaxxer.hikari.HikariDataSource; import liquibase.integration.spring.SpringLiquibase; +import nc.ird.malariaplantdb.config.liquibase.AsyncSpringLiquibase; import org.h2.tools.Server; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -17,7 +16,6 @@ import org.springframework.context.ApplicationContextException; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Profile; import org.springframework.core.env.Environment; import org.springframework.data.elasticsearch.repository.config.EnableElasticsearchRepositories; import org.springframework.data.jpa.repository.config.EnableJpaAuditing; diff --git a/src/main/java/nc/ird/malariaplantdb/config/JHipsterProperties.java b/src/main/java/nc/ird/malariaplantdb/config/JHipsterProperties.java index 6af430f..b933553 100644 --- a/src/main/java/nc/ird/malariaplantdb/config/JHipsterProperties.java +++ b/src/main/java/nc/ird/malariaplantdb/config/JHipsterProperties.java @@ -1,9 +1,9 @@ package nc.ird.malariaplantdb.config; -import javax.validation.constraints.NotNull; - import org.springframework.boot.context.properties.ConfigurationProperties; +import javax.validation.constraints.NotNull; + /** * Properties specific to JHipster. * diff --git a/src/main/java/nc/ird/malariaplantdb/config/JacksonConfiguration.java b/src/main/java/nc/ird/malariaplantdb/config/JacksonConfiguration.java index eeea564..2ec18c1 100644 --- a/src/main/java/nc/ird/malariaplantdb/config/JacksonConfiguration.java +++ b/src/main/java/nc/ird/malariaplantdb/config/JacksonConfiguration.java @@ -1,13 +1,14 @@ package nc.ird.malariaplantdb.config; -import nc.ird.malariaplantdb.domain.util.*; - import com.fasterxml.jackson.databind.SerializationFeature; import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; -import java.time.*; -import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder; +import nc.ird.malariaplantdb.domain.util.JSR310DateTimeSerializer; +import nc.ird.malariaplantdb.domain.util.JSR310LocalDateDeserializer; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder; + +import java.time.*; @Configuration public class JacksonConfiguration { diff --git a/src/main/java/nc/ird/malariaplantdb/config/LocaleConfiguration.java b/src/main/java/nc/ird/malariaplantdb/config/LocaleConfiguration.java index 917c035..2ddd3de 100644 --- a/src/main/java/nc/ird/malariaplantdb/config/LocaleConfiguration.java +++ b/src/main/java/nc/ird/malariaplantdb/config/LocaleConfiguration.java @@ -1,7 +1,6 @@ package nc.ird.malariaplantdb.config; import nc.ird.malariaplantdb.config.locale.AngularCookieLocaleResolver; - import org.springframework.boot.bind.RelaxedPropertyResolver; import org.springframework.context.EnvironmentAware; import org.springframework.context.MessageSource; diff --git a/src/main/java/nc/ird/malariaplantdb/config/LoggingAspectConfiguration.java b/src/main/java/nc/ird/malariaplantdb/config/LoggingAspectConfiguration.java index 5dd3b02..4ce73ca 100644 --- a/src/main/java/nc/ird/malariaplantdb/config/LoggingAspectConfiguration.java +++ b/src/main/java/nc/ird/malariaplantdb/config/LoggingAspectConfiguration.java @@ -1,7 +1,10 @@ package nc.ird.malariaplantdb.config; import nc.ird.malariaplantdb.aop.logging.LoggingAspect; -import org.springframework.context.annotation.*; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.EnableAspectJAutoProxy; +import org.springframework.context.annotation.Profile; @Configuration @EnableAspectJAutoProxy diff --git a/src/main/java/nc/ird/malariaplantdb/config/MetricsConfiguration.java b/src/main/java/nc/ird/malariaplantdb/config/MetricsConfiguration.java index 2fa0bc2..f8a8861 100644 --- a/src/main/java/nc/ird/malariaplantdb/config/MetricsConfiguration.java +++ b/src/main/java/nc/ird/malariaplantdb/config/MetricsConfiguration.java @@ -12,7 +12,9 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; -import org.springframework.context.annotation.*; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Profile; import javax.annotation.PostConstruct; import javax.inject.Inject; diff --git a/src/main/java/nc/ird/malariaplantdb/config/ThymeleafConfiguration.java b/src/main/java/nc/ird/malariaplantdb/config/ThymeleafConfiguration.java index 5f63bb7..bffac79 100644 --- a/src/main/java/nc/ird/malariaplantdb/config/ThymeleafConfiguration.java +++ b/src/main/java/nc/ird/malariaplantdb/config/ThymeleafConfiguration.java @@ -4,7 +4,9 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.context.MessageSource; -import org.springframework.context.annotation.*; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Description; import org.springframework.context.support.ReloadableResourceBundleMessageSource; import org.thymeleaf.templateresolver.ClassLoaderTemplateResolver; diff --git a/src/main/java/nc/ird/malariaplantdb/config/WebConfigurer.java b/src/main/java/nc/ird/malariaplantdb/config/WebConfigurer.java index 8a7f368..d08c5f2 100644 --- a/src/main/java/nc/ird/malariaplantdb/config/WebConfigurer.java +++ b/src/main/java/nc/ird/malariaplantdb/config/WebConfigurer.java @@ -16,9 +16,10 @@ import org.springframework.context.annotation.Configuration; import org.springframework.core.env.Environment; -import java.util.*; import javax.inject.Inject; import javax.servlet.*; +import java.util.Arrays; +import java.util.EnumSet; /** * Configuration of web application with Servlet 3.0 APIs. diff --git a/src/main/java/nc/ird/malariaplantdb/config/apidoc/SwaggerConfiguration.java b/src/main/java/nc/ird/malariaplantdb/config/apidoc/SwaggerConfiguration.java index 7653517..78f33a9 100644 --- a/src/main/java/nc/ird/malariaplantdb/config/apidoc/SwaggerConfiguration.java +++ b/src/main/java/nc/ird/malariaplantdb/config/apidoc/SwaggerConfiguration.java @@ -2,11 +2,11 @@ import nc.ird.malariaplantdb.config.Constants; import nc.ird.malariaplantdb.config.JHipsterProperties; - -import java.util.Date; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.context.annotation.*; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Profile; import org.springframework.http.ResponseEntity; import org.springframework.util.StopWatch; import springfox.documentation.service.ApiInfo; @@ -14,6 +14,8 @@ import springfox.documentation.spring.web.plugins.Docket; import springfox.documentation.swagger2.annotations.EnableSwagger2; +import java.util.Date; + import static springfox.documentation.builders.PathSelectors.regex; /** diff --git a/src/main/java/nc/ird/malariaplantdb/config/audit/AuditEventConverter.java b/src/main/java/nc/ird/malariaplantdb/config/audit/AuditEventConverter.java index b4aa75a..7503121 100644 --- a/src/main/java/nc/ird/malariaplantdb/config/audit/AuditEventConverter.java +++ b/src/main/java/nc/ird/malariaplantdb/config/audit/AuditEventConverter.java @@ -1,7 +1,6 @@ package nc.ird.malariaplantdb.config.audit; import nc.ird.malariaplantdb.domain.PersistentAuditEvent; - import org.springframework.boot.actuate.audit.AuditEvent; import org.springframework.security.web.authentication.WebAuthenticationDetails; import org.springframework.stereotype.Component; diff --git a/src/main/java/nc/ird/malariaplantdb/config/liquibase/AsyncSpringLiquibase.java b/src/main/java/nc/ird/malariaplantdb/config/liquibase/AsyncSpringLiquibase.java index 27cad80..c298121 100644 --- a/src/main/java/nc/ird/malariaplantdb/config/liquibase/AsyncSpringLiquibase.java +++ b/src/main/java/nc/ird/malariaplantdb/config/liquibase/AsyncSpringLiquibase.java @@ -1,7 +1,8 @@ package nc.ird.malariaplantdb.config.liquibase; -import javax.inject.Inject; - +import liquibase.exception.LiquibaseException; +import liquibase.integration.spring.SpringLiquibase; +import nc.ird.malariaplantdb.config.Constants; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Qualifier; @@ -9,9 +10,7 @@ import org.springframework.core.task.TaskExecutor; import org.springframework.util.StopWatch; -import nc.ird.malariaplantdb.config.Constants; -import liquibase.exception.LiquibaseException; -import liquibase.integration.spring.SpringLiquibase; +import javax.inject.Inject; /** * Specific liquibase.integration.spring.SpringLiquibase that will update the database asynchronously. diff --git a/src/main/java/nc/ird/malariaplantdb/config/locale/AngularCookieLocaleResolver.java b/src/main/java/nc/ird/malariaplantdb/config/locale/AngularCookieLocaleResolver.java index 3a01681..2f4ec5d 100644 --- a/src/main/java/nc/ird/malariaplantdb/config/locale/AngularCookieLocaleResolver.java +++ b/src/main/java/nc/ird/malariaplantdb/config/locale/AngularCookieLocaleResolver.java @@ -6,11 +6,12 @@ import org.springframework.web.servlet.i18n.CookieLocaleResolver; import org.springframework.web.util.WebUtils; +import javax.servlet.http.Cookie; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; import java.util.Locale; import java.util.TimeZone; -import javax.servlet.http.*; - /** * Angular cookie saved the locale with a double quote (%22en%22). * So the default CookieLocaleResolver#StringUtils.parseLocaleString(localePart) diff --git a/src/main/java/nc/ird/malariaplantdb/domain/Author.java b/src/main/java/nc/ird/malariaplantdb/domain/Author.java index 89d93e6..293d6b9 100644 --- a/src/main/java/nc/ird/malariaplantdb/domain/Author.java +++ b/src/main/java/nc/ird/malariaplantdb/domain/Author.java @@ -2,11 +2,13 @@ import com.fasterxml.jackson.annotation.JsonPropertyOrder; import nc.ird.malariaplantdb.domain.util.comparator.AuthorComparator; +import org.apache.commons.lang3.builder.EqualsBuilder; import org.hibernate.annotations.Cache; import org.hibernate.annotations.CacheConcurrencyStrategy; import org.springframework.data.elasticsearch.annotations.Document; import javax.persistence.*; +import javax.validation.constraints.Min; import javax.validation.constraints.NotNull; import javax.validation.constraints.Size; import java.io.Serializable; @@ -47,6 +49,10 @@ public class Author implements Serializable, Comparable { @Column(name = "given", length = 255) private String given; + @Min(value = 1) + @Column(name= "position", nullable = false) + private int position; + public Long getId() { return id; } @@ -75,6 +81,14 @@ public String getGiven() { return given; } + public int getPosition() { + return position; + } + + public void setPosition(int position) { + this.position = position; + } + public void setGiven(String given) { this.given = given; } @@ -90,8 +104,12 @@ public boolean equals(Object o) { Author author = (Author) o; - return Objects.equals(id, author.id); + if (id == null && author.id == null) + return EqualsBuilder.reflectionEquals(this, author); + + if ( ! Objects.equals(id, author.id)) return false; + return true; } @Override @@ -105,6 +123,7 @@ public String toString() { "id=" + id + ", family='" + family + "'" + ", given='" + given + "'" + + ", postion='" + position + "'" + '}'; } diff --git a/src/main/java/nc/ird/malariaplantdb/domain/Compiler.java b/src/main/java/nc/ird/malariaplantdb/domain/Compiler.java index 5a1b559..dc9d48a 100644 --- a/src/main/java/nc/ird/malariaplantdb/domain/Compiler.java +++ b/src/main/java/nc/ird/malariaplantdb/domain/Compiler.java @@ -2,6 +2,7 @@ import com.fasterxml.jackson.annotation.JsonPropertyOrder; import nc.ird.malariaplantdb.domain.util.comparator.CompilerComparator; +import org.apache.commons.lang3.builder.EqualsBuilder; import org.hibernate.annotations.Cache; import org.hibernate.annotations.CacheConcurrencyStrategy; import org.hibernate.annotations.Type; @@ -122,8 +123,12 @@ public boolean equals(Object o) { Compiler compiler = (Compiler) o; - return Objects.equals(id, compiler.id); + if (id == null && compiler.id == null) + return EqualsBuilder.reflectionEquals(this, compiler); + if ( ! Objects.equals(id, compiler.id)) return false; + + return true; } @Override diff --git a/src/main/java/nc/ird/malariaplantdb/domain/PlantIngredient.java b/src/main/java/nc/ird/malariaplantdb/domain/PlantIngredient.java index fbee684..5b31fc7 100644 --- a/src/main/java/nc/ird/malariaplantdb/domain/PlantIngredient.java +++ b/src/main/java/nc/ird/malariaplantdb/domain/PlantIngredient.java @@ -2,6 +2,7 @@ import com.fasterxml.jackson.annotation.JsonPropertyOrder; import nc.ird.malariaplantdb.domain.util.comparator.PlantIngredientComparator; +import org.apache.commons.lang3.builder.EqualsBuilder; import org.hibernate.annotations.Cache; import org.hibernate.annotations.CacheConcurrencyStrategy; import org.springframework.data.elasticsearch.annotations.Document; @@ -91,8 +92,12 @@ public boolean equals(Object o) { PlantIngredient plantIngredient = (PlantIngredient) o; - return Objects.equals(id, plantIngredient.id); + if (id == null && plantIngredient.id == null) + return EqualsBuilder.reflectionEquals(this, plantIngredient); + if ( ! Objects.equals(id, plantIngredient.id)) return false; + + return true; } @Override diff --git a/src/main/java/nc/ird/malariaplantdb/domain/PubSpecies.java b/src/main/java/nc/ird/malariaplantdb/domain/PubSpecies.java index b59ef45..4629b1d 100644 --- a/src/main/java/nc/ird/malariaplantdb/domain/PubSpecies.java +++ b/src/main/java/nc/ird/malariaplantdb/domain/PubSpecies.java @@ -40,7 +40,7 @@ public class PubSpecies implements Serializable, Comparable { @NotNull @ManyToOne - private Species species; + private Species species = new Species(); @NotNull @Size(max = 255) diff --git a/src/main/java/nc/ird/malariaplantdb/domain/Publication.java b/src/main/java/nc/ird/malariaplantdb/domain/Publication.java index 95f6749..528483b 100644 --- a/src/main/java/nc/ird/malariaplantdb/domain/Publication.java +++ b/src/main/java/nc/ird/malariaplantdb/domain/Publication.java @@ -30,7 +30,7 @@ @Entity @JsonPropertyOrder({"id", "entryType", "authors", "year", "title", "journal", "pages", "volume", "nbOfVolumes", "number", "bookTitle", "publisher", "edition", "conferenceName", "conferencePlace", "university", "institution", - "doi", "pmid", "isbn", "url", "isReviewed", "compilers", "compilersNotes", "pubSpecies", "ethnologies", + "doi", "pmid", "isbn", "url", "compilers", "compilersNotes", "pubSpecies", "ethnologies", "inVivoPharmacos", "inVitroPharmacos", "citation"}) @Table(name = "publication", uniqueConstraints = @UniqueConstraint(columnNames = {"title"}, name = "uk_publication_title")) diff --git a/src/main/java/nc/ird/malariaplantdb/domain/Remedy.java b/src/main/java/nc/ird/malariaplantdb/domain/Remedy.java index cc24566..b74b2b0 100644 --- a/src/main/java/nc/ird/malariaplantdb/domain/Remedy.java +++ b/src/main/java/nc/ird/malariaplantdb/domain/Remedy.java @@ -3,6 +3,7 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonPropertyOrder; import nc.ird.malariaplantdb.domain.util.comparator.RemedyComparator; +import org.apache.commons.lang3.builder.EqualsBuilder; import org.hibernate.annotations.BatchSize; import org.hibernate.annotations.Cache; import org.hibernate.annotations.CacheConcurrencyStrategy; @@ -67,8 +68,12 @@ public boolean equals(Object o) { Remedy remedy = (Remedy) o; - return Objects.equals(id, remedy.id); + if (id == null && remedy.id == null) + return EqualsBuilder.reflectionEquals(this, remedy); + if ( ! Objects.equals(id, remedy.id)) return false; + + return true; } @Override diff --git a/src/main/java/nc/ird/malariaplantdb/domain/Species.java b/src/main/java/nc/ird/malariaplantdb/domain/Species.java index 57001c6..08d5a71 100644 --- a/src/main/java/nc/ird/malariaplantdb/domain/Species.java +++ b/src/main/java/nc/ird/malariaplantdb/domain/Species.java @@ -2,6 +2,7 @@ import com.fasterxml.jackson.annotation.JsonPropertyOrder; import nc.ird.malariaplantdb.domain.util.comparator.SpeciesComparator; +import org.apache.commons.lang3.builder.EqualsBuilder; import org.hibernate.annotations.Cache; import org.hibernate.annotations.CacheConcurrencyStrategy; import org.springframework.data.elasticsearch.annotations.Document; @@ -77,8 +78,12 @@ public boolean equals(Object o) { Species species = (Species) o; - return Objects.equals(id, species.id); + if (id == null && species.id == null) + return EqualsBuilder.reflectionEquals(this, species); + if ( ! Objects.equals(id, species.id)) return false; + + return true; } @Override diff --git a/src/main/java/nc/ird/malariaplantdb/domain/util/CitationConverter.java b/src/main/java/nc/ird/malariaplantdb/domain/util/CitationConverter.java index 08e8e62..11cb177 100644 --- a/src/main/java/nc/ird/malariaplantdb/domain/util/CitationConverter.java +++ b/src/main/java/nc/ird/malariaplantdb/domain/util/CitationConverter.java @@ -62,6 +62,7 @@ public String convert(Publication pub) { } private CSLItemData buildJournalArticleItem(Publication pub) { + return new CSLItemDataBuilder() .type(CSLType.ARTICLE_JOURNAL) .title(pub.getTitle()) @@ -79,6 +80,7 @@ private CSLItemData buildJournalArticleItem(Publication pub) { } private CSLItemData buildBookItem(Publication pub) { + return new CSLItemDataBuilder() .type(CSLType.BOOK) .title(pub.getTitle()) @@ -96,6 +98,7 @@ private CSLItemData buildBookItem(Publication pub) { } private CSLItemData buildConferencePaperItem(Publication pub){ + return new CSLItemDataBuilder() .type(CSLType.PAPER_CONFERENCE) .title(pub.getTitle()) @@ -113,6 +116,7 @@ private CSLItemData buildConferencePaperItem(Publication pub){ } private CSLItemData buildChapterItem(Publication pub) { + return new CSLItemDataBuilder() .type(CSLType.CHAPTER) .title(pub.getTitle()) @@ -132,6 +136,7 @@ private CSLItemData buildChapterItem(Publication pub) { } private CSLItemData buildThesisItem(Publication pub) { + return new CSLItemDataBuilder() .type(CSLType.THESIS) .title(pub.getTitle()) @@ -146,6 +151,7 @@ private CSLItemData buildThesisItem(Publication pub) { } private CSLItemData buildTechReportItem(Publication pub) { + return new CSLItemDataBuilder() .type(CSLType.REPORT) .title(pub.getTitle()) @@ -160,4 +166,5 @@ private CSLItemData buildTechReportItem(Publication pub) { .URL(pub.getUrl()) .build(); } + } diff --git a/src/main/java/nc/ird/malariaplantdb/domain/util/comparator/AuthorComparator.java b/src/main/java/nc/ird/malariaplantdb/domain/util/comparator/AuthorComparator.java index 8c7ced2..a425d6c 100644 --- a/src/main/java/nc/ird/malariaplantdb/domain/util/comparator/AuthorComparator.java +++ b/src/main/java/nc/ird/malariaplantdb/domain/util/comparator/AuthorComparator.java @@ -11,10 +11,14 @@ */ public class AuthorComparator implements Comparator { - private final static Comparator BY_ID = Comparator.comparing(Author::getId); + private final static Comparator BY_POSITION = Comparator.comparing(Author::getPosition).thenComparing( + Author::getGiven); + + private final static Comparator BY_POSITION_AND_ID = BY_POSITION.thenComparing(Author::getId, + Comparator.nullsFirst(Comparator.naturalOrder())); @Override public int compare(Author a1, Author a2) { - return a1.equals(a2) ? 0 : BY_ID.compare(a1, a2); + return a1.equals(a2) ? 0 : BY_POSITION_AND_ID.compare(a1, a2); } } diff --git a/src/main/java/nc/ird/malariaplantdb/repository/AuthorRepository.java b/src/main/java/nc/ird/malariaplantdb/repository/AuthorRepository.java index 55375a1..4e2209c 100644 --- a/src/main/java/nc/ird/malariaplantdb/repository/AuthorRepository.java +++ b/src/main/java/nc/ird/malariaplantdb/repository/AuthorRepository.java @@ -1,13 +1,13 @@ package nc.ird.malariaplantdb.repository; import nc.ird.malariaplantdb.domain.Author; -import org.springframework.data.jpa.repository.*; - -import java.util.List; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; /** * Spring Data JPA repository for the Author entity. */ +@Repository public interface AuthorRepository extends JpaRepository { } diff --git a/src/main/java/nc/ird/malariaplantdb/repository/AuthorityRepository.java b/src/main/java/nc/ird/malariaplantdb/repository/AuthorityRepository.java index bd3e495..0cffbf9 100644 --- a/src/main/java/nc/ird/malariaplantdb/repository/AuthorityRepository.java +++ b/src/main/java/nc/ird/malariaplantdb/repository/AuthorityRepository.java @@ -1,11 +1,12 @@ package nc.ird.malariaplantdb.repository; import nc.ird.malariaplantdb.domain.Authority; - import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; /** * Spring Data JPA repository for the Authority entity. */ +@Repository public interface AuthorityRepository extends JpaRepository { } diff --git a/src/main/java/nc/ird/malariaplantdb/repository/CompilerRepository.java b/src/main/java/nc/ird/malariaplantdb/repository/CompilerRepository.java index d16bd5f..214d6da 100644 --- a/src/main/java/nc/ird/malariaplantdb/repository/CompilerRepository.java +++ b/src/main/java/nc/ird/malariaplantdb/repository/CompilerRepository.java @@ -1,13 +1,17 @@ package nc.ird.malariaplantdb.repository; import nc.ird.malariaplantdb.domain.Compiler; -import org.springframework.data.jpa.repository.*; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; import java.util.List; /** * Spring Data JPA repository for the Compiler entity. */ +@Repository public interface CompilerRepository extends JpaRepository { + List findByFamilyAndGivenAllIgnoreCase(String family, String given); + } diff --git a/src/main/java/nc/ird/malariaplantdb/repository/CustomAuditEventRepository.java b/src/main/java/nc/ird/malariaplantdb/repository/CustomAuditEventRepository.java index c909a78..1fbb66d 100644 --- a/src/main/java/nc/ird/malariaplantdb/repository/CustomAuditEventRepository.java +++ b/src/main/java/nc/ird/malariaplantdb/repository/CustomAuditEventRepository.java @@ -2,7 +2,6 @@ import nc.ird.malariaplantdb.config.audit.AuditEventConverter; import nc.ird.malariaplantdb.domain.PersistentAuditEvent; - import org.springframework.boot.actuate.audit.AuditEvent; import org.springframework.boot.actuate.audit.AuditEventRepository; import org.springframework.context.annotation.Bean; diff --git a/src/main/java/nc/ird/malariaplantdb/repository/EthnologyRepository.java b/src/main/java/nc/ird/malariaplantdb/repository/EthnologyRepository.java index c23dbf7..c826e1e 100644 --- a/src/main/java/nc/ird/malariaplantdb/repository/EthnologyRepository.java +++ b/src/main/java/nc/ird/malariaplantdb/repository/EthnologyRepository.java @@ -4,12 +4,14 @@ import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; +import org.springframework.stereotype.Repository; import java.util.List; /** * Spring Data JPA repository for the Ethnology entity. */ +@Repository public interface EthnologyRepository extends JpaRepository { List findByPublicationId(Long id); diff --git a/src/main/java/nc/ird/malariaplantdb/repository/InVitroPharmacoRepository.java b/src/main/java/nc/ird/malariaplantdb/repository/InVitroPharmacoRepository.java index a46e1c9..2b2c5bc 100644 --- a/src/main/java/nc/ird/malariaplantdb/repository/InVitroPharmacoRepository.java +++ b/src/main/java/nc/ird/malariaplantdb/repository/InVitroPharmacoRepository.java @@ -4,12 +4,14 @@ import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; +import org.springframework.stereotype.Repository; import java.util.List; /** * Spring Data JPA repository for the InVitroPharmaco entity. */ +@Repository public interface InVitroPharmacoRepository extends JpaRepository { List findByPublicationId(Long id); diff --git a/src/main/java/nc/ird/malariaplantdb/repository/InVivoPharmacoRepository.java b/src/main/java/nc/ird/malariaplantdb/repository/InVivoPharmacoRepository.java index f593f6d..e8dcd75 100644 --- a/src/main/java/nc/ird/malariaplantdb/repository/InVivoPharmacoRepository.java +++ b/src/main/java/nc/ird/malariaplantdb/repository/InVivoPharmacoRepository.java @@ -4,12 +4,14 @@ import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; +import org.springframework.stereotype.Repository; import java.util.List; /** * Spring Data JPA repository for the InVivoPharmaco entity. */ +@Repository public interface InVivoPharmacoRepository extends JpaRepository { List findByPublicationId(Long id); diff --git a/src/main/java/nc/ird/malariaplantdb/repository/PersistenceAuditEventRepository.java b/src/main/java/nc/ird/malariaplantdb/repository/PersistenceAuditEventRepository.java index 9676134..ecc01f8 100644 --- a/src/main/java/nc/ird/malariaplantdb/repository/PersistenceAuditEventRepository.java +++ b/src/main/java/nc/ird/malariaplantdb/repository/PersistenceAuditEventRepository.java @@ -1,15 +1,16 @@ package nc.ird.malariaplantdb.repository; import nc.ird.malariaplantdb.domain.PersistentAuditEvent; - -import java.time.LocalDateTime; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; +import java.time.LocalDateTime; import java.util.List; /** * Spring Data JPA repository for the PersistentAuditEvent entity. */ +@Repository public interface PersistenceAuditEventRepository extends JpaRepository { List findByPrincipal(String principal); diff --git a/src/main/java/nc/ird/malariaplantdb/repository/PlantIngredientRepository.java b/src/main/java/nc/ird/malariaplantdb/repository/PlantIngredientRepository.java index f4ba6a9..9dfc134 100644 --- a/src/main/java/nc/ird/malariaplantdb/repository/PlantIngredientRepository.java +++ b/src/main/java/nc/ird/malariaplantdb/repository/PlantIngredientRepository.java @@ -1,13 +1,13 @@ package nc.ird.malariaplantdb.repository; import nc.ird.malariaplantdb.domain.PlantIngredient; -import org.springframework.data.jpa.repository.*; - -import java.util.List; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; /** * Spring Data JPA repository for the PlantIngredient entity. */ +@Repository public interface PlantIngredientRepository extends JpaRepository { } diff --git a/src/main/java/nc/ird/malariaplantdb/repository/PubSpeciesRepository.java b/src/main/java/nc/ird/malariaplantdb/repository/PubSpeciesRepository.java index 037159b..4bb7357 100644 --- a/src/main/java/nc/ird/malariaplantdb/repository/PubSpeciesRepository.java +++ b/src/main/java/nc/ird/malariaplantdb/repository/PubSpeciesRepository.java @@ -4,12 +4,14 @@ import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; +import org.springframework.stereotype.Repository; import java.util.List; /** * Spring Data JPA repository for the PubSpecies entity. */ +@Repository public interface PubSpeciesRepository extends JpaRepository { List findByPublicationId(Long id); diff --git a/src/main/java/nc/ird/malariaplantdb/repository/PublicationRepository.java b/src/main/java/nc/ird/malariaplantdb/repository/PublicationRepository.java index 6b9805c..8590a9c 100644 --- a/src/main/java/nc/ird/malariaplantdb/repository/PublicationRepository.java +++ b/src/main/java/nc/ird/malariaplantdb/repository/PublicationRepository.java @@ -1,16 +1,15 @@ package nc.ird.malariaplantdb.repository; import nc.ird.malariaplantdb.domain.Publication; -import org.springframework.data.domain.Page; -import org.springframework.data.domain.Pageable; -import org.springframework.data.jpa.repository.*; -import org.springframework.data.repository.query.Param; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; import java.util.List; /** * Spring Data JPA repository for the Publication entity. */ +@Repository public interface PublicationRepository extends JpaRepository { // Other method to fetch the authors and compilers relationships, but as it's by a join, the firsResult and @@ -20,4 +19,6 @@ public interface PublicationRepository extends JpaRepository { // countQuery = "select count(publication) from Publication publication") //Page findAllWithEagerRelationships(Pageable page); + List findByTitleIgnoreCase(String title); + } diff --git a/src/main/java/nc/ird/malariaplantdb/repository/RemedyRepository.java b/src/main/java/nc/ird/malariaplantdb/repository/RemedyRepository.java index 51b6c9e..4c4ce59 100644 --- a/src/main/java/nc/ird/malariaplantdb/repository/RemedyRepository.java +++ b/src/main/java/nc/ird/malariaplantdb/repository/RemedyRepository.java @@ -2,10 +2,12 @@ import nc.ird.malariaplantdb.domain.Remedy; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; /** * Spring Data JPA repository for the Remedy entity. */ +@Repository public interface RemedyRepository extends JpaRepository { } diff --git a/src/main/java/nc/ird/malariaplantdb/repository/SpeciesRepository.java b/src/main/java/nc/ird/malariaplantdb/repository/SpeciesRepository.java index e0bcae1..6d699ee 100644 --- a/src/main/java/nc/ird/malariaplantdb/repository/SpeciesRepository.java +++ b/src/main/java/nc/ird/malariaplantdb/repository/SpeciesRepository.java @@ -2,10 +2,16 @@ import nc.ird.malariaplantdb.domain.Species; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import java.util.Optional; /** * Spring Data JPA repository for the Species entity. */ +@Repository public interface SpeciesRepository extends JpaRepository { + Optional findByFamilyAndSpeciesAllIgnoreCase(String family, String species); + } diff --git a/src/main/java/nc/ird/malariaplantdb/repository/UserRepository.java b/src/main/java/nc/ird/malariaplantdb/repository/UserRepository.java index c9132c9..8512e98 100644 --- a/src/main/java/nc/ird/malariaplantdb/repository/UserRepository.java +++ b/src/main/java/nc/ird/malariaplantdb/repository/UserRepository.java @@ -1,17 +1,17 @@ package nc.ird.malariaplantdb.repository; import nc.ird.malariaplantdb.domain.User; - -import java.time.ZonedDateTime; import org.springframework.data.jpa.repository.JpaRepository; -import org.springframework.data.jpa.repository.Query; +import org.springframework.stereotype.Repository; +import java.time.ZonedDateTime; import java.util.List; import java.util.Optional; /** * Spring Data JPA repository for the User entity. */ +@Repository public interface UserRepository extends JpaRepository { Optional findOneByActivationKey(String activationKey); diff --git a/src/main/java/nc/ird/malariaplantdb/security/AuthenticationProvider.java b/src/main/java/nc/ird/malariaplantdb/security/AuthenticationProvider.java index fcc7a17..9b8569f 100644 --- a/src/main/java/nc/ird/malariaplantdb/security/AuthenticationProvider.java +++ b/src/main/java/nc/ird/malariaplantdb/security/AuthenticationProvider.java @@ -6,14 +6,10 @@ import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.Authentication; import org.springframework.security.core.AuthenticationException; -import org.springframework.security.core.GrantedAuthority; -import org.springframework.security.core.userdetails.User; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.security.crypto.password.PasswordEncoder; -import java.util.Collection; - public class AuthenticationProvider implements org.springframework.security.authentication.AuthenticationProvider { private final Logger log = LoggerFactory.getLogger(AuthenticationProvider.class); diff --git a/src/main/java/nc/ird/malariaplantdb/security/CustomUserDetails.java b/src/main/java/nc/ird/malariaplantdb/security/CustomUserDetails.java index 2a09f77..44f8a9c 100644 --- a/src/main/java/nc/ird/malariaplantdb/security/CustomUserDetails.java +++ b/src/main/java/nc/ird/malariaplantdb/security/CustomUserDetails.java @@ -1,15 +1,15 @@ package nc.ird.malariaplantdb.security; -import java.util.Collection; -import java.util.Set; - import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.userdetails.UserDetails; +import java.util.Collection; +import java.util.Set; + /** * Class representing the Spring Security authenticated user. - * + * * @see UserDetails * */ diff --git a/src/main/java/nc/ird/malariaplantdb/security/SpringSecurityAuditorAware.java b/src/main/java/nc/ird/malariaplantdb/security/SpringSecurityAuditorAware.java index 113baed..176a975 100644 --- a/src/main/java/nc/ird/malariaplantdb/security/SpringSecurityAuditorAware.java +++ b/src/main/java/nc/ird/malariaplantdb/security/SpringSecurityAuditorAware.java @@ -1,7 +1,6 @@ package nc.ird.malariaplantdb.security; import nc.ird.malariaplantdb.config.Constants; - import org.springframework.data.domain.AuditorAware; import org.springframework.stereotype.Component; diff --git a/src/main/java/nc/ird/malariaplantdb/security/UserDetailsService.java b/src/main/java/nc/ird/malariaplantdb/security/UserDetailsService.java index a4c055b..14d367a 100644 --- a/src/main/java/nc/ird/malariaplantdb/security/UserDetailsService.java +++ b/src/main/java/nc/ird/malariaplantdb/security/UserDetailsService.java @@ -1,6 +1,5 @@ package nc.ird.malariaplantdb.security; -import nc.ird.malariaplantdb.domain.Authority; import nc.ird.malariaplantdb.domain.User; import nc.ird.malariaplantdb.repository.UserRepository; import org.slf4j.Logger; diff --git a/src/main/java/nc/ird/malariaplantdb/service/AuditEventService.java b/src/main/java/nc/ird/malariaplantdb/service/AuditEventService.java index 205cb2f..c081459 100644 --- a/src/main/java/nc/ird/malariaplantdb/service/AuditEventService.java +++ b/src/main/java/nc/ird/malariaplantdb/service/AuditEventService.java @@ -3,12 +3,12 @@ import nc.ird.malariaplantdb.config.audit.AuditEventConverter; import nc.ird.malariaplantdb.domain.PersistentAuditEvent; import nc.ird.malariaplantdb.repository.PersistenceAuditEventRepository; -import java.time.LocalDateTime; import org.springframework.boot.actuate.audit.AuditEvent; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import javax.inject.Inject; +import java.time.LocalDateTime; import java.util.List; import java.util.Optional; diff --git a/src/main/java/nc/ird/malariaplantdb/service/UserService.java b/src/main/java/nc/ird/malariaplantdb/service/UserService.java index faeb584..201cd61 100644 --- a/src/main/java/nc/ird/malariaplantdb/service/UserService.java +++ b/src/main/java/nc/ird/malariaplantdb/service/UserService.java @@ -7,8 +7,6 @@ import nc.ird.malariaplantdb.repository.search.UserSearchRepository; import nc.ird.malariaplantdb.security.SecurityUtils; import nc.ird.malariaplantdb.service.util.RandomUtil; -import java.time.ZonedDateTime; -import java.time.LocalDate; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.scheduling.annotation.Scheduled; @@ -17,7 +15,11 @@ import org.springframework.transaction.annotation.Transactional; import javax.inject.Inject; -import java.util.*; +import java.time.ZonedDateTime; +import java.util.HashSet; +import java.util.List; +import java.util.Optional; +import java.util.Set; /** * Service class for managing users. diff --git a/src/main/java/nc/ird/malariaplantdb/service/xls/ExcelChecker.java b/src/main/java/nc/ird/malariaplantdb/service/xls/ExcelChecker.java new file mode 100644 index 0000000..3763a0c --- /dev/null +++ b/src/main/java/nc/ird/malariaplantdb/service/xls/ExcelChecker.java @@ -0,0 +1,66 @@ +package nc.ird.malariaplantdb.service.xls; + +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import nc.ird.malariaplantdb.service.xls.exceptions.ImportRuntimeException; +import nc.ird.malariaplantdb.service.xls.infos.ColumnInfo; +import nc.ird.malariaplantdb.service.xls.infos.SheetInfo; +import nc.ird.malariaplantdb.service.xls.structures.CellError; +import nc.ird.malariaplantdb.service.xls.structures.ClassMap; + +import javax.validation.ConstraintViolation; +import javax.validation.Validation; +import javax.validation.Validator; +import javax.validation.ValidatorFactory; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; + +@Slf4j +@AllArgsConstructor +public class ExcelChecker { + + private List sheetInfos; + + @SuppressWarnings("unchecked") + public List checkBusinessRules(ClassMap dtosMap) { + ArrayList cellErrors = new ArrayList<>(); + + ValidatorFactory factory = Validation.buildDefaultValidatorFactory(); + Validator validator = factory.getValidator(); + + for (SheetInfo sheetInfo : sheetInfos) { + int curLine = sheetInfo.getStartRow(); + List readBeans = dtosMap.getList(sheetInfo.getDtoClass()); + for (Object readBean : readBeans) { + Set> constraintViolations = + validator.validate(readBean); + for (ConstraintViolation constraintViolation : constraintViolations) { + ColumnInfo columnInfo = sheetInfo.getColumnInfoByDtoProperty(constraintViolation + .getPropertyPath().toString()); + + if (columnInfo == null) { + throw new ImportRuntimeException("An unexpected errors occurs during the Excel file checking", + new IllegalArgumentException(String.format("Impossible to find the dtoPropertyName " + + "'%s' in the sheetInfos", constraintViolation.getPropertyPath()))); + } + + cellErrors.add( + new CellError( + constraintViolation.getMessage(), + sheetInfo.getSheetLabel(), + curLine, + columnInfo.getColumnLabel() + ) + ); + } + curLine++; + } + } + + log.debug("Cell Errors : " + (cellErrors.isEmpty() ? "" : "")); + cellErrors.stream().forEach(ce -> log.debug(ce.toString())); + + return cellErrors; + } +} diff --git a/src/main/java/nc/ird/malariaplantdb/service/xls/ExcelETL.java b/src/main/java/nc/ird/malariaplantdb/service/xls/ExcelETL.java new file mode 100644 index 0000000..145791f --- /dev/null +++ b/src/main/java/nc/ird/malariaplantdb/service/xls/ExcelETL.java @@ -0,0 +1,194 @@ +package nc.ird.malariaplantdb.service.xls; + +import lombok.extern.slf4j.Slf4j; +import nc.ird.malariaplantdb.service.xls.annotations.DbEntityRef; +import nc.ird.malariaplantdb.service.xls.annotations.ImportDto; +import nc.ird.malariaplantdb.service.xls.annotations.ImportProperty; +import nc.ird.malariaplantdb.service.xls.annotations.XlsEntityRef; +import nc.ird.malariaplantdb.service.xls.exceptions.ImportException; +import nc.ird.malariaplantdb.service.xls.exceptions.ImportRuntimeException; +import nc.ird.malariaplantdb.service.xls.infos.ColumnInfo; +import nc.ird.malariaplantdb.service.xls.infos.DbEntityRefInfo; +import nc.ird.malariaplantdb.service.xls.infos.SheetInfo; +import nc.ird.malariaplantdb.service.xls.infos.XlsEntityRefInfo; +import nc.ird.malariaplantdb.service.xls.structures.ClassMap; +import nc.ird.malariaplantdb.service.xls.structures.ImportStatus; +import org.reflections.Reflections; +import org.reflections.scanners.SubTypesScanner; +import org.reflections.scanners.TypeAnnotationsScanner; + +import java.io.InputStream; +import java.lang.reflect.Field; +import java.util.*; +import java.util.stream.Collectors; + +import static org.reflections.ReflectionUtils.getAllFields; +import static org.reflections.ReflectionUtils.withAnnotation; + +/** + * Main class for the Excel importation process. As an ETL, it has the responsibility to read the data from the Excel + * file, to check the business and database integrity rules and to finally load the data (by transforming some + * properties) to finals persisted entities. + * + * @author acheype + */ +@Slf4j +public class ExcelETL { + + /** + * Base package scanned to find classes labeled with the annotation ImportDto + */ + private String dtosBasePackage; + + /** + * Xls mapping file path for the xls reading process config file written in xml + */ + private String xlsMappingFilepath; + + private List sheetInfos; + + private ClassMap dtosMap; + + private ClassMap entitiesMap; + + private ImportStatus importStatus; + + /** + * Constructor + * + * @param dtosBasePackage Base package scanned to find classes labeled with the annotations ImportDto + * @param xlsMappingFilepath Xls mapping file path for the xls reading process config file written in xml + */ + public ExcelETL(String dtosBasePackage, String xlsMappingFilepath) { + this.dtosBasePackage = dtosBasePackage; + this.xlsMappingFilepath = xlsMappingFilepath; + sheetInfos = buildSheetInfos(); + dtosMap = new ClassMap(); + entitiesMap = new ClassMap(); + importStatus = new ImportStatus(); + } + + public void startImportProcess(InputStream xlsDataInputStream) throws ImportException { + readXlsFile(xlsDataInputStream); + checkDtos(); + if (getImportStatus().isStatusOK()) { + loadDtos(); + } + } + + private void readXlsFile(InputStream xlsDataInputStream) throws ImportException { + ExcelReader excelReader = new ExcelReader(getSheetInfos(), xlsMappingFilepath); + ExcelReader.ReaderResult readerResult = excelReader.read(xlsDataInputStream); + getImportStatus().getReadErrors().addAll(readerResult.getCellErrors()); + dtosMap = readerResult.getDtosMap(); + // TODO need to sort the read errors + } + + private void checkDtos() { + ExcelChecker excelChecker = new ExcelChecker(getSheetInfos()); + getImportStatus().getBusinessErrors().addAll(excelChecker.checkBusinessRules(dtosMap)); + // TODO sort the business errors + } + + private void loadDtos() { + ExcelLoader excelLoader = new ExcelLoader(getSheetInfos()); + ExcelLoader.LoaderResult loaderResult = excelLoader.loadEntities(dtosMap); + getImportStatus().getIntegrityErrors().addAll(loaderResult.getCellErrors()); + // TODO sort the integrity errors + //getImportStatus().getIntegrityErrors().stream().sorted() + entitiesMap = loaderResult.getEntitiesMap(); + } + + private List buildSheetInfos() { + Reflections reflections = new Reflections(dtosBasePackage, new SubTypesScanner(false), + new TypeAnnotationsScanner()); + Set> dtoClasses = reflections.getTypesAnnotatedWith(ImportDto.class); + + return dtoClasses + .stream() + .sorted(Comparator.comparing(c -> c.getAnnotation(ImportDto.class).importOrder())) + .map(c -> new SheetInfo(c, + c.getAnnotation(ImportDto.class).sheetLabel(), + c.getAnnotation(ImportDto.class).startRow(), + c.getAnnotation(ImportDto.class).outputEntityClass(), + buildXlsRefInfos(c.getAnnotation(ImportDto.class).xlsEntityRef()), + buildDbRefInfos(c.getAnnotation(ImportDto.class).dbEntityRef()), + buildPropertiesInfos(c))) + .collect(Collectors.toList()); + } + + private List buildXlsRefInfos(XlsEntityRef[] xlsEntityRefs) { + return Arrays.stream(xlsEntityRefs).map( + d -> new XlsEntityRefInfo(d.dtoProperties(), + d.dtoPropertiesTransformer(), + d.xlsEntityRefType(), + d.xlsEntityRefProperties(), + d.xlsEntityRefPropertiesLabels(), + d.filler(), + d.fillerEqualsStrategy(), + d.outputProperty(), + d.outputTransformer(), + d.afterFillingTransformer()) + ).collect(Collectors.toList()); + } + + private List buildDbRefInfos(DbEntityRef[] dbEntityRefs) { + return Arrays.stream(dbEntityRefs).map( + d -> new DbEntityRefInfo(d.dtoProperties(), + d.dtoPropertiesTransformer(), + d.filler(), + d.outputProperty(), + d.outputTransformer(), + d.afterFillingTransformer()) + ).collect(Collectors.toList()); + } + + @SuppressWarnings("unchecked") + private List buildPropertiesInfos(Class dtoClass) { + Set dtoFields = getAllFields(dtoClass, withAnnotation(ImportProperty.class)); + + List columnInfos = dtoFields + .stream() + .sorted(Comparator.comparing(f -> f.getAnnotation(ImportProperty.class).columnLetterRef())) + .map(f -> new ColumnInfo( + f.getAnnotation(ImportProperty.class).columnLetterRef(), + f.getAnnotation(ImportProperty.class).columnLabel(), + f.getName(), + //Arrays.asList(f.getAnnotation(ImportProperty.class).propertyLoader().otherProperties()), + f.getAnnotation(ImportProperty.class).propertyLoader().transformer(), + f.getAnnotation(ImportProperty.class).propertyLoader().outputProperty(), + f.getAnnotation(ImportProperty.class).afterLoadingTransformer())) + .collect(Collectors.toList()); + + dtoFields.iterator().next().getAnnotation(ImportProperty.class); + + // Verify that the column letter is unique for the class fields + List columnLettersList = columnInfos + .stream() + .map(ColumnInfo::getColumnLetterRef) + .collect(Collectors.toList()); + + Set columnLettersSet = new HashSet<>(); + for (String letter : columnLettersList) { + if (!columnLettersSet.add(letter)) { + throw new ImportRuntimeException(String.format("Error in the importation process initialization : the " + + "columnLetterRef" + + " value '%s' is not unique for all the fields of the %s class", letter, + dtoClass.getSimpleName())); + } + } + return columnInfos; + } + + public ClassMap getEntitiesMap() { + return entitiesMap; + } + + public ImportStatus getImportStatus() { + return importStatus; + } + + public List getSheetInfos() { + return sheetInfos; + } +} diff --git a/src/main/java/nc/ird/malariaplantdb/service/xls/ExcelLoader.java b/src/main/java/nc/ird/malariaplantdb/service/xls/ExcelLoader.java new file mode 100644 index 0000000..6c9b278 --- /dev/null +++ b/src/main/java/nc/ird/malariaplantdb/service/xls/ExcelLoader.java @@ -0,0 +1,440 @@ +package nc.ird.malariaplantdb.service.xls; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.ToString; +import lombok.extern.slf4j.Slf4j; +import nc.ird.malariaplantdb.service.xls.annotations.PropertyLoader; +import nc.ird.malariaplantdb.service.xls.exceptions.ImportRuntimeException; +import nc.ird.malariaplantdb.service.xls.fillers.EntityRefFiller; +import nc.ird.malariaplantdb.service.xls.fillers.XlsEntityRefFiller; +import nc.ird.malariaplantdb.service.xls.fillers.errors.DbFillerError; +import nc.ird.malariaplantdb.service.xls.fillers.errors.FillerError; +import nc.ird.malariaplantdb.service.xls.fillers.errors.XlsFillerError; +import nc.ird.malariaplantdb.service.xls.infos.*; +import nc.ird.malariaplantdb.service.xls.structures.CellError; +import nc.ird.malariaplantdb.service.xls.structures.ClassMap; +import nc.ird.malariaplantdb.service.xls.structures.PropVals; +import nc.ird.malariaplantdb.service.xls.transformers.EntitiesTransformer; +import nc.ird.malariaplantdb.service.xls.transformers.StringNormalizer; +import org.apache.commons.beanutils.BeanUtils; +import org.apache.commons.beanutils.BeanUtilsBean; +import org.apache.commons.beanutils.PropertyUtils; +import org.apache.commons.beanutils.PropertyUtilsBean; +import org.apache.commons.beanutils.expression.Resolver; +import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.collections.Transformer; +import org.apache.commons.collections.functors.NOPTransformer; + +import java.beans.PropertyDescriptor; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +@Slf4j +@AllArgsConstructor +public class ExcelLoader { + + private List sheetInfos; + + public LoaderResult loadEntities(ClassMap dtosMap) { + LoaderResult loaderResult = new LoaderResult(); + + for (SheetInfo sheetInfo : sheetInfos) { + loadProperties(sheetInfo, dtosMap, loaderResult.getEntitiesMap()); + fillXlsRefEntities(sheetInfo, dtosMap, loaderResult); + fillDbRefEntities(sheetInfo, dtosMap, loaderResult); + } + + return loaderResult; + } + + @SuppressWarnings("unchecked") + private void loadProperties(SheetInfo sheetInfo, ClassMap dtosMap, ClassMap entitiesMap) { + + try { + entitiesMap.putList(sheetInfo.getOutputEntityClass(), new ArrayList()); + + for (Object dto : dtosMap.getList(sheetInfo.getDtoClass())) { + Object entity = sheetInfo.getOutputEntityClass().newInstance(); + + for (ColumnInfo columnInfo : sheetInfo.getColumnInfos()) { + if (columnInfo.getOutputProperty() != null && !PropertyLoader.NO_LOAD.equals(columnInfo + .getOutputProperty())) + setEntityPropertyValue(columnInfo, dto, entity); + } + + entitiesMap.getList(sheetInfo.getOutputEntityClass()).add(entity); + } + + // after all the properties of this dto have been loaded, process the entitiesTransformer of + // each property + for (ColumnInfo columnInfo : sheetInfo.getColumnInfos()) { + EntitiesTransformer entitiesTransformer = columnInfo.getAfterLoadingTransformer() + .getDeclaredConstructor().newInstance(); + entitiesTransformer.transformEntities(entitiesMap); + } + } catch (InstantiationException | IllegalAccessException | InvocationTargetException | + NoSuchMethodException | IllegalArgumentException e) { + throw new ImportRuntimeException(String.format("An unexpected errors occurs during the '%s' entity " + + "loading process", sheetInfo.getOutputEntityClass().getSimpleName()), e); + } + } + + @SuppressWarnings("unchecked") + private void fillXlsRefEntities(SheetInfo sheetInfo, ClassMap dtosMap, LoaderResult loaderResult) { + + List dtos = dtosMap.getList(sheetInfo.getDtoClass()); + List entities = loaderResult.getEntitiesMap().getList(sheetInfo.getOutputEntityClass()); + + assert (dtos.size() == entities.size()); + + try { + for (XlsEntityRefInfo refInfo : sheetInfo.getXlsEntityRefInfos()) { + for (int i = 0; i < dtos.size(); i++) { + EntityRefFiller filler = refInfo.getFiller().getDeclaredConstructor().newInstance(); + + Object curDto = dtos.get(i); + Object curEntity = entities.get(i); + + if (!isExistProperty(curEntity, refInfo.getOutputProperty())) + throw new IllegalArgumentException( + String.format("The property '%s' (can be nested/indexed/mapped/combo) doesn't" + + " exist in the '%s' bean", refInfo.getOutputProperty(), + curEntity.getClass().getSimpleName())); + + filler.setPropValsSearched(initDtoPropVals(curDto, refInfo)); + filler.setOutputEntity(curEntity); + filler.setOutputProperty(refInfo.getOutputProperty()); + filler.setOutputTransformer(refInfo.getOutputTransformer()); + + List refEntities = initXlsRefEntities(sheetInfo, loaderResult, curEntity, refInfo, filler); + + XlsEntityRefFiller xlsFiller = (XlsEntityRefFiller) filler; + xlsFiller.setXlsRefEntities(refEntities); + xlsFiller.setXlsRefEntityProperties(Arrays.asList(refInfo.getXlsEntityRefProperties())); + xlsFiller.setXlsRefEntityPropertiesLabels(Arrays.asList(refInfo.getXlsEntityRefPropertiesLabels())); + xlsFiller.setFillerEqualsStrategy(refInfo.getFillerEqualsStrategy()); + + // no property filling if the dtoProperties are null + if (!CollectionUtils.isEmpty(filler.getPropValsSearched())) { + filler.fillPropertyWithRef(); + + if (filler.getResultError() != null) + loaderResult.getCellErrors().add(xlsFillerErrorToCellError((XlsFillerError) filler + .getResultError(), sheetInfo, i)); + } + } + } + + // after all the xls references of this dto have been filled, process the entitiesTransformer of each + // reference + for (XlsEntityRefInfo refInfo : sheetInfo.getXlsEntityRefInfos()) { + EntitiesTransformer entitiesTransformer = refInfo.getAfterFillingTransformer().getDeclaredConstructor() + .newInstance(); + entitiesTransformer.transformEntities(loaderResult.getEntitiesMap()); + } + } catch (InstantiationException | IllegalAccessException | InvocationTargetException | + NoSuchMethodException | IllegalArgumentException e) { + throw new ImportRuntimeException(String.format("An unexpected errors occurs during the '%s' entity " + + "loading process", sheetInfo.getOutputEntityClass().getSimpleName()), e); + } + } + + @SuppressWarnings("unchecked") + private void fillDbRefEntities(SheetInfo sheetInfo, ClassMap dtosMap, LoaderResult loaderResult) { + + List dtos = dtosMap.getList(sheetInfo.getDtoClass()); + List entities = loaderResult.getEntitiesMap().getList(sheetInfo.getOutputEntityClass()); + + assert (dtos.size() == entities.size()); + + try { + for (DbEntityRefInfo refInfo : sheetInfo.getDbEntityRefInfos()) { + for (int i = 0; i < dtos.size(); i++) { + EntityRefFiller filler = refInfo.getFiller().getDeclaredConstructor().newInstance(); + + Object curDto = dtos.get(i); + Object curEntity = entities.get(i); + + if (!isExistProperty(curEntity, refInfo.getOutputProperty())) + throw new IllegalArgumentException( + String.format("The property '%s' (can be nested/indexed/mapped/combo) doesn't" + + " exist in the '%s' bean", refInfo.getOutputProperty(), + curEntity.getClass().getSimpleName())); + + filler.setPropValsSearched(initDtoPropVals(curDto, refInfo)); + filler.setOutputEntity(curEntity); + filler.setOutputProperty(refInfo.getOutputProperty()); + filler.setOutputTransformer(refInfo.getOutputTransformer()); + + // no property filling if the dtoProperties are null + if (!CollectionUtils.isEmpty(filler.getPropValsSearched())) { + filler.fillPropertyWithRef(); + + if (filler.getResultError() != null) + loaderResult.getCellErrors().add(dbFillerErrorToCellError((DbFillerError) filler + .getResultError(), sheetInfo, i)); + } + } + + // global processing (if defined) after all the entities have been filled for this property + EntitiesTransformer entitiesTransformer = refInfo.getAfterFillingTransformer().getDeclaredConstructor() + .newInstance(); + entitiesTransformer.transformEntities(loaderResult.getEntitiesMap()); + } + } catch (InstantiationException | IllegalAccessException | InvocationTargetException | + NoSuchMethodException | IllegalArgumentException e) { + throw new ImportRuntimeException(String.format("An unexpected errors occurs during the '%s' entity " + + "loading process", sheetInfo.getOutputEntityClass().getSimpleName()), e); + } + } + + @SuppressWarnings("unchecked") + private List initDtoPropVals(Object curDto, EntityRefInfo refInfo) throws IllegalAccessException, + InvocationTargetException, NoSuchMethodException, InstantiationException { + List dtoPropVals = new ArrayList<>(); + + PropVals uniquePropVals = new PropVals(); + boolean oneDtoValueNull = false; + + for (String curDtoProp : Arrays.asList(refInfo.getDtoProperties())) { + if (!isExistProperty(curDto, curDtoProp)) + throw new IllegalArgumentException( + String.format("The property '%s' (can be nested/indexed/mapped/combo) doesn't" + + " exist in the '%s' bean", curDtoProp, + curDto.getClass().getSimpleName())); + + Object curDtoValue = PropertyUtils.getProperty(curDto, curDtoProp); + if (curDtoValue != null) + uniquePropVals.put(curDtoProp, curDtoValue); + else + oneDtoValueNull = true; + } + + if (uniquePropVals.size() > 0 && !oneDtoValueNull) { + if (refInfo.getDtoPropertiesTransformer() == NOPTransformer.class) { + dtoPropVals.add(uniquePropVals); + } else if (refInfo.getDtoPropertiesTransformer() == StringNormalizer.class) { + StringNormalizer normalizer = new StringNormalizer(); + for (String propValsKey : uniquePropVals.keySet()) + uniquePropVals.put(propValsKey, normalizer.transform(uniquePropVals.get(propValsKey))); + dtoPropVals.add(uniquePropVals); + } else { + try { + dtoPropVals = (List) applyTransformer(refInfo.getDtoPropertiesTransformer(), + uniquePropVals); + } catch (ClassCastException e) { + throw new IllegalArgumentException(String.format("The '%s' dtoPropertiesTransformer must " + + "return a List object.", refInfo.getDtoPropertiesTransformer(). + getSimpleName())); + } + } + } + return dtoPropVals; + } + + @SuppressWarnings("unchecked") + private List initXlsRefEntities(SheetInfo sheetInfo, LoaderResult loaderResult, Object curEntity, + XlsEntityRefInfo refInfo, EntityRefFiller filler) throws IllegalAccessException, + InvocationTargetException, NoSuchMethodException { + Class outputPropertyClass = PropertyUtils.getPropertyDescriptor(curEntity, refInfo + .getOutputProperty()) + .getPropertyType(); + + if (refInfo.getOutputTransformer() == NOPTransformer.class && filler.getPropValsSearched() + .size() == 1 && outputPropertyClass != refInfo.getXlsEntityRefType()){ + throw new IllegalArgumentException(String.format("As PropValsSearched contains an unique " + + "PropVals, the '%s' outputProperty of the '%s' class must extends the " + + "'%s' xlsEntityRefType", + refInfo.getOutputProperty(), curEntity.getClass().getSimpleName(), + outputPropertyClass.getSimpleName())); + } + + List refEntities = new ArrayList(); + if (loaderResult.getEntitiesMap().getList(refInfo.getXlsEntityRefType()) != null) + refEntities.addAll(loaderResult.getEntitiesMap().getList(refInfo.getXlsEntityRefType())); + if (refInfo.getXlsEntityRefType() == sheetInfo.getOutputEntityClass()) { + // ref entity is the same class than the entity -> remove the current entity from the list + refEntities.remove(curEntity); + } + return refEntities; + } + + /** + * Test if an property name exists (can be nested/indexed/mapped/combo) + * The code was extracted from BeanUtilsBean.setProperty(Object bean, String name, Object value) + * + * @return The resulted boolean + */ + private boolean isExistProperty(Object bean, String name) throws InvocationTargetException, + IllegalAccessException { + PropertyUtilsBean propertyUtils = BeanUtilsBean.getInstance().getPropertyUtils(); + + // Resolve any nested expression to get the actual target bean + Resolver resolver = propertyUtils.getResolver(); + while (resolver.hasNested(name)) { + try { + bean = propertyUtils.getProperty(bean, resolver.next(name)); + name = resolver.remove(name); + } catch (NoSuchMethodException e) { + return false; + } + } + if (log.isTraceEnabled()) { + log.trace(" Target bean = " + bean); + log.trace(" Target name = " + name); + } + + // Declare local variables we will require + String propName = resolver.getProperty(name); // Simple name of target property + Class type; // Java type of target property + // Calculate the target property type + PropertyDescriptor descriptor; + try { + descriptor = propertyUtils.getPropertyDescriptor(bean, name); + if (descriptor == null) { + return false; // Skip this property setter + } + } catch (NoSuchMethodException e) { + return false; // Skip this property setter + } + type = descriptor.getPropertyType(); + if (type == null) { + // Most likely an indexed setter on a POJB only + if (log.isTraceEnabled()) { + log.trace(" target type for property '" + propName + "' is null, so skipping ths setter"); + } + return false; + } + + return true; + } + + private CellError xlsFillerErrorToCellError(XlsFillerError xlsFillerError, SheetInfo sheetInfo, int i) + throws IllegalArgumentException { + + SheetInfo refSheetInfo = sheetInfos.stream() + .filter(s -> s.getOutputEntityClass().equals(xlsFillerError.getRefEntityClass())) + .findFirst() + .orElse(null); + if (refSheetInfo == null) + throw new IllegalArgumentException(String.format("Impossible to find a sheet which his outputEntityClass " + + "is '%s'", xlsFillerError.getRefEntityClass())); + + if (xlsFillerError.getXlsEntityRefProperties().size() != xlsFillerError.getXlsEntityRefPropertiesLabels() + .size()) + throw new IllegalArgumentException(String.format("The xlsEntityRefProperties {%s} must have the same " + + "length as the xlsEntityRefPropertiesLabels {%s}", + xlsFillerError.getXlsEntityRefProperties().stream().collect(Collectors.joining(", ")), + xlsFillerError.getXlsEntityRefPropertiesLabels().stream().collect(Collectors.joining(", ")))); + + // xlsFillerError.getXlsEntityRefProperties().size == xlsFillerError.getPropVals().size() (verified in the + // filler) + StringBuilder valuesMap = new StringBuilder(); + for (int j = 0; j < xlsFillerError.getXlsEntityRefProperties().size(); j++) { + valuesMap.append(xlsFillerError.getXlsEntityRefPropertiesLabels().get(j)).append(" = '") + .append(xlsFillerError.getPropVals().values().toArray()[j]).append("'"); + if (j != xlsFillerError.getPropVals().size() - 1) + valuesMap.append(", "); + } + + return new CellError( + String.format(FillerError.ErrorCause.NOT_UNIQUE_MATCH.equals(xlsFillerError.getErrorCause()) ? + "Can't find in the '%s' sheet an unique line with %s values%s : %s" : + "Can't find in the '%s' sheet any line with %s value%s : %s", + refSheetInfo.getSheetLabel(), + xlsFillerError.getPropVals().size() > 1 ? "these" : "this", + xlsFillerError.getPropVals().size() > 1 ? "s" : "", + valuesMap), + sheetInfo.getSheetLabel(), + sheetInfo.getStartRow() + i, + xlsFillerError.getPropVals().keySet().stream() + .map(dtoProp -> sheetInfo.getColumnInfoByDtoProperty(dtoProp).getColumnLabel()) + .collect(Collectors.joining(", "))); + } + + private CellError dbFillerErrorToCellError(DbFillerError dbFillerError, SheetInfo sheetInfo, int i) { + + PropVals propsBeforeTransformer = new PropVals(); + for (String propVal : dbFillerError.getPropVals().keySet()){ + ColumnInfo columnInfo = sheetInfo.getColumnInfoByDtoProperty(propVal); + if (columnInfo == null) + throw new IllegalArgumentException(String.format("Impossible to find the dtoProperty in a column of " + + "the '%s' sheet", sheetInfo.getSheetLabel())); + else { + if (!propsBeforeTransformer.containsKey(columnInfo.getColumnLabel())) + propsBeforeTransformer.put(columnInfo.getColumnLabel(), dbFillerError.getPropVals().get + (propVal)); + else { + StringBuilder curValue = new StringBuilder((String) propsBeforeTransformer.get(columnInfo + .getColumnLabel())); + propsBeforeTransformer.put(columnInfo.getColumnLabel(), curValue.append(", ").append(dbFillerError.getPropVals().get + (propVal))); + } + } + } + + String valuesMap = propsBeforeTransformer.keySet().stream().map(dtoProp -> dtoProp + " = " + + propsBeforeTransformer.get(dtoProp)).collect(Collectors.joining(", ")); + + return new CellError( + String.format(FillerError.ErrorCause.NOT_UNIQUE_MATCH.equals(dbFillerError.getErrorCause()) ? + "Can't find in the database an unique entity with %s value%s : %s" : + "Can't find in the database any entity with %s value%s : %s", + dbFillerError.getPropVals().size() > 1 ? "these" : "this", + dbFillerError.getPropVals().size() > 1 ? "s" : "", + valuesMap), + sheetInfo.getSheetLabel(), + sheetInfo.getStartRow() + i, + propsBeforeTransformer.keySet().stream().collect(Collectors.joining(", "))); + } + + private void setEntityPropertyValue(ColumnInfo columnInfo, Object dto, Object entity) throws InstantiationException, + IllegalAccessException, InvocationTargetException, NoSuchMethodException, IllegalArgumentException { + + Object propertyValue = PropertyUtils.getProperty(dto, columnInfo.getDtoPropertyName()); + + String outputProperty = PropertyLoader.DTO_NAME.equals(columnInfo.getOutputProperty()) ? columnInfo + .getDtoPropertyName() : columnInfo.getOutputProperty(); + if (isExistProperty(entity, outputProperty)) { + if (propertyValue == null) { + // if null, no transformation applied + PropertyUtils.setProperty(entity, outputProperty, null); + } else { + BeanUtils.setProperty(entity, outputProperty, + applyTransformer(columnInfo.getPropertyTransformer(), propertyValue)); + } + } else { + throw new IllegalArgumentException( + String.format("The property '%s' (can be nested/indexed/mapped/combo) doesn't" + + " exist in the '%s' bean", columnInfo.getOutputProperty(), + entity.getClass().getSimpleName())); + } + } + + public static Object applyTransformer(Class transformerClass, Object value) throws + NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException { + + Constructor constructor = transformerClass.getDeclaredConstructor(); + constructor.setAccessible(true); + Transformer transformer = (Transformer) constructor.newInstance(); + + return transformer.transform(value); + } + +@Getter +@ToString +@NoArgsConstructor +public static class LoaderResult { + + private ClassMap entitiesMap = new ClassMap(); + + private List cellErrors = new ArrayList<>(); +} + +} diff --git a/src/main/java/nc/ird/malariaplantdb/service/xls/ExcelReader.java b/src/main/java/nc/ird/malariaplantdb/service/xls/ExcelReader.java new file mode 100644 index 0000000..b840a5a --- /dev/null +++ b/src/main/java/nc/ird/malariaplantdb/service/xls/ExcelReader.java @@ -0,0 +1,115 @@ +package nc.ird.malariaplantdb.service.xls; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.ToString; +import lombok.extern.slf4j.Slf4j; +import nc.ird.malariaplantdb.service.xls.exceptions.ImportException; +import nc.ird.malariaplantdb.service.xls.exceptions.ImportRuntimeException; +import nc.ird.malariaplantdb.service.xls.infos.SheetInfo; +import nc.ird.malariaplantdb.service.xls.structures.CellError; +import nc.ird.malariaplantdb.service.xls.structures.ClassMap; +import net.sf.jxls.reader.*; +import org.apache.poi.openxml4j.exceptions.InvalidFormatException; +import org.xml.sax.SAXException; + +import java.io.BufferedInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * ExcelReader based on JXLS library + * + * @author acheype + */ +@Slf4j +public class ExcelReader { + + private List sheetInfos; + + /** + * Xls mapping file path for the xls reading process config file written in xml + */ + private String xlsMappingFilepath; + + public ExcelReader(List sheetInfos, String xlsMappingFilepath) { + this.sheetInfos = sheetInfos; + this.xlsMappingFilepath = xlsMappingFilepath; + } + + @SuppressWarnings("unchecked") + public ReaderResult read(InputStream xlsDataInputStream) throws ImportException { + + // TODO : verify in the XLS mapping file that the declared 'items' corresponds to the annotated dtos + // example of exception we could throw : new ImportException("Impossible to find the dto '%s' in the xls + // mapping file. For each annotated dto, you have to declare once his class name in the 'items' field of this + // xml file."); + + + XLSReader jxlsReader; + try { + jxlsReader = getJxlsReader(); + } catch (IOException | SAXException e) { + throw new ImportException("Error in the importation process initialization : a problem occur with the " + + "xls mapping file", e); + } catch (RuntimeException e) { + throw new ImportRuntimeException("Error in the importation process initialization : a problem occur " + + "with the xls mapping file", e); + } + + try (InputStream inputXLS = new BufferedInputStream(xlsDataInputStream)) { + Map dtoBeans = new HashMap<>(); + + for (SheetInfo sheetInfo : sheetInfos) { + dtoBeans.put(sheetInfo.getDtoClass().getSimpleName(), new ArrayList<>()); + } + + XLSReadStatus readStatus = jxlsReader.read(inputXLS, dtoBeans); + + List cellErrors = new ArrayList<>(); + log.debug("Cell Errors : " + (readStatus.getReadMessages().isEmpty() ? "" : "")); + for (Object readMsg : readStatus.getReadMessages()) { + cellErrors.add(CellError.buildFromReadMessage(sheetInfos, (XLSReadMessage) readMsg)); + log.debug(cellErrors.toString()); + } + + ClassMap dtosMap = new ClassMap(); + for (SheetInfo sheetInfo : sheetInfos) { + dtosMap.putList(sheetInfo.getDtoClass(), + (ArrayList) dtoBeans.get(sheetInfo.getDtoClass().getSimpleName())); + } + + return new ReaderResult(dtosMap, cellErrors); + + } catch (IOException | InvalidFormatException e) { + throw new ImportException("An unexpected errors occurs during the Excel file reading", e); + } catch (RuntimeException e) { + throw new ImportRuntimeException("An unexpected errors occurs during the Excel file reading", e); + } + } + + private XLSReader getJxlsReader() throws IOException, SAXException { + InputStream inputXML = new BufferedInputStream(getClass().getResourceAsStream(xlsMappingFilepath)); + + // continue if an reading errors occurs + ReaderConfig.getInstance().setSkipErrors(true); + + return ReaderBuilder.buildFromXML(inputXML); + } + + @Getter + @ToString + @AllArgsConstructor + public static class ReaderResult { + + private ClassMap dtosMap; + + private List cellErrors; + } + + +} diff --git a/src/main/java/nc/ird/malariaplantdb/service/xls/annotations/DbEntityRef.java b/src/main/java/nc/ird/malariaplantdb/service/xls/annotations/DbEntityRef.java new file mode 100644 index 0000000..0fd7f6e --- /dev/null +++ b/src/main/java/nc/ird/malariaplantdb/service/xls/annotations/DbEntityRef.java @@ -0,0 +1,59 @@ +package nc.ird.malariaplantdb.service.xls.annotations; + +import nc.ird.malariaplantdb.service.xls.fillers.DbEntityRefFiller; +import nc.ird.malariaplantdb.service.xls.transformers.EntitiesTransformer; +import nc.ird.malariaplantdb.service.xls.transformers.NOPEntitiesTransformer; +import org.apache.commons.collections.Transformer; +import org.apache.commons.collections.functors.NOPTransformer; + +/** + * TODO Update comments + * Annotation used to fill a property with an other entity already loaded in the Xls + * worksheet or can be persist in a storage (file, database). + * + * The entity will be set by a {@code filler} in the {@code outputProperty} specified. The {@code filler} defines a + * strategy to find an entity by comparing some dto properties values defined in {@code dtoProperties}. + * + *

If a {@code dtoPropertiesTransformer} is specified, the dto properties values will be transform before to be + * compared.

+ * @author acheype + */ +public @interface DbEntityRef { + + /** + * @return the dto property names used to identify the referenced entity + */ + String[] dtoProperties(); + + /** + * @return the transformer which applies on all {@code dtoIdentifierProperties} values before to be find a + * matching entity (by default an no-operation transformer). The transformer have to receive a{@code PropVals} + * object and return a list of{@code PropVals} (if the list contains several{@code PropVals}, the filler will search for + * a collection of entity) + */ + public Class dtoPropertiesTransformer() default NOPTransformer.class; + + /** + * @return the filler which finds, then set the corresponding entity + */ + Class filler(); + + /** + * @return the output property in which the entity reference will be filled + */ + String outputProperty(); + + /** + * In situation where the outputProperty can't extends the{@code xlsEntityRefType} class, a transformer can be + * defined. + * @return the transformer which transforms the matching entity into an object of the{@code outputProperty} class + */ + Class outputTransformer() default NOPTransformer.class; + + /** + * @return the transformer which modify the entities map after all the filling of this property + */ + Class afterFillingTransformer() default NOPEntitiesTransformer.class; + + +} diff --git a/src/main/java/nc/ird/malariaplantdb/service/xls/annotations/ImportDto.java b/src/main/java/nc/ird/malariaplantdb/service/xls/annotations/ImportDto.java new file mode 100644 index 0000000..3ed09fb --- /dev/null +++ b/src/main/java/nc/ird/malariaplantdb/service/xls/annotations/ImportDto.java @@ -0,0 +1,46 @@ +package nc.ird.malariaplantdb.service.xls.annotations; + +import java.lang.annotation.*; + +/** + * Annotation used as a label to identify which dto to load with Excel data + * + * @author acheype + */ +@Documented +@Target({ElementType.TYPE}) +@Retention(RetentionPolicy.RUNTIME) +public @interface ImportDto { + + /** + * @return the Excel sheet name to tell the user the sheet source reference + */ + public String sheetLabel(); + + /** + * @return the order number. The importation process will begin with the sheet's lowest numbers and finish + * with the greatest ones. + */ + public int importOrder(); + + /** + * @return the number line of the first row imported from the sheet + */ + public int startRow() default 2; + + /** + * @return the output entity class in which the importation data will be loaded + */ + public Class outputEntityClass(); + + /** + * @return the Xls entity references to set in the final entity + */ + public XlsEntityRef[] xlsEntityRef() default {}; + + /** + * @return the DB entity references to set in the final entity + */ + public DbEntityRef[] dbEntityRef() default {}; + +} diff --git a/src/main/java/nc/ird/malariaplantdb/service/xls/annotations/ImportProperty.java b/src/main/java/nc/ird/malariaplantdb/service/xls/annotations/ImportProperty.java new file mode 100644 index 0000000..ce4fcc5 --- /dev/null +++ b/src/main/java/nc/ird/malariaplantdb/service/xls/annotations/ImportProperty.java @@ -0,0 +1,38 @@ +package nc.ird.malariaplantdb.service.xls.annotations; + +import nc.ird.malariaplantdb.service.xls.transformers.EntitiesTransformer; +import nc.ird.malariaplantdb.service.xls.transformers.NOPEntitiesTransformer; + +import java.lang.annotation.*; + +/** + * Annotation used as a label to identify in a DTO which properties to import + * + * @author acheype + */ +@Documented +@Target({ElementType.FIELD}) +@Retention(RetentionPolicy.RUNTIME) +public @interface ImportProperty { + + /** + * @return the Excel column reference (one or several letters) to keep a map between the ref and the label + */ + String columnLetterRef(); + + /** + * @return the Excel column name to tell the user the column source reference + */ + String columnLabel(); + + /** + * @return the loader which uses the field to load the final entity property + */ + PropertyLoader propertyLoader() default @PropertyLoader(outputProperty = PropertyLoader.NO_LOAD); + + /** + * @return the transformer which modify the entities map after all the loadings of this property + */ + Class afterLoadingTransformer() default NOPEntitiesTransformer.class; + +} diff --git a/src/main/java/nc/ird/malariaplantdb/service/xls/annotations/PropertyLoader.java b/src/main/java/nc/ird/malariaplantdb/service/xls/annotations/PropertyLoader.java new file mode 100644 index 0000000..47bd465 --- /dev/null +++ b/src/main/java/nc/ird/malariaplantdb/service/xls/annotations/PropertyLoader.java @@ -0,0 +1,40 @@ +package nc.ird.malariaplantdb.service.xls.annotations; + +import org.apache.commons.collections.Transformer; +import org.apache.commons.collections.functors.NOPTransformer; + +import java.lang.annotation.*; + +/** + * Annotation used to describe how is loaded a property from the dto to the final entity + * + * @author acheype + */ +@Documented +@Target({ElementType.TYPE}) +@Retention(RetentionPolicy.RUNTIME) +public @interface PropertyLoader { + + /** + * @return the transformer which transforms the importation value into the corresponding one for the + * outputProperty (by default an no-operation transformer) + */ + Class transformer() default NOPTransformer.class; + + /** + * @return the entitie's output property in which the imported value will be loaded. If null, the dtoPropertyName will + * be used. + */ + String outputProperty() default DTO_NAME; + + /** + * Value used for outputProperty to define that the name of the dto property will be used + */ + final static String DTO_NAME = ""; + + /** + * Null value used for outputProperty to define there is no property to load + */ + final static String NO_LOAD = "null"; + +} diff --git a/src/main/java/nc/ird/malariaplantdb/service/xls/annotations/XlsEntityRef.java b/src/main/java/nc/ird/malariaplantdb/service/xls/annotations/XlsEntityRef.java new file mode 100644 index 0000000..69959f1 --- /dev/null +++ b/src/main/java/nc/ird/malariaplantdb/service/xls/annotations/XlsEntityRef.java @@ -0,0 +1,88 @@ +package nc.ird.malariaplantdb.service.xls.annotations; + +import nc.ird.malariaplantdb.service.xls.fillers.XlsEntityRefFiller; +import nc.ird.malariaplantdb.service.xls.fillers.util.DefaultEqualsStrategy; +import nc.ird.malariaplantdb.service.xls.fillers.util.EqualsStrategy; +import nc.ird.malariaplantdb.service.xls.transformers.EntitiesTransformer; +import nc.ird.malariaplantdb.service.xls.transformers.NOPEntitiesTransformer; +import org.apache.commons.collections.Transformer; +import org.apache.commons.collections.functors.NOPTransformer; + +/** + *

Annotation used to fill a property with an other entity already loaded from a Xls worksheet.

+ *

The{@code filler} use the{@code fillerEqualsStrategy} to find in the already loaded entities of class{@code + * xlsEntityRefType} a matching entity. Then, the{@code filler} will set this entity into the specified{@code + * outputProperty}. If an output transformer is defined, it will be applied on the entity and the result will be + * set.

+ *

If a {@code dtoPropertiesTransformer} is specified, the dto properties values will be transform before to be + * compared.

+ * + * @author acheype + */ +public @interface XlsEntityRef { + + /** + * @return the dto property names used to identify the referenced entity + */ + String[] dtoProperties(); + + /** + * @return the transformer which applies on all{@code dtoIdentifierProperties} values before to be find a + * matching entity (by default an no-operation transformer). The transformer have to receive a{@code PropVals} + * and return a list of{@code PropVals} (if the list contains several{@code PropVals}, the filler will search for + * a collection of entity). However, an exception is made for the{@code nc.ird.malariaplantdb.service.xls + * .transformers.StringNormalizer} transformer where no{@code PropVals} is needed. + */ + public Class dtoPropertiesTransformer() default NOPTransformer.class; + + /** + * @return the class of the already loaded entity which represents a Xls worksheet + */ + Class xlsEntityRefType(); + + /** + * @return the property names of the worksheet entity which will be compared to the{@code dtoProperties} + * values. If not, a default array will be set. Conventionally, it must have the same property number than the{@code + * dtoProperties}. But if a{@code dtoPropertiesTransformer} will be applied, it must have the same property + * number than the{@code PropVals} returned by the transformer. + */ + String[] xlsEntityRefProperties() default {}; + + /** + * @return the column names in the referenced sheet of the properties compared to the{@code dtoProperties} (used + * to write eventual understandable error messages) + */ + String[] xlsEntityRefPropertiesLabels(); + + + /** + * @return the filler which finds, then set the corresponding entity + */ + Class filler(); + + /** + * @return the equals strategy used to compare the{@code dtoProperties} values and the{@code xlsEntityRefProperties} + * ones. If no equals strategy is supplied, the default strategy consists of comparing one by one in the same order + * each{@code dtoProperties} values and{@code xlsEntityRefType} ones. + */ + Class fillerEqualsStrategy() default DefaultEqualsStrategy.class; + + /** + * @return the output property in which the entity reference will be filled + */ + String outputProperty(); + + /** + * In situation where the outputProperty can't extends the{@code xlsEntityRefType} class, a transformer can be + * defined. + * @return the transformer which transforms the matching entity into an object of the{@code outputProperty} class + */ + Class outputTransformer() default NOPTransformer.class; + + /** + * @return the transformer which modify the entities map after all the filling of this property + */ + Class afterFillingTransformer() default NOPEntitiesTransformer.class; + + +} diff --git a/src/main/java/nc/ird/malariaplantdb/service/xls/dto/EthnologyLine.java b/src/main/java/nc/ird/malariaplantdb/service/xls/dto/EthnologyLine.java new file mode 100644 index 0000000..180c3f2 --- /dev/null +++ b/src/main/java/nc/ird/malariaplantdb/service/xls/dto/EthnologyLine.java @@ -0,0 +1,86 @@ +package nc.ird.malariaplantdb.service.xls.dto; + +import lombok.Data; +import nc.ird.malariaplantdb.service.xls.annotations.ImportProperty; +import nc.ird.malariaplantdb.service.xls.annotations.PropertyLoader; +import nc.ird.malariaplantdb.service.xls.transformers.StringNormalizer; +import org.hibernate.validator.constraints.NotEmpty; + +/** + * DTO for Ethnology entity + * + * @author acheype + */ +@Data +//@ImportDto(sheetLabel = "4 - ETHNO", importOrder = 4, startRow = 2, outputEntityClass = Ethnology.class, +// xlsEntityRef = { +// @XlsEntityRef( +// dtoProperties = {"publication"}, +// xlsEntityRefType = Publication.class, +// xlsEntityRefProperties = {"title"}, +// filler = XlsEntityRefFiller.class, +// outputProperty = "publication" +// ), +// @XlsEntityRef( +// dtoProperties = {"publication", "plantIngredients"}, +// dtoPropertiesTransformer = PlantIngredientsStrTransformer.class, +// xlsEntityRefType = PlantIngredientsTemp.class, +// xlsEntityRefProperties = {"publication.title", "plantIngredient1.species.species", +// "plantIngredient1.partUsed", "plantIngredient2.species.species", +// "plantIngredient2.partUsed", "plantIngredient3.species.species", +// "plantIngredient3.partUsed", "plantIngredient4.species.species", +// "plantIngredient4.partUsed", "plantIngredient5.species.species", +// "plantIngredient5.partUsed", "plantIngredient6.species.species", +// "plantIngredient6.partUsed", "plantIngredient7.species.species", +// "plantIngredient7.partUsed", "plantIngredient8.species.species", +// "plantIngredient8.partUsed", "plantIngredient9.species.species", +// "plantIngredient9.partUsed", "plantIngredient10.species.species", +// "plantIngredient10.partUsed"}, +// filler = XlsEntityRefFiller.class, +// outputProperty = "plantIngredients", +// outputTransformer = PlantIngredientsTempToSet.class +// ) +// }) +public class EthnologyLine { + + @ImportProperty(columnLetterRef = "A", columnLabel = "Publication (title)") + @NotEmpty(message = "The cell is empty or the value invalid") + private String publication; + + @ImportProperty(columnLetterRef = "B", columnLabel = "Plant ingredient(s) used") + @NotEmpty(message = "The cell is empty or the value invalid") + // TODO reactivate the validator just below +// @Pattern(regexp = "^(([a-zA-ZÀ-ÿ &\\.\\-\\(\\)]+),([a-zA-ZÀ-ÿ \\-\\.]+)/)*" + +// "([a-zA-ZÀ-ÿ &\\.\\-\\(\\)]]+),([a-zA-ZÀ-ÿ \\-\\.]+)$", +// message = "The plant ingredient(s) value is not well formatted. Please enter each species name first, " + +// "a coma (,) then the part used. For several plant ingredients, please separate each plant ingredient " + +// "by a slash (/).") + private String plantIngredients; + + @ImportProperty(columnLetterRef = "C", columnLabel = "Ethnopharmalogical relevancy", + propertyLoader = @PropertyLoader(outputProperty = "ethnoRelevancy", + transformer = StringNormalizer.class)) + private String ethnoRelevancy; + + @ImportProperty(columnLetterRef = "D", columnLabel = "Treatment type", + propertyLoader = @PropertyLoader(outputProperty = "treatmentType", + transformer = StringNormalizer.class)) + @NotEmpty(message = "The cell is empty or the value invalid") + private String treatmentType; + + @ImportProperty(columnLetterRef = "E", columnLabel = "Traditional recipe details", + propertyLoader = @PropertyLoader(outputProperty = "traditionalRecipeDetails", + transformer = StringNormalizer.class)) + private String traditionalRecipeDetails; + + @ImportProperty(columnLetterRef = "F", columnLabel = "Preparation mode in traditional recipe", + propertyLoader = @PropertyLoader(outputProperty = "preparationMode", + transformer = StringNormalizer.class)) + private String preparationMode; + + @ImportProperty(columnLetterRef = "G", columnLabel = "Administration route in traditional recipe", + propertyLoader = @PropertyLoader(outputProperty = "administrationRoute", + transformer = StringNormalizer.class)) + private String administrationRoute; + +} diff --git a/src/main/java/nc/ird/malariaplantdb/service/xls/dto/InVivoPharmacoLine.java b/src/main/java/nc/ird/malariaplantdb/service/xls/dto/InVivoPharmacoLine.java new file mode 100644 index 0000000..8b09b79 --- /dev/null +++ b/src/main/java/nc/ird/malariaplantdb/service/xls/dto/InVivoPharmacoLine.java @@ -0,0 +1,102 @@ +package nc.ird.malariaplantdb.service.xls.dto; + +import lombok.Data; +import nc.ird.malariaplantdb.domain.PlantIngredient; +import nc.ird.malariaplantdb.domain.Publication; +import nc.ird.malariaplantdb.service.xls.annotations.ImportProperty; +import nc.ird.malariaplantdb.service.xls.annotations.PropertyLoader; +import nc.ird.malariaplantdb.service.xls.transformers.StringNormalizer; +import org.hibernate.validator.constraints.NotEmpty; + +import javax.persistence.Column; +import javax.validation.constraints.Max; +import javax.validation.constraints.Min; +import javax.validation.constraints.NotNull; +import java.util.List; + +/** + * In vivo pharmacology entity + *

+ * Represents for the plant ingredients of a publication the relevant data in in vivo pharmacology + * + * @author acheype + */ +@Data +public class InVivoPharmacoLine { + + @ImportProperty(columnLetterRef = "A", columnLabel = "Publication (title)") + @NotEmpty(message = "The cell is empty or the value invalid") + private Publication publication; + + @ImportProperty(columnLetterRef = "B", columnLabel = "Plant ingredient(s) tested") + @NotEmpty(message = "The cell is empty or the value invalid") + private List plantIngredients; + + @ImportProperty(columnLetterRef = "C", columnLabel = "Tested entity", + propertyLoader = @PropertyLoader(outputProperty = "testedEntity", + transformer = StringNormalizer.class)) + @NotEmpty(message = "The cell is empty or the value invalid") + private String testedEntity; + + @ImportProperty(columnLetterRef = "D", columnLabel = "Extraction solvent", + propertyLoader = @PropertyLoader(outputProperty = "extractionSolvent", + transformer = StringNormalizer.class)) + private String extractionSolvent; + + @ImportProperty(columnLetterRef = "E", columnLabel = "Additive product", + propertyLoader = @PropertyLoader(outputProperty = "additiveProduct", + transformer = StringNormalizer.class)) + private String additiveProduct; + + @ImportProperty(columnLetterRef = "F", columnLabel = "Compound name", + propertyLoader = @PropertyLoader(outputProperty = "compoundName", + transformer = StringNormalizer.class)) + private String compoundName; + + @ImportProperty(columnLetterRef = "G", columnLabel = "Screening test", + propertyLoader = @PropertyLoader(outputProperty = "screeningTest", + transformer = StringNormalizer.class)) + @NotEmpty(message = "The cell is empty or the value invalid") + private String screeningTest; + + @ImportProperty(columnLetterRef = "H", columnLabel = "Parasite", + propertyLoader = @PropertyLoader(outputProperty = "parasite", + transformer = StringNormalizer.class)) + @NotEmpty(message = "The cell is empty or the value invalid") + private String parasite; + + @ImportProperty(columnLetterRef = "I", columnLabel = "Parasite details", + propertyLoader = @PropertyLoader(outputProperty = "parasiteDetails", + transformer = StringNormalizer.class)) + private String parasiteDetails; + + @ImportProperty(columnLetterRef = "J", columnLabel = "Animal", + propertyLoader = @PropertyLoader(outputProperty = "animal", + transformer = StringNormalizer.class)) + private String animal; + + private String treatmentRoute; + + private Float dose; + + @Min(0) + @Max(100) + private Float inhibition; + + @Min(0) + @Max(100) + private Float survivalPercent; + + @Column(name = "survival_time") + private Float survivalTime; + + private Float ed50; + + @NotNull(message = "The cell is empty or the value invalid") + private Boolean isToxicity; + + private Float ld50; + + private String compilersObservations; + +} diff --git a/src/main/java/nc/ird/malariaplantdb/service/xls/dto/PlantIngredientsLine.java b/src/main/java/nc/ird/malariaplantdb/service/xls/dto/PlantIngredientsLine.java new file mode 100644 index 0000000..0a17de3 --- /dev/null +++ b/src/main/java/nc/ird/malariaplantdb/service/xls/dto/PlantIngredientsLine.java @@ -0,0 +1,204 @@ +package nc.ird.malariaplantdb.service.xls.dto; + +import lombok.Data; +import nc.ird.malariaplantdb.domain.PubSpecies; +import nc.ird.malariaplantdb.service.xls.annotations.ImportDto; +import nc.ird.malariaplantdb.service.xls.annotations.ImportProperty; +import nc.ird.malariaplantdb.service.xls.annotations.PropertyLoader; +import nc.ird.malariaplantdb.service.xls.annotations.XlsEntityRef; +import nc.ird.malariaplantdb.service.xls.fillers.XlsEntityRefFiller; +import nc.ird.malariaplantdb.service.xls.transformers.PubSpeciesToSpecies; +import nc.ird.malariaplantdb.service.xls.transformers.RemedyEntitiesTransformer; +import nc.ird.malariaplantdb.service.xls.transformers.StringNormalizer; +import org.hibernate.validator.constraints.NotEmpty; + +/** + * DTO for a bundle of 10 PlantIngredients + * + * @author acheype + */ +@Data +@ImportDto(sheetLabel = "3 - PLANT INGREDIENTS", importOrder = 3, outputEntityClass = PlantIngredientsTemp.class, + xlsEntityRef = { + @XlsEntityRef( + dtoProperties = {"publication", "species1"}, + dtoPropertiesTransformer = StringNormalizer.class, + xlsEntityRefType = PubSpecies.class, + xlsEntityRefProperties = {"publication.title", "species.species"}, + xlsEntityRefPropertiesLabels = {"Publication", "Species"}, + filler = XlsEntityRefFiller.class, + outputProperty = "plantIngredient1.species", + outputTransformer = PubSpeciesToSpecies.class + ), + @XlsEntityRef( + dtoProperties = {"publication", "species2"}, + dtoPropertiesTransformer = StringNormalizer.class, + xlsEntityRefType = PubSpecies.class, + xlsEntityRefProperties = {"publication.title", "species.species"}, + xlsEntityRefPropertiesLabels = {"Publication", "Species"}, + filler = XlsEntityRefFiller.class, + outputProperty = "plantIngredient2.species", + outputTransformer = PubSpeciesToSpecies.class + ), + @XlsEntityRef( + dtoProperties = {"publication", "species3"}, + dtoPropertiesTransformer = StringNormalizer.class, + xlsEntityRefType = PubSpecies.class, + xlsEntityRefProperties = {"publication.title", "species.species"}, + xlsEntityRefPropertiesLabels = {"Publication", "Species"}, + filler = XlsEntityRefFiller.class, + outputProperty = "plantIngredient3.species", + outputTransformer = PubSpeciesToSpecies.class + ), + @XlsEntityRef( + dtoProperties = {"publication", "species4"}, + dtoPropertiesTransformer = StringNormalizer.class, + xlsEntityRefType = PubSpecies.class, + xlsEntityRefProperties = {"publication.title", "species.species"}, + xlsEntityRefPropertiesLabels = {"Publication", "Species"}, + filler = XlsEntityRefFiller.class, + outputProperty = "plantIngredient4.species", + outputTransformer = PubSpeciesToSpecies.class + ), + @XlsEntityRef( + dtoProperties = {"publication", "species5"}, + dtoPropertiesTransformer = StringNormalizer.class, + xlsEntityRefType = PubSpecies.class, + xlsEntityRefProperties = {"publication.title", "species.species"}, + xlsEntityRefPropertiesLabels = {"Publication", "Species"}, + filler = XlsEntityRefFiller.class, + outputProperty = "plantIngredient5.species", + outputTransformer = PubSpeciesToSpecies.class + ), + @XlsEntityRef( + dtoProperties = {"publication", "species6"}, + dtoPropertiesTransformer = StringNormalizer.class, + xlsEntityRefType = PubSpecies.class, + xlsEntityRefProperties = {"publication.title", "species.species"}, + xlsEntityRefPropertiesLabels = {"Publication", "Species"}, + filler = XlsEntityRefFiller.class, + outputProperty = "plantIngredient6.species", + outputTransformer = PubSpeciesToSpecies.class + ), + @XlsEntityRef( + dtoProperties = {"publication", "species7"}, + dtoPropertiesTransformer = StringNormalizer.class, + xlsEntityRefType = PubSpecies.class, + xlsEntityRefProperties = {"publication.title", "species.species"}, + xlsEntityRefPropertiesLabels = {"Publication", "Species"}, + filler = XlsEntityRefFiller.class, + outputProperty = "plantIngredient7.species", + outputTransformer = PubSpeciesToSpecies.class + ), + @XlsEntityRef( + dtoProperties = {"publication", "species8"}, + dtoPropertiesTransformer = StringNormalizer.class, + xlsEntityRefType = PubSpecies.class, + xlsEntityRefProperties = {"publication.title", "species.species"}, + xlsEntityRefPropertiesLabels = {"Publication", "Species"}, + filler = XlsEntityRefFiller.class, + outputProperty = "plantIngredient8.species", + outputTransformer = PubSpeciesToSpecies.class + ), + @XlsEntityRef( + dtoProperties = {"publication", "species9"}, + dtoPropertiesTransformer = StringNormalizer.class, + xlsEntityRefType = PubSpecies.class, + xlsEntityRefProperties = {"publication.title", "species.species"}, + xlsEntityRefPropertiesLabels = {"Publication", "Species"}, + filler = XlsEntityRefFiller.class, + outputProperty = "plantIngredient9.species", + outputTransformer = PubSpeciesToSpecies.class + ), + @XlsEntityRef( + dtoProperties = {"publication", "species10"}, + dtoPropertiesTransformer = StringNormalizer.class, + xlsEntityRefType = PubSpecies.class, + xlsEntityRefProperties = {"publication.title", "species.species"}, + xlsEntityRefPropertiesLabels = {"Publication", "Species"}, + filler = XlsEntityRefFiller.class, + outputProperty = "plantIngredient10.species", + outputTransformer = PubSpeciesToSpecies.class, + afterFillingTransformer = RemedyEntitiesTransformer.class + ) + }) +//TODO Improve EmptyOrNotIfPropertyValue annotation to some ones with a criteriaProperty "not empty" +public class PlantIngredientsLine { + + @ImportProperty(columnLetterRef = "A", columnLabel = "Publication (title)") + @NotEmpty(message = "The cell is empty or the value invalid") + private String publication; + + @ImportProperty(columnLetterRef = "B", columnLabel = "Plant ingredient #1 (species)") + @NotEmpty(message = "The cell is empty or the value invalid") + private String species1; + + @ImportProperty(columnLetterRef = "C", columnLabel = "Plant ingredient #1 (part used)", + propertyLoader = @PropertyLoader(outputProperty = "plantIngredient1.partUsed", transformer = StringNormalizer.class)) + @NotEmpty(message = "The cell is empty or the value invalid") + private String partUsed1; + + @ImportProperty(columnLetterRef = "D", columnLabel = "Plant ingredient #2 (species)") + private String species2; + + @ImportProperty(columnLetterRef = "E", columnLabel = "Plant ingredient #2 (part used)", + propertyLoader = @PropertyLoader(outputProperty = "plantIngredient2.partUsed", transformer = StringNormalizer.class)) + private String partUsed2; + + @ImportProperty(columnLetterRef = "F", columnLabel = "Plant ingredient #3 (species)") + private String species3; + + @ImportProperty(columnLetterRef = "G", columnLabel = "Plant ingredient #3 (part used)", + propertyLoader = @PropertyLoader(outputProperty = "plantIngredient3.partUsed", transformer = StringNormalizer.class)) + private String partUsed3; + + @ImportProperty(columnLetterRef = "H", columnLabel = "Plant ingredient #4 (species)") + private String species4; + + @ImportProperty(columnLetterRef = "I", columnLabel = "Plant ingredient #4 (part used)", + propertyLoader = @PropertyLoader(outputProperty = "plantIngredient4.partUsed", transformer = StringNormalizer.class)) + private String partUsed4; + + @ImportProperty(columnLetterRef = "J", columnLabel = "Plant ingredient #5 (species)") + private String species5; + + @ImportProperty(columnLetterRef = "K", columnLabel = "Plant ingredient #5 (part used)", + propertyLoader = @PropertyLoader(outputProperty = "plantIngredient5.partUsed", transformer = StringNormalizer.class)) + private String partUsed5; + + @ImportProperty(columnLetterRef = "L", columnLabel = "Plant ingredient #6 (species)") + private String species6; + + @ImportProperty(columnLetterRef = "M", columnLabel = "Plant ingredient #6 (part used)", + propertyLoader = @PropertyLoader(outputProperty = "plantIngredient6.partUsed", transformer = StringNormalizer.class)) + private String partUsed6; + + @ImportProperty(columnLetterRef = "N", columnLabel = "Plant ingredient #7 (species)") + private String species7; + + @ImportProperty(columnLetterRef = "O", columnLabel = "Plant ingredient #7 (part used)", + propertyLoader = @PropertyLoader(outputProperty = "plantIngredient7.partUsed", transformer = StringNormalizer.class)) + private String partUsed7; + + @ImportProperty(columnLetterRef = "P", columnLabel = "Plant ingredient #8 (species)") + private String species8; + + @ImportProperty(columnLetterRef = "Q", columnLabel = "Plant ingredient #8 (part used)", + propertyLoader = @PropertyLoader(outputProperty = "plantIngredient8.partUsed", transformer = StringNormalizer.class)) + private String partUsed8; + + @ImportProperty(columnLetterRef = "R", columnLabel = "Plant ingredient #9 (species)") + private String species9; + + @ImportProperty(columnLetterRef = "S", columnLabel = "Plant ingredient #9 (part used)", + propertyLoader = @PropertyLoader(outputProperty = "plantIngredient9.partUsed", transformer = StringNormalizer.class)) + private String partUsed9; + + @ImportProperty(columnLetterRef = "T", columnLabel = "Plant ingredient #10 (species)") + private String species10; + + @ImportProperty(columnLetterRef = "U", columnLabel = "Plant ingredient #10 (part used)", + propertyLoader = @PropertyLoader(outputProperty = "plantIngredient10.partUsed", transformer = StringNormalizer.class)) + private String partUsed10; + +} diff --git a/src/main/java/nc/ird/malariaplantdb/service/xls/dto/PlantIngredientsTemp.java b/src/main/java/nc/ird/malariaplantdb/service/xls/dto/PlantIngredientsTemp.java new file mode 100644 index 0000000..f950bba --- /dev/null +++ b/src/main/java/nc/ird/malariaplantdb/service/xls/dto/PlantIngredientsTemp.java @@ -0,0 +1,39 @@ +package nc.ird.malariaplantdb.service.xls.dto; + +import lombok.Data; +import nc.ird.malariaplantdb.domain.PlantIngredient; +import nc.ird.malariaplantdb.domain.Publication; + +/** + * Entity which represents a set of plant ingredients defined in a publication. + * The entity will not be persist, but is useful to be referenced from the Ethnology, InVitroPharmaco, or + * InVivoPharmaco entities. + * + * @author acheype + */ +@Data +public class PlantIngredientsTemp { + + private Publication publication; + + private PlantIngredient plantIngredient1 = new PlantIngredient(); + + private PlantIngredient plantIngredient2 = new PlantIngredient(); + + private PlantIngredient plantIngredient3 = new PlantIngredient(); + + private PlantIngredient plantIngredient4 = new PlantIngredient(); + + private PlantIngredient plantIngredient5 = new PlantIngredient(); + + private PlantIngredient plantIngredient6 = new PlantIngredient(); + + private PlantIngredient plantIngredient7 = new PlantIngredient(); + + private PlantIngredient plantIngredient8 = new PlantIngredient(); + + private PlantIngredient plantIngredient9 = new PlantIngredient(); + + private PlantIngredient plantIngredient10 = new PlantIngredient(); + +} diff --git a/src/main/java/nc/ird/malariaplantdb/service/xls/dto/PublicationLine.java b/src/main/java/nc/ird/malariaplantdb/service/xls/dto/PublicationLine.java new file mode 100644 index 0000000..373da01 --- /dev/null +++ b/src/main/java/nc/ird/malariaplantdb/service/xls/dto/PublicationLine.java @@ -0,0 +1,145 @@ +package nc.ird.malariaplantdb.service.xls.dto; + +import lombok.Data; +import lombok.ToString; +import nc.ird.malariaplantdb.domain.Publication; +import nc.ird.malariaplantdb.service.xls.annotations.DbEntityRef; +import nc.ird.malariaplantdb.service.xls.annotations.ImportDto; +import nc.ird.malariaplantdb.service.xls.annotations.ImportProperty; +import nc.ird.malariaplantdb.service.xls.annotations.PropertyLoader; +import nc.ird.malariaplantdb.service.xls.fillers.CompilersDbEntityRefFiller; +import nc.ird.malariaplantdb.service.xls.transformers.CompilersStrTransformer; +import nc.ird.malariaplantdb.service.xls.transformers.StrToAuthorsSet; +import nc.ird.malariaplantdb.service.xls.transformers.StringNormalizer; +import org.hibernate.validator.constraints.NotEmpty; +import org.hibernate.validator.constraints.Range; +import org.hibernate.validator.constraints.URL; + +import javax.persistence.Column; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Pattern; + +/** + * DTO for Publication entity + * + * @author acheype + */ +@Data +@ToString(of = {"title", "authors", "year", "entryType"}) +@ImportDto(sheetLabel = "1 - PUBLI", importOrder = 1, outputEntityClass = Publication.class, + dbEntityRef = { + @DbEntityRef( + dtoProperties = {"compilers"}, + dtoPropertiesTransformer = CompilersStrTransformer.class, + filler = CompilersDbEntityRefFiller.class, + outputProperty = "compilers" + ) + }) +public class PublicationLine { + + @ImportProperty(columnLetterRef = "D", columnLabel = "Title", + propertyLoader = @PropertyLoader(transformer = StringNormalizer.class)) + @NotEmpty(message = "The cell is empty or the value invalid") + private String title; + + @ImportProperty(columnLetterRef = "B", columnLabel = "Author(s)", + propertyLoader = @PropertyLoader(transformer = StrToAuthorsSet.class)) + @NotEmpty(message = "The cell is empty or the value invalid") + @Pattern(regexp = "^(([a-zA-ZÀ-ÿ \\-]+),([a-zA-ZÀ-ÿ \\-.]+)/)*([a-zA-ZÀ-ÿ \\-]+),([a-zA-ZÀ-ÿ \\-.]+)$", + message = "The authors value is not well formatted. Please enter each author name with the last name " + + "first, a coma (,) then the given name initials (with comas). For several authors, " + + "please separate each complete name by a slash (/).") + private String authors; + + @ImportProperty(columnLetterRef = "C", columnLabel = "Year", + propertyLoader = @PropertyLoader()) + @NotNull(message = "The cell is empty or the value invalid") + @Range(min = 0, max = 3000, message = "The integer must be ranged between {min} and {max}") + private Integer year; + + @ImportProperty(columnLetterRef = "A", columnLabel = "Entry Type", + propertyLoader = @PropertyLoader(transformer = StringNormalizer.class)) + @NotEmpty(message = "The cell is empty or the value invalid") + @Column(nullable = false, name = "entry_type") + private String entryType; + + @ImportProperty(columnLetterRef = "E", columnLabel = "Journal", + propertyLoader = @PropertyLoader(transformer = StringNormalizer.class)) + private String journal; + + @ImportProperty(columnLetterRef = "F", columnLabel = "Pages", + propertyLoader = @PropertyLoader(transformer = StringNormalizer.class)) + private String pages; + + @ImportProperty(columnLetterRef = "G", columnLabel = "Volume", + propertyLoader = @PropertyLoader(transformer = StringNormalizer.class)) + private String volume; + + @ImportProperty(columnLetterRef = "H", columnLabel = "Nb of volumes", + propertyLoader = @PropertyLoader(transformer = StringNormalizer.class)) + @Column(name = "nb_of_volumes") + private String nbOfVolumes; + + @ImportProperty(columnLetterRef = "I", columnLabel = "Number", + propertyLoader = @PropertyLoader(transformer = StringNormalizer.class)) + private String number; + + @ImportProperty(columnLetterRef = "J", columnLabel = "Book title", + propertyLoader = @PropertyLoader(transformer = StringNormalizer.class)) + @Column(name = "book_title") + private String bookTitle; + + @ImportProperty(columnLetterRef = "K", columnLabel = "Publisher", + propertyLoader = @PropertyLoader(transformer = StringNormalizer.class)) + private String publisher; + + @ImportProperty(columnLetterRef = "L", columnLabel = "Edition", + propertyLoader = @PropertyLoader(transformer = StringNormalizer.class)) + private String edition; + + @ImportProperty(columnLetterRef = "M", columnLabel = "Conference name", + propertyLoader = @PropertyLoader(transformer = StringNormalizer.class)) + private String conferenceName; + + @ImportProperty(columnLetterRef = "N", columnLabel = "Conference place", + propertyLoader = @PropertyLoader(transformer = StringNormalizer.class)) + private String conferencePlace; + + @ImportProperty(columnLetterRef = "O", columnLabel = "University", + propertyLoader = @PropertyLoader(transformer = StringNormalizer.class)) + private String university; + + @ImportProperty(columnLetterRef = "P", columnLabel = "Institution", + propertyLoader = @PropertyLoader(transformer = StringNormalizer.class)) + private String institution; + + @ImportProperty(columnLetterRef = "Q", columnLabel = "DOI", + propertyLoader = @PropertyLoader(transformer = StringNormalizer.class)) + private String doi; + + @ImportProperty(columnLetterRef = "R", columnLabel = "PMID", + propertyLoader = @PropertyLoader(transformer = StringNormalizer.class)) + private String pmid; + + @ImportProperty(columnLetterRef = "S", columnLabel = "ISBN", + propertyLoader = @PropertyLoader(transformer = StringNormalizer.class)) + private String isbn; + + @ImportProperty(columnLetterRef = "T", columnLabel = "URL", + propertyLoader = @PropertyLoader(transformer = StringNormalizer.class)) + @URL + private String url; + + @NotEmpty(message = "The cell is empty or the value invalid") + @ImportProperty(columnLetterRef = "U", columnLabel = "Compiler(s) name(s)") + @Pattern(regexp = "^(([a-zA-ZÀ-ÿ \\-]+),([a-zA-ZÀ-ÿ \\-]+)/)*([a-zA-ZÀ-ÿ \\-]+),([a-zA-ZÀ-ÿ \\-]+)$", + message = "The compilers value is not well formatted. Please enter each compiler name with the last name " + + "first, a coma (,) then the given name. For several compilers, please separate each complete " + + "name by a slash (/).") + private String compilers; + + @ImportProperty(columnLetterRef = "V", columnLabel = "Note from compiler(s)", + propertyLoader = @PropertyLoader(transformer = StringNormalizer.class)) + private String compilersNotes; + +} diff --git a/src/main/java/nc/ird/malariaplantdb/service/xls/dto/SpeciesLine.java b/src/main/java/nc/ird/malariaplantdb/service/xls/dto/SpeciesLine.java new file mode 100644 index 0000000..ae69aa3 --- /dev/null +++ b/src/main/java/nc/ird/malariaplantdb/service/xls/dto/SpeciesLine.java @@ -0,0 +1,92 @@ +package nc.ird.malariaplantdb.service.xls.dto; + +import lombok.Data; +import nc.ird.malariaplantdb.domain.PubSpecies; +import nc.ird.malariaplantdb.domain.Publication; +import nc.ird.malariaplantdb.service.xls.annotations.ImportDto; +import nc.ird.malariaplantdb.service.xls.annotations.ImportProperty; +import nc.ird.malariaplantdb.service.xls.annotations.PropertyLoader; +import nc.ird.malariaplantdb.service.xls.annotations.XlsEntityRef; +import nc.ird.malariaplantdb.service.xls.fillers.XlsEntityRefFiller; +import nc.ird.malariaplantdb.service.xls.transformers.SpeciesEntitiesTransformer; +import nc.ird.malariaplantdb.service.xls.transformers.StringNormalizer; +import nc.ird.malariaplantdb.service.xls.validators.EmptyOrNotIfPropertyValue; +import org.hibernate.validator.constraints.NotEmpty; + +import javax.validation.constraints.NotNull; + +/** + * DTO for Species entity + * + * @author acheype + */ +@Data +@ImportDto(sheetLabel = "2 - SPECIES", importOrder = 2, outputEntityClass = PubSpecies.class, + xlsEntityRef = { + @XlsEntityRef( + dtoProperties = {"publication"}, + xlsEntityRefType = Publication.class, + xlsEntityRefProperties = {"title"}, + xlsEntityRefPropertiesLabels = {"Title"}, + filler = XlsEntityRefFiller.class, + outputProperty = "publication", + dtoPropertiesTransformer = StringNormalizer.class + ) + }) +@EmptyOrNotIfPropertyValue.List({ + @EmptyOrNotIfPropertyValue( + message = "As 'Herbarium voucher' is YES, 'Herbarium' must not be empty", + criteriaProperty = "isHerbariumVoucher", + criteriaValues = {"true"}, + testedProperty = "herbarium", + isEmpty = false + ), + @EmptyOrNotIfPropertyValue( + message = "As 'Herbarium voucher' is NO, 'Herbarium' must be empty", + criteriaProperty = "isHerbariumVoucher", + criteriaValues = {"false"}, + testedProperty = "herbarium", + isEmpty = true + )} +) +public class SpeciesLine { + + @ImportProperty(columnLetterRef = "A", columnLabel = "Publication (title)") + @NotEmpty(message = "The cell is empty or the value invalid") + private String publication; + + @ImportProperty(columnLetterRef = "B", columnLabel = "Family", + propertyLoader = @PropertyLoader(outputProperty = "species.family", transformer = StringNormalizer.class)) + @NotEmpty(message = "The cell is empty or the value invalid") + private String family; + + @ImportProperty(columnLetterRef = "C", columnLabel = "Species", + propertyLoader = @PropertyLoader(outputProperty = "species.species", transformer = StringNormalizer + .class), afterLoadingTransformer = SpeciesEntitiesTransformer.class) + @NotEmpty(message = "The cell is empty or the value invalid") + private String species; + + @ImportProperty(columnLetterRef = "D", columnLabel = "Species name in publication", + propertyLoader = @PropertyLoader(outputProperty = "speciesNameInPub", transformer = StringNormalizer.class)) + @NotEmpty(message = "The cell is empty or the value invalid") + private String speciesNameInPub; + + @ImportProperty(columnLetterRef = "E", columnLabel = "Herbarium voucher", + propertyLoader = @PropertyLoader(outputProperty = "isHerbariumVoucher")) + @NotNull(message = "The cell is empty or the value invalid") + private Boolean isHerbariumVoucher; + + @ImportProperty(columnLetterRef = "F", columnLabel = "Herbarium", + propertyLoader = @PropertyLoader(outputProperty = "herbarium", transformer = StringNormalizer.class)) + private String herbarium; + + @ImportProperty(columnLetterRef = "G", columnLabel = "Country", + propertyLoader = @PropertyLoader(outputProperty = "country", transformer = StringNormalizer.class)) + @NotEmpty(message = "The cell is empty or the value invalid") + private String country; + + @ImportProperty(columnLetterRef = "H", columnLabel = "Continent", + propertyLoader = @PropertyLoader(outputProperty = "continent", transformer = StringNormalizer.class)) + private String continent; + +} diff --git a/src/main/java/nc/ird/malariaplantdb/service/xls/exceptions/ImportException.java b/src/main/java/nc/ird/malariaplantdb/service/xls/exceptions/ImportException.java new file mode 100644 index 0000000..7cee16f --- /dev/null +++ b/src/main/java/nc/ird/malariaplantdb/service/xls/exceptions/ImportException.java @@ -0,0 +1,15 @@ +package nc.ird.malariaplantdb.service.xls.exceptions; + +/** + * Checked exception which occurs in the xls importation process + */ +public class ImportException extends Exception { + + public ImportException(String message, Throwable cause) { + super(message, cause); + } + + public ImportException(String message) { + super(message); + } +} diff --git a/src/main/java/nc/ird/malariaplantdb/service/xls/exceptions/ImportRuntimeException.java b/src/main/java/nc/ird/malariaplantdb/service/xls/exceptions/ImportRuntimeException.java new file mode 100644 index 0000000..dd025ab --- /dev/null +++ b/src/main/java/nc/ird/malariaplantdb/service/xls/exceptions/ImportRuntimeException.java @@ -0,0 +1,15 @@ +package nc.ird.malariaplantdb.service.xls.exceptions; + +/** + * Unchecked exception which occurs in the xls importation process + */ +public class ImportRuntimeException extends RuntimeException { + + public ImportRuntimeException(String message, Throwable cause) { + super(message, cause); + } + + public ImportRuntimeException(String message) { + super(message); + } +} diff --git a/src/main/java/nc/ird/malariaplantdb/service/xls/fillers/CompilersDbEntityRefFiller.java b/src/main/java/nc/ird/malariaplantdb/service/xls/fillers/CompilersDbEntityRefFiller.java new file mode 100644 index 0000000..af9c9be --- /dev/null +++ b/src/main/java/nc/ird/malariaplantdb/service/xls/fillers/CompilersDbEntityRefFiller.java @@ -0,0 +1,46 @@ +package nc.ird.malariaplantdb.service.xls.fillers; + +import nc.ird.malariaplantdb.repository.CompilerRepository; +import nc.ird.malariaplantdb.service.xls.structures.PropVals; +import nc.ird.malariaplantdb.service.xls.transformers.StringNormalizer; +import org.apache.commons.collections.Transformer; +import org.springframework.beans.BeansException; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.stereotype.Service; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +/** + * Find in the database the{@code Compiler} whose given and family are in a{@code PropVals} object. + * The{@code Compiler} will be searched through a repository accessed by the Spring container. + */ +@Service +public class CompilersDbEntityRefFiller extends DbEntityRefFiller implements ApplicationContextAware { + + // by implementing ApplicationContextAware, the Spring application context is supplied + private static ApplicationContext applicationContext; + + @Override + public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { + this.applicationContext = applicationContext; + } + + @Override + protected List findValuesInDB(PropVals propValsSearched){ + + Transformer transformer = new StringNormalizer(); + String family = (String) transformer.transform(propValsSearched.get("compilers.family")); + String given = (String) transformer.transform(propValsSearched.get("compilers.given")); + + // a better way is to get the repo from the Spring container is to use a factory which lets Spring instantiate + // the filler, but the drawback is that the user must pass the factory to the import module + Map compilerRepos = applicationContext.getBeansOfType(CompilerRepository.class); + + CompilerRepository compilerRepository = compilerRepos.entrySet().iterator().next().getValue(); + + return new ArrayList<>(compilerRepository.findByFamilyAndGivenAllIgnoreCase(family, given)); + } +} diff --git a/src/main/java/nc/ird/malariaplantdb/service/xls/fillers/DbEntityRefFiller.java b/src/main/java/nc/ird/malariaplantdb/service/xls/fillers/DbEntityRefFiller.java new file mode 100644 index 0000000..6243f5b --- /dev/null +++ b/src/main/java/nc/ird/malariaplantdb/service/xls/fillers/DbEntityRefFiller.java @@ -0,0 +1,37 @@ +package nc.ird.malariaplantdb.service.xls.fillers; + +import nc.ird.malariaplantdb.service.xls.fillers.errors.DbFillerError; +import nc.ird.malariaplantdb.service.xls.fillers.errors.FillerError; +import nc.ird.malariaplantdb.service.xls.structures.PropVals; + +import java.lang.reflect.InvocationTargetException; +import java.util.List; + +public abstract class DbEntityRefFiller extends EntityRefFiller { + + /** + * Search in the database the {@code propValsSearched} values. + * @param propValsSearched the {@code PropVals} object which defines the entries from the dto + * @return the list of the objects found + */ + protected abstract List findValuesInDB(PropVals propValsSearched); + + /** + * {@inheritDoc} + */ + public Object findEntityRef(PropVals propValsSearched) throws IllegalAccessException, InvocationTargetException, + NoSuchMethodException, InstantiationException, IllegalArgumentException { + + List result = findValuesInDB(propValsSearched); + + if (result.isEmpty()) + this.setResultError(new DbFillerError(propValsSearched, FillerError.ErrorCause.NO_ENTITY_FOUND)); + else { + if (result.size() != 1) + this.setResultError(new DbFillerError(propValsSearched, FillerError.ErrorCause.NOT_UNIQUE_MATCH)); + else + return result.get(0); + } + return null; + } +} diff --git a/src/main/java/nc/ird/malariaplantdb/service/xls/fillers/EntityRefFiller.java b/src/main/java/nc/ird/malariaplantdb/service/xls/fillers/EntityRefFiller.java new file mode 100644 index 0000000..ccc82d8 --- /dev/null +++ b/src/main/java/nc/ird/malariaplantdb/service/xls/fillers/EntityRefFiller.java @@ -0,0 +1,147 @@ +package nc.ird.malariaplantdb.service.xls.fillers; + +import nc.ird.malariaplantdb.service.xls.ExcelLoader; +import nc.ird.malariaplantdb.service.xls.fillers.errors.FillerError; +import nc.ird.malariaplantdb.service.xls.structures.PropVals; +import org.apache.commons.beanutils.PropertyUtils; +import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.collections.Transformer; + +import java.lang.reflect.InvocationTargetException; +import java.util.Collection; +import java.util.List; + +/** + * Fill the{@code outputProperty} with an already loaded entity searched with the{@code propValsSearch} values. + * + * If a{@code dtoPropertiesTransformer} has initialized the{@code PropValsSearched} list with several objects, the + * result objects will be added to an already constructed collection set in the {@code outputProperties}. + * + * @author acheype + */ +public abstract class EntityRefFiller { + + /** + * List of{@code PropVals} which identify the values to be searched. Each {@code PropVals} object contains a set + * of parameterNames/values in order to find one result object. + */ + List propValsSearched; + + /** + * The entity which contains the property to be set + */ + Object outputEntity; + + /** + * The entity property name which will be set with the result object. + */ + String outputProperty; + + /** + *

In situation where the outputProperty can't extends the{@code xlsEntityRefType} class, a transformer can be + * defined.

+ *

Returns the transformer which transforms the matching entity into an object of the{@code + * outputProperty} class.

+ */ + private Class outputTransformer; + + /** + * The error which could occur during the{@code resultObject} search + */ + FillerError resultError; + + /** + * Search a referenced entity with the value defined in the{@code propValsSearched} + * + * @param propValsSearched the{@code PropVal} searched + */ + public abstract Object findEntityRef(PropVals propValsSearched) throws IllegalAccessException, + InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalArgumentException; + + @SuppressWarnings("unchecked") + public void fillPropertyWithRef() throws IllegalAccessException, NoSuchMethodException, + InvocationTargetException, InstantiationException, IllegalArgumentException { + + if (CollectionUtils.isEmpty(propValsSearched)) + throw new IllegalArgumentException(String.format("The propValsSearched used to find a reference for the " + + "'%s' property of the '%s' class must not be empty or null", outputProperty, outputEntity + .getClass().getSimpleName())); + + else if (propValsSearched.size() == 1) { + Object resultObject = findEntityRef(propValsSearched.get(0)); + + Object outputPropertyVal = PropertyUtils.getProperty(outputEntity, outputProperty); + if (outputPropertyVal instanceof Collection) { + // if the output property is a collection, add the unique object found + Collection outputPropertyCol = (Collection) outputPropertyVal; + if (resultObject != null) + outputPropertyCol.add(ExcelLoader.applyTransformer(outputTransformer, resultObject)); + } else { + // if it's a simple object, set it + try { + if (resultObject != null) + PropertyUtils.setProperty(outputEntity, outputProperty, + ExcelLoader.applyTransformer(outputTransformer, resultObject)); + } catch (IllegalArgumentException e) { + throw new IllegalArgumentException("The outputProperty type is not matching with the found object " + + "type. If a Collection object is expected, the collection must be initialized before.", e); + } + } + } else { + // case where several results objects will be set in a collection + Object outputPropertyVal = PropertyUtils.getProperty(outputEntity, outputProperty); + if (outputPropertyVal == null || !(outputPropertyVal instanceof Collection)) + throw new IllegalArgumentException(String.format("As several objects are searched by the " + + "entityRefFiller, the '%s' property of the '%s' class must be an already constructed collection", + outputProperty, outputEntity.getClass().getSimpleName())); + else { + Collection outputPropertyCol = (Collection) outputPropertyVal; + for (PropVals propVals : propValsSearched) { + Object resultObject = findEntityRef(propVals); + if (resultObject != null) + outputPropertyCol.add(ExcelLoader.applyTransformer(outputTransformer, resultObject)); + } + } + } + } + + public List getPropValsSearched() { + return propValsSearched; + } + + public void setPropValsSearched(List propValsSearched) { + this.propValsSearched = propValsSearched; + } + + public Object getOutputEntity() { + return outputEntity; + } + + public void setOutputEntity(Object outputEntity) { + this.outputEntity = outputEntity; + } + + public String getOutputProperty() { + return outputProperty; + } + + public void setOutputProperty(String outputProperty) { + this.outputProperty = outputProperty; + } + + public Class getOutputTransformer() { + return outputTransformer; + } + + public void setOutputTransformer(Class outputTransformer) { + this.outputTransformer = outputTransformer; + } + + public FillerError getResultError() { + return resultError; + } + + public void setResultError(FillerError resultError) { + this.resultError = resultError; + } +} diff --git a/src/main/java/nc/ird/malariaplantdb/service/xls/fillers/PublicationDbEntityRefFiller.java b/src/main/java/nc/ird/malariaplantdb/service/xls/fillers/PublicationDbEntityRefFiller.java new file mode 100644 index 0000000..fe777a8 --- /dev/null +++ b/src/main/java/nc/ird/malariaplantdb/service/xls/fillers/PublicationDbEntityRefFiller.java @@ -0,0 +1,47 @@ +package nc.ird.malariaplantdb.service.xls.fillers; + +import nc.ird.malariaplantdb.repository.PublicationRepository; +import nc.ird.malariaplantdb.service.xls.structures.PropVals; +import nc.ird.malariaplantdb.service.xls.transformers.StringNormalizer; +import org.springframework.beans.BeansException; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.stereotype.Service; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +/** + * Find from the database an publication entity by its title, then set it to the entity outputProperty. + * + * @author acheype + */ +@Service +public class PublicationDbEntityRefFiller extends DbEntityRefFiller implements ApplicationContextAware { + + // by implementing ApplicationContextAware, the Spring application context is supplied + private static ApplicationContext applicationContext; + + @Override + public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { + this.applicationContext = applicationContext; + } + + @Override + protected List findValuesInDB(PropVals propValsSearched){ + + // a better way is to get the repo from the Spring container is to use a factory which lets Spring instantiate + // the filler, but the drawback is that the user must pass the factory to the import module + Map publiRepos = applicationContext.getBeansOfType(PublicationRepository.class); + + PublicationRepository publiRepo = publiRepos.entrySet().iterator().next().getValue(); + return new ArrayList<>( + publiRepo.findByTitleIgnoreCase( + StringNormalizer.getInstance().transform(propValsSearched.get(propValsSearched.keySet() + .iterator().next()).toString()) + ) + ); + } + +} diff --git a/src/main/java/nc/ird/malariaplantdb/service/xls/fillers/XlsEntityRefFiller.java b/src/main/java/nc/ird/malariaplantdb/service/xls/fillers/XlsEntityRefFiller.java new file mode 100644 index 0000000..a8c40c3 --- /dev/null +++ b/src/main/java/nc/ird/malariaplantdb/service/xls/fillers/XlsEntityRefFiller.java @@ -0,0 +1,107 @@ +package nc.ird.malariaplantdb.service.xls.fillers; + +import nc.ird.malariaplantdb.service.xls.fillers.errors.FillerError; +import nc.ird.malariaplantdb.service.xls.fillers.errors.XlsFillerError; +import nc.ird.malariaplantdb.service.xls.fillers.util.EqualsStrategy; +import nc.ird.malariaplantdb.service.xls.structures.PropVals; +import org.apache.commons.beanutils.PropertyUtils; + +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +public class XlsEntityRefFiller extends EntityRefFiller { + + private List xlsRefEntities; + + private List xlsRefEntityProperties; + + private List xlsRefEntityPropertiesLabels; + + private Class fillerEqualsStrategy; + + /** + * {@inheritDoc} + */ + public Object findEntityRef(PropVals propValsSearched) throws IllegalAccessException, InvocationTargetException, + NoSuchMethodException, InstantiationException, IllegalArgumentException { + + if (getXlsRefEntityProperties().size() != propValsSearched.size()) + throw new IllegalArgumentException(String.format("The xlsEntityRefProperties must have the same number of" + + " properties than the dtoProperties (if a dtoPropertiesTransformer is specified, after its " + + "application). They have respectively these values : [%s] and [%s]", + xlsRefEntityProperties.stream().collect(Collectors.joining(", ")), + propValsSearched.keySet().stream().collect(Collectors.joining(", ")))); + + List result = new ArrayList<>(); + + for (Object curRefEntity : getXlsRefEntities()) { + PropVals refPropVals = new PropVals(); + + for (String refProp : getXlsRefEntityProperties()) + refPropVals.put(refProp, PropertyUtils.getProperty(curRefEntity, refProp)); + + if (compareWithFillerEqualsStrategy(propValsSearched, refPropVals)) { + result.add(curRefEntity); + } + } + + if (result.isEmpty()) + this.setResultError(new XlsFillerError(propValsSearched, FillerError.ErrorCause.NO_ENTITY_FOUND, + xlsRefEntities.isEmpty() ? null : xlsRefEntities.get(0).getClass(), xlsRefEntityProperties, + xlsRefEntityPropertiesLabels)); + else { + if (result.size() != 1) + this.setResultError(new XlsFillerError(propValsSearched, FillerError.ErrorCause.NOT_UNIQUE_MATCH, + xlsRefEntities.isEmpty() ? null : xlsRefEntities.get(0).getClass(), xlsRefEntityProperties, + xlsRefEntityPropertiesLabels)); + else + return result.get(0); + } + return null; + } + + private boolean compareWithFillerEqualsStrategy(PropVals propVals1, PropVals propVals2) throws + NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException { + + Constructor constructor = getFillerEqualsStrategy().getDeclaredConstructor(); + constructor.setAccessible(true); + EqualsStrategy fillerEqualsStrategy = (EqualsStrategy) constructor.newInstance(); + + return fillerEqualsStrategy.equals(propVals1, propVals2); + } + + public List getXlsRefEntities() { + return xlsRefEntities; + } + + public void setXlsRefEntities(List xlsRefEntities) { + this.xlsRefEntities = xlsRefEntities; + } + + public List getXlsRefEntityProperties() { + return xlsRefEntityProperties; + } + + public void setXlsRefEntityProperties(List xlsRefEntityProperties) { + this.xlsRefEntityProperties = xlsRefEntityProperties; + } + + public List getXlsRefEntityPropertiesLabels() { + return xlsRefEntityPropertiesLabels; + } + + public void setXlsRefEntityPropertiesLabels(List xlsRefEntityPropertiesLabels) { + this.xlsRefEntityPropertiesLabels = xlsRefEntityPropertiesLabels; + } + + public Class getFillerEqualsStrategy() { + return fillerEqualsStrategy; + } + + public void setFillerEqualsStrategy(Class fillerEqualsStrategy) { + this.fillerEqualsStrategy = fillerEqualsStrategy; + } +} diff --git a/src/main/java/nc/ird/malariaplantdb/service/xls/fillers/errors/DbFillerError.java b/src/main/java/nc/ird/malariaplantdb/service/xls/fillers/errors/DbFillerError.java new file mode 100644 index 0000000..7ef6a24 --- /dev/null +++ b/src/main/java/nc/ird/malariaplantdb/service/xls/fillers/errors/DbFillerError.java @@ -0,0 +1,14 @@ +package nc.ird.malariaplantdb.service.xls.fillers.errors; + +import nc.ird.malariaplantdb.service.xls.structures.PropVals; + +/** + * Created by adri on 17/11/15. + */ +public class DbFillerError extends FillerError { + + public DbFillerError(PropVals propVals, ErrorCause errorCause) { + super(propVals, errorCause); + } + +} diff --git a/src/main/java/nc/ird/malariaplantdb/service/xls/fillers/errors/FillerError.java b/src/main/java/nc/ird/malariaplantdb/service/xls/fillers/errors/FillerError.java new file mode 100644 index 0000000..881214d --- /dev/null +++ b/src/main/java/nc/ird/malariaplantdb/service/xls/fillers/errors/FillerError.java @@ -0,0 +1,38 @@ +package nc.ird.malariaplantdb.service.xls.fillers.errors; + +import nc.ird.malariaplantdb.service.xls.infos.SheetInfo; +import nc.ird.malariaplantdb.service.xls.structures.PropVals; + +/** + * Created by adri on 17/11/15. + */ +public abstract class FillerError { + + public enum ErrorCause { + NO_ENTITY_FOUND, + NOT_UNIQUE_MATCH + } + + private PropVals propVals; + + private ErrorCause errorCause; + + private SheetInfo sheetInfo; + + public FillerError(PropVals propVals, ErrorCause errorCause) { + this.propVals = propVals; + this.errorCause = errorCause; + } + + public PropVals getPropVals() { + return propVals; + } + + public ErrorCause getErrorCause() { + return errorCause; + } + + public SheetInfo getSheetInfo() { + return sheetInfo; + } +} diff --git a/src/main/java/nc/ird/malariaplantdb/service/xls/fillers/errors/XlsFillerError.java b/src/main/java/nc/ird/malariaplantdb/service/xls/fillers/errors/XlsFillerError.java new file mode 100644 index 0000000..8f93762 --- /dev/null +++ b/src/main/java/nc/ird/malariaplantdb/service/xls/fillers/errors/XlsFillerError.java @@ -0,0 +1,37 @@ +package nc.ird.malariaplantdb.service.xls.fillers.errors; + +import nc.ird.malariaplantdb.service.xls.structures.PropVals; + +import java.util.List; + +/** + * Created by adri on 17/11/15. + */ +public class XlsFillerError extends FillerError { + + private Class refEntityClass; + + private List xlsEntityRefProperties; + + private List xlsEntityRefPropertiesLabels; + + public XlsFillerError(PropVals propVals, ErrorCause errorCause, Class refEntityClass, List + xlsEntityRefProperties, List xlsEntityRefPropertiesLabels) { + super(propVals, errorCause); + this.refEntityClass = refEntityClass; + this.xlsEntityRefProperties = xlsEntityRefProperties; + this.xlsEntityRefPropertiesLabels = xlsEntityRefPropertiesLabels; + } + + public Class getRefEntityClass() { + return refEntityClass; + } + + public List getXlsEntityRefProperties() { + return xlsEntityRefProperties; + } + + public List getXlsEntityRefPropertiesLabels() { + return xlsEntityRefPropertiesLabels; + } +} diff --git a/src/main/java/nc/ird/malariaplantdb/service/xls/fillers/util/DefaultEqualsStrategy.java b/src/main/java/nc/ird/malariaplantdb/service/xls/fillers/util/DefaultEqualsStrategy.java new file mode 100644 index 0000000..3413e67 --- /dev/null +++ b/src/main/java/nc/ird/malariaplantdb/service/xls/fillers/util/DefaultEqualsStrategy.java @@ -0,0 +1,16 @@ +package nc.ird.malariaplantdb.service.xls.fillers.util; + +import nc.ird.malariaplantdb.service.xls.structures.PropVals; + +/** + * The default equals strategy refer to the one defined for the PropVals class, that's to say two{@code + * PropVals} objects are equals when they have the same values in the same order (independently of the key + * value). + */ +public class DefaultEqualsStrategy implements EqualsStrategy { + + @Override + public boolean equals(PropVals o1, PropVals o2) { + return (o1 == null)? o2 == null : o1.equals(o2); + } +} diff --git a/src/main/java/nc/ird/malariaplantdb/service/xls/fillers/util/EqualsStrategy.java b/src/main/java/nc/ird/malariaplantdb/service/xls/fillers/util/EqualsStrategy.java new file mode 100644 index 0000000..db5e82f --- /dev/null +++ b/src/main/java/nc/ird/malariaplantdb/service/xls/fillers/util/EqualsStrategy.java @@ -0,0 +1,13 @@ +package nc.ird.malariaplantdb.service.xls.fillers.util; + +import nc.ird.malariaplantdb.service.xls.structures.PropVals; + +/** + * Interface used to supply an equality strategy applied between two{@code PropVals} objects + * + * @author acheype + */ +public interface EqualsStrategy { + + boolean equals(PropVals o1, PropVals o2); +} diff --git a/src/main/java/nc/ird/malariaplantdb/service/xls/infos/ColumnInfo.java b/src/main/java/nc/ird/malariaplantdb/service/xls/infos/ColumnInfo.java new file mode 100644 index 0000000..d340d65 --- /dev/null +++ b/src/main/java/nc/ird/malariaplantdb/service/xls/infos/ColumnInfo.java @@ -0,0 +1,48 @@ +package nc.ird.malariaplantdb.service.xls.infos; + +import lombok.*; +import nc.ird.malariaplantdb.service.xls.transformers.EntitiesTransformer; +import org.apache.commons.collections.Transformer; + +/** + * Information for a Excel column + */ +@Getter +@Setter +@ToString +@NoArgsConstructor +@AllArgsConstructor +public class ColumnInfo { + + /** + * Excel column reference (one or several letters) to keep a map between the ref and the label + */ + private String columnLetterRef; + + /** + * Excel column name to tell the user the column source reference + */ + private String columnLabel; + + /** + * Name of the dtoPropertyName which map the column in a dto + */ + private String dtoPropertyName; + + /** + * The transformer which transforms the importation value into the corresponding one for the + * outputProperty + */ + private Class propertyTransformer; + + /** + * The output property in which the imported value will be loaded + */ + private String outputProperty; + + /** + * Transformer which modify the entities map after all the loadings of this property + */ + private Class afterLoadingTransformer; + +} diff --git a/src/main/java/nc/ird/malariaplantdb/service/xls/infos/DbEntityRefInfo.java b/src/main/java/nc/ird/malariaplantdb/service/xls/infos/DbEntityRefInfo.java new file mode 100644 index 0000000..f2ff93b --- /dev/null +++ b/src/main/java/nc/ird/malariaplantdb/service/xls/infos/DbEntityRefInfo.java @@ -0,0 +1,19 @@ +package nc.ird.malariaplantdb.service.xls.infos; + +import nc.ird.malariaplantdb.service.xls.fillers.EntityRefFiller; +import nc.ird.malariaplantdb.service.xls.transformers.EntitiesTransformer; +import org.apache.commons.collections.Transformer; + +/** + * Information for a DB entity reference + * + * @author acheype + */ +public class DbEntityRefInfo extends EntityRefInfo { + + public DbEntityRefInfo(String[] dtoProperties, Class dtoPropertiesTransformer, + Class filler, String outputProperty, + Class outputTransformer, Class afterFillingTransformer) { + super(dtoProperties, dtoPropertiesTransformer, filler, outputProperty, outputTransformer, afterFillingTransformer); + } +} diff --git a/src/main/java/nc/ird/malariaplantdb/service/xls/infos/EntityRefInfo.java b/src/main/java/nc/ird/malariaplantdb/service/xls/infos/EntityRefInfo.java new file mode 100644 index 0000000..59d1c81 --- /dev/null +++ b/src/main/java/nc/ird/malariaplantdb/service/xls/infos/EntityRefInfo.java @@ -0,0 +1,55 @@ +package nc.ird.malariaplantdb.service.xls.infos; + +import lombok.*; +import nc.ird.malariaplantdb.service.xls.fillers.EntityRefFiller; +import nc.ird.malariaplantdb.service.xls.transformers.EntitiesTransformer; +import org.apache.commons.collections.Transformer; + +/** + * Abstract class for an entity reference + * + * @author acheype + */ +@Getter +@Setter +@ToString +@NoArgsConstructor +@AllArgsConstructor +public class EntityRefInfo { + + /** + * The dto property names used to identify the referenced entity. + */ + private String[] dtoProperties; + + /** + * The transformer which applies on all {@code dtoIdentifierProperties} values before to be find a + * matching entity (by default an no-operation transformer). The transformer have to receive a PropVals object + * and return an other. + */ + private Class dtoPropertiesTransformer; + + /** + * The filler which finds, then set the corresponding entity + */ + private Class filler; + + /** + * The output property in which the entity reference will be filled + */ + private String outputProperty; + + /** + *

In situation where the outputProperty can't extends the{@code xlsEntityRefType} class, a transformer can be + * defined.

+ *

Returns the transformer which transforms the matching entity into an object of the{@code + * outputProperty} class.

+ */ + private Class outputTransformer; + + /** + * The transformer which modify the entities map after all the loadings for this property + */ + private Class afterFillingTransformer; + +} diff --git a/src/main/java/nc/ird/malariaplantdb/service/xls/infos/SheetInfo.java b/src/main/java/nc/ird/malariaplantdb/service/xls/infos/SheetInfo.java new file mode 100644 index 0000000..3eee0ec --- /dev/null +++ b/src/main/java/nc/ird/malariaplantdb/service/xls/infos/SheetInfo.java @@ -0,0 +1,96 @@ +package nc.ird.malariaplantdb.service.xls.infos; + +import lombok.*; + +import java.util.List; + +/** + * Information for a Excel sheet + */ +@Getter +@Setter +@ToString +@NoArgsConstructor +@AllArgsConstructor +public class SheetInfo { + + /** + * DTO class which corresponds to the sheet + */ + private Class dtoClass; + + /** + * Excel sheet name to tell the user the sheet source reference + */ + private String sheetLabel; + + // property removed because an other solution have been found, could be enabled again for some cases +// /** +// *

The entity class from which the importation process will start. In this case, the reading stage +// * will be omitted ({@code startRow} will be ignored) and the values will be directly read from this entity by the +// * loaders. That option is useful in rare case, when the importation need to transform the model in several steps +// * .

+// *
  • The {@code sheetLabel} must be the same as the dto's one used to produce the entity, in this way the +// * cell errors of these different steps will refer to the same sheet label.
  • +// *
  • The {@code importOrder} must be greater than the dto's one used to produce the entity, so entity values will +// * already be loaded.
  • +// */ +// private Class fromOtherEntity; + + /** + * The number line of the first row imported from the sheet + */ + private int startRow; + + /** + * The output entity class in which the importation data will be loaded + */ + private Class outputEntityClass; + + /** + * Information about the Xls entity references to set in the final entity + */ + private List xlsEntityRefInfos; + + /** + * Information about the DB entity references to set in the final entity + */ + private List dbEntityRefInfos; + + /** + * Fields information list used for the importation + */ + private List columnInfos; + + /** + * Access to a column info by its dto property name. For nested, indexed or mapped properties, refer to the first + * name element. + * + * @param dtoPropertyName the dto property name searched + * @return the column info object or {@code null} if not found + */ + public ColumnInfo getColumnInfoByDtoProperty(@NonNull String dtoPropertyName) { + + return columnInfos.stream() + .filter(c -> dtoPropertyName.split("(\\.)|(\\[)|(\\()")[0] + .equals(c.getDtoPropertyName())) + .findAny() + .orElse(null); + } + + /** + * Access to a column info by its output property name. For nested, indexed or mapped properties, refer to the first + * name element. + * + * @param outputProperty the output property name searched + * @return the column info object or {@code null} if not found + */ + public ColumnInfo getColumnInfoByOutputProperty(@NonNull String outputProperty) { + return columnInfos.stream() + .filter(c -> outputProperty//.split("(\\.)|(\\[)|(\\()")[0] + .equals(c.getOutputProperty())) + .findAny() + .orElse(null); + } + +} diff --git a/src/main/java/nc/ird/malariaplantdb/service/xls/infos/XlsEntityRefInfo.java b/src/main/java/nc/ird/malariaplantdb/service/xls/infos/XlsEntityRefInfo.java new file mode 100644 index 0000000..5333dcf --- /dev/null +++ b/src/main/java/nc/ird/malariaplantdb/service/xls/infos/XlsEntityRefInfo.java @@ -0,0 +1,56 @@ +package nc.ird.malariaplantdb.service.xls.infos; + +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; +import nc.ird.malariaplantdb.service.xls.fillers.EntityRefFiller; +import nc.ird.malariaplantdb.service.xls.fillers.util.EqualsStrategy; +import nc.ird.malariaplantdb.service.xls.transformers.EntitiesTransformer; +import org.apache.commons.collections.Transformer; + +/** + * Information for a Xls entity reference + * + * @author acheype + */ +@Getter +@Setter +@ToString +public class XlsEntityRefInfo extends EntityRefInfo { + + /** + * The class of the already loaded entity which represents a Xls worksheet + */ + private Class xlsEntityRefType; + + /** + * The property names of the worksheet entity which will be compared to the {@code dtoProperties} + * values. If not, a default array will be set. + */ + private String[] xlsEntityRefProperties; + + /** + * The column names in the referenced sheet of the properties compared to the{@code dtoProperties} (used + * to write eventual understandable error messages) + */ + private String[] xlsEntityRefPropertiesLabels; + + + /** + * The equals strategy used to compare the{@code dtoProperties} values and the{@code xlsEntityRefProperties} + * ones. If no equals strategy is supplied, the default strategy consists of comparing one by one in the same order + * each{@code dtoProperties} values and{@code xlsEntityRefType} ones. + */ + private Class fillerEqualsStrategy; + + public XlsEntityRefInfo(String[] dtoProperties, Class dtoPropertiesTransformer, Class + xlsEntityRefType, String[] xlsEntityRefProperties, String[] xlsEntityRefPropertiesLabels, Class filler, Class fillerEqualsStrategy, String outputProperty, + Class outputTransformer, Class afterFillingTransformer) { + super(dtoProperties, dtoPropertiesTransformer, filler, outputProperty, outputTransformer, afterFillingTransformer); + this.xlsEntityRefType = xlsEntityRefType; + this.xlsEntityRefProperties = xlsEntityRefProperties; + this.xlsEntityRefPropertiesLabels = xlsEntityRefPropertiesLabels; + this.fillerEqualsStrategy = fillerEqualsStrategy; + } +} diff --git a/src/main/java/nc/ird/malariaplantdb/service/xls/structures/CellError.java b/src/main/java/nc/ird/malariaplantdb/service/xls/structures/CellError.java new file mode 100644 index 0000000..557ef3e --- /dev/null +++ b/src/main/java/nc/ird/malariaplantdb/service/xls/structures/CellError.java @@ -0,0 +1,98 @@ +package nc.ird.malariaplantdb.service.xls.structures; + +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; +import lombok.extern.slf4j.Slf4j; +import nc.ird.malariaplantdb.service.xls.exceptions.ImportException; +import nc.ird.malariaplantdb.service.xls.infos.ColumnInfo; +import nc.ird.malariaplantdb.service.xls.infos.SheetInfo; +import net.sf.jxls.reader.XLSReadMessage; + +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * Error which occurs on a cell content during the importation process + *

    + * Contains an errors message, and the cell coordinate + * + * @author acheype + */ +@Slf4j +@Getter +@Setter +@ToString +public class CellError { + + public CellError(String message, Exception sourceException) { + this.message = message; + this.sourceExceptionDetails = String.format("%s - %s", + sourceException.getClass().getSimpleName(), + sourceException.getMessage() + ); + } + + public CellError(String message, String sheet, Integer line, String column) { + this.message = message; + this.sheet = sheet; + this.line = line; + this.column = column; + } + + public CellError(String message, String sheet, Integer line, String column, Exception sourceException) { + this(message, sourceException); + this.sheet = sheet; + this.line = line; + this.column = column; + } + + private String message; + + private String sheet; + + private Integer line; + + private String column; + + private String sourceExceptionDetails; + + public static CellError buildFromReadMessage(List sheetsInfo, XLSReadMessage readMessage) { + + Pattern pattern = Pattern.compile("^Can\'t read cell (\\D+)(\\d+) on (.*) spreadsheet$"); + Matcher matcher = pattern.matcher(readMessage.getMessage()); + + if (matcher.find()) { + try { + String sheet = matcher.group(3); + Integer line = Integer.parseInt(matcher.group(2)); + + String columnLetter = matcher.group(1); + + ColumnInfo columnInfo = sheetsInfo.stream() + .filter(si -> si.getSheetLabel().equals(sheet)) + .flatMap(si -> si.getColumnInfos().stream()) + .filter(fi -> fi.getColumnLetterRef().equals(columnLetter)) + .findFirst().orElse(null); + + if (columnInfo == null) { + throw new ImportException(String.format("Impossible to find the columnLetter " + + "'%s' in the FieldInfo objects of the sheet '%s'", columnLetter, sheet)); + } + + return new CellError("Error by reading the cell value. Please verify the value has the expected " + + "format.", + sheet, + line, + columnInfo.getColumnLabel(), + readMessage.getException()); + } catch (IllegalStateException | IndexOutOfBoundsException | NumberFormatException | ImportException e) { + log.warn("Impossible to parse the cell coordinate in the errors message", e); + return new CellError(readMessage.getMessage(), readMessage.getException()); + } + } else + return new CellError(readMessage.getMessage(), readMessage.getException()); + } + +} diff --git a/src/main/java/nc/ird/malariaplantdb/service/xls/structures/ClassMap.java b/src/main/java/nc/ird/malariaplantdb/service/xls/structures/ClassMap.java new file mode 100644 index 0000000..511da9b --- /dev/null +++ b/src/main/java/nc/ird/malariaplantdb/service/xls/structures/ClassMap.java @@ -0,0 +1,32 @@ +package nc.ird.malariaplantdb.service.xls.structures; + +import lombok.NonNull; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Give an access to the list of the class specified + * + * @author acheype + */ +public class ClassMap { + + private Map, List> objLists = new HashMap<>(); + + @SuppressWarnings("unchecked") + public void putList(@NonNull Class clazz, @NonNull List instance) { + objLists.put(clazz, (List) instance); + } + + @SuppressWarnings("unchecked") + public List getList(@NonNull Class clazz) { + return (List) objLists.get(clazz); + } + + public int size() { + return objLists.size(); + } + +} diff --git a/src/main/java/nc/ird/malariaplantdb/service/xls/structures/ImportStatus.java b/src/main/java/nc/ird/malariaplantdb/service/xls/structures/ImportStatus.java new file mode 100644 index 0000000..c0fda2b --- /dev/null +++ b/src/main/java/nc/ird/malariaplantdb/service/xls/structures/ImportStatus.java @@ -0,0 +1,34 @@ +package nc.ird.malariaplantdb.service.xls.structures; + +import lombok.Getter; +import lombok.NonNull; +import lombok.ToString; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +/** + * Importation process status + * + * @author acheype + */ +@Getter +@ToString +public class ImportStatus { + + private List readErrors = new ArrayList<>(); + + private List businessErrors = new ArrayList<>(); + + private List integrityErrors = new ArrayList<>(); + + public List getReadErrorsBySheet(@NonNull String sheet) { + return readErrors.stream().filter(e -> sheet.equals(e.getSheet())).collect(Collectors.toList()); + } + + public boolean isStatusOK() { + return readErrors.isEmpty() && businessErrors.isEmpty() && integrityErrors.isEmpty(); + } + +} diff --git a/src/main/java/nc/ird/malariaplantdb/service/xls/structures/PropVals.java b/src/main/java/nc/ird/malariaplantdb/service/xls/structures/PropVals.java new file mode 100644 index 0000000..f9ca2dd --- /dev/null +++ b/src/main/java/nc/ird/malariaplantdb/service/xls/structures/PropVals.java @@ -0,0 +1,50 @@ +package nc.ird.malariaplantdb.service.xls.structures; + +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.Map; + +/** + *

    Ordered list of the tuple (Property name, Value)

    + *

    Two{@code PropVals} objects are equals when they have the same values in the same order (independently of the key + * value).

    + * + * @author acheype + */ +public class PropVals extends LinkedHashMap{ + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + PropVals oPropVals = (PropVals) o; + if (oPropVals.size() != this.size()) + return false; + + Iterator> thisEntriesIt = this.entrySet().iterator(); + Iterator> oEntriesIt = oPropVals.entrySet().iterator(); + while (thisEntriesIt.hasNext() && oEntriesIt.hasNext()) { + + Map.Entry thisCurEntry = thisEntriesIt.next(); + Map.Entry oCurEntry = oEntriesIt.next(); + + // code removed because the normalization is applied only fewer cases and can be implemented with a + // specific EqualsStrategy class. +// // During the comparison, if both values are Strings, a {@code trim()} and a {@code toLowerCase()} will be +// // applied to compare them. +// if (oCurEntry.getValue() instanceof String && thisCurEntry.getValue() instanceof String) { +// String oStrVal = (String) oCurEntry.getValue(); +// String thisStrVal = (String) thisCurEntry.getValue(); +// if (!oStrVal.trim().toLowerCase().equals(thisStrVal.trim().toLowerCase())) +// return false; +// } else + if (!oCurEntry.getValue().equals(thisCurEntry.getValue())) + return false; + } + return true; + } +} diff --git a/src/main/java/nc/ird/malariaplantdb/service/xls/transformers/CompilersStrTransformer.java b/src/main/java/nc/ird/malariaplantdb/service/xls/transformers/CompilersStrTransformer.java new file mode 100644 index 0000000..1cd3f40 --- /dev/null +++ b/src/main/java/nc/ird/malariaplantdb/service/xls/transformers/CompilersStrTransformer.java @@ -0,0 +1,61 @@ +package nc.ird.malariaplantdb.service.xls.transformers; + +import nc.ird.malariaplantdb.service.xls.structures.PropVals; +import org.apache.commons.collections.Transformer; + +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + *

    Transformer which takes a {@code PropVals} object with a string which corresponds to a list of compilers + * identified with its family and given names and returns a list PropVals which contains for each compiler a element + * with family and given names

    + * + *

    The compiler string format is the followed : each compiler name with the last name first, a coma (,) then the + * given name. For several authors, each compiler complete name must be separated by a slash (/). The format is space + * insensitive. For instance, this string is correct : "Bourdy, Genevieve / Deharo, Eric" + */ +public class CompilersStrTransformer implements Transformer { + + static final private String COMPILERS_REGEXP = "([a-zA-ZÀ-ÿ &\\-]+),([a-zA-ZÀ-ÿ \\-]+)/?"; + + @Override + public Object transform(Object o) { + if (!(o instanceof PropVals)) { + throw new IllegalArgumentException(String.format("The CompilersStrTransformer transformer need a " + + "PropVals object as input. It gots this object instead : '%s'", o)); + } else { + + List resultPropValsList = new ArrayList<>(); + + PropVals dtoPropVals = (PropVals) o; + + Pattern pattern = Pattern.compile(COMPILERS_REGEXP); + String compilersStr = (String) dtoPropVals.get("compilers"); + Matcher matcher = pattern.matcher(compilersStr); + + while (matcher.find()) { + PropVals propVals = new PropVals(); + String family = matcher.group(1); + String given = matcher.group(2); + + // use a point then an ID because for errors, it will take the part until the point to have a link to + // the column name + Transformer transformer = new StringNormalizer(); + propVals.put("compilers.family", transformer.transform(family)); + propVals.put("compilers.given", transformer.transform(given)); + resultPropValsList.add(propVals); + } + + if (resultPropValsList.size() < 1) { + throw new IllegalArgumentException(String.format("The followed 'compilers' field is not correctly " + + "formatted : '%s'\n", + dtoPropVals.get("compilers"))); + } + + return resultPropValsList; + } + } +} diff --git a/src/main/java/nc/ird/malariaplantdb/service/xls/transformers/EntitiesTransformer.java b/src/main/java/nc/ird/malariaplantdb/service/xls/transformers/EntitiesTransformer.java new file mode 100644 index 0000000..1be9090 --- /dev/null +++ b/src/main/java/nc/ird/malariaplantdb/service/xls/transformers/EntitiesTransformer.java @@ -0,0 +1,18 @@ +package nc.ird.malariaplantdb.service.xls.transformers; + +import nc.ird.malariaplantdb.service.xls.structures.ClassMap; + +/** + * Abstract class for transformers which modifies the entities after a loading or a filling on a property. + */ +public abstract class EntitiesTransformer { + + /** + * Process executed on the entities. It could be a transformation of some properties or an object referencing + * in the entities map. + * + * @param entitiesMap the entities map to process + */ + public abstract void transformEntities(ClassMap entitiesMap); + +} diff --git a/src/main/java/nc/ird/malariaplantdb/service/xls/transformers/NOPEntitiesTransformer.java b/src/main/java/nc/ird/malariaplantdb/service/xls/transformers/NOPEntitiesTransformer.java new file mode 100644 index 0000000..f99f396 --- /dev/null +++ b/src/main/java/nc/ird/malariaplantdb/service/xls/transformers/NOPEntitiesTransformer.java @@ -0,0 +1,14 @@ +package nc.ird.malariaplantdb.service.xls.transformers; + +import nc.ird.malariaplantdb.service.xls.structures.ClassMap; + +/** + * Entities transformer which doesn't modify the entities map. + */ +public class NOPEntitiesTransformer extends EntitiesTransformer { + + @Override + public void transformEntities(ClassMap entitiesMap) { + // no processing + } +} diff --git a/src/main/java/nc/ird/malariaplantdb/service/xls/transformers/PlantIngredientsStrTransformer.java b/src/main/java/nc/ird/malariaplantdb/service/xls/transformers/PlantIngredientsStrTransformer.java new file mode 100644 index 0000000..34fff8a --- /dev/null +++ b/src/main/java/nc/ird/malariaplantdb/service/xls/transformers/PlantIngredientsStrTransformer.java @@ -0,0 +1,62 @@ +package nc.ird.malariaplantdb.service.xls.transformers; + +import nc.ird.malariaplantdb.service.xls.structures.PropVals; +import org.apache.commons.collections.Transformer; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + *

    Transformer which takes a {@code PropVals} object with a publication title and a string which corresponds to a + * list of plant ingredients (until 10) and returns a list of PropVals with an unique element which contains the + * publication title and 10 pairs of (species, plantPartUsed)

    + * + *

    The string format for the plant ingredients species is the followed : each species of the ingredient, a coma then + * its part used. For several ingredients, each ingredient complete name must be separated by a slash (/). The format is + * space insensitive. For instance, this string is correct : "Cochlospermum planchonii Hook.f. ex Planch., Root / + * Senna alata (L.) Roxb., Leaf"

    + */ +public class PlantIngredientsStrTransformer implements Transformer { + + static final private String PLANT_INGREDIENTS_REGEXP = "([a-zA-ZÀ-ÿ &\\.\\-\\(\\)]+),([a-zA-ZÀ-ÿ \\-]+)/?"; + + @Override + public Object transform(Object o) { + if (!(o instanceof PropVals)) { + throw new IllegalArgumentException(String.format("The PlantIngredientsStrTransformer transformer need a " + + "PropVals object as input. It gots this object instead : '%s'", o)); + } else { + PropVals resultPropVals = new PropVals(); + + PropVals dtoPropVals = (PropVals) o; + resultPropVals.put("publication", dtoPropVals.get("publication")); + + Pattern pattern = Pattern.compile(PLANT_INGREDIENTS_REGEXP); + String plantIngredientStr = (String) dtoPropVals.get("plantIngredients"); + Matcher matcher = pattern.matcher(plantIngredientStr); + + int i = 1; + while (matcher.find()) { + String species = matcher.group(1); + String partUsed = matcher.group(2); + + // use a point then an ID because for errors, it will take the part until the point to have a link to + // the column name + Transformer transformer = new StringNormalizer(); + resultPropVals.put("sp" + i, transformer.transform(species)); + resultPropVals.put("part" + i, transformer.transform(partUsed)); + i++; + } + + if (resultPropVals.size() <= 1) { + throw new IllegalArgumentException(String.format("The followed 'PlantIngredients' field is not correctly " + + "formatted : '%s'\n", + dtoPropVals.get("plantIngredients"))); + } + + return new ArrayList(Arrays.asList(resultPropVals)); + } + } +} diff --git a/src/main/java/nc/ird/malariaplantdb/service/xls/transformers/PlantIngredientsTempToSet.java b/src/main/java/nc/ird/malariaplantdb/service/xls/transformers/PlantIngredientsTempToSet.java new file mode 100644 index 0000000..664e6c8 --- /dev/null +++ b/src/main/java/nc/ird/malariaplantdb/service/xls/transformers/PlantIngredientsTempToSet.java @@ -0,0 +1,46 @@ +package nc.ird.malariaplantdb.service.xls.transformers; + +import nc.ird.malariaplantdb.domain.PlantIngredient; +import nc.ird.malariaplantdb.service.xls.dto.PlantIngredientsTemp; +import org.apache.commons.collections.Transformer; + +import java.util.Set; +import java.util.TreeSet; + +/** + * Transformer which take a{@code PlantIngredients} and return a list of {@code PlantIngredient} + */ +public class PlantIngredientsTempToSet implements Transformer { + + @Override + public Object transform(Object o) { + + if (!(o instanceof PlantIngredientsTemp)) { + throw new IllegalArgumentException(String.format("The PlantIngredientsTempToList transformer need a " + + "PlantIngredientsTemp object as input. It received this object instead : '%s'", o)); + } else { + PlantIngredientsTemp plantsIngredients = (PlantIngredientsTemp) o; + + Set piSet = new TreeSet<>(); + addPlantIngredient(piSet, plantsIngredients.getPlantIngredient1()); + addPlantIngredient(piSet, plantsIngredients.getPlantIngredient2()); + addPlantIngredient(piSet, plantsIngredients.getPlantIngredient3()); + addPlantIngredient(piSet, plantsIngredients.getPlantIngredient4()); + addPlantIngredient(piSet, plantsIngredients.getPlantIngredient5()); + addPlantIngredient(piSet, plantsIngredients.getPlantIngredient6()); + addPlantIngredient(piSet, plantsIngredients.getPlantIngredient7()); + addPlantIngredient(piSet, plantsIngredients.getPlantIngredient8()); + addPlantIngredient(piSet, plantsIngredients.getPlantIngredient9()); + addPlantIngredient(piSet, plantsIngredients.getPlantIngredient10()); + + return piSet; + } + } + + private void addPlantIngredient(Set piSet, PlantIngredient plantIngredient){ + if (plantIngredient != null && plantIngredient.getSpecies() != null & plantIngredient.getPartUsed() != null){ + piSet.add(plantIngredient); + } + } + +} diff --git a/src/main/java/nc/ird/malariaplantdb/service/xls/transformers/PubSpeciesToSpecies.java b/src/main/java/nc/ird/malariaplantdb/service/xls/transformers/PubSpeciesToSpecies.java new file mode 100644 index 0000000..03622ef --- /dev/null +++ b/src/main/java/nc/ird/malariaplantdb/service/xls/transformers/PubSpeciesToSpecies.java @@ -0,0 +1,21 @@ +package nc.ird.malariaplantdb.service.xls.transformers; + +import nc.ird.malariaplantdb.domain.PubSpecies; +import org.apache.commons.collections.Transformer; + +/** + * Transformer which take a{@code PubSpecies} and return a{@code Species} + */ +public class PubSpeciesToSpecies implements Transformer { + + @Override + public Object transform(Object input) { + if (!(input instanceof PubSpecies)) { + throw new IllegalArgumentException(String.format("The PubSpeciesToSpecies transformer need a " + + "PubSpecies object as input. It received this object instead : '%s'", input)); + } else { + PubSpecies pubSpecies = (PubSpecies) input; + return pubSpecies.getSpecies(); + } + } +} diff --git a/src/main/java/nc/ird/malariaplantdb/service/xls/transformers/RemedyEntitiesTransformer.java b/src/main/java/nc/ird/malariaplantdb/service/xls/transformers/RemedyEntitiesTransformer.java new file mode 100644 index 0000000..f35d606 --- /dev/null +++ b/src/main/java/nc/ird/malariaplantdb/service/xls/transformers/RemedyEntitiesTransformer.java @@ -0,0 +1,59 @@ +package nc.ird.malariaplantdb.service.xls.transformers; + +import nc.ird.malariaplantdb.domain.PlantIngredient; +import nc.ird.malariaplantdb.domain.Remedy; +import nc.ird.malariaplantdb.service.xls.dto.PlantIngredientsTemp; +import nc.ird.malariaplantdb.service.xls.exceptions.ImportRuntimeException; +import nc.ird.malariaplantdb.service.xls.structures.ClassMap; +import org.apache.commons.beanutils.PropertyUtils; + +import java.lang.reflect.InvocationTargetException; +import java.util.ArrayList; +import java.util.List; +import java.util.TreeSet; + +/** + * Special process to transform the PlantIngredientsTemp in Remedy and refer them in the entities map. + */ +public class RemedyEntitiesTransformer extends EntitiesTransformer { + + static final private String[] PLANT_INGREDIENTS_PROPERTIES = {"plantIngredient1", "plantIngredient2", "plantIngredient3", + "plantIngredient4", "plantIngredient5", "plantIngredient6", "plantIngredient7", "plantIngredient8", + "plantIngredient9", "plantIngredient10"}; + + @Override + public void transformEntities(ClassMap entitiesMap) { + List plantIngredientsTemp = entitiesMap.getList(PlantIngredientsTemp.class); + + TreeSet plantIngredients = buildPlantIngredientsSet(plantIngredientsTemp); + entitiesMap.putList(PlantIngredient.class, new ArrayList(plantIngredients)); + + ArrayList remedies = new ArrayList<>(); + for (PlantIngredient pi : plantIngredients){ + Remedy remedy = new Remedy(); + remedy.setPlantIngredients(plantIngredients); + remedies.add(remedy); + } + entitiesMap.putList(Remedy.class, remedies); + } + + private TreeSet buildPlantIngredientsSet(List plantIngredientsTemp) { + TreeSet plantIngredientsSet = new TreeSet<>(); + for (PlantIngredientsTemp plantIngredients : plantIngredientsTemp) { + for (int i = 0; i < 10; i++) { + try { + PlantIngredient plantIngredient = (PlantIngredient) PropertyUtils.getProperty(plantIngredients, + PLANT_INGREDIENTS_PROPERTIES[i]); + if (plantIngredient.getSpecies() != null && plantIngredient.getPartUsed() != null) + plantIngredientsSet.add(plantIngredient); + } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException | + IllegalArgumentException e) { + throw new ImportRuntimeException("An unexpected errors occurs during saving the entities " + + "in the database", e); + } + } + } + return plantIngredientsSet; + } + +} diff --git a/src/main/java/nc/ird/malariaplantdb/service/xls/transformers/SpeciesEntitiesTransformer.java b/src/main/java/nc/ird/malariaplantdb/service/xls/transformers/SpeciesEntitiesTransformer.java new file mode 100644 index 0000000..d4d347e --- /dev/null +++ b/src/main/java/nc/ird/malariaplantdb/service/xls/transformers/SpeciesEntitiesTransformer.java @@ -0,0 +1,49 @@ +package nc.ird.malariaplantdb.service.xls.transformers; + +import nc.ird.malariaplantdb.domain.PubSpecies; +import nc.ird.malariaplantdb.domain.Species; +import nc.ird.malariaplantdb.repository.SpeciesRepository; +import nc.ird.malariaplantdb.service.xls.structures.ClassMap; +import org.springframework.beans.BeansException; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.stereotype.Service; + +import java.util.*; + +/** + * Special process to reference in the entities Map (species are parts of PubSpecies but need to be referenced by + * other entities). If the species already exists in the database, the reference will be used. + */ +@Service +public class SpeciesEntitiesTransformer extends EntitiesTransformer implements ApplicationContextAware { + + // by implementing ApplicationContextAware, the Spring application context is supplied + private static ApplicationContext applicationContext; + + @Override + public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { + this.applicationContext = applicationContext; + } + + @Override + public void transformEntities(ClassMap entitiesMap) { + + List pubSpeciesEntities = entitiesMap.getList(PubSpecies.class); + + TreeSet speciesSet = new TreeSet<>(); + for (PubSpecies pubSp : pubSpeciesEntities){ + + Map speciesRepos = applicationContext.getBeansOfType(SpeciesRepository.class); + SpeciesRepository speciesRepo = speciesRepos.entrySet().iterator().next().getValue(); + + Optional alreadyDefinedSp = speciesRepo.findByFamilyAndSpeciesAllIgnoreCase(pubSp.getSpecies() + .getFamily(), pubSp.getSpecies().getSpecies()); + if (alreadyDefinedSp.isPresent()) + pubSp.setSpecies(alreadyDefinedSp.get()); + + speciesSet.add(pubSp.getSpecies()); + } + entitiesMap.putList(Species.class, new ArrayList(speciesSet)); + } +} diff --git a/src/main/java/nc/ird/malariaplantdb/service/xls/transformers/StrToAuthorsSet.java b/src/main/java/nc/ird/malariaplantdb/service/xls/transformers/StrToAuthorsSet.java new file mode 100644 index 0000000..3b28514 --- /dev/null +++ b/src/main/java/nc/ird/malariaplantdb/service/xls/transformers/StrToAuthorsSet.java @@ -0,0 +1,58 @@ +package nc.ird.malariaplantdb.service.xls.transformers; + +import nc.ird.malariaplantdb.domain.Author; +import org.apache.commons.collections.Transformer; + +import java.util.SortedSet; +import java.util.TreeSet; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * Transform a specific formatted string into a sorted set of Author objects + *

    + * The string format is the followed : each author name with the last name first, a coma (, + * ) then the given name initials (with comas). For several authors, each author complete + * name must be separated by a slash (/). The format is space insensitive. + * For instance, this string is correct : "Krettli, A.U. / Brandao, M.G.L. / Grandi, T.S.M./ + * Rocha, E.M.M. / Sawyer D.R." + */ +public class StrToAuthorsSet implements Transformer { + + static final private String AUTHORS_REGEXP = "([a-zA-ZÀ-ÿ \\-]+),([a-zA-ZÀ-ÿ \\-.]+)/?"; + + @Override + public Object transform(Object o) { + SortedSet authors = new TreeSet<>(); + int position = 1; + + if (!(o instanceof String)) { + throw new IllegalArgumentException(String.format("The followed 'Author(s)' field is not a string : " + + "'%s'", o)); + } else { + String authorsStr = (String) o; + Pattern pattern = Pattern.compile(AUTHORS_REGEXP); + Matcher matcher = pattern.matcher(authorsStr); + + while (matcher.find()) { + String lastName = matcher.group(1); + String givenName = matcher.group(2); + + Author author = new Author(); + author.setFamily(StringNormalizer.getInstance().transform(lastName)); + author.setGiven(StringNormalizer.getInstance().transform(givenName)); + author.setPosition(position); + authors.add(author); + + position++; + } + + if (authors.isEmpty()) { + throw new IllegalArgumentException(String.format("The followed 'Author(s)' field is not correctly " + + "formatted : '%s'\n", authorsStr)); + } + + } + return authors; + } +} diff --git a/src/main/java/nc/ird/malariaplantdb/service/xls/transformers/StringNormalizer.java b/src/main/java/nc/ird/malariaplantdb/service/xls/transformers/StringNormalizer.java new file mode 100644 index 0000000..e3d01ce --- /dev/null +++ b/src/main/java/nc/ird/malariaplantdb/service/xls/transformers/StringNormalizer.java @@ -0,0 +1,23 @@ +package nc.ird.malariaplantdb.service.xls.transformers; + +import org.apache.commons.collections.Transformer; + +/** + * Normalize a specified string by removing beginning and ending spaces (trim) and replacing consecutive whitespaces + * (space, tab, new line, etc.) by a single space + */ +public class StringNormalizer implements Transformer { + + public static final StringNormalizer INSTANCE = new StringNormalizer(); + + @Override + public String transform(Object input) { + if (input == null) + return null; + return input.toString().trim().replaceAll("\\s+", " "); + } + + public static StringNormalizer getInstance() { + return INSTANCE; + } +} diff --git a/src/main/java/nc/ird/malariaplantdb/service/xls/validators/EmptyOrNotIfPropertyValue.java b/src/main/java/nc/ird/malariaplantdb/service/xls/validators/EmptyOrNotIfPropertyValue.java new file mode 100644 index 0000000..788b264 --- /dev/null +++ b/src/main/java/nc/ird/malariaplantdb/service/xls/validators/EmptyOrNotIfPropertyValue.java @@ -0,0 +1,62 @@ +package nc.ird.malariaplantdb.service.xls.validators; + +import javax.validation.Constraint; +import javax.validation.Payload; +import java.lang.annotation.*; + +/** + *

    If a criteria property has its value in the range of a set of criteria values, + * check if a tested property empty or not (must be empty or null if the property {@code isEmpty}' is true, + * must be not empty if false). The tested property can be a string, a collection, + * a map or an array).

    + *

    + *

    If the criteria is not fulfilled, the validators will succeed.

    + * + * @author acheype + */ +@Documented +@Constraint(validatedBy = {EmptyOrNotIfPropertyValueValidator.class}) +@Target({ElementType.TYPE, ElementType.ANNOTATION_TYPE}) +@Retention(RetentionPolicy.RUNTIME) +public @interface EmptyOrNotIfPropertyValue { + + String message() default "As the '{criteriaProperty}' property is in the '{criteriaValues}' range, " + + "'{testedProperty}' property must fulfill the condition isNullorEmpty = {isEmpty}"; + + Class[] groups() default {}; + + Class[] payload() default {}; + + /** + * @return the property name for which its value will be compared + */ + String criteriaProperty(); + + /** + * @return the values to compare to the criteria properties + */ + String[] criteriaValues(); + + /** + * @return the property which will be tested + */ + String testedProperty(); + + /** + * Define if the tested property must be empty or not + */ + boolean isEmpty(); + + /** + * Defines several @NotEmptyIfPropertyValue annotations on the same element + * + * @see EmptyOrNotIfPropertyValue + */ + @Target({ElementType.TYPE, ElementType.ANNOTATION_TYPE}) + @Retention(RetentionPolicy.RUNTIME) + @Documented + @interface List { + EmptyOrNotIfPropertyValue[] value(); + } + +} diff --git a/src/main/java/nc/ird/malariaplantdb/service/xls/validators/EmptyOrNotIfPropertyValueValidator.java b/src/main/java/nc/ird/malariaplantdb/service/xls/validators/EmptyOrNotIfPropertyValueValidator.java new file mode 100644 index 0000000..39e957a --- /dev/null +++ b/src/main/java/nc/ird/malariaplantdb/service/xls/validators/EmptyOrNotIfPropertyValueValidator.java @@ -0,0 +1,105 @@ +package nc.ird.malariaplantdb.service.xls.validators; + +import nc.ird.malariaplantdb.service.xls.exceptions.ImportRuntimeException; +import org.apache.commons.beanutils.PropertyUtils; + +import javax.validation.ConstraintValidator; +import javax.validation.ConstraintValidatorContext; +import java.lang.reflect.Array; +import java.lang.reflect.InvocationTargetException; +import java.util.Arrays; +import java.util.Collection; +import java.util.Map; + +/** + *

    If a criteria property has its value in the range of a set of criteria values, + * check if a tested property empty or not (must be empty or null if the property {@code isEmpty}' is true, + * must be not empty if false). The tested property can be a string, a collection, + * a map or an array).

    + *

    + *

    If the criteria is not fulfilled, the validators will succeed.

    + * + * @author acheype + */ +public class EmptyOrNotIfPropertyValueValidator implements ConstraintValidator { + + /** + * The property name for which its value will be compared + */ + private String criteriaProperty; + + /** + * The values to compare to the criteria properties. + */ + private String[] criteriaValues; + + /** + * The property which will be tested + */ + private String testedProperty; + + /** + * Define if the tested property must be empty or not + */ + private boolean isEmpty; + + /** + * The errors message; + */ + String message; + + @Override + public void initialize(EmptyOrNotIfPropertyValue constraintAnnotation) { + criteriaProperty = constraintAnnotation.criteriaProperty(); + criteriaValues = constraintAnnotation.criteriaValues(); + testedProperty = constraintAnnotation.testedProperty(); + isEmpty = constraintAnnotation.isEmpty(); + message = constraintAnnotation.message(); + } + + @Override + public boolean isValid(Object value, ConstraintValidatorContext context) { + Boolean result; + + Object criteriaVal; + Object testedVal; + + try { + criteriaVal = PropertyUtils.getProperty(value, criteriaProperty); + testedVal = PropertyUtils.getProperty(value, testedProperty); + } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) { + throw new ImportRuntimeException("An errors occurs during the bean validation. Please check " + + "the parameters of the corresponding annotation.", e); + } + + if (criteriaVal != null && !criteriaVal.toString().isEmpty()) { + boolean criteriaOK = Arrays.asList(criteriaValues).contains(criteriaVal.toString()); + if (!criteriaOK) + result = true; + else + result = isEmpty ? isEmpty(testedVal) : !isEmpty(testedVal); + } else + result = true; + + // binds the errors message to the tested property + context.disableDefaultConstraintViolation(); + context.buildConstraintViolationWithTemplate(message).addPropertyNode(testedProperty) + .addConstraintViolation(); + + return result; + } + + private boolean isEmpty(Object value) { + if (value == null) return true; + if (value.getClass().isArray()) { + return Array.getLength(value) == 0; + } else if (value instanceof Collection) { + return ((Collection) value).size() == 0; + } else if (value instanceof Map) { + return ((Map) value).size() == 0; + } else { + return ((String) value).length() == 0; + } + } + +} diff --git a/src/main/java/nc/ird/malariaplantdb/web/rest/AuditResource.java b/src/main/java/nc/ird/malariaplantdb/web/rest/AuditResource.java index 7901c99..9b9191f 100644 --- a/src/main/java/nc/ird/malariaplantdb/web/rest/AuditResource.java +++ b/src/main/java/nc/ird/malariaplantdb/web/rest/AuditResource.java @@ -1,16 +1,15 @@ package nc.ird.malariaplantdb.web.rest; import nc.ird.malariaplantdb.service.AuditEventService; - -import java.time.LocalDate; import org.springframework.boot.actuate.audit.AuditEvent; import org.springframework.format.annotation.DateTimeFormat; -import org.springframework.http.MediaType; -import org.springframework.web.bind.annotation.*; import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; import javax.inject.Inject; +import java.time.LocalDate; import java.util.List; /** diff --git a/src/main/java/nc/ird/malariaplantdb/web/rest/ImportResource.java b/src/main/java/nc/ird/malariaplantdb/web/rest/ImportResource.java new file mode 100644 index 0000000..fce7905 --- /dev/null +++ b/src/main/java/nc/ird/malariaplantdb/web/rest/ImportResource.java @@ -0,0 +1,100 @@ +package nc.ird.malariaplantdb.web.rest; + +import nc.ird.malariaplantdb.domain.*; +import nc.ird.malariaplantdb.repository.*; +import nc.ird.malariaplantdb.service.xls.ExcelETL; +import nc.ird.malariaplantdb.service.xls.exceptions.ImportException; +import nc.ird.malariaplantdb.service.xls.structures.ClassMap; +import nc.ird.malariaplantdb.service.xls.structures.ImportStatus; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.http.MediaType; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RestController; + +import javax.inject.Inject; +import java.io.BufferedInputStream; +import java.io.InputStream; +import java.util.List; + +/** + * Service class for the entities importation process. + *

    + * Some operations are directly exposes as a Rest Web Services via the JAX-RS specification. + * + * @author acheype + */ +@RestController +@RequestMapping("/api") +public class ImportResource { + + private final Logger log = LoggerFactory.getLogger(ImportResource.class); + + @Inject + private PublicationRepository publiRepo; + + @Inject + private PubSpeciesRepository pubSpeciesRepo; + + @Inject + private SpeciesRepository speciesRepo; + + @Inject + private PlantIngredientRepository plantIngredientRepo; + + @Inject + private RemedyRepository remedyRepo; + + @Inject + private EthnologyRepository ethnologyRepo; + + @Transactional + @RequestMapping(value = "/import", + method = RequestMethod.GET, + produces = MediaType.APPLICATION_JSON_VALUE) + public ImportStatus importEntitiesFromXls() throws ImportException { + ExcelETL etl = new ExcelETL("nc.ird.malariaplantdb.service.xls.dto", "/xls/publi_mapping.xml"); + + InputStream excelInput = new BufferedInputStream(getClass().getResourceAsStream("/xls/publi_test.xlsm")); +// try { + etl.startImportProcess(excelInput); +// } catch (ImportException e) { +// log.errors("Exception raised during the XLS importation", e); +// return Response.status(404).build(); +// } + + if (etl.getImportStatus().isStatusOK()) { + persistEntities(etl.getEntitiesMap()); + } + + return etl.getImportStatus(); + } + + private void persistEntities(ClassMap entities) { + + List publications = entities.getList(Publication.class); + for (Publication pub : publications) { + for (Author author : pub.getAuthors()) + author.setPublication(pub); + publiRepo.save(pub); + } + + List species = entities.getList(Species.class); + species.forEach(speciesRepo::save); + + List pubSpecies = entities.getList(PubSpecies.class); + pubSpecies.forEach(pubSpeciesRepo::save); + + List plantIngredients = entities.getList(PlantIngredient.class); + plantIngredients.forEach(plantIngredientRepo::save); + + List remedies = entities.getList(Remedy.class); + remedies.forEach(remedyRepo::save); + + List ethnoNotes = entities.getList(Ethnology.class); + ethnoNotes.forEach(ethnologyRepo::save); + } + +} diff --git a/src/main/java/nc/ird/malariaplantdb/web/rest/LogsResource.java b/src/main/java/nc/ird/malariaplantdb/web/rest/LogsResource.java index 4e92252..c96f9e7 100644 --- a/src/main/java/nc/ird/malariaplantdb/web/rest/LogsResource.java +++ b/src/main/java/nc/ird/malariaplantdb/web/rest/LogsResource.java @@ -1,10 +1,9 @@ package nc.ird.malariaplantdb.web.rest; -import nc.ird.malariaplantdb.web.rest.dto.LoggerDTO; - import ch.qos.logback.classic.Level; import ch.qos.logback.classic.LoggerContext; import com.codahale.metrics.annotation.Timed; +import nc.ird.malariaplantdb.web.rest.dto.LoggerDTO; import org.slf4j.LoggerFactory; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; diff --git a/src/main/java/nc/ird/malariaplantdb/web/rest/UserResource.java b/src/main/java/nc/ird/malariaplantdb/web/rest/UserResource.java index be16aa2..633c617 100644 --- a/src/main/java/nc/ird/malariaplantdb/web/rest/UserResource.java +++ b/src/main/java/nc/ird/malariaplantdb/web/rest/UserResource.java @@ -9,7 +9,6 @@ import nc.ird.malariaplantdb.security.AuthoritiesConstants; import nc.ird.malariaplantdb.service.UserService; import nc.ird.malariaplantdb.web.rest.dto.ManagedUserDTO; -import nc.ird.malariaplantdb.web.rest.dto.UserDTO; import nc.ird.malariaplantdb.web.rest.util.HeaderUtil; import nc.ird.malariaplantdb.web.rest.util.PaginationUtil; import org.slf4j.Logger; @@ -27,11 +26,13 @@ import javax.inject.Inject; import java.net.URI; import java.net.URISyntaxException; -import java.util.*; +import java.util.List; +import java.util.Optional; +import java.util.Set; import java.util.stream.Collectors; import java.util.stream.StreamSupport; -import static org.elasticsearch.index.query.QueryBuilders.*; +import static org.elasticsearch.index.query.QueryBuilders.queryStringQuery; /** * REST controller for managing users. diff --git a/src/main/java/nc/ird/malariaplantdb/web/rest/UserXAuthTokenController.java b/src/main/java/nc/ird/malariaplantdb/web/rest/UserXAuthTokenController.java index e15fe80..97657d6 100644 --- a/src/main/java/nc/ird/malariaplantdb/web/rest/UserXAuthTokenController.java +++ b/src/main/java/nc/ird/malariaplantdb/web/rest/UserXAuthTokenController.java @@ -15,9 +15,6 @@ import org.springframework.web.bind.annotation.RestController; import javax.inject.Inject; -import java.util.HashMap; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; @RestController @RequestMapping("/api") diff --git a/src/main/java/nc/ird/malariaplantdb/web/rest/dto/ManagedUserDTO.java b/src/main/java/nc/ird/malariaplantdb/web/rest/dto/ManagedUserDTO.java index 760e814..80fa32d 100644 --- a/src/main/java/nc/ird/malariaplantdb/web/rest/dto/ManagedUserDTO.java +++ b/src/main/java/nc/ird/malariaplantdb/web/rest/dto/ManagedUserDTO.java @@ -1,9 +1,9 @@ package nc.ird.malariaplantdb.web.rest.dto; -import java.time.ZonedDateTime; - import nc.ird.malariaplantdb.domain.User; +import java.time.ZonedDateTime; + /** * A DTO extending the UserDTO, which is meant to be used in the user management UI. */ diff --git a/src/main/java/nc/ird/malariaplantdb/web/rest/dto/UserDTO.java b/src/main/java/nc/ird/malariaplantdb/web/rest/dto/UserDTO.java index bed79a1..559631a 100644 --- a/src/main/java/nc/ird/malariaplantdb/web/rest/dto/UserDTO.java +++ b/src/main/java/nc/ird/malariaplantdb/web/rest/dto/UserDTO.java @@ -2,10 +2,11 @@ import nc.ird.malariaplantdb.domain.Authority; import nc.ird.malariaplantdb.domain.User; - import org.hibernate.validator.constraints.Email; -import javax.validation.constraints.*; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Pattern; +import javax.validation.constraints.Size; import java.util.Set; import java.util.stream.Collectors; /** diff --git a/src/main/java/nc/ird/malariaplantdb/web/rest/errors/ExceptionTranslator.java b/src/main/java/nc/ird/malariaplantdb/web/rest/errors/ExceptionTranslator.java index df55e7e..5118216 100644 --- a/src/main/java/nc/ird/malariaplantdb/web/rest/errors/ExceptionTranslator.java +++ b/src/main/java/nc/ird/malariaplantdb/web/rest/errors/ExceptionTranslator.java @@ -1,7 +1,5 @@ package nc.ird.malariaplantdb.web.rest.errors; -import java.util.List; - import org.springframework.dao.ConcurrencyFailureException; import org.springframework.http.HttpStatus; import org.springframework.security.access.AccessDeniedException; @@ -9,7 +7,12 @@ import org.springframework.validation.FieldError; import org.springframework.web.HttpRequestMethodNotSupportedException; import org.springframework.web.bind.MethodArgumentNotValidException; -import org.springframework.web.bind.annotation.*; +import org.springframework.web.bind.annotation.ControllerAdvice; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.ResponseStatus; + +import java.util.List; /** * Controller advice to translate the server side exceptions to client-friendly json structures. diff --git a/src/main/resources/config/liquibase/changelog/20151005000001_added_entity_Author.xml b/src/main/resources/config/liquibase/changelog/20151005000001_added_entity_Author.xml index 082a373..c75fb5d 100644 --- a/src/main/resources/config/liquibase/changelog/20151005000001_added_entity_Author.xml +++ b/src/main/resources/config/liquibase/changelog/20151005000001_added_entity_Author.xml @@ -29,6 +29,9 @@ + + + diff --git a/src/main/resources/config/liquibase/test_data/author.csv b/src/main/resources/config/liquibase/test_data/author.csv index 05fe495..367f9d6 100644 --- a/src/main/resources/config/liquibase/test_data/author.csv +++ b/src/main/resources/config/liquibase/test_data/author.csv @@ -1,15 +1,15 @@ -"PUBLICATION_ID","ID","FAMILY","GIVEN" -"1","1","Akuodor","G. C." -"1","2","Anyalewechi","N. A." -"1","3","Ikoro","N.C" -"1","4","Megwas","U. A." -"1","5","Iwuanyanwu","T. C." -"1","6","Osunkwo","U. A." -"2","7","Akuodor","G. C." -"2","8","Idris-Usman","M." -"2","9","Anyalewechi","N." -"2","10","Odo","E." -"2","11","Ugwu","C. T." -"2","12","Akpan","J. L." -"2","13","Gwotmut","M. D." -"2","14","Osunkwo","U. A." +"PUBLICATION_ID","ID","FAMILY","GIVEN","POSITION" +"1","1","Akuodor","G. C.","1" +"1","2","Anyalewechi","N. A.","2" +"1","3","Ikoro","N.C","3" +"1","4","Megwas","U. A.","4" +"1","5","Iwuanyanwu","T. C.","5" +"1","6","Osunkwo","U. A.","6" +"2","7","Akuodor","G. C.","1" +"2","8","Idris-Usman","M.","2" +"2","9","Anyalewechi","N.","3" +"2","10","Odo","E.","4" +"2","11","Ugwu","C. T.","5" +"2","12","Akpan","J. L.","6" +"2","13","Gwotmut","M. D.","7" +"2","14","Osunkwo","U. A.","8" diff --git a/src/main/resources/ehcache.xml b/src/main/resources/ehcache.xml index 4441cce..b31ea32 100644 --- a/src/main/resources/ehcache.xml +++ b/src/main/resources/ehcache.xml @@ -13,9 +13,9 @@ + eternal="false" + overflowToDisk="false" + /> diff --git a/src/main/resources/hibernate.cfg.xml b/src/main/resources/hibernate.cfg.xml index a7bb2a6..a252da1 100644 --- a/src/main/resources/hibernate.cfg.xml +++ b/src/main/resources/hibernate.cfg.xml @@ -3,12 +3,12 @@ "-//Hibernate/Hibernate Configuration DTD//EN" "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd"> - - - - - - - - - \ No newline at end of file + + + + + + + + + diff --git a/src/main/resources/logback-spring.xml b/src/main/resources/logback-spring.xml index df3c516..799c06e 100644 --- a/src/main/resources/logback-spring.xml +++ b/src/main/resources/logback-spring.xml @@ -8,24 +8,24 @@ - - + + + 512 + + + --> @@ -59,7 +59,9 @@ - + + true diff --git a/src/main/resources/xls/publi_mapping.xml b/src/main/resources/xls/publi_mapping.xml new file mode 100644 index 0000000..067b344 --- /dev/null +++ b/src/main/resources/xls/publi_mapping.xml @@ -0,0 +1,190 @@ + + + +

    +
    + +
    + pub.entryType + pub.authors + pub.year + pub.title + pub.journal + pub.pages + pub.volume + pub.nbOfVolumes + pub.number + pub.bookTitle + pub.publisher + pub.edition + pub.conferenceName + pub.conferencePlace + pub.university + pub.institution + pub.doi + pub.pmid + pub.isbn + pub.url + pub.compilers + pub.compilersNotes +
    + + + + + + + + +
    + + +
    +
    + +
    + species.publication + species.family + species.species + species.speciesNameInPub + species.isHerbariumVoucher + species.herbarium + species.country + species.continent +
    + + + + + + + + + +
    +
    + +
    +
    + +
    + plantIngredients.publication + plantIngredients.species1 + plantIngredients.partUsed1 + plantIngredients.species2 + plantIngredients.partUsed2 + plantIngredients.species3 + plantIngredients.partUsed3 + plantIngredients.species4 + plantIngredients.partUsed4 + plantIngredients.species5 + plantIngredients.partUsed5 + plantIngredients.species6 + plantIngredients.partUsed6 + plantIngredients.species7 + plantIngredients.partUsed7 + plantIngredients.species8 + plantIngredients.partUsed8 + plantIngredients.species9 + plantIngredients.partUsed9 + plantIngredients.species10 + plantIngredients.partUsed10 +
    + + + + + + + +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/xls/publi_test.xlsm b/src/main/resources/xls/publi_test.xlsm new file mode 100644 index 0000000..861d6df Binary files /dev/null and b/src/main/resources/xls/publi_test.xlsm differ diff --git a/src/main/resources/xls/publi_test_old.xlsm b/src/main/resources/xls/publi_test_old.xlsm new file mode 100644 index 0000000..093aa14 Binary files /dev/null and b/src/main/resources/xls/publi_test_old.xlsm differ diff --git a/src/main/resources/xls/~$publi_test.xlsm b/src/main/resources/xls/~$publi_test.xlsm new file mode 100644 index 0000000..215cdc7 Binary files /dev/null and b/src/main/resources/xls/~$publi_test.xlsm differ diff --git a/src/main/scss/_variables.scss b/src/main/scss/_variables.scss index bf50c03..bcaf1d5 100644 --- a/src/main/scss/_variables.scss +++ b/src/main/scss/_variables.scss @@ -3,17 +3,18 @@ $bootstrap-sass-asset-helper: false !default; // Variables // -------------------------------------------------- - //== Colors // //## Gray and brand colors for use across Bootstrap. -$gray-base: #000 !default; -$gray-darker: #222 !default; -$gray-dark: #34825e !default; -$gray: #40ff3b !default; -$gray-light: lighten($gray, 15%); // #b4bcc2 -$gray-lighter: lighten($gray-light, 20%) !default; // #ecf0f1 +$gray-base: #000 !default; +$gray-darker: #222 !default; +$gray-dark: #34825e !default; +$gray: #40ff3b !default; +$gray-light: lighten($gray, 15%); +// #b4bcc2 +$gray-lighter: lighten($gray-light, 20%) !default; +// #ecf0f1 $green: #7aba5a !default; $green-lighter: lighten($green, 15%); $green-super-lighter: #efe !default; @@ -22,7 +23,7 @@ $blue2: #3085bb !default$blue2-darker: darken($blue2, 10%) !default; $orange1: lighten(#F39C12, 12%) !default; $orange2: lighten($orange1, 5%) !default; $red1: #e74c3c !default; -$red2 : lighten($red1, 8%) !default; +$red2: lighten($red1, 8%) !default; $brand-primary: $green !default; //#88c14e;//#57a983;//#45c675; @@ -33,15 +34,15 @@ $brand-info: $blue2 !default; $brand-warning: $orange1 !default; $brand-danger: $red1 !default; - //== Scaffolding // //## Settings for some of the most global styles. //** Background color for ``. -$body-bg: $gray-lighter !default; +$body-bg: $gray-lighter !default; //** Global text color on ``. -$text-color: darken(#34825e, 10%) !default;//$brand-primary !default; +$text-color: darken(#34825e, 10%) !default; +//$brand-primary !default; //** Global textual link color. $link-color: $blue2 !default; @@ -51,40 +52,48 @@ $link-hover-color: $blue2-darker !default; //** Link hover decoration. $link-hover-decoration: underline !default; - //== Typography // //## Font, line-height, and color for body text, headings, and more. -$font-family-sans-serif: "Helvetica Neue", Helvetica, Arial, sans-serif !default; //"Lato" -$font-family-serif: Georgia, "Times New Roman", Times, serif !default; +$font-family-sans-serif: "Helvetica Neue", Helvetica, Arial, sans-serif !default; +//"Lato" +$font-family-serif: Georgia, "Times New Roman", Times, serif !default; //** Default monospace fonts for ``, ``, and `
    `.
    -$font-family-monospace:   Menlo, Monaco, Consolas, "Courier New", monospace !default;
    -$font-family-base:        $font-family-sans-serif !default;
    -
    -$font-size-base:          15px !default;
    -$font-size-large:         ceil(($font-size-base * 1.25)) !default; // ~18px
    -$font-size-small:         ceil(($font-size-base * 0.85)) !default; // ~12px
    -$font-size-smaller:       ceil(($font-size-base * 0.75)) !default;
    -
    -$font-size-h1:            floor(($font-size-base * 2.6)) !default; // ~36px
    -$font-size-h2:            floor(($font-size-base * 2.15)) !default; // ~30px
    -$font-size-h3:            ceil(($font-size-base * 1.7)) !default; // ~24px
    -$font-size-h4:            ceil(($font-size-base * 1.25)) !default; // ~18px
    -$font-size-h5:            $font-size-base !default;
    -$font-size-h6:            ceil(($font-size-base * 0.85)) !default; // ~12px
    +$font-family-monospace: Menlo, Monaco, Consolas, "Courier New", monospace !default;
    +$font-family-base: $font-family-sans-serif !default;
    +
    +$font-size-base: 15px !default;
    +$font-size-large: ceil(($font-size-base * 1.25)) !default;
    +// ~18px
    +$font-size-small: ceil(($font-size-base * 0.85)) !default;
    +// ~12px
    +$font-size-smaller: ceil(($font-size-base * 0.75)) !default;
    +
    +$font-size-h1: floor(($font-size-base * 2.6)) !default;
    +// ~36px
    +$font-size-h2: floor(($font-size-base * 2.15)) !default;
    +// ~30px
    +$font-size-h3: ceil(($font-size-base * 1.7)) !default;
    +// ~24px
    +$font-size-h4: ceil(($font-size-base * 1.25)) !default;
    +// ~18px
    +$font-size-h5: $font-size-base !default;
    +$font-size-h6: ceil(($font-size-base * 0.85)) !default;
    +// ~12px
     
     //** Unit-less `line-height` for use in components like buttons.
    -$line-height-base:        1.428571429 !default; // 20/14
    +$line-height-base: 1.428571429 !default;
    +// 20/14
     //** Computed "line-height" (`font-size` * `line-height`) for use with `margin`, `padding`, etc.
    -$line-height-computed:    floor(($font-size-base * $line-height-base)) !default; // ~20px
    +$line-height-computed: floor(($font-size-base * $line-height-base)) !default;
    +// ~20px
     
     //** By default, this inherits from the ``.
    -$headings-font-family:    $font-family-base !default;
    -$headings-font-weight:    400 !default;
    -$headings-line-height:    1.1 !default;
    -$headings-color:          inherit !default;
    -
    +$headings-font-family: $font-family-base !default;
    +$headings-font-weight: 400 !default;
    +$headings-line-height: 1.1 !default;
    +$headings-color: inherit !default;
     
     //== Iconography
     //
    @@ -93,189 +102,184 @@ $headings-color:          inherit !default;
     //** Load fonts from this directory.
     $icon-font-path: if($bootstrap-sass-asset-helper, "bootstrap/", "../fonts/bootstrap/") !default;
     //** File name for all font files.
    -$icon-font-name:          "glyphicons-halflings-regular" !default;
    +$icon-font-name: "glyphicons-halflings-regular" !default;
     //** Element ID within SVG icon file.
    -$icon-font-svg-id:        "glyphicons_halflingsregular" !default;
    -
    +$icon-font-svg-id: "glyphicons_halflingsregular" !default;
     
     //== Components
     //
     //## Define common padding and border radius sizes and more. Values based on 14px text and 1.428 line-height (~20px to start).
     
    -$padding-base-vertical:     10px !default;
    -$padding-base-horizontal:   15px !default;
    +$padding-base-vertical: 10px !default;
    +$padding-base-horizontal: 15px !default;
     
    -$padding-large-vertical:    18px !default;
    -$padding-large-horizontal:  27px !default;
    +$padding-large-vertical: 18px !default;
    +$padding-large-horizontal: 27px !default;
     
    -$padding-small-vertical:    6px !default;
    -$padding-small-horizontal:  9px !default;
    +$padding-small-vertical: 6px !default;
    +$padding-small-horizontal: 9px !default;
     
    -$padding-xs-vertical:       1px !default;
    -$padding-xs-horizontal:     5px !default;
    +$padding-xs-vertical: 1px !default;
    +$padding-xs-horizontal: 5px !default;
     
    -$line-height-large:         1.3333333 !default; // extra decimals for Win 8.1 Chrome
    -$line-height-small:         1.5 !default;
    +$line-height-large: 1.3333333 !default;
    +// extra decimals for Win 8.1 Chrome
    +$line-height-small: 1.5 !default;
     
    -$border-radius-base:        4px !default;
    -$border-radius-large:       6px !default;
    -$border-radius-small:       3px !default;
    +$border-radius-base: 4px !default;
    +$border-radius-large: 6px !default;
    +$border-radius-small: 3px !default;
     
     //** Global color for active items (e.g., navs or dropdowns).
    -$component-active-color:    #fff !default;
    +$component-active-color: #fff !default;
     //** Global background color for active items (e.g., navs or dropdowns).
    -$component-active-bg:       $brand-primary !default;
    +$component-active-bg: $brand-primary !default;
     
     //** Width of the `border` for generating carets that indicator dropdowns.
    -$caret-width-base:          4px !default;
    +$caret-width-base: 4px !default;
     //** Carets increase slightly in size for larger components.
    -$caret-width-large:         5px !default;
    -
    +$caret-width-large: 5px !default;
     
     //== Tables
     //
     //## Customizes the `.table` component with basic values, each used across all table variations.
     
     //** Padding for ``s and ``s.
    -$table-cell-padding:            8px !default;
    +$table-cell-padding: 8px !default;
     //** Padding for cells in `.table-condensed`.
    -$table-condensed-cell-padding:  5px !default;
    +$table-condensed-cell-padding: 5px !default;
     
     //** Default background color used for all tables.
    -$table-bg:                      transparent !default;
    +$table-bg: transparent !default;
     //** Background color used for `.table-striped`.
    -$table-bg-accent:               $gray-lighter !default;
    +$table-bg-accent: $gray-lighter !default;
     //** Background color used for `.table-hover`.
    -$table-bg-hover:                $gray-lighter !default;
    -$table-bg-active:               $table-bg-hover !default;
    +$table-bg-hover: $gray-lighter !default;
    +$table-bg-active: $table-bg-hover !default;
     
     //** Border color for table and cell borders.
    -$table-border-color:            $gray-lighter !default;
    -
    +$table-border-color: $gray-lighter !default;
     
     //== Buttons
     //
     //## For each of Bootstrap's buttons, define text, background and border color.
     
    -$btn-font-weight:                normal !default;
    +$btn-font-weight: normal !default;
     
    -$btn-default-color:              #fff !default;
    -$btn-default-bg:                 $gray !default;
    -$btn-default-border:             $btn-default-bg !default;
    +$btn-default-color: #fff !default;
    +$btn-default-bg: $gray !default;
    +$btn-default-border: $btn-default-bg !default;
     
    -$btn-primary-color:              $btn-default-color !default;
    -$btn-primary-bg:                 $brand-primary !default;
    -$btn-primary-border:             $btn-primary-bg !default;
    +$btn-primary-color: $btn-default-color !default;
    +$btn-primary-bg: $brand-primary !default;
    +$btn-primary-border: $btn-primary-bg !default;
     
    -$btn-success-color:              $btn-default-color !default;
    -$btn-success-bg:                 $brand-success !default;
    -$btn-success-border:             $btn-success-bg !default;
    +$btn-success-color: $btn-default-color !default;
    +$btn-success-bg: $brand-success !default;
    +$btn-success-border: $btn-success-bg !default;
     
    -$btn-info-color:                 $btn-default-color !default;
    -$btn-info-bg:                    $brand-info !default;
    -$btn-info-border:                $btn-info-bg !default;
    +$btn-info-color: $btn-default-color !default;
    +$btn-info-bg: $brand-info !default;
    +$btn-info-border: $btn-info-bg !default;
     
    -$btn-warning-color:              $btn-default-color !default;
    -$btn-warning-bg:                 $brand-warning !default;
    -$btn-warning-border:             $btn-warning-bg !default;
    +$btn-warning-color: $btn-default-color !default;
    +$btn-warning-bg: $brand-warning !default;
    +$btn-warning-border: $btn-warning-bg !default;
     
    -$btn-danger-color:               $btn-default-color !default;
    -$btn-danger-bg:                  $brand-danger !default;
    -$btn-danger-border:              $btn-danger-bg !default;
    +$btn-danger-color: $btn-default-color !default;
    +$btn-danger-bg: $brand-danger !default;
    +$btn-danger-border: $btn-danger-bg !default;
     
    -$btn-link-disabled-color:        $gray-light !default;
    +$btn-link-disabled-color: $gray-light !default;
     
     // Allows for customizing button radius independently from global border radius
    -$btn-border-radius-base:         $border-radius-base !default;
    -$btn-border-radius-large:        $border-radius-large !default;
    -$btn-border-radius-small:        $border-radius-small !default;
    -
    +$btn-border-radius-base: $border-radius-base !default;
    +$btn-border-radius-large: $border-radius-large !default;
    +$btn-border-radius-small: $border-radius-small !default;
     
     //== Forms
     //
     //##
     
     //** `` background color
    -$input-bg:                       #fff !default;
    +$input-bg: #fff !default;
     //** `` background color
    -$input-bg-disabled:              $gray-lighter !default;
    +$input-bg-disabled: $gray-lighter !default;
     
     //** Text color for ``s
    -$input-color:                    $text-color !default;
    +$input-color: $text-color !default;
     //** `` border color
    -$input-border:                   #dce4ec !default;
    +$input-border: #dce4ec !default;
     
     // TODO: Rename `$input-border-radius` to `$input-border-radius-base` in v4
     //** Default `.form-control` border radius
     // This has no effect on ``s in CSS.
    -$input-border-radius:            $border-radius-base !default;
    +$input-border-radius: $border-radius-base !default;
     //** Large `.form-control` border radius
    -$input-border-radius-large:      $border-radius-large !default;
    +$input-border-radius-large: $border-radius-large !default;
     //** Small `.form-control` border radius
    -$input-border-radius-small:      $border-radius-small !default;
    +$input-border-radius-small: $border-radius-small !default;
     
     //** Border color for inputs on focus
    -$input-border-focus:             $brand-primary !default;
    +$input-border-focus: $brand-primary !default;
     
     //** Placeholder text color
    -$input-color-placeholder:        #acb6c0 !default;
    +$input-color-placeholder: #acb6c0 !default;
     
     //** Default `.form-control` height
    -$input-height-base:              ($line-height-computed + ($padding-base-vertical * 2) + 4) !default;
    +$input-height-base: ($line-height-computed + ($padding-base-vertical * 2) + 4) !default;
     //** Large `.form-control` height
    -$input-height-large:             (ceil($font-size-large * $line-height-large) + ($padding-large-vertical * 2) + 4) !default;
    +$input-height-large: (ceil($font-size-large * $line-height-large) + ($padding-large-vertical * 2) + 4) !default;
     //** Small `.form-control` height
    -$input-height-small:             (floor($font-size-small * $line-height-small) + ($padding-small-vertical * 2) + 4) !default;
    +$input-height-small: (floor($font-size-small * $line-height-small) + ($padding-small-vertical * 2) + 4) !default;
     
     //** `.form-group` margin
    -$form-group-margin-bottom:       15px !default;
    +$form-group-margin-bottom: 15px !default;
     
    -$legend-color:                   $text-color !default;
    -$legend-border-color:            transparent !default;
    +$legend-color: $text-color !default;
    +$legend-border-color: transparent !default;
     
     //** Background color for textual input addons
    -$input-group-addon-bg:           $gray-lighter !default;
    +$input-group-addon-bg: $gray-lighter !default;
     //** Border color for textual input addons
     $input-group-addon-border-color: $input-border !default;
     
     //** Disabled cursor for form controls and buttons.
    -$cursor-disabled:                not-allowed !default;
    -
    +$cursor-disabled: not-allowed !default;
     
     //== Dropdowns
     //
     //## Dropdown menu container and contents.
     
     //** Background for the dropdown menu.
    -$dropdown-bg:                    #fff !default;
    +$dropdown-bg: #fff !default;
     //** Dropdown menu `border-color`.
    -$dropdown-border:                rgba(0,0,0,.15) !default;
    +$dropdown-border: rgba(0, 0, 0, .15) !default;
     //** Dropdown menu `border-color` **for IE8**.
    -$dropdown-fallback-border:       #ccc !default;
    +$dropdown-fallback-border: #ccc !default;
     //** Divider color for between dropdown items.
    -$dropdown-divider-bg:            #e5e5e5 !default;
    +$dropdown-divider-bg: #e5e5e5 !default;
     
     //** Dropdown link text color.
    -$dropdown-link-color:            $gray-dark !default;
    +$dropdown-link-color: $gray-dark !default;
     //** Hover color for dropdown links.
    -$dropdown-link-hover-color:      #fff !default;
    +$dropdown-link-hover-color: #fff !default;
     //** Hover background for dropdown links.
    -$dropdown-link-hover-bg:         $component-active-bg !default;
    +$dropdown-link-hover-bg: $component-active-bg !default;
     
     //** Active dropdown menu item text color.
    -$dropdown-link-active-color:     #fff !default;
    +$dropdown-link-active-color: #fff !default;
     //** Active dropdown menu item background color.
    -$dropdown-link-active-bg:        $component-active-bg !default;
    +$dropdown-link-active-bg: $component-active-bg !default;
     
     //** Disabled dropdown menu item background color.
    -$dropdown-link-disabled-color:   $gray-light !default;
    +$dropdown-link-disabled-color: $gray-light !default;
     
     //** Text color for headers within dropdown menus.
    -$dropdown-header-color:          $gray-light !default;
    +$dropdown-header-color: $gray-light !default;
     
     //** Deprecated `$dropdown-caret-color` as of v3.1.0
    -$dropdown-caret-color:           #000 !default;
    -
    +$dropdown-caret-color: #000 !default;
     
     //-- Z-index master list
     //
    @@ -284,14 +288,13 @@ $dropdown-caret-color:           #000 !default;
     //
     // Note: These variables are not generated into the Customizer.
     
    -$zindex-navbar:            1000 !default;
    -$zindex-dropdown:          1000 !default;
    -$zindex-popover:           1060 !default;
    -$zindex-tooltip:           1070 !default;
    -$zindex-navbar-fixed:      1030 !default;
    -$zindex-modal-background:  1040 !default;
    -$zindex-modal:             1050 !default;
    -
    +$zindex-navbar: 1000 !default;
    +$zindex-dropdown: 1000 !default;
    +$zindex-popover: 1060 !default;
    +$zindex-tooltip: 1070 !default;
    +$zindex-navbar-fixed: 1030 !default;
    +$zindex-modal-background: 1040 !default;
    +$zindex-modal: 1050 !default;
     
     //== Media queries breakpoints
     //
    @@ -299,563 +302,539 @@ $zindex-modal:             1050 !default;
     
     // Extra small screen / phone
     //** Deprecated `$screen-xs` as of v3.0.1
    -$screen-xs:                  480px !default;
    +$screen-xs: 480px !default;
     //** Deprecated `$screen-xs-min` as of v3.2.0
    -$screen-xs-min:              $screen-xs !default;
    +$screen-xs-min: $screen-xs !default;
     //** Deprecated `$screen-phone` as of v3.0.1
    -$screen-phone:               $screen-xs-min !default;
    +$screen-phone: $screen-xs-min !default;
     
     // Small screen / tablet
     //** Deprecated `$screen-sm` as of v3.0.1
    -$screen-sm:                  768px !default;
    -$screen-sm-min:              $screen-sm !default;
    +$screen-sm: 768px !default;
    +$screen-sm-min: $screen-sm !default;
     //** Deprecated `$screen-tablet` as of v3.0.1
    -$screen-tablet:              $screen-sm-min !default;
    +$screen-tablet: $screen-sm-min !default;
     
     // Medium screen / desktop
     //** Deprecated `$screen-md` as of v3.0.1
    -$screen-md:                  992px !default;
    -$screen-md-min:              $screen-md !default;
    +$screen-md: 992px !default;
    +$screen-md-min: $screen-md !default;
     //** Deprecated `$screen-desktop` as of v3.0.1
    -$screen-desktop:             $screen-md-min !default;
    +$screen-desktop: $screen-md-min !default;
     
     // Large screen / wide desktop
     //** Deprecated `$screen-lg` as of v3.0.1
    -$screen-lg:                  1200px !default;
    -$screen-lg-min:              $screen-lg !default;
    +$screen-lg: 1200px !default;
    +$screen-lg-min: $screen-lg !default;
     //** Deprecated `$screen-lg-desktop` as of v3.0.1
    -$screen-lg-desktop:          $screen-lg-min !default;
    +$screen-lg-desktop: $screen-lg-min !default;
     
     // So media queries don't overlap when required, provide a maximum
    -$screen-xs-max:              ($screen-sm-min - 1) !default;
    -$screen-sm-max:              ($screen-md-min - 1) !default;
    -$screen-md-max:              ($screen-lg-min - 1) !default;
    -
    +$screen-xs-max: ($screen-sm-min - 1) !default;
    +$screen-sm-max: ($screen-md-min - 1) !default;
    +$screen-md-max: ($screen-lg-min - 1) !default;
     
     //== Grid system
     //
     //## Define your custom responsive grid.
     
     //** Number of columns in the grid.
    -$grid-columns:              12 !default;
    +$grid-columns: 12 !default;
     //** Padding between columns. Gets divided in half for the left and right.
    -$grid-gutter-width:         30px !default;
    +$grid-gutter-width: 30px !default;
     // Navbar collapse
     //** Point at which the navbar becomes uncollapsed.
    -$grid-float-breakpoint:     $screen-sm-min !default;
    +$grid-float-breakpoint: $screen-sm-min !default;
     //** Point at which the navbar begins collapsing.
     $grid-float-breakpoint-max: ($grid-float-breakpoint - 1) !default;
     
    -
     //== Container sizes
     //
     //## Define the maximum width of `.container` for different screen sizes.
     
     // Small screen / tablet
    -$container-tablet:             (720px + $grid-gutter-width) !default;
    +$container-tablet: (720px + $grid-gutter-width) !default;
     //** For `$screen-sm-min` and up.
    -$container-sm:                 $container-tablet !default;
    +$container-sm: $container-tablet !default;
     
     // Medium screen / desktop
    -$container-desktop:            (940px + $grid-gutter-width) !default;
    +$container-desktop: (940px + $grid-gutter-width) !default;
     //** For `$screen-md-min` and up.
    -$container-md:                 $container-desktop !default;
    +$container-md: $container-desktop !default;
     
     // Large screen / wide desktop
    -$container-large-desktop:      (1140px + $grid-gutter-width) !default;
    +$container-large-desktop: (1140px + $grid-gutter-width) !default;
     //** For `$screen-lg-min` and up.
    -$container-lg:                 $container-large-desktop !default;
    -
    +$container-lg: $container-large-desktop !default;
     
     //== Navbar
     //
     //##
     
     // Basics of a navbar
    -$navbar-height:                    60px !default;
    +$navbar-height: 60px !default;
     $navbar-margin-bottom: 0;
     //$line-height-computed !default;
    -$navbar-border-radius:             $border-radius-base !default;
    -$navbar-padding-horizontal:        floor(($grid-gutter-width / 2)) !default;
    -$navbar-padding-vertical:          (($navbar-height - $line-height-computed) / 2) !default;
    -$navbar-collapse-max-height:       340px !default;
    +$navbar-border-radius: $border-radius-base !default;
    +$navbar-padding-horizontal: floor(($grid-gutter-width / 2)) !default;
    +$navbar-padding-vertical: (($navbar-height - $line-height-computed) / 2) !default;
    +$navbar-collapse-max-height: 340px !default;
     
    -$navbar-default-color:             #777 !default;
    -$navbar-default-bg:                $brand-primary !default;
    -$navbar-default-border:            transparent !default;
    +$navbar-default-color: #777 !default;
    +$navbar-default-bg: $brand-primary !default;
    +$navbar-default-border: transparent !default;
     
     // Navbar links
    -$navbar-default-link-color:                #fff !default;
    -$navbar-default-link-hover-color:          darken($brand-primary, 25%) !default;
    -$navbar-default-link-hover-bg:             transparent !default;
    -$navbar-default-link-active-color:         #fff !default;
    -$navbar-default-link-active-bg:            darken($navbar-default-bg, 10%) !default;
    -$navbar-default-link-disabled-color:       #ccc !default;
    -$navbar-default-link-disabled-bg:          transparent !default;
    +$navbar-default-link-color: #fff !default;
    +$navbar-default-link-hover-color: darken($brand-primary, 25%) !default;
    +$navbar-default-link-hover-bg: transparent !default;
    +$navbar-default-link-active-color: #fff !default;
    +$navbar-default-link-active-bg: darken($navbar-default-bg, 10%) !default;
    +$navbar-default-link-disabled-color: #ccc !default;
    +$navbar-default-link-disabled-bg: transparent !default;
     
     // Navbar brand label
    -$navbar-default-brand-color:               $navbar-default-link-color !default;
    -$navbar-default-brand-hover-color:         $navbar-default-link-hover-color !default;
    -$navbar-default-brand-hover-bg:            transparent !default;
    +$navbar-default-brand-color: $navbar-default-link-color !default;
    +$navbar-default-brand-hover-color: $navbar-default-link-hover-color !default;
    +$navbar-default-brand-hover-bg: transparent !default;
     
     // Navbar toggle
    -$navbar-default-toggle-hover-bg:           darken($navbar-default-bg, 10%) !default;
    -$navbar-default-toggle-icon-bar-bg:        #fff !default;
    -$navbar-default-toggle-border-color:       darken($navbar-default-bg, 10%) !default;
    -
    +$navbar-default-toggle-hover-bg: darken($navbar-default-bg, 10%) !default;
    +$navbar-default-toggle-icon-bar-bg: #fff !default;
    +$navbar-default-toggle-border-color: darken($navbar-default-bg, 10%) !default;
     
     //=== Inverted navbar
     // Reset inverted navbar basics
    -$navbar-inverse-color:                      #fff !default;
    -$navbar-inverse-bg:                         $brand-success !default;
    -$navbar-inverse-border:                     transparent !default;
    +$navbar-inverse-color: #fff !default;
    +$navbar-inverse-bg: $brand-success !default;
    +$navbar-inverse-border: transparent !default;
     
     // Inverted navbar links
    -$navbar-inverse-link-color:                 #fff !default;
    -$navbar-inverse-link-hover-color:           $brand-primary !default;
    -$navbar-inverse-link-hover-bg:              transparent !default;
    -$navbar-inverse-link-active-color:          #fff !default;
    -$navbar-inverse-link-active-bg:             darken($navbar-inverse-bg, 5%) !default;
    -$navbar-inverse-link-disabled-color:        #ccc !default;
    -$navbar-inverse-link-disabled-bg:           transparent !default;
    +$navbar-inverse-link-color: #fff !default;
    +$navbar-inverse-link-hover-color: $brand-primary !default;
    +$navbar-inverse-link-hover-bg: transparent !default;
    +$navbar-inverse-link-active-color: #fff !default;
    +$navbar-inverse-link-active-bg: darken($navbar-inverse-bg, 5%) !default;
    +$navbar-inverse-link-disabled-color: #ccc !default;
    +$navbar-inverse-link-disabled-bg: transparent !default;
     
     // Inverted navbar brand label
    -$navbar-inverse-brand-color:                $navbar-inverse-link-color !default;
    -$navbar-inverse-brand-hover-color:          $navbar-inverse-link-hover-color !default;
    -$navbar-inverse-brand-hover-bg:             transparent !default;
    +$navbar-inverse-brand-color: $navbar-inverse-link-color !default;
    +$navbar-inverse-brand-hover-color: $navbar-inverse-link-hover-color !default;
    +$navbar-inverse-brand-hover-bg: transparent !default;
     
     // Inverted navbar toggle
    -$navbar-inverse-toggle-hover-bg:            darken($navbar-inverse-bg, 10%) !default;
    -$navbar-inverse-toggle-icon-bar-bg:         #fff !default;
    -$navbar-inverse-toggle-border-color:        darken($navbar-inverse-bg, 10%) !default;
    -
    +$navbar-inverse-toggle-hover-bg: darken($navbar-inverse-bg, 10%) !default;
    +$navbar-inverse-toggle-icon-bar-bg: #fff !default;
    +$navbar-inverse-toggle-border-color: darken($navbar-inverse-bg, 10%) !default;
     
     //== Navs
     //
     //##
     
     //=== Shared nav styles
    -$nav-link-padding:                          10px 15px !default;
    -$nav-link-hover-bg:                         $gray-lighter !default;
    +$nav-link-padding: 10px 15px !default;
    +$nav-link-hover-bg: $gray-lighter !default;
     
    -$nav-disabled-link-color:                   $gray-light !default;
    -$nav-disabled-link-hover-color:             $gray-light !default;
    +$nav-disabled-link-color: $gray-light !default;
    +$nav-disabled-link-hover-color: $gray-light !default;
     
     //== Tabs
    -$nav-tabs-border-color:                     $gray-lighter !default;
    +$nav-tabs-border-color: $gray-lighter !default;
     
    -$nav-tabs-link-hover-border-color:          $gray-lighter !default;
    +$nav-tabs-link-hover-border-color: $gray-lighter !default;
     
    -$nav-tabs-active-link-hover-bg:             $body-bg !default;
    -$nav-tabs-active-link-hover-color:          $brand-primary !default;
    -$nav-tabs-active-link-hover-border-color:   $gray-lighter !default;
    +$nav-tabs-active-link-hover-bg: $body-bg !default;
    +$nav-tabs-active-link-hover-color: $brand-primary !default;
    +$nav-tabs-active-link-hover-border-color: $gray-lighter !default;
     
    -$nav-tabs-justified-link-border-color:            $gray-lighter !default;
    -$nav-tabs-justified-active-link-border-color:     $body-bg !default;
    +$nav-tabs-justified-link-border-color: $gray-lighter !default;
    +$nav-tabs-justified-active-link-border-color: $body-bg !default;
     
     //== Pills
    -$nav-pills-border-radius:                   $border-radius-base !default;
    -$nav-pills-active-link-hover-bg:            $component-active-bg !default;
    -$nav-pills-active-link-hover-color:         $component-active-color !default;
    -
    +$nav-pills-border-radius: $border-radius-base !default;
    +$nav-pills-active-link-hover-bg: $component-active-bg !default;
    +$nav-pills-active-link-hover-color: $component-active-color !default;
     
     //== Pagination
     //
     //##
     
    -$pagination-color:                     #fff !default;
    -$pagination-bg:                        $brand-success !default;
    -$pagination-border:                    transparent !default;
    +$pagination-color: #fff !default;
    +$pagination-bg: $brand-success !default;
    +$pagination-border: transparent !default;
     
    -$pagination-hover-color:               #fff !default;
    -$pagination-hover-bg:                  darken($brand-success, 15%) !default;
    -$pagination-hover-border:              transparent !default;
    +$pagination-hover-color: #fff !default;
    +$pagination-hover-bg: darken($brand-success, 15%) !default;
    +$pagination-hover-border: transparent !default;
     
    -$pagination-active-color:              #fff !default;
    -$pagination-active-bg:                 darken($brand-success, 15%) !default;
    -$pagination-active-border:             transparent !default;
    -
    -$pagination-disabled-color:            $gray-lighter !default;
    -$pagination-disabled-bg:               lighten($brand-success, 15%) !default;
    -$pagination-disabled-border:           transparent !default;
    +$pagination-active-color: #fff !default;
    +$pagination-active-bg: darken($brand-success, 15%) !default;
    +$pagination-active-border: transparent !default;
     
    +$pagination-disabled-color: $gray-lighter !default;
    +$pagination-disabled-bg: lighten($brand-success, 15%) !default;
    +$pagination-disabled-border: transparent !default;
     
     //== Pager
     //
     //##
     
    -$pager-bg:                             $pagination-bg !default;
    -$pager-border:                         $pagination-border !default;
    -$pager-border-radius:                  15px !default;
    -
    -$pager-hover-bg:                       $pagination-hover-bg !default;
    +$pager-bg: $pagination-bg !default;
    +$pager-border: $pagination-border !default;
    +$pager-border-radius: 15px !default;
     
    -$pager-active-bg:                      $pagination-active-bg !default;
    -$pager-active-color:                   $pagination-active-color !default;
    +$pager-hover-bg: $pagination-hover-bg !default;
     
    -$pager-disabled-color:                 #fff !default;
    +$pager-active-bg: $pagination-active-bg !default;
    +$pager-active-color: $pagination-active-color !default;
     
    +$pager-disabled-color: #fff !default;
     
     //== Jumbotron
     //
     //##
     
    -$jumbotron-padding:              30px !default;
    -$jumbotron-color:                inherit !default;
    -$jumbotron-bg:                   $gray-lighter !default;
    -$jumbotron-heading-color:        inherit !default;
    -$jumbotron-font-size:            ceil(($font-size-base * 1.5)) !default;
    -$jumbotron-heading-font-size:    ceil(($font-size-base * 4.5)) !default;
    -
    +$jumbotron-padding: 30px !default;
    +$jumbotron-color: inherit !default;
    +$jumbotron-bg: $gray-lighter !default;
    +$jumbotron-heading-color: inherit !default;
    +$jumbotron-font-size: ceil(($font-size-base * 1.5)) !default;
    +$jumbotron-heading-font-size: ceil(($font-size-base * 4.5)) !default;
     
     //== Form states and alerts
     //
     //## Define colors for form feedback states and, by default, alerts.
     
    -$state-success-text:             #fff !default;
    -$state-success-bg:               $brand-success !default;
    -$state-success-border:           $brand-success !default;
    +$state-success-text: #fff !default;
    +$state-success-bg: $brand-success !default;
    +$state-success-border: $brand-success !default;
     
    -$state-info-text:                #fff !default;
    -$state-info-bg:                  $brand-info !default;
    -$state-info-border:              $brand-info !default;
    +$state-info-text: #fff !default;
    +$state-info-bg: $brand-info !default;
    +$state-info-border: $brand-info !default;
     
    -$state-warning-text:             #fff !default;
    -$state-warning-bg:               $brand-warning !default;
    -$state-warning-border:           $brand-warning !default;
    -
    -$state-danger-text:              #fff !default;
    -$state-danger-bg:                $brand-danger !default;
    -$state-danger-border:            $brand-danger !default;
    +$state-warning-text: #fff !default;
    +$state-warning-bg: $brand-warning !default;
    +$state-warning-border: $brand-warning !default;
     
    +$state-danger-text: #fff !default;
    +$state-danger-bg: $brand-danger !default;
    +$state-danger-border: $brand-danger !default;
     
     //== Tooltips
     //
     //##
     
     //** Tooltip max width
    -$tooltip-max-width:           200px !default;
    +$tooltip-max-width: 200px !default;
     //** Tooltip text color
    -$tooltip-color:               #fff !default;
    +$tooltip-color: #fff !default;
     //** Tooltip background color
    -$tooltip-bg:                  #000 !default;
    -$tooltip-opacity:             .9 !default;
    +$tooltip-bg: #000 !default;
    +$tooltip-opacity: .9 !default;
     
     //** Tooltip arrow width
    -$tooltip-arrow-width:         5px !default;
    +$tooltip-arrow-width: 5px !default;
     //** Tooltip arrow color
    -$tooltip-arrow-color:         $tooltip-bg !default;
    -
    +$tooltip-arrow-color: $tooltip-bg !default;
     
     //== Popovers
     //
     //##
     
     //** Popover body background color
    -$popover-bg:                          #fff !default;
    +$popover-bg: #fff !default;
     //** Popover maximum width
    -$popover-max-width:                   276px !default;
    +$popover-max-width: 276px !default;
     //** Popover border color
    -$popover-border-color:                rgba(0,0,0,.2) !default;
    +$popover-border-color: rgba(0, 0, 0, .2) !default;
     //** Popover fallback border color
    -$popover-fallback-border-color:       #ccc !default;
    +$popover-fallback-border-color: #ccc !default;
     
     //** Popover title background color
    -$popover-title-bg:                    darken($popover-bg, 3%) !default;
    +$popover-title-bg: darken($popover-bg, 3%) !default;
     
     //** Popover arrow width
    -$popover-arrow-width:                 10px !default;
    +$popover-arrow-width: 10px !default;
     //** Popover arrow color
    -$popover-arrow-color:                 $popover-bg !default;
    +$popover-arrow-color: $popover-bg !default;
     
     //** Popover outer arrow width
    -$popover-arrow-outer-width:           ($popover-arrow-width + 1) !default;
    +$popover-arrow-outer-width: ($popover-arrow-width + 1) !default;
     //** Popover outer arrow color
    -$popover-arrow-outer-color:           fadein($popover-border-color, 5%) !default;
    +$popover-arrow-outer-color: fadein($popover-border-color, 5%) !default;
     //** Popover outer arrow fallback color
    -$popover-arrow-outer-fallback-color:  darken($popover-fallback-border-color, 20%) !default;
    -
    +$popover-arrow-outer-fallback-color: darken($popover-fallback-border-color, 20%) !default;
     
     //== Labels
     //
     //##
     
     //** Default label background color
    -$label-default-bg:            $btn-default-bg !default;
    +$label-default-bg: $btn-default-bg !default;
     //** Primary label background color
    -$label-primary-bg:            $brand-primary !default;
    +$label-primary-bg: $brand-primary !default;
     //** Success label background color
    -$label-success-bg:            $brand-success !default;
    +$label-success-bg: $brand-success !default;
     //** Info label background color
    -$label-info-bg:               $brand-info !default;
    +$label-info-bg: $brand-info !default;
     //** Warning label background color
    -$label-warning-bg:            $brand-warning !default;
    +$label-warning-bg: $brand-warning !default;
     //** Danger label background color
    -$label-danger-bg:             $brand-danger !default;
    +$label-danger-bg: $brand-danger !default;
     
     //** Default label text color
    -$label-color:                 #fff !default;
    +$label-color: #fff !default;
     //** Default text color of a linked label
    -$label-link-hover-color:      #fff !default;
    -
    +$label-link-hover-color: #fff !default;
     
     //== Modals
     //
     //##
     
     //** Padding applied to the modal body
    -$modal-inner-padding:         20px !default;
    +$modal-inner-padding: 20px !default;
     
     //** Padding applied to the modal title
    -$modal-title-padding:         15px !default;
    +$modal-title-padding: 15px !default;
     //** Modal title line-height
    -$modal-title-line-height:     $line-height-base !default;
    +$modal-title-line-height: $line-height-base !default;
     
     //** Background color of modal content area
    -$modal-content-bg:                             #fff !default;
    +$modal-content-bg: #fff !default;
     //** Modal content border color
    -$modal-content-border-color:                   rgba(0,0,0,.2) !default;
    +$modal-content-border-color: rgba(0, 0, 0, .2) !default;
     //** Modal content border color **for IE8**
    -$modal-content-fallback-border-color:          #999 !default;
    +$modal-content-fallback-border-color: #999 !default;
     
     //** Modal backdrop background color
    -$modal-backdrop-bg:           #000 !default;
    +$modal-backdrop-bg: #000 !default;
     //** Modal backdrop opacity
    -$modal-backdrop-opacity:      .5 !default;
    +$modal-backdrop-opacity: .5 !default;
     //** Modal header border color
    -$modal-header-border-color:   #e5e5e5 !default;
    +$modal-header-border-color: #e5e5e5 !default;
     //** Modal footer border color
    -$modal-footer-border-color:   $modal-header-border-color !default;
    -
    -$modal-lg:                    900px !default;
    -$modal-md:                    600px !default;
    -$modal-sm:                    300px !default;
    +$modal-footer-border-color: $modal-header-border-color !default;
     
    +$modal-lg: 900px !default;
    +$modal-md: 600px !default;
    +$modal-sm: 300px !default;
     
     //== Alerts
     //
     //## Define alert colors, border radius, and padding.
     
    -$alert-padding:               15px !default;
    -$alert-border-radius:         $border-radius-base !default;
    -$alert-link-font-weight:      bold !default;
    +$alert-padding: 15px !default;
    +$alert-border-radius: $border-radius-base !default;
    +$alert-link-font-weight: bold !default;
     
    -$alert-success-bg:            $state-success-bg !default;
    -$alert-success-text:          $state-success-text !default;
    -$alert-success-border:        $state-success-border !default;
    +$alert-success-bg: $state-success-bg !default;
    +$alert-success-text: $state-success-text !default;
    +$alert-success-border: $state-success-border !default;
     
    -$alert-info-bg:               $state-info-bg !default;
    -$alert-info-text:             $state-info-text !default;
    -$alert-info-border:           $state-info-border !default;
    +$alert-info-bg: $state-info-bg !default;
    +$alert-info-text: $state-info-text !default;
    +$alert-info-border: $state-info-border !default;
     
    -$alert-warning-bg:            $state-warning-bg !default;
    -$alert-warning-text:          $state-warning-text !default;
    -$alert-warning-border:        $state-warning-border !default;
    -
    -$alert-danger-bg:             $state-danger-bg !default;
    -$alert-danger-text:           $state-danger-text !default;
    -$alert-danger-border:         $state-danger-border !default;
    +$alert-warning-bg: $state-warning-bg !default;
    +$alert-warning-text: $state-warning-text !default;
    +$alert-warning-border: $state-warning-border !default;
     
    +$alert-danger-bg: $state-danger-bg !default;
    +$alert-danger-text: $state-danger-text !default;
    +$alert-danger-border: $state-danger-border !default;
     
     //== Progress bars
     //
     //##
     
     //** Background color of the whole progress component
    -$progress-bg:                 $gray-lighter !default;
    +$progress-bg: $gray-lighter !default;
     //** Progress bar text color
    -$progress-bar-color:          #fff !default;
    +$progress-bar-color: #fff !default;
     //** Variable for setting rounded corners on progress bar.
    -$progress-border-radius:      $border-radius-base !default;
    +$progress-border-radius: $border-radius-base !default;
     
     //** Default progress bar color
    -$progress-bar-bg:             $brand-primary !default;
    +$progress-bar-bg: $brand-primary !default;
     //** Success progress bar color
    -$progress-bar-success-bg:     $brand-success !default;
    +$progress-bar-success-bg: $brand-success !default;
     //** Warning progress bar color
    -$progress-bar-warning-bg:     $brand-warning !default;
    +$progress-bar-warning-bg: $brand-warning !default;
     //** Danger progress bar color
    -$progress-bar-danger-bg:      $brand-danger !default;
    +$progress-bar-danger-bg: $brand-danger !default;
     //** Info progress bar color
    -$progress-bar-info-bg:        $brand-info !default;
    -
    +$progress-bar-info-bg: $brand-info !default;
     
     //== List group
     //
     //##
     
     //** Background color on `.list-group-item`
    -$list-group-bg:                 #fff !default;
    +$list-group-bg: #fff !default;
     //** `.list-group-item` border color
    -$list-group-border:             $gray-lighter !default;
    +$list-group-border: $gray-lighter !default;
     //** List group border radius
    -$list-group-border-radius:      $border-radius-base !default;
    +$list-group-border-radius: $border-radius-base !default;
     
     //** Background color of single list items on hover
    -$list-group-hover-bg:           $gray-lighter !default;
    +$list-group-hover-bg: $gray-lighter !default;
     //** Text color of active list items
    -$list-group-active-color:       $component-active-color !default;
    +$list-group-active-color: $component-active-color !default;
     //** Background color of active list items
    -$list-group-active-bg:          $component-active-bg !default;
    +$list-group-active-bg: $component-active-bg !default;
     //** Border color of active list elements
    -$list-group-active-border:      $list-group-active-bg !default;
    +$list-group-active-border: $list-group-active-bg !default;
     //** Text color for content within active list items
    -$list-group-active-text-color:  lighten($list-group-active-bg, 40%) !default;
    +$list-group-active-text-color: lighten($list-group-active-bg, 40%) !default;
     
     //** Text color of disabled list items
    -$list-group-disabled-color:      $gray-light !default;
    +$list-group-disabled-color: $gray-light !default;
     //** Background color of disabled list items
    -$list-group-disabled-bg:         $gray-lighter !default;
    +$list-group-disabled-bg: $gray-lighter !default;
     //** Text color for content within disabled list items
     $list-group-disabled-text-color: $list-group-disabled-color !default;
     
    -$list-group-link-color:         #555 !default;
    -$list-group-link-hover-color:   $list-group-link-color !default;
    +$list-group-link-color: #555 !default;
    +$list-group-link-hover-color: $list-group-link-color !default;
     $list-group-link-heading-color: #333 !default;
     
    -
     //== Panels
     //
     //##
     
    -$panel-bg:                    #fff !default;
    -$panel-body-padding:          15px !default;
    -$panel-heading-padding:       10px 15px !default;
    -$panel-footer-padding:        $panel-heading-padding !default;
    -$panel-border-radius:         $border-radius-base !default;
    +$panel-bg: #fff !default;
    +$panel-body-padding: 15px !default;
    +$panel-heading-padding: 10px 15px !default;
    +$panel-footer-padding: $panel-heading-padding !default;
    +$panel-border-radius: $border-radius-base !default;
     
     //** Border color for elements within panels
    -$panel-inner-border:          $gray-lighter !default;
    -$panel-footer-bg:             $gray-lighter !default;
    -
    -$panel-default-text:          $text-color !default;
    -$panel-default-border:        $gray-lighter !default;
    -$panel-default-heading-bg:    $gray-lighter !default;
    +$panel-inner-border: $gray-lighter !default;
    +$panel-footer-bg: $gray-lighter !default;
     
    -$panel-primary-text:          #fff !default;
    -$panel-primary-border:        $brand-primary !default;
    -$panel-primary-heading-bg:    $brand-primary !default;
    +$panel-default-text: $text-color !default;
    +$panel-default-border: $gray-lighter !default;
    +$panel-default-heading-bg: $gray-lighter !default;
     
    -$panel-success-text:          $state-success-text !default;
    -$panel-success-border:        $state-success-border !default;
    -$panel-success-heading-bg:    $state-success-bg !default;
    +$panel-primary-text: #fff !default;
    +$panel-primary-border: $brand-primary !default;
    +$panel-primary-heading-bg: $brand-primary !default;
     
    -$panel-info-text:             $state-info-text !default;
    -$panel-info-border:           $state-info-border !default;
    -$panel-info-heading-bg:       $state-info-bg !default;
    +$panel-success-text: $state-success-text !default;
    +$panel-success-border: $state-success-border !default;
    +$panel-success-heading-bg: $state-success-bg !default;
     
    -$panel-warning-text:          $state-warning-text !default;
    -$panel-warning-border:        $state-warning-border !default;
    -$panel-warning-heading-bg:    $state-warning-bg !default;
    +$panel-info-text: $state-info-text !default;
    +$panel-info-border: $state-info-border !default;
    +$panel-info-heading-bg: $state-info-bg !default;
     
    -$panel-danger-text:           $state-danger-text !default;
    -$panel-danger-border:         $state-danger-border !default;
    -$panel-danger-heading-bg:     $state-danger-bg !default;
    +$panel-warning-text: $state-warning-text !default;
    +$panel-warning-border: $state-warning-border !default;
    +$panel-warning-heading-bg: $state-warning-bg !default;
     
    +$panel-danger-text: $state-danger-text !default;
    +$panel-danger-border: $state-danger-border !default;
    +$panel-danger-heading-bg: $state-danger-bg !default;
     
     //== Thumbnails
     //
     //##
     
     //** Padding around the thumbnail image
    -$thumbnail-padding:           4px !default;
    +$thumbnail-padding: 4px !default;
     //** Thumbnail background color
    -$thumbnail-bg:                $body-bg !default;
    +$thumbnail-bg: $body-bg !default;
     //** Thumbnail border color
    -$thumbnail-border:            $gray-lighter !default;
    +$thumbnail-border: $gray-lighter !default;
     //** Thumbnail border radius
    -$thumbnail-border-radius:     $border-radius-base !default;
    +$thumbnail-border-radius: $border-radius-base !default;
     
     //** Custom text color for thumbnail captions
    -$thumbnail-caption-color:     $text-color !default;
    +$thumbnail-caption-color: $text-color !default;
     //** Padding around the thumbnail caption
    -$thumbnail-caption-padding:   9px !default;
    -
    +$thumbnail-caption-padding: 9px !default;
     
     //== Wells
     //
     //##
     
    -$well-bg:                     $gray-lighter !default;//#fff !default;
    -$well-border:                 transparent !default;
    -
    +$well-bg: $gray-lighter !default;
    +//#fff !default;
    +$well-border: transparent !default;
     
     //== Badges
     //
     //##
     
    -$badge-color:                 #fff !default;
    +$badge-color: #fff !default;
     //** Linked badge text color on hover
    -$badge-link-hover-color:      #fff !default;
    -$badge-bg:                    $brand-primary !default;
    +$badge-link-hover-color: #fff !default;
    +$badge-bg: $brand-primary !default;
     
     //** Badge text color in active nav link
    -$badge-active-color:          $brand-primary !default;
    +$badge-active-color: $brand-primary !default;
     //** Badge background color in active nav link
    -$badge-active-bg:             #fff !default;
    -
    -$badge-font-weight:           bold !default;
    -$badge-line-height:           1 !default;
    -$badge-border-radius:         10px !default;
    +$badge-active-bg: #fff !default;
     
    +$badge-font-weight: bold !default;
    +$badge-line-height: 1 !default;
    +$badge-border-radius: 10px !default;
     
     //== Breadcrumbs
     //
     //##
     
    -$breadcrumb-padding-vertical:   8px !default;
    +$breadcrumb-padding-vertical: 8px !default;
     $breadcrumb-padding-horizontal: 15px !default;
     //** Breadcrumb background color
    -$breadcrumb-bg:                 $gray-lighter !default;
    +$breadcrumb-bg: $gray-lighter !default;
     //** Breadcrumb text color
    -$breadcrumb-color:              #ccc !default;
    +$breadcrumb-color: #ccc !default;
     //** Text color of current page in the breadcrumb
    -$breadcrumb-active-color:       $gray !default;
    +$breadcrumb-active-color: $gray !default;
     //** Textual separator for between breadcrumb elements
    -$breadcrumb-separator:          "/" !default;
    -
    +$breadcrumb-separator: "/" !default;
     
     //== Carousel
     //
     //##
     
    -$carousel-text-shadow:                        0 1px 2px rgba(0,0,0,.6) !default;
    -
    -$carousel-control-color:                      #fff !default;
    -$carousel-control-width:                      15% !default;
    -$carousel-control-opacity:                    .5 !default;
    -$carousel-control-font-size:                  20px !default;
    +$carousel-text-shadow: 0 1px 2px rgba(0, 0, 0, .6) !default;
     
    -$carousel-indicator-active-bg:                #fff !default;
    -$carousel-indicator-border-color:             #fff !default;
    +$carousel-control-color: #fff !default;
    +$carousel-control-width: 15% !default;
    +$carousel-control-opacity: .5 !default;
    +$carousel-control-font-size: 20px !default;
     
    -$carousel-caption-color:                      #fff !default;
    +$carousel-indicator-active-bg: #fff !default;
    +$carousel-indicator-border-color: #fff !default;
     
    +$carousel-caption-color: #fff !default;
     
     //== Close
     //
     //##
     
    -$close-font-weight:           bold !default;
    -$close-color:                 #000 !default;
    -$close-text-shadow:           none !default;
    -
    +$close-font-weight: bold !default;
    +$close-color: #000 !default;
    +$close-text-shadow: none !default;
     
     //== Code
     //
     //##
     
    -$code-color:                  #c7254e !default;
    -$code-bg:                     #f9f2f4 !default;
    -
    -$kbd-color:                   #fff !default;
    -$kbd-bg:                      #333 !default;
    +$code-color: #c7254e !default;
    +$code-bg: #f9f2f4 !default;
     
    -$pre-bg:                      $gray-lighter !default;
    -$pre-color:                   $gray-dark !default;
    -$pre-border-color:            #ccc !default;
    -$pre-scrollable-max-height:   340px !default;
    +$kbd-color: #fff !default;
    +$kbd-bg: #333 !default;
     
    +$pre-bg: $gray-lighter !default;
    +$pre-color: $gray-dark !default;
    +$pre-border-color: #ccc !default;
    +$pre-scrollable-max-height: 340px !default;
     
     //== Type
     //
    @@ -864,20 +843,20 @@ $pre-scrollable-max-height:   340px !default;
     //** Horizontal offset for forms and lists.
     $component-offset-horizontal: 180px !default;
     //** Text muted color
    -$text-muted:                  $gray-light !default;
    +$text-muted: $gray-light !default;
     //** Abbreviations and acronyms border color
    -$abbr-border-color:           $gray-light !default;
    +$abbr-border-color: $gray-light !default;
     //** Headings small color
    -$headings-small-color:        $gray-light !default;
    +$headings-small-color: $gray-light !default;
     //** Blockquote small color
    -$blockquote-small-color:      $gray-light !default;
    +$blockquote-small-color: $gray-light !default;
     //** Blockquote font size
    -$blockquote-font-size:        ($font-size-base * 1.25) !default;
    +$blockquote-font-size: ($font-size-base * 1.25) !default;
     //** Blockquote border color
    -$blockquote-border-color:     $gray-lighter !default;
    +$blockquote-border-color: $gray-lighter !default;
     //** Page header border color
    -$page-header-border-color:    transparent !default;
    +$page-header-border-color: transparent !default;
     //** Width of horizontal description list titles
    -$dl-horizontal-offset:        $component-offset-horizontal !default;
    +$dl-horizontal-offset: $component-offset-horizontal !default;
     //** Horizontal line color.
    -$hr-border:                   $gray-lighter !default;
    +$hr-border: $gray-lighter !default;
    diff --git a/src/main/scss/main.scss b/src/main/scss/main.scss
    index cb0a3e5..1816926 100644
    --- a/src/main/scss/main.scss
    +++ b/src/main/scss/main.scss
    @@ -115,7 +115,7 @@ $citation-font: 'EB Garamond';
     }
     
     table > thead > tr > th, table > tbody > tr > th, table > tfoot > tr > th, .table > thead > tr > th,
    -.table > tbody > tr > th, .table > tfoot > tr > th  {
    +.table > tbody > tr > th, .table > tfoot > tr > th {
         font-weight: bold;
         color: $green;
         border-bottom: 2px solid $green;
    @@ -206,7 +206,7 @@ table > thead > tr:hover > td, table > tbody > tr:hover > td, table > tfoot > tr
     
     /* make sure browsers use the pointer cursor for anchors, even with no href */
     a:hover {
    -  cursor: pointer;
    +    cursor: pointer;
     }
     
     .hand {
    @@ -239,32 +239,34 @@ a:hover {
     
     /* start Password strength bar style */
     ul#strength {
    -    display:inline;
    -    list-style:none;
    -    margin:0;
    -    margin-left:15px;
    -    padding:0;
    -    vertical-align:2px;
    +    display: inline;
    +    list-style: none;
    +    margin: 0;
    +    margin-left: 15px;
    +    padding: 0;
    +    vertical-align: 2px;
     }
     
     .point:last {
    -    margin:0 !important;
    +    margin: 0 !important;
     }
    +
     .point {
    -    background:#DDD;
    -    border-radius:2px;
    -    display:inline-block;
    -    height:5px;
    -    margin-right:1px;
    -    width:20px;
    +    background: #DDD;
    +    border-radius: 2px;
    +    display: inline-block;
    +    height: 5px;
    +    margin-right: 1px;
    +    width: 20px;
     }
    +
     /* end Password strength bar style */
     /* Custom alerts for notification */
    -.alerts .alert{
    +.alerts .alert {
         text-overflow: ellipsis;
     }
     
    -.alert pre{
    +.alert pre {
         background: none;
         border: none;
         font: inherit;
    @@ -272,6 +274,7 @@ ul#strength {
         padding: 0;
         margin: 0;
     }
    +
     /* end of Custom alerts for notification */
     
     /* round buttons for icons */
    @@ -423,6 +426,7 @@ ul#strength {
             margin: 0 auto 20px;
         }
     }
    +
     /* End of distributed footer */
     
     /* Forms part */
    @@ -435,7 +439,7 @@ ul#strength {
     .sub-label {
         font-size: $font-size-small;
         font-weight: bolder;
    -    color : $blue2;
    +    color: $blue2;
     }
     
     /* En of forms part */
    @@ -443,8 +447,8 @@ ul#strength {
     /* Database part */
     
     .pub-length {
    -    padding : 10px 0px;
    -    color : $blue2;
    +    padding: 10px 0px;
    +    color: $blue2;
         font-style: italic;
     }
     
    @@ -476,13 +480,14 @@ ul#strength {
         border-left: 5px solid $green;
     }
     
    -.ingredients-item table td { //TODO adjust
    -    padding : 5px 5px 0 5px;
    +.ingredients-item table td {
    +    //TODO adjust
    +    padding: 5px 5px 0 5px;
     }
     
     .ingredients-item-details {
    -    vertical-align:middle;
    -    font-size:large;
    +    vertical-align: middle;
    +    font-size: large;
     }
     
     @media (max-width: 767px) {
    @@ -538,7 +543,7 @@ ul#strength {
         font-size: $font-size-small;
     }
     
    -.cat-test-tag{
    +.cat-test-tag {
         background-color: black;
         color: $gray-dark;
         font-size: $font-size-small;
    @@ -549,7 +554,7 @@ ul#strength {
     }
     
     @media (max-width: 767px) {
    -    .cat-test-tag-block{
    +    .cat-test-tag-block {
             display: block;
         }
     }
    @@ -579,13 +584,13 @@ h3 small {
         color: $gray-base;
     }
     
    -.publication-label{
    +.publication-label {
         font-weight: bolder;
         font-size: $font-size-small;
    -    color : $blue2;
    +    color: $blue2;
     }
     
    -.review-label{
    +.review-label {
         font-weight: bolder;
         color: $blue2;
         margin-top: 20px;
    @@ -593,16 +598,18 @@ h3 small {
         font-size: $font-size-large;
     }
     
    -.btn-review-item{
    +.btn-review-item {
         padding: 5px 2px;
     }
    +
     .nav-pills > li.btn-review-item > a {
         padding: 8px 10px;
         font-size: $font-size-smaller;
     }
    +
     .nav-pills > .active.btn-review-item > a,
     .nav-pills > .active.btn-review-item > a:hover,
    -.nav-pills > .active.btn-review-item > a:focus{
    +.nav-pills > .active.btn-review-item > a:focus {
         background-color: $green-super-lighter;
         color: $green;
         font-weight: bold;
    @@ -653,6 +660,7 @@ h3 small {
     }
     
     .right-buffer {
    -    margin-right : 10px;
    +    margin-right: 10px;
     }
    +
     /* End of database part */
    diff --git a/src/main/webapp/404.html b/src/main/webapp/404.html
    index 8d7925a..2538ad5 100644
    --- a/src/main/webapp/404.html
    +++ b/src/main/webapp/404.html
    @@ -53,8 +53,9 @@
         
     
     
    -    

    Page Not Found

    -

    Sorry, but the page you were trying to view does not exist.

    +

    Page Not Found

    + +

    Sorry, but the page you were trying to view does not exist.

    diff --git a/src/main/webapp/assets/images/mpdbc_carousel1_old.jpg b/src/main/webapp/assets/images/mpdbc_carousel1_old.jpg new file mode 100644 index 0000000..45589ba Binary files /dev/null and b/src/main/webapp/assets/images/mpdbc_carousel1_old.jpg differ diff --git a/src/main/webapp/assets/images/mpdbc_carousel2_old.jpg b/src/main/webapp/assets/images/mpdbc_carousel2_old.jpg new file mode 100644 index 0000000..52619c3 Binary files /dev/null and b/src/main/webapp/assets/images/mpdbc_carousel2_old.jpg differ diff --git a/src/main/webapp/assets/images/mpdbc_carousel3_old.jpg b/src/main/webapp/assets/images/mpdbc_carousel3_old.jpg new file mode 100644 index 0000000..3daab5c Binary files /dev/null and b/src/main/webapp/assets/images/mpdbc_carousel3_old.jpg differ diff --git a/src/main/webapp/assets/images/mpdbc_carousel4_old.jpg b/src/main/webapp/assets/images/mpdbc_carousel4_old.jpg new file mode 100644 index 0000000..c62d935 Binary files /dev/null and b/src/main/webapp/assets/images/mpdbc_carousel4_old.jpg differ diff --git a/src/main/webapp/bower_components/modernizr/test/caniuse_files/mathml.html b/src/main/webapp/bower_components/modernizr/test/caniuse_files/mathml.html index 8203884..bbbd9d8 100644 --- a/src/main/webapp/bower_components/modernizr/test/caniuse_files/mathml.html +++ b/src/main/webapp/bower_components/modernizr/test/caniuse_files/mathml.html @@ -1,5 +1,6 @@ - + + Untitled @@ -117,4 +118,4 @@ - \ No newline at end of file + diff --git a/src/main/webapp/index.html b/src/main/webapp/index.html index fd175f8..82c61fc 100644 --- a/src/main/webapp/index.html +++ b/src/main/webapp/index.html @@ -1,35 +1,38 @@ - - - - Malaria Plant DB Community - - - - - - - - - - - - -
    - -
    -
    -
    -
    -