DS-1241 Statistics Implementation in Elastic Search
Copied in Implementation of Elastic Search, and wired it into DSpace. I changed the ES Node to be a nodeClient, and also one that runs locally within tomcat's JVM. For OSU, we had used either transport-client, or node-client, and both of which required that the server also be running a seperate elastic search instance. By bundling this into DSpace, it should make this a bit more turn-key to get started using Elastic Search. By default, Elastic Search is ON with this commit.
@@ -7,13 +7,13 @@
|
||||
*/
|
||||
package org.dspace.content;
|
||||
|
||||
import java.sql.SQLException;
|
||||
|
||||
import org.dspace.core.Constants;
|
||||
import org.dspace.core.Context;
|
||||
import org.dspace.eperson.EPerson;
|
||||
import org.dspace.eperson.Group;
|
||||
|
||||
import java.sql.SQLException;
|
||||
|
||||
/**
|
||||
* Abstract base class for DSpace objects
|
||||
*/
|
||||
@@ -65,6 +65,15 @@ public abstract class DSpaceObject
|
||||
*/
|
||||
public abstract int getType();
|
||||
|
||||
/**
|
||||
* Provide the text name of the type of this DSpaceObject. It is most likely all uppercase.
|
||||
* @return Object type as text
|
||||
*/
|
||||
public String getTypeText()
|
||||
{
|
||||
return Constants.typeText[this.getType()];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the internal ID (database primary key) of this object
|
||||
*
|
||||
|
@@ -141,7 +141,7 @@
|
||||
<dependency>
|
||||
<groupId>org.apache.solr</groupId>
|
||||
<artifactId>solr-solrj</artifactId>
|
||||
<version>3.3.0</version>
|
||||
<version>${lucene.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.dspace.dependencies</groupId>
|
||||
@@ -194,6 +194,11 @@
|
||||
<version>4.8.2</version>
|
||||
<type>jar</type>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.elasticsearch</groupId>
|
||||
<artifactId>elasticsearch</artifactId>
|
||||
<version>0.18.6</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>commons-cli</groupId>
|
||||
|
@@ -0,0 +1,582 @@
|
||||
package org.dspace.statistics;
|
||||
|
||||
|
||||
import com.maxmind.geoip.Location;
|
||||
import com.maxmind.geoip.LookupService;
|
||||
import org.apache.commons.lang.exception.ExceptionUtils;
|
||||
import org.apache.commons.lang.time.DateFormatUtils;
|
||||
import org.apache.log4j.Logger;
|
||||
import org.dspace.content.*;
|
||||
import org.dspace.content.Collection;
|
||||
import org.dspace.core.ConfigurationManager;
|
||||
import org.dspace.core.Constants;
|
||||
import org.dspace.eperson.EPerson;
|
||||
import org.dspace.statistics.util.DnsLookup;
|
||||
import org.dspace.statistics.util.LocationUtils;
|
||||
import org.dspace.statistics.util.SpiderDetector;
|
||||
import org.elasticsearch.action.ActionFuture;
|
||||
import org.elasticsearch.action.admin.indices.exists.IndicesExistsRequest;
|
||||
import org.elasticsearch.action.admin.indices.exists.IndicesExistsResponse;
|
||||
import org.elasticsearch.action.index.IndexResponse;
|
||||
import org.elasticsearch.client.Client;
|
||||
import org.elasticsearch.client.action.index.IndexRequestBuilder;
|
||||
import org.elasticsearch.client.transport.TransportClient;
|
||||
import org.elasticsearch.common.settings.ImmutableSettings;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.transport.InetSocketTransportAddress;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
import org.elasticsearch.common.xcontent.XContentFactory;
|
||||
import org.elasticsearch.node.Node;
|
||||
import org.elasticsearch.node.NodeBuilder;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.sql.SQLException;
|
||||
import java.util.*;
|
||||
|
||||
import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;
|
||||
|
||||
public class ElasticSearchLogger {
|
||||
|
||||
private static Logger log = Logger.getLogger(ElasticSearchLogger.class);
|
||||
|
||||
private static boolean useProxies;
|
||||
|
||||
public static final String DATE_FORMAT_8601 = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'";
|
||||
|
||||
public static final String DATE_FORMAT_DCDATE = "yyyy-MM-dd'T'HH:mm:ss'Z'";
|
||||
|
||||
private static LookupService locationService;
|
||||
|
||||
private static Map<String, String> metadataStorageInfo;
|
||||
|
||||
public static String clusterName = "dspacestatslogging";
|
||||
public static String indexName = "dspaceindex";
|
||||
public static String indexType = "stats";
|
||||
public static String address = "127.0.0.1";
|
||||
public static int port = 9300;
|
||||
public static boolean storeData = true;
|
||||
|
||||
private static Client client;
|
||||
|
||||
private static boolean havingTroubles = false;
|
||||
|
||||
|
||||
public ElasticSearchLogger() {
|
||||
// nobody should be instantiating this...
|
||||
}
|
||||
|
||||
public ElasticSearchLogger(boolean doInitialize) {
|
||||
initializeElasticSearch();
|
||||
}
|
||||
|
||||
public static ElasticSearchLogger getInstance() {
|
||||
return ElasticSearchLoggerSingletonHolder.instance;
|
||||
}
|
||||
|
||||
// Singleton Pattern of "Initialization on demand holder idiom"
|
||||
private static class ElasticSearchLoggerSingletonHolder {
|
||||
public static final ElasticSearchLogger instance = new ElasticSearchLogger(true);
|
||||
}
|
||||
|
||||
public void initializeElasticSearch() {
|
||||
log.info("DSpace ElasticSearchLogger Initializing");
|
||||
try {
|
||||
LookupService service = null;
|
||||
// Get the db file for the location
|
||||
String dbfile = ConfigurationManager.getProperty("solr-statistics", "dbfile");
|
||||
if (dbfile != null) {
|
||||
try {
|
||||
service = new LookupService(dbfile,
|
||||
LookupService.GEOIP_STANDARD);
|
||||
} catch (FileNotFoundException fe) {
|
||||
log.error("The GeoLite Database file is missing (" + dbfile + ")! Solr Statistics cannot generate location based reports! Please see the DSpace installation instructions for instructions to install this file.", fe);
|
||||
} catch (IOException e) {
|
||||
log.error("Unable to load GeoLite Database file (" + dbfile + ")! You may need to reinstall it. See the DSpace installation instructions for more details.", e);
|
||||
}
|
||||
} else {
|
||||
log.error("The required 'dbfile' configuration is missing in solr-statistics.cfg!");
|
||||
}
|
||||
locationService = service;
|
||||
|
||||
if ("true".equals(ConfigurationManager.getProperty("useProxies"))) {
|
||||
useProxies = true;
|
||||
} else {
|
||||
useProxies = false;
|
||||
}
|
||||
|
||||
log.info("useProxies=" + useProxies);
|
||||
|
||||
metadataStorageInfo = new HashMap<String, String>();
|
||||
int count = 1;
|
||||
String metadataVal;
|
||||
while ((metadataVal = ConfigurationManager.getProperty("solr-statistics", "metadata.item." + count)) != null) {
|
||||
String storeVal = metadataVal.split(":")[0];
|
||||
String metadataField = metadataVal.split(":")[1];
|
||||
|
||||
metadataStorageInfo.put(storeVal, metadataField);
|
||||
log.info("solr-statistics.metadata.item." + count + "=" + metadataVal);
|
||||
count++;
|
||||
}
|
||||
|
||||
// Configurable values for all elasticsearch connection constants
|
||||
clusterName = getConfigurationStringWithFallBack("statistics.elasticsearch.clusterName", clusterName);
|
||||
indexName = getConfigurationStringWithFallBack("statistics.elasticsearch.indexName", indexName);
|
||||
indexType = getConfigurationStringWithFallBack("statistics.elasticsearch.indexType", indexType);
|
||||
address = getConfigurationStringWithFallBack("statistics.elasticsearch.address", address);
|
||||
port = ConfigurationManager.getIntProperty("statistics.elasticsearch.port", port);
|
||||
storeData = ConfigurationManager.getBooleanProperty("statistics.elasticsearch.storeData", storeData);
|
||||
|
||||
//Initialize the connection to Elastic Search, and ensure our index is available.
|
||||
client = getClient();
|
||||
|
||||
IndicesExistsRequest indicesExistsRequest = new IndicesExistsRequest();
|
||||
indicesExistsRequest.indices(new String[] {indexName});
|
||||
|
||||
ActionFuture<IndicesExistsResponse> actionFutureIndicesExist = client.admin().indices().exists(indicesExistsRequest);
|
||||
log.info("DS ES Checking if index exists");
|
||||
if(! actionFutureIndicesExist.actionGet().isExists() ) {
|
||||
//If elastic search index exists, then we are good to go, otherwise, we need to create that index. Should only need to happen once ever.
|
||||
log.info("DS ES index didn't exist, we need to create it.");
|
||||
|
||||
Settings settings = ImmutableSettings.settingsBuilder()
|
||||
.put("number_of_replicas", 1)
|
||||
.put("number_of_shards", 5)
|
||||
.put("cluster.name", clusterName)
|
||||
.build();
|
||||
|
||||
|
||||
|
||||
|
||||
String stringMappingJSON = "{\n" +
|
||||
" \"userAgent\":{\n" +
|
||||
" \"type\":\"string\"\n" +
|
||||
" },\n" +
|
||||
" \"countryCode\":{\n" +
|
||||
" \"type\":\"string\",\n" +
|
||||
" \"index\":\"not_analyzed\",\n" +
|
||||
" \"omit_norms\":true\n" +
|
||||
" },\n" +
|
||||
" \"dns\":{\n" +
|
||||
" \"type\":\"multi_field\",\n" +
|
||||
" \"fields\": {\n" +
|
||||
" \"dns\": {\"type\":\"string\",\"index\":\"analyzed\"},\n" +
|
||||
" \"untouched\":{\"type\":\"string\",\"index\":\"not_analyzed\"}\n" +
|
||||
" }\n" +
|
||||
" },\n" +
|
||||
" \"isBot\":{\n" +
|
||||
" \"type\":\"boolean\"\n" +
|
||||
" },\n" +
|
||||
" \"owningColl\":{\n" +
|
||||
" \"type\":\"integer\",\n" +
|
||||
" \"index\":\"not_analyzed\"\n" +
|
||||
" },\n" +
|
||||
" \"type\":{\n" +
|
||||
" \"type\":\"string\",\n" +
|
||||
" \"index\":\"not_analyzed\",\n" +
|
||||
" \"omit_norms\":true\n" +
|
||||
" },\n" +
|
||||
" \"owningComm\":{\n" +
|
||||
" \"type\":\"integer\",\n" +
|
||||
" \"index\":\"not_analyzed\"\n" +
|
||||
" },\n" +
|
||||
" \"city\":{\n" +
|
||||
" \"type\":\"multi_field\",\n" +
|
||||
" \"fields\": {\n" +
|
||||
" \"city\": {\"type\":\"string\",\"index\":\"analyzed\"},\n" +
|
||||
" \"untouched\":{\"type\":\"string\",\"index\":\"not_analyzed\"}\n" +
|
||||
" }\n" +
|
||||
" },\n" +
|
||||
" \"country\":{\n" +
|
||||
" \"type\":\"multi_field\",\n" +
|
||||
" \"fields\": {\n" +
|
||||
" \"country\": {\"type\":\"string\",\"index\":\"analyzed\"},\n" +
|
||||
" \"untouched\":{\"type\":\"string\",\"index\":\"not_analyzed\"}\n" +
|
||||
" }\n" +
|
||||
" },\n" +
|
||||
" \"ip\":{\n" +
|
||||
" \"type\":\"multi_field\",\n" +
|
||||
" \"fields\": {\n" +
|
||||
" \"ip\": {\"type\":\"string\",\"index\":\"analyzed\"},\n" +
|
||||
" \"untouched\":{\"type\":\"string\",\"index\":\"not_analyzed\"}\n" +
|
||||
" }\n" +
|
||||
" },\n" +
|
||||
" \"id\":{\n" +
|
||||
" \"type\":\"integer\",\n" +
|
||||
" \"index\":\"not_analyzed\"\n" +
|
||||
" },\n" +
|
||||
" \"time\":{\n" +
|
||||
" \"type\":\"date\"\n" +
|
||||
" },\n" +
|
||||
" \"owningItem\":{\n" +
|
||||
" \"type\":\"string\",\n" +
|
||||
" \"index\":\"not_analyzed\"\n" +
|
||||
" },\n" +
|
||||
" \"continent\":{\n" +
|
||||
" \"type\":\"string\",\n" +
|
||||
" \"index\":\"not_analyzed\"\n" +
|
||||
" },\n" +
|
||||
" \"geo\":{\n" +
|
||||
" \"type\":\"geo_point\"\n" +
|
||||
" },\n" +
|
||||
" \"bundleName\":{\n" +
|
||||
" \"type\":\"string\",\n" +
|
||||
" \"index\":\"not_analyzed\"\n" +
|
||||
" },\n" +
|
||||
" \"epersonid\":{\n" +
|
||||
" \"type\":\"string\",\n" +
|
||||
" \"index\":\"not_analyzed\"\n" +
|
||||
" }\n" +
|
||||
"}";
|
||||
|
||||
log.info("Mapping for ["+indexName+"]/["+indexType+"]="+stringMappingJSON);
|
||||
|
||||
IndexResponse response = client.prepareIndex(indexName, indexType, "1")
|
||||
.setSource(jsonBuilder()
|
||||
.startObject()
|
||||
.field("user", "kimchy")
|
||||
.field("postDate", new Date())
|
||||
.field("message", "trying out Elastic Search")
|
||||
.endObject()
|
||||
)
|
||||
.execute()
|
||||
.actionGet();
|
||||
//.setSettings(settings)
|
||||
//client.admin().indices().prepareCreate(indexName).addMapping(indexType, stringMappingJSON).execute().actionGet();
|
||||
log.info("Create INDEX ["+indexName+"]/["+indexType+"]");
|
||||
|
||||
|
||||
//PutMappingRequestBuilder putMappingRequestBuilder = client.admin().indices().preparePutMapping(indexName).setType(indexType);
|
||||
//putMappingRequestBuilder.setSource(stringMappingJSON);
|
||||
//PutMappingResponse response = putMappingRequestBuilder.execute().actionGet();
|
||||
|
||||
//if(!response.getAcknowledged()) {
|
||||
// log.info("Could not define mapping for type ["+indexName+"]/["+indexType+"]");
|
||||
//} else {
|
||||
// log.info("Successfully put mapping for ["+indexName+"]/["+indexType+"]");
|
||||
//}
|
||||
|
||||
|
||||
|
||||
//TODO, need to put the index mapping, i.e. country, city, id, type look at kb-stats-csv-import-elasticsearch/main.php
|
||||
|
||||
log.info("DS ES index didn't exist, but we created it.");
|
||||
} else {
|
||||
log.info("DS ES index already exists");
|
||||
}
|
||||
|
||||
log.info("DSpace ElasticSearchLogger Initialized Successfully (I suppose)");
|
||||
havingTroubles=false;
|
||||
} catch (Exception e) {
|
||||
log.info("Elastic Search crashed during init. " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public void post(DSpaceObject dspaceObject, HttpServletRequest request, EPerson currentUser) {
|
||||
//log.info("DS-ES post for type:"+dspaceObject.getType() + " -- " + dspaceObject.getName());
|
||||
|
||||
client = ElasticSearchLogger.getInstance().getClient();
|
||||
|
||||
boolean isSpiderBot = SpiderDetector.isSpider(request);
|
||||
|
||||
try {
|
||||
if (isSpiderBot &&
|
||||
!ConfigurationManager.getBooleanProperty("solr-statistics", "logBots", true)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// Save our basic info that we already have
|
||||
|
||||
String ip = request.getRemoteAddr();
|
||||
|
||||
if (isUseProxies() && request.getHeader("X-Forwarded-For") != null) {
|
||||
/* This header is a comma delimited list */
|
||||
for (String xfip : request.getHeader("X-Forwarded-For").split(",")) {
|
||||
/* proxy itself will sometime populate this header with the same value in
|
||||
remote address. ordering in spec is vague, we'll just take the last
|
||||
not equal to the proxy
|
||||
*/
|
||||
if (!request.getHeader("X-Forwarded-For").contains(ip)) {
|
||||
ip = xfip.trim();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
XContentBuilder docBuilder = null;
|
||||
|
||||
|
||||
docBuilder = XContentFactory.jsonBuilder().startObject();
|
||||
|
||||
|
||||
docBuilder.field("ip", ip);
|
||||
|
||||
docBuilder.field("id", dspaceObject.getID());
|
||||
|
||||
// The numerical constant that represents the DSpaceObject TYPE. i.e. 0=bitstream, 2=item, ...
|
||||
docBuilder.field("typeIndex", dspaceObject.getType());
|
||||
|
||||
// The text that represent the DSpaceObject TYPE. i.e. BITSTREAM, ITEM, COLLECTION, COMMUNITY
|
||||
docBuilder.field("type", Constants.typeText[dspaceObject.getType()]);
|
||||
|
||||
// Save the current time
|
||||
docBuilder.field("time", DateFormatUtils.format(new Date(), DATE_FORMAT_8601));
|
||||
if (currentUser != null) {
|
||||
docBuilder.field("epersonid", currentUser.getID());
|
||||
}
|
||||
|
||||
try {
|
||||
String dns = DnsLookup.reverseDns(ip);
|
||||
docBuilder.field("dns", dns.toLowerCase());
|
||||
} catch (Exception e) {
|
||||
log.error("Failed DNS Lookup for IP:" + ip);
|
||||
log.debug(e.getMessage(), e);
|
||||
}
|
||||
|
||||
// Save the location information if valid, save the event without
|
||||
// location information if not valid
|
||||
Location location = locationService.getLocation(ip);
|
||||
if (location != null
|
||||
&& !("--".equals(location.countryCode)
|
||||
&& location.latitude == -180 && location.longitude == -180)) {
|
||||
try {
|
||||
docBuilder.field("continent", LocationUtils
|
||||
.getContinentCode(location.countryCode));
|
||||
} catch (Exception e) {
|
||||
System.out
|
||||
.println("COUNTRY ERROR: " + location.countryCode);
|
||||
}
|
||||
docBuilder.field("countryCode", location.countryCode);
|
||||
docBuilder.field("city", location.city);
|
||||
docBuilder.field("latitude", location.latitude);
|
||||
docBuilder.field("longitude", location.longitude);
|
||||
docBuilder.field("isBot", isSpiderBot);
|
||||
|
||||
if (request.getHeader("User-Agent") != null) {
|
||||
docBuilder.field("userAgent", request.getHeader("User-Agent"));
|
||||
}
|
||||
}
|
||||
|
||||
if (dspaceObject instanceof Item) {
|
||||
Item item = (Item) dspaceObject;
|
||||
// Store the metadata
|
||||
for (Object storedField : metadataStorageInfo.keySet()) {
|
||||
String dcField = metadataStorageInfo
|
||||
.get(storedField);
|
||||
|
||||
DCValue[] vals = item.getMetadata(dcField.split("\\.")[0],
|
||||
dcField.split("\\.")[1], dcField.split("\\.")[2],
|
||||
Item.ANY);
|
||||
for (DCValue val1 : vals) {
|
||||
String val = val1.value;
|
||||
docBuilder.field(String.valueOf(storedField), val);
|
||||
docBuilder.field(storedField + "_search", val
|
||||
.toLowerCase());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (dspaceObject instanceof Bitstream) {
|
||||
Bitstream bit = (Bitstream) dspaceObject;
|
||||
Bundle[] bundles = bit.getBundles();
|
||||
docBuilder.field("bundleName").startArray();
|
||||
for (Bundle bundle : bundles) {
|
||||
docBuilder.value(bundle.getName());
|
||||
}
|
||||
docBuilder.endArray();
|
||||
}
|
||||
|
||||
storeParents(docBuilder, getParents(dspaceObject));
|
||||
|
||||
docBuilder.endObject();
|
||||
|
||||
if (docBuilder != null) {
|
||||
IndexRequestBuilder irb = client.prepareIndex(indexName, indexType)
|
||||
.setSource(docBuilder);
|
||||
//log.info("Executing document insert into index");
|
||||
if(client == null) {
|
||||
log.error("Hey, client is null");
|
||||
}
|
||||
irb.execute().actionGet();
|
||||
}
|
||||
|
||||
} catch (RuntimeException re) {
|
||||
log.error("RunTimer in ESL:\n" + ExceptionUtils.getStackTrace(re));
|
||||
havingTroubles=true;
|
||||
throw re;
|
||||
} catch (Exception e) {
|
||||
log.error(e.getMessage());
|
||||
havingTroubles=true;
|
||||
} finally {
|
||||
client.close();
|
||||
}
|
||||
}
|
||||
|
||||
public static String getClusterName() {
|
||||
return clusterName;
|
||||
}
|
||||
|
||||
public static void setClusterName(String clusterName) {
|
||||
ElasticSearchLogger.clusterName = clusterName;
|
||||
}
|
||||
|
||||
public static String getIndexName() {
|
||||
return indexName;
|
||||
}
|
||||
|
||||
public static void setIndexName(String indexName) {
|
||||
ElasticSearchLogger.indexName = indexName;
|
||||
}
|
||||
|
||||
public static String getIndexType() {
|
||||
return indexType;
|
||||
}
|
||||
|
||||
public static void setIndexType(String indexType) {
|
||||
ElasticSearchLogger.indexType = indexType;
|
||||
}
|
||||
|
||||
public static String getAddress() {
|
||||
return address;
|
||||
}
|
||||
|
||||
public static void setAddress(String address) {
|
||||
ElasticSearchLogger.address = address;
|
||||
}
|
||||
|
||||
public static int getPort() {
|
||||
return port;
|
||||
}
|
||||
|
||||
public static void setPort(int port) {
|
||||
ElasticSearchLogger.port = port;
|
||||
}
|
||||
|
||||
public void buildParents(DSpaceObject dso, HashMap<String, ArrayList<Integer>> parents)
|
||||
throws SQLException {
|
||||
if (dso instanceof Community) {
|
||||
Community comm = (Community) dso;
|
||||
while (comm != null && comm.getParentCommunity() != null) {
|
||||
comm = comm.getParentCommunity();
|
||||
parents.get("owningComm").add(comm.getID());
|
||||
}
|
||||
} else if (dso instanceof Collection) {
|
||||
Collection coll = (Collection) dso;
|
||||
for (Community community : coll.getCommunities()) {
|
||||
parents.get("owningComm").add(community.getID());
|
||||
buildParents(community, parents);
|
||||
}
|
||||
} else if (dso instanceof Item) {
|
||||
Item item = (Item) dso;
|
||||
for (Collection collection : item.getCollections()) {
|
||||
parents.get("owningColl").add(collection.getID());
|
||||
buildParents(collection, parents);
|
||||
}
|
||||
} else if (dso instanceof Bitstream) {
|
||||
Bitstream bitstream = (Bitstream) dso;
|
||||
|
||||
for (Bundle bundle : bitstream.getBundles()) {
|
||||
for (Item item : bundle.getItems()) {
|
||||
|
||||
parents.get("owningItem").add(item.getID());
|
||||
buildParents(item, parents);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public HashMap<String, ArrayList<Integer>> getParents(DSpaceObject dso)
|
||||
throws SQLException {
|
||||
HashMap<String, ArrayList<Integer>> parents = new HashMap<String, ArrayList<Integer>>();
|
||||
parents.put("owningComm", new ArrayList<Integer>());
|
||||
parents.put("owningColl", new ArrayList<Integer>());
|
||||
parents.put("owningItem", new ArrayList<Integer>());
|
||||
|
||||
buildParents(dso, parents);
|
||||
return parents;
|
||||
}
|
||||
|
||||
public void storeParents(XContentBuilder docBuilder, HashMap<String, ArrayList<Integer>> parents) throws IOException {
|
||||
|
||||
Iterator it = parents.keySet().iterator();
|
||||
while (it.hasNext()) {
|
||||
|
||||
String key = (String) it.next();
|
||||
|
||||
ArrayList<Integer> ids = parents.get(key);
|
||||
|
||||
if (ids.size() > 0) {
|
||||
docBuilder.field(key).startArray();
|
||||
for (Integer i : ids) {
|
||||
docBuilder.value(i);
|
||||
}
|
||||
docBuilder.endArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public boolean isUseProxies() {
|
||||
return useProxies;
|
||||
}
|
||||
|
||||
// Transport Client will talk to another server on 9300
|
||||
public void createTransportClient(boolean skipCheck) {
|
||||
if(havingTroubles && !skipCheck) {
|
||||
initializeElasticSearch();
|
||||
}
|
||||
Settings settings = ImmutableSettings.settingsBuilder().put("cluster.name", clusterName).build();
|
||||
client = new TransportClient(settings).addTransportAddress(new InetSocketTransportAddress(address, port));
|
||||
}
|
||||
|
||||
public void createElasticClient() {
|
||||
//createElasticClient(true);
|
||||
}
|
||||
|
||||
public Client getClient() {
|
||||
if(client == null) {
|
||||
log.error("getClient reports null client");
|
||||
createNodeClient();
|
||||
|
||||
|
||||
//createElasticClient();
|
||||
}
|
||||
return client;
|
||||
}
|
||||
|
||||
// Node Client will discover other ES nodes running in local JVM
|
||||
public void createNodeClient() {
|
||||
NodeBuilder nodeBuilder = NodeBuilder.nodeBuilder().clusterName(clusterName);
|
||||
|
||||
if(storeData) {
|
||||
String dspaceDir = ConfigurationManager.getProperty("dspace.dir");
|
||||
Settings settings = ImmutableSettings.settingsBuilder().put("path.data", dspaceDir + "/elasticsearch/").build();
|
||||
log.info("Create a Local Node that will store data.");
|
||||
nodeBuilder = nodeBuilder.data(true).local(true).settings(settings);
|
||||
} else {
|
||||
log.info("Create a nodeClient, that looks for a neighbor to be master.");
|
||||
nodeBuilder = nodeBuilder.client(true);
|
||||
}
|
||||
Node node = nodeBuilder.node();
|
||||
log.info("Got node");
|
||||
client = node.client();
|
||||
log.info("Created new node client");
|
||||
}
|
||||
|
||||
public void createElasticClient(boolean skipCheck) {
|
||||
log.info("Creating a new elastic-client");
|
||||
//createTransportClient(skipCheck);
|
||||
}
|
||||
|
||||
public String getConfigurationStringWithFallBack(String configurationKey, String defaultFallbackValue) {
|
||||
String configDrivenValue = ConfigurationManager.getProperty(configurationKey);
|
||||
if(configDrivenValue == null || configDrivenValue.trim().equalsIgnoreCase("")) {
|
||||
return defaultFallbackValue;
|
||||
} else {
|
||||
return configDrivenValue;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,33 @@
|
||||
package org.dspace.statistics;
|
||||
|
||||
import org.apache.log4j.Logger;
|
||||
import org.dspace.eperson.EPerson;
|
||||
import org.dspace.services.model.Event;
|
||||
import org.dspace.usage.AbstractUsageEventListener;
|
||||
import org.dspace.usage.UsageEvent;
|
||||
|
||||
public class ElasticSearchLoggerEventListener extends AbstractUsageEventListener {
|
||||
|
||||
private static Logger log = Logger.getLogger(ElasticSearchLoggerEventListener.class);
|
||||
|
||||
|
||||
public void receiveEvent(Event event) {
|
||||
|
||||
if(event instanceof UsageEvent)
|
||||
{
|
||||
try{
|
||||
|
||||
UsageEvent ue = (UsageEvent) event;
|
||||
|
||||
EPerson currentUser = ue.getContext() == null ? null : ue.getContext().getCurrentUser();
|
||||
|
||||
ElasticSearchLogger.getInstance().post(ue.getObject(), ue.getRequest(), currentUser);
|
||||
log.info("Successfully logged " + ue.getObject().getTypeText() + "_" + ue.getObject().getID() + " " + ue.getObject().getName());
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
log.error("General Exception: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -58,6 +58,24 @@
|
||||
<artifactId>yuicompressor</artifactId>
|
||||
<version>2.3.6</version>
|
||||
</dependency>
|
||||
<!-- Needed for Form Validation -->
|
||||
<dependency>
|
||||
<groupId>commons-validator</groupId>
|
||||
<artifactId>commons-validator</artifactId>
|
||||
<version>1.3.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.sf.opencsv</groupId>
|
||||
<artifactId>opencsv</artifactId>
|
||||
<version>2.0</version>
|
||||
</dependency>
|
||||
<!-- Gson: Java to Json conversion -->
|
||||
<dependency>
|
||||
<groupId>com.google.code.gson</groupId>
|
||||
<artifactId>gson</artifactId>
|
||||
<version>2.1</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
|
@@ -7,25 +7,35 @@
|
||||
*/
|
||||
package org.dspace.app.xmlui.aspect.statistics;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.sql.SQLException;
|
||||
import java.text.ParseException;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import org.apache.log4j.Logger;
|
||||
import org.apache.solr.client.solrj.SolrServerException;
|
||||
import org.dspace.app.xmlui.cocoon.AbstractDSpaceTransformer;
|
||||
import org.dspace.app.xmlui.utils.HandleUtil;
|
||||
import org.dspace.app.xmlui.utils.UIException;
|
||||
import org.dspace.app.xmlui.wing.WingException;
|
||||
import org.dspace.app.xmlui.wing.Message;
|
||||
import org.dspace.app.xmlui.wing.WingException;
|
||||
import org.dspace.app.xmlui.wing.element.*;
|
||||
import org.dspace.authorize.AuthorizeException;
|
||||
import org.dspace.content.DSpaceObject;
|
||||
import org.dspace.core.Constants;
|
||||
import org.dspace.core.Context;
|
||||
import org.dspace.statistics.Dataset;
|
||||
import org.dspace.statistics.ObjectCount;
|
||||
import org.dspace.statistics.content.*;
|
||||
import org.dspace.storage.rdbms.DatabaseManager;
|
||||
import org.dspace.storage.rdbms.TableRow;
|
||||
import org.dspace.storage.rdbms.TableRowIterator;
|
||||
import org.xml.sax.SAXException;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.sql.SQLException;
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.Locale;
|
||||
|
||||
public class StatisticsTransformer extends AbstractDSpaceTransformer {
|
||||
|
||||
private static Logger log = Logger.getLogger(StatisticsTransformer.class);
|
||||
@@ -40,6 +50,30 @@ public class StatisticsTransformer extends AbstractDSpaceTransformer {
|
||||
private static final String T_head_visits_cities = "xmlui.statistics.visits.cities";
|
||||
private static final String T_head_visits_bitstream = "xmlui.statistics.visits.bitstreams";
|
||||
|
||||
private Date dateStart = null;
|
||||
private Date dateEnd = null;
|
||||
|
||||
private SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
|
||||
|
||||
public StatisticsTransformer(Date dateStart, Date dateEnd) {
|
||||
this.dateStart = dateStart;
|
||||
this.dateEnd = dateEnd;
|
||||
|
||||
try {
|
||||
this.context = new Context();
|
||||
} catch (SQLException e) {
|
||||
log.error("Error getting context in StatisticsTransformer:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public StatisticsTransformer() {
|
||||
try {
|
||||
this.context = new Context();
|
||||
} catch (SQLException e) {
|
||||
log.error("Error getting context in StatisticsTransformer:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a page title and trail links
|
||||
*/
|
||||
@@ -387,4 +421,348 @@ public class StatisticsTransformer extends AbstractDSpaceTransformer {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public java.util.List<TableRow> addItemsInContainer(DSpaceObject dso) {
|
||||
java.util.List<TableRow> tableRowList = null;
|
||||
|
||||
String typeTextLower = "";
|
||||
if(dso.getType() == Constants.COMMUNITY) {
|
||||
typeTextLower = "communities";
|
||||
} else {
|
||||
typeTextLower = dso.getTypeText().toLowerCase();
|
||||
}
|
||||
|
||||
String querySpecifyContainer = "SELECT to_char(date_trunc('month', t1.ts), 'YYYY-MM') AS yearmo, count(*) as countitem " +
|
||||
"FROM ( SELECT to_timestamp(text_value, 'YYYY-MM-DD') AS ts FROM metadatavalue, item, " +
|
||||
typeTextLower + "2item " +
|
||||
"WHERE metadata_field_id = 12 AND metadatavalue.item_id = item.item_id AND item.in_archive=true AND "+
|
||||
typeTextLower + "2item.item_id = item.item_id AND "+
|
||||
typeTextLower + "2item." + dso.getTypeText().toLowerCase() +"_id = ? ";
|
||||
|
||||
if (dateStart != null) {
|
||||
String start = dateFormat.format(dateStart);
|
||||
querySpecifyContainer += "AND metadatavalue.text_value > '"+start+"'";
|
||||
}
|
||||
if(dateEnd != null) {
|
||||
String end = dateFormat.format(dateEnd);
|
||||
querySpecifyContainer += " AND metadatavalue.text_value < '"+end+"' ";
|
||||
}
|
||||
|
||||
querySpecifyContainer += ") t1 GROUP BY date_trunc('month', t1.ts) order by yearmo asc";
|
||||
|
||||
try {
|
||||
TableRowIterator tri;
|
||||
tri = DatabaseManager.query(context, querySpecifyContainer, dso.getID());
|
||||
|
||||
tableRowList = tri.toList();
|
||||
return tableRowList;
|
||||
} catch (Exception e) {
|
||||
|
||||
}
|
||||
return tableRowList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Only call this on a container object (collection or community).
|
||||
* @param dso
|
||||
* @param division
|
||||
*/
|
||||
public void addItemsInContainer(DSpaceObject dso, Division division) {
|
||||
java.util.List<TableRow> tableRowList = addItemsInContainer(dso);
|
||||
|
||||
Gson gson = new Gson();
|
||||
try {
|
||||
division.addHidden("gson-itemsAdded").setValue(gson.toJson(tableRowList));
|
||||
|
||||
Integer[][] monthlyDataGrid = convertTableRowListToIntegerGrid(tableRowList, "yearmo", "countitem");
|
||||
displayAsGrid(division, monthlyDataGrid, "itemsAddedGrid", "Number of Items Added to the " + dso.getName());
|
||||
} catch (WingException e) {
|
||||
log.error(e.getMessage()); //To change body of catch statement use File | Settings | File Templates.
|
||||
}
|
||||
}
|
||||
|
||||
public Integer[][] convertTableRowListToIntegerGrid(java.util.List<TableRow> tableRowList, String dateColumn, String valueColumn) {
|
||||
if(tableRowList == null || tableRowList.size() == 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// If we have a requested time-range for data, then we need to ensure we zero-fill the data during that time range.
|
||||
// Otherwise, if no requested time-range, then just make table size of data.
|
||||
// a year-mo is 2008-05
|
||||
Integer yearStart;
|
||||
Integer yearLast;
|
||||
|
||||
if(dateStart != null) {
|
||||
Calendar myTime = Calendar.getInstance();
|
||||
myTime.setTime(dateStart);
|
||||
yearStart = myTime.get(Calendar.YEAR);
|
||||
} else {
|
||||
String yearmoStart = tableRowList.get(0).getStringColumn(dateColumn);
|
||||
yearStart = Integer.valueOf(yearmoStart.split("-")[0]);
|
||||
}
|
||||
|
||||
if(dateEnd != null) {
|
||||
Calendar myTime = Calendar.getInstance();
|
||||
myTime.setTime(dateEnd);
|
||||
yearLast = myTime.get(Calendar.YEAR);
|
||||
} else {
|
||||
String yearmoLast = tableRowList.get(tableRowList.size()-1).getStringColumn(dateColumn);
|
||||
yearLast = Integer.valueOf(yearmoLast.split("-")[0]);
|
||||
}
|
||||
// distinctBetween(2011, 2005) = 7
|
||||
int distinctNumberOfYears = yearLast-yearStart+1;
|
||||
|
||||
/**
|
||||
* monthlyDataGrid will hold all the years down, and the year number, as well as monthly values, plus total across.
|
||||
*/
|
||||
Integer columns = 1 + 12 + 1 + 1;
|
||||
|
||||
Integer[][] monthlyDataGrid = new Integer[distinctNumberOfYears][columns];
|
||||
|
||||
//Initialize all to zero
|
||||
for(int yearIndex = 0; yearIndex < distinctNumberOfYears; yearIndex++) {
|
||||
monthlyDataGrid[yearIndex][0] = yearStart+yearIndex;
|
||||
for(int dataColumnIndex = 1; dataColumnIndex < columns; dataColumnIndex++) {
|
||||
monthlyDataGrid[yearIndex][dataColumnIndex] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
for(TableRow monthRow: tableRowList) {
|
||||
String yearmo = monthRow.getStringColumn(dateColumn);
|
||||
|
||||
String[] yearMonthSplit = yearmo.split("-");
|
||||
Integer currentYear = Integer.parseInt(yearMonthSplit[0]);
|
||||
Integer currentMonth = Integer.parseInt(yearMonthSplit[1]);
|
||||
|
||||
long monthlyHits = monthRow.getLongColumn(valueColumn);
|
||||
|
||||
monthlyDataGrid[currentYear-yearStart][currentMonth] = (int) monthlyHits;
|
||||
}
|
||||
|
||||
// Fill first column with year name. And, fill in last column with cumulative annual total.
|
||||
for(int yearIndex = 0; yearIndex < distinctNumberOfYears; yearIndex++) {
|
||||
Integer yearCumulative=0;
|
||||
for(int monthIndex = 1; monthIndex <= 12; monthIndex++) {
|
||||
yearCumulative += monthlyDataGrid[yearIndex][monthIndex];
|
||||
}
|
||||
|
||||
monthlyDataGrid[yearIndex][13] = yearCumulative;
|
||||
if(yearIndex == 0) {
|
||||
monthlyDataGrid[yearIndex][14] = yearCumulative;
|
||||
} else {
|
||||
monthlyDataGrid[yearIndex][14] = yearCumulative + monthlyDataGrid[yearIndex-1][14];
|
||||
}
|
||||
}
|
||||
return monthlyDataGrid;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Standard conversion of input date, where input is "Month Year", i.e. "December 2011", i.e. "MMMM yyyy".
|
||||
* If you need a different date format, then use the other method.
|
||||
* @param objectCounts
|
||||
* @return
|
||||
* @throws ParseException
|
||||
*/
|
||||
public Integer[][] convertObjectCountsToIntegerGrid(ObjectCount[] objectCounts) throws ParseException {
|
||||
SimpleDateFormat dateFormat = new SimpleDateFormat("MMMM yyyy", Locale.getDefault());
|
||||
return convertObjectCountsToIntegerGrid(objectCounts, dateFormat);
|
||||
}
|
||||
|
||||
public Integer[][] convertObjectCountsToIntegerGrid(ObjectCount[] objectCounts, SimpleDateFormat dateFormat) throws ParseException{
|
||||
|
||||
Calendar calendar = Calendar.getInstance();
|
||||
|
||||
|
||||
|
||||
Date date;
|
||||
|
||||
date = dateFormat.parse(objectCounts[0].getValue());
|
||||
calendar.setTime(date);
|
||||
Integer yearStart = calendar.get(Calendar.YEAR);
|
||||
|
||||
date = dateFormat.parse(objectCounts[objectCounts.length-1].getValue());
|
||||
calendar.setTime(date);
|
||||
Integer yearLast = calendar.get(Calendar.YEAR);
|
||||
|
||||
int distinctNumberOfYears = yearLast-yearStart+1;
|
||||
|
||||
/**
|
||||
* monthlyDataGrid will hold all the years down, and the year number, as well as monthly values, plus total across.
|
||||
*/
|
||||
Integer columns = 1 + 12 + 1 + 1;
|
||||
Integer[][] monthlyDataGrid = new Integer[distinctNumberOfYears][columns];
|
||||
|
||||
//Initialize the dataGrid with yearName and blanks
|
||||
for(int yearIndex = 0; yearIndex < distinctNumberOfYears; yearIndex++) {
|
||||
monthlyDataGrid[yearIndex][0] = yearStart+yearIndex;
|
||||
for(int dataColumnIndex = 1; dataColumnIndex < columns; dataColumnIndex++) {
|
||||
monthlyDataGrid[yearIndex][dataColumnIndex] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
//Fill in monthly values
|
||||
for(ObjectCount objectCountMonth: objectCounts) {
|
||||
date = dateFormat.parse(objectCountMonth.getValue());
|
||||
calendar.setTime(date);
|
||||
|
||||
long monthlyHits = objectCountMonth.getCount();
|
||||
monthlyDataGrid[calendar.get(Calendar.YEAR)-yearStart][calendar.get(Calendar.MONTH)+1] = (int) monthlyHits;
|
||||
}
|
||||
|
||||
// Fill in last column with cumulative annual total.
|
||||
for(int yearIndex = 0; yearIndex < distinctNumberOfYears; yearIndex++) {
|
||||
Integer yearCumulative=0;
|
||||
for(int monthIndex = 1; monthIndex <= 12; monthIndex++) {
|
||||
yearCumulative += monthlyDataGrid[yearIndex][monthIndex];
|
||||
}
|
||||
|
||||
monthlyDataGrid[yearIndex][13] = yearCumulative;
|
||||
if(yearIndex == 0) {
|
||||
monthlyDataGrid[yearIndex][14] = yearCumulative;
|
||||
} else {
|
||||
monthlyDataGrid[yearIndex][14] = yearCumulative + monthlyDataGrid[yearIndex-1][14];
|
||||
}
|
||||
}
|
||||
return monthlyDataGrid;
|
||||
}
|
||||
|
||||
public void displayAsGrid(Division division, Integer[][] monthlyDataGrid, String name, String header) throws WingException {
|
||||
if(monthlyDataGrid == null || monthlyDataGrid.length == 0) {
|
||||
log.error("Grid has no data: "+ header);
|
||||
Table gridTable = division.addTable(name, 1,1);
|
||||
gridTable.setHead("No Data Available for " + header);
|
||||
Row gridHeader = gridTable.addRow(Row.ROLE_HEADER);
|
||||
gridHeader.addCell().addContent("Year");
|
||||
gridHeader.addCell().addContent("JAN");
|
||||
gridHeader.addCell().addContent("FEB");
|
||||
gridHeader.addCell().addContent("MAR");
|
||||
gridHeader.addCell().addContent("APR");
|
||||
gridHeader.addCell().addContent("MAY");
|
||||
gridHeader.addCell().addContent("JUN");
|
||||
gridHeader.addCell().addContent("JUL");
|
||||
gridHeader.addCell().addContent("AUG");
|
||||
gridHeader.addCell().addContent("SEP");
|
||||
gridHeader.addCell().addContent("OCT");
|
||||
gridHeader.addCell().addContent("NOV");
|
||||
gridHeader.addCell().addContent("DEC");
|
||||
gridHeader.addCell().addContent("Total YR");
|
||||
gridHeader.addCell().addContent("Total Cumulative");
|
||||
return;
|
||||
}
|
||||
|
||||
Integer yearStart = monthlyDataGrid[0][0];
|
||||
Integer yearLast = monthlyDataGrid[monthlyDataGrid.length-1][0];
|
||||
int numberOfYears = yearLast-yearStart;
|
||||
|
||||
Integer columns = 1 + 12 + 1 + 1;
|
||||
Table gridTable = division.addTable(name, numberOfYears+1, columns);
|
||||
gridTable.setHead(header);
|
||||
Row gridHeader = gridTable.addRow(Row.ROLE_HEADER);
|
||||
gridHeader.addCell().addContent("Year");
|
||||
gridHeader.addCell().addContent("JAN");
|
||||
gridHeader.addCell().addContent("FEB");
|
||||
gridHeader.addCell().addContent("MAR");
|
||||
gridHeader.addCell().addContent("APR");
|
||||
gridHeader.addCell().addContent("MAY");
|
||||
gridHeader.addCell().addContent("JUN");
|
||||
gridHeader.addCell().addContent("JUL");
|
||||
gridHeader.addCell().addContent("AUG");
|
||||
gridHeader.addCell().addContent("SEP");
|
||||
gridHeader.addCell().addContent("OCT");
|
||||
gridHeader.addCell().addContent("NOV");
|
||||
gridHeader.addCell().addContent("DEC");
|
||||
gridHeader.addCell().addContent("Total YR");
|
||||
gridHeader.addCell().addContent("Total Cumulative");
|
||||
|
||||
for(int yearIndex=0; yearIndex < monthlyDataGrid.length; yearIndex++) {
|
||||
Row yearRow = gridTable.addRow();
|
||||
yearRow.addCell(Cell.ROLE_HEADER).addContent(monthlyDataGrid[yearIndex][0]);
|
||||
for(int yearContentIndex = 1; yearContentIndex<columns; yearContentIndex++) {
|
||||
yearRow.addCell().addContent(monthlyDataGrid[yearIndex][yearContentIndex]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void displayAsTableRows(Division division, java.util.List<TableRow> tableRowList, String title) throws WingException {
|
||||
Table table = division.addTable("itemsInContainer", tableRowList.size()+1, 3);
|
||||
table.setHead(title);
|
||||
|
||||
Row header = table.addRow(Row.ROLE_HEADER);
|
||||
header.addCell().addContent("Month");
|
||||
header.addCell().addContent("Added During Month");
|
||||
header.addCell().addContent("Total Cumulative");
|
||||
|
||||
int cumulativeHits = 0;
|
||||
for(TableRow row : tableRowList) {
|
||||
Row htmlRow = table.addRow(Row.ROLE_DATA);
|
||||
|
||||
String yearmo = row.getStringColumn("yearmo");
|
||||
htmlRow.addCell().addContent(yearmo);
|
||||
|
||||
long monthlyHits = row.getLongColumn("countitem");
|
||||
htmlRow.addCell().addContent(""+monthlyHits);
|
||||
|
||||
cumulativeHits += monthlyHits;
|
||||
htmlRow.addCell().addContent(""+cumulativeHits);
|
||||
}
|
||||
}
|
||||
|
||||
public java.util.List<TableRow> addFilesInContainerQuery(DSpaceObject dso) {
|
||||
java.util.List<TableRow> tableRowList = null;
|
||||
|
||||
String typeTextLower = "";
|
||||
if(dso.getType() == Constants.COMMUNITY) {
|
||||
typeTextLower = "communities";
|
||||
} else {
|
||||
typeTextLower = dso.getTypeText().toLowerCase();
|
||||
}
|
||||
|
||||
String querySpecifyContainer = "SELECT to_char(date_trunc('month', t1.ts), 'YYYY-MM') AS yearmo, count(*) as countitem " +
|
||||
"FROM ( SELECT to_timestamp(text_value, 'YYYY-MM-DD') AS ts FROM metadatavalue, item, item2bundle, bundle, bundle2bitstream, " +
|
||||
typeTextLower + "2item " +
|
||||
"WHERE metadata_field_id = 12 AND metadatavalue.item_id = item.item_id AND item.in_archive=true AND " +
|
||||
"item2bundle.bundle_id = bundle.bundle_id AND item2bundle.item_id = item.item_id AND bundle.bundle_id = bundle2bitstream.bundle_id AND bundle.\"name\" = 'ORIGINAL' AND "+
|
||||
typeTextLower + "2item.item_id = item.item_id AND "+
|
||||
typeTextLower + "2item."+dso.getTypeText().toLowerCase()+"_id = ? ";
|
||||
|
||||
if (dateStart != null) {
|
||||
String start = dateFormat.format(dateStart);
|
||||
querySpecifyContainer += "AND metadatavalue.text_value > '"+start+"'";
|
||||
}
|
||||
if(dateEnd != null) {
|
||||
String end = dateFormat.format(dateEnd);
|
||||
querySpecifyContainer += " AND metadatavalue.text_value < '"+end+"' ";
|
||||
}
|
||||
|
||||
querySpecifyContainer += ") t1 GROUP BY date_trunc('month', t1.ts) order by yearmo asc";
|
||||
|
||||
try {
|
||||
TableRowIterator tri = DatabaseManager.query(context, querySpecifyContainer, dso.getID());
|
||||
|
||||
tableRowList = tri.toList();
|
||||
} catch (Exception e) {
|
||||
|
||||
}
|
||||
|
||||
return tableRowList;
|
||||
}
|
||||
|
||||
public void addFilesInContainer(DSpaceObject dso, Division division) {
|
||||
java.util.List<TableRow> tableRowList = addFilesInContainerQuery(dso);
|
||||
|
||||
Gson gson = new Gson();
|
||||
try {
|
||||
division.addHidden("gson-filesAdded").setValue(gson.toJson(tableRowList));
|
||||
|
||||
Integer[][] monthlyDataGrid = convertTableRowListToIntegerGrid(tableRowList, "yearmo", "countitem");
|
||||
|
||||
displayAsGrid(division, monthlyDataGrid, "filesInContainer-grid", "Number of Files in the "+dso.getName());
|
||||
//displayAsTableRows(division, tableRowList, "Number of Files in the "+getTypeAsString(dso));
|
||||
} catch (WingException e) {
|
||||
log.error(e.getMessage()); //To change body of catch statement use File | Settings | File Templates.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,256 @@
|
||||
package org.dspace.app.xmlui.aspect.statisticsElasticSearch;
|
||||
|
||||
import au.com.bytecode.opencsv.CSVWriter;
|
||||
import org.apache.avalon.excalibur.pool.Recyclable;
|
||||
import org.apache.avalon.framework.parameters.Parameters;
|
||||
import org.apache.cocoon.ProcessingException;
|
||||
import org.apache.cocoon.environment.ObjectModelHelper;
|
||||
import org.apache.cocoon.environment.Request;
|
||||
import org.apache.cocoon.environment.Response;
|
||||
import org.apache.cocoon.environment.SourceResolver;
|
||||
import org.apache.cocoon.reading.AbstractReader;
|
||||
import org.apache.log4j.Logger;
|
||||
import org.dspace.app.xmlui.aspect.statistics.StatisticsTransformer;
|
||||
import org.dspace.app.xmlui.utils.ContextUtil;
|
||||
import org.dspace.app.xmlui.utils.HandleUtil;
|
||||
import org.dspace.app.xmlui.wing.WingException;
|
||||
import org.dspace.content.Bitstream;
|
||||
import org.dspace.content.DCValue;
|
||||
import org.dspace.content.DSpaceObject;
|
||||
import org.dspace.content.Item;
|
||||
import org.dspace.core.Context;
|
||||
import org.dspace.storage.rdbms.TableRow;
|
||||
import org.elasticsearch.action.search.SearchResponse;
|
||||
import org.elasticsearch.client.action.search.SearchRequestBuilder;
|
||||
import org.elasticsearch.search.facet.datehistogram.DateHistogramFacet;
|
||||
import org.elasticsearch.search.facet.terms.TermsFacet;
|
||||
import org.xml.sax.SAXException;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
import java.sql.SQLException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Created by IntelliJ IDEA.
|
||||
* User: peterdietz
|
||||
* Date: 4/20/12
|
||||
* Time: 3:28 PM
|
||||
* To change this template use File | Settings | File Templates.
|
||||
*/
|
||||
public class CSVOutputter extends AbstractReader implements Recyclable
|
||||
{
|
||||
protected static final Logger log = Logger.getLogger(CSVOutputter.class);
|
||||
private static SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
|
||||
|
||||
protected Response response;
|
||||
protected Request request;
|
||||
protected Context context;
|
||||
protected CSVWriter writer = null;
|
||||
|
||||
public void setup(SourceResolver sourceResolver, Map objectModel, String src, Parameters parameters) throws IOException, SAXException, ProcessingException {
|
||||
log.info("CSV Writer for stats");
|
||||
super.setup(sourceResolver, objectModel, src, parameters);
|
||||
|
||||
try {
|
||||
//super.setup(sourceResolver, objectModel, src, parameters);
|
||||
this.request = ObjectModelHelper.getRequest(objectModel);
|
||||
this.response = ObjectModelHelper.getResponse(objectModel);
|
||||
|
||||
context = ContextUtil.obtainContext(objectModel);
|
||||
|
||||
String requestURI = request.getRequestURI();
|
||||
String[] uriSegments = requestURI.split("/");
|
||||
String requestedReport = uriSegments[uriSegments.length-1];
|
||||
if(requestedReport == null || requestedReport.length() < 1) {
|
||||
response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
|
||||
return;
|
||||
}
|
||||
|
||||
response.setContentType("text/csv; encoding='UTF-8'");
|
||||
response.setStatus(HttpServletResponse.SC_OK);
|
||||
writer = new CSVWriter(response.getWriter());
|
||||
DSpaceObject dso = HandleUtil.obtainHandle(objectModel);
|
||||
response.setHeader("Content-Disposition", "attachment; filename=KBStats-" + dso.getHandle() + "-" + requestedReport +".csv");
|
||||
|
||||
Map<String, String> params = new HashMap<String, String>();
|
||||
for (Enumeration<String> paramNames = (Enumeration<String>) request.getParameterNames(); paramNames.hasMoreElements(); ) {
|
||||
String param = paramNames.nextElement();
|
||||
params.put(param, request.getParameter(param));
|
||||
}
|
||||
|
||||
String fromValue = "";
|
||||
if(params.containsKey("from")) {
|
||||
fromValue = params.get("from");
|
||||
}
|
||||
|
||||
String toValue = "";
|
||||
if(params.containsKey("to")) {
|
||||
toValue = params.get("to");
|
||||
}
|
||||
|
||||
Date fromDate;
|
||||
ReportGenerator reportGeneratorInstance = new ReportGenerator();
|
||||
|
||||
if(fromValue.length() > 0) {
|
||||
fromDate = reportGeneratorInstance.tryParse(fromValue);
|
||||
} else {
|
||||
fromDate = null;
|
||||
}
|
||||
|
||||
Date toDate;
|
||||
if(toValue.length() > 0) {
|
||||
toDate = reportGeneratorInstance.tryParse(toValue);
|
||||
} else {
|
||||
toDate = null;
|
||||
}
|
||||
|
||||
ElasticSearchStatsViewer esStatsViewer = new ElasticSearchStatsViewer(dso, fromDate, toDate);
|
||||
StatisticsTransformer statisticsTransformerInstance = new StatisticsTransformer(fromDate, toDate);
|
||||
|
||||
if(requestedReport.equalsIgnoreCase("topCountries"))
|
||||
{
|
||||
SearchRequestBuilder requestBuilder = esStatsViewer.facetedQueryBuilder(esStatsViewer.facetTopCountries);
|
||||
SearchResponse searchResponse = requestBuilder.execute().actionGet();
|
||||
|
||||
TermsFacet topCountriesFacet = searchResponse.getFacets().facet(TermsFacet.class, "top_countries");
|
||||
addTermFacetToWriter(topCountriesFacet, "");
|
||||
}
|
||||
else if (requestedReport.equalsIgnoreCase("fileDownloads"))
|
||||
{
|
||||
SearchRequestBuilder requestBuilder = esStatsViewer.facetedQueryBuilder(esStatsViewer.facetMonthlyDownloads);
|
||||
SearchResponse searchResponse = requestBuilder.execute().actionGet();
|
||||
|
||||
DateHistogramFacet monthlyDownloadsFacet = searchResponse.getFacets().facet(DateHistogramFacet.class, "monthly_downloads");
|
||||
addDateHistogramFacetToWriter(monthlyDownloadsFacet);
|
||||
}
|
||||
else if (requestedReport.equalsIgnoreCase("itemsAdded"))
|
||||
{
|
||||
// 1 - Number of Items in The Container (Community/Collection) (monthly and cumulative for the year)
|
||||
writer.writeNext(new String[]{"Date", "Items Added"});
|
||||
List<TableRow> tableRowList = statisticsTransformerInstance.addItemsInContainer(dso);
|
||||
for(TableRow row: tableRowList) {
|
||||
writer.writeNext(new String[]{row.getStringColumn("yearmo"), row.getLongColumn("countitem") + ""});
|
||||
}
|
||||
}
|
||||
else if(requestedReport.equalsIgnoreCase("filesAdded"))
|
||||
{
|
||||
// 2 - Number of Files in The Container (monthly and cumulative)
|
||||
writer.writeNext(new String[]{"Date", "Files Added"});
|
||||
List<TableRow> tableRowList = statisticsTransformerInstance.addFilesInContainerQuery(dso);
|
||||
for(TableRow row: tableRowList) {
|
||||
writer.writeNext(new String[]{row.getStringColumn("yearmo"), row.getLongColumn("countitem") + ""});
|
||||
}
|
||||
}
|
||||
else if(requestedReport.equalsIgnoreCase("topDownloads"))
|
||||
{
|
||||
SearchRequestBuilder requestBuilder = esStatsViewer.facetedQueryBuilder(esStatsViewer.facetTopBitstreamsAllTime);
|
||||
SearchResponse searchResponse = requestBuilder.execute().actionGet();
|
||||
log.info(searchResponse.toString());
|
||||
|
||||
TermsFacet topBitstreams = searchResponse.getFacets().facet(TermsFacet.class, "top_bitstreams_alltime");
|
||||
addTermFacetToWriter(topBitstreams, "bitstream");
|
||||
}
|
||||
else {
|
||||
response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
|
||||
}
|
||||
|
||||
} catch (SQLException e) {
|
||||
log.error("Some Error:" + e.getMessage());
|
||||
} catch (WingException e) {
|
||||
log.error("Some Error:" + e.getMessage());
|
||||
} catch (IOException e) {
|
||||
log.error("Some Error:" + e.getMessage());
|
||||
} finally {
|
||||
try {
|
||||
if(writer != null) {
|
||||
writer.close();
|
||||
} else {
|
||||
log.error("CSV Writer was null!!");
|
||||
}
|
||||
} catch (IOException e) {
|
||||
log.error("Hilarity Ensues... IO Exception while closing the csv writer.");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private void addTermFacetToWriter(TermsFacet termsFacet, String termType) throws SQLException {
|
||||
List<? extends TermsFacet.Entry> termsFacetEntries = termsFacet.getEntries();
|
||||
|
||||
if(termType.equalsIgnoreCase("bitstream")) {
|
||||
writer.writeNext(new String[]{"BitstreamID", "Bitstream Name", "Bitstream Bundle", "Item Title", "Item Handle", "Item Creator", "Item Publisher", "Item Issue Date", "Count"});
|
||||
} else {
|
||||
writer.writeNext(new String[]{"term", "count"});
|
||||
}
|
||||
if(termsFacetEntries.size() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
for(TermsFacet.Entry facetEntry : termsFacetEntries)
|
||||
{
|
||||
if(termType.equalsIgnoreCase("bitstream"))
|
||||
{
|
||||
Bitstream bitstream = Bitstream.find(context, Integer.parseInt(facetEntry.getTerm()));
|
||||
Item item = (Item) bitstream.getParentObject();
|
||||
|
||||
String[] entryValues = new String[9];
|
||||
|
||||
entryValues[0] = bitstream.getID() + "";
|
||||
entryValues[1] = bitstream.getName();
|
||||
entryValues[2] = bitstream.getBundles()[0].getName();
|
||||
entryValues[3] = item.getName();
|
||||
entryValues[4] = "http://hdl.handle.net/" + item.getHandle();
|
||||
entryValues[5] = wrapInDelimitedString(item.getMetadata("dc.creator"));
|
||||
entryValues[6] = wrapInDelimitedString(item.getMetadata("dc.publisher"));
|
||||
entryValues[7] = wrapInDelimitedString(item.getMetadata("dc.date.issued"));
|
||||
entryValues[8] = facetEntry.getCount() + "";
|
||||
writer.writeNext(entryValues);
|
||||
} else {
|
||||
writer.writeNext(new String[]{facetEntry.getTerm(), String.valueOf(facetEntry.getCount())});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public String wrapInDelimitedString(DCValue[] metadataEntries) {
|
||||
StringBuilder metadataString = new StringBuilder();
|
||||
|
||||
for(DCValue metadataEntry : metadataEntries) {
|
||||
if(metadataString.length() > 0) {
|
||||
// Delimit entries with the || double pipe character sequence.
|
||||
metadataString.append("\\|\\|");
|
||||
}
|
||||
metadataString.append(metadataEntry.value);
|
||||
}
|
||||
|
||||
return metadataString.toString();
|
||||
}
|
||||
|
||||
private void addDateHistogramFacetToWriter(DateHistogramFacet dateHistogramFacet) {
|
||||
List<? extends DateHistogramFacet.Entry> monthlyFacetEntries = dateHistogramFacet.getEntries();
|
||||
|
||||
if(monthlyFacetEntries.size() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
writer.writeNext(new String[]{"Month", "Count"});
|
||||
|
||||
for(DateHistogramFacet.Entry histogramEntry : monthlyFacetEntries) {
|
||||
Date facetDate = new Date(histogramEntry.getTime());
|
||||
writer.writeNext(new String[]{dateFormat.format(facetDate), String.valueOf(histogramEntry.getCount())});
|
||||
}
|
||||
}
|
||||
|
||||
public void generate() throws IOException {
|
||||
log.info("CSV Writer generator for stats");
|
||||
out.flush();
|
||||
out.close();
|
||||
}
|
||||
|
||||
public void recycle() {
|
||||
this.request = null;
|
||||
this.response = null;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,436 @@
|
||||
package org.dspace.app.xmlui.aspect.statisticsElasticSearch;
|
||||
|
||||
import org.apache.cocoon.environment.ObjectModelHelper;
|
||||
import org.apache.cocoon.environment.Request;
|
||||
import org.apache.log4j.Logger;
|
||||
import org.dspace.app.xmlui.aspect.statistics.StatisticsTransformer;
|
||||
import org.dspace.app.xmlui.cocoon.AbstractDSpaceTransformer;
|
||||
import org.dspace.app.xmlui.utils.HandleUtil;
|
||||
import org.dspace.app.xmlui.wing.Message;
|
||||
import org.dspace.app.xmlui.wing.WingException;
|
||||
import org.dspace.app.xmlui.wing.element.*;
|
||||
import org.dspace.content.*;
|
||||
import org.dspace.content.Item;
|
||||
import org.dspace.core.Constants;
|
||||
import org.dspace.statistics.ElasticSearchLogger;
|
||||
import org.elasticsearch.action.search.SearchResponse;
|
||||
import org.elasticsearch.action.search.SearchType;
|
||||
import org.elasticsearch.client.Client;
|
||||
import org.elasticsearch.client.action.search.SearchRequestBuilder;
|
||||
import org.elasticsearch.index.query.*;
|
||||
import org.elasticsearch.search.facet.AbstractFacetBuilder;
|
||||
import org.elasticsearch.search.facet.FacetBuilders;
|
||||
import org.elasticsearch.search.facet.datehistogram.DateHistogramFacet;
|
||||
import org.elasticsearch.search.facet.terms.TermsFacet;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.*;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Created by IntelliJ IDEA.
|
||||
* User: peterdietz
|
||||
* Date: 3/7/12
|
||||
* Time: 11:54 AM
|
||||
* To change this template use File | Settings | File Templates.
|
||||
*/
|
||||
public class ElasticSearchStatsViewer extends AbstractDSpaceTransformer {
|
||||
private static Logger log = Logger.getLogger(ElasticSearchStatsViewer.class);
|
||||
|
||||
public static final String elasticStatisticsPath = "stats";
|
||||
|
||||
private static SimpleDateFormat monthAndYearFormat = new SimpleDateFormat("MMMMM yyyy");
|
||||
private static SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
|
||||
|
||||
private static Client client;
|
||||
private static Division division;
|
||||
private static DSpaceObject dso;
|
||||
private static Date dateStart;
|
||||
private static Date dateEnd;
|
||||
|
||||
protected static TermFilterBuilder justOriginals = FilterBuilders.termFilter("bundleName", "ORIGINAL");
|
||||
|
||||
protected static AbstractFacetBuilder facetTopCountries = FacetBuilders.termsFacet("top_countries").field("country.untouched").size(150)
|
||||
.facetFilter(FilterBuilders.andFilter(
|
||||
justOriginals,
|
||||
FilterBuilders.notFilter(FilterBuilders.termFilter("country.untouched", "")))
|
||||
);
|
||||
|
||||
protected static AbstractFacetBuilder facetMonthlyDownloads = FacetBuilders.dateHistogramFacet("monthly_downloads").field("time").interval("month")
|
||||
.facetFilter(FilterBuilders.andFilter(
|
||||
FilterBuilders.termFilter("type", "BITSTREAM"),
|
||||
justOriginals
|
||||
));
|
||||
|
||||
protected static AbstractFacetBuilder facetTopBitstreamsAllTime = FacetBuilders.termsFacet("top_bitstreams_alltime").field("id")
|
||||
.facetFilter(FilterBuilders.andFilter(
|
||||
FilterBuilders.termFilter("type", "BITSTREAM"),
|
||||
justOriginals
|
||||
));
|
||||
|
||||
protected static AbstractFacetBuilder facetTopUSCities = FacetBuilders.termsFacet("top_US_cities").field("city.untouched").size(50)
|
||||
.facetFilter(FilterBuilders.andFilter(
|
||||
FilterBuilders.termFilter("countryCode", "US"),
|
||||
justOriginals,
|
||||
FilterBuilders.notFilter(FilterBuilders.termFilter("city.untouched", ""))
|
||||
));
|
||||
|
||||
protected static AbstractFacetBuilder facetTopUniqueIP = FacetBuilders.termsFacet("top_unique_ips").field("ip");
|
||||
|
||||
protected static AbstractFacetBuilder facetTopTypes = FacetBuilders.termsFacet("top_types").field("type");
|
||||
|
||||
/** Language strings */
|
||||
private static final Message T_dspace_home = message("xmlui.general.dspace_home");
|
||||
|
||||
private static final Message T_trail = message("xmlui.ArtifactBrowser.ItemViewer.trail");
|
||||
|
||||
public void addPageMeta(PageMeta pageMeta) throws WingException, SQLException {
|
||||
DSpaceObject dso = HandleUtil.obtainHandle(objectModel);
|
||||
|
||||
pageMeta.addMetadata("title").addContent("Statistics Report for : " + dso.getName());
|
||||
|
||||
pageMeta.addTrailLink(contextPath + "/",T_dspace_home);
|
||||
HandleUtil.buildHandleTrail(dso,pageMeta,contextPath);
|
||||
pageMeta.addTrail().addContent("View Statistics");
|
||||
}
|
||||
|
||||
public ElasticSearchStatsViewer() {
|
||||
|
||||
}
|
||||
|
||||
public ElasticSearchStatsViewer(DSpaceObject dso, Date dateStart, Date dateEnd) {
|
||||
this.dso = dso;
|
||||
this.dateStart = dateStart;
|
||||
this.dateEnd = dateEnd;
|
||||
client = ElasticSearchLogger.getInstance().getClient();
|
||||
}
|
||||
|
||||
public void addBody(Body body) throws WingException, SQLException {
|
||||
try {
|
||||
//Try to find our dspace object
|
||||
dso = HandleUtil.obtainHandle(objectModel);
|
||||
client = ElasticSearchLogger.getInstance().getClient();
|
||||
|
||||
division = body.addDivision("elastic-stats");
|
||||
division.setHead("Statistical Report for " + dso.getName());
|
||||
division.addHidden("containerName").setValue(dso.getName());
|
||||
|
||||
division.addHidden("baseURLStats").setValue(contextPath + "/handle/" + dso.getHandle() + "/" + elasticStatisticsPath);
|
||||
Request request = ObjectModelHelper.getRequest(objectModel);
|
||||
String[] requestURIElements = request.getRequestURI().split("/");
|
||||
|
||||
// If we are on the homepage of the statistics portal, then we just show the summary report
|
||||
// Otherwise we will show a form to let user enter more information for deeper detail.
|
||||
if(requestURIElements[requestURIElements.length-1].trim().equalsIgnoreCase(elasticStatisticsPath)) {
|
||||
//Homepage will show the last 5 years worth of Data, and no form generator.
|
||||
Calendar cal = Calendar.getInstance();
|
||||
dateEnd = cal.getTime();
|
||||
|
||||
//Roll back to Jan 1 0:00.000 five years ago.
|
||||
cal.roll(Calendar.YEAR, -5);
|
||||
cal.set(Calendar.MONTH, 0);
|
||||
cal.set(Calendar.DAY_OF_MONTH, 1);
|
||||
cal.set(Calendar.HOUR_OF_DAY,0);
|
||||
cal.set(Calendar.MINUTE, 0);
|
||||
cal.set(Calendar.SECOND, 0);
|
||||
cal.set(Calendar.MILLISECOND, 0);
|
||||
dateStart = cal.getTime();
|
||||
|
||||
division.addHidden("reportDepth").setValue("summary");
|
||||
String dateRange = "Last Five Years";
|
||||
division.addPara("Showing Data ( " + dateRange + " )");
|
||||
division.addHidden("timeRangeString").setValue("Data Range: " + dateRange);
|
||||
if(dateStart != null) {
|
||||
division.addHidden("dateStart").setValue(dateFormat.format(dateStart));
|
||||
}
|
||||
if(dateEnd != null) {
|
||||
division.addHidden("dateEnd").setValue(dateFormat.format(dateEnd));
|
||||
}
|
||||
|
||||
showAllReports();
|
||||
|
||||
} else {
|
||||
//Other pages will show a form to choose which date range.
|
||||
ReportGenerator reportGenerator = new ReportGenerator();
|
||||
reportGenerator.addReportGeneratorForm(division, request);
|
||||
|
||||
dateStart = reportGenerator.getDateStart();
|
||||
dateEnd = reportGenerator.getDateEnd();
|
||||
|
||||
String requestedReport = requestURIElements[requestURIElements.length-1];
|
||||
log.info("Requested report is: "+ requestedReport);
|
||||
division.addHidden("reportDepth").setValue("detail");
|
||||
|
||||
String dateRange = "";
|
||||
if(dateStart != null && dateEnd != null) {
|
||||
dateRange = "from: "+dateFormat.format(dateStart) + " to: "+dateFormat.format(dateEnd);
|
||||
} else if (dateStart != null && dateEnd == null) {
|
||||
dateRange = "starting from: "+dateFormat.format(dateStart);
|
||||
} else if(dateStart == null && dateEnd != null) {
|
||||
dateRange = "ending with: "+dateFormat.format(dateEnd);
|
||||
} else if(dateStart == null && dateEnd == null) {
|
||||
dateRange = "All Data Available";
|
||||
}
|
||||
division.addPara("Showing Data ( " + dateRange + " )");
|
||||
division.addHidden("timeRangeString").setValue(dateRange);
|
||||
if(dateStart != null) {
|
||||
division.addHidden("dateStart").setValue(dateFormat.format(dateStart));
|
||||
}
|
||||
if(dateEnd != null) {
|
||||
division.addHidden("dateEnd").setValue(dateFormat.format(dateEnd));
|
||||
}
|
||||
|
||||
|
||||
division.addHidden("reportName").setValue(requestedReport);
|
||||
|
||||
if(requestedReport.equalsIgnoreCase("topCountries"))
|
||||
{
|
||||
SearchRequestBuilder requestBuilder = facetedQueryBuilder(facetTopCountries, facetTopUSCities);
|
||||
searchResponseToDRI(requestBuilder);
|
||||
}
|
||||
else if(requestedReport.equalsIgnoreCase("fileDownloads"))
|
||||
{
|
||||
SearchRequestBuilder requestBuilder = facetedQueryBuilder(facetMonthlyDownloads);
|
||||
searchResponseToDRI(requestBuilder);
|
||||
}
|
||||
else if(requestedReport.equalsIgnoreCase("topDownloads"))
|
||||
{
|
||||
SearchRequestBuilder requestBuilder = facetedQueryBuilder(facetTopBitstreamsAllTime, facetTopBitstreamsLastMonth());
|
||||
SearchResponse resp = searchResponseToDRI(requestBuilder);
|
||||
|
||||
TermsFacet bitstreamsAllTimeFacet = resp.getFacets().facet(TermsFacet.class, "top_bitstreams_alltime");
|
||||
addTermFacetToTable(bitstreamsAllTimeFacet, division, "Bitstream", "Top Downloads (all time)");
|
||||
|
||||
TermsFacet bitstreamsFacet = resp.getFacets().facet(TermsFacet.class, "top_bitstreams_lastmonth");
|
||||
addTermFacetToTable(bitstreamsFacet, division, "Bitstream", "Top Downloads for " + getLastMonthString());
|
||||
}
|
||||
else if(requestedReport.equalsIgnoreCase("itemsAdded"))
|
||||
{
|
||||
StatisticsTransformer statisticsTransformerInstance = new StatisticsTransformer(dateStart, dateEnd);
|
||||
|
||||
// 1 - Number of Items in The Container (Community/Collection) (monthly and cumulative for the year)
|
||||
if(dso instanceof org.dspace.content.Collection || dso instanceof Community) {
|
||||
statisticsTransformerInstance.addItemsInContainer(dso, division);
|
||||
division.addDivision("chart_div");
|
||||
}
|
||||
}
|
||||
else if(requestedReport.equalsIgnoreCase("filesAdded"))
|
||||
{
|
||||
StatisticsTransformer statisticsTransformerInstance = new StatisticsTransformer(dateStart, dateEnd);
|
||||
// 2 - Number of Files in The Container (monthly and cumulative)
|
||||
if(dso instanceof org.dspace.content.Collection || dso instanceof Community) {
|
||||
statisticsTransformerInstance.addFilesInContainer(dso, division);
|
||||
division.addDivision("chart_div");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} finally {
|
||||
//client.close();
|
||||
}
|
||||
}
|
||||
|
||||
public void showAllReports() throws WingException, SQLException{
|
||||
// Show some non-usage-stats.
|
||||
// @TODO Refactor the non-usage stats out of the StatsTransformer
|
||||
StatisticsTransformer statisticsTransformerInstance = new StatisticsTransformer(dateStart, dateEnd);
|
||||
|
||||
// 1 - Number of Items in The Container (Community/Collection) (monthly and cumulative for the year)
|
||||
if(dso instanceof org.dspace.content.Collection || dso instanceof Community) {
|
||||
statisticsTransformerInstance.addItemsInContainer(dso, division);
|
||||
}
|
||||
|
||||
// 2 - Number of Files in The Container (monthly and cumulative)
|
||||
if(dso instanceof org.dspace.content.Collection || dso instanceof Community) {
|
||||
statisticsTransformerInstance.addFilesInContainer(dso, division);
|
||||
}
|
||||
|
||||
List<AbstractFacetBuilder> summaryFacets = new ArrayList<AbstractFacetBuilder>();
|
||||
summaryFacets.add(facetTopTypes);
|
||||
summaryFacets.add(facetTopUniqueIP);
|
||||
summaryFacets.add(facetTopCountries);
|
||||
summaryFacets.add(facetTopUSCities);
|
||||
summaryFacets.add(facetTopBitstreamsLastMonth());
|
||||
summaryFacets.add(facetTopBitstreamsAllTime);
|
||||
summaryFacets.add(facetMonthlyDownloads);
|
||||
|
||||
SearchRequestBuilder requestBuilder = facetedQueryBuilder(summaryFacets);
|
||||
SearchResponse resp = searchResponseToDRI(requestBuilder);
|
||||
|
||||
// Top Downloads to Owning Object
|
||||
TermsFacet bitstreamsFacet = resp.getFacets().facet(TermsFacet.class, "top_bitstreams_lastmonth");
|
||||
addTermFacetToTable(bitstreamsFacet, division, "Bitstream", "Top Downloads for " + getLastMonthString());
|
||||
}
|
||||
|
||||
public AbstractFacetBuilder facetTopBitstreamsLastMonth() {
|
||||
Calendar calendar = Calendar.getInstance();
|
||||
|
||||
// Show Previous Whole Month
|
||||
calendar.add(Calendar.MONTH, -1);
|
||||
|
||||
calendar.set(Calendar.DAY_OF_MONTH, calendar.getActualMinimum(Calendar.DAY_OF_MONTH));
|
||||
String lowerBound = dateFormat.format(calendar.getTime());
|
||||
|
||||
calendar.set(Calendar.DAY_OF_MONTH, calendar.getActualMaximum(Calendar.DAY_OF_MONTH));
|
||||
String upperBound = dateFormat.format(calendar.getTime());
|
||||
|
||||
log.info("Lower:"+lowerBound+" -- Upper:"+upperBound);
|
||||
|
||||
return FacetBuilders.termsFacet("top_bitstreams_lastmonth").field("id")
|
||||
.facetFilter(FilterBuilders.andFilter(
|
||||
FilterBuilders.termFilter("type", "BITSTREAM"),
|
||||
justOriginals,
|
||||
FilterBuilders.rangeFilter("time").from(lowerBound).to(upperBound)
|
||||
));
|
||||
}
|
||||
|
||||
public String getLastMonthString() {
|
||||
Calendar calendar = Calendar.getInstance();
|
||||
// Show Previous Whole Month
|
||||
calendar.add(Calendar.MONTH, -1);
|
||||
|
||||
calendar.set(Calendar.DAY_OF_MONTH, calendar.getActualMinimum(Calendar.DAY_OF_MONTH));
|
||||
return monthAndYearFormat.format(calendar.getTime());
|
||||
}
|
||||
|
||||
public SearchRequestBuilder facetedQueryBuilder(AbstractFacetBuilder facet) throws WingException{
|
||||
List<AbstractFacetBuilder> facetList = new ArrayList<AbstractFacetBuilder>();
|
||||
facetList.add(facet);
|
||||
return facetedQueryBuilder(facetList);
|
||||
}
|
||||
|
||||
public SearchRequestBuilder facetedQueryBuilder(AbstractFacetBuilder... facets) throws WingException {
|
||||
List<AbstractFacetBuilder> facetList = new ArrayList<AbstractFacetBuilder>();
|
||||
|
||||
for(AbstractFacetBuilder facet : facets) {
|
||||
facetList.add(facet);
|
||||
}
|
||||
|
||||
return facetedQueryBuilder(facetList);
|
||||
}
|
||||
|
||||
public SearchRequestBuilder facetedQueryBuilder(List<AbstractFacetBuilder> facetList) {
|
||||
TermQueryBuilder termQuery = QueryBuilders.termQuery(getOwningText(dso), dso.getID());
|
||||
FilterBuilder rangeFilter = FilterBuilders.rangeFilter("time").from(dateStart).to(dateEnd);
|
||||
FilteredQueryBuilder filteredQueryBuilder = QueryBuilders.filteredQuery(termQuery, rangeFilter);
|
||||
|
||||
SearchRequestBuilder searchRequestBuilder = client.prepareSearch(ElasticSearchLogger.getInstance().indexName)
|
||||
.setSearchType(SearchType.DFS_QUERY_THEN_FETCH)
|
||||
.setQuery(filteredQueryBuilder)
|
||||
.setSize(0);
|
||||
|
||||
for(AbstractFacetBuilder facet : facetList) {
|
||||
searchRequestBuilder.addFacet(facet);
|
||||
}
|
||||
|
||||
return searchRequestBuilder;
|
||||
}
|
||||
|
||||
public SearchResponse searchResponseToDRI(SearchRequestBuilder searchRequestBuilder) throws WingException{
|
||||
division.addHidden("request").setValue(searchRequestBuilder.toString());
|
||||
|
||||
SearchResponse resp = searchRequestBuilder.execute().actionGet();
|
||||
|
||||
if(resp == null) {
|
||||
log.info("Elastic Search is down for searching.");
|
||||
division.addPara("Elastic Search seems to be down :(");
|
||||
return null;
|
||||
}
|
||||
|
||||
division.addHidden("response").setValue(resp.toString());
|
||||
division.addDivision("chart_div");
|
||||
|
||||
return resp;
|
||||
}
|
||||
|
||||
private void addTermFacetToTable(TermsFacet termsFacet, Division division, String termName, String tableHeader) throws WingException, SQLException {
|
||||
List<? extends TermsFacet.Entry> termsFacetEntries = termsFacet.getEntries();
|
||||
|
||||
if(termName.equalsIgnoreCase("country")) {
|
||||
division.addDivision("chart_div_map");
|
||||
}
|
||||
|
||||
Table facetTable = division.addTable("facet-"+termName, termsFacetEntries.size()+1, 10);
|
||||
facetTable.setHead(tableHeader);
|
||||
|
||||
Row facetTableHeaderRow = facetTable.addRow(Row.ROLE_HEADER);
|
||||
if(termName.equalsIgnoreCase("bitstream")) {
|
||||
facetTableHeaderRow.addCellContent("Title");
|
||||
facetTableHeaderRow.addCellContent("Creator");
|
||||
facetTableHeaderRow.addCellContent("Publisher");
|
||||
facetTableHeaderRow.addCellContent("Date");
|
||||
} else {
|
||||
facetTableHeaderRow.addCell().addContent(termName);
|
||||
}
|
||||
|
||||
facetTableHeaderRow.addCell().addContent("Count");
|
||||
|
||||
if(termsFacetEntries.size() == 0) {
|
||||
facetTable.addRow().addCell().addContent("No Data Available");
|
||||
return;
|
||||
}
|
||||
|
||||
for(TermsFacet.Entry facetEntry : termsFacetEntries) {
|
||||
Row row = facetTable.addRow();
|
||||
|
||||
if(termName.equalsIgnoreCase("bitstream")) {
|
||||
Bitstream bitstream = Bitstream.find(context, Integer.parseInt(facetEntry.getTerm()));
|
||||
Item item = (Item) bitstream.getParentObject();
|
||||
row.addCell().addXref(contextPath + "/handle/" + item.getHandle(), item.getName());
|
||||
row.addCellContent(getFirstMetadataValue(item, "dc.creator"));
|
||||
row.addCellContent(getFirstMetadataValue(item, "dc.publisher"));
|
||||
row.addCellContent(getFirstMetadataValue(item, "dc.date.issued"));
|
||||
} else if(termName.equalsIgnoreCase("country")) {
|
||||
row.addCell("country", Cell.ROLE_DATA,"country").addContent(new Locale("en", facetEntry.getTerm()).getDisplayCountry());
|
||||
} else {
|
||||
row.addCell().addContent(facetEntry.getTerm());
|
||||
}
|
||||
row.addCell("count", Cell.ROLE_DATA, "count").addContent(facetEntry.getCount());
|
||||
}
|
||||
}
|
||||
|
||||
private void addDateHistogramToTable(DateHistogramFacet monthlyDownloadsFacet, Division division, String termName, String termDescription) throws WingException {
|
||||
List<? extends DateHistogramFacet.Entry> monthlyFacetEntries = monthlyDownloadsFacet.getEntries();
|
||||
|
||||
if(monthlyFacetEntries.size() == 0) {
|
||||
division.addPara("Empty result set for: "+termName);
|
||||
return;
|
||||
}
|
||||
|
||||
Table monthlyTable = division.addTable(termName, monthlyFacetEntries.size(), 10);
|
||||
monthlyTable.setHead(termDescription);
|
||||
Row tableHeaderRow = monthlyTable.addRow(Row.ROLE_HEADER);
|
||||
tableHeaderRow.addCell("date", Cell.ROLE_HEADER,null).addContent("Month/Date");
|
||||
tableHeaderRow.addCell("count", Cell.ROLE_HEADER,null).addContent("Count");
|
||||
|
||||
for(DateHistogramFacet.Entry histogramEntry : monthlyFacetEntries) {
|
||||
Row dataRow = monthlyTable.addRow();
|
||||
Date facetDate = new Date(histogramEntry.getTime());
|
||||
dataRow.addCell("date", Cell.ROLE_DATA,"date").addContent(dateFormat.format(facetDate));
|
||||
dataRow.addCell("count", Cell.ROLE_DATA,"count").addContent("" + histogramEntry.getCount());
|
||||
}
|
||||
}
|
||||
|
||||
private String getOwningText(DSpaceObject dso) {
|
||||
switch (dso.getType()) {
|
||||
case Constants.ITEM:
|
||||
return "owningItem";
|
||||
case Constants.COLLECTION:
|
||||
return "owningColl";
|
||||
case Constants.COMMUNITY:
|
||||
return "owningComm";
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
private String getFirstMetadataValue(Item item, String metadataKey) {
|
||||
DCValue[] dcValue = item.getMetadata(metadataKey);
|
||||
if(dcValue.length > 0) {
|
||||
return dcValue[0].value;
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,262 @@
|
||||
/*
|
||||
* DashboardViewer.java
|
||||
*
|
||||
* Version: $Revision$
|
||||
*
|
||||
* Date: $Date$
|
||||
*
|
||||
* Copyright (c) 2002, Hewlett-Packard Company and Massachusetts
|
||||
* Institute of Technology. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* - Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* - Neither the name of the Hewlett-Packard Company nor the name of the
|
||||
* Massachusetts Institute of Technology nor the names of their
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
|
||||
* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
|
||||
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
|
||||
* DAMAGE.
|
||||
*/
|
||||
|
||||
package org.dspace.app.xmlui.aspect.statisticsElasticSearch;
|
||||
|
||||
import org.apache.cocoon.environment.Request;
|
||||
import org.apache.commons.validator.routines.DateValidator;
|
||||
import org.apache.log4j.Logger;
|
||||
import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
|
||||
import org.dspace.app.xmlui.wing.WingException;
|
||||
import org.dspace.app.xmlui.wing.element.*;
|
||||
|
||||
import java.text.DateFormat;
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.Enumeration;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Use a form to dynamically generate a variety of reports.
|
||||
*
|
||||
* @author "Ryan McGowan" ("mcgowan.98@osu.edu")
|
||||
* @version
|
||||
*/
|
||||
public class ReportGenerator
|
||||
{
|
||||
/**
|
||||
* A logger for this class.
|
||||
*/
|
||||
private static Logger log = Logger.getLogger(ReportGenerator.class);
|
||||
/**
|
||||
* The minimum date for the from or to field to be. (e.g. The beginning of DSpace)
|
||||
*/
|
||||
private static String MINIMUM_DATE = "2008-01-01";
|
||||
private static SimpleDateFormat dateFormat = new SimpleDateFormat("MM/dd/yyyy");
|
||||
|
||||
// perfect input is 2008-01-22, an alternate format is 01/22/2008
|
||||
static String[] formatStrings = {"MM/dd/yyyy", "yyyy-MM-dd"};
|
||||
|
||||
private Map<String, String> params;
|
||||
|
||||
private Date dateStart;
|
||||
private Date dateEnd;
|
||||
|
||||
public Date getDateStart() {
|
||||
return dateStart;
|
||||
}
|
||||
|
||||
public String getDateStartFormated() {
|
||||
try {
|
||||
return dateFormat.format(dateStart);
|
||||
} catch (Exception e) {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
public void setDateStart() {
|
||||
if(! params.containsKey("from")) {
|
||||
dateStart = null;
|
||||
} else {
|
||||
dateStart = tryParse(params.get("from"));
|
||||
}
|
||||
}
|
||||
|
||||
public Date tryParse(String dateString) {
|
||||
if(dateString == null || dateString.length() == 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
for(String formatString : formatStrings) {
|
||||
try {
|
||||
return new SimpleDateFormat(formatString).parse(dateString);
|
||||
} catch (ParseException e) {
|
||||
log.error("ReportGenerator couldn't parse date: " + dateString + ", with pattern of: "+formatString+" with error message:"+e.getMessage());
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public Date getDateEnd() {
|
||||
return dateEnd;
|
||||
}
|
||||
|
||||
public String getDateEndFormatted() {
|
||||
try {
|
||||
return dateFormat.format(dateEnd);
|
||||
} catch (Exception e) {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
public void setDateEnd() {
|
||||
if(! params.containsKey("to")) {
|
||||
dateEnd= null;
|
||||
} else {
|
||||
dateEnd = tryParse(params.get("to"));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @see org.dspace.app.xmlui.cocoon.DSpaceTransformer#addBody(Body)
|
||||
*/
|
||||
public void addReportGeneratorForm(Division parentDivision, Request request) {
|
||||
try {
|
||||
Division division = parentDivision.addDivision("report-generator", "primary");
|
||||
|
||||
division.setHead("Report Generator");
|
||||
division.addPara("Used to generate reports with an arbitrary date range.");
|
||||
|
||||
Division search = parentDivision.addInteractiveDivision("choose-report", request.getRequestURI(), Division.METHOD_GET, "primary");
|
||||
|
||||
params = new HashMap<String, String>();
|
||||
for (Enumeration<String> paramNames = (Enumeration<String>) request.getParameterNames(); paramNames.hasMoreElements(); ) {
|
||||
String param = paramNames.nextElement();
|
||||
params.put(param, request.getParameter(param));
|
||||
}
|
||||
|
||||
//params = checkAndNormalizeParameters(params);
|
||||
|
||||
//Create Date Range part of form
|
||||
Para reportForm = search.addPara();
|
||||
|
||||
setDateStart();
|
||||
Text from = reportForm.addText("from", "slick");
|
||||
from.setLabel("From");
|
||||
from.setHelp("The start date of the report, ex 01/31/2008");
|
||||
from.setValue(getDateStartFormated());
|
||||
|
||||
setDateEnd();
|
||||
Text to = reportForm.addText("to", "slick");
|
||||
to.setLabel("To");
|
||||
to.setHelp("The end date of the report, ex 12/31/2012");
|
||||
to.setValue(getDateEndFormatted());
|
||||
|
||||
//Add whether it is fiscal or not
|
||||
//CheckBox isFiscal = reportForm.addCheckBox("fiscal", "slick");
|
||||
//isFiscal.setLabel("Use Fiscal Years?");
|
||||
//Set up fiscal option with the correct default
|
||||
//isFiscal.addOption(params.containsKey("fiscal") && params.get("fiscal").equals("1"), 1, "");
|
||||
|
||||
reportForm.addButton("submit_add").setValue("Generate Report");
|
||||
} catch (WingException e) {
|
||||
log.error(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the parameters of the given request to see if they fit the
|
||||
* necessary criteria to run generate a report. The following must be true:
|
||||
*
|
||||
* * from - Must be convertable to a valid date that is greater than the
|
||||
* miniumum date and also less than or equal to the current date.
|
||||
* * to - Must be convertable to a valid date that is greater than from
|
||||
* and equal to or less than the current date.
|
||||
*
|
||||
* @return A map of valid parameters to their values.
|
||||
* @throws InvalidFormatException
|
||||
* @throws ParseException
|
||||
*/
|
||||
private Map<String,String> checkAndNormalizeParameters(Map<String,String> params) {
|
||||
try {
|
||||
|
||||
//Create dateValidator and min and max dates
|
||||
DateValidator dateValidator = new DateValidator(false, DateFormat.SHORT);
|
||||
SimpleDateFormat dateFormat = new SimpleDateFormat("MM/dd/yyyy");
|
||||
|
||||
Date maximumDate = new Date();
|
||||
Date minimumDate = dateFormat.parse(ReportGenerator.MINIMUM_DATE);
|
||||
|
||||
//Check the to and from dates
|
||||
Date fromDate = null;
|
||||
Date toDate = null;
|
||||
boolean validToAndFrom = true;
|
||||
boolean hasFrom = params.containsKey("from") && params.get("from").length() > 0;
|
||||
boolean hasTo = params.containsKey("to") && params.get("to").length() > 0;
|
||||
|
||||
if (hasFrom || hasTo) {
|
||||
if (hasFrom) {
|
||||
fromDate = tryParse(params.get("from"));
|
||||
params.put("from", dateFormat.format(fromDate));
|
||||
validToAndFrom = validToAndFrom && dateValidator.compareDates(minimumDate, fromDate, null) <= 0;
|
||||
}
|
||||
if (hasTo) {
|
||||
toDate = tryParse(params.get("to"));
|
||||
params.put("to", dateFormat.format(toDate));
|
||||
validToAndFrom = validToAndFrom && dateValidator.compareDates(toDate, maximumDate, null) <= 0;
|
||||
}
|
||||
if (hasFrom && hasTo) {
|
||||
//Make sure hasFrom <= hasTo
|
||||
validToAndFrom = validToAndFrom && dateValidator.compareDates(fromDate, toDate, null) <= 0;
|
||||
} else if (hasFrom && !hasTo) {
|
||||
//Make sure hasFrom <= the max date
|
||||
validToAndFrom = validToAndFrom && dateValidator.compareDates(fromDate, maximumDate, null) <= 0;
|
||||
} else {
|
||||
//hasTo && !hasFrom
|
||||
//Make sure hasTo >= the min date
|
||||
validToAndFrom = validToAndFrom && dateValidator.compareDates(minimumDate, toDate, null) <= 0;
|
||||
}
|
||||
// Short circuit if the to and from dates are not valid
|
||||
if (!validToAndFrom) {
|
||||
log.error("To and from dates are not within max/min or are not in order. "+ params.get("from") + " -> " + params.get("to"));
|
||||
return null;
|
||||
}
|
||||
|
||||
//Check fiscal
|
||||
if (params.containsKey("fiscal")) {
|
||||
log.debug("fiscal: " + params.get("fiscal"));
|
||||
if (Integer.parseInt(params.get("fiscal")) != 1) {
|
||||
log.error("Fiscal field did not contain a proper value: " + params.get("fiscal"));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
return params;
|
||||
} catch (ParseException e) {
|
||||
log.error("ParseFormatException likely means a date format failed. "+e.getMessage()); //To change body of catch statement use File | Settings | File Templates.
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,52 @@
|
||||
package org.dspace.app.xmlui.aspect.statisticsElasticSearch;
|
||||
|
||||
import org.apache.avalon.framework.parameters.Parameters;
|
||||
import org.apache.cocoon.selection.Selector;
|
||||
import org.apache.log4j.Logger;
|
||||
import org.dspace.app.xmlui.utils.ContextUtil;
|
||||
import org.dspace.core.Context;
|
||||
import org.dspace.eperson.Group;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Part of cocoon authentication for resource. Requires that user (eperson) is a member of a specified group.
|
||||
*
|
||||
* Author: Peter Dietz dietz.72@osu.edu - The Ohio State University Libraries
|
||||
* Date: 5/11/12
|
||||
* Time: 2:29 PM
|
||||
*/
|
||||
public class SpecifiedGroupAuthenticatedSelector implements Selector {
|
||||
private static Logger log = Logger.getLogger(SpecifiedGroupAuthenticatedSelector.class);
|
||||
|
||||
private String SPECIFIED_GROUP = "statistics_viewer";
|
||||
|
||||
|
||||
@Override
|
||||
public boolean select(String groupName, Map objectModel, Parameters parameters) {
|
||||
boolean authorized = false;
|
||||
|
||||
if(groupName.equals("statistics_viewer")) {
|
||||
try
|
||||
{
|
||||
Context context = ContextUtil.obtainContext(objectModel);
|
||||
Group statsGroup = Group.findByName(context, groupName);
|
||||
|
||||
if(statsGroup != null && context.getCurrentUser() != null) {
|
||||
//The Stats Group exists, now lets check that the current user is a member.
|
||||
if(statsGroup.isMember(context.getCurrentUser())) {
|
||||
//YES, this person is a member of this group. Let them through.
|
||||
authorized = true;
|
||||
}
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
log.error("SQL Error during stats group lookup.");
|
||||
}
|
||||
} else {
|
||||
log.warn("Pattern/test must be statistics_viewer");
|
||||
}
|
||||
|
||||
return authorized;
|
||||
}
|
||||
}
|
@@ -0,0 +1,177 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
sitemap.xmap
|
||||
|
||||
Version: $Revision: 5529 $
|
||||
|
||||
Date: $Date: 2010-10-21 12:28:37 -0400 (Thu, 21 Oct 2010) $
|
||||
|
||||
Copyright (c) 2002-2005, Hewlett-Packard Company and Massachusetts
|
||||
Institute of Technology. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
- Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
- Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
- Neither the name of the Hewlett-Packard Company nor the name of the
|
||||
Massachusetts Institute of Technology nor the names of their
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
|
||||
TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
|
||||
USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
|
||||
DAMAGE.
|
||||
-->
|
||||
|
||||
<!--
|
||||
|
||||
The Dashboard Aspect is responsible providing a dashboard for statistical inquiries to the system.
|
||||
|
||||
-->
|
||||
<map:sitemap xmlns:map="http://apache.org/cocoon/sitemap/1.0">
|
||||
<map:components>
|
||||
|
||||
<map:transformers>
|
||||
<map:transformer name="ElasticSearchStatsViewer" src="org.dspace.app.xmlui.aspect.statisticsElasticSearch.ElasticSearchStatsViewer"/>
|
||||
<map:transformer name="RestrictedItem" src="org.dspace.app.xmlui.aspect.artifactbrowser.RestrictedItem"/>
|
||||
</map:transformers>
|
||||
|
||||
<map:matchers default="wildcard">
|
||||
<map:matcher name="HandleTypeMatcher" src="org.dspace.app.xmlui.aspect.general.HandleTypeMatcher"/>
|
||||
<map:matcher name="StatisticsAuthorizedMatcher" src="org.dspace.app.xmlui.aspect.statistics.StatisticsAuthorizedMatcher"/>
|
||||
</map:matchers>
|
||||
|
||||
<map:selectors>
|
||||
<map:selector name="AuthenticatedSelector" src="org.dspace.app.xmlui.aspect.general.AuthenticatedSelector"/>
|
||||
<map:selector name="SpecifiedGroupAuthenticatedSelector" src="org.dspace.app.xmlui.aspect.statisticsElasticSearch.SpecifiedGroupAuthenticatedSelector"/>
|
||||
</map:selectors>
|
||||
|
||||
</map:components>
|
||||
|
||||
<map:pipelines>
|
||||
<map:pipeline>
|
||||
|
||||
|
||||
<!--<map:match pattern="collection-info">
|
||||
<map:match type="StatisticsAuthorizedMatcher" pattern="READ">
|
||||
<map:transform type="CollectionInfo"/>
|
||||
<map:serialize type="xml"/>
|
||||
</map:match>
|
||||
</map:match>
|
||||
|
||||
<map:match pattern="community-info">
|
||||
<map:match type="StatisticsAuthorizedMatcher" pattern="READ">
|
||||
<map:transform type="CommunityInfo"/>
|
||||
<map:serialize type="xml"/>
|
||||
</map:match>
|
||||
</map:match>-->
|
||||
|
||||
|
||||
|
||||
<map:generate/>
|
||||
|
||||
|
||||
<map:match pattern="handle/*/*/stats">
|
||||
<map:select type="SpecifiedGroupAuthenticatedSelector">
|
||||
<map:when test="statistics_viewer">
|
||||
<map:transform type="ElasticSearchStatsViewer"/>
|
||||
</map:when>
|
||||
<map:otherwise>
|
||||
<map:match type="StatisticsAuthorizedMatcher" pattern="READ">
|
||||
<map:transform type="ElasticSearchStatsViewer"/>
|
||||
</map:match>
|
||||
|
||||
<map:match type="StatisticsAuthorizedMatcher" pattern="!READ">
|
||||
<map:select type="SpecifiedGroupAuthenticatedSelector">
|
||||
<map:when test="statistics_viewer">
|
||||
<map:transform type="RestrictedItem"/>
|
||||
<map:serialize/>
|
||||
</map:when>
|
||||
<map:otherwise>
|
||||
<map:act type="StartAuthentication">
|
||||
<map:parameter name="header" value="xmlui.ArtifactBrowser.RestrictedItem.auth_header"/>
|
||||
<map:parameter name="message" value="xmlui.ArtifactBrowser.RestrictedItem.auth_message"/>
|
||||
</map:act>
|
||||
<map:serialize/>
|
||||
</map:otherwise>
|
||||
</map:select>
|
||||
</map:match>
|
||||
<map:serialize type="xml"/>
|
||||
</map:otherwise>
|
||||
</map:select>
|
||||
</map:match>
|
||||
|
||||
<map:match pattern="handle/*/*/stats/*">
|
||||
<map:select type="SpecifiedGroupAuthenticatedSelector">
|
||||
<map:when test="statistics_viewer">
|
||||
<map:transform type="ElasticSearchStatsViewer"/>
|
||||
</map:when>
|
||||
<map:otherwise>
|
||||
<map:match type="StatisticsAuthorizedMatcher" pattern="READ">
|
||||
<map:transform type="ElasticSearchStatsViewer"/>
|
||||
</map:match>
|
||||
|
||||
<map:match type="StatisticsAuthorizedMatcher" pattern="!READ">
|
||||
<map:select type="AuthenticatedSelector">
|
||||
<map:when test="eperson">
|
||||
<map:transform type="RestrictedItem"/>
|
||||
<map:serialize/>
|
||||
</map:when>
|
||||
<map:otherwise>
|
||||
<map:act type="StartAuthentication">
|
||||
<map:parameter name="header" value="xmlui.ArtifactBrowser.RestrictedItem.auth_header"/>
|
||||
<map:parameter name="message" value="xmlui.ArtifactBrowser.RestrictedItem.auth_message"/>
|
||||
</map:act>
|
||||
<map:serialize/>
|
||||
</map:otherwise>
|
||||
</map:select>
|
||||
</map:match>
|
||||
<map:serialize type="xml"/>
|
||||
</map:otherwise>
|
||||
</map:select>
|
||||
</map:match>
|
||||
|
||||
<!--
|
||||
<map:match pattern="growth-statistics">
|
||||
<map:match type="StatisticsAuthorizedMatcher" pattern="READ">
|
||||
<map:transform type="GrowthStatistics"/>
|
||||
<map:serialize type="xml"/>
|
||||
</map:match>
|
||||
</map:match>
|
||||
|
||||
<map:match pattern="hierarchy-info">
|
||||
<map:match type="StatisticsAuthorizedMatcher" pattern="READ">
|
||||
<map:transform type="HierarchyInfo"/>
|
||||
<map:serialize type="xml"/>
|
||||
</map:match>
|
||||
</map:match>
|
||||
|
||||
<map:match pattern="usage-report">
|
||||
<map:match type="StatisticsAuthorizedMatcher" pattern="READ">
|
||||
<map:transform type="UsageReport"/>
|
||||
<map:serialize type="xml"/>
|
||||
</map:match>
|
||||
</map:match>-->
|
||||
|
||||
<!-- Not a URL we care about, so just pass it on. -->
|
||||
<map:serialize type="xml"/>
|
||||
|
||||
</map:pipeline>
|
||||
</map:pipelines>
|
||||
</map:sitemap>
|
@@ -50,6 +50,13 @@
|
||||
<ref bean="dspace.eventService"/>
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
<!-- Elastic Search -->
|
||||
<bean class="org.dspace.statistics.ElasticSearchLoggerEventListener">
|
||||
<property name="eventService">
|
||||
<ref bean="dspace.eventService" />
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
<!--
|
||||
Uncomment to enable TabFileUsageEventListener
|
||||
|
@@ -95,6 +95,7 @@
|
||||
<map:transformer name="LDAPLogin" src="org.dspace.app.xmlui.aspect.eperson.LDAPLogin"/>
|
||||
<map:transformer name="notice" src="org.dspace.app.xmlui.aspect.general.NoticeTransformer"/>
|
||||
<map:transformer name="NamespaceFilter" src="org.dspace.app.xmlui.cocoon.NamespaceFilterTransformer"/>
|
||||
<map:transformer name="RestrictedItem" src="org.dspace.app.xmlui.aspect.artifactbrowser.RestrictedItem"/>
|
||||
</map:transformers>
|
||||
<map:matchers default="wildcard">
|
||||
<map:matcher logger="sitemap.matcher.wildcard" name="wildcard"
|
||||
@@ -122,6 +123,7 @@
|
||||
<map:parameter name="ignore-missing-tables" value="true"/>
|
||||
</map:matcher>
|
||||
<map:matcher name="request" src="org.apache.cocoon.matching.RequestParameterMatcher"/>
|
||||
<map:matcher name="StatisticsAuthorizedMatcher" src="org.dspace.app.xmlui.aspect.statistics.StatisticsAuthorizedMatcher"/>
|
||||
</map:matchers>
|
||||
<map:selectors>
|
||||
<map:selector name="browser" src="org.apache.cocoon.selection.BrowserSelector"
|
||||
@@ -177,6 +179,8 @@
|
||||
<!-- The statement below tells the selector to unroll as much exceptions as possible -->
|
||||
<exception class="java.lang.Throwable" unroll="true"/>
|
||||
</map:selector>
|
||||
<!-- AuthenticatedSelector for forcing user to login if not authorized - PMD -->
|
||||
<map:selector name="AuthenticatedSelector" src="org.dspace.app.xmlui.aspect.general.AuthenticatedSelector"/>
|
||||
</map:selectors>
|
||||
<map:readers default="resource">
|
||||
<map:reader name="resource" src="org.apache.cocoon.reading.ResourceReader"
|
||||
@@ -186,6 +190,9 @@
|
||||
<map:reader name="BitstreamReader" src="org.dspace.app.xmlui.cocoon.BitstreamReader"/>
|
||||
<map:reader name="ExportReader" src="org.dspace.app.xmlui.cocoon.ItemExportDownloadReader"/>
|
||||
<map:reader name="MetadataExportReader" src="org.dspace.app.xmlui.cocoon.MetadataExportReader"/>
|
||||
|
||||
<map:reader name="CSVOutputter" src="org.dspace.app.xmlui.aspect.statisticsElasticSearch.CSVOutputter"/>
|
||||
|
||||
<map:reader name="OpenURLReader" src="org.dspace.app.xmlui.cocoon.OpenURLReader"/>
|
||||
<map:reader name="SitemapReader" src="org.dspace.app.xmlui.cocoon.SitemapReader"/>
|
||||
<map:reader name="ConcatenationReader" src="org.dspace.app.xmlui.cocoon.ConcatenationReader"/>
|
||||
@@ -378,6 +385,50 @@
|
||||
</map:read>
|
||||
</map:match>
|
||||
|
||||
<map:match pattern="handle/*/*/stats/csv">
|
||||
<map:match type="StatisticsAuthorizedMatcher" pattern="READ">
|
||||
<map:read type="CSVOutputter"/>
|
||||
</map:match>
|
||||
|
||||
<map:match type="StatisticsAuthorizedMatcher" pattern="!READ">
|
||||
<map:select type="AuthenticatedSelector">
|
||||
<map:when test="eperson">
|
||||
<map:transform type="RestrictedItem"/>
|
||||
<map:serialize/>
|
||||
</map:when>
|
||||
<map:otherwise>
|
||||
<map:act type="StartAuthentication">
|
||||
<map:parameter name="header" value="xmlui.ArtifactBrowser.RestrictedItem.auth_header"/>
|
||||
<map:parameter name="message" value="xmlui.ArtifactBrowser.RestrictedItem.auth_message"/>
|
||||
</map:act>
|
||||
<map:serialize/>
|
||||
</map:otherwise>
|
||||
</map:select>
|
||||
</map:match>
|
||||
</map:match>
|
||||
|
||||
<map:match pattern="handle/*/*/stats/csv/*">
|
||||
<map:match type="StatisticsAuthorizedMatcher" pattern="READ">
|
||||
<map:read type="CSVOutputter"/>
|
||||
</map:match>
|
||||
|
||||
<map:match type="StatisticsAuthorizedMatcher" pattern="!READ">
|
||||
<map:select type="AuthenticatedSelector">
|
||||
<map:when test="eperson">
|
||||
<map:transform type="RestrictedItem"/>
|
||||
<map:serialize/>
|
||||
</map:when>
|
||||
<map:otherwise>
|
||||
<map:act type="StartAuthentication">
|
||||
<map:parameter name="header" value="xmlui.ArtifactBrowser.RestrictedItem.auth_header"/>
|
||||
<map:parameter name="message" value="xmlui.ArtifactBrowser.RestrictedItem.auth_message"/>
|
||||
</map:act>
|
||||
<map:serialize/>
|
||||
</map:otherwise>
|
||||
</map:select>
|
||||
</map:match>
|
||||
</map:match>
|
||||
|
||||
<map:match pattern="JSON/discovery/**">
|
||||
<map:mount check-reload="no" src="aspects/json-solr.xmap" uri-prefix="JSON/discovery/"/>
|
||||
</map:match>
|
||||
|
@@ -0,0 +1,33 @@
|
||||
#aspect_artifactbrowser_CommunityBrowser_list_comunity-browser ul {
|
||||
padding: 0 0 0 0px;
|
||||
margin: -4px 5px 3px 0;
|
||||
}
|
||||
#aspect_artifactbrowser_CommunityBrowser_list_comunity-browser li {
|
||||
list-style:none;
|
||||
margin: 8px 0 0 20px;
|
||||
padding: 2px 0 0 0;
|
||||
text-indent: -40px;
|
||||
}
|
||||
#aspect_artifactbrowser_CommunityBrowser_list_comunity-browser ul.hide {
|
||||
display:none;
|
||||
}
|
||||
#aspect_artifactbrowser_CommunityBrowser_list_comunity-browser span {
|
||||
color:#999;
|
||||
font-family:"Courier New", Courier, monospace;
|
||||
font-size: 1.2em;
|
||||
font-weight: bold;
|
||||
cursor:default;
|
||||
}
|
||||
#aspect_artifactbrowser_CommunityBrowser_list_comunity-browser span.expanded, #aspect_artifactbrowser_CommunityBrowser_list_comunity-browser span.collapsed {
|
||||
cursor:pointer;
|
||||
}
|
||||
|
||||
|
||||
#aspect_artifactbrowser_CommunityBrowser_list_comunity-browser a.community {
|
||||
color: #5e7c5e;
|
||||
font-weight: bold;
|
||||
}
|
||||
#aspect_artifactbrowser_CommunityBrowser_list_comunity-browser a.collection {
|
||||
color: #005b7f;
|
||||
font-weight: bold;
|
||||
}
|
@@ -0,0 +1,162 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<!--
|
||||
template.xsl
|
||||
|
||||
Version: $Revision: 3705 $
|
||||
|
||||
Date: $Date: 2009-04-11 13:02:24 -0400 (Sat, 11 Apr 2009) $
|
||||
|
||||
Copyright (c) 2002-2005, Hewlett-Packard Company and Massachusetts
|
||||
Institute of Technology. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
- Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
- Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
- Neither the name of the Hewlett-Packard Company nor the name of the
|
||||
Massachusetts Institute of Technology nor the names of their
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
|
||||
TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
|
||||
USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
|
||||
DAMAGE.
|
||||
-->
|
||||
|
||||
<!--
|
||||
TODO: Describe this XSL file
|
||||
Author: Alexey Maslov
|
||||
|
||||
-->
|
||||
|
||||
<xsl:stylesheet xmlns:i18n="http://apache.org/cocoon/i18n/2.1"
|
||||
xmlns:dri="http://di.tamu.edu/DRI/1.0/"
|
||||
xmlns:mets="http://www.loc.gov/METS/"
|
||||
xmlns:xlink="http://www.w3.org/TR/xlink/"
|
||||
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"
|
||||
xmlns:dim="http://www.dspace.org/xmlns/dspace/dim"
|
||||
xmlns:xhtml="http://www.w3.org/1999/xhtml"
|
||||
xmlns:mods="http://www.loc.gov/mods/v3"
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns="http://www.w3.org/1999/xhtml"
|
||||
exclude-result-prefixes="i18n dri mets xlink xsl dim xhtml mods dc">
|
||||
|
||||
<xsl:import href="../dri2xhtml.xsl"/>
|
||||
<xsl:output indent="yes"/>
|
||||
|
||||
<xsl:template name="extraHead-top">
|
||||
<script type="text/javascript" src="https://www.google.com/jsapi">
|
||||
<xsl:text> </xsl:text>
|
||||
</script>
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template name="extraBody-end">
|
||||
<script type="text/javascript" src="/dspace/themes/dashboard/lib/jquery.ui.datepicker-accessible.min.js">
|
||||
<xsl:text> </xsl:text>
|
||||
</script>
|
||||
<script type="text/javascript" src="/dspace/themes/dashboard/lib/report-generator-mod.js">
|
||||
<xsl:text> </xsl:text>
|
||||
</script>
|
||||
<script type="text/javascript" src="/dspace/themes/dashboard/visualizeData.js">
|
||||
<xsl:text> </xsl:text>
|
||||
</script>
|
||||
</xsl:template>
|
||||
|
||||
<!-- Just a plain old table cell -->
|
||||
<xsl:template match="dri:cell" priority="1">
|
||||
<td>
|
||||
<xsl:call-template name="standardAttributes">
|
||||
<xsl:with-param name="class">ds-table-cell
|
||||
<xsl:if test="(position() mod 2 = 0)">even</xsl:if>
|
||||
<xsl:if test="(position() mod 2 = 1)">odd</xsl:if>
|
||||
<xsl:if test="@n='date'"> date</xsl:if>
|
||||
<xsl:if test="@n='items_added'"> items_added</xsl:if>
|
||||
<xsl:if test="@n='items_total'"> items_total</xsl:if>
|
||||
</xsl:with-param>
|
||||
</xsl:call-template>
|
||||
<xsl:if test="@rows">
|
||||
<xsl:attribute name="rowspan">
|
||||
<xsl:value-of select="@rows"/>
|
||||
</xsl:attribute>
|
||||
</xsl:if>
|
||||
<xsl:if test="@cols">
|
||||
<xsl:attribute name="colspan">
|
||||
<xsl:value-of select="@cols"/>
|
||||
</xsl:attribute>
|
||||
</xsl:if>
|
||||
<xsl:apply-templates />
|
||||
</td>
|
||||
</xsl:template>
|
||||
<xsl:template match="dri:field[@rend='slick']">
|
||||
<div>
|
||||
<xsl:attribute name="class">
|
||||
<xsl:text>slick-wrapper</xsl:text>
|
||||
</xsl:attribute>
|
||||
<xsl:attribute name="id">
|
||||
<xsl:value-of select="translate(@id,'.','_')"/>
|
||||
<xsl:text>_wrapper</xsl:text>
|
||||
</xsl:attribute>
|
||||
<label>
|
||||
<xsl:attribute name="for">
|
||||
<xsl:value-of select="translate(@id,'.','_')"/>
|
||||
</xsl:attribute>
|
||||
<xsl:value-of select="dri:label" />
|
||||
<xsl:text>:</xsl:text>
|
||||
</label>
|
||||
<input>
|
||||
<xsl:attribute name="id">
|
||||
<xsl:value-of select="translate(@id,'.','_')"/>
|
||||
</xsl:attribute>
|
||||
<xsl:attribute name="name">
|
||||
<xsl:value-of select="@n" />
|
||||
</xsl:attribute>
|
||||
<xsl:if test="@type='text'">
|
||||
<xsl:attribute name="class">
|
||||
<xsl:text>date-picker</xsl:text>
|
||||
</xsl:attribute>
|
||||
</xsl:if>
|
||||
<xsl:attribute name="type">
|
||||
<xsl:value-of select="@type" />
|
||||
</xsl:attribute>
|
||||
<xsl:attribute name="value">
|
||||
<xsl:choose>
|
||||
<xsl:when test="@type='checkbox'">
|
||||
<xsl:value-of select="dri:option/@returnValue" />
|
||||
</xsl:when>
|
||||
<xsl:otherwise>
|
||||
<xsl:value-of select="dri:value" />
|
||||
</xsl:otherwise>
|
||||
</xsl:choose>
|
||||
</xsl:attribute>
|
||||
</input>
|
||||
<p>
|
||||
<xsl:attribute name="id">
|
||||
<xsl:text>help_</xsl:text>
|
||||
<xsl:value-of select="translate(@id,'.','_')"/>
|
||||
</xsl:attribute>
|
||||
<xsl:attribute name="class">
|
||||
<xsl:text>help</xsl:text>
|
||||
</xsl:attribute>
|
||||
<xsl:value-of select="dri:help" />
|
||||
</p>
|
||||
</div>
|
||||
</xsl:template>
|
||||
|
||||
</xsl:stylesheet>
|
@@ -0,0 +1,460 @@
|
||||
// Copyright 2009 Google Inc.
|
||||
// All Rights Reserved.
|
||||
|
||||
/**
|
||||
* This file exposes the external Google Visualization API.
|
||||
*
|
||||
* The file can be used to enable auto complete of objects and methods provided by the
|
||||
* Google Visualization API, and for easier exploration of the API.
|
||||
*
|
||||
* To enable auto complete in a development environment - copy the file into the project
|
||||
* you are working on where the development tool you are using can index the file.
|
||||
*
|
||||
* Disclaimer: there may be missing classes and methods and the file may
|
||||
* be updated and/or changed. For the most up to date API reference please visit:
|
||||
* {@link http://code.google.com/intl/iw/apis/visualization/documentation/reference.html}
|
||||
*/
|
||||
|
||||
var google = {};
|
||||
google.visualization = {};
|
||||
|
||||
/** @constructor */
|
||||
google.visualization.DataTable = function(opt_data, opt_version) {};
|
||||
google.visualization.DataTable.prototype.getNumberOfRows = function() {};
|
||||
google.visualization.DataTable.prototype.getNumberOfColumns = function() {};
|
||||
google.visualization.DataTable.prototype.clone = function() {};
|
||||
google.visualization.DataTable.prototype.getColumnId = function(columnIndex) {};
|
||||
google.visualization.DataTable.prototype.getColumnIndex = function(columnId) {};
|
||||
google.visualization.DataTable.prototype.getColumnLabel = function(columnIndex) {};
|
||||
google.visualization.DataTable.prototype.getColumnPattern = function(columnIndex) {};
|
||||
google.visualization.DataTable.prototype.getColumnRole = function(columnIndex) {};
|
||||
google.visualization.DataTable.prototype.getColumnType = function(columnIndex) {};
|
||||
google.visualization.DataTable.prototype.getValue = function(rowIndex, columnIndex) {};
|
||||
google.visualization.DataTable.prototype.getFormattedValue = function(rowIndex, columnIndex) {};
|
||||
google.visualization.DataTable.prototype.getProperty = function(rowIndex, columnIndex, property) {};
|
||||
google.visualization.DataTable.prototype.getProperties = function(rowIndex, columnIndex) {};
|
||||
google.visualization.DataTable.prototype.getTableProperties = function() {};
|
||||
google.visualization.DataTable.prototype.getTableProperty = function(property) {};
|
||||
google.visualization.DataTable.prototype.setTableProperties = function(properties) {};
|
||||
google.visualization.DataTable.prototype.setTableProperty = function(property, value) {};
|
||||
google.visualization.DataTable.prototype.setValue = function(rowIndex, columnIndex, value) {};
|
||||
google.visualization.DataTable.prototype.setFormattedValue = function(rowIndex, columnIndex, formattedValue) {};
|
||||
google.visualization.DataTable.prototype.setProperties = function(rowIndex, columnIndex, properties) {};
|
||||
google.visualization.DataTable.prototype.setProperty = function(rowIndex, columnIndex, property, value) {};
|
||||
google.visualization.DataTable.prototype.setCell = function(rowIndex, columnIndex, opt_value, opt_formattedValue, opt_properties) {};
|
||||
google.visualization.DataTable.prototype.setRowProperties = function(rowIndex, properties) {};
|
||||
google.visualization.DataTable.prototype.setRowProperty = function(rowIndex, property, value) {};
|
||||
google.visualization.DataTable.prototype.getRowProperty = function(rowIndex, property) {};
|
||||
google.visualization.DataTable.prototype.getRowProperties = function(rowIndex) {};
|
||||
google.visualization.DataTable.prototype.setColumnLabel = function(columnIndex, newLabel) {};
|
||||
google.visualization.DataTable.prototype.setColumnProperties = function(columnIndex, properties) {};
|
||||
google.visualization.DataTable.prototype.setColumnProperty = function(columnIndex, property, value) {};
|
||||
google.visualization.DataTable.prototype.getColumnProperty = function(columnIndex, property) {};
|
||||
google.visualization.DataTable.prototype.getColumnProperties = function(columnIndex) {};
|
||||
google.visualization.DataTable.prototype.insertColumn = function(atColIndex, type, opt_label, opt_id) {};
|
||||
google.visualization.DataTable.prototype.addColumn = function(type, opt_label, opt_id) {};
|
||||
google.visualization.DataTable.prototype.insertRows = function(atRowIndex, numOrArray) {};
|
||||
google.visualization.DataTable.prototype.addRows = function(numOrArray) {};
|
||||
google.visualization.DataTable.prototype.addRow = function(opt_cellArray) {};
|
||||
google.visualization.DataTable.prototype.getColumnRange = function(columnIndex) {};
|
||||
google.visualization.DataTable.prototype.getSortedRows = function(sortColumns) {};
|
||||
google.visualization.DataTable.prototype.sort = function(sortColumns) {};
|
||||
google.visualization.DataTable.prototype.getDistinctValues = function(column) {};
|
||||
google.visualization.DataTable.prototype.getFilteredRows = function(columnFilters) {};
|
||||
google.visualization.DataTable.prototype.removeRows = function(fromRowIndex, numRows) {};
|
||||
google.visualization.DataTable.prototype.removeRow = function(rowIndex) {};
|
||||
google.visualization.DataTable.prototype.removeColumns = function(fromColIndex, numCols) {};
|
||||
google.visualization.DataTable.prototype.removeColumn = function(colIndex) {};
|
||||
|
||||
/** @return {string} JSON representation. */
|
||||
google.visualization.DataTable.prototype.toJSON = function() {};
|
||||
|
||||
/** @constructor */
|
||||
google.visualization.QueryResponse = function(responseObj) {};
|
||||
google.visualization.QueryResponse.getVersionFromResponse = function(responseObj) {};
|
||||
google.visualization.QueryResponse.prototype.getVersion = function() {};
|
||||
google.visualization.QueryResponse.prototype.getExecutionStatus = function() {};
|
||||
google.visualization.QueryResponse.prototype.isError = function() {};
|
||||
google.visualization.QueryResponse.prototype.hasWarning = function() {};
|
||||
google.visualization.QueryResponse.prototype.containsReason = function(reason) {};
|
||||
google.visualization.QueryResponse.prototype.getDataSignature = function() {};
|
||||
google.visualization.QueryResponse.prototype.getDataTable = function() {};
|
||||
google.visualization.QueryResponse.prototype.getReasons = function() {};
|
||||
google.visualization.QueryResponse.prototype.getMessage = function() {};
|
||||
google.visualization.QueryResponse.prototype.getDetailedMessage = function() {};
|
||||
|
||||
/** @constructor */
|
||||
google.visualization.Query = function(dataSourceUrl, opt_options) {};
|
||||
google.visualization.Query.refreshAllQueries = function() {};
|
||||
google.visualization.Query.setResponse = function(response) {};
|
||||
google.visualization.Query.prototype.setRefreshInterval = function(intervalSeconds) {};
|
||||
google.visualization.Query.prototype.send = function(responseHandler) {};
|
||||
google.visualization.Query.prototype.makeRequest = function(responseHandler, opt_params) {};
|
||||
google.visualization.Query.prototype.abort = function() {};
|
||||
google.visualization.Query.prototype.setTimeout = function(timeoutSeconds) {};
|
||||
google.visualization.Query.prototype.setRefreshable = function(refreshable) {};
|
||||
google.visualization.Query.prototype.setQuery = function(queryString) {};
|
||||
|
||||
google.visualization.errors = {};
|
||||
google.visualization.errors.addError = function(container, message, opt_detailedMessage, opt_options) {};
|
||||
google.visualization.errors.removeAll = function(container) {};
|
||||
google.visualization.errors.addErrorFromQueryResponse = function(container, response) {};
|
||||
google.visualization.errors.removeError = function(id) {};
|
||||
google.visualization.errors.getContainer = function(errorId) {};
|
||||
|
||||
google.visualization.events = {};
|
||||
google.visualization.events.addListener = function(eventSource, eventName, eventHandler) {};
|
||||
google.visualization.events.trigger = function(eventSource, eventName, eventDetails) {};
|
||||
google.visualization.events.removeListener = function(listener) {};
|
||||
google.visualization.events.removeAllListeners = function(eventSource) {};
|
||||
|
||||
/** @constructor */
|
||||
google.visualization.DataView = function(dataTable) {};
|
||||
google.visualization.DataView.fromJSON = function(dataTable, view) {};
|
||||
google.visualization.DataView.prototype.setColumns = function(colIndices) {};
|
||||
google.visualization.DataView.prototype.setRows = function(arg0, opt_arg1) {};
|
||||
google.visualization.DataView.prototype.getViewColumns = function() {};
|
||||
google.visualization.DataView.prototype.getViewRows = function() {};
|
||||
google.visualization.DataView.prototype.hideColumns = function(colIndices) {};
|
||||
google.visualization.DataView.prototype.hideRows = function(arg0, opt_arg1) {};
|
||||
google.visualization.DataView.prototype.getViewColumnIndex = function(tableColumnIndex) {};
|
||||
google.visualization.DataView.prototype.getViewRowIndex = function(tableRowIndex) {};
|
||||
google.visualization.DataView.prototype.getTableColumnIndex = function(viewColumnIndex) {};
|
||||
google.visualization.DataView.prototype.getUnderlyingTableColumnIndex = function(viewColumnIndex) {};
|
||||
google.visualization.DataView.prototype.getTableRowIndex = function(viewRowIndex) {};
|
||||
google.visualization.DataView.prototype.getUnderlyingTableRowIndex = function(viewRowIndex) {};
|
||||
google.visualization.DataView.prototype.getNumberOfRows = function() {};
|
||||
google.visualization.DataView.prototype.getNumberOfColumns = function() {};
|
||||
google.visualization.DataView.prototype.getColumnId = function(columnIndex) {};
|
||||
google.visualization.DataView.prototype.getColumnIndex = function(columnId) {};
|
||||
google.visualization.DataView.prototype.getColumnLabel = function(columnIndex) {};
|
||||
google.visualization.DataView.prototype.getColumnPattern = function(columnIndex) {};
|
||||
google.visualization.DataView.prototype.getColumnRole = function(columnIndex) {};
|
||||
google.visualization.DataView.prototype.getColumnType = function(columnIndex) {};
|
||||
google.visualization.DataView.prototype.getValue = function(rowIndex, columnIndex) {};
|
||||
google.visualization.DataView.prototype.getFormattedValue = function(rowIndex, columnIndex) {};
|
||||
google.visualization.DataView.prototype.getProperty = function(rowIndex, columnIndex, property) {};
|
||||
google.visualization.DataView.prototype.getColumnProperty = function(columnIndex, property) {};
|
||||
google.visualization.DataView.prototype.getColumnProperties = function(columnIndex) {};
|
||||
google.visualization.DataView.prototype.getTableProperty = function(property) {};
|
||||
google.visualization.DataView.prototype.getTableProperties = function() {};
|
||||
google.visualization.DataView.prototype.getRowProperty = function(rowIndex, property) {};
|
||||
google.visualization.DataView.prototype.getRowProperties = function(rowIndex) {};
|
||||
google.visualization.DataView.prototype.getColumnRange = function(columnIndex) {};
|
||||
google.visualization.DataView.prototype.getDistinctValues = function(columnIndex) {};
|
||||
google.visualization.DataView.prototype.getSortedRows = function(sortColumns) {};
|
||||
google.visualization.DataView.prototype.getFilteredRows = function(columnFilters) {};
|
||||
google.visualization.DataView.prototype.toDataTable = function() {};
|
||||
|
||||
/** @return {string} JSON representation. */
|
||||
google.visualization.DataView.prototype.toJSON = function() {};
|
||||
|
||||
/** @constructor */
|
||||
google.visualization.ArrowFormat = function(opt_options) {};
|
||||
google.visualization.ArrowFormat.prototype.format = function(dataTable, columnIndex) {};
|
||||
|
||||
/** @constructor */
|
||||
google.visualization.BarFormat = function(opt_options) {};
|
||||
google.visualization.BarFormat.prototype.format = function(dataTable, columnIndex) {};
|
||||
|
||||
/** @constructor */
|
||||
google.visualization.ColorFormat = function() {};
|
||||
google.visualization.ColorFormat.prototype.addRange = function(from, to, color, bgcolor) {};
|
||||
google.visualization.ColorFormat.prototype.addGradientRange = function(from, to, color, fromBgColor, toBgColor) {};
|
||||
google.visualization.ColorFormat.prototype.format = function(dataTable, columnIndex) {};
|
||||
|
||||
/** @constructor */
|
||||
google.visualization.DateFormat = function(opt_options) {};
|
||||
google.visualization.DateFormat.prototype.format = function(dataTable, columnIndex) {};
|
||||
google.visualization.DateFormat.prototype.formatValue = function(value) {};
|
||||
|
||||
/** @constructor */
|
||||
google.visualization.NumberFormat = function(opt_options) {};
|
||||
google.visualization.NumberFormat.prototype.format = function(dataTable, columnIndex) {};
|
||||
google.visualization.NumberFormat.prototype.formatValue = function(value) {};
|
||||
google.visualization.NumberFormat.DECIMAL_SEP;
|
||||
google.visualization.NumberFormat.GROUP_SEP;
|
||||
google.visualization.NumberFormat.DECIMAL_PATTERN;
|
||||
|
||||
/** @constructor */
|
||||
google.visualization.PatternFormat = function(pattern) {};
|
||||
google.visualization.PatternFormat.prototype.format = function(dataTable, srcColumnIndices, opt_dstColumnIndex) {};
|
||||
|
||||
/** @constructor */
|
||||
google.visualization.GadgetHelper = function() {};
|
||||
google.visualization.GadgetHelper.prototype.createQueryFromPrefs = function(prefs) {};
|
||||
google.visualization.GadgetHelper.prototype.validateResponse = function(response) {};
|
||||
|
||||
/** @constructor */
|
||||
google.visualization.AnnotatedTimeLine = function(container) {};
|
||||
google.visualization.AnnotatedTimeLine.prototype.draw = function(data, opt_options) {};
|
||||
google.visualization.AnnotatedTimeLine.prototype.getSelection = function() {};
|
||||
google.visualization.AnnotatedTimeLine.prototype.getVisibleChartRange = function() {};
|
||||
google.visualization.AnnotatedTimeLine.prototype.setVisibleChartRange = function(firstDate, lastDate, opt_animate) {};
|
||||
google.visualization.AnnotatedTimeLine.prototype.showDataColumns = function(columnIndexes) {};
|
||||
google.visualization.AnnotatedTimeLine.prototype.hideDataColumns = function(columnIndexes) {};
|
||||
|
||||
/** @constructor */
|
||||
google.visualization.AreaChart = function(container) {};
|
||||
google.visualization.AreaChart.prototype.draw = function(data, opt_options, opt_state) {};
|
||||
google.visualization.AreaChart.prototype.clearChart = function() {};
|
||||
google.visualization.AreaChart.prototype.getSelection = function() {};
|
||||
google.visualization.AreaChart.prototype.setSelection = function(selection) {};
|
||||
|
||||
/** @constructor */
|
||||
google.visualization.BarChart = function(container) {};
|
||||
google.visualization.BarChart.prototype.draw = function(data, opt_options, opt_state) {};
|
||||
google.visualization.BarChart.prototype.clearChart = function() {};
|
||||
google.visualization.BarChart.prototype.getSelection = function() {};
|
||||
google.visualization.BarChart.prototype.setSelection = function(selection) {};
|
||||
|
||||
/** @constructor */
|
||||
google.visualization.BubbleChart = function(container) {};
|
||||
google.visualization.BubbleChart.prototype.draw = function(data, opt_options, opt_state) {};
|
||||
google.visualization.BubbleChart.prototype.clearChart = function() {};
|
||||
google.visualization.BubbleChart.prototype.getSelection = function() {};
|
||||
google.visualization.BubbleChart.prototype.setSelection = function(selection) {};
|
||||
|
||||
/** @constructor */
|
||||
google.visualization.CandlestickChart = function(container) {};
|
||||
google.visualization.CandlestickChart.prototype.draw = function(data, opt_options, opt_state) {};
|
||||
google.visualization.CandlestickChart.prototype.clearChart = function() {};
|
||||
google.visualization.CandlestickChart.prototype.getSelection = function() {};
|
||||
google.visualization.CandlestickChart.prototype.setSelection = function(selection) {};
|
||||
|
||||
/** @constructor */
|
||||
google.visualization.ColumnChart = function(container) {};
|
||||
google.visualization.ColumnChart.prototype.draw = function(data, opt_options, opt_state) {};
|
||||
google.visualization.ColumnChart.prototype.clearChart = function() {};
|
||||
google.visualization.ColumnChart.prototype.getSelection = function() {};
|
||||
google.visualization.ColumnChart.prototype.setSelection = function(selection) {};
|
||||
|
||||
/** @constructor */
|
||||
google.visualization.ComboChart = function(container) {};
|
||||
google.visualization.ComboChart.prototype.draw = function(data, opt_options, opt_state) {};
|
||||
google.visualization.ComboChart.prototype.clearChart = function() {};
|
||||
google.visualization.ComboChart.prototype.getSelection = function() {};
|
||||
google.visualization.ComboChart.prototype.setSelection = function(selection) {};
|
||||
|
||||
/** @constructor */
|
||||
google.visualization.Gauge = function(container) {};
|
||||
google.visualization.Gauge.prototype.draw = function(dataTable, opt_options) {};
|
||||
google.visualization.Gauge.prototype.clearChart = function() {};
|
||||
|
||||
/** @constructor */
|
||||
google.visualization.GeoChart = function(container) {};
|
||||
google.visualization.GeoChart.mapExists = function(userOptions) {};
|
||||
google.visualization.GeoChart.prototype.clearChart = function() {};
|
||||
google.visualization.GeoChart.prototype.draw = function(dataTable, userOptions, opt_state) {};
|
||||
google.visualization.GeoChart.prototype.getSelection = function() {};
|
||||
google.visualization.GeoChart.prototype.setSelection = function(selection) {};
|
||||
|
||||
/** @constructor */
|
||||
google.visualization.GeoMap = function(container) {};
|
||||
google.visualization.GeoMap.clickOnRegion = function(id, zoomLevel, segmentBy, instanceIndex) {};
|
||||
google.visualization.GeoMap.prototype.draw = function(dataTable, opt_options) {};
|
||||
google.visualization.GeoMap.prototype.getSelection = function() {};
|
||||
google.visualization.GeoMap.prototype.setSelection = function(selection) {};
|
||||
|
||||
/** @constructor */
|
||||
google.visualization.Map = function(container) {};
|
||||
google.visualization.Map.prototype.draw = function(dataTable, opt_options) {};
|
||||
google.visualization.Map.prototype.getSelection = function() {};
|
||||
google.visualization.Map.prototype.setSelection = function(selection) {};
|
||||
|
||||
/** @constructor */
|
||||
google.visualization.ImageAreaChart = function(container) {};
|
||||
google.visualization.ImageAreaChart.prototype.draw = function(data, opt_options) {};
|
||||
|
||||
/** @constructor */
|
||||
google.visualization.ImageBarChart = function(container) {};
|
||||
google.visualization.ImageBarChart.prototype.draw = function(data, opt_options) {};
|
||||
|
||||
/** @constructor */
|
||||
google.visualization.ImageCandlestickChart = function(container) {};
|
||||
google.visualization.ImageCandlestickChart.prototype.draw = function(data, opt_options) {};
|
||||
|
||||
/** @constructor */
|
||||
google.visualization.ImageChart = function(container) {};
|
||||
google.visualization.ImageChart.prototype.draw = function(data, opt_options) {};
|
||||
|
||||
/** @constructor */
|
||||
google.visualization.ImageLineChart = function(container) {};
|
||||
google.visualization.ImageLineChart.prototype.draw = function(data, opt_options) {};
|
||||
|
||||
/** @constructor */
|
||||
google.visualization.ImagePieChart = function(container) {};
|
||||
google.visualization.ImagePieChart.prototype.draw = function(data, opt_options) {};
|
||||
|
||||
/** @constructor */
|
||||
google.visualization.ImageSparkLine = function(container, opt_domHelper) {};
|
||||
google.visualization.ImageSparkLine.prototype.draw = function(dataTable, opt_options) {};
|
||||
google.visualization.ImageSparkLine.prototype.getSelection = function() {};
|
||||
google.visualization.ImageSparkLine.prototype.setSelection = function(selection) {};
|
||||
|
||||
/** @constructor */
|
||||
google.visualization.IntensityMap = function(container) {};
|
||||
google.visualization.IntensityMap.prototype.draw = function(dataTable, opt_options) {};
|
||||
google.visualization.IntensityMap.prototype.getSelection = function() {};
|
||||
google.visualization.IntensityMap.prototype.setSelection = function(selection) {};
|
||||
|
||||
/** @constructor */
|
||||
google.visualization.LineChart = function(container) {};
|
||||
google.visualization.LineChart.prototype.draw = function(data, opt_options, opt_state) {};
|
||||
google.visualization.LineChart.prototype.clearChart = function() {};
|
||||
google.visualization.LineChart.prototype.getSelection = function() {};
|
||||
google.visualization.LineChart.prototype.setSelection = function(selection) {};
|
||||
|
||||
/** @constructor */
|
||||
google.visualization.MotionChart = function(container) {};
|
||||
google.visualization.MotionChart.prototype.draw = function(dataTable, opt_options) {};
|
||||
google.visualization.MotionChart.prototype.getState = function() {};
|
||||
|
||||
/** @constructor */
|
||||
google.visualization.OrgChart = function(container) {};
|
||||
google.visualization.OrgChart.prototype.draw = function(dataTable, opt_options) {};
|
||||
google.visualization.OrgChart.prototype.getSelection = function() {};
|
||||
google.visualization.OrgChart.prototype.setSelection = function(selection) {};
|
||||
google.visualization.OrgChart.prototype.getCollapsedNodes = function() {};
|
||||
google.visualization.OrgChart.prototype.getChildrenIndexes = function(rowInd) {};
|
||||
google.visualization.OrgChart.prototype.collapse = function(rowInd, collapse) {};
|
||||
|
||||
/** @constructor */
|
||||
google.visualization.PieChart = function(container) {};
|
||||
google.visualization.PieChart.prototype.draw = function(data, opt_options, opt_state) {};
|
||||
google.visualization.PieChart.prototype.clearChart = function() {};
|
||||
google.visualization.PieChart.prototype.getSelection = function() {};
|
||||
google.visualization.PieChart.prototype.setSelection = function(selection) {};
|
||||
|
||||
/** @constructor */
|
||||
google.visualization.ScatterChart = function(container) {};
|
||||
google.visualization.ScatterChart.prototype.draw = function(data, opt_options, opt_state) {};
|
||||
google.visualization.ScatterChart.prototype.clearChart = function() {};
|
||||
google.visualization.ScatterChart.prototype.getSelection = function() {};
|
||||
google.visualization.ScatterChart.prototype.setSelection = function(selection) {};
|
||||
|
||||
/** @constructor */
|
||||
google.visualization.SparklineChart = function(container) {};
|
||||
google.visualization.SparklineChart.prototype.draw = function(data, opt_options, opt_state) {};
|
||||
google.visualization.SparklineChart.prototype.clearChart = function() {};
|
||||
google.visualization.SparklineChart.prototype.getSelection = function() {};
|
||||
google.visualization.SparklineChart.prototype.setSelection = function(selection) {};
|
||||
|
||||
/** @constructor */
|
||||
google.visualization.SteppedAreaChart = function(container) {};
|
||||
google.visualization.SteppedAreaChart.prototype.draw = function(data, opt_options, opt_state) {};
|
||||
google.visualization.SteppedAreaChart.prototype.clearChart = function() {};
|
||||
google.visualization.SteppedAreaChart.prototype.getSelection = function() {};
|
||||
google.visualization.SteppedAreaChart.prototype.setSelection = function(selection) {};
|
||||
|
||||
/** @constructor */
|
||||
google.visualization.Table = function(container) {};
|
||||
google.visualization.Table.prototype.draw = function(dataTable, opt_options) {};
|
||||
google.visualization.Table.prototype.clearChart = function() {};
|
||||
google.visualization.Table.prototype.getSortInfo = function() {};
|
||||
google.visualization.Table.prototype.getSelection = function() {};
|
||||
google.visualization.Table.prototype.setSelection = function(selection) {};
|
||||
|
||||
/** @constructor */
|
||||
google.visualization.TreeMap = function(container) {};
|
||||
google.visualization.TreeMap.prototype.draw = function(dataTable, opt_options) {};
|
||||
google.visualization.TreeMap.prototype.clearChart = function() {};
|
||||
google.visualization.TreeMap.prototype.getSelection = function() {};
|
||||
google.visualization.TreeMap.prototype.setSelection = function(selection) {};
|
||||
|
||||
google.visualization.drawToolbar = function(container, components) {};
|
||||
|
||||
/** @constructor */
|
||||
google.visualization.ChartWrapper = function(opt_specification) {};
|
||||
google.visualization.ChartWrapper.prototype.draw = function(opt_container) {};
|
||||
google.visualization.ChartWrapper.prototype.getDataSourceUrl = function() {};
|
||||
google.visualization.ChartWrapper.prototype.getDataTable = function() {};
|
||||
google.visualization.ChartWrapper.prototype.getChartName = function() {};
|
||||
google.visualization.ChartWrapper.prototype.getChartType = function() {};
|
||||
google.visualization.ChartWrapper.prototype.getContainerId = function() {};
|
||||
google.visualization.ChartWrapper.prototype.getQuery = function() {};
|
||||
google.visualization.ChartWrapper.prototype.getRefreshInterval = function() {};
|
||||
google.visualization.ChartWrapper.prototype.getView = function() {};
|
||||
google.visualization.ChartWrapper.prototype.getOption = function(key, opt_default) {};
|
||||
google.visualization.ChartWrapper.prototype.getOptions = function() {};
|
||||
google.visualization.ChartWrapper.prototype.setDataSourceUrl = function(dataSourceUrl) {};
|
||||
google.visualization.ChartWrapper.prototype.setDataTable = function(dataTable) {};
|
||||
google.visualization.ChartWrapper.prototype.setChartName = function(chartName) {};
|
||||
google.visualization.ChartWrapper.prototype.setChartType = function(chartType) {};
|
||||
google.visualization.ChartWrapper.prototype.setContainerId = function(containerId) {};
|
||||
google.visualization.ChartWrapper.prototype.setQuery = function(query) {};
|
||||
google.visualization.ChartWrapper.prototype.setRefreshInterval = function(refreshInterval) {};
|
||||
google.visualization.ChartWrapper.prototype.setView = function(view) {};
|
||||
google.visualization.ChartWrapper.prototype.setOption = function(key, value) {};
|
||||
google.visualization.ChartWrapper.prototype.setOptions = function(options) {};
|
||||
|
||||
/** @return {string} JSON representation. */
|
||||
google.visualization.ChartWrapper.prototype.toJSON = function() {};
|
||||
|
||||
/** @constructor */
|
||||
google.visualization.ControlWrapper = function(opt_specification) {};
|
||||
google.visualization.ControlWrapper.prototype.draw = function(opt_container) {};
|
||||
google.visualization.ControlWrapper.prototype.toJSON = function() {};
|
||||
google.visualization.ControlWrapper.prototype.getDataSourceUrl = function() {};
|
||||
google.visualization.ControlWrapper.prototype.getDataTable = function() {};
|
||||
google.visualization.ControlWrapper.prototype.getControlName = function() {};
|
||||
google.visualization.ControlWrapper.prototype.getControlType = function() {};
|
||||
google.visualization.ControlWrapper.prototype.getContainerId = function() {};
|
||||
google.visualization.ControlWrapper.prototype.getQuery = function() {};
|
||||
google.visualization.ControlWrapper.prototype.getRefreshInterval = function() {};
|
||||
google.visualization.ControlWrapper.prototype.getView = function() {};
|
||||
google.visualization.ControlWrapper.prototype.getOption = function(key, opt_default) {};
|
||||
google.visualization.ControlWrapper.prototype.getOptions = function() {};
|
||||
google.visualization.ControlWrapper.prototype.setDataSourceUrl = function(dataSourceUrl) {};
|
||||
google.visualization.ControlWrapper.prototype.setDataTable = function(dataTable) {};
|
||||
google.visualization.ControlWrapper.prototype.setControlName = function(controlName) {};
|
||||
google.visualization.ControlWrapper.prototype.setControlType = function(controlType) {};
|
||||
google.visualization.ControlWrapper.prototype.setContainerId = function(containerId) {};
|
||||
google.visualization.ControlWrapper.prototype.setQuery = function(query) {};
|
||||
google.visualization.ControlWrapper.prototype.setRefreshInterval = function(refreshInterval) {};
|
||||
google.visualization.ControlWrapper.prototype.setView = function(view) {};
|
||||
google.visualization.ControlWrapper.prototype.setOption = function(key, value) {};
|
||||
google.visualization.ControlWrapper.prototype.setOptions = function(options) {};
|
||||
|
||||
/** @return {string} JSON representation. */
|
||||
google.visualization.ChartWrapper.prototype.toJSON = function() {};
|
||||
|
||||
/** @constructor */
|
||||
google.visualization.ChartEditor = function(opt_config) {};
|
||||
google.visualization.ChartEditor.prototype.openDialog = function(specification, opt_options) {};
|
||||
google.visualization.ChartEditor.prototype.getChartWrapper = function() {};
|
||||
google.visualization.ChartEditor.prototype.setChartWrapper = function(chartWrapper) {};
|
||||
google.visualization.ChartEditor.prototype.closeDialog = function() {};
|
||||
|
||||
/** @constructor */
|
||||
google.visualization.Dashboard = function(container) {};
|
||||
google.visualization.Dashboard.prototype.bind = function(controls, participants) {};
|
||||
google.visualization.Dashboard.prototype.draw = function(dataTable) {};
|
||||
|
||||
/** @constructor */
|
||||
google.visualization.StringFilter = function(container) {};
|
||||
google.visualization.StringFilter.prototype.draw = function(dataTable, opt_options, opt_state) {};
|
||||
google.visualization.StringFilter.prototype.applyFilter = function() {};
|
||||
google.visualization.StringFilter.prototype.getState = function() {};
|
||||
google.visualization.StringFilter.prototype.resetControl = function() {};
|
||||
|
||||
/** @constructor */
|
||||
google.visualization.NumberRangeFilter = function(container) {};
|
||||
google.visualization.NumberRangeFilter.prototype.draw = function(dataTable, opt_options, opt_state) {};
|
||||
google.visualization.NumberRangeFilter.prototype.applyFilter = function() {};
|
||||
google.visualization.NumberRangeFilter.prototype.getState = function() {};
|
||||
google.visualization.NumberRangeFilter.prototype.resetControl = function() {};
|
||||
|
||||
/** @constructor */
|
||||
google.visualization.CategoryFilter = function(container) {};
|
||||
google.visualization.CategoryFilter.prototype.draw = function(dataTable, opt_options, opt_state) {};
|
||||
google.visualization.CategoryFilter.prototype.applyFilter = function() {};
|
||||
google.visualization.CategoryFilter.prototype.getState = function() {};
|
||||
google.visualization.CategoryFilter.prototype.resetControl = function() {};
|
||||
|
||||
/** @constructor */
|
||||
google.visualization.ChartRangeFilter = function(container) {};
|
||||
google.visualization.ChartRangeFilter.prototype.draw = function(dataTable, opt_options, opt_state) {};
|
||||
google.visualization.ChartRangeFilter.prototype.applyFilter = function() {};
|
||||
google.visualization.ChartRangeFilter.prototype.getState = function() {};
|
||||
google.visualization.ChartRangeFilter.prototype.resetControl = function() {};
|
@@ -0,0 +1,33 @@
|
||||
#aspect_artifactbrowser_CommunityBrowser_list_comunity-browser ul {
|
||||
padding: 0 0 0 0px;
|
||||
margin: -4px 5px 3px 0;
|
||||
}
|
||||
#aspect_artifactbrowser_CommunityBrowser_list_comunity-browser li {
|
||||
list-style:none;
|
||||
margin: 8px 0 0 20px;
|
||||
padding: 2px 0 0 0;
|
||||
text-indent: -40px;
|
||||
}
|
||||
#aspect_artifactbrowser_CommunityBrowser_list_comunity-browser ul.hide {
|
||||
display:none;
|
||||
}
|
||||
#aspect_artifactbrowser_CommunityBrowser_list_comunity-browser span {
|
||||
color:#999;
|
||||
font-family:"Courier New", Courier, monospace;
|
||||
font-size: 1.2em;
|
||||
font-weight: bold;
|
||||
cursor:default;
|
||||
}
|
||||
#aspect_artifactbrowser_CommunityBrowser_list_comunity-browser span.expanded, #aspect_artifactbrowser_CommunityBrowser_list_comunity-browser span.collapsed {
|
||||
cursor:pointer;
|
||||
}
|
||||
|
||||
|
||||
#aspect_artifactbrowser_CommunityBrowser_list_comunity-browser a.community {
|
||||
color: #5e7c5e;
|
||||
font-weight: bold;
|
||||
}
|
||||
#aspect_artifactbrowser_CommunityBrowser_list_comunity-browser a.collection {
|
||||
color: #005b7f;
|
||||
font-weight: bold;
|
||||
}
|
@@ -0,0 +1,106 @@
|
||||
/**
|
||||
Project: Collapsible Checkbox Tree jQuery Plugin
|
||||
Version: 1.0.1
|
||||
Author: Lewis Jenkins
|
||||
Website: http://www.redcarrot.co.uk/2009/11/11/collapsible-checkbox-tree-jquery-plugin/
|
||||
|
||||
License:
|
||||
The CheckTree jQuery plugin is currently available for use in all personal or
|
||||
commercial projects under both MIT and GPL licenses. This means that you can choose
|
||||
the license that best suits your project and use it accordingly.
|
||||
*/
|
||||
(function($) {
|
||||
|
||||
$.fn.collapsibleCheckboxTree = function(options) {
|
||||
|
||||
var defaults = {
|
||||
checkParents : true, // When checking a box, all parents are checked
|
||||
checkChildren : false, // When checking a box, all children are checked
|
||||
uncheckChildren : true, // When unchecking a box, all children are unchecked
|
||||
initialState : 'default' // Options - 'expand' (fully expanded), 'collapse' (fully collapsed) or default
|
||||
};
|
||||
|
||||
var options = $.extend(defaults, options);
|
||||
|
||||
this.each(function() {
|
||||
|
||||
var $root = this;
|
||||
|
||||
// Add button
|
||||
$(this).before('<button id="expand">Expand All</button><button id="collapse">Collapse All</button>');
|
||||
|
||||
// Hide all except top level
|
||||
$("ul", $(this)).addClass('hide');
|
||||
|
||||
// Add tree links
|
||||
$("li", $(this)).prepend('<span class="something"> </span>');
|
||||
$("li:has(> ul:not(.hide)) > span", $(this)).addClass('expanded').html('-');
|
||||
$("li:has(> ul.hide) > span", $(this)).addClass('collapsed').html('+');
|
||||
|
||||
// Tree function
|
||||
$("li:has(> ul) span", $(this)).click(function(){
|
||||
|
||||
// If was previously collapsed...
|
||||
if ($(this).is(".collapsed")) {
|
||||
|
||||
// ... then expand
|
||||
$("> ul", $(this).parent("li")).removeClass('hide');
|
||||
// ... and update the html
|
||||
$(this).removeClass("collapsed").addClass("expanded").html('-');
|
||||
|
||||
// If was previously expanded...
|
||||
} else if ($(this).is(".expanded")) {
|
||||
|
||||
// ... then collapse
|
||||
$("> ul", $(this).parent("li")).addClass('hide');
|
||||
// and update the html
|
||||
$(this).removeClass("expanded").addClass("collapsed").html('+');
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
// Button functions
|
||||
|
||||
// Expand all
|
||||
$("#expand").click(function () {
|
||||
// Show all children
|
||||
$("ul", $root).removeClass('hide');
|
||||
// and update the html
|
||||
$("li:has(> ul) > span", $root).removeClass("collapsed").addClass("expanded").html('-');
|
||||
return false;
|
||||
});
|
||||
// Collapse all
|
||||
$("#collapse").click(function () {
|
||||
// Hide all children
|
||||
$("ul", $root).addClass('hide');
|
||||
// and update the html
|
||||
$("li:has(> ul) > span", $root).removeClass("expanded").addClass("collapsed").html('+');
|
||||
return false;
|
||||
});
|
||||
// Wrap around checked boxes
|
||||
$("#default").click(function () {
|
||||
// Hide all except top level
|
||||
$("ul", $root).addClass('hide');
|
||||
|
||||
// and update the html
|
||||
$("li:has(> ul:not(.hide)) > span", $root).removeClass('collapsed').addClass('expanded').html('-');
|
||||
$("li:has(> ul.hide) > span", $root).removeClass('expanded').addClass('collapsed').html('+');
|
||||
return false;
|
||||
});
|
||||
|
||||
switch(defaults.initialState) {
|
||||
case 'expand':
|
||||
$("#expand").trigger('click');
|
||||
break;
|
||||
case 'collapse':
|
||||
$("#collapse").trigger('click');
|
||||
break;
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
return this;
|
||||
|
||||
};
|
||||
|
||||
})(jQuery);
|
@@ -0,0 +1,4 @@
|
||||
jQuery(document).ready(function()
|
||||
{
|
||||
$('ul#aspect_artifactbrowser_CommunityBrowser_list_comunity-browser').collapsibleCheckboxTree();
|
||||
});
|
32
dspace-xmlui/dspace-xmlui-webapp/src/main/webapp/themes/dashboard/lib/jquery-1.2.min.js
vendored
Normal file
After Width: | Height: | Size: 180 B |
After Width: | Height: | Size: 178 B |
After Width: | Height: | Size: 120 B |
After Width: | Height: | Size: 105 B |
After Width: | Height: | Size: 111 B |
After Width: | Height: | Size: 110 B |
After Width: | Height: | Size: 119 B |
After Width: | Height: | Size: 101 B |
After Width: | Height: | Size: 4.3 KiB |
After Width: | Height: | Size: 4.3 KiB |
After Width: | Height: | Size: 4.3 KiB |
After Width: | Height: | Size: 4.3 KiB |
After Width: | Height: | Size: 4.3 KiB |
@@ -0,0 +1,354 @@
|
||||
/*
|
||||
* jQuery UI CSS Framework 1.8.18
|
||||
*
|
||||
* Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
|
||||
* Dual licensed under the MIT or GPL Version 2 licenses.
|
||||
* http://jquery.org/license
|
||||
*
|
||||
* http://docs.jquery.com/UI/Theming/API
|
||||
*/
|
||||
|
||||
/* Layout helpers
|
||||
----------------------------------*/
|
||||
.ui-helper-hidden { display: none; }
|
||||
.ui-helper-hidden-accessible { position: absolute !important; clip: rect(1px 1px 1px 1px); clip: rect(1px,1px,1px,1px); }
|
||||
.ui-helper-reset { margin: 0; padding: 0; border: 0; outline: 0; line-height: 1.3; text-decoration: none; font-size: 100%; list-style: none; }
|
||||
.ui-helper-clearfix:before, .ui-helper-clearfix:after { content: ""; display: table; }
|
||||
.ui-helper-clearfix:after { clear: both; }
|
||||
.ui-helper-clearfix { zoom: 1; }
|
||||
.ui-helper-zfix { width: 100%; height: 100%; top: 0; left: 0; position: absolute; opacity: 0; filter:Alpha(Opacity=0); }
|
||||
|
||||
|
||||
/* Interaction Cues
|
||||
----------------------------------*/
|
||||
.ui-state-disabled { cursor: default !important; }
|
||||
|
||||
|
||||
/* Icons
|
||||
----------------------------------*/
|
||||
|
||||
/* states and images */
|
||||
.ui-icon { display: block; text-indent: -99999px; overflow: hidden; background-repeat: no-repeat; }
|
||||
|
||||
|
||||
/* Misc visuals
|
||||
----------------------------------*/
|
||||
|
||||
/* Overlays */
|
||||
.ui-widget-overlay { position: absolute; top: 0; left: 0; width: 100%; height: 100%; }
|
||||
|
||||
|
||||
/*
|
||||
* jQuery UI CSS Framework 1.8.18
|
||||
*
|
||||
* Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
|
||||
* Dual licensed under the MIT or GPL Version 2 licenses.
|
||||
* http://jquery.org/license
|
||||
*
|
||||
* http://docs.jquery.com/UI/Theming/API
|
||||
*
|
||||
* To view and modify this theme, visit http://jqueryui.com/themeroller/?ffDefault=Verdana,Arial,sans-serif&fwDefault=normal&fsDefault=1.1em&cornerRadius=4px&bgColorHeader=cccccc&bgTextureHeader=03_highlight_soft.png&bgImgOpacityHeader=75&borderColorHeader=aaaaaa&fcHeader=222222&iconColorHeader=222222&bgColorContent=ffffff&bgTextureContent=01_flat.png&bgImgOpacityContent=75&borderColorContent=aaaaaa&fcContent=222222&iconColorContent=222222&bgColorDefault=e6e6e6&bgTextureDefault=02_glass.png&bgImgOpacityDefault=75&borderColorDefault=d3d3d3&fcDefault=555555&iconColorDefault=888888&bgColorHover=dadada&bgTextureHover=02_glass.png&bgImgOpacityHover=75&borderColorHover=999999&fcHover=212121&iconColorHover=454545&bgColorActive=ffffff&bgTextureActive=02_glass.png&bgImgOpacityActive=65&borderColorActive=aaaaaa&fcActive=212121&iconColorActive=454545&bgColorHighlight=fbf9ee&bgTextureHighlight=02_glass.png&bgImgOpacityHighlight=55&borderColorHighlight=fcefa1&fcHighlight=363636&iconColorHighlight=2e83ff&bgColorError=fef1ec&bgTextureError=02_glass.png&bgImgOpacityError=95&borderColorError=cd0a0a&fcError=cd0a0a&iconColorError=cd0a0a&bgColorOverlay=aaaaaa&bgTextureOverlay=01_flat.png&bgImgOpacityOverlay=0&opacityOverlay=30&bgColorShadow=aaaaaa&bgTextureShadow=01_flat.png&bgImgOpacityShadow=0&opacityShadow=30&thicknessShadow=8px&offsetTopShadow=-8px&offsetLeftShadow=-8px&cornerRadiusShadow=8px
|
||||
*/
|
||||
|
||||
|
||||
/* Component containers
|
||||
----------------------------------*/
|
||||
.ui-widget { font-family: Verdana,Arial,sans-serif; font-size: 1.1em; }
|
||||
.ui-widget .ui-widget { font-size: 1em; }
|
||||
.ui-widget input, .ui-widget select, .ui-widget textarea, .ui-widget button { font-family: Verdana,Arial,sans-serif; font-size: 1em; }
|
||||
.ui-widget-content { border: 1px solid #aaaaaa; background: #ffffff url(images/ui-bg_flat_75_ffffff_40x100.png) 50% 50% repeat-x; color: #222222; }
|
||||
.ui-widget-content a { color: #222222; }
|
||||
.ui-widget-header { border: 1px solid #aaaaaa; background: #cccccc url(images/ui-bg_highlight-soft_75_cccccc_1x100.png) 50% 50% repeat-x; color: #222222; font-weight: bold; }
|
||||
.ui-widget-header a { color: #222222; }
|
||||
|
||||
/* Interaction states
|
||||
----------------------------------*/
|
||||
.ui-state-default, .ui-widget-content .ui-state-default, .ui-widget-header .ui-state-default { border: 1px solid #d3d3d3; background: #e6e6e6 url(images/ui-bg_glass_75_e6e6e6_1x400.png) 50% 50% repeat-x; font-weight: normal; color: #555555; }
|
||||
.ui-state-default a, .ui-state-default a:link, .ui-state-default a:visited { color: #555555; text-decoration: none; }
|
||||
.ui-state-hover, .ui-widget-content .ui-state-hover, .ui-widget-header .ui-state-hover, .ui-state-focus, .ui-widget-content .ui-state-focus, .ui-widget-header .ui-state-focus { border: 1px solid #999999; background: #dadada url(images/ui-bg_glass_75_dadada_1x400.png) 50% 50% repeat-x; font-weight: normal; color: #212121; }
|
||||
.ui-state-hover a, .ui-state-hover a:hover { color: #212121; text-decoration: none; }
|
||||
.ui-state-active, .ui-widget-content .ui-state-active, .ui-widget-header .ui-state-active { border: 1px solid #aaaaaa; background: #ffffff url(images/ui-bg_glass_65_ffffff_1x400.png) 50% 50% repeat-x; font-weight: normal; color: #212121; }
|
||||
.ui-state-active a, .ui-state-active a:link, .ui-state-active a:visited { color: #212121; text-decoration: none; }
|
||||
.ui-widget :active { outline: none; }
|
||||
|
||||
/* Interaction Cues
|
||||
----------------------------------*/
|
||||
.ui-state-highlight, .ui-widget-content .ui-state-highlight, .ui-widget-header .ui-state-highlight {border: 1px solid #fcefa1; background: #fbf9ee url(images/ui-bg_glass_55_fbf9ee_1x400.png) 50% 50% repeat-x; color: #363636; }
|
||||
.ui-state-highlight a, .ui-widget-content .ui-state-highlight a,.ui-widget-header .ui-state-highlight a { color: #363636; }
|
||||
.ui-state-error, .ui-widget-content .ui-state-error, .ui-widget-header .ui-state-error {border: 1px solid #cd0a0a; background: #fef1ec url(images/ui-bg_glass_95_fef1ec_1x400.png) 50% 50% repeat-x; color: #cd0a0a; }
|
||||
.ui-state-error a, .ui-widget-content .ui-state-error a, .ui-widget-header .ui-state-error a { color: #cd0a0a; }
|
||||
.ui-state-error-text, .ui-widget-content .ui-state-error-text, .ui-widget-header .ui-state-error-text { color: #cd0a0a; }
|
||||
.ui-priority-primary, .ui-widget-content .ui-priority-primary, .ui-widget-header .ui-priority-primary { font-weight: bold; }
|
||||
.ui-priority-secondary, .ui-widget-content .ui-priority-secondary, .ui-widget-header .ui-priority-secondary { opacity: .7; filter:Alpha(Opacity=70); font-weight: normal; }
|
||||
.ui-state-disabled, .ui-widget-content .ui-state-disabled, .ui-widget-header .ui-state-disabled { opacity: .35; filter:Alpha(Opacity=35); background-image: none; }
|
||||
|
||||
/* Icons
|
||||
----------------------------------*/
|
||||
|
||||
/* states and images */
|
||||
.ui-icon { width: 16px; height: 16px; background-image: url(images/ui-icons_222222_256x240.png); }
|
||||
.ui-widget-content .ui-icon {background-image: url(images/ui-icons_222222_256x240.png); }
|
||||
.ui-widget-header .ui-icon {background-image: url(images/ui-icons_222222_256x240.png); }
|
||||
.ui-state-default .ui-icon { background-image: url(images/ui-icons_888888_256x240.png); }
|
||||
.ui-state-hover .ui-icon, .ui-state-focus .ui-icon {background-image: url(images/ui-icons_454545_256x240.png); }
|
||||
.ui-state-active .ui-icon {background-image: url(images/ui-icons_454545_256x240.png); }
|
||||
.ui-state-highlight .ui-icon {background-image: url(images/ui-icons_2e83ff_256x240.png); }
|
||||
.ui-state-error .ui-icon, .ui-state-error-text .ui-icon {background-image: url(images/ui-icons_cd0a0a_256x240.png); }
|
||||
|
||||
/* positioning */
|
||||
.ui-icon-carat-1-n { background-position: 0 0; }
|
||||
.ui-icon-carat-1-ne { background-position: -16px 0; }
|
||||
.ui-icon-carat-1-e { background-position: -32px 0; }
|
||||
.ui-icon-carat-1-se { background-position: -48px 0; }
|
||||
.ui-icon-carat-1-s { background-position: -64px 0; }
|
||||
.ui-icon-carat-1-sw { background-position: -80px 0; }
|
||||
.ui-icon-carat-1-w { background-position: -96px 0; }
|
||||
.ui-icon-carat-1-nw { background-position: -112px 0; }
|
||||
.ui-icon-carat-2-n-s { background-position: -128px 0; }
|
||||
.ui-icon-carat-2-e-w { background-position: -144px 0; }
|
||||
.ui-icon-triangle-1-n { background-position: 0 -16px; }
|
||||
.ui-icon-triangle-1-ne { background-position: -16px -16px; }
|
||||
.ui-icon-triangle-1-e { background-position: -32px -16px; }
|
||||
.ui-icon-triangle-1-se { background-position: -48px -16px; }
|
||||
.ui-icon-triangle-1-s { background-position: -64px -16px; }
|
||||
.ui-icon-triangle-1-sw { background-position: -80px -16px; }
|
||||
.ui-icon-triangle-1-w { background-position: -96px -16px; }
|
||||
.ui-icon-triangle-1-nw { background-position: -112px -16px; }
|
||||
.ui-icon-triangle-2-n-s { background-position: -128px -16px; }
|
||||
.ui-icon-triangle-2-e-w { background-position: -144px -16px; }
|
||||
.ui-icon-arrow-1-n { background-position: 0 -32px; }
|
||||
.ui-icon-arrow-1-ne { background-position: -16px -32px; }
|
||||
.ui-icon-arrow-1-e { background-position: -32px -32px; }
|
||||
.ui-icon-arrow-1-se { background-position: -48px -32px; }
|
||||
.ui-icon-arrow-1-s { background-position: -64px -32px; }
|
||||
.ui-icon-arrow-1-sw { background-position: -80px -32px; }
|
||||
.ui-icon-arrow-1-w { background-position: -96px -32px; }
|
||||
.ui-icon-arrow-1-nw { background-position: -112px -32px; }
|
||||
.ui-icon-arrow-2-n-s { background-position: -128px -32px; }
|
||||
.ui-icon-arrow-2-ne-sw { background-position: -144px -32px; }
|
||||
.ui-icon-arrow-2-e-w { background-position: -160px -32px; }
|
||||
.ui-icon-arrow-2-se-nw { background-position: -176px -32px; }
|
||||
.ui-icon-arrowstop-1-n { background-position: -192px -32px; }
|
||||
.ui-icon-arrowstop-1-e { background-position: -208px -32px; }
|
||||
.ui-icon-arrowstop-1-s { background-position: -224px -32px; }
|
||||
.ui-icon-arrowstop-1-w { background-position: -240px -32px; }
|
||||
.ui-icon-arrowthick-1-n { background-position: 0 -48px; }
|
||||
.ui-icon-arrowthick-1-ne { background-position: -16px -48px; }
|
||||
.ui-icon-arrowthick-1-e { background-position: -32px -48px; }
|
||||
.ui-icon-arrowthick-1-se { background-position: -48px -48px; }
|
||||
.ui-icon-arrowthick-1-s { background-position: -64px -48px; }
|
||||
.ui-icon-arrowthick-1-sw { background-position: -80px -48px; }
|
||||
.ui-icon-arrowthick-1-w { background-position: -96px -48px; }
|
||||
.ui-icon-arrowthick-1-nw { background-position: -112px -48px; }
|
||||
.ui-icon-arrowthick-2-n-s { background-position: -128px -48px; }
|
||||
.ui-icon-arrowthick-2-ne-sw { background-position: -144px -48px; }
|
||||
.ui-icon-arrowthick-2-e-w { background-position: -160px -48px; }
|
||||
.ui-icon-arrowthick-2-se-nw { background-position: -176px -48px; }
|
||||
.ui-icon-arrowthickstop-1-n { background-position: -192px -48px; }
|
||||
.ui-icon-arrowthickstop-1-e { background-position: -208px -48px; }
|
||||
.ui-icon-arrowthickstop-1-s { background-position: -224px -48px; }
|
||||
.ui-icon-arrowthickstop-1-w { background-position: -240px -48px; }
|
||||
.ui-icon-arrowreturnthick-1-w { background-position: 0 -64px; }
|
||||
.ui-icon-arrowreturnthick-1-n { background-position: -16px -64px; }
|
||||
.ui-icon-arrowreturnthick-1-e { background-position: -32px -64px; }
|
||||
.ui-icon-arrowreturnthick-1-s { background-position: -48px -64px; }
|
||||
.ui-icon-arrowreturn-1-w { background-position: -64px -64px; }
|
||||
.ui-icon-arrowreturn-1-n { background-position: -80px -64px; }
|
||||
.ui-icon-arrowreturn-1-e { background-position: -96px -64px; }
|
||||
.ui-icon-arrowreturn-1-s { background-position: -112px -64px; }
|
||||
.ui-icon-arrowrefresh-1-w { background-position: -128px -64px; }
|
||||
.ui-icon-arrowrefresh-1-n { background-position: -144px -64px; }
|
||||
.ui-icon-arrowrefresh-1-e { background-position: -160px -64px; }
|
||||
.ui-icon-arrowrefresh-1-s { background-position: -176px -64px; }
|
||||
.ui-icon-arrow-4 { background-position: 0 -80px; }
|
||||
.ui-icon-arrow-4-diag { background-position: -16px -80px; }
|
||||
.ui-icon-extlink { background-position: -32px -80px; }
|
||||
.ui-icon-newwin { background-position: -48px -80px; }
|
||||
.ui-icon-refresh { background-position: -64px -80px; }
|
||||
.ui-icon-shuffle { background-position: -80px -80px; }
|
||||
.ui-icon-transfer-e-w { background-position: -96px -80px; }
|
||||
.ui-icon-transferthick-e-w { background-position: -112px -80px; }
|
||||
.ui-icon-folder-collapsed { background-position: 0 -96px; }
|
||||
.ui-icon-folder-open { background-position: -16px -96px; }
|
||||
.ui-icon-document { background-position: -32px -96px; }
|
||||
.ui-icon-document-b { background-position: -48px -96px; }
|
||||
.ui-icon-note { background-position: -64px -96px; }
|
||||
.ui-icon-mail-closed { background-position: -80px -96px; }
|
||||
.ui-icon-mail-open { background-position: -96px -96px; }
|
||||
.ui-icon-suitcase { background-position: -112px -96px; }
|
||||
.ui-icon-comment { background-position: -128px -96px; }
|
||||
.ui-icon-person { background-position: -144px -96px; }
|
||||
.ui-icon-print { background-position: -160px -96px; }
|
||||
.ui-icon-trash { background-position: -176px -96px; }
|
||||
.ui-icon-locked { background-position: -192px -96px; }
|
||||
.ui-icon-unlocked { background-position: -208px -96px; }
|
||||
.ui-icon-bookmark { background-position: -224px -96px; }
|
||||
.ui-icon-tag { background-position: -240px -96px; }
|
||||
.ui-icon-home { background-position: 0 -112px; }
|
||||
.ui-icon-flag { background-position: -16px -112px; }
|
||||
.ui-icon-calendar { background-position: -32px -112px; }
|
||||
.ui-icon-cart { background-position: -48px -112px; }
|
||||
.ui-icon-pencil { background-position: -64px -112px; }
|
||||
.ui-icon-clock { background-position: -80px -112px; }
|
||||
.ui-icon-disk { background-position: -96px -112px; }
|
||||
.ui-icon-calculator { background-position: -112px -112px; }
|
||||
.ui-icon-zoomin { background-position: -128px -112px; }
|
||||
.ui-icon-zoomout { background-position: -144px -112px; }
|
||||
.ui-icon-search { background-position: -160px -112px; }
|
||||
.ui-icon-wrench { background-position: -176px -112px; }
|
||||
.ui-icon-gear { background-position: -192px -112px; }
|
||||
.ui-icon-heart { background-position: -208px -112px; }
|
||||
.ui-icon-star { background-position: -224px -112px; }
|
||||
.ui-icon-link { background-position: -240px -112px; }
|
||||
.ui-icon-cancel { background-position: 0 -128px; }
|
||||
.ui-icon-plus { background-position: -16px -128px; }
|
||||
.ui-icon-plusthick { background-position: -32px -128px; }
|
||||
.ui-icon-minus { background-position: -48px -128px; }
|
||||
.ui-icon-minusthick { background-position: -64px -128px; }
|
||||
.ui-icon-close { background-position: -80px -128px; }
|
||||
.ui-icon-closethick { background-position: -96px -128px; }
|
||||
.ui-icon-key { background-position: -112px -128px; }
|
||||
.ui-icon-lightbulb { background-position: -128px -128px; }
|
||||
.ui-icon-scissors { background-position: -144px -128px; }
|
||||
.ui-icon-clipboard { background-position: -160px -128px; }
|
||||
.ui-icon-copy { background-position: -176px -128px; }
|
||||
.ui-icon-contact { background-position: -192px -128px; }
|
||||
.ui-icon-image { background-position: -208px -128px; }
|
||||
.ui-icon-video { background-position: -224px -128px; }
|
||||
.ui-icon-script { background-position: -240px -128px; }
|
||||
.ui-icon-alert { background-position: 0 -144px; }
|
||||
.ui-icon-info { background-position: -16px -144px; }
|
||||
.ui-icon-notice { background-position: -32px -144px; }
|
||||
.ui-icon-help { background-position: -48px -144px; }
|
||||
.ui-icon-check { background-position: -64px -144px; }
|
||||
.ui-icon-bullet { background-position: -80px -144px; }
|
||||
.ui-icon-radio-off { background-position: -96px -144px; }
|
||||
.ui-icon-radio-on { background-position: -112px -144px; }
|
||||
.ui-icon-pin-w { background-position: -128px -144px; }
|
||||
.ui-icon-pin-s { background-position: -144px -144px; }
|
||||
.ui-icon-play { background-position: 0 -160px; }
|
||||
.ui-icon-pause { background-position: -16px -160px; }
|
||||
.ui-icon-seek-next { background-position: -32px -160px; }
|
||||
.ui-icon-seek-prev { background-position: -48px -160px; }
|
||||
.ui-icon-seek-end { background-position: -64px -160px; }
|
||||
.ui-icon-seek-start { background-position: -80px -160px; }
|
||||
/* ui-icon-seek-first is deprecated, use ui-icon-seek-start instead */
|
||||
.ui-icon-seek-first { background-position: -80px -160px; }
|
||||
.ui-icon-stop { background-position: -96px -160px; }
|
||||
.ui-icon-eject { background-position: -112px -160px; }
|
||||
.ui-icon-volume-off { background-position: -128px -160px; }
|
||||
.ui-icon-volume-on { background-position: -144px -160px; }
|
||||
.ui-icon-power { background-position: 0 -176px; }
|
||||
.ui-icon-signal-diag { background-position: -16px -176px; }
|
||||
.ui-icon-signal { background-position: -32px -176px; }
|
||||
.ui-icon-battery-0 { background-position: -48px -176px; }
|
||||
.ui-icon-battery-1 { background-position: -64px -176px; }
|
||||
.ui-icon-battery-2 { background-position: -80px -176px; }
|
||||
.ui-icon-battery-3 { background-position: -96px -176px; }
|
||||
.ui-icon-circle-plus { background-position: 0 -192px; }
|
||||
.ui-icon-circle-minus { background-position: -16px -192px; }
|
||||
.ui-icon-circle-close { background-position: -32px -192px; }
|
||||
.ui-icon-circle-triangle-e { background-position: -48px -192px; }
|
||||
.ui-icon-circle-triangle-s { background-position: -64px -192px; }
|
||||
.ui-icon-circle-triangle-w { background-position: -80px -192px; }
|
||||
.ui-icon-circle-triangle-n { background-position: -96px -192px; }
|
||||
.ui-icon-circle-arrow-e { background-position: -112px -192px; }
|
||||
.ui-icon-circle-arrow-s { background-position: -128px -192px; }
|
||||
.ui-icon-circle-arrow-w { background-position: -144px -192px; }
|
||||
.ui-icon-circle-arrow-n { background-position: -160px -192px; }
|
||||
.ui-icon-circle-zoomin { background-position: -176px -192px; }
|
||||
.ui-icon-circle-zoomout { background-position: -192px -192px; }
|
||||
.ui-icon-circle-check { background-position: -208px -192px; }
|
||||
.ui-icon-circlesmall-plus { background-position: 0 -208px; }
|
||||
.ui-icon-circlesmall-minus { background-position: -16px -208px; }
|
||||
.ui-icon-circlesmall-close { background-position: -32px -208px; }
|
||||
.ui-icon-squaresmall-plus { background-position: -48px -208px; }
|
||||
.ui-icon-squaresmall-minus { background-position: -64px -208px; }
|
||||
.ui-icon-squaresmall-close { background-position: -80px -208px; }
|
||||
.ui-icon-grip-dotted-vertical { background-position: 0 -224px; }
|
||||
.ui-icon-grip-dotted-horizontal { background-position: -16px -224px; }
|
||||
.ui-icon-grip-solid-vertical { background-position: -32px -224px; }
|
||||
.ui-icon-grip-solid-horizontal { background-position: -48px -224px; }
|
||||
.ui-icon-gripsmall-diagonal-se { background-position: -64px -224px; }
|
||||
.ui-icon-grip-diagonal-se { background-position: -80px -224px; }
|
||||
|
||||
|
||||
/* Misc visuals
|
||||
----------------------------------*/
|
||||
|
||||
/* Corner radius */
|
||||
.ui-corner-all, .ui-corner-top, .ui-corner-left, .ui-corner-tl { -moz-border-radius-topleft: 4px; -webkit-border-top-left-radius: 4px; -khtml-border-top-left-radius: 4px; border-top-left-radius: 4px; }
|
||||
.ui-corner-all, .ui-corner-top, .ui-corner-right, .ui-corner-tr { -moz-border-radius-topright: 4px; -webkit-border-top-right-radius: 4px; -khtml-border-top-right-radius: 4px; border-top-right-radius: 4px; }
|
||||
.ui-corner-all, .ui-corner-bottom, .ui-corner-left, .ui-corner-bl { -moz-border-radius-bottomleft: 4px; -webkit-border-bottom-left-radius: 4px; -khtml-border-bottom-left-radius: 4px; border-bottom-left-radius: 4px; }
|
||||
.ui-corner-all, .ui-corner-bottom, .ui-corner-right, .ui-corner-br { -moz-border-radius-bottomright: 4px; -webkit-border-bottom-right-radius: 4px; -khtml-border-bottom-right-radius: 4px; border-bottom-right-radius: 4px; }
|
||||
|
||||
/* Overlays */
|
||||
.ui-widget-overlay { background: #aaaaaa url(images/ui-bg_flat_0_aaaaaa_40x100.png) 50% 50% repeat-x; opacity: .30;filter:Alpha(Opacity=30); }
|
||||
.ui-widget-shadow { margin: -8px 0 0 -8px; padding: 8px; background: #aaaaaa url(images/ui-bg_flat_0_aaaaaa_40x100.png) 50% 50% repeat-x; opacity: .30;filter:Alpha(Opacity=30); -moz-border-radius: 8px; -khtml-border-radius: 8px; -webkit-border-radius: 8px; border-radius: 8px; }/*
|
||||
* jQuery UI Datepicker 1.8.18
|
||||
*
|
||||
* Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
|
||||
* Dual licensed under the MIT or GPL Version 2 licenses.
|
||||
* http://jquery.org/license
|
||||
*
|
||||
* http://docs.jquery.com/UI/Datepicker#theming
|
||||
*/
|
||||
.ui-datepicker { width: 17em; padding: .2em .2em 0; display: none; }
|
||||
.ui-datepicker .ui-datepicker-header { position:relative; padding:.2em 0; }
|
||||
.ui-datepicker .ui-datepicker-prev, .ui-datepicker .ui-datepicker-next { position:absolute; top: 2px; width: 1.8em; height: 1.8em; }
|
||||
.ui-datepicker .ui-datepicker-prev-hover, .ui-datepicker .ui-datepicker-next-hover { top: 1px; }
|
||||
.ui-datepicker .ui-datepicker-prev { left:2px; }
|
||||
.ui-datepicker .ui-datepicker-next { right:2px; }
|
||||
.ui-datepicker .ui-datepicker-prev-hover { left:1px; }
|
||||
.ui-datepicker .ui-datepicker-next-hover { right:1px; }
|
||||
.ui-datepicker .ui-datepicker-prev span, .ui-datepicker .ui-datepicker-next span { display: block; position: absolute; left: 50%; margin-left: -8px; top: 50%; margin-top: -8px; }
|
||||
.ui-datepicker .ui-datepicker-title { margin: 0 2.3em; line-height: 1.8em; text-align: center; }
|
||||
.ui-datepicker .ui-datepicker-title select { font-size:1em; margin:1px 0; }
|
||||
.ui-datepicker select.ui-datepicker-month-year {width: 100%;}
|
||||
.ui-datepicker select.ui-datepicker-month,
|
||||
.ui-datepicker select.ui-datepicker-year { width: 49%;}
|
||||
.ui-datepicker table {width: 100%; font-size: .9em; border-collapse: collapse; margin:0 0 .4em; }
|
||||
.ui-datepicker th { padding: .7em .3em; text-align: center; font-weight: bold; border: 0; }
|
||||
.ui-datepicker td { border: 0; padding: 1px; }
|
||||
.ui-datepicker td span, .ui-datepicker td a { display: block; padding: .2em; text-align: right; text-decoration: none; }
|
||||
.ui-datepicker .ui-datepicker-buttonpane { background-image: none; margin: .7em 0 0 0; padding:0 .2em; border-left: 0; border-right: 0; border-bottom: 0; }
|
||||
.ui-datepicker .ui-datepicker-buttonpane button { float: right; margin: .5em .2em .4em; cursor: pointer; padding: .2em .6em .3em .6em; width:auto; overflow:visible; }
|
||||
.ui-datepicker .ui-datepicker-buttonpane button.ui-datepicker-current { float:left; }
|
||||
|
||||
/* with multiple calendars */
|
||||
.ui-datepicker.ui-datepicker-multi { width:auto; }
|
||||
.ui-datepicker-multi .ui-datepicker-group { float:left; }
|
||||
.ui-datepicker-multi .ui-datepicker-group table { width:95%; margin:0 auto .4em; }
|
||||
.ui-datepicker-multi-2 .ui-datepicker-group { width:50%; }
|
||||
.ui-datepicker-multi-3 .ui-datepicker-group { width:33.3%; }
|
||||
.ui-datepicker-multi-4 .ui-datepicker-group { width:25%; }
|
||||
.ui-datepicker-multi .ui-datepicker-group-last .ui-datepicker-header { border-left-width:0; }
|
||||
.ui-datepicker-multi .ui-datepicker-group-middle .ui-datepicker-header { border-left-width:0; }
|
||||
.ui-datepicker-multi .ui-datepicker-buttonpane { clear:left; }
|
||||
.ui-datepicker-row-break { clear:both; width:100%; font-size:0em; }
|
||||
|
||||
/* RTL support */
|
||||
.ui-datepicker-rtl { direction: rtl; }
|
||||
.ui-datepicker-rtl .ui-datepicker-prev { right: 2px; left: auto; }
|
||||
.ui-datepicker-rtl .ui-datepicker-next { left: 2px; right: auto; }
|
||||
.ui-datepicker-rtl .ui-datepicker-prev:hover { right: 1px; left: auto; }
|
||||
.ui-datepicker-rtl .ui-datepicker-next:hover { left: 1px; right: auto; }
|
||||
.ui-datepicker-rtl .ui-datepicker-buttonpane { clear:right; }
|
||||
.ui-datepicker-rtl .ui-datepicker-buttonpane button { float: left; }
|
||||
.ui-datepicker-rtl .ui-datepicker-buttonpane button.ui-datepicker-current { float:right; }
|
||||
.ui-datepicker-rtl .ui-datepicker-group { float:right; }
|
||||
.ui-datepicker-rtl .ui-datepicker-group-last .ui-datepicker-header { border-right-width:0; border-left-width:1px; }
|
||||
.ui-datepicker-rtl .ui-datepicker-group-middle .ui-datepicker-header { border-right-width:0; border-left-width:1px; }
|
||||
|
||||
/* IE6 IFRAME FIX (taken from datepicker 1.5.3 */
|
||||
.ui-datepicker-cover {
|
||||
display: none; /*sorry for IE5*/
|
||||
display/**/: block; /*sorry for IE5*/
|
||||
position: absolute; /*must have*/
|
||||
z-index: -1; /*must have*/
|
||||
filter: mask(); /*must have*/
|
||||
top: -4px; /*must have*/
|
||||
left: -4px; /*must have*/
|
||||
width: 200px; /*must have*/
|
||||
height: 200px; /*must have*/
|
||||
}
|
@@ -0,0 +1,6 @@
|
||||
// # ReportGenerator Js Mod
|
||||
// ## Addes jQuery datepicker functionality to the ReportGenerator
|
||||
|
||||
jQuery(function ($) {
|
||||
$(".date-picker").datepicker({dateFormat: "mm/dd/yy"});
|
||||
});
|
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
IE Specific stylesheets
|
||||
|
||||
style-ie.css
|
||||
|
||||
Version: $Revision$
|
||||
|
||||
Date: $Date$
|
||||
|
||||
Copyright (c) 2002-2005, Hewlett-Packard Company and Massachusetts
|
||||
Institute of Technology. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
- Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
- Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
- Neither the name of the Hewlett-Packard Company nor the name of the
|
||||
Massachusetts Institute of Technology nor the names of their
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
|
||||
TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
|
||||
USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
|
||||
DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
|
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
style.css
|
||||
Community and Collection list
|
||||
*/
|
||||
|
||||
#chart_div
|
||||
{
|
||||
/*width: 700px;*/
|
||||
/*height: 240px;*/
|
||||
}
|
||||
|
||||
#aspect_artifactbrowser_DashboardViewer_table_items_added_monthly
|
||||
{
|
||||
display: none;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
@@ -0,0 +1,175 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
sitemap.xmap
|
||||
|
||||
Version: $Revision$
|
||||
|
||||
Date: $Date$
|
||||
|
||||
Copyright (c) 2002-2005, Hewlett-Packard Company and Massachusetts
|
||||
Institute of Technology. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
- Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
- Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
- Neither the name of the Hewlett-Packard Company nor the name of the
|
||||
Massachusetts Institute of Technology nor the names of their
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
|
||||
TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
|
||||
USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
|
||||
DAMAGE.
|
||||
-->
|
||||
<map:sitemap xmlns:map="http://apache.org/cocoon/sitemap/1.0">
|
||||
<map:pipelines>
|
||||
|
||||
<!--
|
||||
Define global theme variables that are used later in this
|
||||
sitemap. Two variables are typically defined here, the theme's
|
||||
path and name. The path is the directory name where this theme
|
||||
is located, such as "Reference" for the reference theme. The
|
||||
theme's name is used only for descriptive purposes to describe
|
||||
the theme.
|
||||
-->
|
||||
<map:component-configurations>
|
||||
<global-variables>
|
||||
<theme-path>dashboard</theme-path>
|
||||
<theme-name>The theme template</theme-name>
|
||||
</global-variables>
|
||||
</map:component-configurations>
|
||||
|
||||
|
||||
<map:pipeline>
|
||||
<!-- Allow the browser to cache static content for an hour -->
|
||||
<map:parameter name="expires" value="access plus 1 hours"/>
|
||||
|
||||
<!-- Static content -->
|
||||
<map:match pattern="themes/*/**">
|
||||
<map:read src="{2}"/>
|
||||
</map:match>
|
||||
</map:pipeline>
|
||||
|
||||
<!--
|
||||
The theme's pipeline is used to process all requests handled
|
||||
by the theme. It is broken up into two parts, the first part
|
||||
handles all static theme content while the second part handle
|
||||
all dynamic aspect generated content. The static content is
|
||||
such things as stylesheets, images, or static pages. Typically
|
||||
these are just stored on disk and passed directly to the
|
||||
browser without any processing.
|
||||
-->
|
||||
<map:pipeline>
|
||||
<!-- Never allow the browser to cache dynamic content -->
|
||||
<map:parameter name="expires" value="now"/>
|
||||
|
||||
<!-- Aspect content
|
||||
|
||||
There are five steps to processing aspect content:
|
||||
|
||||
1: Generate the DRI page
|
||||
|
||||
The first step is to generate a DRI page for the request;
|
||||
this is handled by the aspect chain. Once it is generated
|
||||
it is the beginning of a theme's pipeline, the DRI page is
|
||||
ultimately transformed in the resulting XHTML that is
|
||||
given to the user's browser.
|
||||
|
||||
2: Add page metadata
|
||||
|
||||
The next step is to add theme specific metadata to the
|
||||
DRI page. This is metadata about where the theme is
|
||||
located and its name. Typically this metadata is different
|
||||
depending on the users browser, this allows us to give
|
||||
different stylesheets to Internet Explorer than for other
|
||||
browsers.
|
||||
|
||||
3: Transform to XHTML
|
||||
|
||||
The third step is the main component of a theme the XSL
|
||||
transformations will turn the DRI page from the aspects
|
||||
into an XHTML page useable by browsers.
|
||||
|
||||
4: Localize the page
|
||||
|
||||
The second to last step is to localize the content for the
|
||||
particular user, if they user is requesting a page in a
|
||||
particular language then those language strings are inserted
|
||||
into the resulting XHTML.
|
||||
|
||||
5: Serialize to the browser
|
||||
|
||||
The last step sends the page to the user's browser.
|
||||
|
||||
-->
|
||||
<map:match pattern="**">
|
||||
|
||||
<!-- Step 1: Generate the DRI page -->
|
||||
<map:generate type="file" src="cocoon://DRI/{1}"/>
|
||||
|
||||
<!-- Step 2 Add page metadata -->
|
||||
<map:select type="browser">
|
||||
<map:when test="explorer">
|
||||
<map:transform type="IncludePageMeta">
|
||||
<!--<map:parameter name="javascript#1" value="visualizeData.js" />-->
|
||||
|
||||
|
||||
<map:parameter name="theme.path" value="{global:theme-path}"/>
|
||||
<map:parameter name="theme.name" value="{global:theme-name}"/>
|
||||
<map:parameter name="stylesheet.screen#1" value="lib/style.css"/>
|
||||
<map:parameter name="stylesheet.screen#2" value="lib/jquery.ui.smoothness/jquery-ui-1.8.18.custom.css"/>
|
||||
</map:transform>
|
||||
</map:when>
|
||||
<map:otherwise>
|
||||
<map:transform type="IncludePageMeta">
|
||||
<!--<map:parameter name="javascript#1" value="visualizeData.js" />-->
|
||||
|
||||
|
||||
<map:parameter name="theme.path" value="{global:theme-path}"/>
|
||||
<map:parameter name="theme.name" value="{global:theme-name}"/>
|
||||
<map:parameter name="stylesheet.screen#1" value="lib/style.css"/>
|
||||
<map:parameter name="stylesheet.screen#2" value="lib/jquery.ui.smoothness/jquery-ui-1.8.18.custom.css"/>
|
||||
</map:transform>
|
||||
</map:otherwise>
|
||||
</map:select>
|
||||
|
||||
<!-- Debuging output -->
|
||||
<map:match type="request" pattern="XML">
|
||||
<map:serialize type="xml"/>
|
||||
</map:match>
|
||||
|
||||
<!-- Step 3: Transform to XHTML -->
|
||||
<map:transform src="{global:theme-path}.xsl"/>
|
||||
<!-- <map:transform src="template.xsl"/> -->
|
||||
|
||||
<!-- Step 4: Localize the page -->
|
||||
<map:act type="locale">
|
||||
<map:transform type="i18n">
|
||||
<map:parameter name="locale" value="{locale}"/>
|
||||
</map:transform>
|
||||
</map:act>
|
||||
|
||||
<!-- Step 5: Serialize to the browser -->
|
||||
<map:serialize type="xhtml"/>
|
||||
|
||||
</map:match>
|
||||
</map:pipeline>
|
||||
</map:pipelines>
|
||||
</map:sitemap>
|
@@ -0,0 +1,499 @@
|
||||
// # Visualize Data
|
||||
//
|
||||
// Simple script to help visualize data generated by elastic search.
|
||||
|
||||
// Firstly we wrap our code in a closure to keep variables local.
|
||||
(function (context) {
|
||||
|
||||
(function ($) {
|
||||
var dateStart = new Date($('input[name=dateStart]').val());
|
||||
var dateEnd = new Date($('input[name=dateEnd]').val());
|
||||
|
||||
// ### Chart Maker
|
||||
//
|
||||
// Create a helper module called chart maker that allows us to specify and
|
||||
// draw charts.
|
||||
context.ChartMaker = function() {
|
||||
var chartMaker = {};
|
||||
// A place to store charts to draw later.
|
||||
chartMaker.charts = {};
|
||||
|
||||
// A shortcut to the google.visualization.DataTable function.
|
||||
chartMaker.chartData = function () {
|
||||
return new google.visualization.DataTable();
|
||||
};
|
||||
|
||||
// The `addChart` function is used to add chart data to `ChartMaker`.
|
||||
// It does two things: Creates a div under the specified parent div
|
||||
// to put the chart in and add a new chart to `ChartMaker`'s internal
|
||||
// list of charts.
|
||||
//
|
||||
// `addChart` takes an object (`userConfig`) that should have the
|
||||
// following keys.
|
||||
//
|
||||
// * `entries` {object} The entries we use to create the chart.
|
||||
// * `name` {string} A nice name to give the chart so the user can reference it
|
||||
// later.
|
||||
// * `includeTotal` {boolean} Whether or not to include the total in
|
||||
// each row.
|
||||
// * `chartData` {object} An instance of ChartMaker.chartData containing the data for
|
||||
// the chart being added.
|
||||
// * `keyField` {string} The name of the key to use on entries.
|
||||
// * `valueField` {string} The name of the value to use on entries.
|
||||
// * `parentElement` {string} The parent div to create the chart under
|
||||
// * `chartType` {string} The key specifying what type of chart to
|
||||
// use from `google.visualization`.
|
||||
chartMaker.addChart = function (userConfig) {
|
||||
var c = $.extend({
|
||||
entries: [],
|
||||
name: '',
|
||||
includeTotal: false,
|
||||
chartData: null,
|
||||
dataSection: null,
|
||||
keyField: 'term',
|
||||
valueField: 'count',
|
||||
parentElement: 'aspect_dashboard_ElasticSearchStatsViewer_div_chart_div',
|
||||
chartType: 'GeoChart'
|
||||
}, userConfig);
|
||||
|
||||
// `dataValue` will eventually become the rows of data in our
|
||||
// chart.
|
||||
var dataValue = [];
|
||||
var total = 0;
|
||||
|
||||
// Cheat with dates / data, and zero-fill the start/end edges, and "hopefully" things work out...
|
||||
// Set certainty to our cheat date to false, so it gets a dotted line.
|
||||
if (c.chartData.getColumnType(0) == 'date' && isValidDate(dateStart) && c.chartType == 'LineChart') {
|
||||
var cheatDateStart = [];
|
||||
cheatDateStart.push(dateStart);
|
||||
cheatDateStart.push(0);
|
||||
if(c.includeTotal) {
|
||||
cheatDateStart.push(total);
|
||||
}
|
||||
cheatDateStart.push(false);
|
||||
dataValue.push(cheatDateStart);
|
||||
}
|
||||
|
||||
// For each entry construct a vector to add to push onto
|
||||
// `dataValue`.
|
||||
$.each(c.entries, function(index, entry) {
|
||||
if(c.dataSection != null) {
|
||||
entry = entry[c.dataSection];
|
||||
}
|
||||
|
||||
newEntry = [];
|
||||
if (c.chartData.getColumnType(0) == 'date') {
|
||||
newEntry.push(new Date(entry[c.keyField]));
|
||||
} else {
|
||||
newEntry.push(entry[c.keyField]);
|
||||
}
|
||||
|
||||
newEntry.push(entry[c.valueField]);
|
||||
if (c.includeTotal) {
|
||||
total += entry[c.valueField];
|
||||
newEntry.push(total);
|
||||
}
|
||||
|
||||
// Certain data gets a certainty score of true. (solid line)
|
||||
if (c.chartData.getColumnType(0) == 'date' && c.chartType=='LineChart') {
|
||||
newEntry.push(true);
|
||||
}
|
||||
dataValue.push(newEntry);
|
||||
});
|
||||
|
||||
//Cheat and zero-fill in the last date. Certainty is false, so dotted line.
|
||||
if (c.chartData.getColumnType(0) == 'date' && isValidDate(dateEnd) && c.chartType=='LineChart') {
|
||||
var cheatDateEnd = [];
|
||||
cheatDateEnd.push(dateEnd);
|
||||
cheatDateEnd.push(0);
|
||||
if(c.includeTotal) {
|
||||
cheatDateEnd.push(total);
|
||||
}
|
||||
cheatDateEnd.push(false);
|
||||
dataValue.push(cheatDateEnd);
|
||||
}
|
||||
|
||||
// Add rows (`dataValue`) to the chartData.
|
||||
c.chartData.addRows(dataValue);
|
||||
|
||||
// Add a child element
|
||||
var par = $('#' + c.parentElement);
|
||||
par.append("<div style='height:280px; width:750px;' " +
|
||||
"id='dspaceChart_" + c.name + "'> </div>");
|
||||
this.charts[c.name] = {
|
||||
chart: new google.visualization[c.chartType](
|
||||
document.getElementById("dspaceChart_" + c.name)),
|
||||
data: c.chartData,
|
||||
options: c.options
|
||||
};
|
||||
};
|
||||
|
||||
// `drawChart` takes a chart from ChartMaker's internal `charts` array
|
||||
// (specified by the `name` parameter) and draws that chart.
|
||||
chartMaker.drawChart = function(name, globalOptions) {
|
||||
if (typeof globalOptions === 'undefined') {
|
||||
globalOptions = {};
|
||||
}
|
||||
var cobj = this.charts[name];
|
||||
|
||||
// Allow the user to overwrite data with a passed in options.
|
||||
var data = cobj.data;
|
||||
if ('data' in globalOptions) {
|
||||
data = globalOptions.data;
|
||||
}
|
||||
|
||||
// Merge the Global Options with the local options for the chart
|
||||
var combinedOptions = $.extend(globalOptions, cobj.options);
|
||||
// Draw the named chart!
|
||||
cobj.chart.draw(data, combinedOptions);
|
||||
};
|
||||
|
||||
// `drawAllCharts` simply loops through the defined charts and draws
|
||||
// each one.
|
||||
chartMaker.drawAllCharts = function (options) {
|
||||
for (var name in this.charts) {
|
||||
this.drawChart(name, options);
|
||||
}
|
||||
};
|
||||
return chartMaker;
|
||||
};
|
||||
})(jQuery);
|
||||
|
||||
// ## Now some user level code. . .
|
||||
// Load the visualization Library
|
||||
google.load('visualization', '1',{'packages':['annotatedtimeline', 'geochart', 'corechart', 'table']});
|
||||
|
||||
// Set the callback for once the visualization library has loaded and make
|
||||
// sure the DOM has loaded as well.
|
||||
google.setOnLoadCallback(function () {
|
||||
jQuery(document).ready(function ($) {
|
||||
//Create a ChartMaker instance.
|
||||
var chartMaker = new ChartMaker();
|
||||
|
||||
// Get data from elastic that has been dumped on the page.
|
||||
var elasticJSON = $.parseJSON($('#aspect_dashboard_ElasticSearchStatsViewer_field_response').val());
|
||||
|
||||
// `function chartDataHelper` creates a chartData object from a few
|
||||
// parameters.
|
||||
// Required userConfig values: textKey, textValue
|
||||
// Optional userConfig values: textTotal, hasCertainty
|
||||
function chartDataHelper(userConfig) {
|
||||
// Set some defaults, but allow user over-ride
|
||||
var c = $.extend({
|
||||
type:'string',
|
||||
includeTotal : false,
|
||||
hasCertainty: false
|
||||
}, userConfig);
|
||||
|
||||
// Put data from Elastic response into a ChartData object
|
||||
var main_chart_data = chartMaker.chartData();
|
||||
|
||||
main_chart_data.addColumn(c.type, c.textKey);
|
||||
main_chart_data.addColumn('number', c.textValue);
|
||||
|
||||
if (c.includeTotal) {
|
||||
main_chart_data.addColumn('number', c.textTotal);
|
||||
}
|
||||
|
||||
// Add a certainty column for date data.
|
||||
if (c.hasCertainty == true) {
|
||||
main_chart_data.addColumn({type:'boolean',role:'certainty'}); // certainty col.
|
||||
}
|
||||
|
||||
return main_chart_data;
|
||||
}
|
||||
|
||||
// Set the title for the charts.
|
||||
var options = { title : 'Views per DSpaceObject Type' };
|
||||
|
||||
// ### Start adding charts!
|
||||
//
|
||||
// Use a helper to do all the work to create the
|
||||
// associated charts data tables.
|
||||
// There is one parent div chart_div, and we will append child divs for each chart.
|
||||
|
||||
// Add a chart to show total downloads.
|
||||
var name = $('input[name=containerName]').val();
|
||||
|
||||
var optionsItemsAdded = {title: 'Number of Items Added: ' + name};
|
||||
var rawAdded = $('input[name="gson-itemsAdded"]');
|
||||
var addedJSON = $.parseJSON(rawAdded.val());
|
||||
if((addedJSON !== null) && $('input[name=reportDepth]').val() == "detail") {
|
||||
var chartItemsAdded = chartDataHelper({
|
||||
type : 'date',
|
||||
textKey : 'Date',
|
||||
textValue : 'Added Monthly',
|
||||
includeTotal: true,
|
||||
textTotal: 'Added Total',
|
||||
hasCertainty: true
|
||||
});
|
||||
chartMaker.addChart({
|
||||
entries: addedJSON,
|
||||
name: 'itemsAddedMonthly',
|
||||
chartData: chartItemsAdded,
|
||||
dataSection: 'data',
|
||||
keyField: 'yearmo',
|
||||
valueField: 'countitem',
|
||||
includeTotal: true,
|
||||
chartType: 'LineChart',
|
||||
options: optionsItemsAdded});
|
||||
|
||||
var chartItemsAddedNoTotal = chartDataHelper({
|
||||
type : 'date',
|
||||
textKey : 'Date',
|
||||
textValue : 'Added Monthly',
|
||||
includeTotal: false,
|
||||
hasCertainty: true
|
||||
});
|
||||
chartMaker.addChart({
|
||||
entries: addedJSON,
|
||||
name: 'itemsAddedMonthlyNoTotal',
|
||||
chartData: chartItemsAddedNoTotal,
|
||||
dataSection: 'data',
|
||||
keyField: 'yearmo',
|
||||
valueField: 'countitem',
|
||||
includeTotal: false,
|
||||
chartType: 'LineChart',
|
||||
options: optionsItemsAdded});
|
||||
|
||||
var chartItemsAddedTotalTable = chartDataHelper({
|
||||
type : 'date',
|
||||
textKey : 'Date',
|
||||
textValue : 'Added Monthly',
|
||||
includeTotal: true,
|
||||
textTotal: 'Added Total',
|
||||
hasCertainty: false
|
||||
});
|
||||
chartMaker.addChart({
|
||||
entries: addedJSON,
|
||||
name: 'itemsAddedMonthlyTotalTable',
|
||||
chartData: chartItemsAddedTotalTable,
|
||||
dataSection: 'data',
|
||||
keyField: 'yearmo',
|
||||
valueField: 'countitem',
|
||||
includeTotal: true,
|
||||
chartType: 'Table',
|
||||
options: optionsItemsAdded});
|
||||
}
|
||||
|
||||
var optionsFilesAdded = {title: 'Number of Files Added: ' + name};
|
||||
var rawFilesAdded = $('input[name="gson-filesAdded"]');
|
||||
var filesAddedJSON = $.parseJSON(rawFilesAdded.val());
|
||||
if((filesAddedJSON !== null) && $('input[name=reportDepth]').val() == "detail") {
|
||||
var chartFilesAdded = chartDataHelper({
|
||||
type : 'date',
|
||||
textKey : 'Date',
|
||||
textValue : 'Added Monthly',
|
||||
includeTotal: true,
|
||||
textTotal: 'Added Total',
|
||||
hasCertainty:true
|
||||
});
|
||||
chartMaker.addChart({
|
||||
entries: filesAddedJSON,
|
||||
name: 'filesAddedMonthly',
|
||||
chartData: chartFilesAdded,
|
||||
dataSection: 'data',
|
||||
keyField: 'yearmo',
|
||||
valueField: 'countitem',
|
||||
includeTotal: true,
|
||||
chartType: 'LineChart',
|
||||
options: optionsFilesAdded});
|
||||
|
||||
var chartFilesAddedNoTotal = chartDataHelper({
|
||||
type : 'date',
|
||||
textKey : 'Date',
|
||||
textValue : 'Added Monthly',
|
||||
hasCertainty: true
|
||||
});
|
||||
chartMaker.addChart({
|
||||
entries: filesAddedJSON,
|
||||
name: 'filesAddedMonthlyNoTotal',
|
||||
chartData: chartFilesAddedNoTotal,
|
||||
dataSection: 'data',
|
||||
keyField: 'yearmo',
|
||||
valueField: 'countitem',
|
||||
includeTotal: false,
|
||||
chartType: 'LineChart',
|
||||
options: optionsFilesAdded});
|
||||
|
||||
var chartFilesAddedTotalTable = chartDataHelper({
|
||||
type : 'date',
|
||||
textKey : 'Date',
|
||||
textValue : 'Added Monthly',
|
||||
includeTotal: true,
|
||||
textTotal: 'Added Total'
|
||||
});
|
||||
chartMaker.addChart({
|
||||
entries: filesAddedJSON,
|
||||
name: 'filesAddedMonthlyTotalTable',
|
||||
chartData: chartFilesAddedTotalTable,
|
||||
dataSection: 'data',
|
||||
keyField: 'yearmo',
|
||||
valueField: 'countitem',
|
||||
includeTotal: true,
|
||||
chartType: 'Table',
|
||||
options: optionsFilesAdded});
|
||||
}
|
||||
|
||||
var optionsDownloads = {title: 'Number of File Downloads: ' + name };
|
||||
// Add a chart to show monthly downloads (without the total).
|
||||
if ((elasticJSON !== null) && (typeof elasticJSON.facets.monthly_downloads !== 'undefined')) {
|
||||
var chartDataNoTotal = chartDataHelper({
|
||||
type : 'date',
|
||||
textKey : 'Date',
|
||||
textValue : 'File Downloads',
|
||||
hasCertainty: true
|
||||
});
|
||||
chartMaker.addChart({
|
||||
entries: elasticJSON.facets.monthly_downloads.entries,
|
||||
name: 'downloadsMonthly',
|
||||
chartData: chartDataNoTotal,
|
||||
keyField: 'time',
|
||||
chartType: 'LineChart',
|
||||
options: optionsDownloads});
|
||||
|
||||
if ($('input[name=reportDepth]').val() == "detail") {
|
||||
var chartDataTotal = chartDataHelper({
|
||||
type : 'date',
|
||||
textKey : 'Date',
|
||||
textValue : 'File Downloads',
|
||||
includeTotal: true,
|
||||
textTotal: 'Total Downloads'
|
||||
});
|
||||
|
||||
// Table with raw data of # Downloads each month
|
||||
chartMaker.addChart({
|
||||
entries: elasticJSON.facets.monthly_downloads.entries,
|
||||
name: 'downloadsMonthlyTable',
|
||||
chartData: chartDataTotal,
|
||||
includeTotal : true,
|
||||
keyField : 'time',
|
||||
options:optionsDownloads,
|
||||
chartType: 'Table'
|
||||
});
|
||||
|
||||
// Chart of Downloads with aggregate total
|
||||
var chartDataTotal2 = chartDataHelper({
|
||||
type : 'date',
|
||||
textKey : 'Date',
|
||||
textValue : 'File Downloads',
|
||||
includeTotal: true,
|
||||
textTotal: 'Total Downloads',
|
||||
hasCertainty: true
|
||||
});
|
||||
chartMaker.addChart({
|
||||
entries: elasticJSON.facets.monthly_downloads.entries,
|
||||
name: 'downloadsWithTotal',
|
||||
includeTotal: true,
|
||||
chartData: chartDataTotal2,
|
||||
keyField: 'time',
|
||||
chartType: 'LineChart',
|
||||
options: optionsDownloads});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Add a chart to show downloads from various countries.
|
||||
if ((elasticJSON !== null) && (typeof elasticJSON.facets.top_countries !== 'undefined')) {
|
||||
var chartDataGeo = chartDataHelper({
|
||||
type : 'string',
|
||||
textKey : 'Country',
|
||||
textValue : 'Downloads'
|
||||
});
|
||||
chartMaker.addChart({
|
||||
entries: elasticJSON.facets.top_countries.terms,
|
||||
name: 'topCountries',
|
||||
chartData: chartDataGeo,
|
||||
options: options});
|
||||
|
||||
if ($('input[name=reportDepth]').val() == "detail") {
|
||||
chartMaker.addChart({
|
||||
entries: elasticJSON.facets.top_countries.terms,
|
||||
name: 'topCountriesTable',
|
||||
chartData: chartDataGeo,
|
||||
options:options,
|
||||
chartType: 'Table'
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Add a chart to show downloads from various countries.
|
||||
if ((elasticJSON !== null) && typeof elasticJSON.facets.top_US_cities !== 'undefined' && $('input[name=reportDepth]').val() == "detail") {
|
||||
var chartDataGeoUS = chartDataHelper({
|
||||
type : 'string',
|
||||
textKey : 'City',
|
||||
textValue : 'Downloads'
|
||||
});
|
||||
var optionsUS = {region : 'US', displayMode : 'markers', resolution : 'provinces', magnifyingGlass : {enable: true, zoomFactor: 7.5} };
|
||||
chartMaker.addChart({
|
||||
entries: elasticJSON.facets.top_US_cities.terms,
|
||||
name: 'topUSCities',
|
||||
chartData: chartDataGeoUS,
|
||||
options: optionsUS});
|
||||
}
|
||||
|
||||
// Add a pie chart that shows top DSO Types usage.
|
||||
/*
|
||||
if (typeof elasticJSON.facets.top_types !== 'undefined') {
|
||||
var chartDataPie = chartDataHelper('string', 'Type', 'Views', false, '');
|
||||
chartMaker.addChart({
|
||||
entries: elasticJSON.facets.top_types.terms,
|
||||
name: 'topTypes',
|
||||
chartData: chartDataPie,
|
||||
chartType: 'PieChart',
|
||||
options: options});
|
||||
}
|
||||
*/
|
||||
|
||||
// Finally, we draw all of the charts.
|
||||
chartMaker.drawAllCharts();
|
||||
|
||||
//Set Titles to Charts that cannot otherwise set titles automatically (geocharts).
|
||||
var baseURLStats = $('input[name=baseURLStats]').val();
|
||||
var timeRangeString = $('input[name=timeRangeString]').val();
|
||||
|
||||
//TODO these dates are already accessed in different scope/context, its a waste to reexecute
|
||||
var fromDateString = $('input[name=dateStart]').val();
|
||||
var toDateString = $('input[name=dateEnd]').val();
|
||||
|
||||
if ($('input[name=reportDepth]').val() == "summary") {
|
||||
$('<p>'+timeRangeString+' <a href="'+ baseURLStats + '/itemsAdded">For more information.</a></p>').insertBefore('#aspect_dashboard_ElasticSearchStatsViewer_table_itemsAddedGrid');
|
||||
$('<p>'+timeRangeString+' <a href="'+ baseURLStats + '/filesAdded">For more information.</a></p>').insertBefore('#aspect_dashboard_ElasticSearchStatsViewer_table_filesInContainer-grid');
|
||||
$('<h3>Number of File Downloads for ' + name + '</h3>'+timeRangeString+' <a href="'+ baseURLStats + '/fileDownloads">For more information.</a>').insertBefore('#dspaceChart_downloadsMonthly');
|
||||
$('<h3>Countries with most Downloads ' + name + '</h3>'+timeRangeString+' <a href="'+ baseURLStats + '/topCountries">For more information.</a>').insertBefore('#dspaceChart_topCountries');
|
||||
$('<p>'+timeRangeString+' <a href="'+ baseURLStats + '/topUSCities">For more information.</a></p>').insertBefore('#dspaceChart_topUSCities');
|
||||
$('<p>'+timeRangeString+' <a href="'+ baseURLStats + '/topDownloads">For more information.</a></p>').insertBefore('#aspect_dashboard_ElasticSearchStatsViewer_table_facet-Bitstream');
|
||||
}
|
||||
|
||||
var reportName = $('input[name=reportName]').val();
|
||||
|
||||
if ($('input[name=reportDepth]').val() == "detail") {
|
||||
var contextPanel = '<div><p><a href="' + baseURLStats + '">Back to Summary Statistics for ' + name + '</a></p><br/>';
|
||||
contextPanel += '<a href="#" onclick="window.print(); return false;"><img src="http://www.famfamfam.com/lab/icons/silk/icons/printer.png"/>Print This Report</a><br/>';
|
||||
contextPanel += '<a href="' + baseURLStats + '/csv/' + reportName;
|
||||
if(fromDateString !== null && typeof fromDateString !== 'undefined') {
|
||||
contextPanel += '?from=' + fromDateString;
|
||||
}
|
||||
if(toDateString !== null && typeof toDateString !== 'undefined') {
|
||||
if(fromDateString !== null && typeof fromDateString !== 'undefined') {
|
||||
contextPanel += '&';
|
||||
} else {
|
||||
contextPanel =+ '?';
|
||||
}
|
||||
|
||||
contextPanel += 'to=' + toDateString;
|
||||
}
|
||||
contextPanel += '"><img src="http://www.famfamfam.com/lab/icons/silk/icons/page_excel.png"/>Download Data as .csv</a></div>';
|
||||
$(contextPanel).insertAfter('#aspect_dashboard_ElasticSearchStatsViewer_div_chart_div');
|
||||
|
||||
}
|
||||
});
|
||||
});
|
||||
})(this);
|
||||
|
||||
function isValidDate(d) {
|
||||
if ( Object.prototype.toString.call(d) !== "[object Date]" )
|
||||
return false;
|
||||
return !isNaN(d.getTime());
|
||||
}
|
@@ -0,0 +1,240 @@
|
||||
// # Visualize Data
|
||||
//
|
||||
// Simple script to help visualize data generated by elastic search.
|
||||
|
||||
// Firstly we wrap our code in a closure to keep variables local.
|
||||
(function (context) {
|
||||
|
||||
// ### Chart Maker
|
||||
//
|
||||
// Create a helper module called chart maker that allows us to specify and
|
||||
// draw charts.
|
||||
(function ($) {
|
||||
context.ChartMaker = function() {
|
||||
var chartMaker = {};
|
||||
// A place to store charts to draw later.
|
||||
chartMaker.charts = {};
|
||||
|
||||
// A shortcut to the google.visualization.DataTable function.
|
||||
chartMaker.chartData = function () {
|
||||
return new google.visualization.DataTable();
|
||||
};
|
||||
|
||||
// The `addChart` function is used to add chart data to `ChartMaker`.
|
||||
// It does two things: Creates a div under the specified parent div
|
||||
// to put the chart in and add a new chart to `ChartMaker`'s internal
|
||||
// list of charts.
|
||||
//
|
||||
// `addChart` takes an object (`userConfig`) that should have the
|
||||
// following keys.
|
||||
//
|
||||
// * `entries` {object} The entries we use to create the chart.
|
||||
// * `name` {string} A nice name to give the chart so the user can reference it
|
||||
// later.
|
||||
// * `includeTotal` {boolean} Whether or not to include the total in
|
||||
// each row.
|
||||
// * `chartData` {object} An instance of ChartMaker.chartData containing the data for
|
||||
// the chart being added.
|
||||
// * `keyField` {string} The name of the key to use on entries.
|
||||
// * `valueField` {string} The name of the value to use on entries.
|
||||
// * `parentElement` {string} The parent div to create the chart under
|
||||
// * `chartType` {string} The key specifying what type of chart to
|
||||
// use from `google.visualization`.
|
||||
chartMaker.addChart = function (userConfig) {
|
||||
var c = $.extend({
|
||||
entries: [],
|
||||
name: '',
|
||||
includeTotal: false,
|
||||
chartData: null,
|
||||
keyField: 'term',
|
||||
valueField: 'count',
|
||||
parentElement: 'aspect_dashboard_ElasticSearchStatsViewer_div_chart_div',
|
||||
chartType: 'GeoChart'
|
||||
}, userConfig);
|
||||
|
||||
// `dataValue` will eventually become the rows of data in our
|
||||
// chart.
|
||||
var dataValue = [];
|
||||
var total = 0;
|
||||
|
||||
// For each entry construct a vector to add to push onto
|
||||
// `dataValue`.
|
||||
$.each(c.entries, function(index, entry) {
|
||||
newEntry = [];
|
||||
if(c.chartType == 'LineChart') {
|
||||
newEntry.push(new Date(entry[c.keyField]));
|
||||
} else {
|
||||
newEntry.push(entry[c.keyField]);
|
||||
}
|
||||
newEntry.push(entry[c.valueField]);
|
||||
if(c.includeTotal) {
|
||||
total += entry.count;
|
||||
newEntry.push(total);
|
||||
}
|
||||
dataValue.push(newEntry);
|
||||
});
|
||||
|
||||
// Add rows (`dataValue`) to the chartData.
|
||||
c.chartData.addRows(dataValue);
|
||||
|
||||
// Add a child element
|
||||
var par = $('#' + c.parentElement);
|
||||
par.append("<div style='height:280px; width:750px;' " +
|
||||
"id='dspaceChart_" + c.name + "'> </div>");
|
||||
this.charts[c.name] = {
|
||||
chart: new google.visualization[c.chartType](
|
||||
document.getElementById("dspaceChart_" + c.name)),
|
||||
data: c.chartData,
|
||||
options: c.options
|
||||
};
|
||||
};
|
||||
|
||||
// `drawChart` takes a chart from ChartMaker's internal `charts` array
|
||||
// (specified by the `name` parameter) and draws that chart.
|
||||
chartMaker.drawChart = function(name, globalOptions) {
|
||||
if (typeof globalOptions === 'undefined') {
|
||||
globalOptions = {};
|
||||
}
|
||||
var cobj = this.charts[name];
|
||||
|
||||
// Allow the user to overwrite data with a passed in options.
|
||||
var data = cobj.data;
|
||||
if ('data' in globalOptions) {
|
||||
data = globalOptions.data;
|
||||
}
|
||||
|
||||
// Merge the Global Options with the local options for the chart
|
||||
var combinedOptions = $.extend(globalOptions, cobj.options);
|
||||
// Draw the named chart!
|
||||
cobj.chart.draw(data, combinedOptions);
|
||||
};
|
||||
|
||||
// `drawAllCharts` simply loops through the defined charts and draws
|
||||
// each one.
|
||||
chartMaker.drawAllCharts = function (options) {
|
||||
for (var name in this.charts) {
|
||||
this.drawChart(name, options);
|
||||
}
|
||||
};
|
||||
return chartMaker;
|
||||
};
|
||||
})(jQuery);
|
||||
|
||||
// ## Now some user level code. . .
|
||||
// Load the visualization Library
|
||||
google.load('visualization', '1',{'packages':['annotatedtimeline', 'geochart', 'corechart']});
|
||||
|
||||
// Set the callback for once the visualization library has loaded and make
|
||||
// sure the DOM has loaded as well.
|
||||
google.setOnLoadCallback(function () {
|
||||
jQuery(document).ready(function ($) {
|
||||
//Create a ChartMaker instance.
|
||||
var chartMaker = new ChartMaker();
|
||||
|
||||
// Get data from elastic that has been dumped on the page.
|
||||
var elasticJSON = $.parseJSON($('#aspect_dashboard_ElasticSearchStatsViewer_field_response').val());
|
||||
|
||||
// `function chartDataHelper` creates a chartData object from a few
|
||||
// parameters.
|
||||
function chartDataHelper(type, textKey, textValue, includeTotal, textTotal) {
|
||||
// Put data from Elastic response into a ChartData object
|
||||
var main_chart_data = chartMaker.chartData();
|
||||
|
||||
if(type == 'date') {
|
||||
main_chart_data.addColumn('date', textKey);
|
||||
} else {
|
||||
main_chart_data.addColumn('string', textKey);
|
||||
}
|
||||
|
||||
<<<<<<< Updated upstream
|
||||
main_chart_data.addColumn('number', textValue);
|
||||
if(includeTotal) {
|
||||
main_chart_data.addColumn('number', textTotal);
|
||||
}
|
||||
=======
|
||||
var options = { title : 'Views per DSpaceObject Type' };
|
||||
|
||||
// Use a helper to do all the work to create our downloads charts.
|
||||
// There is one parent div chart_div, and we will append child divs for each chart.
|
||||
//var chartDataTotal = chartDataHelper('date', 'Date', 'Bitstreams Downloaded', true, 'Total Bitstreams Downloaded');
|
||||
//elasticDataHelper(elasticJSON.facets.monthly_downloads.entries, 'downloadsWithTotal', true, chartDataTotal, 'time', 'count', 'chart_div', 'LineChart', options);
|
||||
|
||||
var chartDataNoTotal = chartDataHelper('date', 'Date', 'Bitstreams Downloaded', false, 'Total Items');
|
||||
elasticDataHelper(elasticJSON.facets.monthly_downloads.entries, 'downloadsMonthly', false, chartDataNoTotal, 'time', 'count', 'chart_div', 'LineChart', options);
|
||||
|
||||
//TODO Map looks better at size $("#" + mapDivId).height(500).width(780);
|
||||
var chartDataGeo = chartDataHelper('string', 'Country', 'Views', false, 'Total');
|
||||
var countryOptions = { magnifyingGlass : {enable : true, zoomFactor: 5.0}};
|
||||
elasticDataHelper(elasticJSON.facets.top_countries.terms, 'topCountries', false, chartDataGeo, 'term', 'count', 'chart_div', 'GeoChart', countryOptions);
|
||||
|
||||
var chartDataGeoCity = chartDataHelper('string', 'City', 'Views', false, 'Total');
|
||||
var cityOptions = { region:'US', displayMode: 'markers', colorAxis: {colors: ['green', 'blue']} };
|
||||
elasticDataHelper(elasticJSON.facets.top_US_cities.terms, 'topCities', false, chartDataGeoCity, 'term', 'count', 'chart_div', 'GeoChart', cityOptions);
|
||||
|
||||
var chartDataPie = chartDataHelper('string', 'Type', 'Views', false, '');
|
||||
elasticDataHelper(elasticJSON.facets.top_types.terms, 'topTypes', false, chartDataPie, 'term', 'count', 'chart_div', 'PieChart', options);
|
||||
|
||||
// Resize the chart_div parent to fit its contents.
|
||||
var totalChildHeight = 0;
|
||||
$('#chart_div').children().each(function() {
|
||||
totalChildHeight += $(this).height();
|
||||
});
|
||||
$('#chart_div').height(totalChildHeight);
|
||||
>>>>>>> Stashed changes
|
||||
|
||||
return main_chart_data;
|
||||
}
|
||||
|
||||
// Set the title for the charts.
|
||||
var options = { title : 'Views per DSpaceObject Type' };
|
||||
|
||||
// ### Start adding charts!
|
||||
//
|
||||
// Use a helper to do all the work to create the
|
||||
// associated charts data tables.
|
||||
// There is one parent div chart_div, and we will append child divs for each chart.
|
||||
|
||||
// Add a chart to show total downloads.
|
||||
var chartDataTotal = chartDataHelper('date', 'Date', 'Items Added', true, 'Total Items');
|
||||
chartMaker.addChart({
|
||||
entries: elasticJSON.facets.monthly_downloads.entries,
|
||||
name: 'downloadsWithTotal',
|
||||
includeTotal: true,
|
||||
chartData: chartDataTotal,
|
||||
keyField: 'time',
|
||||
chartType: 'LineChart',
|
||||
options: options});
|
||||
|
||||
|
||||
// Add a chart to show monthly downloads (without the total).
|
||||
var chartDataNoTotal = chartDataHelper('date', 'Date', 'Items Added', false, 'Total Items');
|
||||
chartMaker.addChart({
|
||||
entries: elasticJSON.facets.monthly_downloads.entries,
|
||||
name: 'downloadsMonthly',
|
||||
chartData: chartDataNoTotal,
|
||||
keyField: 'time',
|
||||
chartType: 'LineChart',
|
||||
options: options});
|
||||
|
||||
// Add a chart to show downloads from various countries.
|
||||
var chartDataGeo = chartDataHelper('string', 'Country', 'Views', false, 'Total');
|
||||
chartMaker.addChart({
|
||||
entries: elasticJSON.facets.top_countries.terms,
|
||||
name: 'topCountries',
|
||||
chartData: chartDataGeo,
|
||||
options: options});
|
||||
|
||||
// Add a pie chart that shows country download data.
|
||||
var chartDataPie = chartDataHelper('string', 'Type', 'Views', false, '');
|
||||
chartMaker.addChart({
|
||||
entries: elasticJSON.facets.top_types.terms,
|
||||
name: 'topTypes',
|
||||
chartData: chartDataPie,
|
||||
chartType: 'PieChart',
|
||||
options: options});
|
||||
|
||||
// Finally, we draw all of the charts.
|
||||
chartMaker.drawAllCharts();
|
||||
});
|
||||
});
|
||||
})(this);
|
@@ -1767,3 +1767,11 @@ webui.suggest.enable = false
|
||||
# will be loaded in the multiple select list of the RestrictStep
|
||||
xmlui.submission.restrictstep.groups=SubmissionAdmin
|
||||
xmlui.submission.restrictstep.enableAdvancedForm=true
|
||||
|
||||
# Elastic Search Engine for UsageEvent Statistics
|
||||
#statistics.elasticsearch.clusterName = dspacestatslogging
|
||||
#statistics.elasticsearch.indexName = dspaceindex
|
||||
#statistics.elasticsearch.indexType = stats
|
||||
#statistics.elasticsearch.address = 127.0.0.1
|
||||
#statistics.elasticsearch.port = 9300
|
||||
#statistics.elasticsearch.storeData = true
|
||||
|
@@ -71,6 +71,7 @@
|
||||
<aspect name="E-Person" path="resource://aspects/EPerson/" />
|
||||
<aspect name="Submission and Workflow" path="resource://aspects/Submission/" />
|
||||
<aspect name="Statistics" path="resource://aspects/Statistics/" />
|
||||
<aspect name="Statistics - Elastic Search" path="resource://aspects/StatisticsElasticSearch/" />
|
||||
<aspect name="Original Workflow" path="resource://aspects/Workflow/" />
|
||||
<!--<aspect name="XMLWorkflow" path="resource://aspects/XMLWorkflow/" />-->
|
||||
<!--
|
||||
@@ -120,6 +121,8 @@
|
||||
<!-- <theme name="Test Theme 1" handle="123456789/1" path="theme1/"/> -->
|
||||
<!-- <theme name="Test Theme 2" regex="community-list" path="theme2/"/> -->
|
||||
|
||||
<theme name="Elastic Search Statistics Theme" regex="stats" path="dashboard/" />
|
||||
|
||||
<!-- Mirage theme, @mire contributed theme, default since DSpace 3.0 -->
|
||||
<theme name="Atmire Mirage Theme" regex=".*" path="Mirage/" />
|
||||
|
||||
|
5
pom.xml
@@ -24,6 +24,7 @@
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<filters.file>build.properties</filters.file>
|
||||
<lucene.version>3.5.0</lucene.version>
|
||||
</properties>
|
||||
|
||||
<build>
|
||||
@@ -619,12 +620,12 @@
|
||||
<dependency>
|
||||
<groupId>org.apache.lucene</groupId>
|
||||
<artifactId>lucene-core</artifactId>
|
||||
<version>3.3.0</version>
|
||||
<version>${lucene.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.lucene</groupId>
|
||||
<artifactId>lucene-analyzers</artifactId>
|
||||
<version>3.3.0</version>
|
||||
<version>${lucene.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.dspace</groupId>
|
||||
|