fixed job card progress

add-reporting-dashboard
Usama Khan 2025-04-07 17:21:10 -07:00
parent 05c14e0940
commit 93f6b01ece
7 changed files with 265 additions and 139 deletions

View File

@ -3,6 +3,7 @@ package com.utopiaindustries.controller;
import com.utopiaindustries.auth.CuttingRole; import com.utopiaindustries.auth.CuttingRole;
import com.utopiaindustries.auth.ReportingRole; import com.utopiaindustries.auth.ReportingRole;
import com.utopiaindustries.model.ctp.SummaryInventoryReport; import com.utopiaindustries.model.ctp.SummaryInventoryReport;
import com.utopiaindustries.service.ReportingService;
import com.utopiaindustries.service.SummaryInventoryReportService; import com.utopiaindustries.service.SummaryInventoryReportService;
import com.utopiaindustries.util.StringUtils; import com.utopiaindustries.util.StringUtils;
import org.springframework.stereotype.Controller; import org.springframework.stereotype.Controller;
@ -20,10 +21,12 @@ import java.util.Map;
@ReportingRole @ReportingRole
@RequestMapping( "/reporting" ) @RequestMapping( "/reporting" )
public class ReportingController { public class ReportingController {
private final ReportingService reportingService;
private final SummaryInventoryReportService summaryInventoryReportService; private final SummaryInventoryReportService summaryInventoryReportService;
public ReportingController(SummaryInventoryReportService summaryInventoryReportService2) { public ReportingController(SummaryInventoryReportService summaryInventoryReportService2, ReportingService reportingService) {
this.summaryInventoryReportService = summaryInventoryReportService2; this.summaryInventoryReportService = summaryInventoryReportService2;
this.reportingService = reportingService;
} }
@GetMapping( "/summary") @GetMapping( "/summary")
@ -41,9 +44,9 @@ public class ReportingController {
} }
@GetMapping( "/job-card-report") @GetMapping( "/job-card-report")
public String jobCardReport(@RequestParam(value = "item-id", required = false ) String itemId, @RequestParam(value = "sku" , required = false) String sku, @RequestParam(value = "start-date", required = false) String startDate, @RequestParam(value = "end-date", required = false) String endDate, Model model ){ public String jobCardReport(@RequestParam( value = "job-card-id", required = false ) String jobCardId,
int progressValue = 75; Model model ){
model.addAttribute("progressValue", progressValue); model.addAttribute("jobCardProgress", reportingService.getJobCardProgress(jobCardId));
return "/reporting/job-card-report"; return "/reporting/job-card-report";
} }

View File

@ -28,6 +28,7 @@ public class StitchingOfflineItemDAO {
private final String SELECT_BY_IDS = String.format( "SELECT * FROM %s WHERE id IN (:ids)", TABLE_NAME ); private final String SELECT_BY_IDS = String.format( "SELECT * FROM %s WHERE id IN (:ids)", TABLE_NAME );
private final String SELECT_BY_TERM = String.format( "SELECT * FROM %s WHERE barcode LIKE :term ORDER BY ID DESC", TABLE_NAME ); private final String SELECT_BY_TERM = String.format( "SELECT * FROM %s WHERE barcode LIKE :term ORDER BY ID DESC", TABLE_NAME );
private final String SELECT_BY_MASTER_ID = String.format( "SELECT * FROM %s WHERE job_card_id = :job_card_id", TABLE_NAME ); private final String SELECT_BY_MASTER_ID = String.format( "SELECT * FROM %s WHERE job_card_id = :job_card_id", TABLE_NAME );
private final String COUNT_TOTAL_QA_ITEMS= String.format("SELECT COUNT(*) FROM %s WHERE job_card_id = :job_card_id AND qa_remarks IS NOT NULL",TABLE_NAME);
public StitchingOfflineItemDAO(NamedParameterJdbcTemplate namedParameterJdbcTemplate) { public StitchingOfflineItemDAO(NamedParameterJdbcTemplate namedParameterJdbcTemplate) {
this.namedParameterJdbcTemplate = namedParameterJdbcTemplate; this.namedParameterJdbcTemplate = namedParameterJdbcTemplate;
@ -141,4 +142,10 @@ public class StitchingOfflineItemDAO {
return totalCounts; return totalCounts;
} }
public Long CalculateTotalQA( long jobCardId ){
MapSqlParameterSource params = new MapSqlParameterSource();
params.addValue("job_card_id", jobCardId );
Long count = namedParameterJdbcTemplate.queryForObject(COUNT_TOTAL_QA_ITEMS, params, Long.class);
return count != null ? count : 0;
}
} }

View File

@ -0,0 +1,109 @@
package com.utopiaindustries.service;
import com.utopiaindustries.dao.ctp.*;
import com.utopiaindustries.model.ctp.JobCardItem;
import org.springframework.stereotype.Service;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
@Service
public class ReportingService {
private final JobCardItemDAO jobCardItemDAO;
private final CutPieceDAO cutPieceDAO;
private final BundleDAO bundleDAO;
private final InventoryTransactionLegDAO inventoryTransactionLegDAO;
private final InventoryTransactionDAO inventoryTransactionDAO;
private final JobCardDAO jobCardDAO;
private final CryptographyService cryptographyService;
private final MasterBundleDAO masterBundleDAO;
private final FinishedItemDAO finishedItemDAO;
private final StitchingOfflineItemDAO stitchingOfflineItemDAO;
public ReportingService( JobCardItemDAO jobCardItemDAO, CutPieceDAO cutPieceDAO, BundleDAO bundleDAO, InventoryTransactionLegDAO inventoryTransactionLegDAO, InventoryTransactionDAO inventoryTransactionDAO, JobCardDAO jobCardDAO, CryptographyService cryptographyService, MasterBundleDAO masterBundleDAO, FinishedItemDAO finishedItemDAO, StitchingOfflineItemDAO stitchingOfflineItemDAO) {
this.jobCardItemDAO = jobCardItemDAO;
this.cutPieceDAO = cutPieceDAO;
this.bundleDAO = bundleDAO;
this.inventoryTransactionLegDAO = inventoryTransactionLegDAO;
this.inventoryTransactionDAO = inventoryTransactionDAO;
this.jobCardDAO = jobCardDAO;
this.cryptographyService = cryptographyService;
this.masterBundleDAO = masterBundleDAO;
this.finishedItemDAO = finishedItemDAO;
this.stitchingOfflineItemDAO = stitchingOfflineItemDAO;
}
public Map<String,Integer> getJobCardProgress(String jobCardID){
if (jobCardID == null){
return new HashMap<>();
}else {
HashMap<String,Integer> totalProgress = new HashMap<>();
List<JobCardItem> jobCardItems = jobCardItemDAO.findByCardId(Long.parseLong(jobCardID));
BigDecimal totalProduction = jobCardItems.stream()
.map(item -> Optional.ofNullable(item.getTotalProduction()).orElse(BigDecimal.ZERO))
.reduce(BigDecimal.ZERO, BigDecimal::add);
BigDecimal actualProduction = jobCardItems.stream()
.map(item -> Optional.ofNullable(item.getActualProduction()).orElse(BigDecimal.ZERO))
.reduce(BigDecimal.ZERO, BigDecimal::add);
BigDecimal expectedProduction = jobCardItems.stream()
.map(item -> Optional.ofNullable(item.getExpectedProduction()).orElse(BigDecimal.ZERO))
.reduce(BigDecimal.ZERO, BigDecimal::add);
if (totalProduction.compareTo(BigDecimal.ZERO) == 0 && actualProduction.compareTo(BigDecimal.ZERO) == 0 ) {
totalProgress.put("Job Card Progress", BigDecimal.ZERO.intValue());
} else {
if (actualProduction.compareTo(expectedProduction)>0){
BigDecimal progressPercentage = actualProduction.add(totalProduction)
.divide(actualProduction.multiply(BigDecimal.valueOf(2)), 4, RoundingMode.HALF_UP)
.multiply(BigDecimal.valueOf(100));
totalProgress.put("Job Card Progress", progressPercentage.intValue());
}else {
BigDecimal progressPercentage = actualProduction.add(totalProduction)
.divide(expectedProduction.multiply(BigDecimal.valueOf(2)), 4, RoundingMode.HALF_UP)
.multiply(BigDecimal.valueOf(100));
totalProgress.put("Job Card Progress", progressPercentage.intValue());
}
}
if (actualProduction.compareTo(BigDecimal.ZERO) == 0 ) {
totalProgress.put("Cutting Progress", BigDecimal.ZERO.intValue());
} else if (actualProduction.compareTo(expectedProduction) < 0) {
BigDecimal cuttingProgress = expectedProduction
.divide(actualProduction, 4, BigDecimal.ROUND_HALF_UP)
.multiply(BigDecimal.valueOf(100));
totalProgress.put("Cutting Progress", cuttingProgress.intValue());
}else if (actualProduction.compareTo(expectedProduction) > 0 || actualProduction.compareTo(expectedProduction) == 0){
totalProgress.put("Cutting Progress", 100);
}
if (totalProduction.compareTo(BigDecimal.ZERO) == 0){
totalProgress.put("Stitching Progress", BigDecimal.ZERO.intValue());
}else {
BigDecimal stitchingProgress = expectedProduction
.divide(totalProduction, 4, BigDecimal.ROUND_HALF_UP)
.multiply(BigDecimal.valueOf(100));
totalProgress.put("Stitching Progress", stitchingProgress.intValue());
}
Long qaProgressItems = stitchingOfflineItemDAO.CalculateTotalQA(Long.parseLong(jobCardID));
if (qaProgressItems == 0){
totalProgress.put("QA Progress", 0);
}else {
BigDecimal qaProgress = BigDecimal.valueOf(qaProgressItems)
.divide(actualProduction, 4, BigDecimal.ROUND_HALF_UP)
.multiply(BigDecimal.valueOf(100));
totalProgress.put("QA Progress", qaProgress.intValue());
}
return totalProgress;
}
}
}

View File

@ -4,58 +4,63 @@ document.addEventListener("DOMContentLoaded", function () {
return; return;
} }
function createGaugeChart(config) { function createGaugeChart(speed, color, divId, title, height, width , fontSize, fontYAxis, fontXAxis, fontColor, total, actual) {
Highcharts.chart(config.containerId, { console.log(fontSize)
Highcharts.chart(divId, {
chart: { chart: {
type: 'solidgauge', type: 'solidgauge',
width: config.width, width: width,
height: config.height, height: height,
backgroundColor: 'transparent', backgroundColor: 'transparent',
plotBackgroundColor: null, plotBackgroundColor: null,
shadow: false shadow: false
}, },
title: { title: {
text: config.title, text: title,
y: 30 y: 30
}, },
pane: { pane: {
startAngle: 0, startAngle: 0,
endAngle: 360, endAngle: 360,
y: config.CirclePosition, y: 0,
background: [{ background: [{
backgroundColor: 'transparent', backgroundColor: 'transparent',
shape: "arc", shape: "arc",
borderWidth: 0, borderWidth: 0,
innerRadius: config.innerRadius, innerRadius: '60%',
outerRadius: config.outerRadius outerRadius: '100%'
}] }]
}, },
yAxis: { yAxis: {
min: config.minValue, min: 0,
max: config.maxValue, max: 100,
tickPositions: [], tickPositions: [],
lineWidth: 0, lineWidth: 0,
minorTickLength: 0, minorTickLength: 0,
tickWidth: 0, tickWidth: 0,
gridLineWidth: 0, gridLineWidth: 0,
stops: [ stops: [
[1, config.color] [1, color]
] ]
}, },
series: [{ series: [{
name: 'Percentage', name: 'Percentage',
data: [config.speed], data: [speed],
dataLabels: { dataLabels: {
format: '{y}% <br>' + config.totalPiece + " | " + config.completedPiece, format: '{y}% ',
y: config.InnerTextYPosition, // format: '{y}% <br>' + "Total: "+ total+ "<br> Complete: " + actual,
y: fontYAxis,
x: fontXAxis,
borderWidth: 0, borderWidth: 0,
backgroundColor: 'none', backgroundColor: 'none',
style: { style: {
fontSize: config.fontSize, fontSize: fontSize + 'px',
fontWeight: 'bold' fontWeight: 'bold',
} color:fontColor
},
zIndex: 10
}, },
color: config.color, color: color,
showInLegend: false showInLegend: false
}], }],
credits: { credits: {
@ -63,48 +68,36 @@ document.addEventListener("DOMContentLoaded", function () {
} }
}); });
} }
initializeGauges();
function createBarChart(config) { function initializeGauges() {
console.log(`Creating bar chart for '${config.containerId}'`); const gaugeDivs2 = document.querySelectorAll('.gauge-chart2');
gaugeDivs2.forEach(function (div) {
if (!document.getElementById(config.containerId)) { const speed = div.getAttribute('data-speed');
console.error(`Bar chart container '${config.containerId}' not found!`); const color = div.getAttribute('data-color');
return; const title = div.getAttribute('data-title');
} const height = div.getAttribute('data-height');
const width = div.getAttribute('data-width');
Highcharts.chart(config.containerId, { const fontSize = div.getAttribute('data-fontSize');
chart: { const fontColor = div.getAttribute('data-fontColor');
type: 'column', const total = div.getAttribute('data-totalProduction');
height: 400, const actual = div.getAttribute('data-actualProduction');
width: 1600, // Ensure the chart is wider than the container const divId = div.id;
marginTop: 60, createGaugeChart(parseInt(speed), color, divId, title, height, width, fontSize, -20, 4, fontColor, total, actual);
spacingTop: 30 });
}, const gaugeDivs = document.querySelectorAll('.gauge-chart');
xAxis: { gaugeDivs.forEach(function (div) {
categories: ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday", const speed = div.getAttribute('data-speed');
"Next Monday", "Next Tuesday", "Next Wednesday", "Friday", "Saturday", "Sunday"], const color = div.getAttribute('data-color');
labels: { const title = div.getAttribute('data-title');
rotation: -45 // Optional: Rotate labels if needed const height = div.getAttribute('data-height');
} const width = div.getAttribute('data-width');
}, const fontSize = div.getAttribute('data-fontSize');
scrollbar: { const fontColor = div.getAttribute('data-fontColor');
enabled: true // Ensure Highcharts scrollbar is enabled const total = div.getAttribute('data-totalProduction');
}, const actual = div.getAttribute('data-actualProduction');
series: [ const divId = div.id;
{ name: 'Model A', data: [0.8, 0.75, 0.78, 0.79, 0.77, 0.81, 0.80, 0.76, 0.79, 0.80, 0.76, 0.79, 0.80, 0.76, 0.79, 0.80] }, console.log(title)
{ name: 'Model B', data: [0.85, 0.78, 0.82, 0.80, 0.79, 0.84, 0.83, 0.78, 0.82, 0.83, 0.76, 0.79, 0.80, 0.76, 0.79, 0.80] }, createGaugeChart(parseInt(speed), color, divId, title, height, width, fontSize, -15, 2, fontColor, total, actual);
{ name: 'Model C', data: [0.88, 0.80, 0.85, 0.82, 0.81, 0.87, 0.85, 0.80, 0.85, 0.86, 0.76, 0.79, 0.80, 0.76, 0.79, 0.80] }, });
{ name: 'Model D', data: [0.9, 0.83, 0.87, 0.84, 0.83, 0.89, 0.88, 0.83, 0.87, 0.88, 0.76, 0.79, 0.80, 0.76, 0.79, 0.80] }
]
});
console.log(`Bar chart '${config.containerId}' created successfully.`);
} }
gaugeConfigs.forEach(createGaugeChart);
createBarChart(barChartConfigs);
}); });

View File

@ -3438,6 +3438,44 @@ if ( typeof Vue !== 'undefined' ) {
}) })
/*
* job card search for reporting
* */
Vue.component('job-card-for-reporting',{
mixins: [searchComponentMixin],
methods : {
getSearchUrl : function () {
let url = `/ctp/rest/job-cards/search?term=${encodeURIComponent(this.list.term)}&filter=false`;
if( ! this.filter ){
url += '&filter=false'
}
return url;
},
getEmittedEventName: function() {
return 'job-card-select';
},
getTitle: function( card ) {
return `(${card.code}) - ${card.createdBy}`;
}
},
props: {
labelText: {
default: 'Search Job Card'
},
titleFieldName: {
default: 'cardTitle'
},
idFieldName: {
default: 'jobCardId'
},
codeFieldName : {
default : 'jobCardCode'
},
filter : {
default : true
}
}
})
/* /*

View File

@ -7,46 +7,12 @@
<div class="page-filters-sidebar"> <div class="page-filters-sidebar">
<form th:action="@{${#strings.replace(#httpServletRequest.requestURI, #request.getContextPath(), '')}}"> <form th:action="@{${#strings.replace(#httpServletRequest.requestURI, #request.getContextPath(), '')}}">
<h5 class="mb-4">Refine Your Search</h5> <h5 class="mb-4">Refine Your Search</h5>
<div class="form-group">
<label>ID</label>
<input type="text" class="form-control" name="id" maxlength="100" th:value="${param['id']}">
</div>
<div class="form-group">
<label>Title</label>
<input type="text" class="form-control" name="title" maxlength="100"
th:value="${param['title']}">
</div>
<div class="form-group">
<label>Active</label>
<select name="active" class="form-control" >
<option value="">Please select</option>
<option value="1" th:selected="${#strings.equals(param['active'], #strings.toString(1))}">Active</option>
<option value="0" th:selected="${#strings.equals(param['active'], #strings.toString(0))}">Inactive</option>
</select>
</div>
<div class="form-group">
<label>Created By</label>
<input type="text" class="form-control" name="created-by" maxlength="50" th:value="${param['created-by']}">
</div>
<div class="form-group">
<label>Created Start Date</label>
<input type="date" class="form-control" name="start-date" th:value="${param['start-date']}">
</div>
<div class="form-group">
<label>Created End Date</label>
<input type="date" class="form-control" name="end-date" th:value="${param['end-date']}">
</div>
<div class="form-group" th:with="id=${param['site-id']},title=${param['site-title']}" data-vue-app> <div class="form-group" th:with="id=${param['site-id']},title=${param['site-title']}" data-vue-app>
<location-site-search th:attr="id=${id},title=${title}" <job-card-for-reporting th:attr="id=${id},title=${title}"
v-bind:id-field-name="'site-id'" v-bind:id-field-name="'job-card-id'"
v-bind:title-field-name="'site-title'" v-bind:title-field-name="'site-title'"
></location-site-search> ></job-card-for-reporting>
</div> </div>
<div class="form-group">
<label>Count</label>
<input type="number" class="form-control" name="count" th:value="${param['count'] ?: 100}" min="0" step="1" />
</div>
<input type="submit" class="btn btn-secondary btn-block" value="Search"> <input type="submit" class="btn btn-secondary btn-block" value="Search">
<a th:href="@{${#strings.replace(#httpServletRequest.requestURI, #request.getContextPath(), '')}}" <a th:href="@{${#strings.replace(#httpServletRequest.requestURI, #request.getContextPath(), '')}}"
class="btn btn-secondary btn-block">Reset</a> class="btn btn-secondary btn-block">Reset</a>

View File

@ -15,26 +15,38 @@
<tr> <tr>
<td style="padding:0px;"> <td style="padding:0px;">
<div style="border: 2px solid #d5d8dc; padding: 10px; border-radius: 10px; height: 370px;"> <div style="border: 2px solid #d5d8dc; padding: 10px; border-radius: 10px; height: 370px;">
<h1 style="text-align: center; margin-left: 300px">Job Card Report</h1> <h1 style="text-align: center;">Job Card Report</h1>
<div style="display: flex; align-items: center;"> <div style="display: flex; align-items: center;">
<div style="text-align: center;margin-top: -45px"> <div style="text-align: center;margin-top: -45px">
<div id="jobCard" style="width: 300px; height: 330px;"></div> <div style="width: 300px; height: 330px;"
th:id="'gauge-chart2'"
class="gauge-chart2"
th:data-speed="${jobCardProgress.get('Job Card Progress')}"
th:data-title="${'Job Card Progress'}"
th:data-width="350"
th:data-height="350"
th:data-totalProduction="${jobCardProgress.get('totalProduction')}"
th:data-actualProduction="${jobCardProgress.get('actualProduction')}"
th:data-fontSize="30"
th:data-fontColor="'#17202a'"
th:data-color="'#566573'"></div>
</div> </div>
<div style="display: flex; "> <div style="display: flex; ">
<div style="text-align: center; margin-top: 40px;"> <div th:each="title, index : ${jobCardProgress.keySet()}" style="text-align: center; margin-top: 40px;">
<div id="cutting" style="width: 200px; height: 200px;"></div> <div th:if ="${ title != 'Job Card Progress' }"
</div> th:id="'gauge-chart-' + ${index}"
<div style="text-align: center; margin-top: 40px;"> class="gauge-chart"
<div id="stitching" style="width: 200px; height: 200px;"></div> style="width: 200px; height: 230px;"
</div> th:data-speed="${jobCardProgress.get(title)}"
<div style="text-align: center; margin-top: 40px;"> th:data-totalProduction="${jobCardProgress.get('totalProduction')}"
<div id="quality" style="width: 200px; height: 200px;"></div> th:data-actualProduction="${jobCardProgress.get('actualProduction')}"
</div> th:data-title="${title}"
<div style="text-align: center; margin-top: 40px;"> th:data-width="230"
<div id="finishing" style="width: 200px; height: 200px;"></div> th:data-height="230"
</div> th:data-fontSize="20"
<div style="text-align: center; margin-top: 40px;"> th:data-fontColor="'#17202a'"
<div id="Packaging" style="width: 200px; height: 200px;"></div> th:data-color="'#95a5a6'">
</div>
</div> </div>
</div> </div>
</div> </div>
@ -110,17 +122,15 @@
</table> </table>
</td> </td>
</tr> </tr>
<tr> <!-- <tr>-->
<td style="padding-left: 150px;"> <!-- <td style="padding-left: 150px;">-->
<div style="border: 2px solid #d5d8dc; padding-top: 10px; border-radius: 10px; height: 460px; width: 80%; overflow-x: auto;"> <!-- <div style="border: 2px solid #d5d8dc; padding-top: 10px; border-radius: 10px; height: 460px; width: 80%; overflow-x: auto;">-->
<div id="barChart" style="height: 400px; width: 1600px;"></div> <!-- 100% width for responsiveness --> <!-- <div id="barChart" style="height: 400px; width: 1600px;"></div> &lt;!&ndash; 100% width for responsiveness &ndash;&gt;-->
</div> <!-- </div>-->
</td>
</tr>
<!-- </td>-->
<!-- </tr>-->
</tbody> </tbody>
</table> </table>
</div> </div>
@ -226,18 +236,18 @@
completedPiece:100 completedPiece:100
} }
]; ];
const barChartConfigs = { <!-- const barChartConfigs = {-->
containerId: "barChart", <!-- containerId: "barChart",-->
title: "Day-wise Performance Metrics", <!-- title: "Day-wise Performance Metrics",-->
max:16, <!-- max:16,-->
categories: ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday", "Next Monday", "Next Tuesday", "Next Wednesday", "Friday", "Saturday", "Sunday", "Next Monday", "Next Tuesday", "Next Wednesday"], <!-- categories: ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday", "Next Monday", "Next Tuesday", "Next Wednesday", "Friday", "Saturday", "Sunday", "Next Monday", "Next Tuesday", "Next Wednesday"],-->
series: [ <!-- series: [-->
{ name: "Model A", data: [0.8, 0.75, 0.78, 0.76, 0.74, 0.80, 0.77, 0.79, 0.81, 0.76, 0.80, 0.77, 0.79, 0.81, 0.76], color: "#007bff" }, <!-- { name: "Model A", data: [0.8, 0.75, 0.78, 0.76, 0.74, 0.80, 0.77, 0.79, 0.81, 0.76, 0.80, 0.77, 0.79, 0.81, 0.76], color: "#007bff" },-->
{ name: "Model B", data: [0.85, 0.7, 0.77, 0.79, 0.72, 0.83, 0.78, 0.80, 0.82, 0.75,0.83, 0.78, 0.80, 0.82, 0.75], color: "#28a745" }, <!-- { name: "Model B", data: [0.85, 0.7, 0.77, 0.79, 0.72, 0.83, 0.78, 0.80, 0.82, 0.75,0.83, 0.78, 0.80, 0.82, 0.75], color: "#28a745" },-->
{ name: "Model C", data: [0.82, 0.72, 0.79, 0.80, 0.78, 0.85, 0.81, 0.84, 0.86, 0.79, 0.85, 0.81, 0.84, 0.86, 0.79], color: "#dc3545" }, <!-- { name: "Model C", data: [0.82, 0.72, 0.79, 0.80, 0.78, 0.85, 0.81, 0.84, 0.86, 0.79, 0.85, 0.81, 0.84, 0.86, 0.79], color: "#dc3545" },-->
{ name: "Model D", data: [0.88, 0.78, 0.83, 0.85, 0.82, 0.89, 0.85, 0.87, 0.90, 0.83, 0.89, 0.85, 0.87, 0.90, 0.83], color: "#fd7e14" } <!-- { name: "Model D", data: [0.88, 0.78, 0.83, 0.85, 0.82, 0.89, 0.85, 0.87, 0.90, 0.83, 0.89, 0.85, 0.87, 0.90, 0.83], color: "#fd7e14" }-->
] <!-- ]-->
}; <!--};-->
</script> </script>