This is an automated email from the ASF dual-hosted git repository. kaze pushed a commit to branch develop in repository https://gitbox.apache.org/repos/asf/fineract-credit-scorecard.git
commit 238669a673ec2651f21db05bdc2e8af5926bbe2e Author: Nasser Kaze <[email protected]> AuthorDate: Sun Aug 29 21:00:27 2021 +0100 FINCN-351 Make the scorecard integration into a plugin --- .classpath | 6 - .github/workflows/build.yml | 16 +- .gitignore | 5 +- README.md | Bin 10088 -> 10020 bytes api/views.py | 2 +- build.gradle | 46 +- clients/java/.idea/.gitignore | 8 - clients/java/.idea/.name | 1 - clients/java/.idea/compiler.xml | 6 - clients/java/.idea/gradle.xml | 18 - clients/java/.idea/jarRepositories.xml | 20 - clients/java/.idea/misc.xml | 8 - clients/java/.idea/vcs.xml | 6 - clients/java/build.gradle | 114 -- clients/java/gradle.properties | 2 - clients/java/gradle/wrapper/gradle-wrapper.jar | Bin 59203 -> 0 bytes .../java/gradle/wrapper/gradle-wrapper.properties | 5 - clients/java/gradlew | 185 ---- clients/java/gradlew.bat | 89 -- clients/java/pom.xml | 276 ----- clients/java/settings.gradle | 1 - openapi.yml | 1094 ++++++++++---------- {clients/java => scorecard-client}/README.md | 0 scorecard-client/build.gradle | 55 + .../java => scorecard-client}/docs/Algorithm.md | 0 .../docs/AlgorithmsApi.md | 0 {clients/java => scorecard-client}/docs/Dataset.md | 0 .../java => scorecard-client}/docs/DatasetsApi.md | 0 .../docs/PredictionRequest.md | 0 .../docs/PredictionResponse.md | 0 .../java => scorecard-client}/docs/RequestsApi.md | 0 .../src/main/AndroidManifest.xml | 0 .../fineract/credit/scorecard/ApiCallback.java | 0 .../fineract/credit/scorecard/ApiClient.java | 0 .../fineract/credit/scorecard/ApiException.java | 0 .../fineract/credit/scorecard/ApiResponse.java | 0 .../fineract/credit/scorecard/Configuration.java | 0 .../credit/scorecard/GzipRequestInterceptor.java | 0 .../org/apache/fineract/credit/scorecard/JSON.java | 0 .../org/apache/fineract/credit/scorecard/Pair.java | 0 .../credit/scorecard/ProgressRequestBody.java | 0 .../credit/scorecard/ProgressResponseBody.java | 0 .../credit/scorecard/ServerConfiguration.java | 0 .../fineract/credit/scorecard/ServerVariable.java | 0 .../fineract/credit/scorecard/StringUtil.java | 0 .../fineract/credit/scorecard/auth/ApiKeyAuth.java | 0 .../credit/scorecard/auth/Authentication.java | 0 .../credit/scorecard/auth/HttpBasicAuth.java | 0 .../credit/scorecard/auth/HttpBearerAuth.java | 0 .../credit/scorecard/models/Algorithm.java | 0 .../fineract/credit/scorecard/models/Dataset.java | 0 .../credit/scorecard/models/PredictionRequest.java | 0 .../scorecard/models/PredictionResponse.java | 0 .../credit/scorecard/services/AlgorithmsApi.java | 0 .../credit/scorecard/services/DatasetsApi.java | 0 .../credit/scorecard/services/RequestsApi.java | 0 scorecard-plugin/README.md | 39 + scorecard-plugin/TODO.md | 17 + scorecard-plugin/build.gradle | 80 ++ scorecard-plugin/run | 13 + scorecard-plugin/run.bat | 33 + .../service/CreditScorecardAssemblerImpl.java | 255 +++++ ...corecardFeatureDropdownReadPlatformService.java | 52 +- ...cardFeatureDropdownReadPlatformServiceImpl.java | 55 + .../CreditScorecardReadPlatformServiceImpl.java | 228 ++++ .../CreditScorecardWritePlatformServiceImpl.java | 376 +++++++ .../src/main/resources/scorecard-client.properties | 11 +- server/settings.py | 5 +- settings.gradle | 4 + .../statistical_scoring.py | 0 70 files changed, 1759 insertions(+), 1372 deletions(-) diff --git a/.classpath b/.classpath index c0c2f18..4f3f504 100644 --- a/.classpath +++ b/.classpath @@ -1,11 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> <classpath> - <classpathentry kind="src" output="bin/generated" path="build/generated/java/src/main/java"> - <attributes> - <attribute name="gradle_scope" value="generated"/> - <attribute name="gradle_used_by_scope" value="generated"/> - </attributes> - </classpathentry> <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8/"/> <classpathentry kind="con" path="org.eclipse.buildship.core.gradleclasspathcontainer"/> <classpathentry kind="output" path="bin/default"/> diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index e4a2c68..809f075 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -16,14 +16,14 @@ jobs: python-version: [3.7, 3.8] services: - mysql: - image: mysql:5.7 - env: - MYSQL_ROOT_PASSWORD: mysql - MYSQL_DATABASE: fineract_credit_scorecard - ports: - - '3306:3306' - options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3 + mysql: + image: mysql:5.7 + env: + MYSQL_ROOT_PASSWORD: mysql + MYSQL_DATABASE: fineract_credit_scorecard + ports: + - '3306:3306' + options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3 steps: - uses: actions/checkout@v2 diff --git a/.gitignore b/.gitignore index 040215e..623696e 100644 --- a/.gitignore +++ b/.gitignore @@ -121,5 +121,8 @@ build .settings .project +.classpath -data/ \ No newline at end of file +data/ + +.idea diff --git a/README.md b/README.md index e34a5b0..5a2b294 100644 Binary files a/README.md and b/README.md differ diff --git a/api/views.py b/api/views.py index 4198d29..33ee8c8 100644 --- a/api/views.py +++ b/api/views.py @@ -19,7 +19,7 @@ import json import logging -from statistical_scripts.statistical_scoring import stat_score +from stats.statistical_scoring import stat_score from typing import Any, Dict from drf_spectacular.utils import extend_schema, OpenApiParameter, OpenApiExample, inline_serializer diff --git a/build.gradle b/build.gradle index ca95c94..cbd7246 100644 --- a/build.gradle +++ b/build.gradle @@ -25,7 +25,7 @@ buildscript { plugins { id "java" - id "org.openapi.generator" version "5.1.0" + id "org.openapi.generator" version "5.2.1" id "com.github.hierynomus.license" version"0.15.0" } @@ -33,6 +33,11 @@ description = 'Fineract Credit Scorecard Build Tool' ext['openapiSpecFile'] = "$rootDir/openapi.yml".toString() +java { + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 +} + license { header rootProject.file("$rootDir/APACHE_LICENSETEXT.md") excludes([ @@ -93,36 +98,6 @@ task buildJavaSdk(type: org.openapitools.generator.gradle.plugin.tasks.GenerateT ] generateModelTests = false generateApiTests = false - finalizedBy = ["copyClients"] -} - -task buildTypescriptAngularSdk(type: org.openapitools.generator.gradle.plugin.tasks.GenerateTask) { - generatorName = 'typescript-angular' - verbose = false - validateSpec = false - skipValidateSpec = true - inputSpec = "$openapiSpecFile" - outputDir = "$buildDir/generated/typescript".toString() - apiPackage = 'org.apache.fineract.credit.scorecard.services' - invokerPackage = 'org.apache.fineract.credit.scorecard' - modelPackage = 'org.apache.fineract.credit.scorecard.models' - configOptions = [ - apiModulePrefix: 'scorecard', - configurationPrefix: 'scorecard', - ngVersion: '10.0.0', - npmName: 'apache-fineract-credit-scorecard-client' - ] - finalizedBy = [licenseFormat] -} - -task copyClients { - copy { - from 'build/generated' - into 'clients' - exclude '**/*.sbt', '**/*.yml', '**/*.sh', '**/api', '**/.openapi-generator', '**/.openapi-generator-ignore' - // filter(ReplaceTokens, tokens: [version: '2.3.1']) - } - finalizedBy = [licenseFormat] } @@ -134,11 +109,6 @@ sourceSets { } } -java { - sourceCompatibility = JavaVersion.VERSION_1_8 - targetCompatibility = JavaVersion.VERSION_1_8 -} - compileJava { source += sourceSets.generated.java } @@ -147,7 +117,3 @@ configurations { generatedCompile.extendsFrom implementation generatedRuntime.extendsFrom runtime } - -test { - useJUnitPlatform() -} diff --git a/clients/java/.idea/.gitignore b/clients/java/.idea/.gitignore deleted file mode 100644 index 4aa91ea..0000000 --- a/clients/java/.idea/.gitignore +++ /dev/null @@ -1,8 +0,0 @@ -# Default ignored files -/shelf/ -/workspace.xml -# Datasource local storage ignored files -/dataSources/ -/dataSources.local.xml -# Editor-based HTTP Client requests -/httpRequests/ diff --git a/clients/java/.idea/.name b/clients/java/.idea/.name deleted file mode 100644 index da9779d..0000000 --- a/clients/java/.idea/.name +++ /dev/null @@ -1 +0,0 @@ -credit-scorecard-java-client \ No newline at end of file diff --git a/clients/java/.idea/compiler.xml b/clients/java/.idea/compiler.xml deleted file mode 100644 index 245a82c..0000000 --- a/clients/java/.idea/compiler.xml +++ /dev/null @@ -1,6 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<project version="4"> - <component name="CompilerConfiguration"> - <bytecodeTargetLevel target="1.8" /> - </component> -</project> \ No newline at end of file diff --git a/clients/java/.idea/gradle.xml b/clients/java/.idea/gradle.xml deleted file mode 100644 index 222345a..0000000 --- a/clients/java/.idea/gradle.xml +++ /dev/null @@ -1,18 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<project version="4"> - <component name="GradleSettings"> - <option name="linkedExternalProjectsSettings"> - <GradleProjectSettings> - <option name="distributionType" value="DEFAULT_WRAPPED" /> - <option name="externalProjectPath" value="$PROJECT_DIR$" /> - <option name="gradleHome" value="$PROJECT_DIR$/../../../../../../../../tools/Gradle/gradle-7.1" /> - <option name="gradleJvm" value="#JAVA_HOME" /> - <option name="modules"> - <set> - <option value="$PROJECT_DIR$" /> - </set> - </option> - </GradleProjectSettings> - </option> - </component> -</project> \ No newline at end of file diff --git a/clients/java/.idea/jarRepositories.xml b/clients/java/.idea/jarRepositories.xml deleted file mode 100644 index 83f06a7..0000000 --- a/clients/java/.idea/jarRepositories.xml +++ /dev/null @@ -1,20 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<project version="4"> - <component name="RemoteRepositoriesConfiguration"> - <remote-repository> - <option name="id" value="central" /> - <option name="name" value="Maven Central repository" /> - <option name="url" value="https://repo1.maven.org/maven2" /> - </remote-repository> - <remote-repository> - <option name="id" value="jboss.community" /> - <option name="name" value="JBoss Community repository" /> - <option name="url" value="https://repository.jboss.org/nexus/content/repositories/public/" /> - </remote-repository> - <remote-repository> - <option name="id" value="BintrayJCenter" /> - <option name="name" value="BintrayJCenter" /> - <option name="url" value="https://jcenter.bintray.com/" /> - </remote-repository> - </component> -</project> \ No newline at end of file diff --git a/clients/java/.idea/misc.xml b/clients/java/.idea/misc.xml deleted file mode 100644 index a2556d3..0000000 --- a/clients/java/.idea/misc.xml +++ /dev/null @@ -1,8 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<project version="4"> - <component name="ExternalStorageConfigurationManager" enabled="true" /> - <component name="FrameworkDetectionExcludesConfiguration"> - <file type="web" url="file://$PROJECT_DIR$" /> - </component> - <component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" project-jdk-name="11" project-jdk-type="JavaSDK" /> -</project> \ No newline at end of file diff --git a/clients/java/.idea/vcs.xml b/clients/java/.idea/vcs.xml deleted file mode 100644 index c8ade07..0000000 --- a/clients/java/.idea/vcs.xml +++ /dev/null @@ -1,6 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<project version="4"> - <component name="VcsDirectoryMappings"> - <mapping directory="$PROJECT_DIR$/../.." vcs="Git" /> - </component> -</project> \ No newline at end of file diff --git a/clients/java/build.gradle b/clients/java/build.gradle deleted file mode 100644 index 58ff79f..0000000 --- a/clients/java/build.gradle +++ /dev/null @@ -1,114 +0,0 @@ -apply plugin: 'idea' -apply plugin: 'eclipse' -apply plugin: 'java' - -group = 'org.apache.fineract' -version = '0.1.0-SNAPSHOT' - -buildscript { - repositories { - maven { url "https://repo1.maven.org/maven2" } - jcenter() - } - dependencies { - classpath 'com.android.tools.build:gradle:2.3.+' - classpath 'com.github.dcendents:android-maven-gradle-plugin:1.5' - } -} - -repositories { - jcenter() -} -sourceSets { - main.java.srcDirs = ['src/main/java'] -} - -if(hasProperty('target') && target == 'android') { - - apply plugin: 'com.android.library' - apply plugin: 'com.github.dcendents.android-maven' - - android { - compileSdkVersion 25 - buildToolsVersion '25.0.2' - defaultConfig { - minSdkVersion 14 - targetSdkVersion 25 - } - compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 - } - - // Rename the aar correctly - libraryVariants.all { variant -> - variant.outputs.each { output -> - def outputFile = output.outputFile - if (outputFile != null && outputFile.name.endsWith('.aar')) { - def fileName = "${project.name}-${variant.baseName}-${version}.aar" - output.outputFile = new File(outputFile.parent, fileName) - } - } - } - - dependencies { - provided 'javax.annotation:javax.annotation-api:1.3.2' - } - } - - afterEvaluate { - android.libraryVariants.all { variant -> - def task = project.tasks.create "jar${variant.name.capitalize()}", Jar - task.description = "Create jar artifact for ${variant.name}" - task.dependsOn variant.javaCompile - task.from variant.javaCompile.destinationDir - task.destinationDir = project.file("${project.buildDir}/outputs/jar") - task.archiveName = "${project.name}-${variant.baseName}-${version}.jar" - artifacts.add('archives', task); - } - } - - task sourcesJar(type: Jar) { - from android.sourceSets.main.java.srcDirs - classifier = 'sources' - } - - artifacts { - archives sourcesJar - } - -} else { - - apply plugin: 'java' - apply plugin: 'maven' - - sourceCompatibility = JavaVersion.VERSION_1_8 - targetCompatibility = JavaVersion.VERSION_1_8 - - install { - repositories.mavenInstaller { - pom.artifactId = 'credit-scorecard-java-client' - } - } - - task execute(type:JavaExec) { - main = System.getProperty('mainClass') - classpath = sourceSets.main.runtimeClasspath - } -} - -dependencies { - implementation 'io.swagger:swagger-annotations:1.5.24' - implementation "com.google.code.findbugs:jsr305:3.0.2" - implementation 'com.squareup.okhttp3:okhttp:3.14.7' - implementation 'com.squareup.okhttp3:logging-interceptor:3.14.7' - implementation 'com.google.code.gson:gson:2.8.6' - implementation 'io.gsonfire:gson-fire:1.8.4' - implementation group: 'org.apache.commons', name: 'commons-lang3', version: '3.10' - implementation 'javax.annotation:javax.annotation-api:1.3.2' - testImplementation 'junit:junit:4.13.1' -} - -javadoc { - options.tags = [ "http.response.details:a:Http Response Details" ] -} diff --git a/clients/java/gradle.properties b/clients/java/gradle.properties deleted file mode 100644 index 05644f0..0000000 --- a/clients/java/gradle.properties +++ /dev/null @@ -1,2 +0,0 @@ -# Uncomment to build for Android -#target = android \ No newline at end of file diff --git a/clients/java/gradle/wrapper/gradle-wrapper.jar b/clients/java/gradle/wrapper/gradle-wrapper.jar deleted file mode 100644 index e708b1c..0000000 Binary files a/clients/java/gradle/wrapper/gradle-wrapper.jar and /dev/null differ diff --git a/clients/java/gradle/wrapper/gradle-wrapper.properties b/clients/java/gradle/wrapper/gradle-wrapper.properties deleted file mode 100644 index 4d9ca16..0000000 --- a/clients/java/gradle/wrapper/gradle-wrapper.properties +++ /dev/null @@ -1,5 +0,0 @@ -distributionBase=GRADLE_USER_HOME -distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-bin.zip -zipStoreBase=GRADLE_USER_HOME -zipStorePath=wrapper/dists diff --git a/clients/java/gradlew b/clients/java/gradlew deleted file mode 100644 index 4f906e0..0000000 --- a/clients/java/gradlew +++ /dev/null @@ -1,185 +0,0 @@ -#!/usr/bin/env sh - -# -# Copyright 2015 the original author or authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -############################################################################## -## -## Gradle start up script for UN*X -## -############################################################################## - -# Attempt to set APP_HOME -# Resolve links: $0 may be a link -PRG="$0" -# Need this for relative symlinks. -while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG=`dirname "$PRG"`"/$link" - fi -done -SAVED="`pwd`" -cd "`dirname \"$PRG\"`/" >/dev/null -APP_HOME="`pwd -P`" -cd "$SAVED" >/dev/null - -APP_NAME="Gradle" -APP_BASE_NAME=`basename "$0"` - -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' - -# Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD="maximum" - -warn () { - echo "$*" -} - -die () { - echo - echo "$*" - echo - exit 1 -} - -# OS specific support (must be 'true' or 'false'). -cygwin=false -msys=false -darwin=false -nonstop=false -case "`uname`" in - CYGWIN* ) - cygwin=true - ;; - Darwin* ) - darwin=true - ;; - MINGW* ) - msys=true - ;; - NONSTOP* ) - nonstop=true - ;; -esac - -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar - - -# Determine the Java command to use to start the JVM. -if [ -n "$JAVA_HOME" ] ; then - if [ -x "$JAVA_HOME/jre/sh/java" ] ; then - # IBM's JDK on AIX uses strange locations for the executables - JAVACMD="$JAVA_HOME/jre/sh/java" - else - JAVACMD="$JAVA_HOME/bin/java" - fi - if [ ! -x "$JAVACMD" ] ; then - die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." - fi -else - JAVACMD="java" - which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." -fi - -# Increase the maximum file descriptors if we can. -if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then - MAX_FD_LIMIT=`ulimit -H -n` - if [ $? -eq 0 ] ; then - if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then - MAX_FD="$MAX_FD_LIMIT" - fi - ulimit -n $MAX_FD - if [ $? -ne 0 ] ; then - warn "Could not set maximum file descriptor limit: $MAX_FD" - fi - else - warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" - fi -fi - -# For Darwin, add options to specify how the application appears in the dock -if $darwin; then - GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" -fi - -# For Cygwin or MSYS, switch paths to Windows format before running java -if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then - APP_HOME=`cygpath --path --mixed "$APP_HOME"` - CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` - - JAVACMD=`cygpath --unix "$JAVACMD"` - - # We build the pattern for arguments to be converted via cygpath - ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` - SEP="" - for dir in $ROOTDIRSRAW ; do - ROOTDIRS="$ROOTDIRS$SEP$dir" - SEP="|" - done - OURCYGPATTERN="(^($ROOTDIRS))" - # Add a user-defined pattern to the cygpath arguments - if [ "$GRADLE_CYGPATTERN" != "" ] ; then - OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" - fi - # Now convert the arguments - kludge to limit ourselves to /bin/sh - i=0 - for arg in "$@" ; do - CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` - CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option - - if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition - eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` - else - eval `echo args$i`="\"$arg\"" - fi - i=`expr $i + 1` - done - case $i in - 0) set -- ;; - 1) set -- "$args0" ;; - 2) set -- "$args0" "$args1" ;; - 3) set -- "$args0" "$args1" "$args2" ;; - 4) set -- "$args0" "$args1" "$args2" "$args3" ;; - 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; - 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; - 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; - 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; - 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; - esac -fi - -# Escape application args -save () { - for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done - echo " " -} -APP_ARGS=`save "$@"` - -# Collect all arguments for the java command, following the shell quoting and substitution rules -eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" - -exec "$JAVACMD" "$@" diff --git a/clients/java/gradlew.bat b/clients/java/gradlew.bat deleted file mode 100644 index 107acd3..0000000 --- a/clients/java/gradlew.bat +++ /dev/null @@ -1,89 +0,0 @@ -@rem -@rem Copyright 2015 the original author or authors. -@rem -@rem Licensed under the Apache License, Version 2.0 (the "License"); -@rem you may not use this file except in compliance with the License. -@rem You may obtain a copy of the License at -@rem -@rem https://www.apache.org/licenses/LICENSE-2.0 -@rem -@rem Unless required by applicable law or agreed to in writing, software -@rem distributed under the License is distributed on an "AS IS" BASIS, -@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -@rem See the License for the specific language governing permissions and -@rem limitations under the License. -@rem - -@if "%DEBUG%" == "" @echo off -@rem ########################################################################## -@rem -@rem Gradle startup script for Windows -@rem -@rem ########################################################################## - -@rem Set local scope for the variables with windows NT shell -if "%OS%"=="Windows_NT" setlocal - -set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. -set APP_BASE_NAME=%~n0 -set APP_HOME=%DIRNAME% - -@rem Resolve any "." and ".." in APP_HOME to make it shorter. -for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi - -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" - -@rem Find java.exe -if defined JAVA_HOME goto findJavaFromJavaHome - -set JAVA_EXE=java.exe -%JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto execute - -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:findJavaFromJavaHome -set JAVA_HOME=%JAVA_HOME:"=% -set JAVA_EXE=%JAVA_HOME%/bin/java.exe - -if exist "%JAVA_EXE%" goto execute - -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:execute -@rem Setup the command line - -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar - - -@rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* - -:end -@rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd - -:fail -rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of -rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 - -:mainEnd -if "%OS%"=="Windows_NT" endlocal - -:omega diff --git a/clients/java/pom.xml b/clients/java/pom.xml deleted file mode 100644 index 83e6745..0000000 --- a/clients/java/pom.xml +++ /dev/null @@ -1,276 +0,0 @@ -<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> - <modelVersion>4.0.0</modelVersion> - <groupId>org.apache.fineract</groupId> - <artifactId>credit-scorecard-java-client</artifactId> - <packaging>jar</packaging> - <name>credit-scorecard-java-client</name> - <version>0.1.0-SNAPSHOT</version> - <url>https://github.com/openapitools/openapi-generator</url> - <description>OpenAPI Java</description> - <scm> - <connection>scm:git:[email protected]:openapitools/openapi-generator.git</connection> - <developerConnection>scm:git:[email protected]:openapitools/openapi-generator.git</developerConnection> - <url>https://github.com/openapitools/openapi-generator</url> - </scm> - - <licenses> - <license> - <name>Unlicense</name> - <url>https://www.apache.org/licenses/LICENSE-2.0.html</url> - <distribution>repo</distribution> - </license> - </licenses> - - <developers> - <developer> - <name>Apache Fineract</name> - <email>[email protected]</email> - <organization>Apache Software Foundation</organization> - <organizationUrl>https://apache.org</organizationUrl> - </developer> - </developers> - - <build> - <plugins> - <plugin> - <groupId>org.apache.maven.plugins</groupId> - <artifactId>maven-compiler-plugin</artifactId> - <version>3.8.1</version> - <configuration> - <fork>true</fork> - <meminitial>128m</meminitial> - <maxmem>512m</maxmem> - <compilerArgs> - <arg>-Xlint:all</arg> - <arg>-J-Xss4m</arg><!-- Compiling the generated JSON.java file may require larger stack size. --> - </compilerArgs> - </configuration> - </plugin> - <plugin> - <groupId>org.apache.maven.plugins</groupId> - <artifactId>maven-enforcer-plugin</artifactId> - <version>3.0.0-M1</version> - <executions> - <execution> - <id>enforce-maven</id> - <goals> - <goal>enforce</goal> - </goals> - <configuration> - <rules> - <requireMavenVersion> - <version>2.2.0</version> - </requireMavenVersion> - </rules> - </configuration> - </execution> - </executions> - </plugin> - <plugin> - <groupId>org.apache.maven.plugins</groupId> - <artifactId>maven-surefire-plugin</artifactId> - <version>3.0.0-M4</version> - <configuration> - <systemProperties> - <property> - <name>loggerPath</name> - <value>conf/log4j.properties</value> - </property> - </systemProperties> - <argLine>-Xms512m -Xmx1500m</argLine> - <parallel>methods</parallel> - <threadCount>10</threadCount> - </configuration> - </plugin> - <plugin> - <artifactId>maven-dependency-plugin</artifactId> - <executions> - <execution> - <phase>package</phase> - <goals> - <goal>copy-dependencies</goal> - </goals> - <configuration> - <outputDirectory>${project.build.directory}/lib</outputDirectory> - </configuration> - </execution> - </executions> - </plugin> - - <!-- attach test jar --> - <plugin> - <groupId>org.apache.maven.plugins</groupId> - <artifactId>maven-jar-plugin</artifactId> - <version>2.2</version> - <executions> - <execution> - <goals> - <goal>jar</goal> - <goal>test-jar</goal> - </goals> - </execution> - </executions> - <configuration> - </configuration> - </plugin> - - <plugin> - <groupId>org.codehaus.mojo</groupId> - <artifactId>build-helper-maven-plugin</artifactId> - <version>1.10</version> - <executions> - <execution> - <id>add_sources</id> - <phase>generate-sources</phase> - <goals> - <goal>add-source</goal> - </goals> - <configuration> - <sources> - <source>src/main/java</source> - </sources> - </configuration> - </execution> - <execution> - <id>add_test_sources</id> - <phase>generate-test-sources</phase> - <goals> - <goal>add-test-source</goal> - </goals> - <configuration> - <sources> - <source>src/test/java</source> - </sources> - </configuration> - </execution> - </executions> - </plugin> - <plugin> - <groupId>org.apache.maven.plugins</groupId> - <artifactId>maven-javadoc-plugin</artifactId> - <version>3.1.1</version> - <executions> - <execution> - <id>attach-javadocs</id> - <goals> - <goal>jar</goal> - </goals> - </execution> - </executions> - <configuration> - <doclint>none</doclint> - <tags> - <tag> - <name>http.response.details</name> - <placement>a</placement> - <head>Http Response Details:</head> - </tag> - </tags> - </configuration> - </plugin> - <plugin> - <groupId>org.apache.maven.plugins</groupId> - <artifactId>maven-source-plugin</artifactId> - <version>2.2.1</version> - <executions> - <execution> - <id>attach-sources</id> - <goals> - <goal>jar-no-fork</goal> - </goals> - </execution> - </executions> - </plugin> - </plugins> - </build> - - <profiles> - <profile> - <id>sign-artifacts</id> - <build> - <plugins> - <plugin> - <groupId>org.apache.maven.plugins</groupId> - <artifactId>maven-gpg-plugin</artifactId> - <version>1.5</version> - <executions> - <execution> - <id>sign-artifacts</id> - <phase>verify</phase> - <goals> - <goal>sign</goal> - </goals> - </execution> - </executions> - </plugin> - </plugins> - </build> - </profile> - </profiles> - - <dependencies> - <dependency> - <groupId>io.swagger</groupId> - <artifactId>swagger-annotations</artifactId> - <version>${swagger-core-version}</version> - </dependency> - <!-- @Nullable annotation --> - <dependency> - <groupId>com.google.code.findbugs</groupId> - <artifactId>jsr305</artifactId> - <version>3.0.2</version> - </dependency> - <dependency> - <groupId>com.squareup.okhttp3</groupId> - <artifactId>okhttp</artifactId> - <version>${okhttp-version}</version> - </dependency> - <dependency> - <groupId>com.squareup.okhttp3</groupId> - <artifactId>logging-interceptor</artifactId> - <version>${okhttp-version}</version> - </dependency> - <dependency> - <groupId>com.google.code.gson</groupId> - <artifactId>gson</artifactId> - <version>${gson-version}</version> - </dependency> - <dependency> - <groupId>io.gsonfire</groupId> - <artifactId>gson-fire</artifactId> - <version>${gson-fire-version}</version> - </dependency> - <dependency> - <groupId>org.apache.commons</groupId> - <artifactId>commons-lang3</artifactId> - <version>${commons-lang3-version}</version> - </dependency> - <dependency> - <groupId>javax.annotation</groupId> - <artifactId>javax.annotation-api</artifactId> - <version>${javax-annotation-version}</version> - <scope>provided</scope> - </dependency> - <!-- test dependencies --> - <dependency> - <groupId>junit</groupId> - <artifactId>junit</artifactId> - <version>${junit-version}</version> - <scope>test</scope> - </dependency> - </dependencies> - <properties> - <java.version>1.8</java.version> - <maven.compiler.source>${java.version}</maven.compiler.source> - <maven.compiler.target>${java.version}</maven.compiler.target> - <gson-fire-version>1.8.5</gson-fire-version> - <swagger-core-version>1.6.2</swagger-core-version> - <okhttp-version>4.9.1</okhttp-version> - <gson-version>2.8.6</gson-version> - <commons-lang3-version>3.11</commons-lang3-version> - <javax-annotation-version>1.3.2</javax-annotation-version> - <junit-version>4.13.1</junit-version> - <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> - </properties> -</project> diff --git a/clients/java/settings.gradle b/clients/java/settings.gradle deleted file mode 100644 index 805e86f..0000000 --- a/clients/java/settings.gradle +++ /dev/null @@ -1 +0,0 @@ -rootProject.name = "credit-scorecard-java-client" \ No newline at end of file diff --git a/openapi.yml b/openapi.yml index f26c051..e28c543 100644 --- a/openapi.yml +++ b/openapi.yml @@ -1,547 +1,547 @@ -openapi: 3.0.3 -info: - title: Fineract Credit Scorecard - version: 0.1.0-SNAPSHOT - description: An API module for credit risk assessment - license: - name: Apache-2.0 - url: https://www.apache.org/licenses/LICENSE-2.0.html -paths: - /api/v1/algorithms: - get: - operationId: algorithms_list - description: '' - tags: - - algorithms - security: - - {} - responses: - '200': - content: - application/json: - schema: - type: array - items: - $ref: '#/components/schemas/Algorithm' - description: '' - post: - operationId: algorithms_create - description: '' - tags: - - algorithms - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/Algorithm' - application/x-www-form-urlencoded: - schema: - $ref: '#/components/schemas/Algorithm' - multipart/form-data: - schema: - $ref: '#/components/schemas/Algorithm' - required: true - security: - - {} - responses: - '201': - content: - application/json: - schema: - $ref: '#/components/schemas/Algorithm' - description: '' - /api/v1/algorithms/{id}: - get: - operationId: algorithms_retrieve - description: '' - parameters: - - in: path - name: id - schema: - type: integer - description: A unique integer value identifying this algorithm. - required: true - tags: - - algorithms - security: - - {} - responses: - '200': - content: - application/json: - schema: - $ref: '#/components/schemas/Algorithm' - description: '' - put: - operationId: algorithms_update - description: '' - parameters: - - in: path - name: id - schema: - type: integer - description: A unique integer value identifying this algorithm. - required: true - tags: - - algorithms - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/Algorithm' - application/x-www-form-urlencoded: - schema: - $ref: '#/components/schemas/Algorithm' - multipart/form-data: - schema: - $ref: '#/components/schemas/Algorithm' - required: true - security: - - {} - responses: - '200': - content: - application/json: - schema: - $ref: '#/components/schemas/Algorithm' - description: '' - patch: - operationId: algorithms_partial_update - description: '' - parameters: - - in: path - name: id - schema: - type: integer - description: A unique integer value identifying this algorithm. - required: true - tags: - - algorithms - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/Algorithm' - application/x-www-form-urlencoded: - schema: - $ref: '#/components/schemas/Algorithm' - multipart/form-data: - schema: - $ref: '#/components/schemas/Algorithm' - required: true - security: - - {} - responses: - '200': - content: - application/json: - schema: - $ref: '#/components/schemas/Algorithm' - description: '' - delete: - operationId: algorithms_destroy - description: '' - parameters: - - in: path - name: id - schema: - type: integer - description: A unique integer value identifying this algorithm. - required: true - tags: - - algorithms - security: - - {} - responses: - '204': - description: No response body - /api/v1/algorithms/predict: - post: - operationId: algorithms_predict - description: Predict credit risk for a loan - parameters: - - in: query - name: classifier - schema: - type: string - description: The algorithm/classifier to use - required: true - examples: - Example1: - value: RandomForestClassifier - summary: Example 1 - - in: query - name: dataset - schema: - type: string - description: The name of the dataset - examples: - Example1: - value: german - summary: Example 1 - - in: query - name: status - schema: - type: string - description: The status of the algorithm - deprecated: true - examples: - Example1: - value: production - summary: Example 1 - - in: query - name: version - schema: - type: string - default: 0.0.1 - description: Algorithm version - required: true - examples: - Example1: - value: 0.0.1 - summary: Example 1 - tags: - - algorithms - requestBody: - content: - application/json: - schema: - type: object - additionalProperties: {} - description: Unspecified request body - application/x-www-form-urlencoded: - schema: - type: object - additionalProperties: {} - description: Unspecified request body - multipart/form-data: - schema: - type: object - additionalProperties: {} - description: Unspecified request body - security: - - {} - responses: - '200': - content: - application/json: - schema: - $ref: '#/components/schemas/PredictionResponse' - description: '' - /api/v1/datasets: - get: - operationId: datasets_list - description: '' - tags: - - datasets - security: - - {} - responses: - '200': - content: - application/json: - schema: - type: array - items: - $ref: '#/components/schemas/Dataset' - description: '' - /api/v1/datasets/{id}: - get: - operationId: datasets_retrieve - description: '' - parameters: - - in: path - name: id - schema: - type: integer - description: A unique integer value identifying this dataset. - required: true - tags: - - datasets - security: - - {} - responses: - '200': - content: - application/json: - schema: - $ref: '#/components/schemas/Dataset' - description: '' - /api/v1/requests: - get: - operationId: requests_list - description: '' - tags: - - requests - security: - - {} - responses: - '200': - content: - application/json: - schema: - type: array - items: - $ref: '#/components/schemas/PredictionRequest' - description: '' - post: - operationId: requests_create - description: '' - tags: - - requests - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/PredictionRequest' - application/x-www-form-urlencoded: - schema: - $ref: '#/components/schemas/PredictionRequest' - multipart/form-data: - schema: - $ref: '#/components/schemas/PredictionRequest' - required: true - security: - - {} - responses: - '201': - content: - application/json: - schema: - $ref: '#/components/schemas/PredictionRequest' - description: '' - /api/v1/requests/{id}: - get: - operationId: requests_retrieve - description: '' - parameters: - - in: path - name: id - schema: - type: integer - description: A unique integer value identifying this prediction request. - required: true - tags: - - requests - security: - - {} - responses: - '200': - content: - application/json: - schema: - $ref: '#/components/schemas/PredictionRequest' - description: '' - put: - operationId: requests_update - description: '' - parameters: - - in: path - name: id - schema: - type: integer - description: A unique integer value identifying this prediction request. - required: true - tags: - - requests - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/PredictionRequest' - application/x-www-form-urlencoded: - schema: - $ref: '#/components/schemas/PredictionRequest' - multipart/form-data: - schema: - $ref: '#/components/schemas/PredictionRequest' - required: true - security: - - {} - responses: - '200': - content: - application/json: - schema: - $ref: '#/components/schemas/PredictionRequest' - description: '' - patch: - operationId: requests_partial_update - description: '' - parameters: - - in: path - name: id - schema: - type: integer - description: A unique integer value identifying this prediction request. - required: true - tags: - - requests - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/PredictionRequest' - application/x-www-form-urlencoded: - schema: - $ref: '#/components/schemas/PredictionRequest' - multipart/form-data: - schema: - $ref: '#/components/schemas/PredictionRequest' - required: true - security: - - {} - responses: - '200': - content: - application/json: - schema: - $ref: '#/components/schemas/PredictionRequest' - description: '' - delete: - operationId: requests_destroy - description: '' - parameters: - - in: path - name: id - schema: - type: integer - description: A unique integer value identifying this prediction request. - required: true - tags: - - requests - security: - - {} - responses: - '204': - description: No response body -components: - schemas: - Algorithm: - type: object - properties: - id: - type: integer - readOnly: true - classifier: - type: string - maxLength: 128 - description: - type: string - nullable: true - version: - type: string - maxLength: 128 - status: - type: string - maxLength: 128 - created_at: - type: string - format: date-time - readOnly: true - created_by: - type: string - maxLength: 128 - dataset: - type: string - nullable: true - required: - - classifier - - created_at - - created_by - - id - - status - - version - Dataset: - type: object - properties: - id: - type: integer - readOnly: true - name: - type: string - maxLength: 128 - region: - type: string - maxLength: 128 - required: - - id - - name - - region - PredictionRequest: - type: object - properties: - id: - type: integer - readOnly: true - input: - type: object - additionalProperties: {} - response: - type: object - additionalProperties: {} - prediction: - type: string - maxLength: 128 - feedback: - type: string - nullable: true - maxLength: 128 - notes: - type: string - nullable: true - created_at: - type: string - format: date-time - readOnly: true - created_by: - type: string - maxLength: 128 - algorithm: - type: integer - required: - - created_at - - created_by - - id - - input - - prediction - - response - PredictionResponse: - type: object - properties: - probability: - type: number - format: float - label: - type: string - method: - type: string - color: - type: string - wilkis_lambda: - type: number - format: float - pillais_trace: - type: number - format: float - hotelling_tawley: - type: number - format: float - roys_reatest_roots: - type: number - format: float - request_id: - type: integer - required: - - color - - hotelling_tawley - - label - - method - - pillais_trace - - probability - - request_id - - roys_reatest_roots - - wilkis_lambda -servers: -- url: http://127.0.0.1:8000 - description: server on localhost +openapi: 3.0.3 +info: + title: Fineract Credit Scorecard + version: 0.1.0-SNAPSHOT + description: An API module for credit risk assessment + license: + name: Apache-2.0 + url: https://www.apache.org/licenses/LICENSE-2.0.html +paths: + /api/v1/algorithms: + get: + operationId: algorithms_list + description: '' + tags: + - algorithms + security: + - {} + responses: + '200': + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Algorithm' + description: '' + post: + operationId: algorithms_create + description: '' + tags: + - algorithms + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/Algorithm' + application/x-www-form-urlencoded: + schema: + $ref: '#/components/schemas/Algorithm' + multipart/form-data: + schema: + $ref: '#/components/schemas/Algorithm' + required: true + security: + - {} + responses: + '201': + content: + application/json: + schema: + $ref: '#/components/schemas/Algorithm' + description: '' + /api/v1/algorithms/{id}: + get: + operationId: algorithms_retrieve + description: '' + parameters: + - in: path + name: id + schema: + type: integer + description: A unique integer value identifying this algorithm. + required: true + tags: + - algorithms + security: + - {} + responses: + '200': + content: + application/json: + schema: + $ref: '#/components/schemas/Algorithm' + description: '' + put: + operationId: algorithms_update + description: '' + parameters: + - in: path + name: id + schema: + type: integer + description: A unique integer value identifying this algorithm. + required: true + tags: + - algorithms + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/Algorithm' + application/x-www-form-urlencoded: + schema: + $ref: '#/components/schemas/Algorithm' + multipart/form-data: + schema: + $ref: '#/components/schemas/Algorithm' + required: true + security: + - {} + responses: + '200': + content: + application/json: + schema: + $ref: '#/components/schemas/Algorithm' + description: '' + patch: + operationId: algorithms_partial_update + description: '' + parameters: + - in: path + name: id + schema: + type: integer + description: A unique integer value identifying this algorithm. + required: true + tags: + - algorithms + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/Algorithm' + application/x-www-form-urlencoded: + schema: + $ref: '#/components/schemas/Algorithm' + multipart/form-data: + schema: + $ref: '#/components/schemas/Algorithm' + required: true + security: + - {} + responses: + '200': + content: + application/json: + schema: + $ref: '#/components/schemas/Algorithm' + description: '' + delete: + operationId: algorithms_destroy + description: '' + parameters: + - in: path + name: id + schema: + type: integer + description: A unique integer value identifying this algorithm. + required: true + tags: + - algorithms + security: + - {} + responses: + '204': + description: No response body + /api/v1/algorithms/predict: + post: + operationId: algorithms_predict + description: Predict credit risk for a loan + parameters: + - in: query + name: classifier + schema: + type: string + description: The algorithm/classifier to use + required: true + examples: + Example1: + value: RandomForestClassifier + summary: Example 1 + - in: query + name: dataset + schema: + type: string + description: The name of the dataset + examples: + Example1: + value: german + summary: Example 1 + - in: query + name: status + schema: + type: string + description: The status of the algorithm + deprecated: true + examples: + Example1: + value: production + summary: Example 1 + - in: query + name: version + schema: + type: string + default: 0.0.1 + description: Algorithm version + required: true + examples: + Example1: + value: 0.0.1 + summary: Example 1 + tags: + - algorithms + requestBody: + content: + application/json: + schema: + type: object + additionalProperties: {} + description: Unspecified request body + application/x-www-form-urlencoded: + schema: + type: object + additionalProperties: {} + description: Unspecified request body + multipart/form-data: + schema: + type: object + additionalProperties: {} + description: Unspecified request body + security: + - {} + responses: + '200': + content: + application/json: + schema: + $ref: '#/components/schemas/PredictionResponse' + description: '' + /api/v1/datasets: + get: + operationId: datasets_list + description: '' + tags: + - datasets + security: + - {} + responses: + '200': + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Dataset' + description: '' + /api/v1/datasets/{id}: + get: + operationId: datasets_retrieve + description: '' + parameters: + - in: path + name: id + schema: + type: integer + description: A unique integer value identifying this dataset. + required: true + tags: + - datasets + security: + - {} + responses: + '200': + content: + application/json: + schema: + $ref: '#/components/schemas/Dataset' + description: '' + /api/v1/requests: + get: + operationId: requests_list + description: '' + tags: + - requests + security: + - {} + responses: + '200': + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/PredictionRequest' + description: '' + post: + operationId: requests_create + description: '' + tags: + - requests + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/PredictionRequest' + application/x-www-form-urlencoded: + schema: + $ref: '#/components/schemas/PredictionRequest' + multipart/form-data: + schema: + $ref: '#/components/schemas/PredictionRequest' + required: true + security: + - {} + responses: + '201': + content: + application/json: + schema: + $ref: '#/components/schemas/PredictionRequest' + description: '' + /api/v1/requests/{id}: + get: + operationId: requests_retrieve + description: '' + parameters: + - in: path + name: id + schema: + type: integer + description: A unique integer value identifying this prediction request. + required: true + tags: + - requests + security: + - {} + responses: + '200': + content: + application/json: + schema: + $ref: '#/components/schemas/PredictionRequest' + description: '' + put: + operationId: requests_update + description: '' + parameters: + - in: path + name: id + schema: + type: integer + description: A unique integer value identifying this prediction request. + required: true + tags: + - requests + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/PredictionRequest' + application/x-www-form-urlencoded: + schema: + $ref: '#/components/schemas/PredictionRequest' + multipart/form-data: + schema: + $ref: '#/components/schemas/PredictionRequest' + required: true + security: + - {} + responses: + '200': + content: + application/json: + schema: + $ref: '#/components/schemas/PredictionRequest' + description: '' + patch: + operationId: requests_partial_update + description: '' + parameters: + - in: path + name: id + schema: + type: integer + description: A unique integer value identifying this prediction request. + required: true + tags: + - requests + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/PredictionRequest' + application/x-www-form-urlencoded: + schema: + $ref: '#/components/schemas/PredictionRequest' + multipart/form-data: + schema: + $ref: '#/components/schemas/PredictionRequest' + required: true + security: + - {} + responses: + '200': + content: + application/json: + schema: + $ref: '#/components/schemas/PredictionRequest' + description: '' + delete: + operationId: requests_destroy + description: '' + parameters: + - in: path + name: id + schema: + type: integer + description: A unique integer value identifying this prediction request. + required: true + tags: + - requests + security: + - {} + responses: + '204': + description: No response body +components: + schemas: + Algorithm: + type: object + properties: + id: + type: integer + readOnly: true + classifier: + type: string + maxLength: 128 + description: + type: string + nullable: true + version: + type: string + maxLength: 128 + status: + type: string + maxLength: 128 + created_at: + type: string + format: date-time + readOnly: true + created_by: + type: string + maxLength: 128 + dataset: + type: string + nullable: true + required: + - classifier + - created_at + - created_by + - id + - status + - version + Dataset: + type: object + properties: + id: + type: integer + readOnly: true + name: + type: string + maxLength: 128 + region: + type: string + maxLength: 128 + required: + - id + - name + - region + PredictionRequest: + type: object + properties: + id: + type: integer + readOnly: true + input: + type: object + additionalProperties: {} + response: + type: object + additionalProperties: {} + prediction: + type: string + maxLength: 128 + feedback: + type: string + nullable: true + maxLength: 128 + notes: + type: string + nullable: true + created_at: + type: string + format: date-time + readOnly: true + created_by: + type: string + maxLength: 128 + algorithm: + type: integer + required: + - created_at + - created_by + - id + - input + - prediction + - response + PredictionResponse: + type: object + properties: + probability: + type: number + format: float + label: + type: string + method: + type: string + color: + type: string + wilkis_lambda: + type: number + format: float + pillais_trace: + type: number + format: float + hotelling_tawley: + type: number + format: float + roys_reatest_roots: + type: number + format: float + request_id: + type: integer + required: + - color + - hotelling_tawley + - label + - method + - pillais_trace + - probability + - request_id + - roys_reatest_roots + - wilkis_lambda +servers: + - url: http://127.0.0.1:8000 + description: server on localhost diff --git a/clients/java/README.md b/scorecard-client/README.md similarity index 100% rename from clients/java/README.md rename to scorecard-client/README.md diff --git a/scorecard-client/build.gradle b/scorecard-client/build.gradle new file mode 100644 index 0000000..b49b6fa --- /dev/null +++ b/scorecard-client/build.gradle @@ -0,0 +1,55 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +plugins { + id 'java-library' +} + +group = 'org.apache.fineract' +version = '0.1.0-SNAPSHOT' + +repositories { + mavenCentral() +} + +sourceSets { + main.java.srcDirs = ['src/main/java'] +} + +task execute(type:JavaExec) { + classpath = sourceSets.main.runtimeClasspath +} + +dependencies { + implementation 'io.swagger:swagger-annotations:1.5.24' + implementation "com.google.code.findbugs:jsr305:3.0.2" + + api 'com.squareup.okhttp3:okhttp:3.14.7' + api 'com.squareup.okhttp3:logging-interceptor:3.14.7' + api 'com.google.code.gson:gson:2.8.6' + api 'io.gsonfire:gson-fire:1.8.4' + + implementation group: 'org.apache.commons', name: 'commons-lang3', version: '3.10' + implementation 'javax.annotation:javax.annotation-api:1.3.2' + testImplementation 'junit:junit:4.13.1' +} + +javadoc { + options.tags = [ "http.response.details:a:Http Response Details" ] +} diff --git a/clients/java/docs/Algorithm.md b/scorecard-client/docs/Algorithm.md similarity index 100% rename from clients/java/docs/Algorithm.md rename to scorecard-client/docs/Algorithm.md diff --git a/clients/java/docs/AlgorithmsApi.md b/scorecard-client/docs/AlgorithmsApi.md similarity index 100% rename from clients/java/docs/AlgorithmsApi.md rename to scorecard-client/docs/AlgorithmsApi.md diff --git a/clients/java/docs/Dataset.md b/scorecard-client/docs/Dataset.md similarity index 100% rename from clients/java/docs/Dataset.md rename to scorecard-client/docs/Dataset.md diff --git a/clients/java/docs/DatasetsApi.md b/scorecard-client/docs/DatasetsApi.md similarity index 100% rename from clients/java/docs/DatasetsApi.md rename to scorecard-client/docs/DatasetsApi.md diff --git a/clients/java/docs/PredictionRequest.md b/scorecard-client/docs/PredictionRequest.md similarity index 100% rename from clients/java/docs/PredictionRequest.md rename to scorecard-client/docs/PredictionRequest.md diff --git a/clients/java/docs/PredictionResponse.md b/scorecard-client/docs/PredictionResponse.md similarity index 100% rename from clients/java/docs/PredictionResponse.md rename to scorecard-client/docs/PredictionResponse.md diff --git a/clients/java/docs/RequestsApi.md b/scorecard-client/docs/RequestsApi.md similarity index 100% rename from clients/java/docs/RequestsApi.md rename to scorecard-client/docs/RequestsApi.md diff --git a/clients/java/src/main/AndroidManifest.xml b/scorecard-client/src/main/AndroidManifest.xml similarity index 100% rename from clients/java/src/main/AndroidManifest.xml rename to scorecard-client/src/main/AndroidManifest.xml diff --git a/clients/java/src/main/java/org/apache/fineract/credit/scorecard/ApiCallback.java b/scorecard-client/src/main/java/org/apache/fineract/credit/scorecard/ApiCallback.java similarity index 100% rename from clients/java/src/main/java/org/apache/fineract/credit/scorecard/ApiCallback.java rename to scorecard-client/src/main/java/org/apache/fineract/credit/scorecard/ApiCallback.java diff --git a/clients/java/src/main/java/org/apache/fineract/credit/scorecard/ApiClient.java b/scorecard-client/src/main/java/org/apache/fineract/credit/scorecard/ApiClient.java similarity index 100% rename from clients/java/src/main/java/org/apache/fineract/credit/scorecard/ApiClient.java rename to scorecard-client/src/main/java/org/apache/fineract/credit/scorecard/ApiClient.java diff --git a/clients/java/src/main/java/org/apache/fineract/credit/scorecard/ApiException.java b/scorecard-client/src/main/java/org/apache/fineract/credit/scorecard/ApiException.java similarity index 100% rename from clients/java/src/main/java/org/apache/fineract/credit/scorecard/ApiException.java rename to scorecard-client/src/main/java/org/apache/fineract/credit/scorecard/ApiException.java diff --git a/clients/java/src/main/java/org/apache/fineract/credit/scorecard/ApiResponse.java b/scorecard-client/src/main/java/org/apache/fineract/credit/scorecard/ApiResponse.java similarity index 100% rename from clients/java/src/main/java/org/apache/fineract/credit/scorecard/ApiResponse.java rename to scorecard-client/src/main/java/org/apache/fineract/credit/scorecard/ApiResponse.java diff --git a/clients/java/src/main/java/org/apache/fineract/credit/scorecard/Configuration.java b/scorecard-client/src/main/java/org/apache/fineract/credit/scorecard/Configuration.java similarity index 100% rename from clients/java/src/main/java/org/apache/fineract/credit/scorecard/Configuration.java rename to scorecard-client/src/main/java/org/apache/fineract/credit/scorecard/Configuration.java diff --git a/clients/java/src/main/java/org/apache/fineract/credit/scorecard/GzipRequestInterceptor.java b/scorecard-client/src/main/java/org/apache/fineract/credit/scorecard/GzipRequestInterceptor.java similarity index 100% rename from clients/java/src/main/java/org/apache/fineract/credit/scorecard/GzipRequestInterceptor.java rename to scorecard-client/src/main/java/org/apache/fineract/credit/scorecard/GzipRequestInterceptor.java diff --git a/clients/java/src/main/java/org/apache/fineract/credit/scorecard/JSON.java b/scorecard-client/src/main/java/org/apache/fineract/credit/scorecard/JSON.java similarity index 100% rename from clients/java/src/main/java/org/apache/fineract/credit/scorecard/JSON.java rename to scorecard-client/src/main/java/org/apache/fineract/credit/scorecard/JSON.java diff --git a/clients/java/src/main/java/org/apache/fineract/credit/scorecard/Pair.java b/scorecard-client/src/main/java/org/apache/fineract/credit/scorecard/Pair.java similarity index 100% rename from clients/java/src/main/java/org/apache/fineract/credit/scorecard/Pair.java rename to scorecard-client/src/main/java/org/apache/fineract/credit/scorecard/Pair.java diff --git a/clients/java/src/main/java/org/apache/fineract/credit/scorecard/ProgressRequestBody.java b/scorecard-client/src/main/java/org/apache/fineract/credit/scorecard/ProgressRequestBody.java similarity index 100% rename from clients/java/src/main/java/org/apache/fineract/credit/scorecard/ProgressRequestBody.java rename to scorecard-client/src/main/java/org/apache/fineract/credit/scorecard/ProgressRequestBody.java diff --git a/clients/java/src/main/java/org/apache/fineract/credit/scorecard/ProgressResponseBody.java b/scorecard-client/src/main/java/org/apache/fineract/credit/scorecard/ProgressResponseBody.java similarity index 100% rename from clients/java/src/main/java/org/apache/fineract/credit/scorecard/ProgressResponseBody.java rename to scorecard-client/src/main/java/org/apache/fineract/credit/scorecard/ProgressResponseBody.java diff --git a/clients/java/src/main/java/org/apache/fineract/credit/scorecard/ServerConfiguration.java b/scorecard-client/src/main/java/org/apache/fineract/credit/scorecard/ServerConfiguration.java similarity index 100% rename from clients/java/src/main/java/org/apache/fineract/credit/scorecard/ServerConfiguration.java rename to scorecard-client/src/main/java/org/apache/fineract/credit/scorecard/ServerConfiguration.java diff --git a/clients/java/src/main/java/org/apache/fineract/credit/scorecard/ServerVariable.java b/scorecard-client/src/main/java/org/apache/fineract/credit/scorecard/ServerVariable.java similarity index 100% rename from clients/java/src/main/java/org/apache/fineract/credit/scorecard/ServerVariable.java rename to scorecard-client/src/main/java/org/apache/fineract/credit/scorecard/ServerVariable.java diff --git a/clients/java/src/main/java/org/apache/fineract/credit/scorecard/StringUtil.java b/scorecard-client/src/main/java/org/apache/fineract/credit/scorecard/StringUtil.java similarity index 100% rename from clients/java/src/main/java/org/apache/fineract/credit/scorecard/StringUtil.java rename to scorecard-client/src/main/java/org/apache/fineract/credit/scorecard/StringUtil.java diff --git a/clients/java/src/main/java/org/apache/fineract/credit/scorecard/auth/ApiKeyAuth.java b/scorecard-client/src/main/java/org/apache/fineract/credit/scorecard/auth/ApiKeyAuth.java similarity index 100% rename from clients/java/src/main/java/org/apache/fineract/credit/scorecard/auth/ApiKeyAuth.java rename to scorecard-client/src/main/java/org/apache/fineract/credit/scorecard/auth/ApiKeyAuth.java diff --git a/clients/java/src/main/java/org/apache/fineract/credit/scorecard/auth/Authentication.java b/scorecard-client/src/main/java/org/apache/fineract/credit/scorecard/auth/Authentication.java similarity index 100% rename from clients/java/src/main/java/org/apache/fineract/credit/scorecard/auth/Authentication.java rename to scorecard-client/src/main/java/org/apache/fineract/credit/scorecard/auth/Authentication.java diff --git a/clients/java/src/main/java/org/apache/fineract/credit/scorecard/auth/HttpBasicAuth.java b/scorecard-client/src/main/java/org/apache/fineract/credit/scorecard/auth/HttpBasicAuth.java similarity index 100% rename from clients/java/src/main/java/org/apache/fineract/credit/scorecard/auth/HttpBasicAuth.java rename to scorecard-client/src/main/java/org/apache/fineract/credit/scorecard/auth/HttpBasicAuth.java diff --git a/clients/java/src/main/java/org/apache/fineract/credit/scorecard/auth/HttpBearerAuth.java b/scorecard-client/src/main/java/org/apache/fineract/credit/scorecard/auth/HttpBearerAuth.java similarity index 100% rename from clients/java/src/main/java/org/apache/fineract/credit/scorecard/auth/HttpBearerAuth.java rename to scorecard-client/src/main/java/org/apache/fineract/credit/scorecard/auth/HttpBearerAuth.java diff --git a/clients/java/src/main/java/org/apache/fineract/credit/scorecard/models/Algorithm.java b/scorecard-client/src/main/java/org/apache/fineract/credit/scorecard/models/Algorithm.java similarity index 100% rename from clients/java/src/main/java/org/apache/fineract/credit/scorecard/models/Algorithm.java rename to scorecard-client/src/main/java/org/apache/fineract/credit/scorecard/models/Algorithm.java diff --git a/clients/java/src/main/java/org/apache/fineract/credit/scorecard/models/Dataset.java b/scorecard-client/src/main/java/org/apache/fineract/credit/scorecard/models/Dataset.java similarity index 100% rename from clients/java/src/main/java/org/apache/fineract/credit/scorecard/models/Dataset.java rename to scorecard-client/src/main/java/org/apache/fineract/credit/scorecard/models/Dataset.java diff --git a/clients/java/src/main/java/org/apache/fineract/credit/scorecard/models/PredictionRequest.java b/scorecard-client/src/main/java/org/apache/fineract/credit/scorecard/models/PredictionRequest.java similarity index 100% rename from clients/java/src/main/java/org/apache/fineract/credit/scorecard/models/PredictionRequest.java rename to scorecard-client/src/main/java/org/apache/fineract/credit/scorecard/models/PredictionRequest.java diff --git a/clients/java/src/main/java/org/apache/fineract/credit/scorecard/models/PredictionResponse.java b/scorecard-client/src/main/java/org/apache/fineract/credit/scorecard/models/PredictionResponse.java similarity index 100% rename from clients/java/src/main/java/org/apache/fineract/credit/scorecard/models/PredictionResponse.java rename to scorecard-client/src/main/java/org/apache/fineract/credit/scorecard/models/PredictionResponse.java diff --git a/clients/java/src/main/java/org/apache/fineract/credit/scorecard/services/AlgorithmsApi.java b/scorecard-client/src/main/java/org/apache/fineract/credit/scorecard/services/AlgorithmsApi.java similarity index 100% rename from clients/java/src/main/java/org/apache/fineract/credit/scorecard/services/AlgorithmsApi.java rename to scorecard-client/src/main/java/org/apache/fineract/credit/scorecard/services/AlgorithmsApi.java diff --git a/clients/java/src/main/java/org/apache/fineract/credit/scorecard/services/DatasetsApi.java b/scorecard-client/src/main/java/org/apache/fineract/credit/scorecard/services/DatasetsApi.java similarity index 100% rename from clients/java/src/main/java/org/apache/fineract/credit/scorecard/services/DatasetsApi.java rename to scorecard-client/src/main/java/org/apache/fineract/credit/scorecard/services/DatasetsApi.java diff --git a/clients/java/src/main/java/org/apache/fineract/credit/scorecard/services/RequestsApi.java b/scorecard-client/src/main/java/org/apache/fineract/credit/scorecard/services/RequestsApi.java similarity index 100% rename from clients/java/src/main/java/org/apache/fineract/credit/scorecard/services/RequestsApi.java rename to scorecard-client/src/main/java/org/apache/fineract/credit/scorecard/services/RequestsApi.java diff --git a/scorecard-plugin/README.md b/scorecard-plugin/README.md new file mode 100644 index 0000000..7c79239 --- /dev/null +++ b/scorecard-plugin/README.md @@ -0,0 +1,39 @@ +# Credit Scorecard Plugin for Apache Fineract + +This is a [_Plugin_ for Apache Fineract](https://github.com/apache/fineract/blob/develop/fineract-doc/src/docs/en/deployment.adoc). +(This work is inspired by the design approach of the [fineract-pentaho plugin](https://github.com/openMF/fineract-pentaho).) + +see [TODO](TODO.md) for possible future follow-up enhancement work. The TODO list was inherited from [fineract-pentaho plugin]((https://github.com/openMF/fineract-pentaho)) + + +## Build & Use + +This project is currently only tested against the very latest and greatest +bleeding edge Fineract `develop` branch. Building and using it against +older versions may be possible, but is not tested or documented here. + + git clone https://github.com/apache/fineract.git + cd fineract && ./gradlew bootJar && cd .. + + git clone https://github.com/apache/fineract-credit-scorecard.git + cd fineract-credit-scorecard && ./gradlew -x test distZip && cd .. + + ./fineract-credit-scorecard/scorecard-plugin/run + +The [`run`](run) script basically just creates the following directory structure: + + fineract-provider.jar + lib/scorecard-plugin.jar + lib/lib*.jar + +and then launches Apache Fineract with the Pentaho Plugin and all its JARs like this: + + java -Dloader.path=lib/ -jar fineract-provider.jar + +## Contribute + +If this Fineract plugin project is useful to you, please contribute back to it (and +Fineract) by raising Pull Requests yourself with any enhancements you make, and by helping +to maintain this project by helping other users on Issues and reviewing PR from others +(you will be promoted to committer on this project when you contribute). We recommend +that you _Watch_ and _Star_ this project on GitHub to make it easy to get notified. diff --git a/scorecard-plugin/TODO.md b/scorecard-plugin/TODO.md new file mode 100644 index 0000000..deb28c0 --- /dev/null +++ b/scorecard-plugin/TODO.md @@ -0,0 +1,17 @@ +# TODO + +1. move Java package name (back, again); see [FINERACT-1174](https://issues.apache.org/jira/browse/FINERACT-1174) + +1. how to avoid `files("../")` ? We would have to have an `api` artifact, and publish it.. + watch [FINERACT-1171](https://issues.apache.org/jira/browse/FINERACT-1171) + +1. inherit dependency management from Fineract, so that e.g. + JAX RS & commons lang versions don't have to be repeated. + Actually, perhaps JAX RS & commons lang should be considered + part of a (future..) Fineract Plugin API, and not need to be declared at all! + +1. run `ClasspathDuplicateTest`, on all such plugins + +1. run usual Fineract code style checks against plugin code like this + +1. build standalone "service", not just "plugin" diff --git a/scorecard-plugin/build.gradle b/scorecard-plugin/build.gradle new file mode 100644 index 0000000..8e3d14f --- /dev/null +++ b/scorecard-plugin/build.gradle @@ -0,0 +1,80 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +plugins { + id 'java-library' + id 'java-library-distribution' + id 'io.spring.dependency-management' version '1.0.10.RELEASE' +} + +repositories { + flatDir { + dirs "$rootDir/scorecard-client/build/libs" + } + mavenCentral() +} + +dependencies { + compileOnly( + // Fineract! + files("../../fineract/fineract-provider/build/classes/java/main/"), + + // Fineract dependencies (TODO version should *NOT* be specified here...) + 'org.slf4j:slf4j-api:1.7.30', + 'javax.ws.rs:jsr311-api:1.1.1', + 'org.apache.openjpa:openjpa:3.1.2', + 'org.apache.commons:commons-lang3:3.10', + 'org.springframework:spring-context:5.2.6.RELEASE', + 'org.springframework.data:spring-data-commons:2.3.4.RELEASE', + 'org.springframework.security:spring-security-core:5.3.4.RELEASE', + 'org.springframework.boot:spring-boot-starter-data-jpa:2.3.5.RELEASE' + ) + + implementation( + // Fineract Credit Scorecard Dependencies + project(":scorecard-client"), + 'com.google.code.gson:gson:2.8.7' + ) + + testImplementation 'org.junit.jupiter:junit-jupiter-api:5.7.0' + testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.7.0' +} + +tasks.named('test') { + // Use JUnit Platform for unit tests. + useJUnitPlatform() +} + +distributions { + main { + contents { + into('/') { + from "README.md", "../LICENSE", "run", "run.bat" + } + + into('/lib') { + from(jar) + } + + eachFile { FileCopyDetails fcp -> + fcp.relativePath = new RelativePath(true, fcp.relativePath.pathString.replace('scorecard-plugin/', '')) + } + } + } +} diff --git a/scorecard-plugin/run b/scorecard-plugin/run new file mode 100644 index 0000000..5e36220 --- /dev/null +++ b/scorecard-plugin/run @@ -0,0 +1,13 @@ +#!/usr/bin/env bash +set -euxo pipefail + +# Copy Apache Fineract JAR +mkdir -p build/run +cp ./fineract/fineract-provider/build/libs/fineract-provider*.jar build/run + +# Add our scorecard-plugin reporting plugin, like so: +unzip ./FINCS/scorecard-plugin/build/distributions/scorecard-plugin.zip -d build/run/ +rm build/run/scorecard-plugin.jar + +# Start! +java -Dloader.path=build/run/lib/ -jar build/run/fineract-provider.jar diff --git a/scorecard-plugin/run.bat b/scorecard-plugin/run.bat new file mode 100644 index 0000000..32b7c40 --- /dev/null +++ b/scorecard-plugin/run.bat @@ -0,0 +1,33 @@ +@REM +@REM Licensed to the Apache Software Foundation (ASF) under one +@REM or more contributor license agreements. See the NOTICE file +@REM distributed with this work for additional information +@REM regarding copyright ownership. The ASF licenses this file +@REM to you under the Apache License, Version 2.0 (the +@REM "License"); you may not use this file except in compliance +@REM with the License. You may obtain a copy of the License at +@REM +@REM http://www.apache.org/licenses/LICENSE-2.0 +@REM +@REM Unless required by applicable law or agreed to in writing, +@REM software distributed under the License is distributed on an +@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +@REM KIND, either express or implied. See the License for the +@REM specific language governing permissions and limitations +@REM under the License. +@REM + +@echo on + +rem Copy Apache Fineract JAR +md build\run +copy .\fineract\fineract-provider\build\libs\fineract-provider.jar build\run + +rem Add our scorecard-plugin reporting plugin, like so: + +powershell -command "Expand-Archive -Force '.\fineract-credit-scorecard\scorecard-plugin\build\distributions\scorecard-plugin.zip' 'build\run'" +del .\build\run\scorecard-plugin.jar + +rem Start! + +java -Dloader.path=build\run\lib\ -jar build\run\fineract-provider.jar \ No newline at end of file diff --git a/scorecard-plugin/src/main/java/org/apache/fineract/portfolio/creditscorecard/service/CreditScorecardAssemblerImpl.java b/scorecard-plugin/src/main/java/org/apache/fineract/portfolio/creditscorecard/service/CreditScorecardAssemblerImpl.java new file mode 100644 index 0000000..a1f8f04 --- /dev/null +++ b/scorecard-plugin/src/main/java/org/apache/fineract/portfolio/creditscorecard/service/CreditScorecardAssemblerImpl.java @@ -0,0 +1,255 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.fineract.portfolio.creditscorecard.service; + +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; +import org.apache.fineract.infrastructure.core.api.JsonCommand; +import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper; +import org.apache.fineract.portfolio.creditscorecard.annotation.ScorecardService; +import org.apache.fineract.portfolio.creditscorecard.domain.CreditScorecard; +import org.apache.fineract.portfolio.creditscorecard.domain.CreditScorecardFeature; +import org.apache.fineract.portfolio.creditscorecard.domain.FeatureCriteria; +import org.apache.fineract.portfolio.creditscorecard.domain.FeatureCriteriaScore; +import org.apache.fineract.portfolio.creditscorecard.domain.MLScorecard; +import org.apache.fineract.portfolio.creditscorecard.domain.MLScorecardFields; +import org.apache.fineract.portfolio.creditscorecard.domain.RuleBasedScorecard; +import org.apache.fineract.portfolio.creditscorecard.domain.StatScorecard; +import org.apache.fineract.portfolio.loanproduct.domain.LoanProduct; +import org.apache.fineract.portfolio.loanproduct.domain.LoanProductScorecardFeature; +import org.apache.fineract.portfolio.loanproduct.domain.LoanProductScorecardFeatureRepositoryWrapper; +import org.springframework.beans.factory.annotation.Autowired; + +@ScorecardService(name = "CreditScorecardAssembler") +public class CreditScorecardAssemblerImpl implements CreditScorecardAssembler { + + private final FromJsonHelper fromApiJsonHelper; + private final CreditScorecardReadPlatformService readPlatformService; + private final LoanProductScorecardFeatureRepositoryWrapper productFeatureRepository; + + @Autowired + public CreditScorecardAssemblerImpl(final FromJsonHelper fromApiJsonHelper, + final CreditScorecardReadPlatformService readPlatformService, + final LoanProductScorecardFeatureRepositoryWrapper productFeatureRepository) { + this.fromApiJsonHelper = fromApiJsonHelper; + this.readPlatformService = readPlatformService; + this.productFeatureRepository = productFeatureRepository; + } + + @Override + public CreditScorecard assembleFrom(final JsonElement element) { + CreditScorecard creditScorecard = null; + + if (element.isJsonObject()) { + + final String scoringMethod = this.fromApiJsonHelper.extractStringNamed("scoringMethod", element); + final String scoringModel = this.fromApiJsonHelper.extractStringNamed("scoringModel", element); + + RuleBasedScorecard ruleBasedScorecard = null; + StatScorecard statScorecard = null; + MLScorecard mlScorecard = null; + + if (scoringMethod.equalsIgnoreCase("ruleBased")) { + ruleBasedScorecard = this.assembleRuleBasedScorecard(element); + } + + if (scoringMethod.equalsIgnoreCase("ml")) { + mlScorecard = this.assembleMLScorecard(element); + } + + if (scoringMethod.equalsIgnoreCase("stat")) { + statScorecard = this.assembleStatScorecard(element); + } + + creditScorecard = new CreditScorecard(scoringMethod, scoringModel, ruleBasedScorecard, statScorecard, mlScorecard); + } + + return creditScorecard; + } + + public RuleBasedScorecard assembleRuleBasedScorecard(final JsonElement element) { + + final RuleBasedScorecard ruleBasedScorecard = new RuleBasedScorecard(); + + final List<FeatureCriteriaScore> criteriaScores = new ArrayList<>(); + + if (element.isJsonObject()) { + final JsonObject topLevelJsonElement = element.getAsJsonObject(); + + final String dateFormat = this.fromApiJsonHelper.extractDateFormatParameter(topLevelJsonElement); + final Locale locale = this.fromApiJsonHelper.extractLocaleParameter(topLevelJsonElement); + + final String rbScorecardParameterName = "ruleBasedScorecard"; + if (topLevelJsonElement.has(rbScorecardParameterName) && topLevelJsonElement.get(rbScorecardParameterName).isJsonObject()) { + final JsonObject rbScorecardElement = topLevelJsonElement.getAsJsonObject(rbScorecardParameterName); + + final String criteriaScoresParameterName = "criteriaScores"; + if (rbScorecardElement.has(criteriaScoresParameterName) + && rbScorecardElement.get(criteriaScoresParameterName).isJsonArray()) { + final JsonArray array = rbScorecardElement.get(criteriaScoresParameterName).getAsJsonArray(); + for (int i = 0; i < array.size(); i++) { + + final JsonObject criteriaScoreElement = array.get(i).getAsJsonObject(); + + final Long featureId = this.fromApiJsonHelper.extractLongNamed("featureId", criteriaScoreElement); + final String value = this.fromApiJsonHelper.extractStringNamed("value", criteriaScoreElement); + + final LoanProductScorecardFeature lpScorecardFeature = this.productFeatureRepository + .findOneWithNotFoundDetection(featureId); + + criteriaScores.add(new FeatureCriteriaScore(lpScorecardFeature, value)); + + } + + } else { + return null; + + } + } + } + + ruleBasedScorecard.setCriteriaScores(criteriaScores); + + return ruleBasedScorecard; + } + + public StatScorecard assembleStatScorecard(final JsonElement element) { + + StatScorecard statScorecard = null; + + if (element.isJsonObject()) { + + final JsonObject topLevelJsonElement = element.getAsJsonObject(); + + final String dateFormat = this.fromApiJsonHelper.extractDateFormatParameter(topLevelJsonElement); + final Locale locale = this.fromApiJsonHelper.extractLocaleParameter(topLevelJsonElement); + + final String statScorecardParameterName = "statScorecard"; + if (topLevelJsonElement.has(statScorecardParameterName) && topLevelJsonElement.get(statScorecardParameterName).isJsonObject()) { + final JsonObject scorecardDataElement = topLevelJsonElement.getAsJsonObject(statScorecardParameterName); + + statScorecard = new StatScorecard( + new MLScorecardFields(this.fromApiJsonHelper.extractIntegerWithLocaleNamed("age", scorecardDataElement), + this.fromApiJsonHelper.extractStringNamed("sex", scorecardDataElement), + this.fromApiJsonHelper.extractStringNamed("job", scorecardDataElement), + this.fromApiJsonHelper.extractStringNamed("housing", scorecardDataElement), + this.fromApiJsonHelper.extractBigDecimalNamed("creditAmount", scorecardDataElement, locale), + this.fromApiJsonHelper.extractIntegerNamed("duration", scorecardDataElement, locale), + this.fromApiJsonHelper.extractStringNamed("purpose", scorecardDataElement))); + } + } + return statScorecard; + } + + public MLScorecard assembleMLScorecard(final JsonElement element) { + + MLScorecard mlScorecard = null; + + if (element.isJsonObject()) { + + final JsonObject topLevelJsonElement = element.getAsJsonObject(); + + final String dateFormat = this.fromApiJsonHelper.extractDateFormatParameter(topLevelJsonElement); + final Locale locale = this.fromApiJsonHelper.extractLocaleParameter(topLevelJsonElement); + + final String mlScorecardParameterName = "mlScorecard"; + if (topLevelJsonElement.has(mlScorecardParameterName) && topLevelJsonElement.get(mlScorecardParameterName).isJsonObject()) { + final JsonObject scorecardDataElement = topLevelJsonElement.getAsJsonObject(mlScorecardParameterName); + + mlScorecard = new MLScorecard( + new MLScorecardFields(this.fromApiJsonHelper.extractIntegerWithLocaleNamed("age", scorecardDataElement), + this.fromApiJsonHelper.extractStringNamed("sex", scorecardDataElement), + this.fromApiJsonHelper.extractStringNamed("job", scorecardDataElement), + this.fromApiJsonHelper.extractStringNamed("housing", scorecardDataElement), + this.fromApiJsonHelper.extractBigDecimalNamed("creditAmount", scorecardDataElement, locale), + this.fromApiJsonHelper.extractIntegerNamed("duration", scorecardDataElement, locale), + this.fromApiJsonHelper.extractStringNamed("purpose", scorecardDataElement))); + } + } + return mlScorecard; + } + + + @Override + public List<LoanProductScorecardFeature> assembleListOfProductScoringFeatures(final JsonCommand command, LoanProduct loanProduct) { + + final List<LoanProductScorecardFeature> loanProductFeatures = new ArrayList<>(); + + // JsonCommand incentivesCommand = JsonCommand.fromExistingCommand(command, incentiveElement); + // chartSlab.slabFields().validateChartSlabPlatformRules(chartSlabsCommand, baseDataValidator, locale); + + if (command.parameterExists("scorecardFeatures")) { + final JsonArray featuresArray = command.arrayOfParameterNamed("scorecardFeatures"); + if (featuresArray != null) { + for (int i = 0; i < featuresArray.size(); i++) { + + final JsonObject jsonObject = featuresArray.get(i).getAsJsonObject(); + if (jsonObject.has("featureId")) { + final Long id = jsonObject.get("featureId").getAsLong(); + + final BigDecimal weightage = jsonObject.get("weightage").getAsBigDecimal(); + final Integer greenMin = jsonObject.get("greenMin").getAsInt(); + final Integer greenMax = jsonObject.get("greenMax").getAsInt(); + final Integer amberMin = jsonObject.get("amberMin").getAsInt(); + final Integer amberMax = jsonObject.get("amberMax").getAsInt(); + final Integer redMin = jsonObject.get("redMin").getAsInt(); + final Integer redMax = jsonObject.get("redMax").getAsInt(); + + final List<FeatureCriteria> criteria = this + .assembleListOfProductScoringFeatureCriteriaScores(jsonObject.get("criteriaScores").getAsJsonArray()); + + final CreditScorecardFeature feature = this.readPlatformService.findOneFeatureWithNotFoundDetection(id); + + final LoanProductScorecardFeature loanProductFeature = new LoanProductScorecardFeature(feature, weightage, greenMin, + greenMax, amberMin, amberMax, redMin, redMax); + + loanProductFeature.setFeatureCriteria(criteria); + loanProductFeatures.add(loanProductFeature); + } + } + } + } + + return loanProductFeatures; + } + + private List<FeatureCriteria> assembleListOfProductScoringFeatureCriteriaScores(final JsonArray jsonArray) { + + final List<FeatureCriteria> featureCriteria = new ArrayList<>(); + + if (jsonArray != null) { + for (int i = 0; i < jsonArray.size(); i++) { + + final JsonObject jsonObject = jsonArray.get(i).getAsJsonObject(); + final BigDecimal score = jsonObject.get("score").getAsBigDecimal(); + final String scoreCriteria = jsonObject.get("criteria").getAsString(); + + featureCriteria.add(new FeatureCriteria(scoreCriteria, score)); + } + } + + return featureCriteria; + } + +} diff --git a/settings.gradle b/scorecard-plugin/src/main/java/org/apache/fineract/portfolio/creditscorecard/service/CreditScorecardFeatureDropdownReadPlatformService.java similarity index 68% copy from settings.gradle copy to scorecard-plugin/src/main/java/org/apache/fineract/portfolio/creditscorecard/service/CreditScorecardFeatureDropdownReadPlatformService.java index e278339..e3f0eb7 100644 --- a/settings.gradle +++ b/scorecard-plugin/src/main/java/org/apache/fineract/portfolio/creditscorecard/service/CreditScorecardFeatureDropdownReadPlatformService.java @@ -1,20 +1,32 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -rootProject.name = 'fineract-credit-scorecard' +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.fineract.portfolio.creditscorecard.service; + +import java.util.List; +import org.apache.fineract.infrastructure.core.data.EnumOptionData; + +public interface CreditScorecardFeatureDropdownReadPlatformService { + + List<EnumOptionData> retrieveValueTypes(); + + List<EnumOptionData> retrieveDataTypes(); + + List<EnumOptionData> retrieveCategoryTypes(); + +} diff --git a/scorecard-plugin/src/main/java/org/apache/fineract/portfolio/creditscorecard/service/CreditScorecardFeatureDropdownReadPlatformServiceImpl.java b/scorecard-plugin/src/main/java/org/apache/fineract/portfolio/creditscorecard/service/CreditScorecardFeatureDropdownReadPlatformServiceImpl.java new file mode 100644 index 0000000..e92fa76 --- /dev/null +++ b/scorecard-plugin/src/main/java/org/apache/fineract/portfolio/creditscorecard/service/CreditScorecardFeatureDropdownReadPlatformServiceImpl.java @@ -0,0 +1,55 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.fineract.portfolio.creditscorecard.service; + +import java.util.Arrays; +import java.util.List; +import org.apache.fineract.infrastructure.core.data.EnumOptionData; +import org.apache.fineract.portfolio.creditscorecard.domain.FeatureCategory; +import org.apache.fineract.portfolio.creditscorecard.domain.FeatureDataType; +import org.apache.fineract.portfolio.creditscorecard.domain.FeatureValueType; +import org.springframework.stereotype.Service; + +@Service +public class CreditScorecardFeatureDropdownReadPlatformServiceImpl implements CreditScorecardFeatureDropdownReadPlatformService { + + @Override + public List<EnumOptionData> retrieveValueTypes() { + return Arrays.asList(CreditScorecardEnumerations.featureValueType(FeatureValueType.BINARY), + CreditScorecardEnumerations.featureValueType(FeatureValueType.NOMINAL), + CreditScorecardEnumerations.featureValueType(FeatureValueType.RATIO), + CreditScorecardEnumerations.featureValueType(FeatureValueType.INTERVAL)); + } + + @Override + public List<EnumOptionData> retrieveDataTypes() { + return Arrays.asList(CreditScorecardEnumerations.featureDataType(FeatureDataType.STRING), + CreditScorecardEnumerations.featureDataType(FeatureDataType.NUMERIC), + CreditScorecardEnumerations.featureDataType(FeatureDataType.DATE)); + } + + @Override + public List<EnumOptionData> retrieveCategoryTypes() { + return Arrays.asList(CreditScorecardEnumerations.featureCategory(FeatureCategory.INDIVIDUAL), + CreditScorecardEnumerations.featureCategory(FeatureCategory.ORGANISATION), + CreditScorecardEnumerations.featureCategory(FeatureCategory.COUNTRY), + CreditScorecardEnumerations.featureCategory(FeatureCategory.CREDIT_HISTORY), + CreditScorecardEnumerations.featureCategory(FeatureCategory.LOAN)); + } +} diff --git a/scorecard-plugin/src/main/java/org/apache/fineract/portfolio/creditscorecard/service/CreditScorecardReadPlatformServiceImpl.java b/scorecard-plugin/src/main/java/org/apache/fineract/portfolio/creditscorecard/service/CreditScorecardReadPlatformServiceImpl.java new file mode 100644 index 0000000..d99255e --- /dev/null +++ b/scorecard-plugin/src/main/java/org/apache/fineract/portfolio/creditscorecard/service/CreditScorecardReadPlatformServiceImpl.java @@ -0,0 +1,228 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.fineract.portfolio.creditscorecard.service; + +import java.math.BigDecimal; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.Collection; +import java.util.List; +import java.util.stream.Collectors; +import org.apache.fineract.infrastructure.core.data.EnumOptionData; +import org.apache.fineract.infrastructure.core.service.RoutingDataSource; +import org.apache.fineract.portfolio.charge.exception.ChargeIsNotActiveException; +import org.apache.fineract.portfolio.creditscorecard.annotation.ScorecardService; +import org.apache.fineract.portfolio.creditscorecard.data.CreditScorecardData; +import org.apache.fineract.portfolio.creditscorecard.data.CreditScorecardFeatureData; +import org.apache.fineract.portfolio.creditscorecard.data.MLScorecardData; +import org.apache.fineract.portfolio.creditscorecard.data.RuleBasedScorecardData; +import org.apache.fineract.portfolio.creditscorecard.data.ScorecardFeatureCriteriaData; +import org.apache.fineract.portfolio.creditscorecard.data.StatScorecardData; +import org.apache.fineract.portfolio.creditscorecard.domain.CreditScorecard; +import org.apache.fineract.portfolio.creditscorecard.domain.CreditScorecardFeature; +import org.apache.fineract.portfolio.creditscorecard.domain.CreditScorecardFeatureRepository; +import org.apache.fineract.portfolio.creditscorecard.domain.CreditScorecardRepository; +import org.apache.fineract.portfolio.creditscorecard.domain.FeatureNotFoundException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.core.RowMapper; + +@ScorecardService(name = "CreditScorecardReadPlatformService") +public class CreditScorecardReadPlatformServiceImpl implements CreditScorecardReadPlatformService { + + private final JdbcTemplate jdbcTemplate; + private final CreditScorecardRepository scorecardRepository; + private final CreditScorecardFeatureRepository featureRepository; + private final CreditScorecardFeatureDropdownReadPlatformService creditScorecardFeatureDropdownReadPlatformService; + + @Autowired + public CreditScorecardReadPlatformServiceImpl(final RoutingDataSource dataSource, final CreditScorecardRepository scorecardRepository, + final CreditScorecardFeatureRepository featureRepository, + final CreditScorecardFeatureDropdownReadPlatformService creditScorecardFeatureDropdownReadPlatformService) { + this.jdbcTemplate = new JdbcTemplate(dataSource); + this.scorecardRepository = scorecardRepository; + this.featureRepository = featureRepository; + this.creditScorecardFeatureDropdownReadPlatformService = creditScorecardFeatureDropdownReadPlatformService; + } + + @Override + public CreditScorecardFeatureData retrieveNewScorecardFeatureDetails() { + + final Collection<EnumOptionData> valueTypeOptions = this.creditScorecardFeatureDropdownReadPlatformService.retrieveValueTypes(); + final Collection<EnumOptionData> dataTypeOptions = this.creditScorecardFeatureDropdownReadPlatformService.retrieveDataTypes(); + final Collection<EnumOptionData> categoryOptions = this.creditScorecardFeatureDropdownReadPlatformService.retrieveCategoryTypes(); + + return CreditScorecardFeatureData.template(valueTypeOptions, dataTypeOptions, categoryOptions); + } + + @Override + public Collection<CreditScorecardFeatureData> retrieveLoanProductFeatures(Long productId) { + final ScorecardFeatureMapper rm = new ScorecardFeatureMapper(); + + final String sql = "SELECT " + rm.featureSchema() + + " WHERE scf.is_deleted=false AND scf.is_active=true AND lpscf.product_loan_id=? "; + + final Collection<CreditScorecardFeatureData> scorecardFeatures = this.jdbcTemplate.query(sql, rm, new Object[] { productId }); + + for (CreditScorecardFeatureData scorecardFeature : scorecardFeatures) { + Collection<ScorecardFeatureCriteriaData> criteriaData = this.retrieveFeatureCriteria(scorecardFeature.getId()); + scorecardFeature.getCriteria().addAll(criteriaData); + } + return scorecardFeatures; + } + + @Override + public CreditScorecardData retrieveCreditScorecard(Long scorecardId) { + final CreditScorecard scorecard = this.scorecardRepository.findById(scorecardId).orElse(null); + + if (scorecard == null) { + return null; + } + + CreditScorecardData scorecardData = null; + + final String method = scorecard.getScoringMethod(); + switch (method) { + case "ml": + final MLScorecardData mlScorecardData = MLScorecardData.instance(scorecard.getMlScorecard()); + scorecardData = CreditScorecardData.mlInstance(scorecard.getId(), scorecard.getScoringMethod(), scorecard.getScoringModel(), + mlScorecardData); + break; + + case "stat": + final StatScorecardData statScorecardData = StatScorecardData.instance(scorecard.getStatScorecard()); + scorecardData = CreditScorecardData.statInstance(scorecard.getId(), scorecard.getScoringMethod(), + scorecard.getScoringModel(), statScorecardData); + break; + + case "ruleBased": + final RuleBasedScorecardData ruleBasedScorecardData = RuleBasedScorecardData.instance(scorecard.getRuleBasedScorecard()); + scorecardData = CreditScorecardData.ruleBasedInstance(scorecard.getId(), scorecard.getScoringMethod(), + scorecard.getScoringModel(), ruleBasedScorecardData); + break; + + default: + break; + } + + return scorecardData; + } + + @Override + public CreditScorecardData loanScorecardTemplate() { + return CreditScorecardData.loanTemplate(); + } + + @Override + public CreditScorecardData loanScorecardTemplate(CreditScorecardData scorecard) { + return CreditScorecardData.loanScorecardWithTemplate(scorecard); + } + + private Collection<ScorecardFeatureCriteriaData> retrieveFeatureCriteria(Long featureId) { + final FeatureCriteriaMapper rm = new FeatureCriteriaMapper(); + + final String sql = "SELECT " + rm.featureCriteriaSchema() + " WHERE crit.product_loan_scorecard_feature_id=?"; + + return this.jdbcTemplate.query(sql, rm, new Object[] { featureId }); + } + + private static final class FeatureCriteriaMapper implements RowMapper<ScorecardFeatureCriteriaData> { + + public String featureCriteriaSchema() { + return "crit.id as id, crit.criteria as criteria, crit.score as score " + "FROM m_scorecard_feature_criteria crit"; + } + + @Override + public ScorecardFeatureCriteriaData mapRow(ResultSet rs, int rowNum) throws SQLException { + final Long id = rs.getLong("id"); + final String criteria = rs.getString("criteria"); + final int score = rs.getInt("score"); + return ScorecardFeatureCriteriaData.instance(id, criteria, BigDecimal.valueOf(score)); + } + } + + private static final class ScorecardFeatureMapper implements RowMapper<CreditScorecardFeatureData> { + + public String featureSchema() { + return "scf.id as featureId, scf.name as name, scf.value_type_enum as valueType, " + + "scf.data_type_enum as dataType, scf.category_enum as category, scf.is_active as active, " + + "lpscf.id as id, lpscf.weightage as weightage, lpscf.green_min as greenMin, " + + "lpscf.green_max as greenMax, lpscf.amber_min as amberMin, lpscf.amber_max as amberMax, " + + "lpscf.red_min as redMin, lpscf.red_max as redMax " + + + "FROM m_product_loan_scorecard_feature lpscf " + + "JOIN m_credit_scorecard_feature scf ON scf.id = lpscf.scorecard_feature_id "; + } + + @Override + public CreditScorecardFeatureData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException { + final Long id = rs.getLong("id"); + + final Long featureId = rs.getLong("featureId"); + final String name = rs.getString("name"); + + final int valueType = rs.getInt("valueType"); + final EnumOptionData valueTypeEnum = CreditScorecardEnumerations.featureValueType(valueType); + + final int dataType = rs.getInt("dataType"); + final EnumOptionData dataTypeEnum = CreditScorecardEnumerations.featureDataType(dataType); + + final int category = rs.getInt("category"); + final EnumOptionData categoryEnum = CreditScorecardEnumerations.featureCategory(category); + + final boolean active = rs.getBoolean("active"); + + final BigDecimal weightage = rs.getBigDecimal("weightage"); + + final int greenMin = rs.getInt("greenMin"); + final int greenMax = rs.getInt("greenMax"); + + final int amberMin = rs.getInt("amberMin"); + final int amberMax = rs.getInt("amberMax"); + + final int redMin = rs.getInt("redMin"); + final int redMax = rs.getInt("redMax"); + + return CreditScorecardFeatureData.instance(id, featureId, name, valueTypeEnum, dataTypeEnum, categoryEnum, active, weightage, + greenMin, greenMax, amberMin, amberMax, redMin, redMax); + } + } + + @Override + public CreditScorecardFeature findOneFeatureWithNotFoundDetection(final Long id) { + + final CreditScorecardFeature scorecardFeature = this.featureRepository.findById(id) + .orElseThrow(() -> new FeatureNotFoundException(id)); + if (scorecardFeature.isDeleted()) { + throw new FeatureNotFoundException(id); + } + if (!scorecardFeature.isActive()) { + throw new ChargeIsNotActiveException(id, scorecardFeature.getName()); + } + + return scorecardFeature; + } + + @Override + public List<CreditScorecardFeature> findAllFeaturesWithNotFoundDetection() { + return this.featureRepository.findAll().stream().filter(CreditScorecardFeature::isActive).filter(feature -> !feature.isDeleted()) + .collect(Collectors.toList()); + } + +} diff --git a/scorecard-plugin/src/main/java/org/apache/fineract/portfolio/creditscorecard/service/CreditScorecardWritePlatformServiceImpl.java b/scorecard-plugin/src/main/java/org/apache/fineract/portfolio/creditscorecard/service/CreditScorecardWritePlatformServiceImpl.java new file mode 100644 index 0000000..178a3b0 --- /dev/null +++ b/scorecard-plugin/src/main/java/org/apache/fineract/portfolio/creditscorecard/service/CreditScorecardWritePlatformServiceImpl.java @@ -0,0 +1,376 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.fineract.portfolio.creditscorecard.service; + +import java.math.BigDecimal; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import javax.persistence.PersistenceException; +import org.apache.commons.lang3.exception.ExceptionUtils; +import org.apache.fineract.credit.scorecard.ApiException; +import org.apache.fineract.credit.scorecard.Configuration; +import org.apache.fineract.credit.scorecard.models.PredictionResponse; +import org.apache.fineract.credit.scorecard.services.AlgorithmsApi; +import org.apache.fineract.infrastructure.core.api.JsonCommand; +import org.apache.fineract.infrastructure.core.data.CommandProcessingResult; +import org.apache.fineract.infrastructure.core.data.CommandProcessingResultBuilder; +import org.apache.fineract.infrastructure.core.data.EnumOptionData; +import org.apache.fineract.infrastructure.core.exception.PlatformDataIntegrityException; +import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext; +import org.apache.fineract.portfolio.creditscorecard.annotation.ScorecardService; +import org.apache.fineract.portfolio.creditscorecard.domain.CreditScorecard; +import org.apache.fineract.portfolio.creditscorecard.domain.CreditScorecardFeature; +import org.apache.fineract.portfolio.creditscorecard.domain.CreditScorecardFeatureRepository; +import org.apache.fineract.portfolio.creditscorecard.domain.CreditScorecardRepository; +import org.apache.fineract.portfolio.creditscorecard.domain.FeatureConfiguration; +import org.apache.fineract.portfolio.creditscorecard.domain.FeatureCriteria; +import org.apache.fineract.portfolio.creditscorecard.domain.FeatureCriteriaScore; +import org.apache.fineract.portfolio.creditscorecard.domain.MLScorecard; +import org.apache.fineract.portfolio.creditscorecard.domain.MLScorecardFields; +import org.apache.fineract.portfolio.creditscorecard.domain.RuleBasedScorecard; +import org.apache.fineract.portfolio.creditscorecard.domain.StatScorecard; +import org.apache.fineract.portfolio.creditscorecard.exception.FeatureCannotBeDeletedException; +import org.apache.fineract.portfolio.creditscorecard.exception.FeatureNotFoundException; +import org.apache.fineract.portfolio.creditscorecard.serialization.CreditScorecardApiJsonHelper; +import org.apache.fineract.portfolio.loanaccount.domain.Loan; +import org.apache.fineract.portfolio.loanproduct.domain.LoanProduct; +import org.apache.fineract.portfolio.loanproduct.domain.LoanProductRepository; +import org.apache.fineract.portfolio.loanproduct.domain.LoanProductScorecardFeature; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.PropertySource; +import org.springframework.dao.DataIntegrityViolationException; +import org.springframework.orm.jpa.JpaSystemException; +import org.springframework.transaction.annotation.Transactional; + +@PropertySource(value = "classpath:scorecard-client.properties") +@ScorecardService(name = "CreditScorecardWritePlatformService") +public class CreditScorecardWritePlatformServiceImpl implements CreditScorecardWritePlatformService { + + private static final Logger LOG = LoggerFactory.getLogger(CreditScorecardWritePlatformServiceImpl.class); + + @Value("${fineract.credit.scorecard.uid}") + String username; + + @Value("${fineract.credit.scorecard.password}") + String password; + + @Value("${fineract.credit.scorecard.baseUrl}") + String baseUrl; + + private final PlatformSecurityContext context; + + private final LoanProductRepository loanProductRepository; + private final CreditScorecardApiJsonHelper fromApiJsonDeserializer; + private final CreditScorecardFeatureRepository featureRepository; + private final CreditScorecardRepository scorecardRepository; + + AlgorithmsApi scorecardApiClient; + + @Autowired + public CreditScorecardWritePlatformServiceImpl(final PlatformSecurityContext context, + final CreditScorecardApiJsonHelper fromApiJsonDeserializer, + final LoanProductRepository loanProductRepository, + final CreditScorecardFeatureRepository featureRepository, + final CreditScorecardRepository scorecardRepository) { + this.context = context; + this.fromApiJsonDeserializer = fromApiJsonDeserializer; + this.loanProductRepository = loanProductRepository; + this.featureRepository = featureRepository; + this.scorecardRepository = scorecardRepository; + + } + + private void initScorecardClient() { + LOG.warn("Base URL at init : {}", baseUrl); + this.scorecardApiClient = new AlgorithmsApi(Configuration.getDefaultApiClient().setBasePath(baseUrl)); + } + + @Override + public CommandProcessingResult createScoringFeature(JsonCommand command) { + + try { + this.context.authenticatedUser(); + this.fromApiJsonDeserializer.validateFeatureForCreate(command.json()); + + final CreditScorecardFeature scorecardFeature = CreditScorecardFeature.fromJson(command); + this.featureRepository.save(scorecardFeature); + + return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withEntityId(scorecardFeature.getId()).build(); + } catch (final JpaSystemException | DataIntegrityViolationException dve) { + handleDataIntegrityIssues(command, dve.getMostSpecificCause(), dve); + return CommandProcessingResult.empty(); + } catch (final PersistenceException dve) { + Throwable throwable = ExceptionUtils.getRootCause(dve.getCause()); + handleDataIntegrityIssues(command, throwable, dve); + return CommandProcessingResult.empty(); + } + } + + @Override + public CommandProcessingResult updateScoringFeature(Long featureId, JsonCommand command) { + + try { + this.context.authenticatedUser(); + this.fromApiJsonDeserializer.validateFeatureForUpdate(command.json()); + + final CreditScorecardFeature featureForUpdate = this.featureRepository.findById(featureId) + .orElseThrow(() -> new FeatureNotFoundException(featureId)); + + final Map<String, Object> changes = featureForUpdate.update(command); + + if (!changes.isEmpty()) { + this.featureRepository.save(featureForUpdate); + } + + return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withEntityId(featureId).with(changes).build(); + } catch (final JpaSystemException | DataIntegrityViolationException dve) { + handleDataIntegrityIssues(command, dve.getMostSpecificCause(), dve); + return CommandProcessingResult.empty(); + } catch (final PersistenceException dve) { + Throwable throwable = ExceptionUtils.getRootCause(dve.getCause()); + handleDataIntegrityIssues(command, throwable, dve); + return CommandProcessingResult.empty(); + } + } + + @Transactional + @Override + public CreditScorecard assessCreditRisk(final Loan loan) { + final CreditScorecard scorecard = loan.getScorecard(); + + if (scorecard != null) { + final String scoringMethod = scorecard.getScoringMethod(); + final String scoringModel = scorecard.getScoringModel(); + + if (scoringMethod.equalsIgnoreCase("ruleBased")) { + + final RuleBasedScorecard ruleBasedScorecard = scorecard.getRuleBasedScorecard(); + + final List<FeatureCriteriaScore> criteriaScores = ruleBasedScorecard.getCriteriaScores(); + + for (final FeatureCriteriaScore criteriaScore : criteriaScores) { + + final LoanProductScorecardFeature lpFeature = criteriaScore.getFeature(); + + final FeatureConfiguration featureConfig = lpFeature.getFeatureConfiguration(); + + final EnumOptionData dataType = lpFeature.getScorecardFeature().getDataType(); + + final List<FeatureCriteria> featureCriteriaList = lpFeature.getFeatureCriteria(); + + for (final FeatureCriteria featureCriteria : featureCriteriaList) { + + final String value = criteriaScore.getValue(); + + if (value != null) { + + if (dataType.getValue().equalsIgnoreCase("string")) { + + if (value.equalsIgnoreCase(featureCriteria.getCriteria())) { + final BigDecimal score = featureCriteria.getScore().multiply(featureConfig.getWeightage()); + final String color = featureConfig.getColorFromScore(score); + criteriaScore.setScore(score, color); + break; + + } + + } else if (dataType.getValue().equalsIgnoreCase("numeric")) { + + final String criteria = featureCriteria.getCriteria().strip(); + + final float min = Float.parseFloat(criteria.substring(0, criteria.indexOf("-")).strip()); + final float max = Float.parseFloat(criteria.substring(criteria.indexOf("-") + 1).strip()); + + final float floatValue = Float.parseFloat(value); + + if (floatValue >= min && floatValue <= max) { + final BigDecimal score = featureCriteria.getScore().multiply(featureConfig.getWeightage()); + final String color = featureConfig.getColorFromScore(score); + criteriaScore.setScore(score, color); + break; + + } + + } + + } + + } + + } + + BigDecimal scorecardScore = BigDecimal.ZERO; + int greenCount = 0; + int amberCount = 0; + int redCount = 0; + for (final FeatureCriteriaScore ctScore : criteriaScores) { + scorecardScore = scorecardScore.add(ctScore.getScore()); + + if (ctScore.getColor().equalsIgnoreCase("green")) { + greenCount += 1; + + } else if (ctScore.getColor().equalsIgnoreCase("amber")) { + amberCount += 1; + + } else if (ctScore.getColor().equalsIgnoreCase("red")) { + redCount += 1; + + } + + } + + String scorecardColor = "amber"; + if (greenCount > amberCount && greenCount > redCount) { + scorecardColor = "green"; + + } else if (redCount >= greenCount && redCount >= amberCount) { + scorecardColor = "red"; + + } + + ruleBasedScorecard.setScore(scorecardScore, scorecardColor); + + return scorecard; + + } else { + + this.initScorecardClient(); + + if (scoringMethod.equalsIgnoreCase("ml")) { + + final MLScorecard mlScorecard = scorecard.getMlScorecard(); + + final MLScorecardFields loanScorecardFields = mlScorecard.getScorecardFields(); + + final PredictionResponse response = this.fetchScorecard(loanScorecardFields, scoringModel); + + if (response != null) { + mlScorecard.setPredictionResponse(BigDecimal.valueOf(response.getProbability()), + response.getLabel(), response.getRequestId()); + } + + } else if (scoringMethod.equalsIgnoreCase("stat")) { + + final StatScorecard statScorecard = scorecard.getStatScorecard(); + + final MLScorecardFields loanScorecardFields = statScorecard.getScorecardFields(); + + final PredictionResponse response = this.fetchScorecard(loanScorecardFields, scoringModel); + + if (response != null) { + final String method = response.getMethod(); + final String color = response.getColor(); + final BigDecimal probability = BigDecimal.valueOf(response.getProbability()); + + BigDecimal wilikis = null; + BigDecimal pillaisTrace = null; + BigDecimal hotelling = null; + BigDecimal roys = null; + + if (method.equalsIgnoreCase("manova")) { + wilikis = BigDecimal.valueOf(response.getWilkisLambda()); + pillaisTrace = BigDecimal.valueOf(response.getPillaisTrace()); + hotelling = BigDecimal.valueOf(response.getHotellingTawley()); + roys = BigDecimal.valueOf(response.getRoysReatestRoots()); + } + statScorecard.setPredictionResponse(method, color, probability, wilikis, pillaisTrace, hotelling, roys); + } + + } + } + + } else { + return null; + + } + + this.scorecardRepository.save(scorecard); + + return scorecard; + } + + private PredictionResponse fetchScorecard(final MLScorecardFields fields, final String scoringModel) { + try { + + final Map<String, Object> predictionData = new HashMap<>(); + + predictionData.put("age", fields.getAge()); + predictionData.put("sex", fields.getSex()); + predictionData.put("job", fields.getJob()); + predictionData.put("housing", fields.getHousing()); + predictionData.put("credit_amount", fields.getCreditAmount()); + predictionData.put("duration", fields.getDuration()); + predictionData.put("purpose", fields.getPurpose()); + + return this.scorecardApiClient.algorithmsPredict(scoringModel, "0.0.1", null, null, + predictionData); + + } catch (ApiException e) { + LOG.debug("An Error Occurred: {}", e.getLocalizedMessage()); + } + return null; + } + + @Override + public CommandProcessingResult deleteScoringFeature(Long entityId) { + + this.context.authenticatedUser(); + + final CreditScorecardFeature featureForDelete = this.featureRepository.findById(entityId) + .orElseThrow(() -> new FeatureNotFoundException(entityId)); + if (featureForDelete.isDeleted()) { + throw new FeatureNotFoundException(entityId); + } + + final Collection<LoanProduct> loanProducts = this.loanProductRepository.retrieveLoanProductsByScorecardFeatureId(entityId); + + if (!loanProducts.isEmpty()) { + throw new FeatureCannotBeDeletedException("error.msg.scorecard.feature.cannot.be.deleted.it.is.already.used.in.loan", + "This Scoring Feature cannot be deleted, it is already used in loan"); + } + + featureForDelete.delete(); + + this.featureRepository.save(featureForDelete); + + return new CommandProcessingResultBuilder().withEntityId(featureForDelete.getId()).build(); + } + + /* + * Guaranteed to throw an exception no matter what the data integrity issue is. + */ + private void handleDataIntegrityIssues(final JsonCommand command, final Throwable realCause, final Exception dve) { + + if (realCause.getMessage().contains("name")) { + final String name = command.stringValueOfParameterNamed("name"); + throw new PlatformDataIntegrityException("error.msg.scorecard.feature.duplicate.name", + "Scorecard Feature with name `" + name + "` already exists", "name", name); + } + + LOG.error("Error occured.", dve); + throw new PlatformDataIntegrityException("error.msg.scorecard.feature.unknown.data.integrity.issue", + "Unknown data integrity issue with resource: " + realCause.getMessage()); + } + +} diff --git a/MySQL.cnf b/scorecard-plugin/src/main/resources/scorecard-client.properties similarity index 83% rename from MySQL.cnf rename to scorecard-plugin/src/main/resources/scorecard-client.properties index eba0a5f..55f3e96 100644 --- a/MySQL.cnf +++ b/scorecard-plugin/src/main/resources/scorecard-client.properties @@ -1,3 +1,4 @@ +# # Licensed to the Apache Software Foundation (ASF) under one # or more contributor license agreements. See the NOTICE file # distributed with this work for additional information @@ -14,10 +15,8 @@ # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. +# -# MySQL.cnf -[client] -database = fineract_credit_scorecard -user = root -password = mysql -default-character-set = utf8 \ No newline at end of file +fineract.credit.scorecard.uid= +fineract.credit.scorecard.password= +fineract.credit.scorecard.baseUrl=http://127.0.0.1:8000 diff --git a/server/settings.py b/server/settings.py index 6f78666..b67a098 100644 --- a/server/settings.py +++ b/server/settings.py @@ -116,10 +116,7 @@ DATABASES = { 'USER': os.environ.get('DB_USER', 'root'), 'PASSWORD': os.environ.get('DB_PASSWORD', 'mysql'), 'HOST': os.environ.get('DB_HOST', '127.0.0.1'), - 'PORT': os.environ.get('DB_PORT', '3306'), - 'OPTIONS': { - 'read_default_file': 'MySQL.cnf', - }, + 'PORT': os.environ.get('DB_PORT', '3306') }, 'sqlite3': { 'ENGINE': 'django.db.backends.sqlite3', diff --git a/settings.gradle b/settings.gradle index e278339..3f5001d 100644 --- a/settings.gradle +++ b/settings.gradle @@ -18,3 +18,7 @@ */ rootProject.name = 'fineract-credit-scorecard' +include( + 'scorecard-plugin', + 'scorecard-client' +) diff --git a/statistical_scripts/statistical_scoring.py b/stats/statistical_scoring.py similarity index 100% rename from statistical_scripts/statistical_scoring.py rename to stats/statistical_scoring.py
