/*
* Gamers Own Instant Messenger
* Copyright (C) 2005-2006 Herbert Poul (kahless@sphene.net)
* http://goim.sphene.net
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
package net.sphene.goim.stats;
import galena.addins.interfaces.httpd.HTTPModule;
import galena.addins.interfaces.httpd.HTTPServer;
import galena.addins.interfaces.httpd.HTTPViewAddin;
import galena.addins.interfaces.httpd.URLMessage;
import galena.addins.interfaces.httpd.HTTPModule.Args;
import galena.addins.modules.database.DB;
import galena.message.Message;
import galena.message.StandAloneCommandHandler;
import galena.message.StandAloneMessageHandler;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Paint;
import java.awt.Shape;
import java.awt.Stroke;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.text.SimpleDateFormat;
import java.util.List;
import java.util.Map;
import javax.imageio.ImageIO;
import net.sphene.galena.addins.AddinImplementationDefinition;
import net.sphene.galena.addins.AddinInterfaceDefinition;
import net.sphene.galena.addins.AddinInterfaceMethodDefinition;
import net.sphene.goim.stats.StatsCollectorBot.PresenceWrapper;
import org.apache.velocity.Template;
import org.jivesoftware.smack.Roster;
import org.jivesoftware.smack.packet.Presence;
import org.jivesoftware.smack.util.StringUtils;
import org.krysalis.jcharts.axisChart.AxisChart;
import org.krysalis.jcharts.chartData.AxisChartDataSet;
import org.krysalis.jcharts.chartData.ChartDataException;
import org.krysalis.jcharts.chartData.DataSeries;
import org.krysalis.jcharts.encoders.PNGEncoder;
import org.krysalis.jcharts.properties.AxisProperties;
import org.krysalis.jcharts.properties.ChartProperties;
import org.krysalis.jcharts.properties.DataAxisProperties;
import org.krysalis.jcharts.properties.LegendProperties;
import org.krysalis.jcharts.properties.LineChartProperties;
import org.krysalis.jcharts.properties.PointChartProperties;
import org.krysalis.jcharts.properties.PropertyException;
import org.krysalis.jcharts.types.ChartType;
@AddinInterfaceDefinition(
defaultcommandprefix = "HTTP/goimstats",
userlevel = -1
)
@AddinImplementationDefinition(
author = "",
requirements = { "net.sphene.goim.stats.StatsCollectorBot" }
)
public class StatsWebInterface extends HTTPViewAddin {
public static final String STATUSICONSPATH = "/internal/images/status";
StatsCollectorBot bot;
DB db;
private String themedir;
@Override
public void init(Object[] requirements) {
bot = (StatsCollectorBot)requirements[0];
this.db = bot.getDB();
}
@AddinInterfaceMethodDefinition(
userlevel = -1
)
public Template index(HTTPModule.Args args) {
return args.printMessage("Coming soon");
}
@Override
public void beforeInit(Message msg) {
super.beforeInit(msg);
themedir = msg.getOption("themedir");
}
public String getImageNameFromPresence(Presence presence) {
if(presence == null || presence.getType() != Presence.Type.AVAILABLE)
return "offline.png";
else {
if(presence.getMode() == Presence.Mode.AVAILABLE)
return "online.png";
if(presence.getMode() == Presence.Mode.AWAY)
return "away.png";
if(presence.getMode() == Presence.Mode.CHAT)
return "chat.png";
if(presence.getMode() == Presence.Mode.DO_NOT_DISTURB)
return "dnd.png";
if(presence.getMode() == Presence.Mode.INVISIBLE)
return "invisible.png";
if(presence.getMode() == Presence.Mode.EXTENDED_AWAY)
return "xa.png";
return "offline.png";
}
}
public File getImageFile(Presence presence) {
File path = new File(themedir,STATUSICONSPATH);
return new File(path,getImageNameFromPresence(presence));
}
public BufferedImage getImage(Presence presence) {
try {
BufferedImage image = ImageIO.read(getImageFile(presence));
return image;
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
@Override
public void registerHandler() {
super.registerHandler();
StandAloneCommandHandler cmdH = new StandAloneCommandHandler(getCommandPrefix() + "/statusImage") {
class ByteCounter {
public int bytes;
}
public StandAloneMessageHandler setUserLevel(int userlevel) {
separator = "/";
return super.setUserLevel(userlevel);
}
public Message getAddHandlerMessage() {
return (new URLMessage()).setLineString(this.getCommand()).setMessagetype(Message.MSG_ADD_HANDLER).setFrom(this).parse();
}
public int handleMessage(Message msg) {
HTTPServer.Data data = (HTTPServer.Data)msg.getFrom();
//Args args = new Args(data.request,data.response,data.session,msg,data);
final Args args = httpModule.createArgs(data,msg);
args.res.setHeader("content-type:","image/png");
boolean small = args.req.getValue("small","0").contains("1");
BufferedImage image = new BufferedImage((small ? 200 : 400),(small ? 16 : 25),BufferedImage.TYPE_INT_ARGB);
Graphics2D g = image.createGraphics();
Color transparent = new Color(1f,1f,1f,1f);
g.setBackground(transparent);
g.setColor(transparent);
g.fillRect(0,0,image.getWidth(),image.getHeight());
g.setColor(Color.BLACK);
if(args.msg.getMsgParameter() > 0) {
String jid = args.msg.getMsgParameter(0);
Presence presence = bot.getPresenceOf(jid);
g.drawImage(getImage(presence),1,(small ? 0 : 5),null);
PresenceWrapper wrapper = null;
if(presence == null) {
for(String user : bot.presences.keySet()) {
if(StringUtils.parseBareAddress(user).equals(StringUtils.parseBareAddress(jid))) {
PresenceWrapper myWrapper = bot.presences.get(user);
if(wrapper == null || wrapper.date.before(myWrapper.date))
wrapper = myWrapper;
}
}
} else {
wrapper = bot.presences.get(presence.getFrom());
}
if(wrapper != null && wrapper.presence != presence)
wrapper = null;
StringBuffer buf = new StringBuffer();
if(!small) buf.append(jid).append(": ");
if(presence == null)
buf.append("Offline");
else {
if(presence.getType() == Presence.Type.AVAILABLE) {
buf.append(presence.getMode().toString());
//.append(" - ").append(presence.getStatus());
if(!small && presence.getStatus() != null)
g.drawString(presence.getStatus(),20,22);
} else {
buf.append("Offline");
if(presence.getStatus() != null)
buf.append(" ").append(presence.getStatus());
}
if(!small && wrapper != null) {
buf.append(" (since ").append(new SimpleDateFormat("HH:mm:ss").format(wrapper.date)).append(")");
}
}
g.drawString(buf.toString(),20,10);
} else {
g.drawString("ERROR: No JID ?!",20,10);
}
final ByteCounter byteCounter = new ByteCounter(); byteCounter.bytes = 0;
try {
ImageIO.write(image,"png",new OutputStream() {
public void write(int b) throws IOException {
args.res.print((char)b);
byteCounter.bytes++;
}
});
args.res.flush();
} catch (IOException e) {
e.printStackTrace();
}
msg.reply(new Message(Message.MSG_INTERN).setLineString("logsize " + byteCounter.bytes).parse());
return 1;
}
};
cmdH.setUserLevel(-1);
kernel.registerCommandHandler(cmdH);
cmdH = new StandAloneCommandHandler(getCommandPrefix() + "/statsImage") {
class ByteCounter {
public int bytes;
}
public StandAloneMessageHandler setUserLevel(int userlevel) {
separator = "/";
return super.setUserLevel(userlevel);
}
public Message getAddHandlerMessage() {
return (new URLMessage()).setLineString(this.getCommand()).setMessagetype(Message.MSG_ADD_HANDLER).setFrom(this).parse();
}
public final Color availablecolors[] = { Color.gray,Color.blue,Color.red,Color.green,Color.yellow,Color.cyan,Color.magenta,Color.darkGray,Color.lightGray,Color.orange,Color.pink };
public int handleMessage(Message msg) {
HTTPServer.Data data = (HTTPServer.Data)msg.getFrom();
//Args args = new Args(data.request,data.response,data.session,msg,data);
final Args args = httpModule.createArgs(data,msg);
if(args.msg.getMsgParameter() < 1) return 0;
String jid = args.msg.getMsgParameter(0).toLowerCase();
boolean showmodes = Boolean.parseBoolean(args.req.getValue("showmodes","true"));
boolean showgames = Boolean.parseBoolean(args.req.getValue("showgames","true"));
int width = Integer.parseInt(args.req.getValue("width","500"));
int height = Integer.parseInt(args.req.getValue("height","500"));
args.res.setHeader("content-type:","image/png");
List<Map<String,Object>> getjidID = db.getListOfMaps("SELECT * FROM goim_stats_jid WHERE jid = ?",jid);
if(getjidID.size() > 0) {
final int DAYCOUNT = 14;
final int MAXTYPES = 10;
double[][] points = new double[ MAXTYPES ][ DAYCOUNT ];
String[] labels = new String[MAXTYPES];
String[] xAxisLabels = new String[DAYCOUNT];
int jidID = (Integer)getjidID.get(0).get("id");
List<Map<String, Object>> days = db.getListOfMaps("SELECT * FROM goim_stats_day WHERE jid = ? ORDER BY day DESC LIMIT 14",jidID);
int i = DAYCOUNT - days.size();
for(i = 0 ; i < (DAYCOUNT - days.size()) ; i++) {
xAxisLabels[i] = "undefined ?";
}
i = DAYCOUNT-1;
for(Map<String, Object> day : days) {
if(showmodes) {
List<Map<String, Object>> presences = db.getListOfMaps("SELECT * FROM goim_stats_presencestatus WHERE day = ?",day.get("id"));
for(Map<String,Object> presence : presences) {
String mode = (String)presence.get("mode");
int duration = (Integer)presence.get("duration");
int j = 0;
for(; j < MAXTYPES ; j++) {
if(labels[j] == null) {
labels[j] = mode; break;
} else if(labels[j].equals(mode))
break;
}
if(j == MAXTYPES) {
System.err.println("Too much modes ?! Mode: " + mode);
break;
}
points[j][i] = ((double)duration) / ((double)60);
}
}
if(showgames) {
List<Map<String, Object>> games = db.getListOfMaps("SELECT * FROM goim_stats_game WHERE day = ?",day.get("id"));
for(Map<String,Object> game : games) {
String gamestr = (String)game.get("game");
int duration = (Integer)game.get("duration");
int j = 0;
for(; j < MAXTYPES ; j++) {
if(labels[j] == null) {
labels[j] = gamestr; break;
} else if(labels[j].equals(gamestr))
break;
}
if(j == MAXTYPES) {
System.err.println("Too much modes ?! Mode: " + gamestr);
break;
}
points[j][i] = ((double)duration) / ((double)60);
}
}
xAxisLabels[i] = day.get("day").toString();
i--;
}
int realcount = -1;
for(i = 0 ; i < labels.length ; i++) {
if(labels[i] == null) { realcount = i; break; }
}
if(realcount != labels.length) {
double[][] originalpoints = points;
String[] originallabels = labels;
points = new double[ realcount ][ DAYCOUNT ];
labels = new String[ realcount ];
for(i = 0 ; i < realcount ; i++ ) {
points[i] = originalpoints[i];
labels[i] = originallabels[i];
}
}
Stroke[] strokes = new Stroke[realcount];
Shape[] shapes = new Shape[realcount];
for(int j = 0 ; j < realcount ; j++) {
strokes[j] = LineChartProperties.DEFAULT_LINE_STROKE;
shapes[j] = PointChartProperties.SHAPE_CIRCLE;
}
LineChartProperties lineChartProperties = new LineChartProperties(strokes,shapes);
try {
Paint[] paint = new Paint[labels.length];
for(int j = 0 ; j < paint.length ; j++) {
paint[j] = availablecolors[j];
}
AxisChartDataSet axisChartDataSet = new AxisChartDataSet(points,labels,paint,ChartType.LINE,lineChartProperties);
DataSeries dataSeries = new DataSeries(xAxisLabels,"Date","Time in Hours","Online Statistics for " + jid);
dataSeries.addIAxisPlotDataSet(axisChartDataSet);
ChartProperties chartProperties = new ChartProperties();
AxisProperties axisProperties = new AxisProperties( );
axisProperties.setXAxisLabelsAreVertical(true);
DataAxisProperties dataAxisProperties= (DataAxisProperties) axisProperties.getYAxisProperties();
dataAxisProperties.setRoundToNearest(-1);
LegendProperties legendProperties = new LegendProperties();
legendProperties.setPlacement(LegendProperties.RIGHT);
legendProperties.setNumColumns(1);
AxisChart axisChart = new AxisChart(dataSeries,chartProperties,axisProperties,legendProperties,width,height);
final ByteCounter byteCounter = new ByteCounter(); byteCounter.bytes = 0;
PNGEncoder.encode(axisChart,new OutputStream() {
public void write(int b) throws IOException {
args.res.print((char)b);
byteCounter.bytes++;
}
});
args.res.flush();
msg.reply(new Message(Message.MSG_INTERN).setLineString("logsize " + byteCounter.bytes).parse());
return 1;
} catch (ChartDataException e) {
e.printStackTrace();
} catch (PropertyException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
BufferedImage image = new BufferedImage(width,height,BufferedImage.TYPE_INT_ARGB);
Graphics2D g = image.createGraphics();
g.setBackground(Color.WHITE);
g.setColor(Color.BLACK);
g.drawString("Error while creating Statistics.",5,15);
final ByteCounter byteCounter = new ByteCounter(); byteCounter.bytes = 0;
try {
ImageIO.write(image,"png",new OutputStream() {
public void write(int b) throws IOException {
args.res.print((char)b);
byteCounter.bytes++;
}
});
args.res.flush();
} catch (IOException e) {
e.printStackTrace();
}
msg.reply(new Message(Message.MSG_INTERN).setLineString("logsize " + byteCounter.bytes).parse());
//msg.reply(new Message(Message.MSG_INTERN).setLineString("logsize " + byteCounter.bytes).parse());
return 1;
}
};
cmdH.setUserLevel(-1);
kernel.registerCommandHandler(cmdH);
}
@AddinInterfaceMethodDefinition(userlevel = -1)
public Template showStatus(HTTPModule.Args args) {
if(args.msg.getMsgParameter() < 1) return null;
String jid = args.msg.getMsgParameter(0).toLowerCase();
Presence presence = bot.getPresenceOf(jid);
args.context.put("presence",presence);
args.context.put("jid",jid);
args.context.put("statsWebInterface",this);
List<Map<String, Object>> historyLog = db.getListOfMaps("SELECT * FROM goim_presence_log WHERE jid = ? ORDER BY start DESC LIMIT 50",jid);
for(Map<String, Object> log : historyLog) {
log.put("servers",db.getListOfMaps("SELECT * FROM goim_server WHERE presence = ?",log.get("id")));
}
args.context.put("historyLog",historyLog);
List<Map<String,Object>> getjidID = db.getListOfMaps("SELECT * FROM goim_stats_jid WHERE jid = ?",jid);
if(getjidID.size() > 0) {
int jidID = (Integer)getjidID.get(0).get("id");
List<Map<String, Object>> days = db.getListOfMaps("SELECT * FROM goim_stats_day WHERE jid = ? ORDER BY day DESC LIMIT 14",jidID);
for(Map<String, Object> day : days) {
day.put("presences",db.getListOfMaps("SELECT * FROM goim_stats_presencestatus WHERE day = ?",day.get("id")));
day.put("games",db.getListOfMaps("SELECT * FROM goim_stats_game WHERE day = ?",day.get("id")));
}
args.context.put("days",days);
}
return args.getThemeTemplate("goim/stats/showstatus.vm");
//args.printMessage(formatPresence(presence) + (presence == null ? "" : "<br/><br/>Full XML: " + presence.toXML().replace("<","<").replace(">",">")));
}
@AddinInterfaceMethodDefinition(userlevel = -1)
public Template showRoster(HTTPModule.Args args) {
Roster roster = bot.getRoster();
args.context.put("roster",roster);
args.context.put("statsWebInterface",this);
if(args.req.getValue("showOffline","").equals("1"))
args.context.put("showOffline","1");
return args.getThemeTemplate("goim/stats/showroster.vm");
}
public String formatPresence(Presence presence) {
return presence == null ? "Offline" : presence.getType().toString() + ": " + presence.getMode().toString() + " - " + presence.getStatus();
}
}