/**
* $Id: StatisticsDataVisits.java 4414 2009-10-07 16:37:49Z benbosman $
* $URL: https://scm.dspace.org/svn/repo/dspace/tags/dspace-1.6.2/dspace-stats/src/main/java/org/dspace/statistics/content/StatisticsDataVisits.java $
* *************************************************************************
* Copyright (c) 2002-2009, DuraSpace. All rights reserved
* Licensed under the DuraSpace Foundation License.
*
* A copy of the DuraSpace License has been included in this
* distribution and is available at: http://scm.dspace.org/svn/repo/licenses/LICENSE.txt
*/
package org.dspace.statistics.content;
import org.dspace.content.*;
import org.dspace.statistics.Dataset;
import org.dspace.statistics.ObjectCount;
import org.dspace.statistics.SolrLogger;
import org.dspace.statistics.content.filter.StatisticsFilter;
import org.dspace.statistics.content.filter.StatisticsSolrDateFilter;
import org.dspace.statistics.util.LocationUtils;
import org.dspace.core.Context;
import org.dspace.core.Constants;
import org.dspace.core.ConfigurationManager;
import org.dspace.handle.HandleManager;
import org.dspace.app.util.Util;
import org.apache.solr.client.solrj.SolrServerException;
import org.apache.solr.client.solrj.util.ClientUtils;
import java.util.ArrayList;
import java.util.List;
import java.util.HashMap;
import java.util.Map;
import java.sql.SQLException;
import java.text.ParseException;
import java.io.UnsupportedEncodingException;
/**
* Encapsulates the raw data, independent of rendering
*
* @author kevinvandevelde at atmire.com
* Date: 23-feb-2009
* Time: 12:25:20
*
*/
public class StatisticsDataVisits extends StatisticsData{
/** Current currentDso for wich the use the statistics **/
private DSpaceObject currentDso;
public StatisticsDataVisits() {
}
public StatisticsDataVisits(DSpaceObject dso) {
super();
this.currentDso = dso;
}
public StatisticsDataVisits(DSpaceObject currentDso, Dataset dataset) {
super(dataset);
this.currentDso = currentDso;
}
public StatisticsDataVisits(Dataset dataset) {
super(dataset);
}
public Dataset createDataset(Context context) throws SQLException, SolrServerException, ParseException {
//Check if we already have one
//If we do then give it back
if(getDataset() != null)
return getDataset();
///////////////////////////
// 1. DETERMINE OUR AXIS //
///////////////////////////
ArrayList<DatasetQuery> datasetQueries = new ArrayList<DatasetQuery>();
for (int i = 0; i < getDatasetGenerators().size(); i++) {
DatasetGenerator dataSet = getDatasetGenerators().get(i);
processAxis(dataSet, datasetQueries);
}
//Now lets determine our values
//First check if we have a datefacet & if so find it
DatasetTimeGenerator dateFacet = null;
if(getDatasetGenerators().get(0) instanceof DatasetTimeGenerator || (1 < getDatasetGenerators().size() && getDatasetGenerators().get(1) instanceof DatasetTimeGenerator)){
if(getDatasetGenerators().get(0) instanceof DatasetTimeGenerator)
dateFacet = (DatasetTimeGenerator) getDatasetGenerators().get(0);
else
dateFacet = (DatasetTimeGenerator) getDatasetGenerators().get(1);
}
/////////////////////////
// 2. DETERMINE VALUES //
/////////////////////////
boolean showTotal = false;
//Check if we need our total
if((getDatasetGenerators().get(0) != null && getDatasetGenerators().get(0).isIncludeTotal()) || (1 < getDatasetGenerators().size() && getDatasetGenerators().get(1) != null && getDatasetGenerators().get(1).isIncludeTotal()))
showTotal = true;
if(dateFacet != null && dateFacet.getActualStartDate() != null && dateFacet.getActualEndDate() != null){
StatisticsSolrDateFilter dateFilter = new StatisticsSolrDateFilter();
dateFilter.setStartDate(dateFacet.getActualStartDate());
dateFilter.setEndDate(dateFacet.getActualEndDate());
dateFilter.setTypeStr(dateFacet.getDateType());
addFilters(dateFilter);
} else if(dateFacet != null && dateFacet.getStartDate() != null && dateFacet.getEndDate() != null){
StatisticsSolrDateFilter dateFilter = new StatisticsSolrDateFilter();
dateFilter.setStartStr(dateFacet.getStartDate());
dateFilter.setEndStr(dateFacet.getEndDate());
dateFilter.setTypeStr(dateFacet.getDateType());
addFilters(dateFilter);
}
//Determine our filterQuery
String filterQuery = null;
for (int i = 0; i < getFilters().size(); i++) {
if(filterQuery == null)
filterQuery = "";
StatisticsFilter filter = getFilters().get(i);
filterQuery += "(" + filter.toQuery() + ")";
if(i != (getFilters().size() -1))
filterQuery += " AND ";
}
// System.out.println("FILTERQUERY: " + filterQuery);
//We determine our values on the querys resolved above
Dataset dataset = null;
//Run over our queries
//First how many queries do we have ?
if(dateFacet != null){
//So do all the queries and THEN do the date facet
for (int i = 0; i < datasetQueries.size(); i++) {
DatasetQuery dataSetQuery = datasetQueries.get(i);
if(dataSetQuery.getQueries().size() != 1){
//TODO: do this
}else{
String query = dataSetQuery.getQueries().get(0).getQuery();
if(dataSetQuery.getMax() == -1){
//We are asking from our current query all the visits faceted by date
ObjectCount[] results = SolrLogger.queryFacetDate(query, filterQuery, dataSetQuery.getMax(), dateFacet.getDateType(), dateFacet.getStartDate(), dateFacet.getEndDate(), showTotal);
dataset = new Dataset(1, results.length);
//Now that we have our results put em in a matrix
for(int j = 0; j < results.length; j++){
dataset.setColLabel(j, results[j].getValue());
dataset.addValueToMatrix(0, j, results[j].getCount());
}
//TODO: change this !
//Now add the column label
dataset.setRowLabel(0, getResultName(dataSetQuery.getName(), dataSetQuery, context));
dataset.setRowLabelAttr(0, getAttributes(dataSetQuery.getName(), dataSetQuery, context));
}else{
//We need to get the max objects and the next part of the query on them (next part beeing the datasettimequery
ObjectCount[] maxObjectCounts = SolrLogger.queryFacetField(query, filterQuery, dataSetQuery.getFacetField(), dataSetQuery.getMax(), false, null);
for (int j = 0; j < maxObjectCounts.length; j++) {
ObjectCount firstCount = maxObjectCounts[j];
String newQuery = dataSetQuery.getFacetField() + ": " + ClientUtils.escapeQueryChars(firstCount.getValue()) + " AND " + query;
ObjectCount[] maxDateFacetCounts = SolrLogger.queryFacetDate(newQuery, filterQuery, dataSetQuery.getMax(), dateFacet.getDateType(), dateFacet.getStartDate(), dateFacet.getEndDate(), showTotal);
//Make sure we have a dataSet
if(dataset == null)
dataset = new Dataset(maxObjectCounts.length, maxDateFacetCounts.length);
//TODO: this is a very dirty fix chance this ! ! ! ! ! !
dataset.setRowLabel(j, getResultName(firstCount.getValue(), dataSetQuery, context));
dataset.setRowLabelAttr(j, getAttributes(firstCount.getValue(), dataSetQuery, context));
for (int k = 0; k < maxDateFacetCounts.length; k++) {
ObjectCount objectCount = maxDateFacetCounts[k];
//No need to add this many times
if(j == 0)
dataset.setColLabel(k, objectCount.getValue());
dataset.addValueToMatrix(j, k, objectCount.getCount());
}
}
if(dataset != null && !(getDatasetGenerators().get(0) instanceof DatasetTimeGenerator)){
dataset.flipRowCols();
}
}
}
}
}else{
//We do NOT have a datefacet so just do querys after eachother
/*
for (int i = 0; i < datasetQueries.size(); i++) {
DatasetQuery datasetQuery = datasetQueries.get(i);
if(datasetQuery.getQueries().size() != 1){
//TODO: do this
}else{
String query = datasetQuery.getQueries().get(0);
//Loop over the queries & do em
// ObjectCount[] topCounts = SolrLogger.queryFacetField(query, );
}
}
*/
DatasetQuery firsDataset = datasetQueries.get(0);
//Do the first query
ObjectCount[] topCounts1 = null;
// if(firsDataset.getQueries().size() == 1){
topCounts1 = queryFacetField(firsDataset, firsDataset.getQueries().get(0).getQuery(), filterQuery);
// }else{
// TODO: do this
// }
//Check if we have more queries that need to be done
if(datasetQueries.size() == 2){
DatasetQuery secondDataSet = datasetQueries.get(1);
//Now do the second one
ObjectCount[] topCounts2 = queryFacetField(secondDataSet, secondDataSet.getQueries().get(0).getQuery(), filterQuery);
//Now that have results for both of them lets do x.y queries
List<String> facetQueries = new ArrayList<String>();
for (ObjectCount count2 : topCounts2) {
String facetQuery = secondDataSet.getFacetField() + ":" + ClientUtils.escapeQueryChars(count2.getValue());
//Check if we also have a type present (if so this should be put into the query
if ("id".equals(secondDataSet.getFacetField()) && secondDataSet.getQueries().get(0).getDsoType() != -1)
facetQuery += " AND type:" + secondDataSet.getQueries().get(0).getDsoType();
facetQueries.add(facetQuery);
}
for (int i = 0; i < topCounts1.length; i++){
ObjectCount count1 = topCounts1[i];
ObjectCount[] currentResult = new ObjectCount[topCounts2.length];
//Make sure we have a dataSet
if(dataset == null)
dataset = new Dataset(topCounts2.length, topCounts1.length);
dataset.setColLabel(i, getResultName(count1.getValue(), firsDataset, context));
dataset.setColLabelAttr(i, getAttributes(count1.getValue(), firsDataset, context));
String query = firsDataset.getFacetField() + ":" + ClientUtils.escapeQueryChars(count1.getValue());
//Check if we also have a type present (if so this should be put into the query
if("id".equals(firsDataset.getFacetField()) && firsDataset.getQueries().get(0).getDsoType() != -1)
query += " AND type:" + firsDataset.getQueries().get(0).getDsoType();
Map<String, Integer> facetResult = SolrLogger.queryFacetQuery(query, filterQuery, facetQueries);
//TODO: the show total
//No need to add this many times
//TODo: dit vervangen door te displayen value
for (int j = 0; j < topCounts2.length; j++) {
ObjectCount count2 = topCounts2[j];
if(i == 0) {
dataset.setRowLabel(j, getResultName(count2.getValue(), secondDataSet, context));
dataset.setRowLabelAttr(j, getAttributes(count2.getValue(), secondDataSet, context));
}
//Get our value the value is the same as the query
String facetQuery = secondDataSet.getFacetField() + ":" + ClientUtils.escapeQueryChars(count2.getValue());
//Check if we also have a type present (if so this should be put into the query
if ("id".equals(secondDataSet.getFacetField()) && secondDataSet.getQueries().get(0).getDsoType() != -1)
facetQuery += " AND type:" + secondDataSet.getQueries().get(0).getDsoType();
//We got our query so now get the value
dataset.addValueToMatrix(j, i, facetResult.get(facetQuery));
}
/*
for (int j = 0; j < topCounts2.length; j++) {
ObjectCount count2 = topCounts2[j];
String query = firsDataset.getFacetField() + ":" + count1.getValue();
//Check if we also have a type present (if so this should be put into the query
if("id".equals(firsDataset.getFacetField()) && firsDataset.getQueries().get(0).getDsoType() != -1)
query += " AND type:" + firsDataset.getQueries().get(0).getDsoType();
query += " AND " + secondDataSet.getFacetField() + ":" + count2.getValue();
//Check if we also have a type present (if so this should be put into the query
if("id".equals(secondDataSet.getFacetField()) && secondDataSet.getQueries().get(0).getDsoType() != -1)
query += " AND type:" + secondDataSet.getQueries().get(0).getDsoType();
long count = SolrLogger.queryFacetQuery(query, filterQuery);
//TODO: the show total
//No need to add this many times
//TODo: dit vervangen door te displayen value
if(i == 0) {
dataset.setRowLabel(j, getResultName(count2.getValue(), secondDataSet, context));
dataset.setRowLabelAttr(j, getAttributes(count2.getValue(), secondDataSet, context));
}
dataset.addValueToMatrix(j, i, count);
}
*/
}
// System.out.println("BOTH");
} else{
//Make sure we have a dataSet
dataset = new Dataset(1, topCounts1.length);
for (int i = 0; i < topCounts1.length; i++) {
ObjectCount count = topCounts1[i];
dataset.setColLabel(i, getResultName(count.getValue(), firsDataset, context));
dataset.setColLabelAttr(i, getAttributes(count.getValue(), firsDataset, context));
dataset.addValueToMatrix(0, i, count.getCount());
}
}
}
if(dataset != null){
dataset.setRowTitle("Dataset 1");
dataset.setColTitle("Dataset 2");
}else
dataset = new Dataset(0,0);
return dataset;
}
private void processAxis(DatasetGenerator datasetGenerator, List<DatasetQuery> queries) throws SQLException {
if(datasetGenerator instanceof DatasetDSpaceObjectGenerator){
DatasetDSpaceObjectGenerator dspaceObjAxis = (DatasetDSpaceObjectGenerator) datasetGenerator;
//Get the types involved
List<DSORepresentation> dsoRepresentations = dspaceObjAxis.getDsoRepresentations();
for (int i = 0; i < dsoRepresentations.size(); i++){
DatasetQuery datasetQuery = new DatasetQuery();
Integer dsoType = dsoRepresentations.get(i).getType();
boolean seperate = dsoRepresentations.get(i).getSeparate();
Integer dsoLength = dsoRepresentations.get(i).getNameLength();
//Check if our type is our current object
if(currentDso != null && dsoType == currentDso.getType()){
Query query = new Query();
query.setDso(currentDso.getID(), currentDso.getType(), dsoLength);
datasetQuery.addQuery(query);
}else{
//TODO: only do this for bitstreams from an item
Query query = new Query();
if(currentDso != null && seperate && dsoType == Constants.BITSTREAM){
//CURRENTLY THIS IS ONLY POSSIBLE FOR AN ITEM ! ! ! ! ! ! !
//We need to get the seperate bitstreams from our item and make a query for each of them
Item item = (Item) currentDso;
for (int j = 0; j < item.getBundles().length; j++) {
Bundle bundle = item.getBundles()[j];
for (int k = 0; k < bundle.getBitstreams().length; k++) {
Bitstream bitstream = bundle.getBitstreams()[k];
if(!bitstream.getFormat().isInternal()){
//Add a seperate query for each bitstream
query.setDso(bitstream.getID(), bitstream.getType(), dsoLength);
}
}
}
} else {
//We have something else then our current object
//So we need some kind of children from it, so put this in our query
query.setOwningDso(currentDso);
query.setDsoLength(dsoLength);
String title = "";
switch(dsoType){
case Constants.BITSTREAM:
title = "Files";
break;
case Constants.ITEM:
title = "Items";
break;
case Constants.COLLECTION:
title = "Collections";
break;
case Constants.COMMUNITY:
title = "Communities";
break;
}
datasetQuery.setName(title);
//Put the type in so we only get the children of the type specified
query.setDsoType(dsoType);
}
datasetQuery.addQuery(query);
}
datasetQuery.setFacetField("id");
datasetQuery.setMax(dsoRepresentations.get(i).getMax());
queries.add(datasetQuery);
}
}else
if(datasetGenerator instanceof DatasetTypeGenerator){
DatasetTypeGenerator typeAxis = (DatasetTypeGenerator) datasetGenerator;
DatasetQuery datasetQuery = new DatasetQuery();
//First make sure our query is in order
Query query = new Query();
if(currentDso != null)
query.setDso(currentDso.getID(), currentDso.getType());
datasetQuery.addQuery(query);
//Then add the rest
datasetQuery.setMax(typeAxis.getMax());
datasetQuery.setFacetField(typeAxis.getType());
datasetQuery.setName(typeAxis.getType());
queries.add(datasetQuery);
}
}
/**
* Gets the name of the dso (example for collection: ((Collection) dso).getname();
* @return the name of the given dso
*/
private String getResultName(String value, DatasetQuery datasetQuery, Context context) throws SQLException {
if("continent".equals(datasetQuery.getName())){
value = LocationUtils.getContinentName(value);
}else
if("countryCode".equals(datasetQuery.getName())){
value = LocationUtils.getCountryName(value);
}else{
Query query = datasetQuery.getQueries().get(0);
//TODO: CHANGE & THROW AWAY THIS ENTIRE METHOD
//Check if int
int dsoId;
int dsoLength = query.getDsoLength();
try {
dsoId = Integer.parseInt(value);
}catch(Exception e){
dsoId = -1;
}
if(dsoId == -1 && query.getDsoId() != -1 && value == null)
dsoId = query.getDsoId();
if(dsoId != -1 && query.dsoType != -1){
DSpaceObject dso = DSpaceObject.find(context, query.getDsoType(), dsoId);
if(dso != null){
switch(dso.getType()){
case Constants.BITSTREAM:
Bitstream bit = (Bitstream) dso;
return bit.getName();
case Constants.ITEM:
Item item = (Item) dso;
String name = "untitled";
DCValue[] vals = item.getMetadata("dc", "title", null, Item.ANY);
if(vals != null && 0 < vals.length)
name = vals[0].value;
if(dsoLength != -1 && name.length() > dsoLength){
//Cut it off at the first space
int firstSpace = name.indexOf(" ", dsoLength);
if(firstSpace != -1){
name = name.substring(0, firstSpace) + " ...";
}
}
return name;
case Constants.COLLECTION:
Collection coll = (Collection) dso;
name = coll.getName();
if(dsoLength != -1 && name.length() > dsoLength){
//Cut it off at the first space
int firstSpace = name.indexOf(" ", dsoLength);
if(firstSpace != -1){
name = name.substring(0, firstSpace) + " ...";
}
}
return name;
case Constants.COMMUNITY:
Community comm = (Community) dso;
name = comm.getName();
if(dsoLength != -1 && name.length() > dsoLength){
//Cut it off at the first space
int firstSpace = name.indexOf(" ", dsoLength);
if(firstSpace != -1){
name = name.substring(0, firstSpace) + " ...";
}
}
return name;
}
}
}
}
return value;
}
private Map<String, String> getAttributes(String value, DatasetQuery datasetQuery, Context context) throws SQLException {
HashMap<String, String> attrs = new HashMap<String, String>();
Query query = datasetQuery.getQueries().get(0);
//TODO: CHANGE & THROW AWAY THIS ENTIRE METHOD
//Check if int
int dsoId;
try {
dsoId = Integer.parseInt(value);
}catch(Exception e){
dsoId = -1;
}
if(dsoId == -1 && query.getDsoId() != -1 && value == null)
dsoId = query.getDsoId();
if(dsoId != -1 && query.dsoType != -1){
DSpaceObject dso = DSpaceObject.find(context, query.getDsoType(), dsoId);
if(dso != null){
switch(dso.getType()){
case Constants.BITSTREAM:
Bitstream bit = (Bitstream) dso;
//Get our owning item
Item owningItem = null;
Bundle[] bunds = bit.getBundles();
if(0 < bunds.length)
if(0 < bunds[0].getItems().length)
owningItem = bunds[0].getItems()[0];
// If possible refrence this bitstream via a handle, however this may
// be null if a handle has not yet been assigned. In this case refrence the
// item its internal id. In the last case where the bitstream is not associated
// with an item (such as a community logo) then refrence the bitstreamID directly.
String identifier = null;
if (owningItem != null && owningItem.getHandle() != null)
identifier = "handle/"+owningItem.getHandle();
else if (owningItem != null)
identifier = "item/"+owningItem.getID();
else
identifier = "id/"+bit.getID();
String url = ConfigurationManager.getProperty("dspace.url") + "/bitstream/"+identifier+"/";
// If we can put the pretty name of the bitstream on the end of the URL
try
{
if (bit.getName() != null)
url += Util.encodeBitstreamName(bit.getName(), "UTF-8");
}
catch (UnsupportedEncodingException uee)
{
// just ignore it, we don't have to have a pretty
// name on the end of the url because the sequence id will
// locate it. However it means that links in this file might
// not work....
}
url += "?sequence="+bit.getSequenceID();
attrs.put("url", url);
break;
case Constants.ITEM:
Item item = (Item) dso;
attrs.put("url", HandleManager.resolveToURL(context, item.getHandle()));
break;
case Constants.COLLECTION:
Collection coll = (Collection) dso;
attrs.put("url", HandleManager.resolveToURL(context, coll.getHandle()));
break;
case Constants.COMMUNITY:
Community comm = (Community) dso;
attrs.put("url", HandleManager.resolveToURL(context, comm.getHandle()));
break;
}
}
}
return attrs;
}
private ObjectCount[] queryFacetField(DatasetQuery dataset, String query, String filterQuery) throws SolrServerException {
String facetType = dataset.getFacetField() == null ? "id" : dataset.getFacetField();
return SolrLogger.queryFacetField(query, filterQuery, facetType, dataset.getMax(), false, null);
}
public class DatasetQuery {
private String name;
private int max;
private String facetField;
private List<Query> queries;
public DatasetQuery() {
queries = new ArrayList<Query>();
}
public int getMax() {
return max;
}
public void setMax(int max) {
this.max = max;
}
public void addQuery(Query q){
queries.add(q);
}
public List<Query> getQueries() {
return queries;
}
public String getFacetField() {
return facetField;
}
public void setFacetField(String facetField) {
this.facetField = facetField;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
public class Query {
private int dsoType;
private int dsoId;
private int dsoLength;
private DSpaceObject owningDso;
public Query() {
dsoId = -1;
dsoType = -1;
dsoLength = -1;
owningDso = null;
}
public void setOwningDso(DSpaceObject owningDso) {
this.owningDso = owningDso;
}
public void setDso(int dsoId, int dsoType){
this.dsoId = dsoId;
this.dsoType = dsoType;
}
public void setDso(int dsoId, int dsoType, int length){
this.dsoId = dsoId;
this.dsoType = dsoType;
this.dsoLength = length;
}
public void setDsoType(int dsoType) {
this.dsoType = dsoType;
}
public int getDsoLength() {
return dsoLength;
}
public void setDsoLength(int dsoLength) {
this.dsoLength = dsoLength;
}
public int getDsoId() {
return dsoId;
}
public int getDsoType(){
return dsoType;
}
public String getQueryResultName(){
//TODO: This has got to be done differently in case we have a string query
//This is jus a temp solution fix so we can get on with our work
return dsoType + ":" + dsoId;
}
public String getQuery() {
//Time to construct our query
String query = "";
//Check (& add if needed) the dsoType
if(dsoType != -1)
query += "type: " + dsoType;
//Check (& add if needed) the dsoId
if(dsoId != -1)
query += (query.equals("") ? "" : " AND ") + " id:" + dsoId;
if(owningDso != null && currentDso != null){
query += (query.equals("") ? "" : " AND " );
String owningStr = "";
switch(currentDso.getType()){
case Constants.ITEM:
owningStr = "owningItem";
break;
case Constants.COLLECTION:
owningStr = "owningColl";
break;
case Constants.COMMUNITY:
owningStr = "owningComm";
break;
}
owningStr += ":" + currentDso.getID();
query += owningStr;
}
if(query.equals(""))
query = "*:*";
return query;
}
}
}