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.
This commit is contained in:
Peter Dietz
2012-08-10 13:56:23 -04:00
committed by Peter Dietz
parent 3b1cc06e0d
commit 59d1970f1c
44 changed files with 4460 additions and 10 deletions

View File

@@ -7,13 +7,13 @@
*/
package org.dspace.content;
import java.sql.SQLException;
import org.dspace.core.Constants;
import org.dspace.core.Context;
import org.dspace.eperson.EPerson;
import org.dspace.eperson.Group;
import java.sql.SQLException;
/**
* Abstract base class for DSpace objects
*/
@@ -65,6 +65,15 @@ public abstract class DSpaceObject
*/
public abstract int getType();
/**
* Provide the text name of the type of this DSpaceObject. It is most likely all uppercase.
* @return Object type as text
*/
public String getTypeText()
{
return Constants.typeText[this.getType()];
}
/**
* Get the internal ID (database primary key) of this object
*

View File

@@ -141,7 +141,7 @@
<dependency>
<groupId>org.apache.solr</groupId>
<artifactId>solr-solrj</artifactId>
<version>3.3.0</version>
<version>${lucene.version}</version>
</dependency>
<dependency>
<groupId>org.dspace.dependencies</groupId>
@@ -194,6 +194,11 @@
<version>4.8.2</version>
<type>jar</type>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.elasticsearch</groupId>
<artifactId>elasticsearch</artifactId>
<version>0.18.6</version>
</dependency>
<dependency>
<groupId>commons-cli</groupId>

View File

@@ -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;
}
}
}

View File

@@ -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());
}
}
}
}

View File

@@ -58,6 +58,24 @@
<artifactId>yuicompressor</artifactId>
<version>2.3.6</version>
</dependency>
<!-- Needed for Form Validation -->
<dependency>
<groupId>commons-validator</groupId>
<artifactId>commons-validator</artifactId>
<version>1.3.1</version>
</dependency>
<dependency>
<groupId>net.sf.opencsv</groupId>
<artifactId>opencsv</artifactId>
<version>2.0</version>
</dependency>
<!-- Gson: Java to Json conversion -->
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.1</version>
<scope>compile</scope>
</dependency>
</dependencies>

View File

@@ -7,25 +7,35 @@
*/
package org.dspace.app.xmlui.aspect.statistics;
import java.io.IOException;
import java.sql.SQLException;
import java.text.ParseException;
import com.google.gson.Gson;
import org.apache.log4j.Logger;
import org.apache.solr.client.solrj.SolrServerException;
import org.dspace.app.xmlui.cocoon.AbstractDSpaceTransformer;
import org.dspace.app.xmlui.utils.HandleUtil;
import org.dspace.app.xmlui.utils.UIException;
import org.dspace.app.xmlui.wing.WingException;
import org.dspace.app.xmlui.wing.Message;
import org.dspace.app.xmlui.wing.WingException;
import org.dspace.app.xmlui.wing.element.*;
import org.dspace.authorize.AuthorizeException;
import org.dspace.content.DSpaceObject;
import org.dspace.core.Constants;
import org.dspace.core.Context;
import org.dspace.statistics.Dataset;
import org.dspace.statistics.ObjectCount;
import org.dspace.statistics.content.*;
import org.dspace.storage.rdbms.DatabaseManager;
import org.dspace.storage.rdbms.TableRow;
import org.dspace.storage.rdbms.TableRowIterator;
import org.xml.sax.SAXException;
import java.io.IOException;
import java.sql.SQLException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.Locale;
public class StatisticsTransformer extends AbstractDSpaceTransformer {
private static Logger log = Logger.getLogger(StatisticsTransformer.class);
@@ -40,6 +50,30 @@ public class StatisticsTransformer extends AbstractDSpaceTransformer {
private static final String T_head_visits_cities = "xmlui.statistics.visits.cities";
private static final String T_head_visits_bitstream = "xmlui.statistics.visits.bitstreams";
private Date dateStart = null;
private Date dateEnd = null;
private SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
public StatisticsTransformer(Date dateStart, Date dateEnd) {
this.dateStart = dateStart;
this.dateEnd = dateEnd;
try {
this.context = new Context();
} catch (SQLException e) {
log.error("Error getting context in StatisticsTransformer:" + e.getMessage());
}
}
public StatisticsTransformer() {
try {
this.context = new Context();
} catch (SQLException e) {
log.error("Error getting context in StatisticsTransformer:" + e.getMessage());
}
}
/**
* Add a page title and trail links
*/
@@ -387,4 +421,348 @@ public class StatisticsTransformer extends AbstractDSpaceTransformer {
}
}
public java.util.List<TableRow> addItemsInContainer(DSpaceObject dso) {
java.util.List<TableRow> tableRowList = null;
String typeTextLower = "";
if(dso.getType() == Constants.COMMUNITY) {
typeTextLower = "communities";
} else {
typeTextLower = dso.getTypeText().toLowerCase();
}
String querySpecifyContainer = "SELECT to_char(date_trunc('month', t1.ts), 'YYYY-MM') AS yearmo, count(*) as countitem " +
"FROM ( SELECT to_timestamp(text_value, 'YYYY-MM-DD') AS ts FROM metadatavalue, item, " +
typeTextLower + "2item " +
"WHERE metadata_field_id = 12 AND metadatavalue.item_id = item.item_id AND item.in_archive=true AND "+
typeTextLower + "2item.item_id = item.item_id AND "+
typeTextLower + "2item." + dso.getTypeText().toLowerCase() +"_id = ? ";
if (dateStart != null) {
String start = dateFormat.format(dateStart);
querySpecifyContainer += "AND metadatavalue.text_value > '"+start+"'";
}
if(dateEnd != null) {
String end = dateFormat.format(dateEnd);
querySpecifyContainer += " AND metadatavalue.text_value < '"+end+"' ";
}
querySpecifyContainer += ") t1 GROUP BY date_trunc('month', t1.ts) order by yearmo asc";
try {
TableRowIterator tri;
tri = DatabaseManager.query(context, querySpecifyContainer, dso.getID());
tableRowList = tri.toList();
return tableRowList;
} catch (Exception e) {
}
return tableRowList;
}
/**
* Only call this on a container object (collection or community).
* @param dso
* @param division
*/
public void addItemsInContainer(DSpaceObject dso, Division division) {
java.util.List<TableRow> tableRowList = addItemsInContainer(dso);
Gson gson = new Gson();
try {
division.addHidden("gson-itemsAdded").setValue(gson.toJson(tableRowList));
Integer[][] monthlyDataGrid = convertTableRowListToIntegerGrid(tableRowList, "yearmo", "countitem");
displayAsGrid(division, monthlyDataGrid, "itemsAddedGrid", "Number of Items Added to the " + dso.getName());
} catch (WingException e) {
log.error(e.getMessage()); //To change body of catch statement use File | Settings | File Templates.
}
}
public Integer[][] convertTableRowListToIntegerGrid(java.util.List<TableRow> tableRowList, String dateColumn, String valueColumn) {
if(tableRowList == null || tableRowList.size() == 0) {
return null;
}
// If we have a requested time-range for data, then we need to ensure we zero-fill the data during that time range.
// Otherwise, if no requested time-range, then just make table size of data.
// a year-mo is 2008-05
Integer yearStart;
Integer yearLast;
if(dateStart != null) {
Calendar myTime = Calendar.getInstance();
myTime.setTime(dateStart);
yearStart = myTime.get(Calendar.YEAR);
} else {
String yearmoStart = tableRowList.get(0).getStringColumn(dateColumn);
yearStart = Integer.valueOf(yearmoStart.split("-")[0]);
}
if(dateEnd != null) {
Calendar myTime = Calendar.getInstance();
myTime.setTime(dateEnd);
yearLast = myTime.get(Calendar.YEAR);
} else {
String yearmoLast = tableRowList.get(tableRowList.size()-1).getStringColumn(dateColumn);
yearLast = Integer.valueOf(yearmoLast.split("-")[0]);
}
// distinctBetween(2011, 2005) = 7
int distinctNumberOfYears = yearLast-yearStart+1;
/**
* monthlyDataGrid will hold all the years down, and the year number, as well as monthly values, plus total across.
*/
Integer columns = 1 + 12 + 1 + 1;
Integer[][] monthlyDataGrid = new Integer[distinctNumberOfYears][columns];
//Initialize all to zero
for(int yearIndex = 0; yearIndex < distinctNumberOfYears; yearIndex++) {
monthlyDataGrid[yearIndex][0] = yearStart+yearIndex;
for(int dataColumnIndex = 1; dataColumnIndex < columns; dataColumnIndex++) {
monthlyDataGrid[yearIndex][dataColumnIndex] = 0;
}
}
for(TableRow monthRow: tableRowList) {
String yearmo = monthRow.getStringColumn(dateColumn);
String[] yearMonthSplit = yearmo.split("-");
Integer currentYear = Integer.parseInt(yearMonthSplit[0]);
Integer currentMonth = Integer.parseInt(yearMonthSplit[1]);
long monthlyHits = monthRow.getLongColumn(valueColumn);
monthlyDataGrid[currentYear-yearStart][currentMonth] = (int) monthlyHits;
}
// Fill first column with year name. And, fill in last column with cumulative annual total.
for(int yearIndex = 0; yearIndex < distinctNumberOfYears; yearIndex++) {
Integer yearCumulative=0;
for(int monthIndex = 1; monthIndex <= 12; monthIndex++) {
yearCumulative += monthlyDataGrid[yearIndex][monthIndex];
}
monthlyDataGrid[yearIndex][13] = yearCumulative;
if(yearIndex == 0) {
monthlyDataGrid[yearIndex][14] = yearCumulative;
} else {
monthlyDataGrid[yearIndex][14] = yearCumulative + monthlyDataGrid[yearIndex-1][14];
}
}
return monthlyDataGrid;
}
/**
* Standard conversion of input date, where input is "Month Year", i.e. "December 2011", i.e. "MMMM yyyy".
* If you need a different date format, then use the other method.
* @param objectCounts
* @return
* @throws ParseException
*/
public Integer[][] convertObjectCountsToIntegerGrid(ObjectCount[] objectCounts) throws ParseException {
SimpleDateFormat dateFormat = new SimpleDateFormat("MMMM yyyy", Locale.getDefault());
return convertObjectCountsToIntegerGrid(objectCounts, dateFormat);
}
public Integer[][] convertObjectCountsToIntegerGrid(ObjectCount[] objectCounts, SimpleDateFormat dateFormat) throws ParseException{
Calendar calendar = Calendar.getInstance();
Date date;
date = dateFormat.parse(objectCounts[0].getValue());
calendar.setTime(date);
Integer yearStart = calendar.get(Calendar.YEAR);
date = dateFormat.parse(objectCounts[objectCounts.length-1].getValue());
calendar.setTime(date);
Integer yearLast = calendar.get(Calendar.YEAR);
int distinctNumberOfYears = yearLast-yearStart+1;
/**
* monthlyDataGrid will hold all the years down, and the year number, as well as monthly values, plus total across.
*/
Integer columns = 1 + 12 + 1 + 1;
Integer[][] monthlyDataGrid = new Integer[distinctNumberOfYears][columns];
//Initialize the dataGrid with yearName and blanks
for(int yearIndex = 0; yearIndex < distinctNumberOfYears; yearIndex++) {
monthlyDataGrid[yearIndex][0] = yearStart+yearIndex;
for(int dataColumnIndex = 1; dataColumnIndex < columns; dataColumnIndex++) {
monthlyDataGrid[yearIndex][dataColumnIndex] = 0;
}
}
//Fill in monthly values
for(ObjectCount objectCountMonth: objectCounts) {
date = dateFormat.parse(objectCountMonth.getValue());
calendar.setTime(date);
long monthlyHits = objectCountMonth.getCount();
monthlyDataGrid[calendar.get(Calendar.YEAR)-yearStart][calendar.get(Calendar.MONTH)+1] = (int) monthlyHits;
}
// Fill in last column with cumulative annual total.
for(int yearIndex = 0; yearIndex < distinctNumberOfYears; yearIndex++) {
Integer yearCumulative=0;
for(int monthIndex = 1; monthIndex <= 12; monthIndex++) {
yearCumulative += monthlyDataGrid[yearIndex][monthIndex];
}
monthlyDataGrid[yearIndex][13] = yearCumulative;
if(yearIndex == 0) {
monthlyDataGrid[yearIndex][14] = yearCumulative;
} else {
monthlyDataGrid[yearIndex][14] = yearCumulative + monthlyDataGrid[yearIndex-1][14];
}
}
return monthlyDataGrid;
}
public void displayAsGrid(Division division, Integer[][] monthlyDataGrid, String name, String header) throws WingException {
if(monthlyDataGrid == null || monthlyDataGrid.length == 0) {
log.error("Grid has no data: "+ header);
Table gridTable = division.addTable(name, 1,1);
gridTable.setHead("No Data Available for " + header);
Row gridHeader = gridTable.addRow(Row.ROLE_HEADER);
gridHeader.addCell().addContent("Year");
gridHeader.addCell().addContent("JAN");
gridHeader.addCell().addContent("FEB");
gridHeader.addCell().addContent("MAR");
gridHeader.addCell().addContent("APR");
gridHeader.addCell().addContent("MAY");
gridHeader.addCell().addContent("JUN");
gridHeader.addCell().addContent("JUL");
gridHeader.addCell().addContent("AUG");
gridHeader.addCell().addContent("SEP");
gridHeader.addCell().addContent("OCT");
gridHeader.addCell().addContent("NOV");
gridHeader.addCell().addContent("DEC");
gridHeader.addCell().addContent("Total YR");
gridHeader.addCell().addContent("Total Cumulative");
return;
}
Integer yearStart = monthlyDataGrid[0][0];
Integer yearLast = monthlyDataGrid[monthlyDataGrid.length-1][0];
int numberOfYears = yearLast-yearStart;
Integer columns = 1 + 12 + 1 + 1;
Table gridTable = division.addTable(name, numberOfYears+1, columns);
gridTable.setHead(header);
Row gridHeader = gridTable.addRow(Row.ROLE_HEADER);
gridHeader.addCell().addContent("Year");
gridHeader.addCell().addContent("JAN");
gridHeader.addCell().addContent("FEB");
gridHeader.addCell().addContent("MAR");
gridHeader.addCell().addContent("APR");
gridHeader.addCell().addContent("MAY");
gridHeader.addCell().addContent("JUN");
gridHeader.addCell().addContent("JUL");
gridHeader.addCell().addContent("AUG");
gridHeader.addCell().addContent("SEP");
gridHeader.addCell().addContent("OCT");
gridHeader.addCell().addContent("NOV");
gridHeader.addCell().addContent("DEC");
gridHeader.addCell().addContent("Total YR");
gridHeader.addCell().addContent("Total Cumulative");
for(int yearIndex=0; yearIndex < monthlyDataGrid.length; yearIndex++) {
Row yearRow = gridTable.addRow();
yearRow.addCell(Cell.ROLE_HEADER).addContent(monthlyDataGrid[yearIndex][0]);
for(int yearContentIndex = 1; yearContentIndex<columns; yearContentIndex++) {
yearRow.addCell().addContent(monthlyDataGrid[yearIndex][yearContentIndex]);
}
}
}
public void displayAsTableRows(Division division, java.util.List<TableRow> tableRowList, String title) throws WingException {
Table table = division.addTable("itemsInContainer", tableRowList.size()+1, 3);
table.setHead(title);
Row header = table.addRow(Row.ROLE_HEADER);
header.addCell().addContent("Month");
header.addCell().addContent("Added During Month");
header.addCell().addContent("Total Cumulative");
int cumulativeHits = 0;
for(TableRow row : tableRowList) {
Row htmlRow = table.addRow(Row.ROLE_DATA);
String yearmo = row.getStringColumn("yearmo");
htmlRow.addCell().addContent(yearmo);
long monthlyHits = row.getLongColumn("countitem");
htmlRow.addCell().addContent(""+monthlyHits);
cumulativeHits += monthlyHits;
htmlRow.addCell().addContent(""+cumulativeHits);
}
}
public java.util.List<TableRow> addFilesInContainerQuery(DSpaceObject dso) {
java.util.List<TableRow> tableRowList = null;
String typeTextLower = "";
if(dso.getType() == Constants.COMMUNITY) {
typeTextLower = "communities";
} else {
typeTextLower = dso.getTypeText().toLowerCase();
}
String querySpecifyContainer = "SELECT to_char(date_trunc('month', t1.ts), 'YYYY-MM') AS yearmo, count(*) as countitem " +
"FROM ( SELECT to_timestamp(text_value, 'YYYY-MM-DD') AS ts FROM metadatavalue, item, item2bundle, bundle, bundle2bitstream, " +
typeTextLower + "2item " +
"WHERE metadata_field_id = 12 AND metadatavalue.item_id = item.item_id AND item.in_archive=true AND " +
"item2bundle.bundle_id = bundle.bundle_id AND item2bundle.item_id = item.item_id AND bundle.bundle_id = bundle2bitstream.bundle_id AND bundle.\"name\" = 'ORIGINAL' AND "+
typeTextLower + "2item.item_id = item.item_id AND "+
typeTextLower + "2item."+dso.getTypeText().toLowerCase()+"_id = ? ";
if (dateStart != null) {
String start = dateFormat.format(dateStart);
querySpecifyContainer += "AND metadatavalue.text_value > '"+start+"'";
}
if(dateEnd != null) {
String end = dateFormat.format(dateEnd);
querySpecifyContainer += " AND metadatavalue.text_value < '"+end+"' ";
}
querySpecifyContainer += ") t1 GROUP BY date_trunc('month', t1.ts) order by yearmo asc";
try {
TableRowIterator tri = DatabaseManager.query(context, querySpecifyContainer, dso.getID());
tableRowList = tri.toList();
} catch (Exception e) {
}
return tableRowList;
}
public void addFilesInContainer(DSpaceObject dso, Division division) {
java.util.List<TableRow> tableRowList = addFilesInContainerQuery(dso);
Gson gson = new Gson();
try {
division.addHidden("gson-filesAdded").setValue(gson.toJson(tableRowList));
Integer[][] monthlyDataGrid = convertTableRowListToIntegerGrid(tableRowList, "yearmo", "countitem");
displayAsGrid(division, monthlyDataGrid, "filesInContainer-grid", "Number of Files in the "+dso.getName());
//displayAsTableRows(division, tableRowList, "Number of Files in the "+getTypeAsString(dso));
} catch (WingException e) {
log.error(e.getMessage()); //To change body of catch statement use File | Settings | File Templates.
}
}
}

View File

@@ -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;
}
}

View File

@@ -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 "";
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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;
}
}

View File

@@ -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>

View File

@@ -50,6 +50,13 @@
<ref bean="dspace.eventService"/>
</property>
</bean>
<!-- Elastic Search -->
<bean class="org.dspace.statistics.ElasticSearchLoggerEventListener">
<property name="eventService">
<ref bean="dspace.eventService" />
</property>
</bean>
<!--
Uncomment to enable TabFileUsageEventListener

View File

@@ -95,6 +95,7 @@
<map:transformer name="LDAPLogin" src="org.dspace.app.xmlui.aspect.eperson.LDAPLogin"/>
<map:transformer name="notice" src="org.dspace.app.xmlui.aspect.general.NoticeTransformer"/>
<map:transformer name="NamespaceFilter" src="org.dspace.app.xmlui.cocoon.NamespaceFilterTransformer"/>
<map:transformer name="RestrictedItem" src="org.dspace.app.xmlui.aspect.artifactbrowser.RestrictedItem"/>
</map:transformers>
<map:matchers default="wildcard">
<map:matcher logger="sitemap.matcher.wildcard" name="wildcard"
@@ -122,6 +123,7 @@
<map:parameter name="ignore-missing-tables" value="true"/>
</map:matcher>
<map:matcher name="request" src="org.apache.cocoon.matching.RequestParameterMatcher"/>
<map:matcher name="StatisticsAuthorizedMatcher" src="org.dspace.app.xmlui.aspect.statistics.StatisticsAuthorizedMatcher"/>
</map:matchers>
<map:selectors>
<map:selector name="browser" src="org.apache.cocoon.selection.BrowserSelector"
@@ -177,6 +179,8 @@
<!-- The statement below tells the selector to unroll as much exceptions as possible -->
<exception class="java.lang.Throwable" unroll="true"/>
</map:selector>
<!-- AuthenticatedSelector for forcing user to login if not authorized - PMD -->
<map:selector name="AuthenticatedSelector" src="org.dspace.app.xmlui.aspect.general.AuthenticatedSelector"/>
</map:selectors>
<map:readers default="resource">
<map:reader name="resource" src="org.apache.cocoon.reading.ResourceReader"
@@ -186,6 +190,9 @@
<map:reader name="BitstreamReader" src="org.dspace.app.xmlui.cocoon.BitstreamReader"/>
<map:reader name="ExportReader" src="org.dspace.app.xmlui.cocoon.ItemExportDownloadReader"/>
<map:reader name="MetadataExportReader" src="org.dspace.app.xmlui.cocoon.MetadataExportReader"/>
<map:reader name="CSVOutputter" src="org.dspace.app.xmlui.aspect.statisticsElasticSearch.CSVOutputter"/>
<map:reader name="OpenURLReader" src="org.dspace.app.xmlui.cocoon.OpenURLReader"/>
<map:reader name="SitemapReader" src="org.dspace.app.xmlui.cocoon.SitemapReader"/>
<map:reader name="ConcatenationReader" src="org.dspace.app.xmlui.cocoon.ConcatenationReader"/>
@@ -378,6 +385,50 @@
</map:read>
</map:match>
<map:match pattern="handle/*/*/stats/csv">
<map:match type="StatisticsAuthorizedMatcher" pattern="READ">
<map:read type="CSVOutputter"/>
</map:match>
<map:match type="StatisticsAuthorizedMatcher" pattern="!READ">
<map:select type="AuthenticatedSelector">
<map:when test="eperson">
<map:transform type="RestrictedItem"/>
<map:serialize/>
</map:when>
<map:otherwise>
<map:act type="StartAuthentication">
<map:parameter name="header" value="xmlui.ArtifactBrowser.RestrictedItem.auth_header"/>
<map:parameter name="message" value="xmlui.ArtifactBrowser.RestrictedItem.auth_message"/>
</map:act>
<map:serialize/>
</map:otherwise>
</map:select>
</map:match>
</map:match>
<map:match pattern="handle/*/*/stats/csv/*">
<map:match type="StatisticsAuthorizedMatcher" pattern="READ">
<map:read type="CSVOutputter"/>
</map:match>
<map:match type="StatisticsAuthorizedMatcher" pattern="!READ">
<map:select type="AuthenticatedSelector">
<map:when test="eperson">
<map:transform type="RestrictedItem"/>
<map:serialize/>
</map:when>
<map:otherwise>
<map:act type="StartAuthentication">
<map:parameter name="header" value="xmlui.ArtifactBrowser.RestrictedItem.auth_header"/>
<map:parameter name="message" value="xmlui.ArtifactBrowser.RestrictedItem.auth_message"/>
</map:act>
<map:serialize/>
</map:otherwise>
</map:select>
</map:match>
</map:match>
<map:match pattern="JSON/discovery/**">
<map:mount check-reload="no" src="aspects/json-solr.xmap" uri-prefix="JSON/discovery/"/>
</map:match>

View File

@@ -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;
}

View File

@@ -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>

View File

@@ -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() {};

View File

@@ -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;
}

View File

@@ -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">&nbsp;</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);

View File

@@ -0,0 +1,4 @@
jQuery(document).ready(function()
{
$('ul#aspect_artifactbrowser_CommunityBrowser_list_comunity-browser').collapsibleCheckboxTree();
});

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -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*/
}

View File

@@ -0,0 +1,6 @@
// # ReportGenerator Js Mod
// ## Addes jQuery datepicker functionality to the ReportGenerator
jQuery(function ($) {
$(".date-picker").datepicker({dateFormat: "mm/dd/yy"});
});

View File

@@ -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.
*/

View File

@@ -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;
}

View File

@@ -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>

View File

@@ -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());
}

View File

@@ -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);

View File

@@ -1767,3 +1767,11 @@ webui.suggest.enable = false
# will be loaded in the multiple select list of the RestrictStep
xmlui.submission.restrictstep.groups=SubmissionAdmin
xmlui.submission.restrictstep.enableAdvancedForm=true
# Elastic Search Engine for UsageEvent Statistics
#statistics.elasticsearch.clusterName = dspacestatslogging
#statistics.elasticsearch.indexName = dspaceindex
#statistics.elasticsearch.indexType = stats
#statistics.elasticsearch.address = 127.0.0.1
#statistics.elasticsearch.port = 9300
#statistics.elasticsearch.storeData = true

View File

@@ -71,6 +71,7 @@
<aspect name="E-Person" path="resource://aspects/EPerson/" />
<aspect name="Submission and Workflow" path="resource://aspects/Submission/" />
<aspect name="Statistics" path="resource://aspects/Statistics/" />
<aspect name="Statistics - Elastic Search" path="resource://aspects/StatisticsElasticSearch/" />
<aspect name="Original Workflow" path="resource://aspects/Workflow/" />
<!--<aspect name="XMLWorkflow" path="resource://aspects/XMLWorkflow/" />-->
<!--
@@ -120,6 +121,8 @@
<!-- <theme name="Test Theme 1" handle="123456789/1" path="theme1/"/> -->
<!-- <theme name="Test Theme 2" regex="community-list" path="theme2/"/> -->
<theme name="Elastic Search Statistics Theme" regex="stats" path="dashboard/" />
<!-- Mirage theme, @mire contributed theme, default since DSpace 3.0 -->
<theme name="Atmire Mirage Theme" regex=".*" path="Mirage/" />

View File

@@ -24,6 +24,7 @@
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<filters.file>build.properties</filters.file>
<lucene.version>3.5.0</lucene.version>
</properties>
<build>
@@ -619,12 +620,12 @@
<dependency>
<groupId>org.apache.lucene</groupId>
<artifactId>lucene-core</artifactId>
<version>3.3.0</version>
<version>${lucene.version}</version>
</dependency>
<dependency>
<groupId>org.apache.lucene</groupId>
<artifactId>lucene-analyzers</artifactId>
<version>3.3.0</version>
<version>${lucene.version}</version>
</dependency>
<dependency>
<groupId>org.dspace</groupId>