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;
|
package org.dspace.content;
|
||||||
|
|
||||||
import java.sql.SQLException;
|
|
||||||
|
|
||||||
import org.dspace.core.Constants;
|
import org.dspace.core.Constants;
|
||||||
import org.dspace.core.Context;
|
import org.dspace.core.Context;
|
||||||
import org.dspace.eperson.EPerson;
|
import org.dspace.eperson.EPerson;
|
||||||
import org.dspace.eperson.Group;
|
import org.dspace.eperson.Group;
|
||||||
|
|
||||||
|
import java.sql.SQLException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Abstract base class for DSpace objects
|
* Abstract base class for DSpace objects
|
||||||
*/
|
*/
|
||||||
@@ -65,6 +65,15 @@ public abstract class DSpaceObject
|
|||||||
*/
|
*/
|
||||||
public abstract int getType();
|
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
|
* Get the internal ID (database primary key) of this object
|
||||||
*
|
*
|
||||||
|
@@ -141,7 +141,7 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.apache.solr</groupId>
|
<groupId>org.apache.solr</groupId>
|
||||||
<artifactId>solr-solrj</artifactId>
|
<artifactId>solr-solrj</artifactId>
|
||||||
<version>3.3.0</version>
|
<version>${lucene.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.dspace.dependencies</groupId>
|
<groupId>org.dspace.dependencies</groupId>
|
||||||
@@ -194,6 +194,11 @@
|
|||||||
<version>4.8.2</version>
|
<version>4.8.2</version>
|
||||||
<type>jar</type>
|
<type>jar</type>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.elasticsearch</groupId>
|
||||||
|
<artifactId>elasticsearch</artifactId>
|
||||||
|
<version>0.18.6</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>commons-cli</groupId>
|
<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>
|
<artifactId>yuicompressor</artifactId>
|
||||||
<version>2.3.6</version>
|
<version>2.3.6</version>
|
||||||
</dependency>
|
</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>
|
</dependencies>
|
||||||
|
|
||||||
|
@@ -7,25 +7,35 @@
|
|||||||
*/
|
*/
|
||||||
package org.dspace.app.xmlui.aspect.statistics;
|
package org.dspace.app.xmlui.aspect.statistics;
|
||||||
|
|
||||||
import java.io.IOException;
|
import com.google.gson.Gson;
|
||||||
import java.sql.SQLException;
|
|
||||||
import java.text.ParseException;
|
|
||||||
|
|
||||||
import org.apache.log4j.Logger;
|
import org.apache.log4j.Logger;
|
||||||
import org.apache.solr.client.solrj.SolrServerException;
|
import org.apache.solr.client.solrj.SolrServerException;
|
||||||
import org.dspace.app.xmlui.cocoon.AbstractDSpaceTransformer;
|
import org.dspace.app.xmlui.cocoon.AbstractDSpaceTransformer;
|
||||||
import org.dspace.app.xmlui.utils.HandleUtil;
|
import org.dspace.app.xmlui.utils.HandleUtil;
|
||||||
import org.dspace.app.xmlui.utils.UIException;
|
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.Message;
|
||||||
|
import org.dspace.app.xmlui.wing.WingException;
|
||||||
import org.dspace.app.xmlui.wing.element.*;
|
import org.dspace.app.xmlui.wing.element.*;
|
||||||
import org.dspace.authorize.AuthorizeException;
|
import org.dspace.authorize.AuthorizeException;
|
||||||
import org.dspace.content.DSpaceObject;
|
import org.dspace.content.DSpaceObject;
|
||||||
import org.dspace.core.Constants;
|
import org.dspace.core.Constants;
|
||||||
|
import org.dspace.core.Context;
|
||||||
import org.dspace.statistics.Dataset;
|
import org.dspace.statistics.Dataset;
|
||||||
|
import org.dspace.statistics.ObjectCount;
|
||||||
import org.dspace.statistics.content.*;
|
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 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 {
|
public class StatisticsTransformer extends AbstractDSpaceTransformer {
|
||||||
|
|
||||||
private static Logger log = Logger.getLogger(StatisticsTransformer.class);
|
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_cities = "xmlui.statistics.visits.cities";
|
||||||
private static final String T_head_visits_bitstream = "xmlui.statistics.visits.bitstreams";
|
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
|
* 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"/>
|
<ref bean="dspace.eventService"/>
|
||||||
</property>
|
</property>
|
||||||
</bean>
|
</bean>
|
||||||
|
|
||||||
|
<!-- Elastic Search -->
|
||||||
|
<bean class="org.dspace.statistics.ElasticSearchLoggerEventListener">
|
||||||
|
<property name="eventService">
|
||||||
|
<ref bean="dspace.eventService" />
|
||||||
|
</property>
|
||||||
|
</bean>
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
Uncomment to enable TabFileUsageEventListener
|
Uncomment to enable TabFileUsageEventListener
|
||||||
|
@@ -95,6 +95,7 @@
|
|||||||
<map:transformer name="LDAPLogin" src="org.dspace.app.xmlui.aspect.eperson.LDAPLogin"/>
|
<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="notice" src="org.dspace.app.xmlui.aspect.general.NoticeTransformer"/>
|
||||||
<map:transformer name="NamespaceFilter" src="org.dspace.app.xmlui.cocoon.NamespaceFilterTransformer"/>
|
<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:transformers>
|
||||||
<map:matchers default="wildcard">
|
<map:matchers default="wildcard">
|
||||||
<map:matcher logger="sitemap.matcher.wildcard" name="wildcard"
|
<map:matcher logger="sitemap.matcher.wildcard" name="wildcard"
|
||||||
@@ -122,6 +123,7 @@
|
|||||||
<map:parameter name="ignore-missing-tables" value="true"/>
|
<map:parameter name="ignore-missing-tables" value="true"/>
|
||||||
</map:matcher>
|
</map:matcher>
|
||||||
<map:matcher name="request" src="org.apache.cocoon.matching.RequestParameterMatcher"/>
|
<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:matchers>
|
||||||
<map:selectors>
|
<map:selectors>
|
||||||
<map:selector name="browser" src="org.apache.cocoon.selection.BrowserSelector"
|
<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 -->
|
<!-- The statement below tells the selector to unroll as much exceptions as possible -->
|
||||||
<exception class="java.lang.Throwable" unroll="true"/>
|
<exception class="java.lang.Throwable" unroll="true"/>
|
||||||
</map:selector>
|
</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:selectors>
|
||||||
<map:readers default="resource">
|
<map:readers default="resource">
|
||||||
<map:reader name="resource" src="org.apache.cocoon.reading.ResourceReader"
|
<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="BitstreamReader" src="org.dspace.app.xmlui.cocoon.BitstreamReader"/>
|
||||||
<map:reader name="ExportReader" src="org.dspace.app.xmlui.cocoon.ItemExportDownloadReader"/>
|
<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="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="OpenURLReader" src="org.dspace.app.xmlui.cocoon.OpenURLReader"/>
|
||||||
<map:reader name="SitemapReader" src="org.dspace.app.xmlui.cocoon.SitemapReader"/>
|
<map:reader name="SitemapReader" src="org.dspace.app.xmlui.cocoon.SitemapReader"/>
|
||||||
<map:reader name="ConcatenationReader" src="org.dspace.app.xmlui.cocoon.ConcatenationReader"/>
|
<map:reader name="ConcatenationReader" src="org.dspace.app.xmlui.cocoon.ConcatenationReader"/>
|
||||||
@@ -378,6 +385,50 @@
|
|||||||
</map:read>
|
</map:read>
|
||||||
</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="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:match pattern="JSON/discovery/**">
|
||||||
<map:mount check-reload="no" src="aspects/json-solr.xmap" uri-prefix="JSON/discovery/"/>
|
<map:mount check-reload="no" src="aspects/json-solr.xmap" uri-prefix="JSON/discovery/"/>
|
||||||
</map:match>
|
</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
|
# will be loaded in the multiple select list of the RestrictStep
|
||||||
xmlui.submission.restrictstep.groups=SubmissionAdmin
|
xmlui.submission.restrictstep.groups=SubmissionAdmin
|
||||||
xmlui.submission.restrictstep.enableAdvancedForm=true
|
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="E-Person" path="resource://aspects/EPerson/" />
|
||||||
<aspect name="Submission and Workflow" path="resource://aspects/Submission/" />
|
<aspect name="Submission and Workflow" path="resource://aspects/Submission/" />
|
||||||
<aspect name="Statistics" path="resource://aspects/Statistics/" />
|
<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="Original Workflow" path="resource://aspects/Workflow/" />
|
||||||
<!--<aspect name="XMLWorkflow" path="resource://aspects/XMLWorkflow/" />-->
|
<!--<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 1" handle="123456789/1" path="theme1/"/> -->
|
||||||
<!-- <theme name="Test Theme 2" regex="community-list" path="theme2/"/> -->
|
<!-- <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 -->
|
<!-- Mirage theme, @mire contributed theme, default since DSpace 3.0 -->
|
||||||
<theme name="Atmire Mirage Theme" regex=".*" path="Mirage/" />
|
<theme name="Atmire Mirage Theme" regex=".*" path="Mirage/" />
|
||||||
|
|
||||||
|
5
pom.xml
@@ -24,6 +24,7 @@
|
|||||||
<properties>
|
<properties>
|
||||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
<filters.file>build.properties</filters.file>
|
<filters.file>build.properties</filters.file>
|
||||||
|
<lucene.version>3.5.0</lucene.version>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
<build>
|
<build>
|
||||||
@@ -619,12 +620,12 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.apache.lucene</groupId>
|
<groupId>org.apache.lucene</groupId>
|
||||||
<artifactId>lucene-core</artifactId>
|
<artifactId>lucene-core</artifactId>
|
||||||
<version>3.3.0</version>
|
<version>${lucene.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.apache.lucene</groupId>
|
<groupId>org.apache.lucene</groupId>
|
||||||
<artifactId>lucene-analyzers</artifactId>
|
<artifactId>lucene-analyzers</artifactId>
|
||||||
<version>3.3.0</version>
|
<version>${lucene.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.dspace</groupId>
|
<groupId>org.dspace</groupId>
|
||||||
|