accounting-summary-access/src/main/java/org/gcube/data/access/accounting/summary/access/impl/AccountingDaoImpl.java

289 lines
10 KiB
Java

package org.gcube.data.access.accounting.summary.access.impl;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.time.LocalDateTime;
import java.time.Period;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.Date;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.Set;
import org.gcube.data.access.accounting.summary.access.AccountingDao;
import org.gcube.data.access.accounting.summary.access.ParameterException;
import org.gcube.data.access.accounting.summary.access.impl.DBStructure.CONTEXTS;
import org.gcube.data.access.accounting.summary.access.impl.DBStructure.DIMENSIONS;
import org.gcube.data.access.accounting.summary.access.impl.DBStructure.Measure;
import org.gcube.data.access.accounting.summary.access.model.MeasureResolution;
import org.gcube.data.access.accounting.summary.access.model.Record;
import org.gcube.data.access.accounting.summary.access.model.Report;
import org.gcube.data.access.accounting.summary.access.model.ReportElement;
import org.gcube.data.access.accounting.summary.access.model.ScopeDescriptor;
import org.gcube.data.access.accounting.summary.access.model.Series;
import org.gcube.data.access.accounting.summary.access.model.internal.Dimension;
import org.gcube.data.access.accounting.summary.access.model.update.AccountingRecord;
import org.gcube.data.access.accounting.summary.access.model.update.UpdateReport;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class AccountingDaoImpl implements AccountingDao{
private ContextTreeProvider treeProvider=null;
private ConnectionManager connectionManager=null;
public AccountingDaoImpl() {
connectionManager=new BasicConnectionManager();
treeProvider=new BasicContextTreeProvider();
}
public void setTreeProvider(ContextTreeProvider treeProvider) {
this.treeProvider = treeProvider;
}
public AccountingDaoImpl(ContextTreeProvider treeProvider, ConnectionManager connectionManager) {
super();
this.treeProvider = treeProvider;
this.connectionManager = connectionManager;
}
@Override
public Report getReportByScope(ScopeDescriptor desc, Date from, Date to, MeasureResolution resolution) throws SQLException, ParameterException {
DateTimeFormatter formatter=getFormatter(resolution);
LocalDateTime fromDate=from.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
LocalDateTime toDate=to.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
long startReportTime=System.currentTimeMillis();
log.info("Loading report {} for {} between {} and {} ",resolution,desc.getId(),formatter.format(fromDate),formatter.format(toDate));
Connection conn=connectionManager.getConnection();
Queries queries=new Queries(conn);
int timeSlices=getRangeSize(from, to, resolution);
//load available dimensions in time slice
ResultSet dimensionRS=queries.getAvailableDimensions(from, to, desc, resolution);
LinkedList<Dimension> foundDimensions=new LinkedList<>();
while(dimensionRS.next()){
String id=dimensionRS.getString(DIMENSIONS.ID);
String label=dimensionRS.getString(DIMENSIONS.LABEL);
String group=dimensionRS.getString(DIMENSIONS.GROUP);
String aggregatedDim=dimensionRS.getString(DIMENSIONS.AGGREGATED_MEASURE);
foundDimensions.add(new Dimension(id,label,aggregatedDim,group));
}
log.debug("Found {} dimensions to load. ",foundDimensions.size());
// Prepare reports for each Dimension
LinkedList<ReportElement> reports=new LinkedList<>();
for(Dimension entry: foundDimensions){
String xLabel=entry.getLabel();
String yLabel="time";
String category=entry.getGroup();
// Report 1 series for selected Scope
reports.add(new ReportElement(desc.getName()+" "+xLabel,category,
xLabel,yLabel,new Series[]{getSeries(queries, from, to, entry, desc, resolution, timeSlices)}));
// Report 2 series for each children
if(desc.hasChildren()) {
LinkedList<Series> childrenSeries=new LinkedList<>();
for(ScopeDescriptor child:desc.getChildren()){
childrenSeries.add(getSeries(queries, from, to, entry, child, resolution, timeSlices));
}
reports.add(new ReportElement(desc.getName()+" children "+xLabel,category,
xLabel,yLabel,childrenSeries.toArray(new Series[childrenSeries.size()])));
}
// PreparedStatement psMeasure=queries.prepareMeasuresByDimension(desc, resolution);
//
// LocalDateTime toDate=to.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
// for(LocalDateTime toAsk=from.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
// toAsk.isAfter(toDate);toAsk=increment(toAsk,resolution)){
// // Scan for time slice
// }
}
log.info("Loaded {} report elements in {} ms",reports.size(),(System.currentTimeMillis()-startReportTime));
return new Report(reports);
}
@Override
public ScopeDescriptor getTree(Object request) throws Exception {
return treeProvider.getTree(request);
}
@Override
public Set<Dimension> getDimensions() throws SQLException {
Connection conn=connectionManager.getConnection();
Queries q=new Queries(conn);
ResultSet rs=q.listDimensions();
HashSet<Dimension> toReturn=new HashSet<>();
while(rs.next()) {
String id=rs.getString(DIMENSIONS.ID);
String label=rs.getString(DIMENSIONS.LABEL);
String group=rs.getString(DIMENSIONS.GROUP);
String aggregatedMeasure =rs.getString(DIMENSIONS.AGGREGATED_MEASURE);
toReturn.add(new Dimension(id,label,aggregatedMeasure,group));
}
return toReturn;
}
@Override
public Set<ScopeDescriptor> getContexts() throws SQLException {
Connection conn=connectionManager.getConnection();
Queries q=new Queries(conn);
ResultSet rs=q.listContexts();
HashSet<ScopeDescriptor> toReturn=new HashSet<>();
while(rs.next()) {
String id=rs.getString(CONTEXTS.ID);
String label=rs.getString(CONTEXTS.LABEL);
toReturn.add(new ScopeDescriptor(id,label));
}
return toReturn;
}
@Override
public UpdateReport insertRecords(AccountingRecord... toInsert) throws SQLException {
log.trace("Preapring to insert {} records.",toInsert.length);
Set<Dimension> existingDimensions=getDimensions();
Set<ScopeDescriptor> existingContexts=getContexts();
Connection conn=connectionManager.getConnection();
log.debug("Loaded {} existing dimensions and {} contexts ",existingDimensions.size(),existingContexts.size());
Queries q=new Queries(conn);
PreparedStatement psMeasure=q.getMeasureInsertionPreparedStatement();
PreparedStatement psContexts=q.getContextInsertionPreparedStatement();
PreparedStatement psDimensions=q.getDimensionInsertionPreparedStatement();
long insertMeasureCounter=0l;
Set<Dimension> insertedDimensions=new HashSet<>();
Set<ScopeDescriptor> insertedContexts=new HashSet<>();
log.debug("Actually registering records..");
for(AccountingRecord record:toInsert) {
Dimension dim=record.getDimension();
ScopeDescriptor context=record.getContext();
if(!existingDimensions.contains(dim)) {
log.debug("Registering {} ",dim);
//ID,Label,Group,AGG
psDimensions.setString(1, dim.getId());
psDimensions.setString(2, dim.getLabel());
psDimensions.setString(3, dim.getGroup());
psDimensions.setString(4, dim.getAggregatedMeasure());
if(psDimensions.executeUpdate()==0)throw new SQLException("Error registering Dimension : No inserted rows");
insertedDimensions.add(dim);
}
if(!existingContexts.contains(context)) {
log.debug("Registering {} ",context);
//ID,Label
psContexts.setString(1, context.getId());
psContexts.setString(2, context.getName());
if(psContexts.executeUpdate()==0)throw new SQLException("Error registering Context : No inserted rows");
insertedContexts.add(context);
}
//Context, Dim,time,measure
psMeasure.setString(1, context.getId());
psMeasure.setString(2, dim.getId());
psMeasure.setLong(3, record.getTime().getTime());
psMeasure.setLong(4, record.getMeasure());
psMeasure.setLong(5, record.getMeasure());
if(psMeasure.executeUpdate()==0) throw new SQLException("Error registering Measure : No inserted rows");
insertMeasureCounter++;
}
conn.commit();
log.trace("Done inserting {} rows. Registered {} dimensions and {} contexts.",insertMeasureCounter,insertedDimensions.size(),insertedContexts.size());
return new UpdateReport(insertMeasureCounter,insertedContexts,insertedDimensions);
}
private static final int getRangeSize(Date from, Date to, MeasureResolution resolution) throws ParameterException {
log.debug("Evaluating time range between {} , {} [{}]",from,to,resolution);
Period p=Period.between(from.toInstant().atZone(ZoneId.systemDefault()).toLocalDate(), to.toInstant().atZone(ZoneId.systemDefault()).toLocalDate());
switch(resolution) {
case MONTHLY : return p.getMonths()+(p.getYears()*12);
default : throw new ParameterException("Invalid resolution "+resolution);
}
}
private static LocalDateTime increment(LocalDateTime toIncrement,MeasureResolution res,int offset){
switch(res){
case MONTHLY : return toIncrement.plusMonths(1);
default : throw new RuntimeException("Unexpected Resolution "+res);
}
}
private static final DateTimeFormatter monthFormatter=DateTimeFormatter.ofPattern("yyyy-MM");
private static DateTimeFormatter getFormatter(MeasureResolution res){
switch(res){
case MONTHLY : return monthFormatter;
default : throw new RuntimeException("Unexpected Resolution "+res);
}
}
private Series getSeries(Queries queries, Date from, Date to, Dimension dim, ScopeDescriptor scope, MeasureResolution res, int timeSlices) throws SQLException{
Record[] records=new Record[timeSlices];
PreparedStatement ps=queries.prepareMeasuresByDimension(scope, res);
DateTimeFormatter formatter=getFormatter(res);
LocalDateTime toSetStartDate=from.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
for(int i=0;i<timeSlices;i++){
toSetStartDate=increment(toSetStartDate,res,i); // Increment Date
LocalDateTime toSetEndDate=increment(toSetStartDate,res,i+1);
ps.setLong(1, toSetStartDate.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli());
ps.setLong(2, toSetEndDate.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli());
ps.setString(3, dim.getId());
ResultSet rs=ps.executeQuery();
Record toSet=new Record(formatter.format(toSetStartDate),0l);
if(rs.next()){
toSet.setY(rs.getLong(Measure.MEASURE));
}
records[i]=toSet;
}
return new Series(scope.getName(),records);
}
}