Skip to content

Commit

Permalink
4.8.17 merge (sunbird-cb#131)
Browse files Browse the repository at this point in the history
* 4.8.17 dev v5 (sunbird-cb#44)

* KB-6678 - if assessment is submitted without selecting any option then same questions are repeating in each attempt (in question weightage)

1. Getting NaN if both correct and incorrect are 0 , So added a check.

* added api for to show V6 competencies (sunbird-cb#40)

* added api for to show V6 competencies

* worked on code review comments

* worked on code review comments

* Update application.properties removed spaces between the values

---------

Co-authored-by: anilkumar <anilkumar.kammlapalli@tarento.com>

---------

Co-authored-by: SaipradeepR <53404427+SaipradeepR@users.noreply.github.com>
Co-authored-by: tarentomaheshvakkund <139739142+tarentomaheshvakkund@users.noreply.github.com>
Co-authored-by: anilkumarkammalapalli <121931293+anilkumarkammalapalli@users.noreply.github.com>
Co-authored-by: anilkumar <anilkumar.kammlapalli@tarento.com>

* public link generation and calling email sending api

* public link generation and calling email sending api (sunbird-cb#112)

* convertToPng code added

* png and email encryption method added (sunbird-cb#114)

* public link generation and calling email sending api

* convertToPng code added

* pull from upstream

* Cbrelease 4.8.17 (sunbird-cb#115)

* public link generation and calling email sending api

* convertToPng code added

* pull from upstream

* svg issue resolved

* svg issue resolved (sunbird-cb#118)

* public link generation and calling email sending api

* convertToPng code added

* pull from upstream

* svg issue resolved

* 4.8.17 report fix (sunbird-cb#107)

* Adding impl for report download (sunbird-cb#101)

* 4.8.17 mdo report fix (sunbird-cb#105)

* Adding impl for report download

* Adding impl for report download

* Adding impl for report download (sunbird-cb#106)

* upload issue resolved

* Cbrelease 4.8.17 (sunbird-cb#119)

* public link generation and calling email sending api

* convertToPng code added

* pull from upstream

* svg issue resolved

* upload issue resolved

* certificateId hardCode removed

* CertificateId hard code removed (sunbird-cb#120)

* public link generation and calling email sending api

* convertToPng code added

* pull from upstream

* svg issue resolved

* upload issue resolved

* certificateId hardCode removed

* producer code added

* producer code added

* MAde the impl changes for pdf generator (sunbird-cb#122)

* Remove unused impl and adding null check (sunbird-cb#123)

* Remove unused impl and update the check (sunbird-cb#124)

* Remove unused impl (sunbird-cb#125)

* Update the property (sunbird-cb#126)

* Adding the impl changes for pdf issue (sunbird-cb#127)

* Added the check for the kafka event process

* Code format

---------

Co-authored-by: Karthikeyan Rajendran <70887864+karthik-tarento@users.noreply.github.com>
Co-authored-by: SaipradeepR <53404427+SaipradeepR@users.noreply.github.com>
Co-authored-by: tarentomaheshvakkund <139739142+tarentomaheshvakkund@users.noreply.github.com>
Co-authored-by: anilkumarkammalapalli <121931293+anilkumarkammalapalli@users.noreply.github.com>
Co-authored-by: anilkumar <anilkumar.kammlapalli@tarento.com>
Co-authored-by: arpithasureshappa <arpitha.sureshappa@tarento.com>
Co-authored-by: Sharath Prasad <sharaths.kashyap@gmail.com>
  • Loading branch information
8 people authored Oct 1, 2024
1 parent 60bb636 commit 8f6f82a
Show file tree
Hide file tree
Showing 13 changed files with 1,245 additions and 333 deletions.
529 changes: 284 additions & 245 deletions pom.xml

Large diffs are not rendered by default.

105 changes: 67 additions & 38 deletions src/main/java/org/sunbird/common/util/CbExtServerProperties.java
Original file line number Diff line number Diff line change
Expand Up @@ -666,6 +666,34 @@ public void setRedisWheeboxKey(String redisWheeboxKey) {
@Value("${cert-registry-certificate-download-url}")
private String certRegistryCertificateDownloadUrl;

@Value("${public.assessment.encryption.key}")
private String publicAssessmentEncryptionKey;

@Value("${spring.kafka.public.assessment.notification.topic.name}")
private String springKafkaPublicAssessmentNotificationTopicName;

@Value("${public.user.assessment.table.name}")
private String publicUserAssessmentTableName;

public String getPublicUserAssessmentTableName() {
return publicUserAssessmentTableName;
}

public String getSpringKafkaPublicAssessmentNotificationTopicName() {
return springKafkaPublicAssessmentNotificationTopicName;
}

public String getPublicAssessmentEncryptionKey() {
return publicAssessmentEncryptionKey;
}

public String getPublicAssessmentCloudCertificateFolderName() {
return publicAssessmentCloudCertificateFolderName;
}

@Value("${public.assessment.cloud.certificate.folder.name}")
private String publicAssessmentCloudCertificateFolderName;

@Value("${digilocker-issuer-id}")
private String digiLockerIssuerId;

Expand Down Expand Up @@ -767,43 +795,7 @@ public void setRedisWheeboxKey(String redisWheeboxKey) {

@Value("${sb.composite.v4.search}")
private String sbCompositeV4Search;

@Value("${sb.csv.delimiter}")
private char csvDelimiter;

@Value(("${sb.tags.delimiter}"))
private String tagsDelimiter;

@Value("${assessment.questionset.hierarchy}")
private String questionSetHierarchy;

@Value("${assessment.questionset.create}")
private String questionSetCreate;

@Value("${assessment.questionset.read}")
private String questionSetRead;

@Value("${assessment.questionset.hierarchy.update}")
private String questionSetHierarchyUpdate;

@Value("${assessment.questionset.hierarchy.index}")
private String questionSetHierarchyIndex;

@Value("${cios.cloud.container.name}")
private String ciosCloudContainerName;

@Value("${cios.cloud.folder.name}")
private String ciosCloudFolderName;

@Value("${cios.cloud.icon.folder.name}")
private String ciosCloudIconFolderName;

@Value("${questionSet.publish}")
private String questionSetPublish;

public String getCiosCloudIconFolderName() {
return ciosCloudIconFolderName;
}

@Value("${kcm.framework.name}")
private String kcmFrameworkName;

Expand Down Expand Up @@ -891,6 +883,43 @@ public String getCiosCloudIconFolderName() {
@Value("${kafka.topics.cqf.assessment.postpublish}")
private String cqfAssessmentPostPublishTopic;

@Value("${sb.csv.delimiter}")
private char csvDelimiter;

@Value(("${sb.tags.delimiter}"))
private String tagsDelimiter;

@Value("${assessment.questionset.hierarchy}")
private String questionSetHierarchy;

@Value("${assessment.questionset.create}")
private String questionSetCreate;

@Value("${assessment.questionset.read}")
private String questionSetRead;

@Value("${assessment.questionset.hierarchy.update}")
private String questionSetHierarchyUpdate;

@Value("${assessment.questionset.hierarchy.index}")
private String questionSetHierarchyIndex;

@Value("${cios.cloud.container.name}")
private String ciosCloudContainerName;

@Value("${cios.cloud.folder.name}")
private String ciosCloudFolderName;

@Value("${cios.cloud.icon.folder.name}")
private String ciosCloudIconFolderName;

@Value("${questionSet.publish}")
private String questionSetPublish;

public String getCiosCloudIconFolderName() {
return ciosCloudIconFolderName;
}

public boolean qListFromCacheEnabled() {
return qListFromCacheEnabled;
}
Expand Down Expand Up @@ -2854,7 +2883,6 @@ public void setSbCompositeV4Search(String sbCompositeV4Search) {
this.sbCompositeV4Search = sbCompositeV4Search;
}


public char getCsvDelimiter() {
return csvDelimiter;
}
Expand Down Expand Up @@ -2913,6 +2941,7 @@ public void setQuestionSetHierarchyIndex(String questionSetHierarchyIndex) {
public String getQuestionSetPublish() { return questionSetPublish; }

public void setQuestionSetPublish(String questionSetPublish) { this.questionSetPublish = questionSetPublish; }

public String getKcmFrameworkName() {
return kcmFrameworkName;
}
Expand Down
11 changes: 9 additions & 2 deletions src/main/java/org/sunbird/common/util/Constants.java
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,9 @@ public class Constants {
public static final String CONTENT_ID = "content_id";
public static final String BATCH_ID = "batchId";
public static final String COURSE_ID = "courseId";
public static final String PUBLIC_CONTEXT_ID = "contextid";
public static final String PUBLIC_USER_ID = "userid";
public static final String PUBLIC_ASSESSMENT_ID = "assessmentid";
public static final String ENROLMENT_TYPE = "enrollmenttype";
public static final String IDENTIFIER = "identifier";
public static final String START_DATE = "startDate";
Expand Down Expand Up @@ -991,8 +994,8 @@ public class Constants {
public static final String ISSUED_USER_CERTIFICATE = "issuedCertificates";
public static final String DOC_TYPE = "docType";
public static final String LEARNER_LEADER_BOARD = "learnerLeaderBoard";
public static final String PRIMARY_CATEGORY_TAG = "#primaryCategory";
public static final String PASSWORD = "password";
public static final String PRIMARY_CATEGORY_TAG = "#primaryCategory";
public static final String PASSWORD = "password";
public static final String STRING_FORMAT_UNZIP="%s/%s/%s/%s";
public static final String UNZIP_PATH="/unzippath";
public static final String OUTPUT_PATH="output";
Expand Down Expand Up @@ -1172,6 +1175,10 @@ public class Constants {
public static final String COMMON_BATCH_START_YR = "commonBatchStartYear";
public static final String COMMON_BATCH_END_YR = "commonBatchEndYear";
public static final String COMMON_BATCH_EXCLUSION_YR = "commonBatchExclusionYearList";
public static final String CERT_PUBLICURL = "cert_publicurl";
public static final String CHILD_ID = "childId";
public static final String INPUT_PATH = "input/";
public static final String COURSE_ID_LOWER = "courseid";
public static final String OVERALL_SECTION_PERCENTAGE_SCORE = "overallSectionPercentageScore";
public static final String ACHIEVED_PERCENTAGE_SCORE = "achievedPercentageScore";
public static final String SECTION_LEVEL_PERCENTAGE = "sectionLevelPercentage";
Expand Down
227 changes: 227 additions & 0 deletions src/main/java/org/sunbird/consumer/KafkaConsumer.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,227 @@
package org.sunbird.consumer;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.batik.transcoder.TranscoderInput;
import org.apache.batik.transcoder.TranscoderOutput;
import org.apache.batik.transcoder.image.PNGTranscoder;
import org.apache.commons.lang3.StringUtils;
import org.apache.kafka.clients.consumer.ConsumerRecord;
import org.jcodings.exception.TranscoderException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.*;
import org.springframework.kafka.annotation.KafkaListener;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.web.client.RestTemplate;
import org.sunbird.cassandra.utils.CassandraOperation;
import org.sunbird.common.model.SBApiResponse;
import org.sunbird.common.util.CbExtServerProperties;
import org.sunbird.common.util.Constants;
import org.sunbird.consumer.security.EncryptionService;
import org.sunbird.storage.service.StorageService;

import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;

@Component
public class KafkaConsumer {
private Logger logger = LoggerFactory.getLogger(getClass().getName());

@Autowired
private CassandraOperation cassandraOperation;

@Autowired
private ObjectMapper mapper;

@Autowired
private RestTemplate restTemplate;

@Autowired
private StorageService storageService;

@Autowired
CbExtServerProperties serverProperties;

@Autowired
EncryptionService encryptionService;

@Autowired
KafkaProducer kafkaProducer;

@KafkaListener(topics = "${spring.kafka.public.assessment.topic.name}", groupId = "${spring.kafka.public.assessment.consumer.group.id}")
public void publicAssessmentCertificateEmailNotification(ConsumerRecord<String, String> data) throws IOException {
try {
logger.info("KafkaConsumer::publicAssessmentCertificateEmailNotification:topic name: {} and recievedData: {}", data.topic(), data.value());
if (StringUtils.isNoneBlank(data.value())) {
CompletableFuture.runAsync(() -> {
initiatePublicAssessmentCertificateEmailNotification(data.value());
});
} else {
logger.error("Error in publicAssessmentCertificateEmailNotification: Invalid Kafka Msg");
}
} catch (Exception e) {
logger.error("Error while processing the kafka event : " + data);
}
}

private void initiatePublicAssessmentCertificateEmailNotification(String data) {
try {
Map<String, Object> userCourseEnrollMap = mapper.readValue(data, HashMap.class);
String email = userCourseEnrollMap.get(Constants.PUBLIC_USER_ID).toString();
String encryptedEmail = encryptionService.encryptData(email);
Map<String, Object> propertyMap = new HashMap<>();
propertyMap.put(Constants.PUBLIC_USER_ID, encryptedEmail);
propertyMap.put(Constants.PUBLIC_CONTEXT_ID, userCourseEnrollMap.get(Constants.COURSE_ID_LOWER));
propertyMap.put(Constants.PUBLIC_ASSESSMENT_ID, userCourseEnrollMap.get(Constants.PUBLIC_ASSESSMENT_ID));
List<Map<String, Object>> listOfMasterData = cassandraOperation.getRecordsByPropertiesWithoutFiltering(Constants.KEYSPACE_SUNBIRD, serverProperties.getPublicUserAssessmentTableName(), propertyMap, null, 1);
if (!CollectionUtils.isEmpty(listOfMasterData)) {
Map<String, Object> dbData = listOfMasterData.get(0);
JsonNode jsonNode = mapper.convertValue(dbData, JsonNode.class);
String certificateId = "";
JsonNode issuedCertificates = jsonNode.path("issuedCertificates");
if (issuedCertificates.isArray() && issuedCertificates.size() > 0) {
JsonNode firstCertificate = issuedCertificates.get(0).path(Constants.IDENTIFIER);
if (!firstCertificate.isMissingNode() && !firstCertificate.isNull()) {
certificateId = firstCertificate.asText();
logger.info("Certificate id of the user: {}", certificateId);
}
}
String certPublicUrl = (String) dbData.get(Constants.CERT_PUBLICURL);
if (StringUtils.isBlank(certPublicUrl)) {
if (StringUtils.isNotBlank(certificateId)) {
propertyMap.put(Constants.START_TIME, dbData.get(Constants.START_TIME));
String certLink = publicUserCertificateDownload(certificateId);
Map<String, Object> updatedMap = new HashMap<>();
updatedMap.put(Constants.CERT_PUBLICURL, certLink);
cassandraOperation.updateRecord(Constants.KEYSPACE_SUNBIRD, serverProperties.getPublicUserAssessmentTableName(), updatedMap, propertyMap);
Map<String, Object> notificationInput = new HashMap<>();
notificationInput.put(Constants.PUBLIC_USER_ID, email);
notificationInput.put(Constants.PUBLIC_CONTEXT_ID, userCourseEnrollMap.get(Constants.COURSE_ID_LOWER));
notificationInput.put(Constants.PUBLIC_ASSESSMENT_ID, userCourseEnrollMap.get(Constants.PUBLIC_ASSESSMENT_ID));
kafkaProducer.push(serverProperties.getSpringKafkaPublicAssessmentNotificationTopicName(), notificationInput);
} else {
logger.error("The certificateId is not present for kafka event : " + data);
}
} else {
logger.error("The certPublicUrl is already present for kafka event : " + data);
}
}
} catch (Exception e) {
logger.error("Error while processing the kafka event : " + data);
}
}

private String publicUserCertificateDownload(String certificateid) {
logger.info("KafkaConsumer :: publicUserCertificateDownload");
File mFile = null;
try {
String data = callCertRegistryApi(certificateid);
String outputPath = "/tmp/" + certificateid + "_certificate.pdf";
generatePdfFromSvg(data, "pdf", outputPath);
mFile = new File(outputPath);
if (mFile != null && mFile.exists()) {
logger.info("File name uploading into bucket {}", mFile.getAbsolutePath());
SBApiResponse response = storageService.uploadFile(
mFile,
serverProperties.getPublicAssessmentCloudCertificateFolderName(),
serverProperties.getCloudProfileImageContainerName()
);
return response.getResult().get(Constants.URL).toString();
} else {
logger.error("File Not found");
throw new RuntimeException("File Not found");
}
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
if (mFile != null)
mFile.delete();
}
}

private void convertSvgToPng(String svgString, String outputPath) throws IOException, TranscoderException, org.apache.batik.transcoder.TranscoderException {
svgString = cleanInvalidStyles(svgString);
svgString = removeImageTags(svgString);
ByteArrayInputStream inputStream = new ByteArrayInputStream(svgString.getBytes(StandardCharsets.UTF_8));
TranscoderInput input = new TranscoderInput(inputStream);
OutputStream outputStream = new FileOutputStream(outputPath);
PNGTranscoder transcoder = new PNGTranscoder();
TranscoderOutput output = new TranscoderOutput(outputStream);
transcoder.transcode(input, output);
outputStream.flush();
outputStream.close();
inputStream.close();
}

public static String cleanInvalidStyles(String svgContent) {
// Replace 'display: inline-block;' with 'display: inline;' or 'display: block;'
return svgContent.replaceAll("display:\\s*inline-block;", "display: inline;"); // or "display: block;"
}

public static String removeImageTags(String svgContent) {
// Regular expression to match <image> elements in the SVG
return svgContent.replaceAll("<image [^>]*>", "");
}

private String callCertRegistryApi(String certificateid) {
logger.info("StorageServiceImpl :: callCertRegistryApi");
try {
String url = serverProperties.getCertRegistryServiceBaseUrl() + serverProperties.getCertRegistryCertificateDownloadUrl() + certificateid;
HttpHeaders headers = new HttpHeaders();
HttpEntity<String> entity = new HttpEntity<>(headers);
ResponseEntity<JsonNode> response = restTemplate.exchange(
url,
HttpMethod.GET,
entity,
JsonNode.class
);
if (response.getStatusCode().is2xxSuccessful()) {
String printUri = response.getBody().path("result").get("printUri").asText();
return printUri;
} else {
throw new RuntimeException("Failed to retrieve externalId. Status code: " + response.getStatusCodeValue());
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}

public void generatePdfFromSvg(String svgContent, String outputFormat, String outputPath) throws IOException {
try {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);

Map<String, Object> request = new HashMap<>();
request.put("inputFormat", "svg");
request.put("outputFormat", outputFormat);
request.put("printUri", svgContent);

HttpEntity<Object> entity = new HttpEntity<>(request, headers);
ResponseEntity<byte[]> responseEntity = restTemplate.exchange(
serverProperties.getPdfGeneratorServiceBaseUrl() + serverProperties.getPdfGeneratorSvgToPdfUrl(),
HttpMethod.POST,
entity,
byte[].class);

if (responseEntity.getStatusCode() == HttpStatus.OK) {
try (FileOutputStream fos = new FileOutputStream(outputPath, true)) {
// 'true' in FileOutputStream constructor allows appending to the file
fos.write(Objects.requireNonNull(responseEntity.getBody()));
logger.info("Bytes successfully added to the file.");
}
} else {
logger.error("Issue while get the data and response is: ", responseEntity);
}
} catch (Exception e) {
logger.error("Issue while get the data from pdf generator repo", e);
}
}
}
Loading

0 comments on commit 8f6f82a

Please sign in to comment.