├── README.md ├── README ├── .mvn └── wrapper │ ├── maven-wrapper.jar │ ├── maven-wrapper.properties │ └── MavenWrapperDownloader.java ├── Dockerfile.build ├── Dockerfile ├── src └── main │ ├── java │ └── org │ │ └── sunbird │ │ └── scoringengine │ │ ├── models │ │ ├── PropertyFilterMixIn.java │ │ ├── Response.java │ │ ├── QualifierModel.java │ │ ├── CriteriaModel.java │ │ └── EvaluatorModel.java │ │ ├── ScoringEngineApplication.java │ │ ├── service │ │ └── ScoringEngineService.java │ │ ├── exception │ │ ├── BadRequestException.java │ │ └── ApplicationServiceError.java │ │ ├── schema │ │ └── model │ │ │ ├── ScoringSchema.java │ │ │ ├── Range.java │ │ │ ├── Criteria.java │ │ │ ├── ScoringTemplate.java │ │ │ └── Qualifier.java │ │ ├── util │ │ ├── ScoringSchemaLoader.java │ │ ├── MathFunction.java │ │ ├── IndexerService.java │ │ └── ComputeScores.java │ │ ├── controller │ │ └── ScoringController.java │ │ └── serviceimpl │ │ └── ScoringEngineServiceImpl.java │ └── resources │ ├── application.properties │ ├── elastichsearchindex │ └── scorecardindex.json │ └── ScoringSchema.json ├── dockbuild.sh ├── LICENSE ├── Jenkinsfile-sun ├── Jenkinsfile ├── pom.xml ├── mvnw.cmd └── mvnw /README.md: -------------------------------------------------------------------------------- 1 | # sunbird-cb-scoring-engine -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | ADD application.properties in resource folder as shared earlier. 2 | -------------------------------------------------------------------------------- /.mvn/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sphere/sunbird-cb-scoring-engine/main/.mvn/wrapper/maven-wrapper.jar -------------------------------------------------------------------------------- /.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.0/apache-maven-3.6.0-bin.zip 2 | -------------------------------------------------------------------------------- /Dockerfile.build: -------------------------------------------------------------------------------- 1 | FROM openjdk:8 2 | 3 | RUN apt update && apt install maven -y && apt install zip -y 4 | 5 | COPY . /opt 6 | WORKDIR /opt 7 | 8 | RUN mvn clean install -DskipTests 9 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM openjdk:8 2 | COPY scoring-engine-0.0.1-SNAPSHOT.jar /opt/ 3 | EXPOSE 4011 4 | CMD ["java", "-XX:+PrintFlagsFinal", "-XX:+UnlockExperimentalVMOptions", "-XX:+UseCGroupMemoryLimitForHeap", "-jar", "/opt/scoring-engine-0.0.1-SNAPSHOT.jar"] 5 | 6 | -------------------------------------------------------------------------------- /src/main/java/org/sunbird/scoringengine/models/PropertyFilterMixIn.java: -------------------------------------------------------------------------------- 1 | package org.sunbird.scoringengine.models; 2 | 3 | import com.fasterxml.jackson.annotation.JsonFilter; 4 | 5 | @JsonFilter("filter properties by name") 6 | public class PropertyFilterMixIn {} -------------------------------------------------------------------------------- /src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | 2 | server.port=3014 3 | 4 | es.auth.enabled=false 5 | es.host=localhost 6 | es.port=9200 7 | es.username= 8 | es.password= 9 | #es.index=mlsearch_* 10 | es.score.index=scorecard_index 11 | es.score.index.type=resources 12 | es.scoring.enabled=true 13 | 14 | scoring.template.id=temp-01 15 | 16 | 17 | -------------------------------------------------------------------------------- /dockbuild.sh: -------------------------------------------------------------------------------- 1 | docker build --no-cache -f ./Dockerfile.build -t authoring-service-build . 2 | 3 | docker run --name authoring-build authoring-service-build:latest && docker cp authoring-build:/opt/target/wingspan-authoring-services-0.0.1-SNAPSHOT.jar . 4 | docker rm -f authoring-build 5 | docker rmi -f authoring-service-build 6 | 7 | docker build --no-cache -t eagle-docker.tarento.com/lex-sb-ext-authtool-service:gold . 8 | docker push eagle-docker.tarento.com/lex-sb-ext-authtool-service:gold 9 | -------------------------------------------------------------------------------- /src/main/java/org/sunbird/scoringengine/ScoringEngineApplication.java: -------------------------------------------------------------------------------- 1 | 2 | 3 | package org.sunbird.scoringengine; 4 | 5 | import org.springframework.boot.SpringApplication; 6 | import org.springframework.boot.autoconfigure.SpringBootApplication; 7 | import org.springframework.scheduling.annotation.EnableAsync; 8 | 9 | @SpringBootApplication 10 | @EnableAsync 11 | public class ScoringEngineApplication { 12 | 13 | public static void main(String[] args) { 14 | SpringApplication.run(ScoringEngineApplication.class, args); 15 | 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/org/sunbird/scoringengine/service/ScoringEngineService.java: -------------------------------------------------------------------------------- 1 | 2 | 3 | package org.sunbird.scoringengine.service; 4 | 5 | import org.sunbird.scoringengine.models.EvaluatorModel; 6 | import org.sunbird.scoringengine.models.Response; 7 | 8 | public interface ScoringEngineService { 9 | 10 | public Response addV3(EvaluatorModel evaluatorModel) throws Exception; 11 | public Response searchV2(EvaluatorModel evaluatorModel) throws Exception; 12 | public Response getTemplate(String templateId, String rootOrg, String org) throws Exception; 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/org/sunbird/scoringengine/exception/BadRequestException.java: -------------------------------------------------------------------------------- 1 | 2 | 3 | package org.sunbird.scoringengine.exception; 4 | 5 | import org.springframework.http.HttpStatus; 6 | import org.springframework.web.bind.annotation.ResponseBody; 7 | import org.springframework.web.bind.annotation.ResponseStatus; 8 | 9 | @ResponseStatus(value = HttpStatus.BAD_REQUEST) 10 | @ResponseBody 11 | public class BadRequestException extends RuntimeException { 12 | 13 | private static final long serialVersionUID = 1L; 14 | 15 | @Override 16 | public String getMessage() { 17 | return message; 18 | } 19 | 20 | public void setMessage(String message) { 21 | this.message = message; 22 | } 23 | 24 | String message; 25 | 26 | public BadRequestException(String message) { 27 | this.message = message; 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/org/sunbird/scoringengine/exception/ApplicationServiceError.java: -------------------------------------------------------------------------------- 1 | 2 | package org.sunbird.scoringengine.exception; 3 | 4 | import org.springframework.http.HttpStatus; 5 | import org.springframework.web.bind.annotation.ResponseBody; 6 | import org.springframework.web.bind.annotation.ResponseStatus; 7 | 8 | @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) 9 | @ResponseBody 10 | public class ApplicationServiceError extends RuntimeException { 11 | 12 | private static final long serialVersionUID = 1L; 13 | 14 | String message; 15 | 16 | public ApplicationServiceError(String message) { 17 | this.message = message; 18 | } 19 | 20 | @Override 21 | public String getMessage() { 22 | return message; 23 | } 24 | 25 | public void setMessage(String message) { 26 | this.message = message; 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/org/sunbird/scoringengine/schema/model/ScoringSchema.java: -------------------------------------------------------------------------------- 1 | 2 | package org.sunbird.scoringengine.schema.model; 3 | 4 | import org.springframework.stereotype.Component; 5 | 6 | import java.util.ArrayList; 7 | import java.util.List; 8 | 9 | @Component 10 | public class ScoringSchema { 11 | 12 | private String id; 13 | private List scoringTemplates = new ArrayList<>(); 14 | 15 | public String getId() { 16 | return id; 17 | } 18 | 19 | public void setId(String id) { 20 | this.id = id; 21 | } 22 | 23 | public List getScoringTemplates() { 24 | return scoringTemplates; 25 | } 26 | 27 | public void setScoringTemplates(List scoringTemplates) { 28 | this.scoringTemplates = scoringTemplates; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/org/sunbird/scoringengine/schema/model/Range.java: -------------------------------------------------------------------------------- 1 | 2 | 3 | package org.sunbird.scoringengine.schema.model; 4 | 5 | import jdk.nashorn.internal.ir.annotations.Ignore; 6 | 7 | public class Range { 8 | 9 | @Ignore 10 | private String name; 11 | private Integer min; 12 | private Integer max; 13 | 14 | private Integer assignedValue; 15 | 16 | public String getName() { 17 | return name; 18 | } 19 | 20 | public void setName(String name) { 21 | this.name = name; 22 | } 23 | 24 | public Integer getMin() { 25 | return min; 26 | } 27 | 28 | public void setMin(Integer min) { 29 | this.min = min; 30 | } 31 | 32 | public Integer getMax() { 33 | return max; 34 | } 35 | 36 | public void setMax(Integer max) { 37 | this.max = max; 38 | } 39 | 40 | public Integer getAssignedValue() { 41 | return assignedValue; 42 | } 43 | 44 | public void setAssignedValue(Integer assignedValue) { 45 | this.assignedValue = assignedValue; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Sunbird for Capacity Building 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/main/java/org/sunbird/scoringengine/util/ScoringSchemaLoader.java: -------------------------------------------------------------------------------- 1 | 2 | 3 | package org.sunbird.scoringengine.util; 4 | 5 | import com.fasterxml.jackson.databind.ObjectMapper; 6 | 7 | import org.apache.commons.io.FileUtils; 8 | import org.apache.commons.io.IOUtils; 9 | import org.springframework.core.io.ClassPathResource; 10 | import org.springframework.stereotype.Component; 11 | import org.sunbird.scoringengine.schema.model.ScoringSchema; 12 | 13 | import javax.annotation.PostConstruct; 14 | import java.io.File; 15 | import java.io.InputStream; 16 | 17 | @Component 18 | public class ScoringSchemaLoader { 19 | 20 | 21 | private ScoringSchema scoringSchema; 22 | 23 | @PostConstruct 24 | public void load() throws Exception { 25 | 26 | ClassPathResource classPathResource = new ClassPathResource("ScoringSchema.json"); 27 | 28 | InputStream inputStream = classPathResource.getInputStream(); 29 | 30 | File tempFile = File.createTempFile("ScoringSchema", ".json"); 31 | try { 32 | FileUtils.copyInputStreamToFile(inputStream, tempFile); 33 | ObjectMapper objectMapper = new ObjectMapper(); 34 | this.scoringSchema = objectMapper.readValue(tempFile, ScoringSchema.class); 35 | 36 | } finally { 37 | IOUtils.closeQuietly(inputStream); 38 | } 39 | } 40 | 41 | public ScoringSchema getScoringSchema() { 42 | return scoringSchema; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/org/sunbird/scoringengine/models/Response.java: -------------------------------------------------------------------------------- 1 | 2 | 3 | package org.sunbird.scoringengine.models; 4 | 5 | import java.io.Serializable; 6 | import java.util.HashMap; 7 | import java.util.Map; 8 | 9 | public class Response implements Serializable, Cloneable { 10 | 11 | private static final long serialVersionUID = -3773253896160786443L; 12 | private String id; 13 | private String ver; 14 | private String ts; 15 | private Map result = new HashMap<>(); 16 | 17 | public String getId() { 18 | return id; 19 | } 20 | 21 | public void setId(String id) { 22 | this.id = id; 23 | } 24 | 25 | public String getVer() { 26 | return ver; 27 | } 28 | 29 | public void setVer(String ver) { 30 | this.ver = ver; 31 | } 32 | 33 | public String getTs() { 34 | return ts; 35 | } 36 | 37 | public void setTs(String ts) { 38 | this.ts = ts; 39 | } 40 | 41 | public Map getResult() { 42 | return result; 43 | } 44 | 45 | public Object get(String key) { 46 | return result.get(key); 47 | } 48 | 49 | public void put(String key, Object vo) { 50 | result.put(key, vo); 51 | } 52 | 53 | public void putAll(Map map) { 54 | result.putAll(map); 55 | } 56 | 57 | public boolean containsKey(String key) { 58 | return result.containsKey(key); 59 | } 60 | 61 | public Response clone(Response response) { 62 | try { 63 | return (Response) response.clone(); 64 | } catch (CloneNotSupportedException e) { 65 | return null; 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/main/java/org/sunbird/scoringengine/models/QualifierModel.java: -------------------------------------------------------------------------------- 1 | 2 | 3 | package org.sunbird.scoringengine.models; 4 | 5 | import javax.validation.constraints.NotBlank; 6 | import java.util.ArrayList; 7 | import java.util.HashMap; 8 | import java.util.List; 9 | 10 | public class QualifierModel { 11 | 12 | @NotBlank 13 | private String name; 14 | @NotBlank 15 | private String evaluated; 16 | private String description; 17 | private double scoreValue; 18 | private String scoreRange; 19 | private String scoringType; 20 | private List> options = new ArrayList<>(); 21 | 22 | public String getName() { 23 | return name; 24 | } 25 | 26 | public void setName(String name) { 27 | this.name = name; 28 | } 29 | 30 | public String getEvaluated() { 31 | return evaluated; 32 | } 33 | 34 | public void setEvaluated(String evaluated) { 35 | this.evaluated = evaluated; 36 | } 37 | 38 | public double getScoreValue() { 39 | return scoreValue; 40 | } 41 | 42 | public void setScoreValue(double scoreValue) { 43 | 44 | this.scoreValue = scoreValue; 45 | } 46 | 47 | public String getScoreRange() { 48 | return scoreRange; 49 | } 50 | 51 | public void setScoreRange(String scoreRange) { 52 | this.scoreRange = scoreRange; 53 | } 54 | 55 | public String getScoringType() { 56 | return scoringType; 57 | } 58 | 59 | public void setScoringType(String scoringType) { 60 | this.scoringType = scoringType; 61 | } 62 | 63 | public String getDescription() { 64 | return description; 65 | } 66 | 67 | public void setDescription(String description) { 68 | this.description = description; 69 | } 70 | 71 | public List> getOptions() { 72 | return options; 73 | } 74 | 75 | public void setOptions(List> options) { 76 | this.options = options; 77 | } 78 | } 79 | 80 | 81 | 82 | -------------------------------------------------------------------------------- /src/main/java/org/sunbird/scoringengine/util/MathFunction.java: -------------------------------------------------------------------------------- 1 | 2 | package org.sunbird.scoringengine.util; 3 | 4 | import org.springframework.stereotype.Component; 5 | 6 | import java.util.List; 7 | import java.util.OptionalDouble; 8 | 9 | 10 | public class MathFunction { 11 | 12 | public static Double sum(List doubles){ 13 | 14 | return doubles.stream().mapToDouble(Double::doubleValue).sum(); 15 | } 16 | 17 | public static OptionalDouble min(List doubles){ 18 | 19 | return doubles.stream().mapToDouble(Double::doubleValue).min(); 20 | } 21 | 22 | public static OptionalDouble max(List doubles){ 23 | 24 | return doubles.stream().mapToDouble(Double::doubleValue).max(); 25 | } 26 | 27 | public static OptionalDouble mean(List doubles){ 28 | 29 | return doubles.stream().mapToDouble(Double::doubleValue).average(); 30 | } 31 | 32 | public static Double weightedAvg(List doubles, double weigtage){ 33 | 34 | return (doubles.stream().mapToDouble(Double::doubleValue).sum()) * weigtage; 35 | } 36 | 37 | public static Double maxWeightedAvg(Double totalMaxValue, double weigtage){ 38 | return totalMaxValue * weigtage; 39 | } 40 | 41 | public static Double minWeightedAvg(Double totallMinValue, double weigtage){ 42 | return totallMinValue * weigtage; 43 | } 44 | 45 | public static Double maxWeightedAvg(List doubles, double weigtage){ 46 | 47 | OptionalDouble maxValue = doubles.stream().mapToDouble(Double::doubleValue).max() ; 48 | double count = doubles.stream().mapToDouble(Double::doubleValue).count(); 49 | return (maxValue.getAsDouble() * count) * weigtage; 50 | } 51 | 52 | public static Double minWeightedAvg(List doubles, double weigtage){ 53 | 54 | OptionalDouble maxValue = doubles.stream().mapToDouble(Double::doubleValue).max() ; 55 | double count = doubles.stream().mapToDouble(Double::doubleValue).count(); 56 | return (maxValue.getAsDouble() * count) * weigtage; 57 | } 58 | 59 | public static Double weightedScore(double totalValue, double maxvalue, double weigtage) { 60 | if (maxvalue == 0) 61 | return 0.0; 62 | return (totalValue / maxvalue) * weigtage * 100; 63 | } 64 | 65 | 66 | 67 | 68 | 69 | 70 | } 71 | -------------------------------------------------------------------------------- /Jenkinsfile-sun: -------------------------------------------------------------------------------- 1 | @Library('deploy-conf') _ 2 | node('build-slave') { 3 | try { 4 | String ANSI_GREEN = "\u001B[32m" 5 | String ANSI_NORMAL = "\u001B[0m" 6 | String ANSI_BOLD = "\u001B[1m" 7 | String ANSI_RED = "\u001B[31m" 8 | String ANSI_YELLOW = "\u001B[33m" 9 | 10 | ansiColor('xterm') { 11 | stage('Checkout') { 12 | if (!env.hub_org) { 13 | println(ANSI_BOLD + ANSI_RED + "Uh Oh! Please set a Jenkins environment variable named hub_org with value as registery/sunbidrded" + ANSI_NORMAL) 14 | error 'Please resolve the errors and rerun..' 15 | } else 16 | println(ANSI_BOLD + ANSI_GREEN + "Found environment variable named hub_org with value as: " + hub_org + ANSI_NORMAL) 17 | } 18 | 19 | cleanWs() 20 | checkout scm 21 | commit_hash = sh(script: 'git rev-parse --short HEAD', returnStdout: true).trim() 22 | build_tag = sh(script: "echo " + params.github_release_tag.split('/')[-1] + "_" + commit_hash + "_" + env.BUILD_NUMBER, returnStdout: true).trim() 23 | echo "build_tag: " + build_tag 24 | 25 | stage('docker-pre-build') { 26 | sh ''' 27 | 28 | docker build -f ./Dockerfile.build -t $docker_pre_build . 29 | docker run --name $docker_pre_build $docker_pre_build:latest && docker cp $docker_pre_build:/opt/target/scoring-engine-0.0.1-SNAPSHOT.jar . 30 | sleep 30 31 | docker rm -f $docker_pre_build 32 | docker rmi -f $docker_pre_build 33 | ''' 34 | } 35 | stage('Build') { 36 | env.NODE_ENV = "build" 37 | print "Environment will be : ${env.NODE_ENV}" 38 | sh('chmod 777 build.sh') 39 | sh("bash -x build.sh ${build_tag} ${env.NODE_NAME} ${docker_server}") 40 | } 41 | stage('ArchiveArtifacts') { 42 | archiveArtifacts "metadata.json" 43 | currentBuild.description = "${build_tag}" 44 | } 45 | 46 | } 47 | 48 | } 49 | catch (err) { 50 | currentBuild.result = "FAILURE" 51 | throw err 52 | } 53 | finally { 54 | email_notify() 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/org/sunbird/scoringengine/controller/ScoringController.java: -------------------------------------------------------------------------------- 1 | 2 | package org.sunbird.scoringengine.controller; 3 | 4 | import org.springframework.beans.factory.annotation.Autowired; 5 | import org.springframework.http.HttpStatus; 6 | import org.springframework.http.ResponseEntity; 7 | import org.springframework.web.bind.annotation.GetMapping; 8 | import org.springframework.web.bind.annotation.PathVariable; 9 | import org.springframework.web.bind.annotation.PostMapping; 10 | import org.springframework.web.bind.annotation.RequestBody; 11 | import org.springframework.web.bind.annotation.RequestHeader; 12 | import org.springframework.web.bind.annotation.RequestMapping; 13 | import org.springframework.web.bind.annotation.RestController; 14 | import org.sunbird.scoringengine.models.EvaluatorModel; 15 | import org.sunbird.scoringengine.models.Response; 16 | import org.sunbird.scoringengine.service.ScoringEngineService; 17 | 18 | @RestController 19 | @RequestMapping("/v1") 20 | public class ScoringController { 21 | 22 | @Autowired 23 | ScoringEngineService scoringEngineService; 24 | 25 | @PostMapping("/add") 26 | public ResponseEntity add(@RequestBody EvaluatorModel evaluatorModel, @RequestHeader String rootOrg, 27 | @RequestHeader String org) throws Exception { 28 | evaluatorModel.setRootOrg(rootOrg); 29 | evaluatorModel.setOrg(org); 30 | return new ResponseEntity<>(scoringEngineService.addV3(evaluatorModel), HttpStatus.OK); 31 | } 32 | 33 | @PostMapping("/fetch") 34 | public ResponseEntity search(@RequestBody EvaluatorModel evaluatorModel, @RequestHeader String rootOrg, 35 | @RequestHeader String org) throws Exception { 36 | evaluatorModel.setRootOrg(rootOrg); 37 | evaluatorModel.setOrg(org); 38 | return new ResponseEntity<>(scoringEngineService.searchV2(evaluatorModel), HttpStatus.OK); 39 | } 40 | 41 | @GetMapping("/getTemplate/{templateId}") 42 | public ResponseEntity getTemplateConfiguration(@RequestHeader String rootOrg, 43 | @RequestHeader String org, @PathVariable("templateId") String templateId) throws Exception { 44 | return new ResponseEntity<>(scoringEngineService.getTemplate(templateId, rootOrg, org), HttpStatus.OK); 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/org/sunbird/scoringengine/schema/model/Criteria.java: -------------------------------------------------------------------------------- 1 | 2 | 3 | package org.sunbird.scoringengine.schema.model; 4 | 5 | 6 | import java.util.ArrayList; 7 | import java.util.List; 8 | 9 | public class Criteria { 10 | 11 | private String criteria; 12 | private Double weightage; 13 | private Double max_score; 14 | private Double min_acceptable_score; 15 | private String description; 16 | private List qualifiers = new ArrayList<>(); 17 | 18 | private Boolean min_score_weightage_enable; 19 | 20 | private Double min_score_weightage; 21 | 22 | public String getCriteria() { 23 | return criteria; 24 | } 25 | 26 | public void setCriteria(String criteria) { 27 | this.criteria = criteria; 28 | } 29 | 30 | public Double getWeightage() { 31 | return weightage; 32 | } 33 | 34 | public void setWeightage(Double weightage) { 35 | this.weightage = weightage; 36 | } 37 | 38 | public Double getMax_score() { 39 | return max_score; 40 | } 41 | 42 | public void setMax_score(Double max_score) { 43 | this.max_score = max_score; 44 | } 45 | 46 | public Double getMin_acceptable_score() { 47 | return min_acceptable_score; 48 | } 49 | 50 | public void setMin_acceptable_score(Double min_acceptable_score) { 51 | this.min_acceptable_score = min_acceptable_score; 52 | } 53 | 54 | public List getQualifiers() { 55 | return qualifiers; 56 | } 57 | 58 | public void setQualifiers(List qualifiers) { 59 | this.qualifiers = qualifiers; 60 | } 61 | 62 | public String getDescription() { 63 | return description; 64 | } 65 | 66 | public void setDescription(String description) { 67 | this.description = description; 68 | } 69 | 70 | public Boolean getMin_score_weightage_enable() { 71 | return min_score_weightage_enable; 72 | } 73 | 74 | public void setMin_score_weightage_enable(Boolean min_score_weightage_enable) { 75 | this.min_score_weightage_enable = min_score_weightage_enable; 76 | } 77 | 78 | public Double getMin_score_weightage() { 79 | return min_score_weightage; 80 | } 81 | 82 | public void setMin_score_weightage(Double min_score_weightage) { 83 | this.min_score_weightage = min_score_weightage; 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /Jenkinsfile: -------------------------------------------------------------------------------- 1 | node() { 2 | try { 3 | String ANSI_GREEN = "\u001B[32m" 4 | String ANSI_NORMAL = "\u001B[0m" 5 | String ANSI_BOLD = "\u001B[1m" 6 | String ANSI_RED = "\u001B[31m" 7 | String ANSI_YELLOW = "\u001B[33m" 8 | 9 | ansiColor('xterm') { 10 | stage('Checkout') { 11 | cleanWs() 12 | checkout scm 13 | } 14 | } 15 | stage('docker-pre-build') { 16 | sh ''' 17 | cd $docker_file_path 18 | pwd 19 | docker build -f ./Dockerfile.build -t $docker_pre_build . 20 | docker run --name $docker_pre_build $docker_pre_build:latest && docker cp $docker_pre_build:/opt/target/scoring-engine-0.0.1-SNAPSHOT.jar . 21 | docker rm -f $docker_pre_build 22 | docker rmi -f $docker_pre_build 23 | ''' 24 | } 25 | 26 | stage('SonarQube analysis') { 27 | // requires SonarQube Scanner 2.8+ 28 | def scannerHome = tool 'sonar_scanner'; 29 | withSonarQubeEnv('sonarqube') { 30 | sh 'cd $docker_file_path && mvn clean package sonar:sonar' 31 | } 32 | } 33 | stage("Quality Gate") { 34 | timeout(time: 1, unit: 'HOURS') { // Just in case something goes wrong, pipeline will be killed after a timeout 35 | def qg = waitForQualityGate() // Reuse taskId previously collected by withSonarQubeEnv 36 | if (qg.status != 'OK') { 37 | error "Pipeline aborted due to quality gate failure: ${qg.status}" 38 | } 39 | } 40 | } 41 | 42 | 43 | 44 | stage('docker-build') { 45 | sh ''' 46 | commit_id=$(git rev-parse --short HEAD) 47 | echo $commit_id> commit_id.txt 48 | cd $docker_file_path 49 | pwd 50 | docker build -t $docker_server/$docker_repo:$commit_id . 51 | docker tag $docker_server/$docker_repo:$commit_id $docker_server/$docker_repo:$image_tag 52 | ''' 53 | } 54 | stage('docker-push') { 55 | 56 | sh ''' 57 | pwd 58 | commit_id=$(git rev-parse --short HEAD) 59 | docker push $docker_server/$docker_repo:$commit_id 60 | docker push $docker_server/$docker_repo:$image_tag 61 | docker rmi -f $docker_server/$docker_repo:$commit_id 62 | docker rmi -f $docker_server/$docker_repo:$image_tag 63 | ''' 64 | } 65 | 66 | } 67 | catch (err) { 68 | currentBuild.result = "FAILURE" 69 | throw err 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/main/java/org/sunbird/scoringengine/schema/model/ScoringTemplate.java: -------------------------------------------------------------------------------- 1 | 2 | 3 | package org.sunbird.scoringengine.schema.model; 4 | 5 | import java.util.ArrayList; 6 | import java.util.HashMap; 7 | import java.util.List; 8 | 9 | public class ScoringTemplate { 10 | 11 | private String rootOrg; 12 | private String org; 13 | private String template_id; 14 | private String templateName; 15 | private double version; 16 | private double weightage; 17 | private double max_score; 18 | private double min_acceptable_score; 19 | private List criteria = new ArrayList<>(); 20 | private List> score_grades = new ArrayList<>(); 21 | private HashMap status_on_min_criteria = new HashMap<>(); 22 | 23 | public String getRootOrg() { 24 | return rootOrg; 25 | } 26 | 27 | public void setRootOrg(String rootOrg) { 28 | this.rootOrg = rootOrg; 29 | } 30 | 31 | public String getOrg() { 32 | return org; 33 | } 34 | 35 | public void setOrg(String org) { 36 | this.org = org; 37 | } 38 | 39 | public String getTemplate_id() { 40 | return template_id; 41 | } 42 | 43 | public void setTemplate_id(String template_id) { 44 | this.template_id = template_id; 45 | } 46 | 47 | public String getTemplateName() { 48 | return templateName; 49 | } 50 | 51 | public void setTemplateName(String templateName) { 52 | this.templateName = templateName; 53 | } 54 | 55 | public double getVersion() { 56 | return version; 57 | } 58 | 59 | public void setVersion(double version) { 60 | this.version = version; 61 | } 62 | 63 | public double getWeightage() { 64 | return weightage; 65 | } 66 | 67 | public void setWeightage(double weightage) { 68 | this.weightage = weightage; 69 | } 70 | 71 | public double getMax_score() { 72 | return max_score; 73 | } 74 | 75 | public void setMax_score(double max_score) { 76 | this.max_score = max_score; 77 | } 78 | 79 | public double getMin_acceptable_score() { 80 | return min_acceptable_score; 81 | } 82 | 83 | public void setMin_acceptable_score(double min_acceptable_score) { 84 | this.min_acceptable_score = min_acceptable_score; 85 | } 86 | 87 | public List getCriteria() { 88 | return criteria; 89 | } 90 | 91 | public void setCriteria(List criteria) { 92 | this.criteria = criteria; 93 | } 94 | 95 | public List> getScore_grades() { 96 | return score_grades; 97 | } 98 | 99 | public void setScore_grades(List> score_grades) { 100 | this.score_grades = score_grades; 101 | } 102 | 103 | public HashMap getStatus_on_min_criteria() { 104 | return status_on_min_criteria; 105 | } 106 | 107 | public void setStatus_on_min_criteria(HashMap status_on_min_criteria) { 108 | this.status_on_min_criteria = status_on_min_criteria; 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /src/main/java/org/sunbird/scoringengine/models/CriteriaModel.java: -------------------------------------------------------------------------------- 1 | 2 | 3 | package org.sunbird.scoringengine.models; 4 | 5 | import javax.validation.constraints.NotBlank; 6 | import javax.validation.constraints.Size; 7 | import java.util.ArrayList; 8 | import java.util.List; 9 | 10 | public class CriteriaModel { 11 | 12 | @NotBlank 13 | private String criteria; 14 | 15 | private double totalScore; 16 | 17 | private double weightage; 18 | 19 | private double weightedAvg; 20 | 21 | private double maxScore; 22 | 23 | private double minScore; 24 | 25 | private double maxWeightedAvg; 26 | 27 | private double minWeightedAvg; 28 | 29 | private double weightedScore; 30 | 31 | private String description; 32 | 33 | @Size(min =1) 34 | private List qualifiers = new ArrayList<>(); 35 | 36 | private boolean isQualifiedMinCriteria = true; 37 | 38 | public String getCriteria() { 39 | return criteria; 40 | } 41 | 42 | public void setCriteria(String criteria) { 43 | this.criteria = criteria; 44 | } 45 | 46 | public double getTotalScore() { 47 | return totalScore; 48 | } 49 | 50 | public void setTotalScore(double totalScore) { 51 | this.totalScore = totalScore; 52 | } 53 | 54 | public double getWeightedAvg() { 55 | return weightedAvg; 56 | } 57 | 58 | public void setWeightedAvg(double weightedAvg) { 59 | this.weightedAvg = weightedAvg; 60 | } 61 | 62 | public List getQualifiers() { 63 | return qualifiers; 64 | } 65 | 66 | public void setQualifiers(List qualifiers) { 67 | this.qualifiers = qualifiers; 68 | } 69 | 70 | public double getWeightage() { 71 | return weightage; 72 | } 73 | 74 | public void setWeightage(double weightage) { 75 | this.weightage = weightage; 76 | } 77 | 78 | public double getMaxScore() { 79 | return maxScore; 80 | } 81 | 82 | public void setMaxScore(double maxScore) { 83 | this.maxScore = maxScore; 84 | } 85 | 86 | public double getMinScore() { 87 | return minScore; 88 | } 89 | 90 | public void setMinScore(double minScore) { 91 | this.minScore = minScore; 92 | } 93 | 94 | public double getMaxWeightedAvg() { 95 | return maxWeightedAvg; 96 | } 97 | 98 | public void setMaxWeightedAvg(double maxWeightedAvg) { 99 | this.maxWeightedAvg = maxWeightedAvg; 100 | } 101 | 102 | public double getMinWeightedAvg() { 103 | return minWeightedAvg; 104 | } 105 | 106 | public void setMinWeightedAvg(double minWeightedAvg) { 107 | this.minWeightedAvg = minWeightedAvg; 108 | } 109 | 110 | public double getWeightedScore() { 111 | return weightedScore; 112 | } 113 | 114 | public void setWeightedScore(double weightedScore) { 115 | this.weightedScore = weightedScore; 116 | } 117 | 118 | public String getDescription() { 119 | return description; 120 | } 121 | 122 | public void setDescription(String description) { 123 | this.description = description; 124 | } 125 | 126 | public boolean isQualifiedMinCriteria() { 127 | return isQualifiedMinCriteria; 128 | } 129 | 130 | public void setQualifiedMinCriteria(boolean qualifiedMinCriteria) { 131 | isQualifiedMinCriteria = qualifiedMinCriteria; 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /src/main/java/org/sunbird/scoringengine/schema/model/Qualifier.java: -------------------------------------------------------------------------------- 1 | 2 | 3 | package org.sunbird.scoringengine.schema.model; 4 | 5 | import com.fasterxml.jackson.annotation.JsonFilter; 6 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 7 | 8 | import java.util.ArrayList; 9 | import java.util.HashMap; 10 | import java.util.List; 11 | import java.util.Map; 12 | public class Qualifier { 13 | 14 | private String qualifier; 15 | private String description; 16 | private Double weightage; 17 | private Double max_score; 18 | private Double min_acceptable_score; 19 | private Map fixed_score = new HashMap<>(); 20 | //@JsonIgnoreProperties 21 | private Map range_score = new HashMap<>(); 22 | 23 | private Boolean modify_max_score; 24 | 25 | private Map max_score_modify_value = new HashMap<>(); 26 | 27 | private List> options = new ArrayList<>(); 28 | 29 | private List disqualifyOption = new ArrayList<>(); 30 | 31 | public String getQualifier() { 32 | return qualifier; 33 | } 34 | 35 | public void setQualifier(String qualifier) { 36 | this.qualifier = qualifier; 37 | } 38 | 39 | public Double getWeightage() { 40 | return weightage; 41 | } 42 | 43 | public void setWeightage(Double weightage) { 44 | this.weightage = weightage; 45 | } 46 | 47 | public Double getMax_score() { 48 | return max_score; 49 | } 50 | 51 | public void setMax_score(Double max_score) { 52 | this.max_score = max_score; 53 | } 54 | 55 | public Double getMin_acceptable_score() { 56 | return min_acceptable_score; 57 | } 58 | 59 | public void setMin_acceptable_score(Double min_acceptable_score) { 60 | this.min_acceptable_score = min_acceptable_score; 61 | } 62 | 63 | public Map getFixed_score() { 64 | return fixed_score; 65 | } 66 | 67 | public void setFixed_score(Map fixed_score) { 68 | this.fixed_score = fixed_score; 69 | } 70 | 71 | public Map getRange_score() { 72 | return range_score; 73 | } 74 | 75 | public void setRange_score(Map range_score) { 76 | this.range_score = range_score; 77 | } 78 | 79 | public Boolean getModify_max_score() { 80 | return modify_max_score; 81 | } 82 | 83 | public void setModify_max_score(Boolean modify_max_score) { 84 | this.modify_max_score = modify_max_score; 85 | } 86 | 87 | public Map getMax_score_modify_value() { 88 | return max_score_modify_value; 89 | } 90 | 91 | public void setMax_score_modify_value(Map max_score_modify_value) { 92 | this.max_score_modify_value = max_score_modify_value; 93 | } 94 | 95 | public String getDescription() { 96 | return description; 97 | } 98 | 99 | public void setDescription(String description) { 100 | this.description = description; 101 | } 102 | 103 | public List> getOptions() { 104 | return options; 105 | } 106 | 107 | public void setOptions(List> options) { 108 | this.options = options; 109 | } 110 | 111 | public List getDisqualifyOption() { 112 | return disqualifyOption; 113 | } 114 | 115 | public void setDisqualifyOption(List disqualifyOption) { 116 | this.disqualifyOption = disqualifyOption; 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 11 | 4.0.0 12 | 13 | org.springframework.boot 14 | spring-boot-starter-parent 15 | 2.1.4.RELEASE 16 | 17 | 18 | com.infosys 19 | scoring-engine 20 | 0.0.1-SNAPSHOT 21 | scoring-engine 22 | scoring engine 23 | 24 | 25 | 1.8 26 | 27 | 28 | 29 | 30 | org.springframework.boot 31 | spring-boot-starter-web 32 | 33 | 34 | org.springframework.boot 35 | spring-boot-starter-logging 36 | 37 | 38 | 39 | 40 | 41 | org.springframework.boot 42 | spring-boot-starter-log4j2 43 | 44 | 45 | 46 | org.springframework.boot 47 | spring-boot-starter-test 48 | test 49 | 50 | 51 | org.springframework.kafka 52 | spring-kafka 53 | 54 | 55 | org.neo4j.driver 56 | neo4j-java-driver 57 | 1.7.2 58 | 59 | 60 | com.rockymadden.stringmetric 61 | stringmetric-core_2.11 62 | 0.27.4 63 | 64 | 65 | org.springframework.boot 66 | spring-boot-starter-aop 67 | 68 | 69 | org.springframework.boot 70 | spring-boot-starter-log4j2 71 | 72 | 73 | 74 | commons-io 75 | commons-io 76 | 2.6 77 | 78 | 79 | org.elasticsearch 80 | elasticsearch 81 | 6.8.0 82 | 83 | 84 | 85 | org.springframework.boot 86 | spring-boot-starter-actuator 87 | 88 | 89 | 90 | org.elasticsearch.client 91 | elasticsearch-rest-high-level-client 92 | 6.8.0 93 | 94 | 95 | org.elasticsearch 96 | elasticsearch 97 | 98 | 99 | 100 | 101 | 102 | commons-collections 103 | commons-collections 104 | 3.0 105 | 106 | 107 | 108 | 109 | 110 | 111 | org.springframework.boot 112 | spring-boot-maven-plugin 113 | 114 | 115 | 116 | 117 | 118 | -------------------------------------------------------------------------------- /.mvn/wrapper/MavenWrapperDownloader.java: -------------------------------------------------------------------------------- 1 | /* "Copyright 2020 Infosys Ltd. 2 | Use of this source code is governed by GPL v3 license that can be found in the LICENSE file or at https://opensource.org/licenses/GPL-3.0 3 | This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License version 3"*/ 4 | /* 5 | Licensed to the Apache Software Foundation (ASF) under one 6 | or more contributor license agreements. See the NOTICE file 7 | distributed with this work for additional information 8 | regarding copyright ownership. The ASF licenses this file 9 | to you under the Apache License, Version 2.0 (the 10 | "License"); you may not use this file except in compliance 11 | with the License. You may obtain a copy of the License at 12 | 13 | https://www.apache.org/licenses/LICENSE-2.0 14 | 15 | Unless required by applicable law or agreed to in writing, 16 | software distributed under the License is distributed on an 17 | "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 18 | KIND, either express or implied. See the License for the 19 | specific language governing permissions and limitations 20 | under the License. 21 | */ 22 | 23 | import java.io.File; 24 | import java.io.FileInputStream; 25 | import java.io.FileOutputStream; 26 | import java.io.IOException; 27 | import java.net.URL; 28 | import java.nio.channels.Channels; 29 | import java.nio.channels.ReadableByteChannel; 30 | import java.util.Properties; 31 | 32 | public class MavenWrapperDownloader { 33 | 34 | /** 35 | * Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided. 36 | */ 37 | private static final String DEFAULT_DOWNLOAD_URL = 38 | "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.4.2/maven-wrapper-0.4.2.jar"; 39 | 40 | /** 41 | * Path to the maven-wrapper.properties file, which might contain a downloadUrl property to 42 | * use instead of the default one. 43 | */ 44 | private static final String MAVEN_WRAPPER_PROPERTIES_PATH = 45 | ".mvn/wrapper/maven-wrapper.properties"; 46 | 47 | /** 48 | * Path where the maven-wrapper.jar will be saved to. 49 | */ 50 | private static final String MAVEN_WRAPPER_JAR_PATH = 51 | ".mvn/wrapper/maven-wrapper.jar"; 52 | 53 | /** 54 | * Name of the property which should be used to override the default download url for the wrapper. 55 | */ 56 | private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl"; 57 | 58 | public static void main(String args[]) { 59 | System.out.println("- Downloader started"); 60 | File baseDirectory = new File(args[0]); 61 | System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath()); 62 | 63 | // If the maven-wrapper.properties exists, read it and check if it contains a custom 64 | // wrapperUrl parameter. 65 | File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH); 66 | String url = DEFAULT_DOWNLOAD_URL; 67 | if(mavenWrapperPropertyFile.exists()) { 68 | FileInputStream mavenWrapperPropertyFileInputStream = null; 69 | try { 70 | mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile); 71 | Properties mavenWrapperProperties = new Properties(); 72 | mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream); 73 | url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url); 74 | } catch (IOException e) { 75 | System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'"); 76 | } finally { 77 | try { 78 | if(mavenWrapperPropertyFileInputStream != null) { 79 | mavenWrapperPropertyFileInputStream.close(); 80 | } 81 | } catch (IOException e) { 82 | // Ignore ... 83 | } 84 | } 85 | } 86 | System.out.println("- Downloading from: : " + url); 87 | 88 | File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH); 89 | if(!outputFile.getParentFile().exists()) { 90 | if(!outputFile.getParentFile().mkdirs()) { 91 | System.out.println( 92 | "- ERROR creating output direcrory '" + outputFile.getParentFile().getAbsolutePath() + "'"); 93 | } 94 | } 95 | System.out.println("- Downloading to: " + outputFile.getAbsolutePath()); 96 | try { 97 | downloadFileFromURL(url, outputFile); 98 | System.out.println("Done"); 99 | System.exit(0); 100 | } catch (Throwable e) { 101 | System.out.println("- Error downloading"); 102 | e.printStackTrace(); 103 | System.exit(1); 104 | } 105 | } 106 | 107 | private static void downloadFileFromURL(String urlString, File destination) throws Exception { 108 | URL website = new URL(urlString); 109 | ReadableByteChannel rbc; 110 | rbc = Channels.newChannel(website.openStream()); 111 | FileOutputStream fos = new FileOutputStream(destination); 112 | fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE); 113 | fos.close(); 114 | rbc.close(); 115 | } 116 | 117 | } 118 | -------------------------------------------------------------------------------- /mvnw.cmd: -------------------------------------------------------------------------------- 1 | @REM ---------------------------------------------------------------------------- 2 | @REM Licensed to the Apache Software Foundation (ASF) under one 3 | @REM or more contributor license agreements. See the NOTICE file 4 | @REM distributed with this work for additional information 5 | @REM regarding copyright ownership. The ASF licenses this file 6 | @REM to you under the Apache License, Version 2.0 (the 7 | @REM "License"); you may not use this file except in compliance 8 | @REM with the License. You may obtain a copy of the License at 9 | @REM 10 | @REM https://www.apache.org/licenses/LICENSE-2.0 11 | @REM 12 | @REM Unless required by applicable law or agreed to in writing, 13 | @REM software distributed under the License is distributed on an 14 | @REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | @REM KIND, either express or implied. See the License for the 16 | @REM specific language governing permissions and limitations 17 | @REM under the License. 18 | @REM ---------------------------------------------------------------------------- 19 | 20 | @REM ---------------------------------------------------------------------------- 21 | @REM Maven2 Start Up Batch script 22 | @REM 23 | @REM Required ENV vars: 24 | @REM JAVA_HOME - location of a JDK home dir 25 | @REM 26 | @REM Optional ENV vars 27 | @REM M2_HOME - location of maven2's installed home dir 28 | @REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands 29 | @REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending 30 | @REM MAVEN_OPTS - parameters passed to the Java VM when running Maven 31 | @REM e.g. to debug Maven itself, use 32 | @REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 33 | @REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files 34 | @REM ---------------------------------------------------------------------------- 35 | 36 | @REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' 37 | @echo off 38 | @REM set title of command window 39 | title %0 40 | @REM enable echoing my setting MAVEN_BATCH_ECHO to 'on' 41 | @if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% 42 | 43 | @REM set %HOME% to equivalent of $HOME 44 | if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") 45 | 46 | @REM Execute a user defined script before this one 47 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre 48 | @REM check for pre script, once with legacy .bat ending and once with .cmd ending 49 | if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" 50 | if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" 51 | :skipRcPre 52 | 53 | @setlocal 54 | 55 | set ERROR_CODE=0 56 | 57 | @REM To isolate internal variables from possible post scripts, we use another setlocal 58 | @setlocal 59 | 60 | @REM ==== START VALIDATION ==== 61 | if not "%JAVA_HOME%" == "" goto OkJHome 62 | 63 | echo. 64 | echo Error: JAVA_HOME not found in your environment. >&2 65 | echo Please set the JAVA_HOME variable in your environment to match the >&2 66 | echo location of your Java installation. >&2 67 | echo. 68 | goto error 69 | 70 | :OkJHome 71 | if exist "%JAVA_HOME%\bin\java.exe" goto init 72 | 73 | echo. 74 | echo Error: JAVA_HOME is set to an invalid directory. >&2 75 | echo JAVA_HOME = "%JAVA_HOME%" >&2 76 | echo Please set the JAVA_HOME variable in your environment to match the >&2 77 | echo location of your Java installation. >&2 78 | echo. 79 | goto error 80 | 81 | @REM ==== END VALIDATION ==== 82 | 83 | :init 84 | 85 | @REM Find the project base dir, i.e. the directory that contains the folder ".mvn". 86 | @REM Fallback to current working directory if not found. 87 | 88 | set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% 89 | IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir 90 | 91 | set EXEC_DIR=%CD% 92 | set WDIR=%EXEC_DIR% 93 | :findBaseDir 94 | IF EXIST "%WDIR%"\.mvn goto baseDirFound 95 | cd .. 96 | IF "%WDIR%"=="%CD%" goto baseDirNotFound 97 | set WDIR=%CD% 98 | goto findBaseDir 99 | 100 | :baseDirFound 101 | set MAVEN_PROJECTBASEDIR=%WDIR% 102 | cd "%EXEC_DIR%" 103 | goto endDetectBaseDir 104 | 105 | :baseDirNotFound 106 | set MAVEN_PROJECTBASEDIR=%EXEC_DIR% 107 | cd "%EXEC_DIR%" 108 | 109 | :endDetectBaseDir 110 | 111 | IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig 112 | 113 | @setlocal EnableExtensions EnableDelayedExpansion 114 | for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a 115 | @endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% 116 | 117 | :endReadAdditionalConfig 118 | 119 | SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" 120 | set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" 121 | set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 122 | 123 | set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.4.2/maven-wrapper-0.4.2.jar" 124 | FOR /F "tokens=1,2 delims==" %%A IN (%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties) DO ( 125 | IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B 126 | ) 127 | 128 | @REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central 129 | @REM This allows using the maven wrapper in projects that prohibit checking in binary data. 130 | if exist %WRAPPER_JAR% ( 131 | echo Found %WRAPPER_JAR% 132 | ) else ( 133 | echo Couldn't find %WRAPPER_JAR%, downloading it ... 134 | echo Downloading from: %DOWNLOAD_URL% 135 | powershell -Command "(New-Object Net.WebClient).DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')" 136 | echo Finished downloading %WRAPPER_JAR% 137 | ) 138 | @REM End of extension 139 | 140 | %MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* 141 | if ERRORLEVEL 1 goto error 142 | goto end 143 | 144 | :error 145 | set ERROR_CODE=1 146 | 147 | :end 148 | @endlocal & set ERROR_CODE=%ERROR_CODE% 149 | 150 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost 151 | @REM check for post script, once with legacy .bat ending and once with .cmd ending 152 | if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" 153 | if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" 154 | :skipRcPost 155 | 156 | @REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' 157 | if "%MAVEN_BATCH_PAUSE%" == "on" pause 158 | 159 | if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% 160 | 161 | exit /B %ERROR_CODE% 162 | -------------------------------------------------------------------------------- /src/main/java/org/sunbird/scoringengine/serviceimpl/ScoringEngineServiceImpl.java: -------------------------------------------------------------------------------- 1 | 2 | package org.sunbird.scoringengine.serviceimpl; 3 | 4 | import com.fasterxml.jackson.core.type.TypeReference; 5 | import com.fasterxml.jackson.databind.JsonNode; 6 | import com.fasterxml.jackson.databind.ObjectMapper; 7 | import com.fasterxml.jackson.databind.ObjectWriter; 8 | import com.fasterxml.jackson.databind.ser.FilterProvider; 9 | import com.fasterxml.jackson.databind.ser.impl.SimpleBeanPropertyFilter; 10 | import com.fasterxml.jackson.databind.ser.impl.SimpleFilterProvider; 11 | 12 | import org.elasticsearch.rest.RestStatus; 13 | import org.slf4j.Logger; 14 | import org.slf4j.LoggerFactory; 15 | import org.springframework.beans.factory.annotation.Autowired; 16 | import org.springframework.beans.factory.annotation.Value; 17 | import org.springframework.http.HttpStatus; 18 | import org.springframework.stereotype.Service; 19 | import org.springframework.util.StringUtils; 20 | import org.sunbird.scoringengine.exception.BadRequestException; 21 | import org.sunbird.scoringengine.models.EvaluatorModel; 22 | import org.sunbird.scoringengine.models.PropertyFilterMixIn; 23 | import org.sunbird.scoringengine.models.Response; 24 | import org.sunbird.scoringengine.schema.model.ScoringTemplate; 25 | import org.sunbird.scoringengine.service.ScoringEngineService; 26 | import org.sunbird.scoringengine.util.ComputeScores; 27 | import org.sunbird.scoringengine.util.IndexerService; 28 | import org.sunbird.scoringengine.util.ScoringSchemaLoader; 29 | 30 | import java.text.SimpleDateFormat; 31 | import java.util.HashMap; 32 | import java.util.Map; 33 | import java.util.Optional; 34 | 35 | @Service 36 | public class ScoringEngineServiceImpl implements ScoringEngineService { 37 | 38 | 39 | @Autowired 40 | private ScoringSchemaLoader schemaLoader; 41 | 42 | @Autowired 43 | IndexerService indexerService; 44 | 45 | private ObjectMapper mapper = new ObjectMapper(); 46 | @Value("${es.score.index}") 47 | private String esIndex; 48 | 49 | @Value("${es.score.index.type}") 50 | private String esIndexType; 51 | 52 | @Value("${scoring.template.id}") 53 | private String scoringTemplateId; 54 | 55 | @Value("${es.scoring.enabled}") 56 | private boolean esScoringEnabled; 57 | 58 | public SimpleDateFormat formatterDateTime = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); 59 | private Logger logger = LoggerFactory.getLogger(ScoringEngineServiceImpl.class); 60 | 61 | private static final String[] ignorableFields = {"rootOrg", "org", "version", "weightage", "max_score", "min_acceptable_score", 62 | "fixed_score", "range_score", "modify_max_score", "10-20", "20-30", "30-40", "modify_max_score", "max_score_modify_value", 63 | "min_score_weightage_enable", "min_score_weightage", "score_grades", "isQualifiedMinCriteria", "status_on_min_criteria", "disqualifyOption"}; 64 | 65 | @Override 66 | public Response addV3(EvaluatorModel evaluatorModel) throws Exception { 67 | Response response = new Response(); 68 | try { 69 | String templateId = (evaluatorModel.getTemplateId() == null || evaluatorModel.getTemplateId().isEmpty()) == true ? scoringTemplateId 70 | : evaluatorModel.getTemplateId(); 71 | 72 | Optional scoringTemplateOptional = schemaLoader.getScoringSchema().getScoringTemplates().stream().filter(t -> t.getTemplate_id().equals(templateId)).findFirst(); 73 | 74 | if(!scoringTemplateOptional.isPresent()) 75 | throw new BadRequestException("Template is not found on given template id"); 76 | ScoringTemplate scoringTemplate = scoringTemplateOptional.get(); 77 | 78 | ComputeScores computeScores = new ComputeScores(scoringTemplate); 79 | computeScores.computeV2(evaluatorModel); 80 | // post the data into ES index 81 | if (esScoringEnabled && !evaluatorModel.isGateCriteriaCheck()) { 82 | Map indexDocument = mapper.convertValue(evaluatorModel, new TypeReference>() { 83 | }); 84 | RestStatus status = indexerService.addEntity(esIndex, esIndexType, evaluatorModel.getIdentifier(), indexDocument); 85 | response.put("status", status); 86 | response.put("id", evaluatorModel.getIdentifier()); 87 | 88 | } else { 89 | response.put("result", evaluatorModel); 90 | } 91 | 92 | response.put("Message", "Successfully operation"); 93 | 94 | } catch (Exception e) { 95 | e.printStackTrace(); 96 | throw new Exception(e); 97 | } 98 | 99 | return response; 100 | } 101 | 102 | @Override 103 | public Response searchV2(EvaluatorModel evaluatorModel) throws Exception{ 104 | Response response = new Response(); 105 | try{ 106 | if ((null == evaluatorModel.getUserId() || evaluatorModel.getUserId().isEmpty()) && (null == evaluatorModel.getResourceId() || evaluatorModel.getResourceId().isEmpty())) { 107 | throw new BadRequestException("Required fields, userId or resourceId is not valid "); 108 | } 109 | // post the data into ES index 110 | Map searchQuery = new HashMap<>(); 111 | searchQuery.put("userId", evaluatorModel.getUserId()); 112 | searchQuery.put("resourceId", evaluatorModel.getResourceId()); 113 | searchQuery.put("isGetLatestRecordEnabled", evaluatorModel.isGetLatestRecordEnabled()); 114 | if(!StringUtils.isEmpty(evaluatorModel.getIdentifier())){ 115 | searchQuery.put("identifier", evaluatorModel.getIdentifier()); 116 | } 117 | JsonNode searchResponse= indexerService.search(esIndex, searchQuery); 118 | 119 | response.put("resources", searchResponse); 120 | response.put("Message", "Successfully operation"); 121 | 122 | } catch (Exception e){ 123 | e.printStackTrace(); 124 | throw new Exception(e); 125 | } 126 | 127 | return response; 128 | } 129 | 130 | @Override 131 | public Response getTemplate(String templateId, String rootOrg, String org) throws Exception { 132 | Response response = new Response(); 133 | if (StringUtils.isEmpty(templateId)) { 134 | throw new BadRequestException("Template Id is required!"); 135 | } 136 | Optional scoringTemplateOptional = schemaLoader.getScoringSchema().getScoringTemplates().stream().filter(t -> t.getTemplate_id().equals(templateId)).findFirst(); 137 | if(!scoringTemplateOptional.isPresent()) 138 | throw new BadRequestException("Template is not found on given template id"); 139 | ScoringTemplate scoringTemplate = scoringTemplateOptional.get(); 140 | ObjectMapper mapper = new ObjectMapper(); 141 | mapper.addMixIn(Object.class, PropertyFilterMixIn.class); 142 | FilterProvider filters = new SimpleFilterProvider().addFilter("filter properties by name",SimpleBeanPropertyFilter.serializeAllExcept(ignorableFields)); 143 | ObjectWriter writer = mapper.writer(filters); 144 | response.put("result", mapper.readValue(writer.writeValueAsString(scoringTemplate), Object.class)); 145 | response.put("Message", "Successfully operation"); 146 | response.put("resources", HttpStatus.OK); 147 | return response; 148 | } 149 | 150 | } 151 | -------------------------------------------------------------------------------- /src/main/resources/elastichsearchindex/scorecardindex.json: -------------------------------------------------------------------------------- 1 | { 2 | "scorecard_index": { 3 | "mappings": { 4 | "resources": { 5 | "properties": { 6 | "compositeScore": { 7 | "type": "float" 8 | }, 9 | "criteriaModels": { 10 | "properties": { 11 | "criteria": { 12 | "type": "text", 13 | "fields": { 14 | "keyword": { 15 | "type": "keyword", 16 | "ignore_above": 256 17 | } 18 | } 19 | }, 20 | "maxScore": { 21 | "type": "float" 22 | }, 23 | "maxWeightedAvg": { 24 | "type": "float" 25 | }, 26 | "minScore": { 27 | "type": "float" 28 | }, 29 | "minWeightedAvg": { 30 | "type": "float" 31 | }, 32 | "qualifiedMinCriteria": { 33 | "type": "boolean" 34 | }, 35 | "qualifiers": { 36 | "properties": { 37 | "description": { 38 | "type": "text", 39 | "fields": { 40 | "keyword": { 41 | "type": "keyword", 42 | "ignore_above": 256 43 | } 44 | } 45 | }, 46 | "evaluated": { 47 | "type": "text", 48 | "fields": { 49 | "keyword": { 50 | "type": "keyword", 51 | "ignore_above": 256 52 | } 53 | } 54 | }, 55 | "name": { 56 | "type": "text", 57 | "fields": { 58 | "keyword": { 59 | "type": "keyword", 60 | "ignore_above": 256 61 | } 62 | } 63 | }, 64 | "scoreValue": { 65 | "type": "float" 66 | }, 67 | "scoringType": { 68 | "type": "text", 69 | "fields": { 70 | "keyword": { 71 | "type": "keyword", 72 | "ignore_above": 256 73 | } 74 | } 75 | } 76 | } 77 | }, 78 | "totalScore": { 79 | "type": "float" 80 | }, 81 | "weightage": { 82 | "type": "float" 83 | }, 84 | "weightedAvg": { 85 | "type": "float" 86 | }, 87 | "weightedScore": { 88 | "type": "float" 89 | } 90 | } 91 | }, 92 | "finalMaxScore": { 93 | "type": "float" 94 | }, 95 | "finalMaxWeightedAvg": { 96 | "type": "float" 97 | }, 98 | "finalMinScore": { 99 | "type": "float" 100 | }, 101 | "finalMinWeightedAvg": { 102 | "type": "float" 103 | }, 104 | "finalTotalScore": { 105 | "type": "float" 106 | }, 107 | "finalWeightedAvg": { 108 | "type": "float" 109 | }, 110 | "finalWeightedScore": { 111 | "type": "float" 112 | }, 113 | "gateCriteriaCheck": { 114 | "type": "boolean" 115 | }, 116 | "getLatestRecordEnabled": { 117 | "type": "boolean" 118 | }, 119 | "identifier": { 120 | "type": "text", 121 | "fields": { 122 | "keyword": { 123 | "type": "keyword", 124 | "ignore_above": 256 125 | } 126 | } 127 | }, 128 | "minimunQualifier": { 129 | "type": "float" 130 | }, 131 | "org": { 132 | "type": "text", 133 | "fields": { 134 | "keyword": { 135 | "type": "keyword", 136 | "ignore_above": 256 137 | } 138 | } 139 | }, 140 | "qualified": { 141 | "type": "boolean" 142 | }, 143 | "qualifiedMinCriteria": { 144 | "type": "boolean" 145 | }, 146 | "resourceId": { 147 | "type": "text", 148 | "fields": { 149 | "keyword": { 150 | "type": "keyword", 151 | "ignore_above": 256 152 | } 153 | } 154 | }, 155 | "resourceType": { 156 | "type": "text", 157 | "fields": { 158 | "keyword": { 159 | "type": "keyword", 160 | "ignore_above": 256 161 | } 162 | } 163 | }, 164 | "rootOrg": { 165 | "type": "text", 166 | "fields": { 167 | "keyword": { 168 | "type": "keyword", 169 | "ignore_above": 256 170 | } 171 | } 172 | }, 173 | "scoreGrade": { 174 | "type": "text", 175 | "fields": { 176 | "keyword": { 177 | "type": "keyword", 178 | "ignore_above": 256 179 | } 180 | } 181 | }, 182 | "templateId": { 183 | "type": "text", 184 | "fields": { 185 | "keyword": { 186 | "type": "keyword", 187 | "ignore_above": 256 188 | } 189 | } 190 | }, 191 | "templeteName": { 192 | "type": "text", 193 | "fields": { 194 | "keyword": { 195 | "type": "keyword", 196 | "ignore_above": 256 197 | } 198 | } 199 | }, 200 | "timeStamp": { 201 | "type": "text", 202 | "fields": { 203 | "keyword": { 204 | "type": "keyword", 205 | "ignore_above": 256 206 | } 207 | } 208 | }, 209 | "userId": { 210 | "type": "text", 211 | "fields": { 212 | "keyword": { 213 | "type": "keyword", 214 | "ignore_above": 256 215 | } 216 | } 217 | }, 218 | "versionKey": { 219 | "type": "text", 220 | "fields": { 221 | "keyword": { 222 | "type": "keyword", 223 | "ignore_above": 256 224 | } 225 | } 226 | } 227 | } 228 | } 229 | } 230 | } 231 | } -------------------------------------------------------------------------------- /src/main/java/org/sunbird/scoringengine/models/EvaluatorModel.java: -------------------------------------------------------------------------------- 1 | 2 | 3 | package org.sunbird.scoringengine.models; 4 | 5 | import javax.validation.constraints.NotBlank; 6 | import javax.validation.constraints.Size; 7 | import java.util.ArrayList; 8 | import java.util.List; 9 | 10 | public class EvaluatorModel { 11 | 12 | private String identifier; 13 | private String rootOrg; 14 | private String org; 15 | @NotBlank 16 | private String resourceId; 17 | @NotBlank 18 | private String resourceType; 19 | @NotBlank 20 | private String userId; 21 | 22 | private String templateId; 23 | private String templeteName; 24 | 25 | private double compositeScore; 26 | private double minimunQualifier; 27 | 28 | private double finalMaxScore; 29 | private double finalMinScore; 30 | 31 | private double finalTotalScore; 32 | private double finalWeightedAvg; 33 | 34 | private double finalMaxWeightedAvg; 35 | private double finalMinWeightedAvg; 36 | 37 | private double finalWeightedScore; 38 | 39 | private boolean getLatestRecordEnabled; 40 | 41 | 42 | private String scoreGrade; 43 | 44 | private boolean isQualifiedMinCriteria = true; 45 | 46 | private boolean gateCriteriaCheck; 47 | 48 | private boolean isQualified = true; 49 | 50 | @Size(min = 1) 51 | private List criteriaModels = new ArrayList<>(); 52 | 53 | private String timeStamp; 54 | 55 | private String versionKey; 56 | 57 | public String getIdentifier() { 58 | return identifier; 59 | } 60 | 61 | public void setIdentifier(String identifier) { 62 | this.identifier = identifier; 63 | } 64 | 65 | public String getRootOrg() { 66 | return rootOrg; 67 | } 68 | 69 | public void setRootOrg(String rootOrg) { 70 | this.rootOrg = rootOrg; 71 | } 72 | 73 | public String getOrg() { 74 | return org; 75 | } 76 | 77 | public void setOrg(String org) { 78 | this.org = org; 79 | } 80 | 81 | public String getResourceId() { 82 | return resourceId; 83 | } 84 | 85 | public void setResourceId(String resourceId) { 86 | this.resourceId = resourceId; 87 | } 88 | 89 | public String getResourceType() { 90 | return resourceType; 91 | } 92 | 93 | public void setResourceType(String resourceType) { 94 | this.resourceType = resourceType; 95 | } 96 | 97 | public String getTemplateId() { 98 | return templateId; 99 | } 100 | 101 | public void setTemplateId(String templateId) { 102 | this.templateId = templateId; 103 | } 104 | 105 | public double getCompositeScore() { 106 | return compositeScore; 107 | } 108 | 109 | public void setCompositeScore(double compositeScore) { 110 | this.compositeScore = compositeScore; 111 | } 112 | 113 | public double getMinimunQualifier() { 114 | return minimunQualifier; 115 | } 116 | 117 | public void setMinimunQualifier(double minmunQualifier) { 118 | this.minimunQualifier = minmunQualifier; 119 | } 120 | 121 | public List getCriteriaModels() { 122 | return criteriaModels; 123 | } 124 | 125 | public void setCriteriaModels(List criteriaModels) { 126 | this.criteriaModels = criteriaModels; 127 | } 128 | 129 | public double getFinalMaxScore() { 130 | return finalMaxScore; 131 | } 132 | 133 | public void setFinalMaxScore(double finalMaxScore) { 134 | this.finalMaxScore = finalMaxScore; 135 | } 136 | 137 | public double getFinalMinScore() { 138 | return finalMinScore; 139 | } 140 | 141 | public void setFinalMinScore(double finalMinScore) { 142 | this.finalMinScore = finalMinScore; 143 | } 144 | 145 | public double getFinalTotalScore() { 146 | return finalTotalScore; 147 | } 148 | 149 | public void setFinalTotalScore(double finalTotalScore) { 150 | this.finalTotalScore = finalTotalScore; 151 | } 152 | 153 | public double getFinalWeightedAvg() { 154 | return finalWeightedAvg; 155 | } 156 | 157 | public void setFinalWeightedAvg(double finalWeightedAvg) { 158 | this.finalWeightedAvg = finalWeightedAvg; 159 | } 160 | 161 | public double getFinalMaxWeightedAvg() { 162 | return finalMaxWeightedAvg; 163 | } 164 | 165 | public void setFinalMaxWeightedAvg(double finalMaxWeightedAvg) { 166 | this.finalMaxWeightedAvg = finalMaxWeightedAvg; 167 | } 168 | 169 | public double getFinalMinWeightedAvg() { 170 | return finalMinWeightedAvg; 171 | } 172 | 173 | public void setFinalMinWeightedAvg(double finalMinWeightedAvg) { 174 | this.finalMinWeightedAvg = finalMinWeightedAvg; 175 | } 176 | 177 | public String getUserId() { 178 | return userId; 179 | } 180 | 181 | public void setUserId(String userId) { 182 | this.userId = userId; 183 | } 184 | 185 | public String getTimeStamp() { 186 | return timeStamp; 187 | } 188 | 189 | public void setTimeStamp(String timeStamp) { 190 | this.timeStamp = timeStamp; 191 | } 192 | 193 | public String getTempleteName() { 194 | return templeteName; 195 | } 196 | 197 | public void setTempleteName(String templeteName) { 198 | this.templeteName = templeteName; 199 | } 200 | 201 | public double getFinalWeightedScore() { 202 | return finalWeightedScore; 203 | } 204 | 205 | public void setFinalWeightedScore(double finalWeightedScore) { 206 | this.finalWeightedScore = finalWeightedScore; 207 | } 208 | 209 | public boolean isGetLatestRecordEnabled() { 210 | return getLatestRecordEnabled; 211 | } 212 | 213 | public void setGetLatestRecordEnabled(boolean getLatestRecordEnabled) { 214 | this.getLatestRecordEnabled = getLatestRecordEnabled; 215 | } 216 | 217 | public String getScoreGrade() { 218 | return scoreGrade; 219 | } 220 | 221 | public void setScoreGrade(String scoreGrade) { 222 | this.scoreGrade = scoreGrade; 223 | } 224 | 225 | public boolean isQualifiedMinCriteria() { 226 | return isQualifiedMinCriteria; 227 | } 228 | 229 | public void setQualifiedMinCriteria(boolean qualifiedMinCriteria) { 230 | isQualifiedMinCriteria = qualifiedMinCriteria; 231 | } 232 | 233 | public boolean isGateCriteriaCheck() { 234 | return gateCriteriaCheck; 235 | } 236 | 237 | public void setGateCriteriaCheck(boolean gateCriteriaCheck) { 238 | this.gateCriteriaCheck = gateCriteriaCheck; 239 | } 240 | 241 | public boolean isQualified() { 242 | return isQualified; 243 | } 244 | 245 | public void setQualified(boolean qualified) { 246 | isQualified = qualified; 247 | } 248 | 249 | public String getVersionKey() { 250 | return versionKey; 251 | } 252 | 253 | public void setVersionKey(String versionKey) { 254 | this.versionKey = versionKey; 255 | } 256 | } 257 | -------------------------------------------------------------------------------- /src/main/java/org/sunbird/scoringengine/util/IndexerService.java: -------------------------------------------------------------------------------- 1 | 2 | package org.sunbird.scoringengine.util; 3 | 4 | import com.fasterxml.jackson.databind.JsonNode; 5 | import com.fasterxml.jackson.databind.ObjectMapper; 6 | import com.fasterxml.jackson.databind.node.ArrayNode; 7 | import com.fasterxml.jackson.databind.node.JsonNodeFactory; 8 | import org.apache.http.HttpHost; 9 | import org.apache.http.auth.AuthScope; 10 | import org.apache.http.auth.UsernamePasswordCredentials; 11 | import org.apache.http.client.CredentialsProvider; 12 | import org.apache.http.impl.client.BasicCredentialsProvider; 13 | import org.elasticsearch.action.get.GetRequest; 14 | import org.elasticsearch.action.get.GetResponse; 15 | import org.elasticsearch.action.index.IndexRequest; 16 | import org.elasticsearch.action.index.IndexResponse; 17 | import org.elasticsearch.action.search.SearchRequest; 18 | import org.elasticsearch.action.search.SearchResponse; 19 | import org.elasticsearch.action.update.UpdateRequest; 20 | import org.elasticsearch.action.update.UpdateResponse; 21 | import org.elasticsearch.client.RequestOptions; 22 | import org.elasticsearch.client.RestClient; 23 | import org.elasticsearch.client.RestClientBuilder; 24 | import org.elasticsearch.client.RestHighLevelClient; 25 | import org.elasticsearch.index.query.BoolQueryBuilder; 26 | import org.elasticsearch.index.query.QueryBuilders; 27 | import org.elasticsearch.rest.RestStatus; 28 | import org.elasticsearch.search.SearchHit; 29 | import org.elasticsearch.search.builder.SearchSourceBuilder; 30 | import org.elasticsearch.search.sort.SortBuilder; 31 | import org.elasticsearch.search.sort.SortBuilders; 32 | import org.elasticsearch.search.sort.SortOrder; 33 | import org.slf4j.Logger; 34 | import org.slf4j.LoggerFactory; 35 | import org.springframework.beans.factory.annotation.Value; 36 | import org.springframework.stereotype.Service; 37 | 38 | import javax.annotation.PostConstruct; 39 | import java.io.IOException; 40 | import java.util.Map; 41 | 42 | @Service 43 | public class IndexerService { 44 | private RestHighLevelClient esClient; 45 | 46 | @Value("${es.auth.enabled}") 47 | private boolean esAuthEnabled; 48 | 49 | @Value("${es.host}") 50 | private String esHost; 51 | 52 | @Value("${es.port}") 53 | private String esPort; 54 | 55 | @Value("${es.username}") 56 | private String esUsername; 57 | 58 | @Value("${es.password}") 59 | private String esPassword; 60 | 61 | private Logger logger = LoggerFactory.getLogger(IndexerService.class); 62 | 63 | 64 | @PostConstruct 65 | public void init() { 66 | logger.info("#INITIALIZING SCORE INDEXER"); 67 | HttpHost[] hosts = new HttpHost[1]; 68 | hosts[0] = new HttpHost(esHost, Integer.parseInt(esPort)); 69 | logger.info(" " + esHost); 70 | logger.info(" " + esPort); 71 | logger.info(" " + esUsername); 72 | logger.info(" " + esPassword); 73 | RestClientBuilder builder = RestClient.builder(hosts); 74 | if (esAuthEnabled) { 75 | final CredentialsProvider credentialsProvider = new BasicCredentialsProvider(); 76 | credentialsProvider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(esUsername, esPassword)); 77 | builder.setHttpClientConfigCallback(httpClientBuilder -> httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider)); 78 | } 79 | esClient = new RestHighLevelClient(builder); 80 | logger.info("#ES CLIENT INITIALIZED"); 81 | logger.info("#INITIALIZING SCORE INDEXER FINISHED"); 82 | } 83 | 84 | public RestStatus addEntity(String index, String indexType, String entityId, Map indexDocument) { 85 | logger.debug("addEntity starts with index {} and entityId {}", index, entityId); 86 | IndexResponse response = null; 87 | try { 88 | response = esClient.index(new IndexRequest(index, indexType, entityId).source(indexDocument), RequestOptions.DEFAULT); 89 | logger.info("response for entityId {} is {}", entityId, response); 90 | } catch (IOException e) { 91 | logger.error("Exception in adding record to ElasticSearch", e); 92 | } 93 | if(null == response) 94 | return null; 95 | return response.status(); 96 | } 97 | 98 | public JsonNode search(String index, Map searchQuery) throws IOException { 99 | boolean getLatestRecord = (boolean)searchQuery.get("isGetLatestRecordEnabled"); 100 | searchQuery.remove("isGetLatestRecordEnabled"); 101 | BoolQueryBuilder query = buildQuery(searchQuery); 102 | logger.info("Search query " + new ObjectMapper().writeValueAsString(query)); 103 | SearchSourceBuilder sourceBuilder = new SearchSourceBuilder().query(query); 104 | if(getLatestRecord){ 105 | sourceBuilder.sort(SortBuilders.fieldSort("timeStamp.keyword").order(SortOrder.DESC)); 106 | sourceBuilder.size(1); 107 | } 108 | SearchRequest searchRequest = new SearchRequest(index).source(sourceBuilder); 109 | 110 | ArrayNode resultArray = JsonNodeFactory.instance.arrayNode(); 111 | ObjectMapper mapper = new ObjectMapper(); 112 | SearchResponse searchResponse = esClient.search(searchRequest, RequestOptions.DEFAULT); 113 | for (SearchHit hit : searchResponse.getHits()) { 114 | JsonNode node = mapper.readValue(hit.getSourceAsString(), JsonNode.class); 115 | resultArray.add(node); 116 | } 117 | logger.debug("Total search records found " + resultArray.size()); 118 | 119 | return resultArray; 120 | 121 | } 122 | 123 | private BoolQueryBuilder buildQuery(Map searchQuery) { 124 | BoolQueryBuilder query = QueryBuilders.boolQuery(); 125 | 126 | for ( Map.Entry filter : searchQuery.entrySet()) { 127 | String field = filter.getKey(); 128 | Object value = filter.getValue(); 129 | boolean emptyValue = (value instanceof String) ? ((String) value).isEmpty() : false; 130 | if(value != null && !emptyValue) query = query.must(QueryBuilders.matchQuery(field, value)); 131 | } 132 | return query; 133 | } 134 | public Map readEntity(String index, String indexType, String entityId) throws IOException { 135 | logger.info("readEntity starts with index {} and entityId {}", index, entityId); 136 | GetResponse response = null; 137 | response = esClient.get(new GetRequest(index, indexType, entityId), RequestOptions.DEFAULT); 138 | return response.getSourceAsMap(); 139 | } 140 | 141 | 142 | public RestStatus updateEntity(String index, String indexType, String entityId, Map indexDocument) { 143 | logger.debug("updateEntity starts with index {} and entityId {}", index, entityId); 144 | UpdateResponse response = null; 145 | try { 146 | response = esClient.update(new UpdateRequest(index.toLowerCase(), indexType, entityId).doc(indexDocument), RequestOptions.DEFAULT); 147 | } catch (IOException e) { 148 | logger.error("Exception in updating a record to ElasticSearch", e); 149 | } 150 | if(null == response) 151 | return null; 152 | return response.status(); 153 | } 154 | 155 | 156 | 157 | 158 | } 159 | -------------------------------------------------------------------------------- /mvnw: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # ---------------------------------------------------------------------------- 3 | # Licensed to the Apache Software Foundation (ASF) under one 4 | # or more contributor license agreements. See the NOTICE file 5 | # distributed with this work for additional information 6 | # regarding copyright ownership. The ASF licenses this file 7 | # to you under the Apache License, Version 2.0 (the 8 | # "License"); you may not use this file except in compliance 9 | # with the License. You may obtain a copy of the License at 10 | # 11 | # https://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, 14 | # software distributed under the License is distributed on an 15 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | # KIND, either express or implied. See the License for the 17 | # specific language governing permissions and limitations 18 | # under the License. 19 | # ---------------------------------------------------------------------------- 20 | 21 | # ---------------------------------------------------------------------------- 22 | # Maven2 Start Up Batch script 23 | # 24 | # Required ENV vars: 25 | # ------------------ 26 | # JAVA_HOME - location of a JDK home dir 27 | # 28 | # Optional ENV vars 29 | # ----------------- 30 | # M2_HOME - location of maven2's installed home dir 31 | # MAVEN_OPTS - parameters passed to the Java VM when running Maven 32 | # e.g. to debug Maven itself, use 33 | # set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 34 | # MAVEN_SKIP_RC - flag to disable loading of mavenrc files 35 | # ---------------------------------------------------------------------------- 36 | 37 | if [ -z "$MAVEN_SKIP_RC" ] ; then 38 | 39 | if [ -f /etc/mavenrc ] ; then 40 | . /etc/mavenrc 41 | fi 42 | 43 | if [ -f "$HOME/.mavenrc" ] ; then 44 | . "$HOME/.mavenrc" 45 | fi 46 | 47 | fi 48 | 49 | # OS specific support. $var _must_ be set to either true or false. 50 | cygwin=false; 51 | darwin=false; 52 | mingw=false 53 | case "`uname`" in 54 | CYGWIN*) cygwin=true ;; 55 | MINGW*) mingw=true;; 56 | Darwin*) darwin=true 57 | # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home 58 | # See https://developer.apple.com/library/mac/qa/qa1170/_index.html 59 | if [ -z "$JAVA_HOME" ]; then 60 | if [ -x "/usr/libexec/java_home" ]; then 61 | export JAVA_HOME="`/usr/libexec/java_home`" 62 | else 63 | export JAVA_HOME="/Library/Java/Home" 64 | fi 65 | fi 66 | ;; 67 | esac 68 | 69 | if [ -z "$JAVA_HOME" ] ; then 70 | if [ -r /etc/gentoo-release ] ; then 71 | JAVA_HOME=`java-config --jre-home` 72 | fi 73 | fi 74 | 75 | if [ -z "$M2_HOME" ] ; then 76 | ## resolve links - $0 may be a link to maven's home 77 | PRG="$0" 78 | 79 | # need this for relative symlinks 80 | while [ -h "$PRG" ] ; do 81 | ls=`ls -ld "$PRG"` 82 | link=`expr "$ls" : '.*-> \(.*\)$'` 83 | if expr "$link" : '/.*' > /dev/null; then 84 | PRG="$link" 85 | else 86 | PRG="`dirname "$PRG"`/$link" 87 | fi 88 | done 89 | 90 | saveddir=`pwd` 91 | 92 | M2_HOME=`dirname "$PRG"`/.. 93 | 94 | # make it fully qualified 95 | M2_HOME=`cd "$M2_HOME" && pwd` 96 | 97 | cd "$saveddir" 98 | # echo Using m2 at $M2_HOME 99 | fi 100 | 101 | # For Cygwin, ensure paths are in UNIX format before anything is touched 102 | if $cygwin ; then 103 | [ -n "$M2_HOME" ] && 104 | M2_HOME=`cygpath --unix "$M2_HOME"` 105 | [ -n "$JAVA_HOME" ] && 106 | JAVA_HOME=`cygpath --unix "$JAVA_HOME"` 107 | [ -n "$CLASSPATH" ] && 108 | CLASSPATH=`cygpath --path --unix "$CLASSPATH"` 109 | fi 110 | 111 | # For Mingw, ensure paths are in UNIX format before anything is touched 112 | if $mingw ; then 113 | [ -n "$M2_HOME" ] && 114 | M2_HOME="`(cd "$M2_HOME"; pwd)`" 115 | [ -n "$JAVA_HOME" ] && 116 | JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" 117 | # TODO classpath? 118 | fi 119 | 120 | if [ -z "$JAVA_HOME" ]; then 121 | javaExecutable="`which javac`" 122 | if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then 123 | # readlink(1) is not available as standard on Solaris 10. 124 | readLink=`which readlink` 125 | if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then 126 | if $darwin ; then 127 | javaHome="`dirname \"$javaExecutable\"`" 128 | javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" 129 | else 130 | javaExecutable="`readlink -f \"$javaExecutable\"`" 131 | fi 132 | javaHome="`dirname \"$javaExecutable\"`" 133 | javaHome=`expr "$javaHome" : '\(.*\)/bin'` 134 | JAVA_HOME="$javaHome" 135 | export JAVA_HOME 136 | fi 137 | fi 138 | fi 139 | 140 | if [ -z "$JAVACMD" ] ; then 141 | if [ -n "$JAVA_HOME" ] ; then 142 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 143 | # IBM's JDK on AIX uses strange locations for the executables 144 | JAVACMD="$JAVA_HOME/jre/sh/java" 145 | else 146 | JAVACMD="$JAVA_HOME/bin/java" 147 | fi 148 | else 149 | JAVACMD="`which java`" 150 | fi 151 | fi 152 | 153 | if [ ! -x "$JAVACMD" ] ; then 154 | echo "Error: JAVA_HOME is not defined correctly." >&2 155 | echo " We cannot execute $JAVACMD" >&2 156 | exit 1 157 | fi 158 | 159 | if [ -z "$JAVA_HOME" ] ; then 160 | echo "Warning: JAVA_HOME environment variable is not set." 161 | fi 162 | 163 | CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher 164 | 165 | # traverses directory structure from process work directory to filesystem root 166 | # first directory with .mvn subdirectory is considered project base directory 167 | find_maven_basedir() { 168 | 169 | if [ -z "$1" ] 170 | then 171 | echo "Path not specified to find_maven_basedir" 172 | return 1 173 | fi 174 | 175 | basedir="$1" 176 | wdir="$1" 177 | while [ "$wdir" != '/' ] ; do 178 | if [ -d "$wdir"/.mvn ] ; then 179 | basedir=$wdir 180 | break 181 | fi 182 | # workaround for JBEAP-8937 (on Solaris 10/Sparc) 183 | if [ -d "${wdir}" ]; then 184 | wdir=`cd "$wdir/.."; pwd` 185 | fi 186 | # end of workaround 187 | done 188 | echo "${basedir}" 189 | } 190 | 191 | # concatenates all lines of a file 192 | concat_lines() { 193 | if [ -f "$1" ]; then 194 | echo "$(tr -s '\n' ' ' < "$1")" 195 | fi 196 | } 197 | 198 | BASE_DIR=`find_maven_basedir "$(pwd)"` 199 | if [ -z "$BASE_DIR" ]; then 200 | exit 1; 201 | fi 202 | 203 | ########################################################################################## 204 | # Extension to allow automatically downloading the maven-wrapper.jar from Maven-central 205 | # This allows using the maven wrapper in projects that prohibit checking in binary data. 206 | ########################################################################################## 207 | if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then 208 | if [ "$MVNW_VERBOSE" = true ]; then 209 | echo "Found .mvn/wrapper/maven-wrapper.jar" 210 | fi 211 | else 212 | if [ "$MVNW_VERBOSE" = true ]; then 213 | echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..." 214 | fi 215 | jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.4.2/maven-wrapper-0.4.2.jar" 216 | while IFS="=" read key value; do 217 | case "$key" in (wrapperUrl) jarUrl="$value"; break ;; 218 | esac 219 | done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties" 220 | if [ "$MVNW_VERBOSE" = true ]; then 221 | echo "Downloading from: $jarUrl" 222 | fi 223 | wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" 224 | 225 | if command -v wget > /dev/null; then 226 | if [ "$MVNW_VERBOSE" = true ]; then 227 | echo "Found wget ... using wget" 228 | fi 229 | wget "$jarUrl" -O "$wrapperJarPath" 230 | elif command -v curl > /dev/null; then 231 | if [ "$MVNW_VERBOSE" = true ]; then 232 | echo "Found curl ... using curl" 233 | fi 234 | curl -o "$wrapperJarPath" "$jarUrl" 235 | else 236 | if [ "$MVNW_VERBOSE" = true ]; then 237 | echo "Falling back to using Java to download" 238 | fi 239 | javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java" 240 | if [ -e "$javaClass" ]; then 241 | if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then 242 | if [ "$MVNW_VERBOSE" = true ]; then 243 | echo " - Compiling MavenWrapperDownloader.java ..." 244 | fi 245 | # Compiling the Java class 246 | ("$JAVA_HOME/bin/javac" "$javaClass") 247 | fi 248 | if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then 249 | # Running the downloader 250 | if [ "$MVNW_VERBOSE" = true ]; then 251 | echo " - Running MavenWrapperDownloader.java ..." 252 | fi 253 | ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR") 254 | fi 255 | fi 256 | fi 257 | fi 258 | ########################################################################################## 259 | # End of extension 260 | ########################################################################################## 261 | 262 | export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} 263 | if [ "$MVNW_VERBOSE" = true ]; then 264 | echo $MAVEN_PROJECTBASEDIR 265 | fi 266 | MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" 267 | 268 | # For Cygwin, switch paths to Windows format before running java 269 | if $cygwin; then 270 | [ -n "$M2_HOME" ] && 271 | M2_HOME=`cygpath --path --windows "$M2_HOME"` 272 | [ -n "$JAVA_HOME" ] && 273 | JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` 274 | [ -n "$CLASSPATH" ] && 275 | CLASSPATH=`cygpath --path --windows "$CLASSPATH"` 276 | [ -n "$MAVEN_PROJECTBASEDIR" ] && 277 | MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` 278 | fi 279 | 280 | WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 281 | 282 | exec "$JAVACMD" \ 283 | $MAVEN_OPTS \ 284 | -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ 285 | "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ 286 | ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" 287 | -------------------------------------------------------------------------------- /src/main/java/org/sunbird/scoringengine/util/ComputeScores.java: -------------------------------------------------------------------------------- 1 | 2 | package org.sunbird.scoringengine.util; 3 | 4 | import com.fasterxml.jackson.databind.ObjectMapper; 5 | import org.slf4j.Logger; 6 | import org.slf4j.LoggerFactory; 7 | import org.springframework.util.CollectionUtils; 8 | import org.sunbird.scoringengine.models.CriteriaModel; 9 | import org.sunbird.scoringengine.models.EvaluatorModel; 10 | import org.sunbird.scoringengine.models.QualifierModel; 11 | import org.sunbird.scoringengine.schema.model.Criteria; 12 | import org.sunbird.scoringengine.schema.model.Qualifier; 13 | import org.sunbird.scoringengine.schema.model.Range; 14 | import org.sunbird.scoringengine.schema.model.ScoringTemplate; 15 | 16 | import java.text.SimpleDateFormat; 17 | import java.util.Calendar; 18 | import java.util.HashMap; 19 | import java.util.List; 20 | import java.util.Map; 21 | import java.util.stream.Collectors; 22 | 23 | 24 | public class ComputeScores { 25 | 26 | private Logger logger = LoggerFactory.getLogger(ComputeScores.class); 27 | private ObjectMapper mapper = new ObjectMapper(); 28 | 29 | private SimpleDateFormat formatterDateTime = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); 30 | private static String identifierPrefix = "lex_score_"; 31 | 32 | private ScoringTemplate scoringTemplate; 33 | 34 | private static final String MIN_SCORE_PASS_CONST = "pass"; 35 | 36 | private static final String MIN_SCORE_FAIL_CONST = "fail"; 37 | 38 | public ComputeScores(ScoringTemplate scoringTemplate){ 39 | this.scoringTemplate = scoringTemplate; 40 | } 41 | 42 | /** 43 | * Compute using scoring schema and with fixed and range type 44 | * @param evaluatorModel 45 | * @throws Exception 46 | */ 47 | public void computeV2(EvaluatorModel evaluatorModel) throws Exception { 48 | 49 | for (CriteriaModel cm : evaluatorModel.getCriteriaModels()) { 50 | 51 | //to get maxscore , minacceptablescore 52 | Map criteriaMap = scoringTemplate.getCriteria().stream().collect(Collectors.toMap(c -> c.getCriteria(), c -> c)); 53 | Criteria criteria = criteriaMap.get(cm.getCriteria()); 54 | evaluatorModel.setTemplateId(scoringTemplate.getTemplate_id()); 55 | evaluatorModel.setTempleteName(scoringTemplate.getTemplateName()); 56 | evaluatorModel.setRootOrg(scoringTemplate.getRootOrg()); 57 | evaluatorModel.setOrg(scoringTemplate.getOrg()); 58 | //EvaluationCriteria criteria = scoreCriteriaRepository.findCriteriaByName(evaluatorModel.getRootOrg(), evaluatorModel.getOrg(), cm.getCriteria()); 59 | 60 | double maxScore = criteria.getMax_score(); 61 | cm.setMaxScore(maxScore); 62 | double minScore = criteria.getMin_acceptable_score(); 63 | cm.setMinScore(minScore); 64 | double weightage = criteria.getWeightage(); 65 | cm.setWeightage(weightage); 66 | 67 | 68 | Map qualifierMap = criteria.getQualifiers().stream().collect(Collectors.toMap(Qualifier::getQualifier, qualifier -> qualifier)); 69 | 70 | Map> qualifierFixedScores = criteria.getQualifiers().stream().collect(Collectors.toMap(Qualifier::getQualifier, Qualifier::getFixed_score)); 71 | // List scoreQualifiers = scoreQualifierRepository.findQualifiersByCriteria(evaluatorModel.getRootOrg(), evaluatorModel.getOrg(), criteria.getCriteriaId()); 72 | // logger.info("scoreQualifiers: ",mapper.writeValueAsString(scoreQualifiers)); 73 | // Map> qualifierFixedScores = scoreQualifiers.stream().collect( 74 | // Collectors.toMap(ScoreQualifier::getQualifier, ScoreQualifier::getFixedScore)); 75 | Map> qualifierRangeScores = criteria.getQualifiers().stream().collect(Collectors.toMap(Qualifier::getQualifier, Qualifier::getRange_score)); 76 | int maxScoreExcludedValue = 0; 77 | for (QualifierModel qm : cm.getQualifiers()) { 78 | if (qm.getEvaluated() != null) { 79 | int score = qualifierFixedScores.get(qm.getName()).get(qm.getEvaluated()); 80 | qm.setScoreValue(score); 81 | qm.setScoringType("fixed"); 82 | } 83 | else { 84 | Range range = qualifierRangeScores.get(qm.getName()).get(qm.getScoreRange()); 85 | qm.setScoreValue(range.getAssignedValue()); 86 | qm.setScoringType("ranged"); 87 | 88 | } 89 | 90 | if (Boolean.TRUE.equals(qualifierMap.get(qm.getName()).getModify_max_score()) && 91 | qualifierMap.get(qm.getName()).getMax_score_modify_value().containsKey(qm.getEvaluated())) { 92 | maxScoreExcludedValue += qualifierMap.get(qm.getName()).getMax_score_modify_value().get(qm.getEvaluated()); 93 | } 94 | qm.setDescription(qualifierMap.get(qm.getName()).getDescription()); 95 | if (evaluatorModel.isQualified() && !CollectionUtils.isEmpty(qualifierMap.get(qm.getName()).getDisqualifyOption()) 96 | && qualifierMap.get(qm.getName()).getDisqualifyOption().contains(qm.getEvaluated())) { 97 | evaluatorModel.setQualified(false); 98 | } 99 | } 100 | cm.setMaxScore(cm.getMaxScore() - maxScoreExcludedValue); 101 | if (Boolean.TRUE.equals(criteria.getMin_score_weightage_enable())) { 102 | cm.setMinScore(cm.getMaxScore() * criteria.getMin_score_weightage()); 103 | } 104 | List scoreVals = cm.getQualifiers().stream().map(q -> q.getScoreValue()).collect(Collectors.toList()); 105 | cm.setTotalScore(MathFunction.sum(scoreVals)); 106 | cm.setWeightedAvg(MathFunction.weightedAvg(scoreVals, weightage)); 107 | cm.setMaxWeightedAvg(MathFunction.maxWeightedAvg(cm.getMaxScore(), weightage)); 108 | cm.setMinWeightedAvg(MathFunction.minWeightedAvg(minScore, weightage)); 109 | cm.setWeightedScore(MathFunction.weightedScore(cm.getTotalScore(), cm.getMaxScore(), weightage)); 110 | } 111 | 112 | List criteriaScoreValues = evaluatorModel.getCriteriaModels().stream().map(c -> c.getTotalScore()).collect(Collectors.toList()); 113 | evaluatorModel.setFinalTotalScore(MathFunction.sum(criteriaScoreValues)); 114 | 115 | List criteriaWeightedAvgVals = evaluatorModel.getCriteriaModels().stream().map(CriteriaModel::getWeightedAvg).collect(Collectors.toList()); 116 | evaluatorModel.setFinalWeightedAvg(MathFunction.sum(criteriaWeightedAvgVals)); 117 | 118 | 119 | List criteriaMaxScoreVals = evaluatorModel.getCriteriaModels().stream().map(CriteriaModel::getMaxScore).collect(Collectors.toList()); 120 | evaluatorModel.setFinalMaxScore(MathFunction.sum(criteriaMaxScoreVals)); 121 | 122 | List criteriaMaxWeightedAvgVals = evaluatorModel.getCriteriaModels().stream().map(CriteriaModel::getMaxWeightedAvg).collect(Collectors.toList()); 123 | evaluatorModel.setFinalMaxWeightedAvg(MathFunction.sum(criteriaMaxWeightedAvgVals)); 124 | 125 | List criteriaMinScoreVals = evaluatorModel.getCriteriaModels().stream().map(CriteriaModel::getMinScore).collect(Collectors.toList()); 126 | evaluatorModel.setFinalMinScore(MathFunction.sum(criteriaMinScoreVals)); 127 | 128 | List criteriaMinWeightedAvgVals = evaluatorModel.getCriteriaModels().stream().map(CriteriaModel::getMinWeightedAvg).collect(Collectors.toList()); 129 | evaluatorModel.setFinalMinWeightedAvg(MathFunction.sum(criteriaMinWeightedAvgVals)); 130 | 131 | List criteriaWeightsVals = evaluatorModel.getCriteriaModels().stream().map(CriteriaModel::getWeightedScore).collect(Collectors.toList()); 132 | evaluatorModel.setFinalWeightedScore(MathFunction.sum(criteriaWeightsVals)); 133 | 134 | setScoringStatus(evaluatorModel, scoringTemplate); 135 | 136 | String timeStamp = formatterDateTime.format(Calendar.getInstance().getTime()); 137 | evaluatorModel.setTimeStamp(timeStamp); 138 | 139 | long millsec = System.currentTimeMillis(); 140 | evaluatorModel.setIdentifier(identifierPrefix + millsec); 141 | 142 | } 143 | 144 | /* public void compute(EvaluatorModel evaluatorModel) throws Exception{ 145 | 146 | for (CriteriaModel cm : evaluatorModel.getCriteriaModels()){ 147 | 148 | //to get maxscore , minacceptablescore 149 | EvaluationCriteria criteria = scoreCriteriaRepository.findCriteriaByName(evaluatorModel.getRootOrg(), evaluatorModel.getOrg(), cm.getCriteria()); 150 | 151 | logger.info("EvaluationCriteria: ",mapper.writeValueAsString(criteria)); 152 | double maxScore = criteria.getMaxScore(); 153 | cm.setMaxScore(maxScore); 154 | double minScore = criteria.getMinScore(); 155 | cm.setMinScore(minScore); 156 | double weightage = criteria.getWeightage(); 157 | cm.setWeightage(weightage); 158 | 159 | 160 | List scoreQualifiers = scoreQualifierRepository.findQualifiersByCriteria(evaluatorModel.getRootOrg(), evaluatorModel.getOrg(), criteria.getCriteriaId()); 161 | logger.info("scoreQualifiers: ",mapper.writeValueAsString(scoreQualifiers)); 162 | Map> qualifierFixedScores = scoreQualifiers.stream().collect( 163 | Collectors.toMap(ScoreQualifier::getQualifier, ScoreQualifier::getFixedScore)); 164 | 165 | for(QualifierModel qm : cm.getQualifiers()){ 166 | int score = qualifierFixedScores.get(qm.getName()).get(qm.getEvaluated()); 167 | qm.setScoreValue(score); 168 | qm.setScoringType("fixed"); 169 | } 170 | 171 | double totalScore = cm.getQualifiers().stream().mapToDouble(QualifierModel::getScoreValue).sum(); 172 | cm.setTotalScore(totalScore); 173 | 174 | //computed: weitageAvg, maxweightageAvg, minWeightageAvg 175 | double weightedAvg = totalScore * weightage; 176 | double maxweightedAvg = maxScore * weightage; 177 | double minWeightedAvg = minScore * weightage; 178 | 179 | cm.setWeightedAvg(weightedAvg); 180 | cm.setMaxWeightedAvg(maxweightedAvg); 181 | cm.setMinWeightedAvg(minWeightedAvg); 182 | 183 | } 184 | 185 | double finalTotatScore = evaluatorModel.getCriteriaModels().stream().mapToDouble(CriteriaModel::getTotalScore).sum(); 186 | evaluatorModel.setFinalTotalScore(finalTotatScore); 187 | 188 | double finalWeightedAvg = evaluatorModel.getCriteriaModels().stream().mapToDouble(CriteriaModel::getWeightedAvg).sum(); 189 | evaluatorModel.setFinalWeightedAvg(finalWeightedAvg); 190 | 191 | double finalMaxScore = evaluatorModel.getCriteriaModels().stream().mapToDouble(CriteriaModel::getMaxScore).sum(); 192 | evaluatorModel.setFinalMaxScore(finalMaxScore); 193 | 194 | double finalMaxWeightedAvg = evaluatorModel.getCriteriaModels().stream().mapToDouble(CriteriaModel::getMaxWeightedAvg).sum(); 195 | evaluatorModel.setFinalMaxWeightedAvg(finalMaxWeightedAvg); 196 | 197 | double finalMinScore = evaluatorModel.getCriteriaModels().stream().mapToDouble(CriteriaModel::getMinScore).sum(); 198 | evaluatorModel.setFinalMinScore(finalMinScore); 199 | 200 | double finalMinWeightedAvg = evaluatorModel.getCriteriaModels().stream().mapToDouble(CriteriaModel::getMinWeightedAvg).sum(); 201 | evaluatorModel.setFinalMinWeightedAvg(finalMinWeightedAvg); 202 | 203 | 204 | String timeStamp = formatterDateTime.format(Calendar.getInstance().getTime()); 205 | evaluatorModel.setTimeStamp(timeStamp); 206 | 207 | long millsec = System.currentTimeMillis(); 208 | evaluatorModel.setIdentifier(IDENTIFIER_PREFIX + millsec); 209 | 210 | }*/ 211 | 212 | /** 213 | * 214 | * @param evaluatorModel Evaluator Model 215 | * @param scoringTemplate Scoring Template 216 | */ 217 | private void setScoringStatus(EvaluatorModel evaluatorModel, ScoringTemplate scoringTemplate) { 218 | try { 219 | boolean isMinimumCriteriaPassed = true; 220 | for (CriteriaModel criteriaModel : evaluatorModel.getCriteriaModels()) { 221 | if (criteriaModel.getTotalScore() < criteriaModel.getMinScore()) { 222 | isMinimumCriteriaPassed = false; 223 | criteriaModel.setQualifiedMinCriteria(false); 224 | evaluatorModel.setQualifiedMinCriteria(false); 225 | if (!scoringTemplate.getStatus_on_min_criteria().isEmpty()) 226 | evaluatorModel.setScoreGrade(scoringTemplate.getStatus_on_min_criteria().getOrDefault(MIN_SCORE_FAIL_CONST, "")); 227 | } 228 | } 229 | if (isMinimumCriteriaPassed && !scoringTemplate.getScore_grades().isEmpty()) { 230 | for (HashMap hm : scoringTemplate.getScore_grades()) { 231 | Map.Entry entry = hm.entrySet().iterator().next(); 232 | double min = Double.parseDouble(entry.getKey().split("-")[0]); 233 | double max = Double.parseDouble(entry.getKey().split("-")[1]); 234 | if (min < evaluatorModel.getFinalWeightedScore() && max > evaluatorModel.getFinalWeightedScore()) { 235 | evaluatorModel.setScoreGrade(entry.getValue()); 236 | break; 237 | } 238 | } 239 | } 240 | } catch (Exception ex) { 241 | logger.error("Error occurred while setting the score status!"); 242 | logger.error(ex.toString()); 243 | } 244 | } 245 | 246 | } 247 | -------------------------------------------------------------------------------- /src/main/resources/ScoringSchema.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "", 3 | "scoringTemplates": [ 4 | { 5 | "rootOrg": "igot", 6 | "org": "dopt", 7 | "template_id": "temp-01", 8 | "templateName": "Quality score card", 9 | "version": 1.0, 10 | "weightage": 0, 11 | "max_score": 0, 12 | "min_acceptable_score": 0, 13 | "criteria": [ 14 | { 15 | "criteria": "Instructional Methods", 16 | "weightage": 0.3, 17 | "max_score": 45, 18 | "min_acceptable_score": 33.75, 19 | "qualifiers": [ 20 | { 21 | "qualifier": "The course puts the learner at the centre of the learning experience", 22 | "weightage": 0.4, 23 | "max_score": 10, 24 | "min_acceptable_score": 4, 25 | "fixed_score": { 26 | "StronglyAgree": 5, 27 | "Agree": 3, 28 | "Disagree": 1, 29 | "Strongly Disagree": 0 30 | }, 31 | "range_score": { 32 | "10-20": { 33 | "min": 10, 34 | "max": 20, 35 | "assignedValue": 3 36 | }, 37 | "20-30": { 38 | "min": 20, 39 | "max": 30, 40 | "assignedValue": 4 41 | }, 42 | "30-40": { 43 | "min": 30, 44 | "max": 40, 45 | "assignedValue": 6 46 | } 47 | } 48 | }, 49 | { 50 | "qualifier": "Passing criteria is clearly stated", 51 | "weightage": 0.6, 52 | "max_score": 10, 53 | "min_acceptable_score": 3.75, 54 | "fixed_score": { 55 | "StronglyAgree": 5, 56 | "Agree": 3, 57 | "Disagree": 1, 58 | "Strongly Disagree": 0 59 | }, 60 | "range_score": { 61 | "10-20": { 62 | "min": 10, 63 | "max": 20, 64 | "assignedValue": 4 65 | }, 66 | "20-30": { 67 | "min": 20, 68 | "max": 30, 69 | "assignedValue": 5 70 | }, 71 | "30-40": { 72 | "min": 30, 73 | "max": 40, 74 | "assignedValue": 7 75 | } 76 | } 77 | } 78 | ] 79 | } 80 | ] 81 | }, 82 | { 83 | "rootOrg": "igot", 84 | "org": "dopt", 85 | "template_id": "content_scoring_template", 86 | "templateName": "iGOT course quality score card", 87 | "version": 1, 88 | "weightage": 0, 89 | "max_score": 0, 90 | "min_acceptable_score": 0, 91 | "criteria": [ 92 | { 93 | "criteria": "Mandatory Requirements", 94 | "description": "All the requirements mentioned in this section are necessary to be followed. Not meeting any of these requirements would lead to disqualification of the course. Please mark yes if the statement is true for your content.", 95 | "weightage": 0, 96 | "max_score": 0, 97 | "min_acceptable_score": 0, 98 | "qualifiers": [ 99 | { 100 | "qualifier": "Mandatory_Requirement_001", 101 | "description": "The content does not contain hate speech, abuse, violence and profanity.", 102 | "weightage": 0, 103 | "max_score": 0, 104 | "min_acceptable_score": 0, 105 | "fixed_score": { 106 | "Yes": 0, 107 | "No": 0 108 | }, 109 | "range_score": { 110 | "10-20": { 111 | "min": 0, 112 | "max": 0, 113 | "assignedValue": 0 114 | } 115 | }, 116 | "options": [ 117 | { 118 | "name": "Yes" 119 | }, 120 | { 121 | "name": "No" 122 | } 123 | ], 124 | "disqualifyOption": [ 125 | "No" 126 | ] 127 | }, 128 | { 129 | "qualifier": "Mandatory_Requirement_002", 130 | "description": "There is no sexual content, nudity or vulgarity in the course being developed.", 131 | "weightage": 0, 132 | "max_score": 0, 133 | "min_acceptable_score": 0, 134 | "fixed_score": { 135 | "Yes": 0, 136 | "No": 0 137 | }, 138 | "range_score": { 139 | "10-20": { 140 | "min": 0, 141 | "max": 0, 142 | "assignedValue": 0 143 | } 144 | }, 145 | "options": [ 146 | { 147 | "name": "Yes" 148 | }, 149 | { 150 | "name": "No" 151 | } 152 | ], 153 | "disqualifyOption": [ 154 | "No" 155 | ] 156 | }, 157 | { 158 | "qualifier": "Mandatory_Requirement_003", 159 | "description": "There is no defamation of any institution or individual as part of the course.", 160 | "weightage": 0, 161 | "max_score": 0, 162 | "min_acceptable_score": 0, 163 | "fixed_score": { 164 | "Yes": 0, 165 | "No": 0 166 | }, 167 | "range_score": { 168 | "10-20": { 169 | "min": 0, 170 | "max": 0, 171 | "assignedValue": 0 172 | } 173 | }, 174 | "options": [ 175 | { 176 | "name": "Yes" 177 | }, 178 | { 179 | "name": "No" 180 | } 181 | ], 182 | "disqualifyOption": [ 183 | "No" 184 | ] 185 | }, 186 | { 187 | "qualifier": "Mandatory_Requirement_004", 188 | "description": "The content is appropriate for all users on the platform and does not hurt sentiments of any race, caste, religion or gender.", 189 | "weightage": 0, 190 | "max_score": 0, 191 | "min_acceptable_score": 0, 192 | "fixed_score": { 193 | "Yes": 0, 194 | "No": 0 195 | }, 196 | "range_score": { 197 | "10-20": { 198 | "min": 0, 199 | "max": 0, 200 | "assignedValue": 0 201 | } 202 | }, 203 | "options": [ 204 | { 205 | "name": "Yes" 206 | }, 207 | { 208 | "name": "No" 209 | } 210 | ], 211 | "disqualifyOption": [ 212 | "No" 213 | ] 214 | }, 215 | { 216 | "qualifier": "Mandatory_Requirement_005", 217 | "description": "All maps, borders and historical figures have been appropriately and accurately represented.", 218 | "weightage": 0, 219 | "max_score": 0, 220 | "min_acceptable_score": 0, 221 | "fixed_score": { 222 | "Yes": 0, 223 | "No": 0, 224 | "Not Applicable": 0 225 | }, 226 | "range_score": { 227 | "10-20": { 228 | "min": 0, 229 | "max": 0, 230 | "assignedValue": 0 231 | } 232 | }, 233 | "options": [ 234 | { 235 | "name": "Yes" 236 | }, 237 | { 238 | "name": "No" 239 | }, 240 | { 241 | "name": "Not Applicable" 242 | } 243 | ], 244 | "disqualifyOption": [ 245 | "No" 246 | ] 247 | }, 248 | { 249 | "qualifier": "Mandatory_Requirement_006", 250 | "description": "The content does not contain any copyright violation.", 251 | "weightage": 0, 252 | "max_score": 0, 253 | "min_acceptable_score": 0, 254 | "fixed_score": { 255 | "Yes": 0, 256 | "No": 0 257 | }, 258 | "range_score": { 259 | "10-20": { 260 | "min": 0, 261 | "max": 0, 262 | "assignedValue": 0 263 | } 264 | }, 265 | "options": [ 266 | { 267 | "name": "Yes" 268 | }, 269 | { 270 | "name": "No" 271 | } 272 | ], 273 | "disqualifyOption": [ 274 | "No" 275 | ] 276 | }, 277 | { 278 | "qualifier": "Mandatory_Requirement_007", 279 | "description": "The content is devoid of plagiarism and a plagiarism certificate is attached with this documentation.", 280 | "weightage": 0, 281 | "max_score": 0, 282 | "min_acceptable_score": 0, 283 | "fixed_score": { 284 | "Yes": 0, 285 | "No": 0 286 | }, 287 | "range_score": { 288 | "10-20": { 289 | "min": 0, 290 | "max": 0, 291 | "assignedValue": 0 292 | } 293 | }, 294 | "options": [ 295 | { 296 | "name": "Yes" 297 | }, 298 | { 299 | "name": "No" 300 | } 301 | ], 302 | "disqualifyOption": [ 303 | "No" 304 | ] 305 | }, 306 | { 307 | "qualifier": "Mandatory_Requirement_008", 308 | "description": "All sources – online or printed materials – have been duly credited. A list of references is maintained at the end of each module.", 309 | "weightage": 0, 310 | "max_score": 0, 311 | "min_acceptable_score": 0, 312 | "fixed_score": { 313 | "Yes": 0, 314 | "No": 0 315 | }, 316 | "range_score": { 317 | "10-20": { 318 | "min": 0, 319 | "max": 0, 320 | "assignedValue": 0 321 | } 322 | }, 323 | "options": [ 324 | { 325 | "name": "Yes" 326 | }, 327 | { 328 | "name": "No" 329 | } 330 | ], 331 | "disqualifyOption": [ 332 | "No" 333 | ] 334 | }, 335 | { 336 | "qualifier": "Mandatory_Requirement_009", 337 | "description": "The course is tagged to one or more competencies that it addresses.", 338 | "weightage": 0, 339 | "max_score": 0, 340 | "min_acceptable_score": 0, 341 | "fixed_score": { 342 | "Yes": 0, 343 | "No": 0 344 | }, 345 | "range_score": { 346 | "10-20": { 347 | "min": 0, 348 | "max": 0, 349 | "assignedValue": 0 350 | } 351 | }, 352 | "options": [ 353 | { 354 | "name": "Yes" 355 | }, 356 | { 357 | "name": "No" 358 | } 359 | ], 360 | "disqualifyOption": [ 361 | "No" 362 | ] 363 | } 364 | ] 365 | }, 366 | { 367 | "criteria": "Sustainable Development", 368 | "description": "This section assesses if the course uses language and media elements that are based on the leading principles of sustainable development for education and training. Please mark yes if the statement is true for your content.", 369 | "weightage": 0, 370 | "max_score": 0, 371 | "min_acceptable_score": 0, 372 | "qualifiers": [ 373 | { 374 | "qualifier": "Sustainable_Development_001", 375 | "description": "The course conserves the integrity of ecosystems and biodiversity, and promotes sustainable management and use of natural resources.", 376 | "weightage": 0, 377 | "max_score": 0, 378 | "min_acceptable_score": 0, 379 | "fixed_score": { 380 | "Yes": 0, 381 | "No": 0, 382 | "Not Applicable": 0 383 | }, 384 | "range_score": { 385 | "10-20": { 386 | "min": 0, 387 | "max": 0, 388 | "assignedValue": 0 389 | } 390 | }, 391 | "options": [ 392 | { 393 | "name": "Yes" 394 | }, 395 | { 396 | "name": "No" 397 | }, 398 | { 399 | "name": "Not Applicable" 400 | } 401 | ], 402 | "disqualifyOption": [ 403 | "No" 404 | ] 405 | }, 406 | { 407 | "qualifier": "Sustainable_Development_002", 408 | "description": "The course addresses the risks of climate change impact and disasters, integrates climate change adaptation considerations, and does not exacerbate the vulnerability of communities to climate change impacts or disaster risks.", 409 | "weightage": 0, 410 | "max_score": 0, 411 | "min_acceptable_score": 0, 412 | "fixed_score": { 413 | "Yes": 0, 414 | "No": 0, 415 | "Not Applicable": 0 416 | }, 417 | "range_score": { 418 | "10-20": { 419 | "min": 0, 420 | "max": 0, 421 | "assignedValue": 0 422 | } 423 | }, 424 | "options": [ 425 | { 426 | "name": "Yes" 427 | }, 428 | { 429 | "name": "No" 430 | }, 431 | { 432 | "name": "Not Applicable" 433 | } 434 | ], 435 | "disqualifyOption": [ 436 | "No" 437 | ] 438 | }, 439 | { 440 | "qualifier": "Sustainable_Development_003", 441 | "description": "The course does not promote practices related to increased pollution, unsound chemicals and waste management – especially with respect to plastic waste, hazardous wastes, organic and ozone depleting pollutants.", 442 | "weightage": 0, 443 | "max_score": 0, 444 | "min_acceptable_score": 0, 445 | "fixed_score": { 446 | "Yes": 0, 447 | "No": 0, 448 | "Not Applicable": 0 449 | }, 450 | "range_score": { 451 | "10-20": { 452 | "min": 0, 453 | "max": 0, 454 | "assignedValue": 0 455 | } 456 | }, 457 | "options": [ 458 | { 459 | "name": "Yes" 460 | }, 461 | { 462 | "name": "No" 463 | }, 464 | { 465 | "name": "Not Applicable" 466 | } 467 | ], 468 | "disqualifyOption": [ 469 | "No" 470 | ] 471 | }, 472 | { 473 | "qualifier": "Sustainable_Development_004", 474 | "description": "The course promotes sustainable and efficient use of resources (energy, land and water).", 475 | "weightage": 0, 476 | "max_score": 0, 477 | "min_acceptable_score": 0, 478 | "fixed_score": { 479 | "Yes": 0, 480 | "No": 0, 481 | "Not Applicable": 0 482 | }, 483 | "range_score": { 484 | "10-20": { 485 | "min": 0, 486 | "max": 0, 487 | "assignedValue": 0 488 | } 489 | }, 490 | "options": [ 491 | { 492 | "name": "Yes" 493 | }, 494 | { 495 | "name": "No" 496 | }, 497 | { 498 | "name": "Not Applicable" 499 | } 500 | ], 501 | "disqualifyOption": [ 502 | "No" 503 | ] 504 | }, 505 | { 506 | "qualifier": "Sustainable_Development_005", 507 | "description": "The course advocates responsible and sustainable lifestyles, including Green Economy and Green Jobs.", 508 | "weightage": 0, 509 | "max_score": 0, 510 | "min_acceptable_score": 0, 511 | "fixed_score": { 512 | "Yes": 0, 513 | "No": 0, 514 | "Not Applicable": 0 515 | }, 516 | "range_score": { 517 | "10-20": { 518 | "min": 0, 519 | "max": 0, 520 | "assignedValue": 0 521 | } 522 | }, 523 | "options": [ 524 | { 525 | "name": "Yes" 526 | }, 527 | { 528 | "name": "No" 529 | }, 530 | { 531 | "name": "Not Applicable" 532 | } 533 | ], 534 | "disqualifyOption": [ 535 | "No" 536 | ] 537 | }, 538 | { 539 | "qualifier": "Sustainable_Development_006", 540 | "description": "The course does not portray harming living beings (humans, animals or plants) with the intent to benefit from such practices.", 541 | "weightage": 0, 542 | "max_score": 0, 543 | "min_acceptable_score": 0, 544 | "fixed_score": { 545 | "Yes": 0, 546 | "No": 0, 547 | "Not Applicable": 0 548 | }, 549 | "range_score": { 550 | "10-20": { 551 | "min": 0, 552 | "max": 0, 553 | "assignedValue": 0 554 | } 555 | }, 556 | "options": [ 557 | { 558 | "name": "Yes" 559 | }, 560 | { 561 | "name": "No" 562 | }, 563 | { 564 | "name": "Not Applicable" 565 | } 566 | ], 567 | "disqualifyOption": [ 568 | "No" 569 | ] 570 | }, 571 | { 572 | "qualifier": "Sustainable_Development_007", 573 | "description": "The course does not promote forced labour, armed conflicts, delocalization and migration, displacement and involuntary settlement.", 574 | "weightage": 0, 575 | "max_score": 0, 576 | "min_acceptable_score": 0, 577 | "fixed_score": { 578 | "Yes": 0, 579 | "No": 0, 580 | "Not Applicable": 0 581 | }, 582 | "range_score": { 583 | "10-20": { 584 | "min": 0, 585 | "max": 0, 586 | "assignedValue": 0 587 | } 588 | }, 589 | "options": [ 590 | { 591 | "name": "Yes" 592 | }, 593 | { 594 | "name": "No" 595 | }, 596 | { 597 | "name": "Not Applicable" 598 | } 599 | ], 600 | "disqualifyOption": [ 601 | "No" 602 | ] 603 | }, 604 | { 605 | "qualifier": "Sustainable_Development_008", 606 | "description": "The course promotes friendly relations among nations, peaceful solutions, and living together.", 607 | "weightage": 0, 608 | "max_score": 0, 609 | "min_acceptable_score": 0, 610 | "fixed_score": { 611 | "Yes": 0, 612 | "No": 0, 613 | "Not Applicable": 0 614 | }, 615 | "range_score": { 616 | "10-20": { 617 | "min": 0, 618 | "max": 0, 619 | "assignedValue": 0 620 | } 621 | }, 622 | "options": [ 623 | { 624 | "name": "Yes" 625 | }, 626 | { 627 | "name": "No" 628 | }, 629 | { 630 | "name": "Not Applicable" 631 | } 632 | ], 633 | "disqualifyOption": [ 634 | "No" 635 | ] 636 | }, 637 | { 638 | "qualifier": "Sustainable_Development_009", 639 | "description": "The course does not promote/advance any form of violence including (but not limited to) bullying, physical or verbal abuse, gender-based violence, and extremism.", 640 | "weightage": 0, 641 | "max_score": 0, 642 | "min_acceptable_score": 0, 643 | "fixed_score": { 644 | "Yes": 0, 645 | "No": 0, 646 | "Not Applicable": 0 647 | }, 648 | "range_score": { 649 | "10-20": { 650 | "min": 0, 651 | "max": 0, 652 | "assignedValue": 0 653 | } 654 | }, 655 | "options": [ 656 | { 657 | "name": "Yes" 658 | }, 659 | { 660 | "name": "No" 661 | }, 662 | { 663 | "name": "Not Applicable" 664 | } 665 | ], 666 | "disqualifyOption": [ 667 | "No" 668 | ] 669 | }, 670 | { 671 | "qualifier": "Sustainable_Development_010", 672 | "description": "The course promotes/advances equality, inclusion, and non-discrimination – for example by gender, caste, race, class, and disability.", 673 | "weightage": 0, 674 | "max_score": 0, 675 | "min_acceptable_score": 0, 676 | "fixed_score": { 677 | "Yes": 0, 678 | "No": 0, 679 | "Not Applicable": 0 680 | }, 681 | "range_score": { 682 | "10-20": { 683 | "min": 0, 684 | "max": 0, 685 | "assignedValue": 0 686 | } 687 | }, 688 | "options": [ 689 | { 690 | "name": "Yes" 691 | }, 692 | { 693 | "name": "No" 694 | }, 695 | { 696 | "name": "Not Applicable" 697 | } 698 | ], 699 | "disqualifyOption": [ 700 | "No" 701 | ] 702 | }, 703 | { 704 | "qualifier": "Sustainable_Development_011", 705 | "description": "The course does not depict working children under the legal age of 18 years.", 706 | "weightage": 0, 707 | "max_score": 0, 708 | "min_acceptable_score": 0, 709 | "fixed_score": { 710 | "Yes": 0, 711 | "No": 0, 712 | "Not Applicable": 0 713 | }, 714 | "range_score": { 715 | "10-20": { 716 | "min": 0, 717 | "max": 0, 718 | "assignedValue": 0 719 | } 720 | }, 721 | "options": [ 722 | { 723 | "name": "Yes" 724 | }, 725 | { 726 | "name": "No" 727 | }, 728 | { 729 | "name": "Not Applicable" 730 | } 731 | ], 732 | "disqualifyOption": [ 733 | "No" 734 | ] 735 | }, 736 | { 737 | "qualifier": "Sustainable_Development_012", 738 | "description": "The course does not breach any provisions with respect to national employment, labour laws and international commitments.", 739 | "weightage": 0, 740 | "max_score": 0, 741 | "min_acceptable_score": 0, 742 | "fixed_score": { 743 | "Yes": 0, 744 | "No": 0, 745 | "Not Applicable": 0 746 | }, 747 | "range_score": { 748 | "10-20": { 749 | "min": 0, 750 | "max": 0, 751 | "assignedValue": 0 752 | } 753 | }, 754 | "options": [ 755 | { 756 | "name": "Yes" 757 | }, 758 | { 759 | "name": "No" 760 | }, 761 | { 762 | "name": "Not Applicable" 763 | } 764 | ], 765 | "disqualifyOption": [ 766 | "No" 767 | ] 768 | }, 769 | { 770 | "qualifier": "Sustainable_Development_013", 771 | "description": "The course promotes occupational health and safety standards with a special focus on female workers, young workers, migrant workers and workers with disabilities.", 772 | "weightage": 0, 773 | "max_score": 0, 774 | "min_acceptable_score": 0, 775 | "fixed_score": { 776 | "Yes": 0, 777 | "No": 0, 778 | "Not Applicable": 0 779 | }, 780 | "range_score": { 781 | "10-20": { 782 | "min": 0, 783 | "max": 0, 784 | "assignedValue": 0 785 | } 786 | }, 787 | "options": [ 788 | { 789 | "name": "Yes" 790 | }, 791 | { 792 | "name": "No" 793 | }, 794 | { 795 | "name": "Not Applicable" 796 | } 797 | ], 798 | "disqualifyOption": [ 799 | "No" 800 | ] 801 | }, 802 | { 803 | "qualifier": "Sustainable_Development_014", 804 | "description": "The course advocates sustainable cities, communities and health of the planet for future generations.", 805 | "weightage": 0, 806 | "max_score": 0, 807 | "min_acceptable_score": 0, 808 | "fixed_score": { 809 | "Yes": 0, 810 | "No": 0, 811 | "Not Applicable": 0 812 | }, 813 | "range_score": { 814 | "10-20": { 815 | "min": 0, 816 | "max": 0, 817 | "assignedValue": 0 818 | } 819 | }, 820 | "options": [ 821 | { 822 | "name": "Yes" 823 | }, 824 | { 825 | "name": "No" 826 | }, 827 | { 828 | "name": "Not Applicable" 829 | } 830 | ], 831 | "disqualifyOption": [ 832 | "No" 833 | ] 834 | }, 835 | { 836 | "qualifier": "Sustainable_Development_015", 837 | "description": "The course does not adversely affect international or intercultural understanding, solidarity and cooperation.", 838 | "weightage": 0, 839 | "max_score": 0, 840 | "min_acceptable_score": 0, 841 | "fixed_score": { 842 | "Yes": 0, 843 | "No": 0, 844 | "Not Applicable": 0 845 | }, 846 | "range_score": { 847 | "10-20": { 848 | "min": 0, 849 | "max": 0, 850 | "assignedValue": 0 851 | } 852 | }, 853 | "options": [ 854 | { 855 | "name": "Yes" 856 | }, 857 | { 858 | "name": "No" 859 | }, 860 | { 861 | "name": "Not Applicable" 862 | } 863 | ], 864 | "disqualifyOption": [ 865 | "No" 866 | ] 867 | }, 868 | { 869 | "qualifier": "Sustainable_Development_016", 870 | "description": "The course recognizes, respects, protects and preserve indigenous people's culture, knowledge and practices.", 871 | "weightage": 0, 872 | "max_score": 0, 873 | "min_acceptable_score": 0, 874 | "fixed_score": { 875 | "Yes": 0, 876 | "No": 0, 877 | "Not Applicable": 0 878 | }, 879 | "range_score": { 880 | "10-20": { 881 | "min": 0, 882 | "max": 0, 883 | "assignedValue": 0 884 | } 885 | }, 886 | "options": [ 887 | { 888 | "name": "Yes" 889 | }, 890 | { 891 | "name": "No" 892 | }, 893 | { 894 | "name": "Not Applicable" 895 | } 896 | ], 897 | "disqualifyOption": [ 898 | "No" 899 | ] 900 | } 901 | ] 902 | }, 903 | { 904 | "criteria": "Instructional Methods", 905 | "description": "This section evaluates if instructional methods are effectively utilised to acquire the stated competencies and skills. Please mark yes if the statement is true for your content.", 906 | "weightage": 0.25, 907 | "max_score": 65, 908 | "min_acceptable_score": 0, 909 | "min_score_weightage_enable": true, 910 | "min_score_weightage": 0.5, 911 | "qualifiers": [ 912 | { 913 | "qualifier": "Instructional_Methods_001", 914 | "description": "A clear learning pathway is defined, and learners are able to track their journey (E.g. a menu of topics and subtopics indicates what the learner has finished and how much is remaining within the course).", 915 | "weightage": 0.13, 916 | "max_score": 5, 917 | "min_acceptable_score": 2.5, 918 | "fixed_score": { 919 | "Yes": 5, 920 | "No": 0, 921 | "Not Applicable": 0 922 | }, 923 | "range_score": { 924 | "10-20": { 925 | "min": 10, 926 | "max": 20, 927 | "assignedValue": 3 928 | }, 929 | "20-30": { 930 | "min": 20, 931 | "max": 30, 932 | "assignedValue": 4 933 | }, 934 | "30-40": { 935 | "min": 30, 936 | "max": 40, 937 | "assignedValue": 6 938 | } 939 | }, 940 | "modify_max_score": true, 941 | "max_score_modify_value": { 942 | "Not Applicable": 5 943 | }, 944 | "options": [ 945 | { 946 | "name": "Yes" 947 | }, 948 | { 949 | "name": "No" 950 | } 951 | ] 952 | }, 953 | { 954 | "qualifier": "Instructional_Methods_002", 955 | "description": "Each module contains at least one ‘Watch’ element as defined in the WTDET content framework.", 956 | "weightage": 0.13, 957 | "max_score": 5, 958 | "min_acceptable_score": 2.5, 959 | "fixed_score": { 960 | "Yes": 5, 961 | "No": 0, 962 | "Not Applicable": 0 963 | }, 964 | "range_score": { 965 | "10-20": { 966 | "min": 10, 967 | "max": 20, 968 | "assignedValue": 4 969 | }, 970 | "20-30": { 971 | "min": 20, 972 | "max": 30, 973 | "assignedValue": 5 974 | }, 975 | "30-40": { 976 | "min": 30, 977 | "max": 40, 978 | "assignedValue": 7 979 | } 980 | }, 981 | "modify_max_score": true, 982 | "max_score_modify_value": { 983 | "Not Applicable": 5 984 | }, 985 | "options": [ 986 | { 987 | "name": "Yes" 988 | }, 989 | { 990 | "name": "No" 991 | } 992 | ] 993 | }, 994 | { 995 | "qualifier": "Instructional_Methods_003", 996 | "description": "Each module contains at least one ‘Think’ element as defined in the WTDET content framework.", 997 | "weightage": 0.13, 998 | "max_score": 5, 999 | "min_acceptable_score": 2.5, 1000 | "fixed_score": { 1001 | "Yes": 5, 1002 | "No": 0, 1003 | "Not Applicable": 0 1004 | }, 1005 | "range_score": { 1006 | "10-20": { 1007 | "min": 10, 1008 | "max": 20, 1009 | "assignedValue": 4 1010 | }, 1011 | "20-30": { 1012 | "min": 20, 1013 | "max": 30, 1014 | "assignedValue": 5 1015 | }, 1016 | "30-40": { 1017 | "min": 30, 1018 | "max": 40, 1019 | "assignedValue": 7 1020 | } 1021 | }, 1022 | "modify_max_score": true, 1023 | "max_score_modify_value": { 1024 | "Not Applicable": 5 1025 | }, 1026 | "options": [ 1027 | { 1028 | "name": "Yes" 1029 | }, 1030 | { 1031 | "name": "No" 1032 | } 1033 | ] 1034 | }, 1035 | { 1036 | "qualifier": "Instructional_Methods_004", 1037 | "description": "Each module contains at least one ‘Do’ element as defined in the WTDET content framework.", 1038 | "weightage": 0.13, 1039 | "max_score": 5, 1040 | "min_acceptable_score": 2.5, 1041 | "fixed_score": { 1042 | "Yes": 5, 1043 | "No": 0, 1044 | "Not Applicable": 0 1045 | }, 1046 | "range_score": { 1047 | "10-20": { 1048 | "min": 10, 1049 | "max": 20, 1050 | "assignedValue": 4 1051 | }, 1052 | "20-30": { 1053 | "min": 20, 1054 | "max": 30, 1055 | "assignedValue": 5 1056 | }, 1057 | "30-40": { 1058 | "min": 30, 1059 | "max": 40, 1060 | "assignedValue": 7 1061 | } 1062 | }, 1063 | "modify_max_score": true, 1064 | "max_score_modify_value": { 1065 | "Not Applicable": 5 1066 | }, 1067 | "options": [ 1068 | { 1069 | "name": "Yes" 1070 | }, 1071 | { 1072 | "name": "No" 1073 | } 1074 | ] 1075 | }, 1076 | { 1077 | "qualifier": "Instructional_Methods_005", 1078 | "description": "Each module contains at least one ‘Explore’ element as defined in the WTDET content framework.", 1079 | "weightage": 0.13, 1080 | "max_score": 5, 1081 | "min_acceptable_score": 2.5, 1082 | "fixed_score": { 1083 | "Yes": 5, 1084 | "No": 0, 1085 | "Not Applicable": 0 1086 | }, 1087 | "range_score": { 1088 | "10-20": { 1089 | "min": 10, 1090 | "max": 20, 1091 | "assignedValue": 4 1092 | }, 1093 | "20-30": { 1094 | "min": 20, 1095 | "max": 30, 1096 | "assignedValue": 5 1097 | }, 1098 | "30-40": { 1099 | "min": 30, 1100 | "max": 40, 1101 | "assignedValue": 7 1102 | } 1103 | }, 1104 | "modify_max_score": true, 1105 | "max_score_modify_value": { 1106 | "Not Applicable": 5 1107 | }, 1108 | "options": [ 1109 | { 1110 | "name": "Yes" 1111 | }, 1112 | { 1113 | "name": "No" 1114 | } 1115 | ] 1116 | }, 1117 | { 1118 | "qualifier": "Instructional_Methods_006", 1119 | "description": "The course introduction is included as part of the course, and states the learning objectives and intended audience at the beginning of the course.", 1120 | "weightage": 0.13, 1121 | "max_score": 5, 1122 | "min_acceptable_score": 2.5, 1123 | "fixed_score": { 1124 | "Yes": 5, 1125 | "No": 0, 1126 | "Not Applicable": 0 1127 | }, 1128 | "range_score": { 1129 | "10-20": { 1130 | "min": 10, 1131 | "max": 20, 1132 | "assignedValue": 4 1133 | }, 1134 | "20-30": { 1135 | "min": 20, 1136 | "max": 30, 1137 | "assignedValue": 5 1138 | }, 1139 | "30-40": { 1140 | "min": 30, 1141 | "max": 40, 1142 | "assignedValue": 7 1143 | } 1144 | }, 1145 | "modify_max_score": true, 1146 | "max_score_modify_value": { 1147 | "Not Applicable": 5 1148 | }, 1149 | "options": [ 1150 | { 1151 | "name": "Yes" 1152 | }, 1153 | { 1154 | "name": "No" 1155 | } 1156 | ] 1157 | }, 1158 | { 1159 | "qualifier": "Instructional_Methods_007", 1160 | "description": "Module-level learning objectives are stated clearly and align with the overall course goals.", 1161 | "weightage": 0.13, 1162 | "max_score": 5, 1163 | "min_acceptable_score": 2.5, 1164 | "fixed_score": { 1165 | "Yes": 5, 1166 | "No": 0, 1167 | "Not Applicable": 0 1168 | }, 1169 | "range_score": { 1170 | "10-20": { 1171 | "min": 10, 1172 | "max": 20, 1173 | "assignedValue": 4 1174 | }, 1175 | "20-30": { 1176 | "min": 20, 1177 | "max": 30, 1178 | "assignedValue": 5 1179 | }, 1180 | "30-40": { 1181 | "min": 30, 1182 | "max": 40, 1183 | "assignedValue": 7 1184 | } 1185 | }, 1186 | "modify_max_score": true, 1187 | "max_score_modify_value": { 1188 | "Not Applicable": 5 1189 | }, 1190 | "options": [ 1191 | { 1192 | "name": "Yes" 1193 | }, 1194 | { 1195 | "name": "No" 1196 | } 1197 | ] 1198 | }, 1199 | { 1200 | "qualifier": "Instructional_Methods_008", 1201 | "description": "The course employs a variety of multimedia tools as necessary,which appropriate for the content and target group.", 1202 | "weightage": 0.13, 1203 | "max_score": 5, 1204 | "min_acceptable_score": 2.5, 1205 | "fixed_score": { 1206 | "Yes": 5, 1207 | "No": 0, 1208 | "Not Applicable": 0 1209 | }, 1210 | "range_score": { 1211 | "10-20": { 1212 | "min": 10, 1213 | "max": 20, 1214 | "assignedValue": 4 1215 | }, 1216 | "20-30": { 1217 | "min": 20, 1218 | "max": 30, 1219 | "assignedValue": 5 1220 | }, 1221 | "30-40": { 1222 | "min": 30, 1223 | "max": 40, 1224 | "assignedValue": 7 1225 | } 1226 | }, 1227 | "modify_max_score": true, 1228 | "max_score_modify_value": { 1229 | "Not Applicable": 5 1230 | }, 1231 | "options": [ 1232 | { 1233 | "name": "Yes" 1234 | }, 1235 | { 1236 | "name": "No" 1237 | } 1238 | ] 1239 | }, 1240 | { 1241 | "qualifier": "Instructional_Methods_009", 1242 | "description": "There are practice reinforcement questions after approximately every 7 screens.", 1243 | "weightage": 0.13, 1244 | "max_score": 5, 1245 | "min_acceptable_score": 2.5, 1246 | "fixed_score": { 1247 | "Yes": 5, 1248 | "No": 0, 1249 | "Not Applicable": 0 1250 | }, 1251 | "range_score": { 1252 | "10-20": { 1253 | "min": 10, 1254 | "max": 20, 1255 | "assignedValue": 4 1256 | }, 1257 | "20-30": { 1258 | "min": 20, 1259 | "max": 30, 1260 | "assignedValue": 5 1261 | }, 1262 | "30-40": { 1263 | "min": 30, 1264 | "max": 40, 1265 | "assignedValue": 7 1266 | } 1267 | }, 1268 | "modify_max_score": true, 1269 | "max_score_modify_value": { 1270 | "Not Applicable": 5 1271 | }, 1272 | "options": [ 1273 | { 1274 | "name": "Yes" 1275 | }, 1276 | { 1277 | "name": "No" 1278 | } 1279 | ] 1280 | }, 1281 | { 1282 | "qualifier": "Instructional_Methods_010", 1283 | "description": "All practice activities (‘Think’ elements) provide diagnostic feedback i.e. when a learner answers a question (whether incorrectly or correctly), the system provides them with descriptive feedback.", 1284 | "weightage": 0.13, 1285 | "max_score": 5, 1286 | "min_acceptable_score": 2.5, 1287 | "fixed_score": { 1288 | "Yes": 5, 1289 | "No": 0, 1290 | "Not Applicable": 0 1291 | }, 1292 | "range_score": { 1293 | "10-20": { 1294 | "min": 10, 1295 | "max": 20, 1296 | "assignedValue": 4 1297 | }, 1298 | "20-30": { 1299 | "min": 20, 1300 | "max": 30, 1301 | "assignedValue": 5 1302 | }, 1303 | "30-40": { 1304 | "min": 30, 1305 | "max": 40, 1306 | "assignedValue": 7 1307 | } 1308 | }, 1309 | "modify_max_score": true, 1310 | "max_score_modify_value": { 1311 | "Not Applicable": 5 1312 | }, 1313 | "options": [ 1314 | { 1315 | "name": "Yes" 1316 | }, 1317 | { 1318 | "name": "No" 1319 | } 1320 | ] 1321 | }, 1322 | { 1323 | "qualifier": "Instructional_Methods_011", 1324 | "description": "There is at least one active learning activity - such as online discussion/ debate, group project, synchronous online meeting, case study or a learning game - per course.", 1325 | "weightage": 0.13, 1326 | "max_score": 5, 1327 | "min_acceptable_score": 2.5, 1328 | "fixed_score": { 1329 | "Yes": 5, 1330 | "No": 0, 1331 | "Not Applicable": 0 1332 | }, 1333 | "range_score": { 1334 | "10-20": { 1335 | "min": 10, 1336 | "max": 20, 1337 | "assignedValue": 4 1338 | }, 1339 | "20-30": { 1340 | "min": 20, 1341 | "max": 30, 1342 | "assignedValue": 5 1343 | }, 1344 | "30-40": { 1345 | "min": 30, 1346 | "max": 40, 1347 | "assignedValue": 7 1348 | } 1349 | }, 1350 | "modify_max_score": true, 1351 | "max_score_modify_value": { 1352 | "Not Applicable": 5 1353 | }, 1354 | "options": [ 1355 | { 1356 | "name": "Yes" 1357 | }, 1358 | { 1359 | "name": "No" 1360 | }, 1361 | { 1362 | "name": "Not Applicable" 1363 | } 1364 | ] 1365 | }, 1366 | { 1367 | "qualifier": "Instructional_Methods_012", 1368 | "description": "The language used in the course is understandable by the target audience.", 1369 | "weightage": 0.13, 1370 | "max_score": 5, 1371 | "min_acceptable_score": 2.5, 1372 | "fixed_score": { 1373 | "Yes": 5, 1374 | "No": 0, 1375 | "Not Applicable": 0 1376 | }, 1377 | "range_score": { 1378 | "10-20": { 1379 | "min": 10, 1380 | "max": 20, 1381 | "assignedValue": 4 1382 | }, 1383 | "20-30": { 1384 | "min": 20, 1385 | "max": 30, 1386 | "assignedValue": 5 1387 | }, 1388 | "30-40": { 1389 | "min": 30, 1390 | "max": 40, 1391 | "assignedValue": 7 1392 | } 1393 | }, 1394 | "modify_max_score": true, 1395 | "max_score_modify_value": { 1396 | "Not Applicable": 5 1397 | }, 1398 | "options": [ 1399 | { 1400 | "name": "Yes" 1401 | }, 1402 | { 1403 | "name": "No" 1404 | } 1405 | ] 1406 | }, 1407 | { 1408 | "qualifier": "Instructional_Methods_013", 1409 | "description": "Learners are given examples of and asked to explore practical applications of the course in their setting.", 1410 | "weightage": 0.13, 1411 | "max_score": 5, 1412 | "min_acceptable_score": 2.5, 1413 | "fixed_score": { 1414 | "Yes": 5, 1415 | "No": 0, 1416 | "Not Applicable": 0 1417 | }, 1418 | "range_score": { 1419 | "10-20": { 1420 | "min": 10, 1421 | "max": 20, 1422 | "assignedValue": 4 1423 | }, 1424 | "20-30": { 1425 | "min": 20, 1426 | "max": 30, 1427 | "assignedValue": 5 1428 | }, 1429 | "30-40": { 1430 | "min": 30, 1431 | "max": 40, 1432 | "assignedValue": 7 1433 | } 1434 | }, 1435 | "modify_max_score": true, 1436 | "max_score_modify_value": { 1437 | "Not Applicable": 5 1438 | }, 1439 | "options": [ 1440 | { 1441 | "name": "Yes" 1442 | }, 1443 | { 1444 | "name": "No" 1445 | } 1446 | ] 1447 | } 1448 | ] 1449 | }, 1450 | { 1451 | "criteria": "Assessment Design", 1452 | "description": "This section evaluates the assessment design. Good assessment design includes testing target competencies and skills readiness. Please mark yes if the statement is true for your content.", 1453 | "weightage": 0.2, 1454 | "max_score": 45, 1455 | "min_acceptable_score": 0, 1456 | "min_score_weightage_enable": true, 1457 | "min_score_weightage": 0.5, 1458 | "qualifiers": [ 1459 | { 1460 | "qualifier": "Assessment_Design_001", 1461 | "description": "The course features a summative assessment (i.e. end-of-course assessment, the ‘Test’ as defined in the WTDET content framework) with clear instructions on evaluation.", 1462 | "weightage": 0.09, 1463 | "max_score": 5, 1464 | "min_acceptable_score": 2.5, 1465 | "fixed_score": { 1466 | "Yes": 5, 1467 | "No": 0, 1468 | "Not Applicable": 0 1469 | }, 1470 | "range_score": { 1471 | "10-20": { 1472 | "min": 10, 1473 | "max": 20, 1474 | "assignedValue": 3 1475 | }, 1476 | "20-30": { 1477 | "min": 20, 1478 | "max": 30, 1479 | "assignedValue": 4 1480 | }, 1481 | "30-40": { 1482 | "min": 30, 1483 | "max": 40, 1484 | "assignedValue": 6 1485 | } 1486 | }, 1487 | "modify_max_score": true, 1488 | "max_score_modify_value": { 1489 | "Not Applicable": 5 1490 | }, 1491 | "options": [ 1492 | { 1493 | "name": "Yes" 1494 | }, 1495 | { 1496 | "name": "No" 1497 | } 1498 | ] 1499 | }, 1500 | { 1501 | "qualifier": "Assessment_Design_002", 1502 | "description": "The criteria for passing the course are clearly stated in the assessment.", 1503 | "weightage": 0.09, 1504 | "max_score": 5, 1505 | "min_acceptable_score": 2.5, 1506 | "fixed_score": { 1507 | "Yes": 5, 1508 | "No": 0, 1509 | "Not Applicable": 0 1510 | }, 1511 | "range_score": { 1512 | "10-20": { 1513 | "min": 10, 1514 | "max": 20, 1515 | "assignedValue": 3 1516 | }, 1517 | "20-30": { 1518 | "min": 20, 1519 | "max": 30, 1520 | "assignedValue": 4 1521 | }, 1522 | "30-40": { 1523 | "min": 30, 1524 | "max": 40, 1525 | "assignedValue": 6 1526 | } 1527 | }, 1528 | "modify_max_score": true, 1529 | "max_score_modify_value": { 1530 | "Not Applicable": 5 1531 | }, 1532 | "options": [ 1533 | { 1534 | "name": "Yes" 1535 | }, 1536 | { 1537 | "name": "No" 1538 | } 1539 | ] 1540 | }, 1541 | { 1542 | "qualifier": "Assessment_Design_003", 1543 | "description": "The learning activities and assessment are consistent with the learning outcomes of the course.", 1544 | "weightage": 0.09, 1545 | "max_score": 5, 1546 | "min_acceptable_score": 2.5, 1547 | "fixed_score": { 1548 | "Yes": 5, 1549 | "No": 0, 1550 | "Not Applicable": 0 1551 | }, 1552 | "range_score": { 1553 | "10-20": { 1554 | "min": 10, 1555 | "max": 20, 1556 | "assignedValue": 3 1557 | }, 1558 | "20-30": { 1559 | "min": 20, 1560 | "max": 30, 1561 | "assignedValue": 4 1562 | }, 1563 | "30-40": { 1564 | "min": 30, 1565 | "max": 40, 1566 | "assignedValue": 6 1567 | } 1568 | }, 1569 | "modify_max_score": true, 1570 | "max_score_modify_value": { 1571 | "Not Applicable": 5 1572 | }, 1573 | "options": [ 1574 | { 1575 | "name": "Yes" 1576 | }, 1577 | { 1578 | "name": "No" 1579 | } 1580 | ] 1581 | }, 1582 | { 1583 | "qualifier": "Assessment_Design_004", 1584 | "description": "The answer options for multiple choice questions, or distractors, are realistic i.e. learners cannot guess the correct answer by easily eliminating incorrect ones. ", 1585 | "weightage": 0.09, 1586 | "max_score": 5, 1587 | "min_acceptable_score": 2.5, 1588 | "fixed_score": { 1589 | "Yes": 5, 1590 | "No": 0, 1591 | "Not Applicable": 0 1592 | }, 1593 | "range_score": { 1594 | "10-20": { 1595 | "min": 10, 1596 | "max": 20, 1597 | "assignedValue": 3 1598 | }, 1599 | "20-30": { 1600 | "min": 20, 1601 | "max": 30, 1602 | "assignedValue": 4 1603 | }, 1604 | "30-40": { 1605 | "min": 30, 1606 | "max": 40, 1607 | "assignedValue": 6 1608 | } 1609 | }, 1610 | "modify_max_score": true, 1611 | "max_score_modify_value": { 1612 | "Not Applicable": 5 1613 | }, 1614 | "options": [ 1615 | { 1616 | "name": "Yes" 1617 | }, 1618 | { 1619 | "name": "No" 1620 | } 1621 | ] 1622 | }, 1623 | { 1624 | "qualifier": "Assessment_Design_005", 1625 | "description": "The assessment uses understandable language and terms.", 1626 | "weightage": 0.09, 1627 | "max_score": 5, 1628 | "min_acceptable_score": 2.5, 1629 | "fixed_score": { 1630 | "Yes": 5, 1631 | "No": 0, 1632 | "Not Applicable": 0 1633 | }, 1634 | "range_score": { 1635 | "10-20": { 1636 | "min": 10, 1637 | "max": 20, 1638 | "assignedValue": 3 1639 | }, 1640 | "20-30": { 1641 | "min": 20, 1642 | "max": 30, 1643 | "assignedValue": 4 1644 | }, 1645 | "30-40": { 1646 | "min": 30, 1647 | "max": 40, 1648 | "assignedValue": 6 1649 | } 1650 | }, 1651 | "modify_max_score": true, 1652 | "max_score_modify_value": { 1653 | "Not Applicable": 5 1654 | }, 1655 | "options": [ 1656 | { 1657 | "name": "Yes" 1658 | }, 1659 | { 1660 | "name": "No" 1661 | } 1662 | ] 1663 | }, 1664 | { 1665 | "qualifier": "Assessment_Design_006", 1666 | "description": "Case studies and case-oriented assessment questions are based on real-life situations.", 1667 | "weightage": 0.09, 1668 | "max_score": 5, 1669 | "min_acceptable_score": 2.5, 1670 | "fixed_score": { 1671 | "Yes": 5, 1672 | "No": 0, 1673 | "Not Applicable": 0 1674 | }, 1675 | "range_score": { 1676 | "10-20": { 1677 | "min": 10, 1678 | "max": 20, 1679 | "assignedValue": 3 1680 | }, 1681 | "20-30": { 1682 | "min": 20, 1683 | "max": 30, 1684 | "assignedValue": 4 1685 | }, 1686 | "30-40": { 1687 | "min": 30, 1688 | "max": 40, 1689 | "assignedValue": 6 1690 | } 1691 | }, 1692 | "modify_max_score": true, 1693 | "max_score_modify_value": { 1694 | "Not Applicable": 5 1695 | }, 1696 | "options": [ 1697 | { 1698 | "name": "Yes" 1699 | }, 1700 | { 1701 | "name": "No" 1702 | } 1703 | ] 1704 | }, 1705 | { 1706 | "qualifier": "Assessment_Design_007", 1707 | "description": "The assessment uses more than one format of questions.", 1708 | "weightage": 0.09, 1709 | "max_score": 5, 1710 | "min_acceptable_score": 2.5, 1711 | "fixed_score": { 1712 | "Yes": 5, 1713 | "No": 0, 1714 | "Not Applicable": 0 1715 | }, 1716 | "range_score": { 1717 | "10-20": { 1718 | "min": 10, 1719 | "max": 20, 1720 | "assignedValue": 3 1721 | }, 1722 | "20-30": { 1723 | "min": 20, 1724 | "max": 30, 1725 | "assignedValue": 4 1726 | }, 1727 | "30-40": { 1728 | "min": 30, 1729 | "max": 40, 1730 | "assignedValue": 6 1731 | } 1732 | }, 1733 | "modify_max_score": true, 1734 | "max_score_modify_value": { 1735 | "Not Applicable": 5 1736 | }, 1737 | "options": [ 1738 | { 1739 | "name": "Yes" 1740 | }, 1741 | { 1742 | "name": "No" 1743 | } 1744 | ] 1745 | }, 1746 | { 1747 | "qualifier": "Assessment_Design_008", 1748 | "description": "The course avoids True/False questions.", 1749 | "weightage": 0.09, 1750 | "max_score": 5, 1751 | "min_acceptable_score": 2.5, 1752 | "fixed_score": { 1753 | "Yes": 5, 1754 | "No": 0, 1755 | "Not Applicable": 0 1756 | }, 1757 | "range_score": { 1758 | "10-20": { 1759 | "min": 10, 1760 | "max": 20, 1761 | "assignedValue": 3 1762 | }, 1763 | "20-30": { 1764 | "min": 20, 1765 | "max": 30, 1766 | "assignedValue": 4 1767 | }, 1768 | "30-40": { 1769 | "min": 30, 1770 | "max": 40, 1771 | "assignedValue": 6 1772 | } 1773 | }, 1774 | "modify_max_score": true, 1775 | "max_score_modify_value": { 1776 | "Not Applicable": 5 1777 | }, 1778 | "options": [ 1779 | { 1780 | "name": "Yes" 1781 | }, 1782 | { 1783 | "name": "No" 1784 | } 1785 | ] 1786 | }, 1787 | { 1788 | "qualifier": "Assessment_Design_009", 1789 | "description": "The assessment avoids \"All of the above\" and/or \"None of the above\" answers. (They call attention to themselves and are often correct!)", 1790 | "weightage": 0.09, 1791 | "max_score": 5, 1792 | "min_acceptable_score": 2.5, 1793 | "fixed_score": { 1794 | "Yes": 5, 1795 | "No": 0, 1796 | "Not Applicable": 0 1797 | }, 1798 | "range_score": { 1799 | "10-20": { 1800 | "min": 10, 1801 | "max": 20, 1802 | "assignedValue": 3 1803 | }, 1804 | "20-30": { 1805 | "min": 20, 1806 | "max": 30, 1807 | "assignedValue": 4 1808 | }, 1809 | "30-40": { 1810 | "min": 30, 1811 | "max": 40, 1812 | "assignedValue": 6 1813 | } 1814 | }, 1815 | "modify_max_score": true, 1816 | "max_score_modify_value": { 1817 | "Not Applicable": 5 1818 | }, 1819 | "options": [ 1820 | { 1821 | "name": "Yes" 1822 | }, 1823 | { 1824 | "name": "No" 1825 | } 1826 | ] 1827 | } 1828 | ] 1829 | }, 1830 | { 1831 | "criteria": "Competency and Skills", 1832 | "description": "The section assesses how the course helps learners achieve the stated competency and skill acquisition goals. Please mark yes if the statement is true for your content.", 1833 | "weightage": 0.2, 1834 | "max_score": 15, 1835 | "min_acceptable_score": 0, 1836 | "min_score_weightage_enable": true, 1837 | "min_score_weightage": 0.5, 1838 | "qualifiers": [ 1839 | { 1840 | "qualifier": "Competency-And_Skills_001", 1841 | "description": "All target competencies are clearly stated at the beginning of the course.", 1842 | "weightage": 0.09, 1843 | "max_score": 5, 1844 | "min_acceptable_score": 2.5, 1845 | "fixed_score": { 1846 | "Yes": 5, 1847 | "No": 0, 1848 | "Not Applicable": 0 1849 | }, 1850 | "range_score": { 1851 | "10-20": { 1852 | "min": 10, 1853 | "max": 20, 1854 | "assignedValue": 3 1855 | }, 1856 | "20-30": { 1857 | "min": 20, 1858 | "max": 30, 1859 | "assignedValue": 4 1860 | }, 1861 | "30-40": { 1862 | "min": 30, 1863 | "max": 40, 1864 | "assignedValue": 6 1865 | } 1866 | }, 1867 | "modify_max_score": true, 1868 | "max_score_modify_value": { 1869 | "Not Applicable": 5 1870 | }, 1871 | "options": [ 1872 | { 1873 | "name": "Yes" 1874 | }, 1875 | { 1876 | "name": "No" 1877 | } 1878 | ] 1879 | }, 1880 | { 1881 | "qualifier": "Competency-And_Skills_002", 1882 | "description": "All target competencies are clearly tested at the end of the course to demonstrate progression.", 1883 | "weightage": 0.09, 1884 | "max_score": 5, 1885 | "min_acceptable_score": 2.5, 1886 | "fixed_score": { 1887 | "Yes": 5, 1888 | "No": 0, 1889 | "Not Applicable": 0 1890 | }, 1891 | "range_score": { 1892 | "10-20": { 1893 | "min": 10, 1894 | "max": 20, 1895 | "assignedValue": 3 1896 | }, 1897 | "20-30": { 1898 | "min": 20, 1899 | "max": 30, 1900 | "assignedValue": 4 1901 | }, 1902 | "30-40": { 1903 | "min": 30, 1904 | "max": 40, 1905 | "assignedValue": 6 1906 | } 1907 | }, 1908 | "modify_max_score": true, 1909 | "max_score_modify_value": { 1910 | "Not Applicable": 5 1911 | }, 1912 | "options": [ 1913 | { 1914 | "name": "Yes" 1915 | }, 1916 | { 1917 | "name": "No" 1918 | } 1919 | ] 1920 | }, 1921 | { 1922 | "qualifier": "Competency-And_Skills_003", 1923 | "description": "All target competencies have been covered in the course using real-life scenarios.", 1924 | "weightage": 0.09, 1925 | "max_score": 5, 1926 | "min_acceptable_score": 2.5, 1927 | "fixed_score": { 1928 | "Yes": 5, 1929 | "No": 0, 1930 | "Not Applicable": 0 1931 | }, 1932 | "range_score": { 1933 | "10-20": { 1934 | "min": 10, 1935 | "max": 20, 1936 | "assignedValue": 3 1937 | }, 1938 | "20-30": { 1939 | "min": 20, 1940 | "max": 30, 1941 | "assignedValue": 4 1942 | }, 1943 | "30-40": { 1944 | "min": 30, 1945 | "max": 40, 1946 | "assignedValue": 6 1947 | } 1948 | }, 1949 | "modify_max_score": true, 1950 | "max_score_modify_value": { 1951 | "Not Applicable": 5 1952 | }, 1953 | "options": [ 1954 | { 1955 | "name": "Yes" 1956 | }, 1957 | { 1958 | "name": "No" 1959 | } 1960 | ] 1961 | } 1962 | ] 1963 | }, 1964 | { 1965 | "criteria": "Learner Engagement", 1966 | "description": "This section will evaluate learner engagement for the course. These include specific techniques (such as real-life examples) to deepen learner engagement with the learning material. Please mark yes if the statement is true for your content.", 1967 | "weightage": 0.15, 1968 | "max_score": 30, 1969 | "min_acceptable_score": 0, 1970 | "min_score_weightage_enable": true, 1971 | "min_score_weightage": 0.5, 1972 | "qualifiers": [ 1973 | { 1974 | "qualifier": "Learner_Engagement_001", 1975 | "description": "Resources are byte sized (6-10 minutes long).", 1976 | "weightage": 0.09, 1977 | "max_score": 5, 1978 | "min_acceptable_score": 2.5, 1979 | "fixed_score": { 1980 | "Yes": 5, 1981 | "No": 0, 1982 | "Not Applicable": 0 1983 | }, 1984 | "range_score": { 1985 | "10-20": { 1986 | "min": 10, 1987 | "max": 20, 1988 | "assignedValue": 3 1989 | }, 1990 | "20-30": { 1991 | "min": 20, 1992 | "max": 30, 1993 | "assignedValue": 4 1994 | }, 1995 | "30-40": { 1996 | "min": 30, 1997 | "max": 40, 1998 | "assignedValue": 6 1999 | } 2000 | }, 2001 | "modify_max_score": true, 2002 | "max_score_modify_value": { 2003 | "Not Applicable": 5 2004 | }, 2005 | "options": [ 2006 | { 2007 | "name": "Yes" 2008 | }, 2009 | { 2010 | "name": "No" 2011 | } 2012 | ] 2013 | }, 2014 | { 2015 | "qualifier": "Learner_Engagement_002", 2016 | "description": "The technical quality of all media is good i.e. videos and audios play with no distortion.", 2017 | "weightage": 0.09, 2018 | "max_score": 5, 2019 | "min_acceptable_score": 2.5, 2020 | "fixed_score": { 2021 | "Yes": 5, 2022 | "No": 0, 2023 | "Not Applicable": 0 2024 | }, 2025 | "range_score": { 2026 | "10-20": { 2027 | "min": 10, 2028 | "max": 20, 2029 | "assignedValue": 3 2030 | }, 2031 | "20-30": { 2032 | "min": 20, 2033 | "max": 30, 2034 | "assignedValue": 4 2035 | }, 2036 | "30-40": { 2037 | "min": 30, 2038 | "max": 40, 2039 | "assignedValue": 6 2040 | } 2041 | }, 2042 | "modify_max_score": true, 2043 | "max_score_modify_value": { 2044 | "Not Applicable": 5 2045 | }, 2046 | "options": [ 2047 | { 2048 | "name": "Yes" 2049 | }, 2050 | { 2051 | "name": "No" 2052 | } 2053 | ] 2054 | }, 2055 | { 2056 | "qualifier": "Learner_Engagement_003", 2057 | "description": "The reading content (e.g. PDFs, slides) is designed for on-the-go consumption, and contains visual summaries, infographics and other simila.", 2058 | "weightage": 0.09, 2059 | "max_score": 5, 2060 | "min_acceptable_score": 2.5, 2061 | "fixed_score": { 2062 | "Yes": 5, 2063 | "No": 0, 2064 | "Not Applicable": 0 2065 | }, 2066 | "range_score": { 2067 | "10-20": { 2068 | "min": 10, 2069 | "max": 20, 2070 | "assignedValue": 3 2071 | }, 2072 | "20-30": { 2073 | "min": 20, 2074 | "max": 30, 2075 | "assignedValue": 4 2076 | }, 2077 | "30-40": { 2078 | "min": 30, 2079 | "max": 40, 2080 | "assignedValue": 6 2081 | } 2082 | }, 2083 | "modify_max_score": true, 2084 | "max_score_modify_value": { 2085 | "Not Applicable": 5 2086 | }, 2087 | "options": [ 2088 | { 2089 | "name": "Yes" 2090 | }, 2091 | { 2092 | "name": "No" 2093 | }, 2094 | { 2095 | "name": "Not Applicable" 2096 | } 2097 | ] 2098 | }, 2099 | { 2100 | "qualifier": "Learner_Engagement_004", 2101 | "description": "The voice over accent is one that can be easily understood by the target audience.", 2102 | "weightage": 0.09, 2103 | "max_score": 5, 2104 | "min_acceptable_score": 2.5, 2105 | "fixed_score": { 2106 | "Yes": 5, 2107 | "No": 0, 2108 | "Not Applicable": 0 2109 | }, 2110 | "range_score": { 2111 | "10-20": { 2112 | "min": 10, 2113 | "max": 20, 2114 | "assignedValue": 3 2115 | }, 2116 | "20-30": { 2117 | "min": 20, 2118 | "max": 30, 2119 | "assignedValue": 4 2120 | }, 2121 | "30-40": { 2122 | "min": 30, 2123 | "max": 40, 2124 | "assignedValue": 6 2125 | } 2126 | }, 2127 | "modify_max_score": true, 2128 | "max_score_modify_value": { 2129 | "Not Applicable": 5 2130 | }, 2131 | "options": [ 2132 | { 2133 | "name": "Yes" 2134 | }, 2135 | { 2136 | "name": "No" 2137 | } 2138 | ] 2139 | }, 2140 | { 2141 | "qualifier": "Learner_Engagement_005", 2142 | "description": "Web links used in the course are relevant and functional.", 2143 | "weightage": 0.09, 2144 | "max_score": 5, 2145 | "min_acceptable_score": 2.5, 2146 | "fixed_score": { 2147 | "Yes": 5, 2148 | "No": 0, 2149 | "Not Applicable": 0 2150 | }, 2151 | "range_score": { 2152 | "10-20": { 2153 | "min": 10, 2154 | "max": 20, 2155 | "assignedValue": 3 2156 | }, 2157 | "20-30": { 2158 | "min": 20, 2159 | "max": 30, 2160 | "assignedValue": 4 2161 | }, 2162 | "30-40": { 2163 | "min": 30, 2164 | "max": 40, 2165 | "assignedValue": 6 2166 | } 2167 | }, 2168 | "modify_max_score": true, 2169 | "max_score_modify_value": { 2170 | "Not Applicable": 5 2171 | }, 2172 | "options": [ 2173 | { 2174 | "name": "Yes" 2175 | }, 2176 | { 2177 | "name": "No" 2178 | } 2179 | ] 2180 | }, 2181 | { 2182 | "qualifier": "Learner_Engagement_006", 2183 | "description": "The voice used is not machine simulated and robotic.", 2184 | "weightage": 0.09, 2185 | "max_score": 5, 2186 | "min_acceptable_score": 2.5, 2187 | "fixed_score": { 2188 | "Yes": 5, 2189 | "No": 0, 2190 | "Not Applicable": 0 2191 | }, 2192 | "range_score": { 2193 | "10-20": { 2194 | "min": 10, 2195 | "max": 20, 2196 | "assignedValue": 3 2197 | }, 2198 | "20-30": { 2199 | "min": 20, 2200 | "max": 30, 2201 | "assignedValue": 4 2202 | }, 2203 | "30-40": { 2204 | "min": 30, 2205 | "max": 40, 2206 | "assignedValue": 6 2207 | } 2208 | }, 2209 | "modify_max_score": true, 2210 | "max_score_modify_value": { 2211 | "Not Applicable": 5 2212 | }, 2213 | "options": [ 2214 | { 2215 | "name": "Yes" 2216 | }, 2217 | { 2218 | "name": "No" 2219 | } 2220 | ] 2221 | } 2222 | ] 2223 | }, 2224 | { 2225 | "criteria": "Diversity & Inclusion", 2226 | "description": "This section evaluates the diversity and inclusion component of the course – including how the course uses language and media elements that are inclusive and feature diversity in gender, race and religion, and if the course is accessible to differently-abled learners. Please mark yes if the statement is true for your content.", 2227 | "weightage": 0.15, 2228 | "max_score": 100, 2229 | "min_acceptable_score": 0, 2230 | "min_score_weightage_enable": true, 2231 | "min_score_weightage": 0.6, 2232 | "qualifiers": [ 2233 | { 2234 | "qualifier": "Diversity_Inclusion_001", 2235 | "description": "All graphical elements (image, graphics, shapes, charts etc.) used in the course include descriptive 'alt tags' that screen readers read out in descriptions.", 2236 | "weightage": 0.5, 2237 | "max_score": 5, 2238 | "min_acceptable_score": 2.5, 2239 | "fixed_score": { 2240 | "Yes": 5, 2241 | "No": 0, 2242 | "Not Applicable": 0 2243 | }, 2244 | "range_score": { 2245 | "10-20": { 2246 | "min": 10, 2247 | "max": 20, 2248 | "assignedValue": 3 2249 | }, 2250 | "20-30": { 2251 | "min": 20, 2252 | "max": 30, 2253 | "assignedValue": 4 2254 | }, 2255 | "30-40": { 2256 | "min": 30, 2257 | "max": 40, 2258 | "assignedValue": 6 2259 | } 2260 | }, 2261 | "modify_max_score": true, 2262 | "max_score_modify_value": { 2263 | "Not Applicable": 5 2264 | }, 2265 | "options": [ 2266 | { 2267 | "name": "Yes" 2268 | }, 2269 | { 2270 | "name": "No" 2271 | }, 2272 | { 2273 | "name": "Not Applicable" 2274 | } 2275 | ] 2276 | }, 2277 | { 2278 | "qualifier": "Diversity_Inclusion_002", 2279 | "description": "Videos feature closed captions and transcripts.", 2280 | "weightage": 0.5, 2281 | "max_score": 5, 2282 | "min_acceptable_score": 2.5, 2283 | "fixed_score": { 2284 | "Yes": 5, 2285 | "No": 0, 2286 | "Not Applicable": 0 2287 | }, 2288 | "range_score": { 2289 | "10-20": { 2290 | "min": 10, 2291 | "max": 20, 2292 | "assignedValue": 3 2293 | }, 2294 | "20-30": { 2295 | "min": 20, 2296 | "max": 30, 2297 | "assignedValue": 4 2298 | }, 2299 | "30-40": { 2300 | "min": 30, 2301 | "max": 40, 2302 | "assignedValue": 6 2303 | } 2304 | }, 2305 | "modify_max_score": true, 2306 | "max_score_modify_value": { 2307 | "Not Applicable": 5 2308 | }, 2309 | "options": [ 2310 | { 2311 | "name": "Yes" 2312 | }, 2313 | { 2314 | "name": "No" 2315 | }, 2316 | { 2317 | "name": "Not Applicable" 2318 | } 2319 | ] 2320 | }, 2321 | { 2322 | "qualifier": "Diversity_Inclusion_003", 2323 | "description": "Hyperlinks in the course connect to the correct location, and all of them are descriptively titled (not using phrases as “Click here”), underlined, and in a different colour.", 2324 | "weightage": 0.5, 2325 | "max_score": 5, 2326 | "min_acceptable_score": 2.5, 2327 | "fixed_score": { 2328 | "Yes": 5, 2329 | "No": 0, 2330 | "Not Applicable": 0 2331 | }, 2332 | "range_score": { 2333 | "10-20": { 2334 | "min": 10, 2335 | "max": 20, 2336 | "assignedValue": 3 2337 | }, 2338 | "20-30": { 2339 | "min": 20, 2340 | "max": 30, 2341 | "assignedValue": 4 2342 | }, 2343 | "30-40": { 2344 | "min": 30, 2345 | "max": 40, 2346 | "assignedValue": 6 2347 | } 2348 | }, 2349 | "modify_max_score": true, 2350 | "max_score_modify_value": { 2351 | "Not Applicable": 5 2352 | }, 2353 | "options": [ 2354 | { 2355 | "name": "Yes" 2356 | }, 2357 | { 2358 | "name": "No" 2359 | }, 2360 | { 2361 | "name": "Not Applicable" 2362 | } 2363 | ] 2364 | }, 2365 | { 2366 | "qualifier": "Diversity_Inclusion_004", 2367 | "description": "The course provides alternative activities to replace drag-and-drop ones by using a matching activity with typing the correct number or letter.", 2368 | "weightage": 0.5, 2369 | "max_score": 5, 2370 | "min_acceptable_score": 2.5, 2371 | "fixed_score": { 2372 | "Yes": 5, 2373 | "No": 0, 2374 | "Not Applicable": 0 2375 | }, 2376 | "range_score": { 2377 | "10-20": { 2378 | "min": 10, 2379 | "max": 20, 2380 | "assignedValue": 3 2381 | }, 2382 | "20-30": { 2383 | "min": 20, 2384 | "max": 30, 2385 | "assignedValue": 4 2386 | }, 2387 | "30-40": { 2388 | "min": 30, 2389 | "max": 40, 2390 | "assignedValue": 6 2391 | } 2392 | }, 2393 | "modify_max_score": true, 2394 | "max_score_modify_value": { 2395 | "Not Applicable": 5 2396 | }, 2397 | "options": [ 2398 | { 2399 | "name": "Yes" 2400 | }, 2401 | { 2402 | "name": "No" 2403 | }, 2404 | { 2405 | "name": "Not Applicable" 2406 | } 2407 | ] 2408 | }, 2409 | { 2410 | "qualifier": "Diversity_Inclusion_005", 2411 | "description": "The course uses appropriate font size and type, which is adjustable and conforms to standards of World Wide Web Consortium.", 2412 | "weightage": 0.5, 2413 | "max_score": 5, 2414 | "min_acceptable_score": 2.5, 2415 | "fixed_score": { 2416 | "Yes": 5, 2417 | "No": 0, 2418 | "Not Applicable": 0 2419 | }, 2420 | "range_score": { 2421 | "10-20": { 2422 | "min": 10, 2423 | "max": 20, 2424 | "assignedValue": 3 2425 | }, 2426 | "20-30": { 2427 | "min": 20, 2428 | "max": 30, 2429 | "assignedValue": 4 2430 | }, 2431 | "30-40": { 2432 | "min": 30, 2433 | "max": 40, 2434 | "assignedValue": 6 2435 | } 2436 | }, 2437 | "modify_max_score": true, 2438 | "max_score_modify_value": { 2439 | "Not Applicable": 5 2440 | }, 2441 | "options": [ 2442 | { 2443 | "name": "Yes" 2444 | }, 2445 | { 2446 | "name": "No" 2447 | }, 2448 | { 2449 | "name": "Not Applicable" 2450 | } 2451 | ] 2452 | }, 2453 | { 2454 | "qualifier": "Diversity_Inclusion_006", 2455 | "description": "The course uses both colour and symbols to convey messages or visual notifications.", 2456 | "weightage": 0.5, 2457 | "max_score": 5, 2458 | "min_acceptable_score": 2.5, 2459 | "fixed_score": { 2460 | "Yes": 5, 2461 | "No": 0, 2462 | "Not Applicable": 0 2463 | }, 2464 | "range_score": { 2465 | "10-20": { 2466 | "min": 10, 2467 | "max": 20, 2468 | "assignedValue": 3 2469 | }, 2470 | "20-30": { 2471 | "min": 20, 2472 | "max": 30, 2473 | "assignedValue": 4 2474 | }, 2475 | "30-40": { 2476 | "min": 30, 2477 | "max": 40, 2478 | "assignedValue": 6 2479 | } 2480 | }, 2481 | "modify_max_score": true, 2482 | "max_score_modify_value": { 2483 | "Not Applicable": 5 2484 | }, 2485 | "options": [ 2486 | { 2487 | "name": "Yes" 2488 | }, 2489 | { 2490 | "name": "No" 2491 | }, 2492 | { 2493 | "name": "Not Applicable" 2494 | } 2495 | ] 2496 | }, 2497 | { 2498 | "qualifier": "Diversity_Inclusion_007", 2499 | "description": "The course uses patterns and textures as opposed to only contrasting colours for elements that require emphasis.", 2500 | "weightage": 0.5, 2501 | "max_score": 5, 2502 | "min_acceptable_score": 2.5, 2503 | "fixed_score": { 2504 | "Yes": 5, 2505 | "No": 0, 2506 | "Not Applicable": 0 2507 | }, 2508 | "range_score": { 2509 | "10-20": { 2510 | "min": 10, 2511 | "max": 20, 2512 | "assignedValue": 3 2513 | }, 2514 | "20-30": { 2515 | "min": 20, 2516 | "max": 30, 2517 | "assignedValue": 4 2518 | }, 2519 | "30-40": { 2520 | "min": 30, 2521 | "max": 40, 2522 | "assignedValue": 6 2523 | } 2524 | }, 2525 | "modify_max_score": true, 2526 | "max_score_modify_value": { 2527 | "Not Applicable": 5 2528 | }, 2529 | "options": [ 2530 | { 2531 | "name": "Yes" 2532 | }, 2533 | { 2534 | "name": "No" 2535 | }, 2536 | { 2537 | "name": "Not Applicable" 2538 | } 2539 | ] 2540 | }, 2541 | { 2542 | "qualifier": "Diversity_Inclusion_008", 2543 | "description": "Colour schemes used are colour-blind-friendly (please see the W3C standards) and contrast is used in choosing colour combinations.", 2544 | "weightage": 0.5, 2545 | "max_score": 5, 2546 | "min_acceptable_score": 2.5, 2547 | "fixed_score": { 2548 | "Yes": 5, 2549 | "No": 0, 2550 | "Not Applicable": 0 2551 | }, 2552 | "range_score": { 2553 | "10-20": { 2554 | "min": 10, 2555 | "max": 20, 2556 | "assignedValue": 3 2557 | }, 2558 | "20-30": { 2559 | "min": 20, 2560 | "max": 30, 2561 | "assignedValue": 4 2562 | }, 2563 | "30-40": { 2564 | "min": 30, 2565 | "max": 40, 2566 | "assignedValue": 6 2567 | } 2568 | }, 2569 | "modify_max_score": true, 2570 | "max_score_modify_value": { 2571 | "Not Applicable": 5 2572 | }, 2573 | "options": [ 2574 | { 2575 | "name": "Yes" 2576 | }, 2577 | { 2578 | "name": "No" 2579 | }, 2580 | { 2581 | "name": "Not Applicable" 2582 | } 2583 | ] 2584 | }, 2585 | { 2586 | "qualifier": "Diversity_Inclusion_009", 2587 | "description": "PDFs are saved as searchable text not images.", 2588 | "weightage": 0.5, 2589 | "max_score": 5, 2590 | "min_acceptable_score": 2.5, 2591 | "fixed_score": { 2592 | "Yes": 5, 2593 | "No": 0, 2594 | "Not Applicable": 0 2595 | }, 2596 | "range_score": { 2597 | "10-20": { 2598 | "min": 10, 2599 | "max": 20, 2600 | "assignedValue": 3 2601 | }, 2602 | "20-30": { 2603 | "min": 20, 2604 | "max": 30, 2605 | "assignedValue": 4 2606 | }, 2607 | "30-40": { 2608 | "min": 30, 2609 | "max": 40, 2610 | "assignedValue": 6 2611 | } 2612 | }, 2613 | "modify_max_score": true, 2614 | "max_score_modify_value": { 2615 | "Not Applicable": 5 2616 | }, 2617 | "options": [ 2618 | { 2619 | "name": "Yes" 2620 | }, 2621 | { 2622 | "name": "No" 2623 | }, 2624 | { 2625 | "name": "Not Applicable" 2626 | } 2627 | ] 2628 | }, 2629 | { 2630 | "qualifier": "Diversity_Inclusion_010", 2631 | "description": "Audio narration is available for static content.", 2632 | "weightage": 0.5, 2633 | "max_score": 5, 2634 | "min_acceptable_score": 2.5, 2635 | "fixed_score": { 2636 | "Yes": 5, 2637 | "No": 0, 2638 | "Not Applicable": 0 2639 | }, 2640 | "range_score": { 2641 | "10-20": { 2642 | "min": 10, 2643 | "max": 20, 2644 | "assignedValue": 3 2645 | }, 2646 | "20-30": { 2647 | "min": 20, 2648 | "max": 30, 2649 | "assignedValue": 4 2650 | }, 2651 | "30-40": { 2652 | "min": 30, 2653 | "max": 40, 2654 | "assignedValue": 6 2655 | } 2656 | }, 2657 | "modify_max_score": true, 2658 | "max_score_modify_value": { 2659 | "Not Applicable": 5 2660 | }, 2661 | "options": [ 2662 | { 2663 | "name": "Yes" 2664 | }, 2665 | { 2666 | "name": "No" 2667 | }, 2668 | { 2669 | "name": "Not Applicable" 2670 | } 2671 | ] 2672 | }, 2673 | { 2674 | "qualifier": "Diversity_Inclusion_011", 2675 | "description": "The course features diversity in gender.", 2676 | "weightage": 0.5, 2677 | "max_score": 5, 2678 | "min_acceptable_score": 2.5, 2679 | "fixed_score": { 2680 | "Yes": 5, 2681 | "No": 0, 2682 | "Not Applicable": 0 2683 | }, 2684 | "range_score": { 2685 | "10-20": { 2686 | "min": 10, 2687 | "max": 20, 2688 | "assignedValue": 3 2689 | }, 2690 | "20-30": { 2691 | "min": 20, 2692 | "max": 30, 2693 | "assignedValue": 4 2694 | }, 2695 | "30-40": { 2696 | "min": 30, 2697 | "max": 40, 2698 | "assignedValue": 6 2699 | } 2700 | }, 2701 | "modify_max_score": true, 2702 | "max_score_modify_value": { 2703 | "Not Applicable": 5 2704 | }, 2705 | "options": [ 2706 | { 2707 | "name": "Yes" 2708 | }, 2709 | { 2710 | "name": "No" 2711 | }, 2712 | { 2713 | "name": "Not Applicable" 2714 | } 2715 | ] 2716 | }, 2717 | { 2718 | "qualifier": "Diversity_Inclusion_012", 2719 | "description": "The course does not showcase disparity or discrimination among genders (male, female, third gender). The course is gender intentional/ gender transformative.", 2720 | "weightage": 0.5, 2721 | "max_score": 5, 2722 | "min_acceptable_score": 2.5, 2723 | "fixed_score": { 2724 | "Yes": 5, 2725 | "No": 0, 2726 | "Not Applicable": 0 2727 | }, 2728 | "range_score": { 2729 | "10-20": { 2730 | "min": 10, 2731 | "max": 20, 2732 | "assignedValue": 3 2733 | }, 2734 | "20-30": { 2735 | "min": 20, 2736 | "max": 30, 2737 | "assignedValue": 4 2738 | }, 2739 | "30-40": { 2740 | "min": 30, 2741 | "max": 40, 2742 | "assignedValue": 6 2743 | } 2744 | }, 2745 | "modify_max_score": true, 2746 | "max_score_modify_value": { 2747 | "Not Applicable": 5 2748 | }, 2749 | "options": [ 2750 | { 2751 | "name": "Yes" 2752 | }, 2753 | { 2754 | "name": "No" 2755 | }, 2756 | { 2757 | "name": "Not Applicable" 2758 | } 2759 | ] 2760 | }, 2761 | { 2762 | "qualifier": "Diversity_Inclusion_013", 2763 | "description": "The course uses language that is gender inclusive e.g. use of 'they' in favour of 'he' (with appropriate change to sentence structures).", 2764 | "weightage": 0.5, 2765 | "max_score": 5, 2766 | "min_acceptable_score": 2.5, 2767 | "fixed_score": { 2768 | "Yes": 5, 2769 | "No": 0, 2770 | "Not Applicable": 0 2771 | }, 2772 | "range_score": { 2773 | "10-20": { 2774 | "min": 10, 2775 | "max": 20, 2776 | "assignedValue": 3 2777 | }, 2778 | "20-30": { 2779 | "min": 20, 2780 | "max": 30, 2781 | "assignedValue": 4 2782 | }, 2783 | "30-40": { 2784 | "min": 30, 2785 | "max": 40, 2786 | "assignedValue": 6 2787 | } 2788 | }, 2789 | "modify_max_score": true, 2790 | "max_score_modify_value": { 2791 | "Not Applicable": 5 2792 | }, 2793 | "options": [ 2794 | { 2795 | "name": "Yes" 2796 | }, 2797 | { 2798 | "name": "No" 2799 | }, 2800 | { 2801 | "name": "Not Applicable" 2802 | } 2803 | ] 2804 | }, 2805 | { 2806 | "qualifier": "Diversity_Inclusion_014", 2807 | "description": "The course provides a good overview of gender disparities/gaps in the health and education sector.", 2808 | "weightage": 0.5, 2809 | "max_score": 5, 2810 | "min_acceptable_score": 2.5, 2811 | "fixed_score": { 2812 | "Yes": 5, 2813 | "No": 0, 2814 | "Not Applicable": 0 2815 | }, 2816 | "range_score": { 2817 | "10-20": { 2818 | "min": 10, 2819 | "max": 20, 2820 | "assignedValue": 3 2821 | }, 2822 | "20-30": { 2823 | "min": 20, 2824 | "max": 30, 2825 | "assignedValue": 4 2826 | }, 2827 | "30-40": { 2828 | "min": 30, 2829 | "max": 40, 2830 | "assignedValue": 6 2831 | } 2832 | }, 2833 | "modify_max_score": true, 2834 | "max_score_modify_value": { 2835 | "Not Applicable": 5 2836 | }, 2837 | "options": [ 2838 | { 2839 | "name": "Yes" 2840 | }, 2841 | { 2842 | "name": "No" 2843 | }, 2844 | { 2845 | "name": "Not Applicable" 2846 | } 2847 | ] 2848 | }, 2849 | { 2850 | "qualifier": "Diversity_Inclusion_015", 2851 | "description": "The course provides adequate information related to gender budgeting/planning exercises.", 2852 | "weightage": 0.5, 2853 | "max_score": 5, 2854 | "min_acceptable_score": 2.5, 2855 | "fixed_score": { 2856 | "Yes": 5, 2857 | "No": 0, 2858 | "Not Applicable": 0 2859 | }, 2860 | "range_score": { 2861 | "10-20": { 2862 | "min": 10, 2863 | "max": 20, 2864 | "assignedValue": 3 2865 | }, 2866 | "20-30": { 2867 | "min": 20, 2868 | "max": 30, 2869 | "assignedValue": 4 2870 | }, 2871 | "30-40": { 2872 | "min": 30, 2873 | "max": 40, 2874 | "assignedValue": 6 2875 | } 2876 | }, 2877 | "modify_max_score": true, 2878 | "max_score_modify_value": { 2879 | "Not Applicable": 5 2880 | }, 2881 | "options": [ 2882 | { 2883 | "name": "Yes" 2884 | }, 2885 | { 2886 | "name": "No" 2887 | }, 2888 | { 2889 | "name": "Not Applicable" 2890 | } 2891 | ] 2892 | }, 2893 | { 2894 | "qualifier": "Diversity_Inclusion_016", 2895 | "description": "The course effectively transmits information regarding existing gaps in female labor force participation and relevant policy options.", 2896 | "weightage": 0.5, 2897 | "max_score": 5, 2898 | "min_acceptable_score": 2.5, 2899 | "fixed_score": { 2900 | "Yes": 5, 2901 | "No": 0, 2902 | "Not Applicable": 0 2903 | }, 2904 | "range_score": { 2905 | "10-20": { 2906 | "min": 10, 2907 | "max": 20, 2908 | "assignedValue": 3 2909 | }, 2910 | "20-30": { 2911 | "min": 20, 2912 | "max": 30, 2913 | "assignedValue": 4 2914 | }, 2915 | "30-40": { 2916 | "min": 30, 2917 | "max": 40, 2918 | "assignedValue": 6 2919 | } 2920 | }, 2921 | "modify_max_score": true, 2922 | "max_score_modify_value": { 2923 | "Not Applicable": 5 2924 | }, 2925 | "options": [ 2926 | { 2927 | "name": "Yes" 2928 | }, 2929 | { 2930 | "name": "No" 2931 | }, 2932 | { 2933 | "name": "Not Applicable" 2934 | } 2935 | ] 2936 | }, 2937 | { 2938 | "qualifier": "Diversity_Inclusion_017", 2939 | "description": "The course provides adequate information to carry out administrative decisions to enhance women’s safety and violence prevention.", 2940 | "weightage": 0.5, 2941 | "max_score": 5, 2942 | "min_acceptable_score": 2.5, 2943 | "fixed_score": { 2944 | "Yes": 5, 2945 | "No": 0, 2946 | "Not Applicable": 0 2947 | }, 2948 | "range_score": { 2949 | "10-20": { 2950 | "min": 10, 2951 | "max": 20, 2952 | "assignedValue": 3 2953 | }, 2954 | "20-30": { 2955 | "min": 20, 2956 | "max": 30, 2957 | "assignedValue": 4 2958 | }, 2959 | "30-40": { 2960 | "min": 30, 2961 | "max": 40, 2962 | "assignedValue": 6 2963 | } 2964 | }, 2965 | "modify_max_score": true, 2966 | "max_score_modify_value": { 2967 | "Not Applicable": 5 2968 | }, 2969 | "options": [ 2970 | { 2971 | "name": "Yes" 2972 | }, 2973 | { 2974 | "name": "No" 2975 | }, 2976 | { 2977 | "name": "Not Applicable" 2978 | } 2979 | ] 2980 | }, 2981 | { 2982 | "qualifier": "Diversity_Inclusion_018", 2983 | "description": "The course provides comprehensive information issues of the girl child and women in India.", 2984 | "weightage": 0.5, 2985 | "max_score": 5, 2986 | "min_acceptable_score": 2.5, 2987 | "fixed_score": { 2988 | "Yes": 5, 2989 | "No": 0, 2990 | "Not Applicable": 0 2991 | }, 2992 | "range_score": { 2993 | "10-20": { 2994 | "min": 10, 2995 | "max": 20, 2996 | "assignedValue": 3 2997 | }, 2998 | "20-30": { 2999 | "min": 20, 3000 | "max": 30, 3001 | "assignedValue": 4 3002 | }, 3003 | "30-40": { 3004 | "min": 30, 3005 | "max": 40, 3006 | "assignedValue": 6 3007 | } 3008 | }, 3009 | "modify_max_score": true, 3010 | "max_score_modify_value": { 3011 | "Not Applicable": 5 3012 | }, 3013 | "options": [ 3014 | { 3015 | "name": "Yes" 3016 | }, 3017 | { 3018 | "name": "No" 3019 | }, 3020 | { 3021 | "name": "Not Applicable" 3022 | } 3023 | ] 3024 | }, 3025 | { 3026 | "qualifier": "Diversity_Inclusion_019", 3027 | "description": "The course is multilingual and available in multiple official languages.", 3028 | "weightage": 0.5, 3029 | "max_score": 5, 3030 | "min_acceptable_score": 2.5, 3031 | "fixed_score": { 3032 | "Yes": 5, 3033 | "No": 0, 3034 | "Not Applicable": 0 3035 | }, 3036 | "range_score": { 3037 | "10-20": { 3038 | "min": 10, 3039 | "max": 20, 3040 | "assignedValue": 3 3041 | }, 3042 | "20-30": { 3043 | "min": 20, 3044 | "max": 30, 3045 | "assignedValue": 4 3046 | }, 3047 | "30-40": { 3048 | "min": 30, 3049 | "max": 40, 3050 | "assignedValue": 6 3051 | } 3052 | }, 3053 | "modify_max_score": true, 3054 | "max_score_modify_value": { 3055 | "Not Applicable": 5 3056 | }, 3057 | "options": [ 3058 | { 3059 | "name": "Yes" 3060 | }, 3061 | { 3062 | "name": "No" 3063 | }, 3064 | { 3065 | "name": "Not Applicable" 3066 | } 3067 | ] 3068 | }, 3069 | { 3070 | "qualifier": "Diversity_Inclusion_020", 3071 | "description": "Human characters used in the course belong to the context that is being portrayed e.g. use of Indian characters in Indian contexts and situations, and use of international characters where the situation demands.", 3072 | "weightage": 0.5, 3073 | "max_score": 5, 3074 | "min_acceptable_score": 2.5, 3075 | "fixed_score": { 3076 | "Yes": 5, 3077 | "No": 0, 3078 | "Not Applicable": 0 3079 | }, 3080 | "range_score": { 3081 | "10-20": { 3082 | "min": 10, 3083 | "max": 20, 3084 | "assignedValue": 3 3085 | }, 3086 | "20-30": { 3087 | "min": 20, 3088 | "max": 30, 3089 | "assignedValue": 4 3090 | }, 3091 | "30-40": { 3092 | "min": 30, 3093 | "max": 40, 3094 | "assignedValue": 6 3095 | } 3096 | }, 3097 | "modify_max_score": true, 3098 | "max_score_modify_value": { 3099 | "Not Applicable": 5 3100 | }, 3101 | "options": [ 3102 | { 3103 | "name": "Yes" 3104 | }, 3105 | { 3106 | "name": "No" 3107 | }, 3108 | { 3109 | "name": "Not Applicable" 3110 | } 3111 | ] 3112 | } 3113 | ] 3114 | }, 3115 | { 3116 | "criteria": "Learner Support", 3117 | "description": "This section evaluates the resources included with the course, to see if they extend learning and enhance the asynchronous learning experience. Please mark yes if the statement is true for your content.", 3118 | "weightage": 0.05, 3119 | "max_score": 25, 3120 | "min_acceptable_score": 0, 3121 | "min_score_weightage_enable": true, 3122 | "min_score_weightage": 0.5, 3123 | "qualifiers": [ 3124 | { 3125 | "qualifier": "Learner_Support_001", 3126 | "description": "Learners are able to download courses and complete the course offline (e.g. the course avoids using YouTube/ external links to videos).", 3127 | "weightage": 0.5, 3128 | "max_score": 5, 3129 | "min_acceptable_score": 2.5, 3130 | "fixed_score": { 3131 | "Yes": 5, 3132 | "No": 0, 3133 | "Not Applicable": 0 3134 | }, 3135 | "range_score": { 3136 | "10-20": { 3137 | "min": 10, 3138 | "max": 20, 3139 | "assignedValue": 3 3140 | }, 3141 | "20-30": { 3142 | "min": 20, 3143 | "max": 30, 3144 | "assignedValue": 4 3145 | }, 3146 | "30-40": { 3147 | "min": 30, 3148 | "max": 40, 3149 | "assignedValue": 6 3150 | } 3151 | }, 3152 | "modify_max_score": true, 3153 | "max_score_modify_value": { 3154 | "Not Applicable": 5 3155 | }, 3156 | "options": [ 3157 | { 3158 | "name": "Yes" 3159 | }, 3160 | { 3161 | "name": "No" 3162 | }, 3163 | { 3164 | "name": "Not Applicable" 3165 | } 3166 | ] 3167 | }, 3168 | { 3169 | "qualifier": "Learner_Support_002", 3170 | "description": "Learners have access to additional resources that enrich the course content.", 3171 | "weightage": 0.5, 3172 | "max_score": 5, 3173 | "min_acceptable_score": 2.5, 3174 | "fixed_score": { 3175 | "Yes": 5, 3176 | "No": 0, 3177 | "Not Applicable": 0 3178 | }, 3179 | "range_score": { 3180 | "10-20": { 3181 | "min": 10, 3182 | "max": 20, 3183 | "assignedValue": 3 3184 | }, 3185 | "20-30": { 3186 | "min": 20, 3187 | "max": 30, 3188 | "assignedValue": 4 3189 | }, 3190 | "30-40": { 3191 | "min": 30, 3192 | "max": 40, 3193 | "assignedValue": 6 3194 | } 3195 | }, 3196 | "modify_max_score": true, 3197 | "max_score_modify_value": { 3198 | "Not Applicable": 5 3199 | }, 3200 | "options": [ 3201 | { 3202 | "name": "Yes" 3203 | }, 3204 | { 3205 | "name": "No" 3206 | } 3207 | ] 3208 | }, 3209 | { 3210 | "qualifier": "Learner_Support_003", 3211 | "description": "Learners have access to troubleshooting resource(s) and contact details.", 3212 | "weightage": 0.5, 3213 | "max_score": 5, 3214 | "min_acceptable_score": 2.5, 3215 | "fixed_score": { 3216 | "Yes": 5, 3217 | "No": 0, 3218 | "Not Applicable": 0 3219 | }, 3220 | "range_score": { 3221 | "10-20": { 3222 | "min": 10, 3223 | "max": 20, 3224 | "assignedValue": 3 3225 | }, 3226 | "20-30": { 3227 | "min": 20, 3228 | "max": 30, 3229 | "assignedValue": 4 3230 | }, 3231 | "30-40": { 3232 | "min": 30, 3233 | "max": 40, 3234 | "assignedValue": 6 3235 | } 3236 | }, 3237 | "modify_max_score": true, 3238 | "max_score_modify_value": { 3239 | "Not Applicable": 5 3240 | }, 3241 | "options": [ 3242 | { 3243 | "name": "Yes" 3244 | }, 3245 | { 3246 | "name": "No" 3247 | } 3248 | ] 3249 | }, 3250 | { 3251 | "qualifier": "Learner_Support_004", 3252 | "description": "The course features navigational help.", 3253 | "weightage": 0.5, 3254 | "max_score": 5, 3255 | "min_acceptable_score": 2.5, 3256 | "fixed_score": { 3257 | "Yes": 5, 3258 | "No": 0, 3259 | "Not Applicable": 0 3260 | }, 3261 | "range_score": { 3262 | "10-20": { 3263 | "min": 10, 3264 | "max": 20, 3265 | "assignedValue": 3 3266 | }, 3267 | "20-30": { 3268 | "min": 20, 3269 | "max": 30, 3270 | "assignedValue": 4 3271 | }, 3272 | "30-40": { 3273 | "min": 30, 3274 | "max": 40, 3275 | "assignedValue": 6 3276 | } 3277 | }, 3278 | "modify_max_score": true, 3279 | "max_score_modify_value": { 3280 | "Not Applicable": 5 3281 | }, 3282 | "options": [ 3283 | { 3284 | "name": "Yes" 3285 | }, 3286 | { 3287 | "name": "No" 3288 | } 3289 | ] 3290 | }, 3291 | { 3292 | "qualifier": "Learner_Support_005", 3293 | "description": "The course contains a list of abbreviations used and a glossary of terms as part of each module.", 3294 | "weightage": 0.5, 3295 | "max_score": 5, 3296 | "min_acceptable_score": 2.5, 3297 | "fixed_score": { 3298 | "Yes": 5, 3299 | "No": 0, 3300 | "Not Applicable": 0 3301 | }, 3302 | "range_score": { 3303 | "10-20": { 3304 | "min": 10, 3305 | "max": 20, 3306 | "assignedValue": 3 3307 | }, 3308 | "20-30": { 3309 | "min": 20, 3310 | "max": 30, 3311 | "assignedValue": 4 3312 | }, 3313 | "30-40": { 3314 | "min": 30, 3315 | "max": 40, 3316 | "assignedValue": 6 3317 | } 3318 | }, 3319 | "modify_max_score": true, 3320 | "max_score_modify_value": { 3321 | "Not Applicable": 5 3322 | }, 3323 | "options": [ 3324 | { 3325 | "name": "Yes" 3326 | }, 3327 | { 3328 | "name": "No" 3329 | } 3330 | ] 3331 | } 3332 | ] 3333 | } 3334 | ], 3335 | "score_grades": [ 3336 | { 3337 | "0-60": "iGOT Toddler" 3338 | }, 3339 | { 3340 | "60-70": "iGOT Friendly" 3341 | }, 3342 | { 3343 | "70-101": "iGOT Qualified" 3344 | } 3345 | ], 3346 | "status_on_min_criteria": { 3347 | "pass": "", 3348 | "fail": "iGOT Toddler" 3349 | } 3350 | } 3351 | ] 3352 | } --------------------------------------------------------------------------------